<jupyter_start><jupyter_text>Recipe Bot -- "What should I cook tonight?"  a simple rules-based chatbot to solve a common daily challenge most people face. written in Python.```Joke - "There are three main difficulties in life -- What should I eat for breakfast? What to eat for lunch? What shall I have for a good dinner? "```  Step 1 Find a recipe database/dataset For local computer experiment, let's just download a file containing enough recipes for search query. Can be replaced with a SQL database later.I found only one recipe dataset on Kaggle. Link: https://www.kaggle.com/shuyangli94/food-com-recipes-and-user-interactionsIt was used in a 2019 NLP paper by scholars from Hong Kong. (After I came to study in Canada, I'm often amazed that actually I didn't know how good HK scholars are in the research world.)  Step 2 now load the recipe data for chatbot use<jupyter_code>import pandas as pd
recipe_path = r"E:\recipe_bot\food_recipes\RAW_recipes.csv"
recipe_df = pd.read_csv(recipe_path)
recipe_df.head()
# file is 280Mb big, better just load the first few rows to check column format
#ingredients is what I mainly need for the chatbot
recipe_df['ingredients'][0]
#check steps format
recipe_df['steps'][0]
recipe_df['description'][3]
recipe_df['tags'][0]
#I need only 5 columns -- recipe name, recipe ingredients, recipe steps, description, submitted for now. Make a smaller df to ensure faster query time for chatbot

recipe_db = recipe_df[['name','ingredients','steps','submitted']]
recipe_db.head()
recipe_db.memory_usage(deep=True).sum()
recipe_db.to_csv(r'E:\recipe_bot\recipes_4cols.csv') #166Mb in size<jupyter_output><empty_output><jupyter_text>Step 3 draft the recipe search bot Imagine the following conversation:```A : What should I cook now?Bot: any ingredient you want to taste in the meal?A : fish/pork/chicken/rice/curry/tomato (one or more ingredients)Bot: okay. {number} recipes match your choice. Is there any ingredient you hate? I can filter them out.A : Garlic/Kimchi/chili/beef (one or more ingredients)Bot: I see. After filtering out those ingredients, {number} recipes remain. Do you want the first one, a random recipe or the most recent recipe?A: a random one.```<jupyter_code># test filtering of favourable ingredients
liked_ingredients = ['chicken','tomato','potato']
mask1= recipe_db['ingredients'].str.contains('chicken')
mask2 = recipe_db['ingredients'].str.contains('tomato')
mask3 = recipe_db['ingredients'].str.contains('potato')
filtered1 = recipe_db[mask1&mask2&mask3]

#the following also works
#recipe_db[eval("&".join(['mask1','mask2','mask3']))]
# recipe_db[mask1][mask2][mask3]
filtered1
def recipe_bot_query(liked_ingredients):
    
    #initialize a new df
    filtered = recipe_db
    for i,ing in enumerate(liked_ingredients):
        mask= filtered['ingredients'].str.contains(ing)
        filtered = filtered[mask]
    
    return filtered

matching_recipes_df = recipe_bot_query(liked_ingredients)
matching_recipes_df
# test filtering of disliked ingredients
filtered = matching_recipes_df

disliked_ingredients = ['garlic']
for i,ing in enumerate(disliked_ingredients):
    masked= filtered['ingredients'].str.contains(ing)
    filtered = filtered.mask(masked).dropna()
    
filtered
def recipe_remove(matching_recipes_df,disliked_ingredients):
    filtered = matching_recipes_df
    for i,ing in enumerate(disliked_ingredients):
        masked= filtered['ingredients'].str.contains(ing)
        filtered = filtered.mask(masked).dropna()
    
    return filtered

cleaned_recipes = recipe_remove(matching_recipes_df,disliked_ingredients)
cleaned_recipes
# sort recipes by order/random/recency

def rank_recipe(cleaned_recipes,sort_order):
    
    if  'random' in sort_order: #random order
        cleaned_recipes = cleaned_recipes.sample(frac=1)
    elif 'recent' in sort_order: #sort by recency
        cleaned_recipes = cleaned_recipes.sort_values("submitted",ascending = False)
    
    return cleaned_recipes

# test the else condition(sort_order is original order)
sorted_recipes = rank_recipe(cleaned_recipes,"first")
sorted_recipes
#return the top row
sorted_recipes['name'].iloc[0]<jupyter_output><empty_output><jupyter_text>Step 4 Implement the imagined conversation Recall the imagined conversation```A : What should I cook now?Bot: any ingredient you want to taste in the meal?A : fish/pork/chicken/rice/curry/tomato (one or more ingredients)Bot: okay. {number} recipes match your choice. Is there any ingredient you hate? I can filter them out.A : Garlic/Kimchi/chili/beef (one or more ingredients)Bot: I see. After filtering out those ingredients, {number} recipes remain. Do you want the first one, a random recipe or the most recent recipe?A: a random one.```<jupyter_code># need one more function

def next_one(try_or_not):
    if try_or_not in ["yes","y","sure","alright","ok","okay","why not"] or "yes" in try_or_not:
        print("These are the steps of making this recipe:")
        recipe_row = sorted_recipes.iloc[0]
    else:
        print("This is the next recipe:")
        recipe_row = sorted_recipes.iloc[1]
        print(f"Recipe name: {recipe_row['name'].capitalize()}")
        
    for step in eval(recipe_row['steps']): #turn str back into a list
            print("- "+step)
            
next_one('no')<jupyter_output>This is the next recipe:
Recipe name: Hummus pasta
- cook the pasta in boiling water until done
- set aside
- heat a large pan with the 1 teaspoons olive oil
- add the mushrooms , onions , and peppers and cook for 2-3 minutes or until just start to soften
- in a small saucepan , place the hummus and the 4 tablespoons of water
- stir to combine
- heat through
- when the vegetables are done , add the hummus and pasta to the pan
- stir gently
- serve warm<jupyter_text>Step 5 Run the Recipe chatbot!<jupyter_code>print("Ask chatbot: What should I cook now?")
while True:
    liked_ingredients = input("Any ingredient you want to taste in the meal?").lower().strip(" ").split(",")
    matching_recipes_df = recipe_bot_query(liked_ingredients)
    print(f"Okay! {len(matching_recipes_df)} recipes match your choice.")
    disliked_ingredients = input("Is there any ingredient you hate? I can filter them out.").lower().strip(" ").split(",")
    cleaned_recipes = recipe_remove(matching_recipes_df,disliked_ingredients)
    print(f"I see. After filtering out those ingredients, {len(cleaned_recipes)} recipes remain.")
    sort_order = input("Do you want the first one, a random recipe or the most recent recipe?").lower()
    sorted_recipes = rank_recipe(cleaned_recipes, sort_order)
    
    ## extending the unfinished conversation
    print("Here is the first recipe: " + sorted_recipes['name'].iloc[0].capitalize())
    try_or_not =  input("Do you want to try it?").lower()
    next_one(try_or_not)
    break #done conversation<jupyter_output>Ask chatbot: What should I cook now?
Any ingredient you want to taste in the meal?chicken
Okay! 36663 recipes match your choice.
Is there any ingredient you hate? I can filter them out.garlic
I see. After filtering out those ingredients, 18080 recipes remain.
Do you want the first one, a random recipe or the most recent recipe?random
Here is the first recipe: Maple syrup orange chicken
Do you want to try it?yes
These are the steps of making this recipe:
- preheat oven to 400 degrees
- line a 9 x 13-inch baking pan with nonstick foil
- place chicken quarters skin-side up on the prepared pan
- remove the zest from the orange with a grater , then juice the orange
- whisk together orange zest , orange juice , butter , maple syrup , onion powder , and salt
- brush orange mixture over the chicken pieces
- bake 45 to 50 minutes , until done , basting midway with drippings<jupyter_text>Step 6 humanize the robot by varying replies<jupyter_code>import random
print("Ask chatbot: What should I cook now?")





while True:
    Q1_candidates = ["Any ingredient(s) you want to taste in the meal?",
                    "Any food you have in mind to start search?",
                    "Tell me one food you like.",
                    "What ingredients do you like?"]
    
    liked_ingredients = input(random.choice(Q1_candidates)).lower().strip(" ").split(",")
    matching_recipes_df = recipe_bot_query(liked_ingredients)
    
    R1_candidates = [f"Okay! {len(matching_recipes_df)} recipes match your choice.", 
                f"I can find {len(matching_recipes_df)}  in the database that contain {','.join(liked_ingredients)}.",
                f"Alright! There are  {len(matching_recipes_df)} available for your selection. "]
    print(random.choice(R1_candidates))
    
    Q2_candidates = ["Is there any ingredient(s) you hate? I can filter them out.",
                    "What ingredient(s) would you like to avoid?",
                    "Is there something you don't eat?",
                    "There's plenty of choices! You can narrow down further by removing recipes containing certain ingredients. What would you like to filter?"]
    
    disliked_ingredients = input(random.choice(Q2_candidates)).lower().strip(" ").split(",")
    cleaned_recipes = recipe_remove(matching_recipes_df,disliked_ingredients)
    
    R2_candidates = [f"I see. After filtering out those ingredients, {len(cleaned_recipes)} recipes remain.",
                f"Sure I can remove recipes containing {','.join(disliked_ingredients)}! You have narrowed down the number of recipes to  {len(cleaned_recipes)}. ",
                f"I don't like {','.join(disliked_ingredients)} too! Now we have  {len(cleaned_recipes)} options left."]
    print(random.choice(R2_candidates))
    sort_order = input("Do you want the first recipe, a random recipe or the most recent recipe?").lower()
    sorted_recipes = rank_recipe(cleaned_recipes, sort_order)
    
    ## extending the unfinished conversation
    print("Here is the recipe: " + sorted_recipes['name'].iloc[0].capitalize())
    try_or_not =  input("Do you want to try it?").lower()
    next_one(try_or_not)
    break #done conversation<jupyter_output>Ask chatbot: What should I cook now?
Any food you have in mind to start search?egg
Alright! There are  62813 available for your selection. 
Is there something you don't eat?garlic
Sure I can remove recipes containing ['garlic']! You have narrowed down number of recipes to  53316. 
Do you want the first recipe, a random recipe or the most recent recipe?random one
Here is the recipe: Dreamsicle cookie mix in a jar
Do you want to try it?why not
These are the steps of making this recipe:
- mix together flour , baking soda and baking powder
- empty jar of cookie mix into large mixing bowl
- use your hands to thoroughly blend mix
- mix until completely blended , finish mixing with your hands
- shape into balls the size of walnuts
- place 2" apart on sprayed bake sheet
- bake at 375 for 12-14 minutes until tops are very lightly browned
- cool 5 minutes on bake sheet then move to rack
{'fraction_non_alphanumeric': 0.08923020661912598, 'fraction_numerical': 0.008685317242640336, 'mean_word_length': 4.294772507260406, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 18, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '424792', 'license': 'MIT', 'n_tokens_mistral': 3353, 'n_tokens_neox': 3136, 'n_words': 1213, 'path': 'recipe_bot.ipynb', 'repo_name': 'Amyylam/sentimentscale', 'size': '10938'}