tp

Developer Guide

Design

UI component

Ui component

API: Ui.java

The UI component makes use of the following classes:

The UiMessage class has dependencies with the following enumeration classes:

FitnessLevel In UiMessage

Gender In UiMessage

In summary, the UI component,

Model Component

Data of the user’s diet is stored in the app’s memory via the model: a FoodList. API: [FoodList.java] (https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/list/FoodList.java)

FoodList provides the following functions:

Additionally, it is not dependent on the other components listed. Instead, it is dependent on a common Food class, which is used by serval components, including the storage, database, and calculator.

Diagram of FoodList

A more detailed class diagram is availble here ![Class diagram of FoodList](/tp/diagrams/FoodList_Overall_class.png) Some attributes and methods of the classes have been truncated for brevity. See the full details in the next subsections on `FoodListManager` and `FoodManager`.

The above functions and the lack of dependency are met through the following means:

FoodListManager

Fascade pattern of `FoodListManager` ![Class diagram of FoodListManager](/tp/diagrams/FoodList_Manager_class.png) `FoodListManager` obscures the operations performed on the individual items of the list of `FoodEntry` objects. These operations are performed using a functional programming paradigm to reduce code repetition, since all operations typically take the form of a list mapping or filtering. It is only within `FoodListManager` that forced type conversions from `FoodEntry` to `DatedFoodEntry` are performed. Hence, only `FoodListManager` needs to be aware of the functions in `FoodEntry` and `DatedFoodEntry`. `FoodListManager` also reduces code repetition in `FoodList` by having generic list operations that can be combined to achieve the desired result (e.g. A method in `FoodList` may use `FoodListManager` for filtering followed by conversion to string. Yet another method in `FoodList` may use `FoodListManager` for its filtering function, scaling, and then conversion to string).

Overall, FoodList fulfils the role of being the app’s Model component by holding consumption data in the app’s memory. It is currently used as a singleton, but is not necessarily limited to such: e.g. a seperate FoodList for favorites/recurrent entries or entries that are flagged as unhealthy/healthy can be made and maintained by Logic.

FoodManager

Fascade pattern of `FoodManager` ![Class diagram of FoodManager](/tp/diagrams/FoodList_FoodManager_class.png) `FoodManger` obscures the existence of the class `OptionalFood` from `FoodEntry` and all classes dependent on `Food` from `FoodEntry`. It uses an obscured `NutrientCalculator` to handle the missing values from the user by providing a guesstimation/calculation of their value. The use of this fascade pattern, however, comes with downsides that will be further discussed in the implementation section.

Common classes

There are a few common classes/packages that can be used multiple components. These are Food, StringFormatter, and MainLogger, located in the seedu.dietbook.food, seedu.dietbook.utils, and seedu.dietbook.logger packages respectively.

Food

Food is a data class containing all the relevant nutritional information on a food: calories, carbohydrates, proteins, fats. Being common to multiple components/classes, it is a means of data transfer between classes while reducing direct coupling.

StringFormatter

StringFormatter allows the formatting of strings in a manner similar to Python’s fstrings: Strings can be formatted using the pattern ${map_key} and a corresponding key to value map.

Logger

MainLogger provides logging support to all classes.

Implementation

Enter user information feature

Implementation

This feature allows users to enter their personal information into the system so that they can be used for tracking diet progress and calorie recommendation calculation. This feature and its associated command words is only used during the initial setup of the application. Any subsequent editing of the user information can be done using the Edit user information feature.

Commands words used:

Main classes and methods used:

Example usage scenario and how the feature work:
Summary: Only one instance of Person is ever instantiated. A default person is instantiated at the start with default attribute values and when the user enters their information for the first time during the set up, all the default values would be updated to the inputted values. Therefore, the command to enter the user information will result in a change in the attribute values and not the creation of a new Person object.

Step 1. When the user launches the application for the first time. A default Person object will be initialised by Manager and the user will be prompted to enter their name.

Object Diagram:
Enter Info Step1

Step 2. The user inputs name Jack command to enter their name into DietBook. The name command calls Manager#setName(Jack), to store the name in Manager first. After which, user will be prompted to enter all other details.

Object Diagram:
Enter Info Step2

Sequence Diagram:
Name sequence diagram

Step 3. The user inputs a command like the following info g/M a/21 h/175 o/85 c/85 t/75 f/2 to enter all other personal information including age, gender, height, fitness level, original, current and target weight. The info command then calls Parse#executeProcessedInfo(info g/M a/21 h/175 o/85 c/85 t/75 f/2, manager) before calling Manager#setPerson(Jack, Gender.MALE, 21, 175, 85, 85, 75, FitnessLevel.LOW) which proceeds to call Person#setAll(Jack, Gender.MALE, 21, 175, 85, 85, 75, Fitness.LOW).

Object Diagram:
Enter Info Step3

Sequence Diagram:
Info sequence diagram

Design considerations:

Aspect: Whether to enter name and other information separately or together

Aspect: Single or multiple usage of feature and command words

Aspect: Whether to use singleton pattern for Person class

Aspect: Changing attribute values in Person object or creating new Person object

Edit user information feature

Implementation

This feature allows users to edit their personal information after it has been entered into the system during the initial set up using the Enter user information feature. This feature was implemented to allow long term users to update their personal information like age, current weight, etc when necessary and also for careless users to edit their personal information if they have entered it wrongly.

Command word used:

Main classes and methods used:

Example usage scenario and how the feature work
Summary: The corresponding existing values in Person class would be updated to the inputted values, even if the new value given is the same as the existing value.

Step 1. Takes for example the user’s name, age, gender, height, fitness level, original, current and target weight are currently Jack, 21, male, 175,You engage in some form of light exercise or have a job that requires some physical activity. ,85, 85 and 75 respectively.

Object Diagram:
Edit Info Step1

Step 2. When the user wishes to edit their age and current weight, they can enter a command like the following editinfo a/22 c/80. The editinfo command would call Parse#executeEditInfo(editinfo a/22 c/80, manager) before Person#setAge(22) and Person#setCurrentWeight(80) is called.

Object Diagram:
Edit Info Step2

Sequence Diagram:
Edit Info sequence diagram

Design considerations:

Aspect: Whether one or more changes to the personal information can be made using a single command or through the use of various commands

Aspect: Use of multiple or single setter method(s)

Head over to the Design Considerations Section in the Enter user information feature for more related design considerations.

View user information feature

Implementation

This feature allows users to view their personal information stored in system. It was implemented to allow users to validate their personal information so that they can edit it if necessary using the Edit user information feature.

Command word used:

Main classes and methods used:

Example usage scenario and how the feature work

Step 1. When the user wishes to view their personal information, they can enteruserinfo. The userinfo command would call Person#toString().

Sequence Diagram:
User Info sequence diagram

[Proposed] Supporting missing fields in a user entry

This feature gives the user some flexibility, allowing them to make an entry without full knowledge of the nutritional information of the food that they are eating. Due to limitations in what can be estimated, there are only two main scenarios for missing fields: a missing total calorie count or some combination of missing nutritional values (carbohydrates, proteins, fat).

Implementation details

Main components involved: Manager: Parses the user input and creates an AddCommand based on the details provided in the user input. It recognises that some combination of the optional inputs are missing and flags them to FoodList when calling the FoodList#addFood(...) method by using OptionalFood.EMPTY_VALUE = -1 as the input value.

FoodList: A food entry is created via the FoodList#addFood(...) method, which has some arguments set to OptionalFood.EMPTY_VALUE = -1. Hence, when a FoodEntry is instantiated, the FoodManager#createFood(String name, int calorie, int carbohydrate, int protein, int fat) recognises the flags in the arguments and creates an OptionalFood instead of Food, for which a reference is kept in FoodEntry. When a method requiring FoodEntry#getFood() is called, FoodManager is called via FoodManager#retrieveFood(Food food) to return a Food object with guesstimated nutritional values. This guesstimation process is done by the NutritionCalculator class.

Usuage Example

There are essentially two phases to the usuage of FoodManager and its associated dependencies: the creation of a OptionalFood that has missing values and the retrieval of a guesstimated Food object when FoodEntry#getFood() needs to be called. For brevity, the focus will be on the processes within FoodList.

Sequence diagram of Food creation

Creation:

  1. FoodList#addFood(int portionSize,String name, int calorie, int carbohydrate, int protein, int fat) (or its variant for backlogs: FoodList#addFoodAtDateTime(...)) is called by the Logic component to add a new entry with missing nutritional inputs. The missing inputs are encapsulated by OptionalFood.EMPTY_VALUE = -1 flags.
  2. FoodList#addFood(...) instantiates a new instance of DatedFoodEntry, passing on the arguments and flags to it instead. DatedFoodEntry uses FoodManager#createFood(String name, int calorie, int carbohydrate, int protein, int fat) to instantiate a Food object. Because there are missing values, FoodManager actually instantiates OptionalFood, a child class of Food instead. A reference to this OptionalFood object is stored in the DatedFoodEntry. The newly instantiated DatedFoodEntry is also stored in the list of FoodEntry objects in FoodList.

Sequence diagram of Food retrieval

Retrieval:

  1. The method FoodEntry#getFood() is only called within functions of FoodListManager. When a method such as FoodList#getPortionedFoods() is called, FoodListManager#convertListToPortionedFoods(List list) is subsequently called.
  2. FoodListManager#convertListToPortionedFoods(List list) calls ListFunctions#applyFunctionToList(List list, Function function), and passes FoodEntry#getFood() within the function argument.
  3. ListFunctions#applyFunctionToList(List list, Function function) executes the function containing FoodEntry#getFood() in its forEach stream.
  4. FoodEntry#getFood() calls the method FoodManager#retrieveFood(Food food), passing its Food object as an argument.
  5. FoodManager#retrieveFood(Food food) checks whether the Food object is an instance of OptionalFood. If it is an OptionalFood, then it is handled differently based on the missing information. Otherwise, the Food object is simply returned.
  6. The missing information in OptionalFood is calculated using NutritionCalculator based on what is missing: if calorie is missing, then NutritionCalcular.calculateCalorieFromNutrients(int carbohydrate, int protein, int fat)is called, otherwise NutritionCalculator.calculateNutrientsFromCalorie(int calorie, int carbohydrate, int protein, int fat) is called instead to calculate the missing nutrient masses.
  7. With the calculated information, a new Food object containing the estimates is created and returned by FoodManager#retrieveFood(Food food), leaving the original reference to the Food object in FoodEntry unmodified in any case.

Future work

Only simple methods of estimating the missing information is used by NutritionCalculator. We can allow the user the weight the split of missing nutritional values differently (it is currently all weighed equally and split by calorie contribution). This ought to be performed by the Calculator component since that is its main role. However, due to the fascade pattern being used in this implementation, the difficulty to add this feature is increased: in order to maintain the status of FoodList being non-dependent on the other components, it is recommended that functions to split the nutrients be passed to FoodManager instead (i.e. use a functional paradigm).

Additionally, storage of the FoodList should support the storage and retrieval of such missing values. Currently, only the estimated versions of the Foods are stored, and information on its status as a Food that had missing values (an OptionalFood) is lost.

Design Considerations

Save/Load Feature

The Save/Load feature is implemented by the saveload package. At the base of the package, there is the Saver and Loader class.

Design

Alt text Note only the Saver and Loader class is flexible. They can be adapted to new situations without modifying the code. The FoodSaveLoadManager and PersonSaveLoadManager are written specifically for this version. They will have to be modified/replaced for future versions.

Saver class

Stores data in a internal table with length and height specified. Handles the storage of its data by writing to a text file.

Constructor

Specifies the length and height of the internal Saver table

Main Methods

Loader class

Loads data from a text file and stores it in a internal table just like the saver

Constructor

static method Loader.load(folder name , file name) : creates a Loader object with a table storing the data found in the text file

Main Methods

FoodPortionDateSaveLoadManager class

Built on top of Saver and Loader class to implement save/load functionality for list of food items the user has input into the dietbook. Contains a instance of both Saver and Loader. It has its own folder to work with, the user only has to specify the file name. To save the contents of a FoodList, call FoodPortionDateSaveLoadManager#saveFoodList(FoodList foodlist, String fileName) To load a file, call FoodPortionDateSaveLoadManager#load() first to load the contents of the file into the FoodPortionDateSaveLoadManager and then call FoodPortionDateSaveLoadManager#saveFoodList() to return the FoodList with those contents.

Main Methods

PersonSaveLoadManager class

Built on top of Saver and Loader class to implement save/load functionality for user information Same as FoodPortionDateSaveLoadManager, it has its own folder to work with, the user only has to specify the file name Unlike the FoodPortionDateSaveLoadManager, it stores the data inside itself and can be updated.

Main Methods

UML diaghram

FoodPortionDateSaveLoadManager#save()

Alt text

FoodPortionDateSaveLoadManager#load()

Alt text similiar diaghrams for PersonSaveLoadManager

DataBase feature

DataBase stores a list of food items that can be found in NUS and can be accessed by user The data is organized into a Canteen contains a number of Stores each of which contains a list of food items It also offers a number of filtering and searching methods. The data base resource is a text file which can be manually updated.

DataBase class

Stores a List of Canteen objects. Each Canteen object stores a List of Store object, each Store object contains a List of Food objects. Currently has a number of filtering and searching methods. These methods can easily be modified and new ones implemented depending on the needs of the application.

Main Methods

Manually editing the text resource

As of this version, there is no dev mode for an administrator or a user to add new items to the data base. This can be done manually by directly editing the data.txt resource.

Format of data.txt resource

The DataBase#init() method reads the data.txt file line by line &%START and &%STOP the DataBase#init() method will read anything between these two indicators initially the data base is in the _*canteen*_ state, the next line it reads will be the canteen name when it moves from the canteen name line to the next line, it is in the _*store*_ state, again the next line it reads will be the store name, when it moves from the store name line to the next line, now it is in the _*food*_ state. In this state, input a line in the format {food name}|{calorie}|{carbohydrate}|{protein}|{fats}, this will add a food item. Any number of food lines can be written and the database will be in the _*food*_ state. To go back up to the store state, write a line of &%UP. If a store name is given in the next line, the database will again be in the _*food*_ state. To go back up to the canteen state write 2 consecutive lines of &%UP.

Example

|data.txt line|explanation|before state|after state| |—————————————————–|———————————————————–|—————–|—————| |&%START| starts the reading | not reading | canteen | | canteen1 | set the current canteen name to canteen1 | canteen | store | | store 1 | sets the current store name to store1 | store | food | | food1 | adds a food with the data of food1 | food | food | | food2 | adds a food with the data of food2 | food | food | | &%UP | goes up to store | food | store | | store 2 | sets the current store name to store2 | store | food | | food 3 | adds a food with the data of food3 | food | food | | &%UP | goes up to store | food | store | | &%UP | goes up to canteen | store | canteen | |&%STOP | stops the reading | canteen | not reading |

UML diagram

DataBase#init()

Alt text

DataBase#search()

Alt_text

Product scope

Target user profile

NUS students living on campus who would like to track their diet.

Value proposition

DietBook is designed to track the food and different kinds of nutritional intake of the user. It can also provide the user with a daily calorie recommendation based on their personal information. As the application mainly targets _NUS students staying on campus, it has a database prepopulated with food items commonly found around NUS. This allows for such food items to be easily added to the list of food items consumed for tracking.

User Stories

Version As a … I want to … So that I can …
v1.0 v2.0 person with an ideal weight in mind input my target weight and relevant information get daily calorie intake recommendations
v1.0 careless or long term user be able to view my personal information make changes when necessary
v2.0 careless user be able to edit my personal information make changes if I input the wrong information
v2.0 potential long term user be able to edit my personal information make changes to information like age, weight and fitness level as it can changes over time
v2.0 user that wants to track weight changes be able to view the weight I started off with, my current weight and the weight I desire take note of my progress

Non-Functional Requirements

  1. Should work on any mainstream OS as long as Java 11 is installed in the system.
  2. A user who can type fast and prefer typing over mouse/voice commands should be able to accomplish the same tasks faster when using Command Line Interface, the interface implemented by that DietBook, as compared to other modes of inputs.

Glossary

Instructions for manual testing

Given below are instructions to test the app manually.

Entering user information

  1. Entering name or nickname into the application
    1. Test case: name Tom and Jerry
      Expected: The name Tom and Jerry will be stored in the system and a message prompting the user to enter all other details will be displayed.
    2. Test case: name *1*
      Expected: The name *1* will be stored in the system and a message prompting the user to enter all other details will be displayed.
    3. Test case: name Ja/ck
      Expected: No name will not be stored in the system and an error message will be displayed.
    4. Test case: nameJack
      Expected: Similar to previous.
    5. Test case: name
      Expected: Similar to previous.
    6. Test case: Name Jack
      Expected: Similar to previous.
  2. Entering other personal information into the application (all seven pieces of information is required e.g. age, height, etc)
    1. Test case: info g/M a/21 h/175 o/85 c/85 t/75 f/2
      Expected: All information is stored in the system and a message stating that initialising is complete will be displayed.
    2. Test case: info o/85 a/21 f/2 h/175 g/M c/85 t/75 (where parameters can be entered in any order)
      Expected: Similar to previous.
    3. Test case: info g/Ma/21h/175 o/85 c/85 t/75 f/2 (where there are no spaces between the different parameters)
      Expected: Similar to previous.
    4. Test case: Info o/85 a/21 F/2 h/175 g/M c/85 t/75 (where any letter of the command word or parameter tags are capitalised)
      Expected: The information is not stored in the system and an error message will be displayed.
    5. Test case: infog/M a/21 h/175 o/85 c/85 t/75 f/2
      Expected: Similar to previous.
    6. Test case: info g/M a/21 h/175 o/85 c/85 t/75 f/2 z/9 (where extra parameters,parameter tags or words are present)
      Expected: Similar to previous.
    7. Test case: info g//F a/21 h/175 o/85 c/85 t/75 f/2 (where / or any other special characters is used inappropriately)
      Expected: Similar to previous.
    8. Test case: info g/f a/160 h/500 o/900 c/85 t/75.6 f/7(where age, height, weights, gender and fitness level are not within the valid ranges or not valid - refer to User Guide for more information)
      Expected: Similar to previous.
    9. Test case: info a/21 (where any of the required parameters are missing)
      Expected: Similar to previous.

Editing user information

  1. Editing user information store in the application (one or more change(s) is/are allowed)
    1. Test case: editinfo n/Jane
      Expected: User’s personal information is displayed and the name of the user is updated to Jane.
    2. Test case: editinfo a/22 c/80 (where variable number of information is changed)
      Excepted: User’s personal information is displayed and the age of the user is updated to 22 while the current weight is updated to 80.
    3. Test case: editinfo
      Expected: User’s personal information is not updated and an error message is displayed.

Refer to Entering User Information Section under Instructions for manual testing for similar test cases that can be used for testing.

Viewing user information

  1. Viewing user personal information stored in the application
    1. Test case: userinfo
      Expected: User’s personal information is displayed.
    2. Test case: userinfo userinfo
      Expected: User’s personal information will not be displayed. Error message will be shown to user.
    3. Test case: Userinfo
      Expected: Similar to previous.