PROJECT: Cooking Papa


Overview

Cooking Papa is a desktop cookbook application used to manage recipes and ingredients for cooking. The user interacts with it using a CLI (Command Line Input), and it has a GUI (Graphical User Interface) created with JavaFX. It is written in Java, and has about 15 kLoC.

Summary of contributions

  • Major enhancement: implemented and developed Cooking Papa’s GUI

    • What it does: developed Cooking Papa’s GUI to better reflect its casual usage and more cheerful nature, rather than the base AB3’s dark theme.

    • Justification: This feature improves the product significantly because the color scheme is more in line with Cooking Papa’s use, with the bright yet muted colors complementing the fun font styles, giving off a casual vibe, and invites users to use Cooking Papa.

    • Highlights: This enhancement required a lot of understanding of the interactions between various JavaFX objects, as well as the base CSS stylesheets provided by AB3, and integrating them together, which took many hours of trial-and-error, as well as Googling.

      While constraints meant that Cooking Papa would be tested from the command line, a well-designed GUI was imperative to convey the application’s casual nature. Coordinating various colors proved to be a challenge, and the color scheme went through three rounds of reiterations, before settling on the current one. Moving away from the rigid sharp edges of AB3 was also a challenge, and required various UI elements and color choices to "soften" the overall aesthetics of Cooking Papa.

    • Credits: JavaFX 11 as well as SceneBuilder helped immensely in the development of the GUI.

  • Major enhancement: integrated cookbook view recipe command into the GUI

    • Justification: This feature improves the product significantly because it integrated the GUI seamlessly with the command.

    • Highlights: This enhancement arose due to the lack of connection between the existing cookbook view recipe command, which shows users the details of a recipe, and the GUI. It was initially displayed as a plain text result in the result display box of the GUI, and this causes command results (errors, success messages, usage messages), to appear in the same UI component as the recipe details, which meant that the component had two uses.

      The idea to make cookbook view recipe a button on the GUI that could be toggled surfaced to improve user experience. Developing a button that could toggle the details shown was trivial, however, limiting the command to just a button on the GUI meant that it would not be testable on the command line. Therefore, the challenge was to connect the GUI to the Logic component, and typing cookbook view recipe on the command line (or in the command box of the GUI) would trigger the button. The result was a GUI much more interactive and functional, but it was still testable on the command line.

    • Credits: JavaFX 11 as well as SceneBuilder helped immensely in the development of the cookbook view recipe button.

  • Minor enhancement: added an command which allowed users to export the ingredients in their cart to a PDF file, which allows them bring along with them to the supermarkets. This feature solved the constraint of the application being desktop-only, and with it, Cooking Papa can still help users when they are away from their computers.

    Credits: Apache PDFbox and user mkl on StackOverflow for a great example on using PDFbox.

  • Minor enhancement: regularly reviewed source code, and carried out testing and bug fixing. This includes the standardisation of coding style and messages shown to the user ( #122, #228, #242).

  • Code contributed: [Source code]

  • Other contributions:

    • Project management:

      • Managed releases v1.2.1, v1.3, and v1.4 (3 releases) out of 5 releases(v1.1, v1.2, v.1.2.1, v1.3, v1 .4) on GitHub

      • Managed features development under Project on GitHub (features)

    • Enhancements to existing features:

      • Updated the GUI color scheme and designed GUI layout: #137

      • Wrote additional tests for existing features to increase coverage from 56% to 77%: #131

      • Integrated button with cookbook view recipe command: #208

      • Implemented core features: #115, #139, #254

    • Documentation:

      • Removed instances of AB3 in user guide: #13

      • Added UML diagrams and document implementation of core features: #121

      • Reviewed user and developer guide and enforced standardisation: #228

    • Community:

    • Tools:

      • Integrated a third party library (Apache PDFbox) to the project (#212)

      • Integrated new Github plugins to the team repo: TravisCI, AppVeyor, Coveralls, Codacy

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.

Help Command (by Teo Jun Xiong)

This command shows a popup window with a URL to Cooking Papa’s user guide (what you are reading now). This is for access should you need some help executing a certain action in Cooking Papa.

There are 2 ways a user can access the help window:

  • Type help.

  • Click on the Help button on the top left corner of Cooking Papa, and then click on the Help button in the dropdown menu. You can then click the Copy URL button which can then be pasted into a web browser to access Cooking Papa’s user guide.

CookbookHelpExample
Figure 1. Cooking Papa 'Help' button to access the Help window
CookbookHelpExample2
Figure 2. Cooking Papa Help window and 'Copy URL' button

Exit Command (by Teo Jun Xiong)

There are 3 ways a user can exit Cooking Papa:

  • Type exit.

  • Click on the x button on the top right corner of the Cooking Papa.

  • Click on the File button on the top left corner of Cooking Papa, and then click on the Exit button in the dropdown menu.

ExitExample
Figure 3. Cooking Papa 'X' button to exit the application.

Calendar commands [v2.0] (by Teo Jun Xiong)

Calendar commands are commands that allow you to set date-related commands, such as reminders and recipe of the day

Add a recipe to cook on a certain day

Reminds the user to cook a certain recipe on a certain day.

  • Format: calendar set DD-MM-YYYY cook recipe INDEX

  • Example:

Command Result

calendar set 10-10-2020 cook recipe 1

Adds recipe 1 to your calendar for cooking on 10th October 2020.

View recipes to cook on a certain day

User can view the recipes they were supposed to cook on a certain day. The date input can be replaced with 'today' for the current date, or 'tomorrow' for the next day.

  • Format: calendar view recipes DD-MM-YYYY

  • Example:

Command Result

calendar view recipes 10-10-2020

Shows the recipes scheduled for cooking on 10th October 2020.

calendar view recipes today

Shows the recipes scheduled for cooking on the current day.

calendar view recipes tomorrow

Shows the recipes scheduled for cooking on the day after the current day.

Frequently Asked Questions (by Teo Jun Xiong)

Q: How do I transfer my data to another Computer?
A: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous CookingPapa folder.

Q: Where can I install the latest version of CookingPapa?
A: You can find the latest release here. Please download the latest version of CookingPapa.jar to enjoy the most udpated features.

Q: Do I need to be connected to the internet to access this application?
A: All information is stored locally in your own computer. No internet connection is required.

Q: Something is not working as expected. Who can I contact?
A: You may report your bugs here here. Bug reports are highly appreciated!

Q: Is this application free?
A: Yes! This application is open-source under the MIT license. You may feel free to modify, contribute and share this application with the community!

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.

Cooking Papa UI component

UiClassDiagram
Figure 4. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of several UI parts: CommandBox, ResultDisplay, CookbookPanel, RecipeCard, InventoryPanel, CartPanel, IngredientCard, StatusBarFooter. All these, including the MainWindow, inherit from the abstract UiPart class.

The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml

The UI component,

  • Executes user commands using the Logic component.

  • Listens for changes to Model data so that the UI can be updated with the modified data. === Implementation of a core feature: cookbook add recipe

Below is a step by step sequence of what happens when a user enters this command:

  1. The user enters a recipe adding command using the command line input cookbook add recipe n/NAME d/DESCRIPTION [i/INGREDIENT]…​ [q/QUANTITY]…​ [s/STEP_DESCRIPTION]…​ [t/TAG]…​.

  2. CookingPapaParser parses the user input and checks if it is valid. If it is invalid, i.e. an unknown command category, a ParseException will be thrown. If the input is valid, with the command category cookbook, a new CookbookCommandParser is created.

  3. CookbookCommandParser then parses add recipe n/NAME d/DESCRIPTION [i/INGREDIENT]…​ [q/QUANTITY]…​ [s/STEP_DESCRIPTION]…​ [t/TAG]…​. If it is invalid, i.e. an unknown command word, a ParseException will be thrown. If the input is valid, with the command word add, a new CookbookAddCommandParser is created.

  4. CookbookAddCommandParser parses recipe n/NAME d/DESCRIPTION [i/INGREDIENT]…​ [q/QUANTITY]…​ [s/STEP_DESCRIPTION]…​ [t/TAG]…​ and checks if n/NAME and d/DESCRIPTION are provided. If either are not provided, then a ParseException will be thrown.

    It then parses the input into the following fields: recipe name, recipe description, ingredients, steps, and tags.

    Note that the ingredient names and ingredient quantities provided must be the same, or a ParseException will be thrown:

    if (names.size() != quantities.size()) {
        throw new ParseException(
            String.format(MESSAGE_DIFFERENT_NUMBER_OF_INPUTS, names.size(), quantities.size()));
    }

  5. These fields are then passed as parameters for Recipe, which is then passed as the parameter for CookbookAddCommand and returned to LogicManager.

  6. LogicManager calls CookbookAddCommand#execute() which checks if the cookbook already contains the same recipe with the same name, description, ingredient names, ingredient quantities, and tags using Model#hasCookbookRecipe().

    If there is a duplicate, a CommandException is thrown, stating that the user is attempting to add a duplicate recipe:

    if (model.hasCookbookRecipe(toAdd)) {
        throw new CommandException(MESSAGE_DUPLICATE_RECIPE);
    }
  7. If CommandException is not thrown, Model#addCookbookRecipe will be executed, with the recipe to be added as a parameter.

  8. Model#addCookbookRecipe() then executes Cookbook#addRecipe(), which adds the recipe to the cookbook, and the FilteredList<Recipe> representing the recipes in the cookbook are updated with Model#updateFilteredCookbookRecipeList():

    updateFilteredCookbookRecipeList(PREDICATE_SHOW_ALL_RECIPES)

    where PREDICATE_SHOW_ALL_RECIPES = unused → true.

  9. A CommandResult with the text to display to the user is then returned to LogicManager, which can passed back to MainWindow, which displays it to the user on the CLI and GUI: resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()). The text displayed will notify the user on whether their addition was successful.

UML Diagrams for cookbook add recipe

The following sequence diagram shows how the recipe adding function works (full command [cookbook add recipe n/Recipe name d/Recipe description i/Ingredient 1 q/1 piece i/Ingredient 2 q/20 ml s/Do step 1 s/Do step 2 t/This t/Is t/A t/Tag] omitted from diagram for brevity):

CookbookAddRecipeSequenceDiagram
Figure 5. Sequence diagram for CookbookAddCommand

The following activity diagram shows a possible flow of events for a user using this feature:

CookbookAddActivityDiagram
Figure 6. Activity diagram for CookbookAddCommand

Design Considerations for cookbook add recipe

Aspect 1: How to parse optional parameters
Table 1. Design considerations for parsing optional parameters

Design A (current choice): Parse each category separately

Design B: Parse all the categories together

Description

Each category (ingredient name, ingredient quantity, step description, tag) are parsed separately and returned as List. If the returned List is empty, then it means that that field was not provided in the input, and will be set to an empty List in the recipe e.g., no ingredients were provided in the command.

Each category will be parsed together in one function in CookbookAddCommandParser

Pros

  • Provides more flexibility for the user and does not make it mandatory to input fields that they may not necessarily have.

  • No need to deal with null values, can simply check if list is empty.

  • Straightforward

  • No need to create and call multiple methods from other classes

Cons

  • More methods have to be executed which may increase time and NPath complexity.

  • Debugging and tracing becomes more confusing due to the method being defined in the lowest level of abstraction.

  • Have to deal with null values and include null checks (ifPresent() etc.)

  • Method will be very long and decreases readability

Design A was chosen because it was more user-friendly, and removed the restriction of having to include ingredients, steps, and tags at the stage of recipe creation, some of which the user may not have at the moment, i.e. experimenting with different ingredients. Additionally, design A allowed us to be more modular while coding.

Aspect 2: Result to show user
Table 2. Design considerations for results to show users

Design A (current choice): Show a short result on the success of the command

Design B: Show all the details back to the user

Description

Show a message to a usage which notifies them that the command was successful in adding the recipe to the cookbook.

Shows a message similar to design choice A, and also show all the details of the added recipe.

Pros

  • Short and succinct message, tells the user what they need to know

  • User interface is cleaner and more intuitive, and does not overload users with unnecessary information

  • Easier to implement

Cons

  • Requires the graphical user interface to be able to toggle and show recipes, without the need for a command, implemented here:

CookbookAddRecipe3
  • Overloads the user with unnecessary information

  • Requires result display to take up more space than required, to reduce the need for users to scroll down the result display.

Design A was chosen because it did not reuse the same component for multiple uses. Additionally, it allows us to reduce the size of result display, as most of the time, it displays only a short message displaying the success of a command.

Implementation of a core feature: cart export

Below is a step by step sequence of what happens when a user enters this command:

  1. The user enters a cart export command using the command line input cart export.

  2. CookingPapaParser parses the user input and checks if it is valid. If it is invalid, i.e. an unknown command category, a ParseException will be thrown. If the input is valid, with the command category cart, a new CartCommandParser is created.

  3. CartCommandParser then parses export. If it is invalid, i.e. an unknown command word, a ParseException will be thrown. If the input is valid, with the command category export, a new CookbookExportCommandParser is created.

  4. CartExportCommandParser parses the user input and checks if the argument passed to it is an empty String, as the command takes in no extra parameters.

    Note that if the String is not empty, a ParseException will be thrown:

    if (userInput.isEmpty()) {
        return new CartExportCommand();
    } else {
        throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CartExportCommand.MESSAGE_USAGE));
    }

    This means that cart export ingredient will not work.

  5. CartExportCommandParser then returns a CartExportCommand to LogicManager.

  6. LogicManager calls CartExportCommand#execute() calls the static method of PdfExporter, PdfExporter#exportCart(), which takes in the ObservableList<Ingredient> stored in Cart

  7. Step 4 is executed within a try-catch block. If a previously generated pdf (saved as cart.pdf by default) is opened in another program, or there is an issue writing to the PDF file, a CommandResult with an error message will returned to LogicManager (skipping step 7 and 8):

    try {
        PdfExporter.exportCart(model.getCart().getIngredientList());
    } catch (IOException e) {
        return new CommandResult(MESSAGE_FILE_NOT_FOUND);
    }
  8. The ingredients in the Cart is passed to the static method PdfExporter#exportCart(), which then makes use of the library, PDFbox, to parse the data.

  9. Within PdfExporter, PdfExporter#getTextFromCart parse the data and splits them manually, in order to wrap the text (this has to be done due to the inadequacy of PDFbox). The method returns a List<String>, where each string represents a new line on the PDF file.

  10. Subsequently, PdfExporter checks if the number of String s in the list in step 7 is greater than the number of lines a single page of the PDF can accomodate. If it is, it adds a new page, and adds lines to the PDF until the limit is hit. This repeats until all the lines are added to the PDF.

  11. A CommandResult with the text to display to the user will be returned to LogicManager. The CommandResult is then passed back to MainWindow, which displays it to the user on the CLI and GUI: resultDisplay .setFeedbackToUser(commandResult.getFeedbackToUser()). The text displayed will notify the user on whether their addition was successful.

UML Diagrams for cart export

The following sequence diagram shows how the function of exporting ingredients in the cart to a PDF file works:

CartExportCommandSequenceDiagram
Figure 7. Sequence diagram for CartExportCommand

The following activity diagram shows a possible flow of events for a user using this command: .Activity diagram for CartExportCommand image::CartExportActivityDiagram.png[]

Implementation of a core feature: cookbook view recipe

Below is a step by step sequence of what happens when a user enters this command:

  1. The user enters a view recipe command using the command line input cookbook view recipe INDEX.

  2. CookingPapaParser parses the user input and checks if it is valid. If it is invalid, i.e. an unknown command category, a ParseException will be thrown. If the input is valid, with the command category cookbook, a new CookbookCommandParser is created.

  3. CookbookCommandParser then parses view recipe INDEX. If it is invalid, i.e. an unknown command word, a ParseException will be thrown. If the input is valid, with the command category view, a new CookbookViewCommandParser is created.

  4. CookbookViewCommandParser then parses recipe INDEX and checks if the String contains "recipe", and an index. If either are absent, a ParseException will be thrown. If the String is valid, a CookbookView is created.

  5. CookbookViewCommandParser then returns a CookbookViewCommand to LogicManager.

  6. LogicManager calls CookbookViewCommand#execute() which checks if the provided Index is within the bounds of the FilteredCookbookRecipeList() in Cookbook, i.e. index.getZeroBased() >= list.size(). If it is not, a CommandException will be thrown. If it is valid, a CommandResult is created with a boolean value true.

  7. A CommandResult with the text to display to the user will be returned to LogicManager. The CommandResult is then passed back to MainWindow. The boolean value stated in step 6 determines whether a successfully parsed command is a cookbook view recipe INDEX command.

  8. MainWindow#handleViewRecipe is then executed, which creates a new CookbookPanel with the same set of data, calling CookbookPanel#handleViewRecipe, which creates new RecipeCard s for Cookbook, and for the RecipeCard that has an index equal to the index processed from the user’s input, it will create a RecipeCard that toggles open the recipe details. More on how the RecipeCard manages this will be discussed in the following section on how clicking on a button in the GUI has the same effect as the cookbook view recipe INDEX command.

  9. Lastly, the user then is shown a CookbookPanel with the selected recipe toggled open, which displays the full details of that recipe:

    CookbookViewExample2

Below is a step by step sequence of what happens when a user clicks the button on the GUI:

  1. When the button is pressed, the onAction method, RecipeCard#handleViewButtonAction() is executed. A RecipeCard has a variable isFullyDisplayed, which indicates whether it is displaying an overview of the recipe, or fully displaying details of the recipe.

  2. If isFullyDisplayed is false, i.e. the RecipeCard is currently displaying an overview of the recipe, RecipeCard#displayRecipeComplete() is executed, which replaces the text displayed by the FXML object, Label, with the full details of the recipe.

  3. If isFullyDisplayed is true, i.e. the RecipeCard is currently fully displaying the details of the recipe, RecipeCard#displayRecipeOverview() is executed, which replaces the text displayed by the FXML object, Label with the overview of the recipe.

  4. Both methods executed in step 3 and 4 will flip the boolean value of isFullyDisplayed, and this means that the next time the button for the same recipe is clicked, it toggles back. For example, if a recipe with its overview shown has its view button clicked, it will show the full details of the recipe. If the button is clicked again, it toggles, and shows the overview of the recipe.

    This feature is not reflected with cookbook view recipe INDEX when it is entered again in the command line, because the function of the command is to view a recipe, not to "un-view" it.

UML Sequence Diagrams for cookbook view recipe

The following sequence diagram shows how the recipe viewing function interacts between the classes in Logic:

CookbookViewSequenceDiagram

The following sequence diagram shows how the recipe viewing function interacts between the classes in Ui:

CookbookViewSequenceDiagram2