From cc953ed48cec68211a8a578e168abe029cddd849 Mon Sep 17 00:00:00 2001 From: tikimonarch <65004283+tikimonarch@users.noreply.github.com> Date: Thu, 1 Oct 2020 14:32:48 +0800 Subject: [PATCH 001/374] Add files via upload --- test.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 test.txt diff --git a/test.txt b/test.txt new file mode 100644 index 0000000000..b6d9dc8657 --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +dietary book From c12a94f85a0636231a3e56c521a6e8c32c064995 Mon Sep 17 00:00:00 2001 From: HengFuYuen <60005925+HengFuYuen@users.noreply.github.com> Date: Thu, 1 Oct 2020 15:27:33 +0800 Subject: [PATCH 002/374] Set theme jekyll-theme-minimal --- docs/_config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/_config.yml diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..2f7efbeab5 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file From 37d97a8e5ffa0417934fab7f4b9aaac22d2c2d02 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:36:09 +0800 Subject: [PATCH 003/374] test --- docs/AboutUs.md | 3 +++ src/test/java/seedu/duke/DukeTest.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..191e857bbe 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,4 +1,7 @@ # About us +we are an idiosyncratic and narcissistic batch of self-righteous +elitist who defends presumably morally questionable positions. + Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java index 2dda5fd651..fc8da31b5b 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/duke/DukeTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; +import org.testng.annotations.Test; class DukeTest { @Test From e8e50dbe37a6556c8b4402e1619fd8e2fedfcf78 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 1 Oct 2020 17:00:51 +0800 Subject: [PATCH 004/374] Update AboutUs.md with name and GitHub profile --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..2dccd95aba 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -6,4 +6,4 @@ Display | Name | Github Profile | Portfolio ![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Heng Fu Yuen | [Github](https://github.com/HengFuYuen) | [Portfolio](docs/team/johndoe.md) From d8d740c0f14838e20e7cb5f1a2562d28c72a12bd Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Thu, 1 Oct 2020 20:45:53 +0800 Subject: [PATCH 005/374] heap --- docs/AboutUs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 191e857bbe..0a84aca01d 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,6 +1,7 @@ # About us we are an idiosyncratic and narcissistic batch of self-righteous elitist who defends presumably morally questionable positions. +Also we are cheap! Display | Name | Github Profile | Portfolio From 40d517287e771da5588ce12e5dd21c5f4ce67467 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 4 Oct 2020 10:08:40 +0800 Subject: [PATCH 006/374] hahahfdh --- src/test/java/seedu/duke/DukeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java index fc8da31b5b..2dda5fd651 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/duke/DukeTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; class DukeTest { @Test From 7b2dfceb65a5b1074c0314e9880ca1803de4df47 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:00:04 +0800 Subject: [PATCH 007/374] data base --- src/main/java/seedu/duke/database/DataBase.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/seedu/duke/database/DataBase.java diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java new file mode 100644 index 0000000000..53d9a125fb --- /dev/null +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -0,0 +1,5 @@ +package seedu.duke.database; + +public class DataBase { + +} From e3111ed5d4b611c0794eb1178d2625fa7e3bdc61 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:13:32 +0800 Subject: [PATCH 008/374] create food class --- src/main/java/seedu/duke/food/Food.java | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/seedu/duke/food/Food.java diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java new file mode 100644 index 0000000000..9c5b3f47e0 --- /dev/null +++ b/src/main/java/seedu/duke/food/Food.java @@ -0,0 +1,32 @@ +package seedu.duke.food; + +public class Food { + private final String name; + private final int calorie; + private final int carbohydrate; + private final int protein; + public Food(String name, int calorie, int carbohydrate, int protein){ + this.name = name; + this.calorie = calorie; + this.carbohydrate = carbohydrate; + this.protein = protein; + } + + public String getName() { + return name; + } + + public int getCalorie() { + return calorie; + } + + public int getCarbohydrate() { + return carbohydrate; + } + + public int getProtein() { + return protein; + } + + +} From b70f0577db558740907c3eb652b73675326bd12f Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:17:26 +0800 Subject: [PATCH 009/374] create food class --- src/main/java/seedu/duke/food/Food.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 9c5b3f47e0..a6e9143d47 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -11,7 +11,7 @@ public Food(String name, int calorie, int carbohydrate, int protein){ this.carbohydrate = carbohydrate; this.protein = protein; } - + public String getName() { return name; } From f582e3d086dcb9a20ed123c7ed7b2f3fc3d6aa52 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:36:06 +0800 Subject: [PATCH 010/374] data base --- src/main/java/seedu/duke/database/DataBase.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 53d9a125fb..cf5de9c6ae 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -1,5 +1,10 @@ package seedu.duke.database; +import java.io.File; + public class DataBase { + private static final String root_directory = System.getProperty("user.home"); + private static final String duke_folder_name = root_directory + File.separator + "diet-duke"; + private static final String default_file_path = root_directory + File.separator + "diet-duke" + File.separator + "data-base"; } From 8e26b4c66b9c3e64a0534793a0767e1ec41b7202 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:37:20 +0800 Subject: [PATCH 011/374] no message --- src/main/java/seedu/duke/food/Food.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index a6e9143d47..8faad7266a 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -5,6 +5,7 @@ public class Food { private final int calorie; private final int carbohydrate; private final int protein; + public Food(String name, int calorie, int carbohydrate, int protein){ this.name = name; this.calorie = calorie; @@ -27,6 +28,4 @@ public int getCarbohydrate() { public int getProtein() { return protein; } - - } From 0d8b1336316803f6500052dd812944fef48ce680 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:39:17 +0800 Subject: [PATCH 012/374] fats --- src/main/java/seedu/duke/food/Food.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 8faad7266a..3730335a01 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -5,12 +5,18 @@ public class Food { private final int calorie; private final int carbohydrate; private final int protein; + private final int fats; - public Food(String name, int calorie, int carbohydrate, int protein){ + public Food(String name, int calorie, int carbohydrate, int protein, int fats){ this.name = name; this.calorie = calorie; this.carbohydrate = carbohydrate; this.protein = protein; + this.fats = fats; + } + + public int getFats() { + return fats; } public String getName() { From 36bf98256cc2603d5753d0696fb5775e2a641e21 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:56:35 +0800 Subject: [PATCH 013/374] add Store class add Canteen class --- src/main/java/seedu/duke/database/Canteen.java | 10 ++++++++++ src/main/java/seedu/duke/database/DataBase.java | 13 ++++++++----- src/main/java/seedu/duke/database/Store.java | 13 +++++++++++++ src/main/java/seedu/duke/database/data | 0 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/main/java/seedu/duke/database/Canteen.java create mode 100644 src/main/java/seedu/duke/database/Store.java create mode 100644 src/main/java/seedu/duke/database/data diff --git a/src/main/java/seedu/duke/database/Canteen.java b/src/main/java/seedu/duke/database/Canteen.java new file mode 100644 index 0000000000..3ec0409863 --- /dev/null +++ b/src/main/java/seedu/duke/database/Canteen.java @@ -0,0 +1,10 @@ +package seedu.duke.database; + +import java.util.ArrayList; + +public class Canteen { + private final ArrayList StoreList; + public Canteen(){ + this.StoreList = new ArrayList<>(); + } +} diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index cf5de9c6ae..8a5d1b2bb1 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -1,10 +1,13 @@ package seedu.duke.database; -import java.io.File; -public class DataBase { - private static final String root_directory = System.getProperty("user.home"); - private static final String duke_folder_name = root_directory + File.separator + "diet-duke"; - private static final String default_file_path = root_directory + File.separator + "diet-duke" + File.separator + "data-base"; +import seedu.duke.food.Food; +import java.util.ArrayList; +import java.util.Hashtable; + +public class DataBase{ + + public DataBase(){ + } } diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/duke/database/Store.java new file mode 100644 index 0000000000..2046081510 --- /dev/null +++ b/src/main/java/seedu/duke/database/Store.java @@ -0,0 +1,13 @@ +package seedu.duke.database; + +import seedu.duke.food.Food; + +import java.util.ArrayList; + +public class Store { + private final ArrayList FoodList; + public Store(){ + this.FoodList = new ArrayList<>(); + } + +} diff --git a/src/main/java/seedu/duke/database/data b/src/main/java/seedu/duke/database/data new file mode 100644 index 0000000000..e69de29bb2 From 0cd4e86189502b4e00b10836813f7b9bf197fbf2 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 7 Oct 2020 10:50:46 +0800 Subject: [PATCH 014/374] add more functions --- .../java/seedu/duke/database/Canteen.java | 21 ++++++++++++--- .../java/seedu/duke/database/DataBase.java | 14 +++++++--- src/main/java/seedu/duke/database/Store.java | 26 ++++++++++++++++--- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/main/java/seedu/duke/database/Canteen.java b/src/main/java/seedu/duke/database/Canteen.java index 3ec0409863..aa54fff639 100644 --- a/src/main/java/seedu/duke/database/Canteen.java +++ b/src/main/java/seedu/duke/database/Canteen.java @@ -3,8 +3,23 @@ import java.util.ArrayList; public class Canteen { - private final ArrayList StoreList; - public Canteen(){ - this.StoreList = new ArrayList<>(); + private final String name; + private final ArrayList storeList; + + public Canteen(String name){ + this.name = name; + this.storeList = new ArrayList<>(); + } + + /*** + * name of the canteen is for filtering purposes + * @return name of canteen + */ + public String getName() { + return name; + } + + public void addStore(Store store){ + storeList.add(store); } } diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 8a5d1b2bb1..993baeeb08 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -1,13 +1,19 @@ package seedu.duke.database; -import seedu.duke.food.Food; - import java.util.ArrayList; -import java.util.Hashtable; public class DataBase{ - + private final ArrayList canteenList; public DataBase(){ + this.canteenList = new ArrayList<>(); + init(); + } + + /*** + * Reads a file from + */ + private void init(){ + } } diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/duke/database/Store.java index 2046081510..31089e9b10 100644 --- a/src/main/java/seedu/duke/database/Store.java +++ b/src/main/java/seedu/duke/database/Store.java @@ -5,9 +5,27 @@ import java.util.ArrayList; public class Store { - private final ArrayList FoodList; - public Store(){ - this.FoodList = new ArrayList<>(); + private final String name; + private final ArrayList foodList; + + public Store(String name){ + this.name = name; + this.foodList = new ArrayList<>(); + } + + /*** + * the name of the store will be used for filtering purposes + * @return + */ + + public String getName() { + return name; + } + + /** + * This function should only be called when we initialize the data base from the text file + */ + public void addFood(Food food){ + foodList.add(food); } - } From 490054585b9a4d0839b8c4bf45831d2c03e92dde Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Wed, 7 Oct 2020 23:51:55 +0800 Subject: [PATCH 015/374] Add ActivityLevel.java ActivityLevel represents the level of physical activity a person enagages in. --- .../java/seedu/duke/person/ActivityLevel.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/seedu/duke/person/ActivityLevel.java diff --git a/src/main/java/seedu/duke/person/ActivityLevel.java b/src/main/java/seedu/duke/person/ActivityLevel.java new file mode 100644 index 0000000000..a045013b0d --- /dev/null +++ b/src/main/java/seedu/duke/person/ActivityLevel.java @@ -0,0 +1,37 @@ +package seedu.duke.person; + +/** + * Represents the physical activity level of a person or the amount of exercise a person engages in. + * An ActivityLevel has a description. + */ +public enum ActivityLevel { + NONE("You hardly engage in any exercise or have a job that requires little to no physical " + + "activity."), + LOW("You engage in some form of light exercise or have a job that requires some " + + "physical activity."), + MEDIUM("You engage in moderate amount of exercise or have a job that requires moderate " + + "physical activity."), + HIGH("You engage in vigorous exercise or have a physically demanding job."), + EXTREME("You engage in extremely vigorous exercise or have an extremely physically demanding" + + " job."); + + private final String description; + + /** + * Constructs an ActivityLevel given the description. + * + * @param description The description of the activity level. + */ + ActivityLevel(String description) { + this.description = description; + } + + /** + * Returns the description of the activity level. + * + * @return The description of the activity level. + */ + public String getDescription() { + return description; + } +} From b9db47c546c90e456706a8bed6f103ed8b6248f7 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Wed, 7 Oct 2020 23:54:08 +0800 Subject: [PATCH 016/374] Add Person.java A Person object contains all the relevant information of the user including age, height, original weight, target weight and activity level. --- src/main/java/seedu/duke/person/Person.java | 126 ++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/main/java/seedu/duke/person/Person.java diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java new file mode 100644 index 0000000000..314b5b3092 --- /dev/null +++ b/src/main/java/seedu/duke/person/Person.java @@ -0,0 +1,126 @@ +package seedu.duke.person; + +/** + * Represents a Person. + * A Person has an age, height, certain activity level, original and desired weight. + */ +public class Person { + + /* The height of the person in cm */ + private int height; + /* The original weight of the person in kg */ + private int originalWeight; + /* The target weight of the person in kg */ + private int targetWeight; + private int age; + private ActivityLevel activityLevel; + + /** + * Constructs a Person with the given age, height, original weight, target weight and + * activity level. + * + * @param age The age of the person. + * @param height The height of the person. + * @param originalWeight The original weight of the person. + * @param targetWeight The target/desired weight that the person wants to achieve. + * @param activityLevel The activity level of the person or in other words, the amount of exercise the + * person engages in. + */ + public Person(int age, int height, int originalWeight, int targetWeight, ActivityLevel activityLevel) { + this.age = age; + this.height = height; + this.originalWeight = originalWeight; + this.targetWeight = targetWeight; + this.activityLevel = activityLevel; + } + + /** + * Returns the age of the person. + * + * @return The age of the person. + */ + public int getAge() { + return age; + } + + /** + * Sets the age of the person to the new age that is given. + * + * @param newAge The new/revised age of the person. + */ + public void setAge(int newAge) { + age = newAge; + } + + /** + * Returns the height of the person. + * + * @return The height of the person. + */ + public int getHeight() { + return height; + } + + /** + * Sets the height of the person to the new height given. + * + * @param newHeight The new/revised height of the person. + */ + public void setHeight(int newHeight) { + height = newHeight; + } + + /** + * Returns the original weight of the person. + * + * @return The original weight of the person. + */ + public int getOriginalWeight() { + return originalWeight; + } + + /** + * Sets the original weight of the person to the new original weight given. + * + * @param newOriginalWeight The new/revised original weight of the person. + */ + public void setOriginalWeight(int newOriginalWeight) { + originalWeight = newOriginalWeight; + } + + /** + * Returns the target weight the person the person wants to achieve. + * + * @return The target weight the person wants to achieve. + */ + public int getTargetWeight() { + return targetWeight; + } + + /** + * Sets the target weight of the person to the new target weight given. + * + * @param newTargetWeight The new/revised target weight of the person. + */ + public void setTargetWeight(int newTargetWeight) { + targetWeight = newTargetWeight; + } + + /** + * Returns the activity level of the person. + * + * @return The activity level of the person. + */ + public ActivityLevel getActivityLevel() { + return activityLevel; + } + + /** + * Sets the activity level of the person to the new activity level given. + * + * @param newActivityLevel The new/revised activity level of the person. + */ + public void getActivityLevel(ActivityLevel newActivityLevel) { + activityLevel = newActivityLevel; + } +} From fa84230f794e597148c6dc8214b276d73245dc20 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 8 Oct 2020 00:15:43 +0800 Subject: [PATCH 017/374] Person class: correct setter method name The setter method for activity level previously had a getter naming style. --- src/main/java/seedu/duke/person/Person.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java index 314b5b3092..d29131135f 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/duke/person/Person.java @@ -120,7 +120,7 @@ public ActivityLevel getActivityLevel() { * * @param newActivityLevel The new/revised activity level of the person. */ - public void getActivityLevel(ActivityLevel newActivityLevel) { + public void setActivityLevel(ActivityLevel newActivityLevel) { activityLevel = newActivityLevel; } } From 29a016bae6efe4a28b42910e2c31734a0afd5619 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 8 Oct 2020 17:50:40 +0800 Subject: [PATCH 018/374] Add JUnit tests for Person class --- .../java/seedu/duke/person/PersonTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/test/java/seedu/duke/person/PersonTest.java diff --git a/src/test/java/seedu/duke/person/PersonTest.java b/src/test/java/seedu/duke/person/PersonTest.java new file mode 100644 index 0000000000..0685a0383c --- /dev/null +++ b/src/test/java/seedu/duke/person/PersonTest.java @@ -0,0 +1,71 @@ +package seedu.duke.person; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PersonTest { + + private Person person; + + @BeforeEach + public void setUp() { + person = new Person(21,165,65,55,ActivityLevel.LOW); + } + + @Test + void getAge_person_returnsAge() { + assertEquals(person.getAge(),21); + } + + @Test + void setAge_personWithNewAge_returnsNewAge() { + person.setAge(24); + assertEquals(person.getAge(),24); + } + + @Test + void getHeight_person_returnsHeight() { + assertEquals(person.getHeight(),165); + } + + @Test + void setHeight_personWithNewHeight_returnsNewHeight() { + person.setHeight(175); + assertEquals(person.getHeight(),175); + } + + @Test + void getOriginalWeight_person_returnsOriginalWeight() { + assertEquals(person.getOriginalWeight(),65); + } + + @Test + void setOriginalWeight_personWithNewOriginalWeight_returnsNewOriginalWeight() { + person.setOriginalWeight(70); + assertEquals(person.getOriginalWeight(),70); + } + + @Test + void getTargetWeight_person_returnsTargetWeight() { + assertEquals(person.getTargetWeight(),55); + } + + @Test + void setTargetWeight_personWithNewTargetWeight_returnsNewTargetWeight() { + person.setTargetWeight(50); + assertEquals(person.getTargetWeight(),50); + } + + @Test + void getActivityLevel_person_returnsActivityLevel() { + assertEquals(person.getActivityLevel(),ActivityLevel.LOW); + } + + @Test + void setActivityLevel_personWithNewActivityLevel_returnsNewActivityLevel() { + person.setActivityLevel(ActivityLevel.HIGH); + assertEquals(person.getActivityLevel(),ActivityLevel.HIGH); + } +} \ No newline at end of file From 38681f25be2d04491d0e28a75f3457c11a122716 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 8 Oct 2020 17:52:33 +0800 Subject: [PATCH 019/374] Add newline at end of file --- src/test/java/seedu/duke/person/PersonTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/duke/person/PersonTest.java b/src/test/java/seedu/duke/person/PersonTest.java index 0685a0383c..7839f6dc54 100644 --- a/src/test/java/seedu/duke/person/PersonTest.java +++ b/src/test/java/seedu/duke/person/PersonTest.java @@ -68,4 +68,4 @@ void setActivityLevel_personWithNewActivityLevel_returnsNewActivityLevel() { person.setActivityLevel(ActivityLevel.HIGH); assertEquals(person.getActivityLevel(),ActivityLevel.HIGH); } -} \ No newline at end of file +} From ffcf350531c267b90eefac148487d2557d37423d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Fri, 9 Oct 2020 10:12:32 +0800 Subject: [PATCH 020/374] PersonTest.java: swap expected and actual ouput --- .../java/seedu/duke/person/PersonTest.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/test/java/seedu/duke/person/PersonTest.java b/src/test/java/seedu/duke/person/PersonTest.java index 7839f6dc54..b599cdce68 100644 --- a/src/test/java/seedu/duke/person/PersonTest.java +++ b/src/test/java/seedu/duke/person/PersonTest.java @@ -16,56 +16,62 @@ public void setUp() { @Test void getAge_person_returnsAge() { - assertEquals(person.getAge(),21); + assertEquals(21, person.getAge()); } @Test void setAge_personWithNewAge_returnsNewAge() { person.setAge(24); - assertEquals(person.getAge(),24); + assertEquals(24, person.getAge()); } @Test void getHeight_person_returnsHeight() { - assertEquals(person.getHeight(),165); + assertEquals(165, person.getHeight()); } @Test void setHeight_personWithNewHeight_returnsNewHeight() { person.setHeight(175); - assertEquals(person.getHeight(),175); + assertEquals(175, person.getHeight()); } @Test void getOriginalWeight_person_returnsOriginalWeight() { - assertEquals(person.getOriginalWeight(),65); + assertEquals(65, person.getOriginalWeight()); } @Test void setOriginalWeight_personWithNewOriginalWeight_returnsNewOriginalWeight() { person.setOriginalWeight(70); - assertEquals(person.getOriginalWeight(),70); + assertEquals(70, person.getOriginalWeight()); } @Test void getTargetWeight_person_returnsTargetWeight() { - assertEquals(person.getTargetWeight(),55); + assertEquals(55, person.getTargetWeight()); } @Test void setTargetWeight_personWithNewTargetWeight_returnsNewTargetWeight() { person.setTargetWeight(50); - assertEquals(person.getTargetWeight(),50); + assertEquals(50, person.getTargetWeight()); } @Test void getActivityLevel_person_returnsActivityLevel() { - assertEquals(person.getActivityLevel(),ActivityLevel.LOW); + assertEquals(ActivityLevel.LOW, person.getActivityLevel()); } @Test void setActivityLevel_personWithNewActivityLevel_returnsNewActivityLevel() { person.setActivityLevel(ActivityLevel.HIGH); - assertEquals(person.getActivityLevel(),ActivityLevel.HIGH); + assertEquals(ActivityLevel.HIGH, person.getActivityLevel()); + } + + @Test + void setActivityLevel_personWithNullActivityLevel_returnsNullActivityLevel() { + person.setActivityLevel(null); + assertEquals(null, person.getActivityLevel()); } } From d90f5fa7cee02e6dae36cdd87708d4ff84ad930f Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 13:10:55 +0800 Subject: [PATCH 021/374] start implementing init the init function is very complicated --- .../java/seedu/duke/database/Canteen.java | 5 ++ .../java/seedu/duke/database/DataBase.java | 88 ++++++++++++++++++- .../duke/database/DataBaseReadState.java | 49 +++++++++++ src/main/java/seedu/duke/database/Store.java | 7 ++ src/main/java/seedu/duke/database/data | 46 ++++++++++ 5 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 src/main/java/seedu/duke/database/DataBaseReadState.java diff --git a/src/main/java/seedu/duke/database/Canteen.java b/src/main/java/seedu/duke/database/Canteen.java index aa54fff639..fd3a92deff 100644 --- a/src/main/java/seedu/duke/database/Canteen.java +++ b/src/main/java/seedu/duke/database/Canteen.java @@ -1,6 +1,7 @@ package seedu.duke.database; import java.util.ArrayList; +import java.util.List; public class Canteen { private final String name; @@ -22,4 +23,8 @@ public String getName() { public void addStore(Store store){ storeList.add(store); } + + public List getStoreList() { + return storeList; + } } diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 993baeeb08..31566a086d 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -1,19 +1,101 @@ package seedu.duke.database; +import seedu.duke.food.Food; + +import java.io.File; +import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.Scanner; public class DataBase{ + private static final String START_SYMBOL = "&%START"; + private static final String STOP_SYMBOL = "&%STOP"; + private static final String UP_SYMBOL = "&%UP"; + private final ArrayList canteenList; + public DataBase(){ this.canteenList = new ArrayList<>(); - init(); } /*** - * Reads a file from + * Reads a file from the data base and puts it into the DataBase object */ - private void init(){ + public void init() throws FileNotFoundException { + File dataFile = new File(System.getProperty("user.dir") + File.separator + "data.txt"); + Scanner fileReader = new Scanner(dataFile); + String fileLine; + String[] fileData; + boolean start = false; + boolean hasPreviousCanteen = false; + boolean hasPreviousStore = false; + DataBaseReadState readState = DataBaseReadState.CANTEEN; + Canteen currentCanteen = new Canteen("dummy"); + Store currentStore = new Store("dummy"); + Food currentFood; + while (fileReader.hasNext()){ + fileLine = fileReader.nextLine(); + fileData = fileLine.split("\\|"); + // ------ Check if the data base has started scanning ------ + if (fileLine.equals(START_SYMBOL)){ + start = true; + continue; + } + if (!(start)){ + continue; + } + if (fileLine.equals(STOP_SYMBOL)){ + break; + } + // ------ Adjust the state of the data base and end the reading at the lower level ------- + if (fileLine.equals(UP_SYMBOL)){ + if (readState == DataBaseReadState.FOOD){ + currentCanteen.addStore(currentStore); + } + readState = DataBaseReadState.raiseState(readState); + continue; + } + // ----- Start reading the file here ------ + if (readState == DataBaseReadState.CANTEEN){ + if (hasPreviousCanteen){ + canteenList.add(currentCanteen); + } + currentCanteen = new Canteen(fileData[0]); + readState = DataBaseReadState.lowerState(readState); + hasPreviousCanteen = true; + hasPreviousStore = false; + } + else if (readState == DataBaseReadState.STORE){ + if (hasPreviousStore){ + currentCanteen.addStore(currentStore); + } + currentStore = new Store(fileData[0]); + hasPreviousStore = true; + } + else{ + currentFood = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]), + Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); + currentStore.addFood(currentFood); + } + } + } + + /*** + * Debugging function prints out all contents + */ + public void printAllData(){ + System.out.println("Printing out all data"); + for (Canteen canteen : canteenList){ + System.out.println("Canteeh : " + canteen.getName()); + for (Store store : canteen.getStoreList()){ + System.out.println("Store : " + store.getName()); + for (Food food : store.getFoodList()){ + System.out.println(food); + } + } + } + System.out.println("Finished Printing out all data"); } } diff --git a/src/main/java/seedu/duke/database/DataBaseReadState.java b/src/main/java/seedu/duke/database/DataBaseReadState.java new file mode 100644 index 0000000000..eae8999750 --- /dev/null +++ b/src/main/java/seedu/duke/database/DataBaseReadState.java @@ -0,0 +1,49 @@ +package seedu.duke.database; + + +/*** + * This class is used when data base is reading the data from the file + */ +public enum DataBaseReadState { + CANTEEN, + STORE, + FOOD; + + /*** + * lowers the initial state from e.g. CANTEEN ----> STORE + * There will be at most 4 states so this can just be hard coded + * @param initialState the initial state + * @return the state lowered by 1 level, or the same state if there is no lower state + */ + public static DataBaseReadState lowerState(DataBaseReadState initialState){ + DataBaseReadState loweredState; + switch (initialState){ + case CANTEEN: + loweredState = STORE; + break; + case STORE: + loweredState = FOOD; + break; + default: + loweredState = initialState; + break; + } + return loweredState; + } + + public static DataBaseReadState raiseState(DataBaseReadState initialState){ + DataBaseReadState raisedState; + switch (initialState){ + case STORE: + raisedState = CANTEEN; + break; + case FOOD: + raisedState = STORE; + break; + default: + raisedState = initialState; + break; + } + return raisedState; + } +} diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/duke/database/Store.java index 31089e9b10..c34c7dac80 100644 --- a/src/main/java/seedu/duke/database/Store.java +++ b/src/main/java/seedu/duke/database/Store.java @@ -3,11 +3,13 @@ import seedu.duke.food.Food; import java.util.ArrayList; +import java.util.List; public class Store { private final String name; private final ArrayList foodList; + public Store(String name){ this.name = name; this.foodList = new ArrayList<>(); @@ -28,4 +30,9 @@ public String getName() { public void addFood(Food food){ foodList.add(food); } + + public List getFoodList() { + return foodList; + } + } diff --git a/src/main/java/seedu/duke/database/data b/src/main/java/seedu/duke/database/data index e69de29bb2..88b7ed720f 100644 --- a/src/main/java/seedu/duke/database/data +++ b/src/main/java/seedu/duke/database/data @@ -0,0 +1,46 @@ +##################################################################### +# 3 LEVEL DATA BASE # +# Canteen -----> Store ------> Food # +# Commands : # +# &%START : start reading data from the data base # +# &%STOP : stop reading data from the data base # +# &%UP : goes down 1 level e.g. Canteen ---> Store # +# &%DOWN : goes down 1 level e.g. Canteen ---> Store # +# &%ADD format : adds the item with the given format # +# # +# Comments : any line that starts with # is ignored # +# # +# Canteen format : {name} # +# Store format : {name} # +# Food format : {name}|{Calorie}|{Carb}|{Protein}|{Fat} # +##################################################################### + +###################################################################### +# Version 0.1 : # +# there is only UP, once a store or canteen is # +# specified we automatically go down 1 level , for this version # +# there is no going out of a store and then coming back to add more# +# Units : Calorie : kcal : Carbs : g Protein : g : Fats : g # +###################################################################### + +&%START +Science canteen +Halal Mini Wok +Prawn Mee Soup(Dry)(Large)|490|0|0|0 +Prawn Mee Soup(Dry)(Small)|390|0|0|0 +Fried Hokkien Prawn Mee(Large)|470|0|0|0 +Fried Hokkien Prawn Mee(Small)|350|0|0|0 +Clay Pot Chicken|440|0|0|0 +Black Pepper Chicken|490|0|0|0 +&%UP +Ayam Penyet +Ayam Penyet Set|699|0|0|0 +Steamed Chicken Set |475|0|0|0 +Ikan Grouper Penyet Set|669|0|0|0 +&%UP +Michelin Star Restaurant +Bouillabaisse with cock crab and poached lobster|520|45|35|56 +Chicken wings with Reblochon pomme purée|450|25|32|66 +Sea bass with prawn tortellini, fennel purée and white wine sauce|530|76|25|43 +&%STOP + From 1a2b2fa3ab55ff3080a18bd8c7ccb6628209f0d9 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 13:42:42 +0800 Subject: [PATCH 022/374] made another implementation of init() that is easier to understand --- .../java/seedu/duke/database/DataBase.java | 82 ++++++++++--------- src/main/java/seedu/duke/database/data | 2 + 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 31566a086d..0e8f89f308 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -13,6 +13,8 @@ public class DataBase{ private static final String STOP_SYMBOL = "&%STOP"; private static final String UP_SYMBOL = "&%UP"; + private static final String DATA_FILE_SEPERATOR = "\\|"; + private final ArrayList canteenList; public DataBase(){ @@ -26,17 +28,9 @@ public void init() throws FileNotFoundException { File dataFile = new File(System.getProperty("user.dir") + File.separator + "data.txt"); Scanner fileReader = new Scanner(dataFile); String fileLine; - String[] fileData; boolean start = false; - boolean hasPreviousCanteen = false; - boolean hasPreviousStore = false; - DataBaseReadState readState = DataBaseReadState.CANTEEN; - Canteen currentCanteen = new Canteen("dummy"); - Store currentStore = new Store("dummy"); - Food currentFood; while (fileReader.hasNext()){ fileLine = fileReader.nextLine(); - fileData = fileLine.split("\\|"); // ------ Check if the data base has started scanning ------ if (fileLine.equals(START_SYMBOL)){ start = true; @@ -48,38 +42,50 @@ public void init() throws FileNotFoundException { if (fileLine.equals(STOP_SYMBOL)){ break; } + canteenList.add(fillCanteen(fileLine, fileReader)); + } + } - // ------ Adjust the state of the data base and end the reading at the lower level ------- - if (fileLine.equals(UP_SYMBOL)){ - if (readState == DataBaseReadState.FOOD){ - currentCanteen.addStore(currentStore); - } - readState = DataBaseReadState.raiseState(readState); - continue; - } - // ----- Start reading the file here ------ - if (readState == DataBaseReadState.CANTEEN){ - if (hasPreviousCanteen){ - canteenList.add(currentCanteen); - } - currentCanteen = new Canteen(fileData[0]); - readState = DataBaseReadState.lowerState(readState); - hasPreviousCanteen = true; - hasPreviousStore = false; - } - else if (readState == DataBaseReadState.STORE){ - if (hasPreviousStore){ - currentCanteen.addStore(currentStore); - } - currentStore = new Store(fileData[0]); - hasPreviousStore = true; - } - else{ - currentFood = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]), - Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); - currentStore.addFood(currentFood); - } + /*** + * This function is called right after the canteen name is provided + * The very next line that the file reads is the store name + * It will turn call fillStore with that name inserted, when the function fillStore + * finishes executing, fileRead.nextLine() can either provide a new store name or UP_SYMBOL + * if the UP_SYMBOL is provided, the function ends and the final Canteen object is returned + * @param name + * @param fileSegment + * @return + */ + private Canteen fillCanteen(String name, Scanner fileSegment){ + Canteen canteen = new Canteen(name); + String fileLine = fileSegment.nextLine(); + while (!(fileLine.equals(UP_SYMBOL))){ + canteen.addStore(fillStore(name,fileSegment)); + fileLine = fileSegment.nextLine(); + } + return canteen; + } + + /*** + * This function is called right after the store name is provided + * The very next line in the file should be the first food to be added + * The function stops when it hits the line of the file that says UP_SYMBOL + * @param name name of the store + * @param fileSegment the Scanner object used for the init() function + * @return the completed store with all the food loaded + */ + private Store fillStore(String name, Scanner fileSegment){ + Store store = new Store(name); + Food food; + String fileLine = fileSegment.nextLine(); + String[] fileData = fileLine.split(DATA_FILE_SEPERATOR); + while (!(fileLine.equals(UP_SYMBOL))){ + food = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]) + ,Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); + fileLine = fileSegment.nextLine(); + fileData = fileLine.split(DATA_FILE_SEPERATOR); } + return store; } /*** diff --git a/src/main/java/seedu/duke/database/data b/src/main/java/seedu/duke/database/data index 88b7ed720f..7a211bc4e1 100644 --- a/src/main/java/seedu/duke/database/data +++ b/src/main/java/seedu/duke/database/data @@ -42,5 +42,7 @@ Michelin Star Restaurant Bouillabaisse with cock crab and poached lobster|520|45|35|56 Chicken wings with Reblochon pomme purée|450|25|32|66 Sea bass with prawn tortellini, fennel purée and white wine sauce|530|76|25|43 +&%UP +&%UP &%STOP From cdee1559ca69c1e8fa8497d3efaf6a0962820c9c Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 14:53:25 +0800 Subject: [PATCH 023/374] implement initializing of data base --- src/main/java/seedu/duke/database/DataBase.java | 8 +++++++- .../java/seedu/duke/database/{data => data.txt} | 0 .../java/seedu/duke/database/DataBaseTest.java | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) rename src/main/java/seedu/duke/database/{data => data.txt} (100%) create mode 100644 src/test/java/seedu/duke/database/DataBaseTest.java diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 0e8f89f308..42f43f720c 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -15,6 +15,10 @@ public class DataBase{ private static final String DATA_FILE_SEPERATOR = "\\|"; + private static final String rootDirectory = System.getProperty("user.dir"); + private static final String dataFileFolder = "src" + File.separator + "main" + File.separator + + "java" + File.separator + "seedu" + File.separator + "duke" + File.separator + "database"; + private final ArrayList canteenList; public DataBase(){ @@ -25,7 +29,8 @@ public DataBase(){ * Reads a file from the data base and puts it into the DataBase object */ public void init() throws FileNotFoundException { - File dataFile = new File(System.getProperty("user.dir") + File.separator + "data.txt"); + String fileFolder = rootDirectory + File.separator + dataFileFolder; + File dataFile = new File( fileFolder + File.separator + "data.txt"); Scanner fileReader = new Scanner(dataFile); String fileLine; boolean start = false; @@ -82,6 +87,7 @@ private Store fillStore(String name, Scanner fileSegment){ while (!(fileLine.equals(UP_SYMBOL))){ food = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]) ,Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); + store.addFood(food); fileLine = fileSegment.nextLine(); fileData = fileLine.split(DATA_FILE_SEPERATOR); } diff --git a/src/main/java/seedu/duke/database/data b/src/main/java/seedu/duke/database/data.txt similarity index 100% rename from src/main/java/seedu/duke/database/data rename to src/main/java/seedu/duke/database/data.txt diff --git a/src/test/java/seedu/duke/database/DataBaseTest.java b/src/test/java/seedu/duke/database/DataBaseTest.java new file mode 100644 index 0000000000..4551e8571a --- /dev/null +++ b/src/test/java/seedu/duke/database/DataBaseTest.java @@ -0,0 +1,17 @@ +package seedu.duke.database; + +import java.io.FileNotFoundException; + +import static org.junit.jupiter.api.Assertions.*; + +class DataBaseTest { + public static void main(String[] args){ + DataBase database = new DataBase(); + try { + database.init(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + database.printAllData(); + } +} \ No newline at end of file From 90a72a28d90fac81bab27b688952373be059a508 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 14:55:07 +0800 Subject: [PATCH 024/374] no message --- src/main/java/seedu/duke/database/DataBase.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 42f43f720c..2465312513 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -110,4 +110,7 @@ public void printAllData(){ } System.out.println("Finished Printing out all data"); } + + // -------- Search functions -------- + } From 00fd7a46c7bc7e226c25b7af666f2503e42fdd8b Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 14:58:33 +0800 Subject: [PATCH 025/374] implement food toString method --- src/main/java/seedu/duke/food/Food.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 3730335a01..3c0c545a28 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -34,4 +34,10 @@ public int getCarbohydrate() { public int getProtein() { return protein; } + + @Override + public String toString(){ + return name + " :: calorie : " + calorie + " :: protein : " + protein + ":: carbohydrate :" + carbohydrate + + ":: fats :" + fats; + } } From 2254fcd44ddc5f5b308613dd1e1dbd9f636ea8e5 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 15:03:32 +0800 Subject: [PATCH 026/374] fix bug with store name --- src/main/java/seedu/duke/database/DataBase.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 2465312513..9419c0386c 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -57,15 +57,15 @@ public void init() throws FileNotFoundException { * It will turn call fillStore with that name inserted, when the function fillStore * finishes executing, fileRead.nextLine() can either provide a new store name or UP_SYMBOL * if the UP_SYMBOL is provided, the function ends and the final Canteen object is returned - * @param name - * @param fileSegment + * @param name name of store + * @param fileSegment the file reader with the next line being a food item or UP_SYMBOL * @return */ private Canteen fillCanteen(String name, Scanner fileSegment){ Canteen canteen = new Canteen(name); String fileLine = fileSegment.nextLine(); while (!(fileLine.equals(UP_SYMBOL))){ - canteen.addStore(fillStore(name,fileSegment)); + canteen.addStore(fillStore(fileLine,fileSegment)); fileLine = fileSegment.nextLine(); } return canteen; From 8bda40256a89bbb727ac7f19ce01f5849fc9bb42 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 15:04:58 +0800 Subject: [PATCH 027/374] update toString --- src/main/java/seedu/duke/food/Food.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 3c0c545a28..a4537525f5 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -37,7 +37,7 @@ public int getProtein() { @Override public String toString(){ - return name + " :: calorie : " + calorie + " :: protein : " + protein + ":: carbohydrate :" + carbohydrate - + ":: fats :" + fats; + return name + "\\| calorie : " + calorie + "\\| protein : " + protein + "\\| carbohydrate : " + carbohydrate + + "\\| fats : " + fats; } } From 3b788d61c15cc9a54ef7331a2732dc9bc93fae47 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 15:10:16 +0800 Subject: [PATCH 028/374] adjust toString method Add FoodTest --- src/main/java/seedu/duke/food/Food.java | 4 ++-- src/test/java/seedu/duke/food/FoodTest.java | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/test/java/seedu/duke/food/FoodTest.java diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index a4537525f5..941e8e3944 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -37,7 +37,7 @@ public int getProtein() { @Override public String toString(){ - return name + "\\| calorie : " + calorie + "\\| protein : " + protein + "\\| carbohydrate : " + carbohydrate - + "\\| fats : " + fats; + return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate + + " | fats : " + fats; } } diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java new file mode 100644 index 0000000000..70c30be1ca --- /dev/null +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -0,0 +1,12 @@ +package seedu.duke.food; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FoodTest { + public static void main(String[] args){ + Food food = new Food("Kobe Beef", 480,50,40,30); + System.out.println(food); + } +} \ No newline at end of file From 6e225ef145d9cc5d514a1159251c5d8ea44528f5 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 15:12:23 +0800 Subject: [PATCH 029/374] removed deadcode DataBaseReadState --- .../duke/database/DataBaseReadState.java | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 src/main/java/seedu/duke/database/DataBaseReadState.java diff --git a/src/main/java/seedu/duke/database/DataBaseReadState.java b/src/main/java/seedu/duke/database/DataBaseReadState.java deleted file mode 100644 index eae8999750..0000000000 --- a/src/main/java/seedu/duke/database/DataBaseReadState.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.duke.database; - - -/*** - * This class is used when data base is reading the data from the file - */ -public enum DataBaseReadState { - CANTEEN, - STORE, - FOOD; - - /*** - * lowers the initial state from e.g. CANTEEN ----> STORE - * There will be at most 4 states so this can just be hard coded - * @param initialState the initial state - * @return the state lowered by 1 level, or the same state if there is no lower state - */ - public static DataBaseReadState lowerState(DataBaseReadState initialState){ - DataBaseReadState loweredState; - switch (initialState){ - case CANTEEN: - loweredState = STORE; - break; - case STORE: - loweredState = FOOD; - break; - default: - loweredState = initialState; - break; - } - return loweredState; - } - - public static DataBaseReadState raiseState(DataBaseReadState initialState){ - DataBaseReadState raisedState; - switch (initialState){ - case STORE: - raisedState = CANTEEN; - break; - case FOOD: - raisedState = STORE; - break; - default: - raisedState = initialState; - break; - } - return raisedState; - } -} From d9bec8445c623b4024094909d5afbd5bdf74a734 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 16:14:03 +0800 Subject: [PATCH 030/374] implement database query functions search by food name, list first, list all search by store, list first store, list all store search by food and store --- .../java/seedu/duke/database/DataBase.java | 106 +++++++++++++++--- src/main/java/seedu/duke/database/Store.java | 1 + src/main/java/seedu/duke/food/Food.java | 2 +- 3 files changed, 95 insertions(+), 14 deletions(-) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 9419c0386c..593ba6ff8a 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -6,9 +6,12 @@ import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; import java.util.Scanner; +import java.util.stream.Stream; -public class DataBase{ +public class DataBase { private static final String START_SYMBOL = "&%START"; private static final String STOP_SYMBOL = "&%STOP"; private static final String UP_SYMBOL = "&%UP"; @@ -19,9 +22,9 @@ public class DataBase{ private static final String dataFileFolder = "src" + File.separator + "main" + File.separator + "java" + File.separator + "seedu" + File.separator + "duke" + File.separator + "database"; - private final ArrayList canteenList; + private final List canteenList; - public DataBase(){ + public DataBase() { this.canteenList = new ArrayList<>(); } @@ -30,21 +33,21 @@ public DataBase(){ */ public void init() throws FileNotFoundException { String fileFolder = rootDirectory + File.separator + dataFileFolder; - File dataFile = new File( fileFolder + File.separator + "data.txt"); + File dataFile = new File(fileFolder + File.separator + "data.txt"); Scanner fileReader = new Scanner(dataFile); String fileLine; boolean start = false; - while (fileReader.hasNext()){ + while (fileReader.hasNext()) { fileLine = fileReader.nextLine(); // ------ Check if the data base has started scanning ------ - if (fileLine.equals(START_SYMBOL)){ + if (fileLine.equals(START_SYMBOL)) { start = true; continue; } if (!(start)){ continue; } - if (fileLine.equals(STOP_SYMBOL)){ + if (fileLine.equals(STOP_SYMBOL)) { break; } canteenList.add(fillCanteen(fileLine, fileReader)); @@ -61,10 +64,10 @@ public void init() throws FileNotFoundException { * @param fileSegment the file reader with the next line being a food item or UP_SYMBOL * @return */ - private Canteen fillCanteen(String name, Scanner fileSegment){ + private Canteen fillCanteen(String name, Scanner fileSegment) { Canteen canteen = new Canteen(name); String fileLine = fileSegment.nextLine(); - while (!(fileLine.equals(UP_SYMBOL))){ + while (!(fileLine.equals(UP_SYMBOL))) { canteen.addStore(fillStore(fileLine,fileSegment)); fileLine = fileSegment.nextLine(); } @@ -79,12 +82,12 @@ private Canteen fillCanteen(String name, Scanner fileSegment){ * @param fileSegment the Scanner object used for the init() function * @return the completed store with all the food loaded */ - private Store fillStore(String name, Scanner fileSegment){ + private Store fillStore(String name, Scanner fileSegment) { Store store = new Store(name); Food food; String fileLine = fileSegment.nextLine(); String[] fileData = fileLine.split(DATA_FILE_SEPERATOR); - while (!(fileLine.equals(UP_SYMBOL))){ + while (!(fileLine.equals(UP_SYMBOL))) { food = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]) ,Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); store.addFood(food); @@ -97,7 +100,7 @@ private Store fillStore(String name, Scanner fileSegment){ /*** * Debugging function prints out all contents */ - public void printAllData(){ + public void printAllData() { System.out.println("Printing out all data"); for (Canteen canteen : canteenList){ System.out.println("Canteeh : " + canteen.getName()); @@ -112,5 +115,82 @@ public void printAllData(){ } // -------- Search functions -------- - + + /*** + * this method searchs the whole data base and returns the first food item whose name contains the provided string + * @param food part of the name of the food + * @return Food + * @throws NoSuchElementException if no food contains the name provided + */ + public Food searchFoodByName(String food) { + return foodStream().filter( x -> x.getName().contains(food)).findFirst().orElseThrow(); + } + + /*** + * this method searchs the whole data base and returns all of the food whose name contains the provided string + * @param food part of the name of the food e.g. chicken + * @return data stream of all food items + */ + public Stream searchAllFoodContainingName(String food) { + return foodStream().filter( x -> x.getName().contains(food)); + } + + /*** + * search for the first food that contains the string provided in the first store which matchs the store + * string provided + * @param food partial name of the food + * @param store partial name of the store + * @return Food object + */ + public Food searchFoodByNameByStore(String food, String store){ + return canteenList.stream() + .flatMap( x -> x.getStoreList().stream()) + .filter( x -> x.getName().contains(store)) + .findFirst() + .orElseThrow() + .getFoodList() + .stream() + .filter( x -> x.getName().contains(food)) + .findFirst() + .orElseThrow(); + } + + /*** + * This method returns a stream of all the food in the first store that contains the given string + * @param store partial name of the store + * @return food stream + */ + public Stream searchAllFoodOfStore(String store) { + return canteenList.stream() + .flatMap( x -> x.getStoreList().stream()) + .filter( x -> x.getName().contains(store)) + .findFirst() + .orElseThrow() + .getFoodList() + .stream(); + } + + /*** + * this method returns a stream of all the food in all stores that contains the given string + * @param store partial name of the store + * @return food stream + */ + public Stream searchAllFoodOfAllStores(String store){ + return canteenList.stream() + .flatMap( x -> x.getStoreList().stream()) + .filter( x -> x.getName().contains(store)) + .flatMap( x -> x.getFoodList().stream()); + } + + /*** + * Provides a data stream of all the food in the data base + * @return a food stream + */ + private Stream foodStream() { + return canteenList.stream() + .flatMap( x -> x.getStoreList().stream()) + .flatMap( x -> x.getFoodList().stream()); + } + + } diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/duke/database/Store.java index c34c7dac80..5d1727d67b 100644 --- a/src/main/java/seedu/duke/database/Store.java +++ b/src/main/java/seedu/duke/database/Store.java @@ -3,6 +3,7 @@ import seedu.duke.food.Food; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class Store { diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 941e8e3944..2572c66aff 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -36,7 +36,7 @@ public int getProtein() { } @Override - public String toString(){ + public String toString() { return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate + " | fats : " + fats; } From 04c9ad22815c14990a4d30969c3d64b3aabebe6c Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 16:28:27 +0800 Subject: [PATCH 031/374] implement searchFoodByNameByCanteen searchAllFoodByNameByCanteen searchAllFoodBetweenCalorie --- .../java/seedu/duke/database/DataBase.java | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 593ba6ff8a..8dbdb772b5 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -143,13 +143,7 @@ public Stream searchAllFoodContainingName(String food) { * @return Food object */ public Food searchFoodByNameByStore(String food, String store){ - return canteenList.stream() - .flatMap( x -> x.getStoreList().stream()) - .filter( x -> x.getName().contains(store)) - .findFirst() - .orElseThrow() - .getFoodList() - .stream() + return searchAllFoodByStore(store) .filter( x -> x.getName().contains(food)) .findFirst() .orElseThrow(); @@ -160,7 +154,7 @@ public Food searchFoodByNameByStore(String food, String store){ * @param store partial name of the store * @return food stream */ - public Stream searchAllFoodOfStore(String store) { + public Stream searchAllFoodByStore(String store) { return canteenList.stream() .flatMap( x -> x.getStoreList().stream()) .filter( x -> x.getName().contains(store)) @@ -182,15 +176,48 @@ public Stream searchAllFoodOfAllStores(String store){ .flatMap( x -> x.getFoodList().stream()); } + + /*** + * this method returns the first food that matchs the + * @param food partial name of the food + * @param canteen partial name of the canteen + * @return Food object + */ + public Food searchFoodByNameByCanteen(String food, String canteen){ + return searchAllFoodByNameByCanteen(food, canteen) + .findFirst() + .orElseThrow(); + } + + public Stream searchAllFoodByNameByCanteen(String food, String canteen){ + return canteenList.stream() + .filter(x -> x.getName().contains(canteen)) + .findFirst() + .orElseThrow() + .getStoreList() + .stream() + .flatMap( x -> x.getFoodList().stream()) + .filter(x -> x.getName().contains(food)); + } + + /*** + * returns a stream of food whose calorie is below the provided amount + * @param calorie the maximum calorie of the food + * @return a stream + */ + public Stream searchAllFoodBelowCalorie( int calorie){ + return foodStream() + .filter( x -> x.getCalorie() < calorie); + } + + /*** * Provides a data stream of all the food in the data base * @return a food stream */ private Stream foodStream() { - return canteenList.stream() - .flatMap( x -> x.getStoreList().stream()) - .flatMap( x -> x.getFoodList().stream()); + return canteenList.stream() + .flatMap( x -> x.getStoreList().stream()) + .flatMap( x -> x.getFoodList().stream()); } - - } From 7ab189e51855dfe58fcaefa523e999681ca48510 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 16:31:09 +0800 Subject: [PATCH 032/374] fix checkstyle errors --- src/main/java/seedu/duke/food/Food.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 941e8e3944..c1e03882ce 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -7,7 +7,7 @@ public class Food { private final int protein; private final int fats; - public Food(String name, int calorie, int carbohydrate, int protein, int fats){ + public Food(String name, int calorie, int carbohydrate, int protein, int fats) { this.name = name; this.calorie = calorie; this.carbohydrate = carbohydrate; @@ -36,7 +36,7 @@ public int getProtein() { } @Override - public String toString(){ + public String toString() { return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate + " | fats : " + fats; } From eb954f86f6e0c99424e377e7253d9410cf9a44a1 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 16:36:08 +0800 Subject: [PATCH 033/374] implement filter by calorie ranges --- .../java/seedu/duke/database/DataBase.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 8dbdb772b5..b59b503f87 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -142,7 +142,7 @@ public Stream searchAllFoodContainingName(String food) { * @param store partial name of the store * @return Food object */ - public Food searchFoodByNameByStore(String food, String store){ + public Food searchFoodByNameByStore(String food, String store) { return searchAllFoodByStore(store) .filter( x -> x.getName().contains(food)) .findFirst() @@ -169,7 +169,7 @@ public Stream searchAllFoodByStore(String store) { * @param store partial name of the store * @return food stream */ - public Stream searchAllFoodOfAllStores(String store){ + public Stream searchAllFoodOfAllStores(String store) { return canteenList.stream() .flatMap( x -> x.getStoreList().stream()) .filter( x -> x.getName().contains(store)) @@ -183,13 +183,13 @@ public Stream searchAllFoodOfAllStores(String store){ * @param canteen partial name of the canteen * @return Food object */ - public Food searchFoodByNameByCanteen(String food, String canteen){ + public Food searchFoodByNameByCanteen(String food, String canteen) { return searchAllFoodByNameByCanteen(food, canteen) .findFirst() .orElseThrow(); } - public Stream searchAllFoodByNameByCanteen(String food, String canteen){ + public Stream searchAllFoodByNameByCanteen(String food, String canteen) { return canteenList.stream() .filter(x -> x.getName().contains(canteen)) .findFirst() @@ -205,11 +205,19 @@ public Stream searchAllFoodByNameByCanteen(String food, String canteen){ * @param calorie the maximum calorie of the food * @return a stream */ - public Stream searchAllFoodBelowCalorie( int calorie){ - return foodStream() - .filter( x -> x.getCalorie() < calorie); + public Stream searchAllFoodBelowCalorie( int calorie) { + return foodStream().filter( x -> x.getCalorie() < calorie); } + /*** + * returns all food within the calorie range + * @param minCalorie minimum calories + * @param maxCalorie maxinum calories + * @return + */ + public Stream searchAllFoodInCalorieRange( int minCalorie, int maxCalorie){ + return foodStream().filter( x -> x.getCalorie() <= maxCalorie && x.getCalorie() >= minCalorie ); + } /*** * Provides a data stream of all the food in the data base From 6f691a16378ced3f1164a34fadac91e982df5fdb Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 16:47:28 +0800 Subject: [PATCH 034/374] add docs --- src/main/java/seedu/duke/database/DataBase.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index b59b503f87..68077d710b 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -62,7 +62,7 @@ public void init() throws FileNotFoundException { * if the UP_SYMBOL is provided, the function ends and the final Canteen object is returned * @param name name of store * @param fileSegment the file reader with the next line being a food item or UP_SYMBOL - * @return + * @return Canteen objected with all it's stores loaded */ private Canteen fillCanteen(String name, Scanner fileSegment) { Canteen canteen = new Canteen(name); @@ -189,6 +189,12 @@ public Food searchFoodByNameByCanteen(String food, String canteen) { .orElseThrow(); } + /*** + * returns all food that contains the provided food name in the first canteen that matchs the canteen name + * @param food partial name of the food + * @param canteen partial name of the canteen + * @return Food Stream + */ public Stream searchAllFoodByNameByCanteen(String food, String canteen) { return canteenList.stream() .filter(x -> x.getName().contains(canteen)) From d780ab24b15ac6c00581a792789603e49a40c75a Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 9 Oct 2020 16:48:55 +0800 Subject: [PATCH 035/374] correct checkstyle --- src/test/java/seedu/duke/food/FoodTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java index 70c30be1ca..e2b03edb10 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -2,10 +2,9 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; class FoodTest { - public static void main(String[] args){ + public static void main(String[] args) { Food food = new Food("Kobe Beef", 480,50,40,30); System.out.println(food); } From 7de762ada6d5811b328e8826176c1fc21ca27612 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sat, 10 Oct 2020 10:01:46 +0800 Subject: [PATCH 036/374] Calculator class --- .../java/seedu/calculator/calculator.java | 66 +++++++++++++++++++ src/main/java/seedu/duke/Duke.java | 3 +- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/main/java/seedu/calculator/calculator.java diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/calculator.java new file mode 100644 index 0000000000..2d798147e9 --- /dev/null +++ b/src/main/java/seedu/calculator/calculator.java @@ -0,0 +1,66 @@ +package seedu.calculator; + +import java.util.ArrayList; +import seedu.foodList; + +/** + * Represents a calculator of food items in foodList. + */ +public class calculator { + private static Food total = new Food (total, 0, 0, 0, 0); + + /** + * Construct a calculator taking in a foodList. Add up calories, + * carbs, protein, and fats in each food item. + * + * @param foodList foodList containing food items to calculate + */ + public calculator(ArrayList foodList){ + try { + for(int i=0; i< foodList.size(); i++){ + total.calorie += foodList.getCalorie(i); + total.carbs += foodList.getCarbs(i); + total.protein += foodList.getProtein(i); + total.fats += foodList.getFats(i); + } + } catch (NullPointerException e) { + System.out.println("Ops, This foodList is null!"); + } + } + + /** + * Returns an int type variable containing the value of total calorie. + * + * @return the value of total calorie of food items in foodList. + */ + public int calculateCalorie(){ + return total.calorie; + } + + /** + * Returns an int type variable containing the value of total carbs. + * + * @return the value of total carbs of food items in foodList. + */ + public int calculateCarbs(){ + return total.carbs; + } + + /** + * Returns an int type variable containing the value of total protein. + * + * @return the value of total protein of food items in foodList. + */ + public int calculateProtein(){ + return total.protein; + } + + /** + * Returns an int type variable containing the value of total fats. + * + * @return the value of total fats of food items in foodList. + */ + public int calculateFats(){ + return total.fats; + } +} diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 5c74e68d59..74e5175952 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -6,8 +6,7 @@ public class Duke { /** * Main entry-point for the java.duke.Duke application. */ - public static void main(String[] args) { - String logo = " ____ _ \n" + public static void main(String[] args) { String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" From 610bd46a2c73f3057d8a88d0f735f229a0cc6cc6 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sat, 10 Oct 2020 10:59:31 +0800 Subject: [PATCH 037/374] calculator version0.2 issues7 --- src/main/java/seedu/calculator/calculator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/calculator.java index 2d798147e9..3fa67153fd 100644 --- a/src/main/java/seedu/calculator/calculator.java +++ b/src/main/java/seedu/calculator/calculator.java @@ -13,7 +13,7 @@ public class calculator { * Construct a calculator taking in a foodList. Add up calories, * carbs, protein, and fats in each food item. * - * @param foodList foodList containing food items to calculate + * @param foodList foodList containing food items to calculate. */ public calculator(ArrayList foodList){ try { @@ -63,4 +63,4 @@ public int calculateProtein(){ public int calculateFats(){ return total.fats; } -} +} \ No newline at end of file From a9af8694e4f36f229a70874cbe091b831ae90daa Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sat, 10 Oct 2020 11:22:22 +0800 Subject: [PATCH 038/374] calculator version0.3 issue7 --- src/main/java/seedu/calculator/calculator.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/calculator.java index 3fa67153fd..017bd4cf77 100644 --- a/src/main/java/seedu/calculator/calculator.java +++ b/src/main/java/seedu/calculator/calculator.java @@ -7,7 +7,7 @@ * Represents a calculator of food items in foodList. */ public class calculator { - private static Food total = new Food (total, 0, 0, 0, 0); + private static Food total = new Food (total, 1, 0, 0, 0, 0); /** * Construct a calculator taking in a foodList. Add up calories, @@ -18,10 +18,10 @@ public class calculator { public calculator(ArrayList foodList){ try { for(int i=0; i< foodList.size(); i++){ - total.calorie += foodList.getCalorie(i); - total.carbs += foodList.getCarbs(i); - total.protein += foodList.getProtein(i); - total.fats += foodList.getFats(i); + total.calorie += foodList.getPortionSize(i)*foodList.getCalorie(i); + total.carbs += foodList.getPortionSize(i)*foodList.getCarbs(i); + total.protein += foodList.getPortionSize(i)*foodList.getProtein(i); + total.fats += foodList.getPortionSize(i)*foodList.getFats(i); } } catch (NullPointerException e) { System.out.println("Ops, This foodList is null!"); From 75b21beee10276ae22d7371dc94c1e521ebc611a Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 10 Oct 2020 15:05:43 +0800 Subject: [PATCH 039/374] Add Gender.java --- src/main/java/seedu/duke/person/Gender.java | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/seedu/duke/person/Gender.java diff --git a/src/main/java/seedu/duke/person/Gender.java b/src/main/java/seedu/duke/person/Gender.java new file mode 100644 index 0000000000..52b13a5a4b --- /dev/null +++ b/src/main/java/seedu/duke/person/Gender.java @@ -0,0 +1,31 @@ +package seedu.duke.person; + +/** + * Represents the gender of a person. + * A Gender has a description. + */ +public enum Gender { + FEMALE("female"), + MALE("male"); + + private final String description; + + /** + * Constructs a Gender given the description. + * + * @param description The description of the gender. + */ + Gender(String description) { + this.description = description; + } + + /** + * Returns the description of the gender. + * + * @return The description of the gender. + */ + public String getDescription() { + return description; + } + +} From 4c6fdcc3e3e0fa492351585df4d87f0cf45f5b15 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 10 Oct 2020 15:14:24 +0800 Subject: [PATCH 040/374] Person class: add name and gender attributes --- src/main/java/seedu/duke/person/Person.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java index d29131135f..5adce5e9a7 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/duke/person/Person.java @@ -2,7 +2,7 @@ /** * Represents a Person. - * A Person has an age, height, certain activity level, original and desired weight. + * A Person has a name, gender, age, height, certain activity level, original and desired weight. */ public class Person { @@ -14,11 +14,15 @@ public class Person { private int targetWeight; private int age; private ActivityLevel activityLevel; + private Gender gender; + private String name; /** - * Constructs a Person with the given age, height, original weight, target weight and - * activity level. + * Constructs a Person with the given name, gender, age, height, original weight, target + * weight and activity level. * + * @param name The name of the person. + * @param gender The gender of the person. * @param age The age of the person. * @param height The height of the person. * @param originalWeight The original weight of the person. @@ -26,7 +30,10 @@ public class Person { * @param activityLevel The activity level of the person or in other words, the amount of exercise the * person engages in. */ - public Person(int age, int height, int originalWeight, int targetWeight, ActivityLevel activityLevel) { + public Person(String name, Gender gender, int age, int height, int originalWeight, int targetWeight, + ActivityLevel activityLevel) { + this.name = name; + this.gender = gender; this.age = age; this.height = height; this.originalWeight = originalWeight; From dabb6c50f23238988d7394af6613ed4a2f2693a2 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 10 Oct 2020 15:17:15 +0800 Subject: [PATCH 041/374] Person class: add getter and setter for name --- src/main/java/seedu/duke/person/Person.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java index 5adce5e9a7..5b86b1b307 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/duke/person/Person.java @@ -41,6 +41,24 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh this.activityLevel = activityLevel; } + /** + * Returns the name of the person. + * + * @return The name of the person. + */ + public String getName() { + return name; + } + + /** + * Sets the name of the person to the new name given. + * + * @param newName The new/revised name of the person. + */ + public void setName(String newName) { + name = newName; + } + /** * Returns the age of the person. * From f2f58c4739e42558c49b4e4b98e68112206ea329 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 10 Oct 2020 15:17:36 +0800 Subject: [PATCH 042/374] Person class: add getter and setter for gender --- src/main/java/seedu/duke/person/Person.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java index 5b86b1b307..2afc0bb38a 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/duke/person/Person.java @@ -59,6 +59,24 @@ public void setName(String newName) { name = newName; } + /** + * Returns the gender of the person. + * + * @return The gender of the person. + */ + public Gender getGender() { + return gender; + } + + /** + * Sets the gender of the person to the new gender given. + * + * @param newGender The new/revised gender of the person. + */ + public void setGender(Gender newGender) { + gender = newGender; + } + /** * Returns the age of the person. * From 79328f8c22651134623d12c38a80997201c6c1c9 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 10 Oct 2020 15:18:19 +0800 Subject: [PATCH 043/374] Person class: add toString method --- src/main/java/seedu/duke/person/Person.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java index 2afc0bb38a..1cd7b3b825 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/duke/person/Person.java @@ -166,4 +166,25 @@ public ActivityLevel getActivityLevel() { public void setActivityLevel(ActivityLevel newActivityLevel) { activityLevel = newActivityLevel; } + + /** + * Returns a string representation of all information related to the user. + * Information includes name, gender, age, height, original weight, target weight and activity level. + * + * @return A string representation of all information related to the user. + */ + @Override + public String toString() { + String userInformation = "Hi " + name + "! " + + "Here is your information:" + System.lineSeparator() + + System.lineSeparator() + + "Name: " + name + System.lineSeparator() + + "Gender: " + gender.getDescription() + System.lineSeparator() + + "Age: " + age + System.lineSeparator() + + "Height: " + height + "cm" + System.lineSeparator() + + "Original weight: " + originalWeight + "kg" + System.lineSeparator() + + "Target weight: " + targetWeight + "kg" + System.lineSeparator() + + "Activity level: " + activityLevel.getDescription(); + return userInformation; + } } From 4b18e5a769ade8bb6b14ebf0a4af6bb8c51e0157 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 10 Oct 2020 15:37:32 +0800 Subject: [PATCH 044/374] Add JUnit tests for setters, getters and toString Setters and getters include those for name and gender attributes of a Person. --- .../java/seedu/duke/person/PersonTest.java | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/src/test/java/seedu/duke/person/PersonTest.java b/src/test/java/seedu/duke/person/PersonTest.java index b599cdce68..a9f20dbe5f 100644 --- a/src/test/java/seedu/duke/person/PersonTest.java +++ b/src/test/java/seedu/duke/person/PersonTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; class PersonTest { @@ -11,7 +12,36 @@ class PersonTest { @BeforeEach public void setUp() { - person = new Person(21,165,65,55,ActivityLevel.LOW); + person = new Person("Jack", Gender.MALE,21,165,75,65, + ActivityLevel.LOW); + } + + @Test + void getName_person_returnsName() { + assertEquals("Jack", person.getName()); + } + + @Test + void setName_personWithNewName_returnNewName() { + person.setName("Jackie"); + assertEquals("Jackie", person.getName()); + } + + @Test + void gender_person_returnsGender() { + assertEquals(Gender.MALE, person.getGender()); + } + + @Test + void gender_personWithNewGender_returnsNewGender() { + person.setGender(Gender.FEMALE); + assertEquals(Gender.FEMALE, person.getGender()); + } + + @Test + void gender_personWithNullGender_returnsNullGender() { + person.setGender(null); + assertNull(person.getGender()); } @Test @@ -38,7 +68,7 @@ void setHeight_personWithNewHeight_returnsNewHeight() { @Test void getOriginalWeight_person_returnsOriginalWeight() { - assertEquals(65, person.getOriginalWeight()); + assertEquals(75, person.getOriginalWeight()); } @Test @@ -49,13 +79,13 @@ void setOriginalWeight_personWithNewOriginalWeight_returnsNewOriginalWeight() { @Test void getTargetWeight_person_returnsTargetWeight() { - assertEquals(55, person.getTargetWeight()); + assertEquals(65, person.getTargetWeight()); } @Test void setTargetWeight_personWithNewTargetWeight_returnsNewTargetWeight() { - person.setTargetWeight(50); - assertEquals(50, person.getTargetWeight()); + person.setTargetWeight(68); + assertEquals(68, person.getTargetWeight()); } @Test @@ -72,6 +102,20 @@ void setActivityLevel_personWithNewActivityLevel_returnsNewActivityLevel() { @Test void setActivityLevel_personWithNullActivityLevel_returnsNullActivityLevel() { person.setActivityLevel(null); - assertEquals(null, person.getActivityLevel()); + assertNull(person.getActivityLevel()); + } + + @Test + void toString_person_returnsStringRepresentationOfPersonInformation() { + assertEquals("Hi Jack! Here is your information:" + System.lineSeparator() + + System.lineSeparator() + + "Name: Jack" + System.lineSeparator() + + "Gender: male" + System.lineSeparator() + + "Age: 21" + System.lineSeparator() + + "Height: 165cm" + System.lineSeparator() + + "Original weight: 75kg" + System.lineSeparator() + + "Target weight: 65kg" + System.lineSeparator() + + "Activity level: You engage in some form of light exercise or have a job that requires " + + "some physical activity.", person.toString()); } } From f8ab2838ed92e72ac371f66c7ed3163ad4dbdd04 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 10 Oct 2020 17:40:11 +0800 Subject: [PATCH 045/374] no message --- .../java/seedu/duke/database/DataBase.java | 31 +++++++++++++------ src/main/java/seedu/duke/food/Food.java | 1 + src/test/java/seedu/duke/food/FoodTest.java | 2 ++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 68077d710b..98c224e836 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.Scanner; +import java.util.stream.Collectors; import java.util.stream.Stream; public class DataBase { @@ -136,8 +137,9 @@ public Stream searchAllFoodContainingName(String food) { } /*** - * search for the first food that contains the string provided in the first store which matchs the store - * string provided + * Search for the first food that contains the string provided in the first store which matchs the store + * string provided. + * * @param food partial name of the food * @param store partial name of the store * @return Food object @@ -150,7 +152,8 @@ public Food searchFoodByNameByStore(String food, String store) { } /*** - * This method returns a stream of all the food in the first store that contains the given string + * This method returns a stream of all the food in the first store that contains the given string. + * * @param store partial name of the store * @return food stream */ @@ -165,7 +168,8 @@ public Stream searchAllFoodByStore(String store) { } /*** - * this method returns a stream of all the food in all stores that contains the given string + * This method returns a stream of all the food in all stores that contains the given string. + * * @param store partial name of the store * @return food stream */ @@ -178,7 +182,8 @@ public Stream searchAllFoodOfAllStores(String store) { /*** - * this method returns the first food that matchs the + * This method returns the first food that matchs the. + * * @param food partial name of the food * @param canteen partial name of the canteen * @return Food object @@ -190,7 +195,8 @@ public Food searchFoodByNameByCanteen(String food, String canteen) { } /*** - * returns all food that contains the provided food name in the first canteen that matchs the canteen name + * Returns all food that contains the provided food name in the first canteen that matchs the canteen name. + * * @param food partial name of the food * @param canteen partial name of the canteen * @return Food Stream @@ -207,7 +213,8 @@ public Stream searchAllFoodByNameByCanteen(String food, String canteen) { } /*** - * returns a stream of food whose calorie is below the provided amount + * Returns a stream of food whose calorie is below the provided amount. + * * @param calorie the maximum calorie of the food * @return a stream */ @@ -216,7 +223,8 @@ public Stream searchAllFoodBelowCalorie( int calorie) { } /*** - * returns all food within the calorie range + * Returns all food within the calorie range. + * * @param minCalorie minimum calories * @param maxCalorie maxinum calories * @return @@ -226,7 +234,8 @@ public Stream searchAllFoodInCalorieRange( int minCalorie, int maxCalorie) } /*** - * Provides a data stream of all the food in the data base + * Provides a data stream of all the food in the data base. + * * @return a food stream */ private Stream foodStream() { @@ -234,4 +243,8 @@ private Stream foodStream() { .flatMap( x -> x.getStoreList().stream()) .flatMap( x -> x.getFoodList().stream()); } + + public List foodList() { + return foodStream().collect(Collectors.toList()); + } } diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 2572c66aff..48e2738fe6 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -1,5 +1,6 @@ package seedu.duke.food; + public class Food { private final String name; private final int calorie; diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java index 70c30be1ca..1168a32770 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -5,6 +5,8 @@ import static org.junit.jupiter.api.Assertions.*; class FoodTest { + private Food testFood; + public static void main(String[] args){ Food food = new Food("Kobe Beef", 480,50,40,30); System.out.println(food); From 1b3467483fd555a416f68460799868a5d29b5954 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 10 Oct 2020 20:51:41 +0800 Subject: [PATCH 046/374] Update Person class toString and its JUnit test --- src/main/java/seedu/duke/person/Person.java | 17 +++++++---------- src/test/java/seedu/duke/person/PersonTest.java | 16 +++++++--------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java index 1cd7b3b825..91e0c32c2f 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/duke/person/Person.java @@ -175,16 +175,13 @@ public void setActivityLevel(ActivityLevel newActivityLevel) { */ @Override public String toString() { - String userInformation = "Hi " + name + "! " - + "Here is your information:" + System.lineSeparator() - + System.lineSeparator() - + "Name: " + name + System.lineSeparator() - + "Gender: " + gender.getDescription() + System.lineSeparator() - + "Age: " + age + System.lineSeparator() - + "Height: " + height + "cm" + System.lineSeparator() - + "Original weight: " + originalWeight + "kg" + System.lineSeparator() - + "Target weight: " + targetWeight + "kg" + System.lineSeparator() - + "Activity level: " + activityLevel.getDescription(); + String userInformation = " Name: " + name + System.lineSeparator() + + " Gender: " + gender.getDescription() + System.lineSeparator() + + " Age: " + age + System.lineSeparator() + + " Height: " + height + "cm" + System.lineSeparator() + + " Original weight: " + originalWeight + "kg" + System.lineSeparator() + + " Target weight: " + targetWeight + "kg" + System.lineSeparator() + + " Activity level: " + activityLevel.getDescription(); return userInformation; } } diff --git a/src/test/java/seedu/duke/person/PersonTest.java b/src/test/java/seedu/duke/person/PersonTest.java index a9f20dbe5f..415fd43134 100644 --- a/src/test/java/seedu/duke/person/PersonTest.java +++ b/src/test/java/seedu/duke/person/PersonTest.java @@ -107,15 +107,13 @@ void setActivityLevel_personWithNullActivityLevel_returnsNullActivityLevel() { @Test void toString_person_returnsStringRepresentationOfPersonInformation() { - assertEquals("Hi Jack! Here is your information:" + System.lineSeparator() - + System.lineSeparator() - + "Name: Jack" + System.lineSeparator() - + "Gender: male" + System.lineSeparator() - + "Age: 21" + System.lineSeparator() - + "Height: 165cm" + System.lineSeparator() - + "Original weight: 75kg" + System.lineSeparator() - + "Target weight: 65kg" + System.lineSeparator() - + "Activity level: You engage in some form of light exercise or have a job that requires " + assertEquals(" Name: Jack" + System.lineSeparator() + + " Gender: male" + System.lineSeparator() + + " Age: 21" + System.lineSeparator() + + " Height: 165cm" + System.lineSeparator() + + " Original weight: 75kg" + System.lineSeparator() + + " Target weight: 65kg" + System.lineSeparator() + + " Activity level: You engage in some form of light exercise or have a job that requires " + "some physical activity.", person.toString()); } } From 123d10daa5e1ec7a2d6a20745c622cc184c149b4 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sat, 10 Oct 2020 22:11:23 +0800 Subject: [PATCH 047/374] calculator version0.4 Fixes #7 --- src/main/java/seedu/calculator/calculator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/calculator.java index 017bd4cf77..e6fd57fae0 100644 --- a/src/main/java/seedu/calculator/calculator.java +++ b/src/main/java/seedu/calculator/calculator.java @@ -18,10 +18,10 @@ public class calculator { public calculator(ArrayList foodList){ try { for(int i=0; i< foodList.size(); i++){ - total.calorie += foodList.getPortionSize(i)*foodList.getCalorie(i); - total.carbs += foodList.getPortionSize(i)*foodList.getCarbs(i); - total.protein += foodList.getPortionSize(i)*foodList.getProtein(i); - total.fats += foodList.getPortionSize(i)*foodList.getFats(i); + total.calorie += foodList.get(i).portionSize * foodList.get(i).calorie; + total.carbs += foodList.get(i).portionSize * foodList.get(i).carbs(i); + total.protein += foodList.get(i).portionSize * foodList.get(i).protein(i); + total.fats += foodList.get(i).portionSize * foodList.get(i).fats(i); } } catch (NullPointerException e) { System.out.println("Ops, This foodList is null!"); From 1468c3db55c67f3399df6597c0ea9b8464ef920f Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 11 Oct 2020 02:32:00 +0800 Subject: [PATCH 048/374] Add Ui.java --- src/main/java/seedu/duke/Ui.java | 295 +++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 src/main/java/seedu/duke/Ui.java diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java new file mode 100644 index 0000000000..55c0ee3ebb --- /dev/null +++ b/src/main/java/seedu/duke/Ui.java @@ -0,0 +1,295 @@ +package seedu.duke; + +/** + * Represents a text user interface. + * A Ui objects deals with user interaction by showing users the appropriate messages after a + * valid command is executed or when an error occurs. + */ +public class Ui { + + private static final String LINE_SEPARATOR = System.lineSeparator(); + + /** + * Constructs a Ui object. + */ + public Ui() { + } + + /** + * Prints the welcome message from DietBook when it is fist booted up. + */ + public void printWelcomeMessage() { + String logo = " _______ __ ______ ________ _______ ______ ______ __ __" + LINE_SEPARATOR + + "| __ \\| | ___|__ __| __ \\ / __ \\ / __ \\| | / /" + LINE_SEPARATOR + + "| | | | | |___ | | | |__| | | | | | | | |/ /" + LINE_SEPARATOR + + "| | | | | ___| | | | __ <| | | | | | | /" + LINE_SEPARATOR + + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + LINE_SEPARATOR + + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\" + LINE_SEPARATOR"; + print(logo + LINE_SEPARATOR + + "Hello! Welcome to DietBook!" + LINE_SEPARATOR + + "I am Diet, your guide to using DietBook. What is your name?" + LINE_SEPARATOR + + "Please input in the following format:" + LINE_SEPARATOR + + " name YOUR_NAME"); + } + + /** + * Prints a message asking the user to input their personal information related to dieting and health + * which includes gender, age, height, activity level, original weight and target weight. + * + * @param name The name of the user. + */ + public void printAskUserForPersonalInformationMessage(String name) { + print("Hi " + name + "!" + LINE_SEPARATOR + + "Before we get started, I would like to know about about you so that I can make more " + + "accurate calculations for you :)" + LINE_SEPARATOR + + "Therefore, could you please share with me the following:" + LINE_SEPARATOR + + "1. Your gender either F for female or M for male." + LINE_SEPARATOR + + "2. Your age which is a positive integer." + LINE_SEPARATOR + + "3. Your height in cm." + LINE_SEPARATOR + + "4. Your original weight in kg." + LINE_SEPARATOR + + "5. Your target weight in kg, or your original weight if your original weight is also " + + "your target weight." + LINE_SEPARATOR + + "6. Activity level from 1 to 5." + LINE_SEPARATOR + + " 1. " + ActvityLevel.NONE.getDescription() + LINE_SEPARATOR + + " 2. " + ActvityLevel.LOW.getDescription() + LINE_SEPARATOR + + " 3. " + ActvityLevel.MEDIUM.getDescription() + LINE_SEPARATOR + + " 4. " + ActvityLevel.HIGH.getDescription() + LINE_SEPARATOR + + " 5. " + ActvityLevel.EXTEREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + + "Please input your details in the following format:" + LINE_SEPARATOR + + " info g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL o/ORIGINAL_WEIGHT t/TARGET_WEIGHT" + + LINE_SEPARATOR + + " Example: info g/F a/21 h/165 l/2 o/65 t/55"); + //"4. Your current weight in kg." + //"5. Your target weight in kg, or your current weight if your current weight is also your target + // weight."; + //" info g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL c/CURRENT_WEIGHT t/TARGET_WEIGHT"); + //" Example: info g/F a/21 h/165 l/2 c/65 t/55"; + } + +// public void printWelcomeMessage() { +// String logo = " _______ __ ______ ________ _______ ______ ______ __ __" + LINE_SEPARATOR +// + "| __ \\| | ___|__ __| __ \\ / __ \\ / __ \\| | / /" + LINE_SEPARATOR +// + "| | | | | |___ | | | |__| | | | | | | | |/ /" + LINE_SEPARATOR +// + "| | | | | ___| | | | __ <| | | | | | | /" + LINE_SEPARATOR +// + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + LINE_SEPARATOR +// + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\" + LINE_SEPARATOR"; +// System.out.println(DIVIDER); +// System.out.println(logo); +// System.out.println("Hello! Welcome to DietBook!"); +// System.out.println("I am Diet, your guide to using DietBook."); +// System.out.println("Before we get started, I would like to know about about you so that I can make " +// + "more accurate calculations for you :)"); +// System.out.println("Therefore, could you please share with me the following:"); +// System.out.println("1. Your name."); +// System.out.println("2. Your gender either F for female or M for male".); +// System.out.println("3. Your age which is a positive integer."); +// System.out.println("4. Your height in cm."); +// System.out.println("5. Your current weight in kg."); +// System.out.println("5. Your original weight in kg."); +// System.out.println("6. Your target weight in kg, or your original weight if your " +// + "original weight is also your target weight."); +// System.out.println("6. Your target weight in kg, or your current weight if your " +// + "current weight is also your target weight."); +// System.out.println("7. Activity level from 1 to 5."); +// System.out.println(" 1. " + ActvityLevel.NONE.getDescription()); +// System.out.println(" 2. " + ActvityLevel.LOW.getDescription()); +// System.out.println(" 3. " + ActvityLevel.MEDIUM.getDescription()); +// System.out.println(" 4. " + ActvityLevel.HIGH.getDescription()); +// System.out.println(" 5. " + ActvityLevel.EXTEREME.getDescription()); +// System.out.println(LINE_SEPARATOR); +// System.out.println("Please input your details in the following format:"); +// System.out.println(" info n/NAME g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL o/ORIGINAL_WEIGHT " +// + "t/TARGET_WEIGHT"); +// System.out.println(" info n/name g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL c/CURRENT_WEIGHT " +// + "t/TARGET_WEIGHT"); +// System.out.println(" Example: info n/Suzy g/F a/21 h/165 l/2 o/65 t/55"); +// System.out.println(" Example: info n/Suzy g/F a/21 h/165 l/2 c/65 t/55"); +// System.out.println(DIVIDER); +// } + + /** + * Prints a message that notifies the user that DietBook has been initialised and shows a list of user + * commands that the user can input. + */ + public void printTutorialMessage() { + print("Thank you! DietBook has been initialised and you may start by entering any of the commands " + + "listed below." + LINE_SEPARATOR + LINE_SEPARATOR + + "To add you own food: add n/FOOD_NAME x/PORTION_SIZE k/CALORIE [c/CARB] [p/PROTEIN] " + + "[f/FAT]" + LINE_SEPARATOR + + "To view all food in DietBook: list" + LINE_SEPARATOR + + "To delete a food from DietBook: delete INDEX" + LINE_SEPARATOR + + "To delete all food items from the DietBook: clear" + LINE_SEPARATOR + + "To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE\n" + LINE_SEPARATOR + + "To view all food in the database: data" + LINE_SEPARATOR + + "To show user information: userinfo" + LINE_SEPARATOR + + "To calculate carbohydrate intake: calculate carbohydrate" + LINE_SEPARATOR + + "To calculate calorie intake: calculate calorie" + LINE_SEPARATOR + + "To calculate protein intake: calculate protein" + LINE_SEPARATOR + + "To calculate fat intake: calculate fat" + LINE_SEPARATOR + + "To calculate all nutritional intake: calculate all" + LINE_SEPARATOR + + "To exit DietBook: exit"); + } + + /** + * Prints a message to show that the food specified has been added to the food list. + * + * @param newFood The new food item that was added to the food list. + */ + public void printNewFood(Food newFood) { + print("Got it! I've added this food item:" + LINE_SEPARATOR + + " " + newFood); + } + + /** + * Prints all the food items in the food list in the order that they were added or a message stating + * that the food list is empty if there are no food items. + * + * @param foodList The food list containing all the food items. + */ + public void printFoodList(FoodList foodList) { + if (foodList.getFoods().isEmpty()) { + print("DietBook is currently empty."); + } else { + String allFood = ""; + int foodItemNumber = 1; + for (Food food : foodList.getFoods()) { + allFood += LINE_SEPARATOR + " " + foodItemNumber + "." + food; + foodItemNumber++; + } + print("Here are the food items in DietBook:" + allFood); + } + } + + /** + * Prints all the food in the database sorted by the canteen and then the store it is found. + * + * @param foodDatabase The list containing all the food items stored in the database. + */ + public void printDatabase(List foodDatabase) { + String allFood = ""; + int foodItemNumber = 1; + for (Food food: foodDatabase) { + allFood += LINE_SEPARATOR + " " + foodItemNumber + "." + food; + foodItemNumber++; + } + print("Here are the food items in the database:" + allFood); + } + + /** + * Prints all the information related to the user. + * + * @param person The user. + */ + public void printPersonInformation(Person person) { + print("Here is your information:" + LINE_SEPARATOR + + person); + } + + /** + * Prints the total amount of carbohydrates consumed by the user. + * + * @param carbohydrateIntake The total amount of carbohydrates of all the food in the food list. + */ + public void printCarbohydrateIntake(int carbohydrateIntake) { + print("Total carbohydrate intake: " + carbohydrateIntake + "g"); + } + + /** + * Prints the total amount of calories consumed by the user. + * + * @param calorieIntake The total amount of calories of all the food in the food list. + */ + public void printCalorieIntake(int calorieIntake) { + print("Total calorie intake: " + calorieIntake + "kcal"); + } + + /** + * Prints the total amount of proteins consumed by the user. + * + * @param proteinIntake The total amount of proteins of all the food in the food list. + */ + public void printProteinIntake(int proteinIntake) { + print("Total protein intake: " + proteinIntake + "g"); + } + + /** + * Prints the total amount of fats consumed by the user. + * + * @param fatIntake The total amount of fats of all the food in the food list. + */ + public void printFatIntake(int fatIntake) { + print("Total fat intake: " + fatIntake + "g"); + } + + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user. + * + * @param calorieIntake The total amount of calories of all the food in the food list. + * @param carbohydrateIntake The total amount of carbohydrates of all the food in the food list. + * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param fatIntake The total amount of fats of all the food in the food list. + */ + public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, int proteinIntake, + int fatIntake) { + print("Total calorie intake: " + calorieIntake + "kcal" + LINE_SEPARATOR + + "Total carbohydrate intake: " + carbohydrateIntake + "g" + LINE_SEPARATOR + + "Total protein intake: " + proteinIntake + "g" + LINE_SEPARATOR + + "Total fat intake: " + fatIntake + "g"); + } + +// public void printNutrientIntake(String nutritionalIntake) { +// print(nutritionalIntake); +// } + + /** + * Prints a message to show that the food specified has been deleted from the food list. + * + * @param deletedFood The food that was deleted from the food list. + */ + public void printDeletedFood(Food deletedFood) { + print("Noted. I've removed this food item:" + LINE_SEPARATOR + + " " + deletedFood); + } + + /** + * Prints a message to show that the food list has been successfully cleared and is now empty. + */ + public void printClearFoodListMessage() { + print("All previous data has been deleted..." + LINE_SEPARATOR + + "DietBook is now empty."); + } + + /** + * Prints an exit message when DietBook is closed. + * + * @param name The name of the user. + */ + public void printExitMessage(String name) { + print("Bye " + name + "! Hope to see you again soon!"); + } + + /** + * Prints an error message given what or where the error is. + * + * @param errorMessage Message detailing what or where the error is. + */ + public void printErrorMessage(String errorMessage) { + print(":( Oh no..." + errorMessage); + } + + /** + * Prints the given message to the user. + * + * @param message The message to show the user. + */ + public void print(String message) { + String divider = + "__________________________________________________________________________________________" + + "__________"; + + System.out.println(divider + LINE_SEPARATOR + + message + LINE_SEPARATOR + + divider); + + } +} From 4ce010a6256df4e161e47787faa48cbf831d36af Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 11 Oct 2020 02:33:32 +0800 Subject: [PATCH 049/374] Correct spelling errors --- src/main/java/seedu/duke/Ui.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 55c0ee3ebb..5bae580485 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -50,11 +50,11 @@ public void printAskUserForPersonalInformationMessage(String name) { + "5. Your target weight in kg, or your original weight if your original weight is also " + "your target weight." + LINE_SEPARATOR + "6. Activity level from 1 to 5." + LINE_SEPARATOR - + " 1. " + ActvityLevel.NONE.getDescription() + LINE_SEPARATOR - + " 2. " + ActvityLevel.LOW.getDescription() + LINE_SEPARATOR - + " 3. " + ActvityLevel.MEDIUM.getDescription() + LINE_SEPARATOR - + " 4. " + ActvityLevel.HIGH.getDescription() + LINE_SEPARATOR - + " 5. " + ActvityLevel.EXTEREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + + " 1. " + ActivityLevel.NONE.getDescription() + LINE_SEPARATOR + + " 2. " + ActivityLevel.LOW.getDescription() + LINE_SEPARATOR + + " 3. " + ActivityLevel.MEDIUM.getDescription() + LINE_SEPARATOR + + " 4. " + ActivityLevel.HIGH.getDescription() + LINE_SEPARATOR + + " 5. " + ActivityLevel.EXTREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + "Please input your details in the following format:" + LINE_SEPARATOR + " info g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL o/ORIGINAL_WEIGHT t/TARGET_WEIGHT" + LINE_SEPARATOR From 99176291093d919f13af865e1bf9646e724686f8 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 11 Oct 2020 11:22:55 +0800 Subject: [PATCH 050/374] add FoodList Wrapper class around the food object and its necessary details. Uses FoodEntry which is an internal data class to support storage of more details beyond food. Has FoodListManager to streamline and abstract FoodList, while allowing for portential FP implementation later on. --- src/main/java/seedu/duke/list/FoodEntry.java | 36 +++++++++ src/main/java/seedu/duke/list/FoodList.java | 78 +++++++++++++++++++ .../java/seedu/duke/list/FoodListManager.java | 42 ++++++++++ .../duke/list/FoodNotFoundException.java | 4 + src/test/java/seedu/duke/FoodListTest.java | 19 +++++ 5 files changed, 179 insertions(+) create mode 100644 src/main/java/seedu/duke/list/FoodEntry.java create mode 100644 src/main/java/seedu/duke/list/FoodList.java create mode 100644 src/main/java/seedu/duke/list/FoodListManager.java create mode 100644 src/main/java/seedu/duke/list/FoodNotFoundException.java create mode 100644 src/test/java/seedu/duke/FoodListTest.java diff --git a/src/main/java/seedu/duke/list/FoodEntry.java b/src/main/java/seedu/duke/list/FoodEntry.java new file mode 100644 index 0000000000..94d31091d9 --- /dev/null +++ b/src/main/java/seedu/duke/list/FoodEntry.java @@ -0,0 +1,36 @@ +package seedu.duke.list; +import seedu.duke.food.Food; + + +/** + * Data class to story both serving sizes and a food object as a single object + */ +public class FoodEntry { + private int portionSize; + private Food food; + + public FoodEntry(int portionSize, Food food) { + this.portionSize = portionSize; + this.food = food; + } + + public FoodEntry(int portionSize, String name, int calorie, + int carbohydrate, int protein, int fat) { + this.portionSize = portionSize; + this.food = new Food (name, calorie, carbohydrate, protein, fat); + } + + public Food getFood() { + return food; + } + + public int portionSize() { + return portionSize; + } + + @Override + public String toString() { + return String.format("%s -- (%s)", food.toString(), portionSize); + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java new file mode 100644 index 0000000000..d4268dc984 --- /dev/null +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -0,0 +1,78 @@ +package seedu.duke.list; +import java.util.ArrayList; +import seedu.duke.food.Food; + + +/** + * Wrapper class for the implementation of foodlist as an arraylist of foodEntry objects + * Foodlist does not return its ArrayList nor foodEntry objects. + * This is a stateful object. + */ +public class FoodList { + private ArrayList foodEntries; + + public FoodList() { + this.foodEntries = new ArrayList<>(); + } + + protected FoodList(ArrayList entries) { + this.foodEntries = entries; + } + + /** + * Adds food of portion size directly into the foodlist as an entry. + * When date functionality is added, this method will need to be overhauled. + * The adding feature will be largely pushed to FoodListManager (to figure out dates) + * @param portionSize + * @param food + * @return + */ + public String addFood(int portionSize, Food food) { + FoodEntry toAdd = new FoodEntry(portionSize, food); + foodEntries.add(toAdd); + return toAdd.toString(); + } + + public String addFood (int portionSize, String name, int calorie, + int carbohydrate, int protein, int fat) { + FoodEntry toAdd = new FoodEntry(portionSize, name, calorie, carbohydrate, protein, fat); + foodEntries.add(toAdd); + return toAdd.toString(); + } + + /** + * Food database search functionality support. + * Currently just throws a not found exception when called in this manner. + * @param portionSize + * @param name + * @return + * @throws FoodNotFoundException + */ + public String addFood (int portionSize, String name) throws FoodNotFoundException{ + throw new FoodNotFoundException(); + } + + + public String delete (int index) throws IndexOutOfBoundsException { + try{ + return FoodListManager.deleteEntry(foodEntries, index).toString(); + } catch (IndexOutOfBoundsException e) { + throw e; + } + } + + public boolean clear() { + this.foodEntries = new ArrayList<>(); + return true; + } + + public ArrayList getFoods() { + return FoodListManager.listToFoods(foodEntries); + } + + @Override + public String toString() { + return FoodListManager.listToString(foodEntries); + } + +} diff --git a/src/main/java/seedu/duke/list/FoodListManager.java b/src/main/java/seedu/duke/list/FoodListManager.java new file mode 100644 index 0000000000..a6545591d1 --- /dev/null +++ b/src/main/java/seedu/duke/list/FoodListManager.java @@ -0,0 +1,42 @@ +package seedu.duke.list; + +import seedu.duke.food.Food; +import java.util.ArrayList; + +/** + * Class with static methods to execute "complex commands" on FoodList. + * This class handles methods that extend beyond the simple function of an arraylist + * Class contains static methods with logic beyond adding, removing, and instantiating new lists + * This class may be used to support functional programming by merging these function into functors + */ +public class FoodListManager { + + protected static String listToString(ArrayList list) { + String listString = ""; + + for (int i = 1; i <= list.size(); i++) { + FoodEntry task = list.get(i - 1); + listString += i + ". " + + task.toString() + "\n"; + } + return listString; + } + + protected static ArrayList listToFoods(ArrayList list) { + ArrayList foods = new ArrayList<>(); + list.forEach( x -> { + foods.add(x.getFood()); + }); + return foods; + } + + protected static Food deleteEntry(ArrayList list, int index) throws IndexOutOfBoundsException { + int indexToDelete = index - 1; + try { + return list.remove(indexToDelete).getFood(); + } + catch (IndexOutOfBoundsException e) { + throw e; + } + } +} diff --git a/src/main/java/seedu/duke/list/FoodNotFoundException.java b/src/main/java/seedu/duke/list/FoodNotFoundException.java new file mode 100644 index 0000000000..ca99d41a3e --- /dev/null +++ b/src/main/java/seedu/duke/list/FoodNotFoundException.java @@ -0,0 +1,4 @@ +package seedu.duke.list; + +public class FoodNotFoundException extends Exception{ +} diff --git a/src/test/java/seedu/duke/FoodListTest.java b/src/test/java/seedu/duke/FoodListTest.java new file mode 100644 index 0000000000..96b31d9647 --- /dev/null +++ b/src/test/java/seedu/duke/FoodListTest.java @@ -0,0 +1,19 @@ +package seedu.duke; + +import seedu.duke.list.FoodList; +import seedu.duke.food.Food; +import org.junit.jupiter.api.Test; + + +class FoodListTest { + public static void main(String[] args) { + Food food = new Food("Kobe Beef", 480,50,40,30); + FoodList foodList = new FoodList(); + + System.out.println(foodList.addFood(3, food)); + System.out.println(foodList.addFood(2, "Sashimi", 100, 0, 30, 10)); + System.out.println(foodList); + System.out.println(foodList.delete(1)); + System.out.println(foodList); + } +} \ No newline at end of file From 09bcfee2245f519dda2e5b22a204cebdf5901e3b Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 11 Oct 2020 11:35:33 +0800 Subject: [PATCH 051/374] add JUNIT tests --- src/test/java/seedu/duke/food/FoodTest.java | 22 ++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java index e2b03edb10..7a45d44cd0 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -1,11 +1,27 @@ package seedu.duke.food; +import seedu.duke.food.Food; + +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + class FoodTest { - public static void main(String[] args) { - Food food = new Food("Kobe Beef", 480,50,40,30); - System.out.println(food); + private Food testFood; + + @BeforeEach + public void setUp(){ + testFood = new Food("Kobe Beef", 480,50,40,30); + } + + @Test + public void footTest(){ + assertEquals(testFood.getCalorie(), 480); + assertEquals(testFood.getCarbohydrate(), 50); + assertEquals(testFood.getProtein(), 40); + assertEquals(testFood.getFats(),30); } } \ No newline at end of file From 4ad0aefa0ed40ebac1c95fb546e0321845556fa8 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 11 Oct 2020 12:16:55 +0800 Subject: [PATCH 052/374] style changes tweaks to adhere to checkstyle format specified by project --- src/main/java/seedu/duke/list/FoodEntry.java | 5 ++-- src/main/java/seedu/duke/list/FoodList.java | 23 ++++++++++--------- .../java/seedu/duke/list/FoodListManager.java | 5 ++-- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/duke/list/FoodEntry.java b/src/main/java/seedu/duke/list/FoodEntry.java index 94d31091d9..8016d6bf32 100644 --- a/src/main/java/seedu/duke/list/FoodEntry.java +++ b/src/main/java/seedu/duke/list/FoodEntry.java @@ -1,9 +1,10 @@ package seedu.duke.list; + import seedu.duke.food.Food; /** - * Data class to story both serving sizes and a food object as a single object + * Data class to story both serving sizes and a food object as a single object. */ public class FoodEntry { private int portionSize; @@ -17,7 +18,7 @@ public FoodEntry(int portionSize, Food food) { public FoodEntry(int portionSize, String name, int calorie, int carbohydrate, int protein, int fat) { this.portionSize = portionSize; - this.food = new Food (name, calorie, carbohydrate, protein, fat); + this.food = new Food(name, calorie, carbohydrate, protein, fat); } public Food getFood() { diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java index d4268dc984..8e4c67d36e 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -1,10 +1,11 @@ package seedu.duke.list; + import java.util.ArrayList; import seedu.duke.food.Food; /** - * Wrapper class for the implementation of foodlist as an arraylist of foodEntry objects + * Wrapper class for the implementation of foodlist as an arraylist of foodEntry objects. * Foodlist does not return its ArrayList nor foodEntry objects. * This is a stateful object. */ @@ -23,9 +24,9 @@ protected FoodList(ArrayList entries) { * Adds food of portion size directly into the foodlist as an entry. * When date functionality is added, this method will need to be overhauled. * The adding feature will be largely pushed to FoodListManager (to figure out dates) - * @param portionSize - * @param food - * @return + * @param portionSize integer to designate number of servings + * @param food food object to be added + * @return string representation of the food object added */ public String addFood(int portionSize, Food food) { FoodEntry toAdd = new FoodEntry(portionSize, food); @@ -33,7 +34,7 @@ public String addFood(int portionSize, Food food) { return toAdd.toString(); } - public String addFood (int portionSize, String name, int calorie, + public String addFood(int portionSize, String name, int calorie, int carbohydrate, int protein, int fat) { FoodEntry toAdd = new FoodEntry(portionSize, name, calorie, carbohydrate, protein, fat); foodEntries.add(toAdd); @@ -43,17 +44,17 @@ public String addFood (int portionSize, String name, int calorie, /** * Food database search functionality support. * Currently just throws a not found exception when called in this manner. - * @param portionSize - * @param name - * @return - * @throws FoodNotFoundException + * @param portionSize integer to designate number of servings + * @param name food object to be added + * @return string representation of the food object added + * @throws FoodNotFoundException custom exception to indicate search for food in database failed. */ - public String addFood (int portionSize, String name) throws FoodNotFoundException{ + public String addFood(int portionSize, String name) throws FoodNotFoundException { throw new FoodNotFoundException(); } - public String delete (int index) throws IndexOutOfBoundsException { + public String delete(int index) throws IndexOutOfBoundsException { try{ return FoodListManager.deleteEntry(foodEntries, index).toString(); } catch (IndexOutOfBoundsException e) { diff --git a/src/main/java/seedu/duke/list/FoodListManager.java b/src/main/java/seedu/duke/list/FoodListManager.java index a6545591d1..cff588f01b 100644 --- a/src/main/java/seedu/duke/list/FoodListManager.java +++ b/src/main/java/seedu/duke/list/FoodListManager.java @@ -24,7 +24,7 @@ protected static String listToString(ArrayList list) { protected static ArrayList listToFoods(ArrayList list) { ArrayList foods = new ArrayList<>(); - list.forEach( x -> { + list.forEach(x -> { foods.add(x.getFood()); }); return foods; @@ -34,8 +34,7 @@ protected static Food deleteEntry(ArrayList list, int index) throws I int indexToDelete = index - 1; try { return list.remove(indexToDelete).getFood(); - } - catch (IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) { throw e; } } From 71b58f075d93d0cc082b4ec597179a159747b57e Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 11 Oct 2020 12:26:33 +0800 Subject: [PATCH 053/374] style changes --- src/main/java/seedu/duke/list/FoodList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java index 8e4c67d36e..4d862540a1 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -55,7 +55,7 @@ public String addFood(int portionSize, String name) throws FoodNotFoundException public String delete(int index) throws IndexOutOfBoundsException { - try{ + try { return FoodListManager.deleteEntry(foodEntries, index).toString(); } catch (IndexOutOfBoundsException e) { throw e; From c436e5207223903f737e04c199e9d40b65d9c8ff Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 11 Oct 2020 13:14:07 +0800 Subject: [PATCH 054/374] fixed style to pass gradle checkstyle tests add database tests add getFoodList function to return a list of All Food in the data base --- .../java/seedu/duke/database/Canteen.java | 9 +- .../java/seedu/duke/database/DataBase.java | 113 ++++++++++-------- src/main/java/seedu/duke/database/Store.java | 13 +- src/main/java/seedu/duke/food/Food.java | 2 +- .../seedu/duke/database/DataBaseTest.java | 75 ++++++++++++ 5 files changed, 149 insertions(+), 63 deletions(-) diff --git a/src/main/java/seedu/duke/database/Canteen.java b/src/main/java/seedu/duke/database/Canteen.java index fd3a92deff..1243b0d9be 100644 --- a/src/main/java/seedu/duke/database/Canteen.java +++ b/src/main/java/seedu/duke/database/Canteen.java @@ -7,20 +7,21 @@ public class Canteen { private final String name; private final ArrayList storeList; - public Canteen(String name){ + public Canteen(String name) { this.name = name; this.storeList = new ArrayList<>(); } - /*** - * name of the canteen is for filtering purposes + /** + * Name of the canteen is for filtering purposes. + * * @return name of canteen */ public String getName() { return name; } - public void addStore(Store store){ + public void addStore(Store store) { storeList.add(store); } diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 98c224e836..74672a54ef 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -20,8 +20,8 @@ public class DataBase { private static final String DATA_FILE_SEPERATOR = "\\|"; private static final String rootDirectory = System.getProperty("user.dir"); - private static final String dataFileFolder = "src" + File.separator + "main" + File.separator + - "java" + File.separator + "seedu" + File.separator + "duke" + File.separator + "database"; + private static final String dataFileFolder = "src" + File.separator + "main" + File.separator + + "java" + File.separator + "seedu" + File.separator + "duke" + File.separator + "database"; private final List canteenList; @@ -29,8 +29,8 @@ public DataBase() { this.canteenList = new ArrayList<>(); } - /*** - * Reads a file from the data base and puts it into the DataBase object + /** + * Reads a file from the data base and puts it into the DataBase object. */ public void init() throws FileNotFoundException { String fileFolder = rootDirectory + File.separator + dataFileFolder; @@ -45,7 +45,7 @@ public void init() throws FileNotFoundException { start = true; continue; } - if (!(start)){ + if (!(start)) { continue; } if (fileLine.equals(STOP_SYMBOL)) { @@ -55,12 +55,13 @@ public void init() throws FileNotFoundException { } } - /*** + /** * This function is called right after the canteen name is provided * The very next line that the file reads is the store name * It will turn call fillStore with that name inserted, when the function fillStore * finishes executing, fileRead.nextLine() can either provide a new store name or UP_SYMBOL - * if the UP_SYMBOL is provided, the function ends and the final Canteen object is returned + * if the UP_SYMBOL is provided, the function ends and the final Canteen object is returned. + * * @param name name of store * @param fileSegment the file reader with the next line being a food item or UP_SYMBOL * @return Canteen objected with all it's stores loaded @@ -75,10 +76,11 @@ private Canteen fillCanteen(String name, Scanner fileSegment) { return canteen; } - /*** - * This function is called right after the store name is provided - * The very next line in the file should be the first food to be added - * The function stops when it hits the line of the file that says UP_SYMBOL + /** + * This function is called right after the store name is provided. + * The very next line in the file should be the first food to be added. + * The function stops when it hits the line of the file that says UP_SYMBOL. + * * @param name name of the store * @param fileSegment the Scanner object used for the init() function * @return the completed store with all the food loaded @@ -89,8 +91,8 @@ private Store fillStore(String name, Scanner fileSegment) { String fileLine = fileSegment.nextLine(); String[] fileData = fileLine.split(DATA_FILE_SEPERATOR); while (!(fileLine.equals(UP_SYMBOL))) { - food = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]) - ,Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); + food = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]), + Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); store.addFood(food); fileLine = fileSegment.nextLine(); fileData = fileLine.split(DATA_FILE_SEPERATOR); @@ -98,16 +100,16 @@ private Store fillStore(String name, Scanner fileSegment) { return store; } - /*** - * Debugging function prints out all contents + /** + * Debugging function prints out all contents. */ public void printAllData() { System.out.println("Printing out all data"); - for (Canteen canteen : canteenList){ + for (Canteen canteen : canteenList) { System.out.println("Canteeh : " + canteen.getName()); - for (Store store : canteen.getStoreList()){ + for (Store store : canteen.getStoreList()) { System.out.println("Store : " + store.getName()); - for (Food food : store.getFoodList()){ + for (Food food : store.getFoodList()) { System.out.println(food); } } @@ -117,76 +119,83 @@ public void printAllData() { // -------- Search functions -------- - /*** - * this method searchs the whole data base and returns the first food item whose name contains the provided string + /** + * This method searchs the whole data base and returns the first food item whose name contains the provided string. + * ( CASE SENSITIVE ! ) + * * @param food part of the name of the food * @return Food * @throws NoSuchElementException if no food contains the name provided */ public Food searchFoodByName(String food) { - return foodStream().filter( x -> x.getName().contains(food)).findFirst().orElseThrow(); + return foodStream().filter(x -> x.getName().contains(food)).findFirst().orElseThrow(); } - /*** - * this method searchs the whole data base and returns all of the food whose name contains the provided string + /** + * This method searchs the whole data base and returns all of the food whose name contains the provided string. * @param food part of the name of the food e.g. chicken * @return data stream of all food items */ public Stream searchAllFoodContainingName(String food) { - return foodStream().filter( x -> x.getName().contains(food)); + return foodStream().filter(x -> x.getName().contains(food)); } - /*** + /** * Search for the first food that contains the string provided in the first store which matchs the store * string provided. * * @param food partial name of the food * @param store partial name of the store * @return Food object + * @throws NoSuchElementException if no food contains the name provided */ public Food searchFoodByNameByStore(String food, String store) { return searchAllFoodByStore(store) - .filter( x -> x.getName().contains(food)) + .filter(x -> x.getName().contains(food)) .findFirst() .orElseThrow(); } - /*** - * This method returns a stream of all the food in the first store that contains the given string. + /** + * Returns a stream of all the food in the first store that contains the given string. * * @param store partial name of the store * @return food stream + * @throws NoSuchElementException if no there is no store */ public Stream searchAllFoodByStore(String store) { return canteenList.stream() - .flatMap( x -> x.getStoreList().stream()) - .filter( x -> x.getName().contains(store)) + .flatMap(x -> x.getStoreList().stream()) + .filter(x -> x.getName().contains(store)) .findFirst() .orElseThrow() .getFoodList() .stream(); } - /*** - * This method returns a stream of all the food in all stores that contains the given string. + /** + * Returns a stream of all the food in all stores that contains the given string. * * @param store partial name of the store * @return food stream */ public Stream searchAllFoodOfAllStores(String store) { return canteenList.stream() - .flatMap( x -> x.getStoreList().stream()) - .filter( x -> x.getName().contains(store)) - .flatMap( x -> x.getFoodList().stream()); + .flatMap(x -> x.getStoreList().stream()) + .filter(x -> x.getName().contains(store)) + .flatMap(x -> x.getFoodList().stream()); } - /*** - * This method returns the first food that matchs the. + /** + * Returns the first food that contains the food String provided that is in the first canteen that contains the + * canteen String provided. * * @param food partial name of the food * @param canteen partial name of the canteen * @return Food object + * + * @throws NoSuchElementException if no food contains the name provided */ public Food searchFoodByNameByCanteen(String food, String canteen) { return searchAllFoodByNameByCanteen(food, canteen) @@ -194,7 +203,7 @@ public Food searchFoodByNameByCanteen(String food, String canteen) { .orElseThrow(); } - /*** + /** * Returns all food that contains the provided food name in the first canteen that matchs the canteen name. * * @param food partial name of the food @@ -208,43 +217,43 @@ public Stream searchAllFoodByNameByCanteen(String food, String canteen) { .orElseThrow() .getStoreList() .stream() - .flatMap( x -> x.getFoodList().stream()) + .flatMap(x -> x.getFoodList().stream()) .filter(x -> x.getName().contains(food)); } - /*** + /** * Returns a stream of food whose calorie is below the provided amount. * * @param calorie the maximum calorie of the food - * @return a stream + * @return food stream */ - public Stream searchAllFoodBelowCalorie( int calorie) { - return foodStream().filter( x -> x.getCalorie() < calorie); + public Stream searchAllFoodBelowCalorie(int calorie) { + return foodStream().filter(x -> x.getCalorie() < calorie); } - /*** + /** * Returns all food within the calorie range. * * @param minCalorie minimum calories * @param maxCalorie maxinum calories - * @return + * @return food stream */ - public Stream searchAllFoodInCalorieRange( int minCalorie, int maxCalorie){ - return foodStream().filter( x -> x.getCalorie() <= maxCalorie && x.getCalorie() >= minCalorie ); + public Stream searchAllFoodInCalorieRange(int minCalorie, int maxCalorie) { + return foodStream().filter(x -> x.getCalorie() <= maxCalorie && x.getCalorie() >= minCalorie); } - /*** + /** * Provides a data stream of all the food in the data base. * * @return a food stream */ - private Stream foodStream() { + public Stream foodStream() { return canteenList.stream() - .flatMap( x -> x.getStoreList().stream()) - .flatMap( x -> x.getFoodList().stream()); + .flatMap(x -> x.getStoreList().stream()) + .flatMap(x -> x.getFoodList().stream()); } - public List foodList() { + public List getFoodList() { return foodStream().collect(Collectors.toList()); } } diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/duke/database/Store.java index 5d1727d67b..af311ae856 100644 --- a/src/main/java/seedu/duke/database/Store.java +++ b/src/main/java/seedu/duke/database/Store.java @@ -11,14 +11,15 @@ public class Store { private final ArrayList foodList; - public Store(String name){ + public Store(String name) { this.name = name; this.foodList = new ArrayList<>(); } - /*** - * the name of the store will be used for filtering purposes - * @return + /** + * The name of the store will be used for filtering purposes. + * + * @return store name */ public String getName() { @@ -26,9 +27,9 @@ public String getName() { } /** - * This function should only be called when we initialize the data base from the text file + * This function should only be called when we initialize the data base from the text file. */ - public void addFood(Food food){ + public void addFood(Food food) { foodList.add(food); } diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 48e2738fe6..c43d8f0384 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -8,7 +8,7 @@ public class Food { private final int protein; private final int fats; - public Food(String name, int calorie, int carbohydrate, int protein, int fats){ + public Food(String name, int calorie, int carbohydrate, int protein, int fats) { this.name = name; this.calorie = calorie; this.carbohydrate = carbohydrate; diff --git a/src/test/java/seedu/duke/database/DataBaseTest.java b/src/test/java/seedu/duke/database/DataBaseTest.java index 4551e8571a..a060a3ea2c 100644 --- a/src/test/java/seedu/duke/database/DataBaseTest.java +++ b/src/test/java/seedu/duke/database/DataBaseTest.java @@ -1,6 +1,7 @@ package seedu.duke.database; import java.io.FileNotFoundException; +import java.util.NoSuchElementException; import static org.junit.jupiter.api.Assertions.*; @@ -12,6 +13,80 @@ public static void main(String[] args){ } catch (FileNotFoundException e) { e.printStackTrace(); } + // ----- Print everything in the data base ----- database.printAllData(); + + // ---- Using stream version to print ----- + System.out.println("------------ printing using food stream ------------"); + database.foodStream().forEach(System.out::println); + + // ---- Printing out as list ----- + System.out.println("---------- printing food using list --------------"); + database.getFoodList().forEach(System.out::println); + + // ---- search food by name test ----- + try { + System.out.println("------- testing the searchFoodByName function -------"); + System.out.println("Input: Prawn ## OutPut: " + database.searchFoodByName("Prawn")); + System.out.println("Input: Mee ## OutPut: " + database.searchFoodByName("Mee")); + System.out.println("Input: lobster ## OutPut: " + database.searchFoodByName("lobster")); + + System.out.println("Input: Prawn ## OutPut: " + database.searchFoodByName("koala bears")); + } + catch (NoSuchElementException e){ + System.out.println("No such food found! " + e); + } + + // ---- search food by store name test ---- + System.out.println("------- testing the search food by store and by name function --------"); + try { + System.out.println("Input: Fried , Halal Mini Wok ## OutPut: " + database.searchFoodByNameByStore("Fried", "Halal Mini Wok")); + System.out.println("Input: Chicken , Halal Mini Wok ## OutPut: " + database.searchFoodByNameByStore("Chicken", "Halal Mini Wok")); + System.out.println("Input: Chicken , Ayam Penyet ## OutPut: " + database.searchFoodByNameByStore("Chicken", "Ayam Penyet")); + System.out.println("Input: lobster , Michelin ## OutPut: " + database.searchFoodByNameByStore("lobster", "Michelin")); + System.out.println("Input: fish , Halal Mini Wok ## OutPut: " + database.searchFoodByNameByStore("fish", "Halal Mini Wok")); + } + catch (NoSuchElementException e){ + System.out.println("No such food found! " + e); + } + + // ---- search all food by store ----- + try { + System.out.println("------- testing the search ALL food by store and by name function --------"); + System.out.println("------- Input: Halal Mini Wok -------- "); + database.searchAllFoodByStore("Halal Mini Wok").forEach(System.out::println); + System.out.println("------- Input: Ayam Penyet -------- "); + database.searchAllFoodByStore("Ayam Penyet").forEach(System.out::println); + System.out.println("------- Input: Michelin -------- "); + database.searchAllFoodByStore("Michelin").forEach(System.out::println); + System.out.println("------- Input: Gordan Ramsey's restaurant -------- "); + database.searchAllFoodByStore("Gordon Ramsey's restaurant").forEach(System.out::println); + } + catch (NoSuchElementException e) { + System.out.println("There is no such store! " + e); + } + + // ---- search food by Name by canteen ------ + try { + System.out.println("------- testing the search food by canteen and by name function --------"); + System.out.println("Input: Fried , Science ## OutPut: " + database.searchFoodByNameByCanteen("Fried", "Science")); + System.out.println("Input: Chicken , Science ## OutPut: " + database.searchFoodByNameByCanteen("Chicken", "Science")); + System.out.println("Input: lobster , Science ## OutPut: " + database.searchFoodByNameByCanteen("lobster", "Science")); + System.out.println("Input: lobster , Raffles Hotel Suite ## OutPut: " + database.searchFoodByNameByCanteen("lobster", "Raffles Hotel Suite")); + } + catch (NoSuchElementException e) { + System.out.println("There is either no such canteen or no such food in that canteen!" + e); + } + + // ---- search all food below calorie ------ + + System.out.println("------- testing the search food below calorie function --------"); + System.out.println(" ---- Input : 400"); + database.searchAllFoodBelowCalorie(400).forEach(System.out::println); + System.out.println(" ---- Input : 200"); + database.searchAllFoodBelowCalorie(200).forEach(System.out::println); + System.out.println(" ---- Input : 3428"); + database.searchAllFoodBelowCalorie(3428).forEach(System.out::println); + } } \ No newline at end of file From a457ed2e1421ef0a3db2fd07274120622a1d9f66 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 11 Oct 2020 15:34:46 +0800 Subject: [PATCH 055/374] Change input parameter for two methods --- src/main/java/seedu/duke/Ui.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 5bae580485..2c0fab226d 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,5 +1,7 @@ package seedu.duke; +import java.util.ArrayList; + /** * Represents a text user interface. * A Ui objects deals with user interaction by showing users the appropriate messages after a @@ -144,15 +146,15 @@ public void printNewFood(Food newFood) { * Prints all the food items in the food list in the order that they were added or a message stating * that the food list is empty if there are no food items. * - * @param foodList The food list containing all the food items. + * @param foodList The arraylist containing all the food items. */ - public void printFoodList(FoodList foodList) { - if (foodList.getFoods().isEmpty()) { + public void printFoodList(ArrayList foodList) { + if (foodList.isEmpty()) { print("DietBook is currently empty."); } else { String allFood = ""; int foodItemNumber = 1; - for (Food food : foodList.getFoods()) { + for (Food food : foodList) { allFood += LINE_SEPARATOR + " " + foodItemNumber + "." + food; foodItemNumber++; } @@ -178,11 +180,11 @@ public void printDatabase(List foodDatabase) { /** * Prints all the information related to the user. * - * @param person The user. + * @param personInformation The user's personal information. */ - public void printPersonInformation(Person person) { + public void printPersonInformation(String personInformation) { print("Here is your information:" + LINE_SEPARATOR - + person); + + personInformation); } /** From 2879c68d6ad0c60196d6836728b949f817d72869 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 12 Oct 2020 08:50:28 +0800 Subject: [PATCH 056/374] Further documentation - portionSize() -> getPortionSize() --- src/main/java/seedu/duke/list/FoodEntry.java | 6 +++--- src/main/java/seedu/duke/list/FoodList.java | 8 +++++++- src/main/java/seedu/duke/list/FoodListManager.java | 12 ++++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/duke/list/FoodEntry.java b/src/main/java/seedu/duke/list/FoodEntry.java index 8016d6bf32..7aa6f115e6 100644 --- a/src/main/java/seedu/duke/list/FoodEntry.java +++ b/src/main/java/seedu/duke/list/FoodEntry.java @@ -4,7 +4,7 @@ /** - * Data class to story both serving sizes and a food object as a single object. + * Data class to store both serving sizes and a food object as a single object. */ public class FoodEntry { private int portionSize; @@ -25,7 +25,7 @@ public Food getFood() { return food; } - public int portionSize() { + public int getPortionSize() { return portionSize; } @@ -34,4 +34,4 @@ public String toString() { return String.format("%s -- (%s)", food.toString(), portionSize); } -} \ No newline at end of file +} diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java index 4d862540a1..92f92c47e3 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -43,6 +43,7 @@ public String addFood(int portionSize, String name, int calorie, /** * Food database search functionality support. + * Not expected to function. Added for completeness. * Currently just throws a not found exception when called in this manner. * @param portionSize integer to designate number of servings * @param name food object to be added @@ -61,12 +62,17 @@ public String delete(int index) throws IndexOutOfBoundsException { throw e; } } - + public boolean clear() { this.foodEntries = new ArrayList<>(); return true; } + /** + * Obtain the food objects in Foodlist as an ArrayList. + * For other classes that wish to operate on the Food items directly. + * @return Arraylist of ordered Food objects in Foodlist's foodEntries. + */ public ArrayList getFoods() { return FoodListManager.listToFoods(foodEntries); } diff --git a/src/main/java/seedu/duke/list/FoodListManager.java b/src/main/java/seedu/duke/list/FoodListManager.java index cff588f01b..7165cae639 100644 --- a/src/main/java/seedu/duke/list/FoodListManager.java +++ b/src/main/java/seedu/duke/list/FoodListManager.java @@ -11,17 +11,25 @@ */ public class FoodListManager { + /** + * Internal helper method to convert the items in the arraylist into enumed strings. + * Primarily used to obtain String representations of the list. + */ protected static String listToString(ArrayList list) { String listString = ""; for (int i = 1; i <= list.size(); i++) { - FoodEntry task = list.get(i - 1); + FoodEntry entry = list.get(i - 1); listString += i + ". " - + task.toString() + "\n"; + + entry.toString() + "\n"; } return listString; } + /** + * Similar to listToString. + * Extracts only the Food component from FoodEntries. + */ protected static ArrayList listToFoods(ArrayList list) { ArrayList foods = new ArrayList<>(); list.forEach(x -> { From f878ec139ab1e38f3b045d6cc16118bded267efe Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 12 Oct 2020 14:04:30 +0800 Subject: [PATCH 057/374] Change input parameters for three methods --- src/main/java/seedu/duke/Ui.java | 86 ++++++++++++++------------------ 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 2c0fab226d..16810111ea 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,6 +1,6 @@ package seedu.duke; -import java.util.ArrayList; +import java.util.List; /** * Represents a text user interface. @@ -26,7 +26,7 @@ public void printWelcomeMessage() { + "| | | | | |___ | | | |__| | | | | | | | |/ /" + LINE_SEPARATOR + "| | | | | ___| | | | __ <| | | | | | | /" + LINE_SEPARATOR + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + LINE_SEPARATOR - + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\" + LINE_SEPARATOR"; + + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\\" + LINE_SEPARATOR; print(logo + LINE_SEPARATOR + "Hello! Welcome to DietBook!" + LINE_SEPARATOR + "I am Diet, your guide to using DietBook. What is your name?" + LINE_SEPARATOR @@ -40,32 +40,28 @@ public void printWelcomeMessage() { * * @param name The name of the user. */ - public void printAskUserForPersonalInformationMessage(String name) { + public void printAskForUserInfoMessage(String name) { print("Hi " + name + "!" + LINE_SEPARATOR + "Before we get started, I would like to know about about you so that I can make more " - + "accurate calculations for you :)" + LINE_SEPARATOR - + "Therefore, could you please share with me the following:" + LINE_SEPARATOR - + "1. Your gender either F for female or M for male." + LINE_SEPARATOR - + "2. Your age which is a positive integer." + LINE_SEPARATOR - + "3. Your height in cm." + LINE_SEPARATOR - + "4. Your original weight in kg." + LINE_SEPARATOR - + "5. Your target weight in kg, or your original weight if your original weight is also " - + "your target weight." + LINE_SEPARATOR - + "6. Activity level from 1 to 5." + LINE_SEPARATOR - + " 1. " + ActivityLevel.NONE.getDescription() + LINE_SEPARATOR - + " 2. " + ActivityLevel.LOW.getDescription() + LINE_SEPARATOR - + " 3. " + ActivityLevel.MEDIUM.getDescription() + LINE_SEPARATOR - + " 4. " + ActivityLevel.HIGH.getDescription() + LINE_SEPARATOR - + " 5. " + ActivityLevel.EXTREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + + LINE_SEPARATOR + + "accurate calculations for you :). Therefore, could you please share with me the " + + "following:" + LINE_SEPARATOR + + "- Your gender either F for female or M for male." + LINE_SEPARATOR + + "- Your age which is a positive integer." + LINE_SEPARATOR + + "- Your height in cm." + LINE_SEPARATOR + + "- Your original weight in kg." + LINE_SEPARATOR + + "- Your target weight in kg, or your original weight if that is also your target weight." + + LINE_SEPARATOR + + "- Your activity level, represented by a number from 1 to 5." + LINE_SEPARATOR + + " 1 = " + ActivityLevel.NONE.getDescription() + LINE_SEPARATOR + + " 2 = " + ActivityLevel.LOW.getDescription() + LINE_SEPARATOR + + " 3 = " + ActivityLevel.MEDIUM.getDescription() + LINE_SEPARATOR + + " 4 = " + ActivityLevel.HIGH.getDescription() + LINE_SEPARATOR + + " 5 = " + ActivityLevel.EXTREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + "Please input your details in the following format:" + LINE_SEPARATOR - + " info g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL o/ORIGINAL_WEIGHT t/TARGET_WEIGHT" + + " info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL" + LINE_SEPARATOR - + " Example: info g/F a/21 h/165 l/2 o/65 t/55"); - //"4. Your current weight in kg." - //"5. Your target weight in kg, or your current weight if your current weight is also your target - // weight."; - //" info g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL c/CURRENT_WEIGHT t/TARGET_WEIGHT"); - //" Example: info g/F a/21 h/165 l/2 c/65 t/55"; + + " Example: info g/F a/21 h/165 o/65 t/55 l/2"); } // public void printWelcomeMessage() { @@ -93,11 +89,11 @@ public void printAskUserForPersonalInformationMessage(String name) { // System.out.println("6. Your target weight in kg, or your current weight if your " // + "current weight is also your target weight."); // System.out.println("7. Activity level from 1 to 5."); -// System.out.println(" 1. " + ActvityLevel.NONE.getDescription()); -// System.out.println(" 2. " + ActvityLevel.LOW.getDescription()); -// System.out.println(" 3. " + ActvityLevel.MEDIUM.getDescription()); -// System.out.println(" 4. " + ActvityLevel.HIGH.getDescription()); -// System.out.println(" 5. " + ActvityLevel.EXTEREME.getDescription()); +// System.out.println(" 1. " + ActivityLevel.NONE.getDescription()); +// System.out.println(" 2. " + ActivityLevel.LOW.getDescription()); +// System.out.println(" 3. " + ActivityLevel.MEDIUM.getDescription()); +// System.out.println(" 4. " + ActivityLevel.HIGH.getDescription()); +// System.out.println(" 5. " + ActivityLevel.EXTREME.getDescription()); // System.out.println(LINE_SEPARATOR); // System.out.println("Please input your details in the following format:"); // System.out.println(" info n/NAME g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL o/ORIGINAL_WEIGHT " @@ -116,13 +112,13 @@ public void printAskUserForPersonalInformationMessage(String name) { public void printTutorialMessage() { print("Thank you! DietBook has been initialised and you may start by entering any of the commands " + "listed below." + LINE_SEPARATOR + LINE_SEPARATOR - + "To add you own food: add n/FOOD_NAME x/PORTION_SIZE k/CALORIE [c/CARB] [p/PROTEIN] " - + "[f/FAT]" + LINE_SEPARATOR + + "To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR + + "To view all food in the database: data" + LINE_SEPARATOR + + "To add you own food: add n/FOOD_NAME x/PORTION_SIZE k/CALORIE [c/CARBOHYDRATE] " + + "[p/PROTEIN] [f/FAT]" + LINE_SEPARATOR + "To view all food in DietBook: list" + LINE_SEPARATOR + "To delete a food from DietBook: delete INDEX" + LINE_SEPARATOR + "To delete all food items from the DietBook: clear" + LINE_SEPARATOR - + "To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE\n" + LINE_SEPARATOR - + "To view all food in the database: data" + LINE_SEPARATOR + "To show user information: userinfo" + LINE_SEPARATOR + "To calculate carbohydrate intake: calculate carbohydrate" + LINE_SEPARATOR + "To calculate calorie intake: calculate calorie" + LINE_SEPARATOR @@ -135,9 +131,9 @@ public void printTutorialMessage() { /** * Prints a message to show that the food specified has been added to the food list. * - * @param newFood The new food item that was added to the food list. + * @param newFood The string representation of the new food item that was added to the food list. */ - public void printNewFood(Food newFood) { + public void printNewFood(String newFood) { print("Got it! I've added this food item:" + LINE_SEPARATOR + " " + newFood); } @@ -146,19 +142,13 @@ public void printNewFood(Food newFood) { * Prints all the food items in the food list in the order that they were added or a message stating * that the food list is empty if there are no food items. * - * @param foodList The arraylist containing all the food items. + * @param allFood The string representation of all the food items in the food list. */ - public void printFoodList(ArrayList foodList) { - if (foodList.isEmpty()) { + public void printFoodList(String allFood) { + if (allFood.length() < 1) { print("DietBook is currently empty."); } else { - String allFood = ""; - int foodItemNumber = 1; - for (Food food : foodList) { - allFood += LINE_SEPARATOR + " " + foodItemNumber + "." + food; - foodItemNumber++; - } - print("Here are the food items in DietBook:" + allFood); + print("Here are the food items in DietBook:" + LINE_SEPARATOR + allFood); } } @@ -246,9 +236,9 @@ public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, in /** * Prints a message to show that the food specified has been deleted from the food list. * - * @param deletedFood The food that was deleted from the food list. + * @param deletedFood The string representation of the food that was deleted from the food list. */ - public void printDeletedFood(Food deletedFood) { + public void printDeletedFood(String deletedFood) { print("Noted. I've removed this food item:" + LINE_SEPARATOR + " " + deletedFood); } @@ -287,7 +277,7 @@ public void printErrorMessage(String errorMessage) { public void print(String message) { String divider = "__________________________________________________________________________________________" - + "__________"; + + "______________________________________________________"; System.out.println(divider + LINE_SEPARATOR + message + LINE_SEPARATOR From 92a56d932d4ca4bb6cec1b62953a611feb45af55 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 12 Oct 2020 15:11:01 +0800 Subject: [PATCH 058/374] Import necessary classes --- src/main/java/seedu/duke/Ui.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 16810111ea..a33007cac4 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,5 +1,8 @@ package seedu.duke; +import seedu.duke.food.Food; +import seedu.duke.person.ActivityLevel; + import java.util.List; /** From c6d18d912c79c610c60fae19bc2a5dc88f0151f3 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Mon, 12 Oct 2020 21:38:13 +0800 Subject: [PATCH 059/374] calculator v0.5 added JUnit test --- build.gradle | 2 + src/main/java/seedu/calculator/Food.java | 43 ++++++++++++++++ .../java/seedu/calculator/calculator.java | 23 +++++---- .../java/seedu/calculator/calculatorTest.java | 50 +++++++++++++++++++ 4 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 src/main/java/seedu/calculator/Food.java create mode 100644 src/test/java/seedu/calculator/calculatorTest.java diff --git a/build.gradle b/build.gradle index b0c5528fb5..3d863f228c 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,8 @@ repositories { } dependencies { + implementation 'org.junit.jupiter:junit-jupiter:5.4.2' + implementation 'org.junit.jupiter:junit-jupiter:5.4.2' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' } diff --git a/src/main/java/seedu/calculator/Food.java b/src/main/java/seedu/calculator/Food.java new file mode 100644 index 0000000000..e8f19f20f5 --- /dev/null +++ b/src/main/java/seedu/calculator/Food.java @@ -0,0 +1,43 @@ +package seedu.calculator; + +public class Food { + private final String name; + private final int calorie; + private final int carbohydrate; + private final int protein; + private final int fats; + + public Food(String name, int calorie, int carbohydrate, int protein, int fats) { + this.name = name; + this.calorie = calorie; + this.carbohydrate = carbohydrate; + this.protein = protein; + this.fats = fats; + } + + public int getFats() { + return fats; + } + + public String getName() { + return name; + } + + public int getCalorie() { + return calorie; + } + + public int getCarbohydrate() { + return carbohydrate; + } + + public int getProtein() { + return protein; + } + + @Override + public String toString() { + return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate + + " | fats : " + fats; + } +} diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/calculator.java index e6fd57fae0..69e34ca0e4 100644 --- a/src/main/java/seedu/calculator/calculator.java +++ b/src/main/java/seedu/calculator/calculator.java @@ -1,13 +1,16 @@ package seedu.calculator; import java.util.ArrayList; -import seedu.foodList; +//import seedu.foodList; /** * Represents a calculator of food items in foodList. */ public class calculator { - private static Food total = new Food (total, 1, 0, 0, 0, 0); + private static int totalCalorie = 0; + private static int totalCarbohydrate = 0; + private static int totalProtein = 0; + private static int totalFats = 0; /** * Construct a calculator taking in a foodList. Add up calories, @@ -18,10 +21,10 @@ public class calculator { public calculator(ArrayList foodList){ try { for(int i=0; i< foodList.size(); i++){ - total.calorie += foodList.get(i).portionSize * foodList.get(i).calorie; - total.carbs += foodList.get(i).portionSize * foodList.get(i).carbs(i); - total.protein += foodList.get(i).portionSize * foodList.get(i).protein(i); - total.fats += foodList.get(i).portionSize * foodList.get(i).fats(i); + totalCalorie += foodList.get(i).getCalorie(); + totalCarbohydrate += foodList.get(i).getCarbohydrate(); + totalProtein += foodList.get(i).getProtein(); + totalFats += foodList.get(i).getFats(); } } catch (NullPointerException e) { System.out.println("Ops, This foodList is null!"); @@ -34,7 +37,7 @@ public calculator(ArrayList foodList){ * @return the value of total calorie of food items in foodList. */ public int calculateCalorie(){ - return total.calorie; + return totalCalorie; } /** @@ -43,7 +46,7 @@ public int calculateCalorie(){ * @return the value of total carbs of food items in foodList. */ public int calculateCarbs(){ - return total.carbs; + return totalCarbohydrate; } /** @@ -52,7 +55,7 @@ public int calculateCarbs(){ * @return the value of total protein of food items in foodList. */ public int calculateProtein(){ - return total.protein; + return totalProtein; } /** @@ -61,6 +64,6 @@ public int calculateProtein(){ * @return the value of total fats of food items in foodList. */ public int calculateFats(){ - return total.fats; + return totalFats; } } \ No newline at end of file diff --git a/src/test/java/seedu/calculator/calculatorTest.java b/src/test/java/seedu/calculator/calculatorTest.java new file mode 100644 index 0000000000..731ba04da8 --- /dev/null +++ b/src/test/java/seedu/calculator/calculatorTest.java @@ -0,0 +1,50 @@ +package seedu.calculator; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.*; + +class calculatorTest { + + @Test + void calculateCalorie() { + ArrayList foodList = new ArrayList<>(); + foodList.add(new Food("chicken rice", 666, 55, 30, 0)); + foodList.add(new Food("pancake", 150, 16, 0, 0)); + foodList.add(new Food("bao", 290, 0, 16, 0)); + calculator calculator = new calculator(foodList); + assertEquals(666 + 150 + 290, calculator.calculateCalorie()); + } + + @Test + void calculateCarbs() { + ArrayList foodList = new ArrayList<>(); + foodList.add(new Food("chicken rice", 666, 55, 30, 0)); + foodList.add(new Food("pancake", 150, 16, 0, 0)); + foodList.add(new Food("bao", 290, 0, 16, 0)); + calculator calculator = new calculator(foodList); + assertEquals(55 + 16, calculator.calculateCarbs()); + } + + @Test + void calculateProtein() { + ArrayList foodList = new ArrayList<>(); + foodList.add(new Food("chicken rice", 666, 55, 30, 0)); + foodList.add(new Food("pancake", 150, 16, 0, 0)); + foodList.add(new Food("bao", 290, 0, 16, 0)); + calculator calculator = new calculator(foodList); + assertEquals(30 + 16, calculator.calculateProtein()); + } + + @Test + void calculateFats() { + ArrayList foodList = new ArrayList<>(); + foodList.add(new Food("chicken rice", 666, 55, 30, 0)); + foodList.add(new Food("pancake", 150, 16, 0, 0)); + foodList.add(new Food("bao", 290, 0, 16, 0)); + calculator calculator = new calculator(foodList); + assertEquals(0, calculator.calculateFats()); + } +} \ No newline at end of file From 49e1372959fd97537058e73cc0a274d50b22a49b Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Mon, 12 Oct 2020 22:08:57 +0800 Subject: [PATCH 060/374] calculator v0.6 --- src/main/java/seedu/calculator/calculator.java | 2 +- src/test/java/seedu/calculator/calculatorTest.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/calculator.java index 69e34ca0e4..639ff272d5 100644 --- a/src/main/java/seedu/calculator/calculator.java +++ b/src/main/java/seedu/calculator/calculator.java @@ -66,4 +66,4 @@ public int calculateProtein(){ public int calculateFats(){ return totalFats; } -} \ No newline at end of file +} diff --git a/src/test/java/seedu/calculator/calculatorTest.java b/src/test/java/seedu/calculator/calculatorTest.java index 731ba04da8..64cdbae3a7 100644 --- a/src/test/java/seedu/calculator/calculatorTest.java +++ b/src/test/java/seedu/calculator/calculatorTest.java @@ -4,12 +4,12 @@ import java.util.ArrayList; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; class calculatorTest { @Test - void calculateCalorie() { + void calculateCalorie_foodListOfThreeItems_sumOfCalorie() { ArrayList foodList = new ArrayList<>(); foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); @@ -19,7 +19,7 @@ void calculateCalorie() { } @Test - void calculateCarbs() { + void calculateCarbs_foodListOfThreeItems_sumOfCarbs() { ArrayList foodList = new ArrayList<>(); foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); @@ -29,7 +29,7 @@ void calculateCarbs() { } @Test - void calculateProtein() { + void calculateProtein_foodListOfThreeItems_sumOfProtein() { ArrayList foodList = new ArrayList<>(); foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); @@ -39,7 +39,7 @@ void calculateProtein() { } @Test - void calculateFats() { + void calculateFats_foodListOfThreeItems_sumOfFats() { ArrayList foodList = new ArrayList<>(); foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); @@ -47,4 +47,4 @@ void calculateFats() { calculator calculator = new calculator(foodList); assertEquals(0, calculator.calculateFats()); } -} \ No newline at end of file +} From b4b2c503b01e05f87b90f0a42c084f0484f4e487 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Mon, 12 Oct 2020 23:02:35 +0800 Subject: [PATCH 061/374] calculator v0.7 fixes #25 --- src/main/java/seedu/calculator/calculator.java | 16 ++++++++-------- .../java/seedu/calculator/calculatorTest.java | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/calculator.java index 639ff272d5..faf7766439 100644 --- a/src/main/java/seedu/calculator/calculator.java +++ b/src/main/java/seedu/calculator/calculator.java @@ -7,10 +7,10 @@ * Represents a calculator of food items in foodList. */ public class calculator { - private static int totalCalorie = 0; - private static int totalCarbohydrate = 0; - private static int totalProtein = 0; - private static int totalFats = 0; + private int totalCalorie = 0; + private int totalCarbohydrate = 0; + private int totalProtein = 0; + private int totalFat = 0; /** * Construct a calculator taking in a foodList. Add up calories, @@ -24,7 +24,7 @@ public calculator(ArrayList foodList){ totalCalorie += foodList.get(i).getCalorie(); totalCarbohydrate += foodList.get(i).getCarbohydrate(); totalProtein += foodList.get(i).getProtein(); - totalFats += foodList.get(i).getFats(); + totalFat += foodList.get(i).getFats(); } } catch (NullPointerException e) { System.out.println("Ops, This foodList is null!"); @@ -45,7 +45,7 @@ public int calculateCalorie(){ * * @return the value of total carbs of food items in foodList. */ - public int calculateCarbs(){ + public int calculateCarb(){ return totalCarbohydrate; } @@ -63,7 +63,7 @@ public int calculateProtein(){ * * @return the value of total fats of food items in foodList. */ - public int calculateFats(){ - return totalFats; + public int calculateFat(){ + return totalFat; } } diff --git a/src/test/java/seedu/calculator/calculatorTest.java b/src/test/java/seedu/calculator/calculatorTest.java index 64cdbae3a7..f8667b0ea6 100644 --- a/src/test/java/seedu/calculator/calculatorTest.java +++ b/src/test/java/seedu/calculator/calculatorTest.java @@ -8,6 +8,8 @@ class calculatorTest { + + @Test void calculateCalorie_foodListOfThreeItems_sumOfCalorie() { ArrayList foodList = new ArrayList<>(); @@ -25,7 +27,7 @@ void calculateCarbs_foodListOfThreeItems_sumOfCarbs() { foodList.add(new Food("pancake", 150, 16, 0, 0)); foodList.add(new Food("bao", 290, 0, 16, 0)); calculator calculator = new calculator(foodList); - assertEquals(55 + 16, calculator.calculateCarbs()); + assertEquals(55 + 16, calculator.calculateCarb()); } @Test @@ -45,6 +47,6 @@ void calculateFats_foodListOfThreeItems_sumOfFats() { foodList.add(new Food("pancake", 150, 16, 0, 0)); foodList.add(new Food("bao", 290, 0, 16, 0)); calculator calculator = new calculator(foodList); - assertEquals(0, calculator.calculateFats()); + assertEquals(0, calculator.calculateFat()); } } From 006a414e78a393cf68cf079613dd9a6210c43108 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 12 Oct 2020 23:03:14 +0800 Subject: [PATCH 062/374] Add assertions for Person class --- build.gradle | 1 + src/main/java/seedu/duke/person/Person.java | 29 +++++++++++++++++-- .../java/seedu/duke/person/PersonTest.java | 13 --------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index b0c5528fb5..91fffd027d 100644 --- a/build.gradle +++ b/build.gradle @@ -43,4 +43,5 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true } diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/duke/person/Person.java index 91e0c32c2f..ad3d49b348 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/duke/person/Person.java @@ -32,7 +32,20 @@ public class Person { */ public Person(String name, Gender gender, int age, int height, int originalWeight, int targetWeight, ActivityLevel activityLevel) { - this.name = name; + assert name != null : "Name of person should not be null"; + assert name.trim().length() > 0 : "Name of person should not be an empty string"; + assert gender != null : "Gender of person should not be null"; + assert age > 0 : "Age of person should be greater than 0"; + assert age < 125 : "Age of person should be less than 125"; + assert height > 0 : "Height of person should be greater than 0"; + assert height < 273 : "Height of person should be less than 273"; + assert originalWeight > 0 : "Original weight of person should be greater than 0"; + assert originalWeight < 443 : "Original weight of person should be less than 443"; + assert targetWeight > 0 : "Target weight of person should be greater than 0"; + assert targetWeight < 443 : "Target weight of person should be less than 443"; + assert activityLevel != null : "Activity level of person should not be null"; + + this.name = name.trim(); this.gender = gender; this.age = age; this.height = height; @@ -56,7 +69,9 @@ public String getName() { * @param newName The new/revised name of the person. */ public void setName(String newName) { - name = newName; + assert newName != null : "The revised name of person should not be null"; + assert newName.trim().length() > 0 : "The revised name of person should not be an empty string"; + name = newName.trim(); } /** @@ -74,6 +89,7 @@ public Gender getGender() { * @param newGender The new/revised gender of the person. */ public void setGender(Gender newGender) { + assert newGender != null : "The revised gender of person should not be null"; gender = newGender; } @@ -92,6 +108,8 @@ public int getAge() { * @param newAge The new/revised age of the person. */ public void setAge(int newAge) { + assert newAge > 0 : "The revised age of person should be greater than 0"; + assert newAge < 125 : "The revised age of person should be lesser than 125"; age = newAge; } @@ -110,6 +128,8 @@ public int getHeight() { * @param newHeight The new/revised height of the person. */ public void setHeight(int newHeight) { + assert newHeight > 0 : "The revised height of person should be greater than 0"; + assert newHeight < 273 : "The revised height of person should be lesser than 273"; height = newHeight; } @@ -128,6 +148,8 @@ public int getOriginalWeight() { * @param newOriginalWeight The new/revised original weight of the person. */ public void setOriginalWeight(int newOriginalWeight) { + assert newOriginalWeight > 0 : "The revised original weight of person should be greater than 0"; + assert newOriginalWeight < 443 : "The revised original weight of person should be lesser than 443"; originalWeight = newOriginalWeight; } @@ -146,6 +168,8 @@ public int getTargetWeight() { * @param newTargetWeight The new/revised target weight of the person. */ public void setTargetWeight(int newTargetWeight) { + assert newTargetWeight > 0 : "The revised target weight of person should be greater than 0"; + assert newTargetWeight < 443 : "The revised target weight of person should be lesser than 443"; targetWeight = newTargetWeight; } @@ -164,6 +188,7 @@ public ActivityLevel getActivityLevel() { * @param newActivityLevel The new/revised activity level of the person. */ public void setActivityLevel(ActivityLevel newActivityLevel) { + assert newActivityLevel != null : "The revised activity level of person should not be null"; activityLevel = newActivityLevel; } diff --git a/src/test/java/seedu/duke/person/PersonTest.java b/src/test/java/seedu/duke/person/PersonTest.java index 415fd43134..714a66021b 100644 --- a/src/test/java/seedu/duke/person/PersonTest.java +++ b/src/test/java/seedu/duke/person/PersonTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; class PersonTest { @@ -38,12 +37,6 @@ void gender_personWithNewGender_returnsNewGender() { assertEquals(Gender.FEMALE, person.getGender()); } - @Test - void gender_personWithNullGender_returnsNullGender() { - person.setGender(null); - assertNull(person.getGender()); - } - @Test void getAge_person_returnsAge() { assertEquals(21, person.getAge()); @@ -99,12 +92,6 @@ void setActivityLevel_personWithNewActivityLevel_returnsNewActivityLevel() { assertEquals(ActivityLevel.HIGH, person.getActivityLevel()); } - @Test - void setActivityLevel_personWithNullActivityLevel_returnsNullActivityLevel() { - person.setActivityLevel(null); - assertNull(person.getActivityLevel()); - } - @Test void toString_person_returnsStringRepresentationOfPersonInformation() { assertEquals(" Name: Jack" + System.lineSeparator() From 6f6a0dd8f59cf698927d7338973408a0dc6ea7bb Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Mon, 12 Oct 2020 23:21:26 +0800 Subject: [PATCH 063/374] calculator v0.8 --- .../{calculator.java => Calculator.java} | 10 +++-- src/main/java/seedu/calculator/Food.java | 43 ------------------- .../java/seedu/calculator/calculatorTest.java | 8 ++-- 3 files changed, 10 insertions(+), 51 deletions(-) rename src/main/java/seedu/calculator/{calculator.java => Calculator.java} (90%) delete mode 100644 src/main/java/seedu/calculator/Food.java diff --git a/src/main/java/seedu/calculator/calculator.java b/src/main/java/seedu/calculator/Calculator.java similarity index 90% rename from src/main/java/seedu/calculator/calculator.java rename to src/main/java/seedu/calculator/Calculator.java index faf7766439..a30abf152e 100644 --- a/src/main/java/seedu/calculator/calculator.java +++ b/src/main/java/seedu/calculator/Calculator.java @@ -1,12 +1,14 @@ package seedu.calculator; import java.util.ArrayList; -//import seedu.foodList; + +import seedu.duke.Ui; +import seedu.duke.Food; /** * Represents a calculator of food items in foodList. */ -public class calculator { +public class Calculator { private int totalCalorie = 0; private int totalCarbohydrate = 0; private int totalProtein = 0; @@ -18,7 +20,7 @@ public class calculator { * * @param foodList foodList containing food items to calculate. */ - public calculator(ArrayList foodList){ + public Calculator(ArrayList foodList){ try { for(int i=0; i< foodList.size(); i++){ totalCalorie += foodList.get(i).getCalorie(); @@ -27,7 +29,7 @@ public calculator(ArrayList foodList){ totalFat += foodList.get(i).getFats(); } } catch (NullPointerException e) { - System.out.println("Ops, This foodList is null!"); + Ui.printErrorMessage("the foodList is null"); } } diff --git a/src/main/java/seedu/calculator/Food.java b/src/main/java/seedu/calculator/Food.java deleted file mode 100644 index e8f19f20f5..0000000000 --- a/src/main/java/seedu/calculator/Food.java +++ /dev/null @@ -1,43 +0,0 @@ -package seedu.calculator; - -public class Food { - private final String name; - private final int calorie; - private final int carbohydrate; - private final int protein; - private final int fats; - - public Food(String name, int calorie, int carbohydrate, int protein, int fats) { - this.name = name; - this.calorie = calorie; - this.carbohydrate = carbohydrate; - this.protein = protein; - this.fats = fats; - } - - public int getFats() { - return fats; - } - - public String getName() { - return name; - } - - public int getCalorie() { - return calorie; - } - - public int getCarbohydrate() { - return carbohydrate; - } - - public int getProtein() { - return protein; - } - - @Override - public String toString() { - return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate - + " | fats : " + fats; - } -} diff --git a/src/test/java/seedu/calculator/calculatorTest.java b/src/test/java/seedu/calculator/calculatorTest.java index f8667b0ea6..9a5b9f8f59 100644 --- a/src/test/java/seedu/calculator/calculatorTest.java +++ b/src/test/java/seedu/calculator/calculatorTest.java @@ -16,7 +16,7 @@ void calculateCalorie_foodListOfThreeItems_sumOfCalorie() { foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); foodList.add(new Food("bao", 290, 0, 16, 0)); - calculator calculator = new calculator(foodList); + Calculator calculator = new Calculator(foodList); assertEquals(666 + 150 + 290, calculator.calculateCalorie()); } @@ -26,7 +26,7 @@ void calculateCarbs_foodListOfThreeItems_sumOfCarbs() { foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); foodList.add(new Food("bao", 290, 0, 16, 0)); - calculator calculator = new calculator(foodList); + Calculator calculator = new Calculator(foodList); assertEquals(55 + 16, calculator.calculateCarb()); } @@ -36,7 +36,7 @@ void calculateProtein_foodListOfThreeItems_sumOfProtein() { foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); foodList.add(new Food("bao", 290, 0, 16, 0)); - calculator calculator = new calculator(foodList); + Calculator calculator = new Calculator(foodList); assertEquals(30 + 16, calculator.calculateProtein()); } @@ -46,7 +46,7 @@ void calculateFats_foodListOfThreeItems_sumOfFats() { foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); foodList.add(new Food("bao", 290, 0, 16, 0)); - calculator calculator = new calculator(foodList); + Calculator calculator = new Calculator(foodList); assertEquals(0, calculator.calculateFat()); } } From ae7c53b7dd086926e804c6866194f98feb8b432c Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Mon, 12 Oct 2020 23:26:06 +0800 Subject: [PATCH 064/374] calculator v0.9 --- src/test/java/seedu/calculator/calculatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/calculator/calculatorTest.java b/src/test/java/seedu/calculator/calculatorTest.java index 9a5b9f8f59..bc519ef548 100644 --- a/src/test/java/seedu/calculator/calculatorTest.java +++ b/src/test/java/seedu/calculator/calculatorTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -class calculatorTest { +class CalculatorTest { From 7d17630703d0b8420186cd302d98be6215f26716 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 13 Oct 2020 01:17:14 +0800 Subject: [PATCH 065/374] Add assertions for Ui class --- build.gradle | 1 + src/main/java/seedu/duke/Ui.java | 34 ++++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index b0c5528fb5..91fffd027d 100644 --- a/build.gradle +++ b/build.gradle @@ -43,4 +43,5 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true } diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index a33007cac4..d1918cb8cd 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -44,6 +44,8 @@ public void printWelcomeMessage() { * @param name The name of the user. */ public void printAskForUserInfoMessage(String name) { + assert name != null : "Name should not be null"; + assert name.trim().length() > 0 : "Name should not be an empty string"; print("Hi " + name + "!" + LINE_SEPARATOR + "Before we get started, I would like to know about about you so that I can make more " + LINE_SEPARATOR @@ -137,6 +139,9 @@ public void printTutorialMessage() { * @param newFood The string representation of the new food item that was added to the food list. */ public void printNewFood(String newFood) { + assert newFood != null : "String representation of the food that was added should not be null"; + assert newFood.trim().length() > 0 : "String representation of the food that was added should not " + + "be an empty string"; print("Got it! I've added this food item:" + LINE_SEPARATOR + " " + newFood); } @@ -148,7 +153,8 @@ public void printNewFood(String newFood) { * @param allFood The string representation of all the food items in the food list. */ public void printFoodList(String allFood) { - if (allFood.length() < 1) { + assert allFood != null : "String representation of all food in food list should not be null"; + if (allFood.trim().length() < 1) { print("DietBook is currently empty."); } else { print("Here are the food items in DietBook:" + LINE_SEPARATOR + allFood); @@ -161,6 +167,8 @@ public void printFoodList(String allFood) { * @param foodDatabase The list containing all the food items stored in the database. */ public void printDatabase(List foodDatabase) { + assert foodDatabase != null : "Food database should not be null"; + assert foodDatabase.size() > 0 : "Food database should not be empty"; String allFood = ""; int foodItemNumber = 1; for (Food food: foodDatabase) { @@ -173,11 +181,13 @@ public void printDatabase(List foodDatabase) { /** * Prints all the information related to the user. * - * @param personInformation The user's personal information. + * @param personInfo The user's personal information. */ - public void printPersonInformation(String personInformation) { + public void printPersonInfo(String personInfo) { + assert personInfo != null : "Person information should not be null"; + assert personInfo.trim().length() > 0 : "Person information should not be an empty string"; print("Here is your information:" + LINE_SEPARATOR - + personInformation); + + personInfo); } /** @@ -186,6 +196,7 @@ public void printPersonInformation(String personInformation) { * @param carbohydrateIntake The total amount of carbohydrates of all the food in the food list. */ public void printCarbohydrateIntake(int carbohydrateIntake) { + assert carbohydrateIntake >= 0 : "Total carbohydrate intake should be equals to or greater than 0"; print("Total carbohydrate intake: " + carbohydrateIntake + "g"); } @@ -195,6 +206,7 @@ public void printCarbohydrateIntake(int carbohydrateIntake) { * @param calorieIntake The total amount of calories of all the food in the food list. */ public void printCalorieIntake(int calorieIntake) { + assert calorieIntake >= 0 : "Total calorie intake should be equals to or greater than 0"; print("Total calorie intake: " + calorieIntake + "kcal"); } @@ -204,6 +216,7 @@ public void printCalorieIntake(int calorieIntake) { * @param proteinIntake The total amount of proteins of all the food in the food list. */ public void printProteinIntake(int proteinIntake) { + assert proteinIntake >= 0 : "Total protein intake should be equals to or greater than 0 "; print("Total protein intake: " + proteinIntake + "g"); } @@ -213,6 +226,7 @@ public void printProteinIntake(int proteinIntake) { * @param fatIntake The total amount of fats of all the food in the food list. */ public void printFatIntake(int fatIntake) { + assert fatIntake >= 0 : "Total fat intake should be equals to or greater than 0"; print("Total fat intake: " + fatIntake + "g"); } @@ -226,6 +240,11 @@ public void printFatIntake(int fatIntake) { */ public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, int proteinIntake, int fatIntake) { + assert carbohydrateIntake >= 0 : "Total carbohydrate intake should be equals to or greater than 0"; + assert calorieIntake >= 0 : "Total calorie intake should be equals to or greater than 0"; + assert proteinIntake >= 0 : "Total protein intake should be equals to or greater than 0 "; + assert fatIntake >= 0 : "Total fat intake should be equals to or greater than 0"; + print("Total calorie intake: " + calorieIntake + "kcal" + LINE_SEPARATOR + "Total carbohydrate intake: " + carbohydrateIntake + "g" + LINE_SEPARATOR + "Total protein intake: " + proteinIntake + "g" + LINE_SEPARATOR @@ -242,6 +261,9 @@ public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, in * @param deletedFood The string representation of the food that was deleted from the food list. */ public void printDeletedFood(String deletedFood) { + assert deletedFood != null : "String representation of the food that was deleted should not be null"; + assert deletedFood.trim().length() > 0 : "String representation of the food that was deleted should" + + " not be an empty string"; print("Noted. I've removed this food item:" + LINE_SEPARATOR + " " + deletedFood); } @@ -260,6 +282,8 @@ public void printClearFoodListMessage() { * @param name The name of the user. */ public void printExitMessage(String name) { + assert name != null : "Name should not be null"; + assert name.trim().length() > 0 : "Name should not be an empty string"; print("Bye " + name + "! Hope to see you again soon!"); } @@ -269,6 +293,8 @@ public void printExitMessage(String name) { * @param errorMessage Message detailing what or where the error is. */ public void printErrorMessage(String errorMessage) { + assert errorMessage != null : "Error message should not be null"; + assert errorMessage.trim().length() > 0 : "Error message should not be an empty string"; print(":( Oh no..." + errorMessage); } From f233f3a676c0acf468fd69b42a523faa0c20354d Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Tue, 13 Oct 2020 04:25:06 +0800 Subject: [PATCH 066/374] Main, Parse, Manager Main parsing --- src/main/java/seedu/duke/DietBook.java | 42 ++++++ src/main/java/seedu/duke/DietException.java | 12 ++ src/main/java/seedu/duke/Duke.java | 21 --- src/main/java/seedu/duke/Manager.java | 53 +++++++ src/main/java/seedu/duke/Parser.java | 151 ++++++++++++++++++++ 5 files changed, 258 insertions(+), 21 deletions(-) create mode 100644 src/main/java/seedu/duke/DietBook.java create mode 100644 src/main/java/seedu/duke/DietException.java delete mode 100644 src/main/java/seedu/duke/Duke.java create mode 100644 src/main/java/seedu/duke/Manager.java create mode 100644 src/main/java/seedu/duke/Parser.java diff --git a/src/main/java/seedu/duke/DietBook.java b/src/main/java/seedu/duke/DietBook.java new file mode 100644 index 0000000000..eb63d10d7b --- /dev/null +++ b/src/main/java/seedu/duke/DietBook.java @@ -0,0 +1,42 @@ +package seedu.duke; +import java.io.IOException; + +public class DietBook { + private FoodList foodList; + private Ui ui; + private Manager manager; + private DataBase dataBase; + public static boolean isExit = false; + + /** + * Constructor for new DietBook + */ + public DietBook() { + ui = new Ui(); + foodList = new FoodList(); + dataBase = new Database(); + manager = new Manager(foodList, dataBase); + } + + /** + * Main method to run the program. + */ + public static void main(String[] args) { + DietBook dietBook = new DietBook(); + dietBook.ui.printWelcomeMessage(); + + while (!isExit) { + try { + String userInput = dietBook.manager.readCommand(); + Parser.parse(userInput, dietBook.manager, dietBook.ui); + } catch (DietException e) { + dietBook.ui.printErrorMessage(e.getMessage()); + } catch (IOException e) { + dietBook.ui.printErrorMessage(e.getMessage()); + break; + } finally { + dietBook.ui.divider(); + } + } + } +} diff --git a/src/main/java/seedu/duke/DietException.java b/src/main/java/seedu/duke/DietException.java new file mode 100644 index 0000000000..1cc3e09655 --- /dev/null +++ b/src/main/java/seedu/duke/DietException.java @@ -0,0 +1,12 @@ +package seedu.duke; + +public class DietException extends Exception { + public DietException(String message) { + super(message); + } + + @Override + public String toString() { + return getMessage(); + } +} diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 5c74e68d59..0000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} diff --git a/src/main/java/seedu/duke/Manager.java b/src/main/java/seedu/duke/Manager.java new file mode 100644 index 0000000000..27b295470e --- /dev/null +++ b/src/main/java/seedu/duke/Manager.java @@ -0,0 +1,53 @@ +package seedu.duke; + +import java.util.Scanner; + +public class Manager { + private Person person; + private FoodList foodList; + private String name; + private DataBase dataBase; + private Calculator calculator; + private static Scanner s = new Scanner(System.in); + + public Manager(FoodList foodlist, DataBase dataBase) { + this.name = "John Doe"; + this.person = new Person(); + this.foodList = foodlist; + this.dataBase = dataBase; + this.calculator = new Calculator(0,0,0,0); + } + + public String readCommand() { + return s.nextLine(); + } + + public FoodList getFoodList() { + return this.foodList; + } + + public Person getPerson() { + return this.person; + } + + public void setPerson(String gender, String age,String height,String actLvl,String orgWeight,String targWeight) { + this.person = new Person(gender, age, height, actLvl, orgWeight, targWeight); + } + + public Calculator getCalculator() { + return this.calculator; + } + + public DataBase getDataBase() { + return this.dataBase; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java new file mode 100644 index 0000000000..7611544b6c --- /dev/null +++ b/src/main/java/seedu/duke/Parser.java @@ -0,0 +1,151 @@ +package seedu.duke; +import java.util.Arrays; + +public class Parser { + public static final String COMMAND_NAME = "name"; + public static final String COMMAND_LIST = "list"; + public static final String COMMAND_INFO = "info"; + public static final String COMMAND_EXIT = "exit"; + public static final String COMMAND_ADD = "add"; + public static final String COMMAND_CLEAR = "clear"; + public static final String COMMAND_DELETE = "delete"; + public static final String COMMAND_CALCULATE = "calculate"; + public static final String COMMAND_DATA = "data"; + public static final String COMMAND_USERINFO = "userinfo"; + public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; + public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; + + + /** + * Returns the command of a user input. + * @param userInput which is user input. + * @return First word which is the command of the user input. + */ + private static String getCommand(String userInput) { + return userInput.split(" ")[0]; + } + + /** + * Returns the subsequent parameter after the command from the user input. + * @param userInput user input. + * @return parameter part of the user input. + * @throws DietException when the user input is of a wrong format. + */ + private static String getCommandParam(String userInput) throws DietException { + String command = getCommand(userInput); + String[] input = {userInput}; + + if (userInput.split(command).length < 2 + || userInput.split(command)[1].equals(" ")) { + throw new DietException("☹ Error! Missing command parameters!"); + } else { + switch (command) { + case COMMAND_NAME: + return userInput.split(" ")[1]; + case COMMAND_CALCULATE: + for (String param: PARAM_CALCULATE) { + if (userInput.contains(param)) { + return userInput.split(" ")[1]; + } + } + throw new DietException("☹ Oops! Incorrect nutrient type"); + case COMMAND_ADD: + if (!userInput.contains("n/") || !userInput.contains("x/") || !userInput.contains("k/")) { + throw new DietException("☹ Oh no... Missing or incorrect add statement"); + } + return userInput.substring(userInput.indexOf(' ') + 1); + case COMMAND_INFO: + if (!Arrays.asList(input).containsAll(Arrays.asList(PARAM_INFO))) { + throw new DietException("☹ Oh no... Missing or incorrect info statement"); + } + return userInput.substring(userInput.indexOf(' ') + 1); + default: + return null; + } + } + } + + /** + * Returns the index after the command of a user input, e.g. delete 3. + * @param userInput user input. + * @return index part of the user input. + * @throws DietException when the user input is of a wrong format. + */ + private static int getCommandIndex(String userInput) throws DietException { + String command = getCommand(userInput); + + if (userInput.split(command).length < 2 || userInput.split(command)[1].equals(" ")) { + throw new DietException("☹ OOPS!!! Missing index of duke.task!"); + } + try { + return Integer.parseInt(userInput.split(" ")[1]) - 1; + } catch (NumberFormatException e) { + throw new DietException("☹ OOPS!!! No integer index detected!"); + } + } + + /** + * Makes sense of the user input and carries out the functions according to the command given. + * @param userInput user input. + * @throws DietException when the program does not recognize the command given. + */ + public static void parse(String userInput, Manager manager, Ui ui) throws DietException { + Calculator calculator = manager.getCalculator(); + switch (getCommand(userInput)) { + case COMMAND_NAME: + manager.setName(getCommandParam(userInput)); + ui.printAskForUserInfoMessage(manager.getName()) + return; + case COMMAND_EXIT: + ui.printExitMessage(manager.getName()); + DietBook.isExit = true; + return; + case COMMAND_LIST: + ui.printFoodList(manager.getFoodList()); + return; + case COMMAND_USERINFO: + ui.printPersonInformation(personInformation); + return; + case COMMAND_DATA: + manager.getDataBase().printAllData(); + return; + case COMMAND_DELETE: + ui.printDeletedFood(manager.getFoodList().get(getCommandIndex(userInput))); + manager.getFoodList().delete(getCommandIndex(userInput)); + return; + case COMMAND_CLEAR: + ui.printClearFoodListMessage(); + manager.getFoodList().clear; + return; + case COMMAND_CALCULATE: + if (getCommandParam(userInput).equals("all")) { + ui.printAllNutrientIntake(calculator.calculateCalorie(), calculator.calculateCarb(), + calculator.calculateProtein(), calculator.calculateFat()); + } else if (getCommandParam(userInput).equals("calorie")) { + ui.printCalorieIntake(calculator.calculateCalorie()); + } else if (getCommandParam(userInput).equals("carbohydrate")) { + ui.printCarbohydrateIntake(calculator.calculateCarb()); + } else if (getCommandParam(userInput).equals("protein")) { + ui.printProteinIntake(calculator.calculateProtein()); + } else { + ui.printFatIntake(calculator.calculateFat()); + } + return; + case COMMAND_INFO: + String[] processedParam = getCommandParam(userInput).split(" "); + String gender = processedParam[0].substring(processedParam[0].indexOf("/") + 1); + String age = processedParam[1].substring(processedParam[1].indexOf("/") + 1); + String height = processedParam[2].substring(processedParam[2].indexOf("/") + 1); + String actLvl = processedParam[3].substring(processedParam[3].indexOf("/") + 1); + String orgWeight = processedParam[4].substring(processedParam[4].indexOf("/") + 1); + String tarWeight = processedParam[5].substring(processedParam[5].indexOf("/") + 1); + manager.setPerson(gender, age, height, actLvl, orgWeight, tarWeight); + ui.printTutorialMessage(); + return; + case COMMAND_ADD: + return; + default: + throw new DietException("☹ There's no such command!"); + } + } +} From c7890f7032a047c9a13a592ce79ab84231bcc38d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 13 Oct 2020 12:11:35 +0800 Subject: [PATCH 067/374] Remove unnecessary commented out methods --- src/main/java/seedu/duke/Ui.java | 45 -------------------------------- 1 file changed, 45 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index d1918cb8cd..352e2a978c 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -69,47 +69,6 @@ public void printAskForUserInfoMessage(String name) { + " Example: info g/F a/21 h/165 o/65 t/55 l/2"); } -// public void printWelcomeMessage() { -// String logo = " _______ __ ______ ________ _______ ______ ______ __ __" + LINE_SEPARATOR -// + "| __ \\| | ___|__ __| __ \\ / __ \\ / __ \\| | / /" + LINE_SEPARATOR -// + "| | | | | |___ | | | |__| | | | | | | | |/ /" + LINE_SEPARATOR -// + "| | | | | ___| | | | __ <| | | | | | | /" + LINE_SEPARATOR -// + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + LINE_SEPARATOR -// + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\" + LINE_SEPARATOR"; -// System.out.println(DIVIDER); -// System.out.println(logo); -// System.out.println("Hello! Welcome to DietBook!"); -// System.out.println("I am Diet, your guide to using DietBook."); -// System.out.println("Before we get started, I would like to know about about you so that I can make " -// + "more accurate calculations for you :)"); -// System.out.println("Therefore, could you please share with me the following:"); -// System.out.println("1. Your name."); -// System.out.println("2. Your gender either F for female or M for male".); -// System.out.println("3. Your age which is a positive integer."); -// System.out.println("4. Your height in cm."); -// System.out.println("5. Your current weight in kg."); -// System.out.println("5. Your original weight in kg."); -// System.out.println("6. Your target weight in kg, or your original weight if your " -// + "original weight is also your target weight."); -// System.out.println("6. Your target weight in kg, or your current weight if your " -// + "current weight is also your target weight."); -// System.out.println("7. Activity level from 1 to 5."); -// System.out.println(" 1. " + ActivityLevel.NONE.getDescription()); -// System.out.println(" 2. " + ActivityLevel.LOW.getDescription()); -// System.out.println(" 3. " + ActivityLevel.MEDIUM.getDescription()); -// System.out.println(" 4. " + ActivityLevel.HIGH.getDescription()); -// System.out.println(" 5. " + ActivityLevel.EXTREME.getDescription()); -// System.out.println(LINE_SEPARATOR); -// System.out.println("Please input your details in the following format:"); -// System.out.println(" info n/NAME g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL o/ORIGINAL_WEIGHT " -// + "t/TARGET_WEIGHT"); -// System.out.println(" info n/name g/GENDER a/AGE h/HEIGHT l/ACTIVITY_LEVEL c/CURRENT_WEIGHT " -// + "t/TARGET_WEIGHT"); -// System.out.println(" Example: info n/Suzy g/F a/21 h/165 l/2 o/65 t/55"); -// System.out.println(" Example: info n/Suzy g/F a/21 h/165 l/2 c/65 t/55"); -// System.out.println(DIVIDER); -// } - /** * Prints a message that notifies the user that DietBook has been initialised and shows a list of user * commands that the user can input. @@ -251,10 +210,6 @@ public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, in + "Total fat intake: " + fatIntake + "g"); } -// public void printNutrientIntake(String nutritionalIntake) { -// print(nutritionalIntake); -// } - /** * Prints a message to show that the food specified has been deleted from the food list. * From deb48279feb4ffad7a2fe4c594cd3d229d6f10f9 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 13 Oct 2020 18:10:52 +0800 Subject: [PATCH 068/374] calculator after pulling from tp repo. --- src/main/java/seedu/calculator/Calculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/calculator/Calculator.java b/src/main/java/seedu/calculator/Calculator.java index a30abf152e..4385f43329 100644 --- a/src/main/java/seedu/calculator/Calculator.java +++ b/src/main/java/seedu/calculator/Calculator.java @@ -3,7 +3,7 @@ import java.util.ArrayList; import seedu.duke.Ui; -import seedu.duke.Food; +import seedu.duke.Food; /** * Represents a calculator of food items in foodList. From bb8fc4ed5c13aeaed5bd58d5c4c16e39c4f4cf43 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 13 Oct 2020 18:46:03 +0800 Subject: [PATCH 069/374] calculator v1 --- .../java/seedu/calculator/Calculator.java | 30 ++++++++----------- src/main/java/seedu/duke/Duke.java | 3 +- ...alculatorTest.java => CalculatorTest.java} | 1 + 3 files changed, 16 insertions(+), 18 deletions(-) rename src/test/java/seedu/calculator/{calculatorTest.java => CalculatorTest.java} (98%) diff --git a/src/main/java/seedu/calculator/Calculator.java b/src/main/java/seedu/calculator/Calculator.java index 4385f43329..e50cf15f43 100644 --- a/src/main/java/seedu/calculator/Calculator.java +++ b/src/main/java/seedu/calculator/Calculator.java @@ -1,9 +1,8 @@ package seedu.calculator; -import java.util.ArrayList; +import seedu.duke.food.Food; -import seedu.duke.Ui; -import seedu.duke.Food; +import java.util.ArrayList; /** * Represents a calculator of food items in foodList. @@ -20,16 +19,13 @@ public class Calculator { * * @param foodList foodList containing food items to calculate. */ - public Calculator(ArrayList foodList){ - try { - for(int i=0; i< foodList.size(); i++){ - totalCalorie += foodList.get(i).getCalorie(); - totalCarbohydrate += foodList.get(i).getCarbohydrate(); - totalProtein += foodList.get(i).getProtein(); - totalFat += foodList.get(i).getFats(); - } - } catch (NullPointerException e) { - Ui.printErrorMessage("the foodList is null"); + public Calculator(ArrayList foodList) { + assert foodList != null : "the foodList should not be null."; + for (int i = 0; i < foodList.size(); i++) { + totalCalorie += foodList.get(i).getCalorie(); + totalCarbohydrate += foodList.get(i).getCarbohydrate(); + totalProtein += foodList.get(i).getProtein(); + totalFat += foodList.get(i).getFats(); } } @@ -38,7 +34,7 @@ public Calculator(ArrayList foodList){ * * @return the value of total calorie of food items in foodList. */ - public int calculateCalorie(){ + public int calculateCalorie() { return totalCalorie; } @@ -47,7 +43,7 @@ public int calculateCalorie(){ * * @return the value of total carbs of food items in foodList. */ - public int calculateCarb(){ + public int calculateCarb() { return totalCarbohydrate; } @@ -56,7 +52,7 @@ public int calculateCarb(){ * * @return the value of total protein of food items in foodList. */ - public int calculateProtein(){ + public int calculateProtein() { return totalProtein; } @@ -65,7 +61,7 @@ public int calculateProtein(){ * * @return the value of total fats of food items in foodList. */ - public int calculateFat(){ + public int calculateFat() { return totalFat; } } diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 74e5175952..5c74e68d59 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -6,7 +6,8 @@ public class Duke { /** * Main entry-point for the java.duke.Duke application. */ - public static void main(String[] args) { String logo = " ____ _ \n" + public static void main(String[] args) { + String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" diff --git a/src/test/java/seedu/calculator/calculatorTest.java b/src/test/java/seedu/calculator/CalculatorTest.java similarity index 98% rename from src/test/java/seedu/calculator/calculatorTest.java rename to src/test/java/seedu/calculator/CalculatorTest.java index bc519ef548..673e98c51f 100644 --- a/src/test/java/seedu/calculator/calculatorTest.java +++ b/src/test/java/seedu/calculator/CalculatorTest.java @@ -1,6 +1,7 @@ package seedu.calculator; import org.junit.jupiter.api.Test; +import seedu.duke.food.Food; import java.util.ArrayList; From 5d0eea5cf7a503d19ad46f1022ddc6db6b252012 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 13 Oct 2020 18:54:59 +0800 Subject: [PATCH 070/374] calculator version1 changed plural form to single form. --- src/test/java/seedu/calculator/CalculatorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/calculator/CalculatorTest.java b/src/test/java/seedu/calculator/CalculatorTest.java index 673e98c51f..e58f33a249 100644 --- a/src/test/java/seedu/calculator/CalculatorTest.java +++ b/src/test/java/seedu/calculator/CalculatorTest.java @@ -22,7 +22,7 @@ void calculateCalorie_foodListOfThreeItems_sumOfCalorie() { } @Test - void calculateCarbs_foodListOfThreeItems_sumOfCarbs() { + void calculateCarb_foodListOfThreeItems_sumOfCarb() { ArrayList foodList = new ArrayList<>(); foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); @@ -42,7 +42,7 @@ void calculateProtein_foodListOfThreeItems_sumOfProtein() { } @Test - void calculateFats_foodListOfThreeItems_sumOfFats() { + void calculateFat_foodListOfThreeItems_sumOfFat() { ArrayList foodList = new ArrayList<>(); foodList.add(new Food("chicken rice", 666, 55, 30, 0)); foodList.add(new Food("pancake", 150, 16, 0, 0)); From 7b7cd2fb8ad8833819c4417e6ad5cb438c3ba2a7 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 13 Oct 2020 21:12:28 +0800 Subject: [PATCH 071/374] pass gradle checkstyle test --- .../seedu/duke/database/DataBaseTest.java | 43 ++++++++++--------- src/test/java/seedu/duke/food/FoodTest.java | 3 +- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/test/java/seedu/duke/database/DataBaseTest.java b/src/test/java/seedu/duke/database/DataBaseTest.java index a060a3ea2c..116b4192bc 100644 --- a/src/test/java/seedu/duke/database/DataBaseTest.java +++ b/src/test/java/seedu/duke/database/DataBaseTest.java @@ -3,10 +3,9 @@ import java.io.FileNotFoundException; import java.util.NoSuchElementException; -import static org.junit.jupiter.api.Assertions.*; class DataBaseTest { - public static void main(String[] args){ + public static void main(String[] args) { DataBase database = new DataBase(); try { database.init(); @@ -30,23 +29,25 @@ public static void main(String[] args){ System.out.println("Input: Prawn ## OutPut: " + database.searchFoodByName("Prawn")); System.out.println("Input: Mee ## OutPut: " + database.searchFoodByName("Mee")); System.out.println("Input: lobster ## OutPut: " + database.searchFoodByName("lobster")); - System.out.println("Input: Prawn ## OutPut: " + database.searchFoodByName("koala bears")); - } - catch (NoSuchElementException e){ + } catch (NoSuchElementException e) { System.out.println("No such food found! " + e); } // ---- search food by store name test ---- System.out.println("------- testing the search food by store and by name function --------"); try { - System.out.println("Input: Fried , Halal Mini Wok ## OutPut: " + database.searchFoodByNameByStore("Fried", "Halal Mini Wok")); - System.out.println("Input: Chicken , Halal Mini Wok ## OutPut: " + database.searchFoodByNameByStore("Chicken", "Halal Mini Wok")); - System.out.println("Input: Chicken , Ayam Penyet ## OutPut: " + database.searchFoodByNameByStore("Chicken", "Ayam Penyet")); - System.out.println("Input: lobster , Michelin ## OutPut: " + database.searchFoodByNameByStore("lobster", "Michelin")); - System.out.println("Input: fish , Halal Mini Wok ## OutPut: " + database.searchFoodByNameByStore("fish", "Halal Mini Wok")); - } - catch (NoSuchElementException e){ + System.out.println("Input: Fried , Halal Mini Wok ## OutPut: " + + database.searchFoodByNameByStore("Fried", "Halal Mini Wok")); + System.out.println("Input: Chicken , Halal Mini Wok ## OutPut: " + + database.searchFoodByNameByStore("Chicken", "Halal Mini Wok")); + System.out.println("Input: Chicken , Ayam Penyet ## OutPut: " + + database.searchFoodByNameByStore("Chicken", "Ayam Penyet")); + System.out.println("Input: lobster , Michelin ## OutPut: " + + database.searchFoodByNameByStore("lobster", "Michelin")); + System.out.println("Input: fish , Halal Mini Wok ## OutPut: " + + database.searchFoodByNameByStore("fish", "Halal Mini Wok")); + } catch (NoSuchElementException e) { System.out.println("No such food found! " + e); } @@ -61,20 +62,22 @@ public static void main(String[] args){ database.searchAllFoodByStore("Michelin").forEach(System.out::println); System.out.println("------- Input: Gordan Ramsey's restaurant -------- "); database.searchAllFoodByStore("Gordon Ramsey's restaurant").forEach(System.out::println); - } - catch (NoSuchElementException e) { + } catch (NoSuchElementException e) { System.out.println("There is no such store! " + e); } // ---- search food by Name by canteen ------ try { System.out.println("------- testing the search food by canteen and by name function --------"); - System.out.println("Input: Fried , Science ## OutPut: " + database.searchFoodByNameByCanteen("Fried", "Science")); - System.out.println("Input: Chicken , Science ## OutPut: " + database.searchFoodByNameByCanteen("Chicken", "Science")); - System.out.println("Input: lobster , Science ## OutPut: " + database.searchFoodByNameByCanteen("lobster", "Science")); - System.out.println("Input: lobster , Raffles Hotel Suite ## OutPut: " + database.searchFoodByNameByCanteen("lobster", "Raffles Hotel Suite")); - } - catch (NoSuchElementException e) { + System.out.println("Input: Fried , Science ## OutPut: " + + database.searchFoodByNameByCanteen("Fried", "Science")); + System.out.println("Input: Chicken , Science ## OutPut: " + + database.searchFoodByNameByCanteen("Chicken", "Science")); + System.out.println("Input: lobster , Science ## OutPut: " + + database.searchFoodByNameByCanteen("lobster", "Science")); + System.out.println("Input: lobster , Raffles Hotel Suite ## OutPut: " + + database.searchFoodByNameByCanteen("lobster", "Raffles Hotel Suite")); + } catch (NoSuchElementException e) { System.out.println("There is either no such canteen or no such food in that canteen!" + e); } diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java index 1168a32770..97b646a032 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -2,12 +2,11 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; class FoodTest { private Food testFood; - public static void main(String[] args){ + public static void main(String[] args) { Food food = new Food("Kobe Beef", 480,50,40,30); System.out.println(food); } From 55f2117f10d1739742364196615a430e27d9bc3d Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 13 Oct 2020 21:17:05 +0800 Subject: [PATCH 072/374] add documentation for food class --- src/main/java/seedu/duke/food/Food.java | 11 +++++++++++ src/test/java/seedu/duke/food/FoodTest.java | 6 ++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index c1e03882ce..1dd650f493 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -1,5 +1,6 @@ package seedu.duke.food; + public class Food { private final String name; private final int calorie; @@ -7,6 +8,16 @@ public class Food { private final int protein; private final int fats; + /** + * Constructor of the Food class + * Store information regarding a food item: name, number of calories in kcal, amount of carbohydrate in grams, + * amount of protein in grams, amount of fats in grams. + * @param name name of food e.g. chicken rice + * @param calorie number of calories e.g. 480 kcal + * @param carbohydrate amount of carbohydrates e.g. 40 grams + * @param protein amount of protein e.g. 20 grams + * @param fats amount of fats e.g. 20 grams + */ public Food(String name, int calorie, int carbohydrate, int protein, int fats) { this.name = name; this.calorie = calorie; diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java index 7a45d44cd0..79d6e73eeb 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -6,19 +6,17 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - class FoodTest { private Food testFood; @BeforeEach - public void setUp(){ + public void setUp() { testFood = new Food("Kobe Beef", 480,50,40,30); } @Test - public void footTest(){ + public void footTest() { assertEquals(testFood.getCalorie(), 480); assertEquals(testFood.getCarbohydrate(), 50); assertEquals(testFood.getProtein(), 40); From e73dbe8950c7c106fe6b78bdaf9d115650987576 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:27:16 +0800 Subject: [PATCH 073/374] swap order of assertEquals --- src/test/java/seedu/duke/food/FoodTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java index 79d6e73eeb..8734c23d75 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -17,9 +17,9 @@ public void setUp() { @Test public void footTest() { - assertEquals(testFood.getCalorie(), 480); - assertEquals(testFood.getCarbohydrate(), 50); - assertEquals(testFood.getProtein(), 40); - assertEquals(testFood.getFats(),30); + assertEquals(480, testFood.getCalorie()); + assertEquals(50, testFood.getCarbohydrate()); + assertEquals(40, testFood.getProtein()); + assertEquals(30, testFood.getFats()); } } \ No newline at end of file From 57c5d39fb98b644d3d76301a70a9ca023067df90 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:39:53 +0800 Subject: [PATCH 074/374] replace constructor java doc with class java doc --- src/main/java/seedu/duke/food/Food.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 1dd650f493..d4d6050cb8 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -1,6 +1,10 @@ package seedu.duke.food; - +/** + * Constructor of the Food class + * Store information regarding a food item: name, number of calories in kcal, amount of carbohydrate in grams, + * amount of protein in grams, amount of fats in grams. + */ public class Food { private final String name; private final int calorie; @@ -8,16 +12,7 @@ public class Food { private final int protein; private final int fats; - /** - * Constructor of the Food class - * Store information regarding a food item: name, number of calories in kcal, amount of carbohydrate in grams, - * amount of protein in grams, amount of fats in grams. - * @param name name of food e.g. chicken rice - * @param calorie number of calories e.g. 480 kcal - * @param carbohydrate amount of carbohydrates e.g. 40 grams - * @param protein amount of protein e.g. 20 grams - * @param fats amount of fats e.g. 20 grams - */ + public Food(String name, int calorie, int carbohydrate, int protein, int fats) { this.name = name; this.calorie = calorie; From f3c32184dc58a6a460c0bf530056e21623f649d7 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:40:01 +0800 Subject: [PATCH 075/374] no message --- src/main/java/seedu/duke/food/Food.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index d4d6050cb8..3142009e4e 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -12,7 +12,6 @@ public class Food { private final int protein; private final int fats; - public Food(String name, int calorie, int carbohydrate, int protein, int fats) { this.name = name; this.calorie = calorie; @@ -46,4 +45,4 @@ public String toString() { return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate + " | fats : " + fats; } -} +} \ No newline at end of file From b1f031d82a862075887d508e7041c47898f77108 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Wed, 14 Oct 2020 23:43:46 +0800 Subject: [PATCH 076/374] v1.0 prototype v1.0 --- src/main/java/seedu/duke/DietBook.java | 12 ++-- src/main/java/seedu/duke/Manager.java | 17 +++-- src/main/java/seedu/duke/Parser.java | 97 ++++++++++++++++++++------ src/main/java/seedu/duke/Ui.java | 2 +- 4 files changed, 97 insertions(+), 31 deletions(-) diff --git a/src/main/java/seedu/duke/DietBook.java b/src/main/java/seedu/duke/DietBook.java index eb63d10d7b..bd3387630f 100644 --- a/src/main/java/seedu/duke/DietBook.java +++ b/src/main/java/seedu/duke/DietBook.java @@ -1,5 +1,8 @@ package seedu.duke; + import java.io.IOException; +import seedu.duke.database.DataBase; +import seedu.duke.list.FoodList; public class DietBook { private FoodList foodList; @@ -9,12 +12,12 @@ public class DietBook { public static boolean isExit = false; /** - * Constructor for new DietBook + * Constructor for new DietBook. */ public DietBook() { ui = new Ui(); foodList = new FoodList(); - dataBase = new Database(); + dataBase = new DataBase(); manager = new Manager(foodList, dataBase); } @@ -31,11 +34,8 @@ public static void main(String[] args) { Parser.parse(userInput, dietBook.manager, dietBook.ui); } catch (DietException e) { dietBook.ui.printErrorMessage(e.getMessage()); - } catch (IOException e) { - dietBook.ui.printErrorMessage(e.getMessage()); - break; } finally { - dietBook.ui.divider(); + System.out.println("__________________"); } } } diff --git a/src/main/java/seedu/duke/Manager.java b/src/main/java/seedu/duke/Manager.java index 27b295470e..31874debdc 100644 --- a/src/main/java/seedu/duke/Manager.java +++ b/src/main/java/seedu/duke/Manager.java @@ -1,7 +1,16 @@ package seedu.duke; +import seedu.duke.list.FoodList; +import seedu.duke.person.ActivityLevel; +import seedu.duke.person.Person; +import seedu.calculator.Calculator; +import seedu.duke.database.DataBase; +import seedu.duke.person.Gender; + + import java.util.Scanner; + public class Manager { private Person person; private FoodList foodList; @@ -12,10 +21,10 @@ public class Manager { public Manager(FoodList foodlist, DataBase dataBase) { this.name = "John Doe"; - this.person = new Person(); + this.person = new Person(this.name, Gender.MALE, 0,0,0,0, ActivityLevel.LOW); this.foodList = foodlist; this.dataBase = dataBase; - this.calculator = new Calculator(0,0,0,0); + this.calculator = new Calculator(foodList.getFoods()); } public String readCommand() { @@ -30,8 +39,8 @@ public Person getPerson() { return this.person; } - public void setPerson(String gender, String age,String height,String actLvl,String orgWeight,String targWeight) { - this.person = new Person(gender, age, height, actLvl, orgWeight, targWeight); + public void setPerson(String name, Gender gender, int age,int height,int orgWeight,int targWeight, ActivityLevel actLvl) { + this.person = new Person(name, gender, age, height, orgWeight, targWeight, actLvl); } public Calculator getCalculator() { diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 7611544b6c..1c3a360f0e 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,5 +1,9 @@ package seedu.duke; -import java.util.Arrays; + +import seedu.calculator.Calculator; +import seedu.duke.list.FoodList; +import seedu.duke.person.Gender; +import seedu.duke.person.ActivityLevel; public class Parser { public static final String COMMAND_NAME = "name"; @@ -14,6 +18,7 @@ public class Parser { public static final String COMMAND_USERINFO = "userinfo"; public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; + public static final String[] PARAM_ADD = {"n/","x/","k/","f/","p/","c/"}; /** @@ -48,15 +53,19 @@ private static String getCommandParam(String userInput) throws DietException { return userInput.split(" ")[1]; } } - throw new DietException("☹ Oops! Incorrect nutrient type"); + throw new DietException("☹ Incorrect nutrient type"); case COMMAND_ADD: - if (!userInput.contains("n/") || !userInput.contains("x/") || !userInput.contains("k/")) { - throw new DietException("☹ Oh no... Missing or incorrect add statement"); + for (String param: PARAM_ADD) { + if (!userInput.contains(param)) { + throw new DietException("☹ Missing or incorrect add statement"); + } } return userInput.substring(userInput.indexOf(' ') + 1); case COMMAND_INFO: - if (!Arrays.asList(input).containsAll(Arrays.asList(PARAM_INFO))) { - throw new DietException("☹ Oh no... Missing or incorrect info statement"); + for (String param: PARAM_INFO) { + if (!userInput.contains(param)) { + throw new DietException("☹ Missing or incorrect info statement"); + } } return userInput.substring(userInput.indexOf(' ') + 1); default: @@ -65,6 +74,60 @@ private static String getCommandParam(String userInput) throws DietException { } } + /** + * Processes the parameters for add command of user input and adds a Food object. + * @param userInput user input. + * @param foodList the FoodList object. + * @return name of the food that was added. + * @throws DietException when the user input is of a wrong format. + */ + private static String getProcessedAdd(String userInput, FoodList foodList) throws DietException { + String[] processedParam = getCommandParam(userInput).split(" "); + int portionSize = Integer.parseInt(processedParam[0].substring(processedParam[0].indexOf("/") + 1)); + String foodName = processedParam[1].substring(processedParam[1].indexOf("/") + 1); + int calorie = Integer.parseInt(processedParam[2].substring(processedParam[2].indexOf("/") + 1)); + int carb = Integer.parseInt(processedParam[3].substring(processedParam[3].indexOf("/") + 1)); + int protein = Integer.parseInt(processedParam[4].substring(processedParam[4].indexOf("/") + 1)); + int fat = Integer.parseInt(processedParam[5].substring(processedParam[5].indexOf("/") + 1)); + foodList.addFood(portionSize, foodName, calorie, carb, protein, fat); + return foodName; + } + + /** + * Processes the parameters for info command of user input and updates the Person object. + * @param userInput user input. + * @param manager the manager object. + * @throws DietException when the user input is of a wrong format. + */ + private static void executeProcessedInfo(String userInput, Manager manager) throws DietException { + Gender gender; + ActivityLevel actLvl; + String[] processedParam = getCommandParam(userInput).split(" "); + String processGender = processedParam[0].substring(processedParam[0].indexOf("/") + 1); + if (processGender.equals("M")) { + gender = Gender.MALE; + } else { + gender = Gender.FEMALE; + } + int age = Integer.parseInt(processedParam[1].substring(processedParam[1].indexOf("/") + 1)); + int height = Integer.parseInt(processedParam[2].substring(processedParam[2].indexOf("/") + 1)); + int orgWeight = Integer.parseInt(processedParam[3].substring(processedParam[3].indexOf("/") + 1)); + int tarWeight = Integer.parseInt(processedParam[4].substring(processedParam[4].indexOf("/") + 1)); + String processActLvl = processedParam[5].substring(processedParam[5].indexOf("/") + 1); + if (processActLvl.equals("1")) { + actLvl = ActivityLevel.NONE; + } else if (processActLvl.equals("2")) { + actLvl = ActivityLevel.LOW; + } else if (processActLvl.equals("3")) { + actLvl = ActivityLevel.MEDIUM; + } else if (processActLvl.equals("4")) { + actLvl = ActivityLevel.HIGH; + } else { + actLvl = ActivityLevel.EXTREME; + } + manager.setPerson(manager.getName(), gender, age, height, orgWeight, tarWeight, actLvl); + } + /** * Returns the index after the command of a user input, e.g. delete 3. * @param userInput user input. @@ -94,28 +157,28 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx switch (getCommand(userInput)) { case COMMAND_NAME: manager.setName(getCommandParam(userInput)); - ui.printAskForUserInfoMessage(manager.getName()) + ui.printAskForUserInfoMessage(manager.getName()); return; case COMMAND_EXIT: ui.printExitMessage(manager.getName()); DietBook.isExit = true; return; case COMMAND_LIST: - ui.printFoodList(manager.getFoodList()); + ui.printFoodList(manager.getFoodList().toString()); return; case COMMAND_USERINFO: - ui.printPersonInformation(personInformation); + ui.printPersonInfo(manager.getPerson().toString()); return; case COMMAND_DATA: - manager.getDataBase().printAllData(); + ui.printDatabase(manager.getDataBase().getFoodList()); return; case COMMAND_DELETE: - ui.printDeletedFood(manager.getFoodList().get(getCommandIndex(userInput))); + ui.printDeletedFood(manager.getFoodList().getFoods().get(getCommandIndex(userInput)).toString()); manager.getFoodList().delete(getCommandIndex(userInput)); return; case COMMAND_CLEAR: ui.printClearFoodListMessage(); - manager.getFoodList().clear; + manager.getFoodList().clear(); return; case COMMAND_CALCULATE: if (getCommandParam(userInput).equals("all")) { @@ -132,17 +195,11 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx } return; case COMMAND_INFO: - String[] processedParam = getCommandParam(userInput).split(" "); - String gender = processedParam[0].substring(processedParam[0].indexOf("/") + 1); - String age = processedParam[1].substring(processedParam[1].indexOf("/") + 1); - String height = processedParam[2].substring(processedParam[2].indexOf("/") + 1); - String actLvl = processedParam[3].substring(processedParam[3].indexOf("/") + 1); - String orgWeight = processedParam[4].substring(processedParam[4].indexOf("/") + 1); - String tarWeight = processedParam[5].substring(processedParam[5].indexOf("/") + 1); - manager.setPerson(gender, age, height, actLvl, orgWeight, tarWeight); + executeProcessedInfo(userInput, manager); ui.printTutorialMessage(); return; case COMMAND_ADD: + ui.printNewFood(getProcessedAdd(userInput, manager.getFoodList())); return; default: throw new DietException("☹ There's no such command!"); diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 352e2a978c..25f3a8d909 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -78,7 +78,7 @@ public void printTutorialMessage() { + "listed below." + LINE_SEPARATOR + LINE_SEPARATOR + "To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR + "To view all food in the database: data" + LINE_SEPARATOR - + "To add you own food: add n/FOOD_NAME x/PORTION_SIZE k/CALORIE [c/CARBOHYDRATE] " + + "To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] " + "[p/PROTEIN] [f/FAT]" + LINE_SEPARATOR + "To view all food in DietBook: list" + LINE_SEPARATOR + "To delete a food from DietBook: delete INDEX" + LINE_SEPARATOR From 240b357b42799a01ca64cd275d59c640c458b5ee Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 15 Oct 2020 00:02:05 +0800 Subject: [PATCH 077/374] checkstyle fix --- src/main/java/seedu/duke/Manager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Manager.java b/src/main/java/seedu/duke/Manager.java index 31874debdc..b7a8c4cd96 100644 --- a/src/main/java/seedu/duke/Manager.java +++ b/src/main/java/seedu/duke/Manager.java @@ -39,7 +39,8 @@ public Person getPerson() { return this.person; } - public void setPerson(String name, Gender gender, int age,int height,int orgWeight,int targWeight, ActivityLevel actLvl) { + public void setPerson(String name, Gender gender, int age,int height,int orgWeight, + int targWeight, ActivityLevel actLvl) { this.person = new Person(name, gender, age, height, orgWeight, targWeight, actLvl); } From e592a470755666a420e11c20c3979f3fc0dc56e1 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 15 Oct 2020 00:10:06 +0800 Subject: [PATCH 078/374] shortened delete case --- src/main/java/seedu/duke/Parser.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 1c3a360f0e..910119f3a2 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -173,8 +173,7 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx ui.printDatabase(manager.getDataBase().getFoodList()); return; case COMMAND_DELETE: - ui.printDeletedFood(manager.getFoodList().getFoods().get(getCommandIndex(userInput)).toString()); - manager.getFoodList().delete(getCommandIndex(userInput)); + ui.printDeletedFood(manager.getFoodList().delete(getCommandIndex(userInput))); return; case COMMAND_CLEAR: ui.printClearFoodListMessage(); From 444a73ec314c218d8ba13244566c0ce7cb80bda9 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 15 Oct 2020 00:14:15 +0800 Subject: [PATCH 079/374] removed index subtraction --- src/main/java/seedu/duke/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 910119f3a2..932c2f8b08 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -141,7 +141,7 @@ private static int getCommandIndex(String userInput) throws DietException { throw new DietException("☹ OOPS!!! Missing index of duke.task!"); } try { - return Integer.parseInt(userInput.split(" ")[1]) - 1; + return Integer.parseInt(userInput.split(" ")[1]); } catch (NumberFormatException e) { throw new DietException("☹ OOPS!!! No integer index detected!"); } From 0f60ed3150097a8054ff718e2f15273ec76827fc Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 15 Oct 2020 00:28:08 +0800 Subject: [PATCH 080/374] added back duke --- src/main/java/seedu/duke/Duke.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/seedu/duke/Duke.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java new file mode 100644 index 0000000000..9f8063ee94 --- /dev/null +++ b/src/main/java/seedu/duke/Duke.java @@ -0,0 +1,21 @@ +package seedu.duke; + +import java.util.Scanner; + +public class Duke { + /** + * Main entry-point for the java.duke.Duke application. + */ + public static void main(String[] args) { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println("Hello from\n" + logo); + System.out.println("What is your name?"); + + Scanner in = new Scanner(System.in); + System.out.println("Hello " + in.nextLine()); + } +} \ No newline at end of file From 8e3c91bb07d81713e2ec86a6ec0655f68d2a7881 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 15 Oct 2020 02:11:42 +0800 Subject: [PATCH 081/374] Change main class in build.gradle --- META-INF/MANIFEST.MF | 3 +++ build.gradle | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 META-INF/MANIFEST.MF diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..3d5a2c3351 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.duke.DietBook + diff --git a/build.gradle b/build.gradle index c26af4dc9a..0130e89069 100644 --- a/build.gradle +++ b/build.gradle @@ -31,11 +31,11 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "seedu.duke.DietBook" } shadowJar { - archiveBaseName = "duke" + archiveBaseName = "dietbook" archiveClassifier = null } @@ -45,5 +45,5 @@ checkstyle { run{ standardInput = System.in - enableAssertions = true + enableAssertions = false } From 261f0f361f75a36952d001c81ff766e4cc41953b Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 15 Oct 2020 02:15:58 +0800 Subject: [PATCH 082/374] Remove I/O testing --- text-ui-test/EXPECTED.TXT | 8 -------- text-ui-test/input.txt | 1 - 2 files changed, 9 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..8b13789179 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +1 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| -What is your name? -Hello James Gosling diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f95..e69de29bb2 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +0,0 @@ -James Gosling \ No newline at end of file From 09b6f8138c4e41b67ac02bb90ab3d020ef49ca6e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 15 Oct 2020 02:20:03 +0800 Subject: [PATCH 083/374] Update Expected.TXT with welcome message --- text-ui-test/EXPECTED.TXT | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 8b13789179..c55d70517c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1 +1,13 @@ +________________________________________________________________________________________________________________________________________________ + _______ __ ______ ________ _______ ______ ______ __ __ +| __ \| | ___|__ __| __ \ / __ \ / __ \| | / / +| | | | | |___ | | | |__| | | | | | | | |/ / +| | | | | ___| | | | __ <| | | | | | | / +| |__| | | |___ | | | |__| | |__| | | | | |\ \ +|_______/|__|______| |__| |_______/ \______/ \______/|__| \__\ +Hello! Welcome to DietBook! +I am Diet, your guide to using DietBook. What is your name? +Please input in the following format: + name YOUR_NAME +________________________________________________________________________________________________________________________________________________ From 3b22af22444f6b9da49d2163e944b8c7074d1521 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 15 Oct 2020 02:23:38 +0800 Subject: [PATCH 084/374] Add ending divider --- text-ui-test/EXPECTED.TXT | 1 + 1 file changed, 1 insertion(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index c55d70517c..68c874c5d9 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -11,3 +11,4 @@ I am Diet, your guide to using DietBook. What is your name? Please input in the following format: name YOUR_NAME ________________________________________________________________________________________________________________________________________________ +__________________ \ No newline at end of file From 7ad903c4e216655ed7398db32a76cfa4036209a5 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 15 Oct 2020 02:26:24 +0800 Subject: [PATCH 085/374] Add new line at the end of the file --- text-ui-test/EXPECTED.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 68c874c5d9..f47f7db415 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -11,4 +11,4 @@ I am Diet, your guide to using DietBook. What is your name? Please input in the following format: name YOUR_NAME ________________________________________________________________________________________________________________________________________________ -__________________ \ No newline at end of file +__________________ From 17ab7bdd54a9b4f86402ab2af9302752e0951c69 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 15 Oct 2020 02:33:30 +0800 Subject: [PATCH 086/374] data printing --- src/main/java/seedu/duke/DietBook.java | 3 ++- src/main/java/seedu/duke/Parser.java | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/DietBook.java b/src/main/java/seedu/duke/DietBook.java index bd3387630f..b7003f2753 100644 --- a/src/main/java/seedu/duke/DietBook.java +++ b/src/main/java/seedu/duke/DietBook.java @@ -1,5 +1,6 @@ package seedu.duke; +import java.io.FileNotFoundException; import java.io.IOException; import seedu.duke.database.DataBase; import seedu.duke.list.FoodList; @@ -24,7 +25,7 @@ public DietBook() { /** * Main method to run the program. */ - public static void main(String[] args) { + public static void main(String[] args) throws FileNotFoundException { DietBook dietBook = new DietBook(); dietBook.ui.printWelcomeMessage(); diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 932c2f8b08..2539ef863b 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -5,6 +5,8 @@ import seedu.duke.person.Gender; import seedu.duke.person.ActivityLevel; +import java.io.FileNotFoundException; + public class Parser { public static final String COMMAND_NAME = "name"; public static final String COMMAND_LIST = "list"; @@ -152,7 +154,7 @@ private static int getCommandIndex(String userInput) throws DietException { * @param userInput user input. * @throws DietException when the program does not recognize the command given. */ - public static void parse(String userInput, Manager manager, Ui ui) throws DietException { + public static void parse(String userInput, Manager manager, Ui ui) throws DietException, FileNotFoundException { Calculator calculator = manager.getCalculator(); switch (getCommand(userInput)) { case COMMAND_NAME: @@ -170,6 +172,7 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx ui.printPersonInfo(manager.getPerson().toString()); return; case COMMAND_DATA: + manager.getDataBase().init(); ui.printDatabase(manager.getDataBase().getFoodList()); return; case COMMAND_DELETE: From dd57dd644cf398e036b6dc1b92468cb18c7b82cc Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 15 Oct 2020 10:47:50 +0800 Subject: [PATCH 087/374] fixed bugs for data, calculator --- build.gradle | 6 +++--- src/main/java/META-INF/MANIFEST.MF | 3 +++ src/main/java/seedu/duke/DietBook.java | 23 ++++++++++++----------- src/main/java/seedu/duke/Duke.java | 21 --------------------- src/main/java/seedu/duke/Manager.java | 4 ++++ src/main/java/seedu/duke/Parser.java | 12 ++++++++---- 6 files changed, 30 insertions(+), 39 deletions(-) create mode 100644 src/main/java/META-INF/MANIFEST.MF delete mode 100644 src/main/java/seedu/duke/Duke.java diff --git a/build.gradle b/build.gradle index c26af4dc9a..0130e89069 100644 --- a/build.gradle +++ b/build.gradle @@ -31,11 +31,11 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "seedu.duke.DietBook" } shadowJar { - archiveBaseName = "duke" + archiveBaseName = "dietbook" archiveClassifier = null } @@ -45,5 +45,5 @@ checkstyle { run{ standardInput = System.in - enableAssertions = true + enableAssertions = false } diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..3d5a2c3351 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.duke.DietBook + diff --git a/src/main/java/seedu/duke/DietBook.java b/src/main/java/seedu/duke/DietBook.java index bd3387630f..63524b73ca 100644 --- a/src/main/java/seedu/duke/DietBook.java +++ b/src/main/java/seedu/duke/DietBook.java @@ -1,5 +1,6 @@ package seedu.duke; +import java.io.FileNotFoundException; import java.io.IOException; import seedu.duke.database.DataBase; import seedu.duke.list.FoodList; @@ -11,20 +12,10 @@ public class DietBook { private DataBase dataBase; public static boolean isExit = false; - /** - * Constructor for new DietBook. - */ - public DietBook() { - ui = new Ui(); - foodList = new FoodList(); - dataBase = new DataBase(); - manager = new Manager(foodList, dataBase); - } - /** * Main method to run the program. */ - public static void main(String[] args) { + public static void main(String[] args) throws FileNotFoundException { DietBook dietBook = new DietBook(); dietBook.ui.printWelcomeMessage(); @@ -39,4 +30,14 @@ public static void main(String[] args) { } } } + + /** + * Constructor for new DietBook. + */ + public DietBook() { + ui = new Ui(); + foodList = new FoodList(); + dataBase = new DataBase(); + manager = new Manager(foodList, dataBase); + } } diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 9f8063ee94..0000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} \ No newline at end of file diff --git a/src/main/java/seedu/duke/Manager.java b/src/main/java/seedu/duke/Manager.java index b7a8c4cd96..3b755c49ff 100644 --- a/src/main/java/seedu/duke/Manager.java +++ b/src/main/java/seedu/duke/Manager.java @@ -48,6 +48,10 @@ public Calculator getCalculator() { return this.calculator; } + public void setCalculator() { + this.calculator = new Calculator(foodList.getFoods()); + } + public DataBase getDataBase() { return this.dataBase; } diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 932c2f8b08..033fafb972 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -5,6 +5,8 @@ import seedu.duke.person.Gender; import seedu.duke.person.ActivityLevel; +import java.io.FileNotFoundException; + public class Parser { public static final String COMMAND_NAME = "name"; public static final String COMMAND_LIST = "list"; @@ -152,7 +154,7 @@ private static int getCommandIndex(String userInput) throws DietException { * @param userInput user input. * @throws DietException when the program does not recognize the command given. */ - public static void parse(String userInput, Manager manager, Ui ui) throws DietException { + public static void parse(String userInput, Manager manager, Ui ui) throws DietException, FileNotFoundException { Calculator calculator = manager.getCalculator(); switch (getCommand(userInput)) { case COMMAND_NAME: @@ -170,6 +172,7 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx ui.printPersonInfo(manager.getPerson().toString()); return; case COMMAND_DATA: + manager.getDataBase().init(); ui.printDatabase(manager.getDataBase().getFoodList()); return; case COMMAND_DELETE: @@ -180,14 +183,15 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx manager.getFoodList().clear(); return; case COMMAND_CALCULATE: + manager.setCalculator(); if (getCommandParam(userInput).equals("all")) { ui.printAllNutrientIntake(calculator.calculateCalorie(), calculator.calculateCarb(), calculator.calculateProtein(), calculator.calculateFat()); - } else if (getCommandParam(userInput).equals("calorie")) { + } else if (getCommandParam(userInput).contains("calorie")) { ui.printCalorieIntake(calculator.calculateCalorie()); - } else if (getCommandParam(userInput).equals("carbohydrate")) { + } else if (getCommandParam(userInput).contains("carbohydrate")) { ui.printCarbohydrateIntake(calculator.calculateCarb()); - } else if (getCommandParam(userInput).equals("protein")) { + } else if (getCommandParam(userInput).contains("protein")) { ui.printProteinIntake(calculator.calculateProtein()); } else { ui.printFatIntake(calculator.calculateFat()); From f698b2e5c5a009565fbc3f8fad66d15eaf9c8d44 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 15 Oct 2020 10:58:33 +0800 Subject: [PATCH 088/374] fixed calculator --- src/main/java/seedu/duke/Manager.java | 4 ++++ src/main/java/seedu/duke/Parser.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/seedu/duke/Manager.java b/src/main/java/seedu/duke/Manager.java index b7a8c4cd96..3b755c49ff 100644 --- a/src/main/java/seedu/duke/Manager.java +++ b/src/main/java/seedu/duke/Manager.java @@ -48,6 +48,10 @@ public Calculator getCalculator() { return this.calculator; } + public void setCalculator() { + this.calculator = new Calculator(foodList.getFoods()); + } + public DataBase getDataBase() { return this.dataBase; } diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 2539ef863b..c0e32315e0 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -177,6 +177,7 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx return; case COMMAND_DELETE: ui.printDeletedFood(manager.getFoodList().delete(getCommandIndex(userInput))); + manager.setCalculator(); return; case COMMAND_CLEAR: ui.printClearFoodListMessage(); @@ -202,6 +203,7 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx return; case COMMAND_ADD: ui.printNewFood(getProcessedAdd(userInput, manager.getFoodList())); + manager.setCalculator(); return; default: throw new DietException("☹ There's no such command!"); From 69358ede48900e9bbfa7a645e822662ff438385a Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 18 Oct 2020 17:06:52 +0800 Subject: [PATCH 089/374] change FilePathing to ClassPathing Data txt file referenced by DataBase now put in its resource directory as per current Gradle specifications (main/resource). ClassPathing used to access this data in init method. Some minor changes to style and removal of filepathing related exceptions and imports. --- .../java/seedu/duke/database/DataBase.java | 35 +++++++++++-------- .../duke/database => resources}/data.txt | 0 .../seedu/duke/database/DataBaseTest.java | 9 ++--- 3 files changed, 23 insertions(+), 21 deletions(-) rename src/main/{java/seedu/duke/database => resources}/data.txt (100%) diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/duke/database/DataBase.java index 74672a54ef..687d6528a6 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/duke/database/DataBase.java @@ -3,14 +3,12 @@ import seedu.duke.food.Food; -import java.io.File; -import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -import java.util.NoSuchElementException; import java.util.Scanner; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.io.InputStream; public class DataBase { private static final String START_SYMBOL = "&%START"; @@ -19,23 +17,27 @@ public class DataBase { private static final String DATA_FILE_SEPERATOR = "\\|"; - private static final String rootDirectory = System.getProperty("user.dir"); - private static final String dataFileFolder = "src" + File.separator + "main" + File.separator - + "java" + File.separator + "seedu" + File.separator + "duke" + File.separator + "database"; private final List canteenList; + /** + * Instantiate an empty Database object. + */ public DataBase() { this.canteenList = new ArrayList<>(); } /** - * Reads a file from the data base and puts it into the DataBase object. + * Loads and parses the resource main/resource/data.txt + * This data is used to build the internal canteenList. */ - public void init() throws FileNotFoundException { - String fileFolder = rootDirectory + File.separator + dataFileFolder; - File dataFile = new File(fileFolder + File.separator + "data.txt"); - Scanner fileReader = new Scanner(dataFile); + public void init() { + + InputStream dataStream = DataBase.class.getResourceAsStream("/data.txt"); + assert (dataStream != null) : "Could not load resource"; + + Scanner fileReader = new Scanner(dataStream); + String fileLine; boolean start = false; while (fileReader.hasNext()) { @@ -91,8 +93,8 @@ private Store fillStore(String name, Scanner fileSegment) { String fileLine = fileSegment.nextLine(); String[] fileData = fileLine.split(DATA_FILE_SEPERATOR); while (!(fileLine.equals(UP_SYMBOL))) { - food = new Food(fileData[0], Integer.parseInt(fileData[1]),Integer.parseInt(fileData[2]), - Integer.parseInt(fileData[3]),Integer.parseInt(fileData[4])); + food = new Food(fileData[0], Integer.parseInt(fileData[1]), Integer.parseInt(fileData[2]), + Integer.parseInt(fileData[3]), Integer.parseInt(fileData[4])); store.addFood(food); fileLine = fileSegment.nextLine(); fileData = fileLine.split(DATA_FILE_SEPERATOR); @@ -235,7 +237,7 @@ public Stream searchAllFoodBelowCalorie(int calorie) { * Returns all food within the calorie range. * * @param minCalorie minimum calories - * @param maxCalorie maxinum calories + * @param maxCalorie maximum calories * @return food stream */ public Stream searchAllFoodInCalorieRange(int minCalorie, int maxCalorie) { @@ -252,7 +254,10 @@ public Stream foodStream() { .flatMap(x -> x.getStoreList().stream()) .flatMap(x -> x.getFoodList().stream()); } - + + /** + * Provide a list of all food in the data base. + */ public List getFoodList() { return foodStream().collect(Collectors.toList()); } diff --git a/src/main/java/seedu/duke/database/data.txt b/src/main/resources/data.txt similarity index 100% rename from src/main/java/seedu/duke/database/data.txt rename to src/main/resources/data.txt diff --git a/src/test/java/seedu/duke/database/DataBaseTest.java b/src/test/java/seedu/duke/database/DataBaseTest.java index 116b4192bc..004a1d0a70 100644 --- a/src/test/java/seedu/duke/database/DataBaseTest.java +++ b/src/test/java/seedu/duke/database/DataBaseTest.java @@ -1,17 +1,14 @@ package seedu.duke.database; -import java.io.FileNotFoundException; import java.util.NoSuchElementException; class DataBaseTest { public static void main(String[] args) { DataBase database = new DataBase(); - try { - database.init(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } + + database.init(); + // ----- Print everything in the data base ----- database.printAllData(); From 993af58c76f4e32a6b151f20ac0311a1f89fb875 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 19 Oct 2020 12:17:46 +0800 Subject: [PATCH 090/374] Update Food.java Change `getFats()` to its singular `getFat()` for consistency with other methods. --- src/main/java/seedu/duke/food/Food.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/duke/food/Food.java index 9ea158bf5f..619062543a 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/duke/food/Food.java @@ -21,7 +21,7 @@ public Food(String name, int calorie, int carbohydrate, int protein, int fats) { this.fats = fats; } - public int getFats() { + public int getFat() { return fats; } From a55ea8fe39fef46e0e3895ad4ec3ecf8960234c8 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 19 Oct 2020 13:07:28 +0800 Subject: [PATCH 091/374] Update Tests Update tests to use getFat() instead --- src/main/java/seedu/calculator/Calculator.java | 2 +- src/test/java/seedu/duke/food/FoodTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/calculator/Calculator.java b/src/main/java/seedu/calculator/Calculator.java index e50cf15f43..dfbd8a221c 100644 --- a/src/main/java/seedu/calculator/Calculator.java +++ b/src/main/java/seedu/calculator/Calculator.java @@ -25,7 +25,7 @@ public Calculator(ArrayList foodList) { totalCalorie += foodList.get(i).getCalorie(); totalCarbohydrate += foodList.get(i).getCarbohydrate(); totalProtein += foodList.get(i).getProtein(); - totalFat += foodList.get(i).getFats(); + totalFat += foodList.get(i).getFat(); } } diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/duke/food/FoodTest.java index 8c866c2f89..fd7b5866b8 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/duke/food/FoodTest.java @@ -21,6 +21,6 @@ public void footTest() { assertEquals(480, testFood.getCalorie()); assertEquals(50, testFood.getCarbohydrate()); assertEquals(40, testFood.getProtein()); - assertEquals(30, testFood.getFats()); + assertEquals(30, testFood.getFat()); } } \ No newline at end of file From 4ff6f9a2e8c888f47a73fb0f06a09ffec23968e4 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 19 Oct 2020 13:09:11 +0800 Subject: [PATCH 092/374] Update .gitignore Added ignores for IDE agnosticism. --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index f69985ef1f..12141ee20e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ /out/ /*.iml +# VSCode files +/.vscode/ + # Gradle build files /.gradle/ /build/ @@ -15,3 +18,6 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT + +# IDE specific files +.project From f03597c66060da81ed0cf8ba965600566eae7b06 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 19 Oct 2020 13:11:06 +0800 Subject: [PATCH 093/374] Update FoodList: FP & Portion sizing - FoodListManager uses FP to be more DRY - Can get Food with nutrition values scaled according to portion size --- src/main/java/seedu/duke/list/FoodList.java | 12 ++- .../java/seedu/duke/list/FoodListManager.java | 81 +++++++++++++++---- .../seedu/duke/{ => list}/FoodListTest.java | 20 ++++- 3 files changed, 95 insertions(+), 18 deletions(-) rename src/test/java/seedu/duke/{ => list}/FoodListTest.java (56%) diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java index 92f92c47e3..4f949d8adc 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -54,7 +54,10 @@ public String addFood(int portionSize, String name) throws FoodNotFoundException throw new FoodNotFoundException(); } - + /** + * Deletes the the entry of the list at the provided index. + * index starts from 1 (not 0). i.e. is User's understanding of index. + */ public String delete(int index) throws IndexOutOfBoundsException { try { return FoodListManager.deleteEntry(foodEntries, index).toString(); @@ -77,6 +80,13 @@ public ArrayList getFoods() { return FoodListManager.listToFoods(foodEntries); } + /** + * Obtain list of food objects in FoodList, scaled to portion size. + */ + public ArrayList getPortionedFoods() { + return FoodListManager.listToPortionedFoods(foodEntries); + } + @Override public String toString() { return FoodListManager.listToString(foodEntries); diff --git a/src/main/java/seedu/duke/list/FoodListManager.java b/src/main/java/seedu/duke/list/FoodListManager.java index 7165cae639..cc5d8e511c 100644 --- a/src/main/java/seedu/duke/list/FoodListManager.java +++ b/src/main/java/seedu/duke/list/FoodListManager.java @@ -2,6 +2,9 @@ import seedu.duke.food.Food; import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Consumer; /** * Class with static methods to execute "complex commands" on FoodList. @@ -13,37 +16,83 @@ public class FoodListManager { /** * Internal helper method to convert the items in the arraylist into enumed strings. - * Primarily used to obtain String representations of the list. + * Primarily used to obtain String representations of the entire list. */ protected static String listToString(ArrayList list) { String listString = ""; for (int i = 1; i <= list.size(); i++) { FoodEntry entry = list.get(i - 1); - listString += i + ". " + listString += " " + i + ". " + entry.toString() + "\n"; } return listString; } - /** - * Similar to listToString. - * Extracts only the Food component from FoodEntries. - */ - protected static ArrayList listToFoods(ArrayList list) { - ArrayList foods = new ArrayList<>(); - list.forEach(x -> { - foods.add(x.getFood()); - }); - return foods; - } - - protected static Food deleteEntry(ArrayList list, int index) throws IndexOutOfBoundsException { + protected static FoodEntry deleteEntry(List list, int index) throws IndexOutOfBoundsException { int indexToDelete = index - 1; try { - return list.remove(indexToDelete).getFood(); + return list.remove(indexToDelete); } catch (IndexOutOfBoundsException e) { throw e; } } + + /** + * Method to obtain list of foodentries in string rep. + * @param list The foodList arrayList + * @return List of foodEntries in their String rep. + */ + protected static List listToStrings(List list) { + Function function = x -> x.toString(); + return applyFunctionToList(list, function); + } + + /** + * Extracts the list of foods from the foodentries list. + * @param list list of foodEntries + * @return arraylist of Food objects. + */ + protected static ArrayList listToFoods(List list) { + Function function = x -> x.getFood(); + return applyFunctionToList(list, function); + } + + /** + * Creates a list of foods that have their nutritional values scaled by portion size. + * This is based on the FoodEntries in the list provided. + * @param list list of FoodEntries + * @return arraylist of Food objects + */ + protected static ArrayList listToPortionedFoods(List list) { + Function function = x -> { + Food baseFood = x.getFood(); + /** Explicitly getting return type of getPortionSize() is avoided. + * Future updates might see the type change from int to float + * return of getPortionSize() essentially treated as a "multipliable" + */ + return new Food(baseFood.getName(), + baseFood.getCalorie() * x.getPortionSize(), + baseFood.getCarbohydrate() * x.getPortionSize(), + baseFood.getProtein() * x.getPortionSize(), + baseFood.getFat() * x.getPortionSize()); + }; + return applyFunctionToList(list, function); + } + + /** + * Generic method to map a function across a list. + * @param list list to operate on + * @param function function to be mapped across list + * @return list of mapped items under provided function + */ + protected static ArrayList applyFunctionToList(List list, Function function) { + ArrayList appliedList = new ArrayList<>(); + Consumer addResultToAppliedList = x -> appliedList.add(function.apply(x)); + list.forEach(addResultToAppliedList); + return appliedList; + } + } + +// create a functional interface for the function instead: diff --git a/src/test/java/seedu/duke/FoodListTest.java b/src/test/java/seedu/duke/list/FoodListTest.java similarity index 56% rename from src/test/java/seedu/duke/FoodListTest.java rename to src/test/java/seedu/duke/list/FoodListTest.java index 96b31d9647..80df0a28d0 100644 --- a/src/test/java/seedu/duke/FoodListTest.java +++ b/src/test/java/seedu/duke/list/FoodListTest.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.duke.list; import seedu.duke.list.FoodList; import seedu.duke.food.Food; @@ -6,14 +6,32 @@ class FoodListTest { + + private FoodList list; + + @BeforeEAch + protected void setUp() { + this.list = new FoodList(); + + Food food = new Food("Kobe Beef", 480,50,40,30); + foodList.addFood(3, food); + foodList.addFood(2, "Sashimi", 100, 0, 30, 10); + + } + public static void main(String[] args) { + Food food = new Food("Kobe Beef", 480,50,40,30); FoodList foodList = new FoodList(); System.out.println(foodList.addFood(3, food)); System.out.println(foodList.addFood(2, "Sashimi", 100, 0, 30, 10)); System.out.println(foodList); + System.out.println(foodList.getPortionedFoods()); + System.out.println(foodList.delete(1)); System.out.println(foodList); + System.out.println(foodList.getFoods()); + } } \ No newline at end of file From 95976c74ebca8da128477cde420f0cbfa1236a79 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 19 Oct 2020 22:27:51 +0800 Subject: [PATCH 094/374] add Junit tests - Tests for key advanced features (deletion and list mapping). - add /.settings (vscode eclipse-gradle specfic) to gitignore --- .gitignore | 1 + .../java/seedu/duke/list/FoodListTest.java | 46 +++++++++++-------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 12141ee20e..d004289a1f 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ text-ui-test/EXPECTED-UNIX.TXT # IDE specific files .project +/.settings/ diff --git a/src/test/java/seedu/duke/list/FoodListTest.java b/src/test/java/seedu/duke/list/FoodListTest.java index 80df0a28d0..3fed7febda 100644 --- a/src/test/java/seedu/duke/list/FoodListTest.java +++ b/src/test/java/seedu/duke/list/FoodListTest.java @@ -1,37 +1,47 @@ package seedu.duke.list; -import seedu.duke.list.FoodList; -import seedu.duke.food.Food; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import seedu.duke.food.Food; class FoodListTest { private FoodList list; - @BeforeEAch + @BeforeEach protected void setUp() { this.list = new FoodList(); Food food = new Food("Kobe Beef", 480,50,40,30); - foodList.addFood(3, food); - foodList.addFood(2, "Sashimi", 100, 0, 30, 10); + list.addFood(3, food); + list.addFood(2, "Sashimi", 100, 0, 30, 10); } - public static void main(String[] args) { + /** + * getPortionedList() should return list of food with scaled up nutritional values. + * getFoods() should return a list of food (Not food entries). + * Essentially 2 tests in 1. + */ + @Test + void foodPortionScaling_standardList_scaledFoodList() { + FoodList testList = new FoodList(); + + Food food = new Food("Kobe Beef", 480 * 3 ,50 * 3, 40 * 3, 30 * 3); + testList.addFood(1, food); + testList.addFood(1, "Sashimi", 200, 0, 60, 20); + assertEquals(testList.getFoods().toString(), list.getPortionedFoods().toString()); + } + + @Test + void deleteItemTest() { Food food = new Food("Kobe Beef", 480,50,40,30); - FoodList foodList = new FoodList(); - - System.out.println(foodList.addFood(3, food)); - System.out.println(foodList.addFood(2, "Sashimi", 100, 0, 30, 10)); - System.out.println(foodList); - System.out.println(foodList.getPortionedFoods()); - - System.out.println(foodList.delete(1)); - System.out.println(foodList); - System.out.println(foodList.getFoods()); - + FoodEntry entry = new FoodEntry(3, food); + assertEquals(list.delete(1), entry.toString()); } -} \ No newline at end of file + +} From 05f0a6ea29f4b2b41f8fd0807d3977c158d39f78 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 19 Oct 2020 22:41:22 +0800 Subject: [PATCH 095/374] javadocs --- src/main/java/seedu/duke/list/FoodList.java | 11 ++++++++++- src/test/java/seedu/duke/list/FoodListTest.java | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java index 4f949d8adc..8dc4fa3d40 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -12,6 +12,9 @@ public class FoodList { private ArrayList foodEntries; + /** + * Default constructor that instantiates FoodList with an empty foodentry arraylist. + */ public FoodList() { this.foodEntries = new ArrayList<>(); } @@ -33,7 +36,10 @@ public String addFood(int portionSize, Food food) { foodEntries.add(toAdd); return toAdd.toString(); } - + + /** + * Default add method that adds a food entry using the food details and portion size. + */ public String addFood(int portionSize, String name, int calorie, int carbohydrate, int protein, int fat) { FoodEntry toAdd = new FoodEntry(portionSize, name, calorie, carbohydrate, protein, fat); @@ -66,6 +72,9 @@ public String delete(int index) throws IndexOutOfBoundsException { } } + /** + * Discards previous foodEntry list and creates a new one. + */ public boolean clear() { this.foodEntries = new ArrayList<>(); return true; diff --git a/src/test/java/seedu/duke/list/FoodListTest.java b/src/test/java/seedu/duke/list/FoodListTest.java index 3fed7febda..99779fe16e 100644 --- a/src/test/java/seedu/duke/list/FoodListTest.java +++ b/src/test/java/seedu/duke/list/FoodListTest.java @@ -30,7 +30,7 @@ protected void setUp() { void foodPortionScaling_standardList_scaledFoodList() { FoodList testList = new FoodList(); - Food food = new Food("Kobe Beef", 480 * 3 ,50 * 3, 40 * 3, 30 * 3); + Food food = new Food("Kobe Beef", 480 * 3, 50 * 3, 40 * 3, 30 * 3); testList.addFood(1, food); testList.addFood(1, "Sashimi", 200, 0, 60, 20); From 70580ae7cdc46444ca165bfb3ebb90cb4507d624 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 19 Oct 2020 23:30:40 +0800 Subject: [PATCH 096/374] add assertions and docs --- src/main/java/seedu/duke/list/FoodEntry.java | 19 +++++++++++++++++-- src/main/java/seedu/duke/list/FoodList.java | 5 ++++- .../java/seedu/duke/list/FoodListManager.java | 3 ++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/list/FoodEntry.java b/src/main/java/seedu/duke/list/FoodEntry.java index 7aa6f115e6..cd16f35cfe 100644 --- a/src/main/java/seedu/duke/list/FoodEntry.java +++ b/src/main/java/seedu/duke/list/FoodEntry.java @@ -7,24 +7,39 @@ * Data class to store both serving sizes and a food object as a single object. */ public class FoodEntry { - private int portionSize; - private Food food; + private final int portionSize; + private final Food food; + /** + * Convenience constructor mainly for testing. + * In the future, this is expected be the constructor for adding entries using food from the database. + */ public FoodEntry(int portionSize, Food food) { + assert (portionSize > 0) : "Non-positive, invalid portion size not caught."; this.portionSize = portionSize; this.food = food; } + /** + * Default constructor. Creates new food object as part of entry. + */ public FoodEntry(int portionSize, String name, int calorie, int carbohydrate, int protein, int fat) { + assert (portionSize > 0) : "Non-positive, invalid portion size not caught."; this.portionSize = portionSize; this.food = new Food(name, calorie, carbohydrate, protein, fat); } + /** + * Getter method for the Food object. + */ public Food getFood() { return food; } + /** + * Getter method for the portionSize object. + */ public int getPortionSize() { return portionSize; } diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java index 8dc4fa3d40..a334418f87 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -19,6 +19,9 @@ public FoodList() { this.foodEntries = new ArrayList<>(); } + /** + * Convenience constructor mainly for testing purposes. + */ protected FoodList(ArrayList entries) { this.foodEntries = entries; } @@ -36,7 +39,7 @@ public String addFood(int portionSize, Food food) { foodEntries.add(toAdd); return toAdd.toString(); } - + /** * Default add method that adds a food entry using the food details and portion size. */ diff --git a/src/main/java/seedu/duke/list/FoodListManager.java b/src/main/java/seedu/duke/list/FoodListManager.java index cc5d8e511c..45b9a5373f 100644 --- a/src/main/java/seedu/duke/list/FoodListManager.java +++ b/src/main/java/seedu/duke/list/FoodListManager.java @@ -30,6 +30,7 @@ protected static String listToString(ArrayList list) { } protected static FoodEntry deleteEntry(List list, int index) throws IndexOutOfBoundsException { + assert (index > 0) : "Invalid index (negative/zero) was given."; int indexToDelete = index - 1; try { return list.remove(indexToDelete); @@ -95,4 +96,4 @@ protected static ArrayList applyFunctionToList(List list, Function< } -// create a functional interface for the function instead: +// Potential future work: create a functional interface for the functions instead: From 80460038590b2cbef0dad5489faf396db3193174 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 20 Oct 2020 20:46:43 +0800 Subject: [PATCH 097/374] Rename duke package to dietbook --- build.gradle | 2 +- src/main/java/seedu/calculator/Calculator.java | 2 +- src/main/java/seedu/{duke => dietbook}/DietBook.java | 6 +++--- .../java/seedu/{duke => dietbook}/DietException.java | 2 +- src/main/java/seedu/{duke => dietbook}/Manager.java | 12 ++++++------ src/main/java/seedu/{duke => dietbook}/Parser.java | 8 ++++---- src/main/java/seedu/{duke => dietbook}/Ui.java | 6 +++--- .../seedu/{duke => dietbook}/database/Canteen.java | 2 +- .../seedu/{duke => dietbook}/database/DataBase.java | 4 ++-- .../seedu/{duke => dietbook}/database/Store.java | 4 ++-- .../java/seedu/{duke => dietbook}/food/Food.java | 2 +- .../seedu/{duke => dietbook}/list/FoodEntry.java | 4 ++-- .../java/seedu/{duke => dietbook}/list/FoodList.java | 4 ++-- .../{duke => dietbook}/list/FoodListManager.java | 4 ++-- .../list/FoodNotFoundException.java | 2 +- .../{duke => dietbook}/person/ActivityLevel.java | 2 +- .../java/seedu/{duke => dietbook}/person/Gender.java | 2 +- .../java/seedu/{duke => dietbook}/person/Person.java | 2 +- src/test/java/seedu/calculator/CalculatorTest.java | 2 +- src/test/java/seedu/{duke => dietbook}/DukeTest.java | 2 +- .../java/seedu/{duke => dietbook}/FoodListTest.java | 6 +++--- .../{duke => dietbook}/database/DataBaseTest.java | 2 +- .../java/seedu/{duke => dietbook}/food/FoodTest.java | 5 ++--- .../seedu/{duke => dietbook}/person/PersonTest.java | 2 +- 24 files changed, 44 insertions(+), 45 deletions(-) rename src/main/java/seedu/{duke => dietbook}/DietBook.java (91%) rename src/main/java/seedu/{duke => dietbook}/DietException.java (89%) rename src/main/java/seedu/{duke => dietbook}/Manager.java (86%) rename src/main/java/seedu/{duke => dietbook}/Parser.java (98%) rename src/main/java/seedu/{duke => dietbook}/Ui.java (99%) rename src/main/java/seedu/{duke => dietbook}/database/Canteen.java (94%) rename src/main/java/seedu/{duke => dietbook}/database/DataBase.java (99%) rename src/main/java/seedu/{duke => dietbook}/database/Store.java (91%) rename src/main/java/seedu/{duke => dietbook}/food/Food.java (97%) rename src/main/java/seedu/{duke => dietbook}/list/FoodEntry.java (92%) rename src/main/java/seedu/{duke => dietbook}/list/FoodList.java (97%) rename src/main/java/seedu/{duke => dietbook}/list/FoodListManager.java (96%) rename src/main/java/seedu/{duke => dietbook}/list/FoodNotFoundException.java (66%) rename src/main/java/seedu/{duke => dietbook}/person/ActivityLevel.java (97%) rename src/main/java/seedu/{duke => dietbook}/person/Gender.java (95%) rename src/main/java/seedu/{duke => dietbook}/person/Person.java (99%) rename src/test/java/seedu/{duke => dietbook}/DukeTest.java (88%) rename src/test/java/seedu/{duke => dietbook}/FoodListTest.java (83%) rename src/test/java/seedu/{duke => dietbook}/database/DataBaseTest.java (99%) rename src/test/java/seedu/{duke => dietbook}/food/FoodTest.java (89%) rename src/test/java/seedu/{duke => dietbook}/person/PersonTest.java (98%) diff --git a/build.gradle b/build.gradle index 0130e89069..8eee256504 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ test { } application { - mainClassName = "seedu.duke.DietBook" + mainClassName = "seedu.dietbook.DietBook" } shadowJar { diff --git a/src/main/java/seedu/calculator/Calculator.java b/src/main/java/seedu/calculator/Calculator.java index e50cf15f43..fe7c288242 100644 --- a/src/main/java/seedu/calculator/Calculator.java +++ b/src/main/java/seedu/calculator/Calculator.java @@ -1,6 +1,6 @@ package seedu.calculator; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.util.ArrayList; diff --git a/src/main/java/seedu/duke/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java similarity index 91% rename from src/main/java/seedu/duke/DietBook.java rename to src/main/java/seedu/dietbook/DietBook.java index b7003f2753..24128b4a51 100644 --- a/src/main/java/seedu/duke/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -1,9 +1,9 @@ -package seedu.duke; +package seedu.dietbook; import java.io.FileNotFoundException; import java.io.IOException; -import seedu.duke.database.DataBase; -import seedu.duke.list.FoodList; +import seedu.dietbook.database.DataBase; +import seedu.dietbook.list.FoodList; public class DietBook { private FoodList foodList; diff --git a/src/main/java/seedu/duke/DietException.java b/src/main/java/seedu/dietbook/DietException.java similarity index 89% rename from src/main/java/seedu/duke/DietException.java rename to src/main/java/seedu/dietbook/DietException.java index 1cc3e09655..922514bfa5 100644 --- a/src/main/java/seedu/duke/DietException.java +++ b/src/main/java/seedu/dietbook/DietException.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.dietbook; public class DietException extends Exception { public DietException(String message) { diff --git a/src/main/java/seedu/duke/Manager.java b/src/main/java/seedu/dietbook/Manager.java similarity index 86% rename from src/main/java/seedu/duke/Manager.java rename to src/main/java/seedu/dietbook/Manager.java index 3b755c49ff..439b788e06 100644 --- a/src/main/java/seedu/duke/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -1,11 +1,11 @@ -package seedu.duke; +package seedu.dietbook; -import seedu.duke.list.FoodList; -import seedu.duke.person.ActivityLevel; -import seedu.duke.person.Person; +import seedu.dietbook.list.FoodList; +import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.Person; import seedu.calculator.Calculator; -import seedu.duke.database.DataBase; -import seedu.duke.person.Gender; +import seedu.dietbook.database.DataBase; +import seedu.dietbook.person.Gender; import java.util.Scanner; diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/dietbook/Parser.java similarity index 98% rename from src/main/java/seedu/duke/Parser.java rename to src/main/java/seedu/dietbook/Parser.java index c0e32315e0..b15f65b1a1 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/dietbook/Parser.java @@ -1,9 +1,9 @@ -package seedu.duke; +package seedu.dietbook; import seedu.calculator.Calculator; -import seedu.duke.list.FoodList; -import seedu.duke.person.Gender; -import seedu.duke.person.ActivityLevel; +import seedu.dietbook.list.FoodList; +import seedu.dietbook.person.Gender; +import seedu.dietbook.person.ActivityLevel; import java.io.FileNotFoundException; diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/dietbook/Ui.java similarity index 99% rename from src/main/java/seedu/duke/Ui.java rename to src/main/java/seedu/dietbook/Ui.java index 25f3a8d909..aee481e4ff 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -1,7 +1,7 @@ -package seedu.duke; +package seedu.dietbook; -import seedu.duke.food.Food; -import seedu.duke.person.ActivityLevel; +import seedu.dietbook.food.Food; +import seedu.dietbook.person.ActivityLevel; import java.util.List; diff --git a/src/main/java/seedu/duke/database/Canteen.java b/src/main/java/seedu/dietbook/database/Canteen.java similarity index 94% rename from src/main/java/seedu/duke/database/Canteen.java rename to src/main/java/seedu/dietbook/database/Canteen.java index 1243b0d9be..51946e60a8 100644 --- a/src/main/java/seedu/duke/database/Canteen.java +++ b/src/main/java/seedu/dietbook/database/Canteen.java @@ -1,4 +1,4 @@ -package seedu.duke.database; +package seedu.dietbook.database; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java similarity index 99% rename from src/main/java/seedu/duke/database/DataBase.java rename to src/main/java/seedu/dietbook/database/DataBase.java index 687d6528a6..0dd381a166 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -1,7 +1,7 @@ -package seedu.duke.database; +package seedu.dietbook.database; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/dietbook/database/Store.java similarity index 91% rename from src/main/java/seedu/duke/database/Store.java rename to src/main/java/seedu/dietbook/database/Store.java index af311ae856..fcb10d3071 100644 --- a/src/main/java/seedu/duke/database/Store.java +++ b/src/main/java/seedu/dietbook/database/Store.java @@ -1,6 +1,6 @@ -package seedu.duke.database; +package seedu.dietbook.database; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/dietbook/food/Food.java similarity index 97% rename from src/main/java/seedu/duke/food/Food.java rename to src/main/java/seedu/dietbook/food/Food.java index 9ea158bf5f..cdb6447657 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/dietbook/food/Food.java @@ -1,4 +1,4 @@ -package seedu.duke.food; +package seedu.dietbook.food; /** diff --git a/src/main/java/seedu/duke/list/FoodEntry.java b/src/main/java/seedu/dietbook/list/FoodEntry.java similarity index 92% rename from src/main/java/seedu/duke/list/FoodEntry.java rename to src/main/java/seedu/dietbook/list/FoodEntry.java index 7aa6f115e6..b58dc742e3 100644 --- a/src/main/java/seedu/duke/list/FoodEntry.java +++ b/src/main/java/seedu/dietbook/list/FoodEntry.java @@ -1,6 +1,6 @@ -package seedu.duke.list; +package seedu.dietbook.list; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; /** diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java similarity index 97% rename from src/main/java/seedu/duke/list/FoodList.java rename to src/main/java/seedu/dietbook/list/FoodList.java index 92f92c47e3..0e3f26368f 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -1,7 +1,7 @@ -package seedu.duke.list; +package seedu.dietbook.list; import java.util.ArrayList; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; /** diff --git a/src/main/java/seedu/duke/list/FoodListManager.java b/src/main/java/seedu/dietbook/list/FoodListManager.java similarity index 96% rename from src/main/java/seedu/duke/list/FoodListManager.java rename to src/main/java/seedu/dietbook/list/FoodListManager.java index 7165cae639..c0a15dbbd1 100644 --- a/src/main/java/seedu/duke/list/FoodListManager.java +++ b/src/main/java/seedu/dietbook/list/FoodListManager.java @@ -1,6 +1,6 @@ -package seedu.duke.list; +package seedu.dietbook.list; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.util.ArrayList; /** diff --git a/src/main/java/seedu/duke/list/FoodNotFoundException.java b/src/main/java/seedu/dietbook/list/FoodNotFoundException.java similarity index 66% rename from src/main/java/seedu/duke/list/FoodNotFoundException.java rename to src/main/java/seedu/dietbook/list/FoodNotFoundException.java index ca99d41a3e..9ab7aa42f3 100644 --- a/src/main/java/seedu/duke/list/FoodNotFoundException.java +++ b/src/main/java/seedu/dietbook/list/FoodNotFoundException.java @@ -1,4 +1,4 @@ -package seedu.duke.list; +package seedu.dietbook.list; public class FoodNotFoundException extends Exception{ } diff --git a/src/main/java/seedu/duke/person/ActivityLevel.java b/src/main/java/seedu/dietbook/person/ActivityLevel.java similarity index 97% rename from src/main/java/seedu/duke/person/ActivityLevel.java rename to src/main/java/seedu/dietbook/person/ActivityLevel.java index a045013b0d..ba71bd0a74 100644 --- a/src/main/java/seedu/duke/person/ActivityLevel.java +++ b/src/main/java/seedu/dietbook/person/ActivityLevel.java @@ -1,4 +1,4 @@ -package seedu.duke.person; +package seedu.dietbook.person; /** * Represents the physical activity level of a person or the amount of exercise a person engages in. diff --git a/src/main/java/seedu/duke/person/Gender.java b/src/main/java/seedu/dietbook/person/Gender.java similarity index 95% rename from src/main/java/seedu/duke/person/Gender.java rename to src/main/java/seedu/dietbook/person/Gender.java index 52b13a5a4b..b0bbe03e22 100644 --- a/src/main/java/seedu/duke/person/Gender.java +++ b/src/main/java/seedu/dietbook/person/Gender.java @@ -1,4 +1,4 @@ -package seedu.duke.person; +package seedu.dietbook.person; /** * Represents the gender of a person. diff --git a/src/main/java/seedu/duke/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java similarity index 99% rename from src/main/java/seedu/duke/person/Person.java rename to src/main/java/seedu/dietbook/person/Person.java index ad3d49b348..eb85de0f98 100644 --- a/src/main/java/seedu/duke/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -1,4 +1,4 @@ -package seedu.duke.person; +package seedu.dietbook.person; /** * Represents a Person. diff --git a/src/test/java/seedu/calculator/CalculatorTest.java b/src/test/java/seedu/calculator/CalculatorTest.java index e58f33a249..3c4502b25f 100644 --- a/src/test/java/seedu/calculator/CalculatorTest.java +++ b/src/test/java/seedu/calculator/CalculatorTest.java @@ -1,7 +1,7 @@ package seedu.calculator; import org.junit.jupiter.api.Test; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.util.ArrayList; diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/dietbook/DukeTest.java similarity index 88% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/dietbook/DukeTest.java index 2dda5fd651..eef4b4c86a 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/dietbook/DukeTest.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.dietbook; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/seedu/duke/FoodListTest.java b/src/test/java/seedu/dietbook/FoodListTest.java similarity index 83% rename from src/test/java/seedu/duke/FoodListTest.java rename to src/test/java/seedu/dietbook/FoodListTest.java index 96b31d9647..1d3858913a 100644 --- a/src/test/java/seedu/duke/FoodListTest.java +++ b/src/test/java/seedu/dietbook/FoodListTest.java @@ -1,7 +1,7 @@ -package seedu.duke; +package seedu.dietbook; -import seedu.duke.list.FoodList; -import seedu.duke.food.Food; +import seedu.dietbook.list.FoodList; +import seedu.dietbook.food.Food; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/duke/database/DataBaseTest.java b/src/test/java/seedu/dietbook/database/DataBaseTest.java similarity index 99% rename from src/test/java/seedu/duke/database/DataBaseTest.java rename to src/test/java/seedu/dietbook/database/DataBaseTest.java index 004a1d0a70..4729c939e2 100644 --- a/src/test/java/seedu/duke/database/DataBaseTest.java +++ b/src/test/java/seedu/dietbook/database/DataBaseTest.java @@ -1,4 +1,4 @@ -package seedu.duke.database; +package seedu.dietbook.database; import java.util.NoSuchElementException; diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/dietbook/food/FoodTest.java similarity index 89% rename from src/test/java/seedu/duke/food/FoodTest.java rename to src/test/java/seedu/dietbook/food/FoodTest.java index 8c866c2f89..9696e058fa 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/dietbook/food/FoodTest.java @@ -1,7 +1,6 @@ -package seedu.duke.food; - -import seedu.duke.food.Food; +package seedu.dietbook.food; +import seedu.dietbook.food.Food; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/duke/person/PersonTest.java b/src/test/java/seedu/dietbook/person/PersonTest.java similarity index 98% rename from src/test/java/seedu/duke/person/PersonTest.java rename to src/test/java/seedu/dietbook/person/PersonTest.java index 714a66021b..ec5311d784 100644 --- a/src/test/java/seedu/duke/person/PersonTest.java +++ b/src/test/java/seedu/dietbook/person/PersonTest.java @@ -1,4 +1,4 @@ -package seedu.duke.person; +package seedu.dietbook.person; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From d78cdb1e25b0f54d5fc612f57b2a1e1272338811 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 20 Oct 2020 20:48:06 +0800 Subject: [PATCH 098/374] Remove Duke.java --- src/main/java/seedu/duke/Duke.java | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/main/java/seedu/duke/Duke.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 9f8063ee94..0000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} \ No newline at end of file From a169dd4658b7cec96d74d6223d4b1a28b43f8a77 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Wed, 21 Oct 2020 14:59:34 +0800 Subject: [PATCH 099/374] add DatedFoodEntry FoodEntry child class that supports datetime functionality. --- .../java/seedu/duke/list/DatedFoodEntry.java | 64 +++++++++++++++++++ .../java/seedu/duke/list/FoodListTest.java | 16 ++++- 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/main/java/seedu/duke/list/DatedFoodEntry.java diff --git a/src/main/java/seedu/duke/list/DatedFoodEntry.java b/src/main/java/seedu/duke/list/DatedFoodEntry.java new file mode 100644 index 0000000000..ad2c3f9395 --- /dev/null +++ b/src/main/java/seedu/duke/list/DatedFoodEntry.java @@ -0,0 +1,64 @@ +package seedu.duke.list; + +import seedu.duke.food.Food; +import java.time.LocalDateTime; + +public class DatedFoodEntry extends FoodEntry implements Comparable { + + protected final LocalDateTime dateTime; + + /** + * Default constructor method. + * Creates a food entry with a DateTime set to now. + */ + public DatedFoodEntry(int portionSize, String name, int calorie, + int carbohydrate, int protein, int fat) { + super(portionSize, name, calorie, carbohydrate, protein, fat); + this.dateTime = LocalDateTime.now(); + } + + /** + * Convenience constructor for testing. + * Also could be for adding food objects directly via DataBase. + */ + public DatedFoodEntry(int portionSize, Food food) { + super(portionSize, food); + this.dateTime = LocalDateTime.now(); + } + + /** + * Convenience constructor for testing. + * Could also be for adding backlogged entries with food objects directly via Database. + */ + public DatedFoodEntry(int portionSize, Food food, LocalDateTime dateTime) { + super(portionSize, food); + assert (dateTime != null) : "Should not add null DateTime." + + "Use other constructor to create with LocalDateTime.now() instead."; + this.dateTime = dateTime; + } + + /** + * For adding backlogged entries. + */ + public DatedFoodEntry(int portionSize, String name, int calorie, + int carbohydrate, int protein, int fat, LocalDateTime dateTime) { + super(portionSize, name, calorie, carbohydrate, protein, fat); + + assert (dateTime != null) : "Should not add null DateTime." + + "Use other constructor to create with LocalDateTime.now() instead."; + assert (dateTime.isBefore(LocalDateTime.now())) : "Attempting to add entry that hasn't happened yet."; + + this.dateTime = dateTime; + + } + + protected LocalDateTime getDateTime() { + return dateTime; + } + + @Override + public int compareTo(DatedFoodEntry other) { + return dateTime.compareTo(other.getDateTime()); + } + +} diff --git a/src/test/java/seedu/duke/list/FoodListTest.java b/src/test/java/seedu/duke/list/FoodListTest.java index 99779fe16e..e0e056a79b 100644 --- a/src/test/java/seedu/duke/list/FoodListTest.java +++ b/src/test/java/seedu/duke/list/FoodListTest.java @@ -3,19 +3,24 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -import seedu.duke.food.Food; +import java.time.LocalDateTime; +import seedu.duke.food.Food; class FoodListTest { private FoodList list; + private Food food; @BeforeEach protected void setUp() { this.list = new FoodList(); Food food = new Food("Kobe Beef", 480,50,40,30); + this.food = food; + list.addFood(3, food); list.addFood(2, "Sashimi", 100, 0, 30, 10); @@ -44,4 +49,13 @@ void deleteItemTest() { assertEquals(list.delete(1), entry.toString()); } + @Test + void dateComparisonTest() { + DatedFoodEntry entry = new DatedFoodEntry(2, food); + DatedFoodEntry pastEntry = new DatedFoodEntry(2, food, LocalDateTime.MIN); + + assertTrue(entry.compareTo(pastEntry) > 0); + + } + } From 4f1855d2f03f46a747963340fa7c974e62fec0c4 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 21 Oct 2020 15:53:29 +0800 Subject: [PATCH 100/374] implement saver --- src/main/java/seedu/duke/saveload/Saver.java | 65 ++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/seedu/duke/saveload/Saver.java diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/duke/saveload/Saver.java new file mode 100644 index 0000000000..0b831035a1 --- /dev/null +++ b/src/main/java/seedu/duke/saveload/Saver.java @@ -0,0 +1,65 @@ +package seedu.duke.saveload; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class Saver { + private static final String ROOT_DIRECTORY = System.getProperty("user.home"); + private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; + private static final String EMPTY_SYMBOL = "%NULL&!!LL"; + private static final String SEPERATOR_SYMBOL = "&%SEPERATOR%$$"; + private static final String FILE_EXTENSION = ".txt"; + + private final String[][] entries; + private final int height; + private final int width; + + public Saver(int width, int height){ + this.height = height; + this.width = width; + this.entries = new String[height][width]; + + for (int i = 0; i < width; i ++){ + for (int j = 0; j < height; j++){ + entries[j][i] = EMPTY_SYMBOL; + } + } + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void add(String entry, int x_position, int y_position){ + this.entries[y_position][x_position] = entry; + } + + public void delete(int x_position, int y_position){ + this.entries[y_position][x_position] = EMPTY_SYMBOL; + } + + public void save(String folderName, String fileName){ + try { + File directory = new File(BASE_FOLDER_NAME + File.separator + folderName); + directory.mkdir(); + FileWriter writer = new FileWriter(BASE_FOLDER_NAME + File.separator + folderName + + File.separator + fileName + FILE_EXTENSION); + for (int j = 0; j < height; j ++){ + for (int i = 0; i < width; i++){ + writer.write(SEPERATOR_SYMBOL); + writer.write(entries[j][i]); + } + writer.write("\n"); + } + writer.close(); + } + catch(IOException e){ + System.out.println("Oops, the file writer took in a directory for some reason!"); + } + } +} From 3dc0ac39df982fb7c7ddf295a27d9f381e6ffbcf Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 21 Oct 2020 16:40:16 +0800 Subject: [PATCH 101/374] add test for saver class --- src/main/java/seedu/duke/saveload/Loader.java | 5 ++ src/main/java/seedu/duke/saveload/Saver.java | 53 +++++++++++++++++-- .../seedu/duke/saveload/SaveLoadFileTest.java | 12 +++++ .../java/seedu/duke/saveload/SaverTest.java | 37 +++++++++++++ 4 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 src/main/java/seedu/duke/saveload/Loader.java create mode 100644 src/test/java/seedu/duke/saveload/SaveLoadFileTest.java create mode 100644 src/test/java/seedu/duke/saveload/SaverTest.java diff --git a/src/main/java/seedu/duke/saveload/Loader.java b/src/main/java/seedu/duke/saveload/Loader.java new file mode 100644 index 0000000000..c1f5360a72 --- /dev/null +++ b/src/main/java/seedu/duke/saveload/Loader.java @@ -0,0 +1,5 @@ +package seedu.duke.saveload; + +public class Loader { + +} diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/duke/saveload/Saver.java index 0b831035a1..1ae3d08a9c 100644 --- a/src/main/java/seedu/duke/saveload/Saver.java +++ b/src/main/java/seedu/duke/saveload/Saver.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.Optional; public class Saver { private static final String ROOT_DIRECTORY = System.getProperty("user.home"); @@ -11,6 +12,11 @@ public class Saver { private static final String SEPERATOR_SYMBOL = "&%SEPERATOR%$$"; private static final String FILE_EXTENSION = ".txt"; + static { + File rootDirectory = new File(BASE_FOLDER_NAME); + rootDirectory.mkdir(); + } + private final String[][] entries; private final int height; private final int width; @@ -35,20 +41,59 @@ public int getHeight() { return height; } - public void add(String entry, int x_position, int y_position){ - this.entries[y_position][x_position] = entry; + public void add(String entry, int x_position, int y_position) throws IndexOutOfBoundsException{ + try { + this.entries[y_position - 1][x_position - 1] = entry; + } + catch (ArrayIndexOutOfBoundsException e){ + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); + } + } + + public void delete(int x_position, int y_position) throws IndexOutOfBoundsException{ + try { + this.entries[y_position - 1][x_position - 1] = EMPTY_SYMBOL; + } + catch (ArrayIndexOutOfBoundsException e){ + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); + } } - public void delete(int x_position, int y_position){ - this.entries[y_position][x_position] = EMPTY_SYMBOL; + public Optional get(int x_position, int y_position){ + try { + if (this.entries[y_position - 1][x_position - 1].equals(EMPTY_SYMBOL)) { + return Optional.empty(); + } else { + return Optional.of(this.entries[y_position][x_position]); + } + } + catch (ArrayIndexOutOfBoundsException e){ + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); + } } + /*** + * Saves the data table into a text file in the following format: + * width + * height + * row 1 entry 1 (seperator) row 1 entry 2 (separator) .... + * row 2 entry 1 (separator) row 2 entry 2 (separator) .... + * .... + * + * @param folderName name of the folder + * @param fileName name of the file + */ public void save(String folderName, String fileName){ try { File directory = new File(BASE_FOLDER_NAME + File.separator + folderName); directory.mkdir(); FileWriter writer = new FileWriter(BASE_FOLDER_NAME + File.separator + folderName + File.separator + fileName + FILE_EXTENSION); + writer.write(width + "\n"); + writer.write(height + "\n"); for (int j = 0; j < height; j ++){ for (int i = 0; i < width; i++){ writer.write(SEPERATOR_SYMBOL); diff --git a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java new file mode 100644 index 0000000000..b3a7c40196 --- /dev/null +++ b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java @@ -0,0 +1,12 @@ +package seedu.duke.saveload; + +public class SaveLoadFileTest { + public static void main(String[] args){ + Saver saver = new Saver(10, 6); + saver.add("banana", 5, 2); + saver.add("pineapple", 7, 1); + saver.add("cheetan", 2, 3); + saver.add("beetles", 1, 4); + saver.save("save_load_test","test1"); + } +} diff --git a/src/test/java/seedu/duke/saveload/SaverTest.java b/src/test/java/seedu/duke/saveload/SaverTest.java new file mode 100644 index 0000000000..7df613fa0b --- /dev/null +++ b/src/test/java/seedu/duke/saveload/SaverTest.java @@ -0,0 +1,37 @@ +package seedu.duke.saveload; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Optional; + +public class SaverTest { + private Saver saver; + + @BeforeEach + public void setUp() throws Exception{ + saver = new Saver(10, 6); + saver.add("banana", 5, 2); + saver.add("pineapple", 7, 1); + } + + @Test + public void get(){ + assertEquals(Optional.of("banana"), saver.get(5,2)); + assertEquals(Optional.of("pineapple"), saver.get(7,1)); + assertEquals(Optional.empty(), saver.get(1,1)); + assertThrows(IndexOutOfBoundsException.class, () -> saver.get(-1992,3500)); + } + + @Test + public void delete(){ + saver.add("Deletion lotion", 4,2); + assertEquals(Optional.of("Deletion lotion"), saver.get(4,2)); + saver.delete(4,2); + assertEquals(Optional.empty(), saver.get(4,2)); + assertThrows(IndexOutOfBoundsException.class, () -> saver.delete(-3402,9999)); + } +} From 62c030adafbc04a2abc67acd1832f8abcb599c34 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 21 Oct 2020 17:03:50 +0800 Subject: [PATCH 102/374] implement loader --- src/main/java/seedu/duke/saveload/Loader.java | 63 ++++++++++++++++++- src/main/java/seedu/duke/saveload/Saver.java | 6 +- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/saveload/Loader.java b/src/main/java/seedu/duke/saveload/Loader.java index c1f5360a72..0fa4698986 100644 --- a/src/main/java/seedu/duke/saveload/Loader.java +++ b/src/main/java/seedu/duke/saveload/Loader.java @@ -1,5 +1,66 @@ package seedu.duke.saveload; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Optional; +import java.util.Scanner; + public class Loader { - + private static final String ROOT_DIRECTORY = System.getProperty("user.home"); + private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; + private static final String EMPTY_SYMBOL = "%NULL&!!LL"; + private static final String SEPARATOR_SYMBOL = "&%SEPERATOR%AAA%"; + private static final String FILE_EXTENSION = ".txt"; + + private final String[][] entries; + private final int width; + private final int height; + + public static Loader load(String folderName, String fileName) throws FileNotFoundException { + return new Loader(folderName, fileName); + } + + private Loader(String folderName, String fileName) throws FileNotFoundException { + File file = new File(BASE_FOLDER_NAME + File.separator + folderName + +File.separator + fileName + FILE_EXTENSION); + Scanner reader = new Scanner(file); + width = Integer.parseInt(reader.nextLine()); + height = Integer.parseInt(reader.nextLine()); + entries = new String[height][width]; + String[] line; + for (int j = 0; j < height; j++){ + line = reader.nextLine().split(SEPARATOR_SYMBOL); + if (width >= 0) System.arraycopy(line, 1, entries[j], 0, width); + } + } + + /** + * Get the String entry stored at the specified position in the table if it is present. + * @param x_position the x position in the table from 1 to the table width + * @param y_position the y position in the table from 1 to the table height + * @return Optional of the String. The Optional is empty is no entry is stored + * @throws IndexOutOfBoundsException if the x or y given is not as above + */ + public Optional get(int x_position, int y_position) throws IndexOutOfBoundsException { + try { + if (this.entries[y_position - 1][x_position - 1].equals(EMPTY_SYMBOL)) { + return Optional.empty(); + } else { + return Optional.of(this.entries[y_position][x_position]); + } + } + catch (ArrayIndexOutOfBoundsException e){ + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); + } + } + + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } } diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/duke/saveload/Saver.java index 1ae3d08a9c..89be347e28 100644 --- a/src/main/java/seedu/duke/saveload/Saver.java +++ b/src/main/java/seedu/duke/saveload/Saver.java @@ -9,7 +9,7 @@ public class Saver { private static final String ROOT_DIRECTORY = System.getProperty("user.home"); private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; private static final String EMPTY_SYMBOL = "%NULL&!!LL"; - private static final String SEPERATOR_SYMBOL = "&%SEPERATOR%$$"; + private static final String SEPERATOR_SYMBOL = "&%SEPERATOR%AAA%"; private static final String FILE_EXTENSION = ".txt"; static { @@ -79,8 +79,8 @@ public Optional get(int x_position, int y_position){ * Saves the data table into a text file in the following format: * width * height - * row 1 entry 1 (seperator) row 1 entry 2 (separator) .... - * row 2 entry 1 (separator) row 2 entry 2 (separator) .... + * (seperator) row 1 entry 1 (seperator) row 1 entry 2 (separator) .... + * (seperator) row 2 entry 1 (separator) row 2 entry 2 (separator) .... * .... * * @param folderName name of the folder From b43add73ab70d976090cda3d6ed3648885e72957 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 21 Oct 2020 17:18:47 +0800 Subject: [PATCH 103/374] test loader class --- src/main/java/seedu/duke/saveload/Loader.java | 8 ++++++-- .../java/seedu/duke/saveload/SaveLoadFileTest.java | 13 ++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/saveload/Loader.java b/src/main/java/seedu/duke/saveload/Loader.java index 0fa4698986..2d532fd7f3 100644 --- a/src/main/java/seedu/duke/saveload/Loader.java +++ b/src/main/java/seedu/duke/saveload/Loader.java @@ -5,6 +5,10 @@ import java.util.Optional; import java.util.Scanner; +/** + * Handles reading of stored text file. + * Note: the first five fields must be same + */ public class Loader { private static final String ROOT_DIRECTORY = System.getProperty("user.home"); private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; @@ -46,10 +50,10 @@ public Optional get(int x_position, int y_position) throws IndexOutOfBou if (this.entries[y_position - 1][x_position - 1].equals(EMPTY_SYMBOL)) { return Optional.empty(); } else { - return Optional.of(this.entries[y_position][x_position]); + return Optional.of(this.entries[y_position-1][x_position-1]); } } - catch (ArrayIndexOutOfBoundsException e){ + catch (ArrayIndexOutOfBoundsException e) { throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + "saver table!"); } diff --git a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java index b3a7c40196..d46116b9b7 100644 --- a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java +++ b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java @@ -1,12 +1,23 @@ package seedu.duke.saveload; +import java.io.FileNotFoundException; + public class SaveLoadFileTest { - public static void main(String[] args){ + public static void main(String[] args) throws FileNotFoundException { Saver saver = new Saver(10, 6); + Loader loader; saver.add("banana", 5, 2); saver.add("pineapple", 7, 1); saver.add("cheetan", 2, 3); saver.add("beetles", 1, 4); saver.save("save_load_test","test1"); + + loader = Loader.load("save_load_test","test1"); + loader.get(5, 2).ifPresent(System.out::println); + loader.get(7, 1).ifPresent(System.out::println); + loader.get(2, 3).ifPresent(System.out::println); + loader.get(1, 4).ifPresent(System.out::println); + loader.get(1, 3).ifPresent(System.out::println); + loader.get(10, 6).ifPresent(System.out::println); } } From fd5ad65f8e1fb939b966a4f5623d7a0c0d666a2f Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 21 Oct 2020 17:26:09 +0800 Subject: [PATCH 104/374] add documentation --- src/main/java/seedu/duke/saveload/Saver.java | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/duke/saveload/Saver.java index 89be347e28..7d7d3136c6 100644 --- a/src/main/java/seedu/duke/saveload/Saver.java +++ b/src/main/java/seedu/duke/saveload/Saver.java @@ -5,6 +5,11 @@ import java.io.IOException; import java.util.Optional; + +/** + * Saver class allows storage of string data into a table with a given width and height + * It has a function that can write the data stored in it's table. + */ public class Saver { private static final String ROOT_DIRECTORY = System.getProperty("user.home"); private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; @@ -41,6 +46,13 @@ public int getHeight() { return height; } + /** + * Adds the string provided to the position x,y on the table. + * @param entry the entry to be provided into this position + * @param x_position x position + * @param y_position y position + * @throws IndexOutOfBoundsException x or y position is out of bounds + */ public void add(String entry, int x_position, int y_position) throws IndexOutOfBoundsException{ try { this.entries[y_position - 1][x_position - 1] = entry; @@ -51,6 +63,12 @@ public void add(String entry, int x_position, int y_position) throws IndexOutOfB } } + /** + * Deletes the entry in the table. + * @param x_position x position + * @param y_position y position + * @throws IndexOutOfBoundsException + */ public void delete(int x_position, int y_position) throws IndexOutOfBoundsException{ try { this.entries[y_position - 1][x_position - 1] = EMPTY_SYMBOL; @@ -61,6 +79,12 @@ public void delete(int x_position, int y_position) throws IndexOutOfBoundsExcept } } + /** + * Gets a entry stored in the table. + * @param x_position x position + * @param y_position y position + * @return Optional of String that is empty if the position does not contain an entry. + */ public Optional get(int x_position, int y_position){ try { if (this.entries[y_position - 1][x_position - 1].equals(EMPTY_SYMBOL)) { From 30de965ab56cda247a976676fb09c4dc9513c2fe Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 21 Oct 2020 17:42:44 +0800 Subject: [PATCH 105/374] fix check style tests --- src/main/java/seedu/duke/saveload/Loader.java | 25 +++---- src/main/java/seedu/duke/saveload/Saver.java | 66 +++++++++---------- .../seedu/duke/saveload/SaveLoadFileTest.java | 2 +- .../java/seedu/duke/saveload/SaverTest.java | 8 +-- 4 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/main/java/seedu/duke/saveload/Loader.java b/src/main/java/seedu/duke/saveload/Loader.java index 2d532fd7f3..72311fd1b1 100644 --- a/src/main/java/seedu/duke/saveload/Loader.java +++ b/src/main/java/seedu/duke/saveload/Loader.java @@ -26,36 +26,37 @@ public static Loader load(String folderName, String fileName) throws FileNotFoun private Loader(String folderName, String fileName) throws FileNotFoundException { File file = new File(BASE_FOLDER_NAME + File.separator + folderName - +File.separator + fileName + FILE_EXTENSION); + + File.separator + fileName + FILE_EXTENSION); Scanner reader = new Scanner(file); width = Integer.parseInt(reader.nextLine()); height = Integer.parseInt(reader.nextLine()); entries = new String[height][width]; String[] line; - for (int j = 0; j < height; j++){ + for (int j = 0; j < height; j++) { line = reader.nextLine().split(SEPARATOR_SYMBOL); - if (width >= 0) System.arraycopy(line, 1, entries[j], 0, width); + if (width >= 0) { + System.arraycopy(line, 1, entries[j], 0, width); + } } } /** * Get the String entry stored at the specified position in the table if it is present. - * @param x_position the x position in the table from 1 to the table width - * @param y_position the y position in the table from 1 to the table height + * @param xposition the x position in the table from 1 to the table width + * @param yposition the y position in the table from 1 to the table height * @return Optional of the String. The Optional is empty is no entry is stored * @throws IndexOutOfBoundsException if the x or y given is not as above */ - public Optional get(int x_position, int y_position) throws IndexOutOfBoundsException { + public Optional get(int xposition, int yposition) throws IndexOutOfBoundsException { try { - if (this.entries[y_position - 1][x_position - 1].equals(EMPTY_SYMBOL)) { + if (this.entries[yposition - 1][xposition - 1].equals(EMPTY_SYMBOL)) { return Optional.empty(); } else { - return Optional.of(this.entries[y_position-1][x_position-1]); + return Optional.of(this.entries[yposition - 1][xposition - 1]); } - } - catch (ArrayIndexOutOfBoundsException e) { - throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + - "saver table!"); + } catch (ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); } } diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/duke/saveload/Saver.java index 7d7d3136c6..720b0d1ae1 100644 --- a/src/main/java/seedu/duke/saveload/Saver.java +++ b/src/main/java/seedu/duke/saveload/Saver.java @@ -26,13 +26,13 @@ public class Saver { private final int height; private final int width; - public Saver(int width, int height){ + public Saver(int width, int height) { this.height = height; this.width = width; this.entries = new String[height][width]; - for (int i = 0; i < width; i ++){ - for (int j = 0; j < height; j++){ + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { entries[j][i] = EMPTY_SYMBOL; } } @@ -49,57 +49,54 @@ public int getHeight() { /** * Adds the string provided to the position x,y on the table. * @param entry the entry to be provided into this position - * @param x_position x position - * @param y_position y position + * @param xposition x position + * @param yposition y position * @throws IndexOutOfBoundsException x or y position is out of bounds */ - public void add(String entry, int x_position, int y_position) throws IndexOutOfBoundsException{ + public void add(String entry, int xposition, int yposition) throws IndexOutOfBoundsException { try { - this.entries[y_position - 1][x_position - 1] = entry; - } - catch (ArrayIndexOutOfBoundsException e){ - throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + - "saver table!"); + this.entries[yposition - 1][xposition - 1] = entry; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); } } /** * Deletes the entry in the table. - * @param x_position x position - * @param y_position y position - * @throws IndexOutOfBoundsException + * @param xposition x position + * @param yposition y position + * @throws IndexOutOfBoundsException x or y position is out of bounds */ - public void delete(int x_position, int y_position) throws IndexOutOfBoundsException{ + public void delete(int xposition, int yposition) throws IndexOutOfBoundsException { try { - this.entries[y_position - 1][x_position - 1] = EMPTY_SYMBOL; - } - catch (ArrayIndexOutOfBoundsException e){ - throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + - "saver table!"); + this.entries[yposition - 1][xposition - 1] = EMPTY_SYMBOL; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); } } /** * Gets a entry stored in the table. - * @param x_position x position - * @param y_position y position + * @param xposition x position + * @param yposition y position * @return Optional of String that is empty if the position does not contain an entry. */ - public Optional get(int x_position, int y_position){ + public Optional get(int xposition, int yposition) { try { - if (this.entries[y_position - 1][x_position - 1].equals(EMPTY_SYMBOL)) { + if (this.entries[yposition - 1][xposition - 1].equals(EMPTY_SYMBOL)) { return Optional.empty(); } else { - return Optional.of(this.entries[y_position][x_position]); + return Optional.of(this.entries[yposition - 1][xposition - 1]); } - } - catch (ArrayIndexOutOfBoundsException e){ - throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + - "saver table!"); + } catch (ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); } } - /*** + /** * Saves the data table into a text file in the following format: * width * height @@ -110,7 +107,7 @@ public Optional get(int x_position, int y_position){ * @param folderName name of the folder * @param fileName name of the file */ - public void save(String folderName, String fileName){ + public void save(String folderName, String fileName) { try { File directory = new File(BASE_FOLDER_NAME + File.separator + folderName); directory.mkdir(); @@ -118,16 +115,15 @@ public void save(String folderName, String fileName){ + File.separator + fileName + FILE_EXTENSION); writer.write(width + "\n"); writer.write(height + "\n"); - for (int j = 0; j < height; j ++){ - for (int i = 0; i < width; i++){ + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { writer.write(SEPERATOR_SYMBOL); writer.write(entries[j][i]); } writer.write("\n"); } writer.close(); - } - catch(IOException e){ + } catch (IOException e) { System.out.println("Oops, the file writer took in a directory for some reason!"); } } diff --git a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java index d46116b9b7..7f9cd5acaf 100644 --- a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java +++ b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java @@ -5,13 +5,13 @@ public class SaveLoadFileTest { public static void main(String[] args) throws FileNotFoundException { Saver saver = new Saver(10, 6); - Loader loader; saver.add("banana", 5, 2); saver.add("pineapple", 7, 1); saver.add("cheetan", 2, 3); saver.add("beetles", 1, 4); saver.save("save_load_test","test1"); + Loader loader; loader = Loader.load("save_load_test","test1"); loader.get(5, 2).ifPresent(System.out::println); loader.get(7, 1).ifPresent(System.out::println); diff --git a/src/test/java/seedu/duke/saveload/SaverTest.java b/src/test/java/seedu/duke/saveload/SaverTest.java index 7df613fa0b..9fbe56e768 100644 --- a/src/test/java/seedu/duke/saveload/SaverTest.java +++ b/src/test/java/seedu/duke/saveload/SaverTest.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Optional; @@ -12,14 +12,14 @@ public class SaverTest { private Saver saver; @BeforeEach - public void setUp() throws Exception{ + public void setUp() { saver = new Saver(10, 6); saver.add("banana", 5, 2); saver.add("pineapple", 7, 1); } @Test - public void get(){ + public void get() { assertEquals(Optional.of("banana"), saver.get(5,2)); assertEquals(Optional.of("pineapple"), saver.get(7,1)); assertEquals(Optional.empty(), saver.get(1,1)); @@ -27,7 +27,7 @@ public void get(){ } @Test - public void delete(){ + public void delete() { saver.add("Deletion lotion", 4,2); assertEquals(Optional.of("Deletion lotion"), saver.get(4,2)); saver.delete(4,2); From 4bb71e462e1621fad1facca1b32aec443f31950f Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Wed, 21 Oct 2020 17:55:01 +0800 Subject: [PATCH 106/374] Shorten output divider --- src/main/java/seedu/duke/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 25f3a8d909..bd11c231bc 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -261,7 +261,7 @@ public void printErrorMessage(String errorMessage) { public void print(String message) { String divider = "__________________________________________________________________________________________" - + "______________________________________________________"; + + "____________________"; System.out.println(divider + LINE_SEPARATOR + message + LINE_SEPARATOR From 51e3eccf8b7a4c693b8327fa741efefba276e84b Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Wed, 21 Oct 2020 17:58:32 +0800 Subject: [PATCH 107/374] Change printDatabase method input paramater To reduce coupling between Food and Ui class. --- src/main/java/seedu/duke/Ui.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index bd11c231bc..6f41445d95 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -123,18 +123,12 @@ public void printFoodList(String allFood) { /** * Prints all the food in the database sorted by the canteen and then the store it is found. * - * @param foodDatabase The list containing all the food items stored in the database. + * @param foodDatabase The string representation of all the food items stored in the database. */ - public void printDatabase(List foodDatabase) { + public void printDatabase(String foodDatabase) { assert foodDatabase != null : "Food database should not be null"; - assert foodDatabase.size() > 0 : "Food database should not be empty"; - String allFood = ""; - int foodItemNumber = 1; - for (Food food: foodDatabase) { - allFood += LINE_SEPARATOR + " " + foodItemNumber + "." + food; - foodItemNumber++; - } - print("Here are the food items in the database:" + allFood); + assert foodDatabase.trim().length() > 0 : "Food database should not be empty"; + print("Here are the food items in the database:" + LINE_SEPARATOR + foodDatabase); } /** From 838f46af1878839ca03e590b5347558d47dcad45 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 22 Oct 2020 01:04:53 +0800 Subject: [PATCH 108/374] Remove unused import --- src/main/java/seedu/duke/Ui.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 6f41445d95..ea54cfb782 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,6 +1,5 @@ package seedu.duke; -import seedu.duke.food.Food; import seedu.duke.person.ActivityLevel; import java.util.List; From 398ceb005b6be089756e09346a9100c5778f09a6 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 22 Oct 2020 01:07:25 +0800 Subject: [PATCH 109/374] Get gender description from Gender object --- src/main/java/seedu/duke/Ui.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index ea54cfb782..a4cc1724a5 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,6 +1,7 @@ package seedu.duke; import seedu.duke.person.ActivityLevel; +import seedu.duke.person.Gender; import java.util.List; @@ -50,7 +51,8 @@ public void printAskForUserInfoMessage(String name) { + LINE_SEPARATOR + "accurate calculations for you :). Therefore, could you please share with me the " + "following:" + LINE_SEPARATOR - + "- Your gender either F for female or M for male." + LINE_SEPARATOR + + "- Your gender either F for " + Gender.FEMALE.getDescription() + " or M for " + + Gender.MALE.getDescription() + "." + LINE_SEPARATOR + "- Your age which is a positive integer." + LINE_SEPARATOR + "- Your height in cm." + LINE_SEPARATOR + "- Your original weight in kg." + LINE_SEPARATOR From 50bd94d35c95667865df743e9fbce4a22f49899d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 22 Oct 2020 01:09:19 +0800 Subject: [PATCH 110/374] Add method to convert LocalDateTime to String --- src/main/java/seedu/duke/Ui.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index a4cc1724a5..be89fc0942 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -3,7 +3,8 @@ import seedu.duke.person.ActivityLevel; import seedu.duke.person.Gender; -import java.util.List; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; /** * Represents a text user interface. @@ -121,6 +122,18 @@ public void printFoodList(String allFood) { } } + + /** + * Returns a string representation of the date time in the format dd MMM yyyy HHmm. + * + * @param dateTime The date time that needs to be converted into a String. + * @return Returns a string representation of the date and time in the format dd MMM yyyy HHmm. + */ + public String stringDateTime(LocalDateTime dateTime) { + assert dateTime != null : "Date time to be converted into string should not be null"; + return dateTime.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); + } + /** * Prints all the food in the database sorted by the canteen and then the store it is found. * From 6d5a8f034e6aaf32e3183c10bc4440609336c789 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 22 Oct 2020 01:10:54 +0800 Subject: [PATCH 111/374] Add Junit tests for stringDateTime method in Ui --- src/test/java/seedu/duke/UiTest.java | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/test/java/seedu/duke/UiTest.java diff --git a/src/test/java/seedu/duke/UiTest.java b/src/test/java/seedu/duke/UiTest.java new file mode 100644 index 0000000000..f0eeb79c54 --- /dev/null +++ b/src/test/java/seedu/duke/UiTest.java @@ -0,0 +1,36 @@ +package seedu.duke; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.duke.person.Person; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +class UiTest { + + private Ui ui; + + @BeforeEach + public void setUp() { + ui = new Ui(); + } + + @Test + void stringDateTime_nullInput_expectAssertionError() { + assertThrows(AssertionError.class, () -> ui.stringDateTime(null)); + } + + @Test + void stringDateTime_LocalDateTime_returnsStringOfLocalDateTime() { + LocalDateTime dateTime = LocalDateTime.parse("2020-10-21T23:59"); + assertEquals("21 Oct 2020 2359",ui.stringDateTime(dateTime)); + } + + @Test + void stringDateTime_LocalDateTimeWithSeconds_returnsStringOfLocalDateTimeWithoutSeconds() { + LocalDateTime dateTime = LocalDateTime.parse("2020-10-21T23:59:22"); + assertEquals("21 Oct 2020 2359",ui.stringDateTime(dateTime)); + } +} From 50f4f3eb78db22c0263b9adf4841e458b97ec963 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 22 Oct 2020 01:23:49 +0800 Subject: [PATCH 112/374] Add printFoodListGivenTimePeriod method The method prints food items in the food list that were recorded during a certain time period. --- src/main/java/seedu/duke/Ui.java | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index be89fc0942..2de5a54aa9 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -122,6 +122,31 @@ public void printFoodList(String allFood) { } } + /** + * Prints food items recorded into the food list during a given time period in the order that they were + * added or a message stating no food items were recorded during the given time period. + * + * @param foods The string representation of food items in the food list recorded during the time + * period given. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printFoodListGivenTimePeriod(String foods, LocalDateTime start, LocalDateTime end) { + assert foods != null : "String representation of food items in the food list recorded during the " + + "time period given should not be null"; + assert start != null : "Starting date time of the time period should not be null"; + assert end != null : "Ending date time of the time period should not be null"; + assert !start.isAfter(end) : "Starting date time should not be later than ending date time " + + "of the time period"; + String stringStart = stringDateTime(start); + String stringEnd = stringDateTime(end); + if (foods.trim().length() < 1) { + print("No food item was recorded in DietBook between " + stringStart + " and " + stringEnd + "."); + } else { + print("Here are the food items recorded in DietBook between " + stringStart + " and " + stringEnd + + " :" + LINE_SEPARATOR + foods); + } + } /** * Returns a string representation of the date time in the format dd MMM yyyy HHmm. From c471d25243b585c16840bb86dabd7fb5a76bfce9 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 22 Oct 2020 02:18:16 +0800 Subject: [PATCH 113/374] Major Issue Fixes - add -> full information - swappable, optional add command - swappable info command - catch index error for delete --- src/main/java/seedu/duke/Parser.java | 145 +++++++++++++++++++-------- 1 file changed, 103 insertions(+), 42 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 033fafb972..5517f0e10f 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -20,7 +20,7 @@ public class Parser { public static final String COMMAND_USERINFO = "userinfo"; public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; - public static final String[] PARAM_ADD = {"n/","x/","k/","f/","p/","c/"}; + public static final String[] PARAM_ADD = {"n/","x/","k/"}; /** @@ -44,7 +44,7 @@ private static String getCommandParam(String userInput) throws DietException { if (userInput.split(command).length < 2 || userInput.split(command)[1].equals(" ")) { - throw new DietException("☹ Error! Missing command parameters!"); + throw new DietException("Error! Missing command parameters!"); } else { switch (command) { case COMMAND_NAME: @@ -55,18 +55,18 @@ private static String getCommandParam(String userInput) throws DietException { return userInput.split(" ")[1]; } } - throw new DietException("☹ Incorrect nutrient type"); + throw new DietException("Incorrect nutrient type"); case COMMAND_ADD: for (String param: PARAM_ADD) { if (!userInput.contains(param)) { - throw new DietException("☹ Missing or incorrect add statement"); + throw new DietException("Missing or incorrect add statement"); } } return userInput.substring(userInput.indexOf(' ') + 1); case COMMAND_INFO: for (String param: PARAM_INFO) { if (!userInput.contains(param)) { - throw new DietException("☹ Missing or incorrect info statement"); + throw new DietException("Missing or incorrect info statement"); } } return userInput.substring(userInput.indexOf(' ') + 1); @@ -84,15 +84,45 @@ private static String getCommandParam(String userInput) throws DietException { * @throws DietException when the user input is of a wrong format. */ private static String getProcessedAdd(String userInput, FoodList foodList) throws DietException { - String[] processedParam = getCommandParam(userInput).split(" "); - int portionSize = Integer.parseInt(processedParam[0].substring(processedParam[0].indexOf("/") + 1)); - String foodName = processedParam[1].substring(processedParam[1].indexOf("/") + 1); - int calorie = Integer.parseInt(processedParam[2].substring(processedParam[2].indexOf("/") + 1)); - int carb = Integer.parseInt(processedParam[3].substring(processedParam[3].indexOf("/") + 1)); - int protein = Integer.parseInt(processedParam[4].substring(processedParam[4].indexOf("/") + 1)); - int fat = Integer.parseInt(processedParam[5].substring(processedParam[5].indexOf("/") + 1)); - foodList.addFood(portionSize, foodName, calorie, carb, protein, fat); - return foodName; + int portionSize = 1; + String foodName = "Food Name"; + int calorie = 0; + int carb = 0; + int protein = 0; + int fat = 0; + String trimmedParam; + String[] processedParam; + String[] paramList = {"x/", "n/", "k/", "c/", "p/", "f/"}; + for (String param: paramList) { + if (getCommandParam(userInput).contains(param)) { + processedParam = getCommandParam(userInput).split(param); + trimmedParam = processedParam[1].trim(); + if (processedParam[1].contains("/")) { + trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); + } + switch (param) { + case "x/": + portionSize = Integer.parseInt(trimmedParam); + break; + case "n/": + foodName = trimmedParam; + break; + case "k/": + calorie = Integer.parseInt(trimmedParam); + break; + case "c/": + carb = Integer.parseInt(trimmedParam); + break; + case "p/": + protein = Integer.parseInt(trimmedParam); + break; + default: + fat = Integer.parseInt(trimmedParam); + break; + } + } + } + return foodList.addFood(portionSize, foodName, calorie, carb, protein, fat); } /** @@ -102,30 +132,57 @@ private static String getProcessedAdd(String userInput, FoodList foodList) throw * @throws DietException when the user input is of a wrong format. */ private static void executeProcessedInfo(String userInput, Manager manager) throws DietException { - Gender gender; - ActivityLevel actLvl; - String[] processedParam = getCommandParam(userInput).split(" "); - String processGender = processedParam[0].substring(processedParam[0].indexOf("/") + 1); - if (processGender.equals("M")) { - gender = Gender.MALE; - } else { - gender = Gender.FEMALE; - } - int age = Integer.parseInt(processedParam[1].substring(processedParam[1].indexOf("/") + 1)); - int height = Integer.parseInt(processedParam[2].substring(processedParam[2].indexOf("/") + 1)); - int orgWeight = Integer.parseInt(processedParam[3].substring(processedParam[3].indexOf("/") + 1)); - int tarWeight = Integer.parseInt(processedParam[4].substring(processedParam[4].indexOf("/") + 1)); - String processActLvl = processedParam[5].substring(processedParam[5].indexOf("/") + 1); - if (processActLvl.equals("1")) { - actLvl = ActivityLevel.NONE; - } else if (processActLvl.equals("2")) { - actLvl = ActivityLevel.LOW; - } else if (processActLvl.equals("3")) { - actLvl = ActivityLevel.MEDIUM; - } else if (processActLvl.equals("4")) { - actLvl = ActivityLevel.HIGH; - } else { - actLvl = ActivityLevel.EXTREME; + Gender gender = Gender.MALE; + ActivityLevel actLvl = ActivityLevel.NONE; + int age = 0; + int height = 0; + int orgWeight = 0; + int tarWeight = 0; + String trimmedParam; + String[] processedParam; + String[] paramList = {"g/", "a/", "h/", "o/", "t/", "l/"}; + for (String param: paramList) { + processedParam = getCommandParam(userInput).split(param); + trimmedParam = processedParam[1].trim(); + if (processedParam[1].contains("/")) { + trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); + } + switch (param) { + case "g/": + String processGender = trimmedParam; + if (processGender.equals("M")) { + gender = Gender.MALE; + } else { + gender = Gender.FEMALE; + } + break; + case "a/": + age = Integer.parseInt(trimmedParam); + break; + case "h/": + height = Integer.parseInt(trimmedParam); + break; + case "o/": + orgWeight = Integer.parseInt(trimmedParam); + break; + case "t/": + tarWeight = Integer.parseInt(trimmedParam); + break; + default: + String processActLvl = trimmedParam; + if (processActLvl.equals("1")) { + actLvl = ActivityLevel.NONE; + } else if (processActLvl.equals("2")) { + actLvl = ActivityLevel.LOW; + } else if (processActLvl.equals("3")) { + actLvl = ActivityLevel.MEDIUM; + } else if (processActLvl.equals("4")) { + actLvl = ActivityLevel.HIGH; + } else { + actLvl = ActivityLevel.EXTREME; + } + break; + } } manager.setPerson(manager.getName(), gender, age, height, orgWeight, tarWeight, actLvl); } @@ -140,12 +197,12 @@ private static int getCommandIndex(String userInput) throws DietException { String command = getCommand(userInput); if (userInput.split(command).length < 2 || userInput.split(command)[1].equals(" ")) { - throw new DietException("☹ OOPS!!! Missing index of duke.task!"); + throw new DietException("OOPS!!! Missing index of duke.task!"); } try { return Integer.parseInt(userInput.split(" ")[1]); } catch (NumberFormatException e) { - throw new DietException("☹ OOPS!!! No integer index detected!"); + throw new DietException("OOPS!!! No integer index detected!"); } } @@ -176,7 +233,11 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx ui.printDatabase(manager.getDataBase().getFoodList()); return; case COMMAND_DELETE: - ui.printDeletedFood(manager.getFoodList().delete(getCommandIndex(userInput))); + try { + ui.printDeletedFood(manager.getFoodList().delete(getCommandIndex(userInput))); + } catch (IndexOutOfBoundsException e) { + throw new DietException("No such index!"); + } return; case COMMAND_CLEAR: ui.printClearFoodListMessage(); @@ -205,7 +266,7 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx ui.printNewFood(getProcessedAdd(userInput, manager.getFoodList())); return; default: - throw new DietException("☹ There's no such command!"); + throw new DietException("There's no such command!"); } } } From 6d0987880f61b1ff9abc42436c193f39c702199d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 22 Oct 2020 10:26:06 +0800 Subject: [PATCH 114/374] Update package and remove wilcard import --- src/test/java/seedu/dietbook/UiTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/seedu/dietbook/UiTest.java b/src/test/java/seedu/dietbook/UiTest.java index f0eeb79c54..f25dccd228 100644 --- a/src/test/java/seedu/dietbook/UiTest.java +++ b/src/test/java/seedu/dietbook/UiTest.java @@ -1,12 +1,13 @@ -package seedu.duke; +package seedu.dietbook; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.duke.person.Person; +import seedu.dietbook.person.Person; import java.time.LocalDateTime; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class UiTest { From 4174aecb5ddadf191f0723c65af3353d71c1714c Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 22 Oct 2020 10:26:43 +0800 Subject: [PATCH 115/374] Remove unused import in JUnit test --- src/test/java/seedu/dietbook/UiTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/seedu/dietbook/UiTest.java b/src/test/java/seedu/dietbook/UiTest.java index f25dccd228..91076665f1 100644 --- a/src/test/java/seedu/dietbook/UiTest.java +++ b/src/test/java/seedu/dietbook/UiTest.java @@ -2,7 +2,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.dietbook.person.Person; import java.time.LocalDateTime; From 9720f7790f263c9eb9e9f6fc9aa080838c4ab335 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Fri, 23 Oct 2020 00:56:40 +0800 Subject: [PATCH 116/374] Add methods to trim Strings and get length --- src/main/java/seedu/dietbook/Ui.java | 64 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 9ad3cdf8c2..eec8ede96c 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -46,8 +46,8 @@ public void printWelcomeMessage() { */ public void printAskForUserInfoMessage(String name) { assert name != null : "Name should not be null"; - assert name.trim().length() > 0 : "Name should not be an empty string"; - print("Hi " + name + "!" + LINE_SEPARATOR + assert trimStringGetLength(name) > 0 : "Name should not be an empty string"; + print("Hi " + trimString(name) + "!" + LINE_SEPARATOR + "Before we get started, I would like to know about about you so that I can make more " + LINE_SEPARATOR + "accurate calculations for you :). Therefore, could you please share with me the " @@ -101,10 +101,10 @@ public void printTutorialMessage() { */ public void printNewFood(String newFood) { assert newFood != null : "String representation of the food that was added should not be null"; - assert newFood.trim().length() > 0 : "String representation of the food that was added should not " + assert trimStringGetLength(newFood) > 0 : "String representation of the food that was added should not " + "be an empty string"; print("Got it! I've added this food item:" + LINE_SEPARATOR - + " " + newFood); + + " " + trimString(newFood)); } /** @@ -115,10 +115,10 @@ public void printNewFood(String newFood) { */ public void printFoodList(String allFood) { assert allFood != null : "String representation of all food in food list should not be null"; - if (allFood.trim().length() < 1) { + if (trimStringGetLength(allFood) < 1) { print("DietBook is currently empty."); } else { - print("Here are the food items in DietBook:" + LINE_SEPARATOR + allFood); + print("Here are the food items in DietBook:" + LINE_SEPARATOR + trimString(allFood)); } } @@ -140,11 +140,11 @@ public void printFoodListGivenTimePeriod(String foods, LocalDateTime start, Loca + "of the time period"; String stringStart = stringDateTime(start); String stringEnd = stringDateTime(end); - if (foods.trim().length() < 1) { + if (trimStringGetLength(foods) < 1) { print("No food item was recorded in DietBook between " + stringStart + " and " + stringEnd + "."); } else { print("Here are the food items recorded in DietBook between " + stringStart + " and " + stringEnd - + " :" + LINE_SEPARATOR + foods); + + " :" + LINE_SEPARATOR + trimString(foods)); } } @@ -166,8 +166,8 @@ public String stringDateTime(LocalDateTime dateTime) { */ public void printDatabase(String foodDatabase) { assert foodDatabase != null : "Food database should not be null"; - assert foodDatabase.trim().length() > 0 : "Food database should not be empty"; - print("Here are the food items in the database:" + LINE_SEPARATOR + foodDatabase); + assert trimStringGetLength(foodDatabase) > 0 : "Food database should not be empty"; + print("Here are the food items in the database:" + LINE_SEPARATOR + trimString(foodDatabase)); } /** @@ -177,9 +177,9 @@ public void printDatabase(String foodDatabase) { */ public void printPersonInfo(String personInfo) { assert personInfo != null : "Person information should not be null"; - assert personInfo.trim().length() > 0 : "Person information should not be an empty string"; + assert trimStringGetLength(personInfo) > 0 : "Person information should not be an empty string"; print("Here is your information:" + LINE_SEPARATOR - + personInfo); + + trimString(personInfo)); } /** @@ -250,10 +250,10 @@ public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, in */ public void printDeletedFood(String deletedFood) { assert deletedFood != null : "String representation of the food that was deleted should not be null"; - assert deletedFood.trim().length() > 0 : "String representation of the food that was deleted should" + assert trimStringGetLength(deletedFood) > 0 : "String representation of the food that was deleted should" + " not be an empty string"; print("Noted. I've removed this food item:" + LINE_SEPARATOR - + " " + deletedFood); + + " " + trimString(deletedFood)); } /** @@ -271,8 +271,8 @@ public void printClearFoodListMessage() { */ public void printExitMessage(String name) { assert name != null : "Name should not be null"; - assert name.trim().length() > 0 : "Name should not be an empty string"; - print("Bye " + name + "! Hope to see you again soon!"); + assert trimStringGetLength(name) > 0 : "Name should not be an empty string"; + print("Bye " + trimString(name) + "! Hope to see you again soon!"); } /** @@ -282,8 +282,32 @@ public void printExitMessage(String name) { */ public void printErrorMessage(String errorMessage) { assert errorMessage != null : "Error message should not be null"; - assert errorMessage.trim().length() > 0 : "Error message should not be an empty string"; - print(":( Oh no..." + errorMessage); + assert trimStringGetLength(errorMessage) > 0 : "Error message should not be an empty string"; + print(":( Oh no..." + trimString(errorMessage)); + } + + /** + * Returns an integer representing the length of the string after it has been trimmed for leading and + * trailing spaces. + * + * @param string The string to be trimmed and have its length determined. + * @return An integer representing the length of the string after it has been trimmed for leading and + * trailing spaces. + */ + public int trimStringGetLength(String string) { + assert string != null : "String to trim and have length determined should not be null"; + return trimString(string).length(); + } + + /** + * Returns a string that has been trimmed for leading and trailing spaces. + * + * @param string The string to be trimmed for leading and trailing spaces. + * @return A string that has been trimmed for leading and trailing spaces. + */ + public String trimString(String string) { + assert string != null : "String to trim should not be null"; + return string.trim(); } /** @@ -292,12 +316,14 @@ public void printErrorMessage(String errorMessage) { * @param message The message to show the user. */ public void print(String message) { + assert message != null : "Message to print should not be null"; + assert trimStringGetLength(message) > 0 : "Message to print should not be an empty string"; String divider = "__________________________________________________________________________________________" + "____________________"; System.out.println(divider + LINE_SEPARATOR - + message + LINE_SEPARATOR + + trimString(message) + LINE_SEPARATOR + divider); } From 23c9a75719758aa799fb1de59f3e2b66234215d7 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Fri, 23 Oct 2020 00:57:52 +0800 Subject: [PATCH 117/374] Add JUnit tests for two Ui methods --- src/test/java/seedu/dietbook/UiTest.java | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/test/java/seedu/dietbook/UiTest.java b/src/test/java/seedu/dietbook/UiTest.java index 91076665f1..35314fb5f5 100644 --- a/src/test/java/seedu/dietbook/UiTest.java +++ b/src/test/java/seedu/dietbook/UiTest.java @@ -33,4 +33,54 @@ void stringDateTime_LocalDateTimeWithSeconds_returnsStringOfLocalDateTimeWithout LocalDateTime dateTime = LocalDateTime.parse("2020-10-21T23:59:22"); assertEquals("21 Oct 2020 2359",ui.stringDateTime(dateTime)); } + + @Test + void trimStringGetLength_nullInput_expectAssertionError() { + assertThrows(AssertionError.class, () -> ui.trimStringGetLength(null)); + } + + @Test + void trimStringGetLength_stringWithNoLeadingOrTrailingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength("food")); + } + + @Test + void trimStringGetLength_StringWithLeadingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength(" food")); + } + + @Test + void trimStringGetLength_StringWithTrailingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength("food ")); + } + + @Test + void trimStringGetLength_StringWithLeadingAndTrailingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength(" food ")); + } + + @Test + void trimString_nullInput_expectAssertionError() { + assertThrows(AssertionError.class, () -> ui.trimString(null)); + } + + @Test + void trimString_StringWithLeadingSpaces_returnsStringWithoutLeadingSpaces() { + assertEquals("food", ui.trimString(" food")); + } + + @Test + void trimString_StringWithTrailingSpaces_returnsStringWithoutTrailingSpaces() { + assertEquals("food", ui.trimString("food ")); + } + + @Test + void trimString_StringWithLeadingAndTrailingSpaces_returnsStringWithoutLeadingAndTrailingSpaces() { + assertEquals("food", ui.trimString(" food ")); + } + + @Test + void trimString_StringWithNoLeadingAndTrailingSpaces_returnsString() { + assertEquals("food", ui.trimString("food")); + } } From 79bd2b93ba3a03134cdcf606878afab100f45169 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Fri, 23 Oct 2020 13:16:33 +0800 Subject: [PATCH 118/374] Improved input parser --- src/main/java/seedu/duke/Parser.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index f56932e4d1..00e9effc37 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -48,11 +48,11 @@ private static String getCommandParam(String userInput) throws DietException { } else { switch (command) { case COMMAND_NAME: - return userInput.split(" ")[1]; + return userInput.split("name")[1].trim(); case COMMAND_CALCULATE: for (String param: PARAM_CALCULATE) { if (userInput.contains(param)) { - return userInput.split(" ")[1]; + return userInput.split("calculate")[1].trim(); } } throw new DietException("Incorrect nutrient type"); @@ -249,11 +249,11 @@ public static void parse(String userInput, Manager manager, Ui ui) throws DietEx if (getCommandParam(userInput).equals("all")) { ui.printAllNutrientIntake(calculator.calculateCalorie(), calculator.calculateCarb(), calculator.calculateProtein(), calculator.calculateFat()); - } else if (getCommandParam(userInput).contains("calorie")) { + } else if (getCommandParam(userInput).equals("calorie")) { ui.printCalorieIntake(calculator.calculateCalorie()); - } else if (getCommandParam(userInput).contains("carbohydrate")) { + } else if (getCommandParam(userInput).equals("carbohydrate")) { ui.printCarbohydrateIntake(calculator.calculateCarb()); - } else if (getCommandParam(userInput).contains("protein")) { + } else if (getCommandParam(userInput).equals("protein")) { ui.printProteinIntake(calculator.calculateProtein()); } else { ui.printFatIntake(calculator.calculateFat()); From b6b556a56df654f2fc116f2294fb0d8573e89271 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 23 Oct 2020 15:31:07 +0800 Subject: [PATCH 119/374] Implement FoodSaveLoadManager This acts as a inbetween between save and load classes and Food List --- .../java/seedu/duke/saveload/EmptyLoader.java | 26 +++++++ .../java/seedu/duke/saveload/FileLoader.java | 68 ++++++++++++++++ .../duke/saveload/FoodSaveLoadManager.java | 78 +++++++++++++++++++ src/main/java/seedu/duke/saveload/Loader.java | 67 ++-------------- src/main/java/seedu/duke/saveload/Saver.java | 25 +++++- .../saveload/FoodSaveLoadManagerTest.java | 62 +++++++++++++++ .../seedu/duke/saveload/SaveLoadFileTest.java | 18 ++--- 7 files changed, 271 insertions(+), 73 deletions(-) create mode 100644 src/main/java/seedu/duke/saveload/EmptyLoader.java create mode 100644 src/main/java/seedu/duke/saveload/FileLoader.java create mode 100644 src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java create mode 100644 src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java diff --git a/src/main/java/seedu/duke/saveload/EmptyLoader.java b/src/main/java/seedu/duke/saveload/EmptyLoader.java new file mode 100644 index 0000000000..97cc788644 --- /dev/null +++ b/src/main/java/seedu/duke/saveload/EmptyLoader.java @@ -0,0 +1,26 @@ +package seedu.duke.saveload; + +import java.util.Optional; + +/** + * Place holder class for Loader, does not do anything + * Throws IllegalAccessException if there is any attempt to access this class. + */ +public class EmptyLoader extends Loader{ + protected EmptyLoader(){ } + + @Override + public Optional get(int xposition, int yposition) throws IllegalAccessException { + throw new IllegalAccessException("Do not attempt to get from an empty loader!"); + } + + @Override + int getHeight() throws IllegalAccessException { + throw new IllegalAccessException("Do not attempt to get from an empty loader!"); + } + + @Override + int getWidth() throws IllegalAccessException { + throw new IllegalAccessException("Do not attempt to get from an empty loader!"); + } +} diff --git a/src/main/java/seedu/duke/saveload/FileLoader.java b/src/main/java/seedu/duke/saveload/FileLoader.java new file mode 100644 index 0000000000..88d92b6235 --- /dev/null +++ b/src/main/java/seedu/duke/saveload/FileLoader.java @@ -0,0 +1,68 @@ +package seedu.duke.saveload; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Optional; +import java.util.Scanner; + +/** + * Handles reading of stored text file. + * Note: the first five fields must be same + */ +public class FileLoader extends Loader{ + private static final String ROOT_DIRECTORY = System.getProperty("user.home"); + private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; + private static final String EMPTY_SYMBOL = "%NULL&!!LL"; + private static final String SEPARATOR_SYMBOL = "&%SEPERATOR%AAA%"; + private static final String FILE_EXTENSION = ".txt"; + + private final String[][] entries; + private final int width; + private final int height; + + + protected FileLoader(String folderName, String fileName) throws FileNotFoundException { + File file = new File(BASE_FOLDER_NAME + File.separator + folderName + + File.separator + fileName + FILE_EXTENSION); + Scanner reader = new Scanner(file); + width = Integer.parseInt(reader.nextLine()); + height = Integer.parseInt(reader.nextLine()); + entries = new String[height][width]; + String[] line; + for (int j = 0; j < height; j++) { + line = reader.nextLine().split(SEPARATOR_SYMBOL); + if (width >= 0) { + System.arraycopy(line, 1, entries[j], 0, width); + } + } + } + + /** + * Get the String entry stored at the specified position in the table if it is present. + * @param xposition the x position in the table from 1 to the table width + * @param yposition the y position in the table from 1 to the table height + * @return Optional of the String. The Optional is empty is no entry is stored + * @throws IndexOutOfBoundsException if the x or y given is not as above + */ + public Optional get(int xposition, int yposition) throws IndexOutOfBoundsException { + try { + if (this.entries[yposition - 1][xposition - 1].equals(EMPTY_SYMBOL)) { + return Optional.empty(); + } else { + return Optional.of(this.entries[yposition - 1][xposition - 1]); + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" + + "saver table!"); + } + } + + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } +} diff --git a/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java b/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java new file mode 100644 index 0000000000..2154f53b55 --- /dev/null +++ b/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java @@ -0,0 +1,78 @@ +package seedu.duke.saveload; + +import seedu.duke.food.Food; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; + +/** + * Server as a in-between class between loader saver and food list + */ +public class FoodSaveLoadManager { + private static final int DEFAULT_SAVER_WIDTH = 5; + private static final int DEFAULT_SAVER_HEIGHT = 10; + + private static final String FOOD_FOLDER_NAME = "Food%%UOISDN%%FOLDER"; + + private static final String DEFAULT_NAME = "MISSING NAME"; + private static final String DEFAULT_NUTRITION_VALUE = "0"; + + private Saver saver; + private Loader fileLoader; + + public FoodSaveLoadManager() { + this.saver = new Saver(DEFAULT_SAVER_WIDTH, DEFAULT_SAVER_HEIGHT); + this.fileLoader = Loader.loadEmpty(); + } + + /** + * Call this function to load a food file + * @param fileName name of file + * @throws FileNotFoundException if there is no such save file + */ + public void load(String fileName) throws FileNotFoundException { + this.fileLoader = Loader.load(FOOD_FOLDER_NAME, fileName); + } + + public void clearLoader(){ + this.fileLoader = Loader.loadEmpty(); + } + + /** + * Returns a list of food that is stored in the loader + * @return + * @throws IllegalAccessException When this method is called without first loading a food file + */ + public List getFoodList() throws IllegalAccessException { + ArrayList foodlist = new ArrayList<>(); + Food newFood; + for (int j = 1; j < fileLoader.getHeight() + 1; j++) { + newFood = new Food( + fileLoader.get(j,1).orElse(DEFAULT_NAME), + Integer.parseInt(fileLoader.get(j,2).orElse(DEFAULT_NUTRITION_VALUE)), + Integer.parseInt(fileLoader.get(j,3).orElse(DEFAULT_NUTRITION_VALUE)), + Integer.parseInt(fileLoader.get(j,4).orElse(DEFAULT_NUTRITION_VALUE)), + Integer.parseInt(fileLoader.get(j,5).orElse(DEFAULT_NUTRITION_VALUE))); + foodlist.add(newFood); + } + return foodlist; + } + + /** + * saves a input food list to a file + * @param fileName the name of the file to save to + * @param foodlist list of food objects to be saved + */ + public void save(String fileName, List foodlist){ + this.saver.resetSize(DEFAULT_SAVER_WIDTH, foodlist.size()); + for (int j = 1; j < foodlist.size() + 1; j++){ + saver.add(foodlist.get(j-1).getName(), 1, j); + saver.add(Integer.toString(foodlist.get(j-1).getCalorie()), 2, j); + saver.add(Integer.toString(foodlist.get(j-1).getCarbohydrate()), 3, j); + saver.add(Integer.toString(foodlist.get(j-1).getProtein()), 4, j); + saver.add(Integer.toString(foodlist.get(j-1).getFats()), 5, j); + } + saver.save(FOOD_FOLDER_NAME, fileName); + } +} diff --git a/src/main/java/seedu/duke/saveload/Loader.java b/src/main/java/seedu/duke/saveload/Loader.java index 72311fd1b1..15aee3b7e4 100644 --- a/src/main/java/seedu/duke/saveload/Loader.java +++ b/src/main/java/seedu/duke/saveload/Loader.java @@ -1,71 +1,18 @@ package seedu.duke.saveload; -import java.io.File; import java.io.FileNotFoundException; import java.util.Optional; -import java.util.Scanner; - -/** - * Handles reading of stored text file. - * Note: the first five fields must be same - */ -public class Loader { - private static final String ROOT_DIRECTORY = System.getProperty("user.home"); - private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; - private static final String EMPTY_SYMBOL = "%NULL&!!LL"; - private static final String SEPARATOR_SYMBOL = "&%SEPERATOR%AAA%"; - private static final String FILE_EXTENSION = ".txt"; - - private final String[][] entries; - private final int width; - private final int height; +abstract class Loader { public static Loader load(String folderName, String fileName) throws FileNotFoundException { - return new Loader(folderName, fileName); - } - - private Loader(String folderName, String fileName) throws FileNotFoundException { - File file = new File(BASE_FOLDER_NAME + File.separator + folderName - + File.separator + fileName + FILE_EXTENSION); - Scanner reader = new Scanner(file); - width = Integer.parseInt(reader.nextLine()); - height = Integer.parseInt(reader.nextLine()); - entries = new String[height][width]; - String[] line; - for (int j = 0; j < height; j++) { - line = reader.nextLine().split(SEPARATOR_SYMBOL); - if (width >= 0) { - System.arraycopy(line, 1, entries[j], 0, width); - } - } + return new FileLoader(folderName, fileName); } - /** - * Get the String entry stored at the specified position in the table if it is present. - * @param xposition the x position in the table from 1 to the table width - * @param yposition the y position in the table from 1 to the table height - * @return Optional of the String. The Optional is empty is no entry is stored - * @throws IndexOutOfBoundsException if the x or y given is not as above - */ - public Optional get(int xposition, int yposition) throws IndexOutOfBoundsException { - try { - if (this.entries[yposition - 1][xposition - 1].equals(EMPTY_SYMBOL)) { - return Optional.empty(); - } else { - return Optional.of(this.entries[yposition - 1][xposition - 1]); - } - } catch (ArrayIndexOutOfBoundsException e) { - throw new IndexOutOfBoundsException("The x or y position provided must be within the the dimensions of the" - + "saver table!"); - } + public static Loader loadEmpty(){ + return new EmptyLoader(); } - - public int getHeight() { - return height; - } - - public int getWidth() { - return width; - } + abstract Optional get(int xposition, int yposition) throws IllegalAccessException; + abstract int getHeight() throws IllegalAccessException; + abstract int getWidth() throws IllegalAccessException; } diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/duke/saveload/Saver.java index 720b0d1ae1..73ea31814d 100644 --- a/src/main/java/seedu/duke/saveload/Saver.java +++ b/src/main/java/seedu/duke/saveload/Saver.java @@ -22,15 +22,22 @@ public class Saver { rootDirectory.mkdir(); } - private final String[][] entries; - private final int height; - private final int width; + private String[][] entries; + private int height; + private int width; public Saver(int width, int height) { + setWidthAndHeight(width,height); + initEntries(); + } + + private void setWidthAndHeight(int width, int height){ this.height = height; this.width = width; - this.entries = new String[height][width]; + } + private void initEntries(){ + this.entries = new String[height][width]; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { entries[j][i] = EMPTY_SYMBOL; @@ -46,6 +53,16 @@ public int getHeight() { return height; } + /** + * Clears the entire table and set it to the new size + * @param newWidth the new width + * @param newHeight the new height + */ + public void resetSize(int newWidth, int newHeight){ + setWidthAndHeight(newWidth, newHeight); + initEntries(); + } + /** * Adds the string provided to the position x,y on the table. * @param entry the entry to be provided into this position diff --git a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java new file mode 100644 index 0000000000..bbae66f558 --- /dev/null +++ b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java @@ -0,0 +1,62 @@ +package seedu.duke.saveload; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.duke.food.Food; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; + +public class FoodSaveLoadManagerTest { + private FoodSaveLoadManager testManager; + private List inputFoodList; + private Food food1; + private Food food2; + private Food food3; + private Food food4; + + private List testFoodList; + + @BeforeEach + private void setUp() throws FileNotFoundException { + testManager = new FoodSaveLoadManager(); + inputFoodList = new ArrayList<>(); + food1 = new Food("Apple", 20000,20,5,1); + food2 = new Food("Peach", 3000,50,2,3); + food3 = new Food("Bacon", 1000,20,10,99); + food4 = new Food("Silicon", 500,100,50,10); + + inputFoodList.add(food1); + inputFoodList.add(food2); + inputFoodList.add(food3); + inputFoodList.add(food4); + + testManager.save("Victor's Food List", inputFoodList); + testManager.load("Victor's Food List"); + } + + @Test + private void getFoodList_WithoutLoading() throws Exception { + testManager.clearLoader(); + assertThrows(IllegalAccessException.class ,() -> testManager.getFoodList()); + } + + @Test + private void getFoodList_FileDoesNotExist() throws Exception{ + assertThrows(FileNotFoundException.class, () -> testManager.load("Over the Moon!")); + } + + @Test + private void getFoodListTest() throws Exception { + testFoodList = testManager.getFoodList(); + assertEquals(4, testFoodList.size()); + assertEquals("Apple", testFoodList.get(0).getName()); + assertEquals(20000, testFoodList.get(0).getCalorie()); + assertEquals("Silicon", testFoodList.get(3).getName()); + assertEquals(500, testFoodList.get(3).getCalorie()); + } +} diff --git a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java index 7f9cd5acaf..1b09acfb81 100644 --- a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java +++ b/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java @@ -3,7 +3,7 @@ import java.io.FileNotFoundException; public class SaveLoadFileTest { - public static void main(String[] args) throws FileNotFoundException { + public static void main(String[] args) throws FileNotFoundException, IllegalAccessException { Saver saver = new Saver(10, 6); saver.add("banana", 5, 2); saver.add("pineapple", 7, 1); @@ -11,13 +11,13 @@ public static void main(String[] args) throws FileNotFoundException { saver.add("beetles", 1, 4); saver.save("save_load_test","test1"); - Loader loader; - loader = Loader.load("save_load_test","test1"); - loader.get(5, 2).ifPresent(System.out::println); - loader.get(7, 1).ifPresent(System.out::println); - loader.get(2, 3).ifPresent(System.out::println); - loader.get(1, 4).ifPresent(System.out::println); - loader.get(1, 3).ifPresent(System.out::println); - loader.get(10, 6).ifPresent(System.out::println); + Loader fileLoader; + fileLoader = Loader.load("save_load_test","test1"); + fileLoader.get(5, 2).ifPresent(System.out::println); + fileLoader.get(7, 1).ifPresent(System.out::println); + fileLoader.get(2, 3).ifPresent(System.out::println); + fileLoader.get(1, 4).ifPresent(System.out::println); + fileLoader.get(1, 3).ifPresent(System.out::println); + fileLoader.get(10, 6).ifPresent(System.out::println); } } From a1da7e07a1e12ef860387e1a281c60ab7fdb344a Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 23 Oct 2020 15:44:36 +0800 Subject: [PATCH 120/374] debug FoodSaveLoadManager Add manual test Passed manual test --- .../duke/saveload/FoodSaveLoadManager.java | 10 ++--- .../FoodSaveLoadManagerManualTest.java | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java diff --git a/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java b/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java index 2154f53b55..cc83665acf 100644 --- a/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java +++ b/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java @@ -49,11 +49,11 @@ public List getFoodList() throws IllegalAccessException { Food newFood; for (int j = 1; j < fileLoader.getHeight() + 1; j++) { newFood = new Food( - fileLoader.get(j,1).orElse(DEFAULT_NAME), - Integer.parseInt(fileLoader.get(j,2).orElse(DEFAULT_NUTRITION_VALUE)), - Integer.parseInt(fileLoader.get(j,3).orElse(DEFAULT_NUTRITION_VALUE)), - Integer.parseInt(fileLoader.get(j,4).orElse(DEFAULT_NUTRITION_VALUE)), - Integer.parseInt(fileLoader.get(j,5).orElse(DEFAULT_NUTRITION_VALUE))); + fileLoader.get(1,j).orElse(DEFAULT_NAME), + Integer.parseInt(fileLoader.get(2,j).orElse(DEFAULT_NUTRITION_VALUE)), + Integer.parseInt(fileLoader.get(3,j).orElse(DEFAULT_NUTRITION_VALUE)), + Integer.parseInt(fileLoader.get(4,j).orElse(DEFAULT_NUTRITION_VALUE)), + Integer.parseInt(fileLoader.get(5,j).orElse(DEFAULT_NUTRITION_VALUE))); foodlist.add(newFood); } return foodlist; diff --git a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java new file mode 100644 index 0000000000..c90a08ad67 --- /dev/null +++ b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java @@ -0,0 +1,42 @@ +package seedu.duke.saveload; + +import seedu.duke.food.Food; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; + +public class FoodSaveLoadManagerManualTest { + static private FoodSaveLoadManager testManager; + static private List inputFoodList; + static private Food food1; + static private Food food2; + static private Food food3; + static private Food food4; + + static private List testFoodList; + + public static void main(String[] args) throws FileNotFoundException, IllegalAccessException { + testManager = new FoodSaveLoadManager(); + inputFoodList = new ArrayList<>(); + food1 = new Food("Apple", 20000,20,5,1); + food2 = new Food("Peach", 3000,50,2,3); + food3 = new Food("Bacon", 1000,20,10,99); + food4 = new Food("Silicon", 500,100,50,10); + + inputFoodList.add(food1); + inputFoodList.add(food2); + inputFoodList.add(food3); + inputFoodList.add(food4); + + testManager.save("Victor's Food List", inputFoodList); + testManager.load("Victor's Food List"); + + testFoodList = testManager.getFoodList(); + + System.out.println(testFoodList.get(0).getName()); + System.out.println(testFoodList.get(0).getCalorie()); + System.out.println(testFoodList.get(3).getName()); + System.out.println(testFoodList.get(3).getCalorie()); + } +} From 4ba066e2fe1edefe3790ecce22393bd2d4f71a06 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 23 Oct 2020 15:58:26 +0800 Subject: [PATCH 121/374] clear check style tests --- .../java/seedu/duke/saveload/EmptyLoader.java | 5 ++-- .../java/seedu/duke/saveload/FileLoader.java | 2 +- .../duke/saveload/FoodSaveLoadManager.java | 26 +++++++++---------- src/main/java/seedu/duke/saveload/Loader.java | 4 ++- src/main/java/seedu/duke/saveload/Saver.java | 8 +++--- .../FoodSaveLoadManagerManualTest.java | 23 +++++----------- .../saveload/FoodSaveLoadManagerTest.java | 4 +-- 7 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/main/java/seedu/duke/saveload/EmptyLoader.java b/src/main/java/seedu/duke/saveload/EmptyLoader.java index 97cc788644..96e1f82240 100644 --- a/src/main/java/seedu/duke/saveload/EmptyLoader.java +++ b/src/main/java/seedu/duke/saveload/EmptyLoader.java @@ -6,8 +6,9 @@ * Place holder class for Loader, does not do anything * Throws IllegalAccessException if there is any attempt to access this class. */ -public class EmptyLoader extends Loader{ - protected EmptyLoader(){ } +public class EmptyLoader extends Loader { + protected EmptyLoader(){ + } @Override public Optional get(int xposition, int yposition) throws IllegalAccessException { diff --git a/src/main/java/seedu/duke/saveload/FileLoader.java b/src/main/java/seedu/duke/saveload/FileLoader.java index 88d92b6235..70e20b7c72 100644 --- a/src/main/java/seedu/duke/saveload/FileLoader.java +++ b/src/main/java/seedu/duke/saveload/FileLoader.java @@ -9,7 +9,7 @@ * Handles reading of stored text file. * Note: the first five fields must be same */ -public class FileLoader extends Loader{ +public class FileLoader extends Loader { private static final String ROOT_DIRECTORY = System.getProperty("user.home"); private static final String BASE_FOLDER_NAME = ROOT_DIRECTORY + File.separator + "dietbook"; private static final String EMPTY_SYMBOL = "%NULL&!!LL"; diff --git a/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java b/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java index cc83665acf..6e070a7893 100644 --- a/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java +++ b/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java @@ -7,7 +7,7 @@ import java.util.List; /** - * Server as a in-between class between loader saver and food list + * Server as a in-between class between loader saver and food list. */ public class FoodSaveLoadManager { private static final int DEFAULT_SAVER_WIDTH = 5; @@ -27,7 +27,7 @@ public FoodSaveLoadManager() { } /** - * Call this function to load a food file + * Call this function to load a food file. * @param fileName name of file * @throws FileNotFoundException if there is no such save file */ @@ -35,13 +35,13 @@ public void load(String fileName) throws FileNotFoundException { this.fileLoader = Loader.load(FOOD_FOLDER_NAME, fileName); } - public void clearLoader(){ + public void clearLoader() { this.fileLoader = Loader.loadEmpty(); } /** - * Returns a list of food that is stored in the loader - * @return + * Returns a list of food that is stored in the loader. + * @return the food list obtained from the data in the loaded file * @throws IllegalAccessException When this method is called without first loading a food file */ public List getFoodList() throws IllegalAccessException { @@ -60,18 +60,18 @@ public List getFoodList() throws IllegalAccessException { } /** - * saves a input food list to a file + * saves a input food list to a file. * @param fileName the name of the file to save to * @param foodlist list of food objects to be saved */ - public void save(String fileName, List foodlist){ + public void save(String fileName, List foodlist) { this.saver.resetSize(DEFAULT_SAVER_WIDTH, foodlist.size()); - for (int j = 1; j < foodlist.size() + 1; j++){ - saver.add(foodlist.get(j-1).getName(), 1, j); - saver.add(Integer.toString(foodlist.get(j-1).getCalorie()), 2, j); - saver.add(Integer.toString(foodlist.get(j-1).getCarbohydrate()), 3, j); - saver.add(Integer.toString(foodlist.get(j-1).getProtein()), 4, j); - saver.add(Integer.toString(foodlist.get(j-1).getFats()), 5, j); + for (int j = 1; j < foodlist.size() + 1; j++) { + saver.add(foodlist.get(j - 1).getName(), 1, j); + saver.add(Integer.toString(foodlist.get(j - 1).getCalorie()), 2, j); + saver.add(Integer.toString(foodlist.get(j - 1).getCarbohydrate()), 3, j); + saver.add(Integer.toString(foodlist.get(j - 1).getProtein()), 4, j); + saver.add(Integer.toString(foodlist.get(j - 1).getFats()), 5, j); } saver.save(FOOD_FOLDER_NAME, fileName); } diff --git a/src/main/java/seedu/duke/saveload/Loader.java b/src/main/java/seedu/duke/saveload/Loader.java index 15aee3b7e4..076a5e7112 100644 --- a/src/main/java/seedu/duke/saveload/Loader.java +++ b/src/main/java/seedu/duke/saveload/Loader.java @@ -8,11 +8,13 @@ public static Loader load(String folderName, String fileName) throws FileNotFoun return new FileLoader(folderName, fileName); } - public static Loader loadEmpty(){ + public static Loader loadEmpty() { return new EmptyLoader(); } abstract Optional get(int xposition, int yposition) throws IllegalAccessException; + abstract int getHeight() throws IllegalAccessException; + abstract int getWidth() throws IllegalAccessException; } diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/duke/saveload/Saver.java index 73ea31814d..8c2f5075ef 100644 --- a/src/main/java/seedu/duke/saveload/Saver.java +++ b/src/main/java/seedu/duke/saveload/Saver.java @@ -31,12 +31,12 @@ public Saver(int width, int height) { initEntries(); } - private void setWidthAndHeight(int width, int height){ + private void setWidthAndHeight(int width, int height) { this.height = height; this.width = width; } - private void initEntries(){ + private void initEntries() { this.entries = new String[height][width]; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { @@ -54,11 +54,11 @@ public int getHeight() { } /** - * Clears the entire table and set it to the new size + * Clears the entire table and set it to the new size. * @param newWidth the new width * @param newHeight the new height */ - public void resetSize(int newWidth, int newHeight){ + public void resetSize(int newWidth, int newHeight) { setWidthAndHeight(newWidth, newHeight); initEntries(); } diff --git a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java index c90a08ad67..4f07f241f0 100644 --- a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java +++ b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java @@ -7,22 +7,13 @@ import java.util.List; public class FoodSaveLoadManagerManualTest { - static private FoodSaveLoadManager testManager; - static private List inputFoodList; - static private Food food1; - static private Food food2; - static private Food food3; - static private Food food4; - - static private List testFoodList; - public static void main(String[] args) throws FileNotFoundException, IllegalAccessException { - testManager = new FoodSaveLoadManager(); - inputFoodList = new ArrayList<>(); - food1 = new Food("Apple", 20000,20,5,1); - food2 = new Food("Peach", 3000,50,2,3); - food3 = new Food("Bacon", 1000,20,10,99); - food4 = new Food("Silicon", 500,100,50,10); + final FoodSaveLoadManager testManager = new FoodSaveLoadManager(); + List inputFoodList = new ArrayList<>(); + Food food1 = new Food("Apple", 20000, 20, 5, 1); + Food food2 = new Food("Peach", 3000, 50, 2, 3); + Food food3 = new Food("Bacon", 1000, 20, 10, 99); + Food food4 = new Food("Silicon", 500, 100, 50, 10); inputFoodList.add(food1); inputFoodList.add(food2); @@ -32,7 +23,7 @@ public static void main(String[] args) throws FileNotFoundException, IllegalAcce testManager.save("Victor's Food List", inputFoodList); testManager.load("Victor's Food List"); - testFoodList = testManager.getFoodList(); + List testFoodList = testManager.getFoodList(); System.out.println(testFoodList.get(0).getName()); System.out.println(testFoodList.get(0).getCalorie()); diff --git a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java index bbae66f558..e286b9bb45 100644 --- a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java +++ b/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java @@ -42,11 +42,11 @@ private void setUp() throws FileNotFoundException { @Test private void getFoodList_WithoutLoading() throws Exception { testManager.clearLoader(); - assertThrows(IllegalAccessException.class ,() -> testManager.getFoodList()); + assertThrows(IllegalAccessException.class, () -> testManager.getFoodList()); } @Test - private void getFoodList_FileDoesNotExist() throws Exception{ + private void getFoodList_FileDoesNotExist() throws Exception { assertThrows(FileNotFoundException.class, () -> testManager.load("Over the Moon!")); } From 7d084bb3830e9a91dafe33537a0e31a3a2673cb5 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Fri, 23 Oct 2020 16:19:14 +0800 Subject: [PATCH 122/374] copy food over to test --- src/test/java/seedu/duke/food/Food.java | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/test/java/seedu/duke/food/Food.java diff --git a/src/test/java/seedu/duke/food/Food.java b/src/test/java/seedu/duke/food/Food.java new file mode 100644 index 0000000000..c43d8f0384 --- /dev/null +++ b/src/test/java/seedu/duke/food/Food.java @@ -0,0 +1,44 @@ +package seedu.duke.food; + + +public class Food { + private final String name; + private final int calorie; + private final int carbohydrate; + private final int protein; + private final int fats; + + public Food(String name, int calorie, int carbohydrate, int protein, int fats) { + this.name = name; + this.calorie = calorie; + this.carbohydrate = carbohydrate; + this.protein = protein; + this.fats = fats; + } + + public int getFats() { + return fats; + } + + public String getName() { + return name; + } + + public int getCalorie() { + return calorie; + } + + public int getCarbohydrate() { + return carbohydrate; + } + + public int getProtein() { + return protein; + } + + @Override + public String toString() { + return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate + + " | fats : " + fats; + } +} From 6002a6dd1fb141ae528c90329d7861a362273963 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Fri, 23 Oct 2020 22:00:57 +0800 Subject: [PATCH 123/374] refactor location --- src/main/java/seedu/dietbook/Manager.java | 2 +- src/main/java/seedu/dietbook/Parser.java | 2 +- src/main/java/seedu/{ => dietbook}/calculator/Calculator.java | 2 +- .../java/seedu/{ => dietbook}/calculator/CalculatorTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/seedu/{ => dietbook}/calculator/Calculator.java (98%) rename src/test/java/seedu/{ => dietbook}/calculator/CalculatorTest.java (98%) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 439b788e06..a64094e32b 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -3,7 +3,7 @@ import seedu.dietbook.list.FoodList; import seedu.dietbook.person.ActivityLevel; import seedu.dietbook.person.Person; -import seedu.calculator.Calculator; +import seedu.dietbook.calculator.Calculator; import seedu.dietbook.database.DataBase; import seedu.dietbook.person.Gender; diff --git a/src/main/java/seedu/dietbook/Parser.java b/src/main/java/seedu/dietbook/Parser.java index b15f65b1a1..2486e212be 100644 --- a/src/main/java/seedu/dietbook/Parser.java +++ b/src/main/java/seedu/dietbook/Parser.java @@ -1,6 +1,6 @@ package seedu.dietbook; -import seedu.calculator.Calculator; +import seedu.dietbook.calculator.Calculator; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.Gender; import seedu.dietbook.person.ActivityLevel; diff --git a/src/main/java/seedu/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java similarity index 98% rename from src/main/java/seedu/calculator/Calculator.java rename to src/main/java/seedu/dietbook/calculator/Calculator.java index fe7c288242..bcd6208c32 100644 --- a/src/main/java/seedu/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -1,4 +1,4 @@ -package seedu.calculator; +package seedu.dietbook.calculator; import seedu.dietbook.food.Food; diff --git a/src/test/java/seedu/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java similarity index 98% rename from src/test/java/seedu/calculator/CalculatorTest.java rename to src/test/java/seedu/dietbook/calculator/CalculatorTest.java index 3c4502b25f..576c6e3995 100644 --- a/src/test/java/seedu/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -1,4 +1,4 @@ -package seedu.calculator; +package seedu.dietbook.calculator; import org.junit.jupiter.api.Test; import seedu.dietbook.food.Food; From d93f653c54725768771199cdd6ef186a632ef042 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Sat, 24 Oct 2020 04:32:49 +0800 Subject: [PATCH 124/374] Major OOP Update - created the entire command class -> 11 commands - shift parse -> manager (renamed to manage) - removed divider line from main - main execution --- src/main/java/seedu/dietbook/DietBook.java | 7 +- src/main/java/seedu/dietbook/Manager.java | 53 ++++++++++- .../seedu/dietbook/command/AddCommand.java | 18 ++++ .../dietbook/command/CalculateCommand.java | 41 +++++++++ .../seedu/dietbook/command/ClearCommand.java | 12 +++ .../java/seedu/dietbook/command/Command.java | 9 ++ .../seedu/dietbook/command/DataCommand.java | 13 +++ .../seedu/dietbook/command/DeleteCommand.java | 23 +++++ .../seedu/dietbook/command/ExitCommand.java | 13 +++ .../seedu/dietbook/command/HelpCommand.java | 11 +++ .../seedu/dietbook/command/InfoCommand.java | 20 +++++ .../seedu/dietbook/command/ListCommand.java | 11 +++ .../seedu/dietbook/command/NameCommand.java | 20 +++++ .../dietbook/command/UserinfoCommand.java | 11 +++ .../{ => exception}/DietException.java | 2 +- .../seedu/dietbook/{ => parser}/Parser.java | 89 ++----------------- 16 files changed, 266 insertions(+), 87 deletions(-) create mode 100644 src/main/java/seedu/dietbook/command/AddCommand.java create mode 100644 src/main/java/seedu/dietbook/command/CalculateCommand.java create mode 100644 src/main/java/seedu/dietbook/command/ClearCommand.java create mode 100644 src/main/java/seedu/dietbook/command/Command.java create mode 100644 src/main/java/seedu/dietbook/command/DataCommand.java create mode 100644 src/main/java/seedu/dietbook/command/DeleteCommand.java create mode 100644 src/main/java/seedu/dietbook/command/ExitCommand.java create mode 100644 src/main/java/seedu/dietbook/command/HelpCommand.java create mode 100644 src/main/java/seedu/dietbook/command/InfoCommand.java create mode 100644 src/main/java/seedu/dietbook/command/ListCommand.java create mode 100644 src/main/java/seedu/dietbook/command/NameCommand.java create mode 100644 src/main/java/seedu/dietbook/command/UserinfoCommand.java rename src/main/java/seedu/dietbook/{ => exception}/DietException.java (85%) rename src/main/java/seedu/dietbook/{ => parser}/Parser.java (66%) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 347ca85aa8..4da58f94c2 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -4,6 +4,8 @@ import java.io.IOException; import seedu.dietbook.database.DataBase; import seedu.dietbook.list.FoodList; +import seedu.dietbook.exception.DietException; +import seedu.dietbook.command.Command; public class DietBook { private FoodList foodList; @@ -22,11 +24,10 @@ public static void main(String[] args) throws FileNotFoundException { while (!isExit) { try { String userInput = dietBook.manager.readCommand(); - Parser.parse(userInput, dietBook.manager, dietBook.ui); + Command c = dietBook.manager.manage(userInput); + c.execute(dietBook.manager, dietBook.ui); } catch (DietException e) { dietBook.ui.printErrorMessage(e.getMessage()); - } finally { - System.out.println("__________________"); } } } diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 439b788e06..0e33b62f1d 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -1,16 +1,17 @@ package seedu.dietbook; +import seedu.dietbook.command.*; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.ActivityLevel; import seedu.dietbook.person.Person; import seedu.calculator.Calculator; import seedu.dietbook.database.DataBase; import seedu.dietbook.person.Gender; - +import seedu.dietbook.exception.DietException; +import seedu.dietbook.parser.Parser; import java.util.Scanner; - public class Manager { private Person person; private FoodList foodList; @@ -19,6 +20,18 @@ public class Manager { private Calculator calculator; private static Scanner s = new Scanner(System.in); + public static final String COMMAND_ADD = "add"; + public static final String COMMAND_CALCULATE = "calculate"; + public static final String COMMAND_CLEAR = "clear"; + public static final String COMMAND_DATA = "data"; + public static final String COMMAND_DELETE = "delete"; + public static final String COMMAND_EXIT = "exit"; + public static final String COMMAND_HELP = "help"; + public static final String COMMAND_INFO = "info"; + public static final String COMMAND_LIST = "list"; + public static final String COMMAND_NAME = "name"; + public static final String COMMAND_USERINFO = "userinfo"; + public Manager(FoodList foodlist, DataBase dataBase) { this.name = "John Doe"; this.person = new Person(this.name, Gender.MALE, 0,0,0,0, ActivityLevel.LOW); @@ -64,4 +77,40 @@ public void setName(String name) { this.name = name; } + /** + * Makes sense of the user input and carries out the functions according to the command given. + * @param userInput user input. + * @throws DietException when the program does not recognize the command given. + */ + public Command manage(String userInput) throws DietException { + Calculator calculator = this.calculator; + switch (Parser.getCommand(userInput)) { + case COMMAND_ADD: + return new AddCommand(Parser.getProcessedAdd(userInput, getFoodList())); + case COMMAND_CALCULATE: + return new CalculateCommand(calculator.calculateCalorie(), calculator.calculateCarb(), + calculator.calculateProtein(), calculator.calculateFat(), Parser.getCommandParam(userInput)); + case COMMAND_CLEAR: + return new ClearCommand(); + case COMMAND_DATA: + return new DataCommand(); + case COMMAND_DELETE: + return new DeleteCommand(Parser.getCommandIndex(userInput)); + case COMMAND_EXIT: + return new ExitCommand(); + case COMMAND_HELP: + return new HelpCommand(); + case COMMAND_INFO: + return new InfoCommand(userInput); + case COMMAND_LIST: + return new ListCommand(); + case COMMAND_NAME: + return new NameCommand(Parser.getCommandParam(userInput)); + case COMMAND_USERINFO: + return new UserinfoCommand(); + default: + throw new DietException("There's no such command!"); + } + } + } diff --git a/src/main/java/seedu/dietbook/command/AddCommand.java b/src/main/java/seedu/dietbook/command/AddCommand.java new file mode 100644 index 0000000000..6a7b6a7524 --- /dev/null +++ b/src/main/java/seedu/dietbook/command/AddCommand.java @@ -0,0 +1,18 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class AddCommand extends Command { + String foodName; + + public AddCommand(String foodName) { + this.foodName = foodName; + } + + @Override + public void execute(Manager manager, Ui ui) { + ui.printNewFood(this.foodName); + manager.setCalculator(); + } +} diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java new file mode 100644 index 0000000000..6884bf315b --- /dev/null +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -0,0 +1,41 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class CalculateCommand extends Command { + int calorie; + int carb; + int protein; + int fat; + String param; + + public CalculateCommand(int calorie, int carb, int protein, int fat, String param) { + this.calorie = calorie; + this.carb = carb; + this.protein = protein; + this.fat = fat; + this.param = param; + } + + @Override + public void execute(Manager manager, Ui ui) { + manager.setCalculator(); + switch (this.param) { + case "all": + ui.printAllNutrientIntake(this.calorie, this.carb, this.protein, this.fat); + break; + case "calorie": + ui.printCalorieIntake(this.calorie); + break; + case "carbohydrate": + ui.printCarbohydrateIntake(this.carb); + break; + case "protein": + ui.printProteinIntake(this.protein); + break; + default: + ui.printFatIntake(this.fat); + } + } +} diff --git a/src/main/java/seedu/dietbook/command/ClearCommand.java b/src/main/java/seedu/dietbook/command/ClearCommand.java new file mode 100644 index 0000000000..b07f6dda71 --- /dev/null +++ b/src/main/java/seedu/dietbook/command/ClearCommand.java @@ -0,0 +1,12 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class ClearCommand extends Command { + @Override + public void execute(Manager manager, Ui ui) { + ui.printClearFoodListMessage(); + manager.getFoodList().clear(); + } +} diff --git a/src/main/java/seedu/dietbook/command/Command.java b/src/main/java/seedu/dietbook/command/Command.java new file mode 100644 index 0000000000..a5a9e58636 --- /dev/null +++ b/src/main/java/seedu/dietbook/command/Command.java @@ -0,0 +1,9 @@ +package seedu.dietbook.command; + +import seedu.dietbook.exception.DietException; +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public abstract class Command { + public abstract void execute(Manager manager, Ui ui) throws DietException; +} diff --git a/src/main/java/seedu/dietbook/command/DataCommand.java b/src/main/java/seedu/dietbook/command/DataCommand.java new file mode 100644 index 0000000000..12db1c7202 --- /dev/null +++ b/src/main/java/seedu/dietbook/command/DataCommand.java @@ -0,0 +1,13 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class DataCommand extends Command { + + @Override + public void execute(Manager manager, Ui ui) { + manager.getDataBase().init(); + ui.printDatabase(manager.getDataBase().getFoodList()); + } +} diff --git a/src/main/java/seedu/dietbook/command/DeleteCommand.java b/src/main/java/seedu/dietbook/command/DeleteCommand.java new file mode 100644 index 0000000000..260635b297 --- /dev/null +++ b/src/main/java/seedu/dietbook/command/DeleteCommand.java @@ -0,0 +1,23 @@ +package seedu.dietbook.command; + +import seedu.dietbook.exception.DietException; +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class DeleteCommand extends Command { + int index; + + public DeleteCommand(int index) { + this.index = index; + } + + @Override + public void execute(Manager manager, Ui ui) throws DietException { + try { + ui.printDeletedFood(manager.getFoodList().delete(this.index)); + manager.setCalculator(); + } catch (IndexOutOfBoundsException e) { + throw new DietException("No such index!"); + } + } +} diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java new file mode 100644 index 0000000000..9f5b19d0dd --- /dev/null +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -0,0 +1,13 @@ +package seedu.dietbook.command; + +import seedu.dietbook.DietBook; +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class ExitCommand extends Command { + @Override + public void execute(Manager manager, Ui ui) { + ui.printExitMessage(manager.getName()); + DietBook.isExit = true; + } +} diff --git a/src/main/java/seedu/dietbook/command/HelpCommand.java b/src/main/java/seedu/dietbook/command/HelpCommand.java new file mode 100644 index 0000000000..30a57a593f --- /dev/null +++ b/src/main/java/seedu/dietbook/command/HelpCommand.java @@ -0,0 +1,11 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class HelpCommand extends Command { + @Override + public void execute(Manager manager, Ui ui) { + ui.printTutorialMessage(); + } +} diff --git a/src/main/java/seedu/dietbook/command/InfoCommand.java b/src/main/java/seedu/dietbook/command/InfoCommand.java new file mode 100644 index 0000000000..c189c46be3 --- /dev/null +++ b/src/main/java/seedu/dietbook/command/InfoCommand.java @@ -0,0 +1,20 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; +import seedu.dietbook.parser.Parser; + +public class InfoCommand extends Command { + String userInput; + + public InfoCommand(String userInput) { + this.userInput = userInput; + } + + @Override + public void execute(Manager manager, Ui ui) throws DietException { + Parser.executeProcessedInfo(this.userInput, manager); + ui.printTutorialMessage(); + } +} diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java new file mode 100644 index 0000000000..5e44014eec --- /dev/null +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -0,0 +1,11 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class ListCommand extends Command { + @Override + public void execute(Manager manager, Ui ui) { + ui.printFoodList(manager.getFoodList().toString()); + } +} diff --git a/src/main/java/seedu/dietbook/command/NameCommand.java b/src/main/java/seedu/dietbook/command/NameCommand.java new file mode 100644 index 0000000000..11f6d44e3a --- /dev/null +++ b/src/main/java/seedu/dietbook/command/NameCommand.java @@ -0,0 +1,20 @@ +package seedu.dietbook.command; + + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + + +public class NameCommand extends Command { + String name; + + public NameCommand(String name) { + this.name = name; + } + + @Override + public void execute(Manager manager, Ui ui) { + manager.setName(this.name); + ui.printAskForUserInfoMessage(manager.getName()); + } +} diff --git a/src/main/java/seedu/dietbook/command/UserinfoCommand.java b/src/main/java/seedu/dietbook/command/UserinfoCommand.java new file mode 100644 index 0000000000..40c8fca39b --- /dev/null +++ b/src/main/java/seedu/dietbook/command/UserinfoCommand.java @@ -0,0 +1,11 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; + +public class UserinfoCommand extends Command { + @Override + public void execute(Manager manager, Ui ui) { + ui.printPersonInfo(manager.getPerson().toString()); + } +} diff --git a/src/main/java/seedu/dietbook/DietException.java b/src/main/java/seedu/dietbook/exception/DietException.java similarity index 85% rename from src/main/java/seedu/dietbook/DietException.java rename to src/main/java/seedu/dietbook/exception/DietException.java index 922514bfa5..9f3dffc2d5 100644 --- a/src/main/java/seedu/dietbook/DietException.java +++ b/src/main/java/seedu/dietbook/exception/DietException.java @@ -1,4 +1,4 @@ -package seedu.dietbook; +package seedu.dietbook.exception; public class DietException extends Exception { public DietException(String message) { diff --git a/src/main/java/seedu/dietbook/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java similarity index 66% rename from src/main/java/seedu/dietbook/Parser.java rename to src/main/java/seedu/dietbook/parser/Parser.java index 8c2fd309df..195d4ea05b 100644 --- a/src/main/java/seedu/dietbook/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -1,23 +1,16 @@ -package seedu.dietbook; +package seedu.dietbook.parser; -import seedu.calculator.Calculator; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.Gender; import seedu.dietbook.person.ActivityLevel; - -import java.io.FileNotFoundException; +import seedu.dietbook.exception.DietException; +import seedu.dietbook.Manager; public class Parser { public static final String COMMAND_NAME = "name"; - public static final String COMMAND_LIST = "list"; public static final String COMMAND_INFO = "info"; - public static final String COMMAND_EXIT = "exit"; public static final String COMMAND_ADD = "add"; - public static final String COMMAND_CLEAR = "clear"; - public static final String COMMAND_DELETE = "delete"; public static final String COMMAND_CALCULATE = "calculate"; - public static final String COMMAND_DATA = "data"; - public static final String COMMAND_USERINFO = "userinfo"; public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; public static final String[] PARAM_ADD = {"n/","x/","k/"}; @@ -28,7 +21,7 @@ public class Parser { * @param userInput which is user input. * @return First word which is the command of the user input. */ - private static String getCommand(String userInput) { + public static String getCommand(String userInput) { return userInput.split(" ")[0]; } @@ -38,7 +31,7 @@ private static String getCommand(String userInput) { * @return parameter part of the user input. * @throws DietException when the user input is of a wrong format. */ - private static String getCommandParam(String userInput) throws DietException { + public static String getCommandParam(String userInput) throws DietException { String command = getCommand(userInput); String[] input = {userInput}; @@ -83,7 +76,7 @@ private static String getCommandParam(String userInput) throws DietException { * @return name of the food that was added. * @throws DietException when the user input is of a wrong format. */ - private static String getProcessedAdd(String userInput, FoodList foodList) throws DietException { + public static String getProcessedAdd(String userInput, FoodList foodList) throws DietException { int portionSize = 1; String foodName = "Food Name"; int calorie = 0; @@ -131,7 +124,7 @@ private static String getProcessedAdd(String userInput, FoodList foodList) throw * @param manager the manager object. * @throws DietException when the user input is of a wrong format. */ - private static void executeProcessedInfo(String userInput, Manager manager) throws DietException { + public static void executeProcessedInfo(String userInput, Manager manager) throws DietException { Gender gender = Gender.MALE; ActivityLevel actLvl = ActivityLevel.NONE; int age = 0; @@ -193,7 +186,7 @@ private static void executeProcessedInfo(String userInput, Manager manager) thro * @return index part of the user input. * @throws DietException when the user input is of a wrong format. */ - private static int getCommandIndex(String userInput) throws DietException { + public static int getCommandIndex(String userInput) throws DietException { String command = getCommand(userInput); if (userInput.split(command).length < 2 || userInput.split(command)[1].equals(" ")) { @@ -205,70 +198,4 @@ private static int getCommandIndex(String userInput) throws DietException { throw new DietException("OOPS!!! No integer index detected!"); } } - - /** - * Makes sense of the user input and carries out the functions according to the command given. - * @param userInput user input. - * @throws DietException when the program does not recognize the command given. - */ - public static void parse(String userInput, Manager manager, Ui ui) throws DietException, FileNotFoundException { - Calculator calculator = manager.getCalculator(); - switch (getCommand(userInput)) { - case COMMAND_NAME: - manager.setName(getCommandParam(userInput)); - ui.printAskForUserInfoMessage(manager.getName()); - return; - case COMMAND_EXIT: - ui.printExitMessage(manager.getName()); - DietBook.isExit = true; - return; - case COMMAND_LIST: - ui.printFoodList(manager.getFoodList().toString()); - return; - case COMMAND_USERINFO: - ui.printPersonInfo(manager.getPerson().toString()); - return; - case COMMAND_DATA: - manager.getDataBase().init(); - ui.printDatabase(manager.getDataBase().getFoodList()); - return; - case COMMAND_DELETE: - try { - ui.printDeletedFood(manager.getFoodList().delete(getCommandIndex(userInput))); - manager.setCalculator(); - } catch (IndexOutOfBoundsException e) { - throw new DietException("No such index!"); - } - return; - case COMMAND_CLEAR: - ui.printClearFoodListMessage(); - manager.getFoodList().clear(); - return; - case COMMAND_CALCULATE: - manager.setCalculator(); - if (getCommandParam(userInput).equals("all")) { - ui.printAllNutrientIntake(calculator.calculateCalorie(), calculator.calculateCarb(), - calculator.calculateProtein(), calculator.calculateFat()); - } else if (getCommandParam(userInput).equals("calorie")) { - ui.printCalorieIntake(calculator.calculateCalorie()); - } else if (getCommandParam(userInput).equals("carbohydrate")) { - ui.printCarbohydrateIntake(calculator.calculateCarb()); - } else if (getCommandParam(userInput).equals("protein")) { - ui.printProteinIntake(calculator.calculateProtein()); - } else { - ui.printFatIntake(calculator.calculateFat()); - } - return; - case COMMAND_INFO: - executeProcessedInfo(userInput, manager); - ui.printTutorialMessage(); - return; - case COMMAND_ADD: - ui.printNewFood(getProcessedAdd(userInput, manager.getFoodList())); - manager.setCalculator(); - return; - default: - throw new DietException("There's no such command!"); - } - } } From 8eb6d3b7874655cd5a11e400fd32bc383301cad7 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Sat, 24 Oct 2020 13:31:47 +0800 Subject: [PATCH 125/374] Input Checker -Implemented input checker class --- src/main/java/META-INF/MANIFEST.MF | 2 +- src/main/java/seedu/dietbook/DietBook.java | 27 ++-- src/main/java/seedu/dietbook/Manager.java | 11 +- .../seedu/dietbook/checker/InputChecker.java | 153 ++++++++++++++++++ .../java/seedu/dietbook/command/Command.java | 8 + .../java/seedu/dietbook/parser/Parser.java | 83 +++++----- 6 files changed, 230 insertions(+), 54 deletions(-) create mode 100644 src/main/java/seedu/dietbook/checker/InputChecker.java diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF index 3d5a2c3351..d18cc0371f 100644 --- a/src/main/java/META-INF/MANIFEST.MF +++ b/src/main/java/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ Manifest-Version: 1.0 -Main-Class: seedu.duke.DietBook +Main-Class: seedu.dietbook.DietBook diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 4da58f94c2..4bcdbf404d 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -7,6 +7,13 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.command.Command; +/** + * Main class of the program. + * The DietBook program is an application which can store, display and check your daily dietary intake. + * + * @author tikimonarch + */ + public class DietBook { private FoodList foodList; private Ui ui; @@ -14,6 +21,16 @@ public class DietBook { private DataBase dataBase; public static boolean isExit = false; + /** + * Constructor for new DietBook. + */ + public DietBook() { + ui = new Ui(); + foodList = new FoodList(); + dataBase = new DataBase(); + manager = new Manager(foodList, dataBase); + } + /** * Main method to run the program. */ @@ -31,14 +48,4 @@ public static void main(String[] args) throws FileNotFoundException { } } } - - /** - * Constructor for new DietBook. - */ - public DietBook() { - ui = new Ui(); - foodList = new FoodList(); - dataBase = new DataBase(); - manager = new Manager(foodList, dataBase); - } } diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 0e33b62f1d..9f0bf2781b 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -12,6 +12,14 @@ import java.util.Scanner; +/** + * Manager class of the program. + * The manager class takes in the checked and processed input and carry out the command specified. + * Initialization of important classes such as FoodList and Person is done here. + * + * @author tikimonarch + */ + public class Manager { private Person person; private FoodList foodList; @@ -78,8 +86,9 @@ public void setName(String name) { } /** - * Makes sense of the user input and carries out the functions according to the command given. + * Takes in the user input and returns the command to be carried. * @param userInput user input. + * @return Command for the command specified. * @throws DietException when the program does not recognize the command given. */ public Command manage(String userInput) throws DietException { diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java new file mode 100644 index 0000000000..109d1340e8 --- /dev/null +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -0,0 +1,153 @@ +package seedu.dietbook.checker; + +import seedu.dietbook.exception.DietException; + +/** + * InputChecker class of the program. + * This class checks the validity of the user input and throws an exception if input is not as intended/expected. + * + * @author tikimonarch + */ + +public class InputChecker { + public static final int AGE_CAP = 125; + public static final int FOOD_CAP = 100000; + public static final int HEIGHT_CAP = 273; + public static final int WEIGHT_CAP = 443; + public static final String[] PARAM_ACTIVITY = {"1","2","3","4","5"}; + public static final String[] PARAM_ADD = {"n/","x/","k/"}; + public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; + public static final String[] PARAM_GENDER = {"M","F","O"}; + public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; + + /** + * Takes in user input and command to check for any expected parameters after the command. + * @param userInput user input. + * @param command command in user input. + * @throws DietException when at least one parameter is expected but not present. + */ + public static void checkEmpty(String userInput, String command) throws DietException { + if (userInput.split(command).length < 2 + || userInput.split(command)[1].equals(" ")) { + throw new DietException("Error! Missing command parameters!"); + } + } + + /** + * Takes in user input to check if the expected number and type of parameter for the add command is present. + * @param userInput user input. + * @throws DietException when expected parameters are missing. + */ + public static void checkAddParam(String userInput) throws DietException { + for (String param: PARAM_ADD) { + if (!userInput.contains(param)) { + throw new DietException("Missing or incorrect add statement"); + } + } + } + + /** + * Takes in user input to check if the expected number and type of parameter for the info command is present. + * @param userInput user input. + * @throws DietException when expected parameters are missing. + */ + public static void checkInfoParam(String userInput) throws DietException { + for (String param: PARAM_INFO) { + if (!userInput.contains(param)) { + throw new DietException("Missing or incorrect info statement"); + } + } + } + + /** + * Takes in an integer from food to check if the value is within the logical limit. + * @param foodValue integer value of attributes. + * @throws DietException when value is not within the limit. + */ + public static void checkFoodLimit(int foodValue) throws DietException { + if (foodValue < 0) { + throw new DietException("Input value cannot be less than 0!"); + } else if (foodValue > FOOD_CAP) { + throw new DietException("Input value cannot be more than 100,000!"); + } + } + + /** + * Takes in user input to check if the nutrient type is of the expected input. + * @param userInput user input. + * @throws DietException when it is not one of the expected nutrient type. + */ + public static void checkNutrientType(String userInput) throws DietException { + for (String param: PARAM_CALCULATE) { + if (!userInput.contains(param)) { + throw new DietException("Incorrect nutrient type!"); + } + } + } + + /** + * Takes in user input to check if the activity level is of the expected input. + * @param userInput user input. + * @throws DietException when it is not one of the expected activity level. + */ + public static void checkActivity(String userInput) throws DietException { + for (String param: PARAM_ACTIVITY) { + if (!userInput.contains(param)) { + throw new DietException("No such activity level!"); + } + } + } + + /** + * Takes in user input to check if the gender is of the expected input. + * @param userInput user input. + * @throws DietException when it is not one of the expected gender input. + */ + public static void checkGender(String userInput) throws DietException { + for (String param: PARAM_GENDER) { + if (!userInput.contains(param)) { + throw new DietException("Please key in the specified gender characters."); + } + } + } + + /** + * Takes in an integer age to check if the value is within the logical limit. + * @param age integer value of age. + * @throws DietException when value is not within the limit. + */ + public static void checkAgeLimit(int age) throws DietException { + if (age < 0) { + throw new DietException("Input value cannot be less than 0!"); + } else if (age > AGE_CAP) { + throw new DietException("Input value cannot be more than 125!"); + } + } + + /** + * Takes in an integer height to check if the value is within the logical limit. + * @param height integer value of height. + * @throws DietException when value is not within the limit. + */ + public static void checkHeightLimit(int height) throws DietException { + if (height < 0) { + throw new DietException("Input value cannot be less than 0!"); + } else if (height > HEIGHT_CAP) { + throw new DietException("Input value cannot be more than 273!"); + } + } + + /** + * Takes in an integer weight to check if the value is within the logical limit. + * @param weight integer value of weight. + * @throws DietException when value is not within the limit. + */ + public static void checkWeightLimit(int weight) throws DietException { + if (weight < 0) { + throw new DietException("Input value cannot be less than 0!"); + } else if (weight > WEIGHT_CAP) { + throw new DietException("Input value cannot be more than 443!"); + } + } + +} diff --git a/src/main/java/seedu/dietbook/command/Command.java b/src/main/java/seedu/dietbook/command/Command.java index a5a9e58636..5282fc1990 100644 --- a/src/main/java/seedu/dietbook/command/Command.java +++ b/src/main/java/seedu/dietbook/command/Command.java @@ -4,6 +4,14 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +/** + * The command parent class for all commands. + * All commands have an execute method that takes in objects Manager and Ui. + * Each child command class is self-explanatory. + * + * @author tikimonarch + */ + public abstract class Command { public abstract void execute(Manager manager, Ui ui) throws DietException; } diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 195d4ea05b..365b328f3d 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -5,15 +5,22 @@ import seedu.dietbook.person.ActivityLevel; import seedu.dietbook.exception.DietException; import seedu.dietbook.Manager; +import seedu.dietbook.checker.InputChecker; + +/** + * Parser class of the program. + * The parser class takes in user input and process it into command data that manager can use. + * + * @author tikimonarch + */ public class Parser { public static final String COMMAND_NAME = "name"; public static final String COMMAND_INFO = "info"; public static final String COMMAND_ADD = "add"; public static final String COMMAND_CALCULATE = "calculate"; - public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; - public static final String[] PARAM_ADD = {"n/","x/","k/"}; + /** @@ -33,40 +40,24 @@ public static String getCommand(String userInput) { */ public static String getCommandParam(String userInput) throws DietException { String command = getCommand(userInput); - String[] input = {userInput}; - if (userInput.split(command).length < 2 - || userInput.split(command)[1].equals(" ")) { - throw new DietException("Error! Missing command parameters!"); - } else { - switch (command) { - case COMMAND_NAME: - return userInput.split("name")[1].trim(); - case COMMAND_CALCULATE: - for (String param: PARAM_CALCULATE) { - if (userInput.contains(param)) { - return userInput.split("calculate")[1].trim(); - } - } - throw new DietException("Incorrect nutrient type"); - case COMMAND_ADD: - for (String param: PARAM_ADD) { - if (!userInput.contains(param)) { - throw new DietException("Missing or incorrect add statement"); - } - } - return userInput.substring(userInput.indexOf(' ') + 1); - case COMMAND_INFO: - for (String param: PARAM_INFO) { - if (!userInput.contains(param)) { - throw new DietException("Missing or incorrect info statement"); - } - } - return userInput.substring(userInput.indexOf(' ') + 1); - default: - return null; - } + InputChecker.checkEmpty(userInput, command); + switch (command) { + case COMMAND_NAME: + return userInput.split("name")[1].trim(); + case COMMAND_CALCULATE: + InputChecker.checkNutrientType(userInput); + return userInput.split("calculate")[1].trim(); + case COMMAND_ADD: + InputChecker.checkAddParam(userInput); + return userInput.substring(userInput.indexOf(' ') + 1); + case COMMAND_INFO: + InputChecker.checkInfoParam(userInput); + return userInput.substring(userInput.indexOf(' ') + 1); + default: + return null; } + } /** @@ -80,9 +71,9 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws int portionSize = 1; String foodName = "Food Name"; int calorie = 0; - int carb = 0; - int protein = 0; - int fat = 0; + int carb = -1; + int protein = -1; + int fat = -1; String trimmedParam; String[] processedParam; String[] paramList = {"x/", "n/", "k/", "c/", "p/", "f/"}; @@ -96,21 +87,26 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws switch (param) { case "x/": portionSize = Integer.parseInt(trimmedParam); + InputChecker.checkFoodLimit(portionSize); break; case "n/": foodName = trimmedParam; break; case "k/": calorie = Integer.parseInt(trimmedParam); + InputChecker.checkFoodLimit(calorie); break; case "c/": carb = Integer.parseInt(trimmedParam); + InputChecker.checkFoodLimit(carb); break; case "p/": protein = Integer.parseInt(trimmedParam); + InputChecker.checkFoodLimit(protein); break; default: fat = Integer.parseInt(trimmedParam); + InputChecker.checkFoodLimit(fat); break; } } @@ -133,8 +129,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw int tarWeight = 0; String trimmedParam; String[] processedParam; - String[] paramList = {"g/", "a/", "h/", "o/", "t/", "l/"}; - for (String param: paramList) { + for (String param: PARAM_INFO) { processedParam = getCommandParam(userInput).split(param); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { @@ -143,6 +138,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw switch (param) { case "g/": String processGender = trimmedParam; + InputChecker.checkGender(processGender); if (processGender.equals("M")) { gender = Gender.MALE; } else { @@ -151,18 +147,23 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw break; case "a/": age = Integer.parseInt(trimmedParam); + InputChecker.checkAgeLimit(age); break; case "h/": height = Integer.parseInt(trimmedParam); + InputChecker.checkHeightLimit(height); break; case "o/": orgWeight = Integer.parseInt(trimmedParam); + InputChecker.checkWeightLimit(orgWeight); break; case "t/": tarWeight = Integer.parseInt(trimmedParam); + InputChecker.checkWeightLimit(tarWeight); break; default: String processActLvl = trimmedParam; + InputChecker.checkActivity(processActLvl); if (processActLvl.equals("1")) { actLvl = ActivityLevel.NONE; } else if (processActLvl.equals("2")) { @@ -189,9 +190,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw public static int getCommandIndex(String userInput) throws DietException { String command = getCommand(userInput); - if (userInput.split(command).length < 2 || userInput.split(command)[1].equals(" ")) { - throw new DietException("OOPS!!! Missing index of duke.task!"); - } + InputChecker.checkEmpty(userInput, command); try { return Integer.parseInt(userInput.split(" ")[1]); } catch (NumberFormatException e) { From ad72b4d5f4808dc1aedbaaff0a0bcc1fc958dfcf Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Sat, 24 Oct 2020 15:16:11 +0800 Subject: [PATCH 126/374] New check, bug fixes ->fixed for acticity, gender, calculate type checks ->new check if an option is specified but with an empty field --- .../seedu/dietbook/checker/InputChecker.java | 42 ++++++++++++++++--- .../java/seedu/dietbook/parser/Parser.java | 2 + 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 109d1340e8..4c71a5f251 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -10,6 +10,9 @@ */ public class InputChecker { + /** + * The value limits are based on current limits observed in th world. + */ public static final int AGE_CAP = 125; public static final int FOOD_CAP = 100000; public static final int HEIGHT_CAP = 273; @@ -33,6 +36,21 @@ public static void checkEmpty(String userInput, String command) throws DietExcep } } + /** + * Takes in processed user input to check for options specified with an empty field. + * @param input user input. + * @throws DietException when an option is specified but its field is empty. + */ + public static void checkEmptyOption(String[] input) throws DietException { + try { + if (input[1].trim().charAt(1) == '/') { + throw new DietException("Error! Option specified with empty field!"); + } + } catch (IndexOutOfBoundsException e) { + throw new DietException("Error! Option specified with empty field!"); + } + } + /** * Takes in user input to check if the expected number and type of parameter for the add command is present. * @param userInput user input. @@ -78,11 +96,15 @@ public static void checkFoodLimit(int foodValue) throws DietException { * @throws DietException when it is not one of the expected nutrient type. */ public static void checkNutrientType(String userInput) throws DietException { + boolean checkContain = false; for (String param: PARAM_CALCULATE) { - if (!userInput.contains(param)) { - throw new DietException("Incorrect nutrient type!"); + if (userInput.contains(param)) { + checkContain = true; } } + if (!checkContain) { + throw new DietException("Incorrect nutrient type!"); + } } /** @@ -91,11 +113,15 @@ public static void checkNutrientType(String userInput) throws DietException { * @throws DietException when it is not one of the expected activity level. */ public static void checkActivity(String userInput) throws DietException { + boolean checkContain = false; for (String param: PARAM_ACTIVITY) { - if (!userInput.contains(param)) { - throw new DietException("No such activity level!"); + if (userInput.contains(param)) { + checkContain = true; } } + if (!checkContain) { + throw new DietException("No such activity level!"); + } } /** @@ -104,11 +130,15 @@ public static void checkActivity(String userInput) throws DietException { * @throws DietException when it is not one of the expected gender input. */ public static void checkGender(String userInput) throws DietException { + boolean checkContain = false; for (String param: PARAM_GENDER) { - if (!userInput.contains(param)) { - throw new DietException("Please key in the specified gender characters."); + if (userInput.contains(param)) { + checkContain = true; } } + if (!checkContain) { + throw new DietException("Please key in the specified gender characters."); + } } /** diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 365b328f3d..a75df37a6e 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -80,6 +80,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws for (String param: paramList) { if (getCommandParam(userInput).contains(param)) { processedParam = getCommandParam(userInput).split(param); + InputChecker.checkEmptyOption(processedParam); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); @@ -131,6 +132,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw String[] processedParam; for (String param: PARAM_INFO) { processedParam = getCommandParam(userInput).split(param); + InputChecker.checkEmptyOption(processedParam); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); From 609f8ea04430471ea53fc9d22de840d11f74dcf6 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Sat, 24 Oct 2020 15:21:09 +0800 Subject: [PATCH 127/374] Spacings --- src/main/java/seedu/dietbook/Manager.java | 1 + .../java/seedu/dietbook/checker/InputChecker.java | 11 +++++++++++ src/main/java/seedu/dietbook/parser/Parser.java | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 9f0bf2781b..be218c70a1 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -87,6 +87,7 @@ public void setName(String name) { /** * Takes in the user input and returns the command to be carried. + * * @param userInput user input. * @return Command for the command specified. * @throws DietException when the program does not recognize the command given. diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 4c71a5f251..53772478f4 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -25,6 +25,7 @@ public class InputChecker { /** * Takes in user input and command to check for any expected parameters after the command. + * * @param userInput user input. * @param command command in user input. * @throws DietException when at least one parameter is expected but not present. @@ -38,6 +39,7 @@ public static void checkEmpty(String userInput, String command) throws DietExcep /** * Takes in processed user input to check for options specified with an empty field. + * * @param input user input. * @throws DietException when an option is specified but its field is empty. */ @@ -53,6 +55,7 @@ public static void checkEmptyOption(String[] input) throws DietException { /** * Takes in user input to check if the expected number and type of parameter for the add command is present. + * * @param userInput user input. * @throws DietException when expected parameters are missing. */ @@ -66,6 +69,7 @@ public static void checkAddParam(String userInput) throws DietException { /** * Takes in user input to check if the expected number and type of parameter for the info command is present. + * * @param userInput user input. * @throws DietException when expected parameters are missing. */ @@ -79,6 +83,7 @@ public static void checkInfoParam(String userInput) throws DietException { /** * Takes in an integer from food to check if the value is within the logical limit. + * * @param foodValue integer value of attributes. * @throws DietException when value is not within the limit. */ @@ -92,6 +97,7 @@ public static void checkFoodLimit(int foodValue) throws DietException { /** * Takes in user input to check if the nutrient type is of the expected input. + * * @param userInput user input. * @throws DietException when it is not one of the expected nutrient type. */ @@ -109,6 +115,7 @@ public static void checkNutrientType(String userInput) throws DietException { /** * Takes in user input to check if the activity level is of the expected input. + * * @param userInput user input. * @throws DietException when it is not one of the expected activity level. */ @@ -126,6 +133,7 @@ public static void checkActivity(String userInput) throws DietException { /** * Takes in user input to check if the gender is of the expected input. + * * @param userInput user input. * @throws DietException when it is not one of the expected gender input. */ @@ -143,6 +151,7 @@ public static void checkGender(String userInput) throws DietException { /** * Takes in an integer age to check if the value is within the logical limit. + * * @param age integer value of age. * @throws DietException when value is not within the limit. */ @@ -156,6 +165,7 @@ public static void checkAgeLimit(int age) throws DietException { /** * Takes in an integer height to check if the value is within the logical limit. + * * @param height integer value of height. * @throws DietException when value is not within the limit. */ @@ -169,6 +179,7 @@ public static void checkHeightLimit(int height) throws DietException { /** * Takes in an integer weight to check if the value is within the logical limit. + * * @param weight integer value of weight. * @throws DietException when value is not within the limit. */ diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index a75df37a6e..59283b0353 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -25,6 +25,7 @@ public class Parser { /** * Returns the command of a user input. + * * @param userInput which is user input. * @return First word which is the command of the user input. */ @@ -34,6 +35,7 @@ public static String getCommand(String userInput) { /** * Returns the subsequent parameter after the command from the user input. + * * @param userInput user input. * @return parameter part of the user input. * @throws DietException when the user input is of a wrong format. @@ -62,6 +64,7 @@ public static String getCommandParam(String userInput) throws DietException { /** * Processes the parameters for add command of user input and adds a Food object. + * * @param userInput user input. * @param foodList the FoodList object. * @return name of the food that was added. @@ -117,6 +120,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws /** * Processes the parameters for info command of user input and updates the Person object. + * * @param userInput user input. * @param manager the manager object. * @throws DietException when the user input is of a wrong format. @@ -185,6 +189,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw /** * Returns the index after the command of a user input, e.g. delete 3. + * * @param userInput user input. * @return index part of the user input. * @throws DietException when the user input is of a wrong format. From e647a62e57ca55055b7d6bc3c0c30e768988f3fc Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Sat, 24 Oct 2020 19:32:54 +0800 Subject: [PATCH 128/374] checkstyle import --- src/main/java/seedu/dietbook/Manager.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index be218c70a1..ad4c9be9f9 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -1,6 +1,17 @@ package seedu.dietbook; -import seedu.dietbook.command.*; +import seedu.dietbook.command.AddCommand; +import seedu.dietbook.command.CalculateCommand; +import seedu.dietbook.command.ClearCommand; +import seedu.dietbook.command.Command; +import seedu.dietbook.command.DataCommand; +import seedu.dietbook.command.DeleteCommand; +import seedu.dietbook.command.ExitCommand; +import seedu.dietbook.command.HelpCommand; +import seedu.dietbook.command.InfoCommand; +import seedu.dietbook.command.ListCommand; +import seedu.dietbook.command.NameCommand; +import seedu.dietbook.command.UserinfoCommand; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.ActivityLevel; import seedu.dietbook.person.Person; From 1b032b3891784edaee486010f38b9fdbf295066b Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 19:34:06 +0800 Subject: [PATCH 129/374] Provide an example for inputting name --- src/main/java/seedu/dietbook/Ui.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index eec8ede96c..a86655339d 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -35,7 +35,8 @@ public void printWelcomeMessage() { + "Hello! Welcome to DietBook!" + LINE_SEPARATOR + "I am Diet, your guide to using DietBook. What is your name?" + LINE_SEPARATOR + "Please input in the following format:" + LINE_SEPARATOR - + " name YOUR_NAME"); + + " name YOUR_NAME" + LINE_SEPARATOR + + " Example: name Jack"); } /** From 25209b806494d3169d9f883ff98ef4f1660946ed Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 19:40:20 +0800 Subject: [PATCH 130/374] Add others gender --- src/main/java/seedu/dietbook/person/Gender.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/person/Gender.java b/src/main/java/seedu/dietbook/person/Gender.java index b0bbe03e22..d19419da79 100644 --- a/src/main/java/seedu/dietbook/person/Gender.java +++ b/src/main/java/seedu/dietbook/person/Gender.java @@ -6,7 +6,8 @@ */ public enum Gender { FEMALE("female"), - MALE("male"); + MALE("male"), + OTHERS("others"); private final String description; From 1df44a13b2f683a9d9a3ddbaa5a09b7e8279ce6e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 19:47:55 +0800 Subject: [PATCH 131/374] Add comments on organisation of Ui methods --- src/main/java/seedu/dietbook/Ui.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index a86655339d..24c4378971 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -21,6 +21,11 @@ public class Ui { public Ui() { } + // Methods in the Ui class are organised according to their function in the order of: system related, + // database related, person related, food list related, calculator related and other helper methods. + + // Methods required to print system related commands or messages. + /** * Prints the welcome message from DietBook when it is fist booted up. */ From ee69fb2011e512e93afb161abf432cceb8429687 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 20:51:22 +0800 Subject: [PATCH 132/374] Organise related methods together --- src/main/java/seedu/dietbook/Ui.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 24c4378971..cf2f29d43e 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -177,7 +177,13 @@ public void printDatabase(String foodDatabase) { } /** - * Prints all the information related to the user. + * Prints a message to show that the food list has been successfully cleared and is now empty. + */ + public void printClearFoodListMessage() { + print("All previous data has been deleted..." + LINE_SEPARATOR + + "DietBook is now empty."); + } + * * @param personInfo The user's personal information. */ From 26bf7071b123b21dc52a2ac73a40b04edd756f25 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 20:52:31 +0800 Subject: [PATCH 133/374] Add getSystemRelatedCommands method --- src/main/java/seedu/dietbook/Ui.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index cf2f29d43e..eb4c971b48 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -253,6 +253,16 @@ public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, in + "Total carbohydrate intake: " + carbohydrateIntake + "g" + LINE_SEPARATOR + "Total protein intake: " + proteinIntake + "g" + LINE_SEPARATOR + "Total fat intake: " + fatIntake + "g"); + // Helper methods for system related commands or messages + + /** + * Returns a string representation of a list of system related commands that users can input. + * + * @return A string representation of a list of system related commands that users can input. + */ + private String getSystemRelatedCommands() { + return " To view a list of valid commands: help" + LINE_SEPARATOR + + " To exit DietBook: exit"; } /** From 3222b997d34c92cf08ba7d5a5700e785043e552a Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 20:54:55 +0800 Subject: [PATCH 134/374] Update printAskForUserInfoMessage method --- src/main/java/seedu/dietbook/Ui.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index eb4c971b48..a070ebf6d9 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -59,11 +59,14 @@ public void printAskForUserInfoMessage(String name) { + "accurate calculations for you :). Therefore, could you please share with me the " + "following:" + LINE_SEPARATOR + "- Your gender either F for " + Gender.FEMALE.getDescription() + " or M for " - + Gender.MALE.getDescription() + "." + LINE_SEPARATOR + + Gender.MALE.getDescription() + " or O for " + Gender.OTHERS.getDescription() + "." + + LINE_SEPARATOR + "- Your age which is a positive integer." + LINE_SEPARATOR + "- Your height in cm." + LINE_SEPARATOR - + "- Your original weight in kg." + LINE_SEPARATOR - + "- Your target weight in kg, or your original weight if that is also your target weight." + + "- Your original weight in kg, the weight when you first started using DietBook or " + + "you current weight." + LINE_SEPARATOR + + "- Your current weight in kg." + LINE_SEPARATOR + + "- Your target weight in kg, or your current weight if that is also your target weight." + LINE_SEPARATOR + "- Your activity level, represented by a number from 1 to 5." + LINE_SEPARATOR + " 1 = " + ActivityLevel.NONE.getDescription() + LINE_SEPARATOR @@ -72,9 +75,11 @@ public void printAskForUserInfoMessage(String name) { + " 4 = " + ActivityLevel.HIGH.getDescription() + LINE_SEPARATOR + " 5 = " + ActivityLevel.EXTREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + "Please input your details in the following format:" + LINE_SEPARATOR - + " info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL" - + LINE_SEPARATOR - + " Example: info g/F a/21 h/165 o/65 t/55 l/2"); + + " info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT " + + "l/ACTIVITY_LEVEL" + LINE_SEPARATOR + + " Example: info g/F a/21 h/165 o/65 c/65 t/55 l/2"); + } + } /** From 168e6ac49283a6d8e89d2abeddf3fba568e1bd01 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 20:57:38 +0800 Subject: [PATCH 135/374] Change initialisation completed message --- src/main/java/seedu/dietbook/Ui.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index a070ebf6d9..9033e7cd98 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -106,7 +106,14 @@ public void printTutorialMessage() { } /** - * Prints a message to show that the food specified has been added to the food list. + * Prints a message that notifies the user that DietBook has been initialised. + */ + public void printInitialisationCompleteMessage() { + print("Thank you! DietBook has been initialised and you may start by entering any valid commands. " + + LINE_SEPARATOR + + "If you require a list of valid commands, you can enter: help"); + } + * * @param newFood The string representation of the new food item that was added to the food list. */ From f2f9560f4735d4267821bd40b2dc654b7e54869c Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 20:59:11 +0800 Subject: [PATCH 136/374] Update printTutorialMessage method --- src/main/java/seedu/dietbook/Ui.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 9033e7cd98..67d7ba28d7 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -86,23 +86,6 @@ public void printAskForUserInfoMessage(String name) { * Prints a message that notifies the user that DietBook has been initialised and shows a list of user * commands that the user can input. */ - public void printTutorialMessage() { - print("Thank you! DietBook has been initialised and you may start by entering any of the commands " - + "listed below." + LINE_SEPARATOR + LINE_SEPARATOR - + "To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR - + "To view all food in the database: data" + LINE_SEPARATOR - + "To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] " - + "[p/PROTEIN] [f/FAT]" + LINE_SEPARATOR - + "To view all food in DietBook: list" + LINE_SEPARATOR - + "To delete a food from DietBook: delete INDEX" + LINE_SEPARATOR - + "To delete all food items from the DietBook: clear" + LINE_SEPARATOR - + "To show user information: userinfo" + LINE_SEPARATOR - + "To calculate carbohydrate intake: calculate carbohydrate" + LINE_SEPARATOR - + "To calculate calorie intake: calculate calorie" + LINE_SEPARATOR - + "To calculate protein intake: calculate protein" + LINE_SEPARATOR - + "To calculate fat intake: calculate fat" + LINE_SEPARATOR - + "To calculate all nutritional intake: calculate all" + LINE_SEPARATOR - + "To exit DietBook: exit"); } /** From e0315fbb7a708b4fd7bf483674df7fa84851ed10 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:00:10 +0800 Subject: [PATCH 137/374] Add getCalculatorRelatedCommands method --- src/main/java/seedu/dietbook/Ui.java | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 67d7ba28d7..e7bf70ad88 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -261,16 +261,39 @@ private String getSystemRelatedCommands() { } /** - * Prints a message to show that the food specified has been deleted from the food list. + * Returns a string representation of a list of nutritional intake and recommendation related commands + * that users can input. * - * @param deletedFood The string representation of the food that was deleted from the food list. + * @return A string representation of a list of nutritional intake and recommendation related commands + * that users can input. */ - public void printDeletedFood(String deletedFood) { - assert deletedFood != null : "String representation of the food that was deleted should not be null"; - assert trimStringGetLength(deletedFood) > 0 : "String representation of the food that was deleted should" - + " not be an empty string"; - print("Noted. I've removed this food item:" + LINE_SEPARATOR - + " " + trimString(deletedFood)); + private String getCalculatorRelatedCommands() { + return " To get recommended calorie intake: recommend" + LINE_SEPARATOR + LINE_SEPARATOR + + " To calculate carbohydrate intake: calculate carbohydrate" + LINE_SEPARATOR + + " To calculate carbohydrate intake within a time period: calculate carbohydrate " + + "yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + + " To calculate carbohydrate intake from a certain date until now: calculate carbohydrate " + + "yyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR + + " To calculate calorie intake: calculate calorie" + LINE_SEPARATOR + + " To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + + " To calculate calorie intake from a certain date until now: calculate calorie " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR + + " To calculate protein intake: calculate protein" + LINE_SEPARATOR + + " To calculate protein intake within a time period: calculate protein yyyy-mm-ddTHH:mm " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + + " To calculate protein intake from a certain date until now: calculate protein " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR + + " To calculate fat intake: calculate fat" + LINE_SEPARATOR + + " To calculate fat intake within a time period: calculate fat yyyy-mm-ddTHH:mm " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + + " To calculate fat intake from a certain date until now: calculate fat " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR + + " To calculate all nutritional intake: calculate all" + LINE_SEPARATOR + + " To calculate all nutritional intake within a time period: calculate all " + + "yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + + " To calculate all nutritional intake from a certain date until now: calculate all " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR; } /** From ee501adbf76c20c90019a25bbdd198c443f8611a Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:00:57 +0800 Subject: [PATCH 138/374] Add getUserRelatedCommands method --- src/main/java/seedu/dietbook/Ui.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index e7bf70ad88..863510d440 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -297,11 +297,15 @@ private String getCalculatorRelatedCommands() { } /** - * Prints a message to show that the food list has been successfully cleared and is now empty. + * Returns a string representation of a list of user information related commands that users can input. + * + * @return A string representation of a list of user information related commands that users can input. */ - public void printClearFoodListMessage() { - print("All previous data has been deleted..." + LINE_SEPARATOR - + "DietBook is now empty."); + private String getUserRelatedCommands() { + return " To show user information: userinfo" + LINE_SEPARATOR + + " To edit user information: editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] " + + "[o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]" + + LINE_SEPARATOR; } /** From 29618c87b423c2dc872c8702a36c65a9119948ab Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:01:38 +0800 Subject: [PATCH 139/374] Add getFoodListRelatedCommands method --- src/main/java/seedu/dietbook/Ui.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 863510d440..22ed842560 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -309,14 +309,20 @@ private String getUserRelatedCommands() { } /** - * Prints an exit message when DietBook is closed. + * Returns a string representation of a list of food list related commands that users can input. * - * @param name The name of the user. + * @return A string representation of a list of food list related commands that users can input. */ - public void printExitMessage(String name) { - assert name != null : "Name should not be null"; - assert trimStringGetLength(name) > 0 : "Name should not be an empty string"; - print("Bye " + trimString(name) + "! Hope to see you again soon!"); + private String getFoodListRelatedCommands() { + return " To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] " + + "[p/PROTEIN] [f/FAT]" + LINE_SEPARATOR + + " To view all food in DietBook: list" + LINE_SEPARATOR + + " To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm " + + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + + " To view all food in DietBook recorded from a certain date until now: list " + + "yyyy-mm-ddTHH:mm " + LINE_SEPARATOR + + " To delete a food from DietBook: delete INDEX" + LINE_SEPARATOR + + " To delete all food items from the DietBook: clear" + LINE_SEPARATOR; } /** From 5927bccf19d8abe469c562b1d54624028863de35 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:08:15 +0800 Subject: [PATCH 140/374] Update Ui.java --- src/main/java/seedu/dietbook/Ui.java | 620 +++++++++++++++++++++++---- 1 file changed, 527 insertions(+), 93 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 22ed842560..64e20166f0 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -51,8 +51,8 @@ public void printWelcomeMessage() { * @param name The name of the user. */ public void printAskForUserInfoMessage(String name) { - assert name != null : "Name should not be null"; - assert trimStringGetLength(name) > 0 : "Name should not be an empty string"; + performAssertionsForStringInputs(name, "Name"); + print("Hi " + trimString(name) + "!" + LINE_SEPARATOR + "Before we get started, I would like to know about about you so that I can make more " + LINE_SEPARATOR @@ -80,12 +80,25 @@ public void printAskForUserInfoMessage(String name) { + " Example: info g/F a/21 h/165 o/65 c/65 t/55 l/2"); } + /** + * Prints an exit message when DietBook is closed. + * + * @param name The name of the user. + */ + public void printExitMessage(String name) { + performAssertionsForStringInputs(name, "Name"); + print("Bye " + trimString(name) + "! Hope to see you again soon!"); } /** - * Prints a message that notifies the user that DietBook has been initialised and shows a list of user - * commands that the user can input. + * Prints an error message given what or where the error is. + * + * @param errorMessage Message detailing what or where the error is. */ + public void printErrorMessage(String errorMessage) { + performAssertionsForStringInputs(errorMessage,"Error message"); + + print(":( " + trimString(errorMessage)); } /** @@ -97,17 +110,85 @@ public void printInitialisationCompleteMessage() { + "If you require a list of valid commands, you can enter: help"); } + /** + * Prints a string representation of a list of the commands that users can use. + */ + public void printHelpCommandMessage() { + print("Listed below are the valid commands for DietBook:" + LINE_SEPARATOR + LINE_SEPARATOR + + "For database related commands" + LINE_SEPARATOR + + getDatabaseRelatedCommands() + LINE_SEPARATOR + + "For food list related commands" + LINE_SEPARATOR + + getFoodListRelatedCommands() + LINE_SEPARATOR + + "For user information related commands" + LINE_SEPARATOR + + getUserRelatedCommands() + LINE_SEPARATOR + + "For nutritional intake and recommendation related commands" + LINE_SEPARATOR + + getCalculatorRelatedCommands() + LINE_SEPARATOR + + "For other system related commands" + LINE_SEPARATOR + + getSystemRelatedCommands()); + } + + // Methods required to print database related commands or messages. + + /** + * Prints all the food in the database sorted by the canteen and then the store it is found. * - * @param newFood The string representation of the new food item that was added to the food list. + * @param foodDatabase The string representation of all the food items stored in the database. */ - public void printNewFood(String newFood) { - assert newFood != null : "String representation of the food that was added should not be null"; - assert trimStringGetLength(newFood) > 0 : "String representation of the food that was added should not " - + "be an empty string"; - print("Got it! I've added this food item:" + LINE_SEPARATOR - + " " + trimString(newFood)); + public void printDatabase(String foodDatabase) { + performAssertionsForStringInputs(foodDatabase, + "Food database"); + + print("Here are the food items in the database:" + LINE_SEPARATOR + foodDatabase); } + /** + * Prints the food items in the database containing the food name of the food that user wants to + * add sorted by the canteen and then the store it is found. + * This method is only used if more than one food item in the database contains the food name given. + * + * @param matchingFoodDatabase The string representation of the food items stored in the + * database containing the food name given. + */ + public void printMatchingFoodsInDatabase(String matchingFoodDatabase) { + performAssertionsForStringInputs(matchingFoodDatabase, + "Matching food database"); + + print("Here are the matching food items in the database:" + LINE_SEPARATOR + + matchingFoodDatabase + LINE_SEPARATOR + LINE_SEPARATOR + + "Please re-enter with the full name of the food item above in the following format:" + + LINE_SEPARATOR + " add n/FOOD_NAME x/PORTION_SIZE"); + } + + // Methods required to print user information related commands and messages. + + /** + * Prints all the information related to the user. + * + * @param personInfo The user's personal information. + */ + public void printPersonInfo(String personInfo) { + performAssertionsForStringInputs(personInfo, + "Person information"); + + print("Here is your information:" + LINE_SEPARATOR + + personInfo); + } + + /** + * Prints all the updated information related to the user. + * + * @param personInfo The user's personal information. + */ + public void printEditedPersonInfo(String personInfo) { + performAssertionsForStringInputs(personInfo, + "Updated person information"); + + print("Here is your updated information:" + LINE_SEPARATOR + + personInfo); + } + + // Methods required for printing FoodList related commands and messages. + /** * Prints all the food items in the food list in the order that they were added or a message stating * that the food list is empty if there are no food items. @@ -115,11 +196,13 @@ assert trimStringGetLength(newFood) > 0 : "String representation of the food tha * @param allFood The string representation of all the food items in the food list. */ public void printFoodList(String allFood) { - assert allFood != null : "String representation of all food in food list should not be null"; + performAssertionsForNullStringInputs(allFood, + "String representation of all food in food list"); + if (trimStringGetLength(allFood) < 1) { print("DietBook is currently empty."); } else { - print("Here are the food items in DietBook:" + LINE_SEPARATOR + trimString(allFood)); + print("Here are the food items in DietBook:" + LINE_SEPARATOR + allFood); } } @@ -132,43 +215,44 @@ public void printFoodList(String allFood) { * @param start Starting date time of the time period given. * @param end Ending date time of the time period given. */ - public void printFoodListGivenTimePeriod(String foods, LocalDateTime start, LocalDateTime end) { - assert foods != null : "String representation of food items in the food list recorded during the " - + "time period given should not be null"; - assert start != null : "Starting date time of the time period should not be null"; - assert end != null : "Ending date time of the time period should not be null"; - assert !start.isAfter(end) : "Starting date time should not be later than ending date time " - + "of the time period"; - String stringStart = stringDateTime(start); - String stringEnd = stringDateTime(end); + public void printFoodList(String foods, LocalDateTime start, LocalDateTime end) { + performAssertionsForNullStringInputs(foods, + "String representation of food items in the food list recorded during the time " + + "period given"); + performAssertionsForTimePeriod(start, end); + if (trimStringGetLength(foods) < 1) { - print("No food item was recorded in DietBook between " + stringStart + " and " + stringEnd + "."); + print("No food item was recorded in DietBook" + stringDateTimePeriod(start, end) + "."); } else { - print("Here are the food items recorded in DietBook between " + stringStart + " and " + stringEnd - + " :" + LINE_SEPARATOR + trimString(foods)); + print("Here are the food items recorded in DietBook" + stringDateTimePeriod(start, end) + ":" + + LINE_SEPARATOR + foods); } } /** - * Returns a string representation of the date time in the format dd MMM yyyy HHmm. + * Prints a message to show that the food specified has been added to the food list. * - * @param dateTime The date time that needs to be converted into a String. - * @return Returns a string representation of the date and time in the format dd MMM yyyy HHmm. + * @param newFood The string representation of the new food item that was added to the food list. */ - public String stringDateTime(LocalDateTime dateTime) { - assert dateTime != null : "Date time to be converted into string should not be null"; - return dateTime.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); + public void printNewFood(String newFood) { + performAssertionsForStringInputs(newFood, + "String representation of the food that was added"); + + print("Got it! I've added this food item:" + LINE_SEPARATOR + + " " + trimString(newFood)); } /** - * Prints all the food in the database sorted by the canteen and then the store it is found. + * Prints a message to show that the food specified has been deleted from the food list. * - * @param foodDatabase The string representation of all the food items stored in the database. + * @param deletedFood The string representation of the food that was deleted from the food list. */ - public void printDatabase(String foodDatabase) { - assert foodDatabase != null : "Food database should not be null"; - assert trimStringGetLength(foodDatabase) > 0 : "Food database should not be empty"; - print("Here are the food items in the database:" + LINE_SEPARATOR + trimString(foodDatabase)); + public void printDeletedFood(String deletedFood) { + performAssertionsForStringInputs(deletedFood, + "String representation of the food that was deleted"); + + print("Noted. I've removed this food item:" + LINE_SEPARATOR + + " " + trimString(deletedFood)); } /** @@ -179,75 +263,227 @@ public void printClearFoodListMessage() { + "DietBook is now empty."); } + // Methods required to print nutritional intake and recommendation related commands and messages. + + /** + * Prints the daily recommended calorie intake of the user based on the user's personal information. + * + * @param calorieRecommendation The daily recommended calorie intake of the user. + */ + public void printCalorieRecommendation(String name, int calorieRecommendation) { + performAssertionsForStringInputs(name, "Name"); + performAssertionsForCalorieRecommendation(calorieRecommendation); + + print("Hi " + trimString(name) + "!" + LINE_SEPARATOR + + "Here is your daily recommended calorie intake: " + calorieRecommendation + "kcal"); + } + + /** + * Prints the total amount of carbohydrates consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total carbohydrate intake. * - * @param personInfo The user's personal information. + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. */ - public void printPersonInfo(String personInfo) { - assert personInfo != null : "Person information should not be null"; - assert trimStringGetLength(personInfo) > 0 : "Person information should not be an empty string"; - print("Here is your information:" + LINE_SEPARATOR - + trimString(personInfo)); + public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods) { + print(stringOneIntakeAndFoodsWithoutTime(carbIntake,"carbohydrate", + "g", recalculatedFoods)); } /** - * Prints the total amount of carbohydrates consumed by the user. + * Prints the total amount of carbohydrates consumed by the user within a given time period and a list of + * the foods recorded into the food list during the same time period which had their nutritional + * information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total carbohydrate + * intake within a given time period. * - * @param carbohydrateIntake The total amount of carbohydrates of all the food in the food list. + * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. */ - public void printCarbohydrateIntake(int carbohydrateIntake) { - assert carbohydrateIntake >= 0 : "Total carbohydrate intake should be equals to or greater than 0"; - print("Total carbohydrate intake: " + carbohydrateIntake + "g"); + public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String carbIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(carbIntake, + "carbohydrate", "g", recalculatedFoods); + print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); } /** - * Prints the total amount of calories consumed by the user. + * Prints the total amount of calories consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total calorie intake. * * @param calorieIntake The total amount of calories of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods) { + print(stringOneIntakeAndFoodsWithoutTime(calorieIntake,"calorie","kcal", + recalculatedFoods)); + } + + /** + * Prints the total amount of calories consumed by the user within a given time period and a list of + * the foods recorded into the food list during the same time period which had their nutritional + * information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total calorie + * intake within a given time period. + * + * @param calorieIntake The total amount of calories of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. */ - public void printCalorieIntake(int calorieIntake) { - assert calorieIntake >= 0 : "Total calorie intake should be equals to or greater than 0"; - print("Total calorie intake: " + calorieIntake + "kcal"); + public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String calorieIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(calorieIntake, + "calorie", "kcal", recalculatedFoods); + print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); } /** - * Prints the total amount of proteins consumed by the user. + * Prints the total amount of proteins consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total protein intake. * * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods) { + print(stringOneIntakeAndFoodsWithoutTime(proteinIntake,"protein","g", + recalculatedFoods)); + } + + /** + * Prints the total amount of proteins consumed by the user within a given time period and a list of + * the foods recorded into the food list during the same time period which had their nutritional + * information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total protein + * intake within a given time period. + * + * @param proteinIntake The total amount of proteins of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. */ - public void printProteinIntake(int proteinIntake) { - assert proteinIntake >= 0 : "Total protein intake should be equals to or greater than 0 "; - print("Total protein intake: " + proteinIntake + "g"); + public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String proteinIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(proteinIntake, + "protein", "g", recalculatedFoods); + print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); } /** - * Prints the total amount of fats consumed by the user. + * Prints the total amount of fats consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total fat intake. * * @param fatIntake The total amount of fats of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods) { + print(stringOneIntakeAndFoodsWithoutTime(fatIntake,"fat","g", + recalculatedFoods)); + } + + /** + * Prints the total amount of fats consumed by the user within a given time period and a list of + * the foods recorded into the food list during the same time period which had their nutritional + * information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total fat + * intake within a given time period. + * + * @param fatIntake The total amount of fats of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. */ - public void printFatIntake(int fatIntake) { - assert fatIntake >= 0 : "Total fat intake should be equals to or greater than 0"; - print("Total fat intake: " + fatIntake + "g"); + public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String fatIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(fatIntake, + "fat", "g", recalculatedFoods); + print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); } /** - * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user. + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and the + * list of food items which had their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating the individual intakes. * + * @param carbIntake The total amount of carbohydrates of all the food in the food list. * @param calorieIntake The total amount of calories of all the food in the food list. - * @param carbohydrateIntake The total amount of carbohydrates of all the food in the food list. * @param proteinIntake The total amount of proteins of all the food in the food list. * @param fatIntake The total amount of fats of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, String recalculatedFoods) { + print(stringAllIntakeAndFoodsWithoutTime(carbIntake, calorieIntake,proteinIntake, + fatIntake, recalculatedFoods)); + } + + /** + * Prints the total amount of total amount of calories, carbohydrates, fats and proteins consumed by + * the user within a given time period and a list of the foods recorded into the food list during the + * same time period which had their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating the individual intake + * within a given time period. + * + * @param calorieIntake The total amount of calories of food in the food list recorded during the + * time period given. + * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the + * time period given. + * @param proteinIntake The total amount of proteins of food in the food list recorded during the + * time period given. + * @param fatIntake The total amount of fats of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. */ - public void printAllNutrientIntake(int calorieIntake, int carbohydrateIntake, int proteinIntake, - int fatIntake) { - assert carbohydrateIntake >= 0 : "Total carbohydrate intake should be equals to or greater than 0"; - assert calorieIntake >= 0 : "Total calorie intake should be equals to or greater than 0"; - assert proteinIntake >= 0 : "Total protein intake should be equals to or greater than 0 "; - assert fatIntake >= 0 : "Total fat intake should be equals to or greater than 0"; - - print("Total calorie intake: " + calorieIntake + "kcal" + LINE_SEPARATOR - + "Total carbohydrate intake: " + carbohydrateIntake + "g" + LINE_SEPARATOR - + "Total protein intake: " + proteinIntake + "g" + LINE_SEPARATOR - + "Total fat intake: " + fatIntake + "g"); + public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(carbIntake, calorieIntake, + proteinIntake,fatIntake, recalculatedFoods); + print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); + } + // Helper methods for system related commands or messages /** @@ -326,16 +562,168 @@ private String getFoodListRelatedCommands() { } /** - * Prints an error message given what or where the error is. + * Returns a string representation of a list of database related commands that users can input. * - * @param errorMessage Message detailing what or where the error is. + * @return A string representation of a list of database related commands that users can input. */ - public void printErrorMessage(String errorMessage) { - assert errorMessage != null : "Error message should not be null"; - assert trimStringGetLength(errorMessage) > 0 : "Error message should not be an empty string"; - print(":( Oh no..." + trimString(errorMessage)); + private String getDatabaseRelatedCommands() { + return " To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR + + " To view all food in the database: data" + LINE_SEPARATOR; } + // Helper methods for calculator related commands and messages + + /** + * Returns a string with a header and recalculatedFoods or a string stating that no food items had their + * nutritional information recalculated if calculatedFoods is an empty string. + * + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + * @return A string with a header and recalculatedFoods or a string stating that no food items had their + * nutritional information recalculated if calculatedFoods is an empty string. + */ + private String recalculatedFoodsMessage(String recalculatedFoods) { + String message = "No food items had their nutritional information recalculated by DietBook."; + if (trimStringGetLength(recalculatedFoods) > 0) { + message = "Food items which had their nutritional information recalculated by DietBook: " + + LINE_SEPARATOR + recalculatedFoods; + } + return message; + } + + /** + * Return a string representation of the amount of a nutrient consumed by the user which can be either + * the total amount consumed or amount consumed in a given time period. + * + * @param nutrientIntake The amount of a particular type of nutrient consumed. + * @param nutrientType A string representation of the type of nutrient consumed. + * @param nutrientUnit A string representation of the unit of the nutrient consumed. + * @return The amount of a nutrient consumed by the user which can be either the total amount consumed + * or amount consumed in a given time period. + */ + private String stringNutritionalIntake(int nutrientIntake, String nutrientType, String nutrientUnit) { + return "Total " + nutrientType + " intake: " + nutrientIntake + nutrientUnit; + } + + /** + * Returns a string representation of the total amount of a nutrient consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + * + * @param nutrientIntake The amount of a particular type of nutrient consumed. + * @param nutrientType A string representation of the type of nutrient consumed. + * @param nutrientUnit A string representation of the unit of the nutrient consumed. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + * @return A string representation of the the total amount of a nutrient consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + */ + private String stringOneIntakeAndFoodsWithoutTime(int nutrientIntake, String nutrientType, + String nutrientUnit, String recalculatedFoods) { + performAssertionsForStringInputs(nutrientType,"Nutrient Type"); + performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); + performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); + performAssertionsForNullStringInputs(recalculatedFoods, + "List of foods that had their nutritional information recalculated"); + + String stringNutrientIntake = stringNutritionalIntake(nutrientIntake, nutrientType, nutrientUnit); + String message = recalculatedFoodsMessage(recalculatedFoods); + return stringNutrientIntake + LINE_SEPARATOR + message; + } + + /** + * Returns a string representation of the total amount of a nutrient or all nutrientS consumed by the + * user during a given time period and the list of food items recorded during the same time period + * which had their nutritional information recalculated by DietBook if any. + * + * @param intakeAndFoodsWithoutTime A string representation of the the total amount of a nutrient or + * all nutrients consumed by the user and the list of food items which had their nutritional + * information recalculated by DietBook if any. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + * @return A string representation of the the total amount of a nutrient or all nutrient consumed by the + * user during a given time period and the list of food items recorded during the same time period + * which had their nutritional information recalculated by DietBook if any. + */ + private String stringIntakeAndFoodsWithTime(String intakeAndFoodsWithoutTime, + LocalDateTime start, LocalDateTime end) { + performAssertionsForTimePeriod(start, end); + + String timePeriod = "Time period:" + stringDateTimePeriod(start, end); + return timePeriod + LINE_SEPARATOR + LINE_SEPARATOR + intakeAndFoodsWithoutTime; + } + + /** + * Returns a string representation of the total amount of all nutrients consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + * + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + * @param calorieIntake The total amount of calories of all the food in the food list. + * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param fatIntake The total amount of fats of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + * @return A string representation of the total amount of all nutrients consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + */ + private String stringAllIntakeAndFoodsWithoutTime(int carbIntake, int calorieIntake, int proteinIntake, + int fatIntake, String recalculatedFoods) { + performAssertionsForNutritionalIntake(carbIntake, "carbohydrate"); + performAssertionsForNutritionalIntake(calorieIntake, "calorie"); + performAssertionsForNutritionalIntake(proteinIntake, "protein"); + performAssertionsForNutritionalIntake(fatIntake, "fat"); + performAssertionsForNullStringInputs(recalculatedFoods, + "List of foods that had their nutritional information recalculated"); + + String stringCarbIntake = stringNutritionalIntake(carbIntake,"carbohydrate", "g"); + String stringCalorieIntake = stringNutritionalIntake(calorieIntake,"calorie", + "kcal"); + String stringProteinIntake = stringNutritionalIntake(proteinIntake,"protein", "g"); + String stringFatIntake = stringNutritionalIntake(fatIntake,"fat", "g"); + String message = recalculatedFoodsMessage(recalculatedFoods); + + return stringCarbIntake + LINE_SEPARATOR + + stringCalorieIntake + LINE_SEPARATOR + + stringProteinIntake + LINE_SEPARATOR + + stringFatIntake + LINE_SEPARATOR + + message; + + } + + // Other helper methods + + /** + * Prints the given message to the user. + * + * @param message The message to show the user. + */ + private void print(String message) { + performAssertionsForStringInputs(message, "Message to print"); + String divider = + "__________________________________________________________________________________________" + + "___________________________________________"; + + System.out.println(divider + LINE_SEPARATOR + + trimString(message) + LINE_SEPARATOR + + divider); + + } + + /** + * Returns a string representation of the time period with date time in the format dd MMM yyyy HHmm. + * + * @param start Starting date time of the time period given that needs to be converted into a String. + * @param end Ending date time of the time period given that needs to be converted into a String. + * @return The string representation of time period with date time in the format dd MMM yyyy HHmm. + */ + public String stringDateTimePeriod(LocalDateTime start, LocalDateTime end) { + performAssertionsForTimePeriod(start, end); + + String stringStart = start.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); + String stringEnd = end.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); + return " between " + stringStart + " and " + stringEnd; + } + + /** * Returns an integer representing the length of the string after it has been trimmed for leading and * trailing spaces. @@ -345,7 +733,8 @@ public void printErrorMessage(String errorMessage) { * trailing spaces. */ public int trimStringGetLength(String string) { - assert string != null : "String to trim and have length determined should not be null"; + performAssertionsForNullStringInputs(string, "String to trim and have length determined"); + return trimString(string).length(); } @@ -355,26 +744,71 @@ public int trimStringGetLength(String string) { * @param string The string to be trimmed for leading and trailing spaces. * @return A string that has been trimmed for leading and trailing spaces. */ - public String trimString(String string) { - assert string != null : "String to trim should not be null"; + private String trimString(String string) { + performAssertionsForNullStringInputs(string, "String to trim"); + return string.trim(); } /** - * Prints the given message to the user. + * Performs assertions for the string inputs. * - * @param message The message to show the user. + * @param string The input value. + * @param stringDescription A description of what the input value represents. */ - public void print(String message) { - assert message != null : "Message to print should not be null"; - assert trimStringGetLength(message) > 0 : "Message to print should not be an empty string"; - String divider = - "__________________________________________________________________________________________" - + "____________________"; + private void performAssertionsForStringInputs(String string, String stringDescription) { + performAssertionsForNullStringInputs(string, stringDescription); + assert trimStringGetLength(string) > 0 : stringDescription + " should not be an empty string"; + } - System.out.println(divider + LINE_SEPARATOR - + trimString(message) + LINE_SEPARATOR - + divider); + /** + * Performs assertions for the time inputs. + * + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + private void performAssertionsForTimePeriod(LocalDateTime start, LocalDateTime end) { + assert start != null : "Starting date time of the time period given should not be null"; + assert end != null : "Ending date time of the time period given should not be null"; + assert !start.isAfter(end) : "Starting date time should not be later than ending date time " + + "of the time period"; + assert start.isBefore(LocalDateTime.now()) : "Starting date time of the time period given should " + + "not be in the future"; + assert end.isBefore(LocalDateTime.now()) : "Ending date time of the time period given should not be" + + " in the future"; + } + + /** + * Performs assertions for null string inputs. + * + * @param string The input value. + * @param stringDescription A description of what the input value represents. + */ + private void performAssertionsForNullStringInputs(String string, String stringDescription) { + assert string != null : stringDescription + " should not be null"; + } + + /** + * Performs assertions for nutritional intake inputs. + * + * @param nutrientIntake The nutritional intake value. + * @param nutrientType The nutrient type. + */ + private void performAssertionsForNutritionalIntake(int nutrientIntake, String nutrientType) { + assert nutrientIntake >= 0 : "Total " + nutrientType + " intake should be equals to or greater than 0"; + } + /** + * Performs assertions for the calorie recommendation input. + * + * @param calorieRecommendation The recommended daily calorie intake for the user. + */ + private void performAssertionsForCalorieRecommendation(int calorieRecommendation) { + // A minimum daily intake of 1200 calorie is required to stay healthy. + assert calorieRecommendation >= 1200 : "Daily calorie recommendation should be equals to or greater" + + " than 1200"; + // Highest calorie intake for an athlete currently stands at 12000. + assert calorieRecommendation <= 12000 : "Daily calorie recommendation should be equals to or less " + + "than 12,000"; } } From 367c9cd7d67b4f24e7016646746cf0b911fa85e6 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:08:54 +0800 Subject: [PATCH 141/374] Update JUnit test for Ui class --- src/test/java/seedu/dietbook/UiTest.java | 72 +++++++++++++++--------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/test/java/seedu/dietbook/UiTest.java b/src/test/java/seedu/dietbook/UiTest.java index 35314fb5f5..b69ebcfbe3 100644 --- a/src/test/java/seedu/dietbook/UiTest.java +++ b/src/test/java/seedu/dietbook/UiTest.java @@ -18,69 +18,85 @@ public void setUp() { } @Test - void stringDateTime_nullInput_expectAssertionError() { - assertThrows(AssertionError.class, () -> ui.stringDateTime(null)); + void stringDateTimePeriod_startDateTimeIsNullInput_expectAssertionError() { + LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59"); + assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(null, end)); } @Test - void stringDateTime_LocalDateTime_returnsStringOfLocalDateTime() { - LocalDateTime dateTime = LocalDateTime.parse("2020-10-21T23:59"); - assertEquals("21 Oct 2020 2359",ui.stringDateTime(dateTime)); + void stringDateTimePeriod_endDateTimeIsNullInput_expectAssertionError() { + LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); + assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, null)); } @Test - void stringDateTime_LocalDateTimeWithSeconds_returnsStringOfLocalDateTimeWithoutSeconds() { - LocalDateTime dateTime = LocalDateTime.parse("2020-10-21T23:59:22"); - assertEquals("21 Oct 2020 2359",ui.stringDateTime(dateTime)); + void stringDateTimePeriod_endDateTimeIsBeforeStartTime_expectAssertionError() { + LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); + LocalDateTime end = LocalDateTime.parse("2020-10-20T23:59"); + assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, end)); } @Test - void trimStringGetLength_nullInput_expectAssertionError() { - assertThrows(AssertionError.class, () -> ui.trimStringGetLength(null)); + void stringDateTimePeriod_endDateTimeIsInTheFuTure_expectAssertionError() { + LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); + LocalDateTime end = LocalDateTime.now().plusDays(3); + assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, end)); } @Test - void trimStringGetLength_stringWithNoLeadingOrTrailingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength("food")); + void stringDateTimePeriod_StartDateTimeIsInTheFuture_expectAssertionError() { + LocalDateTime start = LocalDateTime.now().plusDays(3); + LocalDateTime end = LocalDateTime.now().plusDays(5); + assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, end)); } + @Test - void trimStringGetLength_StringWithLeadingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength(" food")); + void stringDateTimePeriod_sameStartAndEndDateTime_returnsStringOfTimePeriod() { + LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); + LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59"); + assertEquals(" between 21 Oct 2020 2359 and 21 Oct 2020 2359", + ui.stringDateTimePeriod(start, end)); } @Test - void trimStringGetLength_StringWithTrailingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength("food ")); + void stringDateTimePeriod_validStartAndEndDateTime_returnsStringOfTimePeriod() { + LocalDateTime start = LocalDateTime.parse("2020-10-19T23:59"); + LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59"); + assertEquals(" between 19 Oct 2020 2359 and 21 Oct 2020 2359", + ui.stringDateTimePeriod(start, end)); } @Test - void trimStringGetLength_StringWithLeadingAndTrailingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength(" food ")); + void stringDateTimePeriod_validStartAndEndDateTimeWithSeconds_returnsStringOfTimePeriodWithoutSeconds() { + LocalDateTime start = LocalDateTime.parse("2020-10-19T23:59:22"); + LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59:22"); + assertEquals(" between 19 Oct 2020 2359 and 21 Oct 2020 2359", + ui.stringDateTimePeriod(start, end)); } @Test - void trimString_nullInput_expectAssertionError() { - assertThrows(AssertionError.class, () -> ui.trimString(null)); + void trimStringGetLength_nullInput_expectAssertionError() { + assertThrows(AssertionError.class, () -> ui.trimStringGetLength(null)); } @Test - void trimString_StringWithLeadingSpaces_returnsStringWithoutLeadingSpaces() { - assertEquals("food", ui.trimString(" food")); + void trimStringGetLength_stringWithNoLeadingOrTrailingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength("food")); } @Test - void trimString_StringWithTrailingSpaces_returnsStringWithoutTrailingSpaces() { - assertEquals("food", ui.trimString("food ")); + void trimStringGetLength_StringWithLeadingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength(" food")); } @Test - void trimString_StringWithLeadingAndTrailingSpaces_returnsStringWithoutLeadingAndTrailingSpaces() { - assertEquals("food", ui.trimString(" food ")); + void trimStringGetLength_StringWithTrailingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength("food ")); } @Test - void trimString_StringWithNoLeadingAndTrailingSpaces_returnsString() { - assertEquals("food", ui.trimString("food")); + void trimStringGetLength_StringWithLeadingAndTrailingSpaces_returnsLengthFour() { + assertEquals(4, ui.trimStringGetLength(" food ")); } } From 1263c9953ec81b4f227d9d72da1261767d3656fb Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:19:30 +0800 Subject: [PATCH 142/374] Update Command Classes --- .../seedu/dietbook/command/CalculateCommand.java | 15 ++++++++++----- .../java/seedu/dietbook/command/DataCommand.java | 2 +- .../java/seedu/dietbook/command/HelpCommand.java | 2 +- .../java/seedu/dietbook/command/InfoCommand.java | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index 6884bf315b..babeab4194 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -23,19 +23,24 @@ public void execute(Manager manager, Ui ui) { manager.setCalculator(); switch (this.param) { case "all": - ui.printAllNutrientIntake(this.calorie, this.carb, this.protein, this.fat); + //ui.printAllIntakeAndFoods(); + //ui.printAllNutrientIntake(this.calorie, this.carb, this.protein, this.fat); break; case "calorie": - ui.printCalorieIntake(this.calorie); + //ui.printCalorieIntakeAndFoods(); + //ui.printCalorieIntake(this.calorie); break; case "carbohydrate": - ui.printCarbohydrateIntake(this.carb); + //ui.printCarbIntakeAndFoods(); + //ui.printCarbohydrateIntake(this.carb); break; case "protein": - ui.printProteinIntake(this.protein); + //ui.printProteinIntakeAndFoods(); + //ui.printProteinIntake(this.protein); break; default: - ui.printFatIntake(this.fat); + //ui.printFatIntakeAndFoods(); + //ui.printFatIntake(this.fat); } } } diff --git a/src/main/java/seedu/dietbook/command/DataCommand.java b/src/main/java/seedu/dietbook/command/DataCommand.java index 12db1c7202..deb0767b9a 100644 --- a/src/main/java/seedu/dietbook/command/DataCommand.java +++ b/src/main/java/seedu/dietbook/command/DataCommand.java @@ -8,6 +8,6 @@ public class DataCommand extends Command { @Override public void execute(Manager manager, Ui ui) { manager.getDataBase().init(); - ui.printDatabase(manager.getDataBase().getFoodList()); + //ui.printDatabase(manager.getDataBase().getFoodList()); } } diff --git a/src/main/java/seedu/dietbook/command/HelpCommand.java b/src/main/java/seedu/dietbook/command/HelpCommand.java index 30a57a593f..733cb3e3bb 100644 --- a/src/main/java/seedu/dietbook/command/HelpCommand.java +++ b/src/main/java/seedu/dietbook/command/HelpCommand.java @@ -6,6 +6,6 @@ public class HelpCommand extends Command { @Override public void execute(Manager manager, Ui ui) { - ui.printTutorialMessage(); + ui.printHelpCommandMessage(); } } diff --git a/src/main/java/seedu/dietbook/command/InfoCommand.java b/src/main/java/seedu/dietbook/command/InfoCommand.java index c189c46be3..8a7f215b13 100644 --- a/src/main/java/seedu/dietbook/command/InfoCommand.java +++ b/src/main/java/seedu/dietbook/command/InfoCommand.java @@ -15,6 +15,6 @@ public InfoCommand(String userInput) { @Override public void execute(Manager manager, Ui ui) throws DietException { Parser.executeProcessedInfo(this.userInput, manager); - ui.printTutorialMessage(); + ui.printInitialisationCompleteMessage(); } } From e3dec2a1867cce23ba7740bb0bbee83fd5c12d78 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:28:47 +0800 Subject: [PATCH 143/374] Update I/O testing --- text-ui-test/EXPECTED.TXT | 32 ++++++++++++++++++++++++++++---- text-ui-test/input.txt | 2 ++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index f47f7db415..da9d65fe70 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,5 +1,5 @@ -________________________________________________________________________________________________________________________________________________ - _______ __ ______ ________ _______ ______ ______ __ __ +_____________________________________________________________________________________________________________________________________ +_______ __ ______ ________ _______ ______ ______ __ __ | __ \| | ___|__ __| __ \ / __ \ / __ \| | / / | | | | | |___ | | | |__| | | | | | | | |/ / | | | | | ___| | | | __ <| | | | | | | / @@ -10,5 +10,29 @@ Hello! Welcome to DietBook! I am Diet, your guide to using DietBook. What is your name? Please input in the following format: name YOUR_NAME -________________________________________________________________________________________________________________________________________________ -__________________ + Example: name Jack +_____________________________________________________________________________________________________________________________________ +_____________________________________________________________________________________________________________________________________ +Hi Jack! +Before we get started, I would like to know about about you so that I can make more +accurate calculations for you :). Therefore, could you please share with me the following: +- Your gender either F for female or M for male or O for others. +- Your age which is a positive integer. +- Your height in cm. +- Your original weight in kg, the weight when you first started using DietBook or you current weight. +- Your current weight in kg. +- Your target weight in kg, or your current weight if that is also your target weight. +- Your activity level, represented by a number from 1 to 5. + 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. + 2 = You engage in some form of light exercise or have a job that requires some physical activity. + 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. + 4 = You engage in vigorous exercise or have a physically demanding job. + 5 = You engage in extremely vigorous exercise or have an extremely physically demanding job. + +Please input your details in the following format: + info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL + Example: info g/F a/21 h/165 o/65 c/65 t/55 l/2 +_____________________________________________________________________________________________________________________________________ +_____________________________________________________________________________________________________________________________________ +Bye Jack! Hope to see you again soon! +_____________________________________________________________________________________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..002f71500b 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,2 @@ +name Jack +exit \ No newline at end of file From 77776c385dcd4fcd6e660e9897d61f2014b859e3 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:41:08 +0800 Subject: [PATCH 144/374] Update I/O testing --- text-ui-test/EXPECTED.TXT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index da9d65fe70..61ea9ffb41 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -14,8 +14,8 @@ Please input in the following format: _____________________________________________________________________________________________________________________________________ _____________________________________________________________________________________________________________________________________ Hi Jack! -Before we get started, I would like to know about about you so that I can make more -accurate calculations for you :). Therefore, could you please share with me the following: +Before we get started, I would like to know about about you. +Therefore, could you please share with me the following so I can make more accurate calculations for you :) - Your gender either F for female or M for male or O for others. - Your age which is a positive integer. - Your height in cm. From e8f0c5d2137aae7a63ad9ed21e87dbf59e78e5a8 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 21:53:58 +0800 Subject: [PATCH 145/374] Update I/O testing --- text-ui-test/EXPECTED.TXT | 23 +---------------------- text-ui-test/input.txt | 1 - 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 61ea9ffb41..01a5413702 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -13,26 +13,5 @@ Please input in the following format: Example: name Jack _____________________________________________________________________________________________________________________________________ _____________________________________________________________________________________________________________________________________ -Hi Jack! -Before we get started, I would like to know about about you. -Therefore, could you please share with me the following so I can make more accurate calculations for you :) -- Your gender either F for female or M for male or O for others. -- Your age which is a positive integer. -- Your height in cm. -- Your original weight in kg, the weight when you first started using DietBook or you current weight. -- Your current weight in kg. -- Your target weight in kg, or your current weight if that is also your target weight. -- Your activity level, represented by a number from 1 to 5. - 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. - 2 = You engage in some form of light exercise or have a job that requires some physical activity. - 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. - 4 = You engage in vigorous exercise or have a physically demanding job. - 5 = You engage in extremely vigorous exercise or have an extremely physically demanding job. - -Please input your details in the following format: - info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL - Example: info g/F a/21 h/165 o/65 c/65 t/55 l/2 -_____________________________________________________________________________________________________________________________________ -_____________________________________________________________________________________________________________________________________ -Bye Jack! Hope to see you again soon! +Bye John Doe! Hope to see you again soon! _____________________________________________________________________________________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 002f71500b..ae3bc0a936 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,2 +1 @@ -name Jack exit \ No newline at end of file From 5ca5ddc47ad88b40e0f9b65f8f5647b06bef0cf8 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Sat, 24 Oct 2020 22:38:31 +0800 Subject: [PATCH 146/374] Update Gender --- src/main/java/seedu/dietbook/parser/Parser.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 59283b0353..1e505e4fc9 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -145,10 +145,10 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw case "g/": String processGender = trimmedParam; InputChecker.checkGender(processGender); - if (processGender.equals("M")) { - gender = Gender.MALE; - } else { + if (processGender.equals("F")) { gender = Gender.FEMALE; + } else { + gender = Gender.OTHERS; } break; case "a/": From 9a9069e0db94e2d7d6a9f8b073cfa4af1aa2bb6e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 23:47:59 +0800 Subject: [PATCH 147/374] Update person related code in manager --- src/main/java/seedu/dietbook/Manager.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index ad4c9be9f9..a5885bddfb 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -53,7 +53,8 @@ public class Manager { public Manager(FoodList foodlist, DataBase dataBase) { this.name = "John Doe"; - this.person = new Person(this.name, Gender.MALE, 0,0,0,0, ActivityLevel.LOW); + this.person = new Person(this.name, Gender.MALE, 1,1,1,1, + 1, ActivityLevel.LOW); this.foodList = foodlist; this.dataBase = dataBase; this.calculator = new Calculator(foodList.getFoods()); @@ -71,9 +72,9 @@ public Person getPerson() { return this.person; } - public void setPerson(String name, Gender gender, int age,int height,int orgWeight, + public void setPerson(String name, Gender gender, int age,int height,int orgWeight, int currWeight, int targWeight, ActivityLevel actLvl) { - this.person = new Person(name, gender, age, height, orgWeight, targWeight, actLvl); + this.person = new Person(name, gender, age, height, orgWeight, currWeight, targWeight, actLvl); } public Calculator getCalculator() { From a8a61538ce9d397cf7fd624fb62b3d7aba701597 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 23:48:32 +0800 Subject: [PATCH 148/374] Commented out a line that requires changes --- src/main/java/seedu/dietbook/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 59283b0353..3c90078293 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -184,7 +184,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw break; } } - manager.setPerson(manager.getName(), gender, age, height, orgWeight, tarWeight, actLvl); + //manager.setPerson(manager.getName(), gender, age, height, orgWeight, tarWeight, actLvl); } /** From e3b0935c1f0fd86ab3139dd3448ff09fa387e747 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 23:49:05 +0800 Subject: [PATCH 149/374] Update printExitMessage method --- src/main/java/seedu/dietbook/Ui.java | 6 ++---- src/main/java/seedu/dietbook/command/ExitCommand.java | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 64e20166f0..1cde8236f3 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -83,11 +83,9 @@ public void printAskForUserInfoMessage(String name) { /** * Prints an exit message when DietBook is closed. * - * @param name The name of the user. */ - public void printExitMessage(String name) { - performAssertionsForStringInputs(name, "Name"); - print("Bye " + trimString(name) + "! Hope to see you again soon!"); + public void printExitMessage() { + print("Bye! Hope to see you again soon!"); } /** diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java index 9f5b19d0dd..31b14601e7 100644 --- a/src/main/java/seedu/dietbook/command/ExitCommand.java +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -7,7 +7,7 @@ public class ExitCommand extends Command { @Override public void execute(Manager manager, Ui ui) { - ui.printExitMessage(manager.getName()); + ui.printExitMessage(); DietBook.isExit = true; } } From 3ce5776def429e353a594dcc0ac9cce4f87a0cbc Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 24 Oct 2020 23:50:11 +0800 Subject: [PATCH 150/374] Change the maximum age a person can have --- src/main/java/seedu/dietbook/checker/InputChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 53772478f4..8741b69586 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -13,7 +13,7 @@ public class InputChecker { /** * The value limits are based on current limits observed in th world. */ - public static final int AGE_CAP = 125; + public static final int AGE_CAP = 123; public static final int FOOD_CAP = 100000; public static final int HEIGHT_CAP = 273; public static final int WEIGHT_CAP = 443; From 988229693f433f55277d020d6d6b4ac10f1a2594 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 00:07:45 +0800 Subject: [PATCH 151/374] Add current weight parameter and did refactoring --- .../java/seedu/dietbook/person/Person.java | 184 +++++++++++++++--- 1 file changed, 154 insertions(+), 30 deletions(-) diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index eb85de0f98..fa695a9cf3 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -8,8 +8,10 @@ public class Person { /* The height of the person in cm */ private int height; - /* The original weight of the person in kg */ + /* The original weight of the person when he or she first started using DietBook in kg */ private int originalWeight; + /* The current weight of the person in kg */ + private int currentWeight; /* The target weight of the person in kg */ private int targetWeight; private int age; @@ -18,42 +20,60 @@ public class Person { private String name; /** - * Constructs a Person with the given name, gender, age, height, original weight, target - * weight and activity level. + * Constructs a Person with the given name, gender, age, height, activity level, original, + * current and target weight. * * @param name The name of the person. * @param gender The gender of the person. * @param age The age of the person. * @param height The height of the person. - * @param originalWeight The original weight of the person. + * @param originalWeight The original weight of the person when he or she first started using DietBook. + * @param currentWeight The current weight of the person. * @param targetWeight The target/desired weight that the person wants to achieve. * @param activityLevel The activity level of the person or in other words, the amount of exercise the * person engages in. */ - public Person(String name, Gender gender, int age, int height, int originalWeight, int targetWeight, - ActivityLevel activityLevel) { - assert name != null : "Name of person should not be null"; - assert name.trim().length() > 0 : "Name of person should not be an empty string"; - assert gender != null : "Gender of person should not be null"; - assert age > 0 : "Age of person should be greater than 0"; - assert age < 125 : "Age of person should be less than 125"; - assert height > 0 : "Height of person should be greater than 0"; - assert height < 273 : "Height of person should be less than 273"; - assert originalWeight > 0 : "Original weight of person should be greater than 0"; - assert originalWeight < 443 : "Original weight of person should be less than 443"; - assert targetWeight > 0 : "Target weight of person should be greater than 0"; - assert targetWeight < 443 : "Target weight of person should be less than 443"; - assert activityLevel != null : "Activity level of person should not be null"; + public Person(String name, Gender gender, int age, int height, int originalWeight, + int currentWeight, int targetWeight, ActivityLevel activityLevel) { + performAssertionsForPerson(name, gender, age, height, originalWeight, currentWeight, + targetWeight, activityLevel); this.name = name.trim(); this.gender = gender; this.age = age; this.height = height; this.originalWeight = originalWeight; + this.currentWeight = currentWeight; this.targetWeight = targetWeight; this.activityLevel = activityLevel; } + /** + * Sets all the attributes of a person to the new attributes given. + * + * @param newName The new/revised name of the person. + * @param newGender The new/revised gender of the person. + * @param newAge The new/revised age of the person. + * @param newHeight The new/revised height of the person. + * @param newOriginalWeight The new/revised original weight of the person when he or she first started + * using DietBook. + * @param newCurrentWeight The new/revised current weight of the person. + * @param newTargetWeight The new/revised target weight that the person wants to achieve. + * @param newActivityLevel The new/revised activity level of the person or in other words, the amount + * of exercise the person engages in. + */ + public void setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, + int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) { + setName(newName); + setGender(newGender); + setAge(newAge); + setHeight(newHeight); + setOriginalWeight(newOriginalWeight); + setCurrentWeight(newCurrentWeight); + setTargetWeight(newTargetWeight); + setActivityLevel(newActivityLevel); + } + /** * Returns the name of the person. * @@ -69,8 +89,7 @@ public String getName() { * @param newName The new/revised name of the person. */ public void setName(String newName) { - assert newName != null : "The revised name of person should not be null"; - assert newName.trim().length() > 0 : "The revised name of person should not be an empty string"; + performAssertionsForNameInput(newName); name = newName.trim(); } @@ -89,7 +108,7 @@ public Gender getGender() { * @param newGender The new/revised gender of the person. */ public void setGender(Gender newGender) { - assert newGender != null : "The revised gender of person should not be null"; + performAssertionsForGenderInput(newGender); gender = newGender; } @@ -108,8 +127,7 @@ public int getAge() { * @param newAge The new/revised age of the person. */ public void setAge(int newAge) { - assert newAge > 0 : "The revised age of person should be greater than 0"; - assert newAge < 125 : "The revised age of person should be lesser than 125"; + performAssertionsForAgeInput(newAge); age = newAge; } @@ -128,8 +146,7 @@ public int getHeight() { * @param newHeight The new/revised height of the person. */ public void setHeight(int newHeight) { - assert newHeight > 0 : "The revised height of person should be greater than 0"; - assert newHeight < 273 : "The revised height of person should be lesser than 273"; + performAssertionsForHeight(newHeight); height = newHeight; } @@ -148,11 +165,29 @@ public int getOriginalWeight() { * @param newOriginalWeight The new/revised original weight of the person. */ public void setOriginalWeight(int newOriginalWeight) { - assert newOriginalWeight > 0 : "The revised original weight of person should be greater than 0"; - assert newOriginalWeight < 443 : "The revised original weight of person should be lesser than 443"; + performAssertionsForWeight(newOriginalWeight,"Original weight"); originalWeight = newOriginalWeight; } + /** + * Returns the current weight of the person when he or she first started using DietBook. + * + * @return The current weight of the person when he or she first started using DietBook. + */ + public int getCurrentWeight() { + return currentWeight; + } + + /** + * Sets the current weight of the person to the new current weight given. + * + * @param newCurrentWeight The new/revised current weight of the person. + */ + public void setCurrentWeight(int newCurrentWeight) { + performAssertionsForWeight(newCurrentWeight, "Current weight"); + currentWeight = newCurrentWeight; + } + /** * Returns the target weight the person the person wants to achieve. * @@ -168,8 +203,7 @@ public int getTargetWeight() { * @param newTargetWeight The new/revised target weight of the person. */ public void setTargetWeight(int newTargetWeight) { - assert newTargetWeight > 0 : "The revised target weight of person should be greater than 0"; - assert newTargetWeight < 443 : "The revised target weight of person should be lesser than 443"; + performAssertionsForWeight(newTargetWeight, "Target weight"); targetWeight = newTargetWeight; } @@ -188,7 +222,7 @@ public ActivityLevel getActivityLevel() { * @param newActivityLevel The new/revised activity level of the person. */ public void setActivityLevel(ActivityLevel newActivityLevel) { - assert newActivityLevel != null : "The revised activity level of person should not be null"; + performAssertionsForActivityLevel(newActivityLevel); activityLevel = newActivityLevel; } @@ -205,8 +239,98 @@ public String toString() { + " Age: " + age + System.lineSeparator() + " Height: " + height + "cm" + System.lineSeparator() + " Original weight: " + originalWeight + "kg" + System.lineSeparator() + + " Current weight: " + currentWeight + "kg" + System.lineSeparator() + " Target weight: " + targetWeight + "kg" + System.lineSeparator() + " Activity level: " + activityLevel.getDescription(); return userInformation; } + + /** + * Performs assertions on all possible person inputs parameters. + * + * @param name The name of the person. + * @param gender The gender of the person. + * @param age The age of the person. + * @param height The height of the person. + * @param originalWeight The original weight of the person when he or she first started using DietBook. + * @param currentWeight The current weight of the person. + * @param targetWeight The target/desired weight that the person wants to achieve. + * @param activityLevel The activity level of the person or in other words, the amount of exercise the + * person engages in. + */ + private void performAssertionsForPerson(String name, Gender gender, int age, int height, + int originalWeight, int currentWeight, int targetWeight, + ActivityLevel activityLevel) { + performAssertionsForNameInput(name); + performAssertionsForGenderInput(gender); + performAssertionsForAgeInput(age); + performAssertionsForHeight(height); + performAssertionsForWeight(originalWeight, "Original weight"); + performAssertionsForWeight(currentWeight, "Current weight"); + performAssertionsForWeight(targetWeight, "Target weight"); + performAssertionsForActivityLevel(activityLevel); + } + + /** + * Performs assertion on the activity level input. + * + * @param activityLevel The activity level of the person or in other words, the amount of exercise the + * person engages in. + */ + private void performAssertionsForActivityLevel(ActivityLevel activityLevel) { + assert activityLevel != null : "Activity level of person should not be null"; + } + + /** + * Performs assertions the weight related inputs. + * + * @param weight Either the original, current or target weight of the person. + * @param weightType A string describing whether the weight given the original, current or target weight. + */ + private void performAssertionsForWeight(int weight, String weightType) { + assert weight > 0 : weightType + " of person should be greater than 0"; + // The heaviest person in the world has a weight of 442kg + assert weight < 443 : weightType + " of person should less than 443"; + } + + /** + * Performs assertions on the height input. + * + * @param height The height of the person. + */ + private void performAssertionsForHeight(int height) { + assert height > 0 : "Height of person should be greater than 0"; + // Tallest person in the world has a height of 272cm + assert height < 273 : "Height of person should be less than 273"; + } + + /** + * Performs assertion on the gender input. + * + * @param gender The gender of the person. + */ + private void performAssertionsForGenderInput(Gender gender) { + assert gender != null : "Gender of person should not be null"; + } + + /** + * Performs assertions on the name input. + * + * @param name The name of the person. + */ + private void performAssertionsForNameInput(String name) { + assert name != null : "The name of person should not be null"; + assert name.trim().length() > 0 : "The name of person should not be an empty string"; + } + + /** + * Performs assertions on the age input. + * + * @param age The age of the person. + */ + private void performAssertionsForAgeInput(int age) { + assert age > 0 : "The age of person should be greater than 0"; + // Oldest person to have ever lived, lived until 122 years and 164 days. + assert age < 123 : "The age of person should be lesser than 123"; + } } From 5fa10b93256717e23d85b758c6d9ddbcc99ebb7c Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 00:08:02 +0800 Subject: [PATCH 152/374] Update JUnit test for person class --- .../seedu/dietbook/person/PersonTest.java | 94 +++++++------------ 1 file changed, 34 insertions(+), 60 deletions(-) diff --git a/src/test/java/seedu/dietbook/person/PersonTest.java b/src/test/java/seedu/dietbook/person/PersonTest.java index ec5311d784..9e2a2c562c 100644 --- a/src/test/java/seedu/dietbook/person/PersonTest.java +++ b/src/test/java/seedu/dietbook/person/PersonTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class PersonTest { @@ -12,95 +13,68 @@ class PersonTest { @BeforeEach public void setUp() { person = new Person("Jack", Gender.MALE,21,165,75,65, - ActivityLevel.LOW); + 60, ActivityLevel.LOW); } @Test - void getName_person_returnsName() { - assertEquals("Jack", person.getName()); - } - - @Test - void setName_personWithNewName_returnNewName() { - person.setName("Jackie"); - assertEquals("Jackie", person.getName()); - } - - @Test - void gender_person_returnsGender() { - assertEquals(Gender.MALE, person.getGender()); - } - - @Test - void gender_personWithNewGender_returnsNewGender() { + void gender_setGenderToFemale_returnsCorrectGenderDescription() { person.setGender(Gender.FEMALE); - assertEquals(Gender.FEMALE, person.getGender()); + assertEquals("female", person.getGender().getDescription()); } @Test - void getAge_person_returnsAge() { - assertEquals(21, person.getAge()); + void gender_setGenderToMale_returnsCorrectGenderDescription() { + person.setGender(Gender.MALE); + assertEquals("male", person.getGender().getDescription()); } @Test - void setAge_personWithNewAge_returnsNewAge() { - person.setAge(24); - assertEquals(24, person.getAge()); + void gender_setGenderToOthers_returnsCorrectGenderDescription() { + person.setGender(Gender.OTHERS); + assertEquals("others", person.getGender().getDescription()); } - @Test - void getHeight_person_returnsHeight() { - assertEquals(165, person.getHeight()); + void gender_setGenderToNull_expectAssertionError() { + assertThrows(AssertionError.class, () -> person.setGender(null)); } @Test - void setHeight_personWithNewHeight_returnsNewHeight() { - person.setHeight(175); - assertEquals(175, person.getHeight()); + void setActivityLevel_setNewActivityLevelToNone_returnsCorrectActivityLevelDescription() { + person.setActivityLevel(ActivityLevel.NONE); + assertEquals("You hardly engage in any exercise or have a job that requires little to no " + + "physical activity.", person.getActivityLevel().getDescription()); } @Test - void getOriginalWeight_person_returnsOriginalWeight() { - assertEquals(75, person.getOriginalWeight()); + void setActivityLevel_setActivityLevelToLow_returnsCorrectActivityLevelDescription() { + person.setActivityLevel(ActivityLevel.LOW); + assertEquals("You engage in some form of light exercise or have a job that requires some " + + "physical activity.", person.getActivityLevel().getDescription()); } @Test - void setOriginalWeight_personWithNewOriginalWeight_returnsNewOriginalWeight() { - person.setOriginalWeight(70); - assertEquals(70, person.getOriginalWeight()); + void setActivityLevel_setActivityLevelToMedium_returnsCorrectActivityLevelDescription() { + person.setActivityLevel(ActivityLevel.MEDIUM); + assertEquals("You engage in moderate amount of exercise or have a job that requires moderate " + + "physical activity.", person.getActivityLevel().getDescription()); } @Test - void getTargetWeight_person_returnsTargetWeight() { - assertEquals(65, person.getTargetWeight()); - } - - @Test - void setTargetWeight_personWithNewTargetWeight_returnsNewTargetWeight() { - person.setTargetWeight(68); - assertEquals(68, person.getTargetWeight()); - } - - @Test - void getActivityLevel_person_returnsActivityLevel() { - assertEquals(ActivityLevel.LOW, person.getActivityLevel()); + void setActivityLevel_setActivityLevelToHigh_returnsCorrectActivityLevelDescription() { + person.setActivityLevel(ActivityLevel.HIGH); + assertEquals("You engage in vigorous exercise or have a physically demanding job.", + person.getActivityLevel().getDescription()); } @Test - void setActivityLevel_personWithNewActivityLevel_returnsNewActivityLevel() { - person.setActivityLevel(ActivityLevel.HIGH); - assertEquals(ActivityLevel.HIGH, person.getActivityLevel()); + void setActivityLevel_setActivityLevelToExtreme_returnsCorrectActivityLevelDescription() { + person.setActivityLevel(ActivityLevel.EXTREME); + assertEquals("You engage in extremely vigorous exercise or have an extremely physically " + + "demanding job.", person.getActivityLevel().getDescription()); } @Test - void toString_person_returnsStringRepresentationOfPersonInformation() { - assertEquals(" Name: Jack" + System.lineSeparator() - + " Gender: male" + System.lineSeparator() - + " Age: 21" + System.lineSeparator() - + " Height: 165cm" + System.lineSeparator() - + " Original weight: 75kg" + System.lineSeparator() - + " Target weight: 65kg" + System.lineSeparator() - + " Activity level: You engage in some form of light exercise or have a job that requires " - + "some physical activity.", person.toString()); + void setActivityLevel_setActivityLevelToNull_expectsAssertionErrors() { + assertThrows(AssertionError.class, () -> person.setActivityLevel(null)); } } From 0f6ea15c1be6c57167db663778e45a770ad0c850 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 00:08:15 +0800 Subject: [PATCH 153/374] Update I/O testing --- text-ui-test/EXPECTED.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 01a5413702..d5c9bd655a 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -13,5 +13,5 @@ Please input in the following format: Example: name Jack _____________________________________________________________________________________________________________________________________ _____________________________________________________________________________________________________________________________________ -Bye John Doe! Hope to see you again soon! +Bye! Hope to see you again soon! _____________________________________________________________________________________________________________________________________ From fe2018b4c87bb4b3113d99f235d7ced17397d926 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 00:08:23 +0800 Subject: [PATCH 154/374] Enable assertions --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8eee256504..a6c5d0226a 100644 --- a/build.gradle +++ b/build.gradle @@ -45,5 +45,5 @@ checkstyle { run{ standardInput = System.in - enableAssertions = false + enableAssertions = true } From 44c3209c07ccc03369aed3a665b57d742f466507 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 00:13:28 +0800 Subject: [PATCH 155/374] Fix checktyle error --- src/test/java/seedu/dietbook/person/PersonTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/seedu/dietbook/person/PersonTest.java b/src/test/java/seedu/dietbook/person/PersonTest.java index 9e2a2c562c..83207f1f82 100644 --- a/src/test/java/seedu/dietbook/person/PersonTest.java +++ b/src/test/java/seedu/dietbook/person/PersonTest.java @@ -33,6 +33,7 @@ void gender_setGenderToOthers_returnsCorrectGenderDescription() { person.setGender(Gender.OTHERS); assertEquals("others", person.getGender().getDescription()); } + @Test void gender_setGenderToNull_expectAssertionError() { assertThrows(AssertionError.class, () -> person.setGender(null)); From 5830442f3216720d9976fa776734a7c193052664 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:03:43 +0800 Subject: [PATCH 156/374] Change to less specific age, weight and height cap --- src/main/java/seedu/dietbook/checker/InputChecker.java | 6 +++--- src/main/java/seedu/dietbook/person/Person.java | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 8741b69586..5eba1d1019 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -13,10 +13,10 @@ public class InputChecker { /** * The value limits are based on current limits observed in th world. */ - public static final int AGE_CAP = 123; + public static final int AGE_CAP = 150; public static final int FOOD_CAP = 100000; - public static final int HEIGHT_CAP = 273; - public static final int WEIGHT_CAP = 443; + public static final int HEIGHT_CAP = 300; + public static final int WEIGHT_CAP = 500; public static final String[] PARAM_ACTIVITY = {"1","2","3","4","5"}; public static final String[] PARAM_ADD = {"n/","x/","k/"}; public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index fa695a9cf3..6fd3f52ebb 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -289,8 +289,7 @@ private void performAssertionsForActivityLevel(ActivityLevel activityLevel) { */ private void performAssertionsForWeight(int weight, String weightType) { assert weight > 0 : weightType + " of person should be greater than 0"; - // The heaviest person in the world has a weight of 442kg - assert weight < 443 : weightType + " of person should less than 443"; + assert weight < 500 : weightType + " of person should less than 500"; } /** @@ -300,8 +299,7 @@ private void performAssertionsForWeight(int weight, String weightType) { */ private void performAssertionsForHeight(int height) { assert height > 0 : "Height of person should be greater than 0"; - // Tallest person in the world has a height of 272cm - assert height < 273 : "Height of person should be less than 273"; + assert height < 300 : "Height of person should be less than 300"; } /** @@ -330,7 +328,6 @@ private void performAssertionsForNameInput(String name) { */ private void performAssertionsForAgeInput(int age) { assert age > 0 : "The age of person should be greater than 0"; - // Oldest person to have ever lived, lived until 122 years and 164 days. - assert age < 123 : "The age of person should be lesser than 123"; + assert age < 150 : "The age of person should be lesser than 150"; } } From ab7d99aaa23d16d60bdc0c58e72fd453d3c3c536 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:05:02 +0800 Subject: [PATCH 157/374] Refactor out logo --- src/main/java/seedu/dietbook/Ui.java | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 1cde8236f3..7e6a50e48a 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -30,12 +30,7 @@ public Ui() { * Prints the welcome message from DietBook when it is fist booted up. */ public void printWelcomeMessage() { - String logo = " _______ __ ______ ________ _______ ______ ______ __ __" + LINE_SEPARATOR - + "| __ \\| | ___|__ __| __ \\ / __ \\ / __ \\| | / /" + LINE_SEPARATOR - + "| | | | | |___ | | | |__| | | | | | | | |/ /" + LINE_SEPARATOR - + "| | | | | ___| | | | __ <| | | | | | | /" + LINE_SEPARATOR - + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + LINE_SEPARATOR - + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\\" + LINE_SEPARATOR; + String logo = getLogo(); print(logo + LINE_SEPARATOR + "Hello! Welcome to DietBook!" + LINE_SEPARATOR + "I am Diet, your guide to using DietBook. What is your name?" + LINE_SEPARATOR @@ -484,6 +479,21 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei // Helper methods for system related commands or messages + /** + * Returns the string representation of the logo. + * + * @return The string representation of the logo. + */ + private String getLogo() { + String logo = " _______ __ ______ ________ _______ ______ ______ __ __" + LINE_SEPARATOR + + "| __ \\| | ___|__ __| __ \\ / __ \\ / __ \\| | / /" + LINE_SEPARATOR + + "| | | | | |___ | | | |__| | | | | | | | |/ /" + LINE_SEPARATOR + + "| | | | | ___| | | | __ <| | | | | | | /" + LINE_SEPARATOR + + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + LINE_SEPARATOR + + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\\" + LINE_SEPARATOR; + return logo; + } + /** * Returns a string representation of a list of system related commands that users can input. * From 91a917747fad0aeba53b6d040edf61044e9f3631 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:05:41 +0800 Subject: [PATCH 158/374] Refactor out getStartMessage method --- src/main/java/seedu/dietbook/Ui.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 7e6a50e48a..7a3a9a2896 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -494,6 +494,17 @@ private String getLogo() { return logo; } + /** + * Returns a string stating that DietBook is ready for use. + * + * @return A string stating that DietBook is ready for use. + */ + private String getStartMessage() { + return "and you may start by entering any valid commands. " + + LINE_SEPARATOR + + "If you require a list of valid commands, you can enter: help"; + } + /** * Returns a string representation of a list of system related commands that users can input. * From 79b2f55b54f31ce41edcf15daa7bf9e712e05886 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:07:15 +0800 Subject: [PATCH 159/374] Add printWelcomeBackMessage method --- src/main/java/seedu/dietbook/Ui.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 7a3a9a2896..5401047330 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -98,9 +98,20 @@ public void printErrorMessage(String errorMessage) { * Prints a message that notifies the user that DietBook has been initialised. */ public void printInitialisationCompleteMessage() { - print("Thank you! DietBook has been initialised and you may start by entering any valid commands. " - + LINE_SEPARATOR - + "If you require a list of valid commands, you can enter: help"); + print("Thank you! DietBook has been initialised " + getStartMessage()); + } + + + /** + * Prints the welcome back message when user reboots up DietBook after the first initialisation. + * + * @param name The name of the user. + */ + public void printWelcomeBackMessage(String name) { + performAssertionsForStringInputs(name, "Name"); + + print(getLogo() + LINE_SEPARATOR + "Welcome back to DietBook " + trimString(name) + "!" + LINE_SEPARATOR + + "All your previous data has been successfully loaded " + getStartMessage()); } /** From 00fdbc092ed0d443ac18a40f07677c363594bd3d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:08:12 +0800 Subject: [PATCH 160/374] Update printEditedPersonInfo method --- src/main/java/seedu/dietbook/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 5401047330..8af66267a2 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -187,7 +187,7 @@ public void printEditedPersonInfo(String personInfo) { performAssertionsForStringInputs(personInfo, "Updated person information"); - print("Here is your updated information:" + LINE_SEPARATOR + print("Got it! I've updated your personal information:" + LINE_SEPARATOR + personInfo); } From 28da147313e1868a3e22811ad4f206f95a47c4d4 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:10:05 +0800 Subject: [PATCH 161/374] Shorten JavaDocs for several methods --- src/main/java/seedu/dietbook/Ui.java | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 8af66267a2..61a0750a91 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -299,9 +299,8 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods) { } /** - * Prints the total amount of carbohydrates consumed by the user within a given time period and a list of - * the foods recorded into the food list during the same time period which had their nutritional - * information recalculated by DietBook if any. + * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had + * their nutritional information recalculated by DietBook if any, given a certain time period. * Some food items only have partial nutritional information as users did not provide all the * information when the food items were added. Hence, DietBook does an internal calculation for the * the missing information and these calculated values are used when tabulating total carbohydrate @@ -338,9 +337,8 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo } /** - * Prints the total amount of calories consumed by the user within a given time period and a list of - * the foods recorded into the food list during the same time period which had their nutritional - * information recalculated by DietBook if any. + * Prints the total amount of calories consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a certain time period. * Some food items only have partial nutritional information as users did not provide all the * information when the food items were added. Hence, DietBook does an internal calculation for the * the missing information and these calculated values are used when tabulating total calorie @@ -377,9 +375,8 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo } /** - * Prints the total amount of proteins consumed by the user within a given time period and a list of - * the foods recorded into the food list during the same time period which had their nutritional - * information recalculated by DietBook if any. + * Prints the total amount of proteins consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a certain time period. * Some food items only have partial nutritional information as users did not provide all the * information when the food items were added. Hence, DietBook does an internal calculation for the * the missing information and these calculated values are used when tabulating total protein @@ -416,9 +413,8 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods) { } /** - * Prints the total amount of fats consumed by the user within a given time period and a list of - * the foods recorded into the food list during the same time period which had their nutritional - * information recalculated by DietBook if any. + * Prints the total amount of fats consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a certain time period. * Some food items only have partial nutritional information as users did not provide all the * information when the food items were added. Hence, DietBook does an internal calculation for the * the missing information and these calculated values are used when tabulating total fat @@ -459,12 +455,12 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei } /** - * Prints the total amount of total amount of calories, carbohydrates, fats and proteins consumed by - * the user within a given time period and a list of the foods recorded into the food list during the - * same time period which had their nutritional information recalculated by DietBook if any. + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a + * list of the foods which had their nutritional information recalculated by DietBook if any, given a + * certain time period. * Some food items only have partial nutritional information as users did not provide all the * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating the individual intake + * the missing information and these calculated values are used when tabulating the individual intakes * within a given time period. * * @param calorieIntake The total amount of calories of food in the food list recorded during the From 08d0349a0a6aae94a5a8dfd1b0ff4fc8b0dcd8bd Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:11:27 +0800 Subject: [PATCH 162/374] Remove bug caused by wrong input order --- src/main/java/seedu/dietbook/Ui.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 61a0750a91..662dcecfc9 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -450,7 +450,7 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, */ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, String recalculatedFoods) { - print(stringAllIntakeAndFoodsWithoutTime(carbIntake, calorieIntake,proteinIntake, + print(stringAllIntakeAndFoodsWithoutTime(calorieIntake, carbIntake, proteinIntake, fatIntake, recalculatedFoods)); } @@ -479,8 +479,8 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, String recalculatedFoods, LocalDateTime start, LocalDateTime end) { - String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(carbIntake, calorieIntake, - proteinIntake,fatIntake, recalculatedFoods); + String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(calorieIntake, + carbIntake, proteinIntake, fatIntake, recalculatedFoods); print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); } @@ -691,7 +691,7 @@ private String stringIntakeAndFoodsWithTime(String intakeAndFoodsWithoutTime, * @return A string representation of the total amount of all nutrients consumed by the user and * the list of food items which had their nutritional information recalculated by DietBook if any. */ - private String stringAllIntakeAndFoodsWithoutTime(int carbIntake, int calorieIntake, int proteinIntake, + private String stringAllIntakeAndFoodsWithoutTime(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, String recalculatedFoods) { performAssertionsForNutritionalIntake(carbIntake, "carbohydrate"); performAssertionsForNutritionalIntake(calorieIntake, "calorie"); @@ -707,8 +707,8 @@ private String stringAllIntakeAndFoodsWithoutTime(int carbIntake, int calorieInt String stringFatIntake = stringNutritionalIntake(fatIntake,"fat", "g"); String message = recalculatedFoodsMessage(recalculatedFoods); - return stringCarbIntake + LINE_SEPARATOR - + stringCalorieIntake + LINE_SEPARATOR + return stringCalorieIntake + LINE_SEPARATOR + + stringCarbIntake + LINE_SEPARATOR + stringProteinIntake + LINE_SEPARATOR + stringFatIntake + LINE_SEPARATOR + message; From 76705fa46735ea7996699b1b434e89fa58deeb1d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:12:39 +0800 Subject: [PATCH 163/374] Add methods to calculate nutrient given start date --- src/main/java/seedu/dietbook/Ui.java | 118 ++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 662dcecfc9..d1865e5922 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -320,6 +320,26 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); } + /** + * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had + * their nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total carbohydrate + * intake given a start date. + * + * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the + * start date till now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCarbIntakeAndFoods(carbIntake, recalculatedFoods, start, end); + } + /** * Prints the total amount of calories consumed by the user and the list of food items which had * their nutritional information recalculated by DietBook if any. @@ -358,6 +378,26 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); } + /** + * Prints the total amount of calories consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total calorie intake, + * given a start date. + * + * @param calorieIntake The total amount of calories of food in the food list recorded from the + * start date till now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCalorieIntakeAndFoods(calorieIntake, recalculatedFoods, start, end); + } + /** * Prints the total amount of proteins consumed by the user and the list of food items which had * their nutritional information recalculated by DietBook if any. @@ -396,6 +436,26 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); } + /** + * Prints the total amount of proteins consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total protein intake, + * given a start date. + * + * @param proteinIntake The total amount of proteins of food in the food list recorded from the + * start date till now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printProteinIntakeAndFoods(proteinIntake, recalculatedFoods, start, end); + } + /** * Prints the total amount of fats consumed by the user and the list of food items which had * their nutritional information recalculated by DietBook if any. @@ -434,6 +494,26 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); } + /** + * Prints the total amount of fats consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total fat intake, + * given a start date. + * + * @param fatIntake The total amount of fats of food in the food list recorded from the start date till + * now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printFatIntakeAndFoods(fatIntake, recalculatedFoods, start, end); + } + /** * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and the * list of food items which had their nutritional information recalculated by DietBook if any. @@ -484,6 +564,35 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); } + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a + * list of the foods which had their nutritional information recalculated by DietBook if any, given a + * start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating the individual intakes, + * given a start date. + * + * @param calorieIntake The total amount of calories of food in the food list recorded from the start + * date till now. + * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the start + * date till now. + * @param proteinIntake The total amount of proteins of food in the food list recorded from the start + * date till now. + * @param fatIntake The total amount of fats of food in the food list recorded from the start date till + * now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printAllIntakeAndFoods(calorieIntake, carbIntake, proteinIntake, fatIntake, recalculatedFoods, start, + end); + } + // Helper methods for system related commands or messages /** @@ -794,14 +903,15 @@ private void performAssertionsForStringInputs(String string, String stringDescri * @param end Ending date time of the time period given. */ private void performAssertionsForTimePeriod(LocalDateTime start, LocalDateTime end) { + LocalDateTime now = LocalDateTime.now(); assert start != null : "Starting date time of the time period given should not be null"; assert end != null : "Ending date time of the time period given should not be null"; assert !start.isAfter(end) : "Starting date time should not be later than ending date time " + "of the time period"; - assert start.isBefore(LocalDateTime.now()) : "Starting date time of the time period given should " - + "not be in the future"; - assert end.isBefore(LocalDateTime.now()) : "Ending date time of the time period given should not be" - + " in the future"; + assert !start.isAfter(now) : "Starting date time of the time period given should " + + "not be in the future" + start + LocalDateTime.now(); + assert !end.isAfter(now) : "Ending date time of the time period given should not be" + + " in the future" + end + LocalDateTime.now(); } /** From 6d5178b8b60df0491556a414b798fedbf5f31a02 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 25 Oct 2020 14:29:18 +0800 Subject: [PATCH 164/374] Fix checkstyle error --- src/main/java/seedu/dietbook/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index d1865e5922..cff308b02e 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -903,11 +903,11 @@ private void performAssertionsForStringInputs(String string, String stringDescri * @param end Ending date time of the time period given. */ private void performAssertionsForTimePeriod(LocalDateTime start, LocalDateTime end) { - LocalDateTime now = LocalDateTime.now(); assert start != null : "Starting date time of the time period given should not be null"; assert end != null : "Ending date time of the time period given should not be null"; assert !start.isAfter(end) : "Starting date time should not be later than ending date time " + "of the time period"; + LocalDateTime now = LocalDateTime.now(); assert !start.isAfter(now) : "Starting date time of the time period given should " + "not be in the future" + start + LocalDateTime.now(); assert !end.isAfter(now) : "Ending date time of the time period given should not be" From 4cf65bfc5c47a6fa709530a69b185bb3bd68d146 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Mon, 26 Oct 2020 00:05:03 +0800 Subject: [PATCH 165/374] Recommendation of daily calorie intake calculation is added --- .../seedu/dietbook/calculator/Calculator.java | 62 +++++++++++++++++++ .../dietbook/calculator/CalculatorTest.java | 12 ++++ 2 files changed, 74 insertions(+) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index bcd6208c32..246983b3d6 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -1,6 +1,8 @@ package seedu.dietbook.calculator; import seedu.dietbook.food.Food; +import seedu.dietbook.person.Gender; +import seedu.dietbook.person.Person; import java.util.ArrayList; @@ -64,4 +66,64 @@ public int calculateProtein() { public int calculateFat() { return totalFat; } + + /** + * Returns an int type variable containing the value of recommended daily calorie intake. + * It is calculated based on the gender, activity level, age, height, original weight, + * and targeted weight. + * + * @param person person whose recommended daily calorie intake are to return. + * @return the value of recommended daily calorie intake. + */ + public int calculateRecomendation(Person person) { + double requirement = 0; + int recomendation; + double activityScore; + switch (person.getActivityLevel()) { + case NONE: + activityScore = 1; + break; + case LOW: + if (person.getGender() == Gender.MALE) { + activityScore = 1.11; + } else { + activityScore = 1.12; + } + break; + case MEDIUM: + if (person.getGender() == Gender.MALE) { + activityScore = 1.26; + } else { + activityScore = 1.27; + } + break; + case HIGH: + default: + if (person.getGender() == Gender.MALE) { + activityScore = 1.48; + } else { + activityScore = 1.45; + } + break; + } + + switch (person.getGender()) { + case MALE: + requirement = 662 - 9.53 * person.getAge() + 15.91 * activityScore * person.getOriginalWeight() + + 539.6 * person.getHeight() / 100; + break; + case FEMALE: + requirement = 354 - 6.91 * person.getAge() + 9.36 * activityScore * person.getOriginalWeight() + + 726 * person.getHeight() / 100; + break; + default: + } + + if (person.getOriginalWeight() > person.getTargetWeight()) { + recomendation = (int) requirement - 300; + } else { + recomendation = (int) requirement + 100; + } + return recomendation; + } } diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index 576c6e3995..ea25a7aa09 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -2,6 +2,9 @@ import org.junit.jupiter.api.Test; import seedu.dietbook.food.Food; +import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.Gender; +import seedu.dietbook.person.Person; import java.util.ArrayList; @@ -50,4 +53,13 @@ void calculateFat_foodListOfThreeItems_sumOfFat() { Calculator calculator = new Calculator(foodList); assertEquals(0, calculator.calculateFat()); } + + @Test + void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { + Person Harry = new Person("Harry", Gender.MALE, 19, 182, 66, 75, ActivityLevel.LOW); + Person Erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 45, ActivityLevel.MEDIUM); + Calculator calculator = new Calculator(new ArrayList()); + assertEquals(2728, calculator.calculateRecomendation(Harry)); + assertEquals(1752, calculator.calculateRecomendation(Erica)); + } } From 249af2a545272121a342fc76d5cef4271e2a1ed2 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 26 Oct 2020 11:15:09 +0800 Subject: [PATCH 166/374] Reorder the outputs --- src/main/java/seedu/dietbook/Ui.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index cff308b02e..270286cef7 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -119,12 +119,12 @@ public void printWelcomeBackMessage(String name) { */ public void printHelpCommandMessage() { print("Listed below are the valid commands for DietBook:" + LINE_SEPARATOR + LINE_SEPARATOR + + "For user information related commands" + LINE_SEPARATOR + + getUserRelatedCommands() + LINE_SEPARATOR + "For database related commands" + LINE_SEPARATOR + getDatabaseRelatedCommands() + LINE_SEPARATOR + "For food list related commands" + LINE_SEPARATOR + getFoodListRelatedCommands() + LINE_SEPARATOR - + "For user information related commands" + LINE_SEPARATOR - + getUserRelatedCommands() + LINE_SEPARATOR + "For nutritional intake and recommendation related commands" + LINE_SEPARATOR + getCalculatorRelatedCommands() + LINE_SEPARATOR + "For other system related commands" + LINE_SEPARATOR From 8dc7572c4b8e4b24b80dd931a29aaf6e4e2d83e7 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 26 Oct 2020 11:19:14 +0800 Subject: [PATCH 167/374] Change word --- src/main/java/seedu/dietbook/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 270286cef7..de395c1d5f 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -673,7 +673,7 @@ private String getCalculatorRelatedCommands() { * @return A string representation of a list of user information related commands that users can input. */ private String getUserRelatedCommands() { - return " To show user information: userinfo" + LINE_SEPARATOR + return " To view user information: userinfo" + LINE_SEPARATOR + " To edit user information: editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] " + "[o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]" + LINE_SEPARATOR; From 123f982eaa9283dd93e15b6a48c102ce17582111 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 26 Oct 2020 12:38:40 +0800 Subject: [PATCH 168/374] Update parser to add current weight parameter --- src/main/java/seedu/dietbook/Manager.java | 2 +- src/main/java/seedu/dietbook/parser/Parser.java | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index a5885bddfb..dc35b3075a 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -74,7 +74,7 @@ public Person getPerson() { public void setPerson(String name, Gender gender, int age,int height,int orgWeight, int currWeight, int targWeight, ActivityLevel actLvl) { - this.person = new Person(name, gender, age, height, orgWeight, currWeight, targWeight, actLvl); + this.person.setAll(name, gender, age, height, orgWeight, currWeight, targWeight, actLvl); } public Calculator getCalculator() { diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 3c90078293..777e4fffff 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -19,7 +19,7 @@ public class Parser { public static final String COMMAND_INFO = "info"; public static final String COMMAND_ADD = "add"; public static final String COMMAND_CALCULATE = "calculate"; - public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; + public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/","c/"}; @@ -131,6 +131,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw int age = 0; int height = 0; int orgWeight = 0; + int currWeight = 0; int tarWeight = 0; String trimmedParam; String[] processedParam; @@ -163,6 +164,10 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw orgWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(orgWeight); break; + case "c/": + currWeight = Integer.parseInt(trimmedParam); + InputChecker.checkWeightLimit(currWeight); + break; case "t/": tarWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(tarWeight); @@ -184,7 +189,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw break; } } - //manager.setPerson(manager.getName(), gender, age, height, orgWeight, tarWeight, actLvl); + manager.setPerson(manager.getName(), gender, age, height, orgWeight, currWeight, tarWeight, actLvl); } /** From f53491979ed1fa0c458a760e4f776c6f3a5aa3c8 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 26 Oct 2020 12:39:18 +0800 Subject: [PATCH 169/374] Update parser to support others gender --- src/main/java/seedu/dietbook/parser/Parser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 777e4fffff..695774dbc2 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -148,8 +148,10 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw InputChecker.checkGender(processGender); if (processGender.equals("M")) { gender = Gender.MALE; - } else { + } else if (processGender.equals("F")) { gender = Gender.FEMALE; + } else { + gender = Gender.OTHERS; } break; case "a/": From 561990431d33320abbd99d4a260e81c1cb1c0c7e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 26 Oct 2020 12:43:38 +0800 Subject: [PATCH 170/374] Fix bug for checking of empty inputs --- src/main/java/seedu/dietbook/checker/InputChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 5eba1d1019..91986e54e9 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -45,7 +45,7 @@ public static void checkEmpty(String userInput, String command) throws DietExcep */ public static void checkEmptyOption(String[] input) throws DietException { try { - if (input[1].trim().charAt(1) == '/') { + if (input[1].charAt(0) == ' ') { throw new DietException("Error! Option specified with empty field!"); } } catch (IndexOutOfBoundsException e) { From bafdc7d148e7e254eee580b880fa5f12f50de2a5 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 26 Oct 2020 13:12:27 +0800 Subject: [PATCH 171/374] Revert "Fix bug for checking of empty inputs" This reverts commit 561990431d33320abbd99d4a260e81c1cb1c0c7e. --- src/main/java/seedu/dietbook/checker/InputChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 91986e54e9..5eba1d1019 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -45,7 +45,7 @@ public static void checkEmpty(String userInput, String command) throws DietExcep */ public static void checkEmptyOption(String[] input) throws DietException { try { - if (input[1].charAt(0) == ' ') { + if (input[1].trim().charAt(1) == '/') { throw new DietException("Error! Option specified with empty field!"); } } catch (IndexOutOfBoundsException e) { From 24df057fa69f499b2decec0e0822703d2aaed983 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 26 Oct 2020 18:18:08 +0800 Subject: [PATCH 172/374] Update user guide --- docs/UserGuide.md | 143 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 10 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..0189022c3d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,21 +1,144 @@ -# User Guide +# DietBook User Guide ## Introduction -{Give a product intro} - +DietBook is a desktop application targeting NUS students optimized for use via a **Command Line Interface**. Not only can DietBook track and show the user's food and nutritional intake, it also provides users with a list of commonly eaten food items around and outside NUS. + ## Quick Start - -{Give steps to get started quickly} - 1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +1. Download the latest version of `dietbook.jar` from [here](https://github.com/AY2021S1-CS2113-T14-4/tp/releases). +1. Copy the file to the folder you want to use as the home folder for your DietBook. +1. Either double-click the jar file to start the application or navigate to the folder containing the jar file on command prompt and run the command `java -jar dietbook.jar`. ## Features -{Give detailed description of each feature} - -### Adding a todo: `todo` +Notes about the command format: + +* Words in `UPPER_CASE` are parameters to be supplied by the user.
+e.g. For `delete INDEX`, `delete 1`would be a valid command. + +* Parameters in square brackets are optional. However, if all parameters are optional, at least one parameter needs to be given. In such cases, any one of the parameters would be valid.
+e.g. For `add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT]`, `add x/1 n/Toast k/120`, `add x/1 n/Toast k/120 c/18`, `add x/1 n/Toast k/120 c/18 p/3`, `add x/1 n/Toast k/120 + c/18 p/3 f/4` are all valid commands. + +* For commands with multiple parameters, the parameters can be in any order.
+e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` and `add x/1 n/mee` are both valid. + +* Command words and parameter indicators are case-sensitive.
+e.g. `help` is a valid command but `Help` is not.
+e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add N/mee x/1` is not. + +* Spaces to separate command words, parameters, command word and parameters are important.
+e.g. For `calculate all`, `calculate all` is valid but `calculateall` is not.
+e.g. For `delete INDEX`, `delete 1` is valid if there is a food item with index 1 but`delete1` is not.
+e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add n/meex/1` is not.
+ +### Features related to user information + +#### Viewing user information: `userinfo` + +Shows the user information stored in DietBook. + +Format: `userinfo` + +Output example: +``` +Here is your information: + Name: Jack + Gender: male + Age: 21 + Height: 175cm + Original weight: 85kg + Current weight: 85kg + Target weight: 75kg + Activity level: You engage in some form of light exercise or have a job that requires some physical activity. +``` + +#### Editing user information: `editinfo` + +Edits the user information stored in DietBook. + +Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]` + +* Although all parameters are listed as optional, **at least one of the optional fields needs to be provided**. In this case, any one of the parameters would work. +* If more than one parameter is given, they can be in any order. +* Existing values will be updated to the input values. +* The name must not be empty. +* The gender must be either **`M` for male, `F` for female or `O` for others**. +* The age must be a positive integer **between 0 and 150**. +* The height must be a positive integer **between 0 and 300**. +* The original, current and target weight must be a positive integer **between 0 and 500**. +* The activity level must be a positive integer **from 1 to 5, inclusive**. + * 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. + * 2 = You engage in some form of light exercise or have a job that requires some physical activity. + * 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. + * 4 = You engage in vigorous exercise or have a physically demanding job. + * 5 = You engage in extremely vigorous exercise or have an extremely physically demanding job. + +Input examples: + +* `editinfo n/John` edits the updated the name stored in DietBook. +* Both `editinfo c/75 l/4` and `editinfo l/4 c/75` edits the current weight and activity level of the + user to be `75` and `You engage in vigorous exercise or have a physically demanding job.` respectively. + +Output example for input example 2: +``` +Got it! I've updated your personal information: + Name: Jack + Gender: male + Age: 21 + Height: 175cm + Original weight: 85kg + Current weight: 75kg + Target weight: 75kg + Activity level: You engage in vigorous exercise or have a physically demanding job. +``` + +### Features related to the food database + +To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE +To view all food in the database: data + +### Features related to the food list + +To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] +To view all food in DietBook: list +To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm +To view all food in DietBook recorded from a certain date until now: list yyyy-mm-ddTHH:mm +To delete a food from DietBook: delete INDEX +To delete all food items from the DietBook: clear + +### Features related to nutritional intake and recommendation + +To get recommended calorie intake: recommend + + To calculate carbohydrate intake: calculate carbohydrate + To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm + + To calculate calorie intake: calculate calorie + To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate calorie intake from a certain date until now: calculate calorie yyyy-mm-ddTHH:mm + + To calculate protein intake: calculate protein + To calculate protein intake within a time period: calculate protein yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate protein intake from a certain date until now: calculate protein yyyy-mm-ddTHH:mm + + To calculate fat intake: calculate fat + To calculate fat intake within a time period: calculate fat yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate fat intake from a certain date until now: calculate fat yyyy-mm-ddTHH:mm + + To calculate all nutritional intake: calculate all + To calculate all nutritional intake within a time period: calculate all yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate all nutritional intake from a certain date until now: calculate all yyyy-mm-ddTHH:mm + +### Other features + + To view a list of valid commands: help + To exit DietBook: exit + Saving + +#### Adding a todo: `todo` Adds a new item to the list of todo items. Format: `todo n/TODO_NAME d/DEADLINE` From 3531f413786aac4d198a36183261dc2271340b33 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 26 Oct 2020 18:22:26 +0800 Subject: [PATCH 173/374] add date filtering - FoodList returns List for public methods. Tweak to calculator to accept List instead of ArrayList made. - date filtering functions added (to filter between a range or just after a date) - List function class added to contain constantly reused FP methods on lists. --- .../java/seedu/calculator/Calculator.java | 4 +- src/main/java/seedu/duke/list/FoodList.java | 68 ++++++++++++++++--- .../java/seedu/duke/list/FoodListManager.java | 44 ++++++++---- .../java/seedu/duke/list/ListFunction.java | 34 ++++++++++ .../java/seedu/duke/list/FoodListTest.java | 24 ++++++- 5 files changed, 148 insertions(+), 26 deletions(-) create mode 100644 src/main/java/seedu/duke/list/ListFunction.java diff --git a/src/main/java/seedu/calculator/Calculator.java b/src/main/java/seedu/calculator/Calculator.java index dfbd8a221c..0201fd79ca 100644 --- a/src/main/java/seedu/calculator/Calculator.java +++ b/src/main/java/seedu/calculator/Calculator.java @@ -3,6 +3,7 @@ import seedu.duke.food.Food; import java.util.ArrayList; +import java.util.List; /** * Represents a calculator of food items in foodList. @@ -19,7 +20,7 @@ public class Calculator { * * @param foodList foodList containing food items to calculate. */ - public Calculator(ArrayList foodList) { + public Calculator(List foodList) { assert foodList != null : "the foodList should not be null."; for (int i = 0; i < foodList.size(); i++) { totalCalorie += foodList.get(i).getCalorie(); @@ -29,6 +30,7 @@ public Calculator(ArrayList foodList) { } } + /** * Returns an int type variable containing the value of total calorie. * diff --git a/src/main/java/seedu/duke/list/FoodList.java b/src/main/java/seedu/duke/list/FoodList.java index a334418f87..42c5311cc3 100644 --- a/src/main/java/seedu/duke/list/FoodList.java +++ b/src/main/java/seedu/duke/list/FoodList.java @@ -1,7 +1,9 @@ package seedu.duke.list; import java.util.ArrayList; +import java.util.List; import seedu.duke.food.Food; +import java.time.LocalDateTime; /** @@ -20,7 +22,7 @@ public FoodList() { } /** - * Convenience constructor mainly for testing purposes. + * Convenience constructor for testing purposes. */ protected FoodList(ArrayList entries) { this.foodEntries = entries; @@ -35,9 +37,9 @@ protected FoodList(ArrayList entries) { * @return string representation of the food object added */ public String addFood(int portionSize, Food food) { - FoodEntry toAdd = new FoodEntry(portionSize, food); - foodEntries.add(toAdd); - return toAdd.toString(); + FoodEntry entry = new DatedFoodEntry(portionSize, food); + foodEntries.add(entry); + return entry.toString(); } /** @@ -45,11 +47,12 @@ public String addFood(int portionSize, Food food) { */ public String addFood(int portionSize, String name, int calorie, int carbohydrate, int protein, int fat) { - FoodEntry toAdd = new FoodEntry(portionSize, name, calorie, carbohydrate, protein, fat); - foodEntries.add(toAdd); - return toAdd.toString(); + FoodEntry entry = new DatedFoodEntry(portionSize, name, calorie, carbohydrate, protein, fat); + foodEntries.add(entry); + return entry.toString(); } + /** * Food database search functionality support. * Not expected to function. Added for completeness. @@ -63,6 +66,20 @@ public String addFood(int portionSize, String name) throws FoodNotFoundException throw new FoodNotFoundException(); } + + /** + * Add add method for baglogged entries. + * Allows specificiation of time via LocalDateTime param. + * @param dateTime User specified time for backlogged entry. + */ + public String addFoodAtDateTime(int portionSize, String name, int calorie, + int carbohydrate, int protein, int fat, LocalDateTime dateTime) { + + FoodEntry entry = new DatedFoodEntry(portionSize, name, calorie, carbohydrate, protein, fat, dateTime); + foodEntries.add(entry); + return entry.toString(); + } + /** * Deletes the the entry of the list at the provided index. * index starts from 1 (not 0). i.e. is User's understanding of index. @@ -83,22 +100,55 @@ public boolean clear() { return true; } + /** * Obtain the food objects in Foodlist as an ArrayList. * For other classes that wish to operate on the Food items directly. * @return Arraylist of ordered Food objects in Foodlist's foodEntries. */ - public ArrayList getFoods() { + public List getFoods() { return FoodListManager.listToFoods(foodEntries); } /** * Obtain list of food objects in FoodList, scaled to portion size. */ - public ArrayList getPortionedFoods() { + public List getPortionedFoods() { return FoodListManager.listToPortionedFoods(foodEntries); } + /** + * Obtain list of foods consumed after specified timing. + */ + public List getFoodsAfterDateTime(LocalDateTime dateTime) { + List entriesAfterDateTime = FoodListManager.filterListByDate(foodEntries, dateTime); + return FoodListManager.listToFoods(entriesAfterDateTime); + } + + /** + * Obtain list of foods consumed after specified timing, scaled to portion size. + */ + public List getPortionedFoodsAfterDateTime(LocalDateTime dateTime) { + List entriesAfterDateTime = FoodListManager.filterListByDate(foodEntries, dateTime); + return FoodListManager.listToFoods(entriesAfterDateTime); + } + + /** + * Obtain list of foods consumed within the range of a specified timing. + */ + public List getFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { + List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); + return FoodListManager.listToFoods(entriesInRange); + } + + /** + * Obtain list of foods consumed within the range of a specified timing, scaled to portion size. + */ + public List getPortionedFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { + List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); + return FoodListManager.listToPortionedFoods(entriesInRange); + } + @Override public String toString() { return FoodListManager.listToString(foodEntries); diff --git a/src/main/java/seedu/duke/list/FoodListManager.java b/src/main/java/seedu/duke/list/FoodListManager.java index 45b9a5373f..cf6c2ced0b 100644 --- a/src/main/java/seedu/duke/list/FoodListManager.java +++ b/src/main/java/seedu/duke/list/FoodListManager.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.function.Function; import java.util.function.Consumer; +import java.util.function.Predicate; +import java.time.LocalDateTime; /** * Class with static methods to execute "complex commands" on FoodList. @@ -18,7 +20,7 @@ public class FoodListManager { * Internal helper method to convert the items in the arraylist into enumed strings. * Primarily used to obtain String representations of the entire list. */ - protected static String listToString(ArrayList list) { + protected static String listToString(List list) { String listString = ""; for (int i = 1; i <= list.size(); i++) { @@ -46,7 +48,7 @@ protected static FoodEntry deleteEntry(List list, int index) throws I */ protected static List listToStrings(List list) { Function function = x -> x.toString(); - return applyFunctionToList(list, function); + return ListFunction.applyFunctionToList(list, function); } /** @@ -54,9 +56,9 @@ protected static List listToStrings(List list) { * @param list list of foodEntries * @return arraylist of Food objects. */ - protected static ArrayList listToFoods(List list) { + protected static List listToFoods(List list) { Function function = x -> x.getFood(); - return applyFunctionToList(list, function); + return ListFunction.applyFunctionToList(list, function); } /** @@ -65,7 +67,7 @@ protected static ArrayList listToFoods(List list) { * @param list list of FoodEntries * @return arraylist of Food objects */ - protected static ArrayList listToPortionedFoods(List list) { + protected static List listToPortionedFoods(List list) { Function function = x -> { Food baseFood = x.getFood(); /** Explicitly getting return type of getPortionSize() is avoided. @@ -78,22 +80,34 @@ protected static ArrayList listToPortionedFoods(List list) { baseFood.getProtein() * x.getPortionSize(), baseFood.getFat() * x.getPortionSize()); }; - return applyFunctionToList(list, function); + return ListFunction.applyFunctionToList(list, function); } /** - * Generic method to map a function across a list. - * @param list list to operate on - * @param function function to be mapped across list - * @return list of mapped items under provided function + * Obtain only food entries after a specified dateTime. */ - protected static ArrayList applyFunctionToList(List list, Function function) { - ArrayList appliedList = new ArrayList<>(); - Consumer addResultToAppliedList = x -> appliedList.add(function.apply(x)); - list.forEach(addResultToAppliedList); - return appliedList; + protected static List filterListByDate(List list, LocalDateTime dateTime) { + Predicate predicate = x -> { + assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; + DatedFoodEntry datedEntry = (DatedFoodEntry) x; + return dateTime.isBefore(datedEntry.getDateTime()); + }; + return ListFunction.filterList(list, predicate); } + /** + * Obtain only food entries within a specified range of dateTimes. + */ + protected static List filterListByDate(List list, LocalDateTime start, LocalDateTime end) { + Predicate predicate = x -> { + assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; + DatedFoodEntry datedEntry = (DatedFoodEntry) x; + return start.isBefore(datedEntry.getDateTime()) && end.isAfter(datedEntry.getDateTime()); + }; + return ListFunction.filterList(list, predicate); + } + + } // Potential future work: create a functional interface for the functions instead: diff --git a/src/main/java/seedu/duke/list/ListFunction.java b/src/main/java/seedu/duke/list/ListFunction.java new file mode 100644 index 0000000000..929066355c --- /dev/null +++ b/src/main/java/seedu/duke/list/ListFunction.java @@ -0,0 +1,34 @@ +package seedu.duke.list; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; + + +/** + * Functional programming support methods for Lists. + */ +public class ListFunction { + /** + * Maps a function across a list. + * @param list list to operate on + * @param function function to be mapped across list + * @return list of mapped items under provided function + */ + protected static ArrayList applyFunctionToList(List list, Function function) { + ArrayList appliedList = new ArrayList<>(); + Consumer addResultToAppliedList = x -> appliedList.add(function.apply(x)); + list.forEach(addResultToAppliedList); + return appliedList; + } + + /** + * Filters the list by the given predicate. + */ + protected static List filterList(List list, Predicate predicate) { + return list.stream().filter(predicate).collect(Collectors.toList()); + } +} diff --git a/src/test/java/seedu/duke/list/FoodListTest.java b/src/test/java/seedu/duke/list/FoodListTest.java index e0e056a79b..1c011fbe86 100644 --- a/src/test/java/seedu/duke/list/FoodListTest.java +++ b/src/test/java/seedu/duke/list/FoodListTest.java @@ -46,7 +46,7 @@ void foodPortionScaling_standardList_scaledFoodList() { void deleteItemTest() { Food food = new Food("Kobe Beef", 480,50,40,30); FoodEntry entry = new FoodEntry(3, food); - assertEquals(list.delete(1), entry.toString()); + assertEquals(entry.toString(), list.delete(1)); } @Test @@ -58,4 +58,26 @@ void dateComparisonTest() { } + @Test + void dateFilterAfterTest() { + LocalDateTime timeNow = LocalDateTime.now(); + + assertTrue(list.getFoodsAfterDateTime(timeNow).size() == 0); + assertEquals(list.getFoodsAfterDateTime(LocalDateTime.MIN).toString(), + list.getFoods().toString()); + + // add new entries too: + list.addFood(1, food); + assertEquals(food.toString(), list.getFoodsAfterDateTime(timeNow).get(0).toString()); + + } + + @Test + void dateFilterRangeTest() { + LocalDateTime timeNow = LocalDateTime.now(); + assertTrue(list.getFoodsInDateTimeRange(timeNow, LocalDateTime.MAX).size() == 0); + assertEquals(list.getPortionedFoodsInDateTimeRange(LocalDateTime.MIN, timeNow).toString(), + list.getPortionedFoods().toString()); + } + } From aa20da5b4dabd4dd025d8802ad3a9c62194ef1fe Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 26 Oct 2020 18:45:37 +0800 Subject: [PATCH 174/374] update FoodListTest time test Some computers execute too quickly such that there is not neglible difference in time elapsed. Added a short 1 second sleep to prevent this from causing errors. --- src/test/java/seedu/duke/list/FoodListTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/seedu/duke/list/FoodListTest.java b/src/test/java/seedu/duke/list/FoodListTest.java index 1c011fbe86..5bf8129a2f 100644 --- a/src/test/java/seedu/duke/list/FoodListTest.java +++ b/src/test/java/seedu/duke/list/FoodListTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; import seedu.duke.food.Food; @@ -67,6 +68,13 @@ void dateFilterAfterTest() { list.getFoods().toString()); // add new entries too: + if (! LocalDateTime.now().isAfter(timeNow)) { // Execution is too fast that now() = timeNow. + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + System.out.println("Unexpected Interruption"); + } + } list.addFood(1, food); assertEquals(food.toString(), list.getFoodsAfterDateTime(timeNow).get(0).toString()); From 4c223c95129ffbd063f92fb0ed0385f109b31656 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 26 Oct 2020 19:08:44 +0800 Subject: [PATCH 175/374] update FoodListTest to run slower Executing too quickly affects use of LocalDateTime.now() in tests. --- src/test/java/seedu/duke/list/FoodListTest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/test/java/seedu/duke/list/FoodListTest.java b/src/test/java/seedu/duke/list/FoodListTest.java index 5bf8129a2f..e7bf197d88 100644 --- a/src/test/java/seedu/duke/list/FoodListTest.java +++ b/src/test/java/seedu/duke/list/FoodListTest.java @@ -67,7 +67,7 @@ void dateFilterAfterTest() { assertEquals(list.getFoodsAfterDateTime(LocalDateTime.MIN).toString(), list.getFoods().toString()); - // add new entries too: + // add new entries: if (! LocalDateTime.now().isAfter(timeNow)) { // Execution is too fast that now() = timeNow. try { TimeUnit.SECONDS.sleep(1); @@ -83,7 +83,18 @@ void dateFilterAfterTest() { @Test void dateFilterRangeTest() { LocalDateTime timeNow = LocalDateTime.now(); + + if (! LocalDateTime.now().isAfter(timeNow)) { // Execution is too fast that now() = timeNow. + try { + TimeUnit.SECONDS.sleep(1); + timeNow = LocalDateTime.now(); + } catch (InterruptedException e) { + System.out.println("Unexpected Interruption"); + } + } + assertTrue(list.getFoodsInDateTimeRange(timeNow, LocalDateTime.MAX).size() == 0); + assertEquals(list.getPortionedFoodsInDateTimeRange(LocalDateTime.MIN, timeNow).toString(), list.getPortionedFoods().toString()); } From 1b966fd6a3d0b352538c8aebca9a442638d40fab Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Tue, 27 Oct 2020 01:37:57 +0800 Subject: [PATCH 176/374] Bug Fixes and new sub-features Bug Fixes: -activity lvl .equal to prevent 22 from being accepted - info -> when last option input is a single char New Features: - name and info can only be accepted once - cant use other command if name and info not entered - name first then info ,,, fixed order --- src/main/java/seedu/dietbook/Manager.java | 2 ++ .../java/seedu/dietbook/checker/InputChecker.java | 12 +++++++----- src/main/java/seedu/dietbook/command/AddCommand.java | 8 +++++++- .../seedu/dietbook/command/CalculateCommand.java | 8 +++++++- .../java/seedu/dietbook/command/ClearCommand.java | 8 +++++++- src/main/java/seedu/dietbook/command/Command.java | 1 + .../java/seedu/dietbook/command/DataCommand.java | 8 +++++++- .../java/seedu/dietbook/command/DeleteCommand.java | 5 +++++ .../java/seedu/dietbook/command/HelpCommand.java | 8 +++++++- .../java/seedu/dietbook/command/InfoCommand.java | 7 +++++++ .../java/seedu/dietbook/command/ListCommand.java | 8 +++++++- .../java/seedu/dietbook/command/NameCommand.java | 7 ++++++- .../java/seedu/dietbook/command/UserinfoCommand.java | 10 +++++++++- 13 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index ad4c9be9f9..2b5bdf9dcc 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -1,5 +1,6 @@ package seedu.dietbook; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.command.AddCommand; import seedu.dietbook.command.CalculateCommand; import seedu.dietbook.command.ClearCommand; @@ -35,6 +36,7 @@ public class Manager { private Person person; private FoodList foodList; private String name; + private int commandCount = 1; private DataBase dataBase; private Calculator calculator; private static Scanner s = new Scanner(System.in); diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 53772478f4..c6f85f3680 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -44,11 +44,13 @@ public static void checkEmpty(String userInput, String command) throws DietExcep * @throws DietException when an option is specified but its field is empty. */ public static void checkEmptyOption(String[] input) throws DietException { - try { - if (input[1].trim().charAt(1) == '/') { - throw new DietException("Error! Option specified with empty field!"); + if (input.length > 1) { + if (input[1].length() > 1) { + if (input[1].trim().charAt(1) == '/') { + throw new DietException("Error! Option specified with empty field!"); + } } - } catch (IndexOutOfBoundsException e) { + } else { throw new DietException("Error! Option specified with empty field!"); } } @@ -122,7 +124,7 @@ public static void checkNutrientType(String userInput) throws DietException { public static void checkActivity(String userInput) throws DietException { boolean checkContain = false; for (String param: PARAM_ACTIVITY) { - if (userInput.contains(param)) { + if (userInput.equals(param)) { checkContain = true; } } diff --git a/src/main/java/seedu/dietbook/command/AddCommand.java b/src/main/java/seedu/dietbook/command/AddCommand.java index 6a7b6a7524..d5a6e1da9a 100644 --- a/src/main/java/seedu/dietbook/command/AddCommand.java +++ b/src/main/java/seedu/dietbook/command/AddCommand.java @@ -2,6 +2,7 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; public class AddCommand extends Command { String foodName; @@ -11,7 +12,12 @@ public AddCommand(String foodName) { } @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } ui.printNewFood(this.foodName); manager.setCalculator(); } diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index babeab4194..890aaa643f 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -2,6 +2,7 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; public class CalculateCommand extends Command { int calorie; @@ -19,7 +20,12 @@ public CalculateCommand(int calorie, int carb, int protein, int fat, String para } @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } manager.setCalculator(); switch (this.param) { case "all": diff --git a/src/main/java/seedu/dietbook/command/ClearCommand.java b/src/main/java/seedu/dietbook/command/ClearCommand.java index b07f6dda71..41f7939ab3 100644 --- a/src/main/java/seedu/dietbook/command/ClearCommand.java +++ b/src/main/java/seedu/dietbook/command/ClearCommand.java @@ -2,10 +2,16 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; public class ClearCommand extends Command { @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } ui.printClearFoodListMessage(); manager.getFoodList().clear(); } diff --git a/src/main/java/seedu/dietbook/command/Command.java b/src/main/java/seedu/dietbook/command/Command.java index 5282fc1990..43ff34400e 100644 --- a/src/main/java/seedu/dietbook/command/Command.java +++ b/src/main/java/seedu/dietbook/command/Command.java @@ -13,5 +13,6 @@ */ public abstract class Command { + public static int commandCount = 1; public abstract void execute(Manager manager, Ui ui) throws DietException; } diff --git a/src/main/java/seedu/dietbook/command/DataCommand.java b/src/main/java/seedu/dietbook/command/DataCommand.java index deb0767b9a..a5a708d898 100644 --- a/src/main/java/seedu/dietbook/command/DataCommand.java +++ b/src/main/java/seedu/dietbook/command/DataCommand.java @@ -2,11 +2,17 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; public class DataCommand extends Command { @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } manager.getDataBase().init(); //ui.printDatabase(manager.getDataBase().getFoodList()); } diff --git a/src/main/java/seedu/dietbook/command/DeleteCommand.java b/src/main/java/seedu/dietbook/command/DeleteCommand.java index 260635b297..4236acecab 100644 --- a/src/main/java/seedu/dietbook/command/DeleteCommand.java +++ b/src/main/java/seedu/dietbook/command/DeleteCommand.java @@ -13,6 +13,11 @@ public DeleteCommand(int index) { @Override public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } try { ui.printDeletedFood(manager.getFoodList().delete(this.index)); manager.setCalculator(); diff --git a/src/main/java/seedu/dietbook/command/HelpCommand.java b/src/main/java/seedu/dietbook/command/HelpCommand.java index 733cb3e3bb..75b081d4bd 100644 --- a/src/main/java/seedu/dietbook/command/HelpCommand.java +++ b/src/main/java/seedu/dietbook/command/HelpCommand.java @@ -2,10 +2,16 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; public class HelpCommand extends Command { @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } ui.printHelpCommandMessage(); } } diff --git a/src/main/java/seedu/dietbook/command/InfoCommand.java b/src/main/java/seedu/dietbook/command/InfoCommand.java index 8a7f215b13..98defd4e61 100644 --- a/src/main/java/seedu/dietbook/command/InfoCommand.java +++ b/src/main/java/seedu/dietbook/command/InfoCommand.java @@ -2,6 +2,7 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; @@ -14,7 +15,13 @@ public InfoCommand(String userInput) { @Override public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount != 2) { + throw new DietException("Basic information has already been input!"); + } Parser.executeProcessedInfo(this.userInput, manager); + commandCount++; ui.printInitialisationCompleteMessage(); } } diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java index 5e44014eec..d3e7e44541 100644 --- a/src/main/java/seedu/dietbook/command/ListCommand.java +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -2,10 +2,16 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; public class ListCommand extends Command { @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } ui.printFoodList(manager.getFoodList().toString()); } } diff --git a/src/main/java/seedu/dietbook/command/NameCommand.java b/src/main/java/seedu/dietbook/command/NameCommand.java index 11f6d44e3a..181e1084da 100644 --- a/src/main/java/seedu/dietbook/command/NameCommand.java +++ b/src/main/java/seedu/dietbook/command/NameCommand.java @@ -3,6 +3,7 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; public class NameCommand extends Command { @@ -13,7 +14,11 @@ public NameCommand(String name) { } @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount != 1) { + throw new DietException("Name has already been entered!"); + } + commandCount++; manager.setName(this.name); ui.printAskForUserInfoMessage(manager.getName()); } diff --git a/src/main/java/seedu/dietbook/command/UserinfoCommand.java b/src/main/java/seedu/dietbook/command/UserinfoCommand.java index 40c8fca39b..fe2bb1f786 100644 --- a/src/main/java/seedu/dietbook/command/UserinfoCommand.java +++ b/src/main/java/seedu/dietbook/command/UserinfoCommand.java @@ -2,10 +2,18 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; + +import java.io.DataInput; public class UserinfoCommand extends Command { @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } ui.printPersonInfo(manager.getPerson().toString()); } } From 7c604096db2f9d0c0a2816017d321a453d116ce0 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Tue, 27 Oct 2020 01:40:01 +0800 Subject: [PATCH 177/374] redundant import --- src/main/java/seedu/dietbook/Manager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 2b5bdf9dcc..a4e11aeed7 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -1,6 +1,5 @@ package seedu.dietbook; -import seedu.dietbook.checker.InputChecker; import seedu.dietbook.command.AddCommand; import seedu.dietbook.command.CalculateCommand; import seedu.dietbook.command.ClearCommand; From a9fdcf3cbf587d2327d30bfa10fbe3ba37eac5c3 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Tue, 27 Oct 2020 01:44:24 +0800 Subject: [PATCH 178/374] checkstyle --- src/main/java/seedu/dietbook/command/Command.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/seedu/dietbook/command/Command.java b/src/main/java/seedu/dietbook/command/Command.java index 43ff34400e..03a2d4d3ef 100644 --- a/src/main/java/seedu/dietbook/command/Command.java +++ b/src/main/java/seedu/dietbook/command/Command.java @@ -14,5 +14,6 @@ public abstract class Command { public static int commandCount = 1; + public abstract void execute(Manager manager, Ui ui) throws DietException; } From 1f4604153c01944483ebf03629b90c5963741dd4 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 02:21:05 +0800 Subject: [PATCH 179/374] Update faq and command summary section --- docs/UserGuide.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 0189022c3d..def08b3611 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -156,10 +156,11 @@ Example of usage: **Q**: How do I transfer my data to another computer? -**A**: {your answer here} +**A**: Either download `dietbook.jar` on the other computer and overwrite the empty data files with the data files from your previous computer or copy the whole DietBook home folder from the previous computer to the new computer. ## Command Summary -{Give a 'cheat sheet' of commands here} - -* Add todo `todo n/TODO_NAME d/DEADLINE` +Action | Format, Examples +---- | ---- +userinfo | `userinfo` +editinfo | `[n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]`
e.g.,`editinfo c/75 l/4` From 338a94ad1a422952408d203e50664931070dc4f2 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 02:21:29 +0800 Subject: [PATCH 180/374] Update quick start and features section --- docs/UserGuide.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index def08b3611..1f75db48a1 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -9,6 +9,11 @@ DietBook is a desktop application targeting NUS students optimized for use via a 1. Download the latest version of `dietbook.jar` from [here](https://github.com/AY2021S1-CS2113-T14-4/tp/releases). 1. Copy the file to the folder you want to use as the home folder for your DietBook. 1. Either double-click the jar file to start the application or navigate to the folder containing the jar file on command prompt and run the command `java -jar dietbook.jar`. +1. For first time users: + 1. A CLI similar to the one shown on the picture below should appear within a few seconds. +1. For existing user: + 1. A CLI similar to the one shown on the picture below should appear within a few seconds. +1. Refer to the Features section below for more details of each command. ## Features @@ -75,13 +80,12 @@ Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/ * 4 = You engage in vigorous exercise or have a physically demanding job. * 5 = You engage in extremely vigorous exercise or have an extremely physically demanding job. -Input examples: +Example of usage: -* `editinfo n/John` edits the updated the name stored in DietBook. -* Both `editinfo c/75 l/4` and `editinfo l/4 c/75` edits the current weight and activity level of the - user to be `75` and `You engage in vigorous exercise or have a physically demanding job.` respectively. +* `editinfo n/John` edits the name of the user to be `John`. +* Both `editinfo c/75 l/4` and `editinfo l/4 c/75` edits the current weight and activity level of the user to be `75` and `You engage in vigorous exercise or have a physically demanding job.` respectively. -Output example for input example 2: +Output example for usage example 2: ``` Got it! I've updated your personal information: Name: Jack From 430fe4534a3e3351974b51b5f368cd0e2ae3180e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 11:47:52 +0800 Subject: [PATCH 181/374] Shift readCommand method over to Ui --- src/main/java/seedu/dietbook/DietBook.java | 2 +- src/main/java/seedu/dietbook/Manager.java | 5 ----- src/main/java/seedu/dietbook/Ui.java | 15 +++++++++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 4bcdbf404d..d9dec101b7 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -40,7 +40,7 @@ public static void main(String[] args) throws FileNotFoundException { while (!isExit) { try { - String userInput = dietBook.manager.readCommand(); + String userInput = dietBook.ui.readCommand(); Command c = dietBook.manager.manage(userInput); c.execute(dietBook.manager, dietBook.ui); } catch (DietException e) { diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 856760e0c0..2445486059 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -38,7 +38,6 @@ public class Manager { private int commandCount = 1; private DataBase dataBase; private Calculator calculator; - private static Scanner s = new Scanner(System.in); public static final String COMMAND_ADD = "add"; public static final String COMMAND_CALCULATE = "calculate"; @@ -61,10 +60,6 @@ public Manager(FoodList foodlist, DataBase dataBase) { this.calculator = new Calculator(foodList.getFoods()); } - public String readCommand() { - return s.nextLine(); - } - public FoodList getFoodList() { return this.foodList; } diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index de395c1d5f..c5ff4030ec 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -5,15 +5,17 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Scanner; /** * Represents a text user interface. - * A Ui objects deals with user interaction by showing users the appropriate messages after a - * valid command is executed or when an error occurs. + * A Ui objects deals with user interaction by taking in user inputs and showing users the + * appropriate messages after a valid command is executed or when an error occurs. */ public class Ui { private static final String LINE_SEPARATOR = System.lineSeparator(); + private static Scanner scanner = new Scanner(System.in); /** * Constructs a Ui object. @@ -26,6 +28,15 @@ public Ui() { // Methods required to print system related commands or messages. + /** + * Reads in and returns the user input. + * + * @return The user input. + */ + public String readCommand() { + return scanner.nextLine(); + } + /** * Prints the welcome message from DietBook when it is fist booted up. */ From aeba42fcb718a23a708bc29a39ca8111b4fa3c9f Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Tue, 27 Oct 2020 14:43:48 +0800 Subject: [PATCH 182/374] Resolve merge conflicts Pulled changes from master to resolve conflicts --- .../java/seedu/dietbook/FoodListTest.java | 19 ------------------- .../java/seedu/dietbook/food/FoodTest.java | 1 - .../seedu/dietbook/list/FoodListTest.java | 4 ++-- 3 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 src/test/java/seedu/dietbook/FoodListTest.java diff --git a/src/test/java/seedu/dietbook/FoodListTest.java b/src/test/java/seedu/dietbook/FoodListTest.java deleted file mode 100644 index 1d3858913a..0000000000 --- a/src/test/java/seedu/dietbook/FoodListTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package seedu.dietbook; - -import seedu.dietbook.list.FoodList; -import seedu.dietbook.food.Food; -import org.junit.jupiter.api.Test; - - -class FoodListTest { - public static void main(String[] args) { - Food food = new Food("Kobe Beef", 480,50,40,30); - FoodList foodList = new FoodList(); - - System.out.println(foodList.addFood(3, food)); - System.out.println(foodList.addFood(2, "Sashimi", 100, 0, 30, 10)); - System.out.println(foodList); - System.out.println(foodList.delete(1)); - System.out.println(foodList); - } -} \ No newline at end of file diff --git a/src/test/java/seedu/dietbook/food/FoodTest.java b/src/test/java/seedu/dietbook/food/FoodTest.java index dea27acfa4..5e711152f0 100644 --- a/src/test/java/seedu/dietbook/food/FoodTest.java +++ b/src/test/java/seedu/dietbook/food/FoodTest.java @@ -1,6 +1,5 @@ package seedu.dietbook.food; -import seedu.dietbook.food.Food; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 99779fe16e..62c7f08ffc 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -1,10 +1,10 @@ -package seedu.duke.list; +package seedu.dietbook.list; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.assertEquals; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; class FoodListTest { From 0876b11678055935ec188ef0e4928decafe6f99c Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Tue, 27 Oct 2020 15:17:08 +0800 Subject: [PATCH 183/374] Resolve merge conflicts --- src/main/java/seedu/dietbook/list/DatedFoodEntry.java | 4 ++-- src/main/java/seedu/dietbook/list/FoodList.java | 4 ++-- src/main/java/seedu/dietbook/list/FoodListManager.java | 2 -- src/main/java/seedu/dietbook/list/ListFunction.java | 2 +- src/test/java/seedu/dietbook/list/FoodListTest.java | 4 ++-- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/dietbook/list/DatedFoodEntry.java b/src/main/java/seedu/dietbook/list/DatedFoodEntry.java index ad2c3f9395..484449ae52 100644 --- a/src/main/java/seedu/dietbook/list/DatedFoodEntry.java +++ b/src/main/java/seedu/dietbook/list/DatedFoodEntry.java @@ -1,6 +1,6 @@ -package seedu.duke.list; +package seedu.dietbook.list; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.time.LocalDateTime; public class DatedFoodEntry extends FoodEntry implements Comparable { diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index 42c5311cc3..bb646606b3 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -1,9 +1,9 @@ -package seedu.duke.list; +package seedu.dietbook.list; import java.util.ArrayList; import java.util.List; -import seedu.duke.food.Food; import java.time.LocalDateTime; +import seedu.dietbook.food.Food; /** diff --git a/src/main/java/seedu/dietbook/list/FoodListManager.java b/src/main/java/seedu/dietbook/list/FoodListManager.java index d3b660717e..2c35f45dc2 100644 --- a/src/main/java/seedu/dietbook/list/FoodListManager.java +++ b/src/main/java/seedu/dietbook/list/FoodListManager.java @@ -1,10 +1,8 @@ package seedu.dietbook.list; import seedu.dietbook.food.Food; -import java.util.ArrayList; import java.util.List; import java.util.function.Function; -import java.util.function.Consumer; import java.util.function.Predicate; import java.time.LocalDateTime; diff --git a/src/main/java/seedu/dietbook/list/ListFunction.java b/src/main/java/seedu/dietbook/list/ListFunction.java index 929066355c..2d6174dded 100644 --- a/src/main/java/seedu/dietbook/list/ListFunction.java +++ b/src/main/java/seedu/dietbook/list/ListFunction.java @@ -1,4 +1,4 @@ -package seedu.duke.list; +package seedu.dietbook.list; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index e7bf197d88..586eb5e811 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -1,4 +1,4 @@ -package seedu.duke.list; +package seedu.dietbook.list; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; @@ -7,8 +7,8 @@ import java.time.LocalDateTime; import java.util.concurrent.TimeUnit; +import seedu.dietbook.food.Food; -import seedu.duke.food.Food; class FoodListTest { From fe6f73068f73bd53fd1545dbc1cd491ba3837dfa Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Tue, 27 Oct 2020 16:23:09 +0800 Subject: [PATCH 184/374] Repeated option checker --- .../seedu/dietbook/checker/InputChecker.java | 22 ++++++++++++++++++- .../java/seedu/dietbook/parser/Parser.java | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index c5ad3a2ab8..b2dcc729c5 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -19,9 +19,10 @@ public class InputChecker { public static final int WEIGHT_CAP = 500; public static final String[] PARAM_ACTIVITY = {"1","2","3","4","5"}; public static final String[] PARAM_ADD = {"n/","x/","k/"}; + public static final String[] FULL_PARAM_ADD = {"n/","x/","k/","c/","p/","f/"}; public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; public static final String[] PARAM_GENDER = {"M","F","O"}; - public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; + public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/","c/"}; /** * Takes in user input and command to check for any expected parameters after the command. @@ -55,6 +56,25 @@ public static void checkEmptyOption(String[] input) throws DietException { } } + /** + * Takes in processed user input to check for options specified with an empty field. + * + * @param options option part of user input command. + * @throws DietException when an option is specified but its field is empty. + */ + public static void checkRepeatedOption(String command, String options) throws DietException { + String[] paramList = FULL_PARAM_ADD; + if (command.equals("info")) { + paramList = PARAM_INFO; + } + for (String param: paramList) { + int countOccurrence = options.length() - options.replace(param, "").length(); + if (countOccurrence > 2) { + throw new DietException("There are repeated options!"); + } + } + } + /** * Takes in user input to check if the expected number and type of parameter for the add command is present. * diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index cbda06f42a..99bfdcc386 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -80,6 +80,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws String trimmedParam; String[] processedParam; String[] paramList = {"x/", "n/", "k/", "c/", "p/", "f/"}; + InputChecker.checkRepeatedOption(getCommand(userInput), getCommandParam(userInput)); for (String param: paramList) { if (getCommandParam(userInput).contains(param)) { processedParam = getCommandParam(userInput).split(param); @@ -135,6 +136,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw int tarWeight = 0; String trimmedParam; String[] processedParam; + InputChecker.checkRepeatedOption(getCommand(userInput), getCommandParam(userInput)); for (String param: PARAM_INFO) { processedParam = getCommandParam(userInput).split(param); InputChecker.checkEmptyOption(processedParam); From 3a41a0543b2ec9fa3919ff3df84cd9af41ab5138 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Tue, 27 Oct 2020 17:47:33 +0800 Subject: [PATCH 185/374] Update InputChecker.java --- src/main/java/seedu/dietbook/checker/InputChecker.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index b2dcc729c5..669256a4a3 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -57,10 +57,11 @@ public static void checkEmptyOption(String[] input) throws DietException { } /** - * Takes in processed user input to check for options specified with an empty field. + * Takes in user input to check for repeated options. * + * @param command command part of user input. * @param options option part of user input command. - * @throws DietException when an option is specified but its field is empty. + * @throws DietException when there are options repeatedly specified. */ public static void checkRepeatedOption(String command, String options) throws DietException { String[] paramList = FULL_PARAM_ADD; From 3547101f93701fcfd2220cc363d913bb8a38ebc4 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 19:44:57 +0800 Subject: [PATCH 186/374] updated calculation of recommended daily calorie intake. assertion about the empty name. --- .../seedu/dietbook/calculator/Calculator.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 246983b3d6..b01eddcc6d 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -22,8 +22,11 @@ public class Calculator { * @param foodList foodList containing food items to calculate. */ public Calculator(ArrayList foodList) { - assert foodList != null : "the foodList should not be null."; + assert foodList != null : "The foodList should not be null."; + for (int i = 0; i < foodList.size(); i++) { + assert foodList.get(i).getName().trim().length() != 0 : "Food names should not be empty."; + totalCalorie += foodList.get(i).getCalorie(); totalCarbohydrate += foodList.get(i).getCarbohydrate(); totalProtein += foodList.get(i).getProtein(); @@ -86,23 +89,29 @@ public int calculateRecomendation(Person person) { case LOW: if (person.getGender() == Gender.MALE) { activityScore = 1.11; - } else { + } else if (person.getGender() == Gender.FEMALE) { activityScore = 1.12; + } else { + activityScore = 1.115; } break; case MEDIUM: if (person.getGender() == Gender.MALE) { activityScore = 1.26; - } else { + } else if (person.getGender() == Gender.FEMALE) { activityScore = 1.27; + } else { + activityScore = 1.265; } break; case HIGH: default: if (person.getGender() == Gender.MALE) { activityScore = 1.48; - } else { + } else if (person.getGender() == Gender.FEMALE) { activityScore = 1.45; + } else { + activityScore = 1.465; } break; } @@ -117,6 +126,8 @@ public int calculateRecomendation(Person person) { + 726 * person.getHeight() / 100; break; default: + requirement = 508 - 8.22 * person.getAge() + 12.635 * activityScore * person.getOriginalWeight() + + 632.8 * person.getHeight() / 100; } if (person.getOriginalWeight() > person.getTargetWeight()) { From bdf49d8bff083936f53fbc7e7363bc8393adfed8 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 20:47:30 +0800 Subject: [PATCH 187/374] Update introduction for user guide --- docs/UserGuide.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 1f75db48a1..ea3880e4ff 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,9 +1,11 @@ # DietBook User Guide ## Introduction +DietBook is a desktop application targeting NUS students living on campus, optimized for use via a **Command Line Interface**. Not only can DietBook track and show the user's food and nutritional intake, it also provides users with a list of commonly eaten food items around and outside NUS. + +* Table of Contents +{:toc} -DietBook is a desktop application targeting NUS students optimized for use via a **Command Line Interface**. Not only can DietBook track and show the user's food and nutritional intake, it also provides users with a list of commonly eaten food items around and outside NUS. - ## Quick Start 1. Ensure that you have Java 11 or above installed. 1. Download the latest version of `dietbook.jar` from [here](https://github.com/AY2021S1-CS2113-T14-4/tp/releases). From c7cc3c0d6f3c3f16f7af1804645c113335d07131 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 20:48:13 +0800 Subject: [PATCH 188/374] Update quick start of the user guide --- docs/UserGuide.md | 8 ++++---- docs/images/DietBookWelcomeMessage.PNG | Bin 0 -> 23353 bytes 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 docs/images/DietBookWelcomeMessage.PNG diff --git a/docs/UserGuide.md b/docs/UserGuide.md index ea3880e4ff..c53bb4592d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -12,10 +12,10 @@ DietBook is a desktop application targeting NUS students living on campus, optim 1. Copy the file to the folder you want to use as the home folder for your DietBook. 1. Either double-click the jar file to start the application or navigate to the folder containing the jar file on command prompt and run the command `java -jar dietbook.jar`. 1. For first time users: - 1. A CLI similar to the one shown on the picture below should appear within a few seconds. -1. For existing user: - 1. A CLI similar to the one shown on the picture below should appear within a few seconds. -1. Refer to the Features section below for more details of each command. + 1. A CLI, similar to the one shown below, should appear within a few seconds. Follow the instructions provided to setup DietBook or refer to [name](#Entering username: `name`) and [info](#Entering user information: `info`) for more detailed explanations.
+ ![DietBook Welcome Message](/images/DietBookWelcomeMessage.PNG) +1. Start using DietBook by typing any valid command and pressing Enter to execute it. +1. Refer to the [Features](#Features) section below for more details of each command. ## Features diff --git a/docs/images/DietBookWelcomeMessage.PNG b/docs/images/DietBookWelcomeMessage.PNG new file mode 100644 index 0000000000000000000000000000000000000000..483610d2e69bb9d38169e7faaad7e8c694e93471 GIT binary patch literal 23353 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 Date: Tue, 27 Oct 2020 20:48:42 +0800 Subject: [PATCH 189/374] added the methods to calculate food nutrients from food added within a certain time period (2 methods). --- .../seedu/dietbook/calculator/Calculator.java | 146 +++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index b01eddcc6d..9164ae7fce 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -1,10 +1,12 @@ package seedu.dietbook.calculator; import seedu.dietbook.food.Food; +import seedu.dietbook.list.FoodList; import seedu.dietbook.person.Gender; import seedu.dietbook.person.Person; -import java.util.ArrayList; +import java.time.LocalDateTime; +import java.util.List; /** * Represents a calculator of food items in foodList. @@ -21,7 +23,7 @@ public class Calculator { * * @param foodList foodList containing food items to calculate. */ - public Calculator(ArrayList foodList) { + public Calculator(List foodList) { assert foodList != null : "The foodList should not be null."; for (int i = 0; i < foodList.size(); i++) { @@ -43,6 +45,41 @@ public int calculateCalorie() { return totalCalorie; } + /** + * Returns an int type variable containing the value of total calorie + * of the foods with time after a specific time. + * + * @param startTime the start time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateCalorie(LocalDateTime startTime) { + int calorie = 0; + for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { + calorie += FoodList.getFoodsAfterDateTime(startTime).get(i).getCalorie(); + } + return calorie; + } + + /** + * Returns an int type variable containing the value of total calories + * of the foods with time after a specific time and before a specific time. + * + * @param startTime the start time for food items to be included. + * @param endTime the end time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateCalorie(LocalDateTime startTime, LocalDateTime endTime) { + int calorie = 0; + for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + calorie += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCalorie(); + } + return calorie; + } + /** * Returns an int type variable containing the value of total carbs. * @@ -52,6 +89,41 @@ public int calculateCarb() { return totalCarbohydrate; } + /** + * Returns an int type variable containing the value of total carbs + * of the foods with time after a specific time. + * + * @param startTime the start time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateCarb(LocalDateTime startTime) { + int carb = 0; + for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { + carb += FoodList.getFoodsAfterDateTime(startTime).get(i).getCarbohydrate(); + } + return carb; + } + + /** + * Returns an int type variable containing the value of total carbs + * of the foods with time after a specific time and before a specific time. + * + * @param startTime the start time for food items to be included. + * @param endTime the end time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateCarb(LocalDateTime startTime, LocalDateTime endTime) { + int carb = 0; + for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + carb += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCarbohydrate(); + } + return carb; + } + /** * Returns an int type variable containing the value of total protein. * @@ -61,6 +133,41 @@ public int calculateProtein() { return totalProtein; } + /** + * Returns an int type variable containing the value of total protein + * of the foods with time after a specific time. + * + * @param startTime the start time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateProtein(LocalDateTime startTime) { + int protein = 0; + for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { + protein += FoodList.getFoodsAfterDateTime(startTime).get(i).getProtein(); + } + return protein; + } + + /** + * Returns an int type variable containing the value of total protein + * of the foods with time after a specific time and before a specific time. + * + * @param startTime the start time for food items to be included. + * @param endTime the end time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateProtein(LocalDateTime startTime, LocalDateTime endTime) { + int protein = 0; + for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + protein += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getProtein(); + } + return protein; + } + /** * Returns an int type variable containing the value of total fats. * @@ -70,6 +177,41 @@ public int calculateFat() { return totalFat; } + /** + * Returns an int type variable containing the value of total fats + * of the foods with time after a specific time. + * + * @param startTime the start time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateFat(LocalDateTime startTime) { + int fat = 0; + for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { + fat += FoodList.getFoodsAfterDateTime(startTime).get(i).getFats(); + } + return fat; + } + + /** + * Returns an int type variable containing the value of total fats + * of the foods with time after a specific time and before a specific time. + * + * @param startTime the start time for food items to be included. + * @param endTime the end time for food items to be included. + * + * @return the value of total calorie of food items with time after + * startTime in foodList. + */ + public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { + int fat = 0; + for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + fat += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFats(); + } + return fat; + } + /** * Returns an int type variable containing the value of recommended daily calorie intake. * It is calculated based on the gender, activity level, age, height, original weight, From ab18fd742de4ee0f8978cf0627549a24691e3980 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 20:49:04 +0800 Subject: [PATCH 190/374] Update features section of the user guide --- docs/UserGuide.md | 73 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c53bb4592d..545bd8d34e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -35,13 +35,78 @@ e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` and `add x/1 n/mee` a e.g. `help` is a valid command but `Help` is not.
e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add N/mee x/1` is not. -* Spaces to separate command words, parameters, command word and parameters are important.
-e.g. For `calculate all`, `calculate all` is valid but `calculateall` is not.
+* A single spacing to separate command words, parameters, command word and parameters is required.
+e.g. For `calculate all`, `calculate all` is valid but `calculateall` and `calculate`         `all`is not.
e.g. For `delete INDEX`, `delete 1` is valid if there is a food item with index 1 but`delete1` is not.
e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add n/meex/1` is not.
### Features related to user information +#### Entering username: `name` + +Stores the user's name into DietBook during the initial setup. + +Format: `name YOUR_NAME` + +* The name given must not be empty. +* This command is **only used when setting up DietBook for the first time**. Any subsequent editing of the name can be done using the [editinfo](#Editing user information: `editinfo`) command. + +Example of usage: + +* `name Tom and Jerry`
+* `name Jack` + +Output example for usage example 2: +``` +Hi Jack! +Before we get started, I would like to know about about you so that I can make more +accurate calculations for you :). Therefore, could you please share with me the following: +- Your gender either F for female or M for male or O for others. +- Your age which is a positive integer. +- Your height in cm. +- Your original weight in kg, the weight when you first started using DietBook or you current weight. +- Your current weight in kg. +- Your target weight in kg, or your current weight if that is also your target weight. +- Your activity level, represented by a number from 1 to 5. + 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. + 2 = You engage in some form of light exercise or have a job that requires some physical activity. + 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. + 4 = You engage in vigorous exercise or have a physically demanding job. + 5 = You engage in extremely vigorous exercise or have an extremely physically demanding job. + +Please input your details in the following format: + info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL + Example: info g/F a/21 h/165 o/65 c/65 t/55 l/2 +``` + +#### Entering user information : `info` + +Stores the user's personal information into DietBook during the initial setup. + +Format: `info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL` + +* This command is **only used when setting up DietBook for the first time**. Any subsequent editing of user information can be done using the [editinfo](#Editing user information: `editinfo`) command. +* The gender must be either **`M` for male, `F` for female or `O` for others**. +* The age must be a positive integer **between 0 and 150**. +* The height in cm must be a positive integer **between 0 and 300**. +* The original, current and target weight in kg must be a positive integer **between 0 and 500**. +* The activity level must be a positive integer **from 1 to 5, inclusive**. + * 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. + * 2 = You engage in some form of light exercise or have a job that requires some physical activity. + * 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. + * 4 = You engage in vigorous exercise or have a physically demanding job. + * 5 = You engage in extremely vigorous exercise or have an extremely physically demanding job. + +Example of usage: + +* `info g/M a/21 h/175 o/85 c/85 t/75 l/2` stores the user's gender, age, height, original, current and target weight as well as the activity level to `male`, `21`, `175`, `85`, `85`, `75` and `You engage in some form of light exercise or have a job that requires some physical activity.` respectively. + +Output example: +``` +Thank you! DietBook has been initialised and you may start by entering any valid commands. +If you require a list of valid commands, you can enter: help +``` + #### Viewing user information: `userinfo` Shows the user information stored in DietBook. @@ -73,8 +138,8 @@ Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/ * The name must not be empty. * The gender must be either **`M` for male, `F` for female or `O` for others**. * The age must be a positive integer **between 0 and 150**. -* The height must be a positive integer **between 0 and 300**. -* The original, current and target weight must be a positive integer **between 0 and 500**. +* The height in cm must be a positive integer **between 0 and 300**. +* The original, current and target weight in kg must be a positive integer **between 0 and 500**. * The activity level must be a positive integer **from 1 to 5, inclusive**. * 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. * 2 = You engage in some form of light exercise or have a job that requires some physical activity. From 097b0b88bca0b9861692b53874315a840defa6ac Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 20:49:33 +0800 Subject: [PATCH 191/374] Update command summary of the user guide --- docs/UserGuide.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 545bd8d34e..e12bef5916 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -233,5 +233,7 @@ Example of usage: Action | Format, Examples ---- | ---- -userinfo | `userinfo` -editinfo | `[n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]`
e.g.,`editinfo c/75 l/4` +Enter name | **Note**: Used only when setting up DietBook for the first time.
`name YOUR_NAME`
e.g.,`name Jack` +Enter info | **Note**: Used only when setting up DietBook for the first time.
`info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL`
e.g.,`info g/M a/21 h/175 o/85 c/85 t/75 l/2` +View user info | `userinfo` +Edit user info | `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]`
e.g.,`editinfo c/75 l/4` From 766b291e048dc8eab4b89d570bf543254a4c5936 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 21:05:06 +0800 Subject: [PATCH 192/374] increased accuracy of the calculation by editing the case handling of activityScore. added 2 assertion statements for the initialization of activityScore and requirement. --- .../seedu/dietbook/calculator/Calculator.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 9164ae7fce..5a45177e08 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -223,7 +223,7 @@ public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { public int calculateRecomendation(Person person) { double requirement = 0; int recomendation; - double activityScore; + double activityScore = 0; switch (person.getActivityLevel()) { case NONE: activityScore = 1; @@ -247,7 +247,15 @@ public int calculateRecomendation(Person person) { } break; case HIGH: - default: + if (person.getGender() == Gender.MALE) { + activityScore = 1.37; + } else if (person.getGender() == Gender.FEMALE) { + activityScore = 1.36; + } else { + activityScore = 1.365; + } + break; + case EXTREME: if (person.getGender() == Gender.MALE) { activityScore = 1.48; } else if (person.getGender() == Gender.FEMALE) { @@ -256,6 +264,9 @@ public int calculateRecomendation(Person person) { activityScore = 1.465; } break; + default: + assert activityScore != 0 : "The activityScore should not be 0 if" + + "the activityLevel are one of five given cases."; } switch (person.getGender()) { @@ -267,9 +278,13 @@ public int calculateRecomendation(Person person) { requirement = 354 - 6.91 * person.getAge() + 9.36 * activityScore * person.getOriginalWeight() + 726 * person.getHeight() / 100; break; - default: + case OTHERS: requirement = 508 - 8.22 * person.getAge() + 12.635 * activityScore * person.getOriginalWeight() + 632.8 * person.getHeight() / 100; + break; + default: + assert requirement != 0 : "The requirement should not be 0 if the gender is " + + "ont of the three given cases."; } if (person.getOriginalWeight() > person.getTargetWeight()) { From a82772f7ddcc05b5f02a3b6532a0a8efd187b28f Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 21:43:45 +0800 Subject: [PATCH 193/374] changed from using original weight to calculate recommended daily calorie intake. --- src/main/java/seedu/dietbook/calculator/Calculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 5a45177e08..79ad349f60 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -287,7 +287,7 @@ public int calculateRecomendation(Person person) { + "ont of the three given cases."; } - if (person.getOriginalWeight() > person.getTargetWeight()) { + if (person.getCurrentWeight() > person.getTargetWeight()) { recomendation = (int) requirement - 300; } else { recomendation = (int) requirement + 100; From e6a6a998ab1ea025de976619d65a8cb19fb263dc Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 21:47:38 +0800 Subject: [PATCH 194/374] implement PersonSaveLoadManager allows saving and loading --- .../java/seedu/{duke => dietbook}/Duke.java | 2 +- .../{duke => dietbook}/database/Canteen.java | 2 +- .../{duke => dietbook}/database/DataBase.java | 4 +- .../{duke => dietbook}/database/Store.java | 5 +- .../{duke => dietbook}/database/data.txt | 0 .../seedu/{duke => dietbook}/food/Food.java | 2 +- .../saveload/EmptyLoader.java | 2 +- .../saveload/FileLoader.java | 2 +- .../saveload/FoodSaveLoadManager.java | 4 +- .../{duke => dietbook}/saveload/Loader.java | 2 +- .../saveload/PersonSaveLoadManager.java | 184 ++++++++++++++++++ .../{duke => dietbook}/saveload/Saver.java | 9 +- .../seedu/{duke => dietbook}/DukeTest.java | 2 +- .../database/DataBaseTest.java | 2 +- .../seedu/{duke => dietbook}/food/Food.java | 2 +- .../{duke => dietbook}/food/FoodTest.java | 4 +- .../FoodSaveLoadManagerManualTest.java | 4 +- .../saveload/FoodSaveLoadManagerTest.java | 4 +- .../saveload/SaveLoadFileTest.java | 2 +- .../saveload/SaverTest.java | 2 +- 20 files changed, 214 insertions(+), 26 deletions(-) rename src/main/java/seedu/{duke => dietbook}/Duke.java (96%) rename src/main/java/seedu/{duke => dietbook}/database/Canteen.java (94%) rename src/main/java/seedu/{duke => dietbook}/database/DataBase.java (99%) rename src/main/java/seedu/{duke => dietbook}/database/Store.java (89%) rename src/main/java/seedu/{duke => dietbook}/database/data.txt (100%) rename src/main/java/seedu/{duke => dietbook}/food/Food.java (97%) rename src/main/java/seedu/{duke => dietbook}/saveload/EmptyLoader.java (95%) rename src/main/java/seedu/{duke => dietbook}/saveload/FileLoader.java (98%) rename src/main/java/seedu/{duke => dietbook}/saveload/FoodSaveLoadManager.java (97%) rename src/main/java/seedu/{duke => dietbook}/saveload/Loader.java (94%) create mode 100644 src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java rename src/main/java/seedu/{duke => dietbook}/saveload/Saver.java (97%) rename src/test/java/seedu/{duke => dietbook}/DukeTest.java (88%) rename src/test/java/seedu/{duke => dietbook}/database/DataBaseTest.java (99%) rename src/test/java/seedu/{duke => dietbook}/food/Food.java (97%) rename src/test/java/seedu/{duke => dietbook}/food/FoodTest.java (75%) rename src/test/java/seedu/{duke => dietbook}/saveload/FoodSaveLoadManagerManualTest.java (94%) rename src/test/java/seedu/{duke => dietbook}/saveload/FoodSaveLoadManagerTest.java (96%) rename src/test/java/seedu/{duke => dietbook}/saveload/SaveLoadFileTest.java (96%) rename src/test/java/seedu/{duke => dietbook}/saveload/SaverTest.java (97%) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/dietbook/Duke.java similarity index 96% rename from src/main/java/seedu/duke/Duke.java rename to src/main/java/seedu/dietbook/Duke.java index 5c74e68d59..e2af504b4b 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/dietbook/Duke.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.dietbook; import java.util.Scanner; diff --git a/src/main/java/seedu/duke/database/Canteen.java b/src/main/java/seedu/dietbook/database/Canteen.java similarity index 94% rename from src/main/java/seedu/duke/database/Canteen.java rename to src/main/java/seedu/dietbook/database/Canteen.java index 1243b0d9be..51946e60a8 100644 --- a/src/main/java/seedu/duke/database/Canteen.java +++ b/src/main/java/seedu/dietbook/database/Canteen.java @@ -1,4 +1,4 @@ -package seedu.duke.database; +package seedu.dietbook.database; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java similarity index 99% rename from src/main/java/seedu/duke/database/DataBase.java rename to src/main/java/seedu/dietbook/database/DataBase.java index 74672a54ef..e9956f563e 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -1,7 +1,7 @@ -package seedu.duke.database; +package seedu.dietbook.database; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.io.File; import java.io.FileNotFoundException; diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/dietbook/database/Store.java similarity index 89% rename from src/main/java/seedu/duke/database/Store.java rename to src/main/java/seedu/dietbook/database/Store.java index af311ae856..63ff2fa3fa 100644 --- a/src/main/java/seedu/duke/database/Store.java +++ b/src/main/java/seedu/dietbook/database/Store.java @@ -1,9 +1,8 @@ -package seedu.duke.database; +package seedu.dietbook.database; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class Store { diff --git a/src/main/java/seedu/duke/database/data.txt b/src/main/java/seedu/dietbook/database/data.txt similarity index 100% rename from src/main/java/seedu/duke/database/data.txt rename to src/main/java/seedu/dietbook/database/data.txt diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/dietbook/food/Food.java similarity index 97% rename from src/main/java/seedu/duke/food/Food.java rename to src/main/java/seedu/dietbook/food/Food.java index c43d8f0384..a2d7027f61 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/dietbook/food/Food.java @@ -1,4 +1,4 @@ -package seedu.duke.food; +package seedu.dietbook.food; public class Food { diff --git a/src/main/java/seedu/duke/saveload/EmptyLoader.java b/src/main/java/seedu/dietbook/saveload/EmptyLoader.java similarity index 95% rename from src/main/java/seedu/duke/saveload/EmptyLoader.java rename to src/main/java/seedu/dietbook/saveload/EmptyLoader.java index 96e1f82240..d5c701bcb3 100644 --- a/src/main/java/seedu/duke/saveload/EmptyLoader.java +++ b/src/main/java/seedu/dietbook/saveload/EmptyLoader.java @@ -1,4 +1,4 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; import java.util.Optional; diff --git a/src/main/java/seedu/duke/saveload/FileLoader.java b/src/main/java/seedu/dietbook/saveload/FileLoader.java similarity index 98% rename from src/main/java/seedu/duke/saveload/FileLoader.java rename to src/main/java/seedu/dietbook/saveload/FileLoader.java index 70e20b7c72..9169e4df53 100644 --- a/src/main/java/seedu/duke/saveload/FileLoader.java +++ b/src/main/java/seedu/dietbook/saveload/FileLoader.java @@ -1,4 +1,4 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; import java.io.File; import java.io.FileNotFoundException; diff --git a/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java similarity index 97% rename from src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java rename to src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java index 6e070a7893..0d56dc1df0 100644 --- a/src/main/java/seedu/duke/saveload/FoodSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java @@ -1,6 +1,6 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.io.FileNotFoundException; import java.util.ArrayList; diff --git a/src/main/java/seedu/duke/saveload/Loader.java b/src/main/java/seedu/dietbook/saveload/Loader.java similarity index 94% rename from src/main/java/seedu/duke/saveload/Loader.java rename to src/main/java/seedu/dietbook/saveload/Loader.java index 076a5e7112..912431166e 100644 --- a/src/main/java/seedu/duke/saveload/Loader.java +++ b/src/main/java/seedu/dietbook/saveload/Loader.java @@ -1,4 +1,4 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; import java.io.FileNotFoundException; import java.util.Optional; diff --git a/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java new file mode 100644 index 0000000000..d0650997f8 --- /dev/null +++ b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java @@ -0,0 +1,184 @@ +package seedu.dietbook.saveload; + +import java.io.FileNotFoundException; + +/** + * This class is responsible for saving and loading personal information. + * It has setters and getters for the following fields : + * name, gender, age, height, original weight, current weight, target weight, activity level + * it has a method load which loads from a specified saved file + * it has a method save which saves the current information to a specified file name + */ +public class PersonSaveLoadManager { + private static final int NUM_OF_ENTRIES = 8; + private static final int TABLE_HEIGHT = 1; + private static final int PERSON_DATA_ROW = 1; + private static final String PERSON_FOLDER_NAME = "PERSONA_IS_NO_SUCH_PERSOOSOOSNSNSNS"; + + private static final int NAME_ENTRY_INDEX = 1; + private static final int GENDER_ENTRY_INDEX = 2; + private static final int AGE_ENTRY_INDEX = 3; + private static final int HEIGHT_ENTRY_INDEX = 4; + private static final int ORIGINAL_WEIGHT_ENTRY_INDEX = 5; + private static final int CURRENT_WEIGHT_ENTRY_INDEX = 6; + private static final int TARGET_WEIGHT_ENTRY_INDEX = 7; + private static final int ACTIVITY_LEVEL_ENTRY_INDEX = 8; + + private static final String DEFAULT_NAME = "Missing Name"; + private static final String DEFAULT_GENDER = "Others"; + private static final int DEFAULT_AGE = 0; + private static final int DEFAULT_HEIGHT = 0; + private static final int DEFAULT_ORIGINAL_WEIGHT = 0; + private static final int DEFAULT_CURRENT_WEIGHT = 0; + private static final int DEFAULT_TARGET_WEIGHT = 0; + private static final int DEFAULT_ACTIVITY_LEVEL = 0; + + private String name; + private String gender; + private int age; + private int height; + private int originalWeight; + private int currentWeight; + private int targetWeight; + private int activityLevel; + + private Saver saver; + private Loader fileLoader; + + public PersonSaveLoadManager() { + this.saver = new Saver(NUM_OF_ENTRIES, TABLE_HEIGHT); + this.fileLoader = Loader.loadEmpty(); + + this.name = DEFAULT_NAME; + this.gender = DEFAULT_GENDER; + this.age = DEFAULT_AGE; + this.height = DEFAULT_HEIGHT; + this.originalWeight = DEFAULT_ORIGINAL_WEIGHT; + this.currentWeight = DEFAULT_CURRENT_WEIGHT; + this.targetWeight = DEFAULT_TARGET_WEIGHT; + this.activityLevel = DEFAULT_ACTIVITY_LEVEL; + } + + /** + * loads a saved file and fill up all the fields with the data from the loaded file. + * set the field to default is the loaded file does not contain the field + * @param fileName name of the saved file to be loaded to + * @throws FileNotFoundException there is no save file with the name + * @throws IllegalAccessException the get method is called when the loader is empty, will never happen with the + * current implementation. + */ + public void load(String fileName) throws FileNotFoundException, IllegalAccessException { + this.reset(); + this.fileLoader = Loader.load(PERSON_FOLDER_NAME, fileName); + this.name = this.fileLoader.get(NAME_ENTRY_INDEX, PERSON_DATA_ROW).orElse(DEFAULT_NAME); + this.gender = this.fileLoader.get(GENDER_ENTRY_INDEX, PERSON_DATA_ROW).orElse(DEFAULT_GENDER); + this.age = Integer.parseInt(this.fileLoader.get(AGE_ENTRY_INDEX, PERSON_DATA_ROW).orElse( + Integer.toString(DEFAULT_AGE))); + this.height = Integer.parseInt(this.fileLoader.get(HEIGHT_ENTRY_INDEX, PERSON_DATA_ROW).orElse( + Integer.toString(DEFAULT_HEIGHT))); + this.originalWeight = Integer.parseInt(this.fileLoader.get(ORIGINAL_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW).orElse( + Integer.toString(DEFAULT_ORIGINAL_WEIGHT))); + this.currentWeight = Integer.parseInt(this.fileLoader.get(CURRENT_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW).orElse( + Integer.toString(DEFAULT_CURRENT_WEIGHT))); + this.targetWeight = Integer.parseInt(this.fileLoader.get(TARGET_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW).orElse( + Integer.toString(DEFAULT_TARGET_WEIGHT))); + this.activityLevel = Integer.parseInt(this.fileLoader.get(ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW).orElse( + Integer.toString(DEFAULT_ACTIVITY_LEVEL))); + + } + + /** + * Method saves the current data stored in the fields to the specified file name. + * Will over-write files with the same name. + * @param fileName name of the file to save to + */ + public void save(String fileName) { + this.saver.reset(); + this.saver.add(this.name, NAME_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(this.gender, GENDER_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.age), AGE_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.height), HEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.originalWeight), ORIGINAL_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.currentWeight), CURRENT_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.targetWeight), TARGET_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.currentWeight), ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.save(PERSON_FOLDER_NAME, fileName); + } + + public void reset() { + this.fileLoader = Loader.loadEmpty(); + this.name = DEFAULT_NAME; + this.gender = DEFAULT_GENDER; + this.age = DEFAULT_AGE; + this.height = DEFAULT_HEIGHT; + this.originalWeight = DEFAULT_ORIGINAL_WEIGHT; + this.currentWeight = DEFAULT_CURRENT_WEIGHT; + this.targetWeight = DEFAULT_TARGET_WEIGHT; + this.activityLevel = DEFAULT_ACTIVITY_LEVEL; + } + + // ----- Setters and Getters ------ + public String getName() { + return name; + } + + public String getGender() { + return gender; + } + + public int getAge() { + return age; + } + + public int getHeight() { + return height; + } + + public int getOriginalWeight() { + return originalWeight; + } + + public int getCurrentWeight() { + return currentWeight; + } + + public int getTargetWeight() { + return targetWeight; + } + + public int getActivityLevel() { + return activityLevel; + } + + public void setName(String name) { + this.name = name; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public void setAge(int age) { + this.age = age; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setOriginalWeight(int originalWeight) { + this.originalWeight = originalWeight; + } + + public void setCurrentWeight(int currentWeight) { + this.currentWeight = currentWeight; + } + + public void setTargetWeight(int targetWeight) { + this.targetWeight = targetWeight; + } + + public void setActivityLevel(int activityLevel) { + this.activityLevel = activityLevel; + } +} diff --git a/src/main/java/seedu/duke/saveload/Saver.java b/src/main/java/seedu/dietbook/saveload/Saver.java similarity index 97% rename from src/main/java/seedu/duke/saveload/Saver.java rename to src/main/java/seedu/dietbook/saveload/Saver.java index 8c2f5075ef..b8c08f3c65 100644 --- a/src/main/java/seedu/duke/saveload/Saver.java +++ b/src/main/java/seedu/dietbook/saveload/Saver.java @@ -1,4 +1,4 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; import java.io.File; import java.io.FileWriter; @@ -63,6 +63,13 @@ public void resetSize(int newWidth, int newHeight) { initEntries(); } + /** + * Clears the entire table. + */ + public void reset() { + initEntries(); + } + /** * Adds the string provided to the position x,y on the table. * @param entry the entry to be provided into this position diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/dietbook/DukeTest.java similarity index 88% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/dietbook/DukeTest.java index 2dda5fd651..eef4b4c86a 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/dietbook/DukeTest.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.dietbook; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/seedu/duke/database/DataBaseTest.java b/src/test/java/seedu/dietbook/database/DataBaseTest.java similarity index 99% rename from src/test/java/seedu/duke/database/DataBaseTest.java rename to src/test/java/seedu/dietbook/database/DataBaseTest.java index 116b4192bc..34a097a274 100644 --- a/src/test/java/seedu/duke/database/DataBaseTest.java +++ b/src/test/java/seedu/dietbook/database/DataBaseTest.java @@ -1,4 +1,4 @@ -package seedu.duke.database; +package seedu.dietbook.database; import java.io.FileNotFoundException; import java.util.NoSuchElementException; diff --git a/src/test/java/seedu/duke/food/Food.java b/src/test/java/seedu/dietbook/food/Food.java similarity index 97% rename from src/test/java/seedu/duke/food/Food.java rename to src/test/java/seedu/dietbook/food/Food.java index c43d8f0384..a2d7027f61 100644 --- a/src/test/java/seedu/duke/food/Food.java +++ b/src/test/java/seedu/dietbook/food/Food.java @@ -1,4 +1,4 @@ -package seedu.duke.food; +package seedu.dietbook.food; public class Food { diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/dietbook/food/FoodTest.java similarity index 75% rename from src/test/java/seedu/duke/food/FoodTest.java rename to src/test/java/seedu/dietbook/food/FoodTest.java index 97b646a032..d423c7f1d0 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/dietbook/food/FoodTest.java @@ -1,6 +1,4 @@ -package seedu.duke.food; - -import org.junit.jupiter.api.Test; +package seedu.dietbook.food; class FoodTest { diff --git a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java b/src/test/java/seedu/dietbook/saveload/FoodSaveLoadManagerManualTest.java similarity index 94% rename from src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java rename to src/test/java/seedu/dietbook/saveload/FoodSaveLoadManagerManualTest.java index 4f07f241f0..e9f70ce87e 100644 --- a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerManualTest.java +++ b/src/test/java/seedu/dietbook/saveload/FoodSaveLoadManagerManualTest.java @@ -1,6 +1,6 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.io.FileNotFoundException; import java.util.ArrayList; diff --git a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/FoodSaveLoadManagerTest.java similarity index 96% rename from src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java rename to src/test/java/seedu/dietbook/saveload/FoodSaveLoadManagerTest.java index e286b9bb45..5e885fe969 100644 --- a/src/test/java/seedu/duke/saveload/FoodSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/FoodSaveLoadManagerTest.java @@ -1,8 +1,8 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java b/src/test/java/seedu/dietbook/saveload/SaveLoadFileTest.java similarity index 96% rename from src/test/java/seedu/duke/saveload/SaveLoadFileTest.java rename to src/test/java/seedu/dietbook/saveload/SaveLoadFileTest.java index 1b09acfb81..26f2d7d5de 100644 --- a/src/test/java/seedu/duke/saveload/SaveLoadFileTest.java +++ b/src/test/java/seedu/dietbook/saveload/SaveLoadFileTest.java @@ -1,4 +1,4 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; import java.io.FileNotFoundException; diff --git a/src/test/java/seedu/duke/saveload/SaverTest.java b/src/test/java/seedu/dietbook/saveload/SaverTest.java similarity index 97% rename from src/test/java/seedu/duke/saveload/SaverTest.java rename to src/test/java/seedu/dietbook/saveload/SaverTest.java index 9fbe56e768..ac6d960142 100644 --- a/src/test/java/seedu/duke/saveload/SaverTest.java +++ b/src/test/java/seedu/dietbook/saveload/SaverTest.java @@ -1,4 +1,4 @@ -package seedu.duke.saveload; +package seedu.dietbook.saveload; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 2bb79bede802e9ac56ca2b58a9cbb76255190000 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 21:50:29 +0800 Subject: [PATCH 195/374] automatically changes. --- src/main/java/seedu/dietbook/DietBook.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 24128b4a51..f1ed8f95cb 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -1,10 +1,10 @@ package seedu.dietbook; -import java.io.FileNotFoundException; -import java.io.IOException; import seedu.dietbook.database.DataBase; import seedu.dietbook.list.FoodList; +import java.io.FileNotFoundException; + public class DietBook { private FoodList foodList; private Ui ui; From f5695d95e30fd1c4534da760c90721c705dd3da9 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 21:57:11 +0800 Subject: [PATCH 196/374] Remove magic numbers --- .../java/seedu/dietbook/person/Person.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index 6fd3f52ebb..da30de95a0 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -288,8 +288,10 @@ private void performAssertionsForActivityLevel(ActivityLevel activityLevel) { * @param weightType A string describing whether the weight given the original, current or target weight. */ private void performAssertionsForWeight(int weight, String weightType) { - assert weight > 0 : weightType + " of person should be greater than 0"; - assert weight < 500 : weightType + " of person should less than 500"; + int minWeight = 1; + assert weight >= minWeight : weightType + " of person should be greater than 0"; + int maxWeight = 500; + assert weight < maxWeight : weightType + " of person should less than 500"; } /** @@ -298,8 +300,10 @@ private void performAssertionsForWeight(int weight, String weightType) { * @param height The height of the person. */ private void performAssertionsForHeight(int height) { - assert height > 0 : "Height of person should be greater than 0"; - assert height < 300 : "Height of person should be less than 300"; + int minHeight = 1; + assert height >= minHeight : "Height of person should be greater than 0"; + int maxHeight = 300; + assert height < maxHeight : "Height of person should be less than 300"; } /** @@ -327,7 +331,9 @@ private void performAssertionsForNameInput(String name) { * @param age The age of the person. */ private void performAssertionsForAgeInput(int age) { - assert age > 0 : "The age of person should be greater than 0"; - assert age < 150 : "The age of person should be lesser than 150"; + int minAge = 1; + assert age >= minAge : "The age of person should be greater than 0"; + int maxAge = 150; + assert age < maxAge : "The age of person should be lesser than 150"; } } From bffeea849f6ef7fc1678d4f669fb73f4203e8650 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 22:00:04 +0800 Subject: [PATCH 197/374] Update minimum acceptable height and weight --- src/main/java/seedu/dietbook/checker/InputChecker.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index c5ad3a2ab8..b521fcaf0f 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -172,8 +172,8 @@ public static void checkAgeLimit(int age) throws DietException { * @throws DietException when value is not within the limit. */ public static void checkHeightLimit(int height) throws DietException { - if (height < 0) { - throw new DietException("Input value cannot be less than 0!"); + if (height < 1) { + throw new DietException("Input value cannot be less than 1"); } else if (height > HEIGHT_CAP) { throw new DietException("Input value cannot be more than 273!"); } @@ -186,8 +186,8 @@ public static void checkHeightLimit(int height) throws DietException { * @throws DietException when value is not within the limit. */ public static void checkWeightLimit(int weight) throws DietException { - if (weight < 0) { - throw new DietException("Input value cannot be less than 0!"); + if (weight < 1) { + throw new DietException("Input value cannot be less than 1!"); } else if (weight > WEIGHT_CAP) { throw new DietException("Input value cannot be more than 443!"); } From b1c23eca395e0ea1201593ab91bf1144a2e320c6 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 22:00:35 +0800 Subject: [PATCH 198/374] Change minimum age to zero --- src/main/java/seedu/dietbook/person/Person.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index da30de95a0..a257ff8ba3 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -331,8 +331,8 @@ private void performAssertionsForNameInput(String name) { * @param age The age of the person. */ private void performAssertionsForAgeInput(int age) { - int minAge = 1; - assert age >= minAge : "The age of person should be greater than 0"; + int minAge = 0; + assert age >= minAge : "The age of person should be equals to or greater than 0"; int maxAge = 150; assert age < maxAge : "The age of person should be lesser than 150"; } From 665480bcf2f4d3ac283f8c734c8155d9ed6585e9 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 22:06:23 +0800 Subject: [PATCH 199/374] Update assertions --- src/main/java/seedu/dietbook/person/Person.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index a257ff8ba3..a0e02c7e6b 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -291,7 +291,7 @@ private void performAssertionsForWeight(int weight, String weightType) { int minWeight = 1; assert weight >= minWeight : weightType + " of person should be greater than 0"; int maxWeight = 500; - assert weight < maxWeight : weightType + " of person should less than 500"; + assert weight <= maxWeight : weightType + " of person should less than 500"; } /** @@ -303,7 +303,7 @@ private void performAssertionsForHeight(int height) { int minHeight = 1; assert height >= minHeight : "Height of person should be greater than 0"; int maxHeight = 300; - assert height < maxHeight : "Height of person should be less than 300"; + assert height <= maxHeight : "Height of person should be less than 300"; } /** @@ -334,6 +334,6 @@ private void performAssertionsForAgeInput(int age) { int minAge = 0; assert age >= minAge : "The age of person should be equals to or greater than 0"; int maxAge = 150; - assert age < maxAge : "The age of person should be lesser than 150"; + assert age <= maxAge : "The age of person should be lesser than 150"; } } From 9979d41bcaf99e38fce58cd8fa962980538d9eda Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:12:59 +0800 Subject: [PATCH 200/374] add Junit test --- .../saveload/PersonSaveLoadManagerTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java diff --git a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java new file mode 100644 index 0000000000..bad761d44e --- /dev/null +++ b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java @@ -0,0 +1,21 @@ +package seedu.dietbook.saveload; + +import org.junit.jupiter.api.BeforeEach; + +import static org.junit.jupiter.api.Assertions.*; + +class PersonSaveLoadManagerTest { + private static PersonSaveLoadManager pslTest; + + @BeforeEach + private void setUp(){ + pslTest = new PersonSaveLoadManager(); + pslTest.setName("Victor Chng"); + pslTest.setActivityLevel(0); + pslTest.setGender("UnKnown"); + pslTest.setAge(100); + pslTest.setOriginalWeight(200); + pslTest.setCurrentWeight(300); + pslTest.setTargetWeight(100); + } +} \ No newline at end of file From 12ce0a3de87eb65abd2503167903a6a0fa3aa213 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 22:14:25 +0800 Subject: [PATCH 201/374] Update acceptable values for some parameters --- docs/UserGuide.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index e12bef5916..f5988acfa2 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -87,9 +87,9 @@ Format: `info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGE * This command is **only used when setting up DietBook for the first time**. Any subsequent editing of user information can be done using the [editinfo](#Editing user information: `editinfo`) command. * The gender must be either **`M` for male, `F` for female or `O` for others**. -* The age must be a positive integer **between 0 and 150**. -* The height in cm must be a positive integer **between 0 and 300**. -* The original, current and target weight in kg must be a positive integer **between 0 and 500**. +* The age must be a positive integer **from 0 to 150, inclusive**. +* The height in cm must be a positive integer **from 1 to 300, inclusive**. +* The original, current and target weight in kg must be a positive integer ***from 1 to 500, inclusive**. * The activity level must be a positive integer **from 1 to 5, inclusive**. * 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. * 2 = You engage in some form of light exercise or have a job that requires some physical activity. @@ -137,9 +137,9 @@ Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/ * Existing values will be updated to the input values. * The name must not be empty. * The gender must be either **`M` for male, `F` for female or `O` for others**. -* The age must be a positive integer **between 0 and 150**. -* The height in cm must be a positive integer **between 0 and 300**. -* The original, current and target weight in kg must be a positive integer **between 0 and 500**. +* The age must be a positive integer **from 0 to 150, inclusive**. +* The height must be a positive integer **from 1 to 300, inclusive**. +* The original, current and target weight must be a positive integer **from 1 to 500, inclusive**. * The activity level must be a positive integer **from 1 to 5, inclusive**. * 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. * 2 = You engage in some form of light exercise or have a job that requires some physical activity. From 82d1edf9e736284fe7b04cf4ccc5a3c252342628 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 22:15:09 +0800 Subject: [PATCH 202/374] deleted classes not included in the current main from tp_repo --- .../java/seedu/calculator/Calculator.java | 67 ------ src/main/java/seedu/dietbook/Parser.java | 212 ------------------ .../seedu/dietbook/calculator/Calculator.java | 6 +- 3 files changed, 3 insertions(+), 282 deletions(-) delete mode 100644 src/main/java/seedu/calculator/Calculator.java delete mode 100644 src/main/java/seedu/dietbook/Parser.java diff --git a/src/main/java/seedu/calculator/Calculator.java b/src/main/java/seedu/calculator/Calculator.java deleted file mode 100644 index 44a3b54dc6..0000000000 --- a/src/main/java/seedu/calculator/Calculator.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.calculator; - -import seedu.dietbook.food.Food; - -import java.util.ArrayList; - -/** - * Represents a calculator of food items in foodList. - */ -public class Calculator { - private int totalCalorie = 0; - private int totalCarbohydrate = 0; - private int totalProtein = 0; - private int totalFat = 0; - - /** - * Construct a calculator taking in a foodList. Add up calories, - * carbs, protein, and fats in each food item. - * - * @param foodList foodList containing food items to calculate. - */ - public Calculator(ArrayList foodList) { - assert foodList != null : "the foodList should not be null."; - for (int i = 0; i < foodList.size(); i++) { - totalCalorie += foodList.get(i).getCalorie(); - totalCarbohydrate += foodList.get(i).getCarbohydrate(); - totalProtein += foodList.get(i).getProtein(); - totalFat += foodList.get(i).getFat(); - } - } - - /** - * Returns an int type variable containing the value of total calorie. - * - * @return the value of total calorie of food items in foodList. - */ - public int calculateCalorie() { - return totalCalorie; - } - - /** - * Returns an int type variable containing the value of total carbs. - * - * @return the value of total carbs of food items in foodList. - */ - public int calculateCarb() { - return totalCarbohydrate; - } - - /** - * Returns an int type variable containing the value of total protein. - * - * @return the value of total protein of food items in foodList. - */ - public int calculateProtein() { - return totalProtein; - } - - /** - * Returns an int type variable containing the value of total fats. - * - * @return the value of total fats of food items in foodList. - */ - public int calculateFat() { - return totalFat; - } -} diff --git a/src/main/java/seedu/dietbook/Parser.java b/src/main/java/seedu/dietbook/Parser.java deleted file mode 100644 index 2486e212be..0000000000 --- a/src/main/java/seedu/dietbook/Parser.java +++ /dev/null @@ -1,212 +0,0 @@ -package seedu.dietbook; - -import seedu.dietbook.calculator.Calculator; -import seedu.dietbook.list.FoodList; -import seedu.dietbook.person.Gender; -import seedu.dietbook.person.ActivityLevel; - -import java.io.FileNotFoundException; - -public class Parser { - public static final String COMMAND_NAME = "name"; - public static final String COMMAND_LIST = "list"; - public static final String COMMAND_INFO = "info"; - public static final String COMMAND_EXIT = "exit"; - public static final String COMMAND_ADD = "add"; - public static final String COMMAND_CLEAR = "clear"; - public static final String COMMAND_DELETE = "delete"; - public static final String COMMAND_CALCULATE = "calculate"; - public static final String COMMAND_DATA = "data"; - public static final String COMMAND_USERINFO = "userinfo"; - public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; - public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/"}; - public static final String[] PARAM_ADD = {"n/","x/","k/","f/","p/","c/"}; - - - /** - * Returns the command of a user input. - * @param userInput which is user input. - * @return First word which is the command of the user input. - */ - private static String getCommand(String userInput) { - return userInput.split(" ")[0]; - } - - /** - * Returns the subsequent parameter after the command from the user input. - * @param userInput user input. - * @return parameter part of the user input. - * @throws DietException when the user input is of a wrong format. - */ - private static String getCommandParam(String userInput) throws DietException { - String command = getCommand(userInput); - String[] input = {userInput}; - - if (userInput.split(command).length < 2 - || userInput.split(command)[1].equals(" ")) { - throw new DietException("☹ Error! Missing command parameters!"); - } else { - switch (command) { - case COMMAND_NAME: - return userInput.split(" ")[1]; - case COMMAND_CALCULATE: - for (String param: PARAM_CALCULATE) { - if (userInput.contains(param)) { - return userInput.split(" ")[1]; - } - } - throw new DietException("☹ Incorrect nutrient type"); - case COMMAND_ADD: - for (String param: PARAM_ADD) { - if (!userInput.contains(param)) { - throw new DietException("☹ Missing or incorrect add statement"); - } - } - return userInput.substring(userInput.indexOf(' ') + 1); - case COMMAND_INFO: - for (String param: PARAM_INFO) { - if (!userInput.contains(param)) { - throw new DietException("☹ Missing or incorrect info statement"); - } - } - return userInput.substring(userInput.indexOf(' ') + 1); - default: - return null; - } - } - } - - /** - * Processes the parameters for add command of user input and adds a Food object. - * @param userInput user input. - * @param foodList the FoodList object. - * @return name of the food that was added. - * @throws DietException when the user input is of a wrong format. - */ - private static String getProcessedAdd(String userInput, FoodList foodList) throws DietException { - String[] processedParam = getCommandParam(userInput).split(" "); - int portionSize = Integer.parseInt(processedParam[0].substring(processedParam[0].indexOf("/") + 1)); - String foodName = processedParam[1].substring(processedParam[1].indexOf("/") + 1); - int calorie = Integer.parseInt(processedParam[2].substring(processedParam[2].indexOf("/") + 1)); - int carb = Integer.parseInt(processedParam[3].substring(processedParam[3].indexOf("/") + 1)); - int protein = Integer.parseInt(processedParam[4].substring(processedParam[4].indexOf("/") + 1)); - int fat = Integer.parseInt(processedParam[5].substring(processedParam[5].indexOf("/") + 1)); - foodList.addFood(portionSize, foodName, calorie, carb, protein, fat); - return foodName; - } - - /** - * Processes the parameters for info command of user input and updates the Person object. - * @param userInput user input. - * @param manager the manager object. - * @throws DietException when the user input is of a wrong format. - */ - private static void executeProcessedInfo(String userInput, Manager manager) throws DietException { - Gender gender; - ActivityLevel actLvl; - String[] processedParam = getCommandParam(userInput).split(" "); - String processGender = processedParam[0].substring(processedParam[0].indexOf("/") + 1); - if (processGender.equals("M")) { - gender = Gender.MALE; - } else { - gender = Gender.FEMALE; - } - int age = Integer.parseInt(processedParam[1].substring(processedParam[1].indexOf("/") + 1)); - int height = Integer.parseInt(processedParam[2].substring(processedParam[2].indexOf("/") + 1)); - int orgWeight = Integer.parseInt(processedParam[3].substring(processedParam[3].indexOf("/") + 1)); - int tarWeight = Integer.parseInt(processedParam[4].substring(processedParam[4].indexOf("/") + 1)); - String processActLvl = processedParam[5].substring(processedParam[5].indexOf("/") + 1); - if (processActLvl.equals("1")) { - actLvl = ActivityLevel.NONE; - } else if (processActLvl.equals("2")) { - actLvl = ActivityLevel.LOW; - } else if (processActLvl.equals("3")) { - actLvl = ActivityLevel.MEDIUM; - } else if (processActLvl.equals("4")) { - actLvl = ActivityLevel.HIGH; - } else { - actLvl = ActivityLevel.EXTREME; - } - manager.setPerson(manager.getName(), gender, age, height, orgWeight, tarWeight, actLvl); - } - - /** - * Returns the index after the command of a user input, e.g. delete 3. - * @param userInput user input. - * @return index part of the user input. - * @throws DietException when the user input is of a wrong format. - */ - private static int getCommandIndex(String userInput) throws DietException { - String command = getCommand(userInput); - - if (userInput.split(command).length < 2 || userInput.split(command)[1].equals(" ")) { - throw new DietException("☹ OOPS!!! Missing index of duke.task!"); - } - try { - return Integer.parseInt(userInput.split(" ")[1]); - } catch (NumberFormatException e) { - throw new DietException("☹ OOPS!!! No integer index detected!"); - } - } - - /** - * Makes sense of the user input and carries out the functions according to the command given. - * @param userInput user input. - * @throws DietException when the program does not recognize the command given. - */ - public static void parse(String userInput, Manager manager, Ui ui) throws DietException, FileNotFoundException { - Calculator calculator = manager.getCalculator(); - switch (getCommand(userInput)) { - case COMMAND_NAME: - manager.setName(getCommandParam(userInput)); - ui.printAskForUserInfoMessage(manager.getName()); - return; - case COMMAND_EXIT: - ui.printExitMessage(manager.getName()); - DietBook.isExit = true; - return; - case COMMAND_LIST: - ui.printFoodList(manager.getFoodList().toString()); - return; - case COMMAND_USERINFO: - ui.printPersonInfo(manager.getPerson().toString()); - return; - case COMMAND_DATA: - manager.getDataBase().init(); - ui.printDatabase(manager.getDataBase().getFoodList()); - return; - case COMMAND_DELETE: - ui.printDeletedFood(manager.getFoodList().delete(getCommandIndex(userInput))); - manager.setCalculator(); - return; - case COMMAND_CLEAR: - ui.printClearFoodListMessage(); - manager.getFoodList().clear(); - return; - case COMMAND_CALCULATE: - if (getCommandParam(userInput).equals("all")) { - ui.printAllNutrientIntake(calculator.calculateCalorie(), calculator.calculateCarb(), - calculator.calculateProtein(), calculator.calculateFat()); - } else if (getCommandParam(userInput).equals("calorie")) { - ui.printCalorieIntake(calculator.calculateCalorie()); - } else if (getCommandParam(userInput).equals("carbohydrate")) { - ui.printCarbohydrateIntake(calculator.calculateCarb()); - } else if (getCommandParam(userInput).equals("protein")) { - ui.printProteinIntake(calculator.calculateProtein()); - } else { - ui.printFatIntake(calculator.calculateFat()); - } - return; - case COMMAND_INFO: - executeProcessedInfo(userInput, manager); - ui.printTutorialMessage(); - return; - case COMMAND_ADD: - ui.printNewFood(getProcessedAdd(userInput, manager.getFoodList())); - manager.setCalculator(); - return; - default: - throw new DietException("☹ There's no such command!"); - } - } -} diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 79ad349f60..e8304fe278 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -32,7 +32,7 @@ public Calculator(List foodList) { totalCalorie += foodList.get(i).getCalorie(); totalCarbohydrate += foodList.get(i).getCarbohydrate(); totalProtein += foodList.get(i).getProtein(); - totalFat += foodList.get(i).getFats(); + totalFat += foodList.get(i).getFat(); } } @@ -189,7 +189,7 @@ public int calculateFat() { public int calculateFat(LocalDateTime startTime) { int fat = 0; for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { - fat += FoodList.getFoodsAfterDateTime(startTime).get(i).getFats(); + fat += FoodList.getFoodsAfterDateTime(startTime).get(i).getFat(); } return fat; } @@ -207,7 +207,7 @@ public int calculateFat(LocalDateTime startTime) { public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { int fat = 0; for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - fat += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFats(); + fat += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFat(); } return fat; } From b43b1acac52a7bce2d789ea8d31d221a57746b55 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:17:01 +0800 Subject: [PATCH 203/374] no message --- src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java | 2 +- src/test/java/seedu/dietbook/food/Food.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java index 0d56dc1df0..c866437c4a 100644 --- a/src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/FoodSaveLoadManager.java @@ -71,7 +71,7 @@ public void save(String fileName, List foodlist) { saver.add(Integer.toString(foodlist.get(j - 1).getCalorie()), 2, j); saver.add(Integer.toString(foodlist.get(j - 1).getCarbohydrate()), 3, j); saver.add(Integer.toString(foodlist.get(j - 1).getProtein()), 4, j); - saver.add(Integer.toString(foodlist.get(j - 1).getFats()), 5, j); + saver.add(Integer.toString(foodlist.get(j - 1).getFat()), 5, j); } saver.save(FOOD_FOLDER_NAME, fileName); } diff --git a/src/test/java/seedu/dietbook/food/Food.java b/src/test/java/seedu/dietbook/food/Food.java index a2d7027f61..505588e82b 100644 --- a/src/test/java/seedu/dietbook/food/Food.java +++ b/src/test/java/seedu/dietbook/food/Food.java @@ -16,7 +16,7 @@ public Food(String name, int calorie, int carbohydrate, int protein, int fats) { this.fats = fats; } - public int getFats() { + public int getFat() { return fats; } From 4efc2637e2e82c296445967a74c9b0eb1cfe0023 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:19:18 +0800 Subject: [PATCH 204/374] change directory --- src/main/java/seedu/{duke => dietbook}/Duke.java | 2 +- src/main/java/seedu/{duke => dietbook}/database/Canteen.java | 2 +- .../java/seedu/{duke => dietbook}/database/DataBase.java | 4 ++-- src/main/java/seedu/{duke => dietbook}/database/Store.java | 5 ++--- src/main/java/seedu/{duke => dietbook}/database/data.txt | 0 src/main/java/seedu/{duke => dietbook}/food/Food.java | 2 +- src/test/java/seedu/{duke => dietbook}/DukeTest.java | 2 +- .../java/seedu/{duke => dietbook}/database/DataBaseTest.java | 2 +- src/test/java/seedu/{duke => dietbook}/food/FoodTest.java | 4 +--- 9 files changed, 10 insertions(+), 13 deletions(-) rename src/main/java/seedu/{duke => dietbook}/Duke.java (96%) rename src/main/java/seedu/{duke => dietbook}/database/Canteen.java (94%) rename src/main/java/seedu/{duke => dietbook}/database/DataBase.java (99%) rename src/main/java/seedu/{duke => dietbook}/database/Store.java (89%) rename src/main/java/seedu/{duke => dietbook}/database/data.txt (100%) rename src/main/java/seedu/{duke => dietbook}/food/Food.java (97%) rename src/test/java/seedu/{duke => dietbook}/DukeTest.java (88%) rename src/test/java/seedu/{duke => dietbook}/database/DataBaseTest.java (99%) rename src/test/java/seedu/{duke => dietbook}/food/FoodTest.java (75%) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/dietbook/Duke.java similarity index 96% rename from src/main/java/seedu/duke/Duke.java rename to src/main/java/seedu/dietbook/Duke.java index 5c74e68d59..e2af504b4b 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/dietbook/Duke.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.dietbook; import java.util.Scanner; diff --git a/src/main/java/seedu/duke/database/Canteen.java b/src/main/java/seedu/dietbook/database/Canteen.java similarity index 94% rename from src/main/java/seedu/duke/database/Canteen.java rename to src/main/java/seedu/dietbook/database/Canteen.java index 1243b0d9be..51946e60a8 100644 --- a/src/main/java/seedu/duke/database/Canteen.java +++ b/src/main/java/seedu/dietbook/database/Canteen.java @@ -1,4 +1,4 @@ -package seedu.duke.database; +package seedu.dietbook.database; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/seedu/duke/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java similarity index 99% rename from src/main/java/seedu/duke/database/DataBase.java rename to src/main/java/seedu/dietbook/database/DataBase.java index 74672a54ef..e9956f563e 100644 --- a/src/main/java/seedu/duke/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -1,7 +1,7 @@ -package seedu.duke.database; +package seedu.dietbook.database; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.io.File; import java.io.FileNotFoundException; diff --git a/src/main/java/seedu/duke/database/Store.java b/src/main/java/seedu/dietbook/database/Store.java similarity index 89% rename from src/main/java/seedu/duke/database/Store.java rename to src/main/java/seedu/dietbook/database/Store.java index af311ae856..63ff2fa3fa 100644 --- a/src/main/java/seedu/duke/database/Store.java +++ b/src/main/java/seedu/dietbook/database/Store.java @@ -1,9 +1,8 @@ -package seedu.duke.database; +package seedu.dietbook.database; -import seedu.duke.food.Food; +import seedu.dietbook.food.Food; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class Store { diff --git a/src/main/java/seedu/duke/database/data.txt b/src/main/java/seedu/dietbook/database/data.txt similarity index 100% rename from src/main/java/seedu/duke/database/data.txt rename to src/main/java/seedu/dietbook/database/data.txt diff --git a/src/main/java/seedu/duke/food/Food.java b/src/main/java/seedu/dietbook/food/Food.java similarity index 97% rename from src/main/java/seedu/duke/food/Food.java rename to src/main/java/seedu/dietbook/food/Food.java index c43d8f0384..a2d7027f61 100644 --- a/src/main/java/seedu/duke/food/Food.java +++ b/src/main/java/seedu/dietbook/food/Food.java @@ -1,4 +1,4 @@ -package seedu.duke.food; +package seedu.dietbook.food; public class Food { diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/dietbook/DukeTest.java similarity index 88% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/dietbook/DukeTest.java index 2dda5fd651..eef4b4c86a 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/dietbook/DukeTest.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.dietbook; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/seedu/duke/database/DataBaseTest.java b/src/test/java/seedu/dietbook/database/DataBaseTest.java similarity index 99% rename from src/test/java/seedu/duke/database/DataBaseTest.java rename to src/test/java/seedu/dietbook/database/DataBaseTest.java index 116b4192bc..34a097a274 100644 --- a/src/test/java/seedu/duke/database/DataBaseTest.java +++ b/src/test/java/seedu/dietbook/database/DataBaseTest.java @@ -1,4 +1,4 @@ -package seedu.duke.database; +package seedu.dietbook.database; import java.io.FileNotFoundException; import java.util.NoSuchElementException; diff --git a/src/test/java/seedu/duke/food/FoodTest.java b/src/test/java/seedu/dietbook/food/FoodTest.java similarity index 75% rename from src/test/java/seedu/duke/food/FoodTest.java rename to src/test/java/seedu/dietbook/food/FoodTest.java index 97b646a032..d423c7f1d0 100644 --- a/src/test/java/seedu/duke/food/FoodTest.java +++ b/src/test/java/seedu/dietbook/food/FoodTest.java @@ -1,6 +1,4 @@ -package seedu.duke.food; - -import org.junit.jupiter.api.Test; +package seedu.dietbook.food; class FoodTest { From 34b46b4c0e0aa556c22cf9b0ce9a50b2a3315cd1 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 22:31:35 +0800 Subject: [PATCH 205/374] resolve static problems --- src/main/java/seedu/dietbook/calculator/Calculator.java | 2 +- src/main/java/seedu/dietbook/list/FoodList.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 7df6b6b048..e8304fe278 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -207,7 +207,7 @@ public int calculateFat(LocalDateTime startTime) { public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { int fat = 0; for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - fat += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFats(); + fat += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFat(); } return fat; } diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index 67ab59ec5e..6ff392d0de 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -1,9 +1,10 @@ package seedu.dietbook.list; +import seedu.dietbook.food.Food; + +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import java.time.LocalDateTime; -import seedu.dietbook.food.Food; /** @@ -12,7 +13,7 @@ * This is a stateful object. */ public class FoodList { - private ArrayList foodEntries; + private static ArrayList foodEntries; /** * Default constructor that instantiates FoodList with an empty foodentry arraylist. @@ -136,7 +137,7 @@ public List getPortionedFoodsAfterDateTime(LocalDateTime dateTime) { /** * Obtain list of foods consumed within the range of a specified timing. */ - public List getFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { + public static List getFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); return FoodListManager.listToFoods(entriesInRange); } From 84c6fe540388de60ae9d5cca8ffa4ddcaacbed43 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:34:24 +0800 Subject: [PATCH 206/374] add retrieval methods for portion and date --- .../java/seedu/dietbook/list/FoodList.java | 16 ++++++++++++ .../seedu/dietbook/list/FoodListManager.java | 25 +++++++++++++++++-- .../seedu/dietbook/list/FoodListTest.java | 7 ++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index bb646606b3..1b42b9c0a3 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -149,6 +149,22 @@ public List getPortionedFoodsInDateTimeRange(LocalDateTime start, LocalDat return FoodListManager.listToPortionedFoods(entriesInRange); } + /** + * Obtain list of portion sizes. + * (For storage purposes) + */ + public List getPortionSizes() { + return FoodListManager.listToPortionSizes(foodEntries); + } + + /** + * Obtain list of LocalDateTimes for when the entries were made. + * (For storage purposes) + */ + public List getDateTimes() { + return FoodListManager.listToLocalDateTimes(foodEntries); + } + @Override public String toString() { return FoodListManager.listToString(foodEntries); diff --git a/src/main/java/seedu/dietbook/list/FoodListManager.java b/src/main/java/seedu/dietbook/list/FoodListManager.java index 2c35f45dc2..8ef1efb5c0 100644 --- a/src/main/java/seedu/dietbook/list/FoodListManager.java +++ b/src/main/java/seedu/dietbook/list/FoodListManager.java @@ -52,7 +52,7 @@ protected static List listToStrings(List list) { /** * Extracts the list of foods from the foodentries list. * @param list list of foodEntries - * @return arraylist of Food objects. + * @return list of Food objects. */ protected static List listToFoods(List list) { Function function = x -> x.getFood(); @@ -63,7 +63,7 @@ protected static List listToFoods(List list) { * Creates a list of foods that have their nutritional values scaled by portion size. * This is based on the FoodEntries in the list provided. * @param list list of FoodEntries - * @return arraylist of Food objects + * @return list of Food objects */ protected static List listToPortionedFoods(List list) { Function function = x -> { @@ -81,8 +81,29 @@ protected static List listToPortionedFoods(List list) { return ListFunction.applyFunctionToList(list, function); } + /** + * Obtain the LocalDateTime objects associated with each entry. + */ + protected static List listToLocalDateTimes(List list) { + Function function = x -> { + assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; + DatedFoodEntry datedEntry = (DatedFoodEntry) x; + return datedEntry.getDateTime(); + }; + return ListFunction.applyFunctionToList(list, function); + } + + /** + * Obtain the portion sizes associated with each food entry. + */ + protected static List listToPortionSizes(List list) { + Function function = x -> x.getPortionSize(); + return ListFunction.applyFunctionToList(list, function); + } + /** * Obtain only food entries after a specified dateTime. + * @param dateTime the start/"before" datetime for filtering. */ protected static List filterListByDate(List list, LocalDateTime dateTime) { Predicate predicate = x -> { diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 586eb5e811..6ec36eb032 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -99,4 +99,11 @@ void dateFilterRangeTest() { list.getPortionedFoods().toString()); } + @Test + void getFoodEntryProperties_standardList_FoodEntryProperties() { + assertTrue(list.getDateTimes().get(0) instanceof LocalDateTime); + assertEquals(list.getPortionSizes().get(0), 3); + assertEquals(list.getFoods().get(0), food); + } + } From 0f77f9f06a21b227811976c32cc52ebf0f60a9fe Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 22:39:43 +0800 Subject: [PATCH 207/374] updated with new person format --- src/test/java/seedu/dietbook/calculator/CalculatorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index ea25a7aa09..bd0d3a5d47 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -56,8 +56,8 @@ void calculateFat_foodListOfThreeItems_sumOfFat() { @Test void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { - Person Harry = new Person("Harry", Gender.MALE, 19, 182, 66, 75, ActivityLevel.LOW); - Person Erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 45, ActivityLevel.MEDIUM); + Person Harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, ActivityLevel.LOW); + Person Erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, ActivityLevel.MEDIUM); Calculator calculator = new Calculator(new ArrayList()); assertEquals(2728, calculator.calculateRecomendation(Harry)); assertEquals(1752, calculator.calculateRecomendation(Erica)); From 726d0940451811eface846f533618ee2dbb99580 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:43:47 +0800 Subject: [PATCH 208/374] add Junit test for PersonSaveLoadManager --- .../saveload/PersonSaveLoadManagerTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java index bad761d44e..419cdec53e 100644 --- a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java @@ -1,6 +1,9 @@ package seedu.dietbook.saveload; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.FileNotFoundException; import static org.junit.jupiter.api.Assertions.*; @@ -17,5 +20,25 @@ private void setUp(){ pslTest.setOriginalWeight(200); pslTest.setCurrentWeight(300); pslTest.setTargetWeight(100); + pslTest.save("pslTest"); + } + + @Test + private void Load_NoSuchFile_ExpectFileNotFoundException() { + PersonSaveLoadManager localpslTest = new PersonSaveLoadManager(); + assertThrows(FileNotFoundException.class, () -> localpslTest.load("pie die pie")); + } + + @Test + private void Load_CorrectFile_AllContentsCorrect() throws Exception { + PersonSaveLoadManager localpslTest = new PersonSaveLoadManager(); + localpslTest.load("pslTest"); + assertEquals("Victor Chng", localpslTest.getName()); + assertEquals("Unknown", localpslTest.getGender()); + assertEquals(200, localpslTest.getOriginalWeight()); + assertEquals(100, localpslTest.getAge()); + assertEquals(300, localpslTest.getCurrentWeight()); + assertEquals(100, localpslTest.getTargetWeight()); + assertEquals(0, localpslTest.getActivityLevel()); } } \ No newline at end of file From d080a33bc7d8e9f59180de0c9d1afd57df1dd462 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:45:16 +0800 Subject: [PATCH 209/374] fix check style --- src/main/java/seedu/dietbook/database/DataBase.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index 89b6199de2..9d23267ae1 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -259,14 +259,14 @@ public List getFoodList() { } /** - * Provide a list o all food in the data base in numbered String form + * Provide a list o all food in the data base in numbered String form. * @return String */ - public String getFoodListString(){ + public String getFoodListString() { List foodlist = foodStream().collect(Collectors.toList()); StringBuilder foodListString = new StringBuilder(); int foodnum = 0; - for (Food food : foodlist){ + for (Food food : foodlist) { foodnum++; foodListString.append(" ").append(foodnum).append(". ").append(food.toString()); } From 03fcd8a44a38b8af5c03174b30aa80bb31e36014 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:49:09 +0800 Subject: [PATCH 210/374] Relaxed date filter test for robustness Some computers run the tests so quickly that LocalDateTime.now() has negligible difference in time elapsed. Relaxed upper boundary of test to LocalDateTime.MAX for robustness. --- src/test/java/seedu/dietbook/list/FoodListTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 6ec36eb032..3b7325f856 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -95,8 +95,8 @@ void dateFilterRangeTest() { assertTrue(list.getFoodsInDateTimeRange(timeNow, LocalDateTime.MAX).size() == 0); - assertEquals(list.getPortionedFoodsInDateTimeRange(LocalDateTime.MIN, timeNow).toString(), - list.getPortionedFoods().toString()); + assertEquals(list.getPortionedFoods().toString(), + list.getPortionedFoodsInDateTimeRange(LocalDateTime.MIN, LocalDateTime.MAX).toString()); } @Test From a64c0f7c3fa47d12b616bccd20c0aed336c5ffea Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 22:49:56 +0800 Subject: [PATCH 211/374] fix check style --- .../dietbook/saveload/PersonSaveLoadManagerTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java index 419cdec53e..e7c09650d1 100644 --- a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java @@ -5,14 +5,14 @@ import java.io.FileNotFoundException; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class PersonSaveLoadManagerTest { - private static PersonSaveLoadManager pslTest; @BeforeEach - private void setUp(){ - pslTest = new PersonSaveLoadManager(); + private void setUp() { + PersonSaveLoadManager pslTest = new PersonSaveLoadManager(); pslTest.setName("Victor Chng"); pslTest.setActivityLevel(0); pslTest.setGender("UnKnown"); @@ -24,13 +24,13 @@ private void setUp(){ } @Test - private void Load_NoSuchFile_ExpectFileNotFoundException() { + private void load_noSuchFile_expectFileNotFoundException() { PersonSaveLoadManager localpslTest = new PersonSaveLoadManager(); assertThrows(FileNotFoundException.class, () -> localpslTest.load("pie die pie")); } @Test - private void Load_CorrectFile_AllContentsCorrect() throws Exception { + private void load_correctFile_allContentsCorrect() throws Exception { PersonSaveLoadManager localpslTest = new PersonSaveLoadManager(); localpslTest.load("pslTest"); assertEquals("Victor Chng", localpslTest.getName()); From 23977b84dbcc8f324cba3f40faef794ecbaa483b Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 22:55:25 +0800 Subject: [PATCH 212/374] Add methods without recalculated food --- src/main/java/seedu/dietbook/Ui.java | 126 ++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index c5ff4030ec..bfc4f3cded 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -38,7 +38,7 @@ public String readCommand() { } /** - * Prints the welcome message from DietBook when it is fist booted up. + * Prints the welcome message from DietBook when it is first booted up. */ public void printWelcomeMessage() { String logo = getLogo(); @@ -244,6 +244,19 @@ public void printFoodList(String foods, LocalDateTime start, LocalDateTime end) } } + /** + * Prints food items recorded into the food list after a given timing in the order that they were + * added or a message stating no food items were recorded after the given timing till now. + * + * @param foods The string representation of food items in the food list recorded from the given time + * till now. + * @param start Starting date time of the time period till now. + */ + public void printFoodList(String foods, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printFoodList(foods, start, end); + } + /** * Prints a message to show that the food specified has been added to the food list. * @@ -309,6 +322,11 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods) { "g", recalculatedFoods)); } + public void printCarbIntake(int carbIntake) { + print(stringOneIntakeAndFoodsWithoutTime(carbIntake,"carbohydrate", + "g")); + } + /** * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had * their nutritional information recalculated by DietBook if any, given a certain time period. @@ -331,6 +349,12 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); } + public void printCarbIntake(int carbIntake, LocalDateTime start, LocalDateTime end) { + String carbIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(carbIntake, + "carbohydrate", "g"); + print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); + } + /** * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had * their nutritional information recalculated by DietBook if any, given a start date. @@ -351,6 +375,11 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, printCarbIntakeAndFoods(carbIntake, recalculatedFoods, start, end); } + public void printCarbIntake(int carbIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCarbIntake(carbIntake, start, end); + } + /** * Prints the total amount of calories consumed by the user and the list of food items which had * their nutritional information recalculated by DietBook if any. @@ -367,6 +396,10 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo recalculatedFoods)); } + public void printCalorieIntake(int calorieIntake) { + print(stringOneIntakeAndFoodsWithoutTime(calorieIntake,"calorie","kcal")); + } + /** * Prints the total amount of calories consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a certain time period. @@ -389,6 +422,12 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); } + public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDateTime end) { + String calorieIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(calorieIntake, + "calorie", "kcal"); + print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); + } + /** * Prints the total amount of calories consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a start date. @@ -409,6 +448,11 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo printCalorieIntakeAndFoods(calorieIntake, recalculatedFoods, start, end); } + public void printCalorieIntake(int calorieIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCalorieIntake(calorieIntake, start, end); + } + /** * Prints the total amount of proteins consumed by the user and the list of food items which had * their nutritional information recalculated by DietBook if any. @@ -425,6 +469,10 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo recalculatedFoods)); } + public void printProteinIntake(int proteinIntake) { + print(stringOneIntakeAndFoodsWithoutTime(proteinIntake,"protein","g")); + } + /** * Prints the total amount of proteins consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a certain time period. @@ -447,6 +495,12 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); } + public void printProteinIntake(int proteinIntake, LocalDateTime start, LocalDateTime end) { + String proteinIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(proteinIntake, + "protein", "g"); + print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); + } + /** * Prints the total amount of proteins consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a start date. @@ -467,6 +521,11 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo printProteinIntakeAndFoods(proteinIntake, recalculatedFoods, start, end); } + public void printProteinIntake(int proteinIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printProteinIntake(proteinIntake, start, end); + } + /** * Prints the total amount of fats consumed by the user and the list of food items which had * their nutritional information recalculated by DietBook if any. @@ -483,6 +542,10 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods) { recalculatedFoods)); } + public void printFatIntake(int fatIntake) { + print(stringOneIntakeAndFoodsWithoutTime(fatIntake,"fat","g")); + } + /** * Prints the total amount of fats consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a certain time period. @@ -505,6 +568,12 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); } + public void printFatIntake(int fatIntake, LocalDateTime start, LocalDateTime end) { + String fatIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(fatIntake, + "fat", "g"); + print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); + } + /** * Prints the total amount of fats consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a start date. @@ -525,6 +594,11 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, printFatIntakeAndFoods(fatIntake, recalculatedFoods, start, end); } + public void printFatIntake(int fatIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printFatIntake(fatIntake, start, end); + } + /** * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and the * list of food items which had their nutritional information recalculated by DietBook if any. @@ -545,6 +619,12 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei fatIntake, recalculatedFoods)); } + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake) { + print(stringAllIntakeAndFoodsWithoutTime(calorieIntake, carbIntake, proteinIntake, + fatIntake)); + } + /** * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a * list of the foods which had their nutritional information recalculated by DietBook if any, given a @@ -575,6 +655,13 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); } + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, LocalDateTime start, LocalDateTime end) { + String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(calorieIntake, + carbIntake, proteinIntake, fatIntake); + print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); + } + /** * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a * list of the foods which had their nutritional information recalculated by DietBook if any, given a @@ -604,6 +691,12 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei end); } + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printAllIntake(calorieIntake, carbIntake, proteinIntake, fatIntake, start, end); + } + // Helper methods for system related commands or messages /** @@ -776,6 +869,16 @@ private String stringOneIntakeAndFoodsWithoutTime(int nutrientIntake, String nut return stringNutrientIntake + LINE_SEPARATOR + message; } + private String stringOneIntakeAndFoodsWithoutTime(int nutrientIntake, String nutrientType, + String nutrientUnit) { + performAssertionsForStringInputs(nutrientType,"Nutrient Type"); + performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); + performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); + + String stringNutrientIntake = stringNutritionalIntake(nutrientIntake, nutrientType, nutrientUnit); + return stringNutrientIntake; + } + /** * Returns a string representation of the total amount of a nutrient or all nutrientS consumed by the * user during a given time period and the list of food items recorded during the same time period @@ -835,6 +938,27 @@ private String stringAllIntakeAndFoodsWithoutTime(int calorieIntake, int carbInt } + private String stringAllIntakeAndFoodsWithoutTime(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake) { + performAssertionsForNutritionalIntake(carbIntake, "carbohydrate"); + performAssertionsForNutritionalIntake(calorieIntake, "calorie"); + performAssertionsForNutritionalIntake(proteinIntake, "protein"); + performAssertionsForNutritionalIntake(fatIntake, "fat"); + + + String stringCarbIntake = stringNutritionalIntake(carbIntake,"carbohydrate", "g"); + String stringCalorieIntake = stringNutritionalIntake(calorieIntake,"calorie", + "kcal"); + String stringProteinIntake = stringNutritionalIntake(proteinIntake,"protein", "g"); + String stringFatIntake = stringNutritionalIntake(fatIntake,"fat", "g"); + + return stringCalorieIntake + LINE_SEPARATOR + + stringCarbIntake + LINE_SEPARATOR + + stringProteinIntake + LINE_SEPARATOR + + stringFatIntake + LINE_SEPARATOR; + + } + // Other helper methods /** From 87aa8c7c0bf7893974c9e08a27bd81fae4c94831 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Tue, 27 Oct 2020 23:00:11 +0800 Subject: [PATCH 213/374] resolved name violation --- .../java/seedu/dietbook/calculator/CalculatorTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index bd0d3a5d47..d2cd6e7b10 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -56,10 +56,10 @@ void calculateFat_foodListOfThreeItems_sumOfFat() { @Test void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { - Person Harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, ActivityLevel.LOW); - Person Erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, ActivityLevel.MEDIUM); + Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, ActivityLevel.LOW); + Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, ActivityLevel.MEDIUM); Calculator calculator = new Calculator(new ArrayList()); - assertEquals(2728, calculator.calculateRecomendation(Harry)); - assertEquals(1752, calculator.calculateRecomendation(Erica)); + assertEquals(2728, calculator.calculateRecomendation(harry)); + assertEquals(1752, calculator.calculateRecomendation(erica)); } } From 55d5b07434f811b9653d791960030a438a06329f Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Tue, 27 Oct 2020 23:03:15 +0800 Subject: [PATCH 214/374] copy paste --- .../java/seedu/dietbook/food/FoodTest.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/test/java/seedu/dietbook/food/FoodTest.java b/src/test/java/seedu/dietbook/food/FoodTest.java index d423c7f1d0..91b917608c 100644 --- a/src/test/java/seedu/dietbook/food/FoodTest.java +++ b/src/test/java/seedu/dietbook/food/FoodTest.java @@ -1,11 +1,25 @@ package seedu.dietbook.food; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + class FoodTest { private Food testFood; - public static void main(String[] args) { - Food food = new Food("Kobe Beef", 480,50,40,30); - System.out.println(food); + @BeforeEach + public void setUp() { + testFood = new Food("Kobe Beef", 480,50,40,30); + } + + @Test + public void footTest() { + assertEquals(480, testFood.getCalorie()); + assertEquals(50, testFood.getCarbohydrate()); + assertEquals(40, testFood.getProtein()); + assertEquals(30, testFood.getFat()); } } \ No newline at end of file From 3f63e1ddb8a6864c7c2c0aa39f5bfb8324df1de9 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Tue, 27 Oct 2020 23:09:01 +0800 Subject: [PATCH 215/374] Fix checkstyle error --- src/main/java/seedu/dietbook/Ui.java | 157 ++++++++++++++------------- 1 file changed, 80 insertions(+), 77 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index bfc4f3cded..a03565a0af 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -306,6 +306,86 @@ public void printCalorieRecommendation(String name, int calorieRecommendation) { + "Here is your daily recommended calorie intake: " + calorieRecommendation + "kcal"); } + public void printCarbIntake(int carbIntake) { + print(stringOneIntakeAndFoodsWithoutTime(carbIntake,"carbohydrate", + "g")); + } + + public void printCarbIntake(int carbIntake, LocalDateTime start, LocalDateTime end) { + String carbIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(carbIntake, + "carbohydrate", "g"); + print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); + } + + public void printCarbIntake(int carbIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCarbIntake(carbIntake, start, end); + } + + public void printCalorieIntake(int calorieIntake) { + print(stringOneIntakeAndFoodsWithoutTime(calorieIntake,"calorie","kcal")); + } + + public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDateTime end) { + String calorieIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(calorieIntake, + "calorie", "kcal"); + print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); + } + + public void printCalorieIntake(int calorieIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCalorieIntake(calorieIntake, start, end); + } + + public void printProteinIntake(int proteinIntake) { + print(stringOneIntakeAndFoodsWithoutTime(proteinIntake,"protein","g")); + } + + public void printProteinIntake(int proteinIntake, LocalDateTime start, LocalDateTime end) { + String proteinIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(proteinIntake, + "protein", "g"); + print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); + } + + public void printProteinIntake(int proteinIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printProteinIntake(proteinIntake, start, end); + } + + public void printFatIntake(int fatIntake) { + print(stringOneIntakeAndFoodsWithoutTime(fatIntake,"fat","g")); + } + + public void printFatIntake(int fatIntake, LocalDateTime start, LocalDateTime end) { + String fatIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(fatIntake, + "fat", "g"); + print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); + } + + public void printFatIntake(int fatIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printFatIntake(fatIntake, start, end); + } + + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake) { + print(stringAllIntakeAndFoodsWithoutTime(calorieIntake, carbIntake, proteinIntake, + fatIntake)); + } + + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, LocalDateTime start, LocalDateTime end) { + String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(calorieIntake, + carbIntake, proteinIntake, fatIntake); + print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); + } + + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printAllIntake(calorieIntake, carbIntake, proteinIntake, fatIntake, start, end); + } + /** * Prints the total amount of carbohydrates consumed by the user and the list of food items which had * their nutritional information recalculated by DietBook if any. @@ -322,11 +402,6 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods) { "g", recalculatedFoods)); } - public void printCarbIntake(int carbIntake) { - print(stringOneIntakeAndFoodsWithoutTime(carbIntake,"carbohydrate", - "g")); - } - /** * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had * their nutritional information recalculated by DietBook if any, given a certain time period. @@ -349,12 +424,6 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); } - public void printCarbIntake(int carbIntake, LocalDateTime start, LocalDateTime end) { - String carbIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(carbIntake, - "carbohydrate", "g"); - print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); - } - /** * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had * their nutritional information recalculated by DietBook if any, given a start date. @@ -375,10 +444,6 @@ public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, printCarbIntakeAndFoods(carbIntake, recalculatedFoods, start, end); } - public void printCarbIntake(int carbIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printCarbIntake(carbIntake, start, end); - } /** * Prints the total amount of calories consumed by the user and the list of food items which had @@ -396,10 +461,6 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo recalculatedFoods)); } - public void printCalorieIntake(int calorieIntake) { - print(stringOneIntakeAndFoodsWithoutTime(calorieIntake,"calorie","kcal")); - } - /** * Prints the total amount of calories consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a certain time period. @@ -422,12 +483,6 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); } - public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDateTime end) { - String calorieIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(calorieIntake, - "calorie", "kcal"); - print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); - } - /** * Prints the total amount of calories consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a start date. @@ -448,10 +503,6 @@ public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoo printCalorieIntakeAndFoods(calorieIntake, recalculatedFoods, start, end); } - public void printCalorieIntake(int calorieIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printCalorieIntake(calorieIntake, start, end); - } /** * Prints the total amount of proteins consumed by the user and the list of food items which had @@ -469,10 +520,6 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo recalculatedFoods)); } - public void printProteinIntake(int proteinIntake) { - print(stringOneIntakeAndFoodsWithoutTime(proteinIntake,"protein","g")); - } - /** * Prints the total amount of proteins consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a certain time period. @@ -495,12 +542,6 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); } - public void printProteinIntake(int proteinIntake, LocalDateTime start, LocalDateTime end) { - String proteinIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(proteinIntake, - "protein", "g"); - print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); - } - /** * Prints the total amount of proteins consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a start date. @@ -521,10 +562,6 @@ public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoo printProteinIntakeAndFoods(proteinIntake, recalculatedFoods, start, end); } - public void printProteinIntake(int proteinIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printProteinIntake(proteinIntake, start, end); - } /** * Prints the total amount of fats consumed by the user and the list of food items which had @@ -542,10 +579,6 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods) { recalculatedFoods)); } - public void printFatIntake(int fatIntake) { - print(stringOneIntakeAndFoodsWithoutTime(fatIntake,"fat","g")); - } - /** * Prints the total amount of fats consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a certain time period. @@ -568,12 +601,6 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); } - public void printFatIntake(int fatIntake, LocalDateTime start, LocalDateTime end) { - String fatIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(fatIntake, - "fat", "g"); - print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); - } - /** * Prints the total amount of fats consumed by the user and a list of the foods which had their * nutritional information recalculated by DietBook if any, given a start date. @@ -594,11 +621,6 @@ public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, printFatIntakeAndFoods(fatIntake, recalculatedFoods, start, end); } - public void printFatIntake(int fatIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printFatIntake(fatIntake, start, end); - } - /** * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and the * list of food items which had their nutritional information recalculated by DietBook if any. @@ -619,12 +641,6 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei fatIntake, recalculatedFoods)); } - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake) { - print(stringAllIntakeAndFoodsWithoutTime(calorieIntake, carbIntake, proteinIntake, - fatIntake)); - } - /** * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a * list of the foods which had their nutritional information recalculated by DietBook if any, given a @@ -655,13 +671,6 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); } - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake, LocalDateTime start, LocalDateTime end) { - String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(calorieIntake, - carbIntake, proteinIntake, fatIntake); - print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); - } - /** * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a * list of the foods which had their nutritional information recalculated by DietBook if any, given a @@ -691,12 +700,6 @@ public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int protei end); } - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, - LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printAllIntake(calorieIntake, carbIntake, proteinIntake, fatIntake, start, end); - } - // Helper methods for system related commands or messages /** From 093a59359d0056513a8027afe2e1cfa2591baad7 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 11:07:54 +0800 Subject: [PATCH 216/374] fix bug getFoodListString StringBuilder missing "\n" --- src/main/java/seedu/dietbook/database/DataBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index b9fa8c1e7b..d671260c6c 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -270,7 +270,7 @@ public String getFoodListString() { int foodnum = 0; for (Food food : foodlist) { foodnum++; - foodListString.append(" ").append(foodnum).append(". ").append(food.toString()); + foodListString.append(" ").append(foodnum).append(". ").append(food.toString()).append("\n"); } return foodListString.toString(); } From 5c8ad977ec517bec999003bcafea71a4cdb16972 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 18:15:19 +0800 Subject: [PATCH 217/374] UML diagram --- UML_diaghrams/week 11 DG.puml | 16 +++++++++++ docs/Zhong_Ming_developer_Guide/zm_DG.md | 34 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 UML_diaghrams/week 11 DG.puml create mode 100644 docs/Zhong_Ming_developer_Guide/zm_DG.md diff --git a/UML_diaghrams/week 11 DG.puml b/UML_diaghrams/week 11 DG.puml new file mode 100644 index 0000000000..043a2591a7 --- /dev/null +++ b/UML_diaghrams/week 11 DG.puml @@ -0,0 +1,16 @@ + + + + + +@startuml + +Alice -> Bob: Authentication Request +Bob --> Alice: Authentication Response + +Alice -> Bob: Another authentication Request +Alice <-- Bob: another authentication Response + +Alice -> Bob : insert laser probe +Alice <-- Bob : output cavity liquid +@enduml \ No newline at end of file diff --git a/docs/Zhong_Ming_developer_Guide/zm_DG.md b/docs/Zhong_Ming_developer_Guide/zm_DG.md new file mode 100644 index 0000000000..8faa7b38e6 --- /dev/null +++ b/docs/Zhong_Ming_developer_Guide/zm_DG.md @@ -0,0 +1,34 @@ +## 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. + +### Architecture + +#### 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 +* Saver#save() saves the current data to the file in the folder with the given file name +* Saver#add() Store String data in the x,y position in the table + +#### 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 +* Loader#get() retrives the data stored in the loader + +#### FoodSaveLoadManager 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. +##### Main Methods +* FoodSaveLoadManager#save saves the list of food objects to the specified file name +* FoodSaveLoadManager#load loads the file and returns the list of food objects stored inside it + From 3af64084410e779b92c518c875fa97604785501a Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Wed, 28 Oct 2020 18:17:18 +0800 Subject: [PATCH 218/374] Delete Duke.java --- src/main/java/seedu/dietbook/Duke.java | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/main/java/seedu/dietbook/Duke.java diff --git a/src/main/java/seedu/dietbook/Duke.java b/src/main/java/seedu/dietbook/Duke.java deleted file mode 100644 index e2af504b4b..0000000000 --- a/src/main/java/seedu/dietbook/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.dietbook; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} From 5b73b910fa6796392c63fbedf2b23d58b6123462 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 18:23:32 +0800 Subject: [PATCH 219/374] architecture diaghram --- UML_diaghrams/Architecture.puml | 8 ++++++++ docs/Zhong_Ming_developer_Guide/zm_DG.md | 1 + 2 files changed, 9 insertions(+) create mode 100644 UML_diaghrams/Architecture.puml diff --git a/UML_diaghrams/Architecture.puml b/UML_diaghrams/Architecture.puml new file mode 100644 index 0000000000..30c4607c3b --- /dev/null +++ b/UML_diaghrams/Architecture.puml @@ -0,0 +1,8 @@ +@startuml +object Saver +object Loader +object FoodSaveLoadManager +object PersonSaveLoadManager + + +@enduml diff --git a/docs/Zhong_Ming_developer_Guide/zm_DG.md b/docs/Zhong_Ming_developer_Guide/zm_DG.md index 8faa7b38e6..a56bdb5e6c 100644 --- a/docs/Zhong_Ming_developer_Guide/zm_DG.md +++ b/docs/Zhong_Ming_developer_Guide/zm_DG.md @@ -6,6 +6,7 @@ and Loader class. ### Architecture + #### 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. From d8c8d9f8d3ccd4726d229b37e60fac2fcc4c247e Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 18:53:28 +0800 Subject: [PATCH 220/374] finished words --- UML_diaghrams/Architecture.puml | 11 +++++++++++ .../{week 11 DG.puml => UML diagram.puml} | 7 ------- docs/Zhong_Ming_developer_Guide/zm_DG.md | 17 ++++++++++++++--- 3 files changed, 25 insertions(+), 10 deletions(-) rename UML_diaghrams/{week 11 DG.puml => UML diagram.puml} (71%) diff --git a/UML_diaghrams/Architecture.puml b/UML_diaghrams/Architecture.puml index 30c4607c3b..d4ec941ef8 100644 --- a/UML_diaghrams/Architecture.puml +++ b/UML_diaghrams/Architecture.puml @@ -3,6 +3,17 @@ object Saver object Loader object FoodSaveLoadManager object PersonSaveLoadManager +object Command +object File +File <-up-> Saver +File <-up-> Loader +Saver <-up-> FoodSaveLoadManager +Saver <-up-> PersonSaveLoadManager +Loader <-up-> FoodSaveLoadManager +Loader <-up-> PersonSaveLoadManager + +FoodSaveLoadManager <-up-> Command +PersonSaveLoadManager <-up-> Command @enduml diff --git a/UML_diaghrams/week 11 DG.puml b/UML_diaghrams/UML diagram.puml similarity index 71% rename from UML_diaghrams/week 11 DG.puml rename to UML_diaghrams/UML diagram.puml index 043a2591a7..1d58b9f108 100644 --- a/UML_diaghrams/week 11 DG.puml +++ b/UML_diaghrams/UML diagram.puml @@ -1,16 +1,9 @@ - - - @startuml - Alice -> Bob: Authentication Request Bob --> Alice: Authentication Response Alice -> Bob: Another authentication Request Alice <-- Bob: another authentication Response - -Alice -> Bob : insert laser probe -Alice <-- Bob : output cavity liquid @enduml \ No newline at end of file diff --git a/docs/Zhong_Ming_developer_Guide/zm_DG.md b/docs/Zhong_Ming_developer_Guide/zm_DG.md index a56bdb5e6c..286af4164f 100644 --- a/docs/Zhong_Ming_developer_Guide/zm_DG.md +++ b/docs/Zhong_Ming_developer_Guide/zm_DG.md @@ -5,7 +5,9 @@ At the base of the package, there is the Saver and Loader class. ### Architecture - +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. @@ -30,6 +32,15 @@ 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. ##### Main Methods -* FoodSaveLoadManager#save saves the list of food objects to the specified file name -* FoodSaveLoadManager#load loads the file and returns the list of food objects stored inside it +* FoodSaveLoadManager#save() saves the list of food objects to the specified file name +* FoodSaveLoadManager#load() loads the file and returns the list of food objects stored inside it + +#### PersonSaveLoadManager class +Built on top of Saver and Loader class to implement save/load functionality for user information +Same as FoodSaveLoadManager, it has its own folder to work with, the user only has to specify the file name +Unlike the FoodSaveLoadManager, it stores the data inside itself and can be updated. +##### Main Methods +* PersonSaveLoadManager#save() save the current state into the file +* PersonSaveLoadManager#load() loads the file +* Setters and Getters for all the personal data in this current version From 08b941b4c8dc46015d864194f0c99b3b4f7463ff Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 19:39:05 +0800 Subject: [PATCH 221/374] draw UML diaghrams --- .gitignore | 2 ++ UML_diaghrams/FoodSaveLoadManager_load.puml | 24 +++++++++++++++++++++ UML_diaghrams/FoodSaveLoadManager_save.puml | 24 +++++++++++++++++++++ UML_diaghrams/UML diagram.puml | 16 +++++++++----- docs/Zhong_Ming_developer_Guide/zm_DG.md | 3 +++ 5 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 UML_diaghrams/FoodSaveLoadManager_load.puml create mode 100644 UML_diaghrams/FoodSaveLoadManager_save.puml diff --git a/.gitignore b/.gitignore index f69985ef1f..1fafd04fd6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +docs/Zhong_Ming_developer_Guide/Architecture.png +*.png diff --git a/UML_diaghrams/FoodSaveLoadManager_load.puml b/UML_diaghrams/FoodSaveLoadManager_load.puml new file mode 100644 index 0000000000..51669578f2 --- /dev/null +++ b/UML_diaghrams/FoodSaveLoadManager_load.puml @@ -0,0 +1,24 @@ +@startuml + +-> FoodSaveLoadManager : load() +activate FoodSaveLoadManager + +FoodSaveLoadManager -> Loader : static load() + +activate Loader +Loader -> FileLoader : load() + +activate FileLoader +loop all lines + activate Scanner + FileLoader -> Scanner : readline() + Scanner --> FileLoader : line data + destroy Scanner +end +FileLoader --> Loader : FileLoader +deactivate FileLoader + +Loader --> FoodSaveLoadManager : FileLoader +deactivate Loader +deactivate FoodSaveLoadManager +@enduml \ No newline at end of file diff --git a/UML_diaghrams/FoodSaveLoadManager_save.puml b/UML_diaghrams/FoodSaveLoadManager_save.puml new file mode 100644 index 0000000000..ea1c8a4805 --- /dev/null +++ b/UML_diaghrams/FoodSaveLoadManager_save.puml @@ -0,0 +1,24 @@ +@startuml +-> FoodSaveLoadManager : save +activate FoodSaveLoadManager + + +loop all food items + loop every entry in a food object + activate Saver + FoodSaveLoadManager -> Saver : add() + end +end + +FoodSaveLoadManager -> Saver : save() +deactivate Saver + +activate FileWriter +loop all entries in Saver table + Saver -> FileWriter : write() +end +destroy FileWriter + +deactivate FoodSaveLoadManager + +@enduml \ No newline at end of file diff --git a/UML_diaghrams/UML diagram.puml b/UML_diaghrams/UML diagram.puml index 1d58b9f108..c82d8be9d3 100644 --- a/UML_diaghrams/UML diagram.puml +++ b/UML_diaghrams/UML diagram.puml @@ -1,9 +1,15 @@ - @startuml -Alice -> Bob: Authentication Request -Bob --> Alice: Authentication Response +-> FoodSaveLoadManager : save + +loop all food items + loop every entry in a food object + FoodSaveLoadManager -> Saver : add() + end +end -Alice -> Bob: Another authentication Request -Alice <-- Bob: another authentication Response +FoodSaveLoadManager -> Saver : save() +loop all entries in Saver table + Saver -> FileWriter : write() +end @enduml \ No newline at end of file diff --git a/docs/Zhong_Ming_developer_Guide/zm_DG.md b/docs/Zhong_Ming_developer_Guide/zm_DG.md index 286af4164f..56fd3b7570 100644 --- a/docs/Zhong_Ming_developer_Guide/zm_DG.md +++ b/docs/Zhong_Ming_developer_Guide/zm_DG.md @@ -10,8 +10,10 @@ the code. The FoodSaveLoadManager and PersonSaveLoadManager are written specific will have to be modified/replaced for future versions. #### Saver class +![Alt text](Architecture.png) 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 @@ -44,3 +46,4 @@ Unlike the FoodSaveLoadManager, it stores the data inside itself and can be upda * PersonSaveLoadManager#load() loads the file * Setters and Getters for all the personal data in this current version +#### UML diaghram From 59643b96f7fd46e39a3f058c7922fb833a0159eb Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 19:50:50 +0800 Subject: [PATCH 222/374] complete DG --- docs/Zhong_Ming_developer_Guide/zm_DG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/Zhong_Ming_developer_Guide/zm_DG.md b/docs/Zhong_Ming_developer_Guide/zm_DG.md index 56fd3b7570..50abef666a 100644 --- a/docs/Zhong_Ming_developer_Guide/zm_DG.md +++ b/docs/Zhong_Ming_developer_Guide/zm_DG.md @@ -47,3 +47,8 @@ Unlike the FoodSaveLoadManager, it stores the data inside itself and can be upda * Setters and Getters for all the personal data in this current version #### UML diaghram +##### FoodSaveLoadManager#save() +![Alt text](FoodSaveLoadManager_save.png) +##### FoodSaveLoadManager#load() +![Alt text](FoodSaveLoadManager_load.png) +similiar diaghrams for PersonSaveLoadManager \ No newline at end of file From 63f6a8570de6af512defcdd15d2e3dc7e5eb34c2 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 19:53:36 +0800 Subject: [PATCH 223/374] diaghrams --- .gitignore | 3 +-- .../Zhong_Ming_developer_Guide/Architecture.png | Bin 0 -> 43003 bytes .../FoodSaveLoadManager_load.png | Bin 0 -> 77795 bytes .../FoodSaveLoadManager_save.png | Bin 0 -> 75922 bytes 4 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 docs/Zhong_Ming_developer_Guide/Architecture.png create mode 100644 docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_load.png create mode 100644 docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png diff --git a/.gitignore b/.gitignore index 1fafd04fd6..ea7ac5150a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT -docs/Zhong_Ming_developer_Guide/Architecture.png -*.png + diff --git a/docs/Zhong_Ming_developer_Guide/Architecture.png b/docs/Zhong_Ming_developer_Guide/Architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..1ca758c26adedd91685225c66df5578ba73afbcc GIT binary patch literal 43003 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%0Agux07_eCdzyRD<{cnP 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 literal 0 HcmV?d00001 diff --git a/docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png b/docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png new file mode 100644 index 0000000000000000000000000000000000000000..61891d2a8d921406abaf721cc2d7ec66c4a22801 GIT binary patch literal 75922 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

RP~Nt3!$haqVXk7PW`bqQ4ER70K@A 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

RP~Nt3!$haqVXk7PW`bqQ4ER70K@A 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�*kGnH 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 Saver +and Loader class. + +### Architecture +![Alt text](Architecture.png) +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 +* Saver#save() saves the current data to the file in the folder with the given file name +* Saver#add() Store String data in the x,y position in the table + +#### 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 +* Loader#get() retrives the data stored in the loader + +#### FoodSaveLoadManager 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. +##### Main Methods +* FoodSaveLoadManager#save() saves the list of food objects to the specified file name +* FoodSaveLoadManager#load() loads the file and returns the list of food objects stored inside it + +#### PersonSaveLoadManager class +Built on top of Saver and Loader class to implement save/load functionality for user information +Same as FoodSaveLoadManager, it has its own folder to work with, the user only has to specify the file name +Unlike the FoodSaveLoadManager, it stores the data inside itself and can be updated. +##### Main Methods +* PersonSaveLoadManager#save() save the current state into the file +* PersonSaveLoadManager#load() loads the file +* Setters and Getters for all the personal data in this current version + +#### UML diaghram +##### FoodSaveLoadManager#save() +![Alt text](FoodSaveLoadManager_save.png) +##### FoodSaveLoadManager#load() +![Alt text](FoodSaveLoadManager_load.png) +similiar diaghrams for PersonSaveLoadManager \ No newline at end of file From 7cd69c4de11e0a9be6068db82e86f4ad683aacef Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 20:30:41 +0800 Subject: [PATCH 226/374] rename folders --- .../{ => save_load_feature}/Architecture.puml | 0 .../FoodSaveLoadManager_load.puml | 0 .../FoodSaveLoadManager_save.puml | 0 .../{ => save_load_feature}/UML diagram.puml | 0 .../Architecture.png | Bin .../FoodSaveLoadManager_load.png | Bin .../FoodSaveLoadManager_save.png | Bin .../zm_DG.md | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename UML_diaghrams/{ => save_load_feature}/Architecture.puml (100%) rename UML_diaghrams/{ => save_load_feature}/FoodSaveLoadManager_load.puml (100%) rename UML_diaghrams/{ => save_load_feature}/FoodSaveLoadManager_save.puml (100%) rename UML_diaghrams/{ => save_load_feature}/UML diagram.puml (100%) rename docs/{Zhong_Ming_developer_Guide => save_load_feature}/Architecture.png (100%) rename docs/{Zhong_Ming_developer_Guide => save_load_feature}/FoodSaveLoadManager_load.png (100%) rename docs/{Zhong_Ming_developer_Guide => save_load_feature}/FoodSaveLoadManager_save.png (100%) rename docs/{Zhong_Ming_developer_Guide => save_load_feature}/zm_DG.md (100%) diff --git a/UML_diaghrams/Architecture.puml b/UML_diaghrams/save_load_feature/Architecture.puml similarity index 100% rename from UML_diaghrams/Architecture.puml rename to UML_diaghrams/save_load_feature/Architecture.puml diff --git a/UML_diaghrams/FoodSaveLoadManager_load.puml b/UML_diaghrams/save_load_feature/FoodSaveLoadManager_load.puml similarity index 100% rename from UML_diaghrams/FoodSaveLoadManager_load.puml rename to UML_diaghrams/save_load_feature/FoodSaveLoadManager_load.puml diff --git a/UML_diaghrams/FoodSaveLoadManager_save.puml b/UML_diaghrams/save_load_feature/FoodSaveLoadManager_save.puml similarity index 100% rename from UML_diaghrams/FoodSaveLoadManager_save.puml rename to UML_diaghrams/save_load_feature/FoodSaveLoadManager_save.puml diff --git a/UML_diaghrams/UML diagram.puml b/UML_diaghrams/save_load_feature/UML diagram.puml similarity index 100% rename from UML_diaghrams/UML diagram.puml rename to UML_diaghrams/save_load_feature/UML diagram.puml diff --git a/docs/Zhong_Ming_developer_Guide/Architecture.png b/docs/save_load_feature/Architecture.png similarity index 100% rename from docs/Zhong_Ming_developer_Guide/Architecture.png rename to docs/save_load_feature/Architecture.png diff --git a/docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_load.png b/docs/save_load_feature/FoodSaveLoadManager_load.png similarity index 100% rename from docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_load.png rename to docs/save_load_feature/FoodSaveLoadManager_load.png diff --git a/docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png b/docs/save_load_feature/FoodSaveLoadManager_save.png similarity index 100% rename from docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png rename to docs/save_load_feature/FoodSaveLoadManager_save.png diff --git a/docs/Zhong_Ming_developer_Guide/zm_DG.md b/docs/save_load_feature/zm_DG.md similarity index 100% rename from docs/Zhong_Ming_developer_Guide/zm_DG.md rename to docs/save_load_feature/zm_DG.md From e8cde0b9c70af3859366d49c5ba3158a45c9f411 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 20:30:59 +0800 Subject: [PATCH 227/374] add save load feature part into developerGuide.md --- docs/DeveloperGuide.md | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 0ec3db103d..5899b377f5 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -3,7 +3,61 @@ ## Design & implementation {Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +## 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. + +### Architecture +![Alt text](save_load_feature/Architecture.png) +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 +* Saver#save() saves the current data to the file in the folder with the given file name +* Saver#add() Store String data in the x,y position in the table + +#### 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 +* Loader#get() retrives the data stored in the loader + +#### FoodSaveLoadManager 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. +##### Main Methods +* FoodSaveLoadManager#save() saves the list of food objects to the specified file name +* FoodSaveLoadManager#load() loads the file and returns the list of food objects stored inside it + +#### PersonSaveLoadManager class +Built on top of Saver and Loader class to implement save/load functionality for user information +Same as FoodSaveLoadManager, it has its own folder to work with, the user only has to specify the file name +Unlike the FoodSaveLoadManager, it stores the data inside itself and can be updated. +##### Main Methods +* PersonSaveLoadManager#save() save the current state into the file +* PersonSaveLoadManager#load() loads the file +* Setters and Getters for all the personal data in this current version + +#### UML diaghram +##### FoodSaveLoadManager#save() +![Alt text](save_load_feature/FoodSaveLoadManager_save.png) +##### FoodSaveLoadManager#load() +![Alt text](save_load_feature/FoodSaveLoadManager_load.png) +similiar diaghrams for PersonSaveLoadManager ## Product scope ### Target user profile From 1ce49482ae18a460eacda80c1983a0de9ecc370d Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Wed, 28 Oct 2020 22:19:55 +0800 Subject: [PATCH 228/374] Command recommend and editinfo a few bug fixes as well --- src/main/java/seedu/dietbook/Manager.java | 10 +- .../seedu/dietbook/checker/InputChecker.java | 2 +- .../dietbook/command/EditInfoCommand.java | 25 ++++ .../seedu/dietbook/command/InfoCommand.java | 1 - .../dietbook/command/RecommendCommand.java | 25 ++++ .../java/seedu/dietbook/parser/Parser.java | 120 +++++++++++++++--- 6 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 src/main/java/seedu/dietbook/command/EditInfoCommand.java create mode 100644 src/main/java/seedu/dietbook/command/RecommendCommand.java diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 291497877e..da5f9f73e8 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -6,11 +6,13 @@ import seedu.dietbook.command.Command; import seedu.dietbook.command.DataCommand; import seedu.dietbook.command.DeleteCommand; +import seedu.dietbook.command.EditInfoCommand; import seedu.dietbook.command.ExitCommand; import seedu.dietbook.command.HelpCommand; import seedu.dietbook.command.InfoCommand; import seedu.dietbook.command.ListCommand; import seedu.dietbook.command.NameCommand; +import seedu.dietbook.command.RecommendCommand; import seedu.dietbook.command.UserinfoCommand; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.ActivityLevel; @@ -21,8 +23,6 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; -import java.util.Scanner; - /** * Manager class of the program. * The manager class takes in the checked and processed input and carry out the command specified. @@ -44,11 +44,13 @@ public class Manager { public static final String COMMAND_CLEAR = "clear"; public static final String COMMAND_DATA = "data"; public static final String COMMAND_DELETE = "delete"; + public static final String COMMAND_EDIT_INFO = "editinfo"; public static final String COMMAND_EXIT = "exit"; public static final String COMMAND_HELP = "help"; public static final String COMMAND_INFO = "info"; public static final String COMMAND_LIST = "list"; public static final String COMMAND_NAME = "name"; + public static final String COMMAND_RECOMMEND = "recommend"; public static final String COMMAND_USERINFO = "userinfo"; public Manager(FoodList foodlist, DataBase dataBase) { @@ -114,6 +116,8 @@ public Command manage(String userInput) throws DietException { return new DataCommand(); case COMMAND_DELETE: return new DeleteCommand(Parser.getCommandIndex(userInput)); + case COMMAND_EDIT_INFO: + return new EditInfoCommand(userInput); case COMMAND_EXIT: return new ExitCommand(); case COMMAND_HELP: @@ -124,6 +128,8 @@ public Command manage(String userInput) throws DietException { return new ListCommand(); case COMMAND_NAME: return new NameCommand(Parser.getCommandParam(userInput)); + case COMMAND_RECOMMEND: + return new RecommendCommand(getPerson()); case COMMAND_USERINFO: return new UserinfoCommand(); default: diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index a1b8edaf28..718b7a5f80 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -46,7 +46,7 @@ public static void checkEmpty(String userInput, String command) throws DietExcep */ public static void checkEmptyOption(String[] input) throws DietException { if (input.length > 1) { - if (input[1].length() > 1) { + if (input[1].trim().length() > 1) { if (input[1].trim().charAt(1) == '/') { throw new DietException("Error! Option specified with empty field!"); } diff --git a/src/main/java/seedu/dietbook/command/EditInfoCommand.java b/src/main/java/seedu/dietbook/command/EditInfoCommand.java new file mode 100644 index 0000000000..86bc326e3d --- /dev/null +++ b/src/main/java/seedu/dietbook/command/EditInfoCommand.java @@ -0,0 +1,25 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; +import seedu.dietbook.parser.Parser; + +public class EditInfoCommand extends Command { + String userInput; + + public EditInfoCommand(String userInput) { + this.userInput = userInput; + } + + @Override + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } + Parser.executeEditInfo(this.userInput, manager); + ui.printEditedPersonInfo(manager.getPerson().toString()); + } +} diff --git a/src/main/java/seedu/dietbook/command/InfoCommand.java b/src/main/java/seedu/dietbook/command/InfoCommand.java index 98defd4e61..ab6daae046 100644 --- a/src/main/java/seedu/dietbook/command/InfoCommand.java +++ b/src/main/java/seedu/dietbook/command/InfoCommand.java @@ -2,7 +2,6 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; -import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; diff --git a/src/main/java/seedu/dietbook/command/RecommendCommand.java b/src/main/java/seedu/dietbook/command/RecommendCommand.java new file mode 100644 index 0000000000..ae26da133f --- /dev/null +++ b/src/main/java/seedu/dietbook/command/RecommendCommand.java @@ -0,0 +1,25 @@ +package seedu.dietbook.command; + +import seedu.dietbook.Manager; +import seedu.dietbook.Ui; +import seedu.dietbook.exception.DietException; +import seedu.dietbook.person.Person; + +public class RecommendCommand extends Command { + Person person; + + public RecommendCommand(Person person) { + this.person = person; + } + + @Override + public void execute(Manager manager, Ui ui) throws DietException { + if (commandCount == 1) { + throw new DietException("Please enter your name first!"); + } else if (commandCount == 2) { + throw new DietException("Please enter your basic information first!"); + } + int recommendation = manager.getCalculator().calculateRecomendation(this.person); + ui.printCalorieRecommendation(this.person.getName(), recommendation); + } +} diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 99bfdcc386..4006612be4 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -15,13 +15,13 @@ */ public class Parser { - public static final String COMMAND_NAME = "name"; - public static final String COMMAND_INFO = "info"; public static final String COMMAND_ADD = "add"; public static final String COMMAND_CALCULATE = "calculate"; + public static final String COMMAND_EDIT_INFO = "editinfo"; + public static final String COMMAND_INFO = "info"; + public static final String COMMAND_NAME = "name"; public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/","c/"}; - - + public static final String[] PARAM_EDIT_INFO = {"n/","g/","a/","h/","l/","o/","t/","c/"}; /** * Returns the command of a user input. @@ -33,6 +33,24 @@ public static String getCommand(String userInput) { return userInput.split(" ")[0]; } + /** + * Returns the index after the command of a user input, e.g. delete 3. + * + * @param userInput user input. + * @return index part of the user input. + * @throws DietException when the user input is of a wrong format. + */ + public static int getCommandIndex(String userInput) throws DietException { + String command = getCommand(userInput); + + InputChecker.checkEmpty(userInput, command); + try { + return Integer.parseInt(userInput.split(" ")[1]); + } catch (NumberFormatException e) { + throw new DietException("OOPS!!! No integer index detected!"); + } + } + /** * Returns the subsequent parameter after the command from the user input. * @@ -56,6 +74,8 @@ public static String getCommandParam(String userInput) throws DietException { case COMMAND_INFO: InputChecker.checkInfoParam(userInput); return userInput.substring(userInput.indexOf(' ') + 1); + case COMMAND_EDIT_INFO: + return userInput.substring(userInput.indexOf(' ') + 1); default: return null; } @@ -150,7 +170,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw InputChecker.checkGender(processGender); if (processGender.equals("F")) { gender = Gender.FEMALE; - } else { + } else if (processGender.equals("O")) { gender = Gender.OTHERS; } break; @@ -195,20 +215,90 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw } /** - * Returns the index after the command of a user input, e.g. delete 3. + * Processes the parameters for info command of user input and updates the Person object. * * @param userInput user input. - * @return index part of the user input. + * @param manager the manager object. * @throws DietException when the user input is of a wrong format. */ - public static int getCommandIndex(String userInput) throws DietException { - String command = getCommand(userInput); - - InputChecker.checkEmpty(userInput, command); - try { - return Integer.parseInt(userInput.split(" ")[1]); - } catch (NumberFormatException e) { - throw new DietException("OOPS!!! No integer index detected!"); + public static void executeEditInfo(String userInput, Manager manager) throws DietException { + Gender gender; + ActivityLevel actLvl; + String name; + int age; + int height; + int orgWeight; + int currWeight; + int tarWeight; + String trimmedParam; + String[] processedParam; + InputChecker.checkRepeatedOption(getCommand(userInput), getCommandParam(userInput)); + for (String param : PARAM_EDIT_INFO) { + if (getCommandParam(userInput).contains(param)) { + processedParam = getCommandParam(userInput).split(param); + InputChecker.checkEmptyOption(processedParam); + trimmedParam = processedParam[1].trim(); + if (processedParam[1].contains("/")) { + trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); + } + switch (param) { + case "g/": + String processGender = trimmedParam; + InputChecker.checkGender(processGender); + if (processGender.equals("F")) { + gender = Gender.FEMALE; + } else { + gender = Gender.OTHERS; + } + manager.getPerson().setGender(gender); + break; + case "n/": + name = trimmedParam; + manager.getPerson().setName(name); + break; + case "a/": + age = Integer.parseInt(trimmedParam); + InputChecker.checkAgeLimit(age); + manager.getPerson().setAge(age); + break; + case "h/": + height = Integer.parseInt(trimmedParam); + InputChecker.checkHeightLimit(height); + manager.getPerson().setHeight(height); + break; + case "o/": + orgWeight = Integer.parseInt(trimmedParam); + InputChecker.checkWeightLimit(orgWeight); + manager.getPerson().setOriginalWeight(orgWeight); + break; + case "c/": + currWeight = Integer.parseInt(trimmedParam); + InputChecker.checkWeightLimit(currWeight); + manager.getPerson().setCurrentWeight(currWeight); + break; + case "t/": + tarWeight = Integer.parseInt(trimmedParam); + InputChecker.checkWeightLimit(tarWeight); + manager.getPerson().setTargetWeight(tarWeight); + break; + default: + String processActLvl = trimmedParam; + InputChecker.checkActivity(processActLvl); + if (processActLvl.equals("1")) { + actLvl = ActivityLevel.NONE; + } else if (processActLvl.equals("2")) { + actLvl = ActivityLevel.LOW; + } else if (processActLvl.equals("3")) { + actLvl = ActivityLevel.MEDIUM; + } else if (processActLvl.equals("4")) { + actLvl = ActivityLevel.HIGH; + } else { + actLvl = ActivityLevel.EXTREME; + } + manager.getPerson().setActivityLevel(actLvl); + break; + } + } } } } From 9f088b8e2ca8ee2554fa55b53407ac755bbca6b2 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Wed, 28 Oct 2020 22:23:32 +0800 Subject: [PATCH 229/374] Update Parser.java --- src/main/java/seedu/dietbook/parser/Parser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 4006612be4..c52dc0c0d3 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -215,7 +215,8 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw } /** - * Processes the parameters for info command of user input and updates the Person object. + * Processes the parameters for editinfo command of user input. + * The specified parameters are used to update the Person object. * * @param userInput user input. * @param manager the manager object. From 088746bc9b4bf4012c954eae0f6c7b474f496ed1 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Wed, 28 Oct 2020 22:53:21 +0800 Subject: [PATCH 230/374] Update help command output --- src/main/java/seedu/dietbook/Ui.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index c5ff4030ec..46932a6bde 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -696,8 +696,11 @@ private String getUserRelatedCommands() { * @return A string representation of a list of food list related commands that users can input. */ private String getFoodListRelatedCommands() { - return " To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] " - + "[p/PROTEIN] [f/FAT]" + LINE_SEPARATOR + return " To add a food not in the database that was just consumed: add x/PORTION_SIZE n/FOOD_NAME " + + "k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT]" + LINE_SEPARATOR + + " To add a food not in the database consumed at a certain time: add x/PORTION_SIZE " + + "n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] yyyy-mm-ddTHH:mm" + + LINE_SEPARATOR + " To view all food in DietBook: list" + LINE_SEPARATOR + " To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm " + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR @@ -714,6 +717,8 @@ private String getFoodListRelatedCommands() { */ private String getDatabaseRelatedCommands() { return " To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR + + " To add a food from the database consumed at a certain time: add n/FOOD_NAME " + + "x/PORTION_SIZE yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + " To view all food in the database: data" + LINE_SEPARATOR; } From 81a6782a8bb7f05624afb31cfc27194a091bf8d1 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:40:11 +0800 Subject: [PATCH 231/374] Add Ui component --- docs/DeveloperGuide.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 5899b377f5..97c8cb3685 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,6 +1,19 @@ # Developer Guide -## Design & implementation +## Design + +### UI component +![Ui component](/UML/Ui component.png) + +**API**: [`Ui.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/Ui.java) + +The `UI` component, +* Takes in user command and passes to the `Logic` components for command execution. +* Updates the user about any changes in the data after executing the command or errors encountered when executing the commands. + +The UI has a dependency with two enumeration class, `ActivityLevel` and `Gender` as descriptions of each + `ActivityLevel` and `Gender` is required. Increased coupling was sacrificed to reduce code duplicates and increase ease of code extension/editing. + {Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} ## Save/Load Feature From 79ab1d09ebb8b185efb2bb68d1eef636d14e7ce9 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:43:05 +0800 Subject: [PATCH 232/374] Add UML diagrams --- docs/diagrams/Enter Info Step2.drawio | 1 + docs/diagrams/Enter Info Step2.png | Bin 0 -> 21962 bytes docs/diagrams/Enter Info Step3.drawio | 1 + docs/diagrams/Enter Info Step3.png | Bin 0 -> 22375 bytes docs/diagrams/EnterInfoStep1.drawio | 1 + docs/diagrams/EnterInfoStep1.png | Bin 0 -> 22108 bytes docs/diagrams/Info sequence diagram.drawio | 1 + docs/diagrams/Info sequence diagram.png | Bin 0 -> 47732 bytes docs/diagrams/Name sequence diagram.drawio | 1 + docs/diagrams/Name sequence diagram.png | Bin 0 -> 18183 bytes docs/diagrams/Ui component.drawio | 1 + docs/diagrams/Ui component.png | Bin 0 -> 10992 bytes 12 files changed, 6 insertions(+) create mode 100644 docs/diagrams/Enter Info Step2.drawio create mode 100644 docs/diagrams/Enter Info Step2.png create mode 100644 docs/diagrams/Enter Info Step3.drawio create mode 100644 docs/diagrams/Enter Info Step3.png create mode 100644 docs/diagrams/EnterInfoStep1.drawio create mode 100644 docs/diagrams/EnterInfoStep1.png create mode 100644 docs/diagrams/Info sequence diagram.drawio create mode 100644 docs/diagrams/Info sequence diagram.png create mode 100644 docs/diagrams/Name sequence diagram.drawio create mode 100644 docs/diagrams/Name sequence diagram.png create mode 100644 docs/diagrams/Ui component.drawio create mode 100644 docs/diagrams/Ui component.png diff --git a/docs/diagrams/Enter Info Step2.drawio b/docs/diagrams/Enter Info Step2.drawio new file mode 100644 index 0000000000..80e41442e8 --- /dev/null +++ b/docs/diagrams/Enter Info Step2.drawio @@ -0,0 +1 @@ +7Vlbc6M2FP41nmkf0kFgbO8jjrPZ6SRNZrbTpI9aLANTQKwsO3Z/fXVHAuw4jjfrpLyA9OnoSOcmcQ6D4LLYXBNYpbd4jvKB7803g2A28H3gjyfsxZGtRIafgAQSks0VUQ18zf5FCvQUusrmaOkQUoxzmlUuGOOyRDF1MEgIfnLJFjh3V61gglrA1xjmbfQhm9NUopPQq/EvKEtSvTLw1EgBNbEClimc4ycLCq4GwSXBmMpWsblEOVee1ouc93nHqNkYQSU9ZMLjd7KIwj+36V/T5O5pXKX+H3cXvuSyhvlKCaw2S7daA6icR1yRrBfncLnM4kEwTWmRMwCwJtpk9NFq/83a3m+h6s24J3i6s9WdkpLtoyD0vIkG5EzgAw3Uk0XPmX2PSFYgiogC2+rQ7gJJgugeHYSSDs0dT1BKvEaYrUK2jOCptr82f2qZXmME5ZBma9d/oHLDxLAzK9zjjO3Y91TIGH9RAeOPPJfFEq9IjNQs294NRsGnBqMwcBlJxbQYsYYldg0Jd3qBa4UdrjXKmbKmleNgo+8rHgLTgm0nKwdBxEaH1YY9PespLOxRtKEXMM8SRRczYzMPMDxYK1FvsdICC420F+MDF0tx2nA+YMIX2cVlpYEKkSXmS9/Lhhpn6lk15zBMLt6CqyaWkibyvIJspUj8IkcLKgf3CqNVok9aTRe0p5SwQGKVGX+a4d9xykT3Zhg5UwT3ZQXLzs3HOMdEbo8k3+AvnmB5yUXpav1qpBOWWsAiy7dyeoFLzJaJUZeMYfMqCpnHclScs6anPTgUPsyQGW/z1UPupiGLhedogaHVh85RbPyajYwUM8IfXKkMUEYLtWI1jZRfE0h/Zr1IkEqftolEL7HaivuqMRBEt7BkFxuxiYQ4TUp3VO/SJvA7tu2L24P3QINujQgL8YOUbIaMfuujOuSHoKEc1/bwtpbmAwuXZ3vN3B5Th7wZHFpj7Cw1eGKt37S+6BoXsEHXMRVdy4PN6SEDzA3pPuj6oOuD7o2D7pu5tXkGoW9JcMT9fya3vdb4+5cEk4wxhvnDh5EoXhEeiB9HIJl7vEqeRprKT7GM5e2RzA5mFFc8NlVPSjDFjGqRi1x2kfF0RuxZVR2Ar/qf1cU3+4LyNeJc3Yx3Z6qpTtJ9SaTO8saN5Gyo+laSCXTmZ2eZQE/syiidFO6l+dr4hKUAltXrAoBM6r3RaH9BgHUOzuhlBvxuMvqwYevAa5jw0Iw+BDuc5o0y+snrPWSnTc/EVsPgRNWXpq0C8La2AqDDWEeXX/ZURo4triTMVXioR9eysaeWUn9qnV8Z5Tjhb6Obqxdddj9eoOeU/KLrVtXlzujCbZ2d/oEXbvMAONl9C7pq768OKxiz0y+j2xu0ZnlMEEVOf5+tW2H3ngLq5u6hj6efGU/mo8aKJz9403gafrwfDoUqCVnFoXf8y+E4pez6CwHjfxzivhTal0L7Umj//6EPuj7o/gdB1/l92XX/n/RD9NzqqLoCtq+MOjnNV+hAmc0q09QGC67+Aw== \ No newline at end of file diff --git a/docs/diagrams/Enter Info Step2.png b/docs/diagrams/Enter Info Step2.png new file mode 100644 index 0000000000000000000000000000000000000000..4d7c1acef13cf5d1f507e9e570251421fa88cc76 GIT binary patch literal 21962 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/Enter Info Step3.drawio b/docs/diagrams/Enter Info Step3.drawio new file mode 100644 index 0000000000..9af0567ab0 --- /dev/null +++ b/docs/diagrams/Enter Info Step3.drawio @@ -0,0 +1 @@ +7Vlbc9o4FP41zHQf0rFsDPTRhDSdTtJkpjub7KNqhNHUtlxhCN5fv7pbsoEApSl0/ALSp6Mjnas4h15wna1vKSzm92SK0p7vTde9YNLzfeAPR+yLI5VE+h+ABBKKp4qoBr7i/5ACPYUu8RQtHMKSkLTEhQvGJM9RXDoYpJS8uGQzkrqnFjBBLeBrDNM2+oSn5Vyio9Cr8U8IJ3N9MvDUSgY1sQIWczglLxYU3PSCa0pIKUfZ+hqlXHlaL3Lfxy2r5mIU5eU+G55/0FkU/l3N/xknDy/DYu5/ebjyJZcVTJdKYHXZstIaQPk04opksziFiwWOe8F4XmYpAwAbojUun63xv2zsvQ/VbMI9wdOTSk/yklbPgtDzRhqQO4EPNFBvFjNn9yOiOEMlogpsq0O7C6QJKnfoIJR0aOp4glLiLSLsFFoxgpfa/tr8c8v0GqMohSVeuf4DlRsmhp054ZFgdmPfUyFj/EUFjD/wXBYLsqQxUrtsezcYBR8ajMLAZSQV02LEBpbYNSTc6QDXCje41iBlyhoXjoMNfix5CIwzdh2c94KIrfaLNfv0rE9hYa9E6/IKpjhRdDEzNvMAw4ONEvUtTpoRoZH2YXzhaiGyDecDRvyQbVyWGigQXRB+9KMcqHWmnmVzD8Pk4S24aGJz2kReV5CtFIlfpWhWysWdwmiV6Eyr6YL2lhxmSJwy4Z9m+TOMvzvEgu+igPnGa8ckJVRejCbf4DtPMLvmQmwa/WXkEjaawQynldyekZywY2K0Sbqw+QiFzFc5KjKsmWnfDYX3MmTCx/z0kDtoyKLgNVpgaHW6OYqNX7ORMWJW+AdXKgOUuUKtWE0j5dcE0pPZLBKk0pttIjFLrLHivmwsBNE9zNmTRm0iIU6T0l3Vt7QJ/A3X9sW7wWegQbdClAX3Xko2S0a/dZIOefozlMPaHl5laT6wcJnVa+b2mkrvZrFvrbEsavDEOr9pfTE1LmCDrmMqupYHm7whA8wN5i7ouqDrgu6Ng+6bea957aDfR66hg5/+M3notcqVKGAYXq4shGLGGKZPrkyjCxYpXlIejH+QRLL0aAh0oNc1ylSeyzCr2yNZHUxKUvAIVTMpwpgwqlkqatkZ5uWMuLTqOgBfzT+q52/yCaUrxLm6Fe/WUlPl011FpK7yho3irK/mVpEJdOVnV5lAb9xUUTol3KH12vCErQBW1esGgCzqvcFgd0OATfau6GUFfDEVfdiwdeA1TLhvRR+CLU7zRhX96Oc9ZKtNz8RW/eBE3ZemrQLwtrYCYIOxjm6/7OiMHNtcSZir8FCPbuVgRy+l/sF1fm2U44S/j+5uDnrsfr1Aryn5oOdW9eXO6MFt5U5/zwe3mQBO9t6CTb33nw4rGLPsh8vqDq1YNRNEkTPfZetW2F1SQN09PHXx9DvjyfyoseLJD940nvp/3h8OmWoMWS2iC/7L4TildP9CdA3RriF6zg3RLui6oOuC7rf9C/HK+3/SH6Ln1kfVHbBdbdTRaX6F9pTZrDZNbbDg5n8= \ No newline at end of file diff --git a/docs/diagrams/Enter Info Step3.png b/docs/diagrams/Enter Info Step3.png new file mode 100644 index 0000000000000000000000000000000000000000..9b263c15233b0ba96f0ba9b6bd3de8b25ec1771d GIT binary patch literal 22375 zcmZVlcRZZU`vwdnT698If>^89)mwB{Tb%T5K*HfM2P4j5`smPL`btC zB5H)FNpzw}f1me#ocjlUFX0Dm@Jdfi%Ce_SDkDlf-4G9Sey@9@t1qlfm z9$a@*Q-aU!P|rgW5~|}cgl$-0l$UP+nuK3Q``;^mX-S{p&@g@(9e!zPw~!DCPhU50 ztXp8HL=ZX*`~=DagFSt{e9@l&){&N!mK2wg6qi=Cl9J<>(Ug`Ve#lEm%PHFbTi?wa z9rRy=auSlD0U;aTAkW~)P;hBt1wN!C!Ot>U;0hGT%l>;AB`+%se$fmG2|(MR-I2cF z>AG?<3KBB%;AbJEzLl{NzqB^E4)FCygMWHx5C34|E!sZV;6U(;uA;nzq=c-rtfYjL zloTkibn|k<`u_hkLmUk{%+34Xk%TJxp!^IY6+A2hBE${k<^2_`LG#2q5om0vZ*b7R z)ud!36eZ+{1u_UQZU4;=sO9_LjD$y;$+(!=>sdwUSp*s4a43HR@K9nqL2jU5|IDlz zm=tl)Ct@QC)&cGwO2M+aX0lQ^eRD5KtZt}|nU#f)j-{tnn4y1&U%0yh(gfpUqK^s- zi1N`kk@JdB2yqV$Mreju27npW)ikmUkvH}WatoF9k&=rHa+8WcTH*qgq%Dz>Aqo*z zo@gaI6C<1#TGPyeUs@~L$Ux2-0WN&NpQe@;5*Z?k3D&|Y{4+V4+GcjBa77FAFnwdB zrN5CC!pA&9&(K)O!d55PF2vAMFH2AD8KJeXIwrkq!@2FRFz&nW*OooMi1$1a>$hIaRhHnjJ!wlefY z+UeWchlUxrnVb1b$_9t{hvIy7eIsp5(TcjVwt;pbnvt6B!G5UlAbFXn2x(ksggI!y z+TGYdDbmx_%F-Wvi}nw2H}ekD!CClV%uQtxW+u8?5xRaRK}yl4zNQG{fMD;CP>iIh zg0{T5Au`A-*uc<2(#=6pe6#P>jyTQZltZ2olz*pJPL&k3q|8R?6fQt-TaJ= zH0>?TttF+65LzMbb|#iq9`fcow%UGZSt(zwfM}m!Tfbmq6EEK=IcqEXSa~}m=>VUoKug2OXjyxuK+uepsc{4@0^uDF zu6@1Dg7n-JJ^T%=Ezo}2?qG((%#4j90?h*>i8E+p>yPkrbBi>O4hry$M*G`GN7_jv zB`w`Ck>GA~UtOH8haA!m85(F49TDmlp@T5RDusAi zA*74~wRNz8#+dLBJN-a~NFPnbNE10mQbA>NUg0Dp{In7_STxQ%hRkrl?&TgwXv zthE+K4(xT^Xl*wXHp(baUYBTTZBPMb0g_Tux*Kx+ zttn+?fBY zNL=paBFW1U_G<~Kw+q^lvkiMN>=5a8>OTZ;TP={9UO`_GSpF$l?;jq0j?@BcY^ z8AQsL)p-&UD2aiNrd5>luNc~|1(xlnu*rvmW?GfmzZ@_7Qlq`tD_9Mwsx-DR``<~vxRg37aQkaU%IP@%XLszin6+!OvmG zuq#Jh;Za}k4|W@lVrINmKL&L~{QAakn?k}2CDVzgW=ha!`Wo0e4O_W&pDCe?I_sMf zZ!3pEuvwn_RhS0(Kh?&VNmlq*%DgI)Np?SSMQ6{nv}FHaAte@T@GwJHgch2AdRdIB zXU|LtiBj$wk3`3re7%ASYSm$vtAhjT-qX#J`WR(~hs; z^%u3U+cG=T$B80;C^`Jw1AThZ?99=!r5-KnRc-^g0@?q%uEQjxymD~zCk{@@z$tW5 z%odudcDVdiYPt>cDC*$*t$}QQWWRqSUSMxBSRHe?Qk9+#@3P%l9?>4HL5W6OdPkc5 zy5}Q#dP7`nljNE-PMdw_Yd$s8KO>7z&=@h$vq<`L^rstgM`bIe?PrmH|K=NRxN_ug zu_A?FiH#$ObCl-sPlfKg71xGxh35x(?-*Gz=JY}bE7BU`G$qXg^>KY=Z(b0mnb@X2 z&SNBjP1IW7|96vHUw`Riyu6iIcO<`ur{83>hN}G;L+3L%!4$`6G;4V8DKK8ZhpU!Qalfzwf9JKn)m1}iA zQ*BR=qx){sW1YiSN0}SD4r`XbK7Djg5mVl_LqOK=zPcl3*GmFtG0%Il3m19W{E5*Z z_@Z@!^uMb{!;LAWD69tyUf_7ztt&YF~4}NX_L8;GS)(HMQi9oc66jf%P1*=(jYKUoNyfVSj#VM^{)Z!iRrpCqiTR@6L+W zfEp4u-z&`OySIn0S+B6f4^q_B**`bTcFmgkY;x5&cK%JbW|se#I=dRGaf*Y^UM{lV zv}rr@(qAummzdX7#okfVkTu`$V^pt`{~h{KRZ#afJC6ap?nCSw6d!)dzKGm$>}2P& zY2{+%->pSd5rjFhto=&mDMcPn?Js@Yvv%p+^nX_`HdQ0H!sBS~7a{J2W+Ek7SJRL( zjqB;*cz4?G=?Nn1uK3DvzEb5C)CpJATmk{+_((UyqSZ&; z=M&V|JZSWZ`7-AI0>#R&+3q+wi7=XaY+P)A*O`P;fl`K$z))clG)AF#Je|MO>*T&M_bS^} zl)&NTScQ18yAqzQbQ`>W=#wqI>NrjGpx@Q(nYsqLMcBWl%@M}Lzaq3WLvrJ0x?5Y@ z<0)q6>zjy`;`Cwnpn;n^-`W+9kGjt~!V;-v8gfe7bRrk?Yz!kaul>Q>j5at;Vw6oJ zF1dN0pPy%jP-^C>8=laLXj@O--SG-X3qk%hdY&w#2hF|pJ@sJMlt|?MntEwzAN%GG zx62}I5&?1I3o)mQZ5(1F+YDfCreo&P;0x4;i}fH2B=W3(0!!vn68*Cf+rCQ1b!Y~e z35gba*A}GMyTp#=UATNb<^4PV((7BP>@r@rOB!Pf;kU&EdK zjrJW6N$t;Ht+Nsgsyk)|gIWCRx73f_*pwa#a814l%qjS+v994*Eaxw9Vvl(ku>JEr ze{U7_%GAqHG#+f!)X14=J(Z1~bA&D0$fQI@wXa1xMGpz1ZTjb@$`R=-EA&?zEO#hL z+Wp5RHotn@dHb@Dkj~`Mq|tYnfxI62^WELH+3VZyr7x2^D)$b#XU|kfrdW(T`qC|g zMs~n~kFtsXap50JuF=3mQg}T5Cst6r`r7Z8RkuZH$>?Z#2VFTUv`twaW?MpE|E7RA znk{NwrsmTsuCAch|2$dmpinrpUzW&_tvG3q%R@sJu!lPHF!qF zxAJyJ&$m<|8fcQafI^Bx&>lCOj1T|&0QiUgpSL4ZC$2IdK*$gi>xe?CUFlM4v$NCZ zK`wD8&yDhqTDwn-PhC!KmEKjQGvMkv*>kXqoQ>IMyTyr3q5Bj5mcKy95O>$&AkW5` z85Fd(h&<}4+J7+_lw-(2l@md$#~9sWt-%@jJ}fb@=5MeyC|L5ubeXZ%>? zNZOxIhv%Oc3elvOU^7+wThX!sQ@Nxs-_3wg7bO?*Jv(#?x5u5pULO|fmI)Q(g|-J< zn#~k#$kQIj>HEy-27HIF>IN{XABX7OW-_78AB#I`jk}&74I5sH;4&Vb7hkvXh$>2`j1PfDATB~nD>J>OPC?Ycr?vC&O(k$+!L z+2uoLnMI||{tE-9afg39!{GN27X7r{UIbgyx0=~7R<~ygO#QqtQSiabvNZJl8ht(PSzS(_% z<`M(V!&0W3X#;9AyZPb{j;Vnem*pH&>xby9t?FLJrMEZ#y$9KalM>QDZk(fHVEz2q z_yW-^mae%w}ry$ZB#8Q@9gc)s-DTEn~Q zdbO*o9%2qJjDB9xQ`QZ?w^Aw?cz8~1gFCVAnb-U57@oMuTMS(N@ucS}c6WhKELi^a z^{KX)U9%2U{{RWc^JSefC$Uxw-u=UYc&mWh@tL@Ik^bGY;MuM*YYndcn8TgBe?P-J zH+uaf7Apju~Y|KYOFZScRuyA#}&ZjWoKbo7Y;`vrM_| zoProKtJ)qjLFI7;cuTD5Y}Aj8a)TU!N~x^*3x^q_)y~&wKSCPu!K-PI6@=f|1J!GW zqD`;?#J$#E4=^=VYuxT*MQ<-z<&t~ygg*OP!$;$`Fz7$2xF75+^>FGfkH!fy<^Q5A z7A>YX>c`(1vj<@z@(fovYod;*cyt6fIKwtZZm?Y_Jzx&uxb>TqiW7PYf*?a_+}2yc zbPym8&3>O7eX~4TopbQH^px)`cD1@HP1y4B;JL{c>YOjeog(k^&2Lnzu0DCx9~^h& zunD4fT3gADi3=5wY5~1WPT>d9KQGxa#>S@K2?9ji}8$w(|bk5l@JMrC|#$JR}|S$yW@`<<_~^9fAGO)_9wKk^Zwbe#I6W|zD5J^41e%?%ip;#d}Ki|_fO*2379lS9rc|r)2Js^qw8_2PwlIuL@e-oCM z#dyRFF9i-h=aAF-&!*T{0KfQ}25pmX6d-P=pJd{bRTV+J_IHfZMb>QY!}j{KkA^2U z&0klWxe4hQ*Ma`w#m}bd;!d|+f4%?Fd9qqy6SK_S9G|fMyZh|adF*c_J?XNDJ{2vq z{sq#mEagYsN-GF^-Ecb{E{!JpRVr?=YLO?cSnd!OMn=Z#FsBwD^S5N=?)7&PB33o5 z_{NM!c1~T5$(3%+)DuDoAVpK)&Qo&)PqicrayF0=_JeJYd{6k$wj-kcdNo7y{xBlA$Nm+)|M9`* z7J|*CJWcwKB#yo6H2JP5JTTS8*}CQ)*5YhxTn^Btj>bi@2Azki zwOg)xuvvkXWObema|s6M7wCw7XzEk9UsxDEFR9?yqiUgI)eq4^rOqGXGN^5cL>|}pCtH=I9YI2%glXbWSTd zg|x>p!yzep^7o}G9A?#p{pA)R|6mbanI$@ldNx?JvH}(R`3984G>We7DF;Iu-N+4gJCrq6)oeYAzXfV*9w+&QZJlD+yQzCgS) zciy7zvkhyp>Jl4Xb6mX5eci+21q&)Fod(|BZLi?nj|3G*W z=Nux}>RS&1tUU~EAAAwIWD-3%zugdfm%bE@(WJMwTcp|i;t>2c3%${oeD?4?>AKfS zGMd`+VkyS$7o~7K8y!Lr5A8pQsr-)aFyCsKz?^4!Sh{iJTqFm+A9x(3+ox`Tk&O=P z;f`%)_HL-CS5Vv?+J|5DX5-HAQ+?Ed$v3DOp!k#k>||i80d2_3^nj3zp7gR`Hnf7O zo7%XME}`ug(=vQ`w=CF7#ue2)_KOLMAMZ3ZS5B*i6KIFm7vAPnhEps&b?S&9v%Yfu z^cVyxu#FWbr?#g`2gf_Dfs^vlgNak_>t64R-a0HaN%G=u!0;mFeE?M;rMtF2T&<)t z**HryEi1n6Ji?(-VLmq>AB7icXZz5ybCIi2vKVGs%r&Ah$H>ro-u+fV=Z`z;&L-+$ z;j0b|vpIibI%GUu-r&%q?sn_D`F2?ptfmpBIfdDmq28F&ze~f>svv}tHY5+bETI^y zl|NyNJJAUa`TR`f!_{|SL1kRVNBwU(F)jVQ8(C)|h!o=*7ujg{Jp;J=d)U|O4``?<+^Kj;t~S4}&Mky;Y{(^{ zX@1kCRFvJ48UD=qC|6FnGh}eSiQc}++E)8OW$o zcC9f-d;R+G-K$StpdUGXRsA7LyG;ID3VOu5_sEblJL8hK3r%+BdZYPW{)*E9Biwe4 z3bZrj5{fCxUv1zfpQdOE?*QsxIBCBe zM$`P`;rcyh=VRt}26FZVBEouqw!m^vbLN4z8_&pv9%yy8VYBC6EsU4P>d7TR&Tvcu z*FH^1FnV)-U`s9D?p;V}Q1g$MX`wNIL23BxKT@9jh|_ITzayq{rIfo<^c$YEg3Q<4 zt@8Ej9zm}P7et%D0bA`S-&hiH?6k^UUe)_dY99R|6343%>q0?Yf4vfJNg9@HZ(KlE z3?9CFnfLVO`U{7ZN?Nt?CIQ}dsuXzh-qTz?=!cVH#5&k}Cz8)fw7gW7vR<(ke3>2; zV6ptJ#wbzv=tHisrB1#5^BSWG_)0ReaEQgN7wPzF#q4=$=f-Vkf$rmn$@PIYSKj+G^J1We-!M@H_N9)@aGwR zR#*2&g2#$~d9U&>>ec(nc9TpQW@d60vP8O20GXc3t0L?*!pfnwZI9wV6+cctH}BFH z$aMN$Ul*M{1Mfbzc)f9R7y^5{vo>y8f?KVYo@sqhUQ#iB^bqBHxRfz?HU?eUO}x0~ z^*>a$O`}a3ljAI_QG#t#*(m1_$7^UXk9_aDm3~M4)VA~Va1G_i8Bz$zcbF>nB~+w_ z3CkKZrdcwUy0%?=#YXnl|L4b2ID`7}2FoWt{0yV`18%gl{@J461ydEXKb3CuFedT; z#?n$dJd>@}V|^Ail2KtJh)-Eax;LBB$mrjI#b`7Ow#h?(9o(en@Pu*R-w}y(u}x$y z=!!i#l)ga#W<=2A#~S3@arO)rGU1wWbJuR*oT*>jfNJKi(+r19u{~2{@I{+sX^Q`F zHFalJz&#PN*qu9&=^A(*_pyc3ath^mst*W((9*&><-};=S<~EyA~_@gJ$07>gL%_G z>Gy}SY`I^+f1xB={_^7&C7R{@M)kv12nhSKbSn|#v*S80gGTNaTZL_EfL9>sD}^@f zG|MR(Oeetq#tJ%Ji5d}~Sr)akvb&$Mw?1KMZ<+^QQRWZWrR(7UK8gH%Uu=`H1G~y6 z_9}(x2DPD3(`h+(o0iJy!H@Zc9Mk;neIzdWu6X+8oCih#qq=>(>!AT1bmK~7f7l^5 zq@s216@!H)^!DjyxdS@Kx&L)?9GEegjqFz=>i8N$Ts}>6?0oGE8e$;SNwnPE z!Kt(KuDkr1*>>`1wR`3@@=#&cUw?(3JoD;r#e~))m4b<9-b;4Zhnz{sd)FTsb}w7| zR`l_e56L~$zQsT$LH@bQ6hf^R(L+jQ#fy&vY&Au%!s&vbI`edUcx`hi(^c+|y%`(| z`Tsu45zJ91^WW~v9rfU0kSjrAI%WFQbKi@WZXh|Q3Zk7~xes26;6|+?S`B%*1ej3` z=hwc}UjW9mR1pB93jcFyzAN{YLLc8=FYAz8?x1R5+~~M39=3S$Tcf+$}6?&?^M z&X>-*#~cc&!LvER)?P6*eywel@T0B`l-MIL(5QTcV_nI0PnDiaa zMiB~0E$tw!G+^g_KW}P5i3QNd$%y!hajb9L$(l?0KssbN>V}NhJ0=eKfUH9F>S(oP zJr&zCCOgk88I_?ekj{CQJMpuj<2TFE`ZLpsO^|gl8UakX)@xW;uiURb<(lUbLn^)V zo4^$gu@~X5?Oz0LB70M*%H+Pi`1_V`5cL)EN>(CB5Y?}8N;RwLuGyvE2X)meK z0v3M29i;t|hn2OFjg?zJ+lrd!dI{Hvi7h~^rAm3cw(3~Oy;-@x-+hL^ex;|KR%;Jj zs!EH7enaN-6Kb1!5DEteS8&S@vyZj7|IMDpGHvkTS0!I%*k2cTvv?y<)F#c+LD(RyvpB&9Cu*i$f(G$F1(GqAW@fYq54dn(-q{!@~f%!0N<3UI8QC* zOW<44jh7BvHpA?W+1UKO%{WdrNE&%AmsX-p*Oj;y7K3JJoXxeJ@Am3)vKY0ciKFP{~vO z$rtr}9j#N?bj2UHW#(@P%A9y@HZhQ8j;rKeX$hFH=!$<|qGZYN$RcDl2!3FgfUit* zrfOG?+HfUi7yr6fSX`Tb){GVG`D5SW!_!2y!?A1l5mgL8+H0-DCdjweU^o#B0b+we zxOT;QK^Hgt-s!vB7hvf!!b(H1!Div+RYBA3#N^}FMB)G}Sic9F|T)yQfW_60c5PXYl9SbN>Ox*o||T0XCb#TdH*>P1n`-zIqUfQi^Xh z#lt=wWbtS(JbVgJ!sbRFBQl$=nHpQeAc*g_f9_|{e3MH25H$K!5#Tw#OI=`v@RN3K z4T&zk3u&91Ra_q>$sg`>lAuy}eP1E?0pCIvt$HG#;xs|d zpix{t>nIuG%v9hMQP|>6j3G=Y+D?CPpJlPj2YhWxGySta8wUrwN7=xdNYEG<#&13! zcNRTT0uF3ociaV@YRqq|+kD;jXrjn@+w7C^$?yMZ0gk9W$N7ADb#Dv(qR*0@9=G~+ zZr${tSky^%AxqoPMS}I|)%~f^+G7Ug#;%|z5q@GEE}=P&t5M4ye_t7lg~Ra9p^@2k zg6&ZkSJe842Hx_WX)e6SallWI4zZ45iDUa9*)oS{ZGj~IxRAifGnc;ep;X<4^V<4T zEY6`dC@;UCm=e75z~%a8GM@+3sqYpe1bFwXUHRY{v6v=Tw2tcioz)Xn(24Bjc~?@* z&0Y5V`Gj*+L&LPtVj{nG+?o2(0a^l*paq=gtgViZNA$Q;0mDa~<@4-1Sle6DkcBW& z=sS>GV8CA&Sb07`+2wzaJ7pcP#-idcgrMS05kK6g_zN5@VE7)IFASX;#7t4;^Yf|+ zoRL|(ecvwHH%8|^3lPbs9aeXyd-_KMiyC=5_d0`v|LNyc=|STIq16iEzS3Keq$9x2@W+POb-jR1ihw8aRZ z0wdPH@`PA&{RWvO1^+K%9rpQUp3zEmc<5%)EyB6Qu6Z4wa0t~l@)j0jR$+6;tjzIB znDmnzXIl-F4uzBHwC7flKWiL(>GPU=P3f7idtjaJ18Mqo&)xEeGInro=O7fe9itr1l%+@`uXGS2rcD#+0}Y=_9hpV`oY3*<{#1}Cul;Y3AAex$HtgydPr5Sdd=1_A~&(A0(g$2ZZvBO zkwb7;jWu*s!>2S!#vF!4U%LX?FdI#n1iZbl;MB!{ri9)1Z}Hzj+9AJSj6r!B8g0OI zrc3%@(3X6Qbm}`zIn4Lq)none?d!H{Yjbjp9E9`&p)8eY2w*5P^XA9FbYkj!b7QtZ%;(b#hnaB_r*p`# z`e=bn;P{GK+s0>lBo6b67J^s%yKv#s2#LZ6^Evr=N%^)<6kDl%iq*}2V+*IVLN$pqG5PXcQKlNYq$}HUjH@5j&m4zHw(cl5QQP|?*R$_C@>!Mo z9*Dn5t(<5rIOP{cetilMkl2j?Q}VP#4bKV))jBcxl%uNx!Y#6q%dX~{WVFY(p}$im z(rvpn;my^P%ebwZ!QCg;hZ6}TeM2w7xCFbDQ`oBAqufK1E^2RtO~3g<$&uu)J+gEw z-4FUMrGwUJy3Sbf_V-Zg0Op&*2J@wgb7)GjAvY%wHXA4irK^%5M%A6ep1H(*;UhyJ zdUAFyhR>JSNP(;_u@0-{aGH88Y>gY<4p6<4Hf-)Di2*$Pkr_4XViO4dXeu4qWDCoQ z<)|#_&>9^Lpx`Q4H4`FF!`cGKmu%5MQ)E=R{7<V$Bmc65T7Ns^}-Dq9Vz_UBkvI(`dG zn%D01@1L{gp{0E>T3BLYzWedwdK2gXm%2lV-5qMaRFXY@gW&9xP%)Tmss=NoaTWV# zk%`0$o2a)uKx<=6@UK=dM~Rl7QoIsaMkvff$OB*SkZr;=2psUP_50N1x@9T19$Vr( zNEULt*b6!D=2Gyl7zHOl>NV)opTubvR81iiJYlL^gS-PCcT@#FD6iEKnCyBOSJq48 zPNNH%pIcU$SiHc(3xf{s1(qB>fAvLJuFbfEZ`C!En75?8D8?jW>G2X13ET!Tz>tdG z@_ZV#=|1u@bVeyO~&q(j#cWw$HXd8$`CEh-whSzl~wkQOC(?JHaY8);1S)W}GD?F+(flO;G#2)l< zrvU+wsUwKbwv^JA7XRap0$iV1 z-17HiykyX*YF%eQj=dF6zalAKu}YfOGSz)J*0@meUO8=Qb%DLR?;M5aGa%l2@C7Q^ z?a1Y-o^Lm1lSoN#D7_Y=WS0=ydQXYD-169sv_6G;0~fqu+z{pp)4^B|E~>;vRVLWHAN zRNN^Yf4UsRoZ7A8lVpRYY)gPFW&USz@WoqP5w_>aJ0;b2K55T`daVB+-5#OKxjPnL z*Vr`^Fx`#}mQw4#D3 zvv=Z-Jush*72dwA-I@yhn99tnI|$bk@<}hvTbu|)d{=Aoe9J$IeN{8(|_Ty4Ih3Y z`lba7%#4Cmy+WILVsW@&SS`1b?G`1BTZp#df>cCpqBcp==KzjHY2jL}0tqM1`cC%r z3I)UdUq@Sy;`Zo@!)5N!c6_pNvi^17U*}CQ-I#s#kzK8ls2kvOhC0E!4$Apb6{bQGiJAR{kI+Y1E8glyg?wGX07<9z z%-;}lE+VoIe+eC6^}MC9aB=bbQYReL@k?H*3pONJ5^{s@9j^O;%ef9!!Iq_&3q+45 zdyIC1M)#l~#&-pYxzoYByaO^HRd`F$M938dM1KoY-xV^t;-6=%XT7S0xAEVruyG?P zeD6I6l38LBof}#&1-Pz8uo6X{M8JAolO(}1*`M{#Ws2yctQ|_tjKCBPGU~6KMUNXL z#9AY#FnVvPzU1%J(0crAadk>d-}%PmoIOL%`W{Wqae_2$57VR6B%e3|fg8W(C~XR_ z+O@t#9(`Fe+sLQ4G@tCyq7F(JhHKk>o?{mceOLOCzm%3rco98OL;;m2bGRlQ{yJ{t zBQeGBR%TE%?OX8$K@(Q*(wGRjNWm)8CFl(h$>w~d-CaC?(f#(FQ_RTl-e)Dx%Rn&E z;@S$tndETls}E379}6mvtOjk$cNmx@GSzO|@frvW5#8;NX}zAON#uyxP3 zI+`h(_-lZKtjHTj?rAe9^|!+v_9^8a1Jd(n7f9LJy!`Z9&d@t!D9|U{dG^j#c_`Ui z=p3+tQ;a3Y;b20?>m8QZYu?;`!ZcQQLVRCO%=TyR6rBG3^R!KTPKHLh1wDN6FZUFM z>olGF3QFje-T`2l*iu+X6eyVV*SWG@<(McNIN$55I>H(_1n4(5JND{bf`8gT-pqUM zjK}eFqdxzcsdm{pXE)bJ3(KUI3VEiHkWM}&2kMT~${lh@y2jrF^2x7YM11EXa=75U z_tGCwzZumU8?SBb@?&eJyGedvyvDOSI_36|IL(zDL*p)S@8t}AGxTxkM|F$uXl{d- z6r!w=+mZM?f9@ilRN?(DH6w0^Qb81J+jhSk8MSLr>I_es|JzY;Iw0eR!O^G90=xrz zI}Z>LJSKpdp+0*1Cze#W3W$2~X6$e! zB?C?BgOJ|!zV~tWDinmieg8JG?9#@ZHmytzGW^s8MG&c9${=lE5o#*yjkNiNY&Bi- zA8%j-IJiLVRfzG%)(T>Fbz5bMa_%u{spoWHFW8??D50x*oS3BB8V`@1r84PPPzAHD z(_bdreUVBNCp&2EC#T0t>gf$m9ciV<3xb3=J2g;snWrd6*VHwJ@XX)W5w5T)QMc_*ngmumBlm#YJkQMy(clP>ny|d{c;SsFdxZ z?!n6&I>LlCaCBzr!+k=Y2Esg^PQC&{=yRv56&|`1yTj{iVEyn-fC2nl@9d=p_6-@` z^+46z1@tQj?tA~pMgb`&uxNuN3elxGIbg~?akhEb=E*&@HtxGXLb#a(-Orl7UDCTg zA%#p^n2c;dWG&GeG(xnFuQKbOPk`QfEL>wr=U9B}nxW;-rb}8-nv>yAVJr}S>e!|o z*bbztC;ltXD0`h}$W@IO3T2u}0PeN={_Tsb9ZBu(hNys=E(@&znW;l%c+D)Rg z$-rwj3yr%T+$I1$zL;jP`fe%3vRh>8Qr*J>N1!8CakWj~9etMubh?iKvo!XFd^1Dy zvYQ-2QNG1Su_t&xf$B=u2#bc=ceRIxN)v1*&N0!5;P?Tqf5>f9EkJHxgr049yjE!g z5k(BCNR-7V^*{&F*wTa9oDYbw*;dG};U3}ntERd>Kz$NG3H`qSaa*nl>iVP>z zI$roBD3%9}thrYKS(sXfNX40JxKh$z=7VonFEiF~BXB5p+EAWdqs*UEOkMO66m8I` zPnuLHUwe}XtCp4F^qnq{aUvWB+~ogEfoTT@=@6oYsWZ=_BKe4Qq+lX;MGw~xYIGKMOcGKeGw!(Ph7A@`mzj` zq=3xs6~|V7zx9MUMRIf0()4YX-=bD^vUYxbBCJP_Ws5(c)yWO!Lxm{)1;p4w=^B+> zMOL>0fCPiK^-A8&*=F>x&Yv0lz{l7<87a43@{u;oj}VgP@CE7+&|@M^u1{XB)wA~L zw#d#sn#@d&ABO|^*zKnGUnu=eLE?%+;$c6^DoS9w*^`6u>o0d>PN}pXMRATNxD^-2 z9~UcK=52EG7F5!bNW}37d*ct4X4Fw!Mdo*Bpk)(TGkjW)gb8WIKb%V~@4F`{S7I4% z=rK=5t++5RaW0mmxcmd5ndpH4cVuqRIYncEcypc!F5JK2%Icsm&lKVAx2*j^h`b;d zc%I4QyG8f<;d0ALlFT81-!1@@l5|@9_`~WHPKk^UcTH=El zc1#Qv9XmAGqjvOlnwWdQ?OWw<&g9-`+go~Cr`0?;mAbQ38s`l3kkxgSaeo7TTp zGuY%X-RAib+wFW(JNuC{4+;^7RczX5LiT-cPDe%a00?Wu z1WX@@t$wL@fZ-1!_kT|hfYe5<4p#DfeGWXp8W=5M`GVQtliM|>K2O1P?T}FXi9cYw zo}h$x1Sqa}82#u|UOad~zDV3;i|-HW0<4XY^a@IX?r~*D$?39n2zsD_JVs_|xiT&< zCG}T{_s0i;4tcv^CF?UK{K&kT`Bs>vzgU}@)StXl|Cad5E>`MO3@)c-N8tPC+Siie z;~@R(2X3BGwF;uYzT5MCNDCaK+GBFneX(ADo*zc`0FoM@{|AtnPSb1~m(*{Y{#hNX zt<^CaEWf7xQn7OprmS(YgAC4llF5hvZhikCx$jPWss<)r25(3JGbW_UD^B zzk%p5`|Iim$2-&07b6@WRQDYxAZmY?2C7;eIi0IgE(lmNi+KcfY!JGqOvHFzrMhTo zgtEhc{yf{6+x4jkvpx&4q5K`KpIA&Netzf1`re;e7o$?OX~@c$c1`ssFU*|H@A<;< zN?9k}!N<4I>DpgZc2_FnqKlB1n_0SL&6e)zl&OH7GRg$YGAC~i^N|j<)6L`4ecD_R zCuJh@q9N5K;8U{)jES8zd#2~pQy%6NNP;YbF?v@O08FG>y(SFWpH6qplq^AZ2c8+S1QAVJe-e=|uX>f{JlM@Ktwt{)COZ{-oCM8zp zCa?>}S%j>4$qaaVv}Jtzl^$f0?7~abn7Ts_p2{(&fC0}anxRW|pk(AxKDs2Z4(i*W zf@D}T&nE3HZifpAicHXzjB}_x19F%~{4FnXXmffGFmq(udISyz9f5%#s$XaJqhRN#!Ou5tzE?^pnh^Wq*z*4nm*RcjnQx}#U4Hv z-hDFjU;@S#r|LlHh~D-ZsWRm*EW}K;VNleK*;sIPVIIi0SPl-8Ae#clMJcFS>r@aH zrBpq@8J2_*44Os6+M-gaFuDn~NhWD7F}tXdN+jg()y~0YzC?hqq!mM1VRIa$liJT5 z)7%hZnwP6UaQCIr1rCmP@dD%uwQh8Fg5C8}c0T9=w%AyR_OU@jZY{|nDmK7G__`(m z91FB8>TrI;hZlIL&C8^c3n1<@nu*ZnCXq`8a2JWmd!+})`;?PPwXE-ITo&D3GpXXb zh0eFpuMD`UdA(zoLJzT;SDA3lDqA-s)Sn`heG-&;V<+s490*55(joMNl3pE%g-T-f zCK@jEJXT!T9*|rE9FPtq>+^_~u~`(y zPUUq20MVfE*gJbb44wPqMc}0M6;{KDgH|`XF~K_iH=Loj>mI#UFle(BP>K;_>MaYM zX+|T@yCCK_=gWp|Kj2@zU9ztBQYsJlG2_~`p?LSCY_do9?8ctc`Cln65!Ms=J2%Q^ zfC%RSWY{Whx+4+-&K!EI*oDs}Pz@DqgPbU}qs7t3vtv_(0P{H-Qf@bu?Ob(uY-5Lt zC)Lw=LoI^C)bqu6*i54L$<~o`n$~)fQfV+0Mn>IAdXl4bXdd%pnVwdB~YXjk@c>dj}QOXMf*cp**_j1@-j@CZvWhLQOyO9#hDOr+$P1iSRS=mAosSU z_*&u>p0cR{>FICPj+{m?+KI68fOg(xb+}z~#lnMxi1tc*8>-gR8}IA{A3jE@WTpoM zS)&KdC8QH4hqGn_=PTo4k7`>E`{7qbgOIpD?G!v9z6nmGx}>%^^SK|i`!@l|e2erC ziHu*ni_M8&BcFQB^nk8nf3>#pA~=vybMdv<)hBG0EZH;e4WiyC67o(4V?l0PqpPA7 zHv3B`9Z11drWq>6SV2PV$Mb4~py%R$2Uos0B{66vuuZhwEJ8LqN`?_i!U=0$nlE@- zVzpns3}l0!i1u7e52xveTSvu&gPtWG?r7a39(z)&Wm=`9NP>`qp!M~Z_u_u~Y#cb| zyTu}LdUY=T|7qn)ndeMC( zSx8%~r;TZP;DsB!TODHBNAAm7)?93JZbVm$wU(Vp#eHC^M4mWOMKQngy%ZM>=TVR_ z_}w&c!6WC}k@vagDeVN&FY|P?&G7?)q1;LBhGM}K&-D=w)lS95ZokIqrk4cr7>Q&!i$vzWXsiFwBN z%WxnY{|j`oePwpxE?0ca_#61+zW`Z?UQaW1d&4!4cD~?4-wo77s(32ls%*Jg$~|>S zvAB537WJBl6Pl45xuf-RpH$g|On!$KgHDi}y>;}i=S9I|tG^DyeKh)ccvUMB7=oxH z=qyT^557DJo#IsHv)Um!p({^H-o3Z=Uc88_A4XAP*T0!7-K?EZN<4Z!^fWVRKlpuh zC@&+5jTPyz33tKPtnhg{);V{V1EqE16f3tEE)i@)9BMd6b-a^*5NdKX-pv}mn3D>N z{)+~~;9L!nQBOqpRoJK6_$_b;N{noJtrlv_|WF&{_eQ(M#E$l9b&7~9XD7jOJw>W zWtQ6f^kyNUP149-03Ks#DvQT`JGR65`nYv*T|l*3EJJ3uznV#vjx_FUgFtK^gvKC3 zcbs>8G#C#6zdk~k8Hn&l*iFMKrlZ7|In@K_)zWa!CU)wmi6v}GJRJ9BB&WY8cCO;= zp5+L8uGL7X>}TZ*s8cXWRH1mHdRNown&p4(%c6}rqhnpIF{=HMS^uH!!P5hKM2K*G zt|L5k`Sm}x=J(`HIK9R{(@l1)F6nysaOJx0aO7t>c<2vwgZ?z0ps6w6Rs`89WD={w zwj?*ft6pdE%fshsr|TS6zP21HyupGxtARcKjuxFRv}uT@=P{F3B}k_pdW~Iomq^$h zpd@kaLjj7E1kX{H0oY`uU(Ikf#3#&r_7BEkB)~)>%lpl+3fM zLqt{&o{>}y5gvZ(1r@pE2oy6W4Qgj_$5r)&8H`^rj%fx@Z?tI^W}@W(Ih2;R8Hz8g zPFDIQ%0{8u@AsX*FfQ+D?x>S`Jy~hauXn3CR}2n^nw z8Up7)7euq4s%z18x=f^$CGh9-o5;`E6TfU4JPoX74dsx4jc7;Lt8>D_Gh)RC>{3r~qnjjkQneB4|_{yqGO zKd*;HyB!;9?BRKc?#RqmTsr8w>eMEm z&a}l}>-pjalq+LHU(_5*4MVTB)7dc>C|wnc_Va7|8r4ZWHuOeq~&CzaPhTrb_q zQ@)65SD&^a%Fm28lrzE;c6PqA*;BKx*STwyZ1}Lew5a9Gzl+u#>PF*Mui)mb@n{ew z;N?O%T$(pTk%!2dHjK8gm)?C&LZ|BZz~8Ccb2lq%bRcC^>`rZrcbl!Q!)-3rYmojr zy$$ID^=@p@ zQHcq1Sm$N3BR$c5@Yf$VFXXIDYmP#%sO!keHNT8%$V)es9trj?DvG;h^NLJ&Y}p>0 zdV1b>QZV%gLgHtNgNp@~KQ;gtW4FJ!*ay5K?sjQlTLj1fGcXUBMd+>OpA$=u8b=T_BD0X3e8A(eD zRKM=-DNz_WQo?jI-+29D7m-_yFAc0O8J<|^5JbN&aSzV;EhX)IL0vA z!!v<$^+(&%utisl!ElpfLL6f>3dojj#=*^{=i6Ql%_1d;fBgEolP!p>>kx#Ws^0

?C`6>hWp#eU}! zu9V-YC^xaO06wU|SHX9g8<7k?J?NU5v`f^E7HAlfE z_8^#oCZgWnJuawqgh%=$sRWwr5z-ZZVDe+INMu(VNXW88!GE<*dQe>fu|GY7se%}# z6?}bfyS9851a%jQ5mFMrSd*_@3V%R5{AeE9>zjk9HF9Q{VLGz+%Xg$n5>3r$Fi>r<6lQ+C;(t~N(*?>b|LPBpar!us1&>l;&m(2-g@2(W zBZC4Q7zMrKgN@=(4a_AU#(DSIu8pwr{(qZJ3Nl0>Z+uf-!~h3^vhXJi-C!c=$6mfm z?-KM#axh|E+$-&VME+RE$CtO;kcA|n89a(sFx%r%D*10%C^O<6u6wm9OQJRe!qTl{ za_dHW4UmFrNec-rk;kY)b(Q0cBWBMym;Tt&x$sEAFW!dx9OC6KY%OhAjT`|{6r|L?>hY&3j@iL9D1g7q$L zFSg-3>#T;8!5cDVK$i;lj!lqMwM7is_gi^HFL$dbE$1?A@^-tzuh`Y0@B}wpWi>U~ zIcf6_cTqWaujMI~H>vFv!$dk4RrAG3(KQi9Z%0}>;5Pd@W0}h;l3Sg#C|xa890$OY zWk>PN_po7DogB|eDm`-#vZa00rQkr>xTQ~ghm05e+Z|Cx%a>a%b_Lvg+EspQD0V%% zwjz#zK7GzXv>&k}N9!P#ZA|!^6X?qHyYi>UU(kX5z@llb(MDD7W${Yl7X;U41@FeN zZ%5wWiT@c({#$K3r>&aeHST-oRODudrm#-KpJ_Q(vSI4ZJMz_X7(1S3-5?7JCXEdF z#c9gQsYtBFXn@hXRzA9$X-Ps&GS5Z59n2Lfw=0A@bnd}6k`6(vV5d$`=3440q6}sB z@oty}afRd|OHS|eY#ZAaJ!n7?yo>$CS*u^cBj1Pjvh&REupDTNb$+*?k(yXk=ETkxIVezNw% z`937TFaPkdu=jNlO=0?IEmQ#Ny+~LHnD6}*qSr}WnWf3PG(pU##KnFwvFVwy6()#o z?(5d{X$srnJTjv6tY$(=i$Kww*PGLP--_U_%3v|QYVVUs-Ack;Y^)jaM{9|BC3GiB zSN@68$MhfW&Ks-~&5P)n3YV1* z|M<1>$koJuI-A8IG?P@R;6KNM&9tRtm~aNt1*#o3Zb`ROv!6kQ0c2j6k7bpFi_bg> zBlTyAd)1W+f0la0prF_2SE&S^Vq@`3UZ7OMN@!|r{pi-(guy^r=j(B)x$|v#d4eAw z3zpXtKoF7uzDY*J;ii+Iq7268f?7kg@&-r``_@0 z=_7|C>B|atuYHnIGW;zIQ$ZzA(!}-}Hf*cGF4IRoY_7q6U50{8^IrFzP;ygNz5O3D zMU{;FlY5`FoICd0o>R;IR}+2WKYv=cZ4Oeo{JFWpl++Ch)^Z7oJ9OUPe3o-9GS{Qf zoXp5+VUIk5P}-)P3>@Ny9X zN6m8fpD0lmWofEZD_L#z>h94$?750(5;If;d_uLqZ&a5_E~tDF&Q0zo6Z&@ zOBAU)!S;!u%HiusL_ov&wlj})kyJ%R@G5HE{-aMIF<2jXC=I9oP!)tyq{(NKAT88x z2*SaZ0=f8ePLNM%2u7N|^QVT08aOXXcw1=(Y|g-LwT`ufr*OLSL(`Qmc!;}JDnKsh z`j*T^had%}H6%v;fT>+FsAM7XLDc8Tefxl$_yp6k0iN#31X0DY={N{f(p<~`b&i7M zDPn3Ul?Mp)zO)ZWUAGYgEWKw_VFz_@Dv_xPqU?M6eiF3Sn+~GT9=7=6Fe0!d4KsZ` zC=m2kp%x$kPXjQZw~hLeHaO7Q80;D-7gS}L!|EB5sy?L4H`W(cWs@;T7bJAv=>X(m zw3M0&V)3bn#Yebm18ZcyT$-H)qB!x44dF~95$FQj@}U@&1|@^K=&6vcTS0Gus3~E1 zs)!WksX5m9pdksm>VI>DmPF4?Q0a%~f9ej+tbdZE8YL>&p78FB7$GLJS_uA;ZJbGs I)_y7f2Lb(QV*mgE literal 0 HcmV?d00001 diff --git a/docs/diagrams/EnterInfoStep1.drawio b/docs/diagrams/EnterInfoStep1.drawio new file mode 100644 index 0000000000..b861d31b57 --- /dev/null +++ b/docs/diagrams/EnterInfoStep1.drawio @@ -0,0 +1 @@ +7VlLb+M2EP41BtpDCj0s2z3KcXYXRdIE2KJJj1yZlghIopamHbu/vsOnKMlxHMdNnEAXifw4HHJepGY0CC+LzVeGquyGznE+CLz5ZhDOBkHgB+MJvASyVUg01EDKyFwT1cB38i/WoKfRFZnjZYOQU5pzUjXBhJYlTngDQ4zRxybZgubNVSuU4g7wPUF5F70nc54pdBJ5Nf4NkzQzK/ueHimQIdbAMkNz+uhA4dUgvGSUctUqNpc4F8ozelHzvjwxajfGcMkPmfDwky3i6K9t9vc0vX0cV1nw5+1FoLisUb7SAuvN8q3RAC7nsVAk9JIcLZckGYTTjBc5AD408YbwB6f9D7S93yLdmwlP8ExnazolZ9sHSeh5EwOomX7gG6CeLHuN2XeYkQJzzDSo9o3nHXPW+jH+g1iK+R6lRNY64NaYwipsC/Mea/sb82eO6Q3GcI44WTe3gbQbppadXeGOEthg4OmQsf6iAyYYeU0WS7piCdazXHu3GIW/txhFYZOR0kOHETQcsWtIutMLXCva4VqjHJQ1rRoONvq5EiEwLWA7pByEMYwOqw08PecpLexxvOEXKCeppkvAtuABlge0Uv2WKy2o1Eh3MTFwsZSnjeDjT8QiT3FZGaDCbEnF0neqocdBPav2HMDU4h24amMZayPPK8hVisIvcrzganCvMEYl5qQ1dGF3SokKLFeZiacd/oNmILo3o7gxRXJfVqjcufmE5pSp7bH0B/rFkywvhSi7Wr9a6aSlFqgg+VZNL2hJYZkE75Ixal9FEXisQOU5a3vGgyPpw4DMRFusHgk3jSAWnqP1La05Y45iE9RsVKTYEfEQSgVAGy0yijU0Sn5DoPwZerEkVT7tEsle6rQ191VrIIxvUAkXG3OJpDhtyuao2aVLEOzYdiBvD9HzW3RrzCDED1KyHbL6rY/qSByClnJc28PbOpoPHVyd7TVzd0wf8nZw6IzBWWrx1Fm/bX3ZtS7ggk3H1HQdD7anhwqwZkj3QdcHXR90bxx0P+ytLTIIc0v6R9z/Z3LbG41/fEkoI8AY5fefRqJkxUQgfh6BVO7xKnlaaao4xQjk7bHKDmacViI2dU9JMKVAtchlLrsgIp2Re9ZVBz/Q/S/64pt9w/kaC67NjFcnNurY3JtsdpNIk+WNW8nZUPedJNM3mZ+bZfpm4q6MspHCvTRfG5+wFABZvSkAqKTeG432FwSgc3xGr1Lic83oo5atQ69lwkMz+sh/wmneKKOfvN5DDrXpe9lqGJ6o+tK2Vei/ra18/5Tllz2VkWOLKym4igj1+Ktq7Kml1J9a51dGOU74m/j66kWX3f8v0HNKftF1q+tyZ3Thds7O4MALt30AnOy+9XfV3l8dViiB04/w7TVeQx4TxnGjv8/WnbD7SAF1fXvfx9N7xpP9qHHiKQjfNJ6Gn++HQ6FLQk5x6AP/cjhOKf1fiL4g2hdEz78g2gddH3R90L3bX4hnvgJO+jl6btVUUwfbV0ydnOZbdKDN5hRraoOFV/8B \ No newline at end of file diff --git a/docs/diagrams/EnterInfoStep1.png b/docs/diagrams/EnterInfoStep1.png new file mode 100644 index 0000000000000000000000000000000000000000..f7b3fc05804081a9604ae2a1e2dbf2088269345d GIT binary patch literal 22108 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 AI{*Lx literal 0 HcmV?d00001 diff --git a/docs/diagrams/Info sequence diagram.drawio b/docs/diagrams/Info sequence diagram.drawio new file mode 100644 index 0000000000..3a9c45f381 --- /dev/null +++ b/docs/diagrams/Info sequence diagram.drawio @@ -0,0 +1 @@ +7VrbcuI4EP0aqrIPoXzHPAK5B3Yym9rNzLxsKbYCqjGWSxYB5uu3Zcv4IkMMY8/MbjYPQWrLknX6+HS3oGdOlptrhqLFjPo46Bmav+mZFz3DMDTTgA9h2aaWoeWkhjkjfmrSc8Mj+YalUZPWFfFxXBrIKQ04icpGj4Yh9njJhhij6/KwFxqUV43QHCuGRw8FqvWJ+HyRWl1by+03mMwX2cq6Jq8sUTZYGuIF8um6YDIve+aEUcrT1nIzwYEAL8Mlve9qz9XdgzEc8iY3fL3bRK/rraHPJ0/al5u7e+Pq/twYpNO8omAld9wzR7fhC53Q5RKFvnx2vs0AgW1EorlaBlPyggMSQm8cYUaWmGMGVwJpfsht4/WCcPwYIU/cugaqgG3BlwH0dGiC9ziCW9iuHwQoislzsqoGFoa9FYvJK/4DxylJhJWuuFhpsnO+MKrASKxeMeN4UzBJoK4xhedkWxgirw6lzyRpLUf21zkFdo5eFNxvZjciSbv5burcM9CQzjnGUa7qqKprCoBGlIQ8eQZ73LMvKh6ijC/onIYoKPpIxe0wZRqjaZfRdOvAVLE0Bp1hOVSwxBvgF8dnQHl4o1nPgJm1FflNwRj7IA+ym8N4mVuLtEYBmYfijcAvsKlxDPwn4Xya9C4AsDEO/ZEQKeg+B9T7KohOV6GPfcllcAXbfpLTJZ3Pe1l+0FscsTk+OFASTGzwoFMZDhCHF7EstTU+krc+CCoWyGCXyOBUnBzTFfOwvCf3M6CEtoVhkt97V3G1ulVy0qTz5RTa7e90VplanZQ+IBYLPr1HFbXcqozqDWVU6+rVN3XVST9HRnd0aQ5n+c3ZIfeGjrqdYWnsk9EHRj0cx9gXicQZgX8ik4NFZuI54FPsVFtAQx/ApsRGrlzR8LIGvMpXyaVAjO4ZDloKsofPcZRArEmhVgW64D0BLYFUbiRl+JlyTpf1sruX7m8KZ+bIN4Wz4KfMJ9+ppXolstqO0UhNlYlM/Y2JUhSUiVrTTlOhUsRgrduQcAJRNAaUaDgDToHTz1pzOt4QngTXviF7SXTtDw1T9i82WSAWnW2hU3jlD+tl6oM2Im/3BBpUxaIpgXRLc/pmea5qLtw1h6y6+PsneZ+xVzedSvAd2M2Cr23YXQUM+5cJvtaxeFaCr6nXoamCORh2heVx9WATVaQRhktjH8WLpP5IS47sTMYt6OW51tecsmK6g/YUsyKEDSQ0ZVbrtYrjDPv2sPBXFjjL7bsnxttKRVyV3a6lUi2AFfIUiOEFKI6JVxa8pt47GMbsOv1pKbRV5O/UyFY9CmroqmML1j2P21bFav199+HT5cd7+vGv8OZiNpn68e15TS1kjmbZCci/NGoqkl5D0r0q72YV6tEHf21UrLVOUousbmLmG7jtJ1DjelVvVq+aXSHp/GeQHDRDso3ko/Zp1WotxiK8xzQ8u0NQWiXHp9cQQjDrz0bTSzAIKIQ1qfhFw618JuV+2hx5IPyEb6f4FQf96Yenlst8JcFooBDtF1+26ZTc6lSZ3zhEVYKHMlF76UQtHdTCC1b7HS3brNSzc3Ctb5dOwtPKXXTzxDPpbYu9+tTz16SBXf3eo3mm4h6eqGMaHFfdtV+RWKWKZDDMKpRjK5JTaPGdHBjq+oFCw9G0UwsNRWM0/Ydyovbr5TRSKOR4Fxmmblgnnst0l2I2qAZ/WmI0OC4x+nGnMvVpnPod4P+Z0QlyaFQSGvvEkKgbrnV4ppP1D7r5b3fS4fkvoMzLfwA= \ No newline at end of file diff --git a/docs/diagrams/Info sequence diagram.png b/docs/diagrams/Info sequence diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..7436835d40ada89e7b59a66c988946f0694a93a4 GIT binary patch literal 47732 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/Name sequence diagram.drawio b/docs/diagrams/Name sequence diagram.drawio new file mode 100644 index 0000000000..cb20efcdd5 --- /dev/null +++ b/docs/diagrams/Name sequence diagram.drawio @@ -0,0 +1 @@ +7VnZbptAFP0aS+lDIhZj40cvSdMsVdUoStu3CdzAyAODhnFs9+t7MYNZ7RA7TiK5fgn3MhvnHM5wJx1zHCy+ChL5t9wF1jE0d9ExJx3D0DW9h3+SzDLNdPtmmvAEdVWjPHFH/0LWU2Vn1IW41FByziSNykmHhyE4spQjQvB5udkTZ+VZI+JBLXHnEFbPPlBX+mnWtrQ8fwnU87OZdU3dCUjWWCVin7h8XkiZ5x1zLDiX6VWwGANLwMtwSftdbLi7XpiAULbpML1aRM/zpaF74wftz+XVtXFxfdpNR3kmbKYeuGMOv5MAxjwISOiqpctlhgc+RZRczgJ2Q5+A0RCjUQSCBiBB4B2m0j/y3GjuUwl3EXGSrnNUCuZ8GTCMdLxE8iTBLmIdM0aimD6uZtUwI8CZiZg+w0+IU40kWT6TyUzjNfdJUj0RCAmLjVDpawJQucBxnWKJTbIOfcWZEq2ecTjPJbAm2i/Qb2ZJomTnrcfOmcELRc4riNL1OlNVbgqIRpyGcrUGa9SxJhWKuJA+93hIWJGkVsBtVtFGNK0ymHYTlnUo9e7BoDRqUMIC9SXhBCWPL7ToGDiyNqNfahCDi+6gwhzF8zxblDVh1AuTNwKe8KFGMeqfht7NKpogYCMI3WHiURg+Mu5ME6HzWeiCq7SMsIvlLzXcKvhdUnmynLI3EuHBNqr0PTkVwIjE97BstA0Uqa4/EiEWtGCVtNCrcBzzmXBA9clpRpDIstBMqXvjLLbWNEuumXS8XEHr59tdVFaTkd5mcjpGEzW7Za71bIiPM9HeJ/ZQayuYVSy1diZqHcxD+zUoY5DJh8PJFXGmdeMs4JqgQ/ELa6js8ZFLyYNmO9xgcy1UWEAms4M9vQuFWSLByOTzgnvVBjIHLwyUunhtoLfyKt2ukRcJnGsYTy+4uI9BfAuf+C3EMbrX8fBpajvyab000KH5HDRtPvf0OPedrl3ddz784z1bwafceFL1tN557KZdvA5mf3AoLOuFkKd2nqNwKaNaF+2861QHOrBLGQ1nDXvyxSPAWyOXxP6qZEqrpOwUyU6iBZVJ/XSqnWldFa9KqLP+oKfiyUKZzypYFoLCC7at6Gqhiz1FYNqDM2tQ+JWZ7Ov4OIWftZtCalLr6++rkIYi6r9CWhbWxjaFmLb2JgqpFNam/c4W0qKAK5DvMBLH1Cl/rryJt1tbaq09iay4dO0YrC1VtTPMlnb/2rOWDevd8bAFw/xEPG2e/1/BPP8H \ No newline at end of file diff --git a/docs/diagrams/Name sequence diagram.png b/docs/diagrams/Name sequence diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..f2817b6f667d80c09f3a180a32e1e02cf47901ee GIT binary patch literal 18183 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/Ui component.drawio b/docs/diagrams/Ui component.drawio new file mode 100644 index 0000000000..261a6e6d9a --- /dev/null +++ b/docs/diagrams/Ui component.drawio @@ -0,0 +1 @@ +5VhNc5swEP01HJMxYPxxtJ00bcedpuPJND3KoIAagVwh29Bf35WRAAGJndQfh5zQPkkr6b2VtMJyZ3F2x9Eq+sYCTC2nF2SWe2M5ju0MR/CRSF4g/bFdACEngWpUAQvyFyuwp9A1CXBqNBSMUUFWJuizJMG+MDDEOduazZ4YNUddoRC3gIWPaBv9SQIRFejI61X4Z0zCSI9s91RNjHRjBaQRCti2Brm3ljvjjImiFGczTCV5mpei36cXasuJcZyIQzr8XtDvm+fl1/ns4YezSbf0/j68spU8G0TXasVqtiLXFHC2TgIsvfQsd7qNiMCLFfJl7RZEBywSMQXLhmJ7VmqiG8wFzmqQmuUdZjEWPIcmutZVjOUNe1sJ4GgBojr5mmqkRA9L3xUvUFDUvIGmQQdLAwrDTp8YrLNO1+DPmumKq3QXzxNoYI9WWVUJpVB+H4j2A9MqXBUVLRGOTPGoQfGwTbHdRXH/VAwPT8TwnIXEvwzJ7rhB8sBrkTzu4Ng7FccH7HWcBBN5aoLlU5SmwJ2xvYENnj/WjV/yXLj2tHmTqXOisHJtZUQ81sq1XmBVnaSh+7woQcrW3Mf7g0kgHmKxf1vjwLgD2oLWBOvaFBrjmCJBNubN0aWiGuGekV1oZ3pvmfFS2tpFsW7Vq37SNx15piO3GVEFMS1Hu6Aql/3+ONN392uBVospuaMI3LgTSsIEsCUTgsW7ECqDka0wVE0DlEa7m6gIP50vjF6Llr3q/qdsjjc2t/l7ZWs5Gp5ZNvsjyWaP+9eewXeZc75VuA5XozNL534k6Vz7aNJ1uDq3dF6HdMfIfKyZY01AsnWMObDNEglMp9r5kuuGEx+0ICKf4418vF0kH21cfaUI+/LRk+VK9qlS/oNluYPdhvll9HAGB+gxOKse45YeD19aLMDyhJmwpoKzZzxjlHFAEpZgKRShtAEhdRT6QBqw3j4jYxIEcpjOB7D5RD6GAM7+N3C/g/93vIDBrH5CFKda9SvHvf0H \ No newline at end of file diff --git a/docs/diagrams/Ui component.png b/docs/diagrams/Ui component.png new file mode 100644 index 0000000000000000000000000000000000000000..b2cc3b560f7b6e14401502b3a7a57afae4829e47 GIT binary patch literal 10992 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 Date: Thu, 29 Oct 2020 00:43:29 +0800 Subject: [PATCH 233/374] Add enter user information feature --- docs/DeveloperGuide.md | 78 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 97c8cb3685..dcbec5e82a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -14,8 +14,84 @@ The `UI` component, The UI has a dependency with two enumeration class, `ActivityLevel` and `Gender` as descriptions of each `ActivityLevel` and `Gender` is required. Increased coupling was sacrificed to reduce code duplicates and increase ease of code extension/editing. - +## Implementation {Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} + +### [Proposed] Enter user information feature + +#### Proposed Implementation +The proposed feature utilised two commands words [`name`](UserGuide.md/####Entering username: `name`) and [`info`](UserGuide.md/####Entering user information: `info`) that allows users to enter their name using the [`name`](UserGuide.md/####Entering username: `name`) command and [`info`](UserGuide.md/####Entering user information: `info`) to enter other information, such as age, gender, height, activity level, original, current and target weight, separately. + +The proposed feature to enter user information is facilitated by `Manager` which stores a`Person` which stores all user information provided. It implements the following operation: + +* Manager#setPerson(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) - Calls the method below to set the attribute values of the `Person` object. +* Person#setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) - Updates the attribute values of the `Person` object. + +Both operation are only executed by the `Manager` class of the `Logic` component. 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. + +Given below is the example usage scenario and how the feature works. + +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. + +![Enter Info Step1](/UML/Enter Info Step1.png) + +Step 2. The user executes `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. + +![Enter Info Step2](/UML/Enter Info Step2.png) + +Step 3. The user executes a command like the following `info g/M a/21 h/175 o/85 c/85 t/75 l/2` to enter all other personal information including age, gender, height, activity 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 l/2, manager)` which would parse the user command, check input validity by using methods in `InputChecker` and calls `Manager#setPerson(Jack, Gender.MALE, 21, 175, 85, 85, 75, ActivityLevel.LOW)` which proceeds to call `Person#setAll(Jack, Gender.MALE, 21, 175, 85, 85, 75, ActivityLevel.LOW)`. + +![Enter Info Step3](/UML/Enter Info Step3.png) + +The following sequence diagrams shows how the feature works. + +`name` command + +![Name sequence diagram](/UML/Name sequence diagram.png) + +`info` command + +![Info sequence diagram](/UML/Info sequence diagram.png) + +#### Design considerations: + +Aspect: Whether to enter name and other information separately or together + +* **Alternative 1 (current choice)**: Enter name and other information separately + * Pros: Increase user interaction and engagement. + * Cons: Enter information using two commands. + +* **Alternative 2**: Enter name and other information together + * Pros: Enter all information at once. + * Cons: Increase user interaction and engagement. + +Aspect: Whether to use singleton pattern for Person class + +* **Alternative 1 (current choice)**: Did not use singleton pattern for `Person` + * Pros: Reduce coupling and increase testability. + * Cons: Risk of creating multiple `Person` object by mistake and there might be negative consequence in + creating multiple objects. + + However, there is minimal risk of creating multiple `Person` object by mistake and minimal negative + consequence in creating multiple objects as long as the `Manager` refers the correct instance of + `Person`. + +* **Alternative 2**: Use singleton pattern for `Person` + * Pros: Easy to implement, prevent the instantiation of more than one `Person` object. + * Cons: Increase coupling and reduce testability + +Aspect: Changing attribute values in `Person` object or creating new `Person` object + +* **Alternative 1 (current choice)**: Changing attribute values in `Person` object + * Pros: Reduce the number of objects being created to reduce memory usage and reduce the risk of creating + multiple objects which can potentially lead to negative consequences and bugs. + * Cons: Unable to write tests as method chains. + +* **Alternative 2**: Creating new `Person` object + * Pros: Ability to write tests as method chains. + * Cons: Creation of many objects, which take up memory spaces and ensure that only the correct `Person + ` instance is kept and referred to. + ## Save/Load Feature The Save/Load feature is implemented by the saveload package. From 5fd07febd20abce67eae4a218c3e72e4e9f0a508 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:44:50 +0800 Subject: [PATCH 234/374] Update target user --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index dcbec5e82a..89972680d1 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -151,7 +151,7 @@ similiar diaghrams for PersonSaveLoadManager ## Product scope ### Target user profile -{Describe the target user profile} +NUS students living on campus who would like to track their daily food and nutritional intake. ### Value proposition From ab9aa11b6c19a5a945776c343248830a1a021a4d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:45:04 +0800 Subject: [PATCH 235/374] Update non functional requirements --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 89972680d1..b30a366acf 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -166,7 +166,7 @@ NUS students living on campus who would like to track their daily food and nutri ## Non-Functional Requirements -{Give non-functional requirements} +1. Should work on any mainstream OS as long as `Java 11` is installed in the system. ## Glossary From 465d6bb0d1bbc6185d4efc5760b0539f28002782 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:45:14 +0800 Subject: [PATCH 236/374] Update glossary --- docs/DeveloperGuide.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index b30a366acf..3ba53963a1 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -170,8 +170,7 @@ NUS students living on campus who would like to track their daily food and nutri ## Glossary -* *glossary item* - Definition +* *Mainstream OD* - Windows, Linux, Unix, OS-X +* *Food items* - Includes both food and drinks ## Instructions for manual testing - -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} From a52a92211d996b3c758a9b7d192b488d0d04017e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:24:46 +0800 Subject: [PATCH 237/374] Change minor formatting (cherry picked from commit a809fbe8f057d50b6bd11c85471d865641a51a2d) --- docs/UserGuide.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index f5988acfa2..b7194bcf52 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -11,18 +11,19 @@ DietBook is a desktop application targeting NUS students living on campus, optim 1. Download the latest version of `dietbook.jar` from [here](https://github.com/AY2021S1-CS2113-T14-4/tp/releases). 1. Copy the file to the folder you want to use as the home folder for your DietBook. 1. Either double-click the jar file to start the application or navigate to the folder containing the jar file on command prompt and run the command `java -jar dietbook.jar`. -1. For first time users: - 1. A CLI, similar to the one shown below, should appear within a few seconds. Follow the instructions provided to setup DietBook or refer to [name](#Entering username: `name`) and [info](#Entering user information: `info`) for more detailed explanations.
- ![DietBook Welcome Message](/images/DietBookWelcomeMessage.PNG) +1. For first time users:
+A CLI, similar to the one shown below, should appear within a few seconds. Follow the instructions + provided to setup DietBook or refer to [name](####Entering username: `name`) and [info](####Entering user information: `info`) for more detailed explanations.
+ [DietBook Welcome Message](/images/DietBookWelcomeMessage.PNG) 1. Start using DietBook by typing any valid command and pressing Enter to execute it. -1. Refer to the [Features](#Features) section below for more details of each command. +1. Refer to the [Features](##Features) section below for more details of each command. ## Features Notes about the command format: * Words in `UPPER_CASE` are parameters to be supplied by the user.
-e.g. For `delete INDEX`, `delete 1`would be a valid command. +e.g. For `delete INDEX`, `delete 1` would be a valid command. * Parameters in square brackets are optional. However, if all parameters are optional, at least one parameter needs to be given. In such cases, any one of the parameters would be valid.
e.g. For `add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT]`, `add x/1 n/Toast k/120`, `add x/1 n/Toast k/120 c/18`, `add x/1 n/Toast k/120 c/18 p/3`, `add x/1 n/Toast k/120 @@ -36,7 +37,7 @@ e.g. `help` is a valid command but `Help` is not.
e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add N/mee x/1` is not. * A single spacing to separate command words, parameters, command word and parameters is required.
-e.g. For `calculate all`, `calculate all` is valid but `calculateall` and `calculate`         `all`is not.
+e.g. For `calculate all`, `calculate all` is valid but `calculateall` and `calculate`         `all` is not.
e.g. For `delete INDEX`, `delete 1` is valid if there is a food item with index 1 but`delete1` is not.
e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add n/meex/1` is not.
From fbabae001af5a0457d0a6f6477ab0596acfb9b1d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:50:14 +0800 Subject: [PATCH 238/374] Improve markdown code quality --- docs/DeveloperGuide.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 3ba53963a1..f687c6a833 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -69,12 +69,9 @@ Aspect: Whether to use singleton pattern for Person class * **Alternative 1 (current choice)**: Did not use singleton pattern for `Person` * Pros: Reduce coupling and increase testability. - * Cons: Risk of creating multiple `Person` object by mistake and there might be negative consequence in - creating multiple objects. + * Cons: Risk of creating multiple `Person` object by mistake and there might be negative consequence in creating multiple objects. - However, there is minimal risk of creating multiple `Person` object by mistake and minimal negative - consequence in creating multiple objects as long as the `Manager` refers the correct instance of - `Person`. + However, there is minimal risk of creating multiple `Person` object by mistake and minimal negative consequence in creating multiple objects as long as the `Manager` refers the correct instance of `Person`. * **Alternative 2**: Use singleton pattern for `Person` * Pros: Easy to implement, prevent the instantiation of more than one `Person` object. @@ -83,14 +80,12 @@ Aspect: Whether to use singleton pattern for Person class Aspect: Changing attribute values in `Person` object or creating new `Person` object * **Alternative 1 (current choice)**: Changing attribute values in `Person` object - * Pros: Reduce the number of objects being created to reduce memory usage and reduce the risk of creating - multiple objects which can potentially lead to negative consequences and bugs. + * Pros: Reduce the number of objects being created to reduce memory usage and reduce the risk of creating multiple objects which can potentially lead to negative consequences and bugs. * Cons: Unable to write tests as method chains. * **Alternative 2**: Creating new `Person` object * Pros: Ability to write tests as method chains. - * Cons: Creation of many objects, which take up memory spaces and ensure that only the correct `Person - ` instance is kept and referred to. + * Cons: Creation of many objects, which take up memory spaces and ensure that only the correct `Person` instance is kept and referred to. ## Save/Load Feature From bc695d9e1f698bbc4b47ae8f9e62b56df09046bb Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 00:52:24 +0800 Subject: [PATCH 239/374] Add table of contents --- docs/DeveloperGuide.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index f687c6a833..ae85bf8f04 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,5 +1,8 @@ # Developer Guide +* Table of Contents +{:toc} + ## Design ### UI component From c1bd39becfefd116a0122c04fce9b154efec7934 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 01:41:31 +0800 Subject: [PATCH 240/374] Update diagrams and links --- docs/DeveloperGuide.md | 18 +++++++++--------- docs/UserGuide.md | 5 ++--- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index ae85bf8f04..61bbe1b534 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -6,7 +6,7 @@ ## Design ### UI component -![Ui component](/UML/Ui component.png) +![Ui component](diagrams/Ui component.png) **API**: [`Ui.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/Ui.java) @@ -14,8 +14,7 @@ The `UI` component, * Takes in user command and passes to the `Logic` components for command execution. * Updates the user about any changes in the data after executing the command or errors encountered when executing the commands. -The UI has a dependency with two enumeration class, `ActivityLevel` and `Gender` as descriptions of each - `ActivityLevel` and `Gender` is required. Increased coupling was sacrificed to reduce code duplicates and increase ease of code extension/editing. +The UI has a dependency with two enumeration class, [`ActivityLevel`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/ActivityLevel.java) and [`Gender`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Gender.java) as descriptions of each [`ActivityLevel`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/ActivityLevel.java) and [`Gender`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Gender.java) is required. Increased coupling was sacrificed to reduce code duplicates and increase ease of code extension/editing. ## Implementation {Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} @@ -25,7 +24,8 @@ The UI has a dependency with two enumeration class, `ActivityLevel` and `Gender` #### Proposed Implementation The proposed feature utilised two commands words [`name`](UserGuide.md/####Entering username: `name`) and [`info`](UserGuide.md/####Entering user information: `info`) that allows users to enter their name using the [`name`](UserGuide.md/####Entering username: `name`) command and [`info`](UserGuide.md/####Entering user information: `info`) to enter other information, such as age, gender, height, activity level, original, current and target weight, separately. -The proposed feature to enter user information is facilitated by `Manager` which stores a`Person` which stores all user information provided. It implements the following operation: +The proposed feature to enter user information is facilitated by `Manager` which stores a [`Person`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java) which + stores all user information provided. It implements the following operation: * Manager#setPerson(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) - Calls the method below to set the attribute values of the `Person` object. * Person#setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) - Updates the attribute values of the `Person` object. @@ -36,25 +36,25 @@ Given below is the example usage scenario and how the feature works. 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. -![Enter Info Step1](/UML/Enter Info Step1.png) +![Enter Info Step1](diagrams/Enter Info Step1.png) Step 2. The user executes `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. -![Enter Info Step2](/UML/Enter Info Step2.png) +![Enter Info Step2](diagrams/Enter Info Step2.png) Step 3. The user executes a command like the following `info g/M a/21 h/175 o/85 c/85 t/75 l/2` to enter all other personal information including age, gender, height, activity 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 l/2, manager)` which would parse the user command, check input validity by using methods in `InputChecker` and calls `Manager#setPerson(Jack, Gender.MALE, 21, 175, 85, 85, 75, ActivityLevel.LOW)` which proceeds to call `Person#setAll(Jack, Gender.MALE, 21, 175, 85, 85, 75, ActivityLevel.LOW)`. -![Enter Info Step3](/UML/Enter Info Step3.png) +![Enter Info Step3](diagrams/Enter Info Step3.png) The following sequence diagrams shows how the feature works. `name` command -![Name sequence diagram](/UML/Name sequence diagram.png) +![Name sequence diagram](diagrams/Name sequence diagram.png) `info` command -![Info sequence diagram](/UML/Info sequence diagram.png) +![Info sequence diagram](diagrams/Info sequence diagram.png) #### Design considerations: diff --git a/docs/UserGuide.md b/docs/UserGuide.md index b7194bcf52..87ff008b0c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -12,9 +12,8 @@ DietBook is a desktop application targeting NUS students living on campus, optim 1. Copy the file to the folder you want to use as the home folder for your DietBook. 1. Either double-click the jar file to start the application or navigate to the folder containing the jar file on command prompt and run the command `java -jar dietbook.jar`. 1. For first time users:
-A CLI, similar to the one shown below, should appear within a few seconds. Follow the instructions - provided to setup DietBook or refer to [name](####Entering username: `name`) and [info](####Entering user information: `info`) for more detailed explanations.
- [DietBook Welcome Message](/images/DietBookWelcomeMessage.PNG) +A CLI, similar to the one shown below, should appear within a few seconds. Follow the instructions provided to setup DietBook or refer to [name](####Entering username: `name`) and [info](####Entering user information: `info`) for more detailed explanations.
+![DietBook Welcome Message](images/DietBookWelcomeMessage.PNG) 1. Start using DietBook by typing any valid command and pressing Enter to execute it. 1. Refer to the [Features](##Features) section below for more details of each command. From 0b8c6c07c0fa07dfd1ae9fd5f34b82fa4910f6c8 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 02:03:50 +0800 Subject: [PATCH 241/374] Update file names --- ...nterInfoStep1.drawio => Enter Info Step1.drawio} | 0 .../{EnterInfoStep1.png => Enter Info Step1.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/diagrams/{EnterInfoStep1.drawio => Enter Info Step1.drawio} (100%) rename docs/diagrams/{EnterInfoStep1.png => Enter Info Step1.png} (100%) diff --git a/docs/diagrams/EnterInfoStep1.drawio b/docs/diagrams/Enter Info Step1.drawio similarity index 100% rename from docs/diagrams/EnterInfoStep1.drawio rename to docs/diagrams/Enter Info Step1.drawio diff --git a/docs/diagrams/EnterInfoStep1.png b/docs/diagrams/Enter Info Step1.png similarity index 100% rename from docs/diagrams/EnterInfoStep1.png rename to docs/diagrams/Enter Info Step1.png From d0ce85399532bb0d6361bff84ef57ba06610c52e Mon Sep 17 00:00:00 2001 From: HengFuYuen <60005925+HengFuYuen@users.noreply.github.com> Date: Thu, 29 Oct 2020 02:14:10 +0800 Subject: [PATCH 242/374] Set theme jekyll-theme-architect --- docs/_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_config.yml b/docs/_config.yml index 2f7efbeab5..3397c9a492 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-minimal \ No newline at end of file +theme: jekyll-theme-architect \ No newline at end of file From 8024fdbc42d83bd6fb562e064a84c3d62ea2551b Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Thu, 29 Oct 2020 02:19:04 +0800 Subject: [PATCH 243/374] Remove static referencing Calculator and FoodList no longer reference each other via static method/variables. Instance of FoodList passed to calculator as a parameter for now. --- .../seedu/dietbook/calculator/Calculator.java | 48 +++++++++---------- .../java/seedu/dietbook/list/FoodList.java | 6 +-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index e8304fe278..266ed0781a 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -54,10 +54,10 @@ public int calculateCalorie() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCalorie(LocalDateTime startTime) { + public int calculateCalorie(FoodList foodList, LocalDateTime startTime) { int calorie = 0; - for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { - calorie += FoodList.getFoodsAfterDateTime(startTime).get(i).getCalorie(); + for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { + calorie += foodList.getFoodsAfterDateTime(startTime).get(i).getCalorie(); } return calorie; } @@ -72,10 +72,10 @@ public int calculateCalorie(LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCalorie(LocalDateTime startTime, LocalDateTime endTime) { + public int calculateCalorie(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { int calorie = 0; - for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - calorie += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCalorie(); + for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + calorie += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCalorie(); } return calorie; } @@ -98,10 +98,10 @@ public int calculateCarb() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCarb(LocalDateTime startTime) { + public int calculateCarb(FoodList foodList, LocalDateTime startTime) { int carb = 0; - for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { - carb += FoodList.getFoodsAfterDateTime(startTime).get(i).getCarbohydrate(); + for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { + carb += foodList.getFoodsAfterDateTime(startTime).get(i).getCarbohydrate(); } return carb; } @@ -116,10 +116,10 @@ public int calculateCarb(LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCarb(LocalDateTime startTime, LocalDateTime endTime) { + public int calculateCarb(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { int carb = 0; - for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - carb += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCarbohydrate(); + for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + carb += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCarbohydrate(); } return carb; } @@ -142,10 +142,10 @@ public int calculateProtein() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateProtein(LocalDateTime startTime) { + public int calculateProtein(FoodList foodList, LocalDateTime startTime) { int protein = 0; - for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { - protein += FoodList.getFoodsAfterDateTime(startTime).get(i).getProtein(); + for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { + protein += foodList.getFoodsAfterDateTime(startTime).get(i).getProtein(); } return protein; } @@ -160,10 +160,10 @@ public int calculateProtein(LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateProtein(LocalDateTime startTime, LocalDateTime endTime) { + public int calculateProtein(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { int protein = 0; - for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - protein += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getProtein(); + for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + protein += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getProtein(); } return protein; } @@ -186,10 +186,10 @@ public int calculateFat() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateFat(LocalDateTime startTime) { + public int calculateFat(FoodList foodList, LocalDateTime startTime) { int fat = 0; - for (int i = 0; i < FoodList.getFoodsAfterDateTime(startTime).size(); i++) { - fat += FoodList.getFoodsAfterDateTime(startTime).get(i).getFat(); + for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { + fat += foodList.getFoodsAfterDateTime(startTime).get(i).getFat(); } return fat; } @@ -204,10 +204,10 @@ public int calculateFat(LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { + public int calculateFat(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { int fat = 0; - for (int i = 0; i < FoodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - fat += FoodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFat(); + for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { + fat += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFat(); } return fat; } diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index 59a5dbabfb..fae627557c 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -13,7 +13,7 @@ * This is a stateful object. */ public class FoodList { - private static ArrayList foodEntries; + private ArrayList foodEntries; /** * Default constructor that instantiates FoodList with an empty foodentry arraylist. @@ -121,7 +121,7 @@ public List getPortionedFoods() { /** * Obtain list of foods consumed after specified timing. */ - public static List getFoodsAfterDateTime(LocalDateTime dateTime) { + public List getFoodsAfterDateTime(LocalDateTime dateTime) { List entriesAfterDateTime = FoodListManager.filterListByDate(foodEntries, dateTime); return FoodListManager.listToFoods(entriesAfterDateTime); } @@ -137,7 +137,7 @@ public List getPortionedFoodsAfterDateTime(LocalDateTime dateTime) { /** * Obtain list of foods consumed within the range of a specified timing. */ - public static List getFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { + public List getFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); return FoodListManager.listToFoods(entriesInRange); } From 1a58bc13e805b142b05284de13ff0ce51ffadc6f Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Thu, 29 Oct 2020 02:19:55 +0800 Subject: [PATCH 244/374] Delete Food.java in Test Redundant old file mistakenly added. Likely due to merge conflict resolution. --- src/test/java/seedu/dietbook/food/Food.java | 44 --------------------- 1 file changed, 44 deletions(-) delete mode 100644 src/test/java/seedu/dietbook/food/Food.java diff --git a/src/test/java/seedu/dietbook/food/Food.java b/src/test/java/seedu/dietbook/food/Food.java deleted file mode 100644 index 505588e82b..0000000000 --- a/src/test/java/seedu/dietbook/food/Food.java +++ /dev/null @@ -1,44 +0,0 @@ -package seedu.dietbook.food; - - -public class Food { - private final String name; - private final int calorie; - private final int carbohydrate; - private final int protein; - private final int fats; - - public Food(String name, int calorie, int carbohydrate, int protein, int fats) { - this.name = name; - this.calorie = calorie; - this.carbohydrate = carbohydrate; - this.protein = protein; - this.fats = fats; - } - - public int getFat() { - return fats; - } - - public String getName() { - return name; - } - - public int getCalorie() { - return calorie; - } - - public int getCarbohydrate() { - return carbohydrate; - } - - public int getProtein() { - return protein; - } - - @Override - public String toString() { - return name + " | calorie : " + calorie + " | protein : " + protein + " | carbohydrate : " + carbohydrate - + " | fats : " + fats; - } -} From 99f7e94d21526cb6654f50d8f1ce354f33f999d4 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 02:22:57 +0800 Subject: [PATCH 245/374] Add dataSuccessfullySavedMessage method --- src/main/java/seedu/dietbook/Ui.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 89f861bbb1..b124ad381e 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -112,6 +112,13 @@ public void printInitialisationCompleteMessage() { print("Thank you! DietBook has been initialised " + getStartMessage()); } + /** + * Prints a message informing the user that DietBook has successfully saved all their data. + */ + public void dataSuccessfullySavedMessage() { + print("Your data has been saved successfully."); + } + /** * Prints the welcome back message when user reboots up DietBook after the first initialisation. From 61c0f212ae0cbf0bbdde7ec9074062f30411719c Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Thu, 29 Oct 2020 02:35:29 +0800 Subject: [PATCH 246/374] add toString version of datetime support methods -Tweak test method execution order for robustness of `LocalDateTime.now()` - add methods to fetch portion of list in string representation using dates. --- .../java/seedu/dietbook/list/FoodList.java | 22 +++++++++++++++++++ .../seedu/dietbook/list/FoodListTest.java | 15 +++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index fae627557c..3807882b12 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -158,6 +158,7 @@ public List getPortionSizes() { return FoodListManager.listToPortionSizes(foodEntries); } + /** * Obtain list of LocalDateTimes for when the entries were made. * (For storage purposes) @@ -171,4 +172,25 @@ public String toString() { return FoodListManager.listToString(foodEntries); } + /** + * Returns toString representation of the segmented list based on DateTime. + * @param dateTime Start DateTime. + * @return string representation of FoodList + */ + public String getAfterDateTimeToString(LocalDateTime dateTime) { + List entriesAfterDateTime = FoodListManager.filterListByDate(foodEntries, dateTime); + return FoodListManager.listToString(entriesAfterDateTime); + } + + /** + * Returns toString representation of the segmented list based on DateTime (within a range of 2 datetimes). + * @param start start DateTime. + * @param end end DateTime. + * @return string representation of FoodList + */ + public String getInDateTimeRangeToString(LocalDateTime start, LocalDateTime end) { + List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); + return FoodListManager.listToString(entriesInRange); + } + } diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 3b7325f856..a4072423f9 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -61,11 +61,14 @@ void dateComparisonTest() { @Test void dateFilterAfterTest() { + assertEquals(list.toString(), list.getAfterDateTimeToString(LocalDateTime.MIN)); + LocalDateTime timeNow = LocalDateTime.now(); assertTrue(list.getFoodsAfterDateTime(timeNow).size() == 0); - assertEquals(list.getFoodsAfterDateTime(LocalDateTime.MIN).toString(), - list.getFoods().toString()); + assertEquals(list.getFoods().toString(), + list.getFoodsAfterDateTime(LocalDateTime.MIN).toString()); + // add new entries: if (! LocalDateTime.now().isAfter(timeNow)) { // Execution is too fast that now() = timeNow. @@ -77,11 +80,17 @@ void dateFilterAfterTest() { } list.addFood(1, food); assertEquals(food.toString(), list.getFoodsAfterDateTime(timeNow).get(0).toString()); + } @Test void dateFilterRangeTest() { + + assertEquals(list.getPortionedFoods().toString(), + list.getPortionedFoodsInDateTimeRange(LocalDateTime.MIN, LocalDateTime.MAX).toString()); + assertEquals(list.toString(), list.getInDateTimeRangeToString(LocalDateTime.MIN, LocalDateTime.MAX)); + LocalDateTime timeNow = LocalDateTime.now(); if (! LocalDateTime.now().isAfter(timeNow)) { // Execution is too fast that now() = timeNow. @@ -95,8 +104,6 @@ void dateFilterRangeTest() { assertTrue(list.getFoodsInDateTimeRange(timeNow, LocalDateTime.MAX).size() == 0); - assertEquals(list.getPortionedFoods().toString(), - list.getPortionedFoodsInDateTimeRange(LocalDateTime.MIN, LocalDateTime.MAX).toString()); } @Test From 3ab299d47b89afb1dbef317efa1629bbc11fc254 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 29 Oct 2020 02:44:33 +0800 Subject: [PATCH 247/374] Feature Updates -command EditInfo -command recommend -add date time support for calculate --- src/main/java/seedu/dietbook/Manager.java | 2 +- .../seedu/dietbook/checker/InputChecker.java | 25 ++++ .../dietbook/command/CalculateCommand.java | 112 ++++++++++++++---- .../seedu/dietbook/command/ExitCommand.java | 46 +++++++ .../seedu/dietbook/command/ListCommand.java | 30 ++++- .../dietbook/command/UserinfoCommand.java | 1 - .../java/seedu/dietbook/parser/Parser.java | 2 + .../saveload/PersonSaveLoadManager.java | 2 +- 8 files changed, 196 insertions(+), 24 deletions(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index da5f9f73e8..94f77807f2 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -125,7 +125,7 @@ public Command manage(String userInput) throws DietException { case COMMAND_INFO: return new InfoCommand(userInput); case COMMAND_LIST: - return new ListCommand(); + return new ListCommand(userInput); case COMMAND_NAME: return new NameCommand(Parser.getCommandParam(userInput)); case COMMAND_RECOMMEND: diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 718b7a5f80..43a0d8069a 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -1,6 +1,7 @@ package seedu.dietbook.checker; import seedu.dietbook.exception.DietException; +import seedu.dietbook.parser.Parser; /** * InputChecker class of the program. @@ -90,6 +91,18 @@ public static void checkAddParam(String userInput) throws DietException { } } + /** + * Takes in user input to check if the expected number of parameter is present for the calculate command. + * + * @param param parameter part of user input. + * @throws DietException when number of parameter is not as expected. + */ + public static void checkCalculateParam(String[] param) throws DietException { + if (param.length > 3) { + throw new DietException("Incorrect calculate statement"); + } + } + /** * Takes in user input to check if the expected number and type of parameter for the info command is present. * @@ -104,6 +117,18 @@ public static void checkInfoParam(String userInput) throws DietException { } } + /** + * Takes in user input to check if the expected number of parameter is present for the list command. + * + * @param param parameter part of user input. + * @throws DietException when number of parameter is not as expected. + */ + public static void checkList(String[] param) throws DietException { + if (param.length > 3) { + throw new DietException("Incorrect list statement"); + } + } + /** * Takes in an integer from food to check if the value is within the logical limit. * diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index 890aaa643f..feb2b44aaa 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -2,8 +2,12 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.calculator.Calculator; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; +import java.time.LocalDateTime; + public class CalculateCommand extends Command { int calorie; int carb; @@ -21,32 +25,100 @@ public CalculateCommand(int calorie, int carb, int protein, int fat, String para @Override public void execute(Manager manager, Ui ui) throws DietException { + LocalDateTime startTime; + LocalDateTime endTime; + int calorie; + int carb; + int protein; + int fat; if (commandCount == 1) { throw new DietException("Please enter your name first!"); } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } manager.setCalculator(); - switch (this.param) { - case "all": - //ui.printAllIntakeAndFoods(); - //ui.printAllNutrientIntake(this.calorie, this.carb, this.protein, this.fat); - break; - case "calorie": - //ui.printCalorieIntakeAndFoods(); - //ui.printCalorieIntake(this.calorie); - break; - case "carbohydrate": - //ui.printCarbIntakeAndFoods(); - //ui.printCarbohydrateIntake(this.carb); - break; - case "protein": - //ui.printProteinIntakeAndFoods(); - //ui.printProteinIntake(this.protein); - break; - default: - //ui.printFatIntakeAndFoods(); - //ui.printFatIntake(this.fat); + String[] processedParam = this.param.split("\\s+"); + InputChecker.checkCalculateParam(processedParam); + try { + switch (processedParam[0]) { + case "all": + if (processedParam.length == 1) { + ui.printAllIntake(this.calorie, this.carb, this.protein, this.fat); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + calorie = manager.getCalculator().calculateCalorie(startTime); + carb = manager.getCalculator().calculateCarb(startTime); + protein = manager.getCalculator().calculateProtein(startTime); + fat = manager.getCalculator().calculateFat(startTime); + ui.printAllIntake(calorie, carb, protein, fat, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + endTime = LocalDateTime.parse(processedParam[2]); + calorie = manager.getCalculator().calculateCalorie(startTime, endTime); + carb = manager.getCalculator().calculateCarb(startTime, endTime); + protein = manager.getCalculator().calculateProtein(startTime, endTime); + fat = manager.getCalculator().calculateFat(startTime, endTime); + ui.printAllIntake(calorie, carb, protein, fat, startTime, endTime); + } + break; + case "calorie": + if (processedParam.length == 1) { + ui.printCalorieIntake(this.calorie); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + calorie = manager.getCalculator().calculateCalorie(startTime); + ui.printCalorieIntake(calorie, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + endTime = LocalDateTime.parse(processedParam[2]); + calorie = manager.getCalculator().calculateCalorie(startTime, endTime); + ui.printCalorieIntake(calorie, startTime, endTime); + } + break; + case "carbohydrate": + if (processedParam.length == 1) { + ui.printCarbIntake(this.carb); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + carb = manager.getCalculator().calculateCarb(startTime); + ui.printCarbIntake(carb, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + endTime = LocalDateTime.parse(processedParam[2]); + carb = manager.getCalculator().calculateCarb(startTime, endTime); + ui.printCarbIntake(carb, startTime, endTime); + } + break; + case "protein": + if (processedParam.length == 1) { + ui.printProteinIntake(this.protein); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + protein = manager.getCalculator().calculateProtein(startTime); + ui.printProteinIntake(protein, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + endTime = LocalDateTime.parse(processedParam[2]); + protein = manager.getCalculator().calculateProtein(startTime, endTime); + ui.printProteinIntake(protein, startTime, endTime); + } + break; + default: + if (processedParam.length == 1) { + ui.printFatIntake(this.fat); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + fat = manager.getCalculator().calculateFat(startTime); + ui.printFatIntake(fat, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + endTime = LocalDateTime.parse(processedParam[2]); + fat = manager.getCalculator().calculateFat(startTime, endTime); + ui.printFatIntake(fat, startTime, endTime); + } + } + } catch (Exception e) { + throw new DietException("Wrong date time format! (Format: yyyy-mm-ddTHH:mm)"); } } } diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java index 31b14601e7..c1d4eddb4a 100644 --- a/src/main/java/seedu/dietbook/command/ExitCommand.java +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -3,11 +3,57 @@ import seedu.dietbook.DietBook; import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.Gender; +import seedu.dietbook.saveload.PersonSaveLoadManager; +import seedu.dietbook.saveload.FoodSaveLoadManager; public class ExitCommand extends Command { + PersonSaveLoadManager personSave; + FoodSaveLoadManager foodSave; + + public ExitCommand() { + this.personSave = new PersonSaveLoadManager(); + this.foodSave = new FoodSaveLoadManager(); + } + @Override public void execute(Manager manager, Ui ui) { ui.printExitMessage(); + ActivityLevel actLvl = manager.getPerson().getActivityLevel(); + int actLvlInt = 1; + if (actLvl.equals(ActivityLevel.NONE)) { + actLvlInt = 1; + } else if (actLvl.equals(ActivityLevel.LOW)) { + actLvlInt = 2; + } else if (actLvl.equals(ActivityLevel.MEDIUM)) { + actLvlInt = 3; + } else if (actLvl.equals(ActivityLevel.HIGH)) { + actLvlInt = 4; + } else if (actLvl.equals(ActivityLevel.EXTREME)) { + actLvlInt = 5; + } + + Gender gender = manager.getPerson().getGender(); + String genderString = "Male"; + if (gender.equals(Gender.MALE)) { + genderString = "Male"; + } else if (gender.equals(Gender.FEMALE)) { + genderString = "Female"; + } else { + genderString = "Others"; + } + + personSave.setActivityLevel(actLvlInt); + personSave.setAge(manager.getPerson().getAge()); + personSave.setCurrentWeight(manager.getPerson().getCurrentWeight()); + personSave.setGender(genderString); + personSave.setHeight(manager.getPerson().getHeight()); + personSave.setName(manager.getPerson().getName()); + personSave.setOriginalWeight(manager.getPerson().getOriginalWeight()); + personSave.setTargetWeight(manager.getPerson().getTargetWeight()); + personSave.save("resources/UserInfo.txt"); + foodSave.save("resources/FoodList.txt", manager.getFoodList().getFoods()); DietBook.isExit = true; } } diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java index d3e7e44541..6f11e58c90 100644 --- a/src/main/java/seedu/dietbook/command/ListCommand.java +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -2,16 +2,44 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; +import java.time.LocalDateTime; + public class ListCommand extends Command { + String userInput; + + public ListCommand(String userInput) { + this.userInput = userInput; + } + @Override public void execute(Manager manager, Ui ui) throws DietException { + LocalDateTime startTime; + LocalDateTime endTime; if (commandCount == 1) { throw new DietException("Please enter your name first!"); } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } - ui.printFoodList(manager.getFoodList().toString()); + String[] processedInput = this.userInput.split("\\s+"); + InputChecker.checkList(processedInput); + if (processedInput.length == 1) { + ui.printFoodList(manager.getFoodList().toString()); + } else { + try { + if (processedInput.length == 2) { + startTime = LocalDateTime.parse(processedInput[1]); + ui.printFoodList(manager.getFoodList().toString(), startTime); + } else if (processedInput.length == 3) { + startTime = LocalDateTime.parse(processedInput[1]); + endTime = LocalDateTime.parse(processedInput[2]); + ui.printFoodList(manager.getFoodList().toString(), startTime, endTime); + } + } catch (Exception e) { + throw new DietException("Wrong date time format! (Format: yyyy-mm-ddTHH:mm)"); + } + } } } diff --git a/src/main/java/seedu/dietbook/command/UserinfoCommand.java b/src/main/java/seedu/dietbook/command/UserinfoCommand.java index fe2bb1f786..f7892f1d02 100644 --- a/src/main/java/seedu/dietbook/command/UserinfoCommand.java +++ b/src/main/java/seedu/dietbook/command/UserinfoCommand.java @@ -4,7 +4,6 @@ import seedu.dietbook.Ui; import seedu.dietbook.exception.DietException; -import java.io.DataInput; public class UserinfoCommand extends Command { @Override diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index c52dc0c0d3..1c3fb02f38 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -7,6 +7,8 @@ import seedu.dietbook.Manager; import seedu.dietbook.checker.InputChecker; +import java.time.LocalDateTime; + /** * Parser class of the program. * The parser class takes in user input and process it into command data that manager can use. diff --git a/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java index d0650997f8..996dddfe7b 100644 --- a/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java @@ -101,7 +101,7 @@ public void save(String fileName) { this.saver.add(Integer.toString(this.originalWeight), ORIGINAL_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); this.saver.add(Integer.toString(this.currentWeight), CURRENT_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); this.saver.add(Integer.toString(this.targetWeight), TARGET_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); - this.saver.add(Integer.toString(this.currentWeight), ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.activityLevel), ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW); this.saver.save(PERSON_FOLDER_NAME, fileName); } From ad5424ddde06dca5506c8f489e153a39eff2327c Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 29 Oct 2020 07:25:48 +0800 Subject: [PATCH 248/374] Database Printing --- src/main/java/seedu/dietbook/DietBook.java | 72 ++++++++++++++++++- src/main/java/seedu/dietbook/Manager.java | 6 ++ .../dietbook/command/CalculateCommand.java | 32 ++++----- .../seedu/dietbook/command/DataCommand.java | 2 +- .../seedu/dietbook/command/ExitCommand.java | 9 +-- .../seedu/dietbook/command/ListCommand.java | 6 +- .../java/seedu/dietbook/parser/Parser.java | 8 +++ 7 files changed, 110 insertions(+), 25 deletions(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 6d78746e80..6a512a954d 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -4,6 +4,10 @@ import seedu.dietbook.list.FoodList; import seedu.dietbook.exception.DietException; import seedu.dietbook.command.Command; +import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.Gender; +import seedu.dietbook.saveload.FoodSaveLoadManager; +import seedu.dietbook.saveload.PersonSaveLoadManager; /** * Main class of the program. @@ -12,6 +16,7 @@ * @author tikimonarch */ +import java.io.File; import java.io.FileNotFoundException; public class DietBook { @@ -19,6 +24,8 @@ public class DietBook { private Ui ui; private Manager manager; private DataBase dataBase; + private PersonSaveLoadManager loadPerson; + private FoodSaveLoadManager loadFood; public static boolean isExit = false; /** @@ -26,17 +33,78 @@ public class DietBook { */ public DietBook() { ui = new Ui(); + loadFood = new FoodSaveLoadManager(); + loadPerson = new PersonSaveLoadManager(); foodList = new FoodList(); dataBase = new DataBase(); manager = new Manager(foodList, dataBase); - } + } + + /** + * Method to load Person data. + */ + public void loadPerson() { + try { + loadPerson.load("resources/UserInfo.txt"); + ActivityLevel actLvl = ActivityLevel.NONE; + int actLvlInt = loadPerson.getActivityLevel(); + if (actLvlInt == 1) { + actLvl = ActivityLevel.NONE; + } else if (actLvlInt == 2) { + actLvl = ActivityLevel.LOW; + } else if (actLvlInt == 3) { + actLvl = ActivityLevel.MEDIUM; + } else if (actLvlInt == 4) { + actLvl = ActivityLevel.HIGH; + } else if (actLvlInt == 5) { + actLvl = ActivityLevel.EXTREME; + } + + Gender gender; + String genderString = loadPerson.getGender(); + if (genderString.equals("Male")) { + gender = Gender.MALE; + } else if (genderString.equals("Female")) { + gender = Gender.FEMALE; + } else { + gender = Gender.OTHERS; + } + manager.getPerson().setAll(loadPerson.getName(), gender, loadPerson.getAge(), + loadPerson.getHeight(), loadPerson.getOriginalWeight(), loadPerson.getCurrentWeight(), + loadPerson.getTargetWeight(), actLvl); + + } catch (FileNotFoundException e) { + ui.printErrorMessage("Person data file not found!"); + } catch (IllegalAccessException e) { + ui.printErrorMessage(e.getMessage()); + } + } + + /** + * Method to load DietBook FoodList data. + */ + public void loadFood() { + try { + loadFood.load("resources/UserInfo.txt"); + loadFood.getFoodList(); + //foodList = new FoodList(loadFood.getFoodList()); + ui.printWelcomeBackMessage(manager.getPerson().getName()); + } catch (FileNotFoundException e) { + ui.printErrorMessage("FoodList data file not found!"); + } catch (IllegalAccessException e) { + ui.printErrorMessage(e.getMessage()); + } + + } /** * Main method to run the program. */ - public static void main(String[] args) throws FileNotFoundException { + public static void main(String[] args) { DietBook dietBook = new DietBook(); dietBook.ui.printWelcomeMessage(); + //dietBook.loadPerson(); + //dietBook.loadFood(); while (!isExit) { try { diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 94f77807f2..dacedc03f1 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -23,6 +23,8 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; +import java.util.ArrayList; + /** * Manager class of the program. * The manager class takes in the checked and processed input and carry out the command specified. @@ -66,6 +68,10 @@ public FoodList getFoodList() { return this.foodList; } + public void setFoodList(FoodList foodList) { + this.foodList = foodList; + } + public Person getPerson() { return this.person; } diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index feb2b44aaa..5b767ad184 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -46,18 +46,18 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printAllIntake(this.calorie, this.carb, this.protein, this.fat); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); - calorie = manager.getCalculator().calculateCalorie(startTime); - carb = manager.getCalculator().calculateCarb(startTime); - protein = manager.getCalculator().calculateProtein(startTime); - fat = manager.getCalculator().calculateFat(startTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); ui.printAllIntake(calorie, carb, protein, fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); endTime = LocalDateTime.parse(processedParam[2]); - calorie = manager.getCalculator().calculateCalorie(startTime, endTime); - carb = manager.getCalculator().calculateCarb(startTime, endTime); - protein = manager.getCalculator().calculateProtein(startTime, endTime); - fat = manager.getCalculator().calculateFat(startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); ui.printAllIntake(calorie, carb, protein, fat, startTime, endTime); } break; @@ -66,12 +66,12 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printCalorieIntake(this.calorie); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); - calorie = manager.getCalculator().calculateCalorie(startTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); ui.printCalorieIntake(calorie, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); endTime = LocalDateTime.parse(processedParam[2]); - calorie = manager.getCalculator().calculateCalorie(startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); ui.printCalorieIntake(calorie, startTime, endTime); } break; @@ -80,12 +80,12 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printCarbIntake(this.carb); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); - carb = manager.getCalculator().calculateCarb(startTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); ui.printCarbIntake(carb, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); endTime = LocalDateTime.parse(processedParam[2]); - carb = manager.getCalculator().calculateCarb(startTime, endTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); ui.printCarbIntake(carb, startTime, endTime); } break; @@ -94,12 +94,12 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printProteinIntake(this.protein); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); - protein = manager.getCalculator().calculateProtein(startTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); ui.printProteinIntake(protein, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); endTime = LocalDateTime.parse(processedParam[2]); - protein = manager.getCalculator().calculateProtein(startTime, endTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); ui.printProteinIntake(protein, startTime, endTime); } break; @@ -108,12 +108,12 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printFatIntake(this.fat); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); - fat = manager.getCalculator().calculateFat(startTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); ui.printFatIntake(fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); endTime = LocalDateTime.parse(processedParam[2]); - fat = manager.getCalculator().calculateFat(startTime, endTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); ui.printFatIntake(fat, startTime, endTime); } } diff --git a/src/main/java/seedu/dietbook/command/DataCommand.java b/src/main/java/seedu/dietbook/command/DataCommand.java index a5a708d898..1171366dbd 100644 --- a/src/main/java/seedu/dietbook/command/DataCommand.java +++ b/src/main/java/seedu/dietbook/command/DataCommand.java @@ -14,6 +14,6 @@ public void execute(Manager manager, Ui ui) throws DietException { throw new DietException("Please enter your basic information first!"); } manager.getDataBase().init(); - //ui.printDatabase(manager.getDataBase().getFoodList()); + ui.printDatabase(manager.getDataBase().getFoodListString()); } } diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java index c1d4eddb4a..1d3897d831 100644 --- a/src/main/java/seedu/dietbook/command/ExitCommand.java +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -19,7 +19,6 @@ public ExitCommand() { @Override public void execute(Manager manager, Ui ui) { - ui.printExitMessage(); ActivityLevel actLvl = manager.getPerson().getActivityLevel(); int actLvlInt = 1; if (actLvl.equals(ActivityLevel.NONE)) { @@ -35,7 +34,7 @@ public void execute(Manager manager, Ui ui) { } Gender gender = manager.getPerson().getGender(); - String genderString = "Male"; + String genderString; if (gender.equals(Gender.MALE)) { genderString = "Male"; } else if (gender.equals(Gender.FEMALE)) { @@ -52,8 +51,10 @@ public void execute(Manager manager, Ui ui) { personSave.setName(manager.getPerson().getName()); personSave.setOriginalWeight(manager.getPerson().getOriginalWeight()); personSave.setTargetWeight(manager.getPerson().getTargetWeight()); - personSave.save("resources/UserInfo.txt"); - foodSave.save("resources/FoodList.txt", manager.getFoodList().getFoods()); + personSave.save("UserInfo.txt"); + foodSave.save("FoodList.txt", manager.getFoodList().getFoods()); + ui.dataSuccessfullySavedMessage(); DietBook.isExit = true; + ui.printExitMessage(); } } diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java index 6f11e58c90..9cd41a26bd 100644 --- a/src/main/java/seedu/dietbook/command/ListCommand.java +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -4,6 +4,7 @@ import seedu.dietbook.Ui; import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; +import seedu.dietbook.list.FoodList; import java.time.LocalDateTime; @@ -18,6 +19,7 @@ public ListCommand(String userInput) { public void execute(Manager manager, Ui ui) throws DietException { LocalDateTime startTime; LocalDateTime endTime; + FoodList foodList = manager.getFoodList(); if (commandCount == 1) { throw new DietException("Please enter your name first!"); } else if (commandCount == 2) { @@ -31,11 +33,11 @@ public void execute(Manager manager, Ui ui) throws DietException { try { if (processedInput.length == 2) { startTime = LocalDateTime.parse(processedInput[1]); - ui.printFoodList(manager.getFoodList().toString(), startTime); + ui.printFoodList(foodList.getAfterDateTimeToString(startTime), startTime); } else if (processedInput.length == 3) { startTime = LocalDateTime.parse(processedInput[1]); endTime = LocalDateTime.parse(processedInput[2]); - ui.printFoodList(manager.getFoodList().toString(), startTime, endTime); + ui.printFoodList(foodList.getInDateTimeRangeToString(startTime,endTime), startTime, endTime); } } catch (Exception e) { throw new DietException("Wrong date time format! (Format: yyyy-mm-ddTHH:mm)"); diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 1c3fb02f38..3e0c4db12c 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -1,5 +1,7 @@ package seedu.dietbook.parser; +import seedu.dietbook.database.DataBase; +import seedu.dietbook.food.Food; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.Gender; import seedu.dietbook.person.ActivityLevel; @@ -100,6 +102,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws int protein = -1; int fat = -1; String trimmedParam; + //String[] databaseCheck = getCommandParam(userInput).trim().split("/");; String[] processedParam; String[] paramList = {"x/", "n/", "k/", "c/", "p/", "f/"}; InputChecker.checkRepeatedOption(getCommand(userInput), getCommandParam(userInput)); @@ -138,6 +141,11 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws } } } + //if (databaseCheck.length == 3) { + // DataBase dataBase = new DataBase(); + // Food searchedFood = dataBase.searchFoodByName(foodName); + // return foodList.addFood(portionSize, searchedFood); + //} return foodList.addFood(portionSize, foodName, calorie, carb, protein, fat); } From a13a65844cde172d757517c48e892321437b00d1 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 29 Oct 2020 07:40:59 +0800 Subject: [PATCH 249/374] Update ExitCommand.java --- .../seedu/dietbook/command/ExitCommand.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java index 1d3897d831..089f670a25 100644 --- a/src/main/java/seedu/dietbook/command/ExitCommand.java +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -19,42 +19,42 @@ public ExitCommand() { @Override public void execute(Manager manager, Ui ui) { - ActivityLevel actLvl = manager.getPerson().getActivityLevel(); - int actLvlInt = 1; - if (actLvl.equals(ActivityLevel.NONE)) { - actLvlInt = 1; - } else if (actLvl.equals(ActivityLevel.LOW)) { - actLvlInt = 2; - } else if (actLvl.equals(ActivityLevel.MEDIUM)) { - actLvlInt = 3; - } else if (actLvl.equals(ActivityLevel.HIGH)) { - actLvlInt = 4; - } else if (actLvl.equals(ActivityLevel.EXTREME)) { - actLvlInt = 5; - } + //ActivityLevel actLvl = manager.getPerson().getActivityLevel(); + //int actLvlInt = 1; + //if (actLvl.equals(ActivityLevel.NONE)) { + // actLvlInt = 1; + //} else if (actLvl.equals(ActivityLevel.LOW)) { + // actLvlInt = 2; + //} else if (actLvl.equals(ActivityLevel.MEDIUM)) { + // actLvlInt = 3; + //} else if (actLvl.equals(ActivityLevel.HIGH)) { + // actLvlInt = 4; + //} else if (actLvl.equals(ActivityLevel.EXTREME)) { + // actLvlInt = 5; + //} - Gender gender = manager.getPerson().getGender(); - String genderString; - if (gender.equals(Gender.MALE)) { - genderString = "Male"; - } else if (gender.equals(Gender.FEMALE)) { - genderString = "Female"; - } else { - genderString = "Others"; - } + //Gender gender = manager.getPerson().getGender(); + //String genderString; + //if (gender.equals(Gender.MALE)) { + // genderString = "Male"; + //} else if (gender.equals(Gender.FEMALE)) { + // genderString = "Female"; + //} else { + // genderString = "Others"; + //} - personSave.setActivityLevel(actLvlInt); - personSave.setAge(manager.getPerson().getAge()); - personSave.setCurrentWeight(manager.getPerson().getCurrentWeight()); - personSave.setGender(genderString); - personSave.setHeight(manager.getPerson().getHeight()); - personSave.setName(manager.getPerson().getName()); - personSave.setOriginalWeight(manager.getPerson().getOriginalWeight()); - personSave.setTargetWeight(manager.getPerson().getTargetWeight()); - personSave.save("UserInfo.txt"); - foodSave.save("FoodList.txt", manager.getFoodList().getFoods()); - ui.dataSuccessfullySavedMessage(); - DietBook.isExit = true; + //personSave.setActivityLevel(actLvlInt); + //personSave.setAge(manager.getPerson().getAge()); + //personSave.setCurrentWeight(manager.getPerson().getCurrentWeight()); + //personSave.setGender(genderString); + //personSave.setHeight(manager.getPerson().getHeight()); + //personSave.setName(manager.getPerson().getName()); + //personSave.setOriginalWeight(manager.getPerson().getOriginalWeight()); + //personSave.setTargetWeight(manager.getPerson().getTargetWeight()); + //personSave.save("UserInfo.txt"); + //foodSave.save("FoodList.txt", manager.getFoodList().getFoods()); + //ui.dataSuccessfullySavedMessage(); ui.printExitMessage(); + DietBook.isExit = true; } } From 60fc25c9869859c35fd9718fee20e0ff2a198d8e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 09:07:37 +0800 Subject: [PATCH 250/374] Add case where gender is male --- src/main/java/seedu/dietbook/parser/Parser.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 3e0c4db12c..4ab9ff58f5 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -258,6 +258,8 @@ public static void executeEditInfo(String userInput, Manager manager) throws Die InputChecker.checkGender(processGender); if (processGender.equals("F")) { gender = Gender.FEMALE; + } else if (processGender.equals("M")) { + gender = Gender.MALE; } else { gender = Gender.OTHERS; } From e563a49c1fe57890d85daac5e043f8e858f77435 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 29 Oct 2020 11:19:18 +0800 Subject: [PATCH 251/374] Bug Fixes --- src/main/java/seedu/dietbook/DietBook.java | 1 + src/main/java/seedu/dietbook/command/DataCommand.java | 1 - src/main/java/seedu/dietbook/parser/Parser.java | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 6a512a954d..e11065a3c0 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -37,6 +37,7 @@ public DietBook() { loadPerson = new PersonSaveLoadManager(); foodList = new FoodList(); dataBase = new DataBase(); + dataBase.init(); manager = new Manager(foodList, dataBase); } diff --git a/src/main/java/seedu/dietbook/command/DataCommand.java b/src/main/java/seedu/dietbook/command/DataCommand.java index 1171366dbd..adabf27c11 100644 --- a/src/main/java/seedu/dietbook/command/DataCommand.java +++ b/src/main/java/seedu/dietbook/command/DataCommand.java @@ -13,7 +13,6 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } - manager.getDataBase().init(); ui.printDatabase(manager.getDataBase().getFoodListString()); } } diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 3e0c4db12c..d2a306fd6e 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -98,9 +98,9 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws int portionSize = 1; String foodName = "Food Name"; int calorie = 0; - int carb = -1; - int protein = -1; - int fat = -1; + int carb = 0; + int protein = 0; + int fat = 0; String trimmedParam; //String[] databaseCheck = getCommandParam(userInput).trim().split("/");; String[] processedParam; From 917c55089951896a1a54dfd46289c855b19e3bcb Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 12:23:12 +0800 Subject: [PATCH 252/374] Update help command message --- src/main/java/seedu/dietbook/Ui.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index b124ad381e..49c2282c52 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -819,9 +819,9 @@ private String getFoodListRelatedCommands() { * @return A string representation of a list of database related commands that users can input. */ private String getDatabaseRelatedCommands() { - return " To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR - + " To add a food from the database consumed at a certain time: add n/FOOD_NAME " - + "x/PORTION_SIZE yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + return " [Coming Soon] To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR + + " [Coming Soon] To add a food from the database consumed at a certain time: add " + + "n/FOOD_NAME x/PORTION_SIZE yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + " To view all food in the database: data" + LINE_SEPARATOR; } From b7ba81bc5980b94f92b32fe957af041acfcf2cbd Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Thu, 29 Oct 2020 12:58:03 +0800 Subject: [PATCH 253/374] add addFoodAtDateTime Method overload that accepts food object directly. For save-loading and adding from database. --- .../java/seedu/dietbook/list/FoodList.java | 23 +++++++++++++++---- .../seedu/dietbook/list/FoodListTest.java | 7 ++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index 3807882b12..5e38fa46fc 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -31,11 +31,10 @@ protected FoodList(ArrayList entries) { /** * Adds food of portion size directly into the foodlist as an entry. - * When date functionality is added, this method will need to be overhauled. - * The adding feature will be largely pushed to FoodListManager (to figure out dates) + * Mainly for adding food directly from the data base of foods. * @param portionSize integer to designate number of servings * @param food food object to be added - * @return string representation of the food object added + * @return string representation of the entry added */ public String addFood(int portionSize, Food food) { FoodEntry entry = new DatedFoodEntry(portionSize, food); @@ -60,7 +59,7 @@ public String addFood(int portionSize, String name, int calorie, * Currently just throws a not found exception when called in this manner. * @param portionSize integer to designate number of servings * @param name food object to be added - * @return string representation of the food object added + * @return string representation of entry added * @throws FoodNotFoundException custom exception to indicate search for food in database failed. */ public String addFood(int portionSize, String name) throws FoodNotFoundException { @@ -69,7 +68,7 @@ public String addFood(int portionSize, String name) throws FoodNotFoundException /** - * Add add method for baglogged entries. + * Add add method for backlogged entries. * Allows specificiation of time via LocalDateTime param. * @param dateTime User specified time for backlogged entry. */ @@ -81,6 +80,20 @@ public String addFoodAtDateTime(int portionSize, String name, int calorie, return entry.toString(); } + /** + * Truncated add method for the purpose of save-loading (allows adding food object directly). + * Can also be used to add backlogged entry via database. + * @param portionSize integer to designate number of servings + * @param food Food object to be added (from the save-load/database) + * @param dateTime Save-loaded date-time or user specified time for backlogged entry. + * @return string representation of entry added. + */ + public String addFoodAtDateTime(int portionSize, Food food, LocalDateTime dateTime) { + FoodEntry entry = new DatedFoodEntry(portionSize, food, dateTime); + foodEntries.add(entry); + return entry.toString(); + } + /** * Deletes the the entry of the list at the provided index. * index starts from 1 (not 0). i.e. is User's understanding of index. diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index a4072423f9..60a62dd4f7 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -113,4 +113,11 @@ void getFoodEntryProperties_standardList_FoodEntryProperties() { assertEquals(list.getFoods().get(0), food); } + @Test + void addAndRetrieveEntryAtDateTime_entryAddedAtDateTimeMax_entryAtDateTimeMax() { + list.addFoodAtDateTime(2, food, LocalDateTime.MAX); + assertEquals(food, list.getFoodsAfterDateTime(LocalDateTime.now()).get(0)); + + } + } From 5c97f7933fb17417e62e1611770fd68f7a1b6732 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Thu, 29 Oct 2020 13:25:39 +0800 Subject: [PATCH 254/374] Put Examples of Usage for adding controling food items into the User Guide --- docs/UserGuide.md | 114 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 24 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index f5988acfa2..13d7bcc5fc 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -167,41 +167,107 @@ Got it! I've updated your personal information: ### Features related to the food database -To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE -To view all food in the database: data +* To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE +* To view all food in the database: data ### Features related to the food list -To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] -To view all food in DietBook: list -To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm -To view all food in DietBook recorded from a certain date until now: list yyyy-mm-ddTHH:mm -To delete a food from DietBook: delete INDEX -To delete all food items from the DietBook: clear - +* To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] + * Examples of usage : + * Example 1 : add x/1 n/Salty Chicken Rice k/300 c/10 p/20 f/50 + * Output of Example 1 : + ``` + Got it! I've added this food item: + Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + ``` + * Example 2 : add x/2 n/Chilli Pepper Crab k/400 c/10 p/10 f/20 + * Output of Example 2 : + ``` + Got it! I've added this food item: + Chilli Pepper Crab | calorie : 400 | protein : 10 | carbohydrate : 10 | fats : 20 -- (2) + ``` + +* To view all food in DietBook: list + * Example of usage : list + * Output : + ``` + Here are the food items in DietBook: + 1. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 2. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 3. Chilli Pepper Crab | calorie : 400 | protein : 10 | carbohydrate : 10 | fats : 20 -- (2) + ``` +* To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + * Example of usage : list 2020-10-29T11:30 2020-10-29T16:40 + * Output : + ``` + Here are the food items recorded in DietBook between 29 Oct 2020 1130 and 29 Oct 2020 1640: + 1. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 2. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 3. Chilli Pepper Crab | calorie : 400 | protein : 10 | carbohydrate : 10 | fats : 20 -- (2) + ``` + +* To view all food in DietBook recorded from a certain date until now: list yyyy-mm-ddTHH:mm + * Example of usage : list 1066-10-14T08:00 + * Output : + ``` + Here are the food items recorded in DietBook between 14 Oct 1066 0800 and 29 Oct 2020 1317: + 1. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 2. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 3. Chilli Pepper Crab | calorie : 400 | protein : 10 | carbohydrate : 10 | fats : 20 -- (2) + ``` +* To delete a food from DietBook: delete INDEX + * Example of usage : delete 2 + * Output : + ``` + Noted. I've removed this food item: + Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + ``` + * The list after delete is done : + ``` + Here are the food items in DietBook: + 1. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 2. Chilli Pepper Crab | calorie : 400 | protein : 10 | carbohydrate : 10 | fats : 20 -- (2) + ``` +* To delete all food items from the DietBook: clear + * Example of usage : clear + * Output : + ``` + All previous data has been deleted... + DietBook is now empty. + ``` + * Before clearing : + ``` + Here are the food items in DietBook: + 1. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 2. Chilli Pepper Crab | calorie : 400 | protein : 10 | carbohydrate : 10 | fats : 20 -- (2) + ``` + * After clearing : + ``` + DietBook is currently empty. + ``` ### Features related to nutritional intake and recommendation To get recommended calorie intake: recommend - To calculate carbohydrate intake: calculate carbohydrate - To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm + * To calculate carbohydrate intake: calculate carbohydrate + * To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + * To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm - To calculate calorie intake: calculate calorie - To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate calorie intake from a certain date until now: calculate calorie yyyy-mm-ddTHH:mm + * To calculate calorie intake: calculate calorie + * To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + * To calculate calorie intake from a certain date until now: calculate calorie yyyy-mm-ddTHH:mm - To calculate protein intake: calculate protein - To calculate protein intake within a time period: calculate protein yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate protein intake from a certain date until now: calculate protein yyyy-mm-ddTHH:mm + * To calculate protein intake: calculate protein + * To calculate protein intake within a time period: calculate protein yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + * To calculate protein intake from a certain date until now: calculate protein yyyy-mm-ddTHH:mm - To calculate fat intake: calculate fat - To calculate fat intake within a time period: calculate fat yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate fat intake from a certain date until now: calculate fat yyyy-mm-ddTHH:mm + * To calculate fat intake: calculate fat + * To calculate fat intake within a time period: calculate fat yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + * To calculate fat intake from a certain date until now: calculate fat yyyy-mm-ddTHH:mm - To calculate all nutritional intake: calculate all - To calculate all nutritional intake within a time period: calculate all yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate all nutritional intake from a certain date until now: calculate all yyyy-mm-ddTHH:mm + * To calculate all nutritional intake: calculate all + * To calculate all nutritional intake within a time period: calculate all yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + * To calculate all nutritional intake from a certain date until now: calculate all yyyy-mm-ddTHH:mm ### Other features From 0e12c980ac45a107174023d73df69d0383de6a68 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Thu, 29 Oct 2020 13:48:47 +0800 Subject: [PATCH 255/374] Add examples of usage to UserGuide section --- docs/UserGuide.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d0d7afc813..8104eee790 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -248,11 +248,54 @@ Got it! I've updated your personal information: ### Features related to nutritional intake and recommendation To get recommended calorie intake: recommend + * Example of usage : + * Output : + ``` + Hi Jack! + Here is your daily recommended calorie intake: 2607kcal + ``` + +We use the following list as an example, to set up the list we have the following sequence of inputs + +add x/1 n/Salty Chicken Rice k/300 c/10 p/20 f/50 +add x/2 n/Chilli Pepper Crab k/400 c/10 p/10 f/20 +add x/1 n/Steak Buns k/500 c/20 p/50 f/10 +add x/3 n/Sweat potato tea k/300 c/30 p/0 f/0 +add x/1 n/Chicken Wing Noodles k/400 c/10 p/30 f/10 +list + +``` +Here are the food items in DietBook: + 1. Salty Chicken Rice | calorie : 300 | protein : 20 | carbohydrate : 10 | fats : 50 -- (1) + 2. Chilli Pepper Crab | calorie : 400 | protein : 10 | carbohydrate : 10 | fats : 20 -- (2) + 3. Steak Buns | calorie : 500 | protein : 50 | carbohydrate : 20 | fats : 10 -- (1) + 4. Sweat potato tea | calorie : 300 | protein : 0 | carbohydrate : 30 | fats : 0 -- (3) + 5. Chicken Wing Noodles | calorie : 400 | protein : 30 | carbohydrate : 10 | fats : 10 -- (1) +``` * To calculate carbohydrate intake: calculate carbohydrate + Input : calculate carbohydrate + Output : + ``` + Total carbohydrate intake: 80g + ``` * To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - * To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm + Input : calculate carbohydrate 2020-10-29T08:00 2020-10-29T17:00 + Output : + ``` + Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1700 + Total carbohydrate intake: 80g + ``` + * To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm + Input : calculate carbohydrate 2020-10-29T08:00 + Output : + ``` + Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1340 + + Total carbohydrate intake: 80g + ``` +Similar Inputs and outputs for the following * To calculate calorie intake: calculate calorie * To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm * To calculate calorie intake from a certain date until now: calculate calorie yyyy-mm-ddTHH:mm @@ -266,8 +309,36 @@ To get recommended calorie intake: recommend * To calculate fat intake from a certain date until now: calculate fat yyyy-mm-ddTHH:mm * To calculate all nutritional intake: calculate all + Input : calculate all + OutPut : + ``` + Total calorie intake: 1900kcal + Total carbohydrate intake: 80g + Total protein intake: 110g + Total fat intake: 90g + ``` * To calculate all nutritional intake within a time period: calculate all yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + Input : calculate all 2020-10-29T08:00 2020-10-29T17:00 + OutPut : + ``` + Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1700 + + Total calorie intake: 1900kcal + Total carbohydrate intake: 80g + Total protein intake: 110g + Total fat intake: 90g + ``` * To calculate all nutritional intake from a certain date until now: calculate all yyyy-mm-ddTHH:mm + Input : calculate all 2020-10-29T08:00 + OutPut : + ``` + Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1345 + + Total calorie intake: 1900kcal + Total carbohydrate intake: 80g + Total protein intake: 110g + Total fat intake: 90g + ``` ### Other features From cd4ea6df207d4054f427bb158f92c6b8f815aff1 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Thu, 29 Oct 2020 13:50:12 +0800 Subject: [PATCH 256/374] minor fixes --- docs/UserGuide.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 8104eee790..1a3fe23b11 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -274,22 +274,22 @@ Here are the food items in DietBook: ``` * To calculate carbohydrate intake: calculate carbohydrate - Input : calculate carbohydrate - Output : + * Input : calculate carbohydrate + * Output : ``` Total carbohydrate intake: 80g ``` * To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - Input : calculate carbohydrate 2020-10-29T08:00 2020-10-29T17:00 - Output : + * Input : calculate carbohydrate 2020-10-29T08:00 2020-10-29T17:00 + * Output : ``` Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1700 Total carbohydrate intake: 80g ``` * To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm - Input : calculate carbohydrate 2020-10-29T08:00 - Output : + * Input : calculate carbohydrate 2020-10-29T08:00 + * Output : ``` Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1340 @@ -309,8 +309,8 @@ Similar Inputs and outputs for the following * To calculate fat intake from a certain date until now: calculate fat yyyy-mm-ddTHH:mm * To calculate all nutritional intake: calculate all - Input : calculate all - OutPut : + * Input : calculate all + * OutPut : ``` Total calorie intake: 1900kcal Total carbohydrate intake: 80g @@ -318,8 +318,8 @@ Similar Inputs and outputs for the following Total fat intake: 90g ``` * To calculate all nutritional intake within a time period: calculate all yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - Input : calculate all 2020-10-29T08:00 2020-10-29T17:00 - OutPut : + * Input : calculate all 2020-10-29T08:00 2020-10-29T17:00 + * OutPut : ``` Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1700 @@ -329,8 +329,8 @@ Similar Inputs and outputs for the following Total fat intake: 90g ``` * To calculate all nutritional intake from a certain date until now: calculate all yyyy-mm-ddTHH:mm - Input : calculate all 2020-10-29T08:00 - OutPut : + * Input : calculate all 2020-10-29T08:00 + * OutPut : ``` Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1345 From ae409f59fc7f53cadf38187758602dbccbf8c4d5 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 29 Oct 2020 13:58:39 +0800 Subject: [PATCH 257/374] Update UserGuide.md --- docs/UserGuide.md | 77 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 87ff008b0c..e64a907d03 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -205,9 +205,80 @@ To get recommended calorie intake: recommend ### Other features - To view a list of valid commands: help - To exit DietBook: exit - Saving +#### To view a list of valid commands: help: `help` + +Displays the helping guide for commands. + +Format: `help` + +Output example: +``` +Listed below are the valid commands for DietBook: + +For user information related commands + To view user information: userinfo + To edit user information: editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL] + +For database related commands + [Coming Soon] To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE + [Coming Soon] To add a food from the database consumed at a certain time: add n/FOOD_NAME x/PORTION_SIZE yyyy-mm-ddTHH:mm + To view all food in the database: data + +For food list related commands + To add a food not in the database that was just consumed: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] + To add a food not in the database consumed at a certain time: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] yyyy-mm-ddTHH:mm + To view all food in DietBook: list + To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To view all food in DietBook recorded from a certain date until now: list yyyy-mm-ddTHH:mm + To delete a food from DietBook: delete INDEX + To delete all food items from the DietBook: clear + +For nutritional intake and recommendation related commands + To get recommended calorie intake: recommend + + To calculate carbohydrate intake: calculate carbohydrate + To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm + + To calculate calorie intake: calculate calorie + To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate calorie intake from a certain date until now: calculate calorie yyyy-mm-ddTHH:mm + + To calculate protein intake: calculate protein + To calculate protein intake within a time period: calculate protein yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate protein intake from a certain date until now: calculate protein yyyy-mm-ddTHH:mm + + To calculate fat intake: calculate fat + To calculate fat intake within a time period: calculate fat yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate fat intake from a certain date until now: calculate fat yyyy-mm-ddTHH:mm + + To calculate all nutritional intake: calculate all + To calculate all nutritional intake within a time period: calculate all yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + To calculate all nutritional intake from a certain date until now: calculate all yyyy-mm-ddTHH:mm + +For other system related commands + To view a list of valid commands: help + To exit DietBook: exit +``` + #### To exit DietBook: `exit` + + Exits the DietBook. + + Format: `exit` + + Output example: + ``` + Bye! Hope to see you again soon! + ``` + + #### Saving the DietBook: `Coming soon!` + + Saves the DietBook data when the exit command have been input. The saved data is in 2 files: UserInfo.txt and FoodList.txt. + + Output example: + ``` + Your data has been saved successfully. + ``` #### Adding a todo: `todo` Adds a new item to the list of todo items. From 4e99bb82dff1a0e33a823275d2b7997120d05257 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 29 Oct 2020 21:41:18 +0800 Subject: [PATCH 258/374] Add command with date time --- .../seedu/dietbook/checker/InputChecker.java | 31 +++++++++++++++++++ .../java/seedu/dietbook/parser/Parser.java | 9 ++++++ 2 files changed, 40 insertions(+) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 43a0d8069a..4f887e0c28 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -3,6 +3,8 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; +import java.time.LocalDateTime; + /** * InputChecker class of the program. * This class checks the validity of the user input and throws an exception if input is not as intended/expected. @@ -77,6 +79,35 @@ public static void checkRepeatedOption(String command, String options) throws Di } } + /** + * Takes in user input to check if date format is present. + * + * @param userInput user input. + * @return boolean whereby true if date present, false otherwise. + */ + public static boolean checkDate(String userInput) throws DietException { + String[] processedInput = userInput.split("\\s+"); + if (processedInput[processedInput.length - 1].contains("T")) { + return true; + } else { + return false; + } + } + + /** + * Takes in string format of date time to check if date format is correct. + * + * @param dateString string form of a potential date time. + * @throws DietException if date format is wrong. + */ + public static void checkDateValidity(String dateString) throws DietException { + try { + LocalDateTime.parse(dateString); + } catch (Exception e) { + throw new DietException("Wrong date time format!"); + } + } + /** * Takes in user input to check if the expected number and type of parameter for the add command is present. * diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index d5b2eef077..56dfe329eb 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -111,8 +111,11 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws processedParam = getCommandParam(userInput).split(param); InputChecker.checkEmptyOption(processedParam); trimmedParam = processedParam[1].trim(); + if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); + } else if (trimmedParam.split("\\s+").length == 2) { + trimmedParam = trimmedParam.split("\\s+")[0]; } switch (param) { case "x/": @@ -141,6 +144,12 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws } } } + if (InputChecker.checkDate(userInput)) { + processedParam = userInput.split("\\s+"); + InputChecker.checkDateValidity(processedParam[processedParam.length - 1]); + LocalDateTime time = LocalDateTime.parse(processedParam[processedParam.length - 1]); + return foodList.addFoodAtDateTime(portionSize, foodName, calorie, carb, protein, fat, time); + } //if (databaseCheck.length == 3) { // DataBase dataBase = new DataBase(); // Food searchedFood = dataBase.searchFoodByName(foodName); From 8c2a70284d0633ed88a1188486863ff70c2af209 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 29 Oct 2020 22:08:00 +0800 Subject: [PATCH 259/374] Future date exception --- .../java/seedu/dietbook/checker/InputChecker.java | 12 ++++++++++++ .../seedu/dietbook/command/CalculateCommand.java | 13 +++++++++++-- .../java/seedu/dietbook/command/ListCommand.java | 4 +++- src/main/java/seedu/dietbook/parser/Parser.java | 1 + 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 4f887e0c28..c68e6afa71 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -108,6 +108,18 @@ public static void checkDateValidity(String dateString) throws DietException { } } + /** + * Takes in a date time object and see if it is a future date. + * + * @param time a date time class object. + * @throws DietException if date is in the future. + */ + public static void checkFutureDate(LocalDateTime time) throws DietException { + if (time.isAfter(LocalDateTime.now())) { + throw new DietException("The date cannot be in the future!"); + } + } + /** * Takes in user input to check if the expected number and type of parameter for the add command is present. * diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index 5b767ad184..6eb01e27b2 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -2,7 +2,6 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; -import seedu.dietbook.calculator.Calculator; import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; @@ -46,6 +45,7 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printAllIntake(this.calorie, this.carb, this.protein, this.fat); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); @@ -53,6 +53,7 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printAllIntake(calorie, carb, protein, fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); @@ -66,10 +67,12 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printCalorieIntake(this.calorie); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); ui.printCalorieIntake(calorie, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); ui.printCalorieIntake(calorie, startTime, endTime); @@ -80,10 +83,12 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printCarbIntake(this.carb); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); ui.printCarbIntake(carb, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); ui.printCarbIntake(carb, startTime, endTime); @@ -94,10 +99,12 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printProteinIntake(this.protein); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); ui.printProteinIntake(protein, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); ui.printProteinIntake(protein, startTime, endTime); @@ -108,17 +115,19 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printFatIntake(this.fat); } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); ui.printFatIntake(fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); ui.printFatIntake(fat, startTime, endTime); } } } catch (Exception e) { - throw new DietException("Wrong date time format! (Format: yyyy-mm-ddTHH:mm)"); + throw new DietException("Wrong date time format (Format: yyyy-mm-ddTHH:mm) or future date entered!"); } } } diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java index 9cd41a26bd..933e42289e 100644 --- a/src/main/java/seedu/dietbook/command/ListCommand.java +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -33,14 +33,16 @@ public void execute(Manager manager, Ui ui) throws DietException { try { if (processedInput.length == 2) { startTime = LocalDateTime.parse(processedInput[1]); + InputChecker.checkFutureDate(startTime); ui.printFoodList(foodList.getAfterDateTimeToString(startTime), startTime); } else if (processedInput.length == 3) { startTime = LocalDateTime.parse(processedInput[1]); endTime = LocalDateTime.parse(processedInput[2]); + InputChecker.checkFutureDate(startTime); ui.printFoodList(foodList.getInDateTimeRangeToString(startTime,endTime), startTime, endTime); } } catch (Exception e) { - throw new DietException("Wrong date time format! (Format: yyyy-mm-ddTHH:mm)"); + throw new DietException("Wrong date time format (Format: yyyy-mm-ddTHH:mm) or future date entered!"); } } } diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 56dfe329eb..9fbcddaa74 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -148,6 +148,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws processedParam = userInput.split("\\s+"); InputChecker.checkDateValidity(processedParam[processedParam.length - 1]); LocalDateTime time = LocalDateTime.parse(processedParam[processedParam.length - 1]); + InputChecker.checkFutureDate(time); return foodList.addFoodAtDateTime(portionSize, foodName, calorie, carb, protein, fat, time); } //if (databaseCheck.length == 3) { From 15f3458db13966ac31e7d815828cc1ed6cfcc9ec Mon Sep 17 00:00:00 2001 From: HengFuYuen <60005925+HengFuYuen@users.noreply.github.com> Date: Thu, 29 Oct 2020 22:30:08 +0800 Subject: [PATCH 260/374] Update _config.yml --- docs/_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_config.yml b/docs/_config.yml index 3397c9a492..8b13789179 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-architect \ No newline at end of file + From 48c9fd4ffc7a322fb1fa0ccb18771d6f9b7cb297 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 22:43:51 +0800 Subject: [PATCH 261/374] Fix major formatting issues --- docs/UserGuide.md | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3fdbb301bf..56945867c1 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -248,21 +248,22 @@ Got it! I've updated your personal information: ### Features related to nutritional intake and recommendation To get recommended calorie intake: recommend - * Example of usage : - * Output : +* Example of usage : +* Output : ``` Hi Jack! Here is your daily recommended calorie intake: 2607kcal ``` We use the following list as an example, to set up the list we have the following sequence of inputs - +``` add x/1 n/Salty Chicken Rice k/300 c/10 p/20 f/50 add x/2 n/Chilli Pepper Crab k/400 c/10 p/10 f/20 add x/1 n/Steak Buns k/500 c/20 p/50 f/10 add x/3 n/Sweat potato tea k/300 c/30 p/0 f/0 add x/1 n/Chicken Wing Noodles k/400 c/10 p/30 f/10 list +``` ``` Here are the food items in DietBook: @@ -416,20 +417,6 @@ For other system related commands ``` Your data has been saved successfully. ``` - -#### Adding a todo: `todo` -Adds a new item to the list of todo items. - -Format: `todo n/TODO_NAME d/DEADLINE` - -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. - -Example of usage: - -`todo n/Write the rest of the User Guide d/next week` - -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` ## FAQ From a66165be49284ba2fcf938be74d6fc8fb53f8efd Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 29 Oct 2020 22:59:32 +0800 Subject: [PATCH 262/374] Fix formatting --- docs/UserGuide.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3fdbb301bf..51a36c93c2 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -279,22 +279,24 @@ Here are the food items in DietBook: ``` Total carbohydrate intake: 80g ``` - * To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - * Input : calculate carbohydrate 2020-10-29T08:00 2020-10-29T17:00 - * Output : +* To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm + * Input : calculate carbohydrate 2020-10-29T08:00 2020-10-29T17:00 + * Output : ``` Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1700 Total carbohydrate intake: 80g ``` - * To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm - * Input : calculate carbohydrate 2020-10-29T08:00 - * Output : + +* To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm + * Input : calculate carbohydrate 2020-10-29T08:00 + * Output : ``` Time period: between 29 Oct 2020 0800 and 29 Oct 2020 1340 Total carbohydrate intake: 80g ``` + Similar Inputs and outputs for the following * To calculate calorie intake: calculate calorie * To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm @@ -413,6 +415,7 @@ For other system related commands Saves the DietBook data when the exit command have been input. The saved data is in 2 files: UserInfo.txt and FoodList.txt. Output example: + ``` Your data has been saved successfully. ``` From 6d4887b82a690b4fe4d276de9c149551ee195d61 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Fri, 30 Oct 2020 11:36:18 +0800 Subject: [PATCH 263/374] Update UserGuide.md --- docs/UserGuide.md | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 87f7778fc9..035c9e037c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -167,9 +167,43 @@ Got it! I've updated your personal information: ### Features related to the food database -* To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE -* To view all food in the database: data - +#### To view all food in the database: `data` + +Displays a list of provided food database. + +Format: `data` + +Output example: +``` +Here are the food items in the database: + 1. Prawn Mee Soup(Dry)(Large) | calorie : 490 | protein : 0 | carbohydrate : 0 | fats : 0 + 2. Prawn Mee Soup(Dry)(Small) | calorie : 390 | protein : 0 | carbohydrate : 0 | fats : 0 + 3. Fried Hokkien Prawn Mee(Large) | calorie : 470 | protein : 0 | carbohydrate : 0 | fats : 0 + 4. Fried Hokkien Prawn Mee(Small) | calorie : 350 | protein : 0 | carbohydrate : 0 | fats : 0 + 5. Clay Pot Chicken | calorie : 440 | protein : 0 | carbohydrate : 0 | fats : 0 + 6. Black Pepper Chicken | calorie : 490 | protein : 0 | carbohydrate : 0 | fats : 0 + 7. Ayam Penyet Set | calorie : 699 | protein : 0 | carbohydrate : 0 | fats : 0 + 8. Steamed Chicken Set | calorie : 475 | protein : 0 | carbohydrate : 0 | fats : 0 + 9. Ikan Grouper Penyet Set | calorie : 669 | protein : 0 | carbohydrate : 0 | fats : 0 + 10. Bouillabaisse with cock crab and poached lobster | calorie : 520 | protein : 35 | carbohydrate : 45 | fats : 56 + 11. Chicken wings with Reblochon pomme pur??e | calorie : 450 | protein : 32 | carbohydrate : 25 | fats : 66 + 12. Sea bass with prawn tortellini, fennel pur??e and white wine sauce | calorie : 530 | protein : 25 | carbohydrate : 76 | fats : 43 +``` + +#### To add a food from the database: `add` `Coming Soon!` + +Adds a desired food in the database into the DietBook. + +Format: `add n/FOOD_NAME x/PORTION_SIZE` + +Example of Usage: +* `add n/Prawn Mee x/1` adds the first instance of food with a name that contains Prawn Mee with a portion of 1. + +Output example: +```Here are the food items in DietBook: + 1. Prawn Mee Soup(Dry)(Large) | calorie : 490 | protein : 0 | carbohydrate : 0 | fats : 0 -- (1) +``` + ### Features related to the food list * To add you own food: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] From acd0a80f1276a2440430578c6d8eca60d872f786 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sat, 31 Oct 2020 23:17:10 +0800 Subject: [PATCH 264/374] Added a CalculatorData class to take in a FoodList and return information needed to calculate to Calculator class. Added the initialization of CalculatorData class in manager. Deleted setCalculator method in manager. Changed the inputs of methods related with calculator in CalculateCommand class. --- src/main/java/seedu/dietbook/Manager.java | 20 ++- .../seedu/dietbook/calculator/Calculator.java | 133 ++++++++++-------- .../dietbook/calculator/CalculatorData.java | 133 ++++++++++++++++++ .../dietbook/command/CalculateCommand.java | 32 ++--- .../dietbook/calculator/CalculatorTest.java | 52 +++---- 5 files changed, 261 insertions(+), 109 deletions(-) create mode 100644 src/main/java/seedu/dietbook/calculator/CalculatorData.java diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index dacedc03f1..14622a9278 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -1,5 +1,7 @@ package seedu.dietbook; +import seedu.dietbook.calculator.Calculator; +import seedu.dietbook.calculator.CalculatorData; import seedu.dietbook.command.AddCommand; import seedu.dietbook.command.CalculateCommand; import seedu.dietbook.command.ClearCommand; @@ -14,16 +16,14 @@ import seedu.dietbook.command.NameCommand; import seedu.dietbook.command.RecommendCommand; import seedu.dietbook.command.UserinfoCommand; -import seedu.dietbook.list.FoodList; -import seedu.dietbook.person.ActivityLevel; -import seedu.dietbook.person.Person; -import seedu.dietbook.calculator.Calculator; import seedu.dietbook.database.DataBase; -import seedu.dietbook.person.Gender; import seedu.dietbook.exception.DietException; +import seedu.dietbook.list.FoodList; import seedu.dietbook.parser.Parser; +import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.Gender; +import seedu.dietbook.person.Person; -import java.util.ArrayList; /** * Manager class of the program. @@ -39,6 +39,7 @@ public class Manager { private String name; private int commandCount = 1; private DataBase dataBase; + private CalculatorData data; private Calculator calculator; public static final String COMMAND_ADD = "add"; @@ -61,7 +62,8 @@ public Manager(FoodList foodlist, DataBase dataBase) { 1, ActivityLevel.LOW); this.foodList = foodlist; this.dataBase = dataBase; - this.calculator = new Calculator(foodList.getFoods()); + this.data.inputData(this.foodList); + this.calculator = new Calculator(this.data); } public FoodList getFoodList() { @@ -85,10 +87,6 @@ public Calculator getCalculator() { return this.calculator; } - public void setCalculator() { - this.calculator = new Calculator(foodList.getFoods()); - } - public DataBase getDataBase() { return this.dataBase; } diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 266ed0781a..5d77a7c584 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -1,7 +1,5 @@ package seedu.dietbook.calculator; -import seedu.dietbook.food.Food; -import seedu.dietbook.list.FoodList; import seedu.dietbook.person.Gender; import seedu.dietbook.person.Person; @@ -13,27 +11,20 @@ */ public class Calculator { private int totalCalorie = 0; - private int totalCarbohydrate = 0; + private int totalCarb = 0; private int totalProtein = 0; private int totalFat = 0; - + CalculatorData data = null; /** * Construct a calculator taking in a foodList. Add up calories, * carbs, protein, and fats in each food item. * - * @param foodList foodList containing food items to calculate. + * @param data a CalculatorData class instance containing data + * of food items to calculate. */ - public Calculator(List foodList) { - assert foodList != null : "The foodList should not be null."; - - for (int i = 0; i < foodList.size(); i++) { - assert foodList.get(i).getName().trim().length() != 0 : "Food names should not be empty."; - - totalCalorie += foodList.get(i).getCalorie(); - totalCarbohydrate += foodList.get(i).getCarbohydrate(); - totalProtein += foodList.get(i).getProtein(); - totalFat += foodList.get(i).getFat(); - } + public Calculator(CalculatorData data) { + assert data != null : "The foodList should not be null."; + this.data = data; } /** @@ -42,6 +33,11 @@ public Calculator(List foodList) { * @return the value of total calorie of food items in foodList. */ public int calculateCalorie() { + totalCalorie = 0; + List calories = data.getTotalCalorie(); + for (int calorie : calories) { + totalCalorie += calorie; + } return totalCalorie; } @@ -54,12 +50,13 @@ public int calculateCalorie() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCalorie(FoodList foodList, LocalDateTime startTime) { - int calorie = 0; - for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { - calorie += foodList.getFoodsAfterDateTime(startTime).get(i).getCalorie(); + public int calculateCalorie(LocalDateTime startTime) { + totalCalorie = 0; + List calories = data.getTotalCalorie(startTime); + for (int calorie : calories) { + totalCalorie += calorie; } - return calorie; + return totalCalorie; } /** @@ -72,12 +69,13 @@ public int calculateCalorie(FoodList foodList, LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCalorie(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { - int calorie = 0; - for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - calorie += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCalorie(); + public int calculateCalorie(LocalDateTime startTime, LocalDateTime endTime) { + totalCalorie = 0; + List calories = data.getTotalCalorie(startTime, endTime); + for (int calorie : calories) { + totalCalorie += calorie; } - return calorie; + return totalCalorie; } /** @@ -86,7 +84,12 @@ public int calculateCalorie(FoodList foodList, LocalDateTime startTime, LocalDat * @return the value of total carbs of food items in foodList. */ public int calculateCarb() { - return totalCarbohydrate; + totalCarb = 0; + List carbs = data.getTotalCarb(); + for (int carb : carbs) { + totalCarb += carb; + } + return totalCarb; } /** @@ -98,12 +101,13 @@ public int calculateCarb() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCarb(FoodList foodList, LocalDateTime startTime) { - int carb = 0; - for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { - carb += foodList.getFoodsAfterDateTime(startTime).get(i).getCarbohydrate(); + public int calculateCarb(LocalDateTime startTime) { + totalCarb = 0; + List carbs = data.getTotalCarb(startTime); + for (int carb : carbs) { + totalCarb += carb; } - return carb; + return totalCarb; } /** @@ -116,12 +120,13 @@ public int calculateCarb(FoodList foodList, LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateCarb(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { - int carb = 0; - for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - carb += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getCarbohydrate(); + public int calculateCarb(LocalDateTime startTime, LocalDateTime endTime) { + totalCarb = 0; + List carbs = data.getTotalCarb(startTime, endTime); + for (int carb : carbs) { + totalCarb += carb; } - return carb; + return totalCarb; } /** @@ -130,6 +135,11 @@ public int calculateCarb(FoodList foodList, LocalDateTime startTime, LocalDateTi * @return the value of total protein of food items in foodList. */ public int calculateProtein() { + totalProtein = 0; + List proteins = data.getTotalProtein(); + for (int protein : proteins) { + totalProtein += protein; + } return totalProtein; } @@ -142,12 +152,13 @@ public int calculateProtein() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateProtein(FoodList foodList, LocalDateTime startTime) { - int protein = 0; - for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { - protein += foodList.getFoodsAfterDateTime(startTime).get(i).getProtein(); + public int calculateProtein(LocalDateTime startTime) { + totalProtein = 0; + List proteins = data.getTotalProtein(startTime); + for (int protein : proteins) { + totalProtein += protein; } - return protein; + return totalProtein; } /** @@ -160,12 +171,13 @@ public int calculateProtein(FoodList foodList, LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateProtein(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { - int protein = 0; - for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - protein += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getProtein(); + public int calculateProtein(LocalDateTime startTime, LocalDateTime endTime) { + totalProtein = 0; + List proteins = data.getTotalProtein(startTime, endTime); + for (int protein : proteins) { + totalProtein += protein; } - return protein; + return totalProtein; } /** @@ -174,6 +186,11 @@ public int calculateProtein(FoodList foodList, LocalDateTime startTime, LocalDat * @return the value of total fats of food items in foodList. */ public int calculateFat() { + totalFat = 0; + List fats = data.getTotalFat(); + for (int fat : fats) { + totalFat += fat; + } return totalFat; } @@ -186,12 +203,13 @@ public int calculateFat() { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateFat(FoodList foodList, LocalDateTime startTime) { - int fat = 0; - for (int i = 0; i < foodList.getFoodsAfterDateTime(startTime).size(); i++) { - fat += foodList.getFoodsAfterDateTime(startTime).get(i).getFat(); + public int calculateFat(LocalDateTime startTime) { + totalFat = 0; + List fats = data.getTotalFat(startTime); + for (int fat : fats) { + totalFat += fat; } - return fat; + return totalFat; } /** @@ -204,12 +222,13 @@ public int calculateFat(FoodList foodList, LocalDateTime startTime) { * @return the value of total calorie of food items with time after * startTime in foodList. */ - public int calculateFat(FoodList foodList, LocalDateTime startTime, LocalDateTime endTime) { - int fat = 0; - for (int i = 0; i < foodList.getFoodsInDateTimeRange(startTime, endTime).size(); i++) { - fat += foodList.getFoodsInDateTimeRange(startTime, endTime).get(i).getFat(); + public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { + totalFat = 0; + List fats = data.getTotalFat(startTime, endTime); + for (int fat : fats) { + totalFat += fat; } - return fat; + return totalFat; } /** diff --git a/src/main/java/seedu/dietbook/calculator/CalculatorData.java b/src/main/java/seedu/dietbook/calculator/CalculatorData.java new file mode 100644 index 0000000000..5af62efedb --- /dev/null +++ b/src/main/java/seedu/dietbook/calculator/CalculatorData.java @@ -0,0 +1,133 @@ +package seedu.dietbook.calculator; + +import seedu.dietbook.food.Food; +import seedu.dietbook.list.FoodList; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +public class CalculatorData { + FoodList list; + List foods = null; + + public CalculatorData() { + this.list = new FoodList(); + } + + public CalculatorData(FoodList foodList) { + this.list = foodList; + } + + public void inputData(FoodList foodList) { + this.list = foodList; + } + + public List getTotalCalorie() { + this.foods = list.getFoods(); + List calories = new ArrayList<>(); + for (Food food : foods) { + calories.add(food.getCalorie()); + } + return calories; + } + + public List getTotalCalorie(LocalDateTime startTime) { + this.foods = list.getFoodsAfterDateTime(startTime); + List calories = new ArrayList<>(); + for (Food food : foods) { + calories.add(food.getCalorie()); + } + return calories; + } + + public List getTotalCalorie(LocalDateTime startTime, LocalDateTime endTime) { + this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + List calories = new ArrayList<>(); + for (Food food : foods) { + calories.add(food.getCalorie()); + } + return calories; + } + + public List getTotalCarb() { + this.foods = list.getFoods(); + List carbs = new ArrayList<>(); + for (Food food : foods) { + carbs.add(food.getCarbohydrate()); + } + return carbs; + } + + public List getTotalCarb(LocalDateTime startTime) { + this.foods = list.getFoodsAfterDateTime(startTime); + List carbs = new ArrayList<>(); + for (Food food : foods) { + carbs.add(food.getCalorie()); + } + return carbs; + } + + public List getTotalCarb(LocalDateTime startTime, LocalDateTime endTime) { + this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + List carbs = new ArrayList<>(); + for (Food food : foods) { + carbs.add(food.getCarbohydrate()); + } + return carbs; + } + + public List getTotalProtein() { + this.foods = list.getFoods(); + List proteins = new ArrayList<>(); + for (Food food : foods) { + proteins.add(food.getProtein()); + } + return proteins; + } + + public List getTotalProtein(LocalDateTime startTime) { + this.foods = list.getFoodsAfterDateTime(startTime); + List proteins = new ArrayList<>(); + for (Food food : foods) { + proteins.add(food.getProtein()); + } + return proteins; + } + + public List getTotalProtein(LocalDateTime startTime, LocalDateTime endTime) { + this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + List proteins = new ArrayList<>(); + for (Food food : foods) { + proteins.add(food.getProtein()); + } + return proteins; + } + + public List getTotalFat() { + this.foods = list.getFoods(); + List fats = new ArrayList<>(); + for (Food food : foods) { + fats.add(food.getFat()); + } + return fats; + } + + public List getTotalFat(LocalDateTime startTime) { + this.foods = list.getFoodsAfterDateTime(startTime); + List fats = new ArrayList<>(); + for (Food food : foods) { + fats.add(food.getFat()); + } + return fats; + } + + public List getTotalFat(LocalDateTime startTime, LocalDateTime endTime) { + this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + List fats = new ArrayList<>(); + for (Food food : foods) { + fats.add(food.getFat()); + } + return fats; + } +} diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index 6eb01e27b2..80a55b8f84 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -46,19 +46,19 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); + calorie = manager.getCalculator().calculateCalorie(startTime); + carb = manager.getCalculator().calculateCarb(startTime); + protein = manager.getCalculator().calculateProtein(startTime); + fat = manager.getCalculator().calculateFat(startTime); ui.printAllIntake(calorie, carb, protein, fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(startTime, endTime); + carb = manager.getCalculator().calculateCarb(startTime, endTime); + protein = manager.getCalculator().calculateProtein(startTime, endTime); + fat = manager.getCalculator().calculateFat(startTime, endTime); ui.printAllIntake(calorie, carb, protein, fat, startTime, endTime); } break; @@ -68,13 +68,13 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); + calorie = manager.getCalculator().calculateCalorie(startTime); ui.printCalorieIntake(calorie, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(startTime, endTime); ui.printCalorieIntake(calorie, startTime, endTime); } break; @@ -84,13 +84,13 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); + carb = manager.getCalculator().calculateCarb(startTime); ui.printCarbIntake(carb, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); + carb = manager.getCalculator().calculateCarb(startTime, endTime); ui.printCarbIntake(carb, startTime, endTime); } break; @@ -100,13 +100,13 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); + protein = manager.getCalculator().calculateProtein(startTime); ui.printProteinIntake(protein, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); + protein = manager.getCalculator().calculateProtein(startTime, endTime); ui.printProteinIntake(protein, startTime, endTime); } break; @@ -116,13 +116,13 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); + fat = manager.getCalculator().calculateFat(startTime); ui.printFatIntake(fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); + fat = manager.getCalculator().calculateFat(startTime, endTime); ui.printFatIntake(fat, startTime, endTime); } } diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index d2cd6e7b10..75f6b3b7c5 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -2,55 +2,57 @@ import org.junit.jupiter.api.Test; import seedu.dietbook.food.Food; +import seedu.dietbook.list.FoodList; import seedu.dietbook.person.ActivityLevel; import seedu.dietbook.person.Gender; import seedu.dietbook.person.Person; -import java.util.ArrayList; - import static org.junit.jupiter.api.Assertions.assertEquals; class CalculatorTest { - - @Test void calculateCalorie_foodListOfThreeItems_sumOfCalorie() { - ArrayList foodList = new ArrayList<>(); - foodList.add(new Food("chicken rice", 666, 55, 30, 0)); - foodList.add(new Food("pancake", 150, 16, 0, 0)); - foodList.add(new Food("bao", 290, 0, 16, 0)); - Calculator calculator = new Calculator(foodList); + FoodList foodList = new FoodList(); + foodList.addFood(1, new Food("chicken rice", 666, 55, 30, 0)); + foodList.addFood(1, new Food("pancake", 150, 16, 0, 0)); + foodList.addFood(1, new Food("bao", 290, 0, 16, 0)); + CalculatorData data = new CalculatorData(); + data.inputData(foodList); + Calculator calculator = new Calculator(data); assertEquals(666 + 150 + 290, calculator.calculateCalorie()); } @Test void calculateCarb_foodListOfThreeItems_sumOfCarb() { - ArrayList foodList = new ArrayList<>(); - foodList.add(new Food("chicken rice", 666, 55, 30, 0)); - foodList.add(new Food("pancake", 150, 16, 0, 0)); - foodList.add(new Food("bao", 290, 0, 16, 0)); - Calculator calculator = new Calculator(foodList); + FoodList foodList = new FoodList(); + foodList.addFood(1, new Food("chicken rice", 666, 55, 30, 0)); + foodList.addFood(1, new Food("pancake", 150, 16, 0, 0)); + foodList.addFood(1, new Food("bao", 290, 0, 16, 0)); + CalculatorData data = new CalculatorData(foodList); + Calculator calculator = new Calculator(data); assertEquals(55 + 16, calculator.calculateCarb()); } @Test void calculateProtein_foodListOfThreeItems_sumOfProtein() { - ArrayList foodList = new ArrayList<>(); - foodList.add(new Food("chicken rice", 666, 55, 30, 0)); - foodList.add(new Food("pancake", 150, 16, 0, 0)); - foodList.add(new Food("bao", 290, 0, 16, 0)); - Calculator calculator = new Calculator(foodList); + FoodList foodList = new FoodList(); + foodList.addFood(1, new Food("chicken rice", 666, 55, 30, 0)); + foodList.addFood(1, new Food("pancake", 150, 16, 0, 0)); + foodList.addFood(1, new Food("bao", 290, 0, 16, 0)); + CalculatorData data = new CalculatorData(foodList); + Calculator calculator = new Calculator(data); assertEquals(30 + 16, calculator.calculateProtein()); } @Test void calculateFat_foodListOfThreeItems_sumOfFat() { - ArrayList foodList = new ArrayList<>(); - foodList.add(new Food("chicken rice", 666, 55, 30, 0)); - foodList.add(new Food("pancake", 150, 16, 0, 0)); - foodList.add(new Food("bao", 290, 0, 16, 0)); - Calculator calculator = new Calculator(foodList); + FoodList foodList = new FoodList(); + foodList.addFood(1, new Food("chicken rice", 666, 55, 30, 0)); + foodList.addFood(1, new Food("pancake", 150, 16, 0, 0)); + foodList.addFood(1, new Food("bao", 290, 0, 16, 0)); + CalculatorData data = new CalculatorData(foodList); + Calculator calculator = new Calculator(data); assertEquals(0, calculator.calculateFat()); } @@ -58,7 +60,7 @@ void calculateFat_foodListOfThreeItems_sumOfFat() { void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, ActivityLevel.LOW); Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, ActivityLevel.MEDIUM); - Calculator calculator = new Calculator(new ArrayList()); + Calculator calculator = new Calculator(new CalculatorData()); assertEquals(2728, calculator.calculateRecomendation(harry)); assertEquals(1752, calculator.calculateRecomendation(erica)); } From 6e1ceaa03898e50a8d7baf1cd8510d1d39341bf1 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sat, 31 Oct 2020 23:53:29 +0800 Subject: [PATCH 265/374] added update method in Calculator class. --- src/main/java/seedu/dietbook/calculator/Calculator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 5d77a7c584..2ac83a419c 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -15,6 +15,10 @@ public class Calculator { private int totalProtein = 0; private int totalFat = 0; CalculatorData data = null; + + public Calculator() { + } + /** * Construct a calculator taking in a foodList. Add up calories, * carbs, protein, and fats in each food item. @@ -27,6 +31,10 @@ public Calculator(CalculatorData data) { this.data = data; } + public void update(CalculatorData data) { + this.data = data; + } + /** * Returns an int type variable containing the value of total calorie. * From f5491379275c1055f30865ec61600b61c0e259eb Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 1 Nov 2020 00:01:13 +0800 Subject: [PATCH 266/374] added the setCalculator method --- src/main/java/seedu/dietbook/Manager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 14622a9278..0d738fe0a1 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -87,6 +87,11 @@ public Calculator getCalculator() { return this.calculator; } + public void setCalculator() { + this.data.inputData(foodList); + this.calculator.update(this.data); + } + public DataBase getDataBase() { return this.dataBase; } From 65019bc6d199ca6acedb28bfbd46061cf36c1552 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 1 Nov 2020 00:15:17 +0800 Subject: [PATCH 267/374] Added the initialization of data. --- src/main/java/seedu/dietbook/Manager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index 0d738fe0a1..8ced3c12a4 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -39,7 +39,7 @@ public class Manager { private String name; private int commandCount = 1; private DataBase dataBase; - private CalculatorData data; + private CalculatorData data = new CalculatorData(); private Calculator calculator; public static final String COMMAND_ADD = "add"; From 6fe80a694dcf431741e86c7ed6c869c11647048b Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 2 Nov 2020 16:06:00 +0800 Subject: [PATCH 268/374] add dated string representation - The datetimes of the entries in FoodList can now be displayed to the user. - Filtering the list by datetime is now start and end inclusive. --- .../seedu/dietbook/list/DatedFoodEntry.java | 11 +++ .../java/seedu/dietbook/list/FoodList.java | 69 ++++++++++++------- .../seedu/dietbook/list/FoodListManager.java | 37 +++++++--- .../seedu/dietbook/list/FoodListTest.java | 35 +++++++++- 4 files changed, 117 insertions(+), 35 deletions(-) diff --git a/src/main/java/seedu/dietbook/list/DatedFoodEntry.java b/src/main/java/seedu/dietbook/list/DatedFoodEntry.java index 484449ae52..e2d524347b 100644 --- a/src/main/java/seedu/dietbook/list/DatedFoodEntry.java +++ b/src/main/java/seedu/dietbook/list/DatedFoodEntry.java @@ -2,10 +2,12 @@ import seedu.dietbook.food.Food; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; public class DatedFoodEntry extends FoodEntry implements Comparable { protected final LocalDateTime dateTime; + public static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("dd MMM yyyy HHmm"); /** * Default constructor method. @@ -56,6 +58,15 @@ protected LocalDateTime getDateTime() { return dateTime; } + /** + * To String representation of a dated food entry that also contains a date. + * Do not want to overwrite super method so that option to print with and without date is possible. + * @return string rep of entry in the form of (entry details) [datetime] + */ + public String toDatedString() { + return String.format("%s [%s]", super.toString(), dateTime.format(DATE_TIME_FORMAT)); + } + @Override public int compareTo(DatedFoodEntry other) { return dateTime.compareTo(other.getDateTime()); diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index 5e38fa46fc..9d75762c73 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -53,19 +53,6 @@ public String addFood(int portionSize, String name, int calorie, } - /** - * Food database search functionality support. - * Not expected to function. Added for completeness. - * Currently just throws a not found exception when called in this manner. - * @param portionSize integer to designate number of servings - * @param name food object to be added - * @return string representation of entry added - * @throws FoodNotFoundException custom exception to indicate search for food in database failed. - */ - public String addFood(int portionSize, String name) throws FoodNotFoundException { - throw new FoodNotFoundException(); - } - /** * Add add method for backlogged entries. @@ -121,14 +108,14 @@ public boolean clear() { * @return Arraylist of ordered Food objects in Foodlist's foodEntries. */ public List getFoods() { - return FoodListManager.listToFoods(foodEntries); + return FoodListManager.convertListToFoods(foodEntries); } /** * Obtain list of food objects in FoodList, scaled to portion size. */ public List getPortionedFoods() { - return FoodListManager.listToPortionedFoods(foodEntries); + return FoodListManager.convertListToPortionedFoods(foodEntries); } /** @@ -136,7 +123,7 @@ public List getPortionedFoods() { */ public List getFoodsAfterDateTime(LocalDateTime dateTime) { List entriesAfterDateTime = FoodListManager.filterListByDate(foodEntries, dateTime); - return FoodListManager.listToFoods(entriesAfterDateTime); + return FoodListManager.convertListToFoods(entriesAfterDateTime); } /** @@ -144,7 +131,7 @@ public List getFoodsAfterDateTime(LocalDateTime dateTime) { */ public List getPortionedFoodsAfterDateTime(LocalDateTime dateTime) { List entriesAfterDateTime = FoodListManager.filterListByDate(foodEntries, dateTime); - return FoodListManager.listToFoods(entriesAfterDateTime); + return FoodListManager.convertListToFoods(entriesAfterDateTime); } /** @@ -152,7 +139,7 @@ public List getPortionedFoodsAfterDateTime(LocalDateTime dateTime) { */ public List getFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); - return FoodListManager.listToFoods(entriesInRange); + return FoodListManager.convertListToFoods(entriesInRange); } /** @@ -160,7 +147,7 @@ public List getFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end */ public List getPortionedFoodsInDateTimeRange(LocalDateTime start, LocalDateTime end) { List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); - return FoodListManager.listToPortionedFoods(entriesInRange); + return FoodListManager.convertListToPortionedFoods(entriesInRange); } /** @@ -168,7 +155,7 @@ public List getPortionedFoodsInDateTimeRange(LocalDateTime start, LocalDat * (For storage purposes) */ public List getPortionSizes() { - return FoodListManager.listToPortionSizes(foodEntries); + return FoodListManager.convertListToPortionSizes(foodEntries); } @@ -177,12 +164,12 @@ public List getPortionSizes() { * (For storage purposes) */ public List getDateTimes() { - return FoodListManager.listToLocalDateTimes(foodEntries); + return FoodListManager.convertListToLocalDateTimes(foodEntries); } @Override public String toString() { - return FoodListManager.listToString(foodEntries); + return FoodListManager.convertListToString(foodEntries); } /** @@ -192,18 +179,48 @@ public String toString() { */ public String getAfterDateTimeToString(LocalDateTime dateTime) { List entriesAfterDateTime = FoodListManager.filterListByDate(foodEntries, dateTime); - return FoodListManager.listToString(entriesAfterDateTime); + return FoodListManager.convertListToString(entriesAfterDateTime); } /** * Returns toString representation of the segmented list based on DateTime (within a range of 2 datetimes). - * @param start start DateTime. - * @param end end DateTime. + * @param start lower bound of datetime (inclusive) + * @param end upper bound of datetime (inclusive) * @return string representation of FoodList */ public String getInDateTimeRangeToString(LocalDateTime start, LocalDateTime end) { List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); - return FoodListManager.listToString(entriesInRange); + return FoodListManager.convertListToString(entriesInRange); + } + + /** + * Alternative toString method that also displays all the associated dates with each food entry. + */ + public String toDatedString() { + return FoodListManager.convertListToDatedString(foodEntries); + } + + /** + * Alternative toString method that provides associated dates with each food entry. + * Only returns entries within the bounds of a start and end date. + * @param start lower bound of datetime (inclusive) + * @param end upper bound of datetime (inclusive) + * @return string representation of FoodList with datetimes. + */ + public String toDatedString(LocalDateTime start, LocalDateTime end) { + List entriesInRange = FoodListManager.filterListByDate(foodEntries, start, end); + return FoodListManager.convertListToDatedString(entriesInRange); + } + + /** + * Alternative toString method that provides associated dates with each food entry. + * Only returns entries within the bounds of the start datetime and MAX. + * @param start lower bound of datetime (inclusive). + * @return string representation of FoodList with datetimes. + */ + public String toDatedString(LocalDateTime start) { + List entriesInRange = FoodListManager.filterListByDate(foodEntries, start); + return FoodListManager.convertListToDatedString(entriesInRange); } } diff --git a/src/main/java/seedu/dietbook/list/FoodListManager.java b/src/main/java/seedu/dietbook/list/FoodListManager.java index 8ef1efb5c0..e706e4512e 100644 --- a/src/main/java/seedu/dietbook/list/FoodListManager.java +++ b/src/main/java/seedu/dietbook/list/FoodListManager.java @@ -18,7 +18,7 @@ public class FoodListManager { * Internal helper method to convert the items in the arraylist into enumed strings. * Primarily used to obtain String representations of the entire list. */ - protected static String listToString(List list) { + protected static String convertListToString(List list) { String listString = ""; for (int i = 1; i <= list.size(); i++) { @@ -29,6 +29,20 @@ protected static String listToString(List list) { return listString; } + protected static String convertListToDatedString(List list) { + String datedListString = ""; + Function function = x -> { + assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; + DatedFoodEntry datedEntry = (DatedFoodEntry) x; + return datedEntry.toDatedString(); + }; + List strings = ListFunction.applyFunctionToList(list, function); + for (int i = 1; i <= strings.size(); i++) { + datedListString += String.format(" %d. %s\n", i, strings.get(i - 1)); + } + return datedListString; + } + protected static FoodEntry deleteEntry(List list, int index) throws IndexOutOfBoundsException { assert (index > 0) : "Invalid index (negative/zero) was given."; int indexToDelete = index - 1; @@ -44,7 +58,7 @@ protected static FoodEntry deleteEntry(List list, int index) throws I * @param list The foodList arrayList * @return List of foodEntries in their String rep. */ - protected static List listToStrings(List list) { + protected static List convertListToStrings(List list) { Function function = x -> x.toString(); return ListFunction.applyFunctionToList(list, function); } @@ -54,7 +68,7 @@ protected static List listToStrings(List list) { * @param list list of foodEntries * @return list of Food objects. */ - protected static List listToFoods(List list) { + protected static List convertListToFoods(List list) { Function function = x -> x.getFood(); return ListFunction.applyFunctionToList(list, function); } @@ -65,7 +79,7 @@ protected static List listToFoods(List list) { * @param list list of FoodEntries * @return list of Food objects */ - protected static List listToPortionedFoods(List list) { + protected static List convertListToPortionedFoods(List list) { Function function = x -> { Food baseFood = x.getFood(); /** Explicitly getting return type of getPortionSize() is avoided. @@ -84,7 +98,7 @@ protected static List listToPortionedFoods(List list) { /** * Obtain the LocalDateTime objects associated with each entry. */ - protected static List listToLocalDateTimes(List list) { + protected static List convertListToLocalDateTimes(List list) { Function function = x -> { assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; DatedFoodEntry datedEntry = (DatedFoodEntry) x; @@ -96,7 +110,7 @@ protected static List listToLocalDateTimes(List list) /** * Obtain the portion sizes associated with each food entry. */ - protected static List listToPortionSizes(List list) { + protected static List convertListToPortionSizes(List list) { Function function = x -> x.getPortionSize(); return ListFunction.applyFunctionToList(list, function); } @@ -109,7 +123,7 @@ protected static List filterListByDate(List list, LocalDat Predicate predicate = x -> { assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; DatedFoodEntry datedEntry = (DatedFoodEntry) x; - return dateTime.isBefore(datedEntry.getDateTime()); + return dateTime.isBefore(datedEntry.getDateTime()) || dateTime.isEqual(datedEntry.getDateTime()); }; return ListFunction.filterList(list, predicate); } @@ -118,14 +132,21 @@ protected static List filterListByDate(List list, LocalDat * Obtain only food entries within a specified range of dateTimes. */ protected static List filterListByDate(List list, LocalDateTime start, LocalDateTime end) { + assert (start.isBefore(end)) : "End time should be later than start time."; Predicate predicate = x -> { assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; DatedFoodEntry datedEntry = (DatedFoodEntry) x; - return start.isBefore(datedEntry.getDateTime()) && end.isAfter(datedEntry.getDateTime()); + LocalDateTime entryDateTime = datedEntry.getDateTime(); + return ((start.isBefore(entryDateTime) || start.isEqual(entryDateTime)) + && (end.isAfter(datedEntry.getDateTime()) || end.isEqual(entryDateTime))); }; return ListFunction.filterList(list, predicate); } + /** + * Sort a list of entries by date. + */ + } diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 60a62dd4f7..2d825d5235 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -1,8 +1,10 @@ package seedu.dietbook.list; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDateTime; @@ -13,11 +15,15 @@ class FoodListTest { private FoodList list; + private FoodList datedList; private Food food; + private LocalDateTime start; + private LocalDateTime end; @BeforeEach protected void setUp() { this.list = new FoodList(); + this.datedList = new FoodList(); Food food = new Food("Kobe Beef", 480,50,40,30); this.food = food; @@ -25,6 +31,12 @@ protected void setUp() { list.addFood(3, food); list.addFood(2, "Sashimi", 100, 0, 30, 10); + this.start = LocalDateTime.of(2000, 6, 6, 12, 15); + this.end = LocalDateTime.of(2000, 6, 10, 0, 0); + + datedList.addFoodAtDateTime(2, food, start); + datedList.addFoodAtDateTime(3, food, end); + } /** @@ -33,6 +45,7 @@ protected void setUp() { * Essentially 2 tests in 1. */ @Test + @DisplayName("Portion scaling test") void foodPortionScaling_standardList_scaledFoodList() { FoodList testList = new FoodList(); @@ -45,9 +58,11 @@ void foodPortionScaling_standardList_scaledFoodList() { @Test void deleteItemTest() { - Food food = new Food("Kobe Beef", 480,50,40,30); + // Positive Test: FoodEntry entry = new FoodEntry(3, food); assertEquals(entry.toString(), list.delete(1)); + // Negative Tests: + assertThrows(IndexOutOfBoundsException.class, () -> list.delete(5)); } @Test @@ -107,6 +122,7 @@ void dateFilterRangeTest() { } @Test + @DisplayName("Getter methods test") void getFoodEntryProperties_standardList_FoodEntryProperties() { assertTrue(list.getDateTimes().get(0) instanceof LocalDateTime); assertEquals(list.getPortionSizes().get(0), 3); @@ -116,8 +132,25 @@ void getFoodEntryProperties_standardList_FoodEntryProperties() { @Test void addAndRetrieveEntryAtDateTime_entryAddedAtDateTimeMax_entryAtDateTimeMax() { list.addFoodAtDateTime(2, food, LocalDateTime.MAX); + assertEquals(food, list.getFoodsAfterDateTime(LocalDateTime.now()).get(0)); + // Further test that boundary is inclusive: + assertEquals(food, list.getFoodsAfterDateTime(LocalDateTime.MAX).get(0)); + } + + @Test + @DisplayName("Dated String representation test") + void printDatedList_datedList_matchingStrings() { + assertTrue(! datedList.toString().equals(datedList.toDatedString())); + assertTrue(datedList.toDatedString().stripTrailing().endsWith(String.format("[%s]", + end.format(DatedFoodEntry.DATE_TIME_FORMAT)))); + // Bound inclusivity should render the following true: + assertEquals(datedList.toDatedString(), datedList.toDatedString(start)); + assertEquals(datedList.toDatedString(), datedList.toDatedString(start, end)); + assertTrue(datedList.toDatedString(end).stripTrailing().endsWith(String.format("[%s]", + end.format(DatedFoodEntry.DATE_TIME_FORMAT)))); } + } From 25673fdc5793465dcb2f27be896586c660857d62 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 2 Nov 2020 17:46:59 +0800 Subject: [PATCH 269/374] FoodListTest improvements --- .../seedu/dietbook/list/FoodListTest.java | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 2d825d5235..3f2a6ce907 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -66,12 +66,11 @@ void deleteItemTest() { } @Test - void dateComparisonTest() { + @DisplayName("Dated entry comparison test") + void compareDatedEntries_datedEntriesAtDiffDate_compareToResult() { DatedFoodEntry entry = new DatedFoodEntry(2, food); DatedFoodEntry pastEntry = new DatedFoodEntry(2, food, LocalDateTime.MIN); - assertTrue(entry.compareTo(pastEntry) > 0); - } @Test @@ -104,20 +103,11 @@ void dateFilterRangeTest() { assertEquals(list.getPortionedFoods().toString(), list.getPortionedFoodsInDateTimeRange(LocalDateTime.MIN, LocalDateTime.MAX).toString()); - assertEquals(list.toString(), list.getInDateTimeRangeToString(LocalDateTime.MIN, LocalDateTime.MAX)); + assertEquals(datedList.toString(), + datedList.getInDateTimeRangeToString(start, end)); LocalDateTime timeNow = LocalDateTime.now(); - - if (! LocalDateTime.now().isAfter(timeNow)) { // Execution is too fast that now() = timeNow. - try { - TimeUnit.SECONDS.sleep(1); - timeNow = LocalDateTime.now(); - } catch (InterruptedException e) { - System.out.println("Unexpected Interruption"); - } - } - - assertTrue(list.getFoodsInDateTimeRange(timeNow, LocalDateTime.MAX).size() == 0); + assertTrue(datedList.getFoodsInDateTimeRange(timeNow, LocalDateTime.MAX).size() == 0); } @@ -130,6 +120,7 @@ void getFoodEntryProperties_standardList_FoodEntryProperties() { } @Test + @DisplayName("Adding backlog entry test") void addAndRetrieveEntryAtDateTime_entryAddedAtDateTimeMax_entryAtDateTimeMax() { list.addFoodAtDateTime(2, food, LocalDateTime.MAX); From 66c579131eaebb3427f8ea2b53f46190f0712145 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Mon, 2 Nov 2020 18:00:37 +0800 Subject: [PATCH 270/374] Remove unused imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup of master branch: Removed imports that are unused or duplicated — likely due to merge conflicts. --- src/main/java/seedu/dietbook/DietBook.java | 1 - src/main/java/seedu/dietbook/Manager.java | 3 +-- src/main/java/seedu/dietbook/checker/InputChecker.java | 1 - src/main/java/seedu/dietbook/database/DataBase.java | 4 ---- src/main/java/seedu/dietbook/parser/Parser.java | 2 -- src/main/java/seedu/dietbook/saveload/FileLoader.java | 1 + src/test/java/seedu/dietbook/database/DataBaseTest.java | 1 - src/test/java/seedu/dietbook/food/FoodTest.java | 5 ----- 8 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index e11065a3c0..4866c58ace 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -16,7 +16,6 @@ * @author tikimonarch */ -import java.io.File; import java.io.FileNotFoundException; public class DietBook { diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index dacedc03f1..053322e243 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -23,7 +23,6 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; -import java.util.ArrayList; /** * Manager class of the program. @@ -37,7 +36,7 @@ public class Manager { private Person person; private FoodList foodList; private String name; - private int commandCount = 1; + private int commandCount = 1; // This is currently unused private DataBase dataBase; private Calculator calculator; diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index c68e6afa71..bc52386b46 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -1,7 +1,6 @@ package seedu.dietbook.checker; import seedu.dietbook.exception.DietException; -import seedu.dietbook.parser.Parser; import java.time.LocalDateTime; diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index d671260c6c..ffa3002a28 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -3,12 +3,8 @@ import seedu.dietbook.food.Food; - -import java.io.File; -import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -import java.util.NoSuchElementException; import java.util.Scanner; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 9fbcddaa74..57f99b75a8 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -1,7 +1,5 @@ package seedu.dietbook.parser; -import seedu.dietbook.database.DataBase; -import seedu.dietbook.food.Food; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.Gender; import seedu.dietbook.person.ActivityLevel; diff --git a/src/main/java/seedu/dietbook/saveload/FileLoader.java b/src/main/java/seedu/dietbook/saveload/FileLoader.java index 9169e4df53..84939ce9b8 100644 --- a/src/main/java/seedu/dietbook/saveload/FileLoader.java +++ b/src/main/java/seedu/dietbook/saveload/FileLoader.java @@ -35,6 +35,7 @@ protected FileLoader(String folderName, String fileName) throws FileNotFoundExce System.arraycopy(line, 1, entries[j], 0, width); } } + reader.close(); } /** diff --git a/src/test/java/seedu/dietbook/database/DataBaseTest.java b/src/test/java/seedu/dietbook/database/DataBaseTest.java index d1823de021..e4a3287d53 100644 --- a/src/test/java/seedu/dietbook/database/DataBaseTest.java +++ b/src/test/java/seedu/dietbook/database/DataBaseTest.java @@ -1,6 +1,5 @@ package seedu.dietbook.database; -import java.io.FileNotFoundException; import java.util.NoSuchElementException; diff --git a/src/test/java/seedu/dietbook/food/FoodTest.java b/src/test/java/seedu/dietbook/food/FoodTest.java index 4209f84699..5e711152f0 100644 --- a/src/test/java/seedu/dietbook/food/FoodTest.java +++ b/src/test/java/seedu/dietbook/food/FoodTest.java @@ -5,11 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - class FoodTest { private Food testFood; From 7234e205f43863136b48e8096092ef4966f3a9bd Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Wed, 4 Nov 2020 19:42:14 +0800 Subject: [PATCH 271/374] Rename ActivityLevel to FitnessLevel --- src/main/java/seedu/dietbook/DietBook.java | 29 ++++---- src/main/java/seedu/dietbook/Manager.java | 12 ++-- src/main/java/seedu/dietbook/Ui.java | 19 +++--- .../seedu/dietbook/calculator/Calculator.java | 2 +- .../seedu/dietbook/checker/InputChecker.java | 16 ++--- .../seedu/dietbook/command/ExitCommand.java | 2 +- .../seedu/dietbook/command/HelpCommand.java | 1 + .../seedu/dietbook/command/InfoCommand.java | 1 + .../java/seedu/dietbook/parser/Parser.java | 66 +++++++++---------- .../{ActivityLevel.java => FitnessLevel.java} | 14 ++-- .../java/seedu/dietbook/person/Person.java | 56 ++++++++-------- .../dietbook/calculator/CalculatorTest.java | 6 +- .../seedu/dietbook/person/PersonTest.java | 36 +++++----- 13 files changed, 131 insertions(+), 129 deletions(-) rename src/main/java/seedu/dietbook/person/{ActivityLevel.java => FitnessLevel.java} (71%) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index e11065a3c0..110165db91 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -4,11 +4,12 @@ import seedu.dietbook.list.FoodList; import seedu.dietbook.exception.DietException; import seedu.dietbook.command.Command; -import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Gender; import seedu.dietbook.saveload.FoodSaveLoadManager; import seedu.dietbook.saveload.PersonSaveLoadManager; +//@@author tikimonarch /** * Main class of the program. * The DietBook program is an application which can store, display and check your daily dietary intake. @@ -47,18 +48,18 @@ public DietBook() { public void loadPerson() { try { loadPerson.load("resources/UserInfo.txt"); - ActivityLevel actLvl = ActivityLevel.NONE; - int actLvlInt = loadPerson.getActivityLevel(); - if (actLvlInt == 1) { - actLvl = ActivityLevel.NONE; - } else if (actLvlInt == 2) { - actLvl = ActivityLevel.LOW; - } else if (actLvlInt == 3) { - actLvl = ActivityLevel.MEDIUM; - } else if (actLvlInt == 4) { - actLvl = ActivityLevel.HIGH; - } else if (actLvlInt == 5) { - actLvl = ActivityLevel.EXTREME; + FitnessLevel fitLvl = FitnessLevel.NONE; + int fitLvlInt = loadPerson.getActivityLevel(); + if (fitLvlInt == 1) { + fitLvl = FitnessLevel.NONE; + } else if (fitLvlInt == 2) { + fitLvl = FitnessLevel.LOW; + } else if (fitLvlInt == 3) { + fitLvl = FitnessLevel.MEDIUM; + } else if (fitLvlInt == 4) { + fitLvl = FitnessLevel.HIGH; + } else if (fitLvlInt == 5) { + fitLvl = FitnessLevel.EXTREME; } Gender gender; @@ -72,7 +73,7 @@ public void loadPerson() { } manager.getPerson().setAll(loadPerson.getName(), gender, loadPerson.getAge(), loadPerson.getHeight(), loadPerson.getOriginalWeight(), loadPerson.getCurrentWeight(), - loadPerson.getTargetWeight(), actLvl); + loadPerson.getTargetWeight(), fitLvl); } catch (FileNotFoundException e) { ui.printErrorMessage("Person data file not found!"); diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index dacedc03f1..e44fe2ad08 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -15,7 +15,7 @@ import seedu.dietbook.command.RecommendCommand; import seedu.dietbook.command.UserinfoCommand; import seedu.dietbook.list.FoodList; -import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Person; import seedu.dietbook.calculator.Calculator; import seedu.dietbook.database.DataBase; @@ -23,8 +23,7 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; -import java.util.ArrayList; - +//@@author tikimonarch /** * Manager class of the program. * The manager class takes in the checked and processed input and carry out the command specified. @@ -32,7 +31,6 @@ * * @author tikimonarch */ - public class Manager { private Person person; private FoodList foodList; @@ -58,7 +56,7 @@ public class Manager { public Manager(FoodList foodlist, DataBase dataBase) { this.name = "John Doe"; this.person = new Person(this.name, Gender.MALE, 1,1,1,1, - 1, ActivityLevel.LOW); + 1, FitnessLevel.LOW); this.foodList = foodlist; this.dataBase = dataBase; this.calculator = new Calculator(foodList.getFoods()); @@ -77,8 +75,8 @@ public Person getPerson() { } public void setPerson(String name, Gender gender, int age,int height,int orgWeight, int currWeight, - int targWeight, ActivityLevel actLvl) { - this.person.setAll(name, gender, age, height, orgWeight, currWeight, targWeight, actLvl); + int targWeight, FitnessLevel fitLvl) { + this.person.setAll(name, gender, age, height, orgWeight, currWeight, targWeight, fitLvl); } public Calculator getCalculator() { diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java index 49c2282c52..505cc9ec51 100644 --- a/src/main/java/seedu/dietbook/Ui.java +++ b/src/main/java/seedu/dietbook/Ui.java @@ -1,6 +1,7 @@ package seedu.dietbook; -import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.exception.DietException; +import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Gender; import java.time.LocalDateTime; @@ -74,16 +75,16 @@ public void printAskForUserInfoMessage(String name) { + "- Your current weight in kg." + LINE_SEPARATOR + "- Your target weight in kg, or your current weight if that is also your target weight." + LINE_SEPARATOR - + "- Your activity level, represented by a number from 1 to 5." + LINE_SEPARATOR - + " 1 = " + ActivityLevel.NONE.getDescription() + LINE_SEPARATOR - + " 2 = " + ActivityLevel.LOW.getDescription() + LINE_SEPARATOR - + " 3 = " + ActivityLevel.MEDIUM.getDescription() + LINE_SEPARATOR - + " 4 = " + ActivityLevel.HIGH.getDescription() + LINE_SEPARATOR - + " 5 = " + ActivityLevel.EXTREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + + "- Your fitness level, represented by a number from 1 to 5." + LINE_SEPARATOR + + " 1 = " + FitnessLevel.NONE.getDescription() + LINE_SEPARATOR + + " 2 = " + FitnessLevel.LOW.getDescription() + LINE_SEPARATOR + + " 3 = " + FitnessLevel.MEDIUM.getDescription() + LINE_SEPARATOR + + " 4 = " + FitnessLevel.HIGH.getDescription() + LINE_SEPARATOR + + " 5 = " + FitnessLevel.EXTREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR + "Please input your details in the following format:" + LINE_SEPARATOR + " info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT " - + "l/ACTIVITY_LEVEL" + LINE_SEPARATOR - + " Example: info g/F a/21 h/165 o/65 c/65 t/55 l/2"); + + "f/FITNESS_LEVEL" + LINE_SEPARATOR + + " Example: info g/F a/21 h/165 o/65 c/65 t/55 f/2"); } /** diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 266ed0781a..674ba14f6a 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -224,7 +224,7 @@ public int calculateRecomendation(Person person) { double requirement = 0; int recomendation; double activityScore = 0; - switch (person.getActivityLevel()) { + switch (person.getFitnessLevel()) { case NONE: activityScore = 1; break; diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index c68e6afa71..0fa649bc72 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -5,13 +5,13 @@ import java.time.LocalDateTime; +//@@author tikimonarch /** * InputChecker class of the program. * This class checks the validity of the user input and throws an exception if input is not as intended/expected. * * @author tikimonarch */ - public class InputChecker { /** * The value limits are based on current limits observed in th world. @@ -20,12 +20,12 @@ public class InputChecker { public static final int FOOD_CAP = 100000; public static final int HEIGHT_CAP = 300; public static final int WEIGHT_CAP = 500; - public static final String[] PARAM_ACTIVITY = {"1","2","3","4","5"}; + public static final String[] PARAM_FITNESS = {"1","2","3","4","5"}; public static final String[] PARAM_ADD = {"n/","x/","k/"}; public static final String[] FULL_PARAM_ADD = {"n/","x/","k/","c/","p/","f/"}; public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; public static final String[] PARAM_GENDER = {"M","F","O"}; - public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/","c/"}; + public static final String[] PARAM_INFO = {"g/","a/","h/","f/","o/","t/","c/"}; /** * Takes in user input and command to check for any expected parameters after the command. @@ -205,20 +205,20 @@ public static void checkNutrientType(String userInput) throws DietException { } /** - * Takes in user input to check if the activity level is of the expected input. + * Takes in user input to check if the fitness level is of the expected input. * * @param userInput user input. - * @throws DietException when it is not one of the expected activity level. + * @throws DietException when it is not one of the expected fitness level. */ - public static void checkActivity(String userInput) throws DietException { + public static void checkFitness(String userInput) throws DietException { boolean checkContain = false; - for (String param: PARAM_ACTIVITY) { + for (String param: PARAM_FITNESS) { if (userInput.equals(param)) { checkContain = true; } } if (!checkContain) { - throw new DietException("No such activity level!"); + throw new DietException("No such fitness level!"); } } diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java index 089f670a25..14c69eb6b9 100644 --- a/src/main/java/seedu/dietbook/command/ExitCommand.java +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -3,7 +3,7 @@ import seedu.dietbook.DietBook; import seedu.dietbook.Manager; import seedu.dietbook.Ui; -import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Gender; import seedu.dietbook.saveload.PersonSaveLoadManager; import seedu.dietbook.saveload.FoodSaveLoadManager; diff --git a/src/main/java/seedu/dietbook/command/HelpCommand.java b/src/main/java/seedu/dietbook/command/HelpCommand.java index 75b081d4bd..cb0e26bbc6 100644 --- a/src/main/java/seedu/dietbook/command/HelpCommand.java +++ b/src/main/java/seedu/dietbook/command/HelpCommand.java @@ -4,6 +4,7 @@ import seedu.dietbook.Ui; import seedu.dietbook.exception.DietException; +//@@author tikimonarch public class HelpCommand extends Command { @Override public void execute(Manager manager, Ui ui) throws DietException { diff --git a/src/main/java/seedu/dietbook/command/InfoCommand.java b/src/main/java/seedu/dietbook/command/InfoCommand.java index ab6daae046..d30d6e496c 100644 --- a/src/main/java/seedu/dietbook/command/InfoCommand.java +++ b/src/main/java/seedu/dietbook/command/InfoCommand.java @@ -5,6 +5,7 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; +//@@author tikimonarch public class InfoCommand extends Command { String userInput; diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 9fbcddaa74..92aeb793f0 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -4,28 +4,28 @@ import seedu.dietbook.food.Food; import seedu.dietbook.list.FoodList; import seedu.dietbook.person.Gender; -import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.exception.DietException; import seedu.dietbook.Manager; import seedu.dietbook.checker.InputChecker; import java.time.LocalDateTime; +//@@author tikimonarch /** * Parser class of the program. * The parser class takes in user input and process it into command data that manager can use. * * @author tikimonarch */ - public class Parser { public static final String COMMAND_ADD = "add"; public static final String COMMAND_CALCULATE = "calculate"; public static final String COMMAND_EDIT_INFO = "editinfo"; public static final String COMMAND_INFO = "info"; public static final String COMMAND_NAME = "name"; - public static final String[] PARAM_INFO = {"g/","a/","h/","l/","o/","t/","c/"}; - public static final String[] PARAM_EDIT_INFO = {"n/","g/","a/","h/","l/","o/","t/","c/"}; + public static final String[] PARAM_INFO = {"g/","a/","h/","f/","o/","t/","c/"}; + public static final String[] PARAM_EDIT_INFO = {"n/","g/","a/","h/","f/","o/","t/","c/"}; /** * Returns the command of a user input. @@ -113,7 +113,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { - trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); + trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); } else if (trimmedParam.split("\\s+").length == 2) { trimmedParam = trimmedParam.split("\\s+")[0]; } @@ -168,7 +168,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws */ public static void executeProcessedInfo(String userInput, Manager manager) throws DietException { Gender gender = Gender.MALE; - ActivityLevel actLvl = ActivityLevel.NONE; + FitnessLevel fitLvl = FitnessLevel.NONE; int age = 0; int height = 0; int orgWeight = 0; @@ -182,7 +182,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw InputChecker.checkEmptyOption(processedParam); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { - trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); + trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); } switch (param) { case "g/": @@ -215,23 +215,23 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw InputChecker.checkWeightLimit(tarWeight); break; default: - String processActLvl = trimmedParam; - InputChecker.checkActivity(processActLvl); - if (processActLvl.equals("1")) { - actLvl = ActivityLevel.NONE; - } else if (processActLvl.equals("2")) { - actLvl = ActivityLevel.LOW; - } else if (processActLvl.equals("3")) { - actLvl = ActivityLevel.MEDIUM; - } else if (processActLvl.equals("4")) { - actLvl = ActivityLevel.HIGH; + String processFitLvl = trimmedParam; + InputChecker.checkFitness(processFitLvl); + if (processFitLvl.equals("1")) { + fitLvl = FitnessLevel.NONE; + } else if (processFitLvl.equals("2")) { + fitLvl = FitnessLevel.LOW; + } else if (processFitLvl.equals("3")) { + fitLvl = FitnessLevel.MEDIUM; + } else if (processFitLvl.equals("4")) { + fitLvl = FitnessLevel.HIGH; } else { - actLvl = ActivityLevel.EXTREME; + fitLvl = FitnessLevel.EXTREME; } break; } } - manager.setPerson(manager.getName(), gender, age, height, orgWeight, currWeight, tarWeight, actLvl); + manager.setPerson(manager.getName(), gender, age, height, orgWeight, currWeight, tarWeight, fitLvl); } /** @@ -244,7 +244,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw */ public static void executeEditInfo(String userInput, Manager manager) throws DietException { Gender gender; - ActivityLevel actLvl; + FitnessLevel fitLvl; String name; int age; int height; @@ -260,7 +260,7 @@ public static void executeEditInfo(String userInput, Manager manager) throws Die InputChecker.checkEmptyOption(processedParam); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { - trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 2).trim(); + trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); } switch (param) { case "g/": @@ -305,20 +305,20 @@ public static void executeEditInfo(String userInput, Manager manager) throws Die manager.getPerson().setTargetWeight(tarWeight); break; default: - String processActLvl = trimmedParam; - InputChecker.checkActivity(processActLvl); - if (processActLvl.equals("1")) { - actLvl = ActivityLevel.NONE; - } else if (processActLvl.equals("2")) { - actLvl = ActivityLevel.LOW; - } else if (processActLvl.equals("3")) { - actLvl = ActivityLevel.MEDIUM; - } else if (processActLvl.equals("4")) { - actLvl = ActivityLevel.HIGH; + String processFitLvl = trimmedParam; + InputChecker.checkFitness(processFitLvl); + if (processFitLvl.equals("1")) { + fitLvl = FitnessLevel.NONE; + } else if (processFitLvl.equals("2")) { + fitLvl = FitnessLevel.LOW; + } else if (processFitLvl.equals("3")) { + fitLvl = FitnessLevel.MEDIUM; + } else if (processFitLvl.equals("4")) { + fitLvl = FitnessLevel.HIGH; } else { - actLvl = ActivityLevel.EXTREME; + fitLvl = FitnessLevel.EXTREME; } - manager.getPerson().setActivityLevel(actLvl); + manager.getPerson().setFitnessLevel(fitLvl); break; } } diff --git a/src/main/java/seedu/dietbook/person/ActivityLevel.java b/src/main/java/seedu/dietbook/person/FitnessLevel.java similarity index 71% rename from src/main/java/seedu/dietbook/person/ActivityLevel.java rename to src/main/java/seedu/dietbook/person/FitnessLevel.java index ba71bd0a74..11934abca7 100644 --- a/src/main/java/seedu/dietbook/person/ActivityLevel.java +++ b/src/main/java/seedu/dietbook/person/FitnessLevel.java @@ -2,9 +2,9 @@ /** * Represents the physical activity level of a person or the amount of exercise a person engages in. - * An ActivityLevel has a description. + * An FitnessLevel has a description. */ -public enum ActivityLevel { +public enum FitnessLevel { NONE("You hardly engage in any exercise or have a job that requires little to no physical " + "activity."), LOW("You engage in some form of light exercise or have a job that requires some " @@ -18,18 +18,18 @@ public enum ActivityLevel { private final String description; /** - * Constructs an ActivityLevel given the description. + * Constructs an FitnessLevel given the description. * - * @param description The description of the activity level. + * @param description The description of the fitness level. */ - ActivityLevel(String description) { + FitnessLevel(String description) { this.description = description; } /** - * Returns the description of the activity level. + * Returns the description of the fitness level. * - * @return The description of the activity level. + * @return The description of the fitness level. */ public String getDescription() { return description; diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index a0e02c7e6b..481a8eb8b1 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -2,7 +2,7 @@ /** * Represents a Person. - * A Person has a name, gender, age, height, certain activity level, original and desired weight. + * A Person has a name, gender, age, height, certain fitness level, original and desired weight. */ public class Person { @@ -15,12 +15,12 @@ public class Person { /* The target weight of the person in kg */ private int targetWeight; private int age; - private ActivityLevel activityLevel; + private FitnessLevel fitnessLevel; private Gender gender; private String name; /** - * Constructs a Person with the given name, gender, age, height, activity level, original, + * Constructs a Person with the given name, gender, age, height, fitness level, original, * current and target weight. * * @param name The name of the person. @@ -30,13 +30,13 @@ public class Person { * @param originalWeight The original weight of the person when he or she first started using DietBook. * @param currentWeight The current weight of the person. * @param targetWeight The target/desired weight that the person wants to achieve. - * @param activityLevel The activity level of the person or in other words, the amount of exercise the + * @param fitnessLevel The fitness level of the person or in other words, the amount of exercise the * person engages in. */ public Person(String name, Gender gender, int age, int height, int originalWeight, - int currentWeight, int targetWeight, ActivityLevel activityLevel) { + int currentWeight, int targetWeight, FitnessLevel fitnessLevel) { performAssertionsForPerson(name, gender, age, height, originalWeight, currentWeight, - targetWeight, activityLevel); + targetWeight, fitnessLevel); this.name = name.trim(); this.gender = gender; @@ -45,7 +45,7 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh this.originalWeight = originalWeight; this.currentWeight = currentWeight; this.targetWeight = targetWeight; - this.activityLevel = activityLevel; + this.fitnessLevel = fitnessLevel; } /** @@ -59,11 +59,11 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh * using DietBook. * @param newCurrentWeight The new/revised current weight of the person. * @param newTargetWeight The new/revised target weight that the person wants to achieve. - * @param newActivityLevel The new/revised activity level of the person or in other words, the amount + * @param newFitnessLevel The new/revised fitness level of the person or in other words, the amount * of exercise the person engages in. */ public void setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, - int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) { + int newCurrentWeight, int newTargetWeight, FitnessLevel newFitnessLevel) { setName(newName); setGender(newGender); setAge(newAge); @@ -71,7 +71,7 @@ public void setAll(String newName, Gender newGender, int newAge, int newHeight, setOriginalWeight(newOriginalWeight); setCurrentWeight(newCurrentWeight); setTargetWeight(newTargetWeight); - setActivityLevel(newActivityLevel); + setFitnessLevel(newFitnessLevel); } /** @@ -208,27 +208,27 @@ public void setTargetWeight(int newTargetWeight) { } /** - * Returns the activity level of the person. + * Returns the fitness level of the person. * - * @return The activity level of the person. + * @return The fitness level of the person. */ - public ActivityLevel getActivityLevel() { - return activityLevel; + public FitnessLevel getFitnessLevel() { + return fitnessLevel; } /** - * Sets the activity level of the person to the new activity level given. + * Sets the fitness level of the person to the new fitness level given. * - * @param newActivityLevel The new/revised activity level of the person. + * @param newFitnessLevel The new/revised fitness level of the person. */ - public void setActivityLevel(ActivityLevel newActivityLevel) { - performAssertionsForActivityLevel(newActivityLevel); - activityLevel = newActivityLevel; + public void setFitnessLevel(FitnessLevel newFitnessLevel) { + performAssertionsForFitnessLevel(newFitnessLevel); + fitnessLevel = newFitnessLevel; } /** * Returns a string representation of all information related to the user. - * Information includes name, gender, age, height, original weight, target weight and activity level. + * Information includes name, gender, age, height, original weight, target weight and fitness level. * * @return A string representation of all information related to the user. */ @@ -241,7 +241,7 @@ public String toString() { + " Original weight: " + originalWeight + "kg" + System.lineSeparator() + " Current weight: " + currentWeight + "kg" + System.lineSeparator() + " Target weight: " + targetWeight + "kg" + System.lineSeparator() - + " Activity level: " + activityLevel.getDescription(); + + " Fitness level: " + fitnessLevel.getDescription(); return userInformation; } @@ -255,12 +255,12 @@ public String toString() { * @param originalWeight The original weight of the person when he or she first started using DietBook. * @param currentWeight The current weight of the person. * @param targetWeight The target/desired weight that the person wants to achieve. - * @param activityLevel The activity level of the person or in other words, the amount of exercise the + * @param fitnessLevel The fitness level of the person or in other words, the amount of exercise the * person engages in. */ private void performAssertionsForPerson(String name, Gender gender, int age, int height, int originalWeight, int currentWeight, int targetWeight, - ActivityLevel activityLevel) { + FitnessLevel fitnessLevel) { performAssertionsForNameInput(name); performAssertionsForGenderInput(gender); performAssertionsForAgeInput(age); @@ -268,17 +268,17 @@ private void performAssertionsForPerson(String name, Gender gender, int age, int performAssertionsForWeight(originalWeight, "Original weight"); performAssertionsForWeight(currentWeight, "Current weight"); performAssertionsForWeight(targetWeight, "Target weight"); - performAssertionsForActivityLevel(activityLevel); + performAssertionsForFitnessLevel(fitnessLevel); } /** - * Performs assertion on the activity level input. + * Performs assertion on the fitness level input. * - * @param activityLevel The activity level of the person or in other words, the amount of exercise the + * @param fitnessLevel The fitness level of the person or in other words, the amount of exercise the * person engages in. */ - private void performAssertionsForActivityLevel(ActivityLevel activityLevel) { - assert activityLevel != null : "Activity level of person should not be null"; + private void performAssertionsForFitnessLevel(FitnessLevel fitnessLevel) { + assert fitnessLevel != null : "Fitness level of person should not be null"; } /** diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index d2cd6e7b10..d6b07edd4e 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; import seedu.dietbook.food.Food; -import seedu.dietbook.person.ActivityLevel; +import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Gender; import seedu.dietbook.person.Person; @@ -56,8 +56,8 @@ void calculateFat_foodListOfThreeItems_sumOfFat() { @Test void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { - Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, ActivityLevel.LOW); - Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, ActivityLevel.MEDIUM); + Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, FitnessLevel.LOW); + Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, FitnessLevel.MEDIUM); Calculator calculator = new Calculator(new ArrayList()); assertEquals(2728, calculator.calculateRecomendation(harry)); assertEquals(1752, calculator.calculateRecomendation(erica)); diff --git a/src/test/java/seedu/dietbook/person/PersonTest.java b/src/test/java/seedu/dietbook/person/PersonTest.java index 83207f1f82..d05054091b 100644 --- a/src/test/java/seedu/dietbook/person/PersonTest.java +++ b/src/test/java/seedu/dietbook/person/PersonTest.java @@ -13,7 +13,7 @@ class PersonTest { @BeforeEach public void setUp() { person = new Person("Jack", Gender.MALE,21,165,75,65, - 60, ActivityLevel.LOW); + 60, FitnessLevel.LOW); } @Test @@ -40,42 +40,42 @@ void gender_setGenderToNull_expectAssertionError() { } @Test - void setActivityLevel_setNewActivityLevelToNone_returnsCorrectActivityLevelDescription() { - person.setActivityLevel(ActivityLevel.NONE); + void setFitnessLevel_setNewFitnessLevelToNone_returnsCorrectFitnessLevelDescription() { + person.setFitnessLevel(FitnessLevel.NONE); assertEquals("You hardly engage in any exercise or have a job that requires little to no " - + "physical activity.", person.getActivityLevel().getDescription()); + + "physical activity.", person.getFitnessLevel().getDescription()); } @Test - void setActivityLevel_setActivityLevelToLow_returnsCorrectActivityLevelDescription() { - person.setActivityLevel(ActivityLevel.LOW); + void setFitnessLevel_setFitnessLevelToLow_returnsCorrectFitnessLevelDescription() { + person.setFitnessLevel(FitnessLevel.LOW); assertEquals("You engage in some form of light exercise or have a job that requires some " - + "physical activity.", person.getActivityLevel().getDescription()); + + "physical activity.", person.getFitnessLevel().getDescription()); } @Test - void setActivityLevel_setActivityLevelToMedium_returnsCorrectActivityLevelDescription() { - person.setActivityLevel(ActivityLevel.MEDIUM); + void setFitnessLevel_setFitnessLevelToMedium_returnsCorrectFitnessLevelDescription() { + person.setFitnessLevel(FitnessLevel.MEDIUM); assertEquals("You engage in moderate amount of exercise or have a job that requires moderate " - + "physical activity.", person.getActivityLevel().getDescription()); + + "physical activity.", person.getFitnessLevel().getDescription()); } @Test - void setActivityLevel_setActivityLevelToHigh_returnsCorrectActivityLevelDescription() { - person.setActivityLevel(ActivityLevel.HIGH); + void setFitnessLevel_setFitnessLevelToHigh_returnsCorrectFitnessLevelDescription() { + person.setFitnessLevel(FitnessLevel.HIGH); assertEquals("You engage in vigorous exercise or have a physically demanding job.", - person.getActivityLevel().getDescription()); + person.getFitnessLevel().getDescription()); } @Test - void setActivityLevel_setActivityLevelToExtreme_returnsCorrectActivityLevelDescription() { - person.setActivityLevel(ActivityLevel.EXTREME); + void setFitnessLevel_setFitnessLevelToExtreme_returnsCorrectFitnessLevelDescription() { + person.setFitnessLevel(FitnessLevel.EXTREME); assertEquals("You engage in extremely vigorous exercise or have an extremely physically " - + "demanding job.", person.getActivityLevel().getDescription()); + + "demanding job.", person.getFitnessLevel().getDescription()); } @Test - void setActivityLevel_setActivityLevelToNull_expectsAssertionErrors() { - assertThrows(AssertionError.class, () -> person.setActivityLevel(null)); + void setFitnessLevel_setFitnessLevelToNull_expectsAssertionErrors() { + assertThrows(AssertionError.class, () -> person.setFitnessLevel(null)); } } From 17274ebc738ca0cc079810093327b83c0233fd35 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Wed, 4 Nov 2020 20:09:13 +0800 Subject: [PATCH 272/374] Add logging for Person class --- .../java/seedu/dietbook/person/Person.java | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index 481a8eb8b1..5599fdcd3e 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -1,5 +1,10 @@ package seedu.dietbook.person; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * Represents a Person. * A Person has a name, gender, age, height, certain fitness level, original and desired weight. @@ -18,6 +23,7 @@ public class Person { private FitnessLevel fitnessLevel; private Gender gender; private String name; + private static final Logger LOGGER = Logger.getLogger(Person.class.getName()); /** * Constructs a Person with the given name, gender, age, height, fitness level, original, @@ -38,7 +44,19 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh performAssertionsForPerson(name, gender, age, height, originalWeight, currentWeight, targetWeight, fitnessLevel); + initialiseLogger(); + LOGGER.log(Level.FINE, "Start constructing a person"); + LOGGER.log(Level.FINE, "Name: " + name); + LOGGER.log(Level.FINE, "Gender: " + gender.getDescription()); + LOGGER.log(Level.FINE, "Age: " + age); + LOGGER.log(Level.FINE, "Height: " + height); + LOGGER.log(Level.FINE, "Original weight: " + originalWeight); + LOGGER.log(Level.FINE, "Current weight: " + currentWeight); + LOGGER.log(Level.FINE, "Target weight: " + targetWeight); + LOGGER.log(Level.FINE, "Fitness Level: " + fitnessLevel.getDescription()); + this.name = name.trim(); + LOGGER.log(Level.FINE, "Trimmed Name: " + this.name); this.gender = gender; this.age = age; this.height = height; @@ -46,6 +64,17 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh this.currentWeight = currentWeight; this.targetWeight = targetWeight; this.fitnessLevel = fitnessLevel; + LOGGER.log(Level.FINE, "Person constructed"); + } + + /** + * Initialises the logger and sets the log level. + */ + private void initialiseLogger() { + Handler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.WARNING); + LOGGER.addHandler(consoleHandler); + LOGGER.setLevel(Level.WARNING); } /** @@ -90,7 +119,9 @@ public String getName() { */ public void setName(String newName) { performAssertionsForNameInput(newName); + LOGGER.log(Level.FINE, "New name: " + newName); name = newName.trim(); + LOGGER.log(Level.FINE, "Trimmed new name: " + this.name); } /** @@ -109,6 +140,7 @@ public Gender getGender() { */ public void setGender(Gender newGender) { performAssertionsForGenderInput(newGender); + LOGGER.log(Level.FINE, "New gender: " + newGender.getDescription()); gender = newGender; } @@ -128,6 +160,7 @@ public int getAge() { */ public void setAge(int newAge) { performAssertionsForAgeInput(newAge); + LOGGER.log(Level.FINE, "New age: " + newAge); age = newAge; } @@ -147,6 +180,7 @@ public int getHeight() { */ public void setHeight(int newHeight) { performAssertionsForHeight(newHeight); + LOGGER.log(Level.FINE, "New height: " + newHeight); height = newHeight; } @@ -166,6 +200,7 @@ public int getOriginalWeight() { */ public void setOriginalWeight(int newOriginalWeight) { performAssertionsForWeight(newOriginalWeight,"Original weight"); + LOGGER.log(Level.FINE, "New original weight: " + newOriginalWeight); originalWeight = newOriginalWeight; } @@ -185,6 +220,7 @@ public int getCurrentWeight() { */ public void setCurrentWeight(int newCurrentWeight) { performAssertionsForWeight(newCurrentWeight, "Current weight"); + LOGGER.log(Level.FINE, "New current weight: " + newCurrentWeight); currentWeight = newCurrentWeight; } @@ -204,6 +240,7 @@ public int getTargetWeight() { */ public void setTargetWeight(int newTargetWeight) { performAssertionsForWeight(newTargetWeight, "Target weight"); + LOGGER.log(Level.FINE, "New target weight: " + newTargetWeight); targetWeight = newTargetWeight; } @@ -223,6 +260,7 @@ public FitnessLevel getFitnessLevel() { */ public void setFitnessLevel(FitnessLevel newFitnessLevel) { performAssertionsForFitnessLevel(newFitnessLevel); + LOGGER.log(Level.FINE, "New fitness level: " + newFitnessLevel); fitnessLevel = newFitnessLevel; } @@ -282,10 +320,11 @@ private void performAssertionsForFitnessLevel(FitnessLevel fitnessLevel) { } /** - * Performs assertions the weight related inputs. + * Performs assertions on the weight related inputs. * * @param weight Either the original, current or target weight of the person. - * @param weightType A string describing whether the weight given the original, current or target weight. + * @param weightType A string describing whether the given weight is the original, current or target + * weight. */ private void performAssertionsForWeight(int weight, String weightType) { int minWeight = 1; From e6c036df936246d104309796355bb2abe90f4cd8 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 4 Nov 2020 20:22:25 +0800 Subject: [PATCH 273/374] edit data base --- src/main/resources/data.txt | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/main/resources/data.txt b/src/main/resources/data.txt index 7a211bc4e1..5b0dc3139d 100644 --- a/src/main/resources/data.txt +++ b/src/main/resources/data.txt @@ -26,22 +26,25 @@ &%START Science canteen Halal Mini Wok -Prawn Mee Soup(Dry)(Large)|490|0|0|0 -Prawn Mee Soup(Dry)(Small)|390|0|0|0 -Fried Hokkien Prawn Mee(Large)|470|0|0|0 -Fried Hokkien Prawn Mee(Small)|350|0|0|0 -Clay Pot Chicken|440|0|0|0 -Black Pepper Chicken|490|0|0|0 +Prawn Mee Soup(Dry)(Large)|490|30|20|26 +Prawn Mee Soup(Dry)(Small)|390|25|15|19 +Fried Hokkien Prawn Mee(Large)|470|40|20|20 +Fried Hokkien Prawn Mee(Small)|350|30|15|15 +Clay Pot Chicken|440|34|15|15 +Black Pepper Chicken|490|34|16|16 &%UP Ayam Penyet -Ayam Penyet Set|699|0|0|0 -Steamed Chicken Set |475|0|0|0 -Ikan Grouper Penyet Set|669|0|0|0 +Ayam Penyet Set|699|45|30|30 +Steamed Chicken Set |475|35|20|20 +Ikan Grouper Penyet Set|669|50|40|50 &%UP -Michelin Star Restaurant -Bouillabaisse with cock crab and poached lobster|520|45|35|56 -Chicken wings with Reblochon pomme purée|450|25|32|66 -Sea bass with prawn tortellini, fennel purée and white wine sauce|530|76|25|43 +Korean +kimchi fried rice|520|45|35|56 +ginseng chicken|450|25|32|66 +ramen|530|76|25|43 +&%UP +Gong Cha + &%UP &%UP &%STOP From 0ad3d535b4499629cac610e1e3778209f49c8878 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 4 Nov 2020 20:26:26 +0800 Subject: [PATCH 274/374] update data base --- src/main/java/seedu/dietbook/database/data.txt | 13 +++++++++---- src/main/resources/data.txt | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/dietbook/database/data.txt b/src/main/java/seedu/dietbook/database/data.txt index 7a211bc4e1..122f849a47 100644 --- a/src/main/java/seedu/dietbook/database/data.txt +++ b/src/main/java/seedu/dietbook/database/data.txt @@ -38,10 +38,15 @@ Ayam Penyet Set|699|0|0|0 Steamed Chicken Set |475|0|0|0 Ikan Grouper Penyet Set|669|0|0|0 &%UP -Michelin Star Restaurant -Bouillabaisse with cock crab and poached lobster|520|45|35|56 -Chicken wings with Reblochon pomme purée|450|25|32|66 -Sea bass with prawn tortellini, fennel purée and white wine sauce|530|76|25|43 +Korean +kimchi fried rice|520|45|35|56 +ginseng chicken|450|25|32|66 +ramen|530|76|25|43 +&%UP +Gong Cha +gong cha green tea|100|0|0|0 +gong cha ooloong tea|100|0|0|0 +gong cha bubble tea|200|0|0|0 &%UP &%UP &%STOP diff --git a/src/main/resources/data.txt b/src/main/resources/data.txt index 5b0dc3139d..6dd0327f19 100644 --- a/src/main/resources/data.txt +++ b/src/main/resources/data.txt @@ -44,7 +44,9 @@ ginseng chicken|450|25|32|66 ramen|530|76|25|43 &%UP Gong Cha - +gong cha green tea|100|0|0|0 +gong cha ooloong tea|100|0|0|0 +gong cha bubble tea|200|0|0|0 &%UP &%UP &%STOP From dc7ebe482982b4cd8ae6dc2efd466ba1c68c39a2 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 4 Nov 2020 20:46:04 +0800 Subject: [PATCH 275/374] change activity level to fitness level in person class --- .../saveload/PersonSaveLoadManager.java | 19 ++++++++++--------- .../saveload/PersonSaveLoadManagerTest.java | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java index d0650997f8..7a25cdd021 100644 --- a/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java @@ -31,7 +31,7 @@ public class PersonSaveLoadManager { private static final int DEFAULT_ORIGINAL_WEIGHT = 0; private static final int DEFAULT_CURRENT_WEIGHT = 0; private static final int DEFAULT_TARGET_WEIGHT = 0; - private static final int DEFAULT_ACTIVITY_LEVEL = 0; + private static final int DEFAULT_FITNESS_LEVEL = 0; private String name; private String gender; @@ -40,7 +40,7 @@ public class PersonSaveLoadManager { private int originalWeight; private int currentWeight; private int targetWeight; - private int activityLevel; + private int fitnessLevel; private Saver saver; private Loader fileLoader; @@ -56,7 +56,7 @@ public PersonSaveLoadManager() { this.originalWeight = DEFAULT_ORIGINAL_WEIGHT; this.currentWeight = DEFAULT_CURRENT_WEIGHT; this.targetWeight = DEFAULT_TARGET_WEIGHT; - this.activityLevel = DEFAULT_ACTIVITY_LEVEL; + this.fitnessLevel = DEFAULT_FITNESS_LEVEL; } /** @@ -82,8 +82,8 @@ public void load(String fileName) throws FileNotFoundException, IllegalAccessExc Integer.toString(DEFAULT_CURRENT_WEIGHT))); this.targetWeight = Integer.parseInt(this.fileLoader.get(TARGET_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW).orElse( Integer.toString(DEFAULT_TARGET_WEIGHT))); - this.activityLevel = Integer.parseInt(this.fileLoader.get(ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW).orElse( - Integer.toString(DEFAULT_ACTIVITY_LEVEL))); + this.fitnessLevel = Integer.parseInt(this.fileLoader.get(ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW).orElse( + Integer.toString(DEFAULT_FITNESS_LEVEL))); } @@ -114,7 +114,7 @@ public void reset() { this.originalWeight = DEFAULT_ORIGINAL_WEIGHT; this.currentWeight = DEFAULT_CURRENT_WEIGHT; this.targetWeight = DEFAULT_TARGET_WEIGHT; - this.activityLevel = DEFAULT_ACTIVITY_LEVEL; + this.fitnessLevel = DEFAULT_FITNESS_LEVEL; } // ----- Setters and Getters ------ @@ -146,14 +146,15 @@ public int getTargetWeight() { return targetWeight; } - public int getActivityLevel() { - return activityLevel; + public int getFitnessLevel() { + return fitnessLevel; } public void setName(String name) { this.name = name; } + public void setGender(String gender) { this.gender = gender; } @@ -179,6 +180,6 @@ public void setTargetWeight(int targetWeight) { } public void setActivityLevel(int activityLevel) { - this.activityLevel = activityLevel; + this.fitnessLevel = activityLevel; } } diff --git a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java index e7c09650d1..01de9bd352 100644 --- a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java @@ -39,6 +39,6 @@ private void load_correctFile_allContentsCorrect() throws Exception { assertEquals(100, localpslTest.getAge()); assertEquals(300, localpslTest.getCurrentWeight()); assertEquals(100, localpslTest.getTargetWeight()); - assertEquals(0, localpslTest.getActivityLevel()); + assertEquals(0, localpslTest.getFitnessLevel()); } } \ No newline at end of file From dffa920ef8be99f274e01ad747ccfe844ef2ef4d Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Wed, 4 Nov 2020 23:49:20 +0800 Subject: [PATCH 276/374] Exceptions -Catching all exception -Fixed for specific exceptions --- src/main/java/seedu/dietbook/DietBook.java | 4 +++- .../seedu/dietbook/checker/InputChecker.java | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 237f385fed..daefc0d0c5 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -101,7 +101,7 @@ public void loadFood() { /** * Main method to run the program. */ - public static void main(String[] args) { + public static void main(String[] args) throws DietException { DietBook dietBook = new DietBook(); dietBook.ui.printWelcomeMessage(); //dietBook.loadPerson(); @@ -114,6 +114,8 @@ public static void main(String[] args) { c.execute(dietBook.manager, dietBook.ui); } catch (DietException e) { dietBook.ui.printErrorMessage(e.getMessage()); + } catch (Exception e) { + throw new DietException("Wrong command format!"); } } } diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 8b221d4fec..e201c55514 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -179,9 +179,9 @@ public static void checkList(String[] param) throws DietException { */ public static void checkFoodLimit(int foodValue) throws DietException { if (foodValue < 0) { - throw new DietException("Input value cannot be less than 0!"); + throw new DietException("Portion size, carb, protein or fat value cannot be less than 0!"); } else if (foodValue > FOOD_CAP) { - throw new DietException("Input value cannot be more than 100,000!"); + throw new DietException("Portion size, carb, protein or fat value cannot be more than 100,000!"); } } @@ -247,9 +247,9 @@ public static void checkGender(String userInput) throws DietException { */ public static void checkAgeLimit(int age) throws DietException { if (age < 0) { - throw new DietException("Input value cannot be less than 0!"); + throw new DietException("Age value cannot be less than 0!"); } else if (age > AGE_CAP) { - throw new DietException("Input value cannot be more than 125!"); + throw new DietException("Age value cannot be more than 125!"); } } @@ -261,9 +261,9 @@ public static void checkAgeLimit(int age) throws DietException { */ public static void checkHeightLimit(int height) throws DietException { if (height < 1) { - throw new DietException("Input value cannot be less than 1"); + throw new DietException("Height value cannot be less than 1"); } else if (height > HEIGHT_CAP) { - throw new DietException("Input value cannot be more than 273!"); + throw new DietException("Height value cannot be more than 273!"); } } @@ -275,9 +275,9 @@ public static void checkHeightLimit(int height) throws DietException { */ public static void checkWeightLimit(int weight) throws DietException { if (weight < 1) { - throw new DietException("Input value cannot be less than 1!"); + throw new DietException("Weight value cannot be less than 1!"); } else if (weight > WEIGHT_CAP) { - throw new DietException("Input value cannot be more than 443!"); + throw new DietException("Weight value cannot be more than 443!"); } } From afbbf0c3fe265a160325f2e868b691d5c6fea7b7 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 5 Nov 2020 00:30:32 +0800 Subject: [PATCH 277/374] Bug fixes -fixed calculate rubbish acceptance - fixed accepting empty name/empty param checker --- src/main/java/seedu/dietbook/checker/InputChecker.java | 2 +- src/main/java/seedu/dietbook/command/CalculateCommand.java | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index e201c55514..e83b31a148 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -35,7 +35,7 @@ public class InputChecker { */ public static void checkEmpty(String userInput, String command) throws DietException { if (userInput.split(command).length < 2 - || userInput.split(command)[1].equals(" ")) { + || userInput.split(command)[1].trim().equals("")) { throw new DietException("Error! Missing command parameters!"); } } diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index 6eb01e27b2..c8beefcc65 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -78,7 +78,7 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printCalorieIntake(calorie, startTime, endTime); } break; - case "carbohydrate": + case "carb": if (processedParam.length == 1) { ui.printCarbIntake(this.carb); } else if (processedParam.length == 2) { @@ -110,7 +110,7 @@ public void execute(Manager manager, Ui ui) throws DietException { ui.printProteinIntake(protein, startTime, endTime); } break; - default: + case "fat": if (processedParam.length == 1) { ui.printFatIntake(this.fat); } else if (processedParam.length == 2) { @@ -125,6 +125,9 @@ public void execute(Manager manager, Ui ui) throws DietException { fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); ui.printFatIntake(fat, startTime, endTime); } + break; + default: + throw new DietException("No such nutrient type!"); } } catch (Exception e) { throw new DietException("Wrong date time format (Format: yyyy-mm-ddTHH:mm) or future date entered!"); From 37462a09ade2334a6d8e5df44ace3a11f77fefe8 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 5 Nov 2020 00:53:20 +0800 Subject: [PATCH 278/374] Add fixed -all option swappable but time must be last --- src/main/java/seedu/dietbook/parser/Parser.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 009b66fa94..1f5f13fd91 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -17,6 +17,7 @@ * @author tikimonarch */ public class Parser { + public static final int timeFormatLength = 16; public static final String COMMAND_ADD = "add"; public static final String COMMAND_CALCULATE = "calculate"; public static final String COMMAND_EDIT_INFO = "editinfo"; @@ -109,11 +110,11 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws processedParam = getCommandParam(userInput).split(param); InputChecker.checkEmptyOption(processedParam); trimmedParam = processedParam[1].trim(); - if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); - } else if (trimmedParam.split("\\s+").length == 2) { - trimmedParam = trimmedParam.split("\\s+")[0]; + } else if (trimmedParam.split("\\s+").length >= 2) { + int lengthWithoutTime = trimmedParam.length() - timeFormatLength; + trimmedParam = trimmedParam.substring(0,lengthWithoutTime).trim(); } switch (param) { case "x/": From 32f8183d7717b6e707df5fabaec1ef0338e99000 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 5 Nov 2020 02:12:20 +0800 Subject: [PATCH 279/374] Refactor Ui and add logging --- src/main/java/seedu/dietbook/DietBook.java | 3 +- src/main/java/seedu/dietbook/Ui.java | 1101 ----------------- .../seedu/dietbook/command/AddCommand.java | 2 +- .../dietbook/command/CalculateCommand.java | 2 +- .../seedu/dietbook/command/ClearCommand.java | 2 +- .../java/seedu/dietbook/command/Command.java | 2 +- .../seedu/dietbook/command/DataCommand.java | 2 +- .../seedu/dietbook/command/DeleteCommand.java | 2 +- .../dietbook/command/EditInfoCommand.java | 2 +- .../seedu/dietbook/command/ExitCommand.java | 2 +- .../seedu/dietbook/command/HelpCommand.java | 2 +- .../seedu/dietbook/command/InfoCommand.java | 2 +- .../seedu/dietbook/command/ListCommand.java | 2 +- .../seedu/dietbook/command/NameCommand.java | 2 +- .../dietbook/command/RecommendCommand.java | 2 +- .../dietbook/command/UserinfoCommand.java | 2 +- src/main/java/seedu/dietbook/ui/Ui.java | 753 +++++++++++ src/main/java/seedu/dietbook/ui/UiHelper.java | 127 ++ src/main/java/seedu/dietbook/ui/UiInput.java | 78 ++ .../java/seedu/dietbook/ui/UiMessage.java | 414 +++++++ src/main/java/seedu/dietbook/ui/UiOutput.java | 32 + .../java/seedu/dietbook/ui/UiHelperTest.java | 43 + .../{UiTest.java => ui/UiMessageTest.java} | 56 +- 23 files changed, 1479 insertions(+), 1156 deletions(-) delete mode 100644 src/main/java/seedu/dietbook/Ui.java create mode 100644 src/main/java/seedu/dietbook/ui/Ui.java create mode 100644 src/main/java/seedu/dietbook/ui/UiHelper.java create mode 100644 src/main/java/seedu/dietbook/ui/UiInput.java create mode 100644 src/main/java/seedu/dietbook/ui/UiMessage.java create mode 100644 src/main/java/seedu/dietbook/ui/UiOutput.java create mode 100644 src/test/java/seedu/dietbook/ui/UiHelperTest.java rename src/test/java/seedu/dietbook/{UiTest.java => ui/UiMessageTest.java} (60%) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index 237f385fed..2e351e96ed 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -1,5 +1,6 @@ package seedu.dietbook; +import seedu.dietbook.ui.Ui; import seedu.dietbook.database.DataBase; import seedu.dietbook.list.FoodList; import seedu.dietbook.exception.DietException; @@ -109,7 +110,7 @@ public static void main(String[] args) { while (!isExit) { try { - String userInput = dietBook.ui.readCommand(); + String userInput = dietBook.ui.getCommand(); Command c = dietBook.manager.manage(userInput); c.execute(dietBook.manager, dietBook.ui); } catch (DietException e) { diff --git a/src/main/java/seedu/dietbook/Ui.java b/src/main/java/seedu/dietbook/Ui.java deleted file mode 100644 index 505cc9ec51..0000000000 --- a/src/main/java/seedu/dietbook/Ui.java +++ /dev/null @@ -1,1101 +0,0 @@ -package seedu.dietbook; - -import seedu.dietbook.exception.DietException; -import seedu.dietbook.person.FitnessLevel; -import seedu.dietbook.person.Gender; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Scanner; - -/** - * Represents a text user interface. - * A Ui objects deals with user interaction by taking in user inputs and showing users the - * appropriate messages after a valid command is executed or when an error occurs. - */ -public class Ui { - - private static final String LINE_SEPARATOR = System.lineSeparator(); - private static Scanner scanner = new Scanner(System.in); - - /** - * Constructs a Ui object. - */ - public Ui() { - } - - // Methods in the Ui class are organised according to their function in the order of: system related, - // database related, person related, food list related, calculator related and other helper methods. - - // Methods required to print system related commands or messages. - - /** - * Reads in and returns the user input. - * - * @return The user input. - */ - public String readCommand() { - return scanner.nextLine(); - } - - /** - * Prints the welcome message from DietBook when it is first booted up. - */ - public void printWelcomeMessage() { - String logo = getLogo(); - print(logo + LINE_SEPARATOR - + "Hello! Welcome to DietBook!" + LINE_SEPARATOR - + "I am Diet, your guide to using DietBook. What is your name?" + LINE_SEPARATOR - + "Please input in the following format:" + LINE_SEPARATOR - + " name YOUR_NAME" + LINE_SEPARATOR - + " Example: name Jack"); - } - - /** - * Prints a message asking the user to input their personal information related to dieting and health - * which includes gender, age, height, activity level, original weight and target weight. - * - * @param name The name of the user. - */ - public void printAskForUserInfoMessage(String name) { - performAssertionsForStringInputs(name, "Name"); - - print("Hi " + trimString(name) + "!" + LINE_SEPARATOR - + "Before we get started, I would like to know about about you so that I can make more " - + LINE_SEPARATOR - + "accurate calculations for you :). Therefore, could you please share with me the " - + "following:" + LINE_SEPARATOR - + "- Your gender either F for " + Gender.FEMALE.getDescription() + " or M for " - + Gender.MALE.getDescription() + " or O for " + Gender.OTHERS.getDescription() + "." - + LINE_SEPARATOR - + "- Your age which is a positive integer." + LINE_SEPARATOR - + "- Your height in cm." + LINE_SEPARATOR - + "- Your original weight in kg, the weight when you first started using DietBook or " - + "you current weight." + LINE_SEPARATOR - + "- Your current weight in kg." + LINE_SEPARATOR - + "- Your target weight in kg, or your current weight if that is also your target weight." - + LINE_SEPARATOR - + "- Your fitness level, represented by a number from 1 to 5." + LINE_SEPARATOR - + " 1 = " + FitnessLevel.NONE.getDescription() + LINE_SEPARATOR - + " 2 = " + FitnessLevel.LOW.getDescription() + LINE_SEPARATOR - + " 3 = " + FitnessLevel.MEDIUM.getDescription() + LINE_SEPARATOR - + " 4 = " + FitnessLevel.HIGH.getDescription() + LINE_SEPARATOR - + " 5 = " + FitnessLevel.EXTREME.getDescription() + LINE_SEPARATOR + LINE_SEPARATOR - + "Please input your details in the following format:" + LINE_SEPARATOR - + " info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT " - + "f/FITNESS_LEVEL" + LINE_SEPARATOR - + " Example: info g/F a/21 h/165 o/65 c/65 t/55 f/2"); - } - - /** - * Prints an exit message when DietBook is closed. - * - */ - public void printExitMessage() { - print("Bye! Hope to see you again soon!"); - } - - /** - * Prints an error message given what or where the error is. - * - * @param errorMessage Message detailing what or where the error is. - */ - public void printErrorMessage(String errorMessage) { - performAssertionsForStringInputs(errorMessage,"Error message"); - - print(":( " + trimString(errorMessage)); - } - - /** - * Prints a message that notifies the user that DietBook has been initialised. - */ - public void printInitialisationCompleteMessage() { - print("Thank you! DietBook has been initialised " + getStartMessage()); - } - - /** - * Prints a message informing the user that DietBook has successfully saved all their data. - */ - public void dataSuccessfullySavedMessage() { - print("Your data has been saved successfully."); - } - - - /** - * Prints the welcome back message when user reboots up DietBook after the first initialisation. - * - * @param name The name of the user. - */ - public void printWelcomeBackMessage(String name) { - performAssertionsForStringInputs(name, "Name"); - - print(getLogo() + LINE_SEPARATOR + "Welcome back to DietBook " + trimString(name) + "!" + LINE_SEPARATOR - + "All your previous data has been successfully loaded " + getStartMessage()); - } - - /** - * Prints a string representation of a list of the commands that users can use. - */ - public void printHelpCommandMessage() { - print("Listed below are the valid commands for DietBook:" + LINE_SEPARATOR + LINE_SEPARATOR - + "For user information related commands" + LINE_SEPARATOR - + getUserRelatedCommands() + LINE_SEPARATOR - + "For database related commands" + LINE_SEPARATOR - + getDatabaseRelatedCommands() + LINE_SEPARATOR - + "For food list related commands" + LINE_SEPARATOR - + getFoodListRelatedCommands() + LINE_SEPARATOR - + "For nutritional intake and recommendation related commands" + LINE_SEPARATOR - + getCalculatorRelatedCommands() + LINE_SEPARATOR - + "For other system related commands" + LINE_SEPARATOR - + getSystemRelatedCommands()); - } - - // Methods required to print database related commands or messages. - - /** - * Prints all the food in the database sorted by the canteen and then the store it is found. - * - * @param foodDatabase The string representation of all the food items stored in the database. - */ - public void printDatabase(String foodDatabase) { - performAssertionsForStringInputs(foodDatabase, - "Food database"); - - print("Here are the food items in the database:" + LINE_SEPARATOR + foodDatabase); - } - - /** - * Prints the food items in the database containing the food name of the food that user wants to - * add sorted by the canteen and then the store it is found. - * This method is only used if more than one food item in the database contains the food name given. - * - * @param matchingFoodDatabase The string representation of the food items stored in the - * database containing the food name given. - */ - public void printMatchingFoodsInDatabase(String matchingFoodDatabase) { - performAssertionsForStringInputs(matchingFoodDatabase, - "Matching food database"); - - print("Here are the matching food items in the database:" + LINE_SEPARATOR - + matchingFoodDatabase + LINE_SEPARATOR + LINE_SEPARATOR - + "Please re-enter with the full name of the food item above in the following format:" - + LINE_SEPARATOR + " add n/FOOD_NAME x/PORTION_SIZE"); - } - - // Methods required to print user information related commands and messages. - - /** - * Prints all the information related to the user. - * - * @param personInfo The user's personal information. - */ - public void printPersonInfo(String personInfo) { - performAssertionsForStringInputs(personInfo, - "Person information"); - - print("Here is your information:" + LINE_SEPARATOR - + personInfo); - } - - /** - * Prints all the updated information related to the user. - * - * @param personInfo The user's personal information. - */ - public void printEditedPersonInfo(String personInfo) { - performAssertionsForStringInputs(personInfo, - "Updated person information"); - - print("Got it! I've updated your personal information:" + LINE_SEPARATOR - + personInfo); - } - - // Methods required for printing FoodList related commands and messages. - - /** - * Prints all the food items in the food list in the order that they were added or a message stating - * that the food list is empty if there are no food items. - * - * @param allFood The string representation of all the food items in the food list. - */ - public void printFoodList(String allFood) { - performAssertionsForNullStringInputs(allFood, - "String representation of all food in food list"); - - if (trimStringGetLength(allFood) < 1) { - print("DietBook is currently empty."); - } else { - print("Here are the food items in DietBook:" + LINE_SEPARATOR + allFood); - } - } - - /** - * Prints food items recorded into the food list during a given time period in the order that they were - * added or a message stating no food items were recorded during the given time period. - * - * @param foods The string representation of food items in the food list recorded during the time - * period given. - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - */ - public void printFoodList(String foods, LocalDateTime start, LocalDateTime end) { - performAssertionsForNullStringInputs(foods, - "String representation of food items in the food list recorded during the time " - + "period given"); - performAssertionsForTimePeriod(start, end); - - if (trimStringGetLength(foods) < 1) { - print("No food item was recorded in DietBook" + stringDateTimePeriod(start, end) + "."); - } else { - print("Here are the food items recorded in DietBook" + stringDateTimePeriod(start, end) + ":" - + LINE_SEPARATOR + foods); - } - } - - /** - * Prints food items recorded into the food list after a given timing in the order that they were - * added or a message stating no food items were recorded after the given timing till now. - * - * @param foods The string representation of food items in the food list recorded from the given time - * till now. - * @param start Starting date time of the time period till now. - */ - public void printFoodList(String foods, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printFoodList(foods, start, end); - } - - /** - * Prints a message to show that the food specified has been added to the food list. - * - * @param newFood The string representation of the new food item that was added to the food list. - */ - public void printNewFood(String newFood) { - performAssertionsForStringInputs(newFood, - "String representation of the food that was added"); - - print("Got it! I've added this food item:" + LINE_SEPARATOR - + " " + trimString(newFood)); - } - - /** - * Prints a message to show that the food specified has been deleted from the food list. - * - * @param deletedFood The string representation of the food that was deleted from the food list. - */ - public void printDeletedFood(String deletedFood) { - performAssertionsForStringInputs(deletedFood, - "String representation of the food that was deleted"); - - print("Noted. I've removed this food item:" + LINE_SEPARATOR - + " " + trimString(deletedFood)); - } - - /** - * Prints a message to show that the food list has been successfully cleared and is now empty. - */ - public void printClearFoodListMessage() { - print("All previous data has been deleted..." + LINE_SEPARATOR - + "DietBook is now empty."); - } - - // Methods required to print nutritional intake and recommendation related commands and messages. - - /** - * Prints the daily recommended calorie intake of the user based on the user's personal information. - * - * @param calorieRecommendation The daily recommended calorie intake of the user. - */ - public void printCalorieRecommendation(String name, int calorieRecommendation) { - performAssertionsForStringInputs(name, "Name"); - performAssertionsForCalorieRecommendation(calorieRecommendation); - - print("Hi " + trimString(name) + "!" + LINE_SEPARATOR - + "Here is your daily recommended calorie intake: " + calorieRecommendation + "kcal"); - } - - public void printCarbIntake(int carbIntake) { - print(stringOneIntakeAndFoodsWithoutTime(carbIntake,"carbohydrate", - "g")); - } - - public void printCarbIntake(int carbIntake, LocalDateTime start, LocalDateTime end) { - String carbIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(carbIntake, - "carbohydrate", "g"); - print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); - } - - public void printCarbIntake(int carbIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printCarbIntake(carbIntake, start, end); - } - - public void printCalorieIntake(int calorieIntake) { - print(stringOneIntakeAndFoodsWithoutTime(calorieIntake,"calorie","kcal")); - } - - public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDateTime end) { - String calorieIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(calorieIntake, - "calorie", "kcal"); - print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); - } - - public void printCalorieIntake(int calorieIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printCalorieIntake(calorieIntake, start, end); - } - - public void printProteinIntake(int proteinIntake) { - print(stringOneIntakeAndFoodsWithoutTime(proteinIntake,"protein","g")); - } - - public void printProteinIntake(int proteinIntake, LocalDateTime start, LocalDateTime end) { - String proteinIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(proteinIntake, - "protein", "g"); - print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); - } - - public void printProteinIntake(int proteinIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printProteinIntake(proteinIntake, start, end); - } - - public void printFatIntake(int fatIntake) { - print(stringOneIntakeAndFoodsWithoutTime(fatIntake,"fat","g")); - } - - public void printFatIntake(int fatIntake, LocalDateTime start, LocalDateTime end) { - String fatIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(fatIntake, - "fat", "g"); - print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); - } - - public void printFatIntake(int fatIntake, LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printFatIntake(fatIntake, start, end); - } - - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake) { - print(stringAllIntakeAndFoodsWithoutTime(calorieIntake, carbIntake, proteinIntake, - fatIntake)); - } - - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake, LocalDateTime start, LocalDateTime end) { - String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(calorieIntake, - carbIntake, proteinIntake, fatIntake); - print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); - } - - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, - LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printAllIntake(calorieIntake, carbIntake, proteinIntake, fatIntake, start, end); - } - - /** - * Prints the total amount of carbohydrates consumed by the user and the list of food items which had - * their nutritional information recalculated by DietBook if any. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total carbohydrate intake. - * - * @param carbIntake The total amount of carbohydrates of all the food in the food list. - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - */ - public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods) { - print(stringOneIntakeAndFoodsWithoutTime(carbIntake,"carbohydrate", - "g", recalculatedFoods)); - } - - /** - * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had - * their nutritional information recalculated by DietBook if any, given a certain time period. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total carbohydrate - * intake within a given time period. - * - * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the - * time period given. - * @param recalculatedFoods The list of food items recorded during the given time period which had their - * nutritional information recalculated by DietBook. - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - */ - public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, - LocalDateTime start, LocalDateTime end) { - String carbIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(carbIntake, - "carbohydrate", "g", recalculatedFoods); - print(stringIntakeAndFoodsWithTime(carbIntakeAndFoodsWithoutTime, start, end)); - } - - /** - * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had - * their nutritional information recalculated by DietBook if any, given a start date. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total carbohydrate - * intake given a start date. - * - * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the - * start date till now. - * @param recalculatedFoods The list of food items recorded from the start date till now which had - * their nutritional information recalculated by DietBook. - * @param start Starting date time to calculate from. - */ - public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, - LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printCarbIntakeAndFoods(carbIntake, recalculatedFoods, start, end); - } - - - /** - * Prints the total amount of calories consumed by the user and the list of food items which had - * their nutritional information recalculated by DietBook if any. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total calorie intake. - * - * @param calorieIntake The total amount of calories of all the food in the food list. - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - */ - public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods) { - print(stringOneIntakeAndFoodsWithoutTime(calorieIntake,"calorie","kcal", - recalculatedFoods)); - } - - /** - * Prints the total amount of calories consumed by the user and a list of the foods which had their - * nutritional information recalculated by DietBook if any, given a certain time period. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total calorie - * intake within a given time period. - * - * @param calorieIntake The total amount of calories of food in the food list recorded during the - * time period given. - * @param recalculatedFoods The list of food items recorded during the given time period which had their - * nutritional information recalculated by DietBook. - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - */ - public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods, - LocalDateTime start, LocalDateTime end) { - String calorieIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(calorieIntake, - "calorie", "kcal", recalculatedFoods); - print(stringIntakeAndFoodsWithTime(calorieIntakeAndFoodsWithoutTime, start, end)); - } - - /** - * Prints the total amount of calories consumed by the user and a list of the foods which had their - * nutritional information recalculated by DietBook if any, given a start date. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total calorie intake, - * given a start date. - * - * @param calorieIntake The total amount of calories of food in the food list recorded from the - * start date till now. - * @param recalculatedFoods The list of food items recorded from the start date till now which had - * their nutritional information recalculated by DietBook. - * @param start Starting date time to calculate from. - */ - public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods, - LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printCalorieIntakeAndFoods(calorieIntake, recalculatedFoods, start, end); - } - - - /** - * Prints the total amount of proteins consumed by the user and the list of food items which had - * their nutritional information recalculated by DietBook if any. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total protein intake. - * - * @param proteinIntake The total amount of proteins of all the food in the food list. - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - */ - public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods) { - print(stringOneIntakeAndFoodsWithoutTime(proteinIntake,"protein","g", - recalculatedFoods)); - } - - /** - * Prints the total amount of proteins consumed by the user and a list of the foods which had their - * nutritional information recalculated by DietBook if any, given a certain time period. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total protein - * intake within a given time period. - * - * @param proteinIntake The total amount of proteins of food in the food list recorded during the - * time period given. - * @param recalculatedFoods The list of food items recorded during the given time period which had their - * nutritional information recalculated by DietBook. - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - */ - public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods, - LocalDateTime start, LocalDateTime end) { - String proteinIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(proteinIntake, - "protein", "g", recalculatedFoods); - print(stringIntakeAndFoodsWithTime(proteinIntakeAndFoodsWithoutTime, start, end)); - } - - /** - * Prints the total amount of proteins consumed by the user and a list of the foods which had their - * nutritional information recalculated by DietBook if any, given a start date. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total protein intake, - * given a start date. - * - * @param proteinIntake The total amount of proteins of food in the food list recorded from the - * start date till now. - * @param recalculatedFoods The list of food items recorded from the start date till now which had - * their nutritional information recalculated by DietBook. - * @param start Starting date time to calculate from. - */ - public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods, - LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printProteinIntakeAndFoods(proteinIntake, recalculatedFoods, start, end); - } - - - /** - * Prints the total amount of fats consumed by the user and the list of food items which had - * their nutritional information recalculated by DietBook if any. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total fat intake. - * - * @param fatIntake The total amount of fats of all the food in the food list. - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - */ - public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods) { - print(stringOneIntakeAndFoodsWithoutTime(fatIntake,"fat","g", - recalculatedFoods)); - } - - /** - * Prints the total amount of fats consumed by the user and a list of the foods which had their - * nutritional information recalculated by DietBook if any, given a certain time period. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total fat - * intake within a given time period. - * - * @param fatIntake The total amount of fats of food in the food list recorded during the - * time period given. - * @param recalculatedFoods The list of food items recorded during the given time period which had their - * nutritional information recalculated by DietBook. - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - */ - public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, - LocalDateTime start, LocalDateTime end) { - String fatIntakeAndFoodsWithoutTime = stringOneIntakeAndFoodsWithoutTime(fatIntake, - "fat", "g", recalculatedFoods); - print(stringIntakeAndFoodsWithTime(fatIntakeAndFoodsWithoutTime, start, end)); - } - - /** - * Prints the total amount of fats consumed by the user and a list of the foods which had their - * nutritional information recalculated by DietBook if any, given a start date. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating total fat intake, - * given a start date. - * - * @param fatIntake The total amount of fats of food in the food list recorded from the start date till - * now. - * @param recalculatedFoods The list of food items recorded from the start date till now which had - * their nutritional information recalculated by DietBook. - * @param start Starting date time to calculate from. - */ - public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, - LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printFatIntakeAndFoods(fatIntake, recalculatedFoods, start, end); - } - - /** - * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and the - * list of food items which had their nutritional information recalculated by DietBook if any. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating the individual intakes. - * - * @param carbIntake The total amount of carbohydrates of all the food in the food list. - * @param calorieIntake The total amount of calories of all the food in the food list. - * @param proteinIntake The total amount of proteins of all the food in the food list. - * @param fatIntake The total amount of fats of all the food in the food list. - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - */ - public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake, String recalculatedFoods) { - print(stringAllIntakeAndFoodsWithoutTime(calorieIntake, carbIntake, proteinIntake, - fatIntake, recalculatedFoods)); - } - - /** - * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a - * list of the foods which had their nutritional information recalculated by DietBook if any, given a - * certain time period. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating the individual intakes - * within a given time period. - * - * @param calorieIntake The total amount of calories of food in the food list recorded during the - * time period given. - * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the - * time period given. - * @param proteinIntake The total amount of proteins of food in the food list recorded during the - * time period given. - * @param fatIntake The total amount of fats of food in the food list recorded during the - * time period given. - * @param recalculatedFoods The list of food items recorded during the given time period which had their - * nutritional information recalculated by DietBook. - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - */ - public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake, String recalculatedFoods, - LocalDateTime start, LocalDateTime end) { - String allIntakeAndFoodsWithoutTime = stringAllIntakeAndFoodsWithoutTime(calorieIntake, - carbIntake, proteinIntake, fatIntake, recalculatedFoods); - print(stringIntakeAndFoodsWithTime(allIntakeAndFoodsWithoutTime, start, end)); - } - - /** - * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a - * list of the foods which had their nutritional information recalculated by DietBook if any, given a - * start date. - * Some food items only have partial nutritional information as users did not provide all the - * information when the food items were added. Hence, DietBook does an internal calculation for the - * the missing information and these calculated values are used when tabulating the individual intakes, - * given a start date. - * - * @param calorieIntake The total amount of calories of food in the food list recorded from the start - * date till now. - * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the start - * date till now. - * @param proteinIntake The total amount of proteins of food in the food list recorded from the start - * date till now. - * @param fatIntake The total amount of fats of food in the food list recorded from the start date till - * now. - * @param recalculatedFoods The list of food items recorded from the start date till now which had - * their nutritional information recalculated by DietBook. - * @param start Starting date time to calculate from. - */ - public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake, String recalculatedFoods, - LocalDateTime start) { - LocalDateTime end = LocalDateTime.now(); - printAllIntakeAndFoods(calorieIntake, carbIntake, proteinIntake, fatIntake, recalculatedFoods, start, - end); - } - - // Helper methods for system related commands or messages - - /** - * Returns the string representation of the logo. - * - * @return The string representation of the logo. - */ - private String getLogo() { - String logo = " _______ __ ______ ________ _______ ______ ______ __ __" + LINE_SEPARATOR - + "| __ \\| | ___|__ __| __ \\ / __ \\ / __ \\| | / /" + LINE_SEPARATOR - + "| | | | | |___ | | | |__| | | | | | | | |/ /" + LINE_SEPARATOR - + "| | | | | ___| | | | __ <| | | | | | | /" + LINE_SEPARATOR - + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + LINE_SEPARATOR - + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\\" + LINE_SEPARATOR; - return logo; - } - - /** - * Returns a string stating that DietBook is ready for use. - * - * @return A string stating that DietBook is ready for use. - */ - private String getStartMessage() { - return "and you may start by entering any valid commands. " - + LINE_SEPARATOR - + "If you require a list of valid commands, you can enter: help"; - } - - /** - * Returns a string representation of a list of system related commands that users can input. - * - * @return A string representation of a list of system related commands that users can input. - */ - private String getSystemRelatedCommands() { - return " To view a list of valid commands: help" + LINE_SEPARATOR - + " To exit DietBook: exit"; - } - - /** - * Returns a string representation of a list of nutritional intake and recommendation related commands - * that users can input. - * - * @return A string representation of a list of nutritional intake and recommendation related commands - * that users can input. - */ - private String getCalculatorRelatedCommands() { - return " To get recommended calorie intake: recommend" + LINE_SEPARATOR + LINE_SEPARATOR - + " To calculate carbohydrate intake: calculate carbohydrate" + LINE_SEPARATOR - + " To calculate carbohydrate intake within a time period: calculate carbohydrate " - + "yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm" + LINE_SEPARATOR - + " To calculate carbohydrate intake from a certain date until now: calculate carbohydrate " - + "yyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR - + " To calculate calorie intake: calculate calorie" + LINE_SEPARATOR - + " To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR - + " To calculate calorie intake from a certain date until now: calculate calorie " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR - + " To calculate protein intake: calculate protein" + LINE_SEPARATOR - + " To calculate protein intake within a time period: calculate protein yyyy-mm-ddTHH:mm " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR - + " To calculate protein intake from a certain date until now: calculate protein " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR - + " To calculate fat intake: calculate fat" + LINE_SEPARATOR - + " To calculate fat intake within a time period: calculate fat yyyy-mm-ddTHH:mm " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR - + " To calculate fat intake from a certain date until now: calculate fat " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR + LINE_SEPARATOR - + " To calculate all nutritional intake: calculate all" + LINE_SEPARATOR - + " To calculate all nutritional intake within a time period: calculate all " - + "yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm" + LINE_SEPARATOR - + " To calculate all nutritional intake from a certain date until now: calculate all " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR; - } - - /** - * Returns a string representation of a list of user information related commands that users can input. - * - * @return A string representation of a list of user information related commands that users can input. - */ - private String getUserRelatedCommands() { - return " To view user information: userinfo" + LINE_SEPARATOR - + " To edit user information: editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] " - + "[o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]" - + LINE_SEPARATOR; - } - - /** - * Returns a string representation of a list of food list related commands that users can input. - * - * @return A string representation of a list of food list related commands that users can input. - */ - private String getFoodListRelatedCommands() { - return " To add a food not in the database that was just consumed: add x/PORTION_SIZE n/FOOD_NAME " - + "k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT]" + LINE_SEPARATOR - + " To add a food not in the database consumed at a certain time: add x/PORTION_SIZE " - + "n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] yyyy-mm-ddTHH:mm" - + LINE_SEPARATOR - + " To view all food in DietBook: list" + LINE_SEPARATOR - + " To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm " - + "yyyy-mm-ddTHH:mm" + LINE_SEPARATOR - + " To view all food in DietBook recorded from a certain date until now: list " - + "yyyy-mm-ddTHH:mm " + LINE_SEPARATOR - + " To delete a food from DietBook: delete INDEX" + LINE_SEPARATOR - + " To delete all food items from the DietBook: clear" + LINE_SEPARATOR; - } - - /** - * Returns a string representation of a list of database related commands that users can input. - * - * @return A string representation of a list of database related commands that users can input. - */ - private String getDatabaseRelatedCommands() { - return " [Coming Soon] To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE" + LINE_SEPARATOR - + " [Coming Soon] To add a food from the database consumed at a certain time: add " - + "n/FOOD_NAME x/PORTION_SIZE yyyy-mm-ddTHH:mm" + LINE_SEPARATOR - + " To view all food in the database: data" + LINE_SEPARATOR; - } - - // Helper methods for calculator related commands and messages - - /** - * Returns a string with a header and recalculatedFoods or a string stating that no food items had their - * nutritional information recalculated if calculatedFoods is an empty string. - * - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - * @return A string with a header and recalculatedFoods or a string stating that no food items had their - * nutritional information recalculated if calculatedFoods is an empty string. - */ - private String recalculatedFoodsMessage(String recalculatedFoods) { - String message = "No food items had their nutritional information recalculated by DietBook."; - if (trimStringGetLength(recalculatedFoods) > 0) { - message = "Food items which had their nutritional information recalculated by DietBook: " - + LINE_SEPARATOR + recalculatedFoods; - } - return message; - } - - /** - * Return a string representation of the amount of a nutrient consumed by the user which can be either - * the total amount consumed or amount consumed in a given time period. - * - * @param nutrientIntake The amount of a particular type of nutrient consumed. - * @param nutrientType A string representation of the type of nutrient consumed. - * @param nutrientUnit A string representation of the unit of the nutrient consumed. - * @return The amount of a nutrient consumed by the user which can be either the total amount consumed - * or amount consumed in a given time period. - */ - private String stringNutritionalIntake(int nutrientIntake, String nutrientType, String nutrientUnit) { - return "Total " + nutrientType + " intake: " + nutrientIntake + nutrientUnit; - } - - /** - * Returns a string representation of the total amount of a nutrient consumed by the user and - * the list of food items which had their nutritional information recalculated by DietBook if any. - * - * @param nutrientIntake The amount of a particular type of nutrient consumed. - * @param nutrientType A string representation of the type of nutrient consumed. - * @param nutrientUnit A string representation of the unit of the nutrient consumed. - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - * @return A string representation of the the total amount of a nutrient consumed by the user and - * the list of food items which had their nutritional information recalculated by DietBook if any. - */ - private String stringOneIntakeAndFoodsWithoutTime(int nutrientIntake, String nutrientType, - String nutrientUnit, String recalculatedFoods) { - performAssertionsForStringInputs(nutrientType,"Nutrient Type"); - performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); - performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); - performAssertionsForNullStringInputs(recalculatedFoods, - "List of foods that had their nutritional information recalculated"); - - String stringNutrientIntake = stringNutritionalIntake(nutrientIntake, nutrientType, nutrientUnit); - String message = recalculatedFoodsMessage(recalculatedFoods); - return stringNutrientIntake + LINE_SEPARATOR + message; - } - - private String stringOneIntakeAndFoodsWithoutTime(int nutrientIntake, String nutrientType, - String nutrientUnit) { - performAssertionsForStringInputs(nutrientType,"Nutrient Type"); - performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); - performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); - - String stringNutrientIntake = stringNutritionalIntake(nutrientIntake, nutrientType, nutrientUnit); - return stringNutrientIntake; - } - - /** - * Returns a string representation of the total amount of a nutrient or all nutrientS consumed by the - * user during a given time period and the list of food items recorded during the same time period - * which had their nutritional information recalculated by DietBook if any. - * - * @param intakeAndFoodsWithoutTime A string representation of the the total amount of a nutrient or - * all nutrients consumed by the user and the list of food items which had their nutritional - * information recalculated by DietBook if any. - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - * @return A string representation of the the total amount of a nutrient or all nutrient consumed by the - * user during a given time period and the list of food items recorded during the same time period - * which had their nutritional information recalculated by DietBook if any. - */ - private String stringIntakeAndFoodsWithTime(String intakeAndFoodsWithoutTime, - LocalDateTime start, LocalDateTime end) { - performAssertionsForTimePeriod(start, end); - - String timePeriod = "Time period:" + stringDateTimePeriod(start, end); - return timePeriod + LINE_SEPARATOR + LINE_SEPARATOR + intakeAndFoodsWithoutTime; - } - - /** - * Returns a string representation of the total amount of all nutrients consumed by the user and - * the list of food items which had their nutritional information recalculated by DietBook if any. - * - * @param carbIntake The total amount of carbohydrates of all the food in the food list. - * @param calorieIntake The total amount of calories of all the food in the food list. - * @param proteinIntake The total amount of proteins of all the food in the food list. - * @param fatIntake The total amount of fats of all the food in the food list. - * @param recalculatedFoods The list of food items which had their nutritional information recalculated by - * DietBook. - * @return A string representation of the total amount of all nutrients consumed by the user and - * the list of food items which had their nutritional information recalculated by DietBook if any. - */ - private String stringAllIntakeAndFoodsWithoutTime(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake, String recalculatedFoods) { - performAssertionsForNutritionalIntake(carbIntake, "carbohydrate"); - performAssertionsForNutritionalIntake(calorieIntake, "calorie"); - performAssertionsForNutritionalIntake(proteinIntake, "protein"); - performAssertionsForNutritionalIntake(fatIntake, "fat"); - performAssertionsForNullStringInputs(recalculatedFoods, - "List of foods that had their nutritional information recalculated"); - - String stringCarbIntake = stringNutritionalIntake(carbIntake,"carbohydrate", "g"); - String stringCalorieIntake = stringNutritionalIntake(calorieIntake,"calorie", - "kcal"); - String stringProteinIntake = stringNutritionalIntake(proteinIntake,"protein", "g"); - String stringFatIntake = stringNutritionalIntake(fatIntake,"fat", "g"); - String message = recalculatedFoodsMessage(recalculatedFoods); - - return stringCalorieIntake + LINE_SEPARATOR - + stringCarbIntake + LINE_SEPARATOR - + stringProteinIntake + LINE_SEPARATOR - + stringFatIntake + LINE_SEPARATOR - + message; - - } - - private String stringAllIntakeAndFoodsWithoutTime(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake) { - performAssertionsForNutritionalIntake(carbIntake, "carbohydrate"); - performAssertionsForNutritionalIntake(calorieIntake, "calorie"); - performAssertionsForNutritionalIntake(proteinIntake, "protein"); - performAssertionsForNutritionalIntake(fatIntake, "fat"); - - - String stringCarbIntake = stringNutritionalIntake(carbIntake,"carbohydrate", "g"); - String stringCalorieIntake = stringNutritionalIntake(calorieIntake,"calorie", - "kcal"); - String stringProteinIntake = stringNutritionalIntake(proteinIntake,"protein", "g"); - String stringFatIntake = stringNutritionalIntake(fatIntake,"fat", "g"); - - return stringCalorieIntake + LINE_SEPARATOR - + stringCarbIntake + LINE_SEPARATOR - + stringProteinIntake + LINE_SEPARATOR - + stringFatIntake + LINE_SEPARATOR; - - } - - // Other helper methods - - /** - * Prints the given message to the user. - * - * @param message The message to show the user. - */ - private void print(String message) { - performAssertionsForStringInputs(message, "Message to print"); - String divider = - "__________________________________________________________________________________________" - + "___________________________________________"; - - System.out.println(divider + LINE_SEPARATOR - + trimString(message) + LINE_SEPARATOR - + divider); - - } - - /** - * Returns a string representation of the time period with date time in the format dd MMM yyyy HHmm. - * - * @param start Starting date time of the time period given that needs to be converted into a String. - * @param end Ending date time of the time period given that needs to be converted into a String. - * @return The string representation of time period with date time in the format dd MMM yyyy HHmm. - */ - public String stringDateTimePeriod(LocalDateTime start, LocalDateTime end) { - performAssertionsForTimePeriod(start, end); - - String stringStart = start.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); - String stringEnd = end.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); - return " between " + stringStart + " and " + stringEnd; - } - - - /** - * Returns an integer representing the length of the string after it has been trimmed for leading and - * trailing spaces. - * - * @param string The string to be trimmed and have its length determined. - * @return An integer representing the length of the string after it has been trimmed for leading and - * trailing spaces. - */ - public int trimStringGetLength(String string) { - performAssertionsForNullStringInputs(string, "String to trim and have length determined"); - - return trimString(string).length(); - } - - /** - * Returns a string that has been trimmed for leading and trailing spaces. - * - * @param string The string to be trimmed for leading and trailing spaces. - * @return A string that has been trimmed for leading and trailing spaces. - */ - private String trimString(String string) { - performAssertionsForNullStringInputs(string, "String to trim"); - - return string.trim(); - } - - /** - * Performs assertions for the string inputs. - * - * @param string The input value. - * @param stringDescription A description of what the input value represents. - */ - private void performAssertionsForStringInputs(String string, String stringDescription) { - performAssertionsForNullStringInputs(string, stringDescription); - assert trimStringGetLength(string) > 0 : stringDescription + " should not be an empty string"; - } - - /** - * Performs assertions for the time inputs. - * - * @param start Starting date time of the time period given. - * @param end Ending date time of the time period given. - */ - private void performAssertionsForTimePeriod(LocalDateTime start, LocalDateTime end) { - assert start != null : "Starting date time of the time period given should not be null"; - assert end != null : "Ending date time of the time period given should not be null"; - assert !start.isAfter(end) : "Starting date time should not be later than ending date time " - + "of the time period"; - LocalDateTime now = LocalDateTime.now(); - assert !start.isAfter(now) : "Starting date time of the time period given should " - + "not be in the future" + start + LocalDateTime.now(); - assert !end.isAfter(now) : "Ending date time of the time period given should not be" - + " in the future" + end + LocalDateTime.now(); - } - - /** - * Performs assertions for null string inputs. - * - * @param string The input value. - * @param stringDescription A description of what the input value represents. - */ - private void performAssertionsForNullStringInputs(String string, String stringDescription) { - assert string != null : stringDescription + " should not be null"; - } - - /** - * Performs assertions for nutritional intake inputs. - * - * @param nutrientIntake The nutritional intake value. - * @param nutrientType The nutrient type. - */ - private void performAssertionsForNutritionalIntake(int nutrientIntake, String nutrientType) { - assert nutrientIntake >= 0 : "Total " + nutrientType + " intake should be equals to or greater than 0"; - } - - /** - * Performs assertions for the calorie recommendation input. - * - * @param calorieRecommendation The recommended daily calorie intake for the user. - */ - private void performAssertionsForCalorieRecommendation(int calorieRecommendation) { - // A minimum daily intake of 1200 calorie is required to stay healthy. - assert calorieRecommendation >= 1200 : "Daily calorie recommendation should be equals to or greater" - + " than 1200"; - // Highest calorie intake for an athlete currently stands at 12000. - assert calorieRecommendation <= 12000 : "Daily calorie recommendation should be equals to or less " - + "than 12,000"; - } -} diff --git a/src/main/java/seedu/dietbook/command/AddCommand.java b/src/main/java/seedu/dietbook/command/AddCommand.java index d5a6e1da9a..3b1c7ace77 100644 --- a/src/main/java/seedu/dietbook/command/AddCommand.java +++ b/src/main/java/seedu/dietbook/command/AddCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; public class AddCommand extends Command { diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index 6eb01e27b2..0f9022d82b 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; diff --git a/src/main/java/seedu/dietbook/command/ClearCommand.java b/src/main/java/seedu/dietbook/command/ClearCommand.java index 41f7939ab3..94eda96349 100644 --- a/src/main/java/seedu/dietbook/command/ClearCommand.java +++ b/src/main/java/seedu/dietbook/command/ClearCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; public class ClearCommand extends Command { diff --git a/src/main/java/seedu/dietbook/command/Command.java b/src/main/java/seedu/dietbook/command/Command.java index 03a2d4d3ef..a451ac1c19 100644 --- a/src/main/java/seedu/dietbook/command/Command.java +++ b/src/main/java/seedu/dietbook/command/Command.java @@ -2,7 +2,7 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; /** * The command parent class for all commands. diff --git a/src/main/java/seedu/dietbook/command/DataCommand.java b/src/main/java/seedu/dietbook/command/DataCommand.java index adabf27c11..57aded1de4 100644 --- a/src/main/java/seedu/dietbook/command/DataCommand.java +++ b/src/main/java/seedu/dietbook/command/DataCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; public class DataCommand extends Command { diff --git a/src/main/java/seedu/dietbook/command/DeleteCommand.java b/src/main/java/seedu/dietbook/command/DeleteCommand.java index 4236acecab..52700412ff 100644 --- a/src/main/java/seedu/dietbook/command/DeleteCommand.java +++ b/src/main/java/seedu/dietbook/command/DeleteCommand.java @@ -2,7 +2,7 @@ import seedu.dietbook.exception.DietException; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; public class DeleteCommand extends Command { int index; diff --git a/src/main/java/seedu/dietbook/command/EditInfoCommand.java b/src/main/java/seedu/dietbook/command/EditInfoCommand.java index 86bc326e3d..12f24deed2 100644 --- a/src/main/java/seedu/dietbook/command/EditInfoCommand.java +++ b/src/main/java/seedu/dietbook/command/EditInfoCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java index 14c69eb6b9..5249eedc0a 100644 --- a/src/main/java/seedu/dietbook/command/ExitCommand.java +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -2,9 +2,9 @@ import seedu.dietbook.DietBook; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Gender; +import seedu.dietbook.ui.Ui; import seedu.dietbook.saveload.PersonSaveLoadManager; import seedu.dietbook.saveload.FoodSaveLoadManager; diff --git a/src/main/java/seedu/dietbook/command/HelpCommand.java b/src/main/java/seedu/dietbook/command/HelpCommand.java index cb0e26bbc6..69f99dedc9 100644 --- a/src/main/java/seedu/dietbook/command/HelpCommand.java +++ b/src/main/java/seedu/dietbook/command/HelpCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; //@@author tikimonarch diff --git a/src/main/java/seedu/dietbook/command/InfoCommand.java b/src/main/java/seedu/dietbook/command/InfoCommand.java index d30d6e496c..91453e0b57 100644 --- a/src/main/java/seedu/dietbook/command/InfoCommand.java +++ b/src/main/java/seedu/dietbook/command/InfoCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; import seedu.dietbook.parser.Parser; diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java index 933e42289e..60eac832ef 100644 --- a/src/main/java/seedu/dietbook/command/ListCommand.java +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; import seedu.dietbook.list.FoodList; diff --git a/src/main/java/seedu/dietbook/command/NameCommand.java b/src/main/java/seedu/dietbook/command/NameCommand.java index 181e1084da..cb0d05b076 100644 --- a/src/main/java/seedu/dietbook/command/NameCommand.java +++ b/src/main/java/seedu/dietbook/command/NameCommand.java @@ -2,7 +2,7 @@ import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; diff --git a/src/main/java/seedu/dietbook/command/RecommendCommand.java b/src/main/java/seedu/dietbook/command/RecommendCommand.java index ae26da133f..a9747ef1eb 100644 --- a/src/main/java/seedu/dietbook/command/RecommendCommand.java +++ b/src/main/java/seedu/dietbook/command/RecommendCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; import seedu.dietbook.person.Person; diff --git a/src/main/java/seedu/dietbook/command/UserinfoCommand.java b/src/main/java/seedu/dietbook/command/UserinfoCommand.java index f7892f1d02..cdbf1e28a9 100644 --- a/src/main/java/seedu/dietbook/command/UserinfoCommand.java +++ b/src/main/java/seedu/dietbook/command/UserinfoCommand.java @@ -1,7 +1,7 @@ package seedu.dietbook.command; import seedu.dietbook.Manager; -import seedu.dietbook.Ui; +import seedu.dietbook.ui.Ui; import seedu.dietbook.exception.DietException; diff --git a/src/main/java/seedu/dietbook/ui/Ui.java b/src/main/java/seedu/dietbook/ui/Ui.java new file mode 100644 index 0000000000..f559e99b4d --- /dev/null +++ b/src/main/java/seedu/dietbook/ui/Ui.java @@ -0,0 +1,753 @@ +package seedu.dietbook.ui; + +import seedu.dietbook.exception.DietException; + +import java.time.LocalDateTime; + +/** + * Represents a manager that deals with user interaction and communicating with the logic component. + * A Ui objects deals with user input, output and communication with the logic component. + */ +public class Ui { + + private final UiInput uiInput; + private final UiHelper uiHelper; + private final UiOutput uiOutput; + private final UiMessage uiMessage; + + /** + * Constructs a Ui object. + */ + public Ui() { + uiInput = new UiInput(); + uiHelper = new UiHelper(); + uiOutput = new UiOutput(); + uiMessage = new UiMessage(); + } + + // Methods in the Ui class are organised according to their function in the order of: system related, + // database related, person related, food list related, nutritional related and other helper methods. + + // Methods required to print system related commands or messages. + + /** + * Returns the non-empty user input that has been trimmed. + * + * @return The non-empty user input that has been trimmed. + */ + public String getCommand() throws DietException { + return uiInput.getCommand(); + } + + /** + * Prints the welcome message from DietBook when it is first booted up. + */ + public void printWelcomeMessage() { + uiOutput.print(uiMessage.getWelcomeMessage()); + } + + /** + * Prints a message asking the user to input their personal information related to dieting and health + * which includes gender, age, height, activity level, original weight and target weight. + * + * @param name The name of the user. + */ + public void printAskForUserInfoMessage(String name) { + uiHelper.performAssertionsForStringInputs(name, "Name"); + + uiOutput.print(uiMessage.getAskForUserInfoMessage(name)); + } + + /** + * Prints an exit message when DietBook is closed. + * + */ + public void printExitMessage() { + uiOutput.print(uiMessage.getExitMessage()); + } + + /** + * Prints an error message given what or where the error is. + * + * @param errorMessage Message detailing what or where the error is. + */ + public void printErrorMessage(String errorMessage) { + uiHelper.performAssertionsForStringInputs(errorMessage, "Error message"); + + uiOutput.print(uiMessage.getErrorMessage(errorMessage)); + } + + /** + * Prints a message that notifies the user that DietBook has been initialised. + */ + public void printInitialisationCompleteMessage() { + uiOutput.print(uiMessage.getInitialisationCompleteMessage()); + } + + /** + * Prints a message informing the user that DietBook has successfully saved all their data. + */ + public void dataSuccessfullySavedMessage() { + uiOutput.print(uiMessage.getDataSuccessfullySavedMessage()); + } + + /** + * Prints the welcome back message when user reboots up DietBook after the first initialisation. + * + * @param name The name of the user. + */ + public void printWelcomeBackMessage(String name) { + uiHelper.performAssertionsForStringInputs(name, "Name"); + + uiOutput.print(uiMessage.getWelcomeBackMessage(name)); + } + + /** + * Prints a string representation of a list of the commands that users can use. + */ + public void printHelpCommandMessage() { + uiOutput.print(uiMessage.getHelpCommandMessage()); + } + + // Methods required to print database related commands or messages. + + /** + * Prints all the food in the database sorted by the canteen and then the store it is found. + * + * @param foodDatabase The string representation of all the food items stored in the database. + */ + public void printDatabase(String foodDatabase) { + uiHelper.performAssertionsForStringInputs(foodDatabase,"Database"); + + uiOutput.print(uiMessage.getDatabaseMessage(foodDatabase)); + } + + // Methods required to print user information related commands and messages. + + /** + * Prints all the information related to the user. + * + * @param personInfo The user's personal information. + */ + public void printPersonInfo(String personInfo) { + uiHelper.performAssertionsForStringInputs(personInfo,"Person information"); + + uiOutput.print(uiMessage.getPersonInfoMessage(personInfo)); + } + + /** + * Prints all the updated information related to the user. + * + * @param personInfo The user's personal information. + */ + public void printEditedPersonInfo(String personInfo) { + uiHelper.performAssertionsForStringInputs(personInfo,"Updated person information"); + + uiOutput.print(uiMessage.getEditedPersonInfoMessage(personInfo)); + } + + // Methods required for printing FoodList related commands and messages. + + /** + * Prints all the food items in the food list in the order that they were added or a message stating + * that the food list is empty if there are no food items. + * + * @param allFood The string representation of all the food items in the food list. + */ + public void printFoodList(String allFood) { + uiHelper.performAssertionsForNullStringInputs(allFood, + "String representation of all food in food list"); + + if (uiHelper.isEmptyString(allFood)) { + uiOutput.print(uiMessage.getEmptyFoodListMessage()); + } else { + uiOutput.print(uiMessage.getFoodListMessage(allFood)); + } + } + + /** + * Prints food items recorded into the food list during a given time period in the order that they were + * added or a message stating no food items were recorded during the given time period. + * + * @param foods The string representation of food items in the food list recorded during the time + * period given. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printFoodList(String foods, LocalDateTime start, LocalDateTime end) { + uiHelper.performAssertionsForNullStringInputs(foods, + "String representation of food items in the food list recorded during the " + + "time period given"); + uiHelper.performAssertionsForTimePeriod(start, end); + + if (uiHelper.isEmptyString(foods)) { + uiOutput.print(uiMessage.getEmptyFoodListMessage(start, end)); + } else { + uiOutput.print(uiMessage.getFoodListMessage(foods, start, end)); + } + } + + /** + * Prints food items recorded into the food list after a given timing in the order that they were + * added or a message stating no food items were recorded after the given timing till now. + * + * @param foods The string representation of food items in the food list recorded from the given time + * till now. + * @param start Starting date time of the time period till now. + */ + public void printFoodList(String foods, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printFoodList(foods, start, end); + } + + /** + * Prints a message to show that the food specified has been added to the food list. + * + * @param newFood The string representation of the new food item that was added to the food list. + */ + public void printNewFood(String newFood) { + uiHelper.performAssertionsForStringInputs(newFood, + "String representation of the food that was added"); + + uiOutput.print(uiMessage.getNewFoodMessage(newFood)); + } + + /** + * Prints a message to show that the food specified has been deleted from the food list. + * + * @param deletedFood The string representation of the food that was deleted from the food list. + */ + public void printDeletedFood(String deletedFood) { + uiHelper.performAssertionsForStringInputs(deletedFood, + "String representation of the food that was deleted"); + + uiOutput.print(uiMessage.getDeletedFoodMessage(deletedFood)); + } + + /** + * Prints a message to show that the food list has been successfully cleared and is now empty. + */ + public void printClearFoodListMessage() { + uiOutput.print(uiMessage.getClearFoodListMessage()); + } + + // Methods required to print nutritional intake and recommendation related commands and messages. + + /** + * Prints the daily recommended calorie intake of the user based on the user's personal information. + * + * @param calorieRecommendation The daily recommended calorie intake of the user. + */ + public void printCalorieRecommendation(String name, int calorieRecommendation) { + uiHelper.performAssertionsForStringInputs(name, "Name"); + uiHelper.performAssertionsForCalorieRecommendation(calorieRecommendation); + + uiOutput.print(uiMessage.getCalorieRecommendationMessage(name, calorieRecommendation)); + } + + /** + * Prints the total amount of carbohydrates consumed by the user. + * + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + */ + public void printCarbIntake(int carbIntake) { + uiOutput.print(uiMessage.getOneIntakeMessage(carbIntake, "carbohydrate", + "g")); + } + + /** + * Prints the total amount of carbohydrates consumed by the user given a certain time period. + * + * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printCarbIntake(int carbIntake, LocalDateTime start, LocalDateTime end) { + String stringCarbIntake = uiMessage.getOneIntakeMessage(carbIntake,"carbohydrate", "g"); + uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringCarbIntake, start, end)); + } + + /** + * Prints the total amount of carbohydrates consumed by the user given a start date. + * + * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the + * start date till now. + * @param start Starting date time to calculate from. + */ + public void printCarbIntake(int carbIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCarbIntake(carbIntake, start, end); + } + + /** + * Prints the total amount of calories consumed by the user. + * + * @param calorieIntake The total amount of calories of all the food in the food list. + */ + public void printCalorieIntake(int calorieIntake) { + uiOutput.print(uiMessage.getOneIntakeMessage(calorieIntake, "calorie", "kcal")); + } + + /** + * Prints the total amount of calories consumed by the user given a certain time period. + * + * @param calorieIntake The total amount of calories of food in the food list recorded during the + * time period given. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDateTime end) { + String stringCalorieIntake = uiMessage.getOneIntakeMessage(calorieIntake,"calorie", + "kcal"); + uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringCalorieIntake, start, end)); + } + + /** + * Prints the total amount of calories consumed by the user given a start date. + * + * @param calorieIntake The total amount of calories of food in the food list recorded from the + * start date till now. + * @param start Starting date time to calculate from. + */ + public void printCalorieIntake(int calorieIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCalorieIntake(calorieIntake, start, end); + } + + /** + * Prints the total amount of proteins consumed by the user. + * + * @param proteinIntake The total amount of proteins of all the food in the food list. + */ + public void printProteinIntake(int proteinIntake) { + uiOutput.print(uiMessage.getOneIntakeMessage(proteinIntake, "protein", "g")); + } + + /** + * Prints the total amount of proteins consumed by the user given a certain time period. + * + * @param proteinIntake The total amount of proteins of food in the food list recorded during the + * time period given. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printProteinIntake(int proteinIntake, LocalDateTime start, LocalDateTime end) { + String stringProteinIntake = uiMessage.getOneIntakeMessage(proteinIntake, "protein", "g"); + uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringProteinIntake, start, end)); + } + + /** + * Prints the total amount of proteins consumed by the user given a start date. + * + * @param proteinIntake The total amount of proteins of food in the food list recorded from the + * start date till now. + * @param start Starting date time to calculate from. + */ + public void printProteinIntake(int proteinIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printProteinIntake(proteinIntake, start, end); + } + + /** + * Prints the total amount of fats consumed by the user. + * + * @param fatIntake The total amount of fats of all the food in the food list. + */ + public void printFatIntake(int fatIntake) { + uiOutput.print(uiMessage.getOneIntakeMessage(fatIntake, "fat", "g")); + } + + /** + * Prints the total amount of fats consumed by the user given a certain time period. + * + * @param fatIntake The total amount of fats of food in the food list recorded during the + * time period given. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printFatIntake(int fatIntake, LocalDateTime start, LocalDateTime end) { + String stringFatIntake = uiMessage.getOneIntakeMessage(fatIntake,"fat", "g"); + uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringFatIntake, start, end)); + } + + /** + * Prints the total amount of fats consumed by the user given a start date. + * + * @param fatIntake The total amount of fats of food in the food list recorded from the start date till + * now. + * @param start Starting date time to calculate from. + */ + public void printFatIntake(int fatIntake, LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printFatIntake(fatIntake, start, end); + } + + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user. + * + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + * @param calorieIntake The total amount of calories of all the food in the food list. + * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param fatIntake The total amount of fats of all the food in the food list. + */ + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake) { + uiOutput.print(uiMessage.getAllIntakeMessage(calorieIntake, carbIntake, proteinIntake, + fatIntake)); + } + + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user given a + * certain time period. + * + * @param calorieIntake The total amount of calories of food in the food list recorded during the + * time period given. + * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the + * time period given. + * @param proteinIntake The total amount of proteins of food in the food list recorded during the + * time period given. + * @param fatIntake The total amount of fats of food in the food list recorded during the + * time period given. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, LocalDateTime start, LocalDateTime end) { + String allIntake = uiMessage.getAllIntakeMessage(calorieIntake, carbIntake, proteinIntake, fatIntake); + uiOutput.print(uiMessage.getIntakeWithTimeMessage(allIntake, start, end)); + } + + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user given a + * start date. + * + * @param calorieIntake The total amount of calories of food in the food list recorded from the start + * date till now. + * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the start + * date till now. + * @param proteinIntake The total amount of proteins of food in the food list recorded from the start + * date till now. + * @param fatIntake The total amount of fats of food in the food list recorded from the start date till + * now. + * @param start Starting date time to calculate from. + */ + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printAllIntake(calorieIntake, carbIntake, proteinIntake, fatIntake, start, end); + } + + /** + * Prints the total amount of carbohydrates consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total carbohydrate intake. + * + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods) { + uiOutput.print(uiMessage.getOneIntakeAndFoodsMessage(carbIntake, "carbohydrate", + "g", recalculatedFoods)); + } + + /** + * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had + * their nutritional information recalculated by DietBook if any, given a certain time period. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total carbohydrate + * intake within a given time period. + * + * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String carbIntakeAndFoodsWithoutTime = uiMessage.getOneIntakeAndFoodsMessage(carbIntake, + "carbohydrate", "g", recalculatedFoods); + uiOutput.print(uiMessage.getIntakeAndFoodsWithTimeMessage(carbIntakeAndFoodsWithoutTime, start, end)); + } + + /** + * Prints the total amount of carbohydrates consumed by the user and a list of the foods which had + * their nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total carbohydrate + * intake given a start date. + * + * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the + * start date till now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printCarbIntakeAndFoods(int carbIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCarbIntakeAndFoods(carbIntake, recalculatedFoods, start, end); + } + + + /** + * Prints the total amount of calories consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total calorie intake. + * + * @param calorieIntake The total amount of calories of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods) { + uiOutput.print(uiMessage.getOneIntakeAndFoodsMessage(calorieIntake, "calorie", "kcal", + recalculatedFoods)); + } + + /** + * Prints the total amount of calories consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a certain time period. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total calorie + * intake within a given time period. + * + * @param calorieIntake The total amount of calories of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String calorieIntakeAndFoodsWithoutTime = uiMessage.getOneIntakeAndFoodsMessage(calorieIntake, + "calorie", "kcal", recalculatedFoods); + uiOutput.print(uiMessage.getIntakeAndFoodsWithTimeMessage(calorieIntakeAndFoodsWithoutTime, start, end)); + } + + /** + * Prints the total amount of calories consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total calorie intake, + * given a start date. + * + * @param calorieIntake The total amount of calories of food in the food list recorded from the + * start date till now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printCalorieIntakeAndFoods(int calorieIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printCalorieIntakeAndFoods(calorieIntake, recalculatedFoods, start, end); + } + + + /** + * Prints the total amount of proteins consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total protein intake. + * + * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods) { + uiOutput.print(uiMessage.getOneIntakeAndFoodsMessage(proteinIntake, "protein", "g", + recalculatedFoods)); + } + + /** + * Prints the total amount of proteins consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a certain time period. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total protein + * intake within a given time period. + * + * @param proteinIntake The total amount of proteins of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String proteinIntakeAndFoodsWithoutTime = uiMessage.getOneIntakeAndFoodsMessage(proteinIntake, + "protein", "g", recalculatedFoods); + uiOutput.print(uiMessage.getIntakeAndFoodsWithTimeMessage(proteinIntakeAndFoodsWithoutTime, start, end)); + } + + /** + * Prints the total amount of proteins consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total protein intake, + * given a start date. + * + * @param proteinIntake The total amount of proteins of food in the food list recorded from the + * start date till now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printProteinIntakeAndFoods(int proteinIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printProteinIntakeAndFoods(proteinIntake, recalculatedFoods, start, end); + } + + + /** + * Prints the total amount of fats consumed by the user and the list of food items which had + * their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total fat intake. + * + * @param fatIntake The total amount of fats of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods) { + uiOutput.print(uiMessage.getOneIntakeAndFoodsMessage(fatIntake, "fat", "g", + recalculatedFoods)); + } + + /** + * Prints the total amount of fats consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a certain time period. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total fat + * intake within a given time period. + * + * @param fatIntake The total amount of fats of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String fatIntakeAndFoodsWithoutTime = uiMessage.getOneIntakeAndFoodsMessage(fatIntake, + "fat", "g", recalculatedFoods); + uiOutput.print(uiMessage.getIntakeAndFoodsWithTimeMessage(fatIntakeAndFoodsWithoutTime, start, end)); + } + + /** + * Prints the total amount of fats consumed by the user and a list of the foods which had their + * nutritional information recalculated by DietBook if any, given a start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating total fat intake, + * given a start date. + * + * @param fatIntake The total amount of fats of food in the food list recorded from the start date till + * now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printFatIntakeAndFoods(int fatIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printFatIntakeAndFoods(fatIntake, recalculatedFoods, start, end); + } + + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and the + * list of food items which had their nutritional information recalculated by DietBook if any. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating the individual intakes. + * + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + * @param calorieIntake The total amount of calories of all the food in the food list. + * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param fatIntake The total amount of fats of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + */ + public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, String recalculatedFoods) { + uiOutput.print(uiMessage.getAllIntakeAndFoodsMessage(calorieIntake, carbIntake, proteinIntake, + fatIntake, recalculatedFoods)); + } + + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a + * list of the foods which had their nutritional information recalculated by DietBook if any, given a + * certain time period. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating the individual intakes + * within a given time period. + * + * @param calorieIntake The total amount of calories of food in the food list recorded during the + * time period given. + * @param carbIntake The total amount of carbohydrates of food in the food list recorded during the + * time period given. + * @param proteinIntake The total amount of proteins of food in the food list recorded during the + * time period given. + * @param fatIntake The total amount of fats of food in the food list recorded during the + * time period given. + * @param recalculatedFoods The list of food items recorded during the given time period which had their + * nutritional information recalculated by DietBook. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, String recalculatedFoods, + LocalDateTime start, LocalDateTime end) { + String allIntakeAndFoodsWithoutTime = uiMessage.getAllIntakeAndFoodsMessage(calorieIntake, + carbIntake, proteinIntake, fatIntake, recalculatedFoods); + uiOutput.print(uiMessage.getIntakeAndFoodsWithTimeMessage(allIntakeAndFoodsWithoutTime, start, end)); + } + + /** + * Prints the total amount of calories, carbohydrates, fats and proteins consumed by the user and a + * list of the foods which had their nutritional information recalculated by DietBook if any, given a + * start date. + * Some food items only have partial nutritional information as users did not provide all the + * information when the food items were added. Hence, DietBook does an internal calculation for the + * the missing information and these calculated values are used when tabulating the individual intakes, + * given a start date. + * + * @param calorieIntake The total amount of calories of food in the food list recorded from the start + * date till now. + * @param carbIntake The total amount of carbohydrates of food in the food list recorded from the start + * date till now. + * @param proteinIntake The total amount of proteins of food in the food list recorded from the start + * date till now. + * @param fatIntake The total amount of fats of food in the food list recorded from the start date till + * now. + * @param recalculatedFoods The list of food items recorded from the start date till now which had + * their nutritional information recalculated by DietBook. + * @param start Starting date time to calculate from. + */ + public void printAllIntakeAndFoods(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, String recalculatedFoods, + LocalDateTime start) { + LocalDateTime end = LocalDateTime.now(); + printAllIntakeAndFoods(calorieIntake, carbIntake, proteinIntake, fatIntake, recalculatedFoods, start, + end); + } +} diff --git a/src/main/java/seedu/dietbook/ui/UiHelper.java b/src/main/java/seedu/dietbook/ui/UiHelper.java new file mode 100644 index 0000000000..3653ef319a --- /dev/null +++ b/src/main/java/seedu/dietbook/ui/UiHelper.java @@ -0,0 +1,127 @@ +package seedu.dietbook.ui; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Represents a text user interface that deals with taking in user commands. + * A UiHelper object contains all the helper methods needed for the other Ui related classes. + */ +public class UiHelper { + + static final String LINE_SEPARATOR = System.lineSeparator(); + + private final Logger logger; + + public UiHelper() { + logger = Logger.getLogger(UiHelper.class.getName()); + initialiseLogger(); + } + + /** + * Initialises the logger and sets the log level. + */ + void initialiseLogger() { + Handler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.WARNING); + logger.addHandler(consoleHandler); + logger.setLevel(Level.WARNING); + } + + /** + * Returns true if the string length is zero after it has been trimmed for leading and trailing + * spaces, false otherwise. + * + * @param string The string to be trimmed and determined if length is zero. + * @return True if the string length is zero after it has been trimmed for leading and trailing + * spaces, false otherwise. + */ + boolean isEmptyString(String string) { + logger.log(Level.FINE, "String to check if empty: " + string); + performAssertionsForNullStringInputs(string, "String to be determined if empty"); + + return trimString(string).length() == 0; + } + + /** + * Returns a string that has been trimmed for leading and trailing spaces. + * + * @param string The string to be trimmed for leading and trailing spaces. + * @return A string that has been trimmed for leading and trailing spaces. + */ + String trimString(String string) { + logger.log(Level.FINE, "String to trim: " + string); + performAssertionsForNullStringInputs(string, "String to trim"); + + return string.trim(); + } + + /** + * Performs assertions for the string inputs. + * + * @param string The string input. + * @param stringDescription A description of what the string input represents. + */ + void performAssertionsForStringInputs(String string, String stringDescription) { + performAssertionsForNullStringInputs(string, stringDescription); + assert !isEmptyString(string) : stringDescription + " should not be an empty string"; + } + + /** + * Performs assertions for time inputs. + * + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + */ + void performAssertionsForTimePeriod(LocalDateTime start, LocalDateTime end) { + assert start != null : "Starting date time of the time period given should not be null"; + assert end != null : "Ending date time of the time period given should not be null"; + assert !start.isAfter(end) : "Starting date time should not be later than ending date time " + + "of the time period"; + LocalDateTime now = LocalDateTime.now(); + assert !start.isAfter(now) : "Starting date time of the time period given should " + + "not be in the future" + start + LocalDateTime.now(); + assert !end.isAfter(now) : "Ending date time of the time period given should not be" + + " in the future" + end + LocalDateTime.now(); + } + + /** + * Performs assertions for null string inputs. + * + * @param string The string input. + * @param stringDescription A description of what the string input represents. + */ + void performAssertionsForNullStringInputs(String string, String stringDescription) { + assert string != null : stringDescription + " should not be null"; + } + + /** + * Performs assertions for nutritional intake inputs. + * + * @param nutrientIntake The nutritional intake value. + * @param nutrientType The nutrient type. + */ + void performAssertionsForNutritionalIntake(int nutrientIntake, String nutrientType) { + assert nutrientIntake >= 0 : "Total " + nutrientType + " intake should be equals to or greater than 0"; + } + + /** + * Performs assertions for calorie recommendation input. + * + * @param calorieRecommendation The recommended daily calorie intake for the user. + */ + void performAssertionsForCalorieRecommendation(int calorieRecommendation) { + // A minimum daily intake of 1200 calorie is required to stay healthy. + int minCalorie = 1200; + assert calorieRecommendation >= minCalorie : "Daily calorie recommendation should be equals to or " + + "greater than " + minCalorie; + // Highest calorie intake for an athlete currently stands at 12000. + int maxCalorie = 12000; + assert calorieRecommendation <= maxCalorie : "Daily calorie recommendation should be equals to or " + + "less than " + maxCalorie; + } +} \ No newline at end of file diff --git a/src/main/java/seedu/dietbook/ui/UiInput.java b/src/main/java/seedu/dietbook/ui/UiInput.java new file mode 100644 index 0000000000..ed96bf21f7 --- /dev/null +++ b/src/main/java/seedu/dietbook/ui/UiInput.java @@ -0,0 +1,78 @@ +package seedu.dietbook.ui; + +import seedu.dietbook.exception.DietException; + +import java.util.Scanner; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Represents a text user interface that deals with taking in user commands. + * A UiInput objects deals with user interaction by taking in user inputs and processing them. + */ +public class UiInput { + + private static Scanner scanner = new Scanner(System.in); + + private final Logger logger; + + private final UiHelper uiHelper; + + /** + * Constructs a UiInput object. + */ + public UiInput() { + uiHelper = new UiHelper(); + logger = Logger.getLogger(UiInput.class.getName()); + initialiseLogger(); + } + + /** + * Returns the non-empty user command that has been trimmed. + * + * @return The non-empty user command that has been trimmed. + */ + String getCommand() throws DietException { + String command = readCommand(); + String processedCommand = processCommand(command); + logger.log(Level.FINE, "Processed user command: " + processedCommand); + return processedCommand; + } + + /** + * Reads in and returns the user command. + * + * @return The user command. + */ + String readCommand() { + return scanner.nextLine(); + } + + /** + * Returns the trimmed user command if its is not empty, else a DietException is thrown. + * + * @param command The user command. + * @return The trimmed user command if it is not empty. + * @throws DietException If the the user command is empty after trimming. + */ + String processCommand(String command) throws DietException { + logger.log(Level.FINE, "User command to process: " + command); + if (uiHelper.isEmptyString(command)) { + logger.log(Level.WARNING, "Command is empty!"); + throw new DietException("Command is empty!"); + } + return uiHelper.trimString(command); + } + + /** + * Initialises the logger and sets the log level. + */ + void initialiseLogger() { + Handler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.SEVERE); + logger.addHandler(consoleHandler); + logger.setLevel(Level.SEVERE); + } +} \ No newline at end of file diff --git a/src/main/java/seedu/dietbook/ui/UiMessage.java b/src/main/java/seedu/dietbook/ui/UiMessage.java new file mode 100644 index 0000000000..7a80780239 --- /dev/null +++ b/src/main/java/seedu/dietbook/ui/UiMessage.java @@ -0,0 +1,414 @@ +package seedu.dietbook.ui; + +import seedu.dietbook.person.FitnessLevel; +import seedu.dietbook.person.Gender; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Represents a storage that stores all the messages that Ui can utilise and print out. + * A UiMessage objects contains all the necessary methods required to retrieve the output + * messages. + */ +public class UiMessage { + + private final UiHelper uiHelper; + + private final Logger logger; + + public UiMessage() { + uiHelper = new UiHelper(); + logger = Logger.getLogger(UiMessage.class.getName()); + initialiseLogger(); + } + + /** + * Initialises the logger and sets the log level. + */ + void initialiseLogger() { + Handler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.WARNING); + logger.addHandler(consoleHandler); + logger.setLevel(Level.WARNING); + } + + String getWelcomeMessage() { + return getLogo() + UiHelper.LINE_SEPARATOR + + "Hello! Welcome to DietBook!" + UiHelper.LINE_SEPARATOR + + "I am Diet, your guide to using DietBook. How may I address you?" + UiHelper.LINE_SEPARATOR + + "Please input your name or nickname in the following format:" + UiHelper.LINE_SEPARATOR + + " name YOUR_NAME_OR_NICKNAME" + UiHelper.LINE_SEPARATOR + + " Example: name Jack"; + } + + String getAskForUserInfoMessage(String name) { + return "Hi " + uiHelper.trimString(name) + "!" + UiHelper.LINE_SEPARATOR + + "Before we get started, I would like to know about about you so that I can make more " + + UiHelper.LINE_SEPARATOR + + "accurate calculations for you :). Therefore, could you please share with me the " + + "following:" + UiHelper.LINE_SEPARATOR + + "- Your gender either F for " + Gender.FEMALE.getDescription() + " or M for " + + Gender.MALE.getDescription() + " or O for " + Gender.OTHERS.getDescription() + "." + + UiHelper.LINE_SEPARATOR + + "- Your age which is a positive integer." + UiHelper.LINE_SEPARATOR + + "- Your height in cm." + UiHelper.LINE_SEPARATOR + + "- Your original weight in kg, the weight when you first started using DietBook or " + + "you current weight." + UiHelper.LINE_SEPARATOR + + "- Your current weight in kg." + UiHelper.LINE_SEPARATOR + + "- Your target weight in kg, or your current weight if that is also your target weight." + + UiHelper.LINE_SEPARATOR + + "- Your fitness level, represented by a number from 1 to 5." + UiHelper.LINE_SEPARATOR + + " 1 = " + FitnessLevel.NONE.getDescription() + UiHelper.LINE_SEPARATOR + + " 2 = " + FitnessLevel.LOW.getDescription() + UiHelper.LINE_SEPARATOR + + " 3 = " + FitnessLevel.MEDIUM.getDescription() + UiHelper.LINE_SEPARATOR + + " 4 = " + FitnessLevel.HIGH.getDescription() + UiHelper.LINE_SEPARATOR + + " 5 = " + FitnessLevel.EXTREME.getDescription() + UiHelper.LINE_SEPARATOR + + UiHelper.LINE_SEPARATOR + + "Please input your details in the following format:" + UiHelper.LINE_SEPARATOR + + " info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT " + + "f/FITNESS_LEVEL" + UiHelper.LINE_SEPARATOR + + " Example: info g/F a/21 h/165 o/65 c/65 t/55 f/2"; + } + + String getExitMessage() { + return "Bye! Hope to see you again soon!"; + } + + String getErrorMessage(String errorMessage) { + return ":( " + uiHelper.trimString(errorMessage); + } + + String getInitialisationCompleteMessage() { + return "Thank you! DietBook has been initialised " + getStartMessage(); + } + + String getDataSuccessfullySavedMessage() { + return "Your data has been saved successfully."; + } + + String getWelcomeBackMessage(String name) { + return getLogo() + UiHelper.LINE_SEPARATOR + "Welcome back to DietBook " + uiHelper.trimString(name) + + "!" + UiHelper.LINE_SEPARATOR + + "All your previous data has been successfully loaded " + getStartMessage(); + } + + String getHelpCommandMessage() { + return "Listed below are the valid commands for DietBook:" + + UiHelper.LINE_SEPARATOR + UiHelper.LINE_SEPARATOR + + getUserInfoRelatedCommands() + UiHelper.LINE_SEPARATOR + + getDatabaseRelatedCommands() + UiHelper.LINE_SEPARATOR + + getFoodListRelatedCommands() + UiHelper.LINE_SEPARATOR + + getNutritionalRelatedCommands() + UiHelper.LINE_SEPARATOR + + getSystemRelatedCommands(); + } + + String getDatabaseMessage(String foodDatabase) { + return "Here are the food items in the database:" + UiHelper.LINE_SEPARATOR + foodDatabase; + } + + String getPersonInfoMessage(String personInfo) { + return "Here is your information:" + UiHelper.LINE_SEPARATOR + personInfo; + } + + String getEditedPersonInfoMessage(String personInfo) { + return "Got it! I've updated your personal information:" + UiHelper.LINE_SEPARATOR + personInfo; + } + + String getFoodListMessage(String allFood) { + return "Here are the food items in DietBook:" + UiHelper.LINE_SEPARATOR + allFood; + } + + String getFoodListMessage(String foods, LocalDateTime start, LocalDateTime end) { + return "Here are the food items recorded in DietBook" + stringDateTimePeriod(start, end) + + ":" + UiHelper.LINE_SEPARATOR + foods; + } + + String getEmptyFoodListMessage() { + return "DietBook is currently empty."; + } + + String getEmptyFoodListMessage(LocalDateTime start, LocalDateTime end) { + return "No food item was recorded in DietBook" + stringDateTimePeriod(start, end) + "."; + } + + String getNewFoodMessage(String newFood) { + return "Got it! I've added this food item:" + UiHelper.LINE_SEPARATOR + " " + + uiHelper.trimString(newFood); + } + + String getDeletedFoodMessage(String deletedFood) { + return "Noted. I've removed this food item:" + UiHelper.LINE_SEPARATOR + " " + + uiHelper.trimString(deletedFood); + } + + String getClearFoodListMessage() { + return "All previous data has been deleted..." + UiHelper.LINE_SEPARATOR + "DietBook is now empty."; + } + + String getCalorieRecommendationMessage(String name, int calorieRecommendation) { + return "Hi " + uiHelper.trimString(name) + "!" + UiHelper.LINE_SEPARATOR + + "Here is your daily recommended calorie intake: " + calorieRecommendation + "kcal"; + } + + String getLogo() { + return " _______ __ ______ ________ _______ ______ ______ __ __" + UiHelper.LINE_SEPARATOR + + "| __ \\| | ___|__ __| __ \\ / __ \\ / __ \\| | / /" + UiHelper.LINE_SEPARATOR + + "| | | | | |___ | | | |__| | | | | | | | |/ /" + UiHelper.LINE_SEPARATOR + + "| | | | | ___| | | | __ <| | | | | | | /" + UiHelper.LINE_SEPARATOR + + "| |__| | | |___ | | | |__| | |__| | | | | |\\ \\" + UiHelper.LINE_SEPARATOR + + "|_______/|__|______| |__| |_______/ \\______/ \\______/|__| \\__\\" + UiHelper.LINE_SEPARATOR; + + } + + /** + * Returns a string stating that DietBook is ready for use. + * + * @return A string stating that DietBook is ready for use. + */ + String getStartMessage() { + return "and you may start by entering any valid commands. " + + UiHelper.LINE_SEPARATOR + + "If you require a list of valid commands, you can enter: help"; + } + + String getSystemRelatedCommands() { + return "For other system related commands" + UiHelper.LINE_SEPARATOR + + " To view a list of valid commands: help" + UiHelper.LINE_SEPARATOR + + " To exit DietBook: exit"; + } + + String getNutritionalRelatedCommands() { + return "For nutritional intake and recommendation related commands" + UiHelper.LINE_SEPARATOR + + " To get recommended calorie intake: recommend" + UiHelper.LINE_SEPARATOR + + " To calculate nutritional intake: calculate NUTRIENT_TYPE [yyyy-mm-ddTHH:mm] " + + "[yyyy-mm-ddTHH:mm]" + UiHelper.LINE_SEPARATOR + + " Valid NUTRIENT_TYPE: carb, calorie, fat, protein, all" + UiHelper.LINE_SEPARATOR; + } + + String getUserInfoRelatedCommands() { + return "For user information related commands" + UiHelper.LINE_SEPARATOR + + " To view user information: userinfo" + UiHelper.LINE_SEPARATOR + + " To edit user information: editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] " + + "[o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]" + + UiHelper.LINE_SEPARATOR; + } + + String getFoodListRelatedCommands() { + return "For food list related commands" + UiHelper.LINE_SEPARATOR + + " To add a food not in the database: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE " + + "[c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] [yyyy-mm-ddTHH:mm]" + UiHelper.LINE_SEPARATOR + + " To view all food in DietBook: list [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm]" + + UiHelper.LINE_SEPARATOR + + " To delete a food from DietBook: delete INDEX" + UiHelper.LINE_SEPARATOR + + " To delete all food items from the DietBook: clear" + UiHelper.LINE_SEPARATOR; + } + + String getDatabaseRelatedCommands() { + return "For database related commands" + UiHelper.LINE_SEPARATOR + + " To add a food from the database: add INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm]" + + UiHelper.LINE_SEPARATOR + + " To view all food in the database: data" + UiHelper.LINE_SEPARATOR; + } + + /** + * Returns a string representation of the total amount of a nutrient consumed by the user. + * + * @param nutrientIntake The amount of a particular type of nutrient consumed. + * @param nutrientType A string representation of the type of nutrient consumed. + * @param nutrientUnit A string representation of the unit of the nutrient consumed. + * @return A string representation of the the total amount of a nutrient consumed by the user. + */ + String getOneIntakeMessage(int nutrientIntake, String nutrientType, String nutrientUnit) { + uiHelper.performAssertionsForStringInputs(nutrientType, "Nutrient Type"); + uiHelper.performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); + uiHelper.performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); + + return getNutritionalIntakeMessage(nutrientIntake, nutrientType, nutrientUnit); + } + + /** + * Returns a string representation of the total amount of a nutrient or all nutrients consumed by the + * user during a given time period. + * + * @param intakeWithoutTime A string representation of the the total amount of a nutrient or + * all nutrients consumed by the user. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + * @return A string representation of the the total amount of a nutrient or all nutrient consumed by the + * user during a given time period. + */ + String getIntakeWithTimeMessage(String intakeWithoutTime, LocalDateTime start, LocalDateTime end) { + logger.log(Level.FINE, "Start: " + start); + logger.log(Level.FINE, "End: " + end); + uiHelper.performAssertionsForTimePeriod(start, end); + + String timePeriod = "Time period:" + stringDateTimePeriod(start, end); + return timePeriod + UiHelper.LINE_SEPARATOR + UiHelper.LINE_SEPARATOR + intakeWithoutTime; + } + + /** + * Returns a string representation of the total amount of calories, carbohydrates, fats and proteins + * consumed by the user. + * + * @param calorieIntake The total amount of calories of all the food in the food list. + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param fatIntake The total amount of fats of all the food in the food list. + * @return A string representation of the total amount of each of the nutrients consumed by the user. + */ + String getAllIntakeMessage(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake) { + uiHelper.performAssertionsForNutritionalIntake(calorieIntake, "calorie"); + uiHelper.performAssertionsForNutritionalIntake(carbIntake, "carb"); + uiHelper.performAssertionsForNutritionalIntake(proteinIntake, "protein"); + uiHelper.performAssertionsForNutritionalIntake(fatIntake, "fat"); + + String stringCalorieIntake = getNutritionalIntakeMessage(calorieIntake, "calorie","kcal"); + String stringCarbIntake = getNutritionalIntakeMessage(carbIntake, "carb", "g"); + String stringProteinIntake = getNutritionalIntakeMessage(proteinIntake, "protein", "g"); + String stringFatIntake = getNutritionalIntakeMessage(fatIntake, "fat", "g"); + + return stringCalorieIntake + UiHelper.LINE_SEPARATOR + + stringCarbIntake + UiHelper.LINE_SEPARATOR + + stringProteinIntake + UiHelper.LINE_SEPARATOR + + stringFatIntake + UiHelper.LINE_SEPARATOR; + } + + /** + * Returns a string with a header and recalculatedFoods or a string stating that no food items had their + * nutritional information recalculated if calculatedFoods is an empty string. + * + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + * @return A string with a header and recalculatedFoods or a string stating that no food items had their + * nutritional information recalculated if calculatedFoods is an empty string. + */ + String getRecalculatedFoodsMessage(String recalculatedFoods) { + String message = "No food items had their nutritional information recalculated by DietBook."; + if (!uiHelper.isEmptyString(recalculatedFoods)) { + message = "Food items which had their nutritional information recalculated by DietBook: " + + UiHelper.LINE_SEPARATOR + recalculatedFoods; + } + return message; + } + + /** + * Return a string representation of the amount of a nutrient consumed by the user which can be either + * the total amount consumed or amount consumed in a given time period. + * + * @param nutrientIntake The amount of a particular type of nutrient consumed. + * @param nutrientType A string representation of the type of nutrient consumed. + * @param nutrientUnit A string representation of the unit of the nutrient consumed. + * @return The amount of a nutrient consumed by the user which can be either the total amount consumed + * or amount consumed in a given time period. + */ + String getNutritionalIntakeMessage(int nutrientIntake, String nutrientType, String nutrientUnit) { + return "Total " + nutrientType + " intake: " + nutrientIntake + nutrientUnit; + } + + /** + * Returns a string representation of the total amount of a nutrient consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + * + * @param nutrientIntake The amount of a particular type of nutrient consumed. + * @param nutrientType A string representation of the type of nutrient consumed. + * @param nutrientUnit A string representation of the unit of the nutrient consumed. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + * @return A string representation of the the total amount of a nutrient consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + */ + String getOneIntakeAndFoodsMessage(int nutrientIntake, String nutrientType, + String nutrientUnit, String recalculatedFoods) { + uiHelper.performAssertionsForStringInputs(nutrientType, "Nutrient Type"); + uiHelper.performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); + uiHelper.performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); + uiHelper.performAssertionsForNullStringInputs(recalculatedFoods, + "List of foods that had their nutritional information recalculated"); + + String stringNutrientIntake = getNutritionalIntakeMessage(nutrientIntake, nutrientType, nutrientUnit); + String message = getRecalculatedFoodsMessage(recalculatedFoods); + return stringNutrientIntake + UiHelper.LINE_SEPARATOR + message; + } + + /** + * Returns a string representation of the total amount of a nutrient or all nutrientS consumed by the + * user during a given time period and the list of food items recorded during the same time period + * which had their nutritional information recalculated by DietBook if any. + * + * @param intakeAndFoodsWithoutTime A string representation of the the total amount of a nutrient or + * all nutrients consumed by the user and the list of food items which had their nutritional + * information recalculated by DietBook if any. + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + * @return A string representation of the the total amount of a nutrient or all nutrient consumed by the + * user during a given time period and the list of food items recorded during the same time period + * which had their nutritional information recalculated by DietBook if any. + */ + String getIntakeAndFoodsWithTimeMessage(String intakeAndFoodsWithoutTime, + LocalDateTime start, LocalDateTime end) { + logger.log(Level.FINE, "Start: " + start); + logger.log(Level.FINE, "End: " + end); + uiHelper.performAssertionsForTimePeriod(start, end); + + String timePeriod = "Time period:" + stringDateTimePeriod(start, end); + return timePeriod + UiHelper.LINE_SEPARATOR + UiHelper.LINE_SEPARATOR + intakeAndFoodsWithoutTime; + } + + /** + * Returns a string representation of the total amount of all nutrients consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + * + * @param carbIntake The total amount of carbohydrates of all the food in the food list. + * @param calorieIntake The total amount of calories of all the food in the food list. + * @param proteinIntake The total amount of proteins of all the food in the food list. + * @param fatIntake The total amount of fats of all the food in the food list. + * @param recalculatedFoods The list of food items which had their nutritional information recalculated by + * DietBook. + * @return A string representation of the total amount of all nutrients consumed by the user and + * the list of food items which had their nutritional information recalculated by DietBook if any. + */ + String getAllIntakeAndFoodsMessage(int calorieIntake, int carbIntake, int proteinIntake, + int fatIntake, String recalculatedFoods) { + uiHelper.performAssertionsForNutritionalIntake(carbIntake, "carbohydrate"); + uiHelper.performAssertionsForNutritionalIntake(calorieIntake, "calorie"); + uiHelper.performAssertionsForNutritionalIntake(proteinIntake, "protein"); + uiHelper.performAssertionsForNutritionalIntake(fatIntake, "fat"); + uiHelper.performAssertionsForNullStringInputs(recalculatedFoods, + "List of foods that had their nutritional information recalculated"); + + String stringCarbIntake = getNutritionalIntakeMessage(carbIntake, "carbohydrate", "g"); + String stringCalorieIntake = getNutritionalIntakeMessage(calorieIntake, "calorie", "kcal"); + String stringProteinIntake = getNutritionalIntakeMessage(proteinIntake, "protein", "g"); + String stringFatIntake = getNutritionalIntakeMessage(fatIntake, "fat", "g"); + String message = getRecalculatedFoodsMessage(recalculatedFoods); + + return stringCalorieIntake + UiHelper.LINE_SEPARATOR + + stringCarbIntake + UiHelper.LINE_SEPARATOR + + stringProteinIntake + UiHelper.LINE_SEPARATOR + + stringFatIntake + UiHelper.LINE_SEPARATOR + + message; + + } + + /** + * Returns a string representation of the time period with date time in the format dd MMM yyyy HHmm. + * + * @param start Starting date time of the time period given. + * @param end Ending date time of the time period given. + * @return The string representation of time period with date time in the format dd MMM yyyy HHmm. + */ + public String stringDateTimePeriod(LocalDateTime start, LocalDateTime end) { + logger.log(Level.FINE, "Start: " + start); + logger.log(Level.FINE, "End: " + end); + uiHelper.performAssertionsForTimePeriod(start, end); + + String stringStart = start.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); + String stringEnd = end.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); + return " between " + stringStart + " and " + stringEnd; + } +} \ No newline at end of file diff --git a/src/main/java/seedu/dietbook/ui/UiOutput.java b/src/main/java/seedu/dietbook/ui/UiOutput.java new file mode 100644 index 0000000000..17b7986ff3 --- /dev/null +++ b/src/main/java/seedu/dietbook/ui/UiOutput.java @@ -0,0 +1,32 @@ +package seedu.dietbook.ui; + +/** + * Represents a text user interface that is responsible for printing outputs. + * A UiOutput objects deals with user interaction by showing users the appropriate messages + * after a valid command is executed or when an error occurs. + */ +public class UiOutput { + + private final UiHelper uiHelper; + + public UiOutput() { + uiHelper = new UiHelper(); + } + + /** + * Prints the given message to the user. + * + * @param message The message to show the user. + */ + void print(String message) { + uiHelper.performAssertionsForStringInputs(message, "Message to print"); + String divider = + "__________________________________________________________________________________________" + + "___________________________________________"; + + System.out.println(divider + uiHelper.LINE_SEPARATOR + + uiHelper.trimString(message) + uiHelper.LINE_SEPARATOR + + divider); + + } +} \ No newline at end of file diff --git a/src/test/java/seedu/dietbook/ui/UiHelperTest.java b/src/test/java/seedu/dietbook/ui/UiHelperTest.java new file mode 100644 index 0000000000..279cdcf982 --- /dev/null +++ b/src/test/java/seedu/dietbook/ui/UiHelperTest.java @@ -0,0 +1,43 @@ +package seedu.dietbook.ui; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class UiHelperTest { + + private UiHelper uiHelper; + + @BeforeEach + public void setUp() { + uiHelper = new UiHelper(); + } + + @Test + void isEmptyString_nullInput_expectAssertionError() { + assertThrows(AssertionError.class, () -> uiHelper.isEmptyString(null)); + } + + @Test + void isEmptyString_stringWithNoLeadingOrTrailingSpaces_returnsFalse() { + assertFalse(uiHelper.isEmptyString("food")); + } + + @Test + void isEmptyString_StringWithLeadingSpaces_returnsTrue() { + assertTrue(uiHelper.isEmptyString("")); + } + + @Test + void isEmptyString_StringWithTrailingSpaces_returnsTrue() { + assertTrue(uiHelper.isEmptyString(" ")); + } + + @Test + void isEmptyString_StringWithLeadingAndTrailingSpaces_returnsFalse() { + assertFalse(uiHelper.isEmptyString(" food ")); + } +} \ No newline at end of file diff --git a/src/test/java/seedu/dietbook/UiTest.java b/src/test/java/seedu/dietbook/ui/UiMessageTest.java similarity index 60% rename from src/test/java/seedu/dietbook/UiTest.java rename to src/test/java/seedu/dietbook/ui/UiMessageTest.java index b69ebcfbe3..8b31aa41c6 100644 --- a/src/test/java/seedu/dietbook/UiTest.java +++ b/src/test/java/seedu/dietbook/ui/UiMessageTest.java @@ -1,53 +1,53 @@ -package seedu.dietbook; +package seedu.dietbook.ui; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -class UiTest { +class UiMessageTest { - private Ui ui; + private UiMessage uiMessage; @BeforeEach public void setUp() { - ui = new Ui(); + uiMessage = new UiMessage(); } @Test void stringDateTimePeriod_startDateTimeIsNullInput_expectAssertionError() { LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59"); - assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(null, end)); + assertThrows(AssertionError.class, () -> uiMessage.stringDateTimePeriod(null, end)); } @Test void stringDateTimePeriod_endDateTimeIsNullInput_expectAssertionError() { LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); - assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, null)); + assertThrows(AssertionError.class, () -> uiMessage.stringDateTimePeriod(start, null)); } @Test void stringDateTimePeriod_endDateTimeIsBeforeStartTime_expectAssertionError() { LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); LocalDateTime end = LocalDateTime.parse("2020-10-20T23:59"); - assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, end)); + assertThrows(AssertionError.class, () -> uiMessage.stringDateTimePeriod(start, end)); } @Test void stringDateTimePeriod_endDateTimeIsInTheFuTure_expectAssertionError() { LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); LocalDateTime end = LocalDateTime.now().plusDays(3); - assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, end)); + assertThrows(AssertionError.class, () -> uiMessage.stringDateTimePeriod(start, end)); } @Test void stringDateTimePeriod_StartDateTimeIsInTheFuture_expectAssertionError() { LocalDateTime start = LocalDateTime.now().plusDays(3); LocalDateTime end = LocalDateTime.now().plusDays(5); - assertThrows(AssertionError.class, () -> ui.stringDateTimePeriod(start, end)); + assertThrows(AssertionError.class, () -> uiMessage.stringDateTimePeriod(start, end)); } @@ -56,7 +56,7 @@ void stringDateTimePeriod_sameStartAndEndDateTime_returnsStringOfTimePeriod() { LocalDateTime start = LocalDateTime.parse("2020-10-21T23:59"); LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59"); assertEquals(" between 21 Oct 2020 2359 and 21 Oct 2020 2359", - ui.stringDateTimePeriod(start, end)); + uiMessage.stringDateTimePeriod(start, end)); } @Test @@ -64,7 +64,7 @@ void stringDateTimePeriod_validStartAndEndDateTime_returnsStringOfTimePeriod() { LocalDateTime start = LocalDateTime.parse("2020-10-19T23:59"); LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59"); assertEquals(" between 19 Oct 2020 2359 and 21 Oct 2020 2359", - ui.stringDateTimePeriod(start, end)); + uiMessage.stringDateTimePeriod(start, end)); } @Test @@ -72,31 +72,7 @@ void stringDateTimePeriod_validStartAndEndDateTimeWithSeconds_returnsStringOfTim LocalDateTime start = LocalDateTime.parse("2020-10-19T23:59:22"); LocalDateTime end = LocalDateTime.parse("2020-10-21T23:59:22"); assertEquals(" between 19 Oct 2020 2359 and 21 Oct 2020 2359", - ui.stringDateTimePeriod(start, end)); - } - - @Test - void trimStringGetLength_nullInput_expectAssertionError() { - assertThrows(AssertionError.class, () -> ui.trimStringGetLength(null)); - } - - @Test - void trimStringGetLength_stringWithNoLeadingOrTrailingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength("food")); - } - - @Test - void trimStringGetLength_StringWithLeadingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength(" food")); + uiMessage.stringDateTimePeriod(start, end)); } - @Test - void trimStringGetLength_StringWithTrailingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength("food ")); - } - - @Test - void trimStringGetLength_StringWithLeadingAndTrailingSpaces_returnsLengthFour() { - assertEquals(4, ui.trimStringGetLength(" food ")); - } -} +} \ No newline at end of file From d6b7f3581f083b72b40c3d5a70993c270394ab2a Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 5 Nov 2020 02:13:00 +0800 Subject: [PATCH 280/374] Change global variable to local --- .../java/seedu/dietbook/person/Person.java | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index 5599fdcd3e..eab6392654 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -23,7 +23,7 @@ public class Person { private FitnessLevel fitnessLevel; private Gender gender; private String name; - private static final Logger LOGGER = Logger.getLogger(Person.class.getName()); + private final Logger logger; /** * Constructs a Person with the given name, gender, age, height, fitness level, original, @@ -44,19 +44,20 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh performAssertionsForPerson(name, gender, age, height, originalWeight, currentWeight, targetWeight, fitnessLevel); + logger = Logger.getLogger(Person.class.getName()); initialiseLogger(); - LOGGER.log(Level.FINE, "Start constructing a person"); - LOGGER.log(Level.FINE, "Name: " + name); - LOGGER.log(Level.FINE, "Gender: " + gender.getDescription()); - LOGGER.log(Level.FINE, "Age: " + age); - LOGGER.log(Level.FINE, "Height: " + height); - LOGGER.log(Level.FINE, "Original weight: " + originalWeight); - LOGGER.log(Level.FINE, "Current weight: " + currentWeight); - LOGGER.log(Level.FINE, "Target weight: " + targetWeight); - LOGGER.log(Level.FINE, "Fitness Level: " + fitnessLevel.getDescription()); + logger.log(Level.FINE, "Start constructing a person"); + logger.log(Level.FINE, "Name: " + name); + logger.log(Level.FINE, "Gender: " + gender.getDescription()); + logger.log(Level.FINE, "Age: " + age); + logger.log(Level.FINE, "Height: " + height); + logger.log(Level.FINE, "Original weight: " + originalWeight); + logger.log(Level.FINE, "Current weight: " + currentWeight); + logger.log(Level.FINE, "Target weight: " + targetWeight); + logger.log(Level.FINE, "Fitness Level: " + fitnessLevel.getDescription()); this.name = name.trim(); - LOGGER.log(Level.FINE, "Trimmed Name: " + this.name); + logger.log(Level.FINE, "Trimmed Name: " + this.name); this.gender = gender; this.age = age; this.height = height; @@ -64,7 +65,7 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh this.currentWeight = currentWeight; this.targetWeight = targetWeight; this.fitnessLevel = fitnessLevel; - LOGGER.log(Level.FINE, "Person constructed"); + logger.log(Level.FINE, "Person constructed"); } /** @@ -73,8 +74,8 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh private void initialiseLogger() { Handler consoleHandler = new ConsoleHandler(); consoleHandler.setLevel(Level.WARNING); - LOGGER.addHandler(consoleHandler); - LOGGER.setLevel(Level.WARNING); + logger.addHandler(consoleHandler); + logger.setLevel(Level.WARNING); } /** @@ -119,9 +120,9 @@ public String getName() { */ public void setName(String newName) { performAssertionsForNameInput(newName); - LOGGER.log(Level.FINE, "New name: " + newName); + logger.log(Level.FINE, "New name: " + newName); name = newName.trim(); - LOGGER.log(Level.FINE, "Trimmed new name: " + this.name); + logger.log(Level.FINE, "Trimmed new name: " + this.name); } /** @@ -140,7 +141,7 @@ public Gender getGender() { */ public void setGender(Gender newGender) { performAssertionsForGenderInput(newGender); - LOGGER.log(Level.FINE, "New gender: " + newGender.getDescription()); + logger.log(Level.FINE, "New gender: " + newGender.getDescription()); gender = newGender; } @@ -160,7 +161,7 @@ public int getAge() { */ public void setAge(int newAge) { performAssertionsForAgeInput(newAge); - LOGGER.log(Level.FINE, "New age: " + newAge); + logger.log(Level.FINE, "New age: " + newAge); age = newAge; } @@ -180,7 +181,7 @@ public int getHeight() { */ public void setHeight(int newHeight) { performAssertionsForHeight(newHeight); - LOGGER.log(Level.FINE, "New height: " + newHeight); + logger.log(Level.FINE, "New height: " + newHeight); height = newHeight; } @@ -200,7 +201,7 @@ public int getOriginalWeight() { */ public void setOriginalWeight(int newOriginalWeight) { performAssertionsForWeight(newOriginalWeight,"Original weight"); - LOGGER.log(Level.FINE, "New original weight: " + newOriginalWeight); + logger.log(Level.FINE, "New original weight: " + newOriginalWeight); originalWeight = newOriginalWeight; } @@ -220,7 +221,7 @@ public int getCurrentWeight() { */ public void setCurrentWeight(int newCurrentWeight) { performAssertionsForWeight(newCurrentWeight, "Current weight"); - LOGGER.log(Level.FINE, "New current weight: " + newCurrentWeight); + logger.log(Level.FINE, "New current weight: " + newCurrentWeight); currentWeight = newCurrentWeight; } @@ -240,7 +241,7 @@ public int getTargetWeight() { */ public void setTargetWeight(int newTargetWeight) { performAssertionsForWeight(newTargetWeight, "Target weight"); - LOGGER.log(Level.FINE, "New target weight: " + newTargetWeight); + logger.log(Level.FINE, "New target weight: " + newTargetWeight); targetWeight = newTargetWeight; } @@ -260,7 +261,7 @@ public FitnessLevel getFitnessLevel() { */ public void setFitnessLevel(FitnessLevel newFitnessLevel) { performAssertionsForFitnessLevel(newFitnessLevel); - LOGGER.log(Level.FINE, "New fitness level: " + newFitnessLevel); + logger.log(Level.FINE, "New fitness level: " + newFitnessLevel); fitnessLevel = newFitnessLevel; } From 62dce5a23aa4dd470309a0c0074b00a3996fd044 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 5 Nov 2020 02:15:54 +0800 Subject: [PATCH 281/374] Add new line at the end of the file --- src/test/java/seedu/dietbook/ui/UiHelperTest.java | 2 +- src/test/java/seedu/dietbook/ui/UiMessageTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/dietbook/ui/UiHelperTest.java b/src/test/java/seedu/dietbook/ui/UiHelperTest.java index 279cdcf982..cdeda354e3 100644 --- a/src/test/java/seedu/dietbook/ui/UiHelperTest.java +++ b/src/test/java/seedu/dietbook/ui/UiHelperTest.java @@ -40,4 +40,4 @@ void isEmptyString_StringWithTrailingSpaces_returnsTrue() { void isEmptyString_StringWithLeadingAndTrailingSpaces_returnsFalse() { assertFalse(uiHelper.isEmptyString(" food ")); } -} \ No newline at end of file +} diff --git a/src/test/java/seedu/dietbook/ui/UiMessageTest.java b/src/test/java/seedu/dietbook/ui/UiMessageTest.java index 8b31aa41c6..a53158fba4 100644 --- a/src/test/java/seedu/dietbook/ui/UiMessageTest.java +++ b/src/test/java/seedu/dietbook/ui/UiMessageTest.java @@ -75,4 +75,4 @@ void stringDateTimePeriod_validStartAndEndDateTimeWithSeconds_returnsStringOfTim uiMessage.stringDateTimePeriod(start, end)); } -} \ No newline at end of file +} From 42b591950134f37662cd205b19e20f13ece6e2f8 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 5 Nov 2020 02:18:23 +0800 Subject: [PATCH 282/374] Update JavaDoc comments --- src/main/java/seedu/dietbook/ui/UiHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/ui/UiHelper.java b/src/main/java/seedu/dietbook/ui/UiHelper.java index 3653ef319a..316ccd4eca 100644 --- a/src/main/java/seedu/dietbook/ui/UiHelper.java +++ b/src/main/java/seedu/dietbook/ui/UiHelper.java @@ -8,7 +8,7 @@ import java.util.logging.Logger; /** - * Represents a text user interface that deals with taking in user commands. + * Represents a helper that provide methods utilised in the other Ui related classes. * A UiHelper object contains all the helper methods needed for the other Ui related classes. */ public class UiHelper { From 8724ea38b6856e277f770cd3a858a829e8dec27d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Thu, 5 Nov 2020 02:21:49 +0800 Subject: [PATCH 283/374] Update I/O testing --- text-ui-test/EXPECTED.TXT | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index d5c9bd655a..7d18e92697 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -7,9 +7,9 @@ _______ __ ______ ________ _______ ______ ______ __ __ |_______/|__|______| |__| |_______/ \______/ \______/|__| \__\ Hello! Welcome to DietBook! -I am Diet, your guide to using DietBook. What is your name? -Please input in the following format: - name YOUR_NAME +I am Diet, your guide to using DietBook. How may I address you? +Please input your name or nickname in the following format: + name YOUR_NAME_OR_NICKNAME Example: name Jack _____________________________________________________________________________________________________________________________________ _____________________________________________________________________________________________________________________________________ From 1cdce4e6c0283eb6d0006ed2074bbfca59946c7d Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 5 Nov 2020 02:45:01 +0800 Subject: [PATCH 284/374] Update InputChecker.java --- src/main/java/seedu/dietbook/checker/InputChecker.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index e83b31a148..111b264561 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -249,7 +249,7 @@ public static void checkAgeLimit(int age) throws DietException { if (age < 0) { throw new DietException("Age value cannot be less than 0!"); } else if (age > AGE_CAP) { - throw new DietException("Age value cannot be more than 125!"); + throw new DietException("Age value cannot be more than " + AGE_CAP + "!"); } } @@ -263,7 +263,7 @@ public static void checkHeightLimit(int height) throws DietException { if (height < 1) { throw new DietException("Height value cannot be less than 1"); } else if (height > HEIGHT_CAP) { - throw new DietException("Height value cannot be more than 273!"); + throw new DietException("Height value cannot be more than " + HEIGHT_CAP + "!"); } } @@ -277,7 +277,7 @@ public static void checkWeightLimit(int weight) throws DietException { if (weight < 1) { throw new DietException("Weight value cannot be less than 1!"); } else if (weight > WEIGHT_CAP) { - throw new DietException("Weight value cannot be more than 443!"); + throw new DietException("Weight value cannot be more than " + WEIGHT_CAP + "!"); } } From f1d1c3846198c15591945e0058b9b8f98bbba7b5 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 5 Nov 2020 03:37:43 +0800 Subject: [PATCH 285/374] Improved exception catching --- src/main/java/seedu/dietbook/DietBook.java | 2 +- .../java/seedu/dietbook/checker/InputChecker.java | 15 +++++++++++++++ src/main/java/seedu/dietbook/parser/Parser.java | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index daefc0d0c5..e0ebf87568 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -115,7 +115,7 @@ public static void main(String[] args) throws DietException { } catch (DietException e) { dietBook.ui.printErrorMessage(e.getMessage()); } catch (Exception e) { - throw new DietException("Wrong command format!"); + dietBook.ui.printErrorMessage("Wrong command format!"); } } } diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 111b264561..db06a00237 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -58,6 +58,21 @@ public static void checkEmptyOption(String[] input) throws DietException { } } + /** + * Takes in the parameter in which number is expected and checks for validity. + * + * @param number number to be checked. + * @param param the parameter of the number being checked. + * @throws DietException when a number is not valid. + */ + public static void checkValidNumber(String number, String param) throws DietException { + try { + int check = Integer.parseInt(number); + } catch (NumberFormatException e) { + throw new DietException("Invalid value for option '" + param + "'!"); + } + } + /** * Takes in user input to check for repeated options. * diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 1f5f13fd91..9a38c269a8 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -118,6 +118,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws } switch (param) { case "x/": + InputChecker.checkValidNumber(trimmedParam, param); portionSize = Integer.parseInt(trimmedParam); InputChecker.checkFoodLimit(portionSize); break; @@ -125,18 +126,22 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws foodName = trimmedParam; break; case "k/": + InputChecker.checkValidNumber(trimmedParam, param); calorie = Integer.parseInt(trimmedParam); InputChecker.checkFoodLimit(calorie); break; case "c/": + InputChecker.checkValidNumber(trimmedParam, param); carb = Integer.parseInt(trimmedParam); InputChecker.checkFoodLimit(carb); break; case "p/": + InputChecker.checkValidNumber(trimmedParam, param); protein = Integer.parseInt(trimmedParam); InputChecker.checkFoodLimit(protein); break; default: + InputChecker.checkValidNumber(trimmedParam, param); fat = Integer.parseInt(trimmedParam); InputChecker.checkFoodLimit(fat); break; @@ -194,22 +199,27 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw } break; case "a/": + InputChecker.checkValidNumber(trimmedParam, param); age = Integer.parseInt(trimmedParam); InputChecker.checkAgeLimit(age); break; case "h/": + InputChecker.checkValidNumber(trimmedParam, param); height = Integer.parseInt(trimmedParam); InputChecker.checkHeightLimit(height); break; case "o/": + InputChecker.checkValidNumber(trimmedParam, param); orgWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(orgWeight); break; case "c/": + InputChecker.checkValidNumber(trimmedParam, param); currWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(currWeight); break; case "t/": + InputChecker.checkValidNumber(trimmedParam, param); tarWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(tarWeight); break; @@ -279,26 +289,31 @@ public static void executeEditInfo(String userInput, Manager manager) throws Die manager.getPerson().setName(name); break; case "a/": + InputChecker.checkValidNumber(trimmedParam, param); age = Integer.parseInt(trimmedParam); InputChecker.checkAgeLimit(age); manager.getPerson().setAge(age); break; case "h/": + InputChecker.checkValidNumber(trimmedParam, param); height = Integer.parseInt(trimmedParam); InputChecker.checkHeightLimit(height); manager.getPerson().setHeight(height); break; case "o/": + InputChecker.checkValidNumber(trimmedParam, param); orgWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(orgWeight); manager.getPerson().setOriginalWeight(orgWeight); break; case "c/": + InputChecker.checkValidNumber(trimmedParam, param); currWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(currWeight); manager.getPerson().setCurrentWeight(currWeight); break; case "t/": + InputChecker.checkValidNumber(trimmedParam, param); tarWeight = Integer.parseInt(trimmedParam); InputChecker.checkWeightLimit(tarWeight); manager.getPerson().setTargetWeight(tarWeight); From 743ecb00092552506a6c7e0fea1f03983424fcdd Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 5 Nov 2020 05:19:49 +0800 Subject: [PATCH 286/374] Improved option detection --- .../seedu/dietbook/checker/InputChecker.java | 53 +++++++++++++++++++ .../java/seedu/dietbook/parser/Parser.java | 4 ++ 2 files changed, 57 insertions(+) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index db06a00237..37d2edcf93 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -1,6 +1,7 @@ package seedu.dietbook.checker; import seedu.dietbook.exception.DietException; +import seedu.dietbook.parser.Parser; import java.time.LocalDateTime; @@ -25,6 +26,7 @@ public class InputChecker { public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; public static final String[] PARAM_GENDER = {"M","F","O"}; public static final String[] PARAM_INFO = {"g/","a/","h/","f/","o/","t/","c/"}; + public static final String[] PARAM_EDIT_INFO = {"n/","g/","a/","h/","f/","o/","t/","c/"}; /** * Takes in user input and command to check for any expected parameters after the command. @@ -58,6 +60,57 @@ public static void checkEmptyOption(String[] input) throws DietException { } } + /** + * Takes in user input to check if at least 1 option is present. + * + * @param input user input. + * @throws DietException when an option is not present. + */ + public static void checkForOption(String input) throws DietException { + String parameter = Parser.getCommandParam(input); + boolean isValidOption = false; + if (parameter.contains("/")) { + String checker = parameter.substring(parameter.indexOf("/") - 1,parameter.indexOf("/") + 1); + for (String param: PARAM_EDIT_INFO) { + if (checker.equals(param)) { + isValidOption = true; + } + } + if (!isValidOption) { + throw new DietException("Error! No such option '" + checker + "'!"); + } + } else { + throw new DietException("Error! No option present!"); + } + } + + /** + * Takes in user input to check if all option specified are at least one of the expected option. + * + * @param input user input. + * @param paramList the expected list of options. + * @throws DietException when an option is not of the expected. + */ + public static void checkValidOptions(String input, String[] paramList) throws DietException { + String parameter = Parser.getCommandParam(input); + long noOfOptions = parameter.chars().filter(num -> num == '/').count(); + int slashTracker = parameter.indexOf("/"); + boolean isValidOption; + for (int i = 0; i < noOfOptions; i++) { + isValidOption = false; + String checker = parameter.substring(slashTracker - 1, slashTracker + 1); + for (String param: paramList) { + if (checker.equals(param)) { + isValidOption = true; + } + } + if (!isValidOption) { + throw new DietException("Error! No such option '" + checker + "'!"); + } + slashTracker = parameter.indexOf("/", slashTracker + 1); + } + } + /** * Takes in the parameter in which number is expected and checks for validity. * diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 9a38c269a8..052e33d25b 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -105,6 +105,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws String[] processedParam; String[] paramList = {"x/", "n/", "k/", "c/", "p/", "f/"}; InputChecker.checkRepeatedOption(getCommand(userInput), getCommandParam(userInput)); + InputChecker.checkValidOptions(userInput, paramList); for (String param: paramList) { if (getCommandParam(userInput).contains(param)) { processedParam = getCommandParam(userInput).split(param); @@ -181,6 +182,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw String trimmedParam; String[] processedParam; InputChecker.checkRepeatedOption(getCommand(userInput), getCommandParam(userInput)); + InputChecker.checkValidOptions(userInput, PARAM_INFO); for (String param: PARAM_INFO) { processedParam = getCommandParam(userInput).split(param); InputChecker.checkEmptyOption(processedParam); @@ -263,6 +265,8 @@ public static void executeEditInfo(String userInput, Manager manager) throws Die String trimmedParam; String[] processedParam; InputChecker.checkRepeatedOption(getCommand(userInput), getCommandParam(userInput)); + InputChecker.checkForOption(userInput); + InputChecker.checkValidOptions(userInput, PARAM_EDIT_INFO); for (String param : PARAM_EDIT_INFO) { if (getCommandParam(userInput).contains(param)) { processedParam = getCommandParam(userInput).split(param); From 53afb2510c0a39588c54716d78911b42d110afdd Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 5 Nov 2020 10:32:46 +0800 Subject: [PATCH 287/374] Updated error message --- src/main/java/seedu/dietbook/DietBook.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index e0ebf87568..24f82fc6c5 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -115,7 +115,7 @@ public static void main(String[] args) throws DietException { } catch (DietException e) { dietBook.ui.printErrorMessage(e.getMessage()); } catch (Exception e) { - dietBook.ui.printErrorMessage("Wrong command format!"); + dietBook.ui.printErrorMessage("Oops something went wrong!"); } } } From c98347755bcf7fcbf30b8a610f3023d47940d4d1 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Thu, 5 Nov 2020 23:43:05 +0800 Subject: [PATCH 288/374] Improved command exception - Specific exception messages --- src/main/java/seedu/dietbook/checker/InputChecker.java | 8 +++----- src/main/java/seedu/dietbook/parser/Parser.java | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 37d2edcf93..7b96019c95 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -48,12 +48,10 @@ public static void checkEmpty(String userInput, String command) throws DietExcep * @param input user input. * @throws DietException when an option is specified but its field is empty. */ - public static void checkEmptyOption(String[] input) throws DietException { + public static void checkEmptyOption(String[] input, String param) throws DietException { if (input.length > 1) { - if (input[1].trim().length() > 1) { - if (input[1].trim().charAt(1) == '/') { - throw new DietException("Error! Option specified with empty field!"); - } + if ((input[1].trim().length() > 1 && input[1].trim().charAt(1) == '/') || input[1].trim().equals("")) { + throw new DietException("Error! Option '" + param + "' specified with empty field!"); } } else { throw new DietException("Error! Option specified with empty field!"); diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index 052e33d25b..e98784967e 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -109,7 +109,7 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws for (String param: paramList) { if (getCommandParam(userInput).contains(param)) { processedParam = getCommandParam(userInput).split(param); - InputChecker.checkEmptyOption(processedParam); + InputChecker.checkEmptyOption(processedParam, param); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); @@ -185,7 +185,7 @@ public static void executeProcessedInfo(String userInput, Manager manager) throw InputChecker.checkValidOptions(userInput, PARAM_INFO); for (String param: PARAM_INFO) { processedParam = getCommandParam(userInput).split(param); - InputChecker.checkEmptyOption(processedParam); + InputChecker.checkEmptyOption(processedParam, param); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); @@ -270,7 +270,7 @@ public static void executeEditInfo(String userInput, Manager manager) throws Die for (String param : PARAM_EDIT_INFO) { if (getCommandParam(userInput).contains(param)) { processedParam = getCommandParam(userInput).split(param); - InputChecker.checkEmptyOption(processedParam); + InputChecker.checkEmptyOption(processedParam, param); trimmedParam = processedParam[1].trim(); if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); From 061c9cd6e89c93d9718a6627050540fa2d882bf7 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Fri, 6 Nov 2020 02:09:21 +0800 Subject: [PATCH 289/374] Fixed End Time --- .../java/seedu/dietbook/checker/InputChecker.java | 13 +++++++++++++ .../seedu/dietbook/command/CalculateCommand.java | 5 +++++ .../java/seedu/dietbook/command/ListCommand.java | 1 + 3 files changed, 19 insertions(+) diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index 7b96019c95..e40976eda4 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -185,6 +185,19 @@ public static void checkFutureDate(LocalDateTime time) throws DietException { } } + /** + * Takes in the start and end date time objects and see if the end time is earlier than start time. + * + * @param startTime a date time class object, the start time. + * @param endTime a date time class object, the end time. + * @throws DietException if end date time is before the start date time. + */ + public static void checkEndDate(LocalDateTime startTime, LocalDateTime endTime) throws DietException { + if (startTime.isAfter(endTime)) { + throw new DietException("The start date, time cannot be later than the end date, time!"); + } + } + /** * Takes in user input to check if the expected number and type of parameter for the add command is present. * diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index c8beefcc65..88e52824ed 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -55,6 +55,7 @@ public void execute(Manager manager, Ui ui) throws DietException { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); @@ -74,6 +75,7 @@ public void execute(Manager manager, Ui ui) throws DietException { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); ui.printCalorieIntake(calorie, startTime, endTime); } @@ -90,6 +92,7 @@ public void execute(Manager manager, Ui ui) throws DietException { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); ui.printCarbIntake(carb, startTime, endTime); } @@ -106,6 +109,7 @@ public void execute(Manager manager, Ui ui) throws DietException { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); ui.printProteinIntake(protein, startTime, endTime); } @@ -122,6 +126,7 @@ public void execute(Manager manager, Ui ui) throws DietException { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); ui.printFatIntake(fat, startTime, endTime); } diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java index 933e42289e..9a3f6246ea 100644 --- a/src/main/java/seedu/dietbook/command/ListCommand.java +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -39,6 +39,7 @@ public void execute(Manager manager, Ui ui) throws DietException { startTime = LocalDateTime.parse(processedInput[1]); endTime = LocalDateTime.parse(processedInput[2]); InputChecker.checkFutureDate(startTime); + InputChecker.checkEndDate(startTime, endTime); ui.printFoodList(foodList.getInDateTimeRangeToString(startTime,endTime), startTime, endTime); } } catch (Exception e) { From 15f18ab9128d6978808811a6f95449cd48c02b08 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Fri, 6 Nov 2020 20:47:07 +0800 Subject: [PATCH 290/374] Update README.md --- docs/README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..b54daf2970 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,15 @@ -# Duke +# DietBook -{Give product intro here} +[[!CI status](https://github.com/AY2021S1-CS2113-T14-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2021S1-CS2113-T14-4/tp/actions) + +DietBook is a Command Line Interface (CLI) desktop application designed to **track your food and nutritional intake** as well as provide you with your **daily calorie recommendation**. 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. + +**Useful links:** +* If you would like to use DietBook, head over to [User Guide](UserGuide.md) to get started. +* If you would like to know more about developing DietBook, head over to [Developer Guide](DeveloperGuide.md). +* If you would like to know more about the developers, head over to [About Us](AboutUs.md). + +**Acknowledgements:** + +* Libraries used: [JUnit5](https://github.com/junit-team/junit5) -Useful links: -* [User Guide](UserGuide.md) -* [Developer Guide](DeveloperGuide.md) -* [About Us](AboutUs.md) From b28ede14ac26928f96c2a2415faa87255422ec8d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Fri, 6 Nov 2020 20:52:14 +0800 Subject: [PATCH 291/374] Delete DukeTest.java --- src/test/java/seedu/dietbook/DukeTest.java | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 src/test/java/seedu/dietbook/DukeTest.java diff --git a/src/test/java/seedu/dietbook/DukeTest.java b/src/test/java/seedu/dietbook/DukeTest.java deleted file mode 100644 index eef4b4c86a..0000000000 --- a/src/test/java/seedu/dietbook/DukeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.dietbook; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class DukeTest { - @Test - public void sampleTest() { - assertTrue(true); - } -} From c1753f00ed238f14a0939d81754b4fdeaa221de1 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:23:38 +0800 Subject: [PATCH 292/374] Update JavaDoc comments --- src/main/java/seedu/dietbook/ui/Ui.java | 28 +++++++------------------ 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/main/java/seedu/dietbook/ui/Ui.java b/src/main/java/seedu/dietbook/ui/Ui.java index f559e99b4d..ddb82c3312 100644 --- a/src/main/java/seedu/dietbook/ui/Ui.java +++ b/src/main/java/seedu/dietbook/ui/Ui.java @@ -5,8 +5,10 @@ import java.time.LocalDateTime; /** - * Represents a manager that deals with user interaction and communicating with the logic component. - * A Ui objects deals with user input, output and communication with the logic component. + * Represents a user interface manager that deals with user interaction and communicating with the logic + * component. + * A Ui objects deals with relaying user inputs, printing output messages and communicating with + * the logic component. */ public class Ui { @@ -25,11 +27,6 @@ public Ui() { uiMessage = new UiMessage(); } - // Methods in the Ui class are organised according to their function in the order of: system related, - // database related, person related, food list related, nutritional related and other helper methods. - - // Methods required to print system related commands or messages. - /** * Returns the non-empty user input that has been trimmed. * @@ -109,8 +106,6 @@ public void printHelpCommandMessage() { uiOutput.print(uiMessage.getHelpCommandMessage()); } - // Methods required to print database related commands or messages. - /** * Prints all the food in the database sorted by the canteen and then the store it is found. * @@ -122,8 +117,6 @@ public void printDatabase(String foodDatabase) { uiOutput.print(uiMessage.getDatabaseMessage(foodDatabase)); } - // Methods required to print user information related commands and messages. - /** * Prints all the information related to the user. * @@ -146,8 +139,6 @@ public void printEditedPersonInfo(String personInfo) { uiOutput.print(uiMessage.getEditedPersonInfoMessage(personInfo)); } - // Methods required for printing FoodList related commands and messages. - /** * Prints all the food items in the food list in the order that they were added or a message stating * that the food list is empty if there are no food items. @@ -231,8 +222,6 @@ public void printClearFoodListMessage() { uiOutput.print(uiMessage.getClearFoodListMessage()); } - // Methods required to print nutritional intake and recommendation related commands and messages. - /** * Prints the daily recommended calorie intake of the user based on the user's personal information. * @@ -251,8 +240,7 @@ public void printCalorieRecommendation(String name, int calorieRecommendation) { * @param carbIntake The total amount of carbohydrates of all the food in the food list. */ public void printCarbIntake(int carbIntake) { - uiOutput.print(uiMessage.getOneIntakeMessage(carbIntake, "carbohydrate", - "g")); + uiOutput.print(uiMessage.getOneIntakeMessage(carbIntake, "carbohydrate","g")); } /** @@ -297,8 +285,7 @@ public void printCalorieIntake(int calorieIntake) { * @param end Ending date time of the time period given. */ public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDateTime end) { - String stringCalorieIntake = uiMessage.getOneIntakeMessage(calorieIntake,"calorie", - "kcal"); + String stringCalorieIntake = uiMessage.getOneIntakeMessage(calorieIntake,"calorie", "kcal"); uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringCalorieIntake, start, end)); } @@ -392,8 +379,7 @@ public void printFatIntake(int fatIntake, LocalDateTime start) { */ public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake) { - uiOutput.print(uiMessage.getAllIntakeMessage(calorieIntake, carbIntake, proteinIntake, - fatIntake)); + uiOutput.print(uiMessage.getAllIntakeMessage(calorieIntake, carbIntake, proteinIntake, fatIntake)); } /** From 6c085339249dc3733a61f1597f21836c0b7392e2 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:40:44 +0800 Subject: [PATCH 293/374] Add MainLogger.java --- .../seedu/dietbook/logger/MainLogger.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/seedu/dietbook/logger/MainLogger.java diff --git a/src/main/java/seedu/dietbook/logger/MainLogger.java b/src/main/java/seedu/dietbook/logger/MainLogger.java new file mode 100644 index 0000000000..a5d01a68ba --- /dev/null +++ b/src/main/java/seedu/dietbook/logger/MainLogger.java @@ -0,0 +1,23 @@ +package seedu.dietbook.logger; + +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class MainLogger { + + private final Logger logger; + + public MainLogger(String className) { + logger = Logger.getLogger(className); + Handler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.SEVERE); + logger.addHandler(consoleHandler); + logger.setLevel(Level.SEVERE); + } + + public void log(Level level, String message) { + logger.log(level, message); + } +} From 7bfcd394321f3c8ab2003da6016b6b604d7008b1 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:40:55 +0800 Subject: [PATCH 294/374] Add logging for Ui.java --- src/main/java/seedu/dietbook/ui/Ui.java | 64 +++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/ui/Ui.java b/src/main/java/seedu/dietbook/ui/Ui.java index ddb82c3312..8bfcf0a16a 100644 --- a/src/main/java/seedu/dietbook/ui/Ui.java +++ b/src/main/java/seedu/dietbook/ui/Ui.java @@ -1,8 +1,10 @@ package seedu.dietbook.ui; import seedu.dietbook.exception.DietException; +import seedu.dietbook.logger.MainLogger; import java.time.LocalDateTime; +import java.util.logging.Level; /** * Represents a user interface manager that deals with user interaction and communicating with the logic @@ -16,6 +18,7 @@ public class Ui { private final UiHelper uiHelper; private final UiOutput uiOutput; private final UiMessage uiMessage; + private final MainLogger mainLogger; /** * Constructs a Ui object. @@ -25,6 +28,7 @@ public Ui() { uiHelper = new UiHelper(); uiOutput = new UiOutput(); uiMessage = new UiMessage(); + mainLogger = new MainLogger(Ui.class.getName()); } /** @@ -166,6 +170,10 @@ public void printFoodList(String allFood) { * @param end Ending date time of the time period given. */ public void printFoodList(String foods, LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Foods: " + foods); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + uiHelper.performAssertionsForNullStringInputs(foods, "String representation of food items in the food list recorded during the " + "time period given"); @@ -187,6 +195,9 @@ public void printFoodList(String foods, LocalDateTime start, LocalDateTime end) * @param start Starting date time of the time period till now. */ public void printFoodList(String foods, LocalDateTime start) { + mainLogger.log(Level.FINE, "Foods: " + foods); + mainLogger.log(Level.FINE, "Start: " + start); + LocalDateTime end = LocalDateTime.now(); printFoodList(foods, start, end); } @@ -251,6 +262,10 @@ public void printCarbIntake(int carbIntake) { * @param end Ending date time of the time period given. */ public void printCarbIntake(int carbIntake, LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Carb intake: " + carbIntake); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + String stringCarbIntake = uiMessage.getOneIntakeMessage(carbIntake,"carbohydrate", "g"); uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringCarbIntake, start, end)); } @@ -263,6 +278,9 @@ public void printCarbIntake(int carbIntake, LocalDateTime start, LocalDateTime e * @param start Starting date time to calculate from. */ public void printCarbIntake(int carbIntake, LocalDateTime start) { + mainLogger.log(Level.FINE, "Carb intake: " + carbIntake); + mainLogger.log(Level.FINE, "Start: " + start); + LocalDateTime end = LocalDateTime.now(); printCarbIntake(carbIntake, start, end); } @@ -285,6 +303,10 @@ public void printCalorieIntake(int calorieIntake) { * @param end Ending date time of the time period given. */ public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Calorie intake: " + calorieIntake); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + String stringCalorieIntake = uiMessage.getOneIntakeMessage(calorieIntake,"calorie", "kcal"); uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringCalorieIntake, start, end)); } @@ -297,6 +319,9 @@ public void printCalorieIntake(int calorieIntake, LocalDateTime start, LocalDate * @param start Starting date time to calculate from. */ public void printCalorieIntake(int calorieIntake, LocalDateTime start) { + mainLogger.log(Level.FINE, "Calorie intake: " + calorieIntake); + mainLogger.log(Level.FINE, "Start: " + start); + LocalDateTime end = LocalDateTime.now(); printCalorieIntake(calorieIntake, start, end); } @@ -319,6 +344,10 @@ public void printProteinIntake(int proteinIntake) { * @param end Ending date time of the time period given. */ public void printProteinIntake(int proteinIntake, LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Protein intake: " + proteinIntake); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + String stringProteinIntake = uiMessage.getOneIntakeMessage(proteinIntake, "protein", "g"); uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringProteinIntake, start, end)); } @@ -331,6 +360,9 @@ public void printProteinIntake(int proteinIntake, LocalDateTime start, LocalDate * @param start Starting date time to calculate from. */ public void printProteinIntake(int proteinIntake, LocalDateTime start) { + mainLogger.log(Level.FINE, "Protein intake: " + proteinIntake); + mainLogger.log(Level.FINE, "Start: " + start); + LocalDateTime end = LocalDateTime.now(); printProteinIntake(proteinIntake, start, end); } @@ -353,6 +385,10 @@ public void printFatIntake(int fatIntake) { * @param end Ending date time of the time period given. */ public void printFatIntake(int fatIntake, LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Fat intake: " + fatIntake); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + String stringFatIntake = uiMessage.getOneIntakeMessage(fatIntake,"fat", "g"); uiOutput.print(uiMessage.getIntakeWithTimeMessage(stringFatIntake, start, end)); } @@ -365,6 +401,9 @@ public void printFatIntake(int fatIntake, LocalDateTime start, LocalDateTime end * @param start Starting date time to calculate from. */ public void printFatIntake(int fatIntake, LocalDateTime start) { + mainLogger.log(Level.FINE, "Fat intake: " + fatIntake); + mainLogger.log(Level.FINE, "Start: " + start); + LocalDateTime end = LocalDateTime.now(); printFatIntake(fatIntake, start, end); } @@ -377,8 +416,12 @@ public void printFatIntake(int fatIntake, LocalDateTime start) { * @param proteinIntake The total amount of proteins of all the food in the food list. * @param fatIntake The total amount of fats of all the food in the food list. */ - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake) { + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake) { + mainLogger.log(Level.FINE, "Calorie intake: " + calorieIntake); + mainLogger.log(Level.FINE, "Carb intake: " + carbIntake); + mainLogger.log(Level.FINE, "Protein intake: " + proteinIntake); + mainLogger.log(Level.FINE, "Fat intake: " + fatIntake); + uiOutput.print(uiMessage.getAllIntakeMessage(calorieIntake, carbIntake, proteinIntake, fatIntake)); } @@ -397,8 +440,15 @@ public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, * @param start Starting date time of the time period given. * @param end Ending date time of the time period given. */ - public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, - int fatIntake, LocalDateTime start, LocalDateTime end) { + public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, + LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Calorie intake: " + calorieIntake); + mainLogger.log(Level.FINE, "Carb intake: " + carbIntake); + mainLogger.log(Level.FINE, "Protein intake: " + proteinIntake); + mainLogger.log(Level.FINE, "Fat intake: " + fatIntake); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + String allIntake = uiMessage.getAllIntakeMessage(calorieIntake, carbIntake, proteinIntake, fatIntake); uiOutput.print(uiMessage.getIntakeWithTimeMessage(allIntake, start, end)); } @@ -419,6 +469,12 @@ public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, */ public void printAllIntake(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, LocalDateTime start) { + mainLogger.log(Level.FINE, "Calorie intake: " + calorieIntake); + mainLogger.log(Level.FINE, "Carb intake: " + carbIntake); + mainLogger.log(Level.FINE, "Protein intake: " + proteinIntake); + mainLogger.log(Level.FINE, "Fat intake: " + fatIntake); + mainLogger.log(Level.FINE, "Start: " + start); + LocalDateTime end = LocalDateTime.now(); printAllIntake(calorieIntake, carbIntake, proteinIntake, fatIntake, start, end); } From 2dec8329e07905259630440bcbc943f7f51aa3a6 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:41:33 +0800 Subject: [PATCH 295/374] Add logging for UiHelper.java --- src/main/java/seedu/dietbook/ui/UiHelper.java | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/main/java/seedu/dietbook/ui/UiHelper.java b/src/main/java/seedu/dietbook/ui/UiHelper.java index 316ccd4eca..e42b11ce1a 100644 --- a/src/main/java/seedu/dietbook/ui/UiHelper.java +++ b/src/main/java/seedu/dietbook/ui/UiHelper.java @@ -1,11 +1,9 @@ package seedu.dietbook.ui; +import seedu.dietbook.logger.MainLogger; + import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; import java.util.logging.Level; -import java.util.logging.Logger; /** * Represents a helper that provide methods utilised in the other Ui related classes. @@ -15,21 +13,10 @@ public class UiHelper { static final String LINE_SEPARATOR = System.lineSeparator(); - private final Logger logger; - - public UiHelper() { - logger = Logger.getLogger(UiHelper.class.getName()); - initialiseLogger(); - } + private final MainLogger mainLogger; - /** - * Initialises the logger and sets the log level. - */ - void initialiseLogger() { - Handler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.WARNING); - logger.addHandler(consoleHandler); - logger.setLevel(Level.WARNING); + UiHelper() { + mainLogger = new MainLogger(UiHelper.class.getName()); } /** @@ -41,7 +28,7 @@ void initialiseLogger() { * spaces, false otherwise. */ boolean isEmptyString(String string) { - logger.log(Level.FINE, "String to check if empty: " + string); + mainLogger.log(Level.FINE, "String to check if empty: " + string); performAssertionsForNullStringInputs(string, "String to be determined if empty"); return trimString(string).length() == 0; @@ -54,7 +41,7 @@ boolean isEmptyString(String string) { * @return A string that has been trimmed for leading and trailing spaces. */ String trimString(String string) { - logger.log(Level.FINE, "String to trim: " + string); + mainLogger.log(Level.FINE, "String to trim: " + string); performAssertionsForNullStringInputs(string, "String to trim"); return string.trim(); @@ -115,12 +102,12 @@ void performAssertionsForNutritionalIntake(int nutrientIntake, String nutrientTy * @param calorieRecommendation The recommended daily calorie intake for the user. */ void performAssertionsForCalorieRecommendation(int calorieRecommendation) { - // A minimum daily intake of 1200 calorie is required to stay healthy. - int minCalorie = 1200; + // A minimum daily intake of 1200 calorie is required. + int minCalorie = 1000; assert calorieRecommendation >= minCalorie : "Daily calorie recommendation should be equals to or " + "greater than " + minCalorie; - // Highest calorie intake for an athlete currently stands at 12000. - int maxCalorie = 12000; + // Highest calorie intake recommendation allowed. + int maxCalorie = 20000; assert calorieRecommendation <= maxCalorie : "Daily calorie recommendation should be equals to or " + "less than " + maxCalorie; } From 01af9a05fe6b7d62702549b17d5941fefd9ba158 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:42:37 +0800 Subject: [PATCH 296/374] Use MainLogger for UiInput.java --- src/main/java/seedu/dietbook/ui/UiInput.java | 29 ++++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/main/java/seedu/dietbook/ui/UiInput.java b/src/main/java/seedu/dietbook/ui/UiInput.java index ed96bf21f7..d0c360de04 100644 --- a/src/main/java/seedu/dietbook/ui/UiInput.java +++ b/src/main/java/seedu/dietbook/ui/UiInput.java @@ -1,12 +1,10 @@ package seedu.dietbook.ui; +import seedu.dietbook.logger.MainLogger; import seedu.dietbook.exception.DietException; import java.util.Scanner; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; import java.util.logging.Level; -import java.util.logging.Logger; /** * Represents a text user interface that deals with taking in user commands. @@ -16,17 +14,16 @@ public class UiInput { private static Scanner scanner = new Scanner(System.in); - private final Logger logger; + private final MainLogger mainLogger; private final UiHelper uiHelper; /** * Constructs a UiInput object. */ - public UiInput() { + UiInput() { uiHelper = new UiHelper(); - logger = Logger.getLogger(UiInput.class.getName()); - initialiseLogger(); + mainLogger = new MainLogger(UiInput.class.getName()); } /** @@ -37,7 +34,7 @@ public UiInput() { String getCommand() throws DietException { String command = readCommand(); String processedCommand = processCommand(command); - logger.log(Level.FINE, "Processed user command: " + processedCommand); + mainLogger.log(Level.FINE, "Processed user command: " + processedCommand); return processedCommand; } @@ -58,21 +55,11 @@ String readCommand() { * @throws DietException If the the user command is empty after trimming. */ String processCommand(String command) throws DietException { - logger.log(Level.FINE, "User command to process: " + command); + mainLogger.log(Level.FINE, "User command to process: " + command); if (uiHelper.isEmptyString(command)) { - logger.log(Level.WARNING, "Command is empty!"); + mainLogger.log(Level.WARNING, "Command is empty!"); throw new DietException("Command is empty!"); } return uiHelper.trimString(command); } - - /** - * Initialises the logger and sets the log level. - */ - void initialiseLogger() { - Handler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.SEVERE); - logger.addHandler(consoleHandler); - logger.setLevel(Level.SEVERE); - } -} \ No newline at end of file +} From cc7390fa829e0235a23b5f49bea22d22c1f02d5f Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:43:22 +0800 Subject: [PATCH 297/374] Add logging for UiMessage.java --- .../java/seedu/dietbook/ui/UiMessage.java | 70 +++++++++++++------ 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/src/main/java/seedu/dietbook/ui/UiMessage.java b/src/main/java/seedu/dietbook/ui/UiMessage.java index 7a80780239..d4705a367e 100644 --- a/src/main/java/seedu/dietbook/ui/UiMessage.java +++ b/src/main/java/seedu/dietbook/ui/UiMessage.java @@ -1,14 +1,12 @@ package seedu.dietbook.ui; +import seedu.dietbook.logger.MainLogger; import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Gender; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; import java.util.logging.Level; -import java.util.logging.Logger; /** * Represents a storage that stores all the messages that Ui can utilise and print out. @@ -19,22 +17,11 @@ public class UiMessage { private final UiHelper uiHelper; - private final Logger logger; + private final MainLogger mainLogger; - public UiMessage() { + UiMessage() { uiHelper = new UiHelper(); - logger = Logger.getLogger(UiMessage.class.getName()); - initialiseLogger(); - } - - /** - * Initialises the logger and sets the log level. - */ - void initialiseLogger() { - Handler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.WARNING); - logger.addHandler(consoleHandler); - logger.setLevel(Level.WARNING); + mainLogger = new MainLogger(UiMessage.class.getName()); } String getWelcomeMessage() { @@ -124,6 +111,10 @@ String getFoodListMessage(String allFood) { } String getFoodListMessage(String foods, LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Foods: " + foods); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + return "Here are the food items recorded in DietBook" + stringDateTimePeriod(start, end) + ":" + UiHelper.LINE_SEPARATOR + foods; } @@ -133,6 +124,9 @@ String getEmptyFoodListMessage() { } String getEmptyFoodListMessage(LocalDateTime start, LocalDateTime end) { + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + return "No food item was recorded in DietBook" + stringDateTimePeriod(start, end) + "."; } @@ -224,6 +218,10 @@ String getDatabaseRelatedCommands() { * @return A string representation of the the total amount of a nutrient consumed by the user. */ String getOneIntakeMessage(int nutrientIntake, String nutrientType, String nutrientUnit) { + mainLogger.log(Level.FINE, "Nutrient intake: " + nutrientIntake); + mainLogger.log(Level.FINE, "Nutrient type: " + nutrientType); + mainLogger.log(Level.FINE, "Nutrient unit: " + nutrientUnit); + uiHelper.performAssertionsForStringInputs(nutrientType, "Nutrient Type"); uiHelper.performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); uiHelper.performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); @@ -243,8 +241,9 @@ String getOneIntakeMessage(int nutrientIntake, String nutrientType, String nutri * user during a given time period. */ String getIntakeWithTimeMessage(String intakeWithoutTime, LocalDateTime start, LocalDateTime end) { - logger.log(Level.FINE, "Start: " + start); - logger.log(Level.FINE, "End: " + end); + mainLogger.log(Level.FINE, "Intake without time: " + intakeWithoutTime); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); uiHelper.performAssertionsForTimePeriod(start, end); String timePeriod = "Time period:" + stringDateTimePeriod(start, end); @@ -262,6 +261,11 @@ String getIntakeWithTimeMessage(String intakeWithoutTime, LocalDateTime start, L * @return A string representation of the total amount of each of the nutrients consumed by the user. */ String getAllIntakeMessage(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake) { + mainLogger.log(Level.FINE, "Calorie intake: " + calorieIntake); + mainLogger.log(Level.FINE, "Carb intake: " + carbIntake); + mainLogger.log(Level.FINE, "Protein intake: " + proteinIntake); + mainLogger.log(Level.FINE, "Fat intake: " + fatIntake); + uiHelper.performAssertionsForNutritionalIntake(calorieIntake, "calorie"); uiHelper.performAssertionsForNutritionalIntake(carbIntake, "carb"); uiHelper.performAssertionsForNutritionalIntake(proteinIntake, "protein"); @@ -290,6 +294,9 @@ String getAllIntakeMessage(int calorieIntake, int carbIntake, int proteinIntake, String getRecalculatedFoodsMessage(String recalculatedFoods) { String message = "No food items had their nutritional information recalculated by DietBook."; if (!uiHelper.isEmptyString(recalculatedFoods)) { + mainLogger.log(Level.FINE, "There are food with their nutritional information" + + " being recalculated"); + message = "Food items which had their nutritional information recalculated by DietBook: " + UiHelper.LINE_SEPARATOR + recalculatedFoods; } @@ -307,6 +314,10 @@ String getRecalculatedFoodsMessage(String recalculatedFoods) { * or amount consumed in a given time period. */ String getNutritionalIntakeMessage(int nutrientIntake, String nutrientType, String nutrientUnit) { + mainLogger.log(Level.FINE, "Nutrient intake: " + nutrientIntake); + mainLogger.log(Level.FINE, "Nutrient type: " + nutrientType); + mainLogger.log(Level.FINE, "Nutrient unit: " + nutrientUnit); + return "Total " + nutrientType + " intake: " + nutrientIntake + nutrientUnit; } @@ -324,6 +335,11 @@ String getNutritionalIntakeMessage(int nutrientIntake, String nutrientType, Stri */ String getOneIntakeAndFoodsMessage(int nutrientIntake, String nutrientType, String nutrientUnit, String recalculatedFoods) { + mainLogger.log(Level.FINE, "Nutrient intake: " + nutrientIntake); + mainLogger.log(Level.FINE, "Nutrient type: " + nutrientType); + mainLogger.log(Level.FINE, "Nutrient unit: " + nutrientUnit); + mainLogger.log(Level.FINE, "Recalculated food: " + recalculatedFoods); + uiHelper.performAssertionsForStringInputs(nutrientType, "Nutrient Type"); uiHelper.performAssertionsForStringInputs(nutrientUnit, "Nutrient Unit"); uiHelper.performAssertionsForNutritionalIntake(nutrientIntake, nutrientType); @@ -351,8 +367,10 @@ String getOneIntakeAndFoodsMessage(int nutrientIntake, String nutrientType, */ String getIntakeAndFoodsWithTimeMessage(String intakeAndFoodsWithoutTime, LocalDateTime start, LocalDateTime end) { - logger.log(Level.FINE, "Start: " + start); - logger.log(Level.FINE, "End: " + end); + mainLogger.log(Level.FINE, "Intake and foods without time: " + intakeAndFoodsWithoutTime); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); + uiHelper.performAssertionsForTimePeriod(start, end); String timePeriod = "Time period:" + stringDateTimePeriod(start, end); @@ -374,6 +392,12 @@ String getIntakeAndFoodsWithTimeMessage(String intakeAndFoodsWithoutTime, */ String getAllIntakeAndFoodsMessage(int calorieIntake, int carbIntake, int proteinIntake, int fatIntake, String recalculatedFoods) { + mainLogger.log(Level.FINE, "Calorie intake: " + calorieIntake); + mainLogger.log(Level.FINE, "Carb intake: " + carbIntake); + mainLogger.log(Level.FINE, "Protein intake: " + proteinIntake); + mainLogger.log(Level.FINE, "Fat intake: " + fatIntake); + mainLogger.log(Level.FINE, "Recalculated food: " + recalculatedFoods); + uiHelper.performAssertionsForNutritionalIntake(carbIntake, "carbohydrate"); uiHelper.performAssertionsForNutritionalIntake(calorieIntake, "calorie"); uiHelper.performAssertionsForNutritionalIntake(proteinIntake, "protein"); @@ -403,8 +427,8 @@ String getAllIntakeAndFoodsMessage(int calorieIntake, int carbIntake, int protei * @return The string representation of time period with date time in the format dd MMM yyyy HHmm. */ public String stringDateTimePeriod(LocalDateTime start, LocalDateTime end) { - logger.log(Level.FINE, "Start: " + start); - logger.log(Level.FINE, "End: " + end); + mainLogger.log(Level.FINE, "Start: " + start); + mainLogger.log(Level.FINE, "End: " + end); uiHelper.performAssertionsForTimePeriod(start, end); String stringStart = start.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); From 09de15bf8db1b08c7e7fcc0d18a22493fe854c8c Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:43:43 +0800 Subject: [PATCH 298/374] Make UiOutput package private --- src/main/java/seedu/dietbook/ui/UiOutput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/ui/UiOutput.java b/src/main/java/seedu/dietbook/ui/UiOutput.java index 17b7986ff3..5fa59ba244 100644 --- a/src/main/java/seedu/dietbook/ui/UiOutput.java +++ b/src/main/java/seedu/dietbook/ui/UiOutput.java @@ -9,7 +9,7 @@ public class UiOutput { private final UiHelper uiHelper; - public UiOutput() { + UiOutput() { uiHelper = new UiHelper(); } From db469573f9a529da0a328587b18766ccd107377e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:44:36 +0800 Subject: [PATCH 299/374] Make method package private --- src/main/java/seedu/dietbook/ui/UiMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/ui/UiMessage.java b/src/main/java/seedu/dietbook/ui/UiMessage.java index d4705a367e..f8578001d9 100644 --- a/src/main/java/seedu/dietbook/ui/UiMessage.java +++ b/src/main/java/seedu/dietbook/ui/UiMessage.java @@ -426,7 +426,7 @@ String getAllIntakeAndFoodsMessage(int calorieIntake, int carbIntake, int protei * @param end Ending date time of the time period given. * @return The string representation of time period with date time in the format dd MMM yyyy HHmm. */ - public String stringDateTimePeriod(LocalDateTime start, LocalDateTime end) { + String stringDateTimePeriod(LocalDateTime start, LocalDateTime end) { mainLogger.log(Level.FINE, "Start: " + start); mainLogger.log(Level.FINE, "End: " + end); uiHelper.performAssertionsForTimePeriod(start, end); From 1c08701118454e23263c0f0f2f08034ecf62ec7e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:45:24 +0800 Subject: [PATCH 300/374] Update JUnit tests for UiHelper --- src/test/java/seedu/dietbook/ui/UiHelperTest.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/test/java/seedu/dietbook/ui/UiHelperTest.java b/src/test/java/seedu/dietbook/ui/UiHelperTest.java index cdeda354e3..ce33627672 100644 --- a/src/test/java/seedu/dietbook/ui/UiHelperTest.java +++ b/src/test/java/seedu/dietbook/ui/UiHelperTest.java @@ -22,17 +22,12 @@ void isEmptyString_nullInput_expectAssertionError() { } @Test - void isEmptyString_stringWithNoLeadingOrTrailingSpaces_returnsFalse() { + void isEmptyString_stringWithoutLeadingOrTrailingSpaces_returnsFalse() { assertFalse(uiHelper.isEmptyString("food")); } @Test - void isEmptyString_StringWithLeadingSpaces_returnsTrue() { - assertTrue(uiHelper.isEmptyString("")); - } - - @Test - void isEmptyString_StringWithTrailingSpaces_returnsTrue() { + void isEmptyString_emptyString_returnsTrue() { assertTrue(uiHelper.isEmptyString(" ")); } From 465946db23331a0a09498a8564121ad7450101b2 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 00:45:40 +0800 Subject: [PATCH 301/374] Add JUnit tests for UiInput --- .../java/seedu/dietbook/ui/UiInputTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/test/java/seedu/dietbook/ui/UiInputTest.java diff --git a/src/test/java/seedu/dietbook/ui/UiInputTest.java b/src/test/java/seedu/dietbook/ui/UiInputTest.java new file mode 100644 index 0000000000..9981f99ff0 --- /dev/null +++ b/src/test/java/seedu/dietbook/ui/UiInputTest.java @@ -0,0 +1,34 @@ +package seedu.dietbook.ui; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.dietbook.exception.DietException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UiInputTest { + + private UiInput uiInput; + + @BeforeEach + public void setUp() { + uiInput = new UiInput(); + } + + @Test + void processCommand_emptyCommand_expectDietException() { + assertThrows(DietException.class, () -> uiInput.processCommand(" ")); + } + + @Test + void processCommand_commandWithLeadingAndTrailingSpaces_trimmedCommand() throws DietException { + assertEquals("delete 1", uiInput.processCommand(" delete 1 ")); + } + + @Test + void processCommand_commandWithoutLeadingAndTrailingSpaces_command() throws DietException { + assertEquals("delete 1", uiInput.processCommand("delete 1")); + } + +} \ No newline at end of file From f70f054eafb2e83bd0f961483843bd38ff03f24d Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 01:32:41 +0800 Subject: [PATCH 302/374] Use MainLogger for logging --- .../java/seedu/dietbook/person/Person.java | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/src/main/java/seedu/dietbook/person/Person.java b/src/main/java/seedu/dietbook/person/Person.java index eab6392654..d5a41ed426 100644 --- a/src/main/java/seedu/dietbook/person/Person.java +++ b/src/main/java/seedu/dietbook/person/Person.java @@ -1,5 +1,8 @@ package seedu.dietbook.person; +import seedu.dietbook.logger.MainLogger; +import seedu.dietbook.ui.Ui; + import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; @@ -23,7 +26,7 @@ public class Person { private FitnessLevel fitnessLevel; private Gender gender; private String name; - private final Logger logger; + private final MainLogger mainLogger; /** * Constructs a Person with the given name, gender, age, height, fitness level, original, @@ -44,20 +47,19 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh performAssertionsForPerson(name, gender, age, height, originalWeight, currentWeight, targetWeight, fitnessLevel); - logger = Logger.getLogger(Person.class.getName()); - initialiseLogger(); - logger.log(Level.FINE, "Start constructing a person"); - logger.log(Level.FINE, "Name: " + name); - logger.log(Level.FINE, "Gender: " + gender.getDescription()); - logger.log(Level.FINE, "Age: " + age); - logger.log(Level.FINE, "Height: " + height); - logger.log(Level.FINE, "Original weight: " + originalWeight); - logger.log(Level.FINE, "Current weight: " + currentWeight); - logger.log(Level.FINE, "Target weight: " + targetWeight); - logger.log(Level.FINE, "Fitness Level: " + fitnessLevel.getDescription()); + mainLogger = new MainLogger(Person.class.getName()); + mainLogger.log(Level.FINE, "Start constructing a person"); + mainLogger.log(Level.FINE, "Name: " + name); + mainLogger.log(Level.FINE, "Gender: " + gender.getDescription()); + mainLogger.log(Level.FINE, "Age: " + age); + mainLogger.log(Level.FINE, "Height: " + height); + mainLogger.log(Level.FINE, "Original weight: " + originalWeight); + mainLogger.log(Level.FINE, "Current weight: " + currentWeight); + mainLogger.log(Level.FINE, "Target weight: " + targetWeight); + mainLogger.log(Level.FINE, "Fitness Level: " + fitnessLevel.getDescription()); this.name = name.trim(); - logger.log(Level.FINE, "Trimmed Name: " + this.name); + mainLogger.log(Level.FINE, "Trimmed Name: " + this.name); this.gender = gender; this.age = age; this.height = height; @@ -65,17 +67,7 @@ public Person(String name, Gender gender, int age, int height, int originalWeigh this.currentWeight = currentWeight; this.targetWeight = targetWeight; this.fitnessLevel = fitnessLevel; - logger.log(Level.FINE, "Person constructed"); - } - - /** - * Initialises the logger and sets the log level. - */ - private void initialiseLogger() { - Handler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.WARNING); - logger.addHandler(consoleHandler); - logger.setLevel(Level.WARNING); + mainLogger.log(Level.FINE, "Person constructed"); } /** @@ -120,9 +112,9 @@ public String getName() { */ public void setName(String newName) { performAssertionsForNameInput(newName); - logger.log(Level.FINE, "New name: " + newName); + mainLogger.log(Level.FINE, "New name: " + newName); name = newName.trim(); - logger.log(Level.FINE, "Trimmed new name: " + this.name); + mainLogger.log(Level.FINE, "Trimmed new name: " + this.name); } /** @@ -141,7 +133,7 @@ public Gender getGender() { */ public void setGender(Gender newGender) { performAssertionsForGenderInput(newGender); - logger.log(Level.FINE, "New gender: " + newGender.getDescription()); + mainLogger.log(Level.FINE, "New gender: " + newGender.getDescription()); gender = newGender; } @@ -161,7 +153,7 @@ public int getAge() { */ public void setAge(int newAge) { performAssertionsForAgeInput(newAge); - logger.log(Level.FINE, "New age: " + newAge); + mainLogger.log(Level.FINE, "New age: " + newAge); age = newAge; } @@ -181,7 +173,7 @@ public int getHeight() { */ public void setHeight(int newHeight) { performAssertionsForHeight(newHeight); - logger.log(Level.FINE, "New height: " + newHeight); + mainLogger.log(Level.FINE, "New height: " + newHeight); height = newHeight; } @@ -201,7 +193,7 @@ public int getOriginalWeight() { */ public void setOriginalWeight(int newOriginalWeight) { performAssertionsForWeight(newOriginalWeight,"Original weight"); - logger.log(Level.FINE, "New original weight: " + newOriginalWeight); + mainLogger.log(Level.FINE, "New original weight: " + newOriginalWeight); originalWeight = newOriginalWeight; } @@ -221,7 +213,7 @@ public int getCurrentWeight() { */ public void setCurrentWeight(int newCurrentWeight) { performAssertionsForWeight(newCurrentWeight, "Current weight"); - logger.log(Level.FINE, "New current weight: " + newCurrentWeight); + mainLogger.log(Level.FINE, "New current weight: " + newCurrentWeight); currentWeight = newCurrentWeight; } @@ -241,7 +233,7 @@ public int getTargetWeight() { */ public void setTargetWeight(int newTargetWeight) { performAssertionsForWeight(newTargetWeight, "Target weight"); - logger.log(Level.FINE, "New target weight: " + newTargetWeight); + mainLogger.log(Level.FINE, "New target weight: " + newTargetWeight); targetWeight = newTargetWeight; } @@ -261,7 +253,7 @@ public FitnessLevel getFitnessLevel() { */ public void setFitnessLevel(FitnessLevel newFitnessLevel) { performAssertionsForFitnessLevel(newFitnessLevel); - logger.log(Level.FINE, "New fitness level: " + newFitnessLevel); + mainLogger.log(Level.FINE, "New fitness level: " + newFitnessLevel); fitnessLevel = newFitnessLevel; } From 36aafbd6c8bd3841d802479913707ba09c46df64 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 17:26:33 +0800 Subject: [PATCH 303/374] add resource to test folder --- .../seedu/dietbook/database/DataBase.java | 2 - src/test/resources/data.txt | 48 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/data.txt diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index a379d24f9a..e035c0d74f 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -1,6 +1,4 @@ package seedu.dietbook.database; - - import seedu.dietbook.food.Food; diff --git a/src/test/resources/data.txt b/src/test/resources/data.txt new file mode 100644 index 0000000000..7a211bc4e1 --- /dev/null +++ b/src/test/resources/data.txt @@ -0,0 +1,48 @@ +##################################################################### +# 3 LEVEL DATA BASE # +# Canteen -----> Store ------> Food # +# Commands : # +# &%START : start reading data from the data base # +# &%STOP : stop reading data from the data base # +# &%UP : goes down 1 level e.g. Canteen ---> Store # +# &%DOWN : goes down 1 level e.g. Canteen ---> Store # +# &%ADD format : adds the item with the given format # +# # +# Comments : any line that starts with # is ignored # +# # +# Canteen format : {name} # +# Store format : {name} # +# Food format : {name}|{Calorie}|{Carb}|{Protein}|{Fat} # +##################################################################### + +###################################################################### +# Version 0.1 : # +# there is only UP, once a store or canteen is # +# specified we automatically go down 1 level , for this version # +# there is no going out of a store and then coming back to add more# +# Units : Calorie : kcal : Carbs : g Protein : g : Fats : g # +###################################################################### + +&%START +Science canteen +Halal Mini Wok +Prawn Mee Soup(Dry)(Large)|490|0|0|0 +Prawn Mee Soup(Dry)(Small)|390|0|0|0 +Fried Hokkien Prawn Mee(Large)|470|0|0|0 +Fried Hokkien Prawn Mee(Small)|350|0|0|0 +Clay Pot Chicken|440|0|0|0 +Black Pepper Chicken|490|0|0|0 +&%UP +Ayam Penyet +Ayam Penyet Set|699|0|0|0 +Steamed Chicken Set |475|0|0|0 +Ikan Grouper Penyet Set|669|0|0|0 +&%UP +Michelin Star Restaurant +Bouillabaisse with cock crab and poached lobster|520|45|35|56 +Chicken wings with Reblochon pomme purée|450|25|32|66 +Sea bass with prawn tortellini, fennel purée and white wine sauce|530|76|25|43 +&%UP +&%UP +&%STOP + From 3787888cf525adff5f4d0cd29ff760be273b5ddd Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 17:59:34 +0800 Subject: [PATCH 304/374] fix check style --- src/main/java/seedu/dietbook/database/DataBase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index e035c0d74f..1d13992d21 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -1,4 +1,5 @@ package seedu.dietbook.database; + import seedu.dietbook.food.Food; From a7e73e435cbde1c2bba150ab5b945beb9d4123a6 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 18:25:50 +0800 Subject: [PATCH 305/374] Add attribution --- src/main/java/seedu/dietbook/logger/MainLogger.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/seedu/dietbook/logger/MainLogger.java b/src/main/java/seedu/dietbook/logger/MainLogger.java index a5d01a68ba..dc92487d80 100644 --- a/src/main/java/seedu/dietbook/logger/MainLogger.java +++ b/src/main/java/seedu/dietbook/logger/MainLogger.java @@ -9,6 +9,8 @@ public class MainLogger { private final Logger logger; + //@@author HengFuYuen-reused + //Reused from https://stackoverflow.com/a/6315736 with minor modifications public MainLogger(String className) { logger = Logger.getLogger(className); Handler consoleHandler = new ConsoleHandler(); From 6b90cfd85e77c67d8c695474fc5ae0ecc25da032 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 18:26:16 +0800 Subject: [PATCH 306/374] Add assertions for MainLogger --- src/main/java/seedu/dietbook/logger/MainLogger.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/seedu/dietbook/logger/MainLogger.java b/src/main/java/seedu/dietbook/logger/MainLogger.java index dc92487d80..31f0635946 100644 --- a/src/main/java/seedu/dietbook/logger/MainLogger.java +++ b/src/main/java/seedu/dietbook/logger/MainLogger.java @@ -12,6 +12,9 @@ public class MainLogger { //@@author HengFuYuen-reused //Reused from https://stackoverflow.com/a/6315736 with minor modifications public MainLogger(String className) { + assert className != null : "Class name cannot be null"; + assert className.trim().length() > 0 : "Class name cannot be an empty string"; + logger = Logger.getLogger(className); Handler consoleHandler = new ConsoleHandler(); consoleHandler.setLevel(Level.SEVERE); @@ -20,6 +23,10 @@ public MainLogger(String className) { } public void log(Level level, String message) { + assert level != null : "Logging level should not be null"; + assert message != null : "Log message should not be null"; + assert message.trim().length() > 0 : "Log message should not be an empty string"; + logger.log(level, message); } } From ac38fd8087862edd4b1a1273d5cca3411790c328 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 18:33:55 +0800 Subject: [PATCH 307/374] Add JavaDoc comments for MainLogger --- .../java/seedu/dietbook/logger/MainLogger.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/seedu/dietbook/logger/MainLogger.java b/src/main/java/seedu/dietbook/logger/MainLogger.java index 31f0635946..82af1c59cf 100644 --- a/src/main/java/seedu/dietbook/logger/MainLogger.java +++ b/src/main/java/seedu/dietbook/logger/MainLogger.java @@ -5,12 +5,22 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Represents a logger that other classes can utilise to minimise code duplications. + * A MainLogger object creates a logger and console handler and sets their level. + */ public class MainLogger { private final Logger logger; //@@author HengFuYuen-reused //Reused from https://stackoverflow.com/a/6315736 with minor modifications + + /** + * Constructs a MainLogger given the name of the class that uses the logger. + * + * @param className The name of the class that uses the logger. + */ public MainLogger(String className) { assert className != null : "Class name cannot be null"; assert className.trim().length() > 0 : "Class name cannot be an empty string"; @@ -22,6 +32,12 @@ public MainLogger(String className) { logger.setLevel(Level.SEVERE); } + /** + * Logs into the console handler given a message level identifier and message. + * + * @param level A message level identifier. + * @param message The string message to log. + */ public void log(Level level, String message) { assert level != null : "Logging level should not be null"; assert message != null : "Log message should not be null"; From 6fc3120513cf4e5cc7af38a21a5907aed1a3b0c6 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 19:46:46 +0800 Subject: [PATCH 308/374] Update AboutUs.md --- docs/AboutUs.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 000d8f7514..eb8c4bff14 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,8 +1,6 @@ # About us -we are an idiosyncratic and narcissistic batch of self-righteous -elitist who defends presumably morally questionable positions. -Also we are cheap! +We are a team of students from different faculties in the [National University of Singapore](http://nus.edu.sg/). Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: @@ -10,4 +8,4 @@ Display | Name | Github Profile | Portfolio ![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Heng Fu Yuen | [Github](https://github.com/HengFuYuen) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Heng Fu Yuen | [Github](https://github.com/HengFuYuen) | [Portfolio](docs/team/hengfuyuen.md) From ab173f68d7e1b392251028725089464ea7360c02 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 19:47:03 +0800 Subject: [PATCH 309/374] Add PPP --- docs/team/hengfuyuen.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/team/hengfuyuen.md diff --git a/docs/team/hengfuyuen.md b/docs/team/hengfuyuen.md new file mode 100644 index 0000000000..b40f97b4be --- /dev/null +++ b/docs/team/hengfuyuen.md @@ -0,0 +1,6 @@ +# Heng Fu Yuen - Project Portfolio Page + +## Overview + + +### Summary of Contributions \ No newline at end of file From 5b1e1a8c3188615d709d088ab57d940487fb2526 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:11:46 +0800 Subject: [PATCH 310/374] Update introduction --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 035c9e037c..4a1eba4b44 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,7 +1,7 @@ # DietBook User Guide ## Introduction -DietBook is a desktop application targeting NUS students living on campus, optimized for use via a **Command Line Interface**. Not only can DietBook track and show the user's food and nutritional intake, it also provides users with a list of commonly eaten food items around and outside NUS. +DietBook is a desktop application, optimized for use via a **Command Line Interface** (CLI). It is designed to **track your food and nutritional intake** as well as provide you with your **daily calorie recommendation**. 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. * Table of Contents {:toc} From 2b16f0606650f058e190bfb0525ac0e18aa2eb30 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:11:57 +0800 Subject: [PATCH 311/374] Update quick start --- docs/UserGuide.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 4a1eba4b44..3652ca2896 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -7,15 +7,16 @@ DietBook is a desktop application, optimized for use via a **Command Line Interf {:toc} ## Quick Start -1. Ensure that you have Java 11 or above installed. +1. Ensure that you have **Java 11** installed. 1. Download the latest version of `dietbook.jar` from [here](https://github.com/AY2021S1-CS2113-T14-4/tp/releases). 1. Copy the file to the folder you want to use as the home folder for your DietBook. -1. Either double-click the jar file to start the application or navigate to the folder containing the jar file on command prompt and run the command `java -jar dietbook.jar`. -1. For first time users:
-A CLI, similar to the one shown below, should appear within a few seconds. Follow the instructions provided to setup DietBook or refer to [name](####Entering username: `name`) and [info](####Entering user information: `info`) for more detailed explanations.
+1. Navigate to the folder containing the jar file on command prompt and run the command `java -jar dietbook.jar`. +1. Enable full-screen for a better experience but rest assure that DietBook will still function normally even in non full-screen mode. +1. For **first time users**:
+A CLI, similar to the one shown below, should appear within a few seconds. Follow the instructions provided to setup DietBook or refer to [name](#entering-username-name) and [info](#entering-user-information-info) for more detailed explanations.
![DietBook Welcome Message](images/DietBookWelcomeMessage.PNG) 1. Start using DietBook by typing any valid command and pressing Enter to execute it. -1. Refer to the [Features](##Features) section below for more details of each command. +1. Refer to the [Features](#features) section below for more details of each command or jump to [Command Summary](#command-summary) section for a list of valid commands. ## Features From 7a46f6c7e24feb751c64491be13302fb63b53d5c Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:15:18 +0800 Subject: [PATCH 312/374] Add notes and warnings to features section --- docs/UserGuide.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3652ca2896..a9f4bebc31 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -20,26 +20,27 @@ A CLI, similar to the one shown below, should appear within a few seconds. Follo ## Features -Notes about the command format: +**:information_source: Notes about the command format:** -* Words in `UPPER_CASE` are parameters to be supplied by the user.
-e.g. For `delete INDEX`, `delete 1` would be a valid command. +* Words in `UPPER_CASE` are **parameters to be supplied** by the user.
+e.g. For `name YOUR_NAME_OR_NICKNAME`, `name Jack` would be a valid command. -* Parameters in square brackets are optional. However, if all parameters are optional, at least one parameter needs to be given. In such cases, any one of the parameters would be valid.
-e.g. For `add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT]`, `add x/1 n/Toast k/120`, `add x/1 n/Toast k/120 c/18`, `add x/1 n/Toast k/120 c/18 p/3`, `add x/1 n/Toast k/120 - c/18 p/3 f/4` are all valid commands. +* Parameters in **square brackets are optional**. However, if all parameters are optional, **at least one parameter needs to be given**. In such cases, any one of the parameters would be valid.
+e.g. For `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]`, `editinfo a/31` and `editinfo h/173 o/87` are valid commands but `editinfo +` is not. -* For commands with multiple parameters, the parameters can be in any order.
-e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` and `add x/1 n/mee` are both valid. +* For commands with multiple parameters, the parameters can be in any order **only if there is more than one parameter labelled with parameter tags** like `n/`, `a/`, etc. Otherwise, they **must be entered in the sequence as shown** on this guide, the [Help Command](#to-view-a-list-of-valid-commands-help) or the [Summary Command](#command-summary).
+e.g. For `calculate NUTRIENT_TYPE [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm]`,`calculate fat 2020-07-03T23:59 2020-09-03T23:59` is valid but `calculate 2020-07-03T23:59 fat 2020-09-03T23:59` is not.
+e.g. For `add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] [yyyy-mm-ddTHH:mm]`, `add n/bao x/1 p/5 k/157 f/5 c/23 2020-09-03T23:59` is valid but `add n/bao x/1 p/5 2020-09-03T23:59 k/157 f/5 c/23` is not as the time needs to be entered as the last parameter. + +**:warning: Please take note of the following:** * Command words and parameter indicators are case-sensitive.
e.g. `help` is a valid command but `Help` is not.
e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add N/mee x/1` is not. -* A single spacing to separate command words, parameters, command word and parameters is required.
-e.g. For `calculate all`, `calculate all` is valid but `calculateall` and `calculate`         `all` is not.
-e.g. For `delete INDEX`, `delete 1` is valid if there is a food item with index 1 but`delete1` is not.
-e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add n/meex/1` is not.
+* Spacing to separate command word and parameters is required.
+e.g. For `calculate NUTRIENT_TYPE`, `calculate all` is valid but `calculateall` is not. ### Features related to user information From 0bf41b7eab89e1fbf496b92007553a905955e0c9 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:15:47 +0800 Subject: [PATCH 313/374] Update name command feature --- docs/UserGuide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index a9f4bebc31..8691cfb7eb 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -46,12 +46,12 @@ e.g. For `calculate NUTRIENT_TYPE`, `calculate all` is valid but `calculateall` #### Entering username: `name` -Stores the user's name into DietBook during the initial setup. +Stores the user's name or nickname into DietBook during the initial setup. -Format: `name YOUR_NAME` +Format: `name YOUR_NAME_OR_NICKNAME` * The name given must not be empty. -* This command is **only used when setting up DietBook for the first time**. Any subsequent editing of the name can be done using the [editinfo](#Editing user information: `editinfo`) command. +* This command is **only used when setting up DietBook for the first time**. Any subsequent editing of the name can be done using the [editinfo](#editing-user-information-editinfo) command. Example of usage: From 66cee4c796a2a47847d035e491f0f8fb757a0cac Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:16:06 +0800 Subject: [PATCH 314/374] Update name command feature --- docs/UserGuide.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 8691cfb7eb..2e87a13080 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -55,13 +55,12 @@ Format: `name YOUR_NAME_OR_NICKNAME` Example of usage: -* `name Tom and Jerry`
* `name Jack` -Output example for usage example 2: +Output example: ``` Hi Jack! -Before we get started, I would like to know about about you so that I can make more +Before we get started, I would like to know about about you so that I can make more accurate calculations for you :). Therefore, could you please share with me the following: - Your gender either F for female or M for male or O for others. - Your age which is a positive integer. From 49c8a1106aa32c3b8aedfefc7030bc6c693a6aef Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:16:51 +0800 Subject: [PATCH 315/374] Update info command feature --- docs/UserGuide.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 2e87a13080..76a8f8a02f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -68,7 +68,7 @@ accurate calculations for you :). Therefore, could you please share with me the - Your original weight in kg, the weight when you first started using DietBook or you current weight. - Your current weight in kg. - Your target weight in kg, or your current weight if that is also your target weight. -- Your activity level, represented by a number from 1 to 5. +- Your fitness level, represented by a number from 1 to 5. 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. 2 = You engage in some form of light exercise or have a job that requires some physical activity. 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. @@ -76,22 +76,22 @@ accurate calculations for you :). Therefore, could you please share with me the 5 = You engage in extremely vigorous exercise or have an extremely physically demanding job. Please input your details in the following format: - info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL - Example: info g/F a/21 h/165 o/65 c/65 t/55 l/2 + info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT f/FITNESS_LEVEL + Example: info g/F a/21 h/165 o/65 c/65 t/55 f/2 ``` -#### Entering user information : `info` +#### Entering user information: `info` Stores the user's personal information into DietBook during the initial setup. -Format: `info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL` +Format: `info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT f/FITNESS_LEVEL` -* This command is **only used when setting up DietBook for the first time**. Any subsequent editing of user information can be done using the [editinfo](#Editing user information: `editinfo`) command. +* This command is **only used when setting up DietBook for the first time**. Any subsequent editing of user information can be done using the [editinfo](#editing-user-information-editinfo) command. * The gender must be either **`M` for male, `F` for female or `O` for others**. -* The age must be a positive integer **from 0 to 150, inclusive**. -* The height in cm must be a positive integer **from 1 to 300, inclusive**. -* The original, current and target weight in kg must be a positive integer ***from 1 to 500, inclusive**. -* The activity level must be a positive integer **from 1 to 5, inclusive**. +* The age must be a positive **integer from 0 to 150, inclusive**. +* The height in cm must be a positive **integer from 1 to 300, inclusive**. +* The original, current and target weight in kg must be a positive **integer from 1 to 500, inclusive**. +* The fitness level must be a positive **integer from 1 to 5, inclusive**. * 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. * 2 = You engage in some form of light exercise or have a job that requires some physical activity. * 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. @@ -100,11 +100,11 @@ Format: `info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGE Example of usage: -* `info g/M a/21 h/175 o/85 c/85 t/75 l/2` stores the user's gender, age, height, original, current and target weight as well as the activity level to `male`, `21`, `175`, `85`, `85`, `75` and `You engage in some form of light exercise or have a job that requires some physical activity.` respectively. +* `info g/M a/21 h/175 o/85 c/85 t/75 f/2` stores the user's gender, age, height, original, current and target weight as well as the fitness level to `male`, `21`, `175`, `85`, `85`, `75` and `You engage in some form of light exercise or have a job that requires some physical activity.` respectively. Output example: ``` -Thank you! DietBook has been initialised and you may start by entering any valid commands. +Thank you! DietBook has been initialised and you may start by entering any valid commands. If you require a list of valid commands, you can enter: help ``` @@ -124,7 +124,7 @@ Here is your information: Original weight: 85kg Current weight: 85kg Target weight: 75kg - Activity level: You engage in some form of light exercise or have a job that requires some physical activity. + Fitness level: You engage in some form of light exercise or have a job that requires some physical activity. ``` #### Editing user information: `editinfo` From f7d1d1264bd7f66ed946938adadaa2476aa23186 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:17:19 +0800 Subject: [PATCH 316/374] Update editinfo command feature --- docs/UserGuide.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 76a8f8a02f..7573224e7c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -131,17 +131,16 @@ Here is your information: Edits the user information stored in DietBook. -Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]` +Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]` * Although all parameters are listed as optional, **at least one of the optional fields needs to be provided**. In this case, any one of the parameters would work. -* If more than one parameter is given, they can be in any order. -* Existing values will be updated to the input values. +* Existing values will be updated to the input values, even if the new value given is the same as the existing value. * The name must not be empty. * The gender must be either **`M` for male, `F` for female or `O` for others**. -* The age must be a positive integer **from 0 to 150, inclusive**. -* The height must be a positive integer **from 1 to 300, inclusive**. -* The original, current and target weight must be a positive integer **from 1 to 500, inclusive**. -* The activity level must be a positive integer **from 1 to 5, inclusive**. +* The age must be a positive **integer from 0 to 150, inclusive**. +* The height must be a positive **integer from 1 to 300, inclusive**. +* The original, current and target weight must be a positive **integer from 1 to 500, inclusive**. +* The fitness level must be a positive **integer from 1 to 5, inclusive**. * 1 = You hardly engage in any exercise or have a job that requires little to no physical activity. * 2 = You engage in some form of light exercise or have a job that requires some physical activity. * 3 = You engage in moderate amount of exercise or have a job that requires moderate physical activity. @@ -150,10 +149,9 @@ Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/ Example of usage: -* `editinfo n/John` edits the name of the user to be `John`. -* Both `editinfo c/75 l/4` and `editinfo l/4 c/75` edits the current weight and activity level of the user to be `75` and `You engage in vigorous exercise or have a physically demanding job.` respectively. +* Both `editinfo c/75 f/4` and `editinfo f/4 c/75` edits the current weight and fitness level of the user to be `75` and `You engage in vigorous exercise or have a physically demanding job.` respectively. -Output example for usage example 2: +Output example: ``` Got it! I've updated your personal information: Name: Jack @@ -163,7 +161,7 @@ Got it! I've updated your personal information: Original weight: 85kg Current weight: 75kg Target weight: 75kg - Activity level: You engage in vigorous exercise or have a physically demanding job. + Fitness level: You engage in vigorous exercise or have a physically demanding job. ``` ### Features related to the food database From a508052b3b694165c83fda7e295947fb142a67d2 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:17:41 +0800 Subject: [PATCH 317/374] Update minor formatting issues --- docs/UserGuide.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7573224e7c..824cda5e16 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -199,7 +199,8 @@ Example of Usage: * `add n/Prawn Mee x/1` adds the first instance of food with a name that contains Prawn Mee with a portion of 1. Output example: -```Here are the food items in DietBook: +``` +Here are the food items in DietBook: 1. Prawn Mee Soup(Dry)(Large) | calorie : 490 | protein : 0 | carbohydrate : 0 | fats : 0 -- (1) ``` @@ -378,7 +379,7 @@ Similar Inputs and outputs for the following ### Other features -#### To view a list of valid commands: help: `help` +#### To view a list of valid commands: `help` Displays the helping guide for commands. From 2863695ef3b5599cecc45ff8e43b7d9efbe3e026 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:18:15 +0800 Subject: [PATCH 318/374] Update command summary --- docs/UserGuide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 824cda5e16..1e02d3f485 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -465,7 +465,7 @@ For other system related commands Action | Format, Examples ---- | ---- -Enter name | **Note**: Used only when setting up DietBook for the first time.
`name YOUR_NAME`
e.g.,`name Jack` -Enter info | **Note**: Used only when setting up DietBook for the first time.
`info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT l/ACTIVITY_LEVEL`
e.g.,`info g/M a/21 h/175 o/85 c/85 t/75 l/2` +Enter name | **Note**: Used only when setting up DietBook for the first time.
`name YOUR_NAME_OR_NICKNAME`
e.g.,`name Jack` +Enter info | **Note**: Used only when setting up DietBook for the first time.
`info g/GENDER a/AGE h/HEIGHT o/ORIGINAL_WEIGHT c/CURRENT_WEIGHT t/TARGET_WEIGHT f/FITNESS_LEVEL`
e.g.,`info g/M a/21 h/175 o/85 c/85 t/75 f/2` View user info | `userinfo` -Edit user info | `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL]`
e.g.,`editinfo c/75 l/4` +Edit user info | `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]`
e.g.,`editinfo c/75 f/4` From faba5e1c759ada61acfd36d559df41c2e2e05109 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 20:24:27 +0800 Subject: [PATCH 319/374] Update image --- docs/images/DietBookWelcomeMessage.PNG | Bin 23353 -> 18466 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/DietBookWelcomeMessage.PNG b/docs/images/DietBookWelcomeMessage.PNG index 483610d2e69bb9d38169e7faaad7e8c694e93471..3b938e3a0dad014d46f2dcfe81f4a2c2b6ba76d1 100644 GIT binary patch literal 18466 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 Date: Sat, 7 Nov 2020 20:50:15 +0800 Subject: [PATCH 320/374] Update code quality --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index b54daf2970..a22a61bc76 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,6 +5,7 @@ DietBook is a Command Line Interface (CLI) desktop application designed to **track your food and nutritional intake** as well as provide you with your **daily calorie recommendation**. 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. **Useful links:** + * If you would like to use DietBook, head over to [User Guide](UserGuide.md) to get started. * If you would like to know more about developing DietBook, head over to [Developer Guide](DeveloperGuide.md). * If you would like to know more about the developers, head over to [About Us](AboutUs.md). From 99026767bf3f6d9b8cebfae6bdf8cdf2e404ca5a Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sat, 7 Nov 2020 21:51:16 +0800 Subject: [PATCH 321/374] Changed get... methods in CalculatorData class to getPortioned... methods. --- .../dietbook/calculator/CalculatorData.java | 24 +++++++++---------- .../dietbook/calculator/CalculatorTest.java | 16 ++++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/CalculatorData.java b/src/main/java/seedu/dietbook/calculator/CalculatorData.java index 5af62efedb..ad7e8be609 100644 --- a/src/main/java/seedu/dietbook/calculator/CalculatorData.java +++ b/src/main/java/seedu/dietbook/calculator/CalculatorData.java @@ -24,7 +24,7 @@ public void inputData(FoodList foodList) { } public List getTotalCalorie() { - this.foods = list.getFoods(); + this.foods = list.getPortionedFoods(); List calories = new ArrayList<>(); for (Food food : foods) { calories.add(food.getCalorie()); @@ -33,7 +33,7 @@ public List getTotalCalorie() { } public List getTotalCalorie(LocalDateTime startTime) { - this.foods = list.getFoodsAfterDateTime(startTime); + this.foods = list.getPortionedFoodsAfterDateTime(startTime); List calories = new ArrayList<>(); for (Food food : foods) { calories.add(food.getCalorie()); @@ -42,7 +42,7 @@ public List getTotalCalorie(LocalDateTime startTime) { } public List getTotalCalorie(LocalDateTime startTime, LocalDateTime endTime) { - this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + this.foods = list.getPortionedFoodsInDateTimeRange(startTime, endTime); List calories = new ArrayList<>(); for (Food food : foods) { calories.add(food.getCalorie()); @@ -51,7 +51,7 @@ public List getTotalCalorie(LocalDateTime startTime, LocalDateTime endT } public List getTotalCarb() { - this.foods = list.getFoods(); + this.foods = list.getPortionedFoods(); List carbs = new ArrayList<>(); for (Food food : foods) { carbs.add(food.getCarbohydrate()); @@ -60,7 +60,7 @@ public List getTotalCarb() { } public List getTotalCarb(LocalDateTime startTime) { - this.foods = list.getFoodsAfterDateTime(startTime); + this.foods = list.getPortionedFoodsAfterDateTime(startTime); List carbs = new ArrayList<>(); for (Food food : foods) { carbs.add(food.getCalorie()); @@ -69,7 +69,7 @@ public List getTotalCarb(LocalDateTime startTime) { } public List getTotalCarb(LocalDateTime startTime, LocalDateTime endTime) { - this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + this.foods = list.getPortionedFoodsInDateTimeRange(startTime, endTime); List carbs = new ArrayList<>(); for (Food food : foods) { carbs.add(food.getCarbohydrate()); @@ -78,7 +78,7 @@ public List getTotalCarb(LocalDateTime startTime, LocalDateTime endTime } public List getTotalProtein() { - this.foods = list.getFoods(); + this.foods = list.getPortionedFoods(); List proteins = new ArrayList<>(); for (Food food : foods) { proteins.add(food.getProtein()); @@ -87,7 +87,7 @@ public List getTotalProtein() { } public List getTotalProtein(LocalDateTime startTime) { - this.foods = list.getFoodsAfterDateTime(startTime); + this.foods = list.getPortionedFoodsAfterDateTime(startTime); List proteins = new ArrayList<>(); for (Food food : foods) { proteins.add(food.getProtein()); @@ -96,7 +96,7 @@ public List getTotalProtein(LocalDateTime startTime) { } public List getTotalProtein(LocalDateTime startTime, LocalDateTime endTime) { - this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + this.foods = list.getPortionedFoodsInDateTimeRange(startTime, endTime); List proteins = new ArrayList<>(); for (Food food : foods) { proteins.add(food.getProtein()); @@ -105,7 +105,7 @@ public List getTotalProtein(LocalDateTime startTime, LocalDateTime endT } public List getTotalFat() { - this.foods = list.getFoods(); + this.foods = list.getPortionedFoods(); List fats = new ArrayList<>(); for (Food food : foods) { fats.add(food.getFat()); @@ -114,7 +114,7 @@ public List getTotalFat() { } public List getTotalFat(LocalDateTime startTime) { - this.foods = list.getFoodsAfterDateTime(startTime); + this.foods = list.getPortionedFoodsAfterDateTime(startTime); List fats = new ArrayList<>(); for (Food food : foods) { fats.add(food.getFat()); @@ -123,7 +123,7 @@ public List getTotalFat(LocalDateTime startTime) { } public List getTotalFat(LocalDateTime startTime, LocalDateTime endTime) { - this.foods = list.getFoodsInDateTimeRange(startTime, endTime); + this.foods = list.getPortionedFoodsInDateTimeRange(startTime, endTime); List fats = new ArrayList<>(); for (Food food : foods) { fats.add(food.getFat()); diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index 75f6b3b7c5..6cb4b187bb 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -14,24 +14,24 @@ class CalculatorTest { @Test void calculateCalorie_foodListOfThreeItems_sumOfCalorie() { FoodList foodList = new FoodList(); - foodList.addFood(1, new Food("chicken rice", 666, 55, 30, 0)); - foodList.addFood(1, new Food("pancake", 150, 16, 0, 0)); - foodList.addFood(1, new Food("bao", 290, 0, 16, 0)); + foodList.addFood(2, new Food("chicken rice", 666, 55, 30, 0)); + foodList.addFood(2, new Food("pancake", 150, 16, 0, 0)); + foodList.addFood(2, new Food("bao", 290, 0, 16, 0)); CalculatorData data = new CalculatorData(); data.inputData(foodList); Calculator calculator = new Calculator(data); - assertEquals(666 + 150 + 290, calculator.calculateCalorie()); + assertEquals(2 * (666 + 150 + 290), calculator.calculateCalorie()); } @Test void calculateCarb_foodListOfThreeItems_sumOfCarb() { FoodList foodList = new FoodList(); - foodList.addFood(1, new Food("chicken rice", 666, 55, 30, 0)); - foodList.addFood(1, new Food("pancake", 150, 16, 0, 0)); - foodList.addFood(1, new Food("bao", 290, 0, 16, 0)); + foodList.addFood(3, new Food("chicken rice", 666, 55, 30, 0)); + foodList.addFood(3, new Food("pancake", 150, 16, 0, 0)); + foodList.addFood(3, new Food("bao", 290, 0, 16, 0)); CalculatorData data = new CalculatorData(foodList); Calculator calculator = new Calculator(data); - assertEquals(55 + 16, calculator.calculateCarb()); + assertEquals(3 * (55 + 16), calculator.calculateCarb()); } @Test From 221662a6211d6f2f8f1fbb3f761a56d211c19902 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 22:07:33 +0800 Subject: [PATCH 322/374] implement and test search food by index function in data base --- .../seedu/dietbook/database/DataBase.java | 9 ++++++++ .../seedu/dietbook/database/DataBaseTest.java | 22 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index d671260c6c..713e70809b 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -123,6 +123,15 @@ public void printAllData() { // -------- Search functions -------- + /** + * This method returns the ith food item from the entire list of food from the data base + * @param index entry number of the food + * @return food object + */ + public Food searchFoodByIndex(int index){ + return foodStream().skip(index - 1).findFirst().orElseThrow(); + } + /** * This method searchs the whole data base and returns the first food item whose name contains the provided string. * ( CASE SENSITIVE ! ) diff --git a/src/test/java/seedu/dietbook/database/DataBaseTest.java b/src/test/java/seedu/dietbook/database/DataBaseTest.java index d1823de021..458f5c9155 100644 --- a/src/test/java/seedu/dietbook/database/DataBaseTest.java +++ b/src/test/java/seedu/dietbook/database/DataBaseTest.java @@ -12,12 +12,26 @@ public static void main(String[] args) { database.printAllData(); // ---- Using stream version to print ----- - System.out.println("------------ printing using food stream ------------"); - database.foodStream().forEach(System.out::println); + // System.out.println("------------ printing using food stream ------------"); + // database.foodStream().forEach(System.out::println); // ---- Printing out as list ----- - System.out.println("---------- printing food using list --------------"); - database.getFoodList().forEach(System.out::println); + // System.out.println("---------- printing food using list --------------"); + // database.getFoodList().forEach(System.out::println); + + // ---- get food list string ---- + System.out.print(database.getFoodListString()); + + // ---- search food by index test ---- + System.out.println("-------- testing the searchFoodByIndex function --------"); + System.out.println("Input : 1 ## OutPut : " + database.searchFoodByIndex(1)); + System.out.println("Input : 10 ## Output : " + database.searchFoodByIndex(10)); + System.out.println("Trying a negative test case : Input : 10000"); + try { + database.searchFoodByIndex(10000); + } catch (NoSuchElementException e){ + System.out.println("The index is too high!" + e); + } // ---- search food by name test ----- try { From f2cc9e0a6c7e053b0e18c1cebcca23f3f2939675 Mon Sep 17 00:00:00 2001 From: tikimonarch Date: Sat, 7 Nov 2020 22:13:00 +0800 Subject: [PATCH 323/374] Exception Bug Fixes - calculate error message fixed - list exception message fixed - single word command fixed ->help ->recommend ->exit ->userinfo ->data ->clear -fixed time detection --- src/main/java/seedu/dietbook/Manager.java | 12 +- .../seedu/dietbook/checker/InputChecker.java | 21 +- .../dietbook/command/CalculateCommand.java | 190 +++++++++--------- .../seedu/dietbook/command/ClearCommand.java | 8 + .../seedu/dietbook/command/DataCommand.java | 7 + .../seedu/dietbook/command/ExitCommand.java | 9 +- .../seedu/dietbook/command/HelpCommand.java | 8 + .../seedu/dietbook/command/ListCommand.java | 24 +-- .../dietbook/command/RecommendCommand.java | 8 +- .../dietbook/command/UserinfoCommand.java | 8 + .../java/seedu/dietbook/parser/Parser.java | 6 +- 11 files changed, 177 insertions(+), 124 deletions(-) diff --git a/src/main/java/seedu/dietbook/Manager.java b/src/main/java/seedu/dietbook/Manager.java index ed33dcb4ee..c37e274cb5 100644 --- a/src/main/java/seedu/dietbook/Manager.java +++ b/src/main/java/seedu/dietbook/Manager.java @@ -115,17 +115,17 @@ public Command manage(String userInput) throws DietException { return new CalculateCommand(calculator.calculateCalorie(), calculator.calculateCarb(), calculator.calculateProtein(), calculator.calculateFat(), Parser.getCommandParam(userInput)); case COMMAND_CLEAR: - return new ClearCommand(); + return new ClearCommand(userInput); case COMMAND_DATA: - return new DataCommand(); + return new DataCommand(userInput); case COMMAND_DELETE: return new DeleteCommand(Parser.getCommandIndex(userInput)); case COMMAND_EDIT_INFO: return new EditInfoCommand(userInput); case COMMAND_EXIT: - return new ExitCommand(); + return new ExitCommand(userInput); case COMMAND_HELP: - return new HelpCommand(); + return new HelpCommand(userInput); case COMMAND_INFO: return new InfoCommand(userInput); case COMMAND_LIST: @@ -133,9 +133,9 @@ public Command manage(String userInput) throws DietException { case COMMAND_NAME: return new NameCommand(Parser.getCommandParam(userInput)); case COMMAND_RECOMMEND: - return new RecommendCommand(getPerson()); + return new RecommendCommand(getPerson(), userInput); case COMMAND_USERINFO: - return new UserinfoCommand(); + return new UserinfoCommand(userInput); default: throw new DietException("There's no such command!"); } diff --git a/src/main/java/seedu/dietbook/checker/InputChecker.java b/src/main/java/seedu/dietbook/checker/InputChecker.java index e40976eda4..76881f3692 100644 --- a/src/main/java/seedu/dietbook/checker/InputChecker.java +++ b/src/main/java/seedu/dietbook/checker/InputChecker.java @@ -23,7 +23,8 @@ public class InputChecker { public static final String[] PARAM_FITNESS = {"1","2","3","4","5"}; public static final String[] PARAM_ADD = {"n/","x/","k/"}; public static final String[] FULL_PARAM_ADD = {"n/","x/","k/","c/","p/","f/"}; - public static final String[] PARAM_CALCULATE = {"fat", "carbohydrate","protein", "calorie", "all"}; + public static final String[] PARAM_CALCULATE = {"fat", "carb","protein", "calorie", "all"}; + public static final String[] SINGLE_COMMAND = {"clear", "data","exit", "help", "recommend", "userinfo"}; public static final String[] PARAM_GENDER = {"M","F","O"}; public static final String[] PARAM_INFO = {"g/","a/","h/","f/","o/","t/","c/"}; public static final String[] PARAM_EDIT_INFO = {"n/","g/","a/","h/","f/","o/","t/","c/"}; @@ -42,6 +43,24 @@ public static void checkEmpty(String userInput, String command) throws DietExcep } } + /** + * Takes in user input to check for single word commands. + * + * @param input user input. + * @throws DietException when a single word command has "options" attached. + */ + public static void checkSingleCommand(String input) throws DietException { + boolean isSingleCommand = false; + for (String command: SINGLE_COMMAND) { + if (input.trim().equals(command)) { + isSingleCommand = true; + } + } + if (!isSingleCommand) { + throw new DietException("Error! This command has no option!"); + } + } + /** * Takes in processed user input to check for options specified with an empty field. * diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index 88e52824ed..75c67c5d8b 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -38,104 +38,100 @@ public void execute(Manager manager, Ui ui) throws DietException { manager.setCalculator(); String[] processedParam = this.param.split("\\s+"); InputChecker.checkCalculateParam(processedParam); - try { - switch (processedParam[0]) { - case "all": - if (processedParam.length == 1) { - ui.printAllIntake(this.calorie, this.carb, this.protein, this.fat); - } else if (processedParam.length == 2) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); - ui.printAllIntake(calorie, carb, protein, fat, startTime); - } else if (processedParam.length == 3) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - endTime = LocalDateTime.parse(processedParam[2]); - InputChecker.checkEndDate(startTime, endTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); - ui.printAllIntake(calorie, carb, protein, fat, startTime, endTime); - } - break; - case "calorie": - if (processedParam.length == 1) { - ui.printCalorieIntake(this.calorie); - } else if (processedParam.length == 2) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); - ui.printCalorieIntake(calorie, startTime); - } else if (processedParam.length == 3) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - endTime = LocalDateTime.parse(processedParam[2]); - InputChecker.checkEndDate(startTime, endTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); - ui.printCalorieIntake(calorie, startTime, endTime); - } - break; - case "carb": - if (processedParam.length == 1) { - ui.printCarbIntake(this.carb); - } else if (processedParam.length == 2) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); - ui.printCarbIntake(carb, startTime); - } else if (processedParam.length == 3) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - endTime = LocalDateTime.parse(processedParam[2]); - InputChecker.checkEndDate(startTime, endTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); - ui.printCarbIntake(carb, startTime, endTime); - } - break; - case "protein": - if (processedParam.length == 1) { - ui.printProteinIntake(this.protein); - } else if (processedParam.length == 2) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); - ui.printProteinIntake(protein, startTime); - } else if (processedParam.length == 3) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - endTime = LocalDateTime.parse(processedParam[2]); - InputChecker.checkEndDate(startTime, endTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); - ui.printProteinIntake(protein, startTime, endTime); - } - break; - case "fat": - if (processedParam.length == 1) { - ui.printFatIntake(this.fat); - } else if (processedParam.length == 2) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); - ui.printFatIntake(fat, startTime); - } else if (processedParam.length == 3) { - startTime = LocalDateTime.parse(processedParam[1]); - InputChecker.checkFutureDate(startTime); - endTime = LocalDateTime.parse(processedParam[2]); - InputChecker.checkEndDate(startTime, endTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); - ui.printFatIntake(fat, startTime, endTime); - } - break; - default: - throw new DietException("No such nutrient type!"); + switch (processedParam[0]) { + case "all": + if (processedParam.length == 1) { + ui.printAllIntake(this.calorie, this.carb, this.protein, this.fat); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); + ui.printAllIntake(calorie, carb, protein, fat, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); + ui.printAllIntake(calorie, carb, protein, fat, startTime, endTime); } - } catch (Exception e) { - throw new DietException("Wrong date time format (Format: yyyy-mm-ddTHH:mm) or future date entered!"); + break; + case "calorie": + if (processedParam.length == 1) { + ui.printCalorieIntake(this.calorie); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); + ui.printCalorieIntake(calorie, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); + ui.printCalorieIntake(calorie, startTime, endTime); + } + break; + case "carb": + if (processedParam.length == 1) { + ui.printCarbIntake(this.carb); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); + ui.printCarbIntake(carb, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); + carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); + ui.printCarbIntake(carb, startTime, endTime); + } + break; + case "protein": + if (processedParam.length == 1) { + ui.printProteinIntake(this.protein); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); + ui.printProteinIntake(protein, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); + protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); + ui.printProteinIntake(protein, startTime, endTime); + } + break; + case "fat": + if (processedParam.length == 1) { + ui.printFatIntake(this.fat); + } else if (processedParam.length == 2) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); + ui.printFatIntake(fat, startTime); + } else if (processedParam.length == 3) { + startTime = LocalDateTime.parse(processedParam[1]); + InputChecker.checkFutureDate(startTime); + endTime = LocalDateTime.parse(processedParam[2]); + InputChecker.checkEndDate(startTime, endTime); + fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); + ui.printFatIntake(fat, startTime, endTime); + } + break; + default: + throw new DietException("No such nutrient type!"); } } } diff --git a/src/main/java/seedu/dietbook/command/ClearCommand.java b/src/main/java/seedu/dietbook/command/ClearCommand.java index 41f7939ab3..5373da5b8a 100644 --- a/src/main/java/seedu/dietbook/command/ClearCommand.java +++ b/src/main/java/seedu/dietbook/command/ClearCommand.java @@ -2,9 +2,16 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; public class ClearCommand extends Command { + String input; + + public ClearCommand(String input) { + this.input = input; + } + @Override public void execute(Manager manager, Ui ui) throws DietException { if (commandCount == 1) { @@ -12,6 +19,7 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } + InputChecker.checkSingleCommand(this.input); ui.printClearFoodListMessage(); manager.getFoodList().clear(); } diff --git a/src/main/java/seedu/dietbook/command/DataCommand.java b/src/main/java/seedu/dietbook/command/DataCommand.java index adabf27c11..8aace388b9 100644 --- a/src/main/java/seedu/dietbook/command/DataCommand.java +++ b/src/main/java/seedu/dietbook/command/DataCommand.java @@ -2,9 +2,15 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; public class DataCommand extends Command { + String input; + + public DataCommand(String input) { + this.input = input; + } @Override public void execute(Manager manager, Ui ui) throws DietException { @@ -13,6 +19,7 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } + InputChecker.checkSingleCommand(this.input); ui.printDatabase(manager.getDataBase().getFoodListString()); } } diff --git a/src/main/java/seedu/dietbook/command/ExitCommand.java b/src/main/java/seedu/dietbook/command/ExitCommand.java index 14c69eb6b9..5a80010d92 100644 --- a/src/main/java/seedu/dietbook/command/ExitCommand.java +++ b/src/main/java/seedu/dietbook/command/ExitCommand.java @@ -3,6 +3,8 @@ import seedu.dietbook.DietBook; import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; +import seedu.dietbook.exception.DietException; import seedu.dietbook.person.FitnessLevel; import seedu.dietbook.person.Gender; import seedu.dietbook.saveload.PersonSaveLoadManager; @@ -11,14 +13,16 @@ public class ExitCommand extends Command { PersonSaveLoadManager personSave; FoodSaveLoadManager foodSave; + String input; - public ExitCommand() { + public ExitCommand(String input) { this.personSave = new PersonSaveLoadManager(); this.foodSave = new FoodSaveLoadManager(); + this.input = input; } @Override - public void execute(Manager manager, Ui ui) { + public void execute(Manager manager, Ui ui) throws DietException { //ActivityLevel actLvl = manager.getPerson().getActivityLevel(); //int actLvlInt = 1; //if (actLvl.equals(ActivityLevel.NONE)) { @@ -54,6 +58,7 @@ public void execute(Manager manager, Ui ui) { //personSave.save("UserInfo.txt"); //foodSave.save("FoodList.txt", manager.getFoodList().getFoods()); //ui.dataSuccessfullySavedMessage(); + InputChecker.checkSingleCommand(this.input); ui.printExitMessage(); DietBook.isExit = true; } diff --git a/src/main/java/seedu/dietbook/command/HelpCommand.java b/src/main/java/seedu/dietbook/command/HelpCommand.java index cb0e26bbc6..c6d731bfd4 100644 --- a/src/main/java/seedu/dietbook/command/HelpCommand.java +++ b/src/main/java/seedu/dietbook/command/HelpCommand.java @@ -2,10 +2,17 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; //@@author tikimonarch public class HelpCommand extends Command { + private String input; + + public HelpCommand(String input) { + this.input = input; + } + @Override public void execute(Manager manager, Ui ui) throws DietException { if (commandCount == 1) { @@ -13,6 +20,7 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } + InputChecker.checkSingleCommand(this.input); ui.printHelpCommandMessage(); } } diff --git a/src/main/java/seedu/dietbook/command/ListCommand.java b/src/main/java/seedu/dietbook/command/ListCommand.java index 9a3f6246ea..afe1aba0f1 100644 --- a/src/main/java/seedu/dietbook/command/ListCommand.java +++ b/src/main/java/seedu/dietbook/command/ListCommand.java @@ -30,20 +30,16 @@ public void execute(Manager manager, Ui ui) throws DietException { if (processedInput.length == 1) { ui.printFoodList(manager.getFoodList().toString()); } else { - try { - if (processedInput.length == 2) { - startTime = LocalDateTime.parse(processedInput[1]); - InputChecker.checkFutureDate(startTime); - ui.printFoodList(foodList.getAfterDateTimeToString(startTime), startTime); - } else if (processedInput.length == 3) { - startTime = LocalDateTime.parse(processedInput[1]); - endTime = LocalDateTime.parse(processedInput[2]); - InputChecker.checkFutureDate(startTime); - InputChecker.checkEndDate(startTime, endTime); - ui.printFoodList(foodList.getInDateTimeRangeToString(startTime,endTime), startTime, endTime); - } - } catch (Exception e) { - throw new DietException("Wrong date time format (Format: yyyy-mm-ddTHH:mm) or future date entered!"); + if (processedInput.length == 2) { + startTime = LocalDateTime.parse(processedInput[1]); + InputChecker.checkFutureDate(startTime); + ui.printFoodList(foodList.getAfterDateTimeToString(startTime), startTime); + } else if (processedInput.length == 3) { + startTime = LocalDateTime.parse(processedInput[1]); + endTime = LocalDateTime.parse(processedInput[2]); + InputChecker.checkFutureDate(startTime); + InputChecker.checkEndDate(startTime, endTime); + ui.printFoodList(foodList.getInDateTimeRangeToString(startTime,endTime), startTime, endTime); } } } diff --git a/src/main/java/seedu/dietbook/command/RecommendCommand.java b/src/main/java/seedu/dietbook/command/RecommendCommand.java index ae26da133f..7d838f40e3 100644 --- a/src/main/java/seedu/dietbook/command/RecommendCommand.java +++ b/src/main/java/seedu/dietbook/command/RecommendCommand.java @@ -2,14 +2,17 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; import seedu.dietbook.person.Person; public class RecommendCommand extends Command { - Person person; + private Person person; + private String input; - public RecommendCommand(Person person) { + public RecommendCommand(Person person, String input) { this.person = person; + this.input = input; } @Override @@ -19,6 +22,7 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } + InputChecker.checkSingleCommand(this.input); int recommendation = manager.getCalculator().calculateRecomendation(this.person); ui.printCalorieRecommendation(this.person.getName(), recommendation); } diff --git a/src/main/java/seedu/dietbook/command/UserinfoCommand.java b/src/main/java/seedu/dietbook/command/UserinfoCommand.java index f7892f1d02..0e6ca85764 100644 --- a/src/main/java/seedu/dietbook/command/UserinfoCommand.java +++ b/src/main/java/seedu/dietbook/command/UserinfoCommand.java @@ -2,10 +2,17 @@ import seedu.dietbook.Manager; import seedu.dietbook.Ui; +import seedu.dietbook.checker.InputChecker; import seedu.dietbook.exception.DietException; public class UserinfoCommand extends Command { + private String input; + + public UserinfoCommand(String input) { + this.input = input; + } + @Override public void execute(Manager manager, Ui ui) throws DietException { if (commandCount == 1) { @@ -13,6 +20,7 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (commandCount == 2) { throw new DietException("Please enter your basic information first!"); } + InputChecker.checkSingleCommand(this.input); ui.printPersonInfo(manager.getPerson().toString()); } } diff --git a/src/main/java/seedu/dietbook/parser/Parser.java b/src/main/java/seedu/dietbook/parser/Parser.java index e98784967e..1a96cae442 100644 --- a/src/main/java/seedu/dietbook/parser/Parser.java +++ b/src/main/java/seedu/dietbook/parser/Parser.java @@ -114,8 +114,10 @@ public static String getProcessedAdd(String userInput, FoodList foodList) throws if (processedParam[1].contains("/")) { trimmedParam = processedParam[1].substring(0, processedParam[1].indexOf("/") - 1).trim(); } else if (trimmedParam.split("\\s+").length >= 2) { - int lengthWithoutTime = trimmedParam.length() - timeFormatLength; - trimmedParam = trimmedParam.substring(0,lengthWithoutTime).trim(); + if (InputChecker.checkDate(trimmedParam)) { + int lengthWithoutTime = trimmedParam.length() - timeFormatLength; + trimmedParam = trimmedParam.substring(0, lengthWithoutTime).trim(); + } } switch (param) { case "x/": From 30b2195b2d426849b6f9b44fc776accd2e7d96bb Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 22:18:01 +0800 Subject: [PATCH 324/374] passed check style --- src/main/java/seedu/dietbook/database/DataBase.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index 713e70809b..0af08fffb7 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -121,14 +121,9 @@ public void printAllData() { System.out.println("Finished Printing out all data"); } - // -------- Search functions -------- - - /** - * This method returns the ith food item from the entire list of food from the data base - * @param index entry number of the food - * @return food object - */ - public Food searchFoodByIndex(int index){ + // ----- Food search functions ------- + + public Food searchFoodByIndex(int index) { return foodStream().skip(index - 1).findFirst().orElseThrow(); } From 03e0bee8262d6f77d0cf5cacf5a0d397c9cd869c Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 22:21:35 +0800 Subject: [PATCH 325/374] fix check style --- src/main/java/seedu/dietbook/database/DataBase.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index 0af08fffb7..70115d872a 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -122,10 +122,6 @@ public void printAllData() { } // ----- Food search functions ------- - - public Food searchFoodByIndex(int index) { - return foodStream().skip(index - 1).findFirst().orElseThrow(); - } /** * This method searchs the whole data base and returns the first food item whose name contains the provided string. @@ -137,6 +133,10 @@ public Food searchFoodByIndex(int index) { public Food searchFoodByName(String food) { return foodStream().filter(x -> x.getName().contains(food)).findFirst().orElseThrow(); } + + public Food searchFoodByIndex(int index) { + return foodStream().skip(index - 1).findFirst().orElseThrow(); + } /** * This method searchs the whole data base and returns all of the food whose name contains the provided string. From a3aa5725f48e3c2bed29cfe59e8b42c2522afe12 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 22:21:43 +0800 Subject: [PATCH 326/374] no message --- src/main/java/seedu/dietbook/database/DataBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/database/DataBase.java b/src/main/java/seedu/dietbook/database/DataBase.java index 70115d872a..cd40ca85d5 100644 --- a/src/main/java/seedu/dietbook/database/DataBase.java +++ b/src/main/java/seedu/dietbook/database/DataBase.java @@ -133,7 +133,7 @@ public void printAllData() { public Food searchFoodByName(String food) { return foodStream().filter(x -> x.getName().contains(food)).findFirst().orElseThrow(); } - + public Food searchFoodByIndex(int index) { return foodStream().skip(index - 1).findFirst().orElseThrow(); } From 3084624f21ffaf7aa3d7b94bad2cab81e3d0d24f Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 22:24:33 +0800 Subject: [PATCH 327/374] fix test check style --- src/test/java/seedu/dietbook/database/DataBaseTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/dietbook/database/DataBaseTest.java b/src/test/java/seedu/dietbook/database/DataBaseTest.java index 458f5c9155..ecff6b75fc 100644 --- a/src/test/java/seedu/dietbook/database/DataBaseTest.java +++ b/src/test/java/seedu/dietbook/database/DataBaseTest.java @@ -29,7 +29,7 @@ public static void main(String[] args) { System.out.println("Trying a negative test case : Input : 10000"); try { database.searchFoodByIndex(10000); - } catch (NoSuchElementException e){ + } catch (NoSuchElementException e) { System.out.println("The index is too high!" + e); } From 0d362e4470795e89acd206928cdb77453f59f24b Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sat, 7 Nov 2020 23:10:15 +0800 Subject: [PATCH 328/374] Change preposition --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 1e02d3f485..c3b2ff2fb1 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -29,7 +29,7 @@ e.g. For `name YOUR_NAME_OR_NICKNAME`, `name Jack` would be a valid command. e.g. For `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]`, `editinfo a/31` and `editinfo h/173 o/87` are valid commands but `editinfo ` is not. -* For commands with multiple parameters, the parameters can be in any order **only if there is more than one parameter labelled with parameter tags** like `n/`, `a/`, etc. Otherwise, they **must be entered in the sequence as shown** on this guide, the [Help Command](#to-view-a-list-of-valid-commands-help) or the [Summary Command](#command-summary).
+* For commands with multiple parameters, the parameters can be in any order **only if there is more than one parameter labelled with parameter tags** like `n/`, `a/`, etc. Otherwise, they **must be entered in the sequence as shown** in this guide, the [Help Command](#to-view-a-list-of-valid-commands-help) or the [Summary Command](#command-summary).
e.g. For `calculate NUTRIENT_TYPE [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm]`,`calculate fat 2020-07-03T23:59 2020-09-03T23:59` is valid but `calculate 2020-07-03T23:59 fat 2020-09-03T23:59` is not.
e.g. For `add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] [yyyy-mm-ddTHH:mm]`, `add n/bao x/1 p/5 k/157 f/5 c/23 2020-09-03T23:59` is valid but `add n/bao x/1 p/5 2020-09-03T23:59 k/157 f/5 c/23` is not as the time needs to be entered as the last parameter. From 73c080b93dc7e8e8ba11acfcc1de7b4ce6120f18 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sat, 7 Nov 2020 23:25:21 +0800 Subject: [PATCH 329/374] start implementing FoodPortionDateSaveLoadManager --- .../FoodPortionDateSaveLoadManager.java | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java diff --git a/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java new file mode 100644 index 0000000000..05dbad08ff --- /dev/null +++ b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java @@ -0,0 +1,169 @@ +package seedu.dietbook.saveload; + +import seedu.dietbook.food.Food; + +import java.io.FileNotFoundException; + +/*** + * this class takes care of saving and loading of food, portion and date + */ +public class FoodPortionDateSaveLoadManager { + private static final int DEFAULT_SAVER_WIDTH = 7; + private static final int DEFAULT_SAVER_HEIGHT = 10; + + private static final int FOOD_NAME_INDEX = 1; + private static final int FOOD_CALORIE_INDEX = 2; + private static final int FOOD_CARBOHYDRATE_INDEX = 3; + private static final int FOOD_PROTEIN_INDEX = 4; + private static final int FOOD_FAT_INDEX = 5; + private static final int PORTION_SIZE_INDEX = 6; + private static final int DAY_INDEX = 7; + private static final int MONTH_INDEX = 8; + private static final int YEAR_INDEX = 9; + private static final int TIME_INDEX = 10; + + private static final String DEFAULT_FOOD_NAME = "No Food Name"; + private static final String DEFAULT_FOOD_CALORIE = "0"; + private static final String DEFAULT_FOOD_CARBOHYDRATE= "0"; + private static final String DEFAULT_FOOD_PROTEIN= "0"; + private static final String DEFAULT_FOOD_FAT = "0"; + private static final String DEFAULT_PORTION_SIZE = "1"; + private static final String DEFAULT_DAY = "1"; + private static final String DEFAULT_MONTH= "1"; + private static final String DEFAULT_YEAR = "2000"; + private static final String DEFAULT_TIME = "0"; + + + private static final String FOOD_FOLDER_NAME = "Food##PORTION###dDATE##folder"; + + private static final String DEFAULT_NAME = "MISSING NAME"; + private static final String DEFAULT_NUTRITION_VALUE = "0"; + + private Saver saver; + private Loader fileLoader; + + public FoodPortionDateSaveLoadManager() { + this.saver = new Saver(DEFAULT_SAVER_WIDTH, DEFAULT_SAVER_HEIGHT); + this.fileLoader = Loader.loadEmpty(); + } + + /** + * Call this function to load a food file. + * @param fileName name of file + * @throws FileNotFoundException if there is no such save file + */ + public void load(String fileName) throws FileNotFoundException { + this.fileLoader = Loader.load(FOOD_FOLDER_NAME, fileName); + } + + public void clearLoader() { + this.fileLoader = Loader.loadEmpty(); + } + + /** + * Clears the saver and sets the number of entries it can take + * call this function at the start of a series of functions to store data + * + * @param numEntry num of entries + */ + public void readySaver(Integer numEntry){ + saver.resetSize(DEFAULT_SAVER_WIDTH, numEntry); + } + + public void addFood(Food food, int entryNumber){ + setFoodName(food.getName(), entryNumber); + addFoodCalorie(food.getCalorie(), entryNumber); + setFoodCarbohydrate(food.getCarbohydrate(), entryNumber); + addFoodProtein(food.getProtein(), entryNumber); + addFoodFat(food.getFat(), entryNumber); + } + + public Food getFood(Integer entryNumber) throws IllegalAccessException { + String name = getFoodName(entryNumber); + int calorie = getFoodCalorie(entryNumber); + int carbohydrate = getFoodCarbohydrate(entryNumber); + int protein = getFoodProtein(entryNumber); + int fat = getFoodFat(entryNumber); + return new Food(name, calorie, carbohydrate, protein, fat); + } + + private String getFoodName(Integer entryNumber) throws IllegalAccessException { + return fileLoader.get(FOOD_NAME_INDEX, entryNumber).orElse(DEFAULT_FOOD_NAME); + } + + private int getFoodFat(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_FAT_INDEX, entryNumber).orElse(DEFAULT_FOOD_FAT)); + } + + private int getFoodProtein(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_PROTEIN_INDEX, entryNumber).orElse(DEFAULT_FOOD_PROTEIN)); + } + + private int getFoodCarbohydrate(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_CARBOHYDRATE_INDEX, entryNumber).orElse(DEFAULT_FOOD_CARBOHYDRATE)); + } + + private int getFoodCalorie(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_CALORIE_INDEX, entryNumber).orElse(DEFAULT_FOOD_CALORIE)); + } + + private void setFoodCarbohydrate( int carbohydrate, int entryNumber) { + saver.add(Integer.toString(carbohydrate), FOOD_CARBOHYDRATE_INDEX, entryNumber); + } + + private void setFoodName(String name, int entryNumber) { + saver.add(name, FOOD_NAME_INDEX, entryNumber); + } + + private void addFoodCalorie(int calorie, int entryNumber) { + saver.add(Integer.toString(calorie), FOOD_CALORIE_INDEX, entryNumber); + } + + private void addFoodProtein(int protein, int entryNumber){ + saver.add(Integer.toString(protein), FOOD_PROTEIN_INDEX, entryNumber); + } + + private void addFoodFat(int fat, int entryNumber){ + saver.add(Integer.toString(fat), FOOD_FAT_INDEX, entryNumber); + } + + public void setPortionSize(int portionSize, Integer entryNumber){ + saver.add(Integer.toString(portionSize), PORTION_SIZE_INDEX, entryNumber); + } + + public void setDay(int day, int entryNumber){ + saver.add(Integer.toString(day), DAY_INDEX, entryNumber); + } + + public void setMonth(int month, int entryNumber){ + saver.add(Integer.toString(month), MONTH_INDEX, entryNumber); + } + + public void setYear(int year, int entryNumber){ + saver.add(Integer.toString(year), YEAR_INDEX, entryNumber); + } + + public void setTime(int time, int entryNumber){ + saver.add(Integer.toString(time), TIME_INDEX, entryNumber); + } + + public int getPortionSize(int entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(PORTION_SIZE_INDEX, entryNumber).orElse(DEFAULT_PORTION_SIZE)); + } + + public int getDay(int entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(DAY_INDEX, entryNumber).orElse(DEFAULT_DAY)); + } + + public int getMonth(int entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(MONTH_INDEX, entryNumber).orElse(DEFAULT_MONTH)); + } + + public int getYear(int entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(YEAR_INDEX, entryNumber).orElse(DEFAULT_YEAR)); + } + + public int getTime(int entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(TIME_INDEX, entryNumber).orElse(DEFAULT_TIME)); + } +} From b46c5a7a0456451897882da94e947169a3fe5638 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 00:15:13 +0800 Subject: [PATCH 330/374] Changed the the ActivityLevel to FitnessLevel --- src/test/java/seedu/dietbook/calculator/CalculatorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index 062c4ae2a5..a976f520a9 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -61,8 +61,8 @@ void calculateFat_foodListOfThreeItems_sumOfFat() { @Test void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { - Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, ActivityLevel.LOW); - Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, ActivityLevel.MEDIUM); + Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, FitnessLevel.LOW); + Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, FitnessLevel.MEDIUM); Calculator calculator = new Calculator(new CalculatorData()); assertEquals(2728, calculator.calculateRecomendation(harry)); assertEquals(1752, calculator.calculateRecomendation(erica)); From 2d7028f459fd6d0fc3efea12c0515335ce889801 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 00:24:36 +0800 Subject: [PATCH 331/374] Changed the input attributes in the calculate... methods in CalculateCommand class. --- .../dietbook/command/CalculateCommand.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/dietbook/command/CalculateCommand.java b/src/main/java/seedu/dietbook/command/CalculateCommand.java index a1b3d30de4..d916fb0bf4 100644 --- a/src/main/java/seedu/dietbook/command/CalculateCommand.java +++ b/src/main/java/seedu/dietbook/command/CalculateCommand.java @@ -46,20 +46,20 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); + calorie = manager.getCalculator().calculateCalorie(startTime); + carb = manager.getCalculator().calculateCarb(startTime); + protein = manager.getCalculator().calculateProtein(startTime); + fat = manager.getCalculator().calculateFat(startTime); ui.printAllIntake(calorie, carb, protein, fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); InputChecker.checkEndDate(startTime, endTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(startTime, endTime); + carb = manager.getCalculator().calculateCarb(startTime, endTime); + protein = manager.getCalculator().calculateProtein(startTime, endTime); + fat = manager.getCalculator().calculateFat(startTime, endTime); ui.printAllIntake(calorie, carb, protein, fat, startTime, endTime); } @@ -70,14 +70,14 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime); + calorie = manager.getCalculator().calculateCalorie(startTime); ui.printCalorieIntake(calorie, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); InputChecker.checkEndDate(startTime, endTime); - calorie = manager.getCalculator().calculateCalorie(manager.getFoodList(), startTime, endTime); + calorie = manager.getCalculator().calculateCalorie(startTime, endTime); ui.printCalorieIntake(calorie, startTime, endTime); } break; @@ -87,14 +87,14 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime); + carb = manager.getCalculator().calculateCarb(startTime); ui.printCarbIntake(carb, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); InputChecker.checkEndDate(startTime, endTime); - carb = manager.getCalculator().calculateCarb(manager.getFoodList(), startTime, endTime); + carb = manager.getCalculator().calculateCarb(startTime, endTime); ui.printCarbIntake(carb, startTime, endTime); } break; @@ -104,14 +104,14 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime); + protein = manager.getCalculator().calculateProtein(startTime); ui.printProteinIntake(protein, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); InputChecker.checkEndDate(startTime, endTime); - protein = manager.getCalculator().calculateProtein(manager.getFoodList(), startTime, endTime); + protein = manager.getCalculator().calculateProtein(startTime, endTime); ui.printProteinIntake(protein, startTime, endTime); } break; @@ -121,14 +121,14 @@ public void execute(Manager manager, Ui ui) throws DietException { } else if (processedParam.length == 2) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime); + fat = manager.getCalculator().calculateFat(startTime); ui.printFatIntake(fat, startTime); } else if (processedParam.length == 3) { startTime = LocalDateTime.parse(processedParam[1]); InputChecker.checkFutureDate(startTime); endTime = LocalDateTime.parse(processedParam[2]); InputChecker.checkEndDate(startTime, endTime); - fat = manager.getCalculator().calculateFat(manager.getFoodList(), startTime, endTime); + fat = manager.getCalculator().calculateFat(startTime, endTime); ui.printFatIntake(fat, startTime, endTime); } break; From 3e0700dcf8f44ee4ab1eb5d86d1807edcfd6a1df Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 8 Nov 2020 01:55:27 +0800 Subject: [PATCH 332/374] add support for missing food details - FoodList can now accept entries with missing values. - Missing values are recalulated upon the use of a getFood() method call. - add a string utility class for fstring-like support. --- .../java/seedu/dietbook/list/FoodEntry.java | 4 +- .../java/seedu/dietbook/list/FoodManager.java | 64 ++++++++++++++ .../list/NoReplacementFoundException.java | 12 +++ .../seedu/dietbook/list/NutrientData.java | 22 +++++ .../dietbook/list/NutritionCalculator.java | 73 ++++++++++++++++ .../seedu/dietbook/list/OptionalFood.java | 85 +++++++++++++++++++ .../seedu/dietbook/list/StringFormatter.java | 38 +++++++++ .../seedu/dietbook/list/FoodEntryTest.java | 66 ++++++++++++++ .../dietbook/list/StringFormatterTest.java | 39 +++++++++ 9 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 src/main/java/seedu/dietbook/list/FoodManager.java create mode 100644 src/main/java/seedu/dietbook/list/NoReplacementFoundException.java create mode 100644 src/main/java/seedu/dietbook/list/NutrientData.java create mode 100644 src/main/java/seedu/dietbook/list/NutritionCalculator.java create mode 100644 src/main/java/seedu/dietbook/list/OptionalFood.java create mode 100644 src/main/java/seedu/dietbook/list/StringFormatter.java create mode 100644 src/test/java/seedu/dietbook/list/FoodEntryTest.java create mode 100644 src/test/java/seedu/dietbook/list/StringFormatterTest.java diff --git a/src/main/java/seedu/dietbook/list/FoodEntry.java b/src/main/java/seedu/dietbook/list/FoodEntry.java index 63fc4a6576..e1fd6589e1 100644 --- a/src/main/java/seedu/dietbook/list/FoodEntry.java +++ b/src/main/java/seedu/dietbook/list/FoodEntry.java @@ -27,14 +27,14 @@ public FoodEntry(int portionSize, String name, int calorie, int carbohydrate, int protein, int fat) { assert (portionSize > 0) : "Non-positive, invalid portion size not caught."; this.portionSize = portionSize; - this.food = new Food(name, calorie, carbohydrate, protein, fat); + this.food = FoodManager.createFood(name, calorie, carbohydrate, protein, fat); } /** * Getter method for the Food object. */ public Food getFood() { - return food; + return FoodManager.retrieveFood(food); } /** diff --git a/src/main/java/seedu/dietbook/list/FoodManager.java b/src/main/java/seedu/dietbook/list/FoodManager.java new file mode 100644 index 0000000000..932fa84a17 --- /dev/null +++ b/src/main/java/seedu/dietbook/list/FoodManager.java @@ -0,0 +1,64 @@ +package seedu.dietbook.list; + +import seedu.dietbook.food.Food; + +public class FoodManager { + /** + * Static constructor method to create the Food objects required by FoodEntry. + * It decides if there are empty values provided and creates either an OptionalFood + * or simply the default food object. + * @param name name of food item + * @param calorie int value for calorie content + * @param carbohydrate int value for carbohydrate content + * @param protein int value for protein content + * @param fat int value for fat content + * @return Food object: could be instance of Food or OptionalFood (child). + */ + protected static Food createFood(String name, int calorie, + int carbohydrate, int protein, int fat) { + if (calorie == OptionalFood.EMPTY_VALUE || carbohydrate == OptionalFood.EMPTY_VALUE + || calorie == OptionalFood.EMPTY_VALUE + || fat == OptionalFood.EMPTY_VALUE) { + return new OptionalFood(name, calorie, carbohydrate, protein, fat); + } + return new Food(name, calorie, carbohydrate, protein, fat); + } + + /** + * Returns a new Food object with no missing values. + * Instances of OptionalFood are converted into Food with recalculated values for use by other classes. + * @param food instance of Food or OptionalFood + * @return Food object with no missing values. + */ + protected static Food retrieveFood(Food food) { + if (! (food instanceof OptionalFood)) { + return food; + } + // Find out which parameters are missing. + // Only 4 scenarios: Calorie missing. Other 3 Nutrients missing. 2 Missing. 1 Missing. + + int calorie = food.getCalorie(); + int carbohydrate = food.getCarbohydrate(); + int protein = food.getProtein(); + int fat = food.getFat(); + + // Calorie missing: + if (calorie == OptionalFood.EMPTY_VALUE) { + assert (carbohydrate != OptionalFood.EMPTY_VALUE) : "Carbohydrate cannot be empty when calorie is empty"; + assert (protein != OptionalFood.EMPTY_VALUE) : "Protein cannot be empty when calorie is empty"; + assert (fat != OptionalFood.EMPTY_VALUE) : "Fat cannot be empty when calorie is empty"; + + // calculate calories: + calorie = NutritionCalculator.calculateCalorieFromNutrients(carbohydrate, protein, fat); + return new Food(food.getName(), calorie, carbohydrate, protein, fat); + } + + // Other values missing instead: + // Recalculate missing parameters based on known parameters. + NutrientData data = NutritionCalculator.calculateNutrientsFromCalorie(calorie, carbohydrate, protein, fat); + + return new Food(food.getName(), data.calorie, data.carbohydrate, data.protein, data.fat); + + } + +} diff --git a/src/main/java/seedu/dietbook/list/NoReplacementFoundException.java b/src/main/java/seedu/dietbook/list/NoReplacementFoundException.java new file mode 100644 index 0000000000..8c965b4a37 --- /dev/null +++ b/src/main/java/seedu/dietbook/list/NoReplacementFoundException.java @@ -0,0 +1,12 @@ +package seedu.dietbook.list; + +public class NoReplacementFoundException extends Exception { + + /** + * Constructor for exception: To be thrown when a mapping cannot be established in StringFormatter. + * @param msg error message + */ + public NoReplacementFoundException(String msg) { + super(msg); + } +} diff --git a/src/main/java/seedu/dietbook/list/NutrientData.java b/src/main/java/seedu/dietbook/list/NutrientData.java new file mode 100644 index 0000000000..c5465f49ff --- /dev/null +++ b/src/main/java/seedu/dietbook/list/NutrientData.java @@ -0,0 +1,22 @@ +package seedu.dietbook.list; + +/** + * Data class to hold nutrient info. + */ +public class NutrientData { + protected final int calorie; + protected final int carbohydrate; + protected final int protein; + protected final int fat; + + protected NutrientData(int calorie, int carbohydrate, int protein, int fat) { + assert (calorie >= 0) : "Should not have negative calorie value"; + assert (carbohydrate >= 0) : "Should not have negative carbohydrate value"; + assert (protein >= 0) : "Should not have negative protein value"; + assert (fat >= 0) : "Should not have negative fat value"; + this.calorie = calorie; + this.carbohydrate = carbohydrate; + this.protein = protein; + this.fat = fat; + } +} diff --git a/src/main/java/seedu/dietbook/list/NutritionCalculator.java b/src/main/java/seedu/dietbook/list/NutritionCalculator.java new file mode 100644 index 0000000000..efe29f7d33 --- /dev/null +++ b/src/main/java/seedu/dietbook/list/NutritionCalculator.java @@ -0,0 +1,73 @@ +package seedu.dietbook.list; + +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; + +/** + * Collection of methods that help with guesstimating/calculating missing nutrition values. + */ +public class NutritionCalculator { + private static final int NUM_OF_NUTRIENTS = 3; + + /** + * Calculates the calories by summing up the weighted contributions of the individual nutrients. + */ + protected static int calculateCalorieFromNutrients(int carbohydrate, int protein, int fat) { + return carbohydrate * 4 + protein * 4 + fat * 9; + } + + /** + * Divides up the calories among the nutrients that are empty. + */ + protected static NutrientData calculateNutrientsFromCalorie(int calorie, int carbohydrate, int protein, int fat) { + assert (calorie != OptionalFood.EMPTY_VALUE); + + Map map = new HashMap<>(); + + map.put(Nutrient.CARBOHYDRATE, carbohydrate); + map.put(Nutrient.PROTEIN, protein); + map.put(Nutrient.FAT, fat); + + ArrayList calorieCounts = new ArrayList<>(); + + map.forEach((x, y) -> { + if (y == OptionalFood.EMPTY_VALUE) { + return; + } else if (x.equals(Nutrient.FAT)) { + calorieCounts.add(y * 9); + } else { + calorieCounts.add(y * 4); + } + }); + + int existingCalories = 0; + int emptyCount = NUM_OF_NUTRIENTS - calorieCounts.size(); + + for (Integer calorieCount : calorieCounts) { + existingCalories += calorieCount; + } + + int dividedCalorie = (calorie - existingCalories) / emptyCount; + + Map.copyOf(map).forEach((x, y) -> { + if (y != OptionalFood.EMPTY_VALUE) { + return; + } + if (x.equals(Nutrient.FAT)) { + map.put(x, dividedCalorie / 9); + return; + } + map.put(x, dividedCalorie / 4); + }); + return new NutrientData(calorie, map.get(Nutrient.CARBOHYDRATE), + map.get(Nutrient.PROTEIN), map.get(Nutrient.FAT)); + + } + + private enum Nutrient { + CARBOHYDRATE, + PROTEIN, + FAT + } +} diff --git a/src/main/java/seedu/dietbook/list/OptionalFood.java b/src/main/java/seedu/dietbook/list/OptionalFood.java new file mode 100644 index 0000000000..4e599c9a71 --- /dev/null +++ b/src/main/java/seedu/dietbook/list/OptionalFood.java @@ -0,0 +1,85 @@ +package seedu.dietbook.list; + +import seedu.dietbook.food.Food; + +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Child class of Food that supports "empty" fields. + * Is also handled as a data class with methods to query the existence of empty fields. + */ +public class OptionalFood extends Food { + private final Map nutrientStrings; + public static final int EMPTY_VALUE = -1; + + /** + * Only default constructor method. It is the same as the constructor method for Food. + */ + public OptionalFood(String name, int calorie, int carbohydrate, int protein, int fat) { + super(name, calorie, carbohydrate, protein, fat); + + nutrientStrings = new HashMap(); + + assert (calorie >= 0 || calorie == -1) : "Invalid value for calorie detected"; + addNutrientStringToMap(nutrientStrings, Nutrient.calorie, calorie); + + assert (carbohydrate >= 0 || carbohydrate == -1) : "Invalid value for carbohydrate detected"; + addNutrientStringToMap(nutrientStrings, Nutrient.carbohydrate, carbohydrate); + + assert (protein >= 0 || protein == -1) : "Invalid value for protein detected"; + addNutrientStringToMap(nutrientStrings, Nutrient.protein, protein); + + assert (fat >= 0 || fat == -1) : "Invalid value for fat detected"; + addNutrientStringToMap(nutrientStrings, Nutrient.fat, fat); + + } + + /** + * Checks if nutrient value is empty and assigns its corresponding string representation to the map. + * Either "-" or the integer value. + */ + private void addNutrientStringToMap(Map map, Nutrient nutrient, int nutrientValue) { + if (nutrientValue == EMPTY_VALUE) { + map.put(nutrient, "-"); + } else { + map.put(nutrient, String.valueOf(nutrientValue)); + } + } + + @Override + public String toString() { + ArrayList strings = new ArrayList<>(); + strings.add(super.getName()); + + Map stringMap = new HashMap<>(); + nutrientStrings.forEach((x, y) -> stringMap.put(x.toString(), y)); + String text = "calorie : ${calorie}, protein : ${protein}, carbohydrate : ${carbohydrate}, fats : ${fats}"; + + try { + Arrays.asList(StringFormatter.formatStringWithMap(text, stringMap) + .split(",")) + .forEach(x -> strings.add(x)); + } catch (NoReplacementFoundException e) { // should not be allowed to happen + assert (false) : "Error with String formatting: " + e.getMessage(); + nutrientStrings.forEach((x, y) -> strings.add(String.format("%s : %s", x.toString(), y))); + } + + return String.join(" | ", strings); + } + + private enum Nutrient { + calorie, + carbohydrate, + protein, + fat { + @Override + public String toString() { + return "fats"; + } + }; + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/dietbook/list/StringFormatter.java b/src/main/java/seedu/dietbook/list/StringFormatter.java new file mode 100644 index 0000000000..ea63d0f86c --- /dev/null +++ b/src/main/java/seedu/dietbook/list/StringFormatter.java @@ -0,0 +1,38 @@ +package seedu.dietbook.list; + +import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * Utility class to do regex matching and substitution in a format similar to Python's fstrings. + */ +public class StringFormatter { + private static final Pattern MATCH_TEMPLATE = + Pattern.compile("\\$\\{([a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*)\\}"); + + /** + * Formats the string based on (variable name, variable) pairs provided in map. + * Uses the regex pattern ${variable_name} to detect points in the string to substitute. + * @param string string containing regex pattern to be formatted + * @param map map of (String variable name, variable values) for substitution. + * @return formatted string + */ + public static String formatStringWithMap(String string, Map map) + throws NoReplacementFoundException { + Matcher matcher = MATCH_TEMPLATE.matcher(string); + StringBuffer buffer = new StringBuffer(); + + while (matcher.find()) { + E value = map.get(matcher.group(1)); + if (value == null) { + throw new NoReplacementFoundException("For key of: " + matcher.group(1)); + } + matcher.appendReplacement(buffer, value.toString()); + + } + matcher.appendTail(buffer); + + return buffer.toString(); + } +} diff --git a/src/test/java/seedu/dietbook/list/FoodEntryTest.java b/src/test/java/seedu/dietbook/list/FoodEntryTest.java new file mode 100644 index 0000000000..5b2d5522b6 --- /dev/null +++ b/src/test/java/seedu/dietbook/list/FoodEntryTest.java @@ -0,0 +1,66 @@ +package seedu.dietbook.list; + +import seedu.dietbook.food.Food; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test suite for FoodEntry and dependencies. + */ +public class FoodEntryTest { + + @Test + @DisplayName("Empty Calorie Test") + void getFood_foodWithEmptyCalorie_isNotInstanceOfOptionalFood() { + FoodEntry entry = new FoodEntry(1, "Sashimi", OptionalFood.EMPTY_VALUE, 10, 10, 10); + + // getFood should not return an optional food. + Food foodFromEntry = entry.getFood(); + assertTrue(!(foodFromEntry instanceof OptionalFood)); + + assertEquals(10 * 4 * 2 + 10 * 9, foodFromEntry.getCalorie()); + + // further test that Manager does create OptionalFood: + Food foodFromManager = FoodManager.createFood("Sashimi", OptionalFood.EMPTY_VALUE, 10, 10, 10); + assertTrue(foodFromManager instanceof OptionalFood); + + } + + @Test + @DisplayName("1 Empty Nutritional Values Test") + void getNutritionalValue_foodWithEmptyNutrionalValue_remainingDividedOutValue() { + FoodEntry entryWithNoFat = new FoodEntry(2, "Sashimi", 170, 10, 10, OptionalFood.EMPTY_VALUE); + assertEquals(10, entryWithNoFat.getFood().getFat()); + + FoodEntry entryWithNoCarb = new FoodEntry(2, "Sashimi", 170, OptionalFood.EMPTY_VALUE, 10, 10); + assertEquals(10, entryWithNoCarb.getFood().getCarbohydrate()); + } + + @Test + @DisplayName("3 Empty Nutritional Values Test") + void getNutritionalValues_foodWithEmptyNutrionalValues_dividedOutNutritionalValues() { + FoodEntry entry = new FoodEntry(2, "Balanced", 300, + OptionalFood.EMPTY_VALUE, OptionalFood.EMPTY_VALUE, OptionalFood.EMPTY_VALUE); + assertEquals(300 / 3 / 9, entry.getFood().getFat()); + assertEquals(300 / 3 / 4, entry.getFood().getProtein()); + } + + @Test + @DisplayName("2 Empty Nutritional Values Test") + void getNutrionalValue_foodWithOnlyOneNutritionalValue_dividedOutNutritionalValues() { + FoodEntry entryWithOnlyFat = new FoodEntry(2, "FatOnly", 300, + OptionalFood.EMPTY_VALUE, OptionalFood.EMPTY_VALUE, 10); + assertEquals((300 - 90) / 2 / 4, entryWithOnlyFat.getFood().getCarbohydrate()); + assertEquals((300 - 90) / 2 / 4, entryWithOnlyFat.getFood().getProtein()); + + FoodEntry entryWithOnlyProtein = new FoodEntry(1, "ProteinOnly", 300, + OptionalFood.EMPTY_VALUE, 25, OptionalFood.EMPTY_VALUE); + assertEquals(100 / 9, entryWithOnlyProtein.getFood().getFat()); + assertEquals(100 / 4, entryWithOnlyProtein.getFood().getCarbohydrate()); + + } +} diff --git a/src/test/java/seedu/dietbook/list/StringFormatterTest.java b/src/test/java/seedu/dietbook/list/StringFormatterTest.java new file mode 100644 index 0000000000..e176883923 --- /dev/null +++ b/src/test/java/seedu/dietbook/list/StringFormatterTest.java @@ -0,0 +1,39 @@ +package seedu.dietbook.list; + +import java.util.Map; +import java.util.HashMap; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class StringFormatterTest { + @Test + @DisplayName("Pattern Replacement Test (Positive)") + void replaceStringTest() throws NoReplacementFoundException { + String original = "This test belongs to ${name} and is used in ${class}"; + Map map = new HashMap<>(); + map.put("name", "Me"); + map.put("class", "StringFormatterTest"); + String test = StringFormatter.formatStringWithMap(original, map); + + assertEquals("This test belongs to Me and is used in StringFormatterTest", test); + } + + @Test + @DisplayName("Pattern Mismatch Test (Negative)") + void invalidMapping_mapWithMissingKey_exceptionThrown() { + String original = "This test belongs to ${name} and is used in ${class}"; + Map map = new HashMap<>(); + map.put("name", "Me"); + assertThrows(NoReplacementFoundException.class, () -> StringFormatter.formatStringWithMap(original, map)); + + try { + StringFormatter.formatStringWithMap(original, map); + } catch (NoReplacementFoundException e) { + assertEquals("For key of: class", e.getMessage()); + } + } +} From 58d9636c9c49ddcf5cd455a3634acd152ce04cbb Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 8 Nov 2020 02:05:35 +0800 Subject: [PATCH 333/374] move StringFormatter to utils package StringFormatter is a utility class that is not limited to supporting classes in list. --- src/main/java/seedu/dietbook/list/OptionalFood.java | 2 ++ .../dietbook/{list => utils}/NoReplacementFoundException.java | 2 +- .../java/seedu/dietbook/{list => utils}/StringFormatter.java | 2 +- .../seedu/dietbook/{list => utils}/StringFormatterTest.java | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) rename src/main/java/seedu/dietbook/{list => utils}/NoReplacementFoundException.java (90%) rename src/main/java/seedu/dietbook/{list => utils}/StringFormatter.java (97%) rename src/test/java/seedu/dietbook/{list => utils}/StringFormatterTest.java (97%) diff --git a/src/main/java/seedu/dietbook/list/OptionalFood.java b/src/main/java/seedu/dietbook/list/OptionalFood.java index 4e599c9a71..0a3d12d4b8 100644 --- a/src/main/java/seedu/dietbook/list/OptionalFood.java +++ b/src/main/java/seedu/dietbook/list/OptionalFood.java @@ -1,6 +1,8 @@ package seedu.dietbook.list; import seedu.dietbook.food.Food; +import seedu.dietbook.utils.StringFormatter; +import seedu.dietbook.utils.NoReplacementFoundException; import java.util.Map; import java.util.HashMap; diff --git a/src/main/java/seedu/dietbook/list/NoReplacementFoundException.java b/src/main/java/seedu/dietbook/utils/NoReplacementFoundException.java similarity index 90% rename from src/main/java/seedu/dietbook/list/NoReplacementFoundException.java rename to src/main/java/seedu/dietbook/utils/NoReplacementFoundException.java index 8c965b4a37..b44c964fc5 100644 --- a/src/main/java/seedu/dietbook/list/NoReplacementFoundException.java +++ b/src/main/java/seedu/dietbook/utils/NoReplacementFoundException.java @@ -1,4 +1,4 @@ -package seedu.dietbook.list; +package seedu.dietbook.utils; public class NoReplacementFoundException extends Exception { diff --git a/src/main/java/seedu/dietbook/list/StringFormatter.java b/src/main/java/seedu/dietbook/utils/StringFormatter.java similarity index 97% rename from src/main/java/seedu/dietbook/list/StringFormatter.java rename to src/main/java/seedu/dietbook/utils/StringFormatter.java index ea63d0f86c..5946d95449 100644 --- a/src/main/java/seedu/dietbook/list/StringFormatter.java +++ b/src/main/java/seedu/dietbook/utils/StringFormatter.java @@ -1,4 +1,4 @@ -package seedu.dietbook.list; +package seedu.dietbook.utils; import java.util.Map; import java.util.regex.Pattern; diff --git a/src/test/java/seedu/dietbook/list/StringFormatterTest.java b/src/test/java/seedu/dietbook/utils/StringFormatterTest.java similarity index 97% rename from src/test/java/seedu/dietbook/list/StringFormatterTest.java rename to src/test/java/seedu/dietbook/utils/StringFormatterTest.java index e176883923..7a9ec8492e 100644 --- a/src/test/java/seedu/dietbook/list/StringFormatterTest.java +++ b/src/test/java/seedu/dietbook/utils/StringFormatterTest.java @@ -1,4 +1,4 @@ -package seedu.dietbook.list; +package seedu.dietbook.utils; import java.util.Map; import java.util.HashMap; From 8e626aa14a16714e7e6f46f814b38cd6029c5200 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 8 Nov 2020 02:38:44 +0800 Subject: [PATCH 334/374] relax FoodListTest Remove reliance on time elapsed after datetime.now() --- .../java/seedu/dietbook/list/FoodListTest.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 3f2a6ce907..580d152c51 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -79,22 +79,13 @@ void dateFilterAfterTest() { LocalDateTime timeNow = LocalDateTime.now(); - assertTrue(list.getFoodsAfterDateTime(timeNow).size() == 0); + assertTrue(datedList.getFoodsAfterDateTime(timeNow).size() == 0); + assertEquals(list.getFoods().toString(), list.getFoodsAfterDateTime(LocalDateTime.MIN).toString()); - - // add new entries: - if (! LocalDateTime.now().isAfter(timeNow)) { // Execution is too fast that now() = timeNow. - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - System.out.println("Unexpected Interruption"); - } - } - list.addFood(1, food); - assertEquals(food.toString(), list.getFoodsAfterDateTime(timeNow).get(0).toString()); - + datedList.addFood(1, food); + assertEquals(food.toString(), datedList.getFoodsAfterDateTime(end).get(1).toString()); } From edc6f034128a57d18dd3589759e5908032e1e844 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 8 Nov 2020 02:43:58 +0800 Subject: [PATCH 335/374] Remove unused dependencies --- src/main/java/seedu/dietbook/list/FoodListManager.java | 6 ------ src/main/java/seedu/dietbook/list/OptionalFood.java | 2 +- src/test/java/seedu/dietbook/list/FoodListTest.java | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/seedu/dietbook/list/FoodListManager.java b/src/main/java/seedu/dietbook/list/FoodListManager.java index e706e4512e..1631215ad6 100644 --- a/src/main/java/seedu/dietbook/list/FoodListManager.java +++ b/src/main/java/seedu/dietbook/list/FoodListManager.java @@ -143,11 +143,5 @@ protected static List filterListByDate(List list, LocalDat return ListFunction.filterList(list, predicate); } - /** - * Sort a list of entries by date. - */ - - } -// Potential future work: create a functional interface for the functions instead: diff --git a/src/main/java/seedu/dietbook/list/OptionalFood.java b/src/main/java/seedu/dietbook/list/OptionalFood.java index 0a3d12d4b8..b3dd54c1d0 100644 --- a/src/main/java/seedu/dietbook/list/OptionalFood.java +++ b/src/main/java/seedu/dietbook/list/OptionalFood.java @@ -84,4 +84,4 @@ public String toString() { }; } -} \ No newline at end of file +} diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index 580d152c51..ef543b31a1 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -8,7 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDateTime; -import java.util.concurrent.TimeUnit; import seedu.dietbook.food.Food; From a698e605b2370a742d18e89ed0438a0088b36478 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 8 Nov 2020 02:56:01 +0800 Subject: [PATCH 336/374] Update notes and warnings under Feature section --- docs/UserGuide.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c3b2ff2fb1..dcf039d4c2 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -29,19 +29,21 @@ e.g. For `name YOUR_NAME_OR_NICKNAME`, `name Jack` would be a valid command. e.g. For `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]`, `editinfo a/31` and `editinfo h/173 o/87` are valid commands but `editinfo ` is not. -* For commands with multiple parameters, the parameters can be in any order **only if there is more than one parameter labelled with parameter tags** like `n/`, `a/`, etc. Otherwise, they **must be entered in the sequence as shown** in this guide, the [Help Command](#to-view-a-list-of-valid-commands-help) or the [Summary Command](#command-summary).
+* For commands with multiple parameters, the parameters can be in any order **only if the parameters are attached to a parameter tag** like `n/`, `a/`, etc. Otherwise, they **must be entered in the sequence as shown** in this guide, the [Help Command](#to-view-a-list-of-valid-commands-help) or the [Summary Command](#command-summary).
e.g. For `calculate NUTRIENT_TYPE [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm]`,`calculate fat 2020-07-03T23:59 2020-09-03T23:59` is valid but `calculate 2020-07-03T23:59 fat 2020-09-03T23:59` is not.
-e.g. For `add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] [yyyy-mm-ddTHH:mm]`, `add n/bao x/1 p/5 k/157 f/5 c/23 2020-09-03T23:59` is valid but `add n/bao x/1 p/5 2020-09-03T23:59 k/157 f/5 c/23` is not as the time needs to be entered as the last parameter. +e.g. For `add i/INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm]`, `add x/1 i/1 2020-09-03T23:59` is valid but `add i/1 2020-09-03T23:59 x/1 ` is not as time needs to be entered as the last parameter. **:warning: Please take note of the following:** * Command words and parameter indicators are case-sensitive.
e.g. `help` is a valid command but `Help` is not.
-e.g. For `add n/FOOD_NAME x/PORTION_SIZE`, `add n/mee x/1` is valid but `add N/mee x/1` is not. +e.g. For `add i/INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm]`, `add i/1 x/1 2020-07-03T23:59` is valid but `add I/1 x/1 2020-07-03T23:59` is not. * Spacing to separate command word and parameters is required.
e.g. For `calculate NUTRIENT_TYPE`, `calculate all` is valid but `calculateall` is not. +* Inappropriate usage of `/`, the forward slash, may lead to invalid commands. Only use **one** `/` in parameter tags like `n/`, `a/`, etc and avoid using `/` otherwise. + ### Features related to user information #### Entering username: `name` From b2a21e888bbd3dab9da1fb4e7a27f6c1ba034c21 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 8 Nov 2020 02:56:51 +0800 Subject: [PATCH 337/374] Elaborate on valid inputs for user name --- docs/UserGuide.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index dcf039d4c2..13b1d2e11c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -52,7 +52,9 @@ Stores the user's name or nickname into DietBook during the initial setup. Format: `name YOUR_NAME_OR_NICKNAME` -* The name given must not be empty. +* The name given **must not be empty**. +* The name **can contain any numbers and characters except for `/`**, the forward slash. + * FYI: DietBook accepts all characters from Dec 33 - 126 of the [ASCII table](http://www.asciitable.com/), **except Dec 47**, the forward slash `/`. * This command is **only used when setting up DietBook for the first time**. Any subsequent editing of the name can be done using the [editinfo](#editing-user-information-editinfo) command. Example of usage: @@ -137,7 +139,9 @@ Format: `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/ * Although all parameters are listed as optional, **at least one of the optional fields needs to be provided**. In this case, any one of the parameters would work. * Existing values will be updated to the input values, even if the new value given is the same as the existing value. -* The name must not be empty. +* The name given **must not be empty**. +* The name **can contain any numbers and characters except for `/`**, the forward slash. + * FYI: DietBook accepts all characters from Dec 33 - 126 of the [ASCII table](http://www.asciitable.com/), **except Dec 47**, the forward slash `/`. * The gender must be either **`M` for male, `F` for female or `O` for others**. * The age must be a positive **integer from 0 to 150, inclusive**. * The height must be a positive **integer from 1 to 300, inclusive**. From 95c64716c373fb1993fdcf8c326c25edfd3cde80 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 8 Nov 2020 03:02:32 +0800 Subject: [PATCH 338/374] Combine notes and warning section --- docs/UserGuide.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 13b1d2e11c..8814636f49 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -20,21 +20,18 @@ A CLI, similar to the one shown below, should appear within a few seconds. Follo ## Features -**:information_source: Notes about the command format:** +**:warning: Please take note of the following:** * Words in `UPPER_CASE` are **parameters to be supplied** by the user.
e.g. For `name YOUR_NAME_OR_NICKNAME`, `name Jack` would be a valid command. * Parameters in **square brackets are optional**. However, if all parameters are optional, **at least one parameter needs to be given**. In such cases, any one of the parameters would be valid.
-e.g. For `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]`, `editinfo a/31` and `editinfo h/173 o/87` are valid commands but `editinfo -` is not. +e.g. For `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL]`, `editinfo a/31` and `editinfo h/173 o/87` are valid commands but `editinfo` is not. * For commands with multiple parameters, the parameters can be in any order **only if the parameters are attached to a parameter tag** like `n/`, `a/`, etc. Otherwise, they **must be entered in the sequence as shown** in this guide, the [Help Command](#to-view-a-list-of-valid-commands-help) or the [Summary Command](#command-summary).
e.g. For `calculate NUTRIENT_TYPE [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm]`,`calculate fat 2020-07-03T23:59 2020-09-03T23:59` is valid but `calculate 2020-07-03T23:59 fat 2020-09-03T23:59` is not.
e.g. For `add i/INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm]`, `add x/1 i/1 2020-09-03T23:59` is valid but `add i/1 2020-09-03T23:59 x/1 ` is not as time needs to be entered as the last parameter. -**:warning: Please take note of the following:** - * Command words and parameter indicators are case-sensitive.
e.g. `help` is a valid command but `Help` is not.
e.g. For `add i/INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm]`, `add i/1 x/1 2020-07-03T23:59` is valid but `add I/1 x/1 2020-07-03T23:59` is not. From ee8d4664f01dbd09507873784d0eae06fcd743d3 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 8 Nov 2020 10:24:26 +0800 Subject: [PATCH 339/374] implement 2nd layer of setters and getters --- .../FoodPortionDateSaveLoadManager.java | 99 +++++++++++++------ .../FoodPortionDateSaveLoadManagerTest.java | 15 +++ 2 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java diff --git a/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java index 05dbad08ff..fee6786ece 100644 --- a/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java @@ -3,12 +3,13 @@ import seedu.dietbook.food.Food; import java.io.FileNotFoundException; +import java.time.LocalDateTime; /*** * this class takes care of saving and loading of food, portion and date */ public class FoodPortionDateSaveLoadManager { - private static final int DEFAULT_SAVER_WIDTH = 7; + private static final int DEFAULT_SAVER_WIDTH = 8; private static final int DEFAULT_SAVER_HEIGHT = 10; private static final int FOOD_NAME_INDEX = 1; @@ -20,7 +21,8 @@ public class FoodPortionDateSaveLoadManager { private static final int DAY_INDEX = 7; private static final int MONTH_INDEX = 8; private static final int YEAR_INDEX = 9; - private static final int TIME_INDEX = 10; + private static final int HOUR_INDEX = 10; + private static final int MINUTE_INDEX = 11; private static final String DEFAULT_FOOD_NAME = "No Food Name"; private static final String DEFAULT_FOOD_CALORIE = "0"; @@ -31,8 +33,8 @@ public class FoodPortionDateSaveLoadManager { private static final String DEFAULT_DAY = "1"; private static final String DEFAULT_MONTH= "1"; private static final String DEFAULT_YEAR = "2000"; - private static final String DEFAULT_TIME = "0"; - + private static final String DEFAULT_MINUTE = "0"; + private static final String DEFAULT_HOUR = "0"; private static final String FOOD_FOLDER_NAME = "Food##PORTION###dDATE##folder"; @@ -47,6 +49,8 @@ public FoodPortionDateSaveLoadManager() { this.fileLoader = Loader.loadEmpty(); } + // ------ saving and loading functions ---------- + /** * Call this function to load a food file. * @param fileName name of file @@ -60,6 +64,10 @@ public void clearLoader() { this.fileLoader = Loader.loadEmpty(); } + public void save(String fileName){ + this.saver.save(FOOD_FOLDER_NAME, fileName); + } + /** * Clears the saver and sets the number of entries it can take * call this function at the start of a series of functions to store data @@ -70,15 +78,17 @@ public void readySaver(Integer numEntry){ saver.resetSize(DEFAULT_SAVER_WIDTH, numEntry); } - public void addFood(Food food, int entryNumber){ + // ------- setters and getters ---------- + + public void setFood(Food food, int entryNumber){ setFoodName(food.getName(), entryNumber); - addFoodCalorie(food.getCalorie(), entryNumber); + setFoodCalorie(food.getCalorie(), entryNumber); setFoodCarbohydrate(food.getCarbohydrate(), entryNumber); - addFoodProtein(food.getProtein(), entryNumber); - addFoodFat(food.getFat(), entryNumber); + setFoodProtein(food.getProtein(), entryNumber); + setFoodFat(food.getFat(), entryNumber); } - public Food getFood(Integer entryNumber) throws IllegalAccessException { + public Food getFood(int entryNumber) throws IllegalAccessException { String name = getFoodName(entryNumber); int calorie = getFoodCalorie(entryNumber); int carbohydrate = getFoodCarbohydrate(entryNumber); @@ -87,25 +97,24 @@ public Food getFood(Integer entryNumber) throws IllegalAccessException { return new Food(name, calorie, carbohydrate, protein, fat); } - private String getFoodName(Integer entryNumber) throws IllegalAccessException { - return fileLoader.get(FOOD_NAME_INDEX, entryNumber).orElse(DEFAULT_FOOD_NAME); - } - - private int getFoodFat(Integer entryNumber) throws IllegalAccessException { - return Integer.parseInt(fileLoader.get(FOOD_FAT_INDEX, entryNumber).orElse(DEFAULT_FOOD_FAT)); - } - - private int getFoodProtein(Integer entryNumber) throws IllegalAccessException { - return Integer.parseInt(fileLoader.get(FOOD_PROTEIN_INDEX, entryNumber).orElse(DEFAULT_FOOD_PROTEIN)); + public void setDateTime(LocalDateTime dateTime, int entryNumber){ + setDay(dateTime.getDayOfMonth(), entryNumber); + setMonth(dateTime.getMonthValue(), entryNumber); + setYear(dateTime.getYear(), entryNumber); + setHour(dateTime.getHour(), entryNumber); + setMinute(dateTime.getMinute(), entryNumber); } - private int getFoodCarbohydrate(Integer entryNumber) throws IllegalAccessException { - return Integer.parseInt(fileLoader.get(FOOD_CARBOHYDRATE_INDEX, entryNumber).orElse(DEFAULT_FOOD_CARBOHYDRATE)); + public LocalDateTime getDateTime(int entryNumber) throws IllegalAccessException { + int day = Integer.parseInt(fileLoader.get(DAY_INDEX, entryNumber).orElse(DEFAULT_DAY)); + int month = Integer.parseInt(fileLoader.get(MONTH_INDEX, entryNumber).orElse(DEFAULT_MONTH)); + int year = Integer.parseInt(fileLoader.get(YEAR_INDEX, entryNumber).orElse(DEFAULT_YEAR)); + int hour = Integer.parseInt(fileLoader.get(HOUR_INDEX, entryNumber).orElse(DEFAULT_HOUR)); + int minute = Integer.parseInt(fileLoader.get(MINUTE_INDEX, entryNumber).orElse(DEFAULT_MINUTE)); + return LocalDateTime.of(year, month, day, hour, minute); } - private int getFoodCalorie(Integer entryNumber) throws IllegalAccessException { - return Integer.parseInt(fileLoader.get(FOOD_CALORIE_INDEX, entryNumber).orElse(DEFAULT_FOOD_CALORIE)); - } + // ------ Basic setters and getters ------- private void setFoodCarbohydrate( int carbohydrate, int entryNumber) { saver.add(Integer.toString(carbohydrate), FOOD_CARBOHYDRATE_INDEX, entryNumber); @@ -115,15 +124,15 @@ private void setFoodName(String name, int entryNumber) { saver.add(name, FOOD_NAME_INDEX, entryNumber); } - private void addFoodCalorie(int calorie, int entryNumber) { + private void setFoodCalorie(int calorie, int entryNumber) { saver.add(Integer.toString(calorie), FOOD_CALORIE_INDEX, entryNumber); } - private void addFoodProtein(int protein, int entryNumber){ + private void setFoodProtein(int protein, int entryNumber){ saver.add(Integer.toString(protein), FOOD_PROTEIN_INDEX, entryNumber); } - private void addFoodFat(int fat, int entryNumber){ + private void setFoodFat(int fat, int entryNumber){ saver.add(Integer.toString(fat), FOOD_FAT_INDEX, entryNumber); } @@ -143,8 +152,32 @@ public void setYear(int year, int entryNumber){ saver.add(Integer.toString(year), YEAR_INDEX, entryNumber); } - public void setTime(int time, int entryNumber){ - saver.add(Integer.toString(time), TIME_INDEX, entryNumber); + public void setHour(int hour, int entryNumber){ + saver.add(Integer.toString(hour), HOUR_INDEX, entryNumber); + } + + public void setMinute(int minute, int entryNumber){ + saver.add(Integer.toString(minute), MINUTE_INDEX, entryNumber); + } + + private String getFoodName(Integer entryNumber) throws IllegalAccessException { + return fileLoader.get(FOOD_NAME_INDEX, entryNumber).orElse(DEFAULT_FOOD_NAME); + } + + private int getFoodFat(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_FAT_INDEX, entryNumber).orElse(DEFAULT_FOOD_FAT)); + } + + private int getFoodProtein(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_PROTEIN_INDEX, entryNumber).orElse(DEFAULT_FOOD_PROTEIN)); + } + + private int getFoodCarbohydrate(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_CARBOHYDRATE_INDEX, entryNumber).orElse(DEFAULT_FOOD_CARBOHYDRATE)); + } + + private int getFoodCalorie(Integer entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(FOOD_CALORIE_INDEX, entryNumber).orElse(DEFAULT_FOOD_CALORIE)); } public int getPortionSize(int entryNumber) throws IllegalAccessException { @@ -163,7 +196,11 @@ public int getYear(int entryNumber) throws IllegalAccessException { return Integer.parseInt(fileLoader.get(YEAR_INDEX, entryNumber).orElse(DEFAULT_YEAR)); } - public int getTime(int entryNumber) throws IllegalAccessException { - return Integer.parseInt(fileLoader.get(TIME_INDEX, entryNumber).orElse(DEFAULT_TIME)); + public int getHour(int entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(HOUR_INDEX, entryNumber).orElse(DEFAULT_HOUR)); + } + + public int getMinute(int entryNumber) throws IllegalAccessException { + return Integer.parseInt(fileLoader.get(MINUTE_INDEX, entryNumber).orElse(DEFAULT_MINUTE)); } } diff --git a/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java new file mode 100644 index 0000000000..89f1f325cf --- /dev/null +++ b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java @@ -0,0 +1,15 @@ +package seedu.dietbook.saveload; + +import org.junit.jupiter.api.BeforeEach; + +import static org.junit.jupiter.api.Assertions.*; + +class FoodPortionDateSaveLoadManagerTest { + FoodPortionDateSaveLoadManager test; + + @BeforeEach + private void setUp(){ + test = new FoodPortionDateSaveLoadManager(); + + } +} \ No newline at end of file From bb91199a1694765eeaf498eb78e41827470efbdf Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 8 Nov 2020 10:52:24 +0800 Subject: [PATCH 340/374] add basic functionality test for FoodPortionSaveLoadManager --- src/main/java/seedu/dietbook/DietBook.java | 2 +- .../saveload/PersonSaveLoadManager.java | 2 +- .../FoodPortionDateSaveLoadManagerTest.java | 54 ++++++++++++++++++- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/DietBook.java b/src/main/java/seedu/dietbook/DietBook.java index f717f49e7d..fe0b963fba 100644 --- a/src/main/java/seedu/dietbook/DietBook.java +++ b/src/main/java/seedu/dietbook/DietBook.java @@ -49,7 +49,7 @@ public void loadPerson() { try { loadPerson.load("resources/UserInfo.txt"); FitnessLevel fitLvl = FitnessLevel.NONE; - int fitLvlInt = loadPerson.getActivityLevel(); + int fitLvlInt = loadPerson.getFitnessLevel(); if (fitLvlInt == 1) { fitLvl = FitnessLevel.NONE; } else if (fitLvlInt == 2) { diff --git a/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java index 9112978ddb..5dcce490ce 100644 --- a/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/PersonSaveLoadManager.java @@ -101,7 +101,7 @@ public void save(String fileName) { this.saver.add(Integer.toString(this.originalWeight), ORIGINAL_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); this.saver.add(Integer.toString(this.currentWeight), CURRENT_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); this.saver.add(Integer.toString(this.targetWeight), TARGET_WEIGHT_ENTRY_INDEX, PERSON_DATA_ROW); - this.saver.add(Integer.toString(this.activityLevel), ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW); + this.saver.add(Integer.toString(this.fitnessLevel), ACTIVITY_LEVEL_ENTRY_INDEX, PERSON_DATA_ROW); this.saver.save(PERSON_FOLDER_NAME, fileName); } diff --git a/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java index 89f1f325cf..dee0f2f46e 100644 --- a/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java @@ -1,6 +1,11 @@ package seedu.dietbook.saveload; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.dietbook.food.Food; + +import java.io.FileNotFoundException; +import java.time.LocalDateTime; import static org.junit.jupiter.api.Assertions.*; @@ -10,6 +15,53 @@ class FoodPortionDateSaveLoadManagerTest { @BeforeEach private void setUp(){ test = new FoodPortionDateSaveLoadManager(); - + LocalDateTime testDateTime1 = LocalDateTime.of(2020, 10, 12, 16, 30); + Food testFood1 = new Food("Banana", 100, 50, 30, 20); + int testPortionSize1 = 10; + + LocalDateTime testDateTime2 = LocalDateTime.of(2036, 9, 16, 10, 40); + Food testFood2 = new Food("Solenoid", 200, 20, 40, 30); + int testPortionSize2 = 5; + + test.readySaver(10); + test.setFood(testFood1, 1); + test.setDateTime(testDateTime1, 1); + test.setPortionSize(testPortionSize1, 1); + test.setFood(testFood2, 10); + test.setDateTime(testDateTime2, 10); + test.setPortionSize(testPortionSize2, 10); + test.save("testFoodFile"); + } + + @Test + private void data_loadentry1_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { + test.load("testFoodFile"); + assertEquals("Banana", test.getFood(1).getName()); + assertEquals(100, test.getFood(1).getCalorie()); + assertEquals(50, test.getFood(1).getProtein()); + assertEquals(30, test.getFood(1).getProtein()); + assertEquals(20, test.getFood(1).getFat()); + assertEquals(10, test.getPortionSize(1)); + assertEquals(2020, test.getYear(1)); + assertEquals(10, test.getMonth(1)); + assertEquals(12, test.getDay(1)); + assertEquals(16, test.getHour(1)); + assertEquals(30, test.getMinute(1)); + } + + @Test + private void data_loadentry2_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { + test.load("testFoodFile"); + assertEquals("Solenoid", test.getFood(10).getName()); + assertEquals(200, test.getFood(10).getCalorie()); + assertEquals(20, test.getFood(10).getProtein()); + assertEquals(40, test.getFood(10).getProtein()); + assertEquals(30, test.getFood(10).getFat()); + assertEquals(5, test.getPortionSize(10)); + assertEquals(2036, test.getYear(10)); + assertEquals(9, test.getMonth(10)); + assertEquals(16, test.getDay(10)); + assertEquals(10, test.getHour(10)); + assertEquals(40, test.getMinute(10)); } } \ No newline at end of file From 5376ca39f36ed250dde0cea9762c8172213f90f9 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 8 Nov 2020 12:03:39 +0800 Subject: [PATCH 341/374] implement and test save loading food list --- .../FoodPortionDateSaveLoadManager.java | 39 +++++++++++++++- .../FoodPortionDateSaveLoadManagerTest.java | 46 +++++++++++++++++-- .../saveload/PersonSaveLoadManagerTest.java | 8 ++-- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java index fee6786ece..82241fdee7 100644 --- a/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java @@ -1,15 +1,17 @@ package seedu.dietbook.saveload; import seedu.dietbook.food.Food; +import seedu.dietbook.list.FoodList; import java.io.FileNotFoundException; import java.time.LocalDateTime; +import java.util.List; /*** * this class takes care of saving and loading of food, portion and date */ public class FoodPortionDateSaveLoadManager { - private static final int DEFAULT_SAVER_WIDTH = 8; + private static final int DEFAULT_SAVER_WIDTH = 11; private static final int DEFAULT_SAVER_HEIGHT = 10; private static final int FOOD_NAME_INDEX = 1; @@ -78,6 +80,41 @@ public void readySaver(Integer numEntry){ saver.resetSize(DEFAULT_SAVER_WIDTH, numEntry); } + // ------- top layer save loading ------- + + /** + * Constructs the food list from stored data + * Note : call load function before calling this function or else it will throw illegalAccessException + * + * @return the completed food list + */ + public FoodList getFoodList() throws IllegalAccessException { + FoodList foodlist = new FoodList(); + for (int i = 1; i < fileLoader.getHeight() + 1; i++){ + foodlist.addFoodAtDateTime(this.getPortionSize(i), this.getFood(i), this.getDateTime(i)); + } + return foodlist; + } + + /** + * Takes in a food list object and saves all of it's contents to the specified file name + * The number of entries is equal to the number of items on the food list + * @param foodlist food list + */ + public void saveFoodList(FoodList foodlist, String fileName) { + List foods = foodlist.getFoods(); + List portions = foodlist.getPortionSizes(); + List datetimes = foodlist.getDateTimes(); + int numEntry = foods.size(); + readySaver(numEntry); + for (int i = 1; i < numEntry + 1; i++){ + setFood(foods.get(i-1), i); + setDateTime(datetimes.get(i-1), i); + setPortionSize(portions.get(i-1),i); + } + save(fileName); + } + // ------- setters and getters ---------- public void setFood(Food food, int entryNumber){ diff --git a/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java index dee0f2f46e..b834135601 100644 --- a/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java @@ -3,17 +3,21 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import seedu.dietbook.food.Food; +import seedu.dietbook.list.FoodList; import java.io.FileNotFoundException; import java.time.LocalDateTime; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; class FoodPortionDateSaveLoadManagerTest { FoodPortionDateSaveLoadManager test; + FoodList testfl; + FoodPortionDateSaveLoadManager test2; @BeforeEach - private void setUp(){ + public void setUp(){ test = new FoodPortionDateSaveLoadManager(); LocalDateTime testDateTime1 = LocalDateTime.of(2020, 10, 12, 16, 30); Food testFood1 = new Food("Banana", 100, 50, 30, 20); @@ -31,14 +35,26 @@ private void setUp(){ test.setDateTime(testDateTime2, 10); test.setPortionSize(testPortionSize2, 10); test.save("testFoodFile"); + + LocalDateTime testDateTime3 = LocalDateTime.of(1982, 3, 14, 20, 59); + Food testFood3 = new Food("Bacon", 300, 10, 450, 310); + int testPortionSize3 = 3; + + testfl = new FoodList(); + testfl.addFoodAtDateTime(testPortionSize1, testFood1, testDateTime1); + testfl.addFoodAtDateTime(testPortionSize2, testFood2, testDateTime2); + testfl.addFoodAtDateTime(testPortionSize3, testFood3, testDateTime3); + + test2 = new FoodPortionDateSaveLoadManager(); + test2.saveFoodList(testfl, "testFoodListFile"); } @Test - private void data_loadentry1_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { + public void data_loadentry1_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { test.load("testFoodFile"); assertEquals("Banana", test.getFood(1).getName()); assertEquals(100, test.getFood(1).getCalorie()); - assertEquals(50, test.getFood(1).getProtein()); + assertEquals(50, test.getFood(1).getCarbohydrate()); assertEquals(30, test.getFood(1).getProtein()); assertEquals(20, test.getFood(1).getFat()); assertEquals(10, test.getPortionSize(1)); @@ -50,11 +66,11 @@ private void data_loadentry1_returnsCorrectSavedData() throws FileNotFoundExcept } @Test - private void data_loadentry2_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { + public void data_loadentry2_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { test.load("testFoodFile"); assertEquals("Solenoid", test.getFood(10).getName()); assertEquals(200, test.getFood(10).getCalorie()); - assertEquals(20, test.getFood(10).getProtein()); + assertEquals(20, test.getFood(10).getCarbohydrate()); assertEquals(40, test.getFood(10).getProtein()); assertEquals(30, test.getFood(10).getFat()); assertEquals(5, test.getPortionSize(10)); @@ -64,4 +80,24 @@ private void data_loadentry2_returnsCorrectSavedData() throws FileNotFoundExcept assertEquals(10, test.getHour(10)); assertEquals(40, test.getMinute(10)); } + + @Test + public void data_loadFoodList_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { + test2.load("testFoodListFile"); + FoodList testfl2 = test2.getFoodList(); + List testfl2datetimes= testfl2.getDateTimes(); + List testfl2portions = testfl2.getPortionSizes(); + List testfl2foods = testfl2.getFoods(); + assertEquals(1982, testfl2datetimes.get(2).getYear()); + assertEquals(3, testfl2datetimes.get(2).getMonthValue()); + assertEquals(14, testfl2datetimes.get(2).getDayOfMonth()); + assertEquals(20, testfl2datetimes.get(2).getHour()); + assertEquals(59, testfl2datetimes.get(2).getMinute()); + assertEquals(3, testfl2portions.get(2)); + assertEquals("Bacon", testfl2foods.get(2).getName()); + assertEquals(300, testfl2foods.get(2).getCalorie()); + assertEquals(10, testfl2foods.get(2).getCarbohydrate()); + assertEquals(450, testfl2foods.get(2).getProtein()); + assertEquals(310, testfl2foods.get(2).getFat()); + } } \ No newline at end of file diff --git a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java index 01de9bd352..4f7198dee3 100644 --- a/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/PersonSaveLoadManagerTest.java @@ -11,7 +11,7 @@ class PersonSaveLoadManagerTest { @BeforeEach - private void setUp() { + public void setUp() { PersonSaveLoadManager pslTest = new PersonSaveLoadManager(); pslTest.setName("Victor Chng"); pslTest.setActivityLevel(0); @@ -24,17 +24,17 @@ private void setUp() { } @Test - private void load_noSuchFile_expectFileNotFoundException() { + public void load_noSuchFile_expectFileNotFoundException() { PersonSaveLoadManager localpslTest = new PersonSaveLoadManager(); assertThrows(FileNotFoundException.class, () -> localpslTest.load("pie die pie")); } @Test - private void load_correctFile_allContentsCorrect() throws Exception { + public void load_correctFile_allContentsCorrect() throws Exception { PersonSaveLoadManager localpslTest = new PersonSaveLoadManager(); localpslTest.load("pslTest"); assertEquals("Victor Chng", localpslTest.getName()); - assertEquals("Unknown", localpslTest.getGender()); + assertEquals("UnKnown", localpslTest.getGender()); assertEquals(200, localpslTest.getOriginalWeight()); assertEquals(100, localpslTest.getAge()); assertEquals(300, localpslTest.getCurrentWeight()); From dd44a643b5c470e5b9560f1a58039e8068f41fd6 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 8 Nov 2020 12:20:18 +0800 Subject: [PATCH 342/374] pass check style --- .../FoodPortionDateSaveLoadManager.java | 62 +++++++++---------- .../FoodPortionDateSaveLoadManagerTest.java | 19 +++--- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java index 82241fdee7..dc42836cf2 100644 --- a/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java +++ b/src/main/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManager.java @@ -7,8 +7,8 @@ import java.time.LocalDateTime; import java.util.List; -/*** - * this class takes care of saving and loading of food, portion and date +/** + * This class takes care of saving and loading of food, portion and date. */ public class FoodPortionDateSaveLoadManager { private static final int DEFAULT_SAVER_WIDTH = 11; @@ -28,21 +28,18 @@ public class FoodPortionDateSaveLoadManager { private static final String DEFAULT_FOOD_NAME = "No Food Name"; private static final String DEFAULT_FOOD_CALORIE = "0"; - private static final String DEFAULT_FOOD_CARBOHYDRATE= "0"; - private static final String DEFAULT_FOOD_PROTEIN= "0"; + private static final String DEFAULT_FOOD_CARBOHYDRATE = "0"; + private static final String DEFAULT_FOOD_PROTEIN = "0"; private static final String DEFAULT_FOOD_FAT = "0"; private static final String DEFAULT_PORTION_SIZE = "1"; private static final String DEFAULT_DAY = "1"; - private static final String DEFAULT_MONTH= "1"; + private static final String DEFAULT_MONTH = "1"; private static final String DEFAULT_YEAR = "2000"; private static final String DEFAULT_MINUTE = "0"; private static final String DEFAULT_HOUR = "0"; private static final String FOOD_FOLDER_NAME = "Food##PORTION###dDATE##folder"; - private static final String DEFAULT_NAME = "MISSING NAME"; - private static final String DEFAULT_NUTRITION_VALUE = "0"; - private Saver saver; private Loader fileLoader; @@ -66,39 +63,40 @@ public void clearLoader() { this.fileLoader = Loader.loadEmpty(); } - public void save(String fileName){ + public void save(String fileName) { this.saver.save(FOOD_FOLDER_NAME, fileName); } /** - * Clears the saver and sets the number of entries it can take - * call this function at the start of a series of functions to store data + * Clears the saver and sets the number of entries it can take. + * call this function at the start of a series of functions to store data. * * @param numEntry num of entries */ - public void readySaver(Integer numEntry){ + public void readySaver(Integer numEntry) { saver.resetSize(DEFAULT_SAVER_WIDTH, numEntry); } // ------- top layer save loading ------- /** - * Constructs the food list from stored data - * Note : call load function before calling this function or else it will throw illegalAccessException + * Constructs the food list from stored data. + * Note : call load function before calling this function or else it will throw illegalAccessException. * * @return the completed food list */ public FoodList getFoodList() throws IllegalAccessException { FoodList foodlist = new FoodList(); - for (int i = 1; i < fileLoader.getHeight() + 1; i++){ + for (int i = 1; i < fileLoader.getHeight() + 1; i++) { foodlist.addFoodAtDateTime(this.getPortionSize(i), this.getFood(i), this.getDateTime(i)); } return foodlist; } /** - * Takes in a food list object and saves all of it's contents to the specified file name - * The number of entries is equal to the number of items on the food list + * Takes in a food list object and saves all of it's contents to the specified file name. + * The number of entries is equal to the number of items on the food list. + * * @param foodlist food list */ public void saveFoodList(FoodList foodlist, String fileName) { @@ -107,17 +105,17 @@ public void saveFoodList(FoodList foodlist, String fileName) { List datetimes = foodlist.getDateTimes(); int numEntry = foods.size(); readySaver(numEntry); - for (int i = 1; i < numEntry + 1; i++){ - setFood(foods.get(i-1), i); - setDateTime(datetimes.get(i-1), i); - setPortionSize(portions.get(i-1),i); + for (int i = 1; i < numEntry + 1; i++) { + setFood(foods.get(i - 1), i); + setDateTime(datetimes.get(i - 1), i); + setPortionSize(portions.get(i - 1),i); } save(fileName); } // ------- setters and getters ---------- - public void setFood(Food food, int entryNumber){ + public void setFood(Food food, int entryNumber) { setFoodName(food.getName(), entryNumber); setFoodCalorie(food.getCalorie(), entryNumber); setFoodCarbohydrate(food.getCarbohydrate(), entryNumber); @@ -134,7 +132,7 @@ public Food getFood(int entryNumber) throws IllegalAccessException { return new Food(name, calorie, carbohydrate, protein, fat); } - public void setDateTime(LocalDateTime dateTime, int entryNumber){ + public void setDateTime(LocalDateTime dateTime, int entryNumber) { setDay(dateTime.getDayOfMonth(), entryNumber); setMonth(dateTime.getMonthValue(), entryNumber); setYear(dateTime.getYear(), entryNumber); @@ -153,7 +151,7 @@ public LocalDateTime getDateTime(int entryNumber) throws IllegalAccessException // ------ Basic setters and getters ------- - private void setFoodCarbohydrate( int carbohydrate, int entryNumber) { + private void setFoodCarbohydrate(int carbohydrate, int entryNumber) { saver.add(Integer.toString(carbohydrate), FOOD_CARBOHYDRATE_INDEX, entryNumber); } @@ -165,35 +163,35 @@ private void setFoodCalorie(int calorie, int entryNumber) { saver.add(Integer.toString(calorie), FOOD_CALORIE_INDEX, entryNumber); } - private void setFoodProtein(int protein, int entryNumber){ + private void setFoodProtein(int protein, int entryNumber) { saver.add(Integer.toString(protein), FOOD_PROTEIN_INDEX, entryNumber); } - private void setFoodFat(int fat, int entryNumber){ + private void setFoodFat(int fat, int entryNumber) { saver.add(Integer.toString(fat), FOOD_FAT_INDEX, entryNumber); } - public void setPortionSize(int portionSize, Integer entryNumber){ + public void setPortionSize(int portionSize, Integer entryNumber) { saver.add(Integer.toString(portionSize), PORTION_SIZE_INDEX, entryNumber); } - public void setDay(int day, int entryNumber){ + public void setDay(int day, int entryNumber) { saver.add(Integer.toString(day), DAY_INDEX, entryNumber); } - public void setMonth(int month, int entryNumber){ + public void setMonth(int month, int entryNumber) { saver.add(Integer.toString(month), MONTH_INDEX, entryNumber); } - public void setYear(int year, int entryNumber){ + public void setYear(int year, int entryNumber) { saver.add(Integer.toString(year), YEAR_INDEX, entryNumber); } - public void setHour(int hour, int entryNumber){ + public void setHour(int hour, int entryNumber) { saver.add(Integer.toString(hour), HOUR_INDEX, entryNumber); } - public void setMinute(int minute, int entryNumber){ + public void setMinute(int minute, int entryNumber) { saver.add(Integer.toString(minute), MINUTE_INDEX, entryNumber); } diff --git a/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java index b834135601..f1b9c9e4e9 100644 --- a/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java +++ b/src/test/java/seedu/dietbook/saveload/FoodPortionDateSaveLoadManagerTest.java @@ -9,7 +9,7 @@ import java.time.LocalDateTime; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; class FoodPortionDateSaveLoadManagerTest { FoodPortionDateSaveLoadManager test; @@ -17,7 +17,7 @@ class FoodPortionDateSaveLoadManagerTest { FoodPortionDateSaveLoadManager test2; @BeforeEach - public void setUp(){ + public void setUp() { test = new FoodPortionDateSaveLoadManager(); LocalDateTime testDateTime1 = LocalDateTime.of(2020, 10, 12, 16, 30); Food testFood1 = new Food("Banana", 100, 50, 30, 20); @@ -36,13 +36,16 @@ public void setUp(){ test.setPortionSize(testPortionSize2, 10); test.save("testFoodFile"); - LocalDateTime testDateTime3 = LocalDateTime.of(1982, 3, 14, 20, 59); - Food testFood3 = new Food("Bacon", 300, 10, 450, 310); - int testPortionSize3 = 3; + testfl = new FoodList(); testfl.addFoodAtDateTime(testPortionSize1, testFood1, testDateTime1); testfl.addFoodAtDateTime(testPortionSize2, testFood2, testDateTime2); + + LocalDateTime testDateTime3 = LocalDateTime.of(1982, 3, 14, 20, 59); + Food testFood3 = new Food("Bacon", 300, 10, 450, 310); + int testPortionSize3 = 3; + testfl.addFoodAtDateTime(testPortionSize3, testFood3, testDateTime3); test2 = new FoodPortionDateSaveLoadManager(); @@ -85,9 +88,9 @@ public void data_loadentry2_returnsCorrectSavedData() throws FileNotFoundExcepti public void data_loadFoodList_returnsCorrectSavedData() throws FileNotFoundException, IllegalAccessException { test2.load("testFoodListFile"); FoodList testfl2 = test2.getFoodList(); - List testfl2datetimes= testfl2.getDateTimes(); - List testfl2portions = testfl2.getPortionSizes(); - List testfl2foods = testfl2.getFoods(); + final List testfl2datetimes = testfl2.getDateTimes(); + final List testfl2portions = testfl2.getPortionSizes(); + final List testfl2foods = testfl2.getFoods(); assertEquals(1982, testfl2datetimes.get(2).getYear()); assertEquals(3, testfl2datetimes.get(2).getMonthValue()); assertEquals(14, testfl2datetimes.get(2).getDayOfMonth()); From 9485a2c438ecd1b16037e4d4c2538a3d523089d3 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Sun, 8 Nov 2020 12:34:41 +0800 Subject: [PATCH 343/374] copy resources over to test --- src/test/resources/data.txt | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/test/resources/data.txt diff --git a/src/test/resources/data.txt b/src/test/resources/data.txt new file mode 100644 index 0000000000..6dd0327f19 --- /dev/null +++ b/src/test/resources/data.txt @@ -0,0 +1,53 @@ +##################################################################### +# 3 LEVEL DATA BASE # +# Canteen -----> Store ------> Food # +# Commands : # +# &%START : start reading data from the data base # +# &%STOP : stop reading data from the data base # +# &%UP : goes down 1 level e.g. Canteen ---> Store # +# &%DOWN : goes down 1 level e.g. Canteen ---> Store # +# &%ADD format : adds the item with the given format # +# # +# Comments : any line that starts with # is ignored # +# # +# Canteen format : {name} # +# Store format : {name} # +# Food format : {name}|{Calorie}|{Carb}|{Protein}|{Fat} # +##################################################################### + +###################################################################### +# Version 0.1 : # +# there is only UP, once a store or canteen is # +# specified we automatically go down 1 level , for this version # +# there is no going out of a store and then coming back to add more# +# Units : Calorie : kcal : Carbs : g Protein : g : Fats : g # +###################################################################### + +&%START +Science canteen +Halal Mini Wok +Prawn Mee Soup(Dry)(Large)|490|30|20|26 +Prawn Mee Soup(Dry)(Small)|390|25|15|19 +Fried Hokkien Prawn Mee(Large)|470|40|20|20 +Fried Hokkien Prawn Mee(Small)|350|30|15|15 +Clay Pot Chicken|440|34|15|15 +Black Pepper Chicken|490|34|16|16 +&%UP +Ayam Penyet +Ayam Penyet Set|699|45|30|30 +Steamed Chicken Set |475|35|20|20 +Ikan Grouper Penyet Set|669|50|40|50 +&%UP +Korean +kimchi fried rice|520|45|35|56 +ginseng chicken|450|25|32|66 +ramen|530|76|25|43 +&%UP +Gong Cha +gong cha green tea|100|0|0|0 +gong cha ooloong tea|100|0|0|0 +gong cha bubble tea|200|0|0|0 +&%UP +&%UP +&%STOP + From 33c335f054693148ed82eba78e07ca75853e06ad Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 8 Nov 2020 20:57:02 +0800 Subject: [PATCH 344/374] Fix icon --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index a22a61bc76..2cb9a2762c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # DietBook -[[!CI status](https://github.com/AY2021S1-CS2113-T14-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2021S1-CS2113-T14-4/tp/actions) +[![CI status](https://github.com/AY2021S1-CS2113-T14-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2021S1-CS2113-T14-4/tp/actions) DietBook is a Command Line Interface (CLI) desktop application designed to **track your food and nutritional intake** as well as provide you with your **daily calorie recommendation**. 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. From 518101e98d15ef1f7879f22ec6495d71fd7486b9 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Sun, 8 Nov 2020 21:06:13 +0800 Subject: [PATCH 345/374] Improve formatting --- docs/UserGuide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 8814636f49..846628f759 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -20,7 +20,7 @@ A CLI, similar to the one shown below, should appear within a few seconds. Follo ## Features -**:warning: Please take note of the following:** +:warning: **Please take note of the following:** * Words in `UPPER_CASE` are **parameters to be supplied** by the user.
e.g. For `name YOUR_NAME_OR_NICKNAME`, `name Jack` would be a valid command. @@ -32,11 +32,11 @@ e.g. For `editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c e.g. For `calculate NUTRIENT_TYPE [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm]`,`calculate fat 2020-07-03T23:59 2020-09-03T23:59` is valid but `calculate 2020-07-03T23:59 fat 2020-09-03T23:59` is not.
e.g. For `add i/INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm]`, `add x/1 i/1 2020-09-03T23:59` is valid but `add i/1 2020-09-03T23:59 x/1 ` is not as time needs to be entered as the last parameter. -* Command words and parameter indicators are case-sensitive.
+* Command words and parameter indicators are **case-sensitive**.
e.g. `help` is a valid command but `Help` is not.
e.g. For `add i/INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm]`, `add i/1 x/1 2020-07-03T23:59` is valid but `add I/1 x/1 2020-07-03T23:59` is not. -* Spacing to separate command word and parameters is required.
+* **Spacing** to separate command word and parameters is required.
e.g. For `calculate NUTRIENT_TYPE`, `calculate all` is valid but `calculateall` is not. * Inappropriate usage of `/`, the forward slash, may lead to invalid commands. Only use **one** `/` in parameter tags like `n/`, `a/`, etc and avoid using `/` otherwise. From 9a917574a92169ed181a6f48edcb18cd0814b2d8 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 22:46:30 +0800 Subject: [PATCH 346/374] Added the minimum and maximum caps for recommended calorie intake. --- src/main/java/seedu/dietbook/calculator/Calculator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 446cbe0d48..4bada64881 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -319,6 +319,11 @@ public int calculateRecomendation(Person person) { } else { recomendation = (int) requirement + 100; } + if (recomendation < 1000) { + recomendation = 1000; + } else if (recomendation > 20000) { + recomendation = 20000; + } return recomendation; } } From 5f27da91a4e60fe18c59cb179d960465da710b47 Mon Sep 17 00:00:00 2001 From: mxksowie <34589654+mxksowie@users.noreply.github.com> Date: Sun, 8 Nov 2020 23:26:53 +0800 Subject: [PATCH 347/374] add sort FoodList can be sorted by datetime of its entries --- src/main/java/seedu/dietbook/list/FoodList.java | 11 ++++++++++- .../java/seedu/dietbook/list/FoodListManager.java | 13 +++++++++++++ src/test/java/seedu/dietbook/list/FoodListTest.java | 13 +++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/dietbook/list/FoodList.java b/src/main/java/seedu/dietbook/list/FoodList.java index 9d75762c73..4ac847dff6 100644 --- a/src/main/java/seedu/dietbook/list/FoodList.java +++ b/src/main/java/seedu/dietbook/list/FoodList.java @@ -13,7 +13,7 @@ * This is a stateful object. */ public class FoodList { - private ArrayList foodEntries; + private List foodEntries; /** * Default constructor that instantiates FoodList with an empty foodentry arraylist. @@ -24,6 +24,7 @@ public FoodList() { /** * Convenience constructor for testing purposes. + * This is an unsafe method of building the FoodList as FoodEntry added may not be an instance of DatedFoodEntry. */ protected FoodList(ArrayList entries) { this.foodEntries = entries; @@ -167,6 +168,14 @@ public List getDateTimes() { return FoodListManager.convertListToLocalDateTimes(foodEntries); } + /** + * Sorts the list based on datetime of the entries. + */ + public String sort() { + this.foodEntries = FoodListManager.sortListByDate(foodEntries); + return this.toString(); + } + @Override public String toString() { return FoodListManager.convertListToString(foodEntries); diff --git a/src/main/java/seedu/dietbook/list/FoodListManager.java b/src/main/java/seedu/dietbook/list/FoodListManager.java index 1631215ad6..0951385da1 100644 --- a/src/main/java/seedu/dietbook/list/FoodListManager.java +++ b/src/main/java/seedu/dietbook/list/FoodListManager.java @@ -5,6 +5,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.time.LocalDateTime; +import java.util.Collections; /** * Class with static methods to execute "complex commands" on FoodList. @@ -143,5 +144,17 @@ protected static List filterListByDate(List list, LocalDat return ListFunction.filterList(list, predicate); } + protected static List sortListByDate(List list) { + Function extractChild = x -> { + assert (x instanceof DatedFoodEntry) : "A FoodEntry without a date was unexpectedly added and found"; + return (DatedFoodEntry) x; + }; + List datedList = ListFunction.applyFunctionToList(list, extractChild); + Collections.sort(datedList); + Function useParent = x -> (FoodEntry) x; + return ListFunction.applyFunctionToList(datedList, useParent); + + } + } diff --git a/src/test/java/seedu/dietbook/list/FoodListTest.java b/src/test/java/seedu/dietbook/list/FoodListTest.java index ef543b31a1..2c5dae1c25 100644 --- a/src/test/java/seedu/dietbook/list/FoodListTest.java +++ b/src/test/java/seedu/dietbook/list/FoodListTest.java @@ -133,5 +133,18 @@ void printDatedList_datedList_matchingStrings() { } + @Test + @DisplayName("List sorting test") + void getSortedItem_listWithItemsAddedAtEarlierDates_getItemsAccordingToDateTimeOrder() { + datedList.addFoodAtDateTime(3, food, LocalDateTime.MIN); + datedList.addFoodAtDateTime(2, food, LocalDateTime.of(2000, 6, 7, 0, 0)); + + datedList.sort(); + + for (int i = 0; i < datedList.getFoods().size() - 1; i++) { + assertTrue(datedList.getDateTimes().get(i).isBefore(datedList.getDateTimes().get(i + 1))); + } + } + } From 3fa460e90c6f22dbdf483bf9db2f973e7034da6b Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 23:28:10 +0800 Subject: [PATCH 348/374] Created a testing method corresponding to the added caps. --- .../java/seedu/dietbook/calculator/CalculatorTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index a976f520a9..8da928ef09 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -67,4 +67,13 @@ void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { assertEquals(2728, calculator.calculateRecomendation(harry)); assertEquals(1752, calculator.calculateRecomendation(erica)); } + + @Test + void calculateRecomendedCalorieIntakeReachingCaps_aPerson_recomendationOfCalorieIntake() { + Person henry = new Person("Henry", Gender.MALE, 99, 92, 30, 28, 27, FitnessLevel.NONE); + Person florence = new Person("Florence", Gender.FEMALE, 20, 300, 500, 500, 500, FitnessLevel.EXTREME); + Calculator calculator = new Calculator(new CalculatorData()); + assertEquals(1000, calculator.calculateRecomendation(henry)); + assertEquals(9279, calculator.calculateRecomendation(florence)); + } } From e89793fbcdc5713df569cfaed174f4ffd7320559 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 23:38:17 +0800 Subject: [PATCH 349/374] refactored the calculateRecommendation to improve the code quality. --- .../seedu/dietbook/calculator/Calculator.java | 69 +++++++++++-------- .../dietbook/command/RecommendCommand.java | 2 +- .../dietbook/calculator/CalculatorTest.java | 8 +-- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 4bada64881..3e820ec9f1 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -247,9 +247,38 @@ public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { * @param person person whose recommended daily calorie intake are to return. * @return the value of recommended daily calorie intake. */ - public int calculateRecomendation(Person person) { + public int calculateRecommendation(Person person) { double requirement = 0; - int recomendation; + int recommendation; + double activityScore = 0; + activityScore = getActivityScore(person); + switch (person.getGender()) { + case MALE: + requirement = 662 - 9.53 * person.getAge() + 15.91 * activityScore * person.getOriginalWeight() + + 539.6 * person.getHeight() / 100; + break; + case FEMALE: + requirement = 354 - 6.91 * person.getAge() + 9.36 * activityScore * person.getOriginalWeight() + + 726 * person.getHeight() / 100; + break; + case OTHERS: + requirement = 508 - 8.22 * person.getAge() + 12.635 * activityScore * person.getOriginalWeight() + + 632.8 * person.getHeight() / 100; + break; + default: + assert requirement != 0 : "The requirement should not be 0 if the gender is " + + "one of the three given cases."; + } + if (person.getCurrentWeight() > person.getTargetWeight()) { + recommendation = (int) requirement - 300; + } else { + recommendation = (int) requirement + 100; + } + recommendation = checkCaps(recommendation); + return recommendation; + } + + private double getActivityScore(Person person) { double activityScore = 0; switch (person.getFitnessLevel()) { case NONE: @@ -295,35 +324,15 @@ public int calculateRecomendation(Person person) { assert activityScore != 0 : "The activityScore should not be 0 if" + "the activityLevel are one of five given cases."; } + return activityScore; + } - switch (person.getGender()) { - case MALE: - requirement = 662 - 9.53 * person.getAge() + 15.91 * activityScore * person.getOriginalWeight() - + 539.6 * person.getHeight() / 100; - break; - case FEMALE: - requirement = 354 - 6.91 * person.getAge() + 9.36 * activityScore * person.getOriginalWeight() - + 726 * person.getHeight() / 100; - break; - case OTHERS: - requirement = 508 - 8.22 * person.getAge() + 12.635 * activityScore * person.getOriginalWeight() - + 632.8 * person.getHeight() / 100; - break; - default: - assert requirement != 0 : "The requirement should not be 0 if the gender is " - + "ont of the three given cases."; - } - - if (person.getCurrentWeight() > person.getTargetWeight()) { - recomendation = (int) requirement - 300; - } else { - recomendation = (int) requirement + 100; - } - if (recomendation < 1000) { - recomendation = 1000; - } else if (recomendation > 20000) { - recomendation = 20000; + private int checkCaps(int recommendation) { + if (recommendation < 1000) { + recommendation = 1000; + } else if (recommendation > 20000) { + recommendation = 20000; } - return recomendation; + return recommendation; } } diff --git a/src/main/java/seedu/dietbook/command/RecommendCommand.java b/src/main/java/seedu/dietbook/command/RecommendCommand.java index b12ee9c277..25c3619967 100644 --- a/src/main/java/seedu/dietbook/command/RecommendCommand.java +++ b/src/main/java/seedu/dietbook/command/RecommendCommand.java @@ -23,7 +23,7 @@ public void execute(Manager manager, Ui ui) throws DietException { throw new DietException("Please enter your basic information first!"); } InputChecker.checkSingleCommand(this.input); - int recommendation = manager.getCalculator().calculateRecomendation(this.person); + int recommendation = manager.getCalculator().calculateRecommendation(this.person); ui.printCalorieRecommendation(this.person.getName(), recommendation); } } diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index 8da928ef09..9a11073366 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -64,8 +64,8 @@ void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, FitnessLevel.LOW); Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, FitnessLevel.MEDIUM); Calculator calculator = new Calculator(new CalculatorData()); - assertEquals(2728, calculator.calculateRecomendation(harry)); - assertEquals(1752, calculator.calculateRecomendation(erica)); + assertEquals(2728, calculator.calculateRecommendation(harry)); + assertEquals(1752, calculator.calculateRecommendation(erica)); } @Test @@ -73,7 +73,7 @@ void calculateRecomendedCalorieIntakeReachingCaps_aPerson_recomendationOfCalorie Person henry = new Person("Henry", Gender.MALE, 99, 92, 30, 28, 27, FitnessLevel.NONE); Person florence = new Person("Florence", Gender.FEMALE, 20, 300, 500, 500, 500, FitnessLevel.EXTREME); Calculator calculator = new Calculator(new CalculatorData()); - assertEquals(1000, calculator.calculateRecomendation(henry)); - assertEquals(9279, calculator.calculateRecomendation(florence)); + assertEquals(1000, calculator.calculateRecommendation(henry)); + assertEquals(9279, calculator.calculateRecommendation(florence)); } } From 3792450b6570ab4002a8ead738bfc73ecec954c0 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 23:45:47 +0800 Subject: [PATCH 350/374] Changed the weight used in calculateRecommendation method from original ones to current ones. --- src/main/java/seedu/dietbook/calculator/Calculator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 3e820ec9f1..4d620ce6a5 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -254,15 +254,15 @@ public int calculateRecommendation(Person person) { activityScore = getActivityScore(person); switch (person.getGender()) { case MALE: - requirement = 662 - 9.53 * person.getAge() + 15.91 * activityScore * person.getOriginalWeight() + requirement = 662 - 9.53 * person.getAge() + 15.91 * activityScore * person.getCurrentWeight() + 539.6 * person.getHeight() / 100; break; case FEMALE: - requirement = 354 - 6.91 * person.getAge() + 9.36 * activityScore * person.getOriginalWeight() + requirement = 354 - 6.91 * person.getAge() + 9.36 * activityScore * person.getCurrentWeight() + 726 * person.getHeight() / 100; break; case OTHERS: - requirement = 508 - 8.22 * person.getAge() + 12.635 * activityScore * person.getOriginalWeight() + requirement = 508 - 8.22 * person.getAge() + 12.635 * activityScore * person.getCurrentWeight() + 632.8 * person.getHeight() / 100; break; default: From a71c91f1e9a96b6936af7660faf016206a1a87f8 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 23:50:09 +0800 Subject: [PATCH 351/374] change the activity stuff to fitness... --- .../seedu/dietbook/calculator/Calculator.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 4d620ce6a5..59e7b77210 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -251,7 +251,7 @@ public int calculateRecommendation(Person person) { double requirement = 0; int recommendation; double activityScore = 0; - activityScore = getActivityScore(person); + activityScore = getFitnessScore(person); switch (person.getGender()) { case MALE: requirement = 662 - 9.53 * person.getAge() + 15.91 * activityScore * person.getCurrentWeight() @@ -278,53 +278,53 @@ public int calculateRecommendation(Person person) { return recommendation; } - private double getActivityScore(Person person) { - double activityScore = 0; + private double getFitnessScore(Person person) { + double fitnessScore = 0; switch (person.getFitnessLevel()) { case NONE: - activityScore = 1; + fitnessScore = 1; break; case LOW: if (person.getGender() == Gender.MALE) { - activityScore = 1.11; + fitnessScore = 1.11; } else if (person.getGender() == Gender.FEMALE) { - activityScore = 1.12; + fitnessScore = 1.12; } else { - activityScore = 1.115; + fitnessScore = 1.115; } break; case MEDIUM: if (person.getGender() == Gender.MALE) { - activityScore = 1.26; + fitnessScore = 1.26; } else if (person.getGender() == Gender.FEMALE) { - activityScore = 1.27; + fitnessScore = 1.27; } else { - activityScore = 1.265; + fitnessScore = 1.265; } break; case HIGH: if (person.getGender() == Gender.MALE) { - activityScore = 1.37; + fitnessScore = 1.37; } else if (person.getGender() == Gender.FEMALE) { - activityScore = 1.36; + fitnessScore = 1.36; } else { - activityScore = 1.365; + fitnessScore = 1.365; } break; case EXTREME: if (person.getGender() == Gender.MALE) { - activityScore = 1.48; + fitnessScore = 1.48; } else if (person.getGender() == Gender.FEMALE) { - activityScore = 1.45; + fitnessScore = 1.45; } else { - activityScore = 1.465; + fitnessScore = 1.465; } break; default: - assert activityScore != 0 : "The activityScore should not be 0 if" + assert fitnessScore != 0 : "The activityScore should not be 0 if" + "the activityLevel are one of five given cases."; } - return activityScore; + return fitnessScore; } private int checkCaps(int recommendation) { From a77bcddd09534cdb7ee0b29a324d5612dca37d7d Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Sun, 8 Nov 2020 23:57:40 +0800 Subject: [PATCH 352/374] activity to fitness --- .../java/seedu/dietbook/calculator/Calculator.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/dietbook/calculator/Calculator.java b/src/main/java/seedu/dietbook/calculator/Calculator.java index 59e7b77210..4b6b45ecd6 100644 --- a/src/main/java/seedu/dietbook/calculator/Calculator.java +++ b/src/main/java/seedu/dietbook/calculator/Calculator.java @@ -250,19 +250,19 @@ public int calculateFat(LocalDateTime startTime, LocalDateTime endTime) { public int calculateRecommendation(Person person) { double requirement = 0; int recommendation; - double activityScore = 0; - activityScore = getFitnessScore(person); + double fitnessScore; + fitnessScore = getFitnessScore(person); switch (person.getGender()) { case MALE: - requirement = 662 - 9.53 * person.getAge() + 15.91 * activityScore * person.getCurrentWeight() + requirement = 662 - 9.53 * person.getAge() + 15.91 * fitnessScore * person.getCurrentWeight() + 539.6 * person.getHeight() / 100; break; case FEMALE: - requirement = 354 - 6.91 * person.getAge() + 9.36 * activityScore * person.getCurrentWeight() + requirement = 354 - 6.91 * person.getAge() + 9.36 * fitnessScore * person.getCurrentWeight() + 726 * person.getHeight() / 100; break; case OTHERS: - requirement = 508 - 8.22 * person.getAge() + 12.635 * activityScore * person.getCurrentWeight() + requirement = 508 - 8.22 * person.getAge() + 12.635 * fitnessScore * person.getCurrentWeight() + 632.8 * person.getHeight() / 100; break; default: From 9d2c93cb0acd22d65eb7f17eb01b1f06518f83b4 Mon Sep 17 00:00:00 2001 From: yuqiaoluolong Date: Mon, 9 Nov 2020 00:06:48 +0800 Subject: [PATCH 353/374] Changed some numbers accordingly. --- src/test/java/seedu/dietbook/calculator/CalculatorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java index 9a11073366..3609ea276f 100644 --- a/src/test/java/seedu/dietbook/calculator/CalculatorTest.java +++ b/src/test/java/seedu/dietbook/calculator/CalculatorTest.java @@ -64,8 +64,8 @@ void calculateRecomendedCalorieIntake_aPerson_recomendationOfCalorieIntake() { Person harry = new Person("Harry", Gender.MALE, 19, 182, 66, 69, 75, FitnessLevel.LOW); Person erica = new Person("Erica", Gender.FEMALE, 20, 168, 52, 50, 45, FitnessLevel.MEDIUM); Calculator calculator = new Calculator(new CalculatorData()); - assertEquals(2728, calculator.calculateRecommendation(harry)); - assertEquals(1752, calculator.calculateRecommendation(erica)); + assertEquals(2781, calculator.calculateRecommendation(harry)); + assertEquals(1729, calculator.calculateRecommendation(erica)); } @Test From b6d8e95877c2254234872c7340f335653acb1e9f Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 00:32:30 +0800 Subject: [PATCH 354/374] Update the Ui component description --- docs/DeveloperGuide.md | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 61bbe1b534..ba2431a445 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -8,13 +8,37 @@ ### UI component ![Ui component](diagrams/Ui component.png) -**API**: [`Ui.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/Ui.java) +**API**: [`Ui.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/Ui.java) -The `UI` component, -* Takes in user command and passes to the `Logic` components for command execution. -* Updates the user about any changes in the data after executing the command or errors encountered when executing the commands. +The `UI` component makes use of the following classes: -The UI has a dependency with two enumeration class, [`ActivityLevel`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/ActivityLevel.java) and [`Gender`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Gender.java) as descriptions of each [`ActivityLevel`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/ActivityLevel.java) and [`Gender`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Gender.java) is required. Increased coupling was sacrificed to reduce code duplicates and increase ease of code extension/editing. +* [`Ui`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/Ui.java): Responsible for communication between the other classes in the `UI` component and with the `Logic` component. +* [`UiHelper`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiHelper.java): Responsible for providing helper methods to the other classes in the `UI` component. +* [`UiInput`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiInput.java): Responsible for reading in the user commands and checking if it is empty. +* [`UiOuput`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiOutput.java): Responsible for printing the outputs. +* [`UiMessage`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiMessage.java): Responsible for storing output messages in methods so that they can be retrieved and printed when necessary. + +The `UiMessage` class has **dependencies** with the following enumeration classes: + +* **Rationale**: Increased coupling was sacrificed to reduce code duplicates and increase ease of code extension/editing. +* [`FitnessLevel`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/FitnessLevel.java): Descriptions of the five `FitnessLevel` are required in `UiMessage#getAskForUserInfoMessage(String name)` as shown in the code snippet below. +``` ++ "- Your fitness level, represented by a number from 1 to 5." + UiHelper.LINE_SEPARATOR + + " 1 = " + FitnessLevel.NONE.getDescription() + UiHelper.LINE_SEPARATOR + + " 2 = " + FitnessLevel.LOW.getDescription() + UiHelper.LINE_SEPARATOR + + " 3 = " + FitnessLevel.MEDIUM.getDescription() + UiHelper.LINE_SEPARATOR + + " 4 = " + FitnessLevel.HIGH.getDescription() + UiHelper.LINE_SEPARATOR + + " 5 = " + FitnessLevel.EXTREME.getDescription() + UiHelper.LINE_SEPARATOR +``` +* [`Gender`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Gender.java): Descriptions of the three `Gender` are required in `UiMessage#getAskForUserInfoMessage(String name)`as shown in the code snippet below. +``` + "- Your gender either F for " + Gender.FEMALE.getDescription() + " or M for " + + Gender.MALE.getDescription() + " or O for " + Gender.OTHERS.getDescription() + "." +``` + +In summary, the `UI` component, +* Takes in user command, ensure that it is not empty before passing it to the `Logic` component for command execution. +* Updates the user about any changes in the data after executing the command or errors encountered when executing the commands as instructed by the `Logic` component. ## Implementation {Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} From d47e8681aa42c7e794770a590fffd727ec560057 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 00:40:40 +0800 Subject: [PATCH 355/374] Update Ui component diagram --- docs/diagrams/Ui component.drawio | 2 +- docs/diagrams/Ui component.png | Bin 10992 -> 35152 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/diagrams/Ui component.drawio b/docs/diagrams/Ui component.drawio index 261a6e6d9a..1d9c26226a 100644 --- a/docs/diagrams/Ui component.drawio +++ b/docs/diagrams/Ui component.drawio @@ -1 +1 @@ -5VhNc5swEP01HJMxYPxxtJ00bcedpuPJND3KoIAagVwh29Bf35WRAAGJndQfh5zQPkkr6b2VtMJyZ3F2x9Eq+sYCTC2nF2SWe2M5ju0MR/CRSF4g/bFdACEngWpUAQvyFyuwp9A1CXBqNBSMUUFWJuizJMG+MDDEOduazZ4YNUddoRC3gIWPaBv9SQIRFejI61X4Z0zCSI9s91RNjHRjBaQRCti2Brm3ljvjjImiFGczTCV5mpei36cXasuJcZyIQzr8XtDvm+fl1/ns4YezSbf0/j68spU8G0TXasVqtiLXFHC2TgIsvfQsd7qNiMCLFfJl7RZEBywSMQXLhmJ7VmqiG8wFzmqQmuUdZjEWPIcmutZVjOUNe1sJ4GgBojr5mmqkRA9L3xUvUFDUvIGmQQdLAwrDTp8YrLNO1+DPmumKq3QXzxNoYI9WWVUJpVB+H4j2A9MqXBUVLRGOTPGoQfGwTbHdRXH/VAwPT8TwnIXEvwzJ7rhB8sBrkTzu4Ng7FccH7HWcBBN5aoLlU5SmwJ2xvYENnj/WjV/yXLj2tHmTqXOisHJtZUQ81sq1XmBVnaSh+7woQcrW3Mf7g0kgHmKxf1vjwLgD2oLWBOvaFBrjmCJBNubN0aWiGuGekV1oZ3pvmfFS2tpFsW7Vq37SNx15piO3GVEFMS1Hu6Aql/3+ONN392uBVospuaMI3LgTSsIEsCUTgsW7ECqDka0wVE0DlEa7m6gIP50vjF6Llr3q/qdsjjc2t/l7ZWs5Gp5ZNvsjyWaP+9eewXeZc75VuA5XozNL534k6Vz7aNJ1uDq3dF6HdMfIfKyZY01AsnWMObDNEglMp9r5kuuGEx+0ICKf4418vF0kH21cfaUI+/LRk+VK9qlS/oNluYPdhvll9HAGB+gxOKse45YeD19aLMDyhJmwpoKzZzxjlHFAEpZgKRShtAEhdRT6QBqw3j4jYxIEcpjOB7D5RD6GAM7+N3C/g/93vIDBrH5CFKda9SvHvf0H \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/diagrams/Ui component.png b/docs/diagrams/Ui component.png index b2cc3b560f7b6e14401502b3a7a57afae4829e47..8c06d167e4bf0ff1365cbfdd40a0504f040985be 100644 GIT binary patch literal 35152 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 Date: Mon, 9 Nov 2020 01:18:00 +0800 Subject: [PATCH 356/374] Update enter user information feature description --- docs/DeveloperGuide.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index ba2431a445..39c1ee3bcf 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -41,22 +41,28 @@ In summary, the `UI` component, * Updates the user about any changes in the data after executing the command or errors encountered when executing the commands as instructed by the `Logic` component. ## Implementation -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} -### [Proposed] Enter user information feature +### Enter user information feature -#### Proposed Implementation -The proposed feature utilised two commands words [`name`](UserGuide.md/####Entering username: `name`) and [`info`](UserGuide.md/####Entering user information: `info`) that allows users to enter their name using the [`name`](UserGuide.md/####Entering username: `name`) command and [`info`](UserGuide.md/####Entering user information: `info`) to enter other information, such as age, gender, height, activity level, original, current and target weight, separately. +#### Implementation -The proposed feature to enter user information is facilitated by `Manager` which stores a [`Person`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java) which - stores all user information provided. It implements the following operation: +**This feature utilises two commands words** -* Manager#setPerson(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) - Calls the method below to set the attribute values of the `Person` object. -* Person#setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel) - Updates the attribute values of the `Person` object. - -Both operation are only executed by the `Manager` class of the `Logic` component. 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. +* [`name`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-username-name): Saves the user's name or nickname into the application. +* [`info`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-user-information-info): Saves the user's age, gender, height, fitness level, original, current and target weight into the application. -Given below is the example usage scenario and how the feature works. +**Main classes and methods used** + +* [`Manager`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/Manager.java): Stores a `Person` object. + * `Manager#setPerson(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight + , int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel)`: Calls a method in `Person` class (listed below) to set the attribute values of the `Person` object. +* [`Person`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java): Stores all user information provided. + * `Person#setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel)`: Updates the attribute values of the `Person` object. + + +**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. @@ -90,13 +96,13 @@ Aspect: Whether to enter name and other information separately or together * **Alternative 2**: Enter name and other information together * Pros: Enter all information at once. - * Cons: Increase user interaction and engagement. + * Cons: Decrease user interaction and engagement. Aspect: Whether to use singleton pattern for Person class * **Alternative 1 (current choice)**: Did not use singleton pattern for `Person` * Pros: Reduce coupling and increase testability. - * Cons: Risk of creating multiple `Person` object by mistake and there might be negative consequence in creating multiple objects. + * Cons: Risk of creating multiple `Person` object by mistake and there might be negative consequence in creating multiple objects. However, there is minimal risk of creating multiple `Person` object by mistake and minimal negative consequence in creating multiple objects as long as the `Manager` refers the correct instance of `Person`. From 2af801f717654e6a4fdee3d8da71bfa854c564f2 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 03:10:14 +0800 Subject: [PATCH 357/374] Update enter user information feature diagrams --- docs/DeveloperGuide.md | 35 ++++++++++----------- docs/diagrams/Enter Info Step1.drawio | 2 +- docs/diagrams/Enter Info Step1.png | Bin 22108 -> 18192 bytes docs/diagrams/Enter Info Step2.drawio | 2 +- docs/diagrams/Enter Info Step2.png | Bin 21962 -> 18355 bytes docs/diagrams/Enter Info Step3.drawio | 2 +- docs/diagrams/Enter Info Step3.png | Bin 22375 -> 18544 bytes docs/diagrams/Info sequence diagram.drawio | 2 +- docs/diagrams/Info sequence diagram.png | Bin 47732 -> 65712 bytes docs/diagrams/Name sequence diagram.drawio | 2 +- docs/diagrams/Name sequence diagram.png | Bin 18183 -> 21369 bytes 11 files changed, 22 insertions(+), 23 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 39c1ee3bcf..361772663f 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -54,38 +54,36 @@ In summary, the `UI` component, **Main classes and methods used** * [`Manager`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/Manager.java): Stores a `Person` object. - * `Manager#setPerson(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight - , int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel)`: Calls a method in `Person` class (listed below) to set the attribute values of the `Person` object. + * `Manager#setPerson(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, FitnessLevel newFitnessLevel)`: Calls a method in `Person` class (listed below) to set the attribute values of the `Person` object. * [`Person`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java): Stores all user information provided. - * `Person#setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, ActivityLevel newActivityLevel)`: Updates the attribute values of the `Person` object. + * `Person#setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, FitnessLevel newFitnessLevel)`: Updates the attribute values of the `Person` object. **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. +**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](diagrams/Enter Info Step1.png) -Step 2. The user executes `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. - -![Enter Info Step2](diagrams/Enter Info Step2.png) - -Step 3. The user executes a command like the following `info g/M a/21 h/175 o/85 c/85 t/75 l/2` to enter all other personal information including age, gender, height, activity 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 l/2, manager)` which would parse the user command, check input validity by using methods in `InputChecker` and calls `Manager#setPerson(Jack, Gender.MALE, 21, 175, 85, 85, 75, ActivityLevel.LOW)` which proceeds to call `Person#setAll(Jack, Gender.MALE, 21, 175, 85, 85, 75, ActivityLevel.LOW)`. +**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. -![Enter Info Step3](diagrams/Enter Info Step3.png) - -The following sequence diagrams shows how the feature works. - -`name` command +_Object Diagram:_
+![Enter Info Step2](diagrams/Enter Info Step2.png) +_Sequence Diagram:_
![Name sequence diagram](diagrams/Name sequence diagram.png) - -`info` command + +**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)`. -![Info sequence diagram](diagrams/Info sequence diagram.png) +_Object Diagram:_
+![Enter Info Step3](diagrams/Enter Info Step3.png) +_Sequence Diagram:_
+![Info sequence diagram](diagrams/Info sequence diagram.png) + #### Design considerations: Aspect: Whether to enter name and other information separately or together @@ -118,7 +116,8 @@ Aspect: Changing attribute values in `Person` object or creating new `Person` ob * **Alternative 2**: Creating new `Person` object * Pros: Ability to write tests as method chains. - * Cons: Creation of many objects, which take up memory spaces and ensure that only the correct `Person` instance is kept and referred to. + * Cons: Creation of many objects, which takes up memory space. Have to ensure that only the correct `Person` instance is kept and referred to. + ## Save/Load Feature diff --git a/docs/diagrams/Enter Info Step1.drawio b/docs/diagrams/Enter Info Step1.drawio index b861d31b57..bff4a7e602 100644 --- a/docs/diagrams/Enter Info Step1.drawio +++ b/docs/diagrams/Enter Info Step1.drawio @@ -1 +1 @@ -7VlLb+M2EP41BtpDCj0s2z3KcXYXRdIE2KJJj1yZlghIopamHbu/vsOnKMlxHMdNnEAXifw4HHJepGY0CC+LzVeGquyGznE+CLz5ZhDOBkHgB+MJvASyVUg01EDKyFwT1cB38i/WoKfRFZnjZYOQU5pzUjXBhJYlTngDQ4zRxybZgubNVSuU4g7wPUF5F70nc54pdBJ5Nf4NkzQzK/ueHimQIdbAMkNz+uhA4dUgvGSUctUqNpc4F8ozelHzvjwxajfGcMkPmfDwky3i6K9t9vc0vX0cV1nw5+1FoLisUb7SAuvN8q3RAC7nsVAk9JIcLZckGYTTjBc5AD408YbwB6f9D7S93yLdmwlP8ExnazolZ9sHSeh5EwOomX7gG6CeLHuN2XeYkQJzzDSo9o3nHXPW+jH+g1iK+R6lRNY64NaYwipsC/Mea/sb82eO6Q3GcI44WTe3gbQbppadXeGOEthg4OmQsf6iAyYYeU0WS7piCdazXHu3GIW/txhFYZOR0kOHETQcsWtIutMLXCva4VqjHJQ1rRoONvq5EiEwLWA7pByEMYwOqw08PecpLexxvOEXKCeppkvAtuABlge0Uv2WKy2o1Eh3MTFwsZSnjeDjT8QiT3FZGaDCbEnF0neqocdBPav2HMDU4h24amMZayPPK8hVisIvcrzganCvMEYl5qQ1dGF3SokKLFeZiacd/oNmILo3o7gxRXJfVqjcufmE5pSp7bH0B/rFkywvhSi7Wr9a6aSlFqgg+VZNL2hJYZkE75Ixal9FEXisQOU5a3vGgyPpw4DMRFusHgk3jSAWnqP1La05Y45iE9RsVKTYEfEQSgVAGy0yijU0Sn5DoPwZerEkVT7tEsle6rQ191VrIIxvUAkXG3OJpDhtyuao2aVLEOzYdiBvD9HzW3RrzCDED1KyHbL6rY/qSByClnJc28PbOpoPHVyd7TVzd0wf8nZw6IzBWWrx1Fm/bX3ZtS7ggk3H1HQdD7anhwqwZkj3QdcHXR90bxx0P+ytLTIIc0v6R9z/Z3LbG41/fEkoI8AY5fefRqJkxUQgfh6BVO7xKnlaaao4xQjk7bHKDmacViI2dU9JMKVAtchlLrsgIp2Re9ZVBz/Q/S/64pt9w/kaC67NjFcnNurY3JtsdpNIk+WNW8nZUPedJNM3mZ+bZfpm4q6MspHCvTRfG5+wFABZvSkAqKTeG432FwSgc3xGr1Lic83oo5atQ69lwkMz+sh/wmneKKOfvN5DDrXpe9lqGJ6o+tK2Vei/ra18/5Tllz2VkWOLKym4igj1+Ktq7Kml1J9a51dGOU74m/j66kWX3f8v0HNKftF1q+tyZ3Thds7O4MALt30AnOy+9XfV3l8dViiB04/w7TVeQx4TxnGjv8/WnbD7SAF1fXvfx9N7xpP9qHHiKQjfNJ6Gn++HQ6FLQk5x6AP/cjhOKf1fiL4g2hdEz78g2gddH3R90L3bX4hnvgJO+jl6btVUUwfbV0ydnOZbdKDN5hRraoOFV/8B \ No newline at end of file +7Vpbb+I6EP41SHseqHLBQB+5lF2t2tNKXW2756XKgklylMRZEwo5v/6Mr3EuLV0KtFuFh+AZj8eezzMTj5WOO4m3n6mXBldkgaOOYy22HXfacRzbGQzhj3FywUE9yfBpuJBCBeM2/A9LpiW563CBVyXBjJAoC9Myc06SBM+zEs+jlGzKYksSlWdNPR/XGLdzL6pz78JFFgjuEFkF/wsO/UDNbFuyJ/aUsGSsAm9BNgbLvei4E0pIJlrxdoIjBp7CRYybPdGrF0Zxkr1kwL8PN/HDP99GPy4expdeOsitu7zrCC2PXrSWBsvFZrlCgJJ1ssBMidVxx5sgzPBt6s1Z7wb2HHhBFkdA2dCU6jDN8PbJddraenAbTGKc0RxE5ICexEs6jD2Q9KaAf6AwDQzoHbUnntxyX6suUIGGBKYZpPtfdDlC3/Lg+9i/3gzSwPn7uosaQOpHMO04LUHV/7VmmzmOPeqHSccdQW8v3cLTMp4cJQvwybpeFPpSbg7oYFrogJYv//lMSwLoNU3GOrorHjdMj91nkzylZa0YKaYrwqa+EQ3ZD/Csq2OAJyavsdMqL6BVzm6ATFAEvxvhZSY6h88ZoyBROUPJufUhiRdjPsuUPXX3VxKA6daU4NIQrn2Veknj4uckIlQsj/o/vU8WVzlhpjS1/tLW8Z1aenEY5WJ4TBKy4qHUYCOqJlUEHsu4PGNoSnkw4j4MnClrs9kRc1MEjr9L1tayKkr3UuMUakSk6B72YKACQ24aUsAqGWG/EhD+DNSIi9rcDUwhTvlGW2pfVzrc0ZWXQIqmphA3pypZ7lWrNAWchmU7PPUxyq7IyRT4EpB1l8a3SIqIpUUtOSj2w8oN5F2DL9Jkodzsk+lSd/aMPsiamu8b81d3n5PaBUxm2TGlXM2DdfYQAVYO6Tbo2qBrg+7EQfdTv7XZWVi9Je093v/v5G2vEP/zLSE0BMVedPdhLJqvKQvEj2NQBorx6+ypFFwsi4VQgY5EdTDNCCuyZK0wFRaMCUgtI15PLkNWzvA1y/rZdiQ9ky++6RccPWKmtVyusaET8WqFmd0Z/CYTvaZazdZQ2T1ZxjmqbpN1nK7rjDrO7jfUcZp58DrOtg9ZyJ2oUvMxVN/s6PNZNJ6p1IpE/pFqt6vR5cUe9hxz9Y1A7xvQsvL/A0IaOXuG9PEiuun+qhJAh4nLZZgleLW6xI9wqHJHM5PcI/xqq3u34Xd5fddG37uIPrscfa5bjz7HPWn09T7exWgsS1ejiG2vRtur0faWpr2ledtbmjbo2qBrg+7NrkZ3vPIPeiZ90RVP/+1PpOpg+bvloLoZes2BtPFrhv7urxlwshixz0KAIikGtMfAmXF4OVxAmWf+2qcNeFH7XmTnhw1NeFAceVn4WNbVBIhUd0NCfqRTt2vVUrx6yF+RNZ1jOaqAta6oek1XVSTuN2uK+P5oG/ffssExtwxvw+yetc8sC0n6B9DWmYXOJT1lQFiKyBWRgFn3JiHGOUjRxThO5YaWG0xDwIVVEVPrNzwHrOa79kzJJT9FEZuy86rzJK7onlc8aLinKyK0Q9GRXXF4VFd8jUM1+7Dt7PDhd+KKzslcsddDZ+jc+FVSm3027FnFz93TT91qykQH8tOOPLQY4sVxxb34Hw== \ No newline at end of file diff --git a/docs/diagrams/Enter Info Step1.png b/docs/diagrams/Enter Info Step1.png index f7b3fc05804081a9604ae2a1e2dbf2088269345d..2f52f139f808d81552d2165a06bee0bf964f79cb 100644 GIT binary patch literal 18192 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 literal 22108 zcmZsC1yodB8!jM{QU)DEs-QGO2*LmZGjx|QeALhk%`ix}2$CY8fPj*UA}t}3iimWm zG$JK6blp9E-@n#f>)y4PnZw!V?09!Q@AE!8UQb7zhKiMnh=_;=iBL5lA|h!9*R2#} z;Csi%{)mW(g2)$c=If3{JG(g$@k^-ud&Ms<>V)y}<(E+97ZRR?6bv=WQGvJpRj2 zN>~(RaKqHu!yXgh11@!pz?YaPxGkX!u0R4*^507=RPrXc1M~EBb1-$V)pQ1>t4T>n z3rj%3?Hig1BW*2yaTRdw=IrVKe$*Z8Trq@KRGhpq?%_840mCs%t@UuioJ6?a>AEel=n0O1*b2X7x|jK{x^iAe~{ z2uuB&;2Y@a@b6tm493w76e`Xy0SCtX&uZXZ=l|^F7oaC$t!JTbPzAbuz|J~j()Z}CWd~-l8({=dd6x} zYSN+}c2bg__R1PkI%r89SDZdh6J+M+fN*x#N7~!Mv|V*@pkO~In54b2qrbYLv7N86 zmyV~Mq?!x{=ir9G`MU&QZCudmcG|9b?r7Bj*FXtpsHUv1tiPS8znQ3=iI_(K=n-Wx zHH?F2AhWv1e9BWdF0g3!iBy}!JXj& zTBa~*Pvtua#yQ$}sQ7Ec&E2HD^u_Ge)g%ltu9{kE4i?TfcJ`nt7#(+{ z7DfUi?T9rn(?EmD+xem0;ZmN4p3=4mQ7{(OywzL-1N@x~ouy5^Wo@7~hHh$Jj*h^_ zCKftc;$Y-ixWc8SkXRcxSv9zsvyZ;6v6PL3zq(_fo@aohsy(X6oUk zC#Gd1DrGJyvDvQ9GN*H+KR8--r zVw!;nTP>)!IcS8Jp>m*#n}>#-xQw2yt|vwhVG?LBZDfM87Y#tmcvu8LkvL^jQ*W%8 znmc&h)=a{}*HGFKR2w0N(6?8Ww()QZ@CvYSb&=4ww?mj2$coxxv7&mw41vB{Mo1)> z15g85Foo1CB(!YRCBXushK9RhkTNiHDYOIHz)QmwYpm~xc7iIK=t^TXJ=|<;Y%r2o zeHT|?X-!pk8#@zcq`1AYtcJFx3C!2d$xX}K#}Vl%Zr~tmVWLdPS|5B2!3Qeo=_?xm zE?jL?pwc=nYIYtve%|I18ooaEo;pZZP%Bd%Q_w79JqLRY9c>ABunQ>LTA)4MCB2~9 zKAs2_eXH)TY5|6hb`n^1Z!s-rI~fTKM#{!lA1dn> z;HphnA$*)bxdHAT4w{;}Xn!3kRaI{@PX}2yFLezl2BiCHikmnP7AkKWF&Xe9W#s0D z6E|@+)z#7rbeD8?bOEMO*7Ws~)^v9h(~z~10^?HGSXz=`cNZws)znM^E#ohW(9uNu zs)>0KvJAA4Fmkf@k=B*ehH3dqh{8N{1HpY+Gqj0>n~c4Qv$L|OB%xZMM;-LBl15k$ zbr*j^>82jGU}b@+iJ{H>(O6j>X{@K7tQP{|tf}vU*3p2fdw_hvA3`6hrls#Hg@wXE zpQ_opIO%Gl#n8@Ll8)j|_HdlEjH9@ty*)w#bi9^?t(l~ghrgqSE_l<$4QlIaVr(3M zwlxtAwD-`pu(j7z)sb>C@V3F|s+oY?WR$^X1b+XwAN}2&!2kbk_7ZTO#h=fKh#*8r zRhSVLwQfaOf2}h^II})XQ%{oW^RM~@HNFQ_0&OL?BB-iv#IdmwcjnJ=GO_E!ER#7T zh2w9Lyw0CNs6o8q^PP<5ev|4C>kr4#dO5bVKJ_uIMD4FPHvjnEYdyVmeWUqLna`Qr z$nO5K;gVamO?wQ@gG35?Vjd;ZI3k9)i{XjT$s3PD#wi&n5{OElNqe)U;A5n2PN@@; zAtb8#cS#`4d>qveEx47Achqx>>P$>NdBy)tgJe=9`!0A}NEBXOe^ewb(WL?VFX1~# zun#(`5>_B2tU|R8|4ZN{B-j^t3knbtieGBi{96pf9V8UFt3BYS0||9zjJodszLAdx z3B#9gv=!+Erzv=E?*CQ2go-5aI!EZyP~LDdx61IBEQyiapg&nMFUw6UP4=gPDzond zyvA*HGL4Mh!@QzZJn{6Ki@#ph5`0`1L&GK@9I2pGBUYl?*7V()-)wwLJV@m#9xFm< zau_fVb$C-+k_$u2M(a7M?}?bovw*2Um+9brV+9;u@729JlY`SCUBy=+=f~5og9;u$ zWNME8yf+=8EsusVzu5K&wGmATLf{JXyMz91Mi@mhUcmMOHIwl0)DLQrCOxbBTb(Yw zx9ch-zOGibzSdDZ^&M#m4)QzQ9V_pXo|b2jZIa_(e#JWcmR5C;P3Y;`!+$&NL6Xwt z+*;kchpO7yl1S|=ad`0A;nK=epRf8>@BNJ<$>~hyyC03^9S^h|tq5_Dc^2P@WaIaJk=@=I0;#}e-scdpYZ6dN2LtFNq6!hmR1eHyn?bW%ASq0qI=I&}lxKat>3OPGWy_Jv?VTJ9PN|M?6vKN6~!f z`F?0k@lI)R?x3pYBZWP^(1Uia;m;XDBjjB7^Et6I#M-#?!~W2+UXfNyg|mbCqs9w# z@_OALA|yEj7g9ix`f_L2it0=&Q7)j;14Z>g`+rs{Lr=?F{(MuR8<{1k*NIyKFIRxR ze_5$sPdq79*<`@eobm8`gOB4#5%S7UF2u@I(8jB5Y0T?TREGDJKaOQK4rv&fcA(|r-%RB9`8udo(s9(sfFv6e){w) ztSl*>H*_K6M4M_fGlfTrn2MMe7^eE6?vv@8q|WQpt*wo_qxYl^FD}CoANkoGBEMQu`ec?|mzgNC0+%#YkLRelFfPF&3i~lyi$`YThA=64Ar=ftt1oK3n$lNw`{c| zU^u!vYoe#|T<2skk8k;~Ww|I}$ggX&^c2?gg9+{bsIz;iF3TM%91#t4)NHgIdo+9H z-QPw^-9PZu#?AV@_O;|*Ui>vEFyp=6%@taq&`QmCtHdXWQ;foO)oaq)jAanVg~V7m z#&|RQU2~KK7~;}=E38{l-2@{TaWk4U6%zfzgdXRt3>7~Tc(J+m@!kXai>)yS(_(gA zzrPV&Lr87Ad;Jgag|l6GK_~$CIE)lnjq(9fA($-jqR{4Fdnh!?7%;p~rfzg~i zN*K*@66p#5jplfzr$vR9*b5Y_v@x?Y*-^amJ6|N^piHryC)w1?el(bay3k{34l)J@ zOA%SGxIbSc`iE!P1AchLPNH+%HeLx_htamWmoVJ(6jVnr^cxaHl%+@b&vY z1{`<%hgUum%i+1nr#!K@KG%oH@cd%-=3Qj9ja&ch$`y=HY5bM#THW%?rsYP}`1mku zq$oASl+u_%Gx;NzYN=9$*?06%SB9DF$tdzReW2J`Ice~gyCDW&W0w#A7JfreX7OsCS z8q>)Tm@nYlBqnFq<7w9K-u*m9q$KS&p3ixI6ZYzkd^19fj83v?TBbwVeNr;Co0a1A(72>#B?w=|9ROu<_lNKGtO@ms)<+HK*nO zVB!wL0daWKt-Yq-u=igCNUB@8eyxKgZ}^4xG;a0wY3L!HVj+(Cf!zI_KIx;$SGAKG z>tH{SYaT;he|A-98IGJ~LSbP){lB|UmOqSnGR1~z(hqWH`1-BhYhG^oOOx-;;6uMuwhV+x`U3riI(@tkRFYgXk6Q z-dnC18oGDWw(0H&#e&1zCHNyFHtOgdI0Um78$lM*!MIfr(oyTs`#6`G`x&Erx#H2l zqv1!PCw5YPD@G$=iyo};nvbn~E7EdM(0KSO2RP~2Onn{Z25*M%jayx8L)r04wtJb^ zoshU4$(-TnS(<8M9{LL;UTL+=s#I$vEVL(aw`s$(IvCFxE8TZ~^<6lw6{%lH)$}bP zrj45oV^U`O@)&+1NER+##7I1ucRrkqe>v)Q(~OyPyJ~!=K>dmAf8@Sd@tAB`yG zKi(s+teBn^>zD?wzSPb!xxiX1<`SQm7S?oQjN;oF0%zzr6MFo8+83U8O~|AKnI^ET zI)~E!tX1%*(Wn*qO#J5M8zKn`BDkV9$uBkNUZ0t8@-pkQ?aUSF*26@Nk8DrKPTPBu zt5089y&ugSFW3y@dJBie-Ks9y>Lnpdf9wCZ5Ld_GySzDUTQ`fJ7U-T9bk$FIVrP$5 zE64o$`c|Y%4F%vNi+_7a@<~jE6qe+X%v_ut&*|w#s41QNW|+(OTK7^HV^;guHJ7}` zxoG181Kqan$bUbfee~ScZ1f)cfdj{d&(?STnxW=h3(;T#=~89W_JeFOyO_z)^V3yJ z8VtqY%s-K4@6a>~&9v{HWOn8ot6nK=TAfE|{7>P;Le?`axgBx_@U+Zchxb+nt>pHtxxM z7W<4whvfP$)4hR&T&mImkZeEt=;wcEY;G`|B0cq)f@B98nLbN*^ESLn&st}=VrHRxOt#~s08Iq7yb zOMLGY)aystV4m~sZd#N>xFgAE3@(t<8qu&pZ?$oB{sCyr*Q)GVY_RArFqMnP@D7<) zFD(@r<>)cJn@^#c}N`(iBv>iNfJRS`CvK!iy8{ZzX{m%i4>ztnv3E z`;;0cXdl9P?`8o#JgH(TXW^(%P$2z14h)GhRb0^S$_xJq2PSO_82kCjlAE zo$gkKj)W@Wzp0FXg$5t9KFJ0Bo*>d_|MyJ&K0`QgGh@8?-vo|7xENfIDMDx z?OqYnM~Y|lEwo-?Jy~&HDMDTV0^%&=TipkX;suWH{HZUT4BBXx3h(YOnTb4m%hIEE ze*n&O^FCRJW;n~-=FfoXQUW-&BZ&w*d8mxD+U*rY>3??qK0vBS%Z+pTqZ{*%_71>r*5CGt-l^n@*RWo;e+#4!_ zd)DekPvn~^ygo>*0Ps5$C7*!nZhrRhEJ<$V#xB_Me>>)Iz3ueLdL>NKGtsL?>JtCq z?(v$T_xOlGl4KIcz0cxqM(f|}#`u}_4d{v(a}BYf)**)uQo%>Q+C&306CGqx{SVeB zYlfz*lI4;u@Bhj42Rm>Xq1CE;OHY2TqqAJ`JKtrpswH<|6`m4xaTc-Q;>yx(~J_3p9T>17BvEpUTc z_W0TjFIBOtwN}hsb9l~&WGw74UT#LNaQlZ>vM393bB*$%?t%wH-1~u{`XfflO48Z3Xj_U`C~+?Pp_%w}SrXwixkUiB3wM3h-hSmjOax*qBSM`no4hyJh@DKbYH{wPW&%q&v1@C$!e zOkHmRo{>^yOx+O07?g@bXjMKV>9?$egi|cX+wR9@Tr6!~ef=JfpM_suV2bT}^T%Gk z<0xW0!c*g29!GDc$iC%0VUg^`D(RWkE6OFwVnk1R>jS@i_v^!2Wfq;nQ-p-}FSk~F zF}k~0bo$~cK0AlENmynk#!RJy{iCMQ&1Ju3x?H^|6y*FB^>R)8d!`~`wl_ZnWS;&9K&Tb`YHJ$<>ReU~%YN${j*7=w8^y*uJ0iA8 zvwqP_EDKj+hfgf74hrS@FbED*m`fyHyh}Hq_6CXb8h;&!ZMd5b;6U223T>6b(eJ{Q z{770sn9wYZnU6{=t#-$s>5yah(<;h4U78#ymuBUM_6jo&)FLlruCDH0>vZV5=ztM* zr+#jRtk}2sz21lhoB{+s$fk%}6B8ss{^8PhDBnGmqQfL_65sj5bTg9vUO+pIw2>-K zXt{qC@}1xuNSoiElA^1ceRqqtq)kZZIX&4Z}JIx)^H}+SS2IM-vG??)4%~RG?MXZ zum?rbA^34SBhu6#iUprdR*ykIVm1r7Cl!0I!{q%}hLLT(k^Om<&3oA&ivwgm=c~4i z2d5kTR`CLK-U)B_zhe}GAFy+x?RDt~g;+Sig4L4x5Yx1!RvK|-?wtP0@9Wljdt~&^ zl2I}cYmw~b=ywXuf!#u%3wj1^)uHqqk&&Jegqpa z3=XWYv(a+L8bEu9Pr3>~XE&Gl1FY-UXd{Q25nhwvX=}fv{g~*8}!`$#5LV0ywn;fDOf2w-r{fA6i zdOfUv3RIV(4C^2Ub-P{hvmMO(aI-PyD>NcCGC#=rYwwF~3F?GDoiejV&t|%$Ox;@H zsC#Db&e8cQboeaJ+K4nT^eTX8XTLkuaD`fWlYii!#qWDxWQjAl!R?OY=hw~N{AjDL zC4*vbI@>wESMw)a>zA33NyWDg0$(&R^sbZf-i@g7nbsm?rRL+5BDa{+mu$opyiqS^ zE=IrdCPl~zc0Ua0h$UJVu^CLTe20MMy&zK#|Ivo{kW}VnAb!RE!rPclE$*kpSy#XK zqRj`zbj-`6nV{xwwKi-&QVEKJVCIUi)nj6yqb=^we5zsZ~$0QNC7Y5Nv8q3|5zdE#04SG^8m-q(!$ zp(!SmoG5ld6=#FX5utq^jGLb)ZM!u`0Nr7^y44Xd zgF>`t!{nZOM(|=;K5R5sMW0)Y`a^z6>Q(cqC%I+GO4@o{3W$MHozp@8& ze(>;b)K#3=vg~YW`KOJkiGIkn zTctyS#FG7EXyVFSxBcxYVESY;ldtYc`z>ae3d%_(j8T%yH zu0*zCR;Zhq`l1F>MEP@(Mn&SYr-e$|Ve&Nv6W07brcQ(RDD|44Y!vp+Z}(S5G=mQp zGYOm=RYu#F$NUOaHZ18?mr0w}Farhf$<>7ROh~KDm)>8AN&};n^!no+fQu_gZn!90 z+ZS{HNAWZNY=KsaoULsuBhb?`V7x|jo=w5+liSnE`MikXuuEx5r{O979N;6ZL zi7j{1^cW^3lXLnO7Bf*$Ff{)zNEildLJ11E4szVIGGD0sZ-4Epb{bHQy*gQBx;@M* zz&wkhh*sLlULGnge|-Di<1m3`nh+r;Y2MY80*8|LEM>3KN(gvUvBbhFxpdVP-v@VlZ;m+I88v?m+ZyY zXLnYA9Q+Ee)j(nzBHcFC+K+D?ekiYLN#*SJI~BOC&U##2ES>mHIZK&0`L7$LtjmX~ zxJi;R&PCTal2@j&S-z~=Em@CLeeb(mkbvaAk_fQEmyeNm96n!_3Ozf)5jf1}3}~6i ztFDv4!&qf^Do@8hzSA=bHouW~j=JmNUk(3uP{9AUgFcQ=bDbZkb8LTz^!S9fMizd` zR>d!*>5fhrQhF&(d9m}URQ!2k(>{EG*BPJ=WVV{l!!8qqIRuPl-rupsS=EaX%bT__#}?cS)_qz_W)9Zt#ibJvBS+2RgV7jW+s zV$-LT|H%v9)5pH=_s)^?b9vlZye6vz`79@R1gk4Zq0NcYa8e$7ZFNnVLIBW1$w?LZ zg=)Kqjq=KtvgDR6rq+Y#m7nheo@GS-IK(o(Mm0D3{w-?OK=PPcZNb{qt=e~akOo`- zmLXi8J1o}qF&nLyrxiMoY==8&*w1J<(gV)R0cFVe7PZ)$S^lbS%3>V=Ww~nNzCFB7 zuDrRUzo-iHmyyVCe!SNFLJRd8qA<_^(eo5ThMgz&iEyL;mDVB!=H}YqI*k z5jN-{k}eWBD8zepIsYD*@=VJa^yJApSZA18hV|B~Jk| z1It=j#C@+dq*et|CVcdZ(~lK$Um`8!@Ib^(gQiCp2klC15WA* z1J5!|73Z0P;eY&*77JbUZEiqzES~1y8$1{=idT~T%Q;wbUxY#a(j-?q*1U_+HEVQv zk5j(yAIv{8-6X=)jF>chsIoX%B)KKXaEGK?F?DJ@@d3xnEB{$kN9ef`jKXN>Y#aOS zRrbNfKK^r*{NIc_-yKD`JpDV%s9a*QOy4u^T_^vUE$Lj)+!FMs!4d6MaWN$8VnRfB z5)mOD=3i(VyR7i*ZWz;()Kn|V{)T!lAp9mAGB3twe{Q{?_}2wW-i>&5mqflt zt-R+NNzIRnc~Wr^Tt__RaTzB`O)YpSCf!8(hi9bRb`NiPvAjADi7KQHM4{w#)(K&T zUth8ccPMp04C$3cjfUytiIC&<)m?}zwLY9os@>|lA_%t_jAY-#G#k4&EIxi+XX4A@ z+ptnrzVb7&KmG<=eD*Rj#6ogwdHijm%XCYyTf=47W^+?X1_$A<37q;{2f%oKw0(v; zvhW)B?{(DeaE`ncs@4>CKDJj>{8^?tE|`f9xmV-z0*C+oyp@^w-5bs|$w@BSM((Zv0++tPwD&=c5Tz&~4Obcz z%AwR5o|BNIWs=_=He5+oq1A6a?88XfOaShE_X%(zUf~u})Q5*l4M+0M@Yka!$4Xp- zzsfG9zkYYzPF8FG>9W-W`n(?j-`%C9&|jE2)WTw7rSI0|~x zQf|J#Kr53!rI|Y3@I%73gJS7hgobFF-0#5`2`-}*Sp<%^ro!JWg=@Jp!TJ2`RI@#c zU%fLy-sv0U)IsHQMmDbAa=xzXB<|G=I@Wo7hp_6(6&c$z$gVfUyuNKw{cWEDZPT6} zu~wOSm~d>t?)&~^yLV)F%sOu((!6bGAqe|xSoB8nRz8q;& zb*|uQf%e(RkF)>N0@VHV&j|_33;dO3!JF)S!j{wAQ4V`dn2akRx;?=6y?lZ#W=v|) z#nbM`@kXol74M$tJlN%?yXQeEQ^k297SC`irGIzFb8^}bu8W^D_!};={WDqRpc&oA zEW(yd-Fl^>SrngrMezy+Pczm$ye6;42Ic9Jx35t}W3@i_&;Zg(EnK9Rn_EPQ`Zc^X7<7$s`zKe`f9Gjf9=kY( zc_=qcV@>GyB;=U7QE|C-KA)|rWus|_madVX26}v*L{>SUXYdZ7z-mBr-*p%4xaA6d zu^j$SrsNfm*Cf`jMLEtEBnTVixK+is`~l!$Ow4R^ZdNYsP1Y%aCUQ~RO9)viez@y}$%5c#-*D>L3{A3YR zt~R|Ny&*j|Wdq=sSb?AqRNlbb>|$;2OkgT!kwBM#GFxe5#y9hqGOa^OE87!P8e}Oo zajj=Zt_tJ?H&#G4xfdStkjX!UC-Ur^qbT^D|A+*FPO<+b(tE7Zy7E(HuwHKydUs{y z$nyy@-TmXmtgSmu6qK^~Bh`(iHs2B1&wfzOkg5;4TO>*6g`c=(1Ceiz-%<+x@uGZj zgXtq=zt@zO?Ot2k+J&tOo;qwpvwcFu^FU1UX{VIVaLA?>`C+t5H2Kehf=|csN@on7 z0?m-8zn1PZ^(QsJWisg*Kk7hpgiJd=uN zK~1?;IlcDd_JPTD9R}pY9Wfx&F3tBwoEprij5>^(f5XCfViI~${OqN@`D__ zoKN#1^&i;Y(x7_H>o%A)y>-<4t^9j6dP_2@kQ@q`G*pRWuIe6d^Cm#|b8iL-t_S07 zi=uWmox6wj!L9Vr_lhDLsmUVoi7IP6cNLDLuu)9Hmdput(9gbL5Hxb%BJD6~svB%A zl~#r-p(EeywSU`%^Tpvmrrn>7W-6yH{Y1c6<_w~j97WjKD4)1oIX`R1LWNT9s(_};}GZ?*zC z)^y=4#P>#e=zp7UR?TOo=upGejoVVMJ9n~4Lm8uFAy-LeN#Y(wX7UIRx^iuLW|ftQ zH4EGINSR-6rXXgK@yx$_D@wM#SB<|&`pbz?1ml4)XI~MW9Ox*BI+l21Mr%Yo)nHVG8%5|j3((*+#(7_a+Z5tqI=Suci>_hdB zb+^xQJQ|T3D)PS{^L<{+p|HOaK8_G+dVl?yZ|dFQm`LPzw|9wZs&n2cix9!()5`NB zk!Kf4PzdZRL1$zqp6AMiy9ytvBQIc`lyP44G~Q%jr-{5Mt~_%liN)~;U4UA2Y&U*> z^eyAfj^QWERQ#FH>tR}c(Rrn*!z0Q<&Xdqc;QtVn9h+k6@KPuBv8@Cm0_ zecVu@1}^0@HTjFXN56TW9THvtIzVp_E(w(xzqlRWS8qWR`!z{n&y1rnEE4NP@s{YY z-Er#A&-dl|k#3L#!Wl_yTgOP>=jd!I+}si zx#WKli&OHak4wa#^`DACQ!k<#!{Fmd8?Hno%3Hjie06xhQa?^Kr)ZM{lrf{RcLzQu zqkcFpE$@D2c)C#dZM09QqU-CBD{B67Uy=_Ce%X) zmR=b2up;(?HzbJS_Q9_ljIYi=^fMqpwt}}9@#wjB6UG1VCvZcb$#b9#oKc+|o*WC0 zNwF793qnA2Uv(# z&-LC^Q7rsC!+uMo4x_kwg*bJ}_G^$w&DUP^GbROZS0`E1iW>ySURK(^Or7)=0HDs7 z8203pVxZSnBDz>6DZ$={rKg}PPkIq z)LfZv-%cEnxVC-swG?0b;IG?!p(*PkNs%5SS~*$Q-xmt2LYzsVlgZ>U$&wmtc@#!0 z;XKK-3x{y2k6!a#%c)~-%dmWkJyY8C65uK)S>tYHrP7Coa)!*YK4;Fea4| zc^#>gyIwh1Dje{QCIb;L9UQO=T=z2GNAgv}eyp3vKoc@j08j9R4}&@6I&tzqDWkok zrMujkNvl~S^ZgIgMA!Uz0Kk1SG;T5)d;9?UGmXE}xwY}PE?=L@mZ?ADeBQCZ-q3Ec z%pSO_HMp82UCBpc4FBLaRNea%mq7xpY@4o|K(m2(UZWg;MWn_|*NY~%{LTF5&yA!N zn!TR0Su4KYW15az-uSed&ldMXH}uGG1mLAzpnQAr0RMR`o#4dmu8xU72UkZbd)F4@ z@n?kCHh^1=AxQ=HGRoBe*8L_?dFCpk{wFyF-)Pq<6tDD)-jkaHQsz2ZaWlm+>63K>&{)FKGl+JFq1DN5q1L1oF?yNcqUO=PJ9` zWe)AH@7Dm*D$T)is92XvD&zz^9a8(lyH`~YvaC%yY9>ch)oMoe$@9ARxAkZylpfH5 z#UITjhz!5X@hF=D5)(q zVM6>7Kw4S99`4wZA)Pzg1mya{ui3VfDp;W@<-oVqf8By0iLCKmHmK$5a-x<*H*^PixcGHb0Q>UStv6;`vJoUL`Sm3ZhOf|FMF` zgy{UFeVX=4Nrx;|Z6)8Yp8nSeoW#ku7rzhH@-Gi^tNOP^Tx7kvRc$-8xm}dqF}}g- zMp7^Q>Dz_WDg2}-|6V5Qc522YKHr6&6phOe_qUylWroE%axK!gLJqtmJ);PMq_k4C zMAp*QlSL7i!6M{jT3n^j0ms}!217Z!u1nfYN{zXJzgV;{ivV??9}xa6=fTvthblvM zg|zR3(D35raQCs-&(3)80M)K!P7z3=3j)^OtddUo9v~v3&_cgRpwq6P-&vo~ii~2> zGl3VxBoMTm5@5`3QdM~biW9LPDb`2+!N&7Zg&HR}(PaQ`0q zVWy9l-3Eo2mC(m52f)cG2alrqkpTY<14pTnGiXihG=hladZJJI8hf<)&wm$3)qE<7 zdbe!<3Ss#=>FN7EHy7G6@OvB6(}smbbXh5QleT1K+F9FB?)P6$+#zw0_jw^lr2=v2 z3tkJmUb#YSS5+6B7hP}j7<#EqiK6$HFW-E6;m|MTXN`#IAoW9-_kf5mL0QmM?6dUM0=1*Wwsd1c zmTCvWy-X9yy1`9itOSYk?IAM|z6Dxl!fM+4WJ-`(DKVC!s+BdtbZ8c)1iSCKeFmf; zM%JOHH3Cl#s%Z?Wfefj<|L*avtINBFmpZ7(i_!hLuRbl)NdL%Z6hT>bzjK*h^+v1^ ze3@CA>$zCln;pdVw4{H4PSqG_#NJ(5?xg;Z<(ONFEQ^wn2Sm@&qLO^Ra1ql%Tf!yG z_C+Sk{2BMhF>gp_rfOVG*B5D0g8{ql{ij!|3Dzaq&Z4^SUiY*1YdG|)zWc9|AMN2q zbuS<3(CUz$Fi`Mtpk~QYTa7TWW^Or@_MOsf% zeT)iad6xUmBr;g9(uYF^XKTU{^^q)(XFvr0dSBlqI`kfyqKNm8A*De!>4L3YCi~E> z#Pt5$k8}GV>g!yE1)At(=6gjdu0I=VBF47M%3lu_F-;jI%T|{ZF$F^;iB&Jh&MDy- zQo}A?OAQ#0#usqWtN);;+tLl$zQPgW^@%?--J8@@85ik$rTpgg_w-JgEtc}lXb+TP zd8foR^2sPq^2GIR+mEtG#O|50RJiPKLu^s>=Q^Y=QDMC=of!keo-ZQs=3RzN4K|2`Q_5 zN!-s2zvcbq6QEi#X0q~4NupG9QU=iT=yCrJ1)I_nGTYR?3(l{LOsiPKN;ZIEPwql= zN3$(E)1Xl7^vqA_#_qdR?<&*5OzP*j-4Rn`03e4&Lbj+mTHT;U6@V5m`G zi@xj^`$Yw91TK<(ZaQ8mk)Gn6b7P#~)FWCBIntLj-ph~17OsRBt%>A0jf@Fi3aNs^ zMw6pqbLO))s!xRw>B*={6$1jLMa!Rjiz>M-8n2AQKAe?;A5Zj$yj1vo8b3J?bt+ByY52g+3wMKxfiTaH9I z26La&uu3A*%q1MM4*AKSQzBZU(P?tWvU4MqMRy{l@kMDmgNI?Wx1pL6pb_<5fQB{2 z=p;`w7crm=i|pD@BQDJOhwi_hUUs8|Gw z?VIau6$?(QF-zB!sXUcD0d-U2<0?Dh^<2{$Ro;?*( zTebX>9P}5_s`tqt5TH!5e_j{eMzZv)(K3L#`r{C zx*q3MSq=802h5e%NsN^tUtXH?56EZ{)90VUa~29_V=EYXM7MqyK3Yk4Su`kxt2_>f zg@^eR;WfwA*b3~w(I(v8-~|U}Zjr#hOFGjoD~Ihdx!-*dGktQnwh>4a3 z?)(}cTKM^v_XAj#5`UJAb2G$Ihv#ccydJhoW#_owWwo>2XyO`KR}2me2!2xWNzIMV zeNlgFBQ_*rO=N0ddyH+XclxP8;To@!E@p2jnRVeR;dS9bHnO`+gZC1LoxEAuuXiJH z?O%${6<~Wz8%~F-i3f`qol^Zh;7|0Ou^U7_<)N>3W_*UvE*<}3q%JEJTULI}KGc}$ zhTVnBtq--CJ8zQ$&nEf_S}+SvK#bhYGLlvI551qg;pj^snry)JU2aFs(x3zF7Ir!0 zv2X4s-hPqTIh62Lj59mye2kOML0jMa=4*jdl}GRd#!|q&xU=$>e;Rp1 z8gdK-(=Pz`$44~Yh&zvh)ID1n2lJ2az-1hg!q^pXzVUA+$Ht4sXf^3c*5EGc51F5r ztHL4~2vFxt>auYCVUE_I6Zu~PCWzo`A|M!t}SY#qV0D}-#tQ;c{+|7I(x zKh4=;n2jVMuxttyGRE#W3s{u?<16Rt?bF}LfNTEZQy=r?DI*f@HKyeu-S)xdYGMuink8=m72Xk4xGD_FZ@!USV&+wFRisIkt7Uz zGjRD?2SR47K*~ZO6yx!8)4@l0eQ+?RGVN2FKsAbd0)g9c2m=nVi2M9l%t7a)n?Zae z`k-?XA3!#)lTdPl-f2vo+FSn~q;X@Gim#bc`=74&0DwO%&n!6r=ux{m)L#cgMr26$ zCLFmKiz&!CHh4%r@z4WK2Z_;(3uhU+r>!euhfAx;D`_U5upU0!xv$uQ`^U!t3{pri z#T%Y`N57&jGlYMbBN8qO0GwmXEEPp$+~uM7_McMaSgtTnC5sdSkvBB2LxeS?DTjhD zoT!~GRVgF*ayTAVqGloRDB%#NAz%Jxy-LfeANlglkoN=?>y_&|6><3NnbV-R9*5iGKw6<44(xmg0oo}yui_vMK?uK7+nmPbk7!ch%G0fA}?hnajt4D zyU8)H)FGlKI&qC5juQ0DlQ4T}Rfq?v%UkCMWBZ%!bRvsqHSOGUqiLrvu7q2RTg#xG+3h<5Bm?v_FIi|5r#OIijB6_)hG;{8WLYJwNIKX@IX*5-EvzUq2Iv)?LI=KG z4;k2;|EUV01Vc0JKP!TlFT}0!NCL6`w{Kf7q*#3kc8(!JVr;)v+40cR5T_to!} za`6bkH~gx2e!m8UaLW4?&n=%19}^Dk5wKqExNXAVhez_;adN8_@2-xD^t?jW6BukvsuAjetw)~dRnD+&9g zQ4cV25MYDG7GoXzZ@P;zgp+IpCli1uBAxvHrpjqjh-O8v24dqOi-hEJ4q5M_6>v^V zpmtCxZlhs|^E84w;*RHx()w02IJ$L>LKsz%146nY=Y)uY#4I=$IHFi6_bX9)ss#Go zP2zM1gn5bx?DEJ9%}iD6Cs!^=6>_BE(Y|vx{LdJAj$b;Prtg$hkl6+SC0Z#s6jcJ^ z{HuKzbI4ht%qQ9!yxr~$&X8uF_K1tr)i}OpuYPau)5YLZfmq8Eh4-mb;B<&JK_tWB z`GI%^$nq;yu<)|KL8^)A@K$_QB8nu<;sH3xe-Krd*M zItLC?l!8-$E7QiStJ)_$=t}j}skJ^9eqjod*E#5h%k9dyV64}*p5&WUm_3gl0^Z_q zD!--%-{38lOIU{ET5#TnpHnM`NTY=4C_@J7cGcgF72$fkx2`FHM%ksIEUD*QQ_W5u zj=r_)mJ8k4eltnR;61@7`|ztlgn3+Xx>0SnN|W_dQp=<6mpBl*{7VUhlD!%wTXqS( zXr6Sf9C;!bbL%aeC2|800i{WIkDO|N=HG8vU;XfNq@(fD*Jo18>r>6|ap2fq&6xk@ zOxp{3vgApLetE;6APCpUl{{B!Sp0ZYB=;JWLFW>Yzol|mQD&DCslfw)vMCK@9KSxj zJWEU<1KvaW`lDJ^_SG`sTIEBAlj~n`D&Qo8l6t z1#WhRG--ZsTU~lmM#SQ*ap$!iF6Dm$x2Z| zK9af7mE#lpF_3IjF6vJ82Z4sU7oDerUtGZ)G>OGe=34iN`u^6F@?SG^uy#G8Db8(H z=2(mLuTbP#+Ks)n&T<7LrU_dm%|p*d*eR!B!f6ohlMncp4?G2k zo4~vu0X12PA{n>;D$>+;iR(tdn=g2Ojkri{d+r=$`nntSuYTZq@9s`!Uzr)RW_~sx zg2cG}dUl)?F^c&^j7W)iT{I>YJx(~8+J0^3YFIH7jaUl>rvCiwc(>s~)E!HoKB*U? zlu!-FwzyEfDV9G?WpeQb!xFJi@~N12|9vZw}B6PY6s3 z@?8Oyu%Av-VauBkeOIaVRXCtUOu-z)!Bcf0s=qJ+kA-e-+L#sQ&Zik>&p(^F`d&S- zMXY?k%s47W@tMj|nTF>QZOKiEFTb~n;0CYt(W>9;w4=&iyA#1nuU@tB!$tmi*lLl_ zhR!uCi^g&XWw`Ry>E1!4PcH*7cL939=E=V?XmiIc?tc|CS||uu^RQgC$T2Q``;vYn ziRP4wTu>>v2DVW6SH74EM!Egjo?r0a3$H=#8g~miO!40d@H{ z;OB}mTJFm0Sx+MKLXOi~R73VYK=Tm0oKOo0KlmSbX<5&XmfoY9$15FVfPUtt`rjo)Xz^?0 zmr&N@{OL0M-@T}5QOL{=9-`3fqqnXQksP-mG(DA?KFYQ>h-<`9Vz%^{(b)A#UC>bp zGrVw%M!L9q=tr!^{L3YjgYXVz7DVF)zJkoMU2Y)U(hw_fa8B=E5cTN+5aW_?ubG8R z%F?$CYeV8>@!mHoS$i2YKH0W3-dUx9JLa(a$g6*8FM=$dPhfP^Clu2&T@m{`|6Vuc zUI0=w?qjWtv{I6CND8yF?~3BTC>tdTzEH0;2{tN^5%xSi?qY;#yCW#Z<^a6Jbu`oQ zp+{+AaBTud{@OEl4=_9E7c>_@0e^~vWz6e&d3>=`sD4(I9kp)315Yl*iw>%<0(k`Y zi`YXSsGISmGmRj=O3_m@kNl`Yk|8(r?Q<9j9uK()PMuy1z;W;!VivF{@m{29BwP&F{M8O{I1zw$|sNeW8icrAy z4&-m*O_$(HSI}w4KKuf%#SG*hpd`e8aq>jq7Xo zZsiXB^@H)P*Uy)HckBVweyWSh@J(Yml%K6{b=8pv0-yi4gRj*4S&d7*d$fa=!N|=E$*Brhz5dd^l{Yd?2|c;n-;(6W$?~Y z4&FHdONcPym-oZ>ABA93cF-qQ*^3l0Sy7>uT$Dn5TD_I8>!)4kQsog#koyGw3OAg2 z{-t%xs3GVq#7eXVKOv8bs29SZU>v}kZwcF|r7mD8rPdFgcS>(5fy{qnFIUL(I)LT` z{q&Z3eKoUNlzKRTM~L@(!8tjfR4KA&&pnU0pO+rZZ>-JNGypF6+orolrB!|MGkBg& zPr2*s3D-rOLvmc}_813#B#K*{h>Vm(PY?|8<9KB3TVXyJaSnVk`<_D?)SjIaW}yIl z$qAL>Y<&BhX}Pf{FLin5Ic9Uu?;4bOInAxf8OWju6E~)OKR99nSx|$2ArpV z?4Ioe8ih0sjYa0}wcC>o8n){unoP}hQd2cC*YRxJG3Chz=}IxnX;CEUcdo_iC7(Ed$dkEG4m$AshGuh;eM4%38cOFjF# zr>aQR-l?iwX-i`0nOgJx63~_f1Yq0hu$1gQk(W_mac}WdS^L2JsZea=(0j5+SR0Kr zKuK*hu@om$B(8M9sTH$h1ttQDjkbxF7}nP;kl21bx_oLIZ5ImEVD-l3v;_Jz0hG1r zuLdaAPUqMZiNN-iM?_KP4UgsU&*hot$l0~dV;6ett=PReBn|KFwHwgdkM~cb(FIZF z)YEesIJ^B#0uA0JQ79WG(ngpsLP)xQeP2A5H7lYl1x7vOe3jw$U^1eS!%#1>38GjO zM~6)N|46c)CaAvs10>bGeE1HYf1w04K;T&{NRD32B(tSqpq8s{I@mYvw#U^dAc`(p zttC|ps)v@s>yt0`(;dx?d3go_-fB2p*!t#q+1ww`^vn}ZV%?Q7EJ3Ff`wTcTatsCX zp8O|tbO@n2mX{PS$$do^4$(SVCGjn(N!hd+cP5JGuH>2Van#`2qC$-ZefVI5ThHa2 zF0b-^=OV{yf(<4QYvzW8Do1O_*aO*J`zA$%6t3cr()MO6f{7w7=FgNMtu*fj2_st~ z0gn*Bie;%u9zBDuvv=#gM>ryEv6(Dv9t^%*d>N#{D$@`?RSbo!HMZo>OUU35}{upv}-FUxjg0-fxvfQq+0rf?up?-iQ z5L}iFlohl*bIG(BIBGG~oEx?TkV#7LXZ;0~dHM`684BXKkShN;7_#zK^K#;jMP}A) zfG=1|1HrMLnkNS*o)EEv_&aRr2E`MkvFXa5LSR4&9SQubq&30}4TL|BbVlPh%dr~@ z`9P<1=x-pL)-7OYV26fjok4(QI|l)?yh)?*wv&xgLazWDwNSk*#KE#{2hFUdZ$MpO zR1nuAj0y2gCl6Ol%v`@q34L~X5}^Pxs$;H-xI?R|BXuKW&kOF5aAf2Gs0`rMY@i^1 z)ucH#@SpTk**`OCJ2X-kes?R@k8vGqEf4g+?+*$nmnROK14$wPhQ20xleZUZLU&;d z-9?uGw}yDwO?rw0W`O8in=6i<70FW!KEZ;5An)bP8 zqzTGNZlM=EMk?LQ0P(Ag+!VH9lST!ObPjY+?ErSaY*mj*5clqOGb&{`-iTHcVkm*K zniyZMK|l$FZ_FwMILXW3SEilAwciQ+>i9VQjre&G`E}=WS+m$hpcvFoz`4 z60++=pGRqCCr~M+!@boAnocP1lGVogSbzQV!WLK55)$7kLI{|YwK$p?TqCx*<* z0YC;gOM;ntr`=MXF`3+@+w#(H;8yqki!V(+4YFrk7noTq*C2aJz{>(_Yb)runSgJF z*QSZ(Yjfzf#^w9O*OICQ6Hkx{r2b8)EKg(%-wxJ~lHt~DcIaQ4tseW>u+e(EbZn;UhnK`{-5HK^i%*z|6{Am*-XP=yMmU91a$(%>H`^NOq?OO&W$v$tvGtd=Z zf=uL|idAr(>+-Qs@e9l_%fFd1Y9pSu@S?PHxJL&jMK5&TaY&Wd(@3|!kz!@D2ynX& zU#Ma66PAxsu7P$Ab+k97DSw;{1Wrn0SmQfqJ;GfYw#8I{f>wTwTv0W*!94(3&+kEWY%6fgL;fVS|Tr zzb_wLZE1y9-_FYtiA=l7cbu)hRdgv<(K+Jg#1=FLV%{@pspqpB%`@#2$Yw;Ta8iiI zy3Cje6U7g9rHcR`5ZEtNtjzx#pA36)8eo4)A|$)`Eeb_R{ z`=ajtZ!ZfLkPMDIM&Xf3^v=fkzxoE;msbJ$l39zyAqc>HF z5xe;{=xr&Jg1PrZcX~f`qVE={quE~|(PhvhA))Vt{5g3fTO%8$tL?5WbCRo8Jd?+| zJXB^Vhc*owQ<0NKzM<%9+2F#cv>8xjOrwGsG4=4BykIO#m!W~Z(PTh{}_fj{$zQDO?|qE!|B`Y2KLN`-l6)LXf~2B z6|X~#cWvhX^rr&GV!%EmmW*+IuWz-{dB{R%5qE{dQGy=m=H0+!f7l5<`a}B^gJmNf zX9)=RG8LMmuy^%}%ZF*3j9Wu5nzXsrP5@`zg#)Mg{^0!OEO$VR(=-k-Kt5Q{jwaF( zkb*#%o_*2;W|sgwGeWf(kAC+Pq0O<+giFGK>iDg69s0$9W9Hrvw`_Nm@GeZl&=+Yn5^%T*cHyOV^5CkX#K#(qo zB>ykyf1^5-qOWN|7TkBFvM{S{``t~zFTj5&vD_yGf3fPJ*|9r_X4a+*f?L$T0OiqH AI{*Lx diff --git a/docs/diagrams/Enter Info Step2.drawio b/docs/diagrams/Enter Info Step2.drawio index 80e41442e8..15c726e7b7 100644 --- a/docs/diagrams/Enter Info Step2.drawio +++ b/docs/diagrams/Enter Info Step2.drawio @@ -1 +1 @@ -7Vlbc6M2FP41nmkf0kFgbO8jjrPZ6SRNZrbTpI9aLANTQKwsO3Z/fXVHAuw4jjfrpLyA9OnoSOcmcQ6D4LLYXBNYpbd4jvKB7803g2A28H3gjyfsxZGtRIafgAQSks0VUQ18zf5FCvQUusrmaOkQUoxzmlUuGOOyRDF1MEgIfnLJFjh3V61gglrA1xjmbfQhm9NUopPQq/EvKEtSvTLw1EgBNbEClimc4ycLCq4GwSXBmMpWsblEOVee1ouc93nHqNkYQSU9ZMLjd7KIwj+36V/T5O5pXKX+H3cXvuSyhvlKCaw2S7daA6icR1yRrBfncLnM4kEwTWmRMwCwJtpk9NFq/83a3m+h6s24J3i6s9WdkpLtoyD0vIkG5EzgAw3Uk0XPmX2PSFYgiogC2+rQ7gJJgugeHYSSDs0dT1BKvEaYrUK2jOCptr82f2qZXmME5ZBma9d/oHLDxLAzK9zjjO3Y91TIGH9RAeOPPJfFEq9IjNQs294NRsGnBqMwcBlJxbQYsYYldg0Jd3qBa4UdrjXKmbKmleNgo+8rHgLTgm0nKwdBxEaH1YY9PespLOxRtKEXMM8SRRczYzMPMDxYK1FvsdICC420F+MDF0tx2nA+YMIX2cVlpYEKkSXmS9/Lhhpn6lk15zBMLt6CqyaWkibyvIJspUj8IkcLKgf3CqNVok9aTRe0p5SwQGKVGX+a4d9xykT3Zhg5UwT3ZQXLzs3HOMdEbo8k3+AvnmB5yUXpav1qpBOWWsAiy7dyeoFLzJaJUZeMYfMqCpnHclScs6anPTgUPsyQGW/z1UPupiGLhedogaHVh85RbPyajYwUM8IfXKkMUEYLtWI1jZRfE0h/Zr1IkEqftolEL7HaivuqMRBEt7BkFxuxiYQ4TUp3VO/SJvA7tu2L24P3QINujQgL8YOUbIaMfuujOuSHoKEc1/bwtpbmAwuXZ3vN3B5Th7wZHFpj7Cw1eGKt37S+6BoXsEHXMRVdy4PN6SEDzA3pPuj6oOuD7o2D7pu5tXkGoW9JcMT9fya3vdb4+5cEk4wxhvnDh5EoXhEeiB9HIJl7vEqeRprKT7GM5e2RzA5mFFc8NlVPSjDFjGqRi1x2kfF0RuxZVR2Ar/qf1cU3+4LyNeJc3Yx3Z6qpTtJ9SaTO8saN5Gyo+laSCXTmZ2eZQE/syiidFO6l+dr4hKUAltXrAoBM6r3RaH9BgHUOzuhlBvxuMvqwYevAa5jw0Iw+BDuc5o0y+snrPWSnTc/EVsPgRNWXpq0C8La2AqDDWEeXX/ZURo4triTMVXioR9eysaeWUn9qnV8Z5Tjhb6Obqxdddj9eoOeU/KLrVtXlzujCbZ2d/oEXbvMAONl9C7pq768OKxiz0y+j2xu0ZnlMEEVOf5+tW2H3ngLq5u6hj6efGU/mo8aKJz9403gafrwfDoUqCVnFoXf8y+E4pez6CwHjfxzivhTal0L7Umj//6EPuj7o/gdB1/l92XX/n/RD9NzqqLoCtq+MOjnNV+hAmc0q09QGC67+Aw== \ No newline at end of file +7Vrdb6M4EP9rIt09tAIM+XhMQtPVXqut1JO6ey8nGhzCHmDOIW1yf/2NPzEfbdo0yXa75IF4xuOx5+eZwWPRQ9N0c0mDfHlNQpz0HCvc9JDfcxzbGQzhj3G2guOObMGIaBxKoZJxG/+HJdOS3HUc4lVFsCAkKeK8ypyTLMPzosILKCWPVbEFSaqz5kGEG4zbeZA0uXdxWCwFd+hZJf8TjqOlmtm2ZE8aKGHJWC2DkDwaLHTRQ1NKSCFa6WaKEwaewkWMmz3RqxdGcVa8ZED45+d7/6/t1P3y9yj7/sfNKL8szqSWhyBZS4PlYoutQoCSdRZipsTqocnjMi7wbR7MWe8j7DnwlkWaAGVDU6rDtMCbJ9dpa+vBbTBJcUG3ICIHuBIv6TD2QNKPJfwDhenSgN5RexLILY+06hIVaEhgXgGS0wJSP4FpJ3kFqv6/a7aZkzSgUZz10Bh63XwDT8t4cpQswKc4C5I4knJzQAfTUge0IvnPZ1oQQK9tMtZxtuJxw/TYfTbJU1rWipFjuiJs6hvRkP0Az7o+Bnhi8gY7r/OWtM7ZDZAJiuCfJXhRiM7hc8YoSFTOUHKoOSQLUsxn8dlTd38mSzDd8gmuDOHaV3mQtS5+ThJCxfJodB/8ZnGVU2ZKW+t3bR3fqUWQxslWDE9JRlY8lFps9OpJ1QOPZVyeMTSlPNjjPgwcn7XZ7B5zUw8cf5esrWVVlO6lxinViEjRPezBQAWG3DRPAatkhP1KQPgzUGMuanM3MIU4FRltqX1d60Dj6yCDFE1NIW5OXbLaq1ZpCjgty3Z46mOUXZOTKfAlIOsujW+ZFD2WFrXkoNwPa2sgjwy+SJOlcrNPpkvd6Rp9kDU1PzLmr+8+J7ULmMyqY0q5hgfr7CECrBrSXdB1QdcF3YmD7l6/tdlZWL0l7T3e/+/kba8Q//ktITQGxUFy92Esmq8pC8SPY1ABivHb7KkVXCyLxVCBjkV14BeEFVmyVvCFBRMCUouE15OLmJUzfM2yfrYdSc/ki8//hJMHzLRWyzU2dCperTAzmsFvOj1MGeeouk3WcbquM+o4u99Sx2nmwes4dMg67kSFWoSh+GYnn0vReKZQK/P4RyrdrsdXF3vYc8zVtwK9bzzLwv8niGjP2TOijxbQ7pMB3QiOt4XlIi4yvFpd4Qc4UqHxzCT3iL7G6t5t9F19ueuC710En10NPoSaweegUwaf9/FuRVNZtxoV7K95LxrM/6kId5cz3eVMdznT3Yh2QdcF3S8QdC8+yx/0MPqim53+jz+KqhPla8tAdSF08JNof/dHDDgLx+xrEKBIjgHtCXBmHF4OF1DmYb/xRQMOG5+J7ISrDQ+Kk6CIH6q62gCR6m5IzA9z6lKtXoLXT/crsqZzLEeVsDYV1W/n6orEtWZDEd8fbeP+WzY45pbhTVx8Ze1zy/Ik/Q1o69zyRpL2GRCWIraKyMCsryYhxjmeostxnNoaWm4wjQEXVj/41is8B6zmu/YMXBIdsSm7bjhP4oloVHOg4Z6e6Hk7FB3ZE4dH9cS3+FO7C9vODhd+H57onswTXdc790bGr5bY7POha5U/tKebonrC9A7kpj15ZDHEy8MKuvgf \ No newline at end of file diff --git a/docs/diagrams/Enter Info Step2.png b/docs/diagrams/Enter Info Step2.png index 4d7c1acef13cf5d1f507e9e570251421fa88cc76..88f5526eec563c392f9745aa66e14a8f1874cbe6 100644 GIT binary patch literal 18355 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 diff --git a/docs/diagrams/Enter Info Step3.drawio b/docs/diagrams/Enter Info Step3.drawio index 9af0567ab0..eea9b1045e 100644 --- a/docs/diagrams/Enter Info Step3.drawio +++ b/docs/diagrams/Enter Info Step3.drawio @@ -1 +1 @@ -7Vlbc9o4FP41zHQf0rFsDPTRhDSdTtJkpjub7KNqhNHUtlxhCN5fv7pbsoEApSl0/ALSp6Mjnas4h15wna1vKSzm92SK0p7vTde9YNLzfeAPR+yLI5VE+h+ABBKKp4qoBr7i/5ACPYUu8RQtHMKSkLTEhQvGJM9RXDoYpJS8uGQzkrqnFjBBLeBrDNM2+oSn5Vyio9Cr8U8IJ3N9MvDUSgY1sQIWczglLxYU3PSCa0pIKUfZ+hqlXHlaL3Lfxy2r5mIU5eU+G55/0FkU/l3N/xknDy/DYu5/ebjyJZcVTJdKYHXZstIaQPk04opksziFiwWOe8F4XmYpAwAbojUun63xv2zsvQ/VbMI9wdOTSk/yklbPgtDzRhqQO4EPNFBvFjNn9yOiOEMlogpsq0O7C6QJKnfoIJR0aOp4glLiLSLsFFoxgpfa/tr8c8v0GqMohSVeuf4DlRsmhp054ZFgdmPfUyFj/EUFjD/wXBYLsqQxUrtsezcYBR8ajMLAZSQV02LEBpbYNSTc6QDXCje41iBlyhoXjoMNfix5CIwzdh2c94KIrfaLNfv0rE9hYa9E6/IKpjhRdDEzNvMAw4ONEvUtTpoRoZH2YXzhaiGyDecDRvyQbVyWGigQXRB+9KMcqHWmnmVzD8Pk4S24aGJz2kReV5CtFIlfpWhWysWdwmiV6Eyr6YL2lhxmSJwy4Z9m+TOMvzvEgu+igPnGa8ckJVRejCbf4DtPMLvmQmwa/WXkEjaawQynldyekZywY2K0Sbqw+QiFzFc5KjKsmWnfDYX3MmTCx/z0kDtoyKLgNVpgaHW6OYqNX7ORMWJW+AdXKgOUuUKtWE0j5dcE0pPZLBKk0pttIjFLrLHivmwsBNE9zNmTRm0iIU6T0l3Vt7QJ/A3X9sW7wWegQbdClAX3Xko2S0a/dZIOefozlMPaHl5laT6wcJnVa+b2mkrvZrFvrbEsavDEOr9pfTE1LmCDrmMqupYHm7whA8wN5i7ouqDrgu6Ng+6bea957aDfR66hg5/+M3notcqVKGAYXq4shGLGGKZPrkyjCxYpXlIejH+QRLL0aAh0oNc1ylSeyzCr2yNZHUxKUvAIVTMpwpgwqlkqatkZ5uWMuLTqOgBfzT+q52/yCaUrxLm6Fe/WUlPl011FpK7yho3irK/mVpEJdOVnV5lAb9xUUTol3KH12vCErQBW1esGgCzqvcFgd0OATfau6GUFfDEVfdiwdeA1TLhvRR+CLU7zRhX96Oc9ZKtNz8RW/eBE3ZemrQLwtrYCYIOxjm6/7OiMHNtcSZir8FCPbuVgRy+l/sF1fm2U44S/j+5uDnrsfr1Aryn5oOdW9eXO6MFt5U5/zwe3mQBO9t6CTb33nw4rGLPsh8vqDq1YNRNEkTPfZetW2F1SQN09PHXx9DvjyfyoseLJD940nvp/3h8OmWoMWS2iC/7L4TildP9CdA3RriF6zg3RLui6oOuC7rf9C/HK+3/SH6Ln1kfVHbBdbdTRaX6F9pTZrDZNbbDg5n8= \ No newline at end of file +7VpZb9s4EP41BnYfEuiwbOfRR5yiSLDBpkDapwUr0bJQSVRpKbb31+/wFHUkdlzbm6Tyg8IZDoecjzMjDqOeO002NxRlyzsS4LjnWMGm5856jmM7wxH8YZyt4PSvbMEIaRRIoZLxEP2LJdOS3CIK8KoimBMS51FWZfokTbGfV3iIUrKuii1IXJ01QyFuMB58FDe5j1GQLwV35Fkl/xOOwqWa2bZkT4KUsGSsligga4PlXvfcKSUkF61kM8UxA0/hIsbNn+nVC6M4zfcZkH3b/H1TzIPB5+2Xn9PH5J/Bl/sLqeUJxYU0WC423yoEKCnSADMlVs+drJdRjh8y5LPeNew58JZ5EgNlQ1OqwzTHm2fXaWvrwW0wSXBOtyAiBwwlXtJhbAX1uoR/qDBdGtA7ShDJLQ+16hIVaEhgXgGS0wLSIIZpJ1kFqsHPgm3mJEE0jNKeO4befraBp2U8OUoW4JNfoDgKpZwP6GBa6oBWKP/ymRYE0GubjHVcrHjcMD32gE3ynJZCMTJMV4RNfS8ash/gKepjgCcmb7CzOm9J65zdAJmgCP5FjBe56By9ZIyCROUMJec2h6QowXyWGXvq7s/I/1ER5npXGUpbl+2TmFCxMBp+R39YXNmUGdHW+lPbxfdogZIo3orhCUnJigdRi3VePZ164KuMy3OFppTvetx7gTNjbTa7xxzUA5ffJWtrWRWfB6lxSjUiRnQPezBQgSG3y1PAKhlhvxIQngzUmIva3AFMIU6FRltqL2od7vgOpZCcqSnEzalLVnvVKk0Bp2XZDk96jLJrcjL57QOy7tL4lunQYwlRSw7L/bC2BvKuwRcJslRu9slEqTv7Rh/kS80Pjfnru89J7QIms+qYUq7hwTpviACrBnMXdF3QdUF35qD7rt/X7BSs3o8MoVe/+t/Ii15BLk2xh977tYXQCBSj+LFq0+gdm+QXlAXjB7IoB8W4btArva5WcLFcFkEFOhbVwSwnrMiStcJMmDAhILWIeT25iFg5wxct62fbkfRcvv5mn3D8hJnWarnGhk7FCxZmdufwm06PU8a5qhCWdZyug406zh601HGaefQ6zj1mHXemQi3EUHyz88+NaLxQqJXZ/COVbnfj2+sD7Dnl6luBPjSeZeH/DiLa8w6M6JMFdP/ZgG4Ex6+F5SLKU7xa3eInOFi547lJHhB9jdW92ei7/euxC743EXz9avC5djP4eGFytuDzPt6taCKrV6OO7e5Fuyua7oqmu6Lp7kW7oOuC7jcJur3P8kc9jO51szP4/4+iVweWgeof+0c/iQ52f8SA02DMvgYBimQY0J4AZ87h5XABZR72G1804KDxmchOuNrwoDhGefRU1dUGiFR3TyJ+mNvUvnFQ2PdrmK5IQX0sR5WwNhQ1bufqisS9ZkMR3x9t4+FbNjzlluFNlH9l7UvL8iT9DWjr0vKuJD1jQFiK2CoiBbO+moQY53iKLsdxamtoucc0AlxY/TCzXuE5YDXftRfgkuiITdl1w3kWT+w7NQeq15n7eqI32qHoxJ44Oqkn/oo/tbuw7exw4bfhif3zeeLQu/SujF8tsdmXo75V/twD3XRQT5jekdy0J48shnh5WHGv/wM= \ No newline at end of file diff --git a/docs/diagrams/Enter Info Step3.png b/docs/diagrams/Enter Info Step3.png index 9b263c15233b0ba96f0ba9b6bd3de8b25ec1771d..c53caf4171076601668abf97e5667a64097263ed 100644 GIT binary patch literal 18544 zcmeIaXH=72*ENcW3Zg&&L8XKaihxLy7Fv+rg-BNr>79U-fG9!$1?f$C2MI0orWBEG zKnNX?UII!dA#g6d@8@~HG0y)p&N<`#<8YAdeeJ#W+H1|V=FAnYtF3bV>g}r}BqZ0> zRH6DLB%~PN{{-^Oz`viKeG4QZVIxt4Dm;E^flI&s-Kfj=5Ni+laa;K9BhKOndAadY z+`nTVDNE(L-r%yg%;nMr-D3j@dzbZC?R_+tLU3&$&AjvK;}f>B zFZZ4A@w`{ky03ZJK>4!sNW2efp~>f9)^o;EOLz>;KYu&_IBS$FN@|_&Ez$+fn6>lw ztN)BLreMpdrsb6VpIPKLfIpeeT_X8Eru~pW!Xd>kJgoHZV}Os)86+Hy`{pDh|2rAY ztN*px|GLKCOZdOn>HoV?L*5dKe%^a2_yGm8q(@%=uQ&1U!I!?M^2pNIy$0{=5*1y# zhA6*ivQv=#-aVPgb>+Z+i&@zE_UCoda`j}66*El@}-k+LvjK7J)KgnvmQ0iI+A{@6=qI0DC@l3xf0?f zp~eHt%-Am#ErpdEMKTEJ_azD#1RdB0Eh9YBPMR}0Ob9!(Epmn%*tvpGYA{1ha0Ym( zZJ^CxLicM3a`oM3hYV(r`J2o1AlZlY%I|OQE3Ax^owWKNQjgX;OOLLPN>tDeKrmvf zqg|;td2_lVk}U8(HBY}>t#KIrB^Ye}>Fp@L78H$ar_+9zy^;B>BNR?NBg`JQuHUkZ zRX)R)pWO~$-3~e6~ zssGl*&9doUo3?d~c7-=?SsP++&mh#Q$s+TzNMkXTzcV(5IQfL<;L8`@AAh>Rg+`xV zR_qeeeW)HStz&geppH!gh4z((H`U0)PNwKF?RwS2H-tN6^8)6%k1f0~Iojs94>W}R zGCGM*Dd+Z_>^1;?g8U^Y8H6*=VBNA-}G2KR^s@(Zvp( z9QTbjtY|;>T$#oEFTx8KC0DD)tQ@*_v!qg>^z`OjCT z^s}*`xS%%T!Bk99ZC4G$oPCho>Cr-Dk#@F?%)z*I4D$4Q7=)hae{%Gva)}YQ0(cY_7UN*O#}an*mTkb3^q|vjDd(8S zL(P8dc?^6UouBUP+GJC?&>I3vI*Xk0oH+23rrE& zj3HGaj(MXZkrv-~laUr>hWPADRfBIKYY5^rZ@G9uF&a6RV|e@P{`^Ntws$w(73mqB z zlhZ8NYK>hj>=)O20V}hmDf^I2vt^ZA%ZS2{9{OB?ygICN`>Fj5N+_7~3uC__= zfp{MA(F`8sDSOi6`T{KD_H*A(Y=VkKeYXaExN`}EB@Na{Ye3o8TMk7?ESPi_%6oG75W9=k9iJn+F@;Y!~J3%L~p~@ zgvV_;Jg||_dTh>eM}D*VC_*==nbS9!sHP)dP@Ymcjr#rJVUV}1Q$vXSFzjrZ-zlx( zn1`?Dvg|3B>_c_F6vw*gdbKImAT#PVEk=xQz;v?;^rcymnh9UTw9oIK3Wel4bibCj zA@K1APg|~>e3|3JON?eTGe+^5-LiS9L=Dyt=HQ>Hrg@fzbQNeHTOqIKv1@p7qTi&>{=vmp_i8v{_-Q64!()_ z$3~yLcXvpxoHgLLf|&a+D4c++E3LHdF5y(k*mmV+apiyq*CG?$u3{6$&Jbi(#~=7O zq_!Xa+A&SClNd=+G=+MZ$ReO#(@ghUo)pw5@d&pPedzz1opiTEUD-xo%)kprSM@TzS`nku zbBOgH)nU!I#ld&?dZqX=zA~-T{?GlNNH0qZ(koN`Y4JZSInSQ@E15y9_(^Q5(H&6w zK}8BZ1FaRe)W$&_zXSV+OF5

  • #cGI%$2!xz|S(fb%wW!tHAja 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 literal 22375 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

    ?C`6>hWp#eU}! zu9V-YC^xaO06wU|SHX9g8<7k?J?NU5v`f^E7HAlfE z_8^#oCZgWnJuawqgh%=$sRWwr5z-ZZVDe+INMu(VNXW88!GE<*dQe>fu|GY7se%}# z6?}bfyS9851a%jQ5mFMrSd*_@3V%R5{AeE9>zjk9HF9Q{VLGz+%Xg$n5>3r$Fi>r<6lQ+C;(t~N(*?>b|LPBpar!us1&>l;&m(2-g@2(W zBZC4Q7zMrKgN@=(4a_AU#(DSIu8pwr{(qZJ3Nl0>Z+uf-!~h3^vhXJi-C!c=$6mfm z?-KM#axh|E+$-&VME+RE$CtO;kcA|n89a(sFx%r%D*10%C^O<6u6wm9OQJRe!qTl{ za_dHW4UmFrNec-rk;kY)b(Q0cBWBMym;Tt&x$sEAFW!dx9OC6KY%OhAjT`|{6r|L?>hY&3j@iL9D1g7q$L zFSg-3>#T;8!5cDVK$i;lj!lqMwM7is_gi^HFL$dbE$1?A@^-tzuh`Y0@B}wpWi>U~ zIcf6_cTqWaujMI~H>vFv!$dk4RrAG3(KQi9Z%0}>;5Pd@W0}h;l3Sg#C|xa890$OY zWk>PN_po7DogB|eDm`-#vZa00rQkr>xTQ~ghm05e+Z|Cx%a>a%b_Lvg+EspQD0V%% zwjz#zK7GzXv>&k}N9!P#ZA|!^6X?qHyYi>UU(kX5z@llb(MDD7W${Yl7X;U41@FeN zZ%5wWiT@c({#$K3r>&aeHST-oRODudrm#-KpJ_Q(vSI4ZJMz_X7(1S3-5?7JCXEdF z#c9gQsYtBFXn@hXRzA9$X-Ps&GS5Z59n2Lfw=0A@bnd}6k`6(vV5d$`=3440q6}sB z@oty}afRd|OHS|eY#ZAaJ!n7?yo>$CS*u^cBj1Pjvh&REupDTNb$+*?k(yXk=ETkxIVezNw% z`937TFaPkdu=jNlO=0?IEmQ#Ny+~LHnD6}*qSr}WnWf3PG(pU##KnFwvFVwy6()#o z?(5d{X$srnJTjv6tY$(=i$Kww*PGLP--_U_%3v|QYVVUs-Ack;Y^)jaM{9|BC3GiB zSN@68$MhfW&Ks-~&5P)n3YV1* z|M<1>$koJuI-A8IG?P@R;6KNM&9tRtm~aNt1*#o3Zb`ROv!6kQ0c2j6k7bpFi_bg> zBlTyAd)1W+f0la0prF_2SE&S^Vq@`3UZ7OMN@!|r{pi-(guy^r=j(B)x$|v#d4eAw z3zpXtKoF7uzDY*J;ii+Iq7268f?7kg@&-r``_@0 z=_7|C>B|atuYHnIGW;zIQ$ZzA(!}-}Hf*cGF4IRoY_7q6U50{8^IrFzP;ygNz5O3D zMU{;FlY5`FoICd0o>R;IR}+2WKYv=cZ4Oeo{JFWpl++Ch)^Z7oJ9OUPe3o-9GS{Qf zoXp5+VUIk5P}-)P3>@Ny9X zN6m8fpD0lmWofEZD_L#z>h94$?750(5;If;d_uLqZ&a5_E~tDF&Q0zo6Z&@ zOBAU)!S;!u%HiusL_ov&wlj})kyJ%R@G5HE{-aMIF<2jXC=I9oP!)tyq{(NKAT88x z2*SaZ0=f8ePLNM%2u7N|^QVT08aOXXcw1=(Y|g-LwT`ufr*OLSL(`Qmc!;}JDnKsh z`j*T^had%}H6%v;fT>+FsAM7XLDc8Tefxl$_yp6k0iN#31X0DY={N{f(p<~`b&i7M zDPn3Ul?Mp)zO)ZWUAGYgEWKw_VFz_@Dv_xPqU?M6eiF3Sn+~GT9=7=6Fe0!d4KsZ` zC=m2kp%x$kPXjQZw~hLeHaO7Q80;D-7gS}L!|EB5sy?L4H`W(cWs@;T7bJAv=>X(m zw3M0&V)3bn#Yebm18ZcyT$-H)qB!x44dF~95$FQj@}U@&1|@^K=&6vcTS0Gus3~E1 zs)!WksX5m9pdksm>VI>DmPF4?Q0a%~f9ej+tbdZE8YL>&p78FB7$GLJS_uA;ZJbGs I)_y7f2Lb(QV*mgE diff --git a/docs/diagrams/Info sequence diagram.drawio b/docs/diagrams/Info sequence diagram.drawio index 3a9c45f381..c90ac4adac 100644 --- a/docs/diagrams/Info sequence diagram.drawio +++ b/docs/diagrams/Info sequence diagram.drawio @@ -1 +1 @@ -7VrbcuI4EP0aqrIPoXzHPAK5B3Yym9rNzLxsKbYCqjGWSxYB5uu3Zcv4IkMMY8/MbjYPQWrLknX6+HS3oGdOlptrhqLFjPo46Bmav+mZFz3DMDTTgA9h2aaWoeWkhjkjfmrSc8Mj+YalUZPWFfFxXBrIKQ04icpGj4Yh9njJhhij6/KwFxqUV43QHCuGRw8FqvWJ+HyRWl1by+03mMwX2cq6Jq8sUTZYGuIF8um6YDIve+aEUcrT1nIzwYEAL8Mlve9qz9XdgzEc8iY3fL3bRK/rraHPJ0/al5u7e+Pq/twYpNO8omAld9wzR7fhC53Q5RKFvnx2vs0AgW1EorlaBlPyggMSQm8cYUaWmGMGVwJpfsht4/WCcPwYIU/cugaqgG3BlwH0dGiC9ziCW9iuHwQoislzsqoGFoa9FYvJK/4DxylJhJWuuFhpsnO+MKrASKxeMeN4UzBJoK4xhedkWxgirw6lzyRpLUf21zkFdo5eFNxvZjciSbv5burcM9CQzjnGUa7qqKprCoBGlIQ8eQZ73LMvKh6ijC/onIYoKPpIxe0wZRqjaZfRdOvAVLE0Bp1hOVSwxBvgF8dnQHl4o1nPgJm1FflNwRj7IA+ym8N4mVuLtEYBmYfijcAvsKlxDPwn4Xya9C4AsDEO/ZEQKeg+B9T7KohOV6GPfcllcAXbfpLTJZ3Pe1l+0FscsTk+OFASTGzwoFMZDhCHF7EstTU+krc+CCoWyGCXyOBUnBzTFfOwvCf3M6CEtoVhkt97V3G1ulVy0qTz5RTa7e90VplanZQ+IBYLPr1HFbXcqozqDWVU6+rVN3XVST9HRnd0aQ5n+c3ZIfeGjrqdYWnsk9EHRj0cx9gXicQZgX8ik4NFZuI54FPsVFtAQx/ApsRGrlzR8LIGvMpXyaVAjO4ZDloKsofPcZRArEmhVgW64D0BLYFUbiRl+JlyTpf1sruX7m8KZ+bIN4Wz4KfMJ9+ppXolstqO0UhNlYlM/Y2JUhSUiVrTTlOhUsRgrduQcAJRNAaUaDgDToHTz1pzOt4QngTXviF7SXTtDw1T9i82WSAWnW2hU3jlD+tl6oM2Im/3BBpUxaIpgXRLc/pmea5qLtw1h6y6+PsneZ+xVzedSvAd2M2Cr23YXQUM+5cJvtaxeFaCr6nXoamCORh2heVx9WATVaQRhktjH8WLpP5IS47sTMYt6OW51tecsmK6g/YUsyKEDSQ0ZVbrtYrjDPv2sPBXFjjL7bsnxttKRVyV3a6lUi2AFfIUiOEFKI6JVxa8pt47GMbsOv1pKbRV5O/UyFY9CmroqmML1j2P21bFav199+HT5cd7+vGv8OZiNpn68e15TS1kjmbZCci/NGoqkl5D0r0q72YV6tEHf21UrLVOUousbmLmG7jtJ1DjelVvVq+aXSHp/GeQHDRDso3ko/Zp1WotxiK8xzQ8u0NQWiXHp9cQQjDrz0bTSzAIKIQ1qfhFw618JuV+2hx5IPyEb6f4FQf96Yenlst8JcFooBDtF1+26ZTc6lSZ3zhEVYKHMlF76UQtHdTCC1b7HS3brNSzc3Ctb5dOwtPKXXTzxDPpbYu9+tTz16SBXf3eo3mm4h6eqGMaHFfdtV+RWKWKZDDMKpRjK5JTaPGdHBjq+oFCw9G0UwsNRWM0/Ydyovbr5TRSKOR4Fxmmblgnnst0l2I2qAZ/WmI0OC4x+nGnMvVpnPod4P+Z0QlyaFQSGvvEkKgbrnV4ppP1D7r5b3fS4fkvoMzLfwA= \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/diagrams/Info sequence diagram.png b/docs/diagrams/Info sequence diagram.png index 7436835d40ada89e7b59a66c988946f0694a93a4..e33f2ddc876f6eaa1b6e68adc9287ebe92168b41 100644 GIT binary patch literal 65712 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 diff --git a/docs/diagrams/Name sequence diagram.drawio b/docs/diagrams/Name sequence diagram.drawio index cb20efcdd5..44b578a699 100644 --- a/docs/diagrams/Name sequence diagram.drawio +++ b/docs/diagrams/Name sequence diagram.drawio @@ -1 +1 @@ -7VnZbptAFP0aS+lDIhZj40cvSdMsVdUoStu3CdzAyAODhnFs9+t7MYNZ7RA7TiK5fgn3MhvnHM5wJx1zHCy+ChL5t9wF1jE0d9ExJx3D0DW9h3+SzDLNdPtmmvAEdVWjPHFH/0LWU2Vn1IW41FByziSNykmHhyE4spQjQvB5udkTZ+VZI+JBLXHnEFbPPlBX+mnWtrQ8fwnU87OZdU3dCUjWWCVin7h8XkiZ5x1zLDiX6VWwGANLwMtwSftdbLi7XpiAULbpML1aRM/zpaF74wftz+XVtXFxfdpNR3kmbKYeuGMOv5MAxjwISOiqpctlhgc+RZRczgJ2Q5+A0RCjUQSCBiBB4B2m0j/y3GjuUwl3EXGSrnNUCuZ8GTCMdLxE8iTBLmIdM0aimD6uZtUwI8CZiZg+w0+IU40kWT6TyUzjNfdJUj0RCAmLjVDpawJQucBxnWKJTbIOfcWZEq2ecTjPJbAm2i/Qb2ZJomTnrcfOmcELRc4riNL1OlNVbgqIRpyGcrUGa9SxJhWKuJA+93hIWJGkVsBtVtFGNK0ymHYTlnUo9e7BoDRqUMIC9SXhBCWPL7ToGDiyNqNfahCDi+6gwhzF8zxblDVh1AuTNwKe8KFGMeqfht7NKpogYCMI3WHiURg+Mu5ME6HzWeiCq7SMsIvlLzXcKvhdUnmynLI3EuHBNqr0PTkVwIjE97BstA0Uqa4/EiEWtGCVtNCrcBzzmXBA9clpRpDIstBMqXvjLLbWNEuumXS8XEHr59tdVFaTkd5mcjpGEzW7Za71bIiPM9HeJ/ZQayuYVSy1diZqHcxD+zUoY5DJh8PJFXGmdeMs4JqgQ/ELa6js8ZFLyYNmO9xgcy1UWEAms4M9vQuFWSLByOTzgnvVBjIHLwyUunhtoLfyKt2ukRcJnGsYTy+4uI9BfAuf+C3EMbrX8fBpajvyab000KH5HDRtPvf0OPedrl3ddz784z1bwafceFL1tN557KZdvA5mf3AoLOuFkKd2nqNwKaNaF+2861QHOrBLGQ1nDXvyxSPAWyOXxP6qZEqrpOwUyU6iBZVJ/XSqnWldFa9KqLP+oKfiyUKZzypYFoLCC7at6Gqhiz1FYNqDM2tQ+JWZ7Ov4OIWftZtCalLr6++rkIYi6r9CWhbWxjaFmLb2JgqpFNam/c4W0qKAK5DvMBLH1Cl/rryJt1tbaq09iay4dO0YrC1VtTPMlnb/2rOWDevd8bAFw/xEPG2e/1/BPP8H \ No newline at end of file +5Vpdk6I4FP01Vs0+tBUIoDwqtjvTXzW1vV2zOy9baYlICYQJsdX59RMkfMcGu8HunfZFcgmXeM7NzcmNA2j5uz8pCle3xMbeQAX2bgBnA1VV1NGYf8WWfWLRNWFwqGuLTrnh3v2JhREI68a1cVTqyAjxmBuWjQsSBHjBSjZEKdmWuy2JV35riBxcM9wvkFe3fnNttkqsYx3k9s/YdVbpmxUg7vgo7SwM0QrZZFswwcsBtCghLLnydxb2YvBSXJLn5kfuZgOjOGBtHrjR//55993f3s7W13fsx9Wd9d/oQoGJmyfkbcQvFqNl+xQCSjaBjWMvYACn25XL8H2IFvHdLSed21bM93hL4ZdL1/Ms4hF6eBZa1nxumtwuXoMpw7ujP0DJYOHxhImPGd3zLuIBHZjJIyKUxgLYbc6LoguvqwInMO2IRCw4mescLn4hEDsBvXHf4F3q83k34I1AE3YaBD1ht77ahU/bvao41jfw/fPVtTq/vtDq2MHJHfKxRXwfBXYNST5/wvhy43s37hJ7bsBb0xBTlw8Ix5B5wvw1tzUhztMGQ/wRmrU9D4WR+3h4a0wZxYsNjdwn/BeOkuwUW8mGxW+ysqwTG5fcmchgilGnE0LTLNBZ407C8FE6s/y4TxOtZDJoEkJVs6/JoDZPhgLwIXEDdhiCPh3oswqThLIVcUiAvCKXrabB8WA7Cqahl7BMm0UoJUgqo96Q1JuhLMyGGY4YJXzw0/h7jQshB4BhWFZjKPYAqTosgyrP1pL47CvfyDA1PCambQlc48eGpDcukkk/4R0UI9zlN/mVc/iGk1sUcBlAU398eIlL0eF/mMj6S1xwXF7EpYkLyFYio6/IMM6UuBqnlH4qmFp5jpkyKPuZYtK81UJLvtkKoJ8CpALPh6R0tMroJChjQFy+Z5l4rhNw2yNhjPj8Bg7sSbwJim0eWayfm8PYLu2E6lgVwEiTOcUeYjyllBzJ0BDevsacF1TMuLJIKBVAI7KhCyyeKu5tKo402OCIIepg9oyjtCNZLiPMagRmMLyC0xa7hZ455XvnAw7PjDLbrR6Qb5J7ZwkSsyx1YXXWtQ0SvcnRewgSsz+Z8uD+tgoFANMEoBuFosMWW6vzKpR0BG8vUbL4fKlGUUG7pbU/LJWTsOw/DR+XUudZgo0yP/pLsyvUyo6095ddVUnR6ZXckxDzW1MbRatDlU9Jbosy0LjbqDiy5r4yBLRKwlOrhY22IZAVC1NHhplWARqCoDOCW1RO3pDgoqh6Jr12zrCuV5RPtarbmuEGP93RKz+tOG0dPCV3c+7o/p9YXQyBmrb/FW0zNcx2Qn8krX2xVVhNZ7kUqcVA+5L9BRiqigHLYqRem+xqIaionrH+shBRR/qQL/L5p7K1HmtDUzXzT7sE0dMqIY8yVRJlXWjwK8RDraUE5+qKlfVzubgbkIMmL+pgYUIi2hc8zA7SrjoNfNe249dIBXv5vGogF3unRDHUygW/bHUpHj2ds0qlyERAJ1ss4eUxNdwQx10UCH+s9vwwQVA9YpFUKs8bA7Kab8JJFKKgwxh4+NIQAMn7fvcA0A3wvgJAVozrIgngHV5sGP7kp2dCfHxg4/7x4aZ8hXDFUGuMK+Y5KZdu/LugnMuQ+H8Ln+Ll/ePxDKsCT3YKddZjqHRT0DnRjiD6A5Jc2R2okgOy8Vk5lp01dsFxSPk2YxKt54Q+RJh+CZbkFkcRz+UfdH7Xjl8kBdxM07+Se97M/w+ZbOfyf5XCy18= \ No newline at end of file diff --git a/docs/diagrams/Name sequence diagram.png b/docs/diagrams/Name sequence diagram.png index f2817b6f667d80c09f3a180a32e1e02cf47901ee..d05694d4af54e2ceabc38f2c31c4685d8a0a736e 100644 GIT binary patch literal 21369 zcmc%xWmuGL*9HtDj)VdZh=58AouVUxAUSk*r;bQS2?$8bpfH4Vw@8ObNrN$fbT@*6 zbV)b7$Dr4BU)TLS&$fNvpZEQPn=^Bs=Mn2zYhU}?*BPLyEJJ+p`b8WZ9AY_HNp&0? zTr>{Oxo`qJ@X73R@n<+VFdR8agywVOm1N>FBPq}MIdK>SiVH!cLlOIAiH{+#@Gf@b z!yepZitA?4(S0Ge7bcI)VDk|ZqwsstNI)1?pBX!hV%omMwsY0eNB!vaK$(nvcKp7LC`dtE7;ox?_UcmSsrHHREqo@|! z?{H&3gIe4@2Py998`|q+M88fI$HP7V2bZ2q3@VW{FdIyWuM#F zhJ(`mBKJn&_wUX*Lj!z_!K2*eQmrI@-)$Jdg!e=i2T|}Nqnr89m4axD*eyaBx-O%u zoc{L!mtkb*d6{0o2A`_WlR;V;-XG*PH%jqjGj*7h@9`CPdErMn0t|`^o-Wum)wX1El~TosgSL9k9Jz2lmGn{bTE)zoa;teh zt%rP)>rYAy=9)LXnaMppBD!KFf`{s(R8UNBckgY;q6`@3WKuUHfhnmNloVq;LGx$A znu=+ZDBX2v6*UA!{{qH>RAwogF?f!uUc6SA%1T|8WQ&d>iKTqW8^ApcKOQU!krwF#2lKY zfMXuavg=U5kFnR#MVDlVTKO#(>LT4T#pT*I+d{YM7uv!8ZHP2&U=Lub=K^3m5h6o= zVvP`Mh}xv^U5!@xlMKvu*U>Xhse)8xzM}9CzB!|E3VJ%hFTrIWz-8VD!-k#5_5(J3 zjUxEV^!$y>6pIAuH*U#Gr1d}yFZ6iad_B4!iP|gf>y-2Spqd#v)bO5f`x@r_q~JT| z7y{(mF9DsgiZ}>Kr~*lGNC)F@dr^g-(?Kkk`NlD$mtopMxrW@jz%J)HoQBNl>zLcrbY^iS*=vNg!`K50VCSsLEUpaH6so0l;ZHPg}ac8 z3@>1_zO|1}l9B7olVmc~yR`O=B|MFtH7jA7@`u9gvmPmKgpi)sl$rG17P!gEG;Kba zG+c1}B$vSbUvERN-YRtW+H4r-F?Nh}3e+3?F{XW;2KlUB3=B*}v0P&hlV_4mk>b5{ zZE7!Fx3JdBea4x-)Oc5( z5M|)t5~R~e9&_$)U7sCVqenXnX|X#)zdcvGjtlK3VDsrW(Rpi}RCr2a0e$g8@EoKk z`5JE^;|mx?b3_abN^|bmK(qbRrpTB1&pziU!FqC0v2_Rl3r~KkMuiK#O_(9a;2@hM z_AwO)HQyCRQO)`S=G+yM@aOFJ0$a>{llkxx7{vvqK&+1eP9cxw1x#BhX=wrM^VGSs zk6@}2V7|nx%<0_pXi_CUiC1U7V-_j~P2|y`-yug3*=t|Ba^}(Qrr@B4_t>qsaG)4z z6}n{qv!AL0pYW#Bny2X%*00SopZ!!)8V3<<{n?7o5K?S*Mcn1gW#ZsQ0RKb11^x~z z92nmCJ%BI-4ua{fn;Z^enc4Fl@tNlXPY>|94t$aoKl?Mn-@p4v!T|67GYZtvj?sNbRKESn`cbA1immZQsvD`UKxM`T2|YTRaSo*fgPq=P@Rk#% zJYxA3R2V9n)Wd2`d7L)_ZPU84<{=!byO0ahUSr%&$hp65<_9G}23+YK(wP+f&P6Arw##aO9to6u>MVXXZrQg;21$#?EmP?SIIb+ zVSaZvX&)h=!r%2YrJy%qY8g^sqEv!my4)zFy-g+NB1)HfSGwXQEJo0i z09oZBVU;Y!TXyZHix78^SC#O!(fh@Qyz8AqjsaI8#m0_9r4umpOu7uTZRUpl5?9`v z4vVU3d%dl;6+)CQugTC-6}aQP{JriCaI-5xu$Z=cWrL)cZnLuSb@1q!9SEWzp=fE_ z5P1RdEc1qiE2MZlKQB-G35-Zg(o&rN_Z93JZC=|fZ z@1C8%844y_{Cl!A2qGKlHk#13bQ4<@C?;>e+PI5Z8!D+&?GpngnXLPJfkR+^d~at$ znjiPvs$eBR4tN+9&5>~uq;q}~Yhy&uD0887xhK2zWuOv?Jr2#&CG)XkS0Ket>ffEM zTTirY+6)*LD+Pip*3P2vkYLKurIWJ3)wF8Z<-i76BTQl;v3al=8e}qlS8Dm##e_m| z^NFWNPp}{JDhD9t+NKn=h|Dn>>leo*#6|nI0ocj|pJkP9YQbP$GaE z5fuAFrykN{BE``y;%FP{cYQ(WV0rp~uVi&sI>&|e5j!IW=hL8T5)@6gKi^t^uoTZf zsA{W}o3df;yjgRp{7FNifP>yvK5c;t*Bs&0Aft5P!?94ss~=-&jaj_*Rr=J|eDkjL zFPv@Z{r04M*!{cTHnESRb6L z1H0)YE|*aAIBoCd-+1y!PJaLXJ39DZp<3;AL zIFsENJgU5TA(n;Cb-}3W%4TciZqUjuEvxqAea27lsg*}pa@|)JqFcCMAIXgs_dnJe zP_wj)#vM2NIp1$2F7QFAsDN^8rT7&3Nh{alI3cicRVMZ3;PKq+DHqWi*4&CAnOl@K zbdJgq5tcP-hl&Oa#+7S$+08iMP~=UI@Wlvj zE8IC4Xh@nQLo_@@N& z!GPz(=B)7ttC*4LVSy=?qEqGh^>vil9$wCcxwp8#f zCEKfQ!lB8LUjwhFPS8anv}xFhxCOJp{toO$g56z#t?qm}$v_|B2ebRw2|N{N2&jhT zxl2TAIc7|0$KG$8Okzx|y4N(;xhy9wgV*`K3Cw*||oPJd*Yt6%;y@7cx?$v2=^^V-d% zVr=uHB((3Pu3VyZP0sp)p#DKf_$a;l20gQM=6;8L+~Fqg4H^_u>gsXPt$gyjF zHeZFA5hC@yom`85r0Zfs-Xxt!angX!CLNJ{UO)A++jH-dNbA6alcP6o6Knfx#(krY z%Sa^?t17Na9`9>;7A(?T0}i@p(O&olqE>^zJ`0M(H+Fm^aUD_iMlyb$V2>-$GAL61 z%G!2M8>V!!y37fOq0pLC(E3h!GQguMrW5qhLi>?6xpy3zo5$r^BMKRsSTEm>%P#6z z&yc$*h;AHjwanGM)~22}nuCGl zV*8Ohw34oDQYYS!9=H2YNh?kA-~h26ltoHTl;Eq43^V;ejm!j3^>)jkoFq{mC#%DR z8v{Cy2qkyC;z06J@&}s_UhW6$rNGQG#a!{=r^@#(s1?U{y$Km!5-ya`tIlbOyPJAR zbA@QqM$zGGqZxjW*{V$nVwoviUX>Jcq?@A^pv)Sn?YGWw)nP`lIL z*CKz?{)NTunv;HIB6+Ezjd{6^S5Od_x!0z`Xi$lb3S5UCtBsarFjO<5ks9+sFd}Ze26NDzp zVwJ0P=##8Z4>4uY8cc_-mKq>k(bNg6W|B2r5bwqozAp}lI zdI8DwfD;WbdDt#Nh;f$941j+>nCL2LnkVC|oy{uHv(T;a7%;B7(sVC3>c2sdsl^h! z$zilcqR*K>Kjo6^8GvzzOYqjR0HBir5EcMB+fY zVLERPVT6S#7~>B0j&}fM3DWs02e3r&tmSN)vDXz4FG4h ze*g4;fqC`-;bG3t6N51@{Q+AP1830fY(&7z=|04VXTXr`TX(YfSFcR1Cy_O+HM@^!MAT7T!m

    Mp^PaWek5lZ(l#@oU0iID^Vd*RU$}Ur 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 From b74db1a7a1fa52a273a87638f4489f513c28e4bf Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 03:12:51 +0800 Subject: [PATCH 358/374] Update user stories related to Person class --- docs/DeveloperGuide.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 361772663f..30d77e5ba2 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -188,8 +188,12 @@ NUS students living on campus who would like to track their daily food and nutri |Version| As a ... | I want to ... | So that I can ...| |--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| +|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|clumsy or long term user|be able to view my personal information|make changes when necessary| +|v2.0|clumsy 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 From 27fbc63d92cc2fd5aea85b3cc81085303e14ab44 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 03:21:00 +0800 Subject: [PATCH 359/374] Update non-functional requirements --- docs/DeveloperGuide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 30d77e5ba2..aabae44a42 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -198,6 +198,7 @@ NUS students living on campus who would like to track their daily food and nutri ## Non-Functional Requirements 1. Should work on any mainstream OS as long as `Java 11` is installed in the system. +1. 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 From 251c0de6f3f1b54fe5069b45189496ad4e821d8e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 03:21:50 +0800 Subject: [PATCH 360/374] Update glossary --- docs/DeveloperGuide.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index aabae44a42..a6bee3d001 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -202,7 +202,10 @@ NUS students living on campus who would like to track their daily food and nutri ## Glossary -* *Mainstream OD* - Windows, Linux, Unix, OS-X -* *Food items* - Includes both food and drinks +* _Mainstream OD_ - Windows, Linux, Unix, OS-X +* _Food items_ - Includes both food and drinks +* _Nutrient / Nutritional intake_ - Includes carbohydrates, fats, proteins and calories +* _Personal information_ - Includes name, age, gender, height, fitness level, original, current and target weight. +* _Database_ - Contains a list of commonly found food items in the National University of Singapore ## Instructions for manual testing From 9375397d0efe59bdae0edae88e6864142002b8c6 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 03:27:00 +0800 Subject: [PATCH 361/374] Update target user profile and value preposition --- docs/DeveloperGuide.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index a6bee3d001..b0ab7e0741 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -178,11 +178,10 @@ similiar diaghrams for PersonSaveLoadManager ## Product scope ### Target user profile -NUS students living on campus who would like to track their daily food and nutritional intake. +NUS students living on campus who would like to track their diet. ### Value proposition - -{Describe the value proposition: what problem does it solve?} +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 From a0d6cc48addb46d9752c4fe2a6658153a9228ec0 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 03:45:20 +0800 Subject: [PATCH 362/374] Update feature description --- docs/DeveloperGuide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index b0ab7e0741..ec03452755 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -46,6 +46,8 @@ In summary, the `UI` component, #### Implementation +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](#edit-user-information-feature). + **This feature utilises two commands words** * [`name`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-username-name): Saves the user's name or nickname into the application. From 9305e77e5a30c49fbc707ae77905ed8878014a67 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 03:45:47 +0800 Subject: [PATCH 363/374] Update edit user info feature --- docs/DeveloperGuide.md | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index ec03452755..06cd624c73 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -120,6 +120,62 @@ Aspect: Changing attribute values in `Person` object or creating new `Person` ob * Pros: Ability to write tests as method chains. * Cons: Creation of many objects, which takes up memory space. Have to ensure that only the correct `Person` instance is kept and referred to. +### Edit user information feature + +#### Implementation + +**This feature utilises the following command word** + +* [`editinfo`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#editing-user-information-editinfo): Edits the user information stored in the application. + +**Main classes and methods used** + +* [`Person`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java): Stores all user information provided. + * `Person#setName(String newName)`: Updates the name the `Person` object. + * `Person#setGender(Gender newGender)`: Updates the gender of the `Person` object. + * `Person#setAge(int newAge)`: Updates the age of the `Person` object. + * `Person#setHeight(int newHeight)`: Updates the height of the `Person` object. + * `Person#setOriginalWeight(int newOriginalWeight)`: Updates the original of the `Person` object. + * `Person#setCurrentWeight(int newCurrentWeight)`: Updates the current weight of the `Person` object. + * `Person#setTargetWeight(int newTargetWeight)`: Updates the target weight of the `Person` object. + * `Person#setFitnessLevel(FitnessLevel newFitnessLevel)`: Updates the fitness level of the `Person` object + +**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](diagrams/Enter Info Step1.png) + +**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](diagrams/Enter Info Step2.png) + +_Sequence Diagram:_
    +![Name sequence diagram](diagrams/Name sequence diagram.png) + +**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](diagrams/Enter Info Step3.png) + +_Sequence Diagram:_
    +![Info sequence diagram](diagrams/Info sequence diagram.png) + +#### Design considerations: + +Aspect: Whether to enter name and other information separately or together + +* **Alternative 1 (current choice)**: Enter name and other information separately + * Pros: Increase user interaction and engagement. + * Cons: Enter information using two commands. + +* **Alternative 2**: Enter name and other information together + * Pros: Enter all information at once. + * Cons: Decrease user interaction and engagement. ## Save/Load Feature From ee18b7137d8b5fc20e68364f9c7e9acc36ccc74e Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 06:30:01 +0800 Subject: [PATCH 364/374] Rename files to remove spacing --- docs/DeveloperGuide.md | 12 ++++++------ ...nter Info Step1.drawio => EnterInfoStep1.drawio} | 0 .../{Enter Info Step1.png => EnterInfoStep1.png} | Bin ...nter Info Step2.drawio => EnterInfoStep2.drawio} | 0 .../{Enter Info Step2.png => EnterInfoStep2.png} | Bin ...nter Info Step3.drawio => EnterInfoStep3.drawio} | 0 .../{Enter Info Step3.png => EnterInfoStep3.png} | Bin ...ce diagram.drawio => InfoSequenceDiagram.drawio} | 0 ...sequence diagram.png => InfoSequenceDiagram.png} | Bin ...ce diagram.drawio => NameSequenceDiagram.drawio} | 0 ...sequence diagram.png => NameSequenceDiagram.png} | Bin .../{Ui component.drawio => UiComponent.drawio} | 0 docs/diagrams/{Ui component.png => UiComponent.png} | Bin 13 files changed, 6 insertions(+), 6 deletions(-) rename docs/diagrams/{Enter Info Step1.drawio => EnterInfoStep1.drawio} (100%) rename docs/diagrams/{Enter Info Step1.png => EnterInfoStep1.png} (100%) rename docs/diagrams/{Enter Info Step2.drawio => EnterInfoStep2.drawio} (100%) rename docs/diagrams/{Enter Info Step2.png => EnterInfoStep2.png} (100%) rename docs/diagrams/{Enter Info Step3.drawio => EnterInfoStep3.drawio} (100%) rename docs/diagrams/{Enter Info Step3.png => EnterInfoStep3.png} (100%) rename docs/diagrams/{Info sequence diagram.drawio => InfoSequenceDiagram.drawio} (100%) rename docs/diagrams/{Info sequence diagram.png => InfoSequenceDiagram.png} (100%) rename docs/diagrams/{Name sequence diagram.drawio => NameSequenceDiagram.drawio} (100%) rename docs/diagrams/{Name sequence diagram.png => NameSequenceDiagram.png} (100%) rename docs/diagrams/{Ui component.drawio => UiComponent.drawio} (100%) rename docs/diagrams/{Ui component.png => UiComponent.png} (100%) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 06cd624c73..fcc8e2172a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -6,7 +6,7 @@ ## Design ### UI component -![Ui component](diagrams/Ui component.png) +![Ui component](diagrams/UiComponent.png) **API**: [`Ui.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/Ui.java) @@ -68,23 +68,23 @@ _Summary_: Only one instance of `Person` is ever instantiated. A default person **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](diagrams/Enter Info Step1.png) +![Enter Info Step1](diagrams/EnterInfoStep1.png) **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](diagrams/Enter Info Step2.png) +![Enter Info Step2](diagrams/EnterInfoStep2.png) _Sequence Diagram:_
    -![Name sequence diagram](diagrams/Name sequence diagram.png) +![Name sequence diagram](diagrams/NameSequenceDiagram.png) **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](diagrams/Enter Info Step3.png) +![Enter Info Step3](diagrams/EnterInfoStep3.png) _Sequence Diagram:_
    -![Info sequence diagram](diagrams/Info sequence diagram.png) +![Info sequence diagram](diagrams/InfoSequenceDiagram.png) #### Design considerations: diff --git a/docs/diagrams/Enter Info Step1.drawio b/docs/diagrams/EnterInfoStep1.drawio similarity index 100% rename from docs/diagrams/Enter Info Step1.drawio rename to docs/diagrams/EnterInfoStep1.drawio diff --git a/docs/diagrams/Enter Info Step1.png b/docs/diagrams/EnterInfoStep1.png similarity index 100% rename from docs/diagrams/Enter Info Step1.png rename to docs/diagrams/EnterInfoStep1.png diff --git a/docs/diagrams/Enter Info Step2.drawio b/docs/diagrams/EnterInfoStep2.drawio similarity index 100% rename from docs/diagrams/Enter Info Step2.drawio rename to docs/diagrams/EnterInfoStep2.drawio diff --git a/docs/diagrams/Enter Info Step2.png b/docs/diagrams/EnterInfoStep2.png similarity index 100% rename from docs/diagrams/Enter Info Step2.png rename to docs/diagrams/EnterInfoStep2.png diff --git a/docs/diagrams/Enter Info Step3.drawio b/docs/diagrams/EnterInfoStep3.drawio similarity index 100% rename from docs/diagrams/Enter Info Step3.drawio rename to docs/diagrams/EnterInfoStep3.drawio diff --git a/docs/diagrams/Enter Info Step3.png b/docs/diagrams/EnterInfoStep3.png similarity index 100% rename from docs/diagrams/Enter Info Step3.png rename to docs/diagrams/EnterInfoStep3.png diff --git a/docs/diagrams/Info sequence diagram.drawio b/docs/diagrams/InfoSequenceDiagram.drawio similarity index 100% rename from docs/diagrams/Info sequence diagram.drawio rename to docs/diagrams/InfoSequenceDiagram.drawio diff --git a/docs/diagrams/Info sequence diagram.png b/docs/diagrams/InfoSequenceDiagram.png similarity index 100% rename from docs/diagrams/Info sequence diagram.png rename to docs/diagrams/InfoSequenceDiagram.png diff --git a/docs/diagrams/Name sequence diagram.drawio b/docs/diagrams/NameSequenceDiagram.drawio similarity index 100% rename from docs/diagrams/Name sequence diagram.drawio rename to docs/diagrams/NameSequenceDiagram.drawio diff --git a/docs/diagrams/Name sequence diagram.png b/docs/diagrams/NameSequenceDiagram.png similarity index 100% rename from docs/diagrams/Name sequence diagram.png rename to docs/diagrams/NameSequenceDiagram.png diff --git a/docs/diagrams/Ui component.drawio b/docs/diagrams/UiComponent.drawio similarity index 100% rename from docs/diagrams/Ui component.drawio rename to docs/diagrams/UiComponent.drawio diff --git a/docs/diagrams/Ui component.png b/docs/diagrams/UiComponent.png similarity index 100% rename from docs/diagrams/Ui component.png rename to docs/diagrams/UiComponent.png From f64c7b90b013b02e8dedda89e220c26ce3199b41 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 09:29:08 +0800 Subject: [PATCH 365/374] Update edit user information feature --- docs/DeveloperGuide.md | 90 +++++++++++-------- docs/diagrams/EditInfoSequenceDiagram.drawio | 1 + docs/diagrams/EditInfoSequenceDiagram.png | Bin 0 -> 61223 bytes docs/diagrams/EditInfoStep2.drawio | 1 + docs/diagrams/EditInfoStep2.png | Bin 0 -> 18499 bytes 5 files changed, 57 insertions(+), 35 deletions(-) create mode 100644 docs/diagrams/EditInfoSequenceDiagram.drawio create mode 100644 docs/diagrams/EditInfoSequenceDiagram.png create mode 100644 docs/diagrams/EditInfoStep2.drawio create mode 100644 docs/diagrams/EditInfoStep2.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index fcc8e2172a..6554f7f1b3 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -46,14 +46,14 @@ In summary, the `UI` component, #### Implementation -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](#edit-user-information-feature). +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](#edit-user-information-feature). -**This feature utilises two commands words** +**This feature utilises two commands words**: * [`name`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-username-name): Saves the user's name or nickname into the application. * [`info`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-user-information-info): Saves the user's age, gender, height, fitness level, original, current and target weight into the application. -**Main classes and methods used** +**Main classes and methods used**: * [`Manager`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/Manager.java): Stores a `Person` object. * `Manager#setPerson(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, FitnessLevel newFitnessLevel)`: Calls a method in `Person` class (listed below) to set the attribute values of the `Person` object. @@ -61,9 +61,8 @@ This feature and its associated command words is **only used during the initial * `Person#setAll(String newName, Gender newGender, int newAge, int newHeight, int newOriginalWeight, int newCurrentWeight, int newTargetWeight, FitnessLevel newFitnessLevel)`: Updates the attribute values of the `Person` object. -**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. +**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. @@ -97,6 +96,16 @@ Aspect: Whether to enter name and other information separately or together * **Alternative 2**: Enter name and other information together * Pros: Enter all information at once. * Cons: Decrease user interaction and engagement. + +Aspect: Single or multiple usage of feature and command words + +* **Alternative 1 (current choice)**: Single usage of feature and command words, requiring any subsequent editing of user information to be done through another command + * Pros: Increase user interaction and engagement during initial setup. Enable users to edit any information thereafter more conveniently through one command. The two features of entering and editing personal information are clearly differentiated. + * Cons: Three commands need to be implemented, potentially leading to more bugs. + +* **Alternative 2**: Multiple usage of feature and command words such that any subsequent editing of user information can be to be done the same command + * Pros: Two commands need to be implemented, likely to have lesser bugs. + * Cons: Decrease user interaction and engagement during initial set up. Subsequent editing needs to be done through two commands and there is no differentiation between the two features of entering and editing personal information. Aspect: Whether to use singleton pattern for Person class @@ -104,7 +113,7 @@ Aspect: Whether to use singleton pattern for Person class * Pros: Reduce coupling and increase testability. * Cons: Risk of creating multiple `Person` object by mistake and there might be negative consequence in creating multiple objects. - However, there is minimal risk of creating multiple `Person` object by mistake and minimal negative consequence in creating multiple objects as long as the `Manager` refers the correct instance of `Person`. + However, there is minimal risk of creating multiple `Person` object by mistake and minimal negative consequence in creating multiple objects as long as the `Manager` refers the correct instance of `Person`. * **Alternative 2**: Use singleton pattern for `Person` * Pros: Easy to implement, prevent the instantiation of more than one `Person` object. @@ -124,11 +133,17 @@ Aspect: Changing attribute values in `Person` object or creating new `Person` ob #### Implementation -**This feature utilises the following command word** +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](#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. -* [`editinfo`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#editing-user-information-editinfo): Edits the user information stored in the application. +**This feature utilises the following command word**: -**Main classes and methods used** +* [`editinfo`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#editing-user-information-editinfo): Edits the user information stored in the application.
    +The command is implemented in such a way that **one or more changes to the personal information can be made** using a single command. Below are some examples of valid commands. + * `editinfo a/22`: Edits the age of the user to `22` + * `editinfo a/22 c/80`: Edits the age of the user to `22` and the current weight to `80kg`. + * `editinfo n/Jane g/F a/22 h/165 o/70 c/63 t/60 f/3`: Edit the name, gender, age, height, original, current and target weight as well as the fitness level of the user to `Jane`, `female`, `22`,`165`, `70`, `63`, `60` and `You engage in moderate amount of exercise or have a job that requires moderate physical activity.` respectively. + +**Main classes and methods used**: * [`Person`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java): Stores all user information provided. * `Person#setName(String newName)`: Updates the name the `Person` object. @@ -141,42 +156,47 @@ Aspect: Changing attribute values in `Person` object or creating new `Person` ob * `Person#setFitnessLevel(FitnessLevel newFitnessLevel)`: Updates the fitness level of the `Person` object **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. +_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**. 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](diagrams/Enter Info Step1.png) - -**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. +**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:_
    -![Enter Info Step2](diagrams/Enter Info Step2.png) - -_Sequence Diagram:_
    -![Name sequence diagram](diagrams/Name sequence diagram.png) - -**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)`. +![Edit Info Step1](diagrams/EnterInfoStep3.png) + +**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:_
    -![Enter Info Step3](diagrams/Enter Info Step3.png) +![Edit Info Step2](diagrams/EditInfoStep2.png) _Sequence Diagram:_
    -![Info sequence diagram](diagrams/Info sequence diagram.png) +![Edit Info sequence diagram](diagrams/EditInfoSequenceDiagram.png) #### Design considerations: -Aspect: Whether to enter name and other information separately or together +Aspect: Whether one or more changes to the personal information can be made using a single command or through the use of various commands -* **Alternative 1 (current choice)**: Enter name and other information separately - * Pros: Increase user interaction and engagement. - * Cons: Enter information using two commands. +* **Alternative 1 (current choice)**: One or more changes to the personal information can be made using a single command + * Pros: Easier and more convenient for the user when editing multiple pieces of personal information. Less user commands required. + * Cons: Harder to implement. -* **Alternative 2**: Enter name and other information together - * Pros: Enter all information at once. - * Cons: Decrease user interaction and engagement. +* **Alternative 2**: Each piece of personal information is changed using separate commands + * Pros: Easier to implement. + * Cons: Tedious for the user to edit multiple pieces of personal information. More user commands required. +Aspect: Use of multiple or single setter method(s) + +* **Alternative 1 (current choice)**: Use of multiple setter methods each updating an attribute in the `Person` class
    +E.g.`Person#setName(String newName)`: Updates the name the `Person` object.
    +E.g.`Person#setGender(Gender newGender)`: Updates the gender of the `Person` object.
    + * Pros: Easier to implement and uses the KISS principle. + * Cons: More method calls is required, possibly resulting in more bugs due to wrong method calls. + +* **Alternative 2**: Use of a single setter method that is able to update a variable number of attributes in `Person` class + * Pros: Lesser method calls is required, possibly resulting in lesser bugs due to wrong method calls. + * Cons: Harder to implement. + +Head over to the Design Considerations Section in the [Enter user information feature](#enter-user-information-feature) for more related design considerations. + ## Save/Load Feature The Save/Load feature is implemented by the saveload package. @@ -246,8 +266,8 @@ DietBook is designed to **track the food and different kinds of nutritional inta |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|clumsy or long term user|be able to view my personal information|make changes when necessary| -|v2.0|clumsy user|be able to edit my personal information|make changes if I input the wrong information| +|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| diff --git a/docs/diagrams/EditInfoSequenceDiagram.drawio b/docs/diagrams/EditInfoSequenceDiagram.drawio new file mode 100644 index 0000000000..46ca57fbe6 --- /dev/null +++ b/docs/diagrams/EditInfoSequenceDiagram.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/diagrams/EditInfoSequenceDiagram.png b/docs/diagrams/EditInfoSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..ec6e6e66b0aa62eeb4a3d35b87a1983b7759de9c GIT binary patch literal 61223 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/EditInfoStep2.drawio b/docs/diagrams/EditInfoStep2.drawio new file mode 100644 index 0000000000..c0eac2435a --- /dev/null +++ b/docs/diagrams/EditInfoStep2.drawio @@ -0,0 +1 @@ +7VpLc+I4EP41VO0eSMk2AufIc1JTyU6qckjm6AFhVGtbHmES2F+/rZctPxIIA2wyaw6OutVqqT91t9WKO9443n7hQbq6YwsSdVy02Ha8Scd1HXfgwx/B2SkO7mlGyOlCCxWMB/oP0UykuRu6IOuSYMZYlNG0zJyzJCHzrMQLOGcvZbEli8qzpkFIaoyHeRDVuY90ka0U18eo4N8QGq7MzA7SPXFghDVjvQoW7MViedOON+aMZaoVb8ckEuAZXNS42Su9+cI4SbJDBtABunH9v6be027mL+Mu+uZPu1rLcxBttMF6sdnOIMDZJlkQoQR1vNHLimbkIQ3movcF9hx4qyyOgHKgqdURnpHtq+t0cuvBbQiLScZ3IKIHDDRe2mEcA/VLAf/AYLqyoHeNYKC3PMxVF6hAQwPzDpDcBpD6EUw7SktQ9X9uxGaO4oCHNOl4Q+jtpVt4IuspUUKAT9YNIhpquTmgQ3ihA1qh/itnWjJAr2ky0dFdy7gRepy+mOQ1LRvDSAlfMzH1vWrofoBnUx0DPDV5jZ1WeSte5ewHyAZF8bsRWWaq03/LGAOJyRlGzqsPSYKYyFkm4pl3fw3mf5eEpd51GiSNy56ziHG1MB7+CP5AUtlYGNHU+jO3S+7RMohptFPDY5awtQyiButwNZ1i8FXBlbkip4zvYum9wJmItpgdCwfF4PL7ZJ1c1sTnUWrcQo2KkbxHPASowNDbhQ2wRkbZbwSUJwM1lKKOdABbSFKh1dbaN5UOb3gXJJCcuS0kzalKlnvNKm0Bt2HZrkx6gnIqcjr5HQJy3pXjW6RDLBJiLjko9gPtLOQ9i68SZKHc7tOJMu/sWX2QL3N+aM1f3X1J5i5gM8uOqeVqHpznDRVg5WBug64NujboLhx0P/L3tTgFm/djcbx5x6v/g7zoDeTaFGeAP68tjFNQHESPZZv8T2zSfMNFMFYtQp/XogwUk6pB7/S6SsElchmFCnSoqoNJxkSRpWuFiTJhxEBqGcl6cklFOSMXretnx9X0TL/+JjckeiZCa7lcE0PH6gULM3sz+I3HpynjPFMI6zour4OtOs7pN9RxOfPkdZx3yjruQoVaSKD4FuefL6rxRqFWZPPfqXS7G95Oj7DnnKtvBPrYeNaF/yeIaIyPjOizBXTv1YCuBcevheWSZglZr2/JMxysvOHMJo+IvtrqPmz03X57bIPvQwRfrxx8nlMPPlmYXCz48O93Kxrr6tWqY9t70faKpr2iaa9o2nvRNujaoPufBN3BZ/mTHkYPutnp//dH0esjy0Dzj/2Tn0T7+z9iIMliKL4GAYqlBNAeAWcm4ZVwAWUf9mtfNJBF7TORvXA14cFJFGT0uayrCRCt7p5ReZjbVr5xMNj3Kpiu2YbPiR5VwFpTVLudqypS95o1RXJ/chuP37LBObeMbGn2JNpXCGFNfwcaXSF8remJAAIZYmeIBMx6sgk1zsWGLsZJamdpuSecAi6ifpigd3gOWC137Q24NDpqU/bdcF7EE3tuxYGqdeahnoj9PYrO7In+WT3xV/yp2YUdd48LfwxP7F3OEwf4Cl9bv0pic678Hip+3pFu2q8mTHwiN+3oI4slXhxWvOm/ \ No newline at end of file diff --git a/docs/diagrams/EditInfoStep2.png b/docs/diagrams/EditInfoStep2.png new file mode 100644 index 0000000000000000000000000000000000000000..49c0da6d5c3801fd9e6a06551fc4f02aed46bf3b GIT binary patch literal 18499 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 SF2LnLRO%|)P>j;kSN{(sK20?M literal 0 HcmV?d00001 From f5431a57971c2313fbfdcbaf549c13d9c5cc785a Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 09:53:38 +0800 Subject: [PATCH 366/374] Update view user information feature --- docs/DeveloperGuide.md | 29 +++++++++++++++++-- docs/diagrams/UserInfoSequenceDiagram.drawio | 1 + docs/diagrams/UserInfoSequenceDiagram.png | Bin 0 -> 29539 bytes 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 docs/diagrams/UserInfoSequenceDiagram.drawio create mode 100644 docs/diagrams/UserInfoSequenceDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6554f7f1b3..23514a73d0 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -48,7 +48,7 @@ In summary, the `UI` component, 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](#edit-user-information-feature). -**This feature utilises two commands words**: +**Commands words used**: * [`name`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-username-name): Saves the user's name or nickname into the application. * [`info`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-user-information-info): Saves the user's age, gender, height, fitness level, original, current and target weight into the application. @@ -135,7 +135,7 @@ Aspect: Changing attribute values in `Person` object or creating new `Person` ob 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](#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. -**This feature utilises the following command word**: +**Command word used**: * [`editinfo`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#editing-user-information-editinfo): Edits the user information stored in the application.
    The command is implemented in such a way that **one or more changes to the personal information can be made** using a single command. Below are some examples of valid commands. @@ -197,6 +197,29 @@ E.g.`Person#setGender(Gender newGender)`: Updates the gender of the `Person` obj Head over to the Design Considerations Section in the [Enter user information feature](#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](#edit-user-information-feature). + +**Command word used**: + +* [`userinfo`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#viewing-user-information-userinfo): Shows the user information stored in the application. + +**Main classes and methods used**: + +* [`Person`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java): Stores all user information provided. + * `Person#toString()`: Returns a string representation of all user information. + +**Example usage scenario and how the feature work**
    + +**Step 1**. When the user wishes to view their personal information, they can enter`userinfo`. The + `userinfo` command would call `Person#toString()`. + +_Sequence Diagram:_
    +![User Info sequence diagram](diagrams/UserInfoSequenceDiagram.png) + ## Save/Load Feature The Save/Load feature is implemented by the saveload package. @@ -282,7 +305,7 @@ DietBook is designed to **track the food and different kinds of nutritional inta * _Mainstream OD_ - Windows, Linux, Unix, OS-X * _Food items_ - Includes both food and drinks * _Nutrient / Nutritional intake_ - Includes carbohydrates, fats, proteins and calories -* _Personal information_ - Includes name, age, gender, height, fitness level, original, current and target weight. +* _Information / Personal information_ - Includes name, age, gender, height, fitness level, original, current and target weight. * _Database_ - Contains a list of commonly found food items in the National University of Singapore ## Instructions for manual testing diff --git a/docs/diagrams/UserInfoSequenceDiagram.drawio b/docs/diagrams/UserInfoSequenceDiagram.drawio new file mode 100644 index 0000000000..47b2acf2cb --- /dev/null +++ b/docs/diagrams/UserInfoSequenceDiagram.drawio @@ -0,0 +1 @@ +5Vvfc5s4EP5rPJN7iAchxI/HxInbzLXXTDOZuz4SIwNTQJyQa/v++hMgMAhsEweIG/vFaBEL/r7d1e4KT+As3Hyidux9JQ4OJqribCbwbqKqACka/0ol21yCDDMXuNR3xKSd4Mn/DwuhIqQr38FJbSIjJGB+XBcuSBThBavJbErJuj5tSYL6XWPbxQ3B08IOmtK/fYd5udREyk7+GfuuV9wZKOJMaBeThSDxbIesKyJ4P4EzSgjLj8LNDAcpeAUu+XXzPWfLB6M4Yl0ucJ8D75t3+6cLXujDev78l/H907Uq6PllByvxi8XTsm0BASWryMGpFmUCb9eez/BTbC/Ss2tOOpd5LAz4CPDDpR8EMxIQml0L5/MZ/3C5uA2mDG/2/gBQwsLtCZMQM7rlU8QFqLAJYUqmGK53vABNyLwKJ7CYaAtbcEvVO7j4gUDsFeiBYcGbzeZzy+oHPF0/Dh4CY4KnDg3ePZrP+wHPOIqdBkc1PNjEDt48J5j60ZLMSBjakdNAkwegOD1chcEXf4kDP+Kj25hfxB8Kp7AFQvy4kx1DncddZvNLaDkOAjtO/JfsriltFC9WNPF/4e84ycN7KiUrlt5pVobtVLjkysQSAPQmpRBaVl+UqlIwAWrXaKIORWqHUFwBPiZ+xLJHQLcTdCcxSSjziEsiO6hy2Qm3/Qa3P7igGpbFsAplC5LAQgMhqbcgqQdMWFgNUv3fFSlOXOf2ecMnAD3e7E7yIzf7hjdf7Ygv+bTQxx8vVykm/IY+N5yPQdM67mNKm4/pQ/mYecY+ph8EU6v7mNUGZRPJwYIVaMt9evKxZ//DupeiWJai9JTSmeDc3At0SOrezb/AYTglB1Oh0cnDhsOyJck7gGWKiM/r1pvAdyMueyGMkZCfwJFzkxbCqSwgi58lhthplL5HTa8CRpHcUhzYjPtCTVUbGkLbY0p6JREz9TruQAI0ISu6wOKqan0rKdKgdlgRs6mL2QFFxUSyXCaYNQgsYXgDp6/L8bpwSmLMT906duJllRLIT4s02nwT22+ktiyKSpc6kVrVlH2zG7W90YbegTa88dk/6SoyzWqhdPgjnThVgBjebcQikw22lUElYN4przAB/osySo6G+N5tBaG6rTRq5662ItmcBsxxTaWt9ugnanOq6Da3CEUtxj/E2CoEO6PIRtvq6FSz2LtmXitTFeiwnoY0q7++lgqpZDfRaSaiGmiqKpUPkvRqU0u1dh/UyYJGXUeMFivLU+YktqPTs2+efyUkqqTfubo96TdPwVg9d04YJT9xkfBGJMvHqzmwENnC3hfc0LK0TnaE0Hec9DatyXq9LzjpoVLV6pVqGUWqLb5Ry6u2QrWX8kpoeSkEX4jrLyqEv8gzj9VgH8YI5DZWS4k9rg1YAzl5wwaeH44YwGVEAQOAszKAQnHvQQBv8GLF8FVYNDP58ykr/4+Lc3lpUwzoaoNxYI1KeVsDpQ/K04wlW9uvLo9lKCeOLY5tjspyW2unD5ZjyjPQnOeHaEmuGHliXOR+W+bCC+Reritb2nrl/u445Lc1E3rqnD/K6fvH6p7P5zr/9GMYSGoUgOJVmKPdc6QPZRltVd3+3sG43fPcbLt2zwFsWUnbwBzsDYmhcqdG8tzwucutoJCUT713Ag3ajKCfZtzk5NbZEFsodeeDyKir6NoYM1T9sKIzaH3BDi/dfQhOpW0xQz+NU2RohxWdA6dDVT1F/nuJRY+8Z4b29+VHCcaw//3sM977RIY5laKycqIHq6ZxTNXAm1rFznrvnUi5Pr24lqMm7T1B8M4pU4ed7krZeIdT3LcN/LM3nXQ9e/N/yJd4DVXyDK1l46bYkHwjfny4+7NG7lm7v7zA+/8B \ No newline at end of file diff --git a/docs/diagrams/UserInfoSequenceDiagram.png b/docs/diagrams/UserInfoSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7982d2fc5b972377f92f55ddfdc7ff56e8831f GIT binary patch literal 29539 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 a6DYYZsCJL_E;u}s%gkuMVUB_G<^KUy`I9sN literal 0 HcmV?d00001 From cfe39f2fc361c5eb2e9e27151d155cdebeb13226 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 11:20:27 +0800 Subject: [PATCH 367/374] Update instructions for manual testing --- docs/DeveloperGuide.md | 62 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 23514a73d0..eb23ca18f8 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -140,7 +140,7 @@ This feature allows users to edit their personal information after it has been e * [`editinfo`](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#editing-user-information-editinfo): Edits the user information stored in the application.
    The command is implemented in such a way that **one or more changes to the personal information can be made** using a single command. Below are some examples of valid commands. * `editinfo a/22`: Edits the age of the user to `22` - * `editinfo a/22 c/80`: Edits the age of the user to `22` and the current weight to `80kg`. + * `editinfo a/22 c/80`: Edits the age of the user to `22` and the current weight to `80`. * `editinfo n/Jane g/F a/22 h/165 o/70 c/63 t/60 f/3`: Edit the name, gender, age, height, original, current and target weight as well as the fitness level of the user to `Jane`, `female`, `22`,`165`, `70`, `63`, `60` and `You engage in moderate amount of exercise or have a job that requires moderate physical activity.` respectively. **Main classes and methods used**: @@ -309,3 +309,63 @@ DietBook is designed to **track the food and different kinds of nutritional inta * _Database_ - Contains a list of commonly found food items in the National University of Singapore ## 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. + 1. 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. + 1. Test case: `name Ja/ck`
    + Expected: No name will not be stored in the system and an error message will be displayed. + 1. Test case: `nameJack`
    + Expected: Similar to previous. + 1. Test case: `name`
    + Expected: Similar to previous. + 1. Test case: `Name Jack`
    + Expected: Similar to previous. + +1. 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. + 1. 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. + 1. 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. + 1. 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. + 1. Test case: `infog/M a/21 h/175 o/85 c/85 t/75 f/2`
    + Expected: Similar to previous. + 1. 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. + 1. 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. + 1. 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](UserGuide.md) for more information)
    + Expected: Similar to previous. + 1. 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`. + 1. 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`. + 1. 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](#entering-user-information) 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. + 1. Test case: `userinfo userinfo`
    + Expected: User's personal information will not be displayed. Error message will be shown to user. + 1. Test case: `Userinfo`
    + Expected: Similar to previous. From 3a0d2473a7eac07023d581fc547c012823f17465 Mon Sep 17 00:00:00 2001 From: HengFuYuen <60005925+HengFuYuen@users.noreply.github.com> Date: Mon, 9 Nov 2020 11:24:48 +0800 Subject: [PATCH 368/374] Update _config.yml --- docs/_config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/_config.yml b/docs/_config.yml index 8b13789179..c34df6fb7b 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1 +1,2 @@ - +plugins: + - jemoji From 29d1cd3744d07fd5b12145d0a448222100ce25e1 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 11:25:59 +0800 Subject: [PATCH 369/374] Update HelpCommand display for user guide --- docs/UserGuide.md | 84 ++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 846628f759..b68f127821 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -391,51 +391,45 @@ Format: `help` Output example: ``` Listed below are the valid commands for DietBook: - -For user information related commands - To view user information: userinfo - To edit user information: editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [l/ACTIVITY_LEVEL] - -For database related commands - [Coming Soon] To add a food from the database: add n/FOOD_NAME x/PORTION_SIZE - [Coming Soon] To add a food from the database consumed at a certain time: add n/FOOD_NAME x/PORTION_SIZE yyyy-mm-ddTHH:mm - To view all food in the database: data - -For food list related commands - To add a food not in the database that was just consumed: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] - To add a food not in the database consumed at a certain time: add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] yyyy-mm-ddTHH:mm - To view all food in DietBook: list - To view all food in DietBook recorded within a time period: list yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To view all food in DietBook recorded from a certain date until now: list yyyy-mm-ddTHH:mm - To delete a food from DietBook: delete INDEX - To delete all food items from the DietBook: clear - -For nutritional intake and recommendation related commands - To get recommended calorie intake: recommend - - To calculate carbohydrate intake: calculate carbohydrate - To calculate carbohydrate intake within a time period: calculate carbohydrate yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate carbohydrate intake from a certain date until now: calculate carbohydrate yyy-mm-ddTHH:mm - - To calculate calorie intake: calculate calorie - To calculate calorie intake within a time period: calculate calorie yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate calorie intake from a certain date until now: calculate calorie yyyy-mm-ddTHH:mm - - To calculate protein intake: calculate protein - To calculate protein intake within a time period: calculate protein yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate protein intake from a certain date until now: calculate protein yyyy-mm-ddTHH:mm - - To calculate fat intake: calculate fat - To calculate fat intake within a time period: calculate fat yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate fat intake from a certain date until now: calculate fat yyyy-mm-ddTHH:mm - - To calculate all nutritional intake: calculate all - To calculate all nutritional intake within a time period: calculate all yyyy-mm-ddTHH:mm yyyy-mm-ddTHH:mm - To calculate all nutritional intake from a certain date until now: calculate all yyyy-mm-ddTHH:mm - -For other system related commands - To view a list of valid commands: help - To exit DietBook: exit +__________________________________________________________________________________________________________________________________________ +__________________________________________________________________________________________________________________________________________ + userinfo | To view user information: + | userinfo +__________________________________________________________________________________________________________________________________________ + editinfo | To edit user information: + | editinfo [n/NAME] [g/GENDER] [a/AGE] [h/HEIGHT] [o/ORIGINAL_WEIGHT] [c/CURRENT_WEIGHT] [t/TARGET_WEIGHT] [f/FITNESS_LEVEL] +__________________________________________________________________________________________________________________________________________ + add | To add a food from the database: + | add i/INDEX x/PORTION_SIZE [yyyy-mm-ddTHH:mm] +__________________________________________________________________________________________________________________________________________ + data | To view all food in the database: + | data +__________________________________________________________________________________________________________________________________________ + add | To add a food not in the database: + | add x/PORTION_SIZE n/FOOD_NAME k/CALORIE [c/CARBOHYDRATE] [p/PROTEIN] [f/FAT] [yyyy-mm-ddTHH:mm] +__________________________________________________________________________________________________________________________________________ + list | To view all food in DietBook: + | list [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm] +__________________________________________________________________________________________________________________________________________ + delete | To delete a food from DietBook: + | delete INDEX +__________________________________________________________________________________________________________________________________________ + clear | To delete all food items from the DietBook: + | clear +__________________________________________________________________________________________________________________________________________ + recommend | To get recommended calorie intake: + | recommend +__________________________________________________________________________________________________________________________________________ + calculate | To calculate nutritional intake: + | calculate NUTRIENT_TYPE [yyyy-mm-ddTHH:mm] [yyyy-mm-ddTHH:mm] + | Valid NUTRIENT_TYPE: carb, calorie, fat, protein, all +__________________________________________________________________________________________________________________________________________ + help | To view a list of valid commands: + | help +__________________________________________________________________________________________________________________________________________ + exit | To exit DietBook: + | exit +__________________________________________________________________________________________________________________________________________ ``` #### To exit DietBook: `exit` From 374ac757f1c758475e51f4c866254b01e8f7a87b Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 11:46:17 +0800 Subject: [PATCH 370/374] Fix code snippet formatting with image --- docs/DeveloperGuide.md | 17 +++++------------ docs/images/FitnessLevelInUiMessage.PNG | Bin 0 -> 44908 bytes docs/images/GenderInUiMessage.PNG | Bin 0 -> 13459 bytes 3 files changed, 5 insertions(+), 12 deletions(-) create mode 100644 docs/images/FitnessLevelInUiMessage.PNG create mode 100644 docs/images/GenderInUiMessage.PNG diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index eb23ca18f8..df3238a252 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -22,19 +22,12 @@ The `UiMessage` class has **dependencies** with the following enumeration classe * **Rationale**: Increased coupling was sacrificed to reduce code duplicates and increase ease of code extension/editing. * [`FitnessLevel`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/FitnessLevel.java): Descriptions of the five `FitnessLevel` are required in `UiMessage#getAskForUserInfoMessage(String name)` as shown in the code snippet below. -``` -+ "- Your fitness level, represented by a number from 1 to 5." + UiHelper.LINE_SEPARATOR - + " 1 = " + FitnessLevel.NONE.getDescription() + UiHelper.LINE_SEPARATOR - + " 2 = " + FitnessLevel.LOW.getDescription() + UiHelper.LINE_SEPARATOR - + " 3 = " + FitnessLevel.MEDIUM.getDescription() + UiHelper.LINE_SEPARATOR - + " 4 = " + FitnessLevel.HIGH.getDescription() + UiHelper.LINE_SEPARATOR - + " 5 = " + FitnessLevel.EXTREME.getDescription() + UiHelper.LINE_SEPARATOR -``` + +![FitnessLevel In UiMessage](images/FitnessLevelInUiMessage.PNG) + * [`Gender`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Gender.java): Descriptions of the three `Gender` are required in `UiMessage#getAskForUserInfoMessage(String name)`as shown in the code snippet below. -``` - "- Your gender either F for " + Gender.FEMALE.getDescription() + " or M for " - + Gender.MALE.getDescription() + " or O for " + Gender.OTHERS.getDescription() + "." -``` + +![Gender In UiMessage](images/GenderInUiMessage.PNG) In summary, the `UI` component, * Takes in user command, ensure that it is not empty before passing it to the `Logic` component for command execution. diff --git a/docs/images/FitnessLevelInUiMessage.PNG b/docs/images/FitnessLevelInUiMessage.PNG new file mode 100644 index 0000000000000000000000000000000000000000..9a1536e913032ab15a02fc802a9e55d0a03e0a09 GIT binary patch literal 44908 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 literal 0 HcmV?d00001 diff --git a/docs/images/GenderInUiMessage.PNG b/docs/images/GenderInUiMessage.PNG new file mode 100644 index 0000000000000000000000000000000000000000..42bad55ffc6c853e3a1f9126e71c0b95d4da6ac0 GIT binary patch literal 13459 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 LBvUD69Q^+P?)5h| literal 0 HcmV?d00001 From 6f21a7c501d3e32f159b6af519a81ab39049a151 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 12:01:54 +0800 Subject: [PATCH 371/374] Improve code quality --- docs/DeveloperGuide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index df3238a252..858037e9c8 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -275,6 +275,7 @@ similiar diaghrams for PersonSaveLoadManager 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 From 9670687a66df96fb3568a34e73e816cebf639431 Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 13:47:22 +0800 Subject: [PATCH 372/374] Update multiplicities in the diagram --- docs/diagrams/UiComponent.drawio | 2 +- docs/diagrams/UiComponent.png | Bin 35152 -> 32994 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/diagrams/UiComponent.drawio b/docs/diagrams/UiComponent.drawio index 1d9c26226a..4b6ffdaf74 100644 --- a/docs/diagrams/UiComponent.drawio +++ b/docs/diagrams/UiComponent.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/diagrams/UiComponent.png b/docs/diagrams/UiComponent.png index 8c06d167e4bf0ff1365cbfdd40a0504f040985be..d05516051da3649e211d184cfe0f35563d281770 100644 GIT binary patch literal 32994 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 Date: Mon, 9 Nov 2020 15:37:40 +0800 Subject: [PATCH 373/374] Add hengfuyuen.md --- docs/team/hengfuyuen.md | 60 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/docs/team/hengfuyuen.md b/docs/team/hengfuyuen.md index b40f97b4be..a4d96389ed 100644 --- a/docs/team/hengfuyuen.md +++ b/docs/team/hengfuyuen.md @@ -1,6 +1,64 @@ # Heng Fu Yuen - Project Portfolio Page ## Overview +DietBook is a Command Line Interface (CLI) desktop application designed mainly _NUS students staying on campus_. It helps users **track their food and nutritional intake** as well as provide them with their **daily calorie recommendation**. It also has a **database prepopulated with food items commonly found around NUS** so that thse food items can be easily added to the list of food items consumed for tracking. DietBook is written mainly in Java. +* Table of Contents +{:toc} -### Summary of Contributions \ No newline at end of file +### Summary of Contributions + +#### Code contributed + +* Code contributed: [RepoSense link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=hengfuyuen&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) +* Authored more than 30 [PRs](https://github.com/AY2021S1-CS2113-T14-4/tp/pulls?q=is%3Apr+author%3AHengFuYuen+) + +#### Features implemented + +* Implemented [`Person.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Person.java), [`FitnessLevel.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/FitnessLevel.java) and [`Gender.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/person/Gender.java) to support four commands `name`, `info`, `userinfo` and `editinfo` + * Implemented relevant assertions, logging and JUnit for the above three classes. + +* Implemented [`Ui.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/Ui.java), [`UiHelper.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiHelper.java), [`UiInput.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiInput.java), [`UiOuput.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiOutput.java), [`UiMessage.java`](https://github.com/AY2021S1-CS2113-T14-4/tp/blob/master/src/main/java/seedu/dietbook/ui/UiMessage.java) to support the `Ui Component` which is responsible for taking in user commands, communicating with `Logic` to execute the command and printing out the relevant output or error messages + * Implemented relevant assertions, exceptions, logging and JUnit tests for the above five classes. + +#### Documentation + +* **User Guide**
    + * Added four features related to user information - [Entering username](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-username-name), [Entering user information](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#entering-user-information-info), [Viewing user information](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#viewing-user-information-userinfo) and [Editing user information](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#editing-user-information-editinfo): [#72](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/72/files) [#77](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/77/files) [#94](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/94/files) [#178](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/178/files) + * Added `name`, `info`, `userinfo` and `editinfo` commands to the [Command Summary](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#command-summary): [#72](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/72/files) [#77](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/77/files) [#94](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/94/files) [#178](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/178/files) + * Updated the [Introduction](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#introduction), [Quick start](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#quick-start), and [FAQ](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#faq): [#72](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/72/files) [#77](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/77/files) [#94](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/94/files) [#178](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/178/files) + * Added notes about command format under [Features](https://ay2021s1-cs2113-t14-4.github.io/tp/UserGuide.html#features): [#72](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/72/files) [#77](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/77/files) [#94](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/94/files) [#178](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/178/files) [#188](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/188/files) [#190](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/190/files) + +* **Developer Guide** + * Added the [UI Component](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#ui-component) in the Design section: [#94](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/94/files) [#97](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/97/files) [#194](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/194) [#176](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/176) + * Added the descriptions and diagrams for three features in the implementation section - [Enter user information feature](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#enter-user-information-feature), [Edit user information feature](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#edit-user-information-feature) and [View user information feature](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#view-user-information-feature): [#94](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/94/files) [#97](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/97/files) [#100](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/100/files) [#194](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/194) [#176](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/176) + * Added five [User Stories](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#user-stories) related to user information features: [#194](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/194) + * Updated instructions for manual testing section for three features - [Entering user information](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#entering-user-information), [Editing user information](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#editing-user-information) feature and [Viewing user information](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#viewing-user-information): [#194](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/194) + * Updated the [Target User Profile](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#target-user-profile), [Value Proposition](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#value-proposition), [Non-Functional Requirement](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#non-functional-requirements) and [Glossary](https://ay2021s1-cs2113-t14-4.github.io/tp/DeveloperGuide.html#glossary): [#194](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/194) + +* **Product website** + * Updated the product [Home page](https://ay2021s1-cs2113-t14-4.github.io/tp/) [#171](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/171) + * Added a trivial introduction in [AboutUs Page](https://ay2021s1-cs2113-t14-4.github.io/tp/AboutUs.html): [#176](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/176) + +#### Contributions to team-based tasks + +* Initiate weekly meetings +* Helped in managing `v2.1` submissions +* Helped in managing `v1.0` release +* Managed release [DietBook v2.0](https://github.com/AY2021S1-CS2113-T14-4/tp/releases/tag/v2.0.2) on GitHub +* Authored more than 45 [Issues](https://github.com/AY2021S1-CS2113-T14-4/tp/issues/created_by/HengFuYuen) in the team repo issue tracker +* Helped in bug catching (examples: [#40](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/40) [#104](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/104) [#61](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/61) [#105](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/105)) +* Configure the build.gradle file [#46](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/46/files) [#68](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/68) +* Helped in setting up the tP Organisation and Team Repo - Set up a `developers` team in the organisation, enabled the issue tracker, updated the labels in issue tracker and created the milestones used for managing the project +* Refer to [Documentation](#documentation) section for contributions to user guide, developer guide, etc that are not specific to a feature + +#### Review/mentoring contributions + +* Reviewed more than 30 [PRs](https://github.com/AY2021S1-CS2113-T14-4/tp/pulls?q=is%3Apr+is%3Aopen+reviewed-by%3A%40me+) most of which contains only trivial comments +* Reviewed about 5-10 PRs with non-trivial review comments (examples: [#14](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/14) [#40](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/40) [#71](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/71) [#166](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/166) [#199](https://github.com/AY2021S1-CS2113-T14-4/tp/pull/191)) + +#### Contributions beyond the project team + +* Participated in forum discussions (examples: [#13](https://github.com/nus-cs2113-AY2021S1/forum/issues/13) [#52](https://github.com/nus-cs2113-AY2021S1/forum/issues/52) [#87](https://github.com/nus-cs2113-AY2021S1/forum/issues/87)) +* Reported [seven bugs and suggestions](https://github.com/AY2021S1-CS2113T-W11-4/tp/issues?q=is%3Aissue+HengFuYuen) for another team during the PE Dry run +* Reported [Major bug](https://github.com/AY2021S1-CS2113-T16-4/tp/issues/61) for differnt team during the PE Dry run From 563c8fd3844169c2f8ede8737dfddb5572fdb1fc Mon Sep 17 00:00:00 2001 From: HengFuYuen Date: Mon, 9 Nov 2020 17:30:51 +0800 Subject: [PATCH 374/374] Update AboutUs.md --- docs/AboutUs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index eb8c4bff14..7e3a4c364e 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,8 +4,8 @@ We are a team of students from different faculties in the [National University o Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Heng Fu Yuen | [Github](https://github.com/HengFuYuen) | [Portfolio](docs/team/hengfuyuen.md) +![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Heng Fu Yuen | [Github](https://github.com/HengFuYuen) | [Portfolio](team/hengfuyuen.md)

  • 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�*kGnH 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 Date: Wed, 28 Oct 2020 19:57:48 +0800 Subject: [PATCH 224/374] complete architecture --- docs/Zhong_Ming_developer_Guide/zm_DG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Zhong_Ming_developer_Guide/zm_DG.md b/docs/Zhong_Ming_developer_Guide/zm_DG.md index 50abef666a..0ecbca8d7f 100644 --- a/docs/Zhong_Ming_developer_Guide/zm_DG.md +++ b/docs/Zhong_Ming_developer_Guide/zm_DG.md @@ -5,12 +5,13 @@ At the base of the package, there is the Saver and Loader class. ### Architecture +![Alt text](Architecture.png) 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 -![Alt text](Architecture.png) + Stores data in a internal table with length and height specified. Handles the storage of its data by writing to a text file. From d1e0651b81ec64f797cf3d889addd4ea12708d66 Mon Sep 17 00:00:00 2001 From: snowbanana12345 <50855780+snowbanana12345@users.noreply.github.com> Date: Wed, 28 Oct 2020 20:13:34 +0800 Subject: [PATCH 225/374] manually moved UML diaghrams and DG over to this branch --- UML_diaghrams/Architecture.puml | 19 ++++++ UML_diaghrams/FoodSaveLoadManager_load.puml | 24 ++++++++ UML_diaghrams/FoodSaveLoadManager_save.puml | 24 ++++++++ UML_diaghrams/UML diagram.puml | 15 +++++ .../Architecture.png | Bin 0 -> 43003 bytes .../FoodSaveLoadManager_load.png | Bin 0 -> 77795 bytes .../FoodSaveLoadManager_save.png | Bin 0 -> 75922 bytes docs/Zhong_Ming_developer_Guide/zm_DG.md | 55 ++++++++++++++++++ 8 files changed, 137 insertions(+) create mode 100644 UML_diaghrams/Architecture.puml create mode 100644 UML_diaghrams/FoodSaveLoadManager_load.puml create mode 100644 UML_diaghrams/FoodSaveLoadManager_save.puml create mode 100644 UML_diaghrams/UML diagram.puml create mode 100644 docs/Zhong_Ming_developer_Guide/Architecture.png create mode 100644 docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_load.png create mode 100644 docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png create mode 100644 docs/Zhong_Ming_developer_Guide/zm_DG.md diff --git a/UML_diaghrams/Architecture.puml b/UML_diaghrams/Architecture.puml new file mode 100644 index 0000000000..d4ec941ef8 --- /dev/null +++ b/UML_diaghrams/Architecture.puml @@ -0,0 +1,19 @@ +@startuml +object Saver +object Loader +object FoodSaveLoadManager +object PersonSaveLoadManager +object Command +object File + +File <-up-> Saver +File <-up-> Loader + +Saver <-up-> FoodSaveLoadManager +Saver <-up-> PersonSaveLoadManager +Loader <-up-> FoodSaveLoadManager +Loader <-up-> PersonSaveLoadManager + +FoodSaveLoadManager <-up-> Command +PersonSaveLoadManager <-up-> Command +@enduml diff --git a/UML_diaghrams/FoodSaveLoadManager_load.puml b/UML_diaghrams/FoodSaveLoadManager_load.puml new file mode 100644 index 0000000000..51669578f2 --- /dev/null +++ b/UML_diaghrams/FoodSaveLoadManager_load.puml @@ -0,0 +1,24 @@ +@startuml + +-> FoodSaveLoadManager : load() +activate FoodSaveLoadManager + +FoodSaveLoadManager -> Loader : static load() + +activate Loader +Loader -> FileLoader : load() + +activate FileLoader +loop all lines + activate Scanner + FileLoader -> Scanner : readline() + Scanner --> FileLoader : line data + destroy Scanner +end +FileLoader --> Loader : FileLoader +deactivate FileLoader + +Loader --> FoodSaveLoadManager : FileLoader +deactivate Loader +deactivate FoodSaveLoadManager +@enduml \ No newline at end of file diff --git a/UML_diaghrams/FoodSaveLoadManager_save.puml b/UML_diaghrams/FoodSaveLoadManager_save.puml new file mode 100644 index 0000000000..ea1c8a4805 --- /dev/null +++ b/UML_diaghrams/FoodSaveLoadManager_save.puml @@ -0,0 +1,24 @@ +@startuml +-> FoodSaveLoadManager : save +activate FoodSaveLoadManager + + +loop all food items + loop every entry in a food object + activate Saver + FoodSaveLoadManager -> Saver : add() + end +end + +FoodSaveLoadManager -> Saver : save() +deactivate Saver + +activate FileWriter +loop all entries in Saver table + Saver -> FileWriter : write() +end +destroy FileWriter + +deactivate FoodSaveLoadManager + +@enduml \ No newline at end of file diff --git a/UML_diaghrams/UML diagram.puml b/UML_diaghrams/UML diagram.puml new file mode 100644 index 0000000000..c82d8be9d3 --- /dev/null +++ b/UML_diaghrams/UML diagram.puml @@ -0,0 +1,15 @@ + +@startuml +-> FoodSaveLoadManager : save + +loop all food items + loop every entry in a food object + FoodSaveLoadManager -> Saver : add() + end +end + +FoodSaveLoadManager -> Saver : save() +loop all entries in Saver table + Saver -> FileWriter : write() +end +@enduml \ No newline at end of file diff --git a/docs/Zhong_Ming_developer_Guide/Architecture.png b/docs/Zhong_Ming_developer_Guide/Architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..1ca758c26adedd91685225c66df5578ba73afbcc GIT binary patch literal 43003 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%0Agux07_eCdzyRD<{cnP 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 literal 0 HcmV?d00001 diff --git a/docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png b/docs/Zhong_Ming_developer_Guide/FoodSaveLoadManager_save.png new file mode 100644 index 0000000000000000000000000000000000000000..61891d2a8d921406abaf721cc2d7ec66c4a22801 GIT binary patch literal 75922 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