Overview
Souschef is a desktop smart cooking sidekick, offering personalised guidance every step of the way. From recipe
recommendations just for you, to meal planning and inventory management, SousChef has everything you need to improve
life in the kitchen.
Our users interact via CLI and GUI created with JavaFX. Written in Java, experience what many are enjoying.
Summary of contributions
-
Code contributed: [Functional code]
-
Major enhancement: added the ability to manage and manipulate recipes
-
Multi-line Add Recipe Functionality
-
What it does: Allow users to add a recipe in progression using multi-line command .
-
Justification: The multi-line add recipe command improves the product by providing a intuitive yet convenient way to create a recipe entry as opposite to forcing users to input a lengthy recipe entry containing a number of instructions into a single command.
-
Highlights: This enhancement requires the handling of a incomplete recipe data when the add is being executed in progression by the user. The challenges faced include morphing address book into a nested recipe data structure that stores instructions which stores ingredients. Secondly, the analysis and design to create a in-progress recipe instance that allows the appending of more recipe details when the user contributes to the incomplete recipe. Thirdly, the retention and accessing of this incomplete recipe in order to appending the details and ultimately create an actual recipe instance to be stored in the model.
-
-
All-field Recipe Search
-
What it does: Retrieve recipes with name, difficulty, cooktime or ingredients that matches the keywords by the user.
-
Justification: This feature improves the application because users can now narrow down their wanted recipes based other parameters such as difficulty, cooktime and ingredients rather than restricting their search to just the name of the recipe.
-
Highlights: Ingredients are being stored in each instructions of a recipe, as such to support the search of ingredients, the design of a recipe data to provide ease of access to all ingredients is required.
-
-
Other Associated Functionality implemented: Edit Recipe
-
-
Project management:
-
Setting up of GitHub, Travis, AppVeyor, coveralls, codacy
-
Managed releases
v1.1
-v1.4
(4 releases) on GitHub -
Maintaining of Github issue tracker (allocate assignee, labels) and milestones
-
-
General/Team Enhancements:
-
Documentation:
-
Community:
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Features
In Sous Chef, there are 2 types of command: universal commands and context-unique commands.
The universal commands can be used throughout the application and perform application-wide action. You can identify a
universal command from its "-" convention followed by a keyword (e.g. -exit).
The context-unique commands on the other hand are only valid for that context. (e.g. Recipes Commands (Only applicable in recipe context) can only be
used in -recipe context). These commands contains only a action key word followed by the required parameters, if any. (e.g. select INDEX)
Command Format Convention
-
Words in
UPPER_CASE
are the parameters to be supplied by the user e.g. inadd n/NAME c/COOKTIME d/DIFFICULTY [t/TAG]
,NAME
,COOKTIME
andDIFFICULTY
andTAG
are parameters are to be replaced:add n/Fried Chicken d/2 c/20M t/Fastfood
. -
Items in square brackets are optional (i.e. can be omitted).
-
Items with
…
after them can be used multiple times e.g.[t/TAG]…
can be used 1 or more:t/Japanese
,t/Halal t/Seafood
etc. With[]
it can also be omitted totally too.
Universal Commands
View help: -help
To view the help menu containing all the universal commands.
Format:
-help
View history: -history
To view previous commands entered.
Format:
-history
Switch to recipe context: -recipe
Switch to recipe context and display recipes.
See Recipes Commands (Only applicable in recipe context) to view commands for recipe context.
Format:
-recipe
Switch to ingredient manager context: -ingredient
Switch to ingredient manager context and display ingredient manager, which helps stock
tracking of ingredient that the user currently have.
See [Ingredient Manager Commands (Only applicable in ingredient context)] to view commands for ingredient manager
context.
Format:
-ingredient
Switch to favourites context: -favourite
Switch to favourites context and display favourites list, which keeps a list of all
the users' favourite recipes.
See [Favourite Recipe Commands (Only applicable in favourites context)] to view commands for favourites context.
Format:
-favourite
Switch to ingredient-recipe query context: -cross
Switch to ingredient-recipe query context and where you can sort, filter the recipe list by name of ingredients
included and get information of needed amounts of ingredients.
See [Ingredient-Recipe Query Commands (Only applicable in cross context)] to view commands for ingredient-recipe
query context.
Format:
-cross
Switch to meal planner context: -mealplanner
Switch to meal planner context and display the planned meals for breakfast, lunch and dinner for previously planned days.
See [Meal Planner Commands (Only applicable in meal planner context)] to view commands for meal planner context.
Format:
-mealplanner
Switch to health plan context: -healthplan
Switch to health plan context and display health plan set by the user and the days added into the plan which is tied
to meal plans.
See [Health Plan Commands (Only applicable in health plan context)] to view commands for health plan context.
Format:
-healthplan
Exit application: -exit
Format:
-exit
Recipes Commands (Only applicable in recipe context)
List recipes: list
Show all recipes.
Format:
list
Add a recipe: add
cont
end
Add new recipe.
Format:
add n/NAME c/COOKTIME d/DIFFICULTY [t/TAG]…
cont i/INSTRUCTION… [c/COOKTIME]
cont…
end
INSTRUCTION:
TEXT… [#INGREDIENT_NAME AMOUNT SERVING_UNIT]…
Full set of commands |
Examples:
-
add n/Chicken Rice d/2 c/45M
cont i/Clean and cut #chicken 1.2 kg.
cont i/Put the chicken in #boiled water 900 ml for 10 mins. c/10M
cont i/Remove the chicken and put #soy sauce 100 ml.
cont i/Cook for another 20 mins. c/20M
end
Edit a recipe: edit
Edit new recipe.
Format:
edit INDEX [n/NAME] [c/COOKTIME] [d/DIFFICULTY] [t/TAG]…
or
edit INDEX s/STEP i/INSTRUCTION [c/COOKTIME]
INSTRUCTION:
TEXT… [#INGREDIENT_NAME AMOUNT SERVING_UNIT]…
Examples:
-
edit 1 c/20M t/Asian t/Staple
-
edit 1 s/2 i/Pour #water 300 ml into the mix.
Display recipe details: select
View a recipe and its details from the list.
Format:
select INDEX
Search recipes: find
Show recipes related to the keyword(s).
Keywords include but not limited to cuisines (Indian, Japanese),
dietary types (Vegetarian, Halal),
ingredients (egg, broccoli),
preparation time (30M, 1H40M)
and difficulty (1, 2, …, 5).
Format:
find KEYWORD…
Examples:
-
find rice asian 3
-
find korean kimchi staple
Delete recipe: delete
Delete a recipe and its details from the list.
Format:
delete INDEX
Activate cook-mode [coming in V2.0]: cook
A cook mode that provides step-by-step guidance to aid real-time cooking.
Format:
cook INDEX
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Model component
API : ModelSet.java
,
Model.java
The ModelSet
,
-
stores a
UserPref
object that represents the user’s preferences. -
stores the Sous Chef application data.
-
Contains multiple
Model
each in-charge of a feature’s data.
-
-
shares a single instance of VersionedAppContent to ensure single version of truth.
-
does not depend on any of the other three components.
The Model
,
-
each represents a feature-unique data.
-
ensures data abstraction for each feature.
-
-
is reusable as model is now generic.
-
exposes an unmodifiable
ObservableList<T extends UniqueType>
that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
Recipe Management
Multi-line Recipe Adding
Current Implementation
As the adding of recipes deal with lengthy input parameters from the basic details such as name to a list of instructions, single-line add command proves to be inefficient and often providing an inconvenient user experience. To allow flexibility, adding of recipes have been implemented as a multi-line command fitting into this CLI application.
Multi-line add function consists of 3 main commands: add
cont
end
. The add
command requires user input of the
recipe basic information such as Name
, Difficulty
, CookTime
and the optional Tag
. The cont
command requires
user input of the recipe Instruction
. Each cont
command allows only 1 Instruction
. Again this is to
break down the lengthy input parameter of Instruction
. Lastly, with the end
command the Recipe
is being
recognised and is added to the Model
.
The following is the sequence diagram of the add
command:
Once user’s input is being parsed, a RecipeBuilder
is created instead of a Recipe
instance. This RecipeBuilder
will be stored in History
when the command is being executed.
Repeating use of |
The following is the sequence diagram of the cont
command:
Upon parsing the user’s input that contains Instruction
, a Instruction
instance will be created. This
Instruction
is to be passed to the command for execution. The execution process once again make use of the History
to append the Instruction
into RecipeBuilder
.
The following is the sequence diagram of the end
command:
Upon receiving the end
command, Recipe
will be built from the RecipeBuilder
. The parsing process checks to
ensure that the Recipe
built contains at least a single Instruction
to prevent adding of Recipe
without
Instruction
. With a valid Recipe
, a AddCommand
will be created and the RecipeBuilder
in History
will be
cleared (i.e. set to null). This AddCommand
when executed will add the Recipe
into Model
.
Why it is implemented that way:
Due to the nature of a multi-line add command where the Recipe
is incomplete together with the inability to execute
the add
function immediately, 2 mechanisms namely, a builder and a historical storage, have to be put
in place. The builder works effectively in managing the data of a partial Recipe
while allowing continuous
modification. The historical storage, History
in this context, ensures that lossless partial
Recipe
data. Moreover, since History
interaction with commands has already been well establish in Logic
component and a partial Recipe
is indeed a representation the historical commands, storing in History
becomes a dominant choice.
Alternatives:
-
Adding of incomplete recipe to the model and edit that recipe with the subsequent commands.
-
Pros - Make use of existing add and edit command without the need of additional implementation.
-
Cons - Model contains incomplete recipe that violates the validity of recipe data. This might be exploited and in the process the application can be filled with incomplete recipes.
-
All-field Recipe Search
The all-field recipe search matches keywords with details of Recipe
.
Each keyword goes through the matching process against the Name
, Tag
, Difficulty
, CookTime
and even
IngredientPortion
from each Instruction
of every recipe.
Such search feature provides a complex querying, rather than a superficial search on merely recipes' name.
Current Implementation
The following recipe class diagram shows the data structure of Recipe
to support searching:
Other than the basic Recipe
details accessible via the getters()
, nested attribute such as IngredientPortion
can post difficulty for the search function since Recipe
and IngredientPortion
is not associated initially. To
allow direct access to all IngredientPortion
of that recipe, a tabulateIngredients()
method is added. This method
stores all
IngredientPortion
in the HashMap
of ingredients
attribute by searching through all Instruction
. As Recipe
is immutable, the generating of the ingredients
is
done in the constructor of Recipe
(i.e. tabulateIngredients()
is called in the constructor). This allow the
direct accessing all attributes required for the search to completed.
To implement this feature, recipe Model
is implemented with FilteredList
. This list takes in a predicate to
filter away recipes that does not meet the requirement.
The desired search keywords, once parsed as List
of String
will be turned to Stream
. Each keyword is checked
against a recipe Name
for any matching word, Difficult
for exact matching value, Tag
for any matching
word, and Ingredient
for any matching IngredientDefinition
. All keywords must return true (i.e. matching at least
one attribute of the recipe) before the recipe is deemed fit to be kept in the FilteredList
.
The following code snippet shows how the predict determines each recipe:
public boolean test(Recipe recipe) { if (keywords.size() == 0) { return false; } return (keywords.stream() .allMatch(keyword -> { return StringUtil.containsWordIgnoreCase(recipe.getName().fullName, keyword) || recipe.getCookTime().toString().toLowerCase().equalsIgnoreCase(keyword.toLowerCase()) || recipe.getDifficulty().toString().equals(keyword) || recipe.getTags().stream() .anyMatch(tag -> StringUtil.containsWordIgnoreCase(tag.tagName, keyword)) || recipe.getIngredients().keySet().stream().anyMatch(ingredientDefinition -> StringUtil .containsWordIgnoreCase(ingredientDefinition.toString(), keyword)); })); }
The following sequence diagram shows how the keywords are being parsed to feed into the predicate:
Why it is implemented that way:
Due to the existing data structure implemented by model, to access the attributes within a recipe, the system needs to use its getter and perform matching with its keywords. Hence, a filter via predicate testing is deemed fit.
Alternatives:
-
Tag saving recipes that uses it.
-
Pros - Search done more effectively as the system only need to display all recipes stored in a tag
-
Cons - Bi-directional association, increase difficulty in maintaining data integrity
-
-
Saving IngredientPortion directly in recipe instead of individual instruction.
-
Pros - Remove the need to search through the initial every instruction to gather the ingredients for every new instance of recipe. Speed up the recipe add and read process.
-
Cons - Since ingredients are aggregated from instruction, by allowing IngredientPortion to be a direct attribute of recipe imply a possibility of mismatch between the ingredients in the instruction and the ingredients in the recipe. (e.g. User entering ingredients that are not used by any instruction)
-