From d9ed761037f61ce2b09a9484d7e4248f6b56c3cd Mon Sep 17 00:00:00 2001 From: sosokker Date: Tue, 2 May 2023 14:21:10 +0700 Subject: [PATCH] Use sqlite as a main tool to manage data/Add GUI --- Essential/FoodSearch.py | 13 +++ .../__pycache__/FoodSearch.cpython-311.pyc | Bin 0 -> 1355 bytes .../__pycache__/foodItem.cpython-311.pyc | Bin 0 -> 7358 bytes Essential/foodDatabase.py | 83 +++++++++++++++ Essential/foodItem.py | 9 +- Essential/prepare_db.py | 24 +++++ app.py | 97 ++++++++++++++++++ 7 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 Essential/FoodSearch.py create mode 100644 Essential/__pycache__/FoodSearch.cpython-311.pyc create mode 100644 Essential/__pycache__/foodItem.cpython-311.pyc create mode 100644 Essential/foodDatabase.py create mode 100644 Essential/prepare_db.py create mode 100644 app.py diff --git a/Essential/FoodSearch.py b/Essential/FoodSearch.py new file mode 100644 index 0000000..25ad312 --- /dev/null +++ b/Essential/FoodSearch.py @@ -0,0 +1,13 @@ +import sqlite3 + +class FoodSearch: + def __init__(self, db_path): + self.conn = sqlite3.connect(db_path) + self.cursor = self.conn.cursor() + + def search(self, user_input) -> list: + query = f"SELECT * FROM food_data WHERE product_name LIKE '%{user_input}%'" + self.cursor.execute(query) + results = self.cursor.fetchall() + + return results \ No newline at end of file diff --git a/Essential/__pycache__/FoodSearch.cpython-311.pyc b/Essential/__pycache__/FoodSearch.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6134ad6a885c8ff22f3c1fec8634c526517429a4 GIT binary patch literal 1355 zcmZ`&-D?v;5TE->E@=}rmiQ5nqtKdY@j>52M9}zwTEz%LC>-&2V^gn5x_bv2qMXo& zJoqGq7DVV%Mfy+pCzujwSP%rCe2df%B>`pY)m`#k z0r)ImMxy^R_MW4$4g`qeLK#%J21H#2qHU=F+qj2PR#c!0(YFw18}~1-O4Utm^fvw^ z#vhS7<{R{nd7jeGg);xMn#GOXHP90C^(hxDnXS~4*s?a%gt0GPw+Rci$$mN}u^<$2lUvL`B zP1bm1bGm%iu40=DMb2s6t5w{E-(8<+J{6{I*XlLTwp)kC4n|J-$@v_r06tFT*6wUx z>rTz}rsg{O%S1nWe66;do!!aKcC-0jHjmDU4z4}fm54Fw`Kw~iB-27+lQJ^K!^vwM8l!#aGVz#SLKQ&&VCjAN z!@<(<*D?C$rHi}(yx@S&*FsJ*O?Aqzw%+%EL*W{k+kh)?9G+-q;0SG6?b^TQWz_Ax5hn|Me`km*?Idy=*Sm?CUScMQ^}z^?p!y=UnhL@d7V{I_bt`rN literal 0 HcmV?d00001 diff --git a/Essential/__pycache__/foodItem.cpython-311.pyc b/Essential/__pycache__/foodItem.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93fbb59638c8f1398468d83bed02c6f08854ec71 GIT binary patch literal 7358 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 literal 0 HcmV?d00001 diff --git a/Essential/foodDatabase.py b/Essential/foodDatabase.py new file mode 100644 index 0000000..a02a969 --- /dev/null +++ b/Essential/foodDatabase.py @@ -0,0 +1,83 @@ +import sqlite3 + +conn = sqlite3.connect('food.db') +c = conn.cursor() + +# create table +c.execute('''CREATE TABLE food + (id INTEGER PRIMARY KEY AUTOINCREMENT, + product_name TEXT, + brands TEXT, + brands_tags TEXT, + categories TEXT, + categories_tags TEXT, + categories_en TEXT, + origins TEXT, + origins_tags TEXT, + origins_en TEXT, + countries TEXT, + countries_tags TEXT, + countries_en TEXT, + image_url TEXT, + image_ingredients_url TEXT, + image_nutrition_url TEXT, + energy_kcal_100g REAL, + fat_100g REAL, + saturated_fat_100g REAL, + unsaturated_fat_100g REAL, + omega_3_fat_100g REAL, + omega_6_fat_100g REAL, + omega_9_fat_100g REAL, + trans_fat_100g REAL, + cholesterol_100g REAL, + carbohydrates_100g REAL, + sugars_100g REAL, + sucrose_100g REAL, + glucose_100g REAL, + fructose_100g REAL, + lactose_100g REAL, + maltose_100g REAL, + fiber_100g REAL, + soluble_fiber_100g REAL, + insoluble_fiber_100g REAL, + proteins_100g REAL, + salt_100g REAL, + added_salt_100g REAL, + sodium_100g REAL, + alcohol_100g REAL, + vitamin_a_100g REAL, + beta_carotene_100g REAL, + vitamin_d_100g REAL, + vitamin_e_100g REAL, + vitamin_k_100g REAL, + vitamin_c_100g REAL, + vitamin_b1_100g REAL, + vitamin_b2_100g REAL, + vitamin_pp_100g REAL, + vitamin_b6_100g REAL, + vitamin_b9_100g REAL, + vitamin_b12_100g REAL, + bicarbonate_100g REAL, + potassium_100g REAL, + chloride_100g REAL, + calcium_100g REAL, + phosphorus_100g REAL, + iron_100g REAL, + magnesium_100g REAL, + zinc_100g REAL, + copper_100g REAL, + manganese_100g REAL, + fluoride_100g REAL, + selenium_100g REAL, + chromium_100g REAL, + molybdenum_100g REAL, + iodine_100g REAL, + caffeine_100g REAL, + carbon_footprint_100g REAL, + carbon_footprint_from_meat_or_fish_100g REAL, + cocoa_100g REAL)''') + +# commit changes and close connection +conn.commit() +conn.close() + diff --git a/Essential/foodItem.py b/Essential/foodItem.py index d3deba8..3365bfc 100644 --- a/Essential/foodItem.py +++ b/Essential/foodItem.py @@ -78,13 +78,11 @@ class foodItem(): return False def nutrient_score_intl(self): - # define the nutrient score factors nutrient_factors = {'fat': 9, 'saturated_fat': 10, 'trans_fat': 10, 'cholesterol': 5, 'sodium': 6, 'carbohydrates': 7, 'sugars': 6, 'fiber': 5, 'proteins': 5, 'vitamin_a': 15, 'vitamin_c': 15, 'calcium': 10, 'iron': 10} - # calculate the nutrient score for each nutrient nutrient_scores = {} for nutrient, factor in nutrient_factors.items(): value = getattr(self, nutrient+'_100g') @@ -97,11 +95,11 @@ class foodItem(): return total_score def _get_daily_requirement(self, nutrient): - # define the daily requirement for each nutrient daily_requirement = {'fat': 70, 'saturated_fat': 20, 'trans_fat': 2, 'cholesterol': 300, 'sodium': 2000, 'carbohydrates': 260, 'sugars': 90, 'fiber': 38, 'proteins': 50, 'vitamin_a': 900, 'vitamin_c': 90, 'calcium': 1000, 'iron': 18} + # for nutrients without a daily requirement, return None return daily_requirement.get(nutrient, None) @@ -111,4 +109,7 @@ class foodItem(): for i, row in df.iterrows(): product = cls(row) products.append(product) - return products \ No newline at end of file + return products + + def __str__(self) -> str: + return self.product_name \ No newline at end of file diff --git a/Essential/prepare_db.py b/Essential/prepare_db.py new file mode 100644 index 0000000..a92987c --- /dev/null +++ b/Essential/prepare_db.py @@ -0,0 +1,24 @@ +import sqlite3 +import pandas as pd +import re + +thai_df = pd.read_csv('thai_data.csv') +us_df = pd.read_csv('us_data.csv') +japan_df = pd.read_csv('japan_data.csv') + +conn = sqlite3.connect('food_data.db') + +thai_df = thai_df[~thai_df.product_name.str.contains('to be deleted', na=False, flags=re.IGNORECASE)] +japan_df = japan_df[~japan_df.product_name.str.contains('to be deleted', na=False, flags=re.IGNORECASE)] +us_df = us_df[~us_df.product_name.str.contains('to be deleted', na=False, flags=re.IGNORECASE)] + +thai_df = thai_df.dropna(subset=['product_name']) +japan_df = japan_df.dropna(subset=['product_name']) +us_df = us_df.dropna(subset=['product_name']) + +thai_df.to_sql('thai_food', conn, if_exists='replace', index=False) +us_df.to_sql('us_food', conn, if_exists='replace', index=False) +japan_df.to_sql('japan_food', conn, if_exists='replace', index=False) + +conn.commit() +conn.close() \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..5c72a3b --- /dev/null +++ b/app.py @@ -0,0 +1,97 @@ +import tkinter as tk +import requests +import io +from PIL import Image, ImageTk +from Essential.FoodSearch import FoodSearch + + +class App: + def __init__(self, master): + self.master = master + self.food_search = FoodSearch('food_data.db') + + self.search_var = tk.StringVar() + self.search_var.trace('w', self.search_callback) + + self.search_entry = tk.Entry(self.master, textvariable=self.search_var) + self.search_entry.grid(row=0, column=0, padx=10, pady=10) + + self.search_button = tk.Button(self.master, text="Search", command=self.search) + self.search_button.grid(row=1, column=0, padx=10, pady=10) + + self.results_frame = tk.Frame(self.master) + self.results_frame.grid(row=2, column=0, padx=10, pady=10, sticky="nsew") + + self.scrollbar = tk.Scrollbar(self.results_frame) + self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + self.results_listbox = tk.Listbox(self.results_frame, yscrollcommand=self.scrollbar.set) + self.results_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + + self.scrollbar.config(command=self.results_listbox.yview) + + self.selected_item = None + + self.image_frame = tk.Frame(self.master, bg='white') + self.image_frame.grid(row=0, column=2, rowspan=3, padx=10, pady=10, sticky="nsew") + + self.master.columnconfigure(0, weight=1) + self.master.columnconfigure(1, weight=0) + self.master.columnconfigure(2, weight=1) + self.master.rowconfigure(2, weight=1) + + def search_callback(self, *args): + pass + + def search(self): + results = self.food_search.search(self.search_var.get()) + self.update_results(results) + + def update_results(self, results): + self.results_listbox.delete(0, tk.END) + for result in results: + self.results_listbox.insert(tk.END, result[1]) + + def clear_results(self): + self.results_listbox.delete(0, tk.END) + + def on_item_selected(self, event): + widget = event.widget + selection = widget.curselection() + if selection: + index = selection[0] + value = widget.get(index) + self.selected_item = value + self.show_image(self.selected_item) + + def show_image(self, item): + image_url = None + for result in self.food_search.search(self.search_var.get()): + if result[1] == item: + image_url = result[13] + break + + if not image_url: + return + + response = requests.get(image_url) + img_data = response.content + img = Image.open(io.BytesIO(img_data)) + img = img.resize((300, 300), Image.ANTIALIAS) + img_tk = ImageTk.PhotoImage(img) + + # remove old image label widget + for widget in self.image_frame.winfo_children(): + widget.destroy() + + self.image_frame.configure(bg='white') + label = tk.Label(self.image_frame, image=img_tk, bg='white') + label.image = img_tk + label.pack(fill=tk.BOTH, expand=True) +root = tk.Tk() +app = App(root) +root.geometry("800x400") +root.title('Food Search') +app.results_listbox.bind('<>', app.on_item_selected) + +root.mainloop()