Vo Minh Vu commited on
Commit
738ecfc
·
1 Parent(s): 6c670dd

Upload chat with gemini api

Browse files
.vscode/launch.json CHANGED
@@ -12,6 +12,14 @@
12
  "console": "integratedTerminal",
13
  "justMyCode": true
14
  },
 
 
 
 
 
 
 
 
15
  {
16
  "name": "Python: Attach to chat.py",
17
  "type": "debugpy",
 
12
  "console": "integratedTerminal",
13
  "justMyCode": true
14
  },
15
+ {
16
+ "name": "Python: app.py",
17
+ "type": "debugpy",
18
+ "request": "launch",
19
+ "program": "${workspaceFolder}/app.py",
20
+ "console": "integratedTerminal",
21
+ "justMyCode": true
22
+ },
23
  {
24
  "name": "Python: Attach to chat.py",
25
  "type": "debugpy",
app.py CHANGED
@@ -1,10 +1,9 @@
1
- import torch
2
- from transformers import pipeline, AutoTokenizer
3
- from gradio import Blocks, Textbox, Button, Row, Column
4
  import os
5
  import logging
 
6
  from dotenv import load_dotenv
7
- from src.chat import process_query
8
  from src.database.init_db import init_db
9
  from src.database.db_connection import get_db_engine
10
 
@@ -19,43 +18,140 @@ load_dotenv()
19
  def setup_database():
20
  """Initialize the database at application startup"""
21
  logger.info("Initializing database...")
22
- if init_db():
23
- logger.info("Database initialized successfully")
24
- else:
25
- logger.error("Failed to initialize database")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  # Gradio UI
28
  def create_ui():
29
  with Blocks() as app:
 
 
30
 
31
  with Row():
32
- with Column():
33
- input_text = Textbox(label="Input", placeholder="Type your message here...")
34
- submit_button = Button("Submit")
35
- output_text = Textbox(label="Output", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- def respond(input_text):
38
- response = process_query(input_text)
39
- return response
 
 
 
 
40
 
41
- submit_button.click(respond, inputs=input_text, outputs=output_text)
 
 
 
 
 
 
 
 
 
 
42
 
43
  return app
44
 
45
  if __name__ == "__main__":
46
  # Ensure database is set up before starting the application
47
- setup_database()
48
 
49
  # Check if database connection is working
50
  engine = get_db_engine()
51
  if engine is None:
52
  logger.error("Failed to connect to database. Please check your connection settings.")
 
53
  else:
54
  logger.info("Successfully connected to database")
55
 
56
  # Launch the Gradio app
57
  app = create_ui()
58
- app.launch(share=True)
 
 
 
 
 
 
 
 
 
 
59
 
60
 
61
 
 
1
+ from gradio import Blocks, Chatbot, TextArea, Button, Row, Column, State
 
 
2
  import os
3
  import logging
4
+ import traceback
5
  from dotenv import load_dotenv
6
+ from src.chat import generate_response
7
  from src.database.init_db import init_db
8
  from src.database.db_connection import get_db_engine
9
 
 
18
  def setup_database():
19
  """Initialize the database at application startup"""
20
  logger.info("Initializing database...")
21
+ try:
22
+ if init_db():
23
+ logger.info("Database initialized successfully")
24
+ return True
25
+ else:
26
+ logger.error("Failed to initialize database")
27
+ return False
28
+ except Exception as e:
29
+ logger.error(f"Database initialization error: {str(e)}")
30
+ logger.debug(traceback.format_exc())
31
+ return False
32
+
33
+ # Update chat.py to support conversation history
34
+ def update_chat_for_multi_turn():
35
+ """
36
+ Helper function to handle conversation state for multi-turn chat.
37
+ This handles reconstruction of conversation context for the model.
38
+ """
39
+ # This is handled in the respond function below
40
+ pass
41
 
42
  # Gradio UI
43
  def create_ui():
44
  with Blocks() as app:
45
+ # Initialize conversation state
46
+ chat_history = State([])
47
 
48
  with Row():
49
+ with Column(scale=6):
50
+ # Replace Textbox with Chatbot for multi-turn conversation
51
+ chatbot = Chatbot(
52
+ label="Business Interior Design Assistant",
53
+ height=500,
54
+ bubble_full_width=False,
55
+ show_copy_button=True
56
+ )
57
+
58
+ with Row():
59
+ user_input = TextArea(
60
+ placeholder="Type your message here...",
61
+ lines=2,
62
+ label="Your Message",
63
+ scale=5
64
+ )
65
+ submit_button = Button("Send", scale=1)
66
+
67
+ # Add a clear chat button for better UX
68
+ clear_button = Button("Clear Conversation")
69
+
70
+ def respond(message, history, chat_state):
71
+ """Process user input and update conversation with error handling"""
72
+ if not message.strip():
73
+ return history, chat_state, ""
74
+
75
+ try:
76
+ # Generate response with conversation history context
77
+ try:
78
+ # Check if we need to reset Gemini chat context
79
+ # Currently Gemini chat manages its own state, but
80
+ # we're keeping our own history for resilience
81
+ response = generate_response(message)
82
+
83
+ # Update the visible chat UI
84
+ history.append((message, response))
85
+
86
+ except Exception as e:
87
+ error_msg = f"I apologize, but I encountered an error: {str(e)}"
88
+ logger.error(f"Error generating response: {str(e)}")
89
+ logger.debug(traceback.format_exc())
90
+
91
+ # Add error message to chat history
92
+ history.append((message, error_msg))
93
+
94
+ return history, chat_state, ""
95
+
96
+ except Exception as e:
97
+ logger.error(f"Error in respond function: {str(e)}")
98
+ logger.debug(traceback.format_exc())
99
+
100
+ error_msg = "I apologize, but something went wrong with our conversation system."
101
+ history.append((message, error_msg))
102
+ return history, chat_state, ""
103
+
104
+ def clear_chat():
105
+ """Clear the conversation history"""
106
+ return [], []
107
 
108
+ # Connect UI components to functions
109
+ submit_button.click(
110
+ respond,
111
+ inputs=[user_input, chatbot, chat_history],
112
+ outputs=[chatbot, chat_history, user_input],
113
+ api_name="chat"
114
+ )
115
 
116
+ # Enable pressing Enter to send messages
117
+ user_input.submit(
118
+ respond,
119
+ inputs=[user_input, chatbot, chat_history],
120
+ outputs=[chatbot, chat_history, user_input]
121
+ )
122
+
123
+ clear_button.click(
124
+ clear_chat,
125
+ outputs=[chatbot, chat_history]
126
+ )
127
 
128
  return app
129
 
130
  if __name__ == "__main__":
131
  # Ensure database is set up before starting the application
132
+ db_setup_success = setup_database()
133
 
134
  # Check if database connection is working
135
  engine = get_db_engine()
136
  if engine is None:
137
  logger.error("Failed to connect to database. Please check your connection settings.")
138
+ logger.warning("Application will continue with limited database functionality")
139
  else:
140
  logger.info("Successfully connected to database")
141
 
142
  # Launch the Gradio app
143
  app = create_ui()
144
+
145
+ # Configure for different deployment environments
146
+ server_name = os.getenv("SERVER_NAME", "127.0.0.1")
147
+ server_port = int(os.getenv("PORT", 7860))
148
+ share = os.getenv("SHARE_APP", "true").lower() == "true"
149
+
150
+ app.launch(
151
+ server_name=server_name,
152
+ server_port=server_port,
153
+ share=share
154
+ )
155
 
156
 
157
 
src/__init__.py CHANGED
@@ -19,5 +19,5 @@ __version__ = '0.1.0'
19
  __author__ = 'Business Interior Team'
20
 
21
  # Import key modules for simplified access
22
- from src.core.function_definitions import calculate_office_space_function, get_product_recommendations_function
23
- from src.services.chat_service import calculate_office_space, get_product_recommendations
 
19
  __author__ = 'Business Interior Team'
20
 
21
  # Import key modules for simplified access
22
+ from src.core.function_definitions import calculate_office_space, get_all_categories
23
+ from src.services.chat_service import calculate_office_space, get_all_categories
src/__pycache__/__init__.cpython-313.pyc CHANGED
Binary files a/src/__pycache__/__init__.cpython-313.pyc and b/src/__pycache__/__init__.cpython-313.pyc differ
 
src/__pycache__/chat.cpython-313.pyc CHANGED
Binary files a/src/__pycache__/chat.cpython-313.pyc and b/src/__pycache__/chat.cpython-313.pyc differ
 
src/chat.py CHANGED
@@ -1,321 +1,337 @@
1
- # from google import genai
2
- # from google.genai import types
3
- # import os
4
- # from dotenv import load_dotenv
5
- # import json
6
- # from src.services.chat_service import (
7
- # calculate_office_space as d_calculate_office_space,
8
- # get_product_recommendations as d_get_product_recommendations,
9
- # get_all_categories as d_get_all_categories,
10
- # )
 
 
 
 
 
 
 
 
 
11
 
12
- # load_dotenv()
13
 
14
- # calculate_office_space = {
15
- # "name": "calculate_office_space",
16
- # "description": "Calculate the total space required for a list of furniture items.",
17
- # "parameters": {
18
- # "type": "object",
19
- # "properties": {
20
- # "furniture_items": {
21
- # "type": "array",
22
- # "items": {
23
- # "type": "string",
24
- # "description": "List of furniture items to calculate space for (e.g., desk, chair).",
25
- # },
26
- # },
27
- # },
28
- # "required": ["furniture_items"],
29
- # },
30
- # }
31
-
32
- # get_product_recommendations = {
33
- # "name": "get_product_recommendations",
34
- # "description": "Get product recommendations based on the user's preferences.",
35
- # "parameters": {
36
- # "type": "object",
37
- # "properties": {
38
- # "product_type": {
39
- # "type": "string",
40
- # "description": "The type of product to recommend (e.g., chair, desk).",
41
- # },
42
- # "style": {
43
- # "type": "string",
44
- # "description": "The style of the product (e.g., modern, classic).",
45
- # },
46
- # "budget": {
47
- # "type": "number",
48
- # "description": "The budget for the product recommendations.",
49
- # },
50
- # },
51
- # "required": ["product_type", "style"],
52
- # },
53
- # }
54
 
55
- # get_all_categories = {
56
- # "name": "get_all_categories",
57
- # "description": "Get all available categories of products.",
58
- # "parameters": {
59
- # "type": "object",
60
- # "properties": {},
61
- # "required": [],
62
- # },
63
- # }
64
 
65
 
66
- # chat_tools = [
67
- # types.Tool(function_declarations=[calculate_office_space, get_product_recommendations, get_all_categories])
68
- # ]
 
69
 
70
- # config = {
71
- # "tools": chat_tools,
72
- # "automatic_function_calling": {"disable": True},
73
- # "tool_config": {"function_calling_config": {"mode": "any"}},
74
- # }
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- # # Config the client - Fix typo in environment variable name
78
- # client = genai.Client(
79
- # api_key=os.getenv("GEMINI_API_KEY"),
80
- # )
 
 
81
 
82
- # chat = client.chats.create(
83
- # model="gemini-2.0-flash",
84
- # config=config,
85
- # )
 
86
 
87
- # def handle_function_call(function_name, arguments):
88
- # # Handle the function call here
89
- # if function_name == "calculate_office_space":
90
- # return d_calculate_office_space(arguments.get("furniture_items", []))
91
- # elif function_name == "get_product_recommendations":
92
- # return d_get_product_recommendations(
93
- # arguments.get("product_type", ""),
94
- # arguments.get("style", ""),
95
- # arguments.get("budget", None),
96
- # )
97
- # elif function_name == "get_all_categories":
98
- # return d_get_all_categories()
99
- # else:
100
- # raise ValueError(f"Unknown function: {function_name}")
101
 
102
- # def detect_and_extract_function_call(response):
103
- # # Check if response has function call
104
- # if hasattr(response.candidates[0].content.parts[0], 'function_call'):
105
- # tool_call = response.candidates[0].content.parts[0].function_call
106
- # function_name = tool_call.name
107
- # arguments = tool_call.args
108
 
109
- # result = handle_function_call(function_name, arguments)
110
- # return tool_call, result
111
- # else:
112
- # # No function call detected, return the text response
113
- # return None, None
114
 
115
- # def generate_natural_language_response(tool_call, function_response_part):
116
- # # Convert the function response part to a natural language response
117
- # print("Function response part:", function_response_part)
118
- # print("Tool call:", tool_call)
119
- # contents = []
120
- # contents.append(types.Content(role="model", parts=[types.Part(function_call=tool_call)])) # Append the model's function call message
121
- # contents.append(types.Content(role="user", parts=[function_response_part])) # Append the function response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
- # final_response = client.models.generate_content(
124
- # model="gemini-2.0-flash",
125
- # contents=contents,
126
- # )
 
127
 
128
- # return final_response.text
 
 
 
 
129
 
130
- # def generate_response(input_text):
131
- # try:
132
- # # Call the chat model with the input text
133
- # model_response = chat.send_message(input_text)
134
 
135
- # tool_call, function_result = detect_and_extract_function_call(model_response)
136
 
137
- # # If no function call was detected, return the text response
138
- # if tool_call is None:
139
- # return model_response.text
140
 
141
- # function_response_part = types.Part.from_function_response(
142
- # name=tool_call.name,
143
- # response={"result": function_result},
144
- # )
145
 
146
- # natural_response = generate_natural_language_response(tool_call, function_response_part)
147
- # return natural_response
148
- # except Exception as e:
149
- # print(f"Error generating response: {e}")
150
- # return f"I'm sorry, there was an error processing your request: {str(e)}"
151
 
152
 
153
- import torch
154
- import json
155
- import re
156
- from transformers import pipeline
157
- from typing import List, Dict, Any, Optional
158
 
159
- # Function definitions that will be available to the model
160
- FUNCTION_DEFINITIONS = [
161
- {
162
- "name": "get_product_information",
163
- "description": "Get information about a specific interior product",
164
- "parameters": {
165
- "type": "object",
166
- "properties": {
167
- "product_id": {
168
- "type": "string",
169
- "description": "ID of the product to look up"
170
- },
171
- "details_required": {
172
- "type": "array",
173
- "items": {
174
- "type": "string",
175
- "enum": ["price", "dimensions", "materials", "availability", "color_options"]
176
- },
177
- "description": "What product details are needed"
178
- }
179
- },
180
- "required": ["product_id"]
181
- }
182
- },
183
- {
184
- "name": "search_products",
185
- "description": "Search for interior products based on criteria",
186
- "parameters": {
187
- "type": "object",
188
- "properties": {
189
- "category": {
190
- "type": "string",
191
- "description": "Product category (e.g., sofa, table, chair, lamp)"
192
- },
193
- "price_range": {
194
- "type": "object",
195
- "properties": {
196
- "min": {"type": "number"},
197
- "max": {"type": "number"}
198
- },
199
- "description": "Price range for the products"
200
- },
201
- "style": {
202
- "type": "string",
203
- "description": "Design style (e.g., modern, traditional, industrial)"
204
- }
205
- },
206
- "required": ["category"]
207
- }
208
- }
209
- ]
210
 
211
- def create_function_calling_prompt(user_query: str, functions: List[Dict[str, Any]]) -> str:
212
- """Create a prompt for the model that includes function calling instructions"""
213
 
214
- functions_str = json.dumps(functions, indent=2)
215
 
216
- prompt = f"""You are a helpful assistant for a business interior design company.
217
- You have access to the following functions: {functions_str}
218
- If the user's request can be fulfilled using one of these functions, generate a response in the following JSON format:
219
- {{
220
- "function_call": {{
221
- "name": "function_name",
222
- "parameters": {{
223
- "param1": "value1",
224
- "param2": "value2"
225
- }}
226
- }}
227
- }}
228
 
229
- If no function is needed, respond conversationally.
230
 
231
- User: {user_query}
232
- Assistant:"""
233
 
234
- return prompt
235
 
236
- def extract_function_call(generated_text: str) -> Optional[Dict[str, Any]]:
237
- """Extract the function call from the generated text"""
238
- # Look for JSON-like patterns in the output
239
- pattern = r'(\{[\s\S]*\})'
240
- match = re.search(pattern, generated_text)
241
 
242
- if match:
243
- try:
244
- # Try to parse as JSON
245
- json_str = match.group(1)
246
- result = json.loads(json_str)
247
- if "function_call" in result:
248
- return result["function_call"]
249
- except json.JSONDecodeError:
250
- pass
251
 
252
- return None
253
 
254
- def execute_function(function_name: str, parameters: Dict[str, Any]) -> str:
255
- """Execute the specified function with the given parameters"""
256
- # In a real implementation, these functions would connect to your database or APIs
257
- if function_name == "get_product_information":
258
- # Simulate product information retrieval
259
- product_id = parameters.get("product_id")
260
- return f"Retrieved information for product {product_id}: Price: $1,299, Material: Leather, Availability: In stock"
261
 
262
- elif function_name == "search_products":
263
- # Simulate product search
264
- category = parameters.get("category")
265
- return f"Found 5 {category} products matching your criteria. Would you like to see the results?"
266
 
267
- return "Function execution failed. Please try again."
268
 
269
- # Load the model
270
- model_id = "meta-llama/Llama-3.2-1B"
271
 
272
- pipe = pipeline(
273
- "text-generation",
274
- model=model_id,
275
- torch_dtype=torch.bfloat16,
276
- device_map="auto",
277
- max_new_tokens=800, # Increase token limit for detailed responses
278
- do_sample=True,
279
- temperature=0.2, # Lower temperature for more deterministic outputs
280
- top_p=0.95 # Control diversity while maintaining structure
281
- )
282
 
283
- # Example usage
284
- def process_query(user_query: str) -> str:
285
- # Create the prompt with function specifications
286
- prompt = create_function_calling_prompt(user_query, FUNCTION_DEFINITIONS)
287
 
288
- print(f"Prompt: {prompt}")
289
 
290
- # Generate response from the model
291
- result = pipe(prompt)
292
- generated_text = result[0]['generated_text']
293
 
294
- # Try to extract a function call
295
- function_call = extract_function_call(generated_text)
296
 
297
- if function_call:
298
- # If a function call was detected, execute it
299
- function_name = function_call.get("name")
300
- parameters = function_call.get("parameters", {})
301
 
302
- print(f"Detected function call: {function_name}")
303
- print(f"Parameters: {parameters}")
304
 
305
- # Execute the function and return result
306
- return execute_function(function_name, parameters)
307
- else:
308
- # Return the normal conversational response
309
- # Extract just the assistant's response
310
- assistant_response = generated_text.split("Assistant:")[-1].strip()
311
- return assistant_response
312
 
313
- # Test with some example queries
314
- queries = [
315
- "I need information about product ID FUR-2345",
316
- ]
317
 
318
- for query in queries:
319
- print(f"\nUser: {query}")
320
- response = process_query(query)
321
- print(f"Response: {response}")
 
1
+ from google import genai
2
+ from google.genai import types
3
+ import os
4
+ from dotenv import load_dotenv
5
+ import json
6
+ from src.core.function_definitions import (
7
+ calculate_office_space,
8
+ get_top_sales_products,
9
+ get_all_categories,
10
+ get_product_information_by_name,
11
+ find_products_by_price_range,
12
+ )
13
+ from src.services.chat_service import (
14
+ calculate_office_space as d_calculate_office_space,
15
+ get_all_categories as d_get_all_categories,
16
+ get_top_sales_products as d_get_top_sales_products,
17
+ get_product_information_by_name as d_get_product_information_by_name,
18
+ get_product_by_price_range as d_get_product_by_price_range,
19
+ )
20
 
21
+ load_dotenv()
22
 
23
+ chat_tools = [
24
+ types.Tool(function_declarations=[calculate_office_space,
25
+ get_top_sales_products, get_all_categories, get_product_information_by_name,
26
+ find_products_by_price_range],),
27
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ config = {
30
+ "tools": chat_tools,
31
+ "automatic_function_calling": {"disable": True},
32
+ "tool_config": {"function_calling_config": {"mode": "any"}},
33
+ }
 
 
 
 
34
 
35
 
36
+ # Config the client - Fix typo in environment variable name
37
+ client = genai.Client(
38
+ api_key=os.getenv("GEMINI_API_KEY"),
39
+ )
40
 
41
+ chat = client.chats.create(
42
+ model="gemini-2.0-flash",
43
+ config=config,
44
+ )
 
45
 
46
+ def handle_function_call(function_name, arguments):
47
+ # Handle the function call here
48
+ if function_name == "calculate_office_space":
49
+ return d_calculate_office_space(arguments.get("furniture_items", []))
50
+ elif function_name == "get_top_sales_products":
51
+ return d_get_top_sales_products(
52
+ arguments.get("category", ""),
53
+ arguments.get("limit", 5)
54
+ )
55
+ elif function_name == "get_all_categories":
56
+ return d_get_all_categories()
57
+ elif function_name == "get_product_information_by_name":
58
+ return d_get_product_information_by_name(
59
+ arguments.get("product_name", "")
60
+ )
61
+ elif function_name == "find_products_by_price_range":
62
+ return d_get_product_by_price_range(
63
+ arguments.get("category", None),
64
+ arguments.get("min_price", 0),
65
+ arguments.get("max_price", 0)
66
+ )
67
+ else:
68
+ raise ValueError(f"Unknown function: {function_name}")
69
 
70
+ def detect_and_extract_function_call(response):
71
+ # Check if response has function call
72
+ if hasattr(response.candidates[0].content.parts[0], 'function_call'):
73
+ tool_call = response.candidates[0].content.parts[0].function_call
74
+ function_name = tool_call.name
75
+ arguments = tool_call.args
76
 
77
+ result = handle_function_call(function_name, arguments)
78
+ return tool_call, result
79
+ else:
80
+ # No function call detected, return the text response
81
+ return None, None
82
 
83
+ def generate_natural_language_response(tool_call, function_response_part):
84
+ contents = []
85
+ contents.append(types.Content(role="model", parts=[types.Part(function_call=tool_call)])) # Append the model's function call message
86
+ contents.append(types.Content(role="user", parts=[function_response_part])) # Append the function response
 
 
 
 
 
 
 
 
 
 
87
 
88
+ final_response = client.models.generate_content(
89
+ model="gemini-2.0-flash",
90
+ contents=contents,
91
+ )
 
 
92
 
93
+ print("Tool call:", tool_call)
 
 
 
 
94
 
95
+ return final_response.text
96
+ # Extract function name and arguments for context
97
+ # function_name = tool_call.name
98
+ # function_arguments = tool_call.args
99
+
100
+ # # Create a system prompt to guide the model
101
+ # system_prompt = types.Content(
102
+ # role="system",
103
+ # parts=[types.Part(text="""
104
+ # You are a helpful assistant for a business interior design company.
105
+ # When presenting data from function calls:
106
+ # - Format the information in a readable, conversational way
107
+ # - Explain what the data means in the context of interior design
108
+ # - Make helpful suggestions based on the results
109
+ # - Use professional but friendly language
110
+ # - If the data is a list, present it in a structured format
111
+ # - Don't mention that you called a function, just present the information naturally
112
+ # """)]
113
+ # )
114
+
115
+ # # Create model's function call message
116
+ # function_call_content = types.Content(
117
+ # role="model",
118
+ # parts=[types.Part(function_call=tool_call)]
119
+ # )
120
+
121
+ # # Handle different result types for better presentation
122
+ # user_content = types.Content(
123
+ # role="user",
124
+ # parts=[function_response_part]
125
+ # )
126
+
127
+ # # Build the full conversation context
128
+ # contents = [
129
+ # system_prompt,
130
+ # function_call_content,
131
+ # user_content
132
+ # ]
133
 
134
+ # # Generate the natural language response
135
+ # final_response = client.models.generate_content(
136
+ # model="gemini-2.0-flash",
137
+ # contents=contents,
138
+ # )
139
 
140
+ # # Log debugging information
141
+ # print(f"Function call: {function_name}({json.dumps(function_arguments, indent=2)})")
142
+ # print(f"Function result type: {type(function_result)}")
143
+
144
+ # return final_response.text
145
 
146
+ def generate_response(input_text):
147
+ try:
148
+ # Call the chat model with the input text
149
+ model_response = chat.send_message(input_text)
150
 
151
+ tool_call, function_result = detect_and_extract_function_call(model_response)
152
 
153
+ # If no function call was detected, return the text response
154
+ if tool_call is None:
155
+ return model_response.text
156
 
157
+ function_response_part = types.Part.from_function_response(
158
+ name=tool_call.name,
159
+ response={"result": function_result},
160
+ )
161
 
162
+ natural_response = generate_natural_language_response(tool_call, function_response_part)
163
+ return natural_response
164
+ except Exception as e:
165
+ print(f"Error generating response: {e}")
166
+ return f"I'm sorry, there was an error processing your request: {str(e)}"
167
 
168
 
169
+ # import torch
170
+ # import json
171
+ # import re
172
+ # from transformers import pipeline
173
+ # from typing import List, Dict, Any, Optional
174
 
175
+ # # Function definitions that will be available to the model
176
+ # FUNCTION_DEFINITIONS = [
177
+ # {
178
+ # "name": "get_product_information",
179
+ # "description": "Get information about a specific interior product",
180
+ # "parameters": {
181
+ # "type": "object",
182
+ # "properties": {
183
+ # "product_id": {
184
+ # "type": "string",
185
+ # "description": "ID of the product to look up"
186
+ # },
187
+ # "details_required": {
188
+ # "type": "array",
189
+ # "items": {
190
+ # "type": "string",
191
+ # "enum": ["price", "dimensions", "materials", "availability", "color_options"]
192
+ # },
193
+ # "description": "What product details are needed"
194
+ # }
195
+ # },
196
+ # "required": ["product_id"]
197
+ # }
198
+ # },
199
+ # {
200
+ # "name": "search_products",
201
+ # "description": "Search for interior products based on criteria",
202
+ # "parameters": {
203
+ # "type": "object",
204
+ # "properties": {
205
+ # "category": {
206
+ # "type": "string",
207
+ # "description": "Product category (e.g., sofa, table, chair, lamp)"
208
+ # },
209
+ # "price_range": {
210
+ # "type": "object",
211
+ # "properties": {
212
+ # "min": {"type": "number"},
213
+ # "max": {"type": "number"}
214
+ # },
215
+ # "description": "Price range for the products"
216
+ # },
217
+ # "style": {
218
+ # "type": "string",
219
+ # "description": "Design style (e.g., modern, traditional, industrial)"
220
+ # }
221
+ # },
222
+ # "required": ["category"]
223
+ # }
224
+ # }
225
+ # ]
226
 
227
+ # def create_function_calling_prompt(user_query: str, functions: List[Dict[str, Any]]) -> str:
228
+ # """Create a prompt for the model that includes function calling instructions"""
229
 
230
+ # functions_str = json.dumps(functions, indent=2)
231
 
232
+ # prompt = f"""You are a helpful assistant for a business interior design company.
233
+ # You have access to the following functions: {functions_str}
234
+ # If the user's request can be fulfilled using one of these functions, generate a response in the following JSON format:
235
+ # {{
236
+ # "function_call": {{
237
+ # "name": "function_name",
238
+ # "parameters": {{
239
+ # "param1": "value1",
240
+ # "param2": "value2"
241
+ # }}
242
+ # }}
243
+ # }}
244
 
245
+ # If no function is needed, respond conversationally.
246
 
247
+ # User: {user_query}
248
+ # Assistant:"""
249
 
250
+ # return prompt
251
 
252
+ # def extract_function_call(generated_text: str) -> Optional[Dict[str, Any]]:
253
+ # """Extract the function call from the generated text"""
254
+ # # Look for JSON-like patterns in the output
255
+ # pattern = r'(\{[\s\S]*\})'
256
+ # match = re.search(pattern, generated_text)
257
 
258
+ # if match:
259
+ # try:
260
+ # # Try to parse as JSON
261
+ # json_str = match.group(1)
262
+ # result = json.loads(json_str)
263
+ # if "function_call" in result:
264
+ # return result["function_call"]
265
+ # except json.JSONDecodeError:
266
+ # pass
267
 
268
+ # return None
269
 
270
+ # def execute_function(function_name: str, parameters: Dict[str, Any]) -> str:
271
+ # """Execute the specified function with the given parameters"""
272
+ # # In a real implementation, these functions would connect to your database or APIs
273
+ # if function_name == "get_product_information":
274
+ # # Simulate product information retrieval
275
+ # product_id = parameters.get("product_id")
276
+ # return f"Retrieved information for product {product_id}: Price: $1,299, Material: Leather, Availability: In stock"
277
 
278
+ # elif function_name == "search_products":
279
+ # # Simulate product search
280
+ # category = parameters.get("category")
281
+ # return f"Found 5 {category} products matching your criteria. Would you like to see the results?"
282
 
283
+ # return "Function execution failed. Please try again."
284
 
285
+ # # Load the model
286
+ # model_id = "meta-llama/Llama-3.2-1B"
287
 
288
+ # pipe = pipeline(
289
+ # "text-generation",
290
+ # model=model_id,
291
+ # torch_dtype=torch.bfloat16,
292
+ # device_map="auto",
293
+ # max_new_tokens=800, # Increase token limit for detailed responses
294
+ # do_sample=True,
295
+ # temperature=0.2, # Lower temperature for more deterministic outputs
296
+ # top_p=0.95 # Control diversity while maintaining structure
297
+ # )
298
 
299
+ # # Example usage
300
+ # def process_query(user_query: str) -> str:
301
+ # # Create the prompt with function specifications
302
+ # prompt = create_function_calling_prompt(user_query, FUNCTION_DEFINITIONS)
303
 
304
+ # print(f"Prompt: {prompt}")
305
 
306
+ # # Generate response from the model
307
+ # result = pipe(prompt)
308
+ # generated_text = result[0]['generated_text']
309
 
310
+ # # Try to extract a function call
311
+ # function_call = extract_function_call(generated_text)
312
 
313
+ # if function_call:
314
+ # # If a function call was detected, execute it
315
+ # function_name = function_call.get("name")
316
+ # parameters = function_call.get("parameters", {})
317
 
318
+ # print(f"Detected function call: {function_name}")
319
+ # print(f"Parameters: {parameters}")
320
 
321
+ # # Execute the function and return result
322
+ # return execute_function(function_name, parameters)
323
+ # else:
324
+ # # Return the normal conversational response
325
+ # # Extract just the assistant's response
326
+ # assistant_response = generated_text.split("Assistant:")[-1].strip()
327
+ # return assistant_response
328
 
329
+ # # Test with some example queries
330
+ # queries = [
331
+ # "I need information about product ID FUR-2345",
332
+ # ]
333
 
334
+ # for query in queries:
335
+ # print(f"\nUser: {query}")
336
+ # response = process_query(query)
337
+ # print(f"Response: {response}")
src/core/__pycache__/function_definitions.cpython-313.pyc CHANGED
Binary files a/src/core/__pycache__/function_definitions.cpython-313.pyc and b/src/core/__pycache__/function_definitions.cpython-313.pyc differ
 
src/core/function_definitions.py CHANGED
@@ -1,43 +1,245 @@
1
  import json
2
 
3
- calculate_office_space_function = {
4
- "name": "calculate_office_space_function",
5
- "description": "Calculate the office space needed for a given number of employees.",
6
  "parameters": {
7
  "type": "object",
8
  "properties": {
9
- "number_of_employees": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  "type": "integer",
11
- "description": "The number of employees in the office.",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  },
13
- "office_space_per_employee": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  "type": "number",
15
- "description": "The amount of office space allocated per employee in square feet.",
16
  },
17
  },
18
- "required": ["number_of_employees", "office_space_per_employee"],
19
  },
20
  }
21
 
22
- get_product_recommendations_function = {
23
- "name": "get_product_recommendations_function",
24
- "description": "Get product recommendations based on the user's preferences.",
 
25
  "parameters": {
26
  "type": "object",
27
  "properties": {
28
  "product_type": {
29
  "type": "string",
30
- "description": "The type of product to recommend (e.g., chair, desk).",
31
  },
32
- "style": {
33
- "type": "string",
34
- "description": "The style of the product (e.g., modern, classic).",
 
 
 
 
35
  },
36
- "budget": {
37
  "type": "number",
38
- "description": "The budget for the product recommendations.",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  },
40
  },
41
- "required": ["product_type", "style"],
42
  },
43
  }
 
 
 
 
 
1
  import json
2
 
3
+ calculate_office_space = {
4
+ "name": "calculate_office_space",
5
+ "description": "Calculate the office space needed for furniture items.",
6
  "parameters": {
7
  "type": "object",
8
  "properties": {
9
+ "furniture_items": {
10
+ "type": "array",
11
+ "items": {
12
+ "type": "object",
13
+ "properties": {
14
+ "item_name": {
15
+ "type": "string",
16
+ "description": "Name of the furniture item (e.g., desk, chair).",
17
+ },
18
+ "quantity": {
19
+ "type": "integer",
20
+ "description": "Number of items needed.",
21
+ },
22
+ },
23
+ "required": ["item_name", "quantity"],
24
+ },
25
+ "description": "List of furniture items and their quantities.",
26
+ },
27
+ },
28
+ "required": ["furniture_items"],
29
+ },
30
+ }
31
+
32
+ get_top_sales_products = {
33
+ "name": "get_top_sales_products",
34
+ "description": "Get top-selling products based on sales data.",
35
+ "parameters": {
36
+ "type": "object",
37
+ "properties": {
38
+ "category": {
39
+ "type": "string",
40
+ "description": "Category of products to filter by (e.g., 'furniture', 'decor').",
41
+ },
42
+ "limit": {
43
  "type": "integer",
44
+ "description": "Number of top-selling products to return.",
45
+ },
46
+ },
47
+ "required": ["category", "limit"],
48
+ },
49
+ }
50
+
51
+ get_all_categories = {
52
+ "name": "get_all_categories",
53
+ "description": "Get all available categories of products.",
54
+ "parameters": {
55
+ "type": "object",
56
+ "properties": {},
57
+ "required": [],
58
+ },
59
+ }
60
+
61
+ get_product_information_by_name = {
62
+ "name": "get_product_information_by_name",
63
+ "description": "Get product information by name.",
64
+ "parameters": {
65
+ "type": "object",
66
+ "properties": {
67
+ "product_name": {
68
+ "type": "string",
69
+ "description": "Name of the product to retrieve information for.",
70
+ },
71
+ },
72
+ "required": ["product_name"],
73
+ },
74
+ }
75
+
76
+ get_product_recommendations = {
77
+ "name": "get_product_recommendations",
78
+ "description": "Get personalized product recommendations based on needs.",
79
+ "parameters": {
80
+ "type": "object",
81
+ "properties": {
82
+ "office_size": {
83
+ "type": "integer",
84
+ "description": "Size of the office in square feet.",
85
+ },
86
+ "employee_count": {
87
+ "type": "integer",
88
+ "description": "Number of employees to accommodate.",
89
+ },
90
+ "style_preference": {
91
+ "type": "string",
92
+ "description": "Preferred style (e.g., 'modern', 'traditional', 'minimalist').",
93
+ },
94
+ },
95
+ "required": ["office_size", "employee_count"],
96
+ },
97
+ }
98
+
99
+ request_custom_quote = {
100
+ "name": "request_custom_quote",
101
+ "description": "Request a custom quote for bulk orders or custom furniture.",
102
+ "parameters": {
103
+ "type": "object",
104
+ "properties": {
105
+ "product_details": {
106
+ "type": "array",
107
+ "items": {
108
+ "type": "object",
109
+ "properties": {
110
+ "product_name": {
111
+ "type": "string",
112
+ "description": "Name of the product.",
113
+ },
114
+ "quantity": {
115
+ "type": "integer",
116
+ "description": "Quantity needed.",
117
+ },
118
+ "customization": {
119
+ "type": "string",
120
+ "description": "Any customization requirements.",
121
+ },
122
+ },
123
+ "required": ["product_name", "quantity"],
124
+ },
125
+ "description": "List of products for the quote.",
126
  },
127
+ "contact_information": {
128
+ "type": "object",
129
+ "properties": {
130
+ "name": {
131
+ "type": "string",
132
+ "description": "Contact person's name.",
133
+ },
134
+ "email": {
135
+ "type": "string",
136
+ "description": "Contact email address.",
137
+ },
138
+ "phone": {
139
+ "type": "string",
140
+ "description": "Contact phone number.",
141
+ },
142
+ },
143
+ "required": ["name", "email"],
144
+ },
145
+ },
146
+ "required": ["product_details", "contact_information"],
147
+ },
148
+ }
149
+
150
+ check_product_availability = {
151
+ "name": "check_product_availability",
152
+ "description": "Check if products are in stock and available for purchase.",
153
+ "parameters": {
154
+ "type": "object",
155
+ "properties": {
156
+ "product_name": {
157
+ "type": "string",
158
+ "description": "Name of the product to check availability for.",
159
+ },
160
+ "quantity": {
161
+ "type": "integer",
162
+ "description": "Quantity needed.",
163
+ },
164
+ },
165
+ "required": ["product_name", "quantity"],
166
+ },
167
+ }
168
+
169
+ find_products_by_price_range = {
170
+ "name": "find_products_by_price_range",
171
+ "description": "Find products within a specific price range.",
172
+ "parameters": {
173
+ "type": "object",
174
+ "properties": {
175
+ "category": {
176
+ "type": "string",
177
+ "description": "Category of products to search within.",
178
+ },
179
+ "min_price": {
180
+ "type": "number",
181
+ "description": "Minimum price (in dollars).",
182
+ },
183
+ "max_price": {
184
  "type": "number",
185
+ "description": "Maximum price (in dollars).",
186
  },
187
  },
188
+ "required": ["min_price", "max_price"],
189
  },
190
  }
191
 
192
+
193
+ find_products_by_dimension = {
194
+ "name": "find_products_by_dimension",
195
+ "description": "Find furniture that fits specific spatial requirements.",
196
  "parameters": {
197
  "type": "object",
198
  "properties": {
199
  "product_type": {
200
  "type": "string",
201
+ "description": "Type of product (e.g., 'desk', 'bookshelf').",
202
  },
203
+ "max_width": {
204
+ "type": "number",
205
+ "description": "Maximum width in inches.",
206
+ },
207
+ "max_depth": {
208
+ "type": "number",
209
+ "description": "Maximum depth in inches.",
210
  },
211
+ "max_height": {
212
  "type": "number",
213
+ "description": "Maximum height in inches.",
214
+ },
215
+ },
216
+ "required": ["product_type"],
217
+ },
218
+ }
219
+
220
+ get_product_reviews = {
221
+ "name": "get_product_reviews",
222
+ "description": "Get customer reviews for a specific product.",
223
+ "parameters": {
224
+ "type": "object",
225
+ "properties": {
226
+ "product_id": {
227
+ "type": "string",
228
+ "description": "ID of the product to get reviews for.",
229
+ },
230
+ "limit": {
231
+ "type": "integer",
232
+ "description": "Maximum number of reviews to return.",
233
+ },
234
+ "min_rating": {
235
+ "type": "integer",
236
+ "description": "Minimum rating (1-5) to filter by.",
237
  },
238
  },
239
+ "required": ["product_id"],
240
  },
241
  }
242
+
243
+
244
+
245
+ __all__ = ["calculate_office_space", "get_product_recommendations", "get_all_categories", "get_top_sales_products", "get_product_information_by_name"]
src/database/__pycache__/db_connection.cpython-313.pyc CHANGED
Binary files a/src/database/__pycache__/db_connection.cpython-313.pyc and b/src/database/__pycache__/db_connection.cpython-313.pyc differ
 
src/database/__pycache__/models.cpython-313.pyc CHANGED
Binary files a/src/database/__pycache__/models.cpython-313.pyc and b/src/database/__pycache__/models.cpython-313.pyc differ
 
src/database/__pycache__/querries.cpython-313.pyc CHANGED
Binary files a/src/database/__pycache__/querries.cpython-313.pyc and b/src/database/__pycache__/querries.cpython-313.pyc differ
 
src/database/db_connection.py CHANGED
@@ -55,7 +55,7 @@ def get_db_engine():
55
 
56
  # The direct pyodbc connection approach for macOS
57
  # Use DSN-less connection which is more reliable on macOS with Azure SQL
58
- conn_str = f'DRIVER={{ODBC Driver 18 for SQL Server}};SERVER={server};DATABASE=DearHome;UID={username};PWD={quoted_password}'
59
 
60
  # Create connection string for SQLAlchemy
61
  connection_url = f"mssql+pyodbc:///?odbc_connect={urllib.parse.quote_plus(conn_str)}"
 
55
 
56
  # The direct pyodbc connection approach for macOS
57
  # Use DSN-less connection which is more reliable on macOS with Azure SQL
58
+ conn_str = f'DRIVER={{ODBC Driver 18 for SQL Server}};SERVER={server};DATABASE=INTERIOR_SHOPPING;UID={username};PWD={quoted_password};Encrypt=yes;TrustServerCertificate=yes;Connection Timeout=30;'
59
 
60
  # Create connection string for SQLAlchemy
61
  connection_url = f"mssql+pyodbc:///?odbc_connect={urllib.parse.quote_plus(conn_str)}"
src/database/generate_models.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ from dotenv import load_dotenv
4
+ import urllib.parse
5
+
6
+ # Load environment variables
7
+ load_dotenv()
8
+
9
+ # Get connection parameters from environment variables
10
+ server = os.getenv("SQL_SERVER")
11
+ database = os.getenv("SQL_DATABASE")
12
+ username = os.getenv("SQL_USERNAME")
13
+ password = os.getenv("SQL_PASSWORD")
14
+
15
+ if not all([server, database, username, password]):
16
+ print("Error: SQL connection parameters not found in environment variables")
17
+ sys.exit(1)
18
+
19
+ # Clean the server name (remove 'tcp:' prefix if present)
20
+ if server.startswith('tcp:'):
21
+ server = server[4:]
22
+
23
+ # URL encode the password to handle special characters
24
+ quoted_password = urllib.parse.quote_plus(password)
25
+
26
+ # Create the connection string based on the driver availability
27
+ try:
28
+ import pyodbc
29
+ print("Using pyodbc driver for connection")
30
+
31
+ # Create ODBC connection string
32
+ conn_str = f'DRIVER={{ODBC Driver 18 for SQL Server}};SERVER={server};DATABASE={database};UID={username};PWD={quoted_password}'
33
+ connection_url = f"mssql+pyodbc:///?odbc_connect={urllib.parse.quote_plus(conn_str)}"
34
+ except ImportError:
35
+ try:
36
+ import pymssql
37
+ print("Using pymssql driver for connection")
38
+
39
+ # Parse server and port if needed
40
+ if ',' in server:
41
+ server_parts = server.split(',')
42
+ host = server_parts[0]
43
+ port = int(server_parts[1]) if len(server_parts) > 1 else 1433
44
+ else:
45
+ host = server
46
+ port = 1433
47
+
48
+ connection_url = f"mssql+pymssql://{username}:{password}@{host}:{port}/{database}"
49
+ except ImportError:
50
+ print("Error: Neither pyodbc nor pymssql is installed")
51
+ sys.exit(1)
52
+
53
+ # Print the command to run (without password)
54
+ safe_connection_url = connection_url.replace(quoted_password, "********")
55
+ print(f"Running sqlacodegen with connection: {safe_connection_url}")
56
+
57
+ # Execute sqlacodegen
58
+ command = f"sqlacodegen --outfile=src/database/models.py '{connection_url}'"
59
+ print("Generating models...")
60
+ os.system(command)
61
+ print("Models generated successfully in src/database/models.py")
src/database/models.py CHANGED
@@ -1,96 +1,589 @@
1
- from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Boolean, Float
2
- from sqlalchemy.ext.declarative import declarative_base
3
- from sqlalchemy.orm import relationship
 
 
4
  import datetime
 
5
  import uuid
6
- from sqlalchemy.dialects.mssql import UNIQUEIDENTIFIER
7
 
8
- # Create the base class for SQLAlchemy ORM models
9
- Base = declarative_base()
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- class User(Base):
12
- """User model representing application users."""
 
 
 
13
  __tablename__ = 'AspNetUsers'
14
-
15
- id = Column(UNIQUEIDENTIFIER, primary_key=True, default=uuid.uuid4)
16
- username = Column(String(256), nullable=False, unique=True, name="UserName")
17
- email = Column(String(256), nullable=False, unique=True, name="Email")
18
- password_hash = Column(String(256), nullable=False, name="PasswordHash")
19
- first_name = Column(String(256), name="FirstName")
20
- last_name = Column(String(256), name="LastName")
21
- created_at = Column(DateTime, default=datetime.datetime.utcnow, name="CreatedAt")
22
- updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow, name="UpdatedAt")
23
- is_active = Column(Boolean, default=True, name="IsActive")
24
- normalized_user_name = Column(String(256), name="NormalizedUserName")
25
- normalized_email = Column(String(256), name="NormalizedEmail")
26
- email_confirmed = Column(Boolean, default=False, name="EmailConfirmed")
27
- security_stamp = Column(String(256), name="SecurityStamp")
28
- concurrency_stamp = Column(String(256), name="ConcurrencyStamp")
29
- phone_number = Column(String(256), name="PhoneNumber")
30
- phone_number_confirmed = Column(Boolean, default=False, name="PhoneNumberConfirmed")
31
- two_factor_enabled = Column(Boolean, default=False, name="TwoFactorEnabled")
32
- lockout_end = Column(DateTime, name="LockoutEnd")
33
- lockout_enabled = Column(Boolean, default=False, name="LockoutEnabled")
34
- access_failed_count = Column(Integer, default=0, name="AccessFailedCount")
35
-
36
- # Relationships
37
- chats = relationship("Chat", back_populates="user")
38
-
39
- def __repr__(self):
40
- return f"<User(id={self.id}, username={self.username})>"
41
-
42
-
43
- class Chat(Base):
44
- """Chat model representing a conversation session."""
45
- __tablename__ = 'chats'
46
-
47
- id = Column(UNIQUEIDENTIFIER, primary_key=True, default=uuid.uuid4)
48
- user_id = Column(UNIQUEIDENTIFIER, ForeignKey('AspNetUsers.id'), nullable=False)
49
- title = Column(String(255), nullable=False, default="New Chat")
50
- created_at = Column(DateTime, default=datetime.datetime.utcnow)
51
- updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
52
- is_active = Column(Boolean, default=True)
53
-
54
- # Relationships
55
- user = relationship("User", back_populates="chats")
56
- messages = relationship("Message", back_populates="chat", cascade="all, delete-orphan")
57
-
58
- def __repr__(self):
59
- return f"<Chat(id={self.id}, title={self.title})>"
60
-
61
-
62
- class Message(Base):
63
- """Message model representing individual messages in a chat."""
64
- __tablename__ = 'messages'
65
-
66
- id = Column(UNIQUEIDENTIFIER, primary_key=True, default=uuid.uuid4)
67
- chat_id = Column(UNIQUEIDENTIFIER, ForeignKey('chats.id'), nullable=False)
68
- content = Column(Text, nullable=False)
69
- role = Column(String(50), nullable=False) # 'user', 'assistant', 'system', etc.
70
- created_at = Column(DateTime, default=datetime.datetime.utcnow)
71
- updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
72
-
73
- # Relationships
74
- chat = relationship("Chat", back_populates="messages")
75
-
76
- def __repr__(self):
77
- return f"<Message(id={self.id}, role={self.role})>"
78
-
79
-
80
- class FunctionCall(Base):
81
- """Function call model for tracking function executions."""
82
- __tablename__ = 'function_calls'
83
-
84
- id = Column(UNIQUEIDENTIFIER, primary_key=True, default=uuid.uuid4)
85
- message_id = Column(UNIQUEIDENTIFIER, ForeignKey('messages.id'), nullable=False)
86
- function_name = Column(String(255), nullable=False)
87
- parameters = Column(Text, nullable=True) # JSON string
88
- result = Column(Text, nullable=True) # JSON string
89
- execution_time = Column(Float, nullable=True) # in seconds
90
- created_at = Column(DateTime, default=datetime.datetime.utcnow)
91
-
92
- # Relationships
93
- message = relationship("Message")
94
-
95
- def __repr__(self):
96
- return f"<FunctionCall(id={self.id}, function_name={self.function_name})>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional
2
+
3
+ from sqlalchemy import BigInteger, Boolean, Column, DECIMAL, Date, ForeignKeyConstraint, Identity, Index, Integer, PrimaryKeyConstraint, Table, Unicode, Uuid, text
4
+ from sqlalchemy.dialects.mssql import DATETIME2, DATETIMEOFFSET
5
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
6
  import datetime
7
+ import decimal
8
  import uuid
 
9
 
10
+ class Base(DeclarativeBase):
11
+ pass
12
+
13
+
14
+ class AspNetRoles(Base):
15
+ __tablename__ = 'AspNetRoles'
16
+ __table_args__ = (
17
+ PrimaryKeyConstraint('Id', name='PK_AspNetRoles'),
18
+ Index('RoleNameIndex', 'NormalizedName', unique=True)
19
+ )
20
+
21
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
22
+ Name: Mapped[Optional[str]] = mapped_column(Unicode(256, 'SQL_Latin1_General_CP1_CI_AS'))
23
+ NormalizedName: Mapped[Optional[str]] = mapped_column(Unicode(256, 'SQL_Latin1_General_CP1_CI_AS'))
24
+ ConcurrencyStamp: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
25
 
26
+ AspNetUsers: Mapped[List['AspNetUsers']] = relationship('AspNetUsers', secondary='AspNetUserRoles', back_populates='AspNetRoles_')
27
+ AspNetRoleClaims: Mapped[List['AspNetRoleClaims']] = relationship('AspNetRoleClaims', back_populates='AspNetRoles_')
28
+
29
+
30
+ class AspNetUsers(Base):
31
  __tablename__ = 'AspNetUsers'
32
+ __table_args__ = (
33
+ PrimaryKeyConstraint('Id', name='PK_AspNetUsers'),
34
+ Index('EmailIndex', 'NormalizedEmail'),
35
+ Index('UserNameIndex', 'NormalizedUserName', unique=True)
36
+ )
37
+
38
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
39
+ Name: Mapped[str] = mapped_column(Unicode(100, 'SQL_Latin1_General_CP1_CI_AS'))
40
+ IsActive: Mapped[bool] = mapped_column(Boolean, server_default=text('(CONVERT([bit],(1)))'))
41
+ CustomerPoints: Mapped[int] = mapped_column(Integer, server_default=text('((0))'))
42
+ CustomerLevel: Mapped[int] = mapped_column(Integer, server_default=text('((0))'))
43
+ UserName: Mapped[str] = mapped_column(Unicode(50, 'SQL_Latin1_General_CP1_CI_AS'))
44
+ Email: Mapped[str] = mapped_column(Unicode(100, 'SQL_Latin1_General_CP1_CI_AS'))
45
+ EmailConfirmed: Mapped[bool] = mapped_column(Boolean)
46
+ PasswordHash: Mapped[str] = mapped_column(Unicode(100, 'SQL_Latin1_General_CP1_CI_AS'))
47
+ PhoneNumber: Mapped[str] = mapped_column(Unicode(15, 'SQL_Latin1_General_CP1_CI_AS'))
48
+ PhoneNumberConfirmed: Mapped[bool] = mapped_column(Boolean)
49
+ TwoFactorEnabled: Mapped[bool] = mapped_column(Boolean)
50
+ LockoutEnabled: Mapped[bool] = mapped_column(Boolean)
51
+ AccessFailedCount: Mapped[int] = mapped_column(Integer)
52
+ ImageUrl: Mapped[Optional[str]] = mapped_column(Unicode(200, 'SQL_Latin1_General_CP1_CI_AS'))
53
+ DateOfBirth: Mapped[Optional[datetime.date]] = mapped_column(Date)
54
+ NormalizedUserName: Mapped[Optional[str]] = mapped_column(Unicode(256, 'SQL_Latin1_General_CP1_CI_AS'))
55
+ NormalizedEmail: Mapped[Optional[str]] = mapped_column(Unicode(256, 'SQL_Latin1_General_CP1_CI_AS'))
56
+ SecurityStamp: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
57
+ ConcurrencyStamp: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
58
+ LockoutEnd: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIMEOFFSET)
59
+
60
+ AspNetRoles_: Mapped[List['AspNetRoles']] = relationship('AspNetRoles', secondary='AspNetUserRoles', back_populates='AspNetUsers')
61
+ Addresses: Mapped[List['Addresses']] = relationship('Addresses', back_populates='AspNetUsers_')
62
+ AspNetUserClaims: Mapped[List['AspNetUserClaims']] = relationship('AspNetUserClaims', back_populates='AspNetUsers_')
63
+ AspNetUserLogins: Mapped[List['AspNetUserLogins']] = relationship('AspNetUserLogins', back_populates='AspNetUsers_')
64
+ AspNetUserTokens: Mapped[List['AspNetUserTokens']] = relationship('AspNetUserTokens', back_populates='AspNetUsers_')
65
+ Payments: Mapped[List['Payments']] = relationship('Payments', back_populates='AspNetUsers_')
66
+ Orders: Mapped[List['Orders']] = relationship('Orders', back_populates='AspNetUsers_')
67
+ Reviews: Mapped[List['Reviews']] = relationship('Reviews', back_populates='AspNetUsers_')
68
+
69
+
70
+ class Attributes(Base):
71
+ __tablename__ = 'Attributes'
72
+ __table_args__ = (
73
+ PrimaryKeyConstraint('Id', name='PK_Attributes'),
74
+ )
75
+
76
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
77
+ Name: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
78
+ Type: Mapped[int] = mapped_column(Integer)
79
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
80
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
81
+
82
+ CategoryAttributes: Mapped[List['CategoryAttributes']] = relationship('CategoryAttributes', back_populates='Attributes_')
83
+ AttributeValues: Mapped[List['AttributeValues']] = relationship('AttributeValues', back_populates='Attributes_')
84
+
85
+
86
+ class Categories(Base):
87
+ __tablename__ = 'Categories'
88
+ __table_args__ = (
89
+ ForeignKeyConstraint(['ParentCategoryId'], ['Categories.Id'], name='FK_Categories_Categories_ParentCategoryId'),
90
+ PrimaryKeyConstraint('Id', name='PK_Categories'),
91
+ Index('IX_Categories_ParentCategoryId', 'ParentCategoryId')
92
+ )
93
+
94
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
95
+ Name: Mapped[str] = mapped_column(Unicode(100, 'SQL_Latin1_General_CP1_CI_AS'))
96
+ Slug: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
97
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
98
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
99
+ Description: Mapped[Optional[str]] = mapped_column(Unicode(500, 'SQL_Latin1_General_CP1_CI_AS'))
100
+ ImageUrl: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
101
+ ParentCategoryId: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid)
102
+
103
+ Categories: Mapped[Optional['Categories']] = relationship('Categories', remote_side=[Id], back_populates='Categories_reverse')
104
+ Categories_reverse: Mapped[List['Categories']] = relationship('Categories', remote_side=[ParentCategoryId], back_populates='Categories')
105
+ CategoryAttributes: Mapped[List['CategoryAttributes']] = relationship('CategoryAttributes', back_populates='Categories_')
106
+ Products: Mapped[List['Products']] = relationship('Products', back_populates='Categories_')
107
+
108
+
109
+ class Combos(Base):
110
+ __tablename__ = 'Combos'
111
+ __table_args__ = (
112
+ PrimaryKeyConstraint('Id', name='PK_Combos'),
113
+ )
114
+
115
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
116
+ Name: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
117
+ Description: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
118
+ DiscountPercentage: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
119
+ IsActive: Mapped[bool] = mapped_column(Boolean)
120
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
121
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
122
+
123
+ ProductCombos: Mapped[List['ProductCombos']] = relationship('ProductCombos', back_populates='Combos_')
124
+
125
+
126
+ class Placements(Base):
127
+ __tablename__ = 'Placements'
128
+ __table_args__ = (
129
+ PrimaryKeyConstraint('Id', name='PK_Placements'),
130
+ )
131
+
132
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
133
+ Name: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
134
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
135
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
136
+ Description: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
137
+
138
+ Products: Mapped[List['Products']] = relationship('Products', back_populates='Placements_')
139
+
140
+
141
+ class Promotions(Base):
142
+ __tablename__ = 'Promotions'
143
+ __table_args__ = (
144
+ PrimaryKeyConstraint('Id', name='PK_Promotions'),
145
+ )
146
+
147
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
148
+ Name: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
149
+ Code: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
150
+ DiscountPercentage: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
151
+ StartDate: Mapped[datetime.datetime] = mapped_column(DATETIME2)
152
+ EndDate: Mapped[datetime.datetime] = mapped_column(DATETIME2)
153
+ IsActive: Mapped[bool] = mapped_column(Boolean)
154
+ Ussage: Mapped[int] = mapped_column(Integer)
155
+ CustomerLevel: Mapped[int] = mapped_column(Integer)
156
+ Type: Mapped[int] = mapped_column(Integer)
157
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
158
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
159
+ Description: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
160
+ ProductIds: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
161
+
162
+ Products: Mapped[List['Products']] = relationship('Products', secondary='ProductPromotion', back_populates='Promotions_')
163
+ Orders: Mapped[List['Orders']] = relationship('Orders', back_populates='Promotions_')
164
+
165
+
166
+ class VerificationCodes(Base):
167
+ __tablename__ = 'VerificationCodes'
168
+ __table_args__ = (
169
+ PrimaryKeyConstraint('Id', name='PK_VerificationCodes'),
170
+ )
171
+
172
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
173
+ Code: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
174
+ Email: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
175
+ ExpirationDate: Mapped[datetime.datetime] = mapped_column(DATETIME2)
176
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
177
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
178
+
179
+
180
+ class EFMigrationsHistory(Base):
181
+ __tablename__ = '__EFMigrationsHistory'
182
+ __table_args__ = (
183
+ PrimaryKeyConstraint('MigrationId', name='PK___EFMigrationsHistory'),
184
+ )
185
+
186
+ MigrationId: Mapped[str] = mapped_column(Unicode(150, 'SQL_Latin1_General_CP1_CI_AS'), primary_key=True)
187
+ ProductVersion: Mapped[str] = mapped_column(Unicode(32, 'SQL_Latin1_General_CP1_CI_AS'))
188
+
189
+
190
+ class Addresses(Base):
191
+ __tablename__ = 'Addresses'
192
+ __table_args__ = (
193
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], ondelete='CASCADE', name='FK_Addresses_AspNetUsers_UserId'),
194
+ PrimaryKeyConstraint('Id', name='PK_Addresses'),
195
+ Index('IX_Addresses_UserId', 'UserId')
196
+ )
197
+
198
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
199
+ IsDefault: Mapped[bool] = mapped_column(Boolean)
200
+ UserId: Mapped[uuid.UUID] = mapped_column(Uuid)
201
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
202
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
203
+ Street: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
204
+ District: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
205
+ City: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
206
+ PostalCode: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
207
+ Country: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
208
+ Ward: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
209
+
210
+ AspNetUsers_: Mapped['AspNetUsers'] = relationship('AspNetUsers', back_populates='Addresses')
211
+ Orders: Mapped[List['Orders']] = relationship('Orders', back_populates='Addresses_')
212
+
213
+
214
+ class AspNetRoleClaims(Base):
215
+ __tablename__ = 'AspNetRoleClaims'
216
+ __table_args__ = (
217
+ ForeignKeyConstraint(['RoleId'], ['AspNetRoles.Id'], ondelete='CASCADE', name='FK_AspNetRoleClaims_AspNetRoles_RoleId'),
218
+ PrimaryKeyConstraint('Id', name='PK_AspNetRoleClaims'),
219
+ Index('IX_AspNetRoleClaims_RoleId', 'RoleId')
220
+ )
221
+
222
+ Id: Mapped[int] = mapped_column(Integer, Identity(start=1, increment=1), primary_key=True)
223
+ RoleId: Mapped[uuid.UUID] = mapped_column(Uuid)
224
+ ClaimType: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
225
+ ClaimValue: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
226
+
227
+ AspNetRoles_: Mapped['AspNetRoles'] = relationship('AspNetRoles', back_populates='AspNetRoleClaims')
228
+
229
+
230
+ class AspNetUserClaims(Base):
231
+ __tablename__ = 'AspNetUserClaims'
232
+ __table_args__ = (
233
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], ondelete='CASCADE', name='FK_AspNetUserClaims_AspNetUsers_UserId'),
234
+ PrimaryKeyConstraint('Id', name='PK_AspNetUserClaims'),
235
+ Index('IX_AspNetUserClaims_UserId', 'UserId')
236
+ )
237
+
238
+ Id: Mapped[int] = mapped_column(Integer, Identity(start=1, increment=1), primary_key=True)
239
+ UserId: Mapped[uuid.UUID] = mapped_column(Uuid)
240
+ ClaimType: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
241
+ ClaimValue: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
242
+
243
+ AspNetUsers_: Mapped['AspNetUsers'] = relationship('AspNetUsers', back_populates='AspNetUserClaims')
244
+
245
+
246
+ class AspNetUserLogins(Base):
247
+ __tablename__ = 'AspNetUserLogins'
248
+ __table_args__ = (
249
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], ondelete='CASCADE', name='FK_AspNetUserLogins_AspNetUsers_UserId'),
250
+ PrimaryKeyConstraint('LoginProvider', 'ProviderKey', name='PK_AspNetUserLogins'),
251
+ Index('IX_AspNetUserLogins_UserId', 'UserId')
252
+ )
253
+
254
+ LoginProvider: Mapped[str] = mapped_column(Unicode(450, 'SQL_Latin1_General_CP1_CI_AS'), primary_key=True)
255
+ ProviderKey: Mapped[str] = mapped_column(Unicode(450, 'SQL_Latin1_General_CP1_CI_AS'), primary_key=True)
256
+ UserId: Mapped[uuid.UUID] = mapped_column(Uuid)
257
+ ProviderDisplayName: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
258
+
259
+ AspNetUsers_: Mapped['AspNetUsers'] = relationship('AspNetUsers', back_populates='AspNetUserLogins')
260
+
261
+
262
+ t_AspNetUserRoles = Table(
263
+ 'AspNetUserRoles', Base.metadata,
264
+ Column('UserId', Uuid, primary_key=True, nullable=False),
265
+ Column('RoleId', Uuid, primary_key=True, nullable=False),
266
+ ForeignKeyConstraint(['RoleId'], ['AspNetRoles.Id'], ondelete='CASCADE', name='FK_AspNetUserRoles_AspNetRoles_RoleId'),
267
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], ondelete='CASCADE', name='FK_AspNetUserRoles_AspNetUsers_UserId'),
268
+ PrimaryKeyConstraint('UserId', 'RoleId', name='PK_AspNetUserRoles'),
269
+ Index('IX_AspNetUserRoles_RoleId', 'RoleId')
270
+ )
271
+
272
+
273
+ class AspNetUserTokens(Base):
274
+ __tablename__ = 'AspNetUserTokens'
275
+ __table_args__ = (
276
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], ondelete='CASCADE', name='FK_AspNetUserTokens_AspNetUsers_UserId'),
277
+ PrimaryKeyConstraint('UserId', 'LoginProvider', 'Name', name='PK_AspNetUserTokens')
278
+ )
279
+
280
+ UserId: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
281
+ LoginProvider: Mapped[str] = mapped_column(Unicode(450, 'SQL_Latin1_General_CP1_CI_AS'), primary_key=True)
282
+ Name: Mapped[str] = mapped_column(Unicode(450, 'SQL_Latin1_General_CP1_CI_AS'), primary_key=True)
283
+ Value: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
284
+
285
+ AspNetUsers_: Mapped['AspNetUsers'] = relationship('AspNetUsers', back_populates='AspNetUserTokens')
286
+
287
+
288
+ class CategoryAttributes(Base):
289
+ __tablename__ = 'CategoryAttributes'
290
+ __table_args__ = (
291
+ ForeignKeyConstraint(['AttributeId'], ['Attributes.Id'], ondelete='CASCADE', name='FK_CategoryAttributes_Attributes_AttributeId'),
292
+ ForeignKeyConstraint(['CategoryId'], ['Categories.Id'], ondelete='CASCADE', name='FK_CategoryAttributes_Categories_CategoryId'),
293
+ PrimaryKeyConstraint('Id', name='PK_CategoryAttributes'),
294
+ Index('IX_CategoryAttributes_AttributeId', 'AttributeId'),
295
+ Index('IX_CategoryAttributes_CategoryId', 'CategoryId')
296
+ )
297
+
298
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
299
+ CategoryId: Mapped[uuid.UUID] = mapped_column(Uuid)
300
+ AttributeId: Mapped[uuid.UUID] = mapped_column(Uuid)
301
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
302
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
303
+
304
+ Attributes_: Mapped['Attributes'] = relationship('Attributes', back_populates='CategoryAttributes')
305
+ Categories_: Mapped['Categories'] = relationship('Categories', back_populates='CategoryAttributes')
306
+
307
+
308
+ class Payments(Base):
309
+ __tablename__ = 'Payments'
310
+ __table_args__ = (
311
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], ondelete='CASCADE', name='FK_Payments_AspNetUsers_UserId'),
312
+ PrimaryKeyConstraint('Id', name='PK_Payments'),
313
+ Index('IX_Payments_UserId', 'UserId')
314
+ )
315
+
316
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
317
+ CardHolderName: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
318
+ CardNumber: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
319
+ CardType: Mapped[str] = mapped_column(Unicode(20, 'SQL_Latin1_General_CP1_CI_AS'))
320
+ ExpirationDate: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
321
+ Method: Mapped[str] = mapped_column(Unicode(20, 'SQL_Latin1_General_CP1_CI_AS'))
322
+ IsDefault: Mapped[bool] = mapped_column(Boolean, server_default=text('(CONVERT([bit],(0)))'))
323
+ UserId: Mapped[uuid.UUID] = mapped_column(Uuid)
324
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
325
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
326
+
327
+ AspNetUsers_: Mapped['AspNetUsers'] = relationship('AspNetUsers', back_populates='Payments')
328
+ Orders: Mapped[List['Orders']] = relationship('Orders', back_populates='Payments_')
329
+
330
+
331
+ class Products(Base):
332
+ __tablename__ = 'Products'
333
+ __table_args__ = (
334
+ ForeignKeyConstraint(['CategoryId'], ['Categories.Id'], ondelete='CASCADE', name='FK_Products_Categories_CategoryId'),
335
+ ForeignKeyConstraint(['PlacementId'], ['Placements.Id'], ondelete='CASCADE', name='FK_Products_Placements_PlacementId'),
336
+ PrimaryKeyConstraint('Id', name='PK_Products'),
337
+ Index('IX_Products_CategoryId', 'CategoryId'),
338
+ Index('IX_Products_PlacementId', 'PlacementId')
339
+ )
340
+
341
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
342
+ Name: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
343
+ Price: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
344
+ Description: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
345
+ IsActive: Mapped[bool] = mapped_column(Boolean)
346
+ CategoryId: Mapped[uuid.UUID] = mapped_column(Uuid)
347
+ PlacementId: Mapped[uuid.UUID] = mapped_column(Uuid)
348
+ Status: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
349
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
350
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
351
+ ImageUrl: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
352
+
353
+ Categories_: Mapped['Categories'] = relationship('Categories', back_populates='Products')
354
+ Placements_: Mapped['Placements'] = relationship('Placements', back_populates='Products')
355
+ Promotions_: Mapped[List['Promotions']] = relationship('Promotions', secondary='ProductPromotion', back_populates='Products')
356
+ AttributeValues: Mapped[List['AttributeValues']] = relationship('AttributeValues', back_populates='Products_')
357
+ ProductCombos: Mapped[List['ProductCombos']] = relationship('ProductCombos', back_populates='Products_')
358
+ Variants: Mapped[List['Variants']] = relationship('Variants', back_populates='Products_')
359
+
360
+
361
+ class AttributeValues(Base):
362
+ __tablename__ = 'AttributeValues'
363
+ __table_args__ = (
364
+ ForeignKeyConstraint(['AttributeId'], ['Attributes.Id'], ondelete='CASCADE', name='FK_AttributeValues_Attributes_AttributeId'),
365
+ ForeignKeyConstraint(['ProductId'], ['Products.Id'], name='FK_AttributeValues_Products_ProductId'),
366
+ PrimaryKeyConstraint('Id', name='PK_AttributeValues'),
367
+ Index('IX_AttributeValues_AttributeId', 'AttributeId'),
368
+ Index('IX_AttributeValues_ProductId', 'ProductId')
369
+ )
370
+
371
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
372
+ Value: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
373
+ AttributeId: Mapped[uuid.UUID] = mapped_column(Uuid)
374
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
375
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
376
+ ProductId: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid)
377
+
378
+ Attributes_: Mapped['Attributes'] = relationship('Attributes', back_populates='AttributeValues')
379
+ Products_: Mapped[Optional['Products']] = relationship('Products', back_populates='AttributeValues')
380
+ VariantAttributes: Mapped[List['VariantAttributes']] = relationship('VariantAttributes', back_populates='AttributeValues_')
381
+
382
+
383
+ class Orders(Base):
384
+ __tablename__ = 'Orders'
385
+ __table_args__ = (
386
+ ForeignKeyConstraint(['AddressId'], ['Addresses.Id'], name='FK_Orders_Addresses_AddressId'),
387
+ ForeignKeyConstraint(['PaymentId'], ['Payments.Id'], name='FK_Orders_Payments_PaymentId'),
388
+ ForeignKeyConstraint(['PromotionId'], ['Promotions.Id'], name='FK_Orders_Promotions_PromotionId'),
389
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], name='FK_Orders_AspNetUsers_UserId'),
390
+ PrimaryKeyConstraint('Id', name='PK_Orders'),
391
+ Index('IX_Orders_AddressId', 'AddressId'),
392
+ Index('IX_Orders_PaymentId', 'PaymentId'),
393
+ Index('IX_Orders_PromotionId', 'PromotionId'),
394
+ Index('IX_Orders_UserId', 'UserId')
395
+ )
396
+
397
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
398
+ UserId: Mapped[uuid.UUID] = mapped_column(Uuid)
399
+ AddressId: Mapped[uuid.UUID] = mapped_column(Uuid)
400
+ Status: Mapped[int] = mapped_column(Integer)
401
+ TotalPrice: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
402
+ Discount: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
403
+ FinalPrice: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
404
+ OrderDate: Mapped[datetime.datetime] = mapped_column(DATETIME2)
405
+ PaymentMethod: Mapped[int] = mapped_column(Integer)
406
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
407
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
408
+ PromotionId: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid)
409
+ Note: Mapped[Optional[str]] = mapped_column(Unicode(500, 'SQL_Latin1_General_CP1_CI_AS'))
410
+ PaymentId: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid)
411
+ ShippingCode: Mapped[Optional[str]] = mapped_column(Unicode(50, 'SQL_Latin1_General_CP1_CI_AS'))
412
+ ShippingRate: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
413
+ PaymentLinkId: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
414
+ PaymentLinkUrl: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
415
+ PaymentOrderCode: Mapped[Optional[int]] = mapped_column(BigInteger)
416
+
417
+ Addresses_: Mapped['Addresses'] = relationship('Addresses', back_populates='Orders')
418
+ Payments_: Mapped[Optional['Payments']] = relationship('Payments', back_populates='Orders')
419
+ Promotions_: Mapped[Optional['Promotions']] = relationship('Promotions', back_populates='Orders')
420
+ AspNetUsers_: Mapped['AspNetUsers'] = relationship('AspNetUsers', back_populates='Orders')
421
+ OrderDetails: Mapped[List['OrderDetails']] = relationship('OrderDetails', back_populates='Orders_')
422
+
423
+
424
+ class ProductCombos(Base):
425
+ __tablename__ = 'ProductCombos'
426
+ __table_args__ = (
427
+ ForeignKeyConstraint(['ComboId'], ['Combos.Id'], ondelete='CASCADE', name='FK_ProductCombos_Combos_ComboId'),
428
+ ForeignKeyConstraint(['ProductId'], ['Products.Id'], ondelete='CASCADE', name='FK_ProductCombos_Products_ProductId'),
429
+ PrimaryKeyConstraint('Id', name='PK_ProductCombos'),
430
+ Index('IX_ProductCombos_ComboId', 'ComboId'),
431
+ Index('IX_ProductCombos_ProductId', 'ProductId')
432
+ )
433
+
434
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
435
+ ProductId: Mapped[uuid.UUID] = mapped_column(Uuid)
436
+ ComboId: Mapped[uuid.UUID] = mapped_column(Uuid)
437
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
438
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
439
+
440
+ Combos_: Mapped['Combos'] = relationship('Combos', back_populates='ProductCombos')
441
+ Products_: Mapped['Products'] = relationship('Products', back_populates='ProductCombos')
442
+
443
+
444
+ t_ProductPromotion = Table(
445
+ 'ProductPromotion', Base.metadata,
446
+ Column('ProductsId', Uuid, primary_key=True, nullable=False),
447
+ Column('PromotionsId', Uuid, primary_key=True, nullable=False),
448
+ ForeignKeyConstraint(['ProductsId'], ['Products.Id'], ondelete='CASCADE', name='FK_ProductPromotion_Products_ProductsId'),
449
+ ForeignKeyConstraint(['PromotionsId'], ['Promotions.Id'], ondelete='CASCADE', name='FK_ProductPromotion_Promotions_PromotionsId'),
450
+ PrimaryKeyConstraint('ProductsId', 'PromotionsId', name='PK_ProductPromotion'),
451
+ Index('IX_ProductPromotion_PromotionsId', 'PromotionsId')
452
+ )
453
+
454
+
455
+ class Variants(Base):
456
+ __tablename__ = 'Variants'
457
+ __table_args__ = (
458
+ ForeignKeyConstraint(['ProductId'], ['Products.Id'], name='FK_Variants_Products_ProductId'),
459
+ PrimaryKeyConstraint('Id', name='PK_Variants'),
460
+ Index('IX_Variants_ProductId', 'ProductId')
461
+ )
462
+
463
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
464
+ ImageUrls: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
465
+ PriceAdjustment: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
466
+ Stock: Mapped[int] = mapped_column(Integer)
467
+ IsActive: Mapped[bool] = mapped_column(Boolean)
468
+ Sku: Mapped[str] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
469
+ ProductId: Mapped[uuid.UUID] = mapped_column(Uuid)
470
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
471
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
472
+
473
+ Products_: Mapped['Products'] = relationship('Products', back_populates='Variants')
474
+ GoodReceivedNotes: Mapped[List['GoodReceivedNotes']] = relationship('GoodReceivedNotes', back_populates='Variants_')
475
+ OrderDetails: Mapped[List['OrderDetails']] = relationship('OrderDetails', back_populates='Variants_')
476
+ VariantAttributes: Mapped[List['VariantAttributes']] = relationship('VariantAttributes', back_populates='Variants_')
477
+ GoodReceivedItems: Mapped[List['GoodReceivedItems']] = relationship('GoodReceivedItems', back_populates='Variants_')
478
+
479
+
480
+ class GoodReceivedNotes(Base):
481
+ __tablename__ = 'GoodReceivedNotes'
482
+ __table_args__ = (
483
+ ForeignKeyConstraint(['VariantId'], ['Variants.Id'], name='FK_GoodReceivedNotes_Variants_VariantId'),
484
+ PrimaryKeyConstraint('Id', name='PK_GoodReceivedNotes'),
485
+ Index('IX_GoodReceivedNotes_VariantId', 'VariantId')
486
+ )
487
+
488
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
489
+ ReceivedDate: Mapped[datetime.datetime] = mapped_column(DATETIME2)
490
+ Quantity: Mapped[int] = mapped_column(Integer)
491
+ TotalCost: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
492
+ ShippingCost: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
493
+ Status: Mapped[int] = mapped_column(Integer)
494
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
495
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
496
+ Note: Mapped[Optional[str]] = mapped_column(Unicode(collation='SQL_Latin1_General_CP1_CI_AS'))
497
+ VariantId: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid)
498
+
499
+ Variants_: Mapped[Optional['Variants']] = relationship('Variants', back_populates='GoodReceivedNotes')
500
+ GoodReceivedItems: Mapped[List['GoodReceivedItems']] = relationship('GoodReceivedItems', back_populates='GoodReceivedNotes_')
501
+
502
+
503
+ class OrderDetails(Base):
504
+ __tablename__ = 'OrderDetails'
505
+ __table_args__ = (
506
+ ForeignKeyConstraint(['OrderId'], ['Orders.Id'], name='FK_OrderDetails_Orders_OrderId'),
507
+ ForeignKeyConstraint(['VariantId'], ['Variants.Id'], name='FK_OrderDetails_Variants_VariantId'),
508
+ PrimaryKeyConstraint('Id', name='PK_OrderDetails'),
509
+ Index('IX_OrderDetails_OrderId', 'OrderId'),
510
+ Index('IX_OrderDetails_VariantId', 'VariantId')
511
+ )
512
+
513
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
514
+ OrderId: Mapped[uuid.UUID] = mapped_column(Uuid)
515
+ VariantId: Mapped[uuid.UUID] = mapped_column(Uuid)
516
+ Quantity: Mapped[int] = mapped_column(Integer, server_default=text('((1))'))
517
+ UnitPrice: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2), server_default=text('((0.0))'))
518
+ TotalPrice: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2), server_default=text('((0.0))'))
519
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
520
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
521
+
522
+ Orders_: Mapped['Orders'] = relationship('Orders', back_populates='OrderDetails')
523
+ Variants_: Mapped['Variants'] = relationship('Variants', back_populates='OrderDetails')
524
+ Reviews: Mapped[List['Reviews']] = relationship('Reviews', back_populates='OrderDetails_')
525
+
526
+
527
+ class VariantAttributes(Base):
528
+ __tablename__ = 'VariantAttributes'
529
+ __table_args__ = (
530
+ ForeignKeyConstraint(['AttributeValueId'], ['AttributeValues.Id'], name='FK_VariantAttributes_AttributeValues_AttributeValueId'),
531
+ ForeignKeyConstraint(['VariantId'], ['Variants.Id'], ondelete='CASCADE', name='FK_VariantAttributes_Variants_VariantId'),
532
+ PrimaryKeyConstraint('Id', name='PK_VariantAttributes'),
533
+ Index('IX_VariantAttributes_AttributeValueId', 'AttributeValueId'),
534
+ Index('IX_VariantAttributes_VariantId', 'VariantId')
535
+ )
536
+
537
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
538
+ VariantId: Mapped[uuid.UUID] = mapped_column(Uuid)
539
+ AttributeValueId: Mapped[uuid.UUID] = mapped_column(Uuid)
540
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
541
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
542
+
543
+ AttributeValues_: Mapped['AttributeValues'] = relationship('AttributeValues', back_populates='VariantAttributes')
544
+ Variants_: Mapped['Variants'] = relationship('Variants', back_populates='VariantAttributes')
545
+
546
+
547
+ class GoodReceivedItems(Base):
548
+ __tablename__ = 'GoodReceivedItems'
549
+ __table_args__ = (
550
+ ForeignKeyConstraint(['GoodReceivedNoteId'], ['GoodReceivedNotes.Id'], ondelete='CASCADE', name='FK_GoodReceivedItems_GoodReceivedNotes_GoodReceivedNoteId'),
551
+ ForeignKeyConstraint(['VariantId'], ['Variants.Id'], ondelete='CASCADE', name='FK_GoodReceivedItems_Variants_VariantId'),
552
+ PrimaryKeyConstraint('Id', name='PK_GoodReceivedItems'),
553
+ Index('IX_GoodReceivedItems_GoodReceivedNoteId', 'GoodReceivedNoteId'),
554
+ Index('IX_GoodReceivedItems_VariantId', 'VariantId')
555
+ )
556
+
557
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
558
+ VariantId: Mapped[uuid.UUID] = mapped_column(Uuid)
559
+ GoodReceivedNoteId: Mapped[uuid.UUID] = mapped_column(Uuid)
560
+ Quantity: Mapped[int] = mapped_column(Integer)
561
+ UnitCost: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
562
+ TotalCost: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
563
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
564
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
565
+
566
+ GoodReceivedNotes_: Mapped['GoodReceivedNotes'] = relationship('GoodReceivedNotes', back_populates='GoodReceivedItems')
567
+ Variants_: Mapped['Variants'] = relationship('Variants', back_populates='GoodReceivedItems')
568
+
569
+
570
+ class Reviews(Base):
571
+ __tablename__ = 'Reviews'
572
+ __table_args__ = (
573
+ ForeignKeyConstraint(['OrderDetailId'], ['OrderDetails.Id'], ondelete='CASCADE', name='FK_Reviews_OrderDetails_OrderDetailId'),
574
+ ForeignKeyConstraint(['UserId'], ['AspNetUsers.Id'], name='FK_Reviews_AspNetUsers_UserId'),
575
+ PrimaryKeyConstraint('Id', name='PK_Reviews'),
576
+ Index('IX_Reviews_OrderDetailId', 'OrderDetailId'),
577
+ Index('IX_Reviews_UserId', 'UserId')
578
+ )
579
+
580
+ Id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
581
+ Rating: Mapped[decimal.Decimal] = mapped_column(DECIMAL(18, 2))
582
+ ReviewText: Mapped[str] = mapped_column(Unicode(1000, 'SQL_Latin1_General_CP1_CI_AS'), server_default=text("(N'')"))
583
+ OrderDetailId: Mapped[uuid.UUID] = mapped_column(Uuid)
584
+ CreatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
585
+ UpdatedAt: Mapped[datetime.datetime] = mapped_column(DATETIME2)
586
+ UserId: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid)
587
+
588
+ OrderDetails_: Mapped['OrderDetails'] = relationship('OrderDetails', back_populates='Reviews')
589
+ AspNetUsers_: Mapped[Optional['AspNetUsers']] = relationship('AspNetUsers', back_populates='Reviews')
src/database/querries.py CHANGED
@@ -1,9 +1,10 @@
 
1
  from sqlalchemy.orm import Session
2
  from sqlalchemy.exc import SQLAlchemyError
3
  import logging
4
  import uuid
5
  from src.database.db_connection import create_session
6
- from src.database.models import User, Chat, Message, FunctionCall
7
 
8
  # Set up logging
9
  logger = logging.getLogger(__name__)
@@ -47,7 +48,7 @@ def get_all_categories(session=None):
47
  try:
48
  # Using SQLAlchemy to query distinct product names
49
  from sqlalchemy import text
50
- result = session.execute(text("SELECT DISTINCT name FROM Categories"))
51
  categories = [row[0] for row in result.fetchall()]
52
 
53
  return {
@@ -104,18 +105,7 @@ def get_all_products(session=None):
104
  if close_session:
105
  session.close()
106
 
107
- # User-related queries
108
- def get_user_by_id(user_id, session=None):
109
- """
110
- Get a user by their ID.
111
-
112
- Args:
113
- user_id (str or UUID): The ID of the user to retrieve.
114
- session (Session, optional): SQLAlchemy session.
115
-
116
- Returns:
117
- dict: A dictionary containing the user or error information
118
- """
119
  close_session = False
120
 
121
  if session is None:
@@ -124,105 +114,50 @@ def get_user_by_id(user_id, session=None):
124
  return session_result
125
  session = session_result["session"]
126
  close_session = True
127
-
128
  try:
129
- # Ensure user_id is a valid UUID
130
- if isinstance(user_id, str):
131
- user_id = parse_uuid(user_id)
132
- if not user_id:
133
- return {
134
- "success": False,
135
- "error": "Invalid user ID format"
136
- }
137
-
138
- user = session.query(User).filter(User.id == user_id).first()
139
-
140
- if user:
141
- return {
142
- "success": True,
143
- "user": user
144
- }
145
- else:
146
- return {
147
- "success": False,
148
- "error": f"User with ID {user_id} not found"
149
- }
150
- except SQLAlchemyError as e:
151
- logger.error(f"Error fetching user: {str(e)}")
152
- return {
153
- "success": False,
154
- "error": f"Error fetching user: {str(e)}"
155
- }
156
- finally:
157
- if close_session:
158
- session.close()
159
 
160
- # Chat-related queries
161
- def get_chats_by_user_id(user_id, limit=10, offset=0, session=None):
162
- """
163
- Get a list of chats for a specific user.
164
-
165
- Args:
166
- user_id (str or UUID): The ID of the user.
167
- limit (int): The maximum number of chats to retrieve.
168
- offset (int): The offset for pagination.
169
- session (Session, optional): SQLAlchemy session.
170
 
171
- Returns:
172
- dict: A dictionary containing the chats or error information
173
- """
174
- close_session = False
175
-
176
- if session is None:
177
- session_result = create_session()
178
- if not session_result.get("success"):
179
- return session_result
180
- session = session_result["session"]
181
- close_session = True
182
-
183
- try:
184
- # Ensure user_id is a valid UUID
185
- if isinstance(user_id, str):
186
- user_id = parse_uuid(user_id)
187
- if not user_id:
188
- return {
189
- "success": False,
190
- "error": "Invalid user ID format"
191
- }
192
 
193
- chats = session.query(Chat).filter(
194
- Chat.user_id == user_id,
195
- Chat.is_active == True
196
- ).order_by(Chat.updated_at.desc()).offset(offset).limit(limit).all()
 
 
 
 
 
 
 
 
197
 
198
  return {
199
  "success": True,
200
- "chats": chats
201
  }
202
  except SQLAlchemyError as e:
203
- logger.error(f"Error fetching chats: {str(e)}")
204
  return {
205
  "success": False,
206
- "error": f"Error fetching chats: {str(e)}"
207
  }
208
  finally:
209
  if close_session:
210
  session.close()
211
 
212
- # Message-related queries
213
- def get_messages_by_chat_id(chat_id, limit=50, offset=0, session=None):
214
- """
215
- Get messages for a specific chat.
216
-
217
- Args:
218
- chat_id (str or UUID): The ID of the chat.
219
- limit (int): The maximum number of messages to retrieve.
220
- offset (int): The offset for pagination.
221
- session (Session, optional): SQLAlchemy session.
222
-
223
- Returns:
224
- dict: A dictionary containing the messages or error information
225
- """
226
  close_session = False
227
 
228
  if session is None:
@@ -231,47 +166,78 @@ def get_messages_by_chat_id(chat_id, limit=50, offset=0, session=None):
231
  return session_result
232
  session = session_result["session"]
233
  close_session = True
234
-
235
  try:
236
- # Ensure chat_id is a valid UUID
237
- if isinstance(chat_id, str):
238
- chat_id = parse_uuid(chat_id)
239
- if not chat_id:
240
- return {
241
- "success": False,
242
- "error": "Invalid chat ID format"
243
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
- messages = session.query(Message).filter(
246
- Message.chat_id == chat_id
247
- ).order_by(Message.created_at).offset(offset).limit(limit).all()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  return {
250
  "success": True,
251
- "messages": messages
252
  }
253
  except SQLAlchemyError as e:
254
- logger.error(f"Error fetching messages: {str(e)}")
255
  return {
256
  "success": False,
257
- "error": f"Error fetching messages: {str(e)}"
258
  }
259
  finally:
260
  if close_session:
261
  session.close()
262
 
263
- # Create a new chat
264
- def create_chat(user_id, title="New Chat", session=None):
265
  """
266
- Create a new chat for a user.
267
 
268
  Args:
269
- user_id (str or UUID): The ID of the user.
270
- title (str): The title of the chat.
271
  session (Session, optional): SQLAlchemy session.
272
 
273
  Returns:
274
- dict: A dictionary containing the new chat or error information
275
  """
276
  close_session = False
277
 
@@ -292,39 +258,39 @@ def create_chat(user_id, title="New Chat", session=None):
292
  "error": "Invalid user ID format"
293
  }
294
 
295
- # Create a new chat
296
- new_chat = Chat(user_id=user_id, title=title)
297
- session.add(new_chat)
298
- session.commit()
299
 
300
- return {
301
- "success": True,
302
- "chat": new_chat
303
- }
 
 
 
 
 
 
304
  except SQLAlchemyError as e:
305
- session.rollback()
306
- logger.error(f"Error creating chat: {str(e)}")
307
  return {
308
  "success": False,
309
- "error": f"Error creating chat: {str(e)}"
310
  }
311
  finally:
312
  if close_session:
313
  session.close()
314
 
315
- # Add a message to a chat
316
- def add_message(chat_id, content, role, session=None):
317
  """
318
- Add a new message to a chat.
319
 
320
  Args:
321
- chat_id (str or UUID): The ID of the chat.
322
- content (str): The content of the message.
323
- role (str): The role of the message (user, assistant, etc.).
324
  session (Session, optional): SQLAlchemy session.
325
 
326
  Returns:
327
- dict: A dictionary containing the new message or error information
328
  """
329
  close_session = False
330
 
@@ -336,37 +302,49 @@ def add_message(chat_id, content, role, session=None):
336
  close_session = True
337
 
338
  try:
339
- # Ensure chat_id is a valid UUID
340
- if isinstance(chat_id, str):
341
- chat_id = parse_uuid(chat_id)
342
- if not chat_id:
343
- return {
344
- "success": False,
345
- "error": "Invalid chat ID format"
346
- }
347
-
348
- # Create a new message
349
- new_message = Message(chat_id=chat_id, content=content, role=role)
350
- session.add(new_message)
351
-
352
- # Update the chat's updated_at timestamp
353
- chat = session.query(Chat).filter(Chat.id == chat_id).first()
354
- if chat:
355
- chat.updated_at = new_message.created_at
356
-
357
- session.commit()
 
 
 
 
358
 
359
  return {
360
  "success": True,
361
- "message": new_message
362
  }
363
  except SQLAlchemyError as e:
364
- session.rollback()
365
- logger.error(f"Error adding message: {str(e)}")
366
  return {
367
  "success": False,
368
- "error": f"Error adding message: {str(e)}"
369
  }
370
  finally:
371
  if close_session:
372
- session.close()
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import func
2
  from sqlalchemy.orm import Session
3
  from sqlalchemy.exc import SQLAlchemyError
4
  import logging
5
  import uuid
6
  from src.database.db_connection import create_session
7
+ from src.database.models import AspNetUsers, Products, Categories, Variants, OrderDetails, Orders, Placements, Attributes, AttributeValues, VariantAttributes
8
 
9
  # Set up logging
10
  logger = logging.getLogger(__name__)
 
48
  try:
49
  # Using SQLAlchemy to query distinct product names
50
  from sqlalchemy import text
51
+ result = session.execute(text("SELECT DISTINCT Name FROM Categories"))
52
  categories = [row[0] for row in result.fetchall()]
53
 
54
  return {
 
105
  if close_session:
106
  session.close()
107
 
108
+ def get_product_by_price_range(category = None, min_price = None, max_price = None, session=None):
 
 
 
 
 
 
 
 
 
 
 
109
  close_session = False
110
 
111
  if session is None:
 
114
  return session_result
115
  session = session_result["session"]
116
  close_session = True
117
+
118
  try:
119
+ # Using SQLAlchemy to query products by price range and category
120
+ if min_price is None:
121
+ min_price = 0
122
+ if max_price is None:
123
+ max_price = float('inf')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
+ query = session.query(Products).filter(
126
+ Products.Price >= min_price,
127
+ Products.Price <= max_price
128
+ )
 
 
 
 
 
 
129
 
130
+ if category:
131
+ query = query.filter(Products.CategoryId == category)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+ result = query.all()
134
+
135
+ products = []
136
+ for row in result:
137
+ product_dict = {
138
+ "product": {
139
+ "name": row.Name,
140
+ "description": row.Description,
141
+ "price": float(row.Price),
142
+ },
143
+ }
144
+ products.append(product_dict)
145
 
146
  return {
147
  "success": True,
148
+ "results": products
149
  }
150
  except SQLAlchemyError as e:
151
+ logger.error(f"Error fetching products by price range: {str(e)}")
152
  return {
153
  "success": False,
154
+ "error": f"Error fetching products by price range: {str(e)}"
155
  }
156
  finally:
157
  if close_session:
158
  session.close()
159
 
160
+ def get_product_information_by_name(product_name, session=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  close_session = False
162
 
163
  if session is None:
 
166
  return session_result
167
  session = session_result["session"]
168
  close_session = True
169
+
170
  try:
171
+ # Using SQLAlchemy to query product information by name
172
+ query = session.query(
173
+ Products,
174
+ Categories,
175
+ Placements,
176
+ Variants,
177
+ VariantAttributes,
178
+ AttributeValues,
179
+ Attributes
180
+ ).join(
181
+ Categories, Products.CategoryId == Categories.Id
182
+ ).join(
183
+ Placements, Products.PlacementId == Placements.Id
184
+ ).join(
185
+ Variants, Products.Id == Variants.ProductId
186
+ ).join(
187
+ VariantAttributes, Variants.Id == VariantAttributes.VariantId
188
+ ).join(
189
+ AttributeValues, VariantAttributes.AttributeValueId == AttributeValues.Id
190
+ ).join(
191
+ Attributes, AttributeValues.AttributeId == Attributes.Id
192
+ ).filter(
193
+ Products.Name == product_name
194
+ )
195
+
196
+ result = query.all()
197
 
198
+ # Transform the result into a dictionary format
199
+ products = []
200
+ for row in result:
201
+ product_dict = {
202
+ "product": {
203
+ "name": row.Products.Name,
204
+ "description": row.Products.Description,
205
+ "price": float(row.Products.Price),
206
+ "category": row.Categories.Name,
207
+ "placement": row.Placements.Name,
208
+ },
209
+ "attribute": {
210
+ "name": row.Attributes.Name,
211
+ "value": row.AttributeValues.Value
212
+ }
213
+ }
214
+ products.append(product_dict)
215
 
216
  return {
217
  "success": True,
218
+ "results": products
219
  }
220
  except SQLAlchemyError as e:
221
+ logger.error(f"Error fetching product information: {str(e)}")
222
  return {
223
  "success": False,
224
+ "error": f"Error fetching product information: {str(e)}"
225
  }
226
  finally:
227
  if close_session:
228
  session.close()
229
 
230
+ # User-related queries
231
+ def get_user_by_id(user_id, session=None):
232
  """
233
+ Get a user by their ID.
234
 
235
  Args:
236
+ user_id (str or UUID): The ID of the user to retrieve.
 
237
  session (Session, optional): SQLAlchemy session.
238
 
239
  Returns:
240
+ dict: A dictionary containing the user or error information
241
  """
242
  close_session = False
243
 
 
258
  "error": "Invalid user ID format"
259
  }
260
 
261
+ user = session.query(AspNetUsers).filter(AspNetUsers.id == user_id).first()
 
 
 
262
 
263
+ if user:
264
+ return {
265
+ "success": True,
266
+ "user": user
267
+ }
268
+ else:
269
+ return {
270
+ "success": False,
271
+ "error": f"User with ID {user_id} not found"
272
+ }
273
  except SQLAlchemyError as e:
274
+ logger.error(f"Error fetching user: {str(e)}")
 
275
  return {
276
  "success": False,
277
+ "error": f"Error fetching user: {str(e)}"
278
  }
279
  finally:
280
  if close_session:
281
  session.close()
282
 
283
+ def get_top_sale_products(category, limit=10, session=None):
 
284
  """
285
+ Get top-selling products based on sales data.
286
 
287
  Args:
288
+ category (str): The category of products to filter by.
289
+ limit (int): The maximum number of top-selling products to return.
 
290
  session (Session, optional): SQLAlchemy session.
291
 
292
  Returns:
293
+ dict: A dictionary containing the top-selling products or error information
294
  """
295
  close_session = False
296
 
 
302
  close_session = True
303
 
304
  try:
305
+ # Using SQLAlchemy to query top-selling products
306
+ result = session.execute(Categories.__table__.join(
307
+ Products, Categories.Id == Products.CategoryId
308
+ ).join(
309
+ Variants, Products.Id== Variants.ProductId
310
+ ).join(
311
+ OrderDetails, Variants.Id == OrderDetails.VariantId
312
+ ).join(
313
+ Orders, OrderDetails.OrderId == Orders.Id
314
+ ).
315
+ filter(
316
+ Categories.Name == category
317
+ ).group_by(
318
+ Products.Id
319
+ ).order_by(
320
+ func.sum(OrderDetails.Quantity).desc()
321
+ ).limit(limit))
322
+
323
+ print(f"Query executed: {result}")
324
+
325
+ top_sales_products = [dict(row._mapping) for row in result.fetchall()]
326
+
327
+ print(f"Top-selling products in category '{category}': {top_sales_products}")
328
 
329
  return {
330
  "success": True,
331
+ "top_sales_products": top_sales_products
332
  }
333
  except SQLAlchemyError as e:
334
+ logger.error(f"Error fetching top-selling products: {str(e)}")
 
335
  return {
336
  "success": False,
337
+ "error": f"Error fetching top-selling products: {str(e)}"
338
  }
339
  finally:
340
  if close_session:
341
+ session.close()
342
+
343
+ top_products = [dict(row._mapping) for row in result.fetchall()]
344
+
345
+ return {
346
+ "success": True,
347
+ "top_products": top_products
348
+ }
349
+
350
+
src/services/__pycache__/chat_service.cpython-313.pyc CHANGED
Binary files a/src/services/__pycache__/chat_service.cpython-313.pyc and b/src/services/__pycache__/chat_service.cpython-313.pyc differ
 
src/services/chat_service.py CHANGED
@@ -16,51 +16,27 @@ def calculate_office_space(furniture_items):
16
  }
17
 
18
  total_space = 0
19
- unknown_item = []
20
  for item in furniture_items:
21
- if item in space_requirements:
22
- total_space += space_requirements[item]
 
 
23
  else:
24
- unknown_item.append(item)
25
 
26
  result = {
27
  "total_space": total_space,
28
- "unknown_items": unknown_item
29
  }
30
 
31
  return result
32
 
33
- def get_product_recommendations(product_type, style, budget = 0):
34
- catalog = {
35
- "chair": {
36
- "modern": [
37
- {"name": "Ergonomic Mesh Chair", "price": 299, "description": "Adjustable modern mesh chair with lumbar support"},
38
- {"name": "Sleek Executive Chair", "price": 499, "description": "Premium modern chair with memory foam padding"}
39
- ],
40
- "classic": [
41
- {"name": "Leather Executive Chair", "price": 399, "description": "Traditional leather chair with brass accents"},
42
- {"name": "Wooden Armchair", "price": 299, "description": "Solid wood chair with cushioned seat"}
43
- ]
44
- },
45
- "desk": {
46
- "modern": [
47
- {"name": "Glass Computer Desk", "price": 349, "description": "Minimalist glass desk with metal frame"},
48
- {"name": "Adjustable Standing Desk", "price": 649, "description": "Electric height-adjustable modern desk"}
49
- ],
50
- "classic": [
51
- {"name": "Executive Wooden Desk", "price": 799, "description": "Mahogany desk with classic design"},
52
- {"name": "Traditional Writing Desk", "price": 459, "description": "Solid wood desk with drawers"}
53
- ]
54
- }
55
- }
56
-
57
- if product_type.lower() in catalog:
58
- products = catalog[product_type][style]
59
-
60
- if budget is not None:
61
- products = [product for product in products if product["price"] <= budget]
62
-
63
- return products
64
 
65
  def get_all_categories():
66
  # Import the database function at the function level to avoid circular imports
@@ -68,3 +44,14 @@ def get_all_categories():
68
  all_categories = db_get_all_categories()
69
  return all_categories
70
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
  total_space = 0
19
+ unknown_items = []
20
  for item in furniture_items:
21
+ item_name = item.get("item_name", "").lower()
22
+ quantity = item.get("quantity", 0)
23
+ if item_name in space_requirements:
24
+ total_space += space_requirements[item_name] * quantity
25
  else:
26
+ unknown_items.append(item_name)
27
 
28
  result = {
29
  "total_space": total_space,
30
+ "unknown_items": unknown_items
31
  }
32
 
33
  return result
34
 
35
+ def get_top_sales_products(category, limit):
36
+ # Import the database function at the function level to avoid circular imports
37
+ from src.database.querries import get_top_sale_products
38
+ top_sales = get_top_sale_products(category, limit)
39
+ return top_sales
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  def get_all_categories():
42
  # Import the database function at the function level to avoid circular imports
 
44
  all_categories = db_get_all_categories()
45
  return all_categories
46
 
47
+ def get_product_information_by_name(product_name):
48
+ # Import the database function at the function level to avoid circular imports
49
+ from src.database.querries import get_product_information_by_name as db_get_product_information_by_name
50
+ product_info = db_get_product_information_by_name(product_name)
51
+ return product_info
52
+
53
+ def get_product_by_price_range(category, min_price, max_price):
54
+ # Import the database function at the function level to avoid circular imports
55
+ from src.database.querries import get_product_by_price_range as db_get_product_by_price_range
56
+ products = db_get_product_by_price_range(category, min_price, max_price)
57
+ return products