PROJECT: Souschef


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 ingredients

    • What it does: Allows the user to track the stock of ingredients, view recipe according to ingredient and check shop amount.

    • Justification: This is a practical feature which would enhance user experience of cooking preparation.

    • Highlights: Rather than mere stock tracking, this feature contains functions providing cognitive shortcut for users. Also, it enables proper integration between information related to ingredient and recipe.

  • Major enhancement: restructured entire logic component

    • Justification: This was necessary to enable integration across multiple features, while maintaining efficiency by distinguishing shared processes and making use of general classes.

    • Highlights: This enables both modularization and efficient integration of features.

    • PR#64

  • Other contributions:

    • General/Team Enhancements:

      • Implementation of universal commands (Pull requests #66)

      • Fix UI for desired view (Pull requests #151)

    • Community:

      • PRs reviewed (mainly related to logic component): #72, #77

      • Reported bugs and suggestions for other teams in the class (examples: 1, 2, 3, 4, 5)

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.

Ingredient Manager Commands (Only applicable in ingredient context)

Add an ingredient: add

Adds an ingredient to the ingredient manager.
Format:
add NAME AMOUNT SERVING_UNIT DATE

  • For compound word, ‘_’ is used to separate words.

  • Serving units are pre-defined in the serving unit dictionary. Those undefined in the dictionary are not available.

  • Currently available serving units are gram(g), kilogram(kg), pinch, piece, whole, clove, cm3, ml, l, tablespoon, teaspoon and cup.

  • Amounts are converted with common serving unit(e.g. gram).

  • Format for date should be MONTH-DAY-YEAR.

  • Date is meant to be the date of input, but it is up to user to tweak its usage. For example, it can be used to show expiry date.

  • It is not allowed to add ingredients with same name and same date. To add in more amount, edit function should be used instead.

Sort and list all ingredients: list

Shows a list of ingredients in ingredient manager. Ingredients are sorted by date, so that the ingredients with earlier date are placed high in order.
Format:
list

Edit ingredient info: edit

Edit an existing ingredient in ingredient manager.
Format:
edit INDEX FIELD_NAME NEW_VALUE [MORE FIELD_NAME NEW_VALUE]…​

  • Field name is either name, amount, or date.

  • Restrictions for respective field’s input are same as in add command(3.3.1.).

Find ingredient: find

Find ingredients in ingredient manager whose name contains any of the given keywords.
Format:
find KEYWORD [MORE_KEYWORDS]…​

  • Only the name is searched.

  • Match by full words.

Delete ingredient: delete

Delete ingredient in ingredient manager according to its index in the last shown list.
Format:
delete INDEX

Clear all ingredients: clear

Clears all ingredients in ingredient manager.
Format:
clear

Ingredient-Recipe Query Commands (Only applicable in cross context)

View Recipes based on Ingredients view

By default, the page show the recipe list that is same with the recipe list shown in recipe context at the point of initiation of the application. User can filter or sort the recipes based on ingredients contained in recipes. Also, needed amounts of ingredients are calculated by considering the number of serving units and ingredients stored in ingredient manager, so that user can take a look by applying select command later.
Format:
view NUMBER_OF_SERVINGS [include [inventory] KEYWORD [MORE_KEYWORDS]…​] [prioritize [inventory] KEYWORD [MORE_KEYWORDS]…​]

  • Number of servings can be a floating point value.

  • Names of ingredients are used as keywords. If the keyword is a compound word, '_' is used to separate between the words. (For example, spring_onion)

  • "include" keyword filters the list, only leaving the recipes that includes all of following ingredients in the list.

  • "prioritize" keyword sorts the recipes based on number of the following ingredients contained. Recipes containing the most number of following ingredients would be placed high in order.

  • "include" part and "prioritize" part is optional. It is possible that only one of either part is provided. (For example, "view 1 prioritize onion") If both are not provided(For example, "view 4"), recipe list remains the same and only the number of servings are considered for calculation of needed amounts ingredients.

  • "inventory" keyword provides all the ingredients in Ingredient Manager as parameter to the keyword it follows.

  • It is not allowed to provide same ingredient as a parameter for both "include" and "prioritize" part. This is also applicable for "inventory" parameter becasues it is also a set of ingredients.

View needed amounts of ingredients for a Recipe select

For a recipe in the list shown as a result of above command(3.4.1.), user can view amounts of respective ingredient in the recipe that needs to be additionally prepared. It reflects number of serving units and refers to Ingredient Manager to get the amount of ingredients the user currently have.
Format:
select INDEX

  • Amounts of necessary ingredients are calculated based on the information in Ingredient Manager.
    e.g. If positive, (Number of Servings × Total Amount of an ingredient for a Recipe - Amount of an ingredient stored in ingredient manager). Otherwise, 0.

List all recipes and undo calculation: list

Shows a list of recipes. It restores the default state of the list, undoing the calculation resulting from 'view recipes based on ingredients' command(3.4.1.).
Format:
list

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.

Logic component

LogicClassDiagram
Figure 1. Structure of the Logic Component

API : Logic.java

  1. Logic uses the AppContentParser class to parse the user command.

  2. AppContentParser passes necessary model to each feature parser such as IngredientParser according to the context.

  3. This results in a Command object which is executed by the LogicManager.

  4. The command execution can affect the Model (e.g. adding an ingredient) and/or raise events.

  5. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("add onion 100 gram 10-23-2018") API call.

LogicSequenceDiagram
Figure 2. Interactions Inside the Logic Component for the adding an ingredient

Ingredient Manager

Total management of ingredients that user currently have

Ingredient Manager is an address book of ingredients, with some basic functions similar to that of a normal address book such as add, edit, delete and list. Additionally, Ingredient Manager has some useful functions specific for management of ingredients.
Firstly, serving units can be converted to a common serving unit with reference to the serving unit dictionary. Conversion to a common serving unit is necessary to enable arithmetic calculation between amounts. Also, it is easier to compare between ingredients when the amounts are shown in common serving units. Secondly, when you apply the list command, ingredients are sorted by date. Since ingredients have to be consumed in a limited period, it keeps user be reminded of old ingredients.

Current implementation of serving units dictionary

Ingredient Manager uses gram for the common serving unit. Serving unit dictionary is implemented with static HashMap in IngredientServingUnit class, which contains names of a serving unit as a key and a IngredientServingUnitDefinition object as a value. IngredientServingUnitDefinition has three fields, which are a serving unit to be converted, a common serving unit, and a constant to be multiplied for conversion. When conversion is to be done, you find the value in the HashMap with an input serving unit as a key. Then, you multiply the amount of ingredient with the constant and replace the current unit with a common unit. This process is wrapped in a method called convertToCommonUnit() in ingredient models which contains amount field.

Alternative Implementation of serving units dictionary

In the current implementation, a serving unit to be converted exists both as a key and in a value of the HashMap. This duplication can be removed by transforming the form of the value in the HashMap from a definition object to an array containing the other two fields of the object. However, creating a separate class to wrap three fields is more desired for abstraction. In the current version of the application, definition object is only used for conversion to a common serving unit. But in later version, conversion can be done the other way around for the purpose of display. Also, in a more complete version of the dictionary, definition should be expanded to cover information related to validity of combination between specific serving unit and ingredient, and its conversion value. Also, using HashMap brings huge convenience in further implementation of functions which include searching of value in the dictionary, cancelling out the cost of duplication.

View recipes according to ingredients, and check shop amount

This command works on separate context called cross, because the result of command manipulates list of recipes, not ingredients. The command filters(include) and sorts(prioritize) recipes based on ingredients given in the command, and makes use of information stored in Ingredient Manager in two ways. Firstly, you can provide all ingredients in Ingredient Manager as parameter for filtering or sorting. This might be useful when a user wants to find recipe that maximizes the use of existing ingredients. Secondly, for the recipes that are on the list as a result of filtering and sorting, additional amount of ingredients that need to be prepared for a recipe is calculated. Because ingredients in Ingredient Manager are considered as stock that the user currently have, their amounts are subtracted from the desired amount, calculated considering the number of servings. After cooking, she can go back to Ingredient Manager and edit amount of ingredients or delete ingredients used for cooking.

Current implementation of cross recipe model

Although it looks similar, models used for recipe context and cross context are different. crossRecipe class extends Recipe class with one more additional field, a Map named neededIngredients. This has IngredientDefinition object as a key, and IngredientPortion object as a value. It is to store information of needed amount of an ingredient to amount field of IngredientPortion object after calculation is done.

Current implementation of ingredient models
IngredientModelDiagram

There are three ingredient related models, namely IngredientDefinition, IngredientPortion, and Ingredient. IngredientDefinition is a wrapper class for ingredient name. IngredientPortion inherits IngredientDefinition and additionally includes amount field. Ingredient again inherits IngredientPortion, and it is the most complete version of ingredient with date information, used by Ingredient Manager. Reason for hierarchical implementation is that there exists different needs for ingredient related fields across features or functions. For example, ingredient in recipe uses IngredientPortion, and ingredient in Ingredient Manager uses Ingredient. IngredientDefinition is used as a key of a HashMap in implementing 'view recipes according to ingredients' function. It can be used to construct ingredient dictionary or to expand unit dictionary in the future version of the application.