PROJECT: CaloFit

Overview

My team was tasked with improving the basic Address Book 3 (AB3) application for our Software Engineering project. We chose to morph the existing AB3 application to a calorie recording system called CaloFit which also stores a native food database with accurate calorie information. This enhanced application enables individuals to track their calorie intake everyday, manage their diet and get monthly reports on their calorie consumption.

CaloFit is a Command Line Interface (CLI) desktop application that assist users in keeping track of their calories. It was designed with the user experience in mind as we set to keep the number of commands to a minimum. The Graphic User Interface (GUI) created using JavaFX displays useful information clearly to the user. To interact with CaloFit, the user just enters a command at the top in the command box and press the Enter key.

Shown below in Figure 1 is how the project looks like:

CalofitUImAC

Figure 1: The GUI for CaloFit.

My role was to design and write the codes for the meal tracking which are the add, edit and delete commands. The following sections illustrate my contributions to the project as well as relevant documentations I have added to the user and developer guide using with regards the these enhancements.

Note the following symbols and formatting used in this document:

This symbol indicates important information.

add - A grey highlight(called a mark-up) indicates that this is a command that can be inputted into the command line and executed by the application.

Summary of contributions

This section shows a summary of my coding, documentation, and other helpful contributions to the team project

  • Major enhancement: added the ability to track consumed meals and calories.

    • What it does: Allows the user to add, edit and delete their meal entries from the meal section.

    • Justification: This achieves the main goal of CaloFit which is to allow for the users to track what they consume daily as well as their calorie intake.

    • Highlights: Adding and deleting multiple meals at one go. Ability to remove specific tags of a meal instead of all tags.

  • Code contributed: [Functional code]

  • Other contributions:

    • Project management:

      • Managed release v1.4 (1 release) on GitHub.

      • Assigned issues to team mates relating to their features.

    • Documentation:

      • Made the User Guide more reader friendly and easy to understand.

      • Added images to improve readability and reduce confusion.

    • Community:

Contributions to the User Guide

Given below are sections I contributed to the User Guide as we had to update the original AddressBook-Level3 User Guide with instructions for the enhancements that we had added. The following is an excerpt from our CaloFit User Guide, showing additions that I have made for the Meal Tracking feature. They showcase my ability to write documentation targeting end-users.

Meal Tracking

Adding a meal: add

Adds a meal that the user consumed.
There are 2 formats that can be used:

Format 1: add n/MEAL_NAME [c/CALORIES] [t/TAGS]
Inputting calories of the meal is optional.
If the meal is a dish stored in CaloFit, the calories will be taken from CaloFit. If not, a default value of 700 will be added for you,
Tags are completely optional and there is no default tag. You could also have more than 1 tag.

Format 2: add NUMBER_IN_SUGGESTED_MEAL_LIST [NUMBER_IN_SUGGESTED_MEAL_LIST] …​
NUMBER_IN_SUGGESTED_MEAL_LIST is the number that corresponds to the meal in suggested list on the right side of the application. You can also add multiple meals at one go using the indexes. This can be done by separating the numbers with a space. e.g. add 1 2 3

MEAL_NAME can only be a maximum of 30 characters to ensure it displays correctly for you.

Examples usage:
It is lunchtime and you are about to go have some Mushroom Soup. You would like to add the meal into CaloFit to record it down. What do you do?

If format 1 is your preferred choice, you could do the following:

  • Type add n/Mushroom Soup into the command box and press the enter key.

AddCommandInitialFormat1
  • If calofit has the information for the meal (Mushroom Soup in this case), it will grab the relevant information and update the meal log accordingly. The meal that is added will be shown in the result window.

AddCommandAddedFormat1
  • If CaloFit does not have the information, and the information is not provided, the default calorie information will be 700 calories

If format 2 is your prefered choice, you could do the following:

  • Type find mushroom soup into the command box and press the enter key.

AddCommandFind1Command
  • If the meal exist in our meal database, it will show up under the suggested meals header.

AddCommandSearchResult
  • Afterwards, enter the command add 1 to the command box and press the enter key.

AddCommandAdd1
  • The meal "Mushroom Soup" will then be added to the consumed meals.

AddCommandAdd1Success

Commands below are some other example commands:

  • add n/Spaghetti c/480 t/tasty - Adds a meal named Spaghetti of 480 calories with the tag "tasty".

  • add n/Chicken Rice c/500 - Adds a meal named Chicken Rice of 500 calories.

  • add n/Mushroom Soup - Adds a meal named Mushroom Soup of 700 calories by default.

  • add 1 - Adds the first meal shown under the suggested meal section.

  • add 1 2 3 - Adds the first and second meal shown under the suggested meal section.

Editing previous meal entry : edit

Edits a meal that the user previously consumed today.
Format: edit MEAL-NUMBER [n/MEAL-NAME] [c/CALORIES] [t/TAGS] [tr/TAGS-TO-REMOVE]
The input of at least 1 field (either NAME, CALORIES or TAGS) is required. A mix of the different fields is possible as well with some restrictions stated below.

You cannot add tags and clear all tags at the same time. (i.e. You cannot execute a command that contains both "t/" and "t/[TAGS]".)

Example usages:
While rushing to type in the meals, you misspelled "Mushroom Soup" as "Mushroom Soop" and added 2000 calories instead of the correct 200 calories. What do you do?

  • Type "edit 1 n/Mushroom Soup c/200" into the command box and press the enter key.

EditCommandInitial
  • The name of the meal will be edited to "Mushroom Soup" and the calories will be edited to the correct amount of 200 in the calorie bar.

EditCommandSuccess

Commands below are some other example commands:

  • edit 1 n/Wanton Noodle c/1000 - Edits the meal at index 1 to name "Wanton Noodle" with calorie 1000.

  • edit 2 n/Chicken Rice - Edits the meal at index 2 to name "Chicken Rice" without changing anything else.

  • edit 3 c/500 - Edits the meal at index 3 to have a calorie count of 500 without changing the other fields.

  • edit 4 t/tasty - Adds the tag "tasty" to the existing list of tags for meal at index 4.

  • edit 5 tr/tasty - Removes the tag "tasty" if it exists in the list of tags for meal at index 5.

  • edit 6 t/ - Clears all tags for meal at index 6.

Deleting previous meal entry : delete

Deletes a meal that the user previously consumed today.
The meal will be removed from the consumed meal section
and the calorie tracking bar.

Format: delete MEAL_NUMBER [MEAL_NUMBER] …​

Example usage:
You thought you were going to have Mushroom Soup for lunch when you were queuing. So you decided to add Mushroom Soup to the meal log using the add command. However, while queuing, your boss called you saying that there is an emergency and wants you back in the office immediately. You abandon the queue and go back to the office. Since you did not consume the meal, you would want to remove it from the meal log. What do you do?

  • Type delete 1 into the command bar and press the enter key.

DeleteCommandDelete1
  • When the meal is successfully deleted, a message will appear in the result box and the meal will be removed.

DeleteCommandDelete1Success
"1" can be changed to any number in the consumed meal section. However, in this example, Mushroom Soup is located at index 1.

Commands below are some other example commands:

  • delete 1 - deletes the first meal in the consumed meal section.

  • delete 1 2 3 - bulk deleting of meals 1, 2 and 3 in the consumed meal section.

Contributions to the Developer Guide

Given below are sections I contributed to the Developer Guide for the Meal Tracking feature. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

Meal Tracking Feature

Add feature

Implementation
Addition though flags (e.g add n/Chicken c/200 t/dry)

The add feature is implemented through the AddCommand class that extends the abstract Command class. It interacts with other objects through the Model interface to manipulate the meal log and dish database to add meals.

The addition of meals is done through Model#addMeal() which would add the meal from the meal log list.

The add feature uses the Model to check if the dish is already in the database. If the dish is already in the database, the meal will be added to the meal log only through the Model#addMeal() method. If the dish the user wants to add is not in the database, then the dish will be added to both the meal log and the dish database through the Model#addMeal() and Model#addDish() methods respectively.

There are a few cases to take note:

  • If the dish name is in the dish database:

    • The user inserts the calorie information that is the same as the one in the dish database

      • The dish will be added to the meal log only.

    • The user inserts the calorie information different from the calorie in the dish database

      • This will create a new dish all together as there are differences in the calorie

      • The new dish will be added to both the meal log as well as the dish database

    • The user does not insert the calorie information

      • The program will look through the dish database and get the calorie information from that dish.

  • If the dish name is not in the dish database

    • The user inserts the calorie information

      • A new dish is created with that name and calorie information

      • The dish will then be added to both the dish database as well as the meal log.

    • The user does not insert the calorie information

      • The program sets the calorie information to 700 by default.

      • The dish will then be added to both the dish database as well as the meal log

The following sequence diagram shows how the add operation works when calorie information is included:

AddSequenceDiagram

To detect whether the calorie tag is used in the user input, we use an UNKNOWN_CALORIE in the Calorie class. This will trigger either search for the dish in the dish database or create a new dish and input it into the dish database.

The input by the user and the dish in the dish database is considered equal only if both the name and the calorie information are the same.

Addition of tags are supported. However, they are not crucial to the implementation of the add function as tags are not considered when deciding equality of dishes.

Addition though indexes in suggested/find meal list (e.g add 1 2 3)

The add feature also supports the addition of meals through indexes in the suggested meal list. This is done to improve the user experience as they do not have to always enter the long commands.

This feature is also implemented through the AddCommand class that extends the abstract Command class.

The add command in this case takes in a list of numbers pass to it through the AddCommandParser. The checking of valid indexes is done by the AddCommandParser. Once the list is passed to the AddCommand, it calls Model#getFilteredDishlist() which returns the filtered dish list. The add command will then loop through the list of indexes and get the respective dishes from the filtered dish list.

We cannot add the meal immediately to the meal log as the filtered dish list would change when the calorie count changes. Hence the meals are first added to separate a toBeAddedMealList and once all the indexes are added to that list, MealLog#addListOfMeals(toBeAddedMealList) is called to add all the meals in to the meal log at once.

Design Consideration
Aspect: What is considered the same meal
  • Alternative 1 (Current choice): Compare name and calorie only

    • Pros: More precise compared to just comparing names.

    • Cons: Complications when adding meals.

  • Alternative 2: Compare name only

    • Pros: Easy comparison. Reduces complications when adding meals.

    • Cons: High collision.

  • Alternative 3 Compare name, calories and tags

    • Pros: Very precise comparison.

    • Cons: May cause the dish database to get very large just because the tag is different

  • Explanation of choice: As a calorie counter, the calories is an important part of the meal. Hence we need to consider it when comparing meals. Certain meals with the same name may not have the same amount of calories. Hence we decided to go with alternative 1.

Aspect: Data structure when storing in meals in meal log
  • Alternative 1 (Current choice): Use array list to store meals in meal log

    • Pros: Able to delete by index.

    • Cons: Interacting with the meal log will be slow.

  • Alternative 2: Use a hash map to store the meals in meal log

    • Pros: Able quickly retrieve information from the meal log.

    • Cons: Unable to delete through index. Also unable to store duplicates of the same meal.

  • Explanation of choice: The user may consume the same meal within the day. If we use a hash map, we will not be able to store the duplicate meals easily. Hash map does not preserve order. Hence, showing on the meal list section would be difficult as well. A user may not have a large amount of meals daily. Hence the array list would not be that large. Therefore, despite its limitation, an array list is still used.

Edit feature

Implementation

The edit feature is implemented through the EditCommand class that extends the abstract Command class. It interacts with other objects through the Model interface to manipulate the meal log and edit meals.

The edit command allows the user to edit any fields in their previous meal entries of the same day with a single command. The edit command takes in a Index which specifies the location of the meal in the meal log and a EditDishDescriptor which contains the information the user wants to edit the meal with.

The edit of meal is done through a 2 step process. The meal is first retrieved from the meal log. Afterwards, the meal will be edited with the information in the EditDishDescriptor. Following that, the edited meal is inserted using the Model#setMeal() command. This swaps out the old meal for the new edited meal.

Below is the activity diagram that summarises the scenario when "edit 1 n/Mushroom Soup c/200 tr/shitake" is called by the user.

EditActivityDiagram
Design Consideration
Aspect: How editing meal in meal log will change the dish database.
  • Alternative 1 (Current choice): No change to dish database

    • Pros: Simple and easy for user to understand

    • Cons: Less accurate data in the dish database

  • Alternative 2: Edit the corresponding meal that was previously added when adding the meal

    • Pros: Better more accurate data in the dish database

    • Cons: Raises many complications that may get very confusing for the user

  • Explaination of choice: Interacting with the dish database will cause multiple different edge cases that need to be considered. This may cause it to be confusing for the user. For example, if they user intends to enters "Mushroom Soup" and get all the information from the dish database. However, the user spelt it wrongly and entered "Mushroom Soop" instead. When editing, do we search the dish database for the correct information of "Mushroom Soup" or do we just change the name and leave the other information untouched? Hence it is decided to allow the user to edit the fields and have no interaction with the dish database for a better user experience.

Delete feature

Implementation

The delete feature is implemented through the DeleteCommand class that extends the abstract Command class. It interacts with other objects through the Model interface to manipulate the meal log and remove meals.

The delete command allows for removal of multiple dishes with a single command. The delete command takes in a listOfIndex passed to it by the DeleteCommandParser. The checking of valid integers is done by the DeleteCommandParser. Once the listOfIndex is passed to the DeleteCommand, it sorts the list from largest index to smallest index using the Collections.sort(listOfIndex, Comparator.reverseOrder()). The DeleteCommand then loops through the sorted list and checks if the index is within the size of the meal log. If the index is valid, DeleteCommand will remove the respective meal from the meal log.

The removal of meals is done through the Model#removeMeal(meal) which would remove the meal from the meal log list.

The following sequence diagram shows how the delete operation works when index 1 is deleted:

DeleteSequenceDiagram

Below is the activity diagram that summarises the scenario when "delete 1" is called by the user.

DeleteActivityDiagram
Design Consideration
Aspect: How is the meal removed
  • Alternative 1 (Current Choice): Removed after the listOfIndex is sorted.

    • Pros: Prevent the reordering of the meal log causing the larger indexes to correspond to a different meal or out of bounds. (e.g delete 1 2 for a meal log with 2 meals. If we delete 1 first, the meal log will change to having 1 meal. The meal previously at index 2 is now at index 1. When doing delete 2, the meal at index 2 is now out of bounds as the meal log only has 1 meal.)

    • Cons: The command may take some time when handling large amounts of input. This is due to the sorting required.

  • Alternative 2: Loop through the indexes in the DeleteCommandParser and create a new DeleteCommand for every index.

    • Pros: Simple morphing of previous delete method.

    • Cons: Have to change other parts of the model breaking abstraction.

  • Explanation of Choice: The command is for the deletion of meals for that day. A person on average consumes 3 - 4 meals a day. Hence on average, the most meals to be deleted is 4. Thus the sorting time would not be too significant.