Prathamesh Sable commited on
Commit
d10bf47
·
1 Parent(s): 1219476

Add product module and update ingredient model

Browse files

Add a new `Product` model and update the `Ingredient` model with additional fields.

* **Product Model**:
- Add `models/product.py` to define the `Product` model with fields like `product_name`, `generic_name`, `brands`, `ingredients`, `ingredients_text`, `ingredients_analysis`, `nutriscore`, `nutrient_levels`, `nutriments`, and `data_quality_warnings`.
- Update `models/__init__.py` to import and include the `Product` model.

* **Ingredient Model**:
- Add new fields to `models/ingredient.py`: `description`, `origin`, `allergens`, `vegan`, and `vegetarian`.
- Create Alembic migration script `migrations/versions/xxxx_add_product_and_update_ingredient.py` to add the `Product` table and update the `Ingredient` table.

* **Services**:
- Modify `services/ai_agent.py` to save product details in the `Product` model and ingredient details in the `Ingredient` model.
- Update `services/ingredients.py` to fetch and save new fields for ingredients.

* **Endpoints**:
- Update `routers/analysis.py` to handle saving product information and ingredient search results.

* **Tests**:
- Add tests in `tests/test_ai_agent_service.py` for saving product and ingredient details.
- Add tests in `tests/test_ingredients_service.py` for fetching and saving new fields in the `Ingredient` model.
- Add tests in `tests/test_analysis_agent_service.py` for handling new fields in the `Ingredient` model.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/prathameshks/FoodAnalyzer-API/tree/main?shareId=XXXX-XXXX-XXXX-XXXX).

migrations/versions/xxxx_add_product_and_update_ingredient.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """add product and update ingredient
2
+
3
+ Revision ID: xxxx
4
+ Revises: fcf5a0dab27d
5
+ Create Date: 2025-03-22 10:00:00.000000
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = 'xxxx'
16
+ down_revision: Union[str, None] = 'fcf5a0dab27d'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.create_table('products',
25
+ sa.Column('id', sa.Integer(), nullable=False),
26
+ sa.Column('product_name', sa.String(), nullable=False),
27
+ sa.Column('generic_name', sa.String(), nullable=True),
28
+ sa.Column('brands', sa.String(), nullable=True),
29
+ sa.Column('ingredients', sa.JSON(), nullable=True),
30
+ sa.Column('ingredients_text', sa.String(), nullable=True),
31
+ sa.Column('ingredients_analysis', sa.JSON(), nullable=True),
32
+ sa.Column('nutriscore', sa.JSON(), nullable=True),
33
+ sa.Column('nutrient_levels', sa.JSON(), nullable=True),
34
+ sa.Column('nutriments', sa.JSON(), nullable=True),
35
+ sa.Column('data_quality_warnings', sa.JSON(), nullable=True),
36
+ sa.PrimaryKeyConstraint('id')
37
+ )
38
+ op.create_index(op.f('ix_products_id'), 'products', ['id'], unique=False)
39
+ op.add_column('ingredients', sa.Column('description', sa.String(), nullable=True))
40
+ op.add_column('ingredients', sa.Column('origin', sa.String(), nullable=True))
41
+ op.add_column('ingredients', sa.Column('allergens', sa.String(), nullable=True))
42
+ op.add_column('ingredients', sa.Column('vegan', sa.Boolean(), nullable=True))
43
+ op.add_column('ingredients', sa.Column('vegetarian', sa.Boolean(), nullable=True))
44
+ # ### end Alembic commands ###
45
+
46
+
47
+ def downgrade() -> None:
48
+ """Downgrade schema."""
49
+ # ### commands auto generated by Alembic - please adjust! ###
50
+ op.drop_index(op.f('ix_products_id'), table_name='products')
51
+ op.drop_table('products')
52
+ op.drop_column('ingredients', 'description')
53
+ op.drop_column('ingredients', 'origin')
54
+ op.drop_column('ingredients', 'allergens')
55
+ op.drop_column('ingredients', 'vegan')
56
+ op.drop_column('ingredients', 'vegetarian')
57
+ # ### end Alembic commands ###
models/__init__.py CHANGED
@@ -3,5 +3,6 @@ from .user import User
3
  from .user_preferences import UserPreferences
4
  from .ingredient import Ingredient
5
  from .scan_history import ScanHistory
 
6
 
7
- __all__ = ["Base", "User", "UserPreferences", "Ingredient", "ScanHistory"]
 
3
  from .user_preferences import UserPreferences
4
  from .ingredient import Ingredient
5
  from .scan_history import ScanHistory
6
+ from .product import Product
7
 
8
+ __all__ = ["Base", "User", "UserPreferences", "Ingredient", "ScanHistory", "Product"]
models/ingredient.py CHANGED
@@ -1,4 +1,4 @@
1
- from sqlalchemy import Column, Integer, String, JSON
2
  from .base import Base
3
 
4
  class Ingredient(Base):
@@ -7,3 +7,8 @@ class Ingredient(Base):
7
  id = Column(Integer, primary_key=True, index=True)
8
  name = Column(String, unique=True, index=True)
9
  nutritional_info = Column(JSON)
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, String, JSON, Boolean
2
  from .base import Base
3
 
4
  class Ingredient(Base):
 
7
  id = Column(Integer, primary_key=True, index=True)
8
  name = Column(String, unique=True, index=True)
9
  nutritional_info = Column(JSON)
10
+ description = Column(String, nullable=True)
11
+ origin = Column(String, nullable=True)
12
+ allergens = Column(String, nullable=True)
13
+ vegan = Column(Boolean, nullable=True)
14
+ vegetarian = Column(Boolean, nullable=True)
models/product.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, String, JSON
2
+ from .base import Base
3
+
4
+ class Product(Base):
5
+ __tablename__ = "products"
6
+
7
+ id = Column(Integer, primary_key=True, index=True)
8
+ product_name = Column(String, nullable=False)
9
+ generic_name = Column(String, nullable=True)
10
+ brands = Column(String, nullable=True)
11
+ ingredients = Column(JSON, nullable=True)
12
+ ingredients_text = Column(String, nullable=True)
13
+ ingredients_analysis = Column(JSON, nullable=True)
14
+ nutriscore = Column(JSON, nullable=True)
15
+ nutrient_levels = Column(JSON, nullable=True)
16
+ nutriments = Column(JSON, nullable=True)
17
+ data_quality_warnings = Column(JSON, nullable=True)
routers/analysis.py CHANGED
@@ -4,8 +4,10 @@ from typing import List, Dict, Any
4
  from database import get_db
5
  from models.user import User
6
  from models.ingredient import Ingredient
 
7
  from services.analysis_agent import analyze_ingredients, provide_personalized_recommendations
8
  from services.auth_service import get_current_user
 
9
 
10
  router = APIRouter()
11
 
@@ -24,3 +26,11 @@ def personalized_recommendations_endpoint(db: Session = Depends(get_db), current
24
  return recommendations
25
  except Exception as e:
26
  raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
4
  from database import get_db
5
  from models.user import User
6
  from models.ingredient import Ingredient
7
+ from models.product import Product
8
  from services.analysis_agent import analyze_ingredients, provide_personalized_recommendations
9
  from services.auth_service import get_current_user
10
+ from services.ai_agent import process_data
11
 
12
  router = APIRouter()
13
 
 
26
  return recommendations
27
  except Exception as e:
28
  raise HTTPException(status_code=500, detail=str(e))
29
+
30
+ @router.post("/process_product")
31
+ def process_product_endpoint(barcode: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
32
+ try:
33
+ product_data = process_data(db, barcode)
34
+ return product_data
35
+ except Exception as e:
36
+ raise HTTPException(status_code=500, detail=str(e))
services/ai_agent.py CHANGED
@@ -2,6 +2,7 @@ from sqlalchemy.orm import Session
2
  from fastapi import HTTPException
3
  from utils import fetch_product_data_from_api, save_json_file
4
  from models.ingredient import Ingredient
 
5
  from services.ingredients import get_ingredient_by_name, save_ingredient_data, fetch_ingredient_data_from_api
6
  from typing import Dict, Any
7
  import json
@@ -85,6 +86,32 @@ def process_data(db: Session, barcode: str) -> Dict[str, Any]:
85
  data = clean_data(data)
86
  data = standardize_data(data)
87
  data = enrich_data(db, data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  save_json_file(barcode, data)
89
  return data
90
 
 
2
  from fastapi import HTTPException
3
  from utils import fetch_product_data_from_api, save_json_file
4
  from models.ingredient import Ingredient
5
+ from models.product import Product
6
  from services.ingredients import get_ingredient_by_name, save_ingredient_data, fetch_ingredient_data_from_api
7
  from typing import Dict, Any
8
  import json
 
86
  data = clean_data(data)
87
  data = standardize_data(data)
88
  data = enrich_data(db, data)
89
+
90
+ # Save product details in the Product model
91
+ product = Product(
92
+ product_name=data["product_name"],
93
+ generic_name=data["generic_name"],
94
+ brands=data["brands"],
95
+ ingredients=data["ingredients"],
96
+ ingredients_text=data["ingredients_text"],
97
+ ingredients_analysis=data["ingredients_analysis"],
98
+ nutriscore=data["nutriscore"],
99
+ nutrient_levels=data["nutrient_levels"],
100
+ nutriments=data["nutriments"],
101
+ data_quality_warnings=data["data_quality_warnings"]
102
+ )
103
+ db.add(product)
104
+ db.commit()
105
+ db.refresh(product)
106
+
107
+ # Save ingredient details in the Ingredient model
108
+ for ingredient in data["ingredients"]:
109
+ ingredient_data = get_ingredient_by_name(db, ingredient["text"])
110
+ if not ingredient_data:
111
+ ingredient_data = fetch_ingredient_data_from_api(ingredient["text"])
112
+ save_ingredient_data(db, ingredient["text"], ingredient_data)
113
+ ingredient["nutritional_info"] = ingredient_data
114
+
115
  save_json_file(barcode, data)
116
  return data
117
 
services/ingredients.py CHANGED
@@ -16,18 +16,41 @@ def fetch_ingredient_data_from_api(name: str) -> Dict[str, Any]:
16
  response = requests.get(url)
17
  if response.status_code != 200:
18
  raise HTTPException(status_code=response.status_code, detail=f"Failed to fetch data for ingredient {name}")
19
- return response.json()
 
 
 
 
 
 
 
 
20
 
21
  def get_ingredient_data(db: Session, name: str) -> Dict[str, Any]:
22
  ingredient = get_ingredient_by_name(db, name)
23
  if ingredient:
24
- return ingredient.nutritional_info
 
 
 
 
 
 
 
25
  data = fetch_ingredient_data_from_api(name)
26
  save_ingredient_data(db, name, data)
27
  return data
28
 
29
  def save_ingredient_data(db: Session, name: str, data: Dict[str, Any]):
30
- ingredient = Ingredient(name=name, nutritional_info=data)
 
 
 
 
 
 
 
 
31
  db.add(ingredient)
32
  db.commit()
33
  db.refresh(ingredient)
 
16
  response = requests.get(url)
17
  if response.status_code != 200:
18
  raise HTTPException(status_code=response.status_code, detail=f"Failed to fetch data for ingredient {name}")
19
+ data = response.json()
20
+ return {
21
+ "nutritional_info": data.get("nutritional_info", {}),
22
+ "description": data.get("description", ""),
23
+ "origin": data.get("origin", ""),
24
+ "allergens": data.get("allergens", ""),
25
+ "vegan": data.get("vegan", False),
26
+ "vegetarian": data.get("vegetarian", False)
27
+ }
28
 
29
  def get_ingredient_data(db: Session, name: str) -> Dict[str, Any]:
30
  ingredient = get_ingredient_by_name(db, name)
31
  if ingredient:
32
+ return {
33
+ "nutritional_info": ingredient.nutritional_info,
34
+ "description": ingredient.description,
35
+ "origin": ingredient.origin,
36
+ "allergens": ingredient.allergens,
37
+ "vegan": ingredient.vegan,
38
+ "vegetarian": ingredient.vegetarian
39
+ }
40
  data = fetch_ingredient_data_from_api(name)
41
  save_ingredient_data(db, name, data)
42
  return data
43
 
44
  def save_ingredient_data(db: Session, name: str, data: Dict[str, Any]):
45
+ ingredient = Ingredient(
46
+ name=name,
47
+ nutritional_info=data.get("nutritional_info", {}),
48
+ description=data.get("description", ""),
49
+ origin=data.get("origin", ""),
50
+ allergens=data.get("allergens", ""),
51
+ vegan=data.get("vegan", False),
52
+ vegetarian=data.get("vegetarian", False)
53
+ )
54
  db.add(ingredient)
55
  db.commit()
56
  db.refresh(ingredient)
tests/test_ai_agent_service.py CHANGED
@@ -2,6 +2,8 @@ import unittest
2
  from unittest.mock import patch, MagicMock
3
  from sqlalchemy.orm import Session
4
  from services.ai_agent import preprocess_data, validate_data, clean_data, standardize_data, enrich_data, process_data, integrate_hugging_face_transformers
 
 
5
 
6
  class TestAIAgentService(unittest.TestCase):
7
 
@@ -143,5 +145,58 @@ class TestAIAgentService(unittest.TestCase):
143
  result = integrate_hugging_face_transformers('test_model', 'Test text')
144
  self.assertEqual(result, 'Test sequence')
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  if __name__ == '__main__':
147
  unittest.main()
 
2
  from unittest.mock import patch, MagicMock
3
  from sqlalchemy.orm import Session
4
  from services.ai_agent import preprocess_data, validate_data, clean_data, standardize_data, enrich_data, process_data, integrate_hugging_face_transformers
5
+ from models.product import Product
6
+ from models.ingredient import Ingredient
7
 
8
  class TestAIAgentService(unittest.TestCase):
9
 
 
145
  result = integrate_hugging_face_transformers('test_model', 'Test text')
146
  self.assertEqual(result, 'Test sequence')
147
 
148
+ @patch('services.ai_agent.get_ingredient_by_name')
149
+ @patch('services.ai_agent.fetch_ingredient_data_from_api')
150
+ @patch('services.ai_agent.save_ingredient_data')
151
+ def test_process_data_saves_ingredient_details(self, mock_save_ingredient_data, mock_fetch_ingredient_data_from_api, mock_get_ingredient_by_name):
152
+ db = MagicMock(spec=Session)
153
+ mock_get_ingredient_by_name.return_value = None
154
+ mock_fetch_ingredient_data_from_api.return_value = {'nutritional_info': 'Test Info'}
155
+ data = {
156
+ 'product_name': 'Test Product',
157
+ 'generic_name': 'Test Generic',
158
+ 'brands': 'Test Brand',
159
+ 'ingredients': [
160
+ {
161
+ 'text': 'Test Ingredient',
162
+ 'sub_ingredients': []
163
+ }
164
+ ],
165
+ 'ingredients_text': 'Test Ingredients Text',
166
+ 'ingredients_analysis': {},
167
+ 'nutriscore': {},
168
+ 'nutrient_levels': {},
169
+ 'nutriments': {},
170
+ 'data_quality_warnings': []
171
+ }
172
+ with patch('services.ai_agent.preprocess_data', return_value=data), \
173
+ patch('services.ai_agent.validate_data', return_value=True), \
174
+ patch('services.ai_agent.clean_data', return_value=data), \
175
+ patch('services.ai_agent.standardize_data', return_value=data), \
176
+ patch('services.ai_agent.enrich_data', return_value=data):
177
+ result = process_data(db, 'test_barcode')
178
+ self.assertEqual(result['product_name'], 'Test Product')
179
+ mock_save_ingredient_data.assert_called_once_with(db, 'Test Ingredient', {'nutritional_info': 'Test Info'})
180
+
181
+ @patch('services.ai_agent.preprocess_data')
182
+ @patch('services.ai_agent.validate_data')
183
+ @patch('services.ai_agent.clean_data')
184
+ @patch('services.ai_agent.standardize_data')
185
+ @patch('services.ai_agent.enrich_data')
186
+ @patch('services.ai_agent.save_json_file')
187
+ def test_process_data_saves_product_details(self, mock_save_json_file, mock_enrich_data, mock_standardize_data, mock_clean_data, mock_validate_data, mock_preprocess_data):
188
+ db = MagicMock(spec=Session)
189
+ mock_preprocess_data.return_value = {'product_name': 'Test Product'}
190
+ mock_validate_data.return_value = True
191
+ mock_clean_data.return_value = {'product_name': 'Test Product'}
192
+ mock_standardize_data.return_value = {'product_name': 'Test Product'}
193
+ mock_enrich_data.return_value = {'product_name': 'Test Product'}
194
+ result = process_data(db, 'test_barcode')
195
+ self.assertEqual(result['product_name'], 'Test Product')
196
+ mock_save_json_file.assert_called_once_with('test_barcode', {'product_name': 'Test Product'})
197
+ db.add.assert_called_once()
198
+ db.commit.assert_called_once()
199
+ db.refresh.assert_called_once()
200
+
201
  if __name__ == '__main__':
202
  unittest.main()
tests/test_analysis_agent_service.py CHANGED
@@ -52,5 +52,37 @@ class TestAnalysisAgentService(unittest.TestCase):
52
  self.assertIn("recommended_ingredients", result)
53
  self.assertEqual(len(result["recommended_ingredients"]), 3)
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  if __name__ == "__main__":
56
  unittest.main()
 
52
  self.assertIn("recommended_ingredients", result)
53
  self.assertEqual(len(result["recommended_ingredients"]), 3)
54
 
55
+ def test_analyze_ingredients_with_new_fields(self):
56
+ self.db.query.return_value.filter.return_value.first.return_value = self.preferences
57
+ self.db.query.return_value.all.return_value = [
58
+ Ingredient(name="sugar", nutritional_info={"calories": 100}, description="Sweetener", origin="USA", allergens="", vegan=True, vegetarian=True),
59
+ Ingredient(name="salt", nutritional_info={"sodium": 200}, description="Seasoning", origin="India", allergens="", vegan=True, vegetarian=True),
60
+ Ingredient(name="flour", nutritional_info={"carbs": 300}, description="Baking ingredient", origin="Canada", allergens="", vegan=True, vegetarian=True)
61
+ ]
62
+
63
+ result = analyze_ingredients(self.db, self.ingredients, self.user_id)
64
+
65
+ self.assertIn("safe_ingredients", result)
66
+ self.assertIn("unsafe_ingredients", result)
67
+ self.assertIn("additional_facts", result)
68
+ self.assertEqual(len(result["safe_ingredients"]), 2)
69
+ self.assertEqual(len(result["unsafe_ingredients"]), 1)
70
+ self.assertEqual(result["safe_ingredients"][0]["description"], "Sweetener")
71
+ self.assertEqual(result["safe_ingredients"][1]["origin"], "India")
72
+
73
+ def test_provide_personalized_recommendations_with_new_fields(self):
74
+ self.db.query.return_value.all.return_value = [
75
+ Ingredient(name="sugar", nutritional_info={"calories": 100}, description="Sweetener", origin="USA", allergens="", vegan=True, vegetarian=True),
76
+ Ingredient(name="salt", nutritional_info={"sodium": 200}, description="Seasoning", origin="India", allergens="", vegan=True, vegetarian=True),
77
+ Ingredient(name="flour", nutritional_info={"carbs": 300}, description="Baking ingredient", origin="Canada", allergens="", vegan=True, vegetarian=True)
78
+ ]
79
+
80
+ result = provide_personalized_recommendations(self.db, self.user_id)
81
+
82
+ self.assertIn("recommended_ingredients", result)
83
+ self.assertEqual(len(result["recommended_ingredients"]), 3)
84
+ self.assertEqual(result["recommended_ingredients"][0]["description"], "Sweetener")
85
+ self.assertEqual(result["recommended_ingredients"][1]["origin"], "India")
86
+
87
  if __name__ == "__main__":
88
  unittest.main()
tests/test_ingredients_service.py CHANGED
@@ -11,19 +11,47 @@ class TestIngredientService(unittest.TestCase):
11
  def test_get_ingredient_data(self, mock_save_ingredient_data, mock_fetch_ingredient_data_from_api, mock_get_ingredient_by_name):
12
  db = MagicMock(spec=Session)
13
  mock_get_ingredient_by_name.return_value = None
14
- mock_fetch_ingredient_data_from_api.return_value = {"name": "test_ingredient", "nutritional_info": "test_info"}
 
 
 
 
 
 
 
15
 
16
  result = get_ingredient_data(db, "test_ingredient")
17
 
18
  mock_get_ingredient_by_name.assert_called_once_with(db, "test_ingredient")
19
  mock_fetch_ingredient_data_from_api.assert_called_once_with("test_ingredient")
20
- mock_save_ingredient_data.assert_called_once_with(db, "test_ingredient", {"name": "test_ingredient", "nutritional_info": "test_info"})
21
- self.assertEqual(result, {"name": "test_ingredient", "nutritional_info": "test_info"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  def test_save_ingredient_data(self):
24
  db = MagicMock(spec=Session)
25
  name = "test_ingredient"
26
- data = {"name": "test_ingredient", "nutritional_info": "test_info"}
 
 
 
 
 
 
 
27
 
28
  save_ingredient_data(db, name, data)
29
 
 
11
  def test_get_ingredient_data(self, mock_save_ingredient_data, mock_fetch_ingredient_data_from_api, mock_get_ingredient_by_name):
12
  db = MagicMock(spec=Session)
13
  mock_get_ingredient_by_name.return_value = None
14
+ mock_fetch_ingredient_data_from_api.return_value = {
15
+ "nutritional_info": "test_info",
16
+ "description": "test_description",
17
+ "origin": "test_origin",
18
+ "allergens": "test_allergens",
19
+ "vegan": True,
20
+ "vegetarian": True
21
+ }
22
 
23
  result = get_ingredient_data(db, "test_ingredient")
24
 
25
  mock_get_ingredient_by_name.assert_called_once_with(db, "test_ingredient")
26
  mock_fetch_ingredient_data_from_api.assert_called_once_with("test_ingredient")
27
+ mock_save_ingredient_data.assert_called_once_with(db, "test_ingredient", {
28
+ "nutritional_info": "test_info",
29
+ "description": "test_description",
30
+ "origin": "test_origin",
31
+ "allergens": "test_allergens",
32
+ "vegan": True,
33
+ "vegetarian": True
34
+ })
35
+ self.assertEqual(result, {
36
+ "nutritional_info": "test_info",
37
+ "description": "test_description",
38
+ "origin": "test_origin",
39
+ "allergens": "test_allergens",
40
+ "vegan": True,
41
+ "vegetarian": True
42
+ })
43
 
44
  def test_save_ingredient_data(self):
45
  db = MagicMock(spec=Session)
46
  name = "test_ingredient"
47
+ data = {
48
+ "nutritional_info": "test_info",
49
+ "description": "test_description",
50
+ "origin": "test_origin",
51
+ "allergens": "test_allergens",
52
+ "vegan": True,
53
+ "vegetarian": True
54
+ }
55
 
56
  save_ingredient_data(db, name, data)
57