Implement a multi-level digital recipe manager
RampSoftware Engineer · Online AssessmentLast updated
Tests skills in data modeling and stateful class implementation, covering CRUD operations, case-insensitive string handling, recipe ID generation, list management, search and sort logic, and basic user/edit permissions.
Implement a RecipeManager class for a digital recipe manager. The task is progressive (Levels 1–4). You can assume a single-process in-memory implementation. Correctness matters; efficiency is not required. Unless otherwise stated, all string comparisons for recipe names and ingredient matching are case-insensitive.
Data model
A recipe has:
recipe_id: formatted as"recipe" + N, whereNis a sequential integer starting from 1 in creation order.name: unique across all recipes (case-insensitive).ingredients: list of strings (keep original order).steps: list of strings (keep original order).
When returning a recipe, represent it as:
[name, ingredients_as_string, steps_as_string]ingredients_as_stringis the ingredients joined by","(comma separator), and similarly forsteps_as_string.
If a method returns a list of recipe IDs, they are strings like "recipe1", "recipe2", etc.
Level 1: Basic CRUD (no users)
Implement:
add_recipe(self, name: str, ingredients: list[str], steps: list[str]) -> str | None
- Add a new recipe and return its
recipe_id. - If a recipe with the same name already exists (case-insensitive), return
None.
get_recipe(self, recipe_id: str) -> list[str]
- Return
[name, ingredients_as_string, steps_as_string]for the recipe. - If the recipe does not exist, return an empty array
[]. - Ingredients must be returned in the same order they were added.
update_recipe(self, recipe_id: str, name: str, ingredients: list[str], steps: list[str]) -> bool
- Update the recipe fields.
- Return
Trueif successful. - Return
Falseif the recipe does not exist, or if the new name conflicts with another existing recipe name (case-insensitive).
delete_recipe(self, recipe_id: str) -> bool
- Delete the recipe.
- Return
Trueif it existed and was deleted; otherwiseFalse.
Level 2: Searching and sorting
Implement:
search_recipes_by_ingredient(self, ingredient: str) -> list[str]
- Return recipe IDs for recipes that contain the specified ingredient (case-insensitive match).
- Sort results by:
- number of ingredients ascending
recipe_idascending (tie-breaker)
list_recipes(self, sort_by: str) -> list[str]
- Return all recipe IDs sorted by
sort_by, wheresort_byis either: "name": lexicographic ascending by recipe name; tie-breakerrecipe_idascending"ingredient_count": ascending by number of ingredients; tie-breakerrecipe_idascending- If
sort_byis invalid, default to sorting by"name".
Level 3: Users and edits
Introduce users.
add_user(self, user_id: str) -> bool
- Add a new user.
- Return
Trueif added;Falseif the user already exists.
edit_recipe(self, user_id: str, recipe_id: str, new_name: str, new_ingredients: list[str], new_steps: list[str]) -> bool
- Any existing user may edit any recipe.
- Enforce recipe-name uniqueness (case-insensitive).
- Return
Trueon success. - Return
Falseif the user does not exist, the recipe does not exist, or the new name conflicts with another recipe name.
Level 4: Version history and rollback
Add version control for recipes.
Definitions:
- Recipes created by
add_recipehave no version history until the first successfuledit_recipeorupdate_recipe. - Every successful
edit_recipeorupdate_recipecreates a new version entry. - Version entries are numbered starting at 1 and increase by 1 per entry.
- Each version string format:
<version>:<name>:<ingredients_as_string>:<steps_as_string>:<last_edited_by>- For
edit_recipe,<last_edited_by>is theuser_id. - For
update_recipe,<last_edited_by>is the literal string"system".
Implement:
version_recipe(self, recipe_id: str) -> list[str]
- Return all versions for the recipe, sorted by version number ascending.
- Return
[]if the recipe does not exist or if it was never edited/updated (i.e., no version history).
rollback_recipe(self, recipe_id: str, version: int) -> bool
- Roll back the current recipe state (name/ingredients/steps) to match the specified historical
version. - Rolling back creates a new version entry appended to the history (history is immutable), with editor set to the literal string
"rollback". - Return
Trueif successful. - Return
Falseif the recipe does not exist, the requested version does not exist, or if rolling back would cause a name conflict with another existing recipe (case-insensitive).
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.