CodingOA Software Engineer
Create an in-memory recipe manager with CRUD support, searching, authentication, and version handling. The task is divided into four cumulative levels, with each later level extending the earlier work.
A recipe consists of a unique string recipe_id, a string name, and a list of string ingredients. Implement the functionality progressively through the four levels.
Build a RecipeManager class that can add, fetch, modify, and remove recipes.
class RecipeManager:
def __init__(self):
"""Initialize the recipe management system."""
pass
def add_recipe(self, recipe_id: str, name: str, ingredients: list[str]) -> bool:
"""
Create a new recipe with the given identifier, name, and list of ingredients.
Args:
recipe_id: Unique identifier for the recipe.
name: The name of the recipe.
ingredients: List of ingredient strings.
Returns:
True if the recipe was successfully created,
False if a recipe with that id already exists or
a recipe with that name already exists (case-insensitive comparison).
"""
pass
def get_recipe(self, recipe_id: str) -> dict | None:
"""
Retrieve the recipe with the given identifier.
Args:
recipe_id: The ID of the recipe to retrieve.
Returns:
The recipe data as a dictionary containing id, name, and ingredients,
or None if the recipe doesn't exist.
"""
pass
def update_recipe(self, recipe_id: str, name: str, ingredients: list[str]) -> bool:
"""
Update an existing recipe with new name and ingredients.
Args:
recipe_id: The ID of the recipe to update.
name: The new name for the recipe.
ingredients: The new list of ingredients.
Returns:
True if the recipe was successfully updated,
False if the recipe doesn't exist or the new name
conflicts with another recipe's name (case-insensitive).
"""
pass
def delete_recipe(self, recipe_id: str) -> bool:
"""
Delete the recipe with the given identifier.
Args:
recipe_id: The ID of the recipe to delete.
Returns:
True if the recipe was successfully deleted,
False if the recipe doesn't exist.
"""
pass
Important: Recipe titles must be distinct without regard to letter case. For instance, once "Lemon Chicken" has been stored, adding either "lemon chicken" or "LEMON CHICKEN" must be rejected. When updating a recipe, it may retain its own title, including a casing-only change, but it may not take a title assigned to another recipe.
manager = RecipeManager()
manager.add_recipe("10", "Lemon Chicken", ["chicken", "lemon", "garlic"]) # True
manager.add_recipe("11", "Bean Soup", ["beans", "broth", "carrot"]) # True
manager.add_recipe("12", "LEMON CHICKEN", ["chicken"]) # False (duplicate name)
manager.get_recipe("10") # {"id": "10", "name": "Lemon Chicken", "ingredients": [...]}
manager.get_recipe("404") # None
manager.update_recipe("10", "Garlic Lemon Chicken", ["chicken", "lemon", "garlic"]) # True
manager.update_recipe("10", "bean soup", ["chicken"]) # False (name conflict)
manager.update_recipe("404", "Missing Recipe", []) # False (doesn't exist)
manager.delete_recipe("11") # True
manager.delete_recipe("404") # False
Add recipe lookup capabilities and require the returned recipes to follow a defined sort order.
def search_recipes(self, query: str) -> list[dict]:
"""
Search for recipes where the name contains the query string (case-insensitive).
Args:
query: The search string to match against recipe names.
Returns:
A list of matching recipes sorted by:
1. Number of ingredients in ascending order (fewer first)
2. Recipe ID in ascending order (numerically for numeric strings)
Returns an empty list if no recipes match.
"""
pass
def get_all_recipes(self) -> list[dict]:
"""
Return all recipes in the system.
Returns:
All recipes sorted using the same sorting rules as search_recipes.
"""
pass
manager = RecipeManager()
manager.add_recipe("7", "Tomato Rice", ["rice", "tomato", "onion"])
manager.add_recipe("8", "Greek Salad", ["cucumber", "olive", "feta"])
manager.add_recipe("9", "Quick Toast", ["bread", "butter"])
manager.search_recipes("rice")
# [
# {"id": "7", "name": "Tomato Rice", "ingredients": ["rice", "tomato", "onion"]}
# ]
# "Tomato Rice" matches because its name includes "rice".
# "Quick Toast" does not contain "rice" in its name.
manager.search_recipes("salad")
# [{"id": "8", "name": "Greek Salad", "ingredients": ["cucumber", "olive", "feta"]}]
manager.get_all_recipes()
# Returns all 3 recipes ordered first by ascending ingredient count and then by ID.
Enhance the manager with users. Some actions must verify the user before they are allowed to proceed.
def add_user(self, user_id: str, username: str) -> bool:
"""
Create a new user with the given identifier and username.
Args:
user_id: Unique identifier for the user.
username: The username for the user.
Returns:
True if the user was successfully created,
False if a user with that id already exists.
"""
pass
def edit_recipe(self, user_id: str, recipe_id: str, name: str, ingredients: list[str]) -> bool:
"""
Update an existing recipe, but only if the user_id is valid.
Args:
user_id: The user performing the edit.
recipe_id: The recipe to update.
name: The new name for the recipe.
ingredients: The new list of ingredients.
Returns:
True if the recipe was successfully updated,
False if:
- The user_id doesn't exist (user validation fails).
- The recipe_id doesn't exist.
- The new name conflicts with an existing recipe name (case-insensitive).
"""
pass
Note: The Level 1 update_recipe method may remain available; edit_recipe applies the same update behavior while additionally requiring a valid user.
manager = RecipeManager()
manager.add_recipe("21", "Mushroom Risotto", ["rice", "mushrooms", "stock"])
manager.add_user("chef7", "cook_maria")
manager.edit_recipe("chef7", "21", "Creamy Mushroom Risotto", ["rice", "mushrooms", "stock", "cream"]) # True
manager.edit_recipe("unknown", "21", "Another Title", ["rice"]) # False (user doesn't exist)
manager.edit_recipe("chef7", "404", "Another Title", ["rice"]) # False (recipe doesn't exist)