shaileshjadhavSS commited on
Commit
cd570f9
·
1 Parent(s): cbf81e5

Added UI and backend for bot

Browse files
.gitignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ **/__pycache__/
2
+ *.env
3
+ *.jpg
4
+ *.jpeg
5
+ *.mp3
6
+ app.log
7
+ *.png
app.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ from streamlit_chat import message
4
+ from model.utils import create_model, setup_logger, generate_content
5
+ import google.generativeai as genai
6
+ import base64
7
+ from settings.base import setup_logger, GOOGLE_API_KEY
8
+ import json
9
+ from datetime import datetime
10
+
11
+ from user_auth.user_manager import UserManager
12
+ from user_auth.auth_manager import AuthManager
13
+
14
+ # Set up logging
15
+ logger = setup_logger()
16
+
17
+ # Initialize session state variables
18
+ if 'history' not in st.session_state:
19
+ st.session_state.history = []
20
+ if 'chat_session' not in st.session_state:
21
+ st.session_state.chat_session = None
22
+ if 'authenticated' not in st.session_state:
23
+ st.session_state.authenticated = False
24
+ if 'email' not in st.session_state:
25
+ st.session_state.email = ""
26
+ if 'profile' not in st.session_state:
27
+ st.session_state.profile = None
28
+ if 'model' not in st.session_state:
29
+ st.session_state.model = None
30
+
31
+
32
+
33
+ def save_chat_history(user_name, chat_history):
34
+ """
35
+ Save chat history to a file in a user-specific directory with a timestamp.
36
+ """
37
+ base_dir = "chat_history"
38
+ user_dir = os.path.join(base_dir, user_name)
39
+ os.makedirs(user_dir, exist_ok=True)
40
+
41
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
42
+ file_path = os.path.join(user_dir, f"session_{timestamp}.json")
43
+
44
+ # Prepare a serializable version of chat_history
45
+ serializable_history = []
46
+ for entry in chat_history:
47
+ if entry['role'] == 'user':
48
+ user_entry = {
49
+ 'role': 'user',
50
+ 'parts': entry['parts'][1] if len(entry['parts']) > 1 else entry['parts'][0]
51
+ }
52
+ serializable_history.append(user_entry)
53
+ elif entry['role'] == 'model':
54
+ model_entry = {
55
+ 'role': 'model',
56
+ 'parts': entry['parts']
57
+ }
58
+ serializable_history.append(model_entry)
59
+
60
+ with open(file_path, "w") as f:
61
+ json.dump(serializable_history, f, indent=4)
62
+
63
+ logger.info(f"Chat history saved to {file_path}")
64
+
65
+
66
+ def authenticate_user(email, password):
67
+ """
68
+ Authenticate user using AuthManager.
69
+ """
70
+ user_manager = UserManager()
71
+ auth_manager = AuthManager(user_manager)
72
+ authenticated = auth_manager.authenticate_user(email, password)
73
+ return authenticated
74
+
75
+ def conversation_chat(file_path, text_prompt):
76
+ """
77
+ Handle the conversation with the chat session using the provided file path and text prompt.
78
+ """
79
+ temp_dir = "temp"
80
+ os.makedirs(temp_dir, exist_ok=True)
81
+
82
+ try:
83
+ user_input = text_prompt if text_prompt else ""
84
+
85
+ if file_path:
86
+ temp_file_path = os.path.join(temp_dir, file_path.name)
87
+ with open(temp_file_path, "wb") as f:
88
+ f.write(file_path.read()) # Write the uploaded file to temp location
89
+
90
+ logger.info(f"Successfully saved uploaded file: {file_path.name}")
91
+
92
+ file = genai.upload_file(path=temp_file_path, display_name=file_path.name)
93
+ user_entry = {
94
+ "role": "user",
95
+ "parts": [file, user_input]
96
+ }
97
+ else:
98
+ user_entry = {
99
+ "role": "user",
100
+ "parts": [user_input]
101
+ }
102
+
103
+ st.session_state.history.append(user_entry)
104
+
105
+ response_text = generate_content(st.session_state.model, st.session_state.history)
106
+
107
+ bot_entry = {
108
+ "role": "model",
109
+ "parts": response_text
110
+ }
111
+
112
+ st.session_state.history.append(bot_entry)
113
+
114
+ logger.info("Conversation successfully processed")
115
+
116
+ except Exception as e:
117
+ logger.error(f"Error processing file: {e}")
118
+ st.session_state.history.append("Error")
119
+
120
+ def display_chat():
121
+ """
122
+ Display chat input and responses.
123
+ """
124
+ profile = st.session_state.profile
125
+ st.title("Wellness Bot 🦾🤖")
126
+
127
+ chat_container = st.container()
128
+ upload_container = st.container()
129
+
130
+ clear_chat_button = st.button('Clear Chat')
131
+ if st.button('Logout'):
132
+
133
+ # Save chat history before logging out
134
+ save_chat_history(st.session_state.email, st.session_state.history)
135
+
136
+ st.session_state.authenticated = False
137
+ st.session_state.email = ""
138
+ st.session_state.history = []
139
+ st.session_state.chat_session = None
140
+ st.session_state.profile = None
141
+ st.session_state.model = None
142
+ st.rerun()
143
+
144
+
145
+ with upload_container:
146
+ with st.form(key='chat_form', clear_on_submit=True):
147
+ file_path = st.file_uploader("Upload an image or audio of a meal:", type=["jpg", "jpeg", "png", "mpeg", "mp3", "wav", "ogg", "mp4"])
148
+ text_prompt = st.text_input("Type Here...")
149
+ submit_button = st.form_submit_button(label='Send ⬆️')
150
+
151
+ if submit_button:
152
+ conversation_chat(file_path, text_prompt)
153
+
154
+ if clear_chat_button:
155
+ st.session_state.history = []
156
+ st.session_state.chat_session = None
157
+
158
+ with chat_container:
159
+
160
+ message(f"Hey {profile['name']}! I'm here to assist you with your meals. Let's make healthy eating a breeze. Feel free to upload an image/video/audio of your meal or ask any questions about nutrition, dietary needs, and meal planning. Together, we'll achieve your goals and ensure you stay healthy and happy.", avatar_style="bottts")
161
+ for i, entry in enumerate(st.session_state.history):
162
+ if entry['role'] == 'user':
163
+ if len(entry['parts']) > 1:
164
+ uploaded_file = entry['parts'][0]
165
+ if uploaded_file.mime_type.startswith("image/"):
166
+ file_name= uploaded_file.display_name
167
+ with open(os.path.join("temp", file_name), "rb") as file:
168
+ encoded_img = base64.b64encode(file.read()).decode('utf-8')
169
+ img_html = f'<img src="data:image/png;base64,{encoded_img}" width="200" style="margin-top: 5px;"/>'
170
+ st.markdown(f"""
171
+ <div style="display: flex; justify-content: flex-end; margin-bottom: 10px;">
172
+ <div style="max-width: 300px; background-color: #E8E8E8; border-radius: 10px; padding: 10px; position: relative;">
173
+ <div style="text-align: right;">
174
+ {img_html}
175
+ </div>
176
+ </div>
177
+ </div>
178
+ """, unsafe_allow_html=True)
179
+ else:
180
+ message(uploaded_file.display_name, is_user=True, key=f"{i}_user", avatar_style="adventurer")
181
+
182
+ if entry['parts'][1] != "":
183
+ message(entry['parts'][1], is_user=True, key=f"{i}_user", avatar_style="adventurer")
184
+ else:
185
+ message(entry['parts'][0], is_user=True, key=f"{i}_user", avatar_style="adventurer")
186
+ elif entry['role'] == 'model':
187
+ message(entry['parts'], key=str(i), avatar_style="bottts")
188
+
189
+ def main():
190
+ """
191
+ Main function to run the Streamlit app.
192
+ """
193
+ user_manager = UserManager()
194
+ auth_manager = AuthManager(user_manager)
195
+ st.set_page_config(page_title="Wellness Bot 🦾🤖", page_icon=":fork_and_knife:")
196
+
197
+ if st.session_state.authenticated:
198
+ if st.session_state.profile is None or st.session_state.model is None:
199
+ st.session_state.profile = user_manager.get_user(st.session_state.email).profile
200
+ st.session_state.model = create_model(GOOGLE_API_KEY, st.session_state.profile)
201
+
202
+ display_chat()
203
+ else:
204
+ st.title("Wellness Bot 🦾🤖 \n\nLogin/SignUp")
205
+
206
+ tab1, tab2 = st.tabs(["Login", "Sign Up"])
207
+
208
+ with tab1:
209
+ st.header("Login")
210
+ email = st.text_input("Email", key="login_email")
211
+ password = st.text_input("Password", type="password", key="login_password")
212
+ if st.button("Login"):
213
+ if auth_manager.authenticate_user(email, password):
214
+ st.success("Login successful!")
215
+ st.session_state.authenticated = True
216
+ st.session_state.email = email
217
+ st.rerun()
218
+ else:
219
+ st.error("Invalid email or password")
220
+
221
+ with tab2:
222
+ st.header("Create Account")
223
+ email = st.text_input("Email", key="signup_email")
224
+ password = st.text_input("Password", type="password", key="signup_password")
225
+ confirm_password = st.text_input("Confirm Password", type="password", key="signup_confirm_password")
226
+
227
+ name = st.text_input("Name")
228
+ age = st.number_input("Age", min_value=1, max_value=120)
229
+ gender = st.selectbox("Gender", ["Male", "Female", "Other"])
230
+ height = st.number_input("Height (cm)", min_value=30, max_value=300)
231
+ weight = st.number_input("Weight (kg)", min_value=1, max_value=500)
232
+ location = st.text_input("Location")
233
+ allergies = st.text_input("Allergies (comma-separated)")
234
+ spec_diet_pref = st.text_input("Special Dietary Preferences")
235
+ primary_goal = st.text_area("Primary Goal")
236
+ health_condition = st.text_area("Health Condition")
237
+ activity_level = st.selectbox("Activity Level", ["Sedentary", "Lightly Active", "Moderately Active", "Very Active"])
238
+ daily_calorie_intake = st.number_input("Daily Calorie Intake", min_value=1, max_value=10000)
239
+
240
+ if st.button("Sign Up"):
241
+ if password != confirm_password:
242
+ st.error("Passwords do not match")
243
+ else:
244
+ profile = {
245
+ "name": name,
246
+ "age": age,
247
+ "gender": gender,
248
+ "height": height,
249
+ "weight": weight,
250
+ "location": location,
251
+ "allergies": allergies.split(','),
252
+ "spec_diet_pref": spec_diet_pref,
253
+ "primary_goal": primary_goal,
254
+ "health_condition": health_condition,
255
+ "activity_level": activity_level,
256
+ "daily_calorie_intake": daily_calorie_intake
257
+ }
258
+ success, message = user_manager.create_user(email, password, profile)
259
+ if success:
260
+ st.success(message)
261
+ else:
262
+ st.error(message)
263
+
264
+ if __name__ == "__main__":
265
+ main()
model/prompts.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def chatbot_prompt(profile):
2
+ age = profile.get('age', 'unknown')
3
+ gender = profile.get('gender', 'unknown')
4
+ location = profile.get('location', 'unknown')
5
+ spec_diet_pref = profile.get('spec_diet_pref', 'unknown')
6
+ primary_goal = profile.get('primary_goal', 'unknown')
7
+ health_condition = profile.get('health_condition', 'unknown')
8
+ activity_level = profile.get('activity_level', 'unknown')
9
+ daily_calorie_intake = profile.get('daily_calorie_intake', 'unknown')
10
+
11
+
12
+ return(f"""You are a highly knowledgeable nutritionist. You provide expert advice on meal planning, dietary needs, nutritional values, and healthy eating habits.
13
+ You avoid answering any questions that are not related to meals, food, nutrition, or diet.
14
+ Keep your responses focused on offering practical and evidence-based nutritional guidance.
15
+ Keep your tone motivational and supportive.
16
+
17
+ when you provide your expert advice always consider user details which are as follows and explain in what way it is tailored for the user:
18
+ Age: {age},Gender: {gender},Location: {location}, Special Diet Preference: {spec_diet_pref}, Primary Goal: {primary_goal},
19
+ Health Condition: {health_condition} Activity Level: {activity_level}, Daily Calorie Intake: {daily_calorie_intake} calories
20
+
21
+ Chat Flow:
22
+ 1. Ask the user which meal of the day is uploading (breakfast, lunch, dinner, or snack).
23
+ 2. Identify, mention what you could identified and ask required follow up questions as needed to provide a proper response
24
+ a. if user uploads an image:
25
+ - analyze the image to identify the meal name, ingredients, and detailed and necessary nutritional information including portion of each nutrient with its metric. \n\nMeal Analysis:\n\nMeal Name:\n[Identified Meal Name]\n\nIdentified Ingredient:\nIngredient 1\nIngredient 2\nIngredient 3\n...\n\nNutritional value Information (per serving)
26
+ - If the image is not of a meal, respond with 'Please add a valid image.' If the image or meal details are unclear or incomplete, ask specific questions for clarification. If the image appears fake, respond with 'Please add an original meal image.'
27
+ b. if user uploads audio:
28
+ - transcribe the audio and identify the meal related information mentioned and reply to the user the transcribed text and get a confirmation.
29
+ - respond with the identified meal name, ingredients and thier quantity (if not mentioned ask the user to provide), provide the necessary nutritional information in detail including portion of each nutrient with its metric. \n\nMeal Analysis:\n\nMeal Name:\n[Identified Meal Name]\n\nIdentified Ingredient:\nIngredient 1\nIngredient 2\nIngredient 3\n...\n\nNutritional value Information (per serving)
30
+ - If the audio is not of a meal, respond with 'Please provide a valid meal description.' If the audio or meal details are unclear or incomplete, ask specific questions for clarification. If the audio appears fake, respond with 'Please provide proper clear audio.'
31
+ c. if user uploads video:
32
+ - analyse the video uploaded and identify the meal related information mentioned or visible in the frames and reply to the user the transcription and get a confirmation.
33
+ - If the video has audio, transcribe the audio and identify all the meal related information mentioned (it could be the meal name or the ingredients) and reply to the user the transcribed text and get a confirmation.
34
+ - respond with the identified meal name, ingredients, and detailed and necessary nutritional information including portion of each nutrient with its metric. \n\nMeal Analysis:\n\nMeal Name:\n[Identified Meal Name]\n\nIdentified Ingredient:\nIngredient 1\nIngredient 2\nIngredient 3\n...\n\nNutritional Information (per serving)
35
+ - if the video is not a meal or the meal details are unclear or incomplete or if the video has no audio or the audio does not mention any meal , ask specific questions for clarification. If the video appears fake, respond with 'Please provide a valid meal video.'
36
+ d. if user begins the conversation with a text input of their meal
37
+ - Ask additional questions to gather information required to provide a proper response.
38
+ 3. Additional Information Needed:\\n(If applicable) Please provide more details on:\\n- each ingredient clarification(size, type/kind)\\n- Specific cooking instructions (e.g., fried, baked)
39
+ 4. Please confirm if the identified details are correct.
40
+ -If correct, rewrite the identified details in a structured format and suggest a healthy alternative meal with detailed nutritional information and explanation why it is a better alternative.
41
+ 4. Based on identified ingredients and nutritional information, suggest better alternatives cosidering the user's profile information, that would help improve the user's health or achiever goals, with detailed nutritional information and explanation why is this a better alternative. Healthy Alternative Suggestion:\\n\\n\"\n Ingredients:\\nIngredient 1\\nIngredient 2\\nIngredient 3\\n...\\n\\n\"\n Nutritional Information (per serving):\\n\\n
42
+ 5. Then suggest the next healthy meal with a detailed ingredient list , recipe and nutritional value information per serving, explanation on how the suggested meal is beneficial for the user's health or goals. Also, help User keep track of their calorie intake by providing a calorie intake breakdown of the current meal ,suggested next meal and their future meals of the day\"\n
43
+
44
+ Every time you provide something, please confirm from the user, If any details are incorrect, please ask for additional information and always consider user details provided.\"\n""")
45
+
model/utils.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import google.generativeai as genai
3
+ from model.prompts import chatbot_prompt
4
+ from settings.base import setup_logger
5
+
6
+ logger = setup_logger()
7
+
8
+ def create_model(api_key, profile):
9
+ """
10
+ Create a GenerativeModel instance using the provided API key.
11
+ """
12
+ generation_config = {
13
+ "temperature": 0.8,
14
+ "top_p": 0.95,
15
+ "top_k": 64,
16
+ "max_output_tokens": 8192,
17
+ "response_mime_type": "text/plain",
18
+ }
19
+
20
+ try:
21
+ genai.configure(api_key=api_key)
22
+ model = genai.GenerativeModel(
23
+ model_name="gemini-1.5-pro",
24
+ generation_config=generation_config,
25
+ system_instruction= chatbot_prompt(profile),
26
+ )
27
+ logger.info("Model created successfully")
28
+ return model
29
+ except Exception as e:
30
+ logger.error(f"Error creating model: {e}")
31
+ return None
32
+
33
+
34
+ def generate_content(model, message):
35
+ """
36
+ Send a message to the chat session and get the response.
37
+ """
38
+ try:
39
+ # msg = f"Query:{message}/n/n Every time you provide something please confirm from the user if the identified details are correct. If they are correct, suggest the next healthy meal with the detailed recipe. If any details are incorrect, please ask for additional information."
40
+ response = model.generate_content(message)
41
+ logger.info(f"Generated content for message")
42
+ return response.text
43
+ except Exception as e:
44
+ logger.error(f"Error generating content for message: {e}")
45
+ return f"Error generating content: {e}"
requirements.txt ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ altair==5.3.0
2
+ annotated-types==0.7.0
3
+ attrs==23.2.0
4
+ bcrypt==4.1.3
5
+ blinker==1.8.2
6
+ cachelib==0.13.0
7
+ cachetools==5.3.3
8
+ certifi==2024.6.2
9
+ charset-normalizer==3.3.2
10
+ click==8.1.7
11
+ Flask==3.0.3
12
+ Flask-Session==0.8.0
13
+ gitdb==4.0.11
14
+ GitPython==3.1.43
15
+ google-ai-generativelanguage==0.6.6
16
+ google-api-core==2.19.1
17
+ google-api-python-client==2.134.0
18
+ google-auth==2.30.0
19
+ google-auth-httplib2==0.2.0
20
+ google-generativeai==0.7.1
21
+ googleapis-common-protos==1.63.2
22
+ grpcio==1.64.1
23
+ grpcio-status==1.62.2
24
+ httplib2==0.22.0
25
+ idna==3.7
26
+ itsdangerous==2.2.0
27
+ Jinja2==3.1.4
28
+ jsonschema==4.22.0
29
+ jsonschema-specifications==2023.12.1
30
+ markdown-it-py==3.0.0
31
+ MarkupSafe==2.1.5
32
+ mdurl==0.1.2
33
+ msgspec==0.18.6
34
+ numpy==2.0.0
35
+ packaging==24.1
36
+ pandas==2.2.2
37
+ pillow==10.3.0
38
+ proto-plus==1.24.0
39
+ protobuf==4.25.3
40
+ psycopg2-binary==2.9.9
41
+ pyarrow==16.1.0
42
+ pyasn1==0.6.0
43
+ pyasn1_modules==0.4.0
44
+ pydantic==2.7.4
45
+ pydantic_core==2.18.4
46
+ pydeck==0.9.1
47
+ Pygments==2.18.0
48
+ pyparsing==3.1.2
49
+ python-dateutil==2.9.0.post0
50
+ pytz==2024.1
51
+ referencing==0.35.1
52
+ requests==2.32.3
53
+ rich==13.7.1
54
+ rpds-py==0.18.1
55
+ rsa==4.9
56
+ six==1.16.0
57
+ smmap==5.0.1
58
+ streamlit==1.36.0
59
+ streamlit-chat==0.1.1
60
+ tenacity==8.4.2
61
+ toml==0.10.2
62
+ toolz==0.12.1
63
+ tornado==6.4.1
64
+ tqdm==4.66.4
65
+ typing_extensions==4.12.2
66
+ tzdata==2024.1
67
+ uritemplate==4.1.1
68
+ urllib3==2.2.2
69
+ watchdog==4.0.1
70
+ Werkzeug==3.0.3
settings/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from settings.base import *
settings/base.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+
4
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
5
+
6
+ DB_NAME = os.environ.get("DB_NAME")
7
+ DB_USER = os.environ.get("DB_USER")
8
+ DB_PASSWORD = os.environ.get("DB_PASSWORD")
9
+ DB_HOST = os.environ.get("DB_HOST")
10
+ DB_PORT = os.environ.get("DB_PORT")
11
+
12
+ def setup_logger():
13
+ logger = logging.getLogger('app_logger')
14
+ logger.setLevel(logging.DEBUG)
15
+
16
+ # Avoid adding multiple handlers if the logger already has handlers
17
+ if not logger.hasHandlers():
18
+ fh = logging.FileHandler('debug.log', mode='w')
19
+ fh.setLevel(logging.DEBUG)
20
+ formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
21
+ fh.setFormatter(formatter)
22
+ logger.addHandler(fh)
23
+
24
+ return logger
user_auth/__init__.py ADDED
File without changes
user_auth/auth_manager.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from user_auth.user_manager import UserManager
2
+ import logging
3
+ from settings.base import setup_logger
4
+
5
+
6
+ logger = setup_logger()
7
+
8
+ class AuthManager:
9
+ def __init__(self, user_manager):
10
+ self.user_manager = user_manager
11
+
12
+ def authenticate_user(self, email, password):
13
+ user = self.user_manager.get_user(email)
14
+ if user:
15
+ logger.info(f'Attempting to authenticate user: {user.email}')
16
+ if user.check_password(password):
17
+ logger.info(f'User authenticated successfully: {user.email}')
18
+ return True
19
+ else:
20
+ logger.error(f'Invalid password for user: {user.email}')
21
+ else:
22
+ logger.error(f'User not found: {email}')
23
+ return False
user_auth/user.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import bcrypt
2
+
3
+ class User:
4
+ def __init__(self, email, password, profile):
5
+ self.email = email
6
+ self.password_hash = self._hash_password(password)
7
+ self.password = password
8
+ self.profile = profile
9
+
10
+ def _hash_password(self, password):
11
+ hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
12
+ return hashed_password
13
+
14
+ def check_password(self, password):
15
+ entered_password = password.encode('utf-8')
16
+ stored_pwd = self.password.encode('utf-8')
17
+ return bcrypt.checkpw(entered_password, stored_pwd)
18
+
user_auth/user_manager.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import psycopg2
2
+ from user_auth.user import User
3
+ from settings.base import setup_logger, DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_USER
4
+
5
+
6
+ logger = setup_logger()
7
+
8
+ class UserManager:
9
+ def __init__(self):
10
+ self.conn = psycopg2.connect(
11
+ dbname=DB_NAME, user=DB_USER, password=DB_PASSWORD, host=DB_HOST, port=DB_PORT
12
+ )
13
+ self.cursor = self.conn.cursor()
14
+
15
+ def create_user(self, email, password, profile):
16
+ if self.get_user(email):
17
+ return False, "User already exists!"
18
+ user = User(email, password, profile)
19
+ try:
20
+ self.cursor.execute(
21
+ """
22
+ INSERT INTO users (username, password, name, age, gender, height, weight, location, allergies, spec_diet_pref, primary_goal, health_condition, activity_level, daily_calorie_intake)
23
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
24
+ """,
25
+ (email, user.password_hash.decode("utf-8"), profile['name'], profile['age'], profile['gender'], profile['height'], profile['weight'], profile['location'], profile['allergies'], profile['spec_diet_pref'], profile['primary_goal'], profile['health_condition'], profile['activity_level'], profile['daily_calorie_intake'])
26
+ )
27
+ self.conn.commit()
28
+ logger.info(f'User created successfully: {email}')
29
+ return True, "Account created successfully!"
30
+ except Exception as e:
31
+ self.conn.rollback()
32
+ logger.error(f'Error creating user: {e}')
33
+ return False, str(e)
34
+
35
+ def get_user(self, email):
36
+ self.cursor.execute("SELECT * FROM users WHERE username = %s", (email,))
37
+ result = self.cursor.fetchone()
38
+ if result:
39
+ profile = {
40
+ "name": result[3],
41
+ "age": result[4],
42
+ "gender": result[5],
43
+ "height": result[6],
44
+ "weight": result[7],
45
+ "location": result[8],
46
+ "allergies": result[9],
47
+ "spec_diet_pref": result[10],
48
+ "primary_goal": result[11],
49
+ "health_condition": result[12],
50
+ "activity_level": result[13],
51
+ "daily_calorie_intake": result[14]
52
+ }
53
+ logger.info(f'User retrieved from database: {result[1]}')
54
+ return User(email=result[1], password=result[1], profile=profile)
55
+ return None
56
+
57
+ def close(self):
58
+ self.cursor.close()
59
+ self.conn.close()