async def get_kitchen_contents(user_id: str) -> Response: """ Gets all the ingredients a user has in their kitchen """ with get_session() as db: try: user_db = db.query(User).filter( User.id == decrypt(user_id)).first() if not user_db: return Response(success=False, message="No kitchen found for user.") # Join kitchen to ingredients to get names of all ingredients user has ingredients = db.query(Ingredient)\ .join(Kitchen, Ingredient.id == Kitchen.ingredient_id)\ .filter(Kitchen.user_id == user_db.id)\ .all() return Response( success=True, data={ingredient.name for ingredient in ingredients}) except Exception as e: return Response(success=False, message=str(e))
def delete_recipe(recipe_name: str, reference_id: str) -> Response: """ Deletes a recipe from the database. """ with get_session() as db: try: if not check_referenced_user_permissions(db, reference_id, False, True, False): return Response( success=False, message='Reference user cannot perform this operation') recipe = db.query(Recipe).filter( Recipe.name == recipe_name.lower()).first() if not recipe: return Response(success=False, message='Recipe not found.') ingredient_ids = { r.ingredient_id for r in db.query(RecipeIngredient).filter( RecipeIngredient.recipe_id == recipe.id).all() } db.query(RecipeIngredient).filter( RecipeIngredient.recipe_id == recipe.id).delete() db.delete(recipe) for ingredient_id in ingredient_ids: remaining_recipes = db.query(RecipeIngredient).filter( RecipeIngredient.ingredient_id == ingredient_id).count() if not remaining_recipes: # Remove orphans db.query(Ingredient).filter( Ingredient.id == ingredient_id).delete() db.commit() return Response(success=True) except Exception as e: return Response(success=False, message=str(e))
def get_all_recipes(ingredients: List[str]) -> Response: """ Finds all the recipes that can be made with anything in the list of ingredients """ with get_session() as db: try: recipes = dict() # Find all the ingredients with the given names found_recipes = db.query(Ingredient, RecipeIngredient, Recipe) \ .join(RecipeIngredient, Ingredient.id == RecipeIngredient.ingredient_id) \ .join(Recipe, RecipeIngredient.recipe_id == Recipe.id) \ .filter(Ingredient.name.in_(ingredients)).all() for recipe in found_recipes: if not recipe[1].recipe_id in recipes: # If we don't have the recipe already, add it to the list with all the ingredients in it ingredients = [ IngredientInfo(name=ing[0].name, quantity=ing[1].quantity, unit=ing[1].unit, required=ing[1].required) for ing in db.query(Ingredient, RecipeIngredient).join( RecipeIngredient, RecipeIngredient.ingredient_id == Ingredient.id).filter(RecipeIngredient.recipe_id == recipe[1].recipe_id).all() ] recipes[recipe[1].recipe_id] = FullRecipe( recipe_info=RecipeInfo( name=recipe[2].name, directions=recipe[2].directions), ingredients=ingredients) return Response(success=True, data=list(recipes.values())) except Exception as e: return Response(success=False, message=str(e))
def get_matching_recipes(ingredients: List[str]) -> Response: """ Finds all the recipes that can be made with the list of ingredients """ with get_session() as db: try: # Find all the ingredients and recipes with those found_recipes = db.query(Ingredient, RecipeIngredient, Recipe) \ .join(RecipeIngredient, Ingredient.id == RecipeIngredient.ingredient_id) \ .join(Recipe, RecipeIngredient.recipe_id == Recipe.id) \ .filter(Ingredient.name.in_(ingredients)) \ .all() # Find all the recipes that match the ingredients and build a dict for each recipe that will hold # a set of all the found ingredients recipe_pieces = defaultdict(list) recipe_info = dict() for recipe in found_recipes: if recipe[2].id not in recipe_info: recipe_info[recipe[2].id] = RecipeInfo( name=recipe[2].name, directions=recipe[2].directions) recipe_pieces[recipe[2].id].append( IngredientInfo(name=recipe[0].name, quantity=recipe[1].quantity, unit=recipe[1].unit, required=recipe[1].required)) # Search for the full recipe for each possible recipe and see if we have all the required ingredients for it full_recipes = list() for recipe_id in recipe_info.keys(): # IngredientInfo objects of the full ingredients for this recipe full_ingredients = [ IngredientInfo(name=r[1].name, quantity=r[0].quantity, unit=r[0].unit, required=r[0].required) for r in db.query(RecipeIngredient, Ingredient).join( Ingredient, Ingredient.id == RecipeIngredient.ingredient_id).filter( RecipeIngredient.recipe_id == recipe_id).all() ] # Only check on ingredients that are required non_required_full_ingredients = len( [r for r in full_ingredients if r.required]) non_required_found_ingredients = len( [r for r in recipe_pieces[recipe_id] if r.required]) if non_required_full_ingredients == non_required_found_ingredients: # If we have everything then pass it full_recipes.append( FullRecipe(recipe_info=recipe_info[recipe_id], ingredients=full_ingredients)) return Response(success=True, data=full_recipes) except Exception as e: return Response(success=False, message=str(e))
async def get_all_ingredients() -> Response: """ Gets a list of all ingredient names """ with get_session() as db: try: ingredients = db.query(Ingredient).all() return Response(success=True, data=[i.name for i in ingredients]) except Exception as e: return Response(success=False, message=str(e))
async def update_password(user_id: str, password: str) -> Response: """ Updates password on a user """ with get_session() as db: try: user_db = db.query(User).filter( User.name == decrypt(user_id)).first() if not user_db: return Response(success=False, message="User not found.") user_db.set_password(password) db.commit() return Response(success=True) except Exception as e: return Response(success=False, message=str(e))
async def sign_in(username: str, password: str) -> Response: """ Tries to log a user in. Returns: Response with encrypted username as data """ with get_session() as db: try: user_db = db.query(User).filter(User.name == username).first() if not user_db: return Response(success=False, message='User not found.') if not user_db.check_password(password): return Response(success=False, message='Password incorrect') return Response(success=True, data=encrypt(str(user_db.id).encode('utf-8'))) except Exception as e: return Response(success=False, message=str(e))
async def add_user(username: str, password: str, can_write: Optional[bool] = False, can_delete: Optional[bool] = False, can_alter_users: Optional[bool] = False) -> Response: """ Adds a new user to the database. """ with get_session() as db: if db.query(User).filter(User.name == username).count(): return Response(success=False, message="User already exists.") new_user = User(name=username) new_user.set_password(password) new_user.set_permissions(can_write, can_delete, can_alter_users) db.add(new_user) db.commit() return Response(success=True)
async def delete_user(username: str, reference_id: str) -> Response: """ Deletes a user from the database """ with get_session() as db: try: if not check_referenced_user_permissions(db, reference_id, False, False, True): return Response( success=False, message='Reference user cannot perform this operation') user_db = db.query(User).filter(User.name == username).first() if not user_db: return Response(success=False, message="User not found.") db.query(Kitchen).filter(Kitchen.user_id == user_db.id).delete( ) # Delete their kitchen first db.delete(user_db) db.commit() return Response(success=True) except Exception as e: return Response(success=False, message=str(e))
async def update_kitchen(user_id: str, ingredients: List[str]): """ Updates a user's kitchen """ with get_session() as db: try: user_db = db.query(User).filter( User.id == decrypt(user_id)).first() if not user_db: return Response(success=False, message="User not found.") # Make sets of what user has in DB and what they have now user_ingredients = { k.ingredient_id for k in db.query(Kitchen).filter( Kitchen.user_id == user_db.id).all() } ingredient_ids = { k.id for k in db.query(Ingredient).filter( Ingredient.name.in_(ingredients)).all() } # Find the set differences to know what to add and delete to_add = ingredient_ids - user_ingredients to_remove = user_ingredients - ingredient_ids for add_id in to_add: db.add(Kitchen(user_id=user_db.id, ingredient_id=add_id)) for delete_id in to_remove: db.query(Kitchen).filter( and_(Kitchen.user_id == user_db.id, Kitchen.ingredient_id == delete_id)).delete() db.commit() return Response(success=True) except Exception as e: return Response(success=False, message=str(e))
async def add_recipe(recipe: NewRecipe, reference_id: str) -> Response: """ Adds a new recipe to the database """ with get_session() as db: try: if not check_referenced_user_permissions(db, reference_id, True, False, False): return Response( success=False, message='Reference user cannot perform this operation') if db.query(Recipe).filter(Recipe.name == recipe.name).first(): # No duplicates return Response(success=False, message="Recipe already exists.") recipe_db = Recipe(name=recipe.name, directions=recipe.directions) db.add(recipe_db) for ingredient in recipe.ingredients: ingredient_db = db.query(Ingredient).filter( Ingredient.name == ingredient.name).first() if not ingredient_db: # New ingredient, need to add it ingredient_db = Ingredient(name=ingredient.name) db.add(ingredient_db) db.flush() link = RecipeIngredient(recipe_id=recipe_db.id, ingredient_id=ingredient_db.id, quantity=ingredient.quantity, unit=ingredient.unit, required=ingredient.required) db.add(link) db.commit() return Response(success=True) except Exception as e: return Response(success=False, message=str(e))
async def update_permissions( username: str, reference_id: str, can_write: Optional[bool] = False, can_delete: Optional[bool] = False, can_alter_users: Optional[bool] = False) -> Response: """ Updates permissions on a user """ with get_session() as db: try: if not check_referenced_user_permissions(db, reference_id, False, False, True): return Response( success=False, message='Reference user cannot perform this operation') user_db = db.query(User).filter(User.name == username).first() if not user_db: return Response(success=False, message="User not found.") user_db.set_permissions(can_write, can_delete, can_alter_users) db.commit() return Response(success=True) except Exception as e: return Response(success=False, message=str(e))
async def get_recipe(name: str) -> Response: """ Gets a recipe from the database using the name """ with get_session() as db: recipes = db.query(Recipe, RecipeIngredient, Ingredient) \ .join(RecipeIngredient, Recipe.id == RecipeIngredient.recipe_id) \ .join(Ingredient, RecipeIngredient.ingredient_id == Ingredient.id) \ .filter(Recipe.name == name.lower()).all() if not recipes: return Response(success=False, message="Recipe not found.") recipe_info = RecipeInfo(name=recipes[0][0].name, directions=recipes[0][0].directions) ingredients_info = [] for recipe in recipes: ingredients_info.append( IngredientInfo(name=recipe[2].name, quantity=recipe[1].quantity, unit=recipe[1].unit, required=recipe[1].required)) return Response(success=True, data=FullRecipe(recipe_info=recipe_info, ingredients=ingredients_info))