Prathamesh Sable commited on
Commit
8986db1
·
1 Parent(s): fd00bfb

working add product

Browse files
db/repositories.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from sqlalchemy.orm import Session
2
  from sqlalchemy import cast, or_, String
3
  from sqlalchemy.dialects.postgresql import JSONB
@@ -38,15 +39,24 @@ class IngredientRepository:
38
  return self.db.query(models.Ingredient).offset(skip).limit(limit).all()
39
 
40
  def create_ingredient(self, ingredient_data: IngredientAnalysisResult):
 
 
 
 
 
 
 
 
 
41
  # Create ingredient record
42
  db_ingredient = models.Ingredient(
43
- name=ingredient_data.name,
44
- alternate_names=ingredient_data.alternate_names,
45
- safety_rating=ingredient_data.safety_rating,
46
- description=ingredient_data.description,
47
- health_effects=ingredient_data.health_effects,
48
- allergic_info=ingredient_data.allergic_info,
49
- diet_type=ingredient_data.diet_type
50
  )
51
  self.db.add(db_ingredient)
52
  self.db.commit()
@@ -102,7 +112,7 @@ class ProductRepository:
102
 
103
  def add_product(self, product_create: ProductCreate):
104
  db_product = self._create_product(product_create)
105
- self._store_analysis_data(db_product, product_create.ingredients_analysis)
106
  return db_product
107
 
108
  def _create_product(self, product_create: ProductCreate):
 
1
+ import json
2
  from sqlalchemy.orm import Session
3
  from sqlalchemy import cast, or_, String
4
  from sqlalchemy.dialects.postgresql import JSONB
 
39
  return self.db.query(models.Ingredient).offset(skip).limit(limit).all()
40
 
41
  def create_ingredient(self, ingredient_data: IngredientAnalysisResult):
42
+ # convert the json data to string using json.dumps
43
+ name = ingredient_data.name
44
+ alternate_names = json.dumps(ingredient_data.alternate_names)
45
+ safety_rating = ingredient_data.safety_rating
46
+ description = ingredient_data.description
47
+ health_effects = json.dumps(ingredient_data.health_effects)
48
+ allergic_info = json.dumps(ingredient_data.allergic_info) if ingredient_data.allergic_info else None
49
+ diet_type = ingredient_data.diet_type
50
+
51
  # Create ingredient record
52
  db_ingredient = models.Ingredient(
53
+ name=name,
54
+ alternate_names=alternate_names,
55
+ safety_rating=safety_rating,
56
+ description=description,
57
+ health_effects=health_effects,
58
+ allergic_info=allergic_info,
59
+ diet_type=diet_type
60
  )
61
  self.db.add(db_ingredient)
62
  self.db.commit()
 
112
 
113
  def add_product(self, product_create: ProductCreate):
114
  db_product = self._create_product(product_create)
115
+ # self._store_analysis_data(db_product, product_create.ingredients_analysis)
116
  return db_product
117
 
118
  def _create_product(self, product_create: ProductCreate):
env.py CHANGED
@@ -7,7 +7,10 @@ load_dotenv()
7
 
8
  # Environment variables for FoodAnalyzer-API
9
  PORT = int(os.getenv("PORT", 8000))
10
-
 
 
 
11
  # JWT Secret Key
12
  SECRET_KEY = os.getenv("SECRET_KEY", "09d8f7a6b5c4e3d2f1a0b9c8d7e6f5a4")
13
  ALGORITHM = os.getenv("ALGORITHM", "HS256")
 
7
 
8
  # Environment variables for FoodAnalyzer-API
9
  PORT = int(os.getenv("PORT", 8000))
10
+ UPLOADED_IMAGES_DIR = "uploaded_images"
11
+ if not os.path.exists(UPLOADED_IMAGES_DIR):
12
+ os.makedirs(UPLOADED_IMAGES_DIR)
13
+
14
  # JWT Secret Key
15
  SECRET_KEY = os.getenv("SECRET_KEY", "09d8f7a6b5c4e3d2f1a0b9c8d7e6f5a4")
16
  ALGORITHM = os.getenv("ALGORITHM", "HS256")
interfaces/productModels.py CHANGED
@@ -8,17 +8,17 @@ class ProductIngredientsRequest(BaseModel):
8
 
9
  class ProductCreate(BaseModel):
10
  product_name: str
11
- ingredients: List[str]
12
  overall_safety_score: int
13
  suitable_diet_types: str
14
- allergy_warnings: List[str]
15
  usage_recommendations: str
16
- health_insights: Dict[str, List[str]]
17
- ingredient_interactions: List[str]
18
  key_takeaway: str
19
  ingredients_count: int
20
  user_id: int
21
  timestamp: datetime
22
- ingredient_ids: List[int]
23
 
24
 
 
8
 
9
  class ProductCreate(BaseModel):
10
  product_name: str
11
+ ingredients: List[str]|str
12
  overall_safety_score: int
13
  suitable_diet_types: str
14
+ allergy_warnings: List[str]|str
15
  usage_recommendations: str
16
+ health_insights: Dict[str, List[str]]|str
17
+ ingredient_interactions: List[str]|str
18
  key_takeaway: str
19
  ingredients_count: int
20
  user_id: int
21
  timestamp: datetime
22
+ ingredient_ids: List[int]|str
23
 
24
 
requirements.txt CHANGED
@@ -2,14 +2,15 @@
2
  fastapi==0.115.12
3
  uvicorn==0.34.0
4
  python-multipart==0.0.20
5
- jinja2
 
6
 
7
  # Database
8
  sqlalchemy==2.0.40
9
  alembic==1.15.2
10
  psycopg2-binary==2.9.10
11
- mysqlclient
12
- pymysql
13
 
14
  # Authentication
15
  python-jose==3.3.0
@@ -19,16 +20,16 @@ bcrypt==4.0.1 # Using 4.0.1 to avoid the attribute error
19
  # AI & ML
20
  langchain==0.3.23
21
  langchain-community==0.3.21
22
- langchain-google-genai
23
  langchain-openai==0.3.12
24
- google-generativeai
25
  openai==1.73.0
26
  langgraph==0.3.27
27
  langsmith==0.3.30
28
 
29
  # Computer Vision
30
- tensorflow
31
- tensorflow_hub
32
  pillow==11.1.0
33
  opencv-python==4.11.0.86
34
  pytesseract==0.3.13
 
2
  fastapi==0.115.12
3
  uvicorn==0.34.0
4
  python-multipart==0.0.20
5
+ jinja2==3.1.6
6
+ aiohttp==3.11.16
7
 
8
  # Database
9
  sqlalchemy==2.0.40
10
  alembic==1.15.2
11
  psycopg2-binary==2.9.10
12
+ mysqlclient==2.2.7
13
+ pymysql==1.1.1
14
 
15
  # Authentication
16
  python-jose==3.3.0
 
20
  # AI & ML
21
  langchain==0.3.23
22
  langchain-community==0.3.21
23
+ langchain-google-genai==2.0.10
24
  langchain-openai==0.3.12
25
+ google-generativeai==0.8.4
26
  openai==1.73.0
27
  langgraph==0.3.27
28
  langsmith==0.3.30
29
 
30
  # Computer Vision
31
+ tensorflow==2.19.0
32
+ tensorflow_hub==0.16.1
33
  pillow==11.1.0
34
  opencv-python==4.11.0.86
35
  pytesseract==0.3.13
routers/product.py CHANGED
@@ -1,9 +1,11 @@
 
1
  import io
2
  from fastapi import APIRouter, Request, HTTPException, File, UploadFile, Form
3
  from fastapi.responses import JSONResponse, FileResponse
4
  from typing import List, Dict, Any
5
  from logger_manager import log_debug, log_info, log_error
6
  import os
 
7
  from services.product_service import ProductService
8
  from db.models import Marker, Product
9
  from sqlalchemy.orm import Session
@@ -24,14 +26,14 @@ from db.repositories import ProductRepository, IngredientRepository
24
 
25
  from services.ingredients import IngredientService
26
  from services.productAnalyzerAgent import analyze_product_ingredients
 
27
  from utils.db_utils import add_product_to_database
28
- from utils.vuforia_utils import add_target_to_vuforia, UPLOADED_IMAGES_DIR
29
  from utils.fetch_data import fetch_product_data_from_api
30
  import uuid
31
  import json
32
 
33
  # import environment variables
34
- from env import FAKE_TARGET_IMAGE_NAME, SEND_FAKE_TARGET, VUFORIA_SERVER_ACCESS_KEY,VUFORIA_SERVER_SECRET_KEY,VUFORIA_TARGET_DATABASE_NAME,VUFORIA_TARGET_DATABASE_ID
35
 
36
  router = APIRouter()
37
 
@@ -108,55 +110,77 @@ async def create_product(
108
  # Save the uploaded image
109
  image_path = os.path.join(UPLOADED_IMAGES_DIR, image_name)
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  # Create product data model
112
  product_create_data = ProductCreate(
113
  product_name=name,
114
- ingredients=ingredients_list,
115
- overall_safety_score=0, # Default values
116
- suitable_diet_types=[],
117
- allergy_warnings=[],
118
- usage_recommendations="",
119
- health_insights="",
120
- ingredient_interactions="",
121
- key_takeaway="",
122
- ingredients_count=len(ingredients_list),
123
- user_id=None, # Can be updated later if needed
124
- timestamp=None,
125
- ingredient_ids=[]
126
  )
127
 
128
- # Find ingredients and append their IDs
129
- ingredient_repo = IngredientRepository(db)
130
- for ingredient_name in product_create_data.ingredients:
131
- ingredient = ingredient_repo.get_ingredient_by_name(ingredient_name)
132
- if ingredient:
133
- product_create_data.ingredient_ids.append(ingredient.id)
134
-
135
- # Analyze product ingredients
136
- ingredient_results = []
137
- for ingredient_name in product_create_data.ingredients:
138
- ingredient = ingredient_repo.get_ingredient_by_name(ingredient_name)
139
- if ingredient:
140
- ingredient_results.append(ingredient)
141
-
142
- product_analysis = await analyze_product_ingredients(
143
- ingredients_data=ingredient_results,
144
- user_preferences={
145
- "user_id": product_create_data.user_id,
146
- "allergies": None,
147
- "dietary_restrictions": None
148
- }
149
- )
150
- product_create_data.ingredients_analysis = product_analysis
151
 
152
  # Add product to database
153
  product_repo = ProductRepository(db)
154
  product = product_repo.add_product(product_create_data)
155
 
 
 
156
  # Add Vuforia target if needed
157
  await add_product_to_database(product.id, [image_name], db, {
158
  "name": name,
159
- "ingredients": ingredients,
160
  "image_name": image_name,
161
  })
162
 
 
1
+ from datetime import datetime
2
  import io
3
  from fastapi import APIRouter, Request, HTTPException, File, UploadFile, Form
4
  from fastapi.responses import JSONResponse, FileResponse
5
  from typing import List, Dict, Any
6
  from logger_manager import log_debug, log_info, log_error
7
  import os
8
+ from services.auth_service import get_current_user
9
  from services.product_service import ProductService
10
  from db.models import Marker, Product
11
  from sqlalchemy.orm import Session
 
26
 
27
  from services.ingredients import IngredientService
28
  from services.productAnalyzerAgent import analyze_product_ingredients
29
+ from utils.analyze import process_product_ingredients
30
  from utils.db_utils import add_product_to_database
 
31
  from utils.fetch_data import fetch_product_data_from_api
32
  import uuid
33
  import json
34
 
35
  # import environment variables
36
+ from env import FAKE_TARGET_IMAGE_NAME, SEND_FAKE_TARGET,UPLOADED_IMAGES_DIR, VUFORIA_SERVER_ACCESS_KEY,VUFORIA_SERVER_SECRET_KEY,VUFORIA_TARGET_DATABASE_NAME,VUFORIA_TARGET_DATABASE_ID
37
 
38
  router = APIRouter()
39
 
 
110
  # Save the uploaded image
111
  image_path = os.path.join(UPLOADED_IMAGES_DIR, image_name)
112
 
113
+ # analyze the product ingredients
114
+ results = await process_product_ingredients(ingredients_list)
115
+
116
+ # extract data from the analysis results
117
+ # result = {
118
+ # "ingredients_count": len(product_ingredients),
119
+ # "processed_ingredients": ingredient_results,
120
+ # "ingredient_ids": product_analysis["ingredient_ids"],
121
+ # "overall_analysis": product_analysis,
122
+ # "timestamp": datetime.now(tz=pytz.timezone('Asia/Kolkata')).isoformat()
123
+ # }
124
+ # {{
125
+ # "overall_safety_score": (number between 1-10),
126
+ # "suitable_diet_types": (strings from "Vegan", "Vegetarian", "Non-Vegetarian"),
127
+ # "allergy_warnings": (array of strings),
128
+ # "usage_recommendations": (string with specific guidance),
129
+ # "health_insights": {{
130
+ # "benefits": (array of strings),
131
+ # "concerns": (array of strings)
132
+ # }},
133
+ # "ingredient_interactions": (array of strings),
134
+ # "key_takeaway": (string)
135
+ # }}
136
+
137
+ # Check if the analysis results are valid
138
+ analysis_results = results.get("overall_analysis", {})
139
+ overall_safety_score = analysis_results.get("overall_safety_score", 0)
140
+ suitable_diet_types = analysis_results.get("suitable_diet_types", [])
141
+ allergy_warnings = analysis_results.get("allergy_warnings", [])
142
+ usage_recommendations = analysis_results.get("usage_recommendations", "")
143
+ health_insights = analysis_results.get("health_insights", {})
144
+ ingredient_interactions = analysis_results.get("ingredient_interactions", [])
145
+ key_takeaway = analysis_results.get("key_takeaway", "")
146
+
147
+ current_user_id = 0
148
+ try:
149
+ current_user = await get_current_user()
150
+ current_user_id = current_user.id
151
+ except:
152
+ # Handle case where user is not authenticated
153
+ log_error("User not authenticated, using default user ID")
154
+ current_user_id = 0 # Default user ID, change as needed
155
+
156
  # Create product data model
157
  product_create_data = ProductCreate(
158
  product_name=name,
159
+ ingredients=json.dumps(ingredients_list),
160
+ overall_safety_score=overall_safety_score,
161
+ suitable_diet_types=json.dumps(suitable_diet_types),
162
+ allergy_warnings=json.dumps(allergy_warnings),
163
+ usage_recommendations=usage_recommendations,
164
+ health_insights=json.dumps(health_insights),
165
+ ingredient_interactions=json.dumps(ingredient_interactions),
166
+ key_takeaway=json.dumps(key_takeaway),
167
+ ingredients_count=results.get("ingredients_count", 0),
168
+ user_id=current_user_id, # Can be updated later if needed
169
+ timestamp=results.get("timestamp", datetime.now().isoformat()),
170
+ ingredient_ids=json.dumps(results.get("ingredient_ids", [])),
171
  )
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
  # Add product to database
175
  product_repo = ProductRepository(db)
176
  product = product_repo.add_product(product_create_data)
177
 
178
+ print(product)
179
+
180
  # Add Vuforia target if needed
181
  await add_product_to_database(product.id, [image_name], db, {
182
  "name": name,
183
+ "ingredients": ingredients_list,
184
  "image_name": image_name,
185
  })
186
 
services/ingredientFinderAgent.py CHANGED
@@ -412,12 +412,34 @@ class IngredientInfoAgentLangGraph:
412
  # Extract the result or create a default
413
  if final_state.get("result"):
414
  log_info(f"Analysis complete for {ingredient}")
415
- return IngredientAnalysisResult(**final_state["result"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  else:
417
  log_info(f"No result in final state for {ingredient}, returning default")
 
418
  return IngredientAnalysisResult(
419
  name=ingredient,
420
- is_found=len(sources_data) > 0,
 
 
 
 
 
421
  details_with_source=sources_data
422
  )
423
 
 
412
  # Extract the result or create a default
413
  if final_state.get("result"):
414
  log_info(f"Analysis complete for {ingredient}")
415
+ # Ensure id field is present
416
+ if "id" not in final_state["result"]:
417
+ final_state["result"]["id"] = 0 # Will be replaced with actual DB ID
418
+
419
+ result = IngredientAnalysisResult(**final_state["result"])
420
+
421
+ # Save to database using SessionLocal
422
+ from db.database import SessionLocal
423
+ from db.repositories import IngredientRepository
424
+
425
+ with SessionLocal() as db:
426
+ repo = IngredientRepository(db)
427
+ db_ingredient = repo.create_ingredient(result)
428
+ # Update with real database ID
429
+ result.id = db_ingredient.id
430
+
431
+ return result
432
  else:
433
  log_info(f"No result in final state for {ingredient}, returning default")
434
+ # Include id field in default result
435
  return IngredientAnalysisResult(
436
  name=ingredient,
437
+ is_found=len(sources_data) > 0,
438
+ id=0, # Required field
439
+ alternate_names=[],
440
+ safety_rating=0,
441
+ description="No reliable information found",
442
+ health_effects=["Unknown"],
443
  details_with_source=sources_data
444
  )
445
 
services/productAnalyzerAgent.py CHANGED
@@ -46,6 +46,8 @@ Description: {ingredient.description[:200] + '...' if len(ingredient.description
46
  allergies = user_preferences.get("allergies", "None specified")
47
  diet = user_preferences.get("dietary_restrictions", "None specified")
48
  user_context = f"""
 
 
49
  User has the following preferences:
50
  - Dietary Restrictions: {diet}
51
  - Allergies: {allergies}
@@ -62,7 +64,6 @@ analysis that would be helpful for a consumer viewing this in an AR application.
62
  ## INGREDIENTS INFORMATION:
63
  {''.join(ingredients_summary)}
64
 
65
- ## Also consider the following user preferences:
66
  {user_context}
67
 
68
  ## REQUIRED ANALYSIS:
 
46
  allergies = user_preferences.get("allergies", "None specified")
47
  diet = user_preferences.get("dietary_restrictions", "None specified")
48
  user_context = f"""
49
+ ## Also consider the following user preferences:
50
+
51
  User has the following preferences:
52
  - Dietary Restrictions: {diet}
53
  - Allergies: {allergies}
 
64
  ## INGREDIENTS INFORMATION:
65
  {''.join(ingredients_summary)}
66
 
 
67
  {user_context}
68
 
69
  ## REQUIRED ANALYSIS:
utils/analyze.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ from datetime import datetime
3
+ import pytz
4
+ from typing import List, Dict, Any
5
+ from logger_manager import log_info, log_error
6
+ from services.productAnalyzerAgent import analyze_product_ingredients
7
+ from utils.ingredient_utils import process_single_ingredient
8
+
9
+ # Load environment variables
10
+ from env import PARALLEL_RATE_LIMIT
11
+
12
+ log_info(f"Using parallel rate limit of {PARALLEL_RATE_LIMIT}")
13
+
14
+ # Create a semaphore to limit concurrent API calls
15
+ llm_semaphore = asyncio.Semaphore(PARALLEL_RATE_LIMIT)
16
+
17
+
18
+ async def process_product_ingredients(product_ingredients: List[str]) -> Dict[str, Any]:
19
+ log_info(f"process_product_ingredients called for {len(product_ingredients)} ingredients")
20
+ try:
21
+ # Step 1: Process individual ingredients
22
+ ingredient_results = []
23
+
24
+ log_info(f"Starting parallel ingredient processing with rate limit {PARALLEL_RATE_LIMIT}")
25
+
26
+ # Create tasks for parallel processing
27
+ tasks = []
28
+ for ingredient_name in product_ingredients:
29
+ task = process_single_ingredient(ingredient_name)
30
+ tasks.append(task)
31
+
32
+ # Execute tasks concurrently with rate limiting
33
+ ingredient_results = await asyncio.gather(*tasks)
34
+ log_info(f"Completed parallel processing of {len(ingredient_results)} ingredients")
35
+
36
+ product_analysis = await analyze_product_ingredients(
37
+ ingredients_data=ingredient_results
38
+ )
39
+
40
+ # print("Product analysis result:", product_analysis)
41
+
42
+ # Step 3: Prepare final response
43
+ result = {
44
+ "ingredients_count": len(product_ingredients),
45
+ "processed_ingredients": ingredient_results,
46
+ "ingredient_ids": product_analysis["ingredient_ids"],
47
+ "overall_analysis": product_analysis,
48
+ "timestamp": datetime.now(tz=pytz.timezone('Asia/Kolkata')).isoformat()
49
+ }
50
+
51
+ log_info("process_product_ingredients completed successfully")
52
+ return result
53
+
54
+ except Exception as e:
55
+ log_error(f"Error in process_product_ingredients: {str(e)}",e)
56
+ return None
utils/db_utils.py CHANGED
@@ -7,7 +7,8 @@ from logger_manager import log_info, log_error
7
  from fastapi import HTTPException
8
  import os
9
  from services.product_service import ProductService
10
- from utils.vuforia_utils import add_target_to_vuforia, UPLOADED_IMAGES_DIR # Assuming add_target_to_vuforia and UPLOADED_IMAGES_DIR are needed and will remain in product.py for now. If they are also moved, the import needs adjustment.
 
11
  import json
12
 
13
 
 
7
  from fastapi import HTTPException
8
  import os
9
  from services.product_service import ProductService
10
+ from utils.vuforia_utils import add_target_to_vuforia
11
+ from env import UPLOADED_IMAGES_DIR # Assuming add_target_to_vuforia and UPLOADED_IMAGES_DIR are needed and will remain in product.py for now. If they are also moved, the import needs adjustment.
12
  import json
13
 
14
 
utils/image_processing_utils.py CHANGED
@@ -5,7 +5,7 @@ from PIL import Image, ImageDraw, ImageFont, ImageOps
5
  import requests
6
  from io import BytesIO
7
  import os
8
-
9
 
10
  # Load the model from TF Hub
11
  # Cache the model globally
@@ -14,10 +14,6 @@ detector = hub.load("https://tfhub.dev/google/openimages_v4/ssd/mobilenet_v2/1")
14
  # Classes you care about
15
  TARGET_CLASSES = set(["Food processor", "Fast food", "Food", "Seafood", "Snack"])
16
 
17
- UPLOADED_IMAGES_DIR = "uploaded_images"
18
- if not os.path.exists(UPLOADED_IMAGES_DIR):
19
- os.makedirs(UPLOADED_IMAGES_DIR)
20
-
21
 
22
  def load_image_from_url(url, size=(640, 480)):
23
  response = requests.get(url)
 
5
  import requests
6
  from io import BytesIO
7
  import os
8
+ from env import UPLOADED_IMAGES_DIR
9
 
10
  # Load the model from TF Hub
11
  # Cache the model globally
 
14
  # Classes you care about
15
  TARGET_CLASSES = set(["Food processor", "Fast food", "Food", "Seafood", "Snack"])
16
 
 
 
 
 
17
 
18
  def load_image_from_url(url, size=(640, 480)):
19
  response = requests.get(url)
utils/vuforia_utils.py CHANGED
@@ -1,58 +1,93 @@
1
  import json
 
 
 
 
 
2
  from logger_manager import log_info, log_error
3
- from PIL import Image
4
  import os
5
- from pathlib import Path
6
- import requests
7
-
8
- UPLOADED_IMAGES_DIR = "uploaded_images"
9
- if not os.path.exists(UPLOADED_IMAGES_DIR):
10
- os.makedirs(UPLOADED_IMAGES_DIR)
11
-
12
- from env import VUFORIA_SERVER_ACCESS_KEY, VUFORIA_SERVER_SECRET_KEY, VUFORIA_TARGET_DATABASE_NAME, VUFORIA_TARGET_DATABASE_ID
13
-
14
- def get_vuforia_auth_headers():
15
- """
16
- Returns the authentication headers for Vuforia API requests.
17
- """
18
- return {
19
- "Authorization": f"VWS {VUFORIA_SERVER_ACCESS_KEY}:{VUFORIA_SERVER_SECRET_KEY}",
20
- "Content-Type": "application/json",
21
- }
22
 
 
23
 
24
  async def add_target_to_vuforia(image_name: str, image_path: str) -> str:
25
  """
26
  Adds a target to the Vuforia database and returns the Vuforia target ID.
 
27
  """
28
  log_info(f"Adding target {image_name} to Vuforia")
29
 
30
  try:
 
31
  with open(image_path, "rb") as image_file:
32
  image_data = image_file.read()
33
-
34
- url = f"https://vws.vuforia.com/targets"
35
-
36
- headers = get_vuforia_auth_headers()
 
 
 
 
 
 
37
  payload = {
38
  "name": image_name,
39
- "width": 1.0, # Default width
40
- "image": image_data.hex(), # Convert image data to hex
41
  "active_flag": True,
42
  }
43
-
44
- response = await requests.post(url, headers=headers, json=payload)
45
- response_data = json.loads(response.text)
46
- if response.status_code == 201:
47
- log_info(
48
- f"Target {image_name} added successfully with Vuforia ID: {response_data['target_id']}"
49
- )
50
- return response_data["target_id"]
51
- else:
52
- log_error(f"Failed to add target {image_name}: {response.text}")
53
- raise Exception(f"Failed to add target {image_name}: {response.text}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  except Exception as e:
55
- log_error(f"Error adding target {image_name}: {e}",e)
56
- raise
57
-
58
-
 
1
  import json
2
+ import hmac
3
+ import hashlib
4
+ import base64
5
+ import time
6
+ from datetime import datetime
7
  from logger_manager import log_info, log_error
 
8
  import os
9
+ import aiohttp
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ from env import VUFORIA_SERVER_ACCESS_KEY, VUFORIA_SERVER_SECRET_KEY,UPLOADED_IMAGES_DIR
12
 
13
  async def add_target_to_vuforia(image_name: str, image_path: str) -> str:
14
  """
15
  Adds a target to the Vuforia database and returns the Vuforia target ID.
16
+ Implements proper Vuforia authentication and request format.
17
  """
18
  log_info(f"Adding target {image_name} to Vuforia")
19
 
20
  try:
21
+ # Read image data
22
  with open(image_path, "rb") as image_file:
23
  image_data = image_file.read()
24
+
25
+ # Base64 encode the image
26
+ image_base64 = base64.b64encode(image_data).decode('utf-8')
27
+
28
+ # Create request data
29
+ request_path = '/targets'
30
+ host = 'vws.vuforia.com'
31
+ url = f"https://{host}{request_path}"
32
+
33
+ # Create payload
34
  payload = {
35
  "name": image_name,
36
+ "width": 1.0, # Default width in scene units
37
+ "image": image_base64,
38
  "active_flag": True,
39
  }
40
+
41
+ # Convert payload to JSON
42
+ body = json.dumps(payload)
43
+
44
+ # Get current date in proper format for Vuforia
45
+ date = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
46
+
47
+ # Set content type
48
+ content_type = 'application/json'
49
+
50
+ # Calculate MD5 of request body
51
+ content_md5 = hashlib.md5(body.encode('utf-8')).hexdigest()
52
+
53
+ # Create string to sign according to Vuforia docs
54
+ string_to_sign = f"POST\n{content_md5}\n{content_type}\n{date}\n{request_path}"
55
+
56
+ # Generate signature
57
+ signature = hmac.new(
58
+ VUFORIA_SERVER_SECRET_KEY.encode('utf-8'),
59
+ string_to_sign.encode('utf-8'),
60
+ hashlib.sha1
61
+ ).digest()
62
+ signature_hex = base64.b64encode(signature).decode('utf-8')
63
+
64
+ # Create headers
65
+ headers = {
66
+ 'Authorization': f'VWS {VUFORIA_SERVER_ACCESS_KEY}:{signature_hex}',
67
+ 'Content-Type': content_type,
68
+ 'Date': date,
69
+ 'Content-MD5': content_md5
70
+ }
71
+
72
+ # Make the API request
73
+ async with aiohttp.ClientSession() as session:
74
+ async with session.post(url, headers=headers, data=body) as response:
75
+ # Get response text and try to parse as JSON
76
+ response_text = await response.text()
77
+ try:
78
+ response_json = json.loads(response_text)
79
+ except:
80
+ response_json = {"error": "Failed to parse response"}
81
+
82
+ log_info(f"Vuforia response status: {response.status}")
83
+
84
+ if response.status == 201: # Created
85
+ log_info(f"Target added successfully: {response_json}")
86
+ return response_json.get("target_id", "unknown_target_id")
87
+ else:
88
+ log_error(f"Failed to add target: Status {response.status}, Response: {response_text}")
89
+ raise Exception(f"Failed to add target {image_name}: Status {response.status}, Error: {response_json.get('result_code', 'Unknown')}")
90
+
91
  except Exception as e:
92
+ log_error(f"Error adding target {image_name}: {e}", e)
93
+ raise