File size: 6,368 Bytes
ead8552
 
 
 
 
 
 
 
 
 
 
 
 
c5137a6
 
 
 
ead8552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79b91ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ead8552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#uvicorn bytext:app --reload
from pydantic import BaseModel
from fastapi import FastAPI,Query
from typing import List
import difflib
import csv
from fastapi.middleware.cors import CORSMiddleware
import random
from fooddata import csv_data
from io import StringIO

app = FastAPI()

@app.get("/")
async def read_main():
    return {"msg": "Hello World"}

# Enable CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Parse CSV data (assuming 'csv_data' is defined somewhere)
csv_lines = csv_data.strip().split('\n')
csv_reader = csv.DictReader(csv_lines)
foods = list(csv_reader)

def calculate_similarity(user_input, food):
    # Compare user input directly with the "foodname" field
    food_name_lower = food['foodname'].lower()
    similarity_score = difflib.SequenceMatcher(None, user_input, food_name_lower).ratio()
    return similarity_score

@app.post("/recommend")
async def find_food(
    keyword: str = Query(None, title="General Search Keyword", min_length=1),
    avoid_preference: str = Query(None, title="Avoid Preference"),
):
    # Split avoid_preference into a list of individual preferences
    avoid_preferences_list = avoid_preference.lower().split(",") if avoid_preference else []

    # If there's no input for keyword, return 5 random foods without the specified avoid preferences
    if not keyword and avoid_preferences_list:
        foods_without_avoid = [food for food in foods if not any(avoid_pref in food['avoid'].lower() for avoid_pref in avoid_preferences_list)]
        random_foods = random.sample(foods_without_avoid, min(5, len(foods_without_avoid)))
        result = [{"foodname": food['foodname'], "type": food['type'], "price": food['price'], "country": food['country'], "images": food['images']} for food in random_foods]
        return result

    # Filter out foods to avoid
    foods_to_consider = [food for food in foods if not any(avoid_pref in food['avoid'].lower() for avoid_pref in avoid_preferences_list)] if avoid_preference else foods

    # If there's no input for both keyword and avoid_preference, return random 5 foods
    if not keyword and not avoid_preference:
        random_foods = random.sample(foods, 5)
        result = [{"foodname": food['foodname'], "type": food['type'], "price": food['price'], "country": food['country'], "images": food['images']} for food in random_foods]
        return result

    # Convert user input to lowercase
    user_input = keyword.lower()

    # Calculate similarity using a more flexible approach
    similarity_scores = [
        (
            food['foodname'],
            food['type'],
            food['price'],
            food['country'],
            food['images'],  # Add 'images' here
            calculate_similarity(user_input, food)
        ) 
        for food in foods_to_consider
    ]

    # Find the top 5 foods with the highest similarity scores
    top_matches = sorted(similarity_scores, key=lambda x: x[5], reverse=True)[:5]

    # Format the results
    result = [{"foodname": foodname, "type": foodtype, "price": foodprice, "country": foodcountry, "images": images}
              for foodname, foodtype, foodprice, foodcountry, images, _ in top_matches]
    return result



def filter_food_by_criteria(data, criteria, header_mapping):
    filtered_food = [food for i, food in enumerate(csv.reader(data.strip().split("\n"))) if i > 0]

    for criterion, value in criteria.items():
        if value and criterion != 'price':
            if criterion == 'avoid':
                avoid_values = [av.lower() for av in value.split(',')]
                filtered_food = [food for food in filtered_food if not any(any(av.lower() in tag.lower() for tag in food[header_mapping['avoid']].split(',')) for av in avoid_values)]
            elif criterion == 'country':
                # Improved handling of 'country' criterion (case-insensitive and stripping whitespace)
                filtered_food = [food for food in filtered_food if isinstance(food, list) and value.lower().strip() in food[header_mapping[criterion]].lower().strip()]
            else:
                filtered_food = [food for food in filtered_food if isinstance(food, list) and any(val.lower() in food[header_mapping[criterion]].lower() for val in value.split(','))]

        if value and criterion == 'price':
            min_price, max_price = map(int, value.split('-'))
            if isinstance(filtered_food[0][header_mapping[criterion]], list):
                # Case when it's a list
                filtered_food = [food for food in filtered_food if min_price < int(food[header_mapping[criterion]][0].split('-')[0]) < max_price]
            else:
                # Case when it's not a list
                filtered_food = [food for food in filtered_food if min_price < int(food[header_mapping[criterion]].split('-')[0]) < max_price]

    return filtered_food

# รหัสหัวข้อ
header_mapping = {
    'foodname': 0,
    'country': 1,
    'taste': 2,
    'type': 3,
    'price': 4,
    'avoid': 5,
    'images': 6,
}

@app.post("/all")
def recommend_food(
    avoid: str = Query(None, description="Foods to avoid, use comma as separator"),
    country: str = Query(None, description="Preferred country"),
    taste: str = Query(None, description="Preferred taste"),
    price: str = Query(None, description="Preferred price range"),
    food_type: str = Query(None, description="Preferred food type")
):
    try:
        user_criteria = {
            'avoid': avoid,
            'country': country,
            'taste': taste,
            'price': price,
            'type': food_type,
        }

        # Filter food based on user criteria
        recommended_food = filter_food_by_criteria(csv_data, user_criteria, header_mapping)

        # Extract relevant information for the response
        response_data = [
            {
                "foodname": food[header_mapping['foodname']],
                "price": food[header_mapping['price']],
                "type": food[header_mapping['type']],
                "country": food[header_mapping['country']],
                "images": food[header_mapping['images']],
            } for food in recommended_food
        ]

        return response_data

    except Exception as e:
        return {"error": str(e)}