From b5b9712d6b3b1795383d467e6bb9f501138aed1f Mon Sep 17 00:00:00 2001 From: Seungpyo Suh Date: Fri, 11 Oct 2024 22:38:32 +0900 Subject: [PATCH] feat: optimize refrigerator --- app/main.py | 2 + .../rearrange_refrigerator_models.py | 0 app/routes/recipe/cooking_step.py | 2 +- app/routes/refrigerator/ingredient_detect.py | 6 +- .../refrigerator/rearrange_refrigerator.py | 108 ++++++++++++++++++ app/routes/refrigerator/refrigerator.py | 10 +- 6 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 app/models/refrigerator/rearrange_refrigerator_models.py create mode 100644 app/routes/refrigerator/rearrange_refrigerator.py diff --git a/app/main.py b/app/main.py index cd47247..e207a8a 100644 --- a/app/main.py +++ b/app/main.py @@ -5,6 +5,7 @@ from app.routes import ping, root from app.routes.recipe import recipe, ingredient_info, cooking_step from app.routes.refrigerator import ingredient_detect, refrigerator +from app.routes.refrigerator import rearrange_refrigerator app = FastAPI( title="Deening API", @@ -35,3 +36,4 @@ app.include_router(ingredient_detect.router, dependencies=[Depends(verify_token)]) app.include_router(refrigerator.router, dependencies=[Depends(verify_token)]) +app.include_router(rearrange_refrigerator.router, dependencies=[Depends(verify_token)]) diff --git a/app/models/refrigerator/rearrange_refrigerator_models.py b/app/models/refrigerator/rearrange_refrigerator_models.py new file mode 100644 index 0000000..e69de29 diff --git a/app/routes/recipe/cooking_step.py b/app/routes/recipe/cooking_step.py index f36d06f..8f61d38 100644 --- a/app/routes/recipe/cooking_step.py +++ b/app/routes/recipe/cooking_step.py @@ -12,7 +12,7 @@ router = APIRouter() -@router.post("/recipe/cooking_step", tags=["Recipe"], response_model=CookingStepResponse, +@router.post("/recipe/cooking-step", tags=["Recipe"], response_model=CookingStepResponse, responses={400: {"model": ErrorResponse}, 404: {"model": ErrorResponse}}) async def get_cooking_step_info(request: CookingStepRequest): """ diff --git a/app/routes/refrigerator/ingredient_detect.py b/app/routes/refrigerator/ingredient_detect.py index 13088a0..f2c5976 100644 --- a/app/routes/refrigerator/ingredient_detect.py +++ b/app/routes/refrigerator/ingredient_detect.py @@ -11,7 +11,7 @@ router = APIRouter() -@router.post("/refrigerator/ingredient_detect", tags=["Refrigerator"], +@router.post("/refrigerator/ingredient-detect", tags=["Refrigerator"], response_model=IngredientDetectResponse, responses={400: {"model": ErrorResponse}, 404: {"model": NoIngredientsFoundResponse}}) async def ingredient_detect(image: UploadFile = File(...)): @@ -41,8 +41,8 @@ async def ingredient_detect(image: UploadFile = File(...)): {{ "ingredients": [] }} - - 주의: 반드시 다른 텍스트 없이 유효한 JSON 형식으로만 응답해주세요. + + 주의: 반드시 다른 텍스트나 코드블록 없이 유효한 JSON 형식으로만 응답해주세요. """ # OpenAI API 호출 diff --git a/app/routes/refrigerator/rearrange_refrigerator.py b/app/routes/refrigerator/rearrange_refrigerator.py new file mode 100644 index 0000000..9608f18 --- /dev/null +++ b/app/routes/refrigerator/rearrange_refrigerator.py @@ -0,0 +1,108 @@ +import json + +from fastapi import APIRouter, HTTPException + +from app.config import client as openai_client +from app.database import refrigerator_collection +from app.models.error_models import ErrorResponse +from app.models.refrigerator.refrigerator_models import GetIngredientsResponse, Refrigerator, IngredientCategory, \ + Ingredient + +router = APIRouter() + + +@router.post("/refrigerator/rearrange-refrigerator", tags=["Refrigerator"], response_model=GetIngredientsResponse, + responses={400: {"model": ErrorResponse}}) +async def rearrange_refrigerator(): + try: + # 현재 냉장고 내용물 가져오기 + ingredients = await refrigerator_collection.find().to_list(length=None) + + # ChatGPT에 보낼 메시지 준비 + rearrange_prompt = f"""냉장고 내용물을 최적화하고 재정렬해주세요. 다음 구조를 따르는 JSON 형식으로 응답해주세요: + + {{ + "categories": [ + {{ + "category": "카테고리명", + "ingredients": [ + {{ + "name": "재료명", + "amount": 수량, + "unit": "단위" + }} + ], + }} + ], + }} + + 현재 냉장고 내용물: + {json.dumps([{ + "name": ing["name"], + "amount": ing["amount"], + "unit": ing["unit"], + "category": ing["category"] + } for ing in ingredients], ensure_ascii=False)} + + 주의사항: + 1. 카테고리를 최적화하고, 필요한 경우 새로운 카테고리를 만들거나 기존 카테고리를 병합하세요. + 6. 반드시 다른 텍스트나 코드블록 없이 유효한 JSON 형식으로만 응답해주세요. + """ + + # ChatGPT로부터 제안 받기 + response = openai_client.chat.completions.create( + model="chatgpt-4o-latest", + messages=[ + {"role": "system", + "content": "You are an expert in food storage and refrigerator organization. Provide detailed and practical advice for optimizing refrigerator contents."}, + {"role": "user", "content": rearrange_prompt} + ] + ) + + # ChatGPT 응답 파싱 + response_content = response.choices[0].message.content.strip() + try: + optimized_data = json.loads(response_content) + except json.JSONDecodeError: + print(f"Failed to parse JSON. Raw response: {response_content}") + raise HTTPException(status_code=500, detail="Failed to parse optimization suggestion") + + if 'categories' not in optimized_data: + raise HTTPException(status_code=500, detail="Invalid optimization suggestion format") + + # 현재 냉장고 내용물 비우기 + await refrigerator_collection.delete_many({}) + + # 최적화된 재료를 데이터베이스에 다시 삽입 + for category in optimized_data['categories']: + for ingredient in category['ingredients']: + await refrigerator_collection.insert_one({ + "name": ingredient['name'], + "amount": ingredient['amount'], + "unit": ingredient['unit'], + "category": category['category'] + }) + + # 업데이트된 냉장고 내용물 가져오기 + updated_ingredients = await refrigerator_collection.find().to_list(length=None) + + # 응답 준비 + categories = [] + for category in optimized_data['categories']: + category_ingredients = [ + Ingredient(id=str(ing['_id']), name=ing['name'], amount=ing['amount'], unit=ing['unit'], + category=ing['category']) + for ing in updated_ingredients if ing['category'] == category['category'] + ] + categories.append(IngredientCategory( + category=category['category'], + ingredients=category_ingredients, + )) + + refrigerator = Refrigerator( + categories=categories, + ) + return GetIngredientsResponse(refrigerator=refrigerator) + + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) diff --git a/app/routes/refrigerator/refrigerator.py b/app/routes/refrigerator/refrigerator.py index e6e1e83..79c5f65 100644 --- a/app/routes/refrigerator/refrigerator.py +++ b/app/routes/refrigerator/refrigerator.py @@ -43,23 +43,23 @@ async def get_ingredients(): response_model=AddIngredientResponse) async def add_ingredients(request: AddIngredientRequest): """ - 냉장고에 여러 재료를 추가합니다. 이미 존재하는 재료의 경우 양을 더합니다. + 냉장고에 여러 재료를 추가합니다. 이미 존재하는 재료의 경우 단위가 같을 때만 양을 더합니다. """ try: for ingredient in request.ingredients: # 기존 재료 찾기 existing_ingredient = await refrigerator_collection.find_one( - {"name": ingredient.name, "category": ingredient.category}) + {"name": ingredient.name, "category": ingredient.category, "unit": ingredient.unit}) if existing_ingredient: - # 이미 존재하는 재료라면 양을 더함 + # 이미 존재하는 재료이고 단위가 같다면 양을 더함 new_amount = existing_ingredient["amount"] + ingredient.amount await refrigerator_collection.update_one( - {"name": ingredient.name, "category": ingredient.category}, + {"_id": existing_ingredient["_id"]}, {"$set": {"amount": new_amount}} ) else: - # 새로운 재료라면 추가 + # 새로운 재료이거나 단위가 다르다면 새로 추가 await refrigerator_collection.insert_one(ingredient.dict()) return {"message": "재료가 성공적으로 추가되었습니다."}