From ec5aa8c234a2abd26fa4dd29df304ab330db7971 Mon Sep 17 00:00:00 2001 From: Paturi Karthik <64789669+paturikarthik@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:20:06 +0800 Subject: [PATCH 001/414] Update README.md --- docs/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..e4fb456ea5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,4 @@ -# Duke - +# LifeTrack {Give product intro here} Useful links: From c2d18df5ddcc987f0c7df9eb04125b572466e440 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:51:55 +0800 Subject: [PATCH 002/414] yanyu --- docs/AboutUs.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..a2a5443d80 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,7 @@ # About us -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) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +Display | Name | Github Profile | Portfolio +--------|:-----:|:--------------:|:---------: +![](https://via.placeholder.com/100.png?text=Photo) | Yanyu | [Github](https://github.com/a-wild-chocolate/) | [Portfolio](docs/team/johndoe.md) + + From 7e98e37eed4a0a76b62cc24f57d8e82087e97d98 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 8 Mar 2024 15:54:08 +0800 Subject: [PATCH 003/414] Trial run --- docs/AboutUs.md | 10 +++------- docs/team/paturikarthik.md | 7 +++++++ 2 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 docs/team/paturikarthik.md diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..4d4c57be50 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,5 @@ # About us -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) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +Display | Name | Github Profile | Portfolio +--------|:--------------:|:------------------------------------------:|:---------: +![](https://drive.google.com/file/d/1uMHCtP7f5p0-rQEjezbzxItLioc5VzM5/view?usp=drive_link) | Paturi Karthik | [Github](https://github.com/paturikarthik) | [Portfolio](docs/team/paturikarthik.md) diff --git a/docs/team/paturikarthik.md b/docs/team/paturikarthik.md new file mode 100644 index 0000000000..aeb15c98d7 --- /dev/null +++ b/docs/team/paturikarthik.md @@ -0,0 +1,7 @@ +# Paturi Karthik - Project Portfolio Page + +## Overview +Hello + +### Summary of Contributions +Hello \ No newline at end of file From 25c3805e8960d95ca126ec896a864a2cb33adc34 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 8 Mar 2024 15:54:41 +0800 Subject: [PATCH 004/414] Edit AboutUs.md to include rex yong details --- docs/AboutUs.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..a3c3fd1317 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,5 @@ # About us -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) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +Display | Name | Github Profile | Portfolio +--------|:------------------:|:--------------:|:---------: +![](https://drive.google.com/file/d/1BM0lQP13brp_vlVJsgHYzQHSWaREHrnP/view?usp=drive_link) | Rex Yong Jin Xiang | [Github](https://github.com/rexyyong) | [Portfolio](docs/team/johndoe.md) From 722b918ba8311d46e3ca524bccea38ee48ab9d76 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:54:57 +0800 Subject: [PATCH 005/414] Add shawnpong.md and README --- docs/AboutUs.md | 10 +++------- docs/team/shawnpong.md | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) create mode 100644 docs/team/shawnpong.md diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..4a79d561d5 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,5 @@ # About us -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) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +Display | Name | Github Profile | Portfolio +--------|:----------:|:--------------------------------------:|:---------: +![](https://avatars.githubusercontent.com/u/110764881?s=400&u=f41e3f40315f394bd71538063882c06bcfa2b624&v=4) | Shawn Pong | [Github](https://github.com/shawnpong) | [Portfolio](docs/team/shawnpong.md) diff --git a/docs/team/shawnpong.md b/docs/team/shawnpong.md new file mode 100644 index 0000000000..15b8fa817c --- /dev/null +++ b/docs/team/shawnpong.md @@ -0,0 +1 @@ +this is my profile :D \ No newline at end of file From 4ed14607e5a4c6597ffb637527c5319d53021386 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 8 Mar 2024 16:32:28 +0800 Subject: [PATCH 006/414] Update PP --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 771f549458..b8d6a36d07 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,5 +2,5 @@ Display | Name | Github Profile | Portfolio --------|:--------------:|:------------------------------------------:|:---------: -![](https://drive.google.com/file/d/1uMHCtP7f5p0-rQEjezbzxItLioc5VzM5/view?usp=drive_link) | Paturi Karthik | [Github](https://github.com/paturikarthik) | [Portfolio](docs/team/paturikarthik.md) +![](https://avatars.githubusercontent.com/u/64789669?v=4) | Paturi Karthik | [Github](https://github.com/paturikarthik) | [Portfolio](docs/team/paturikarthik.md) ![](https://via.placeholder.com/100.png?text=Photo) | Yanyu | [Github](https://github.com/a-wild-chocolate/) | [Portfolio](docs/team/johndoe.md) From 6c6b117d445f285d846634608583c8af1e151472 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Mon, 11 Mar 2024 15:32:17 +0800 Subject: [PATCH 007/414] Create Template --- build.gradle | 4 +- src/main/java/seedu/duke/Duke.java | 21 ---------- src/main/java/seedu/lifetrack/LifeTrack.java | 30 +++++++++++++++ .../seedu/lifetrack/activity/Activity.java | 38 +++++++++++++++++++ .../lifetrack/calorielist/CalorieList.java | 13 +++++++ .../seedu/lifetrack/calorielist/Entry.java | 31 +++++++++++++++ .../seedu/lifetrack/calories/Calorie.java | 27 +++++++++++++ .../seedu/{duke => lifetrack}/DukeTest.java | 2 +- 8 files changed, 142 insertions(+), 24 deletions(-) delete mode 100644 src/main/java/seedu/duke/Duke.java create mode 100644 src/main/java/seedu/lifetrack/LifeTrack.java create mode 100644 src/main/java/seedu/lifetrack/activity/Activity.java create mode 100644 src/main/java/seedu/lifetrack/calorielist/CalorieList.java create mode 100644 src/main/java/seedu/lifetrack/calorielist/Entry.java create mode 100644 src/main/java/seedu/lifetrack/calories/Calorie.java rename src/test/java/seedu/{duke => lifetrack}/DukeTest.java (88%) diff --git a/build.gradle b/build.gradle index ea82051fab..57f81c0f7c 100644 --- a/build.gradle +++ b/build.gradle @@ -29,11 +29,11 @@ test { } application { - mainClass.set("seedu.duke.Duke") + mainClass.set("seedu.duke.LifeTrack") } shadowJar { - archiveBaseName.set("duke") + archiveBaseName.set("lifetrack") archiveClassifier.set("") } 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/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java new file mode 100644 index 0000000000..f4a1fd7424 --- /dev/null +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -0,0 +1,30 @@ +package seedu.lifetrack; + +import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.calories.Calorie; + +import java.util.Scanner; + +public class LifeTrack { + /** + * Main entry-point for the java.lifetrack.LifeTrack application. + */ + public static void main(String[] args) { +// String logo = " ____ _ \n" +// + "| _ \\ _ _| | _____ \n" +// + "| | | | | | | |/ / _ \\\n" +// + "| |_| | |_| | < __/\n" +// + "|____/ \\__,_|_|\\_\\___|\n"; + String logo = "LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + + "L I F E T R R A A C C K K \n" + + "LLL I FFFF EEEE T RRRR AAAAA C KK \n" + + "L I F E T R R A A C C K K \n" + + "LLLLL IIIII F EEEEE TTTT R R A A CCC K K\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()); + CalorieList calorieList = new CalorieList(); + } +} diff --git a/src/main/java/seedu/lifetrack/activity/Activity.java b/src/main/java/seedu/lifetrack/activity/Activity.java new file mode 100644 index 0000000000..b4daebbcc8 --- /dev/null +++ b/src/main/java/seedu/lifetrack/activity/Activity.java @@ -0,0 +1,38 @@ +package seedu.lifetrack.activity; + +public class Activity { + + private String date; + private String time; + private String description; + + public Activity(String date, String time,String description){ + this.date= date; + this.time= time; + this.description=description; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java new file mode 100644 index 0000000000..3e93d0a59d --- /dev/null +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -0,0 +1,13 @@ +package seedu.lifetrack.calorielist; + +import seedu.lifetrack.calories.Calorie; + +import java.util.ArrayList; + +public class CalorieList { + public ArrayList calorieArrayList; + + public CalorieList(){ + calorieArrayList= new ArrayList<>(); + } +} diff --git a/src/main/java/seedu/lifetrack/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calorielist/Entry.java new file mode 100644 index 0000000000..9b47a3faf9 --- /dev/null +++ b/src/main/java/seedu/lifetrack/calorielist/Entry.java @@ -0,0 +1,31 @@ +package seedu.lifetrack.calorielist; + +import seedu.lifetrack.activity.Activity; +import seedu.lifetrack.calories.Calorie; + +public class Entry { + + private Activity activity; + private Calorie calorie; + + public Entry(Activity activity, Calorie calorie){ + this.calorie = calorie; + this.activity= activity; + } + + public Activity getActivity() { + return activity; + } + + public void setActivity(Activity activity) { + this.activity = activity; + } + + public Calorie getCalorie() { + return calorie; + } + + public void setCalorie(Calorie calorie) { + this.calorie = calorie; + } +} diff --git a/src/main/java/seedu/lifetrack/calories/Calorie.java b/src/main/java/seedu/lifetrack/calories/Calorie.java new file mode 100644 index 0000000000..d75b5ca1c0 --- /dev/null +++ b/src/main/java/seedu/lifetrack/calories/Calorie.java @@ -0,0 +1,27 @@ +package seedu.lifetrack.calories; + +public class Calorie { + + private int calories; + private boolean isIntake; + public Calorie (int calories, boolean isIntake){ + this.calories = calories; + this.isIntake = isIntake; + } + + public int getCalories() { + return calories; + } + + public void setCalories(int calories) { + this.calories = calories; + } + + public boolean isIntake() { + return isIntake; + } + + public void setIntake(boolean intake) { + isIntake = intake; + } +} diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java similarity index 88% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/lifetrack/DukeTest.java index 2dda5fd651..903db4b48d 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -1,4 +1,4 @@ -package seedu.duke; +package seedu.lifetrack; import static org.junit.jupiter.api.Assertions.assertTrue; From bf6bbe44a3d3075e3e4c5c35508f080da0cdf33e Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:56:37 +0800 Subject: [PATCH 008/414] add deleteCalorie --- .../lifetrack/calorielist/CalorieList.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 3e93d0a59d..88b2604af1 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -10,4 +10,25 @@ public class CalorieList { public CalorieList(){ calorieArrayList= new ArrayList<>(); } + + /** + * Index should be in an integer from 1 to size of the list. + * @param index the index of calorie record user want to delete + */ + public void deleteCalorie(int index) + { + try{ + if(index>calorieArrayList.size()) + { + System.out.println("Sorry, this index is out of out of range. Please enter a valid index."); + return; + } + this.calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 + } catch (IndexOutOfBoundsException e) + { + System.out.println("Sorry, this index is invalid. Please enter a positive integer."); + } + + + } } From d1ba1960e2db5ec6447d6397c380c3f826718690 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:03:12 +0800 Subject: [PATCH 009/414] add successful msg. --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 88b2604af1..263d00dc15 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -24,11 +24,13 @@ public void deleteCalorie(int index) return; } this.calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 + System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { System.out.println("Sorry, this index is invalid. Please enter a positive integer."); } + } } From f4ed49a92c03107e1be34fc02b927dce777038de Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:04:05 +0800 Subject: [PATCH 010/414] modify format --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 263d00dc15..89dd82d632 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -29,8 +29,6 @@ public void deleteCalorie(int index) { System.out.println("Sorry, this index is invalid. Please enter a positive integer."); } - - - } + } From fb444e5c2d2fa39467a0365e8325982313c7dc15 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:06:49 +0800 Subject: [PATCH 011/414] Add Ui Class to with readUserInput method Ui Class handles the interactions with user readUserInput reads in what user keys in and according to input it calls calorieIn method to add calorie Intake --- src/main/java/seedu/lifetrack/LifeTrack.java | 1 + src/main/java/seedu/lifetrack/ui/Ui.java | 31 ++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/seedu/lifetrack/ui/Ui.java diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index f4a1fd7424..87a155365f 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -26,5 +26,6 @@ public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.println("Hello " + in.nextLine()); CalorieList calorieList = new CalorieList(); + Ui.readUserInput(); } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java new file mode 100644 index 0000000000..1f0ead8132 --- /dev/null +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -0,0 +1,31 @@ +package seedu.lifetrack.ui; + +import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.calories.Calorie; + +import java.util.Scanner; + +/** + * Reads user input from the console and processes it. + * + * This method continuously reads input from the console until the user + * inputs "bye". For each input line, it checks if it's empty and prompts + * the user to enter a non-empty input if it is. If the input line starts + * with "calories in", it attempts to parse the input as calorie intake + * information using the calorieIn method from the CalorieList class. + */ +public class Ui { + public static void readUserInput() { + String line; + Scanner in = new Scanner(System.in); + line = in.nextLine(); + while (!line.equalsIgnoreCase("bye")) { + if (line.trim().isEmpty()) { + System.out.println("Please enter a non empty Input!"); + } else if (line.startsWith("calories in")) { + CalorieList.calorieIn(line); + } + line = in.nextLine(); + } + } +} From c7d1e2184b69319c56f231828b90c2116b9c471b Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:08:23 +0800 Subject: [PATCH 012/414] Add Parser Class to extract input information Add parseCaloriesIn method in class Parser to read in the date, time, activity, calories typed in by user --- .../lifetrack/calorielist/CalorieList.java | 1 + .../java/seedu/lifetrack/parser/Parser.java | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/main/java/seedu/lifetrack/parser/Parser.java diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 89dd82d632..75265f3c51 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -31,4 +31,5 @@ public void deleteCalorie(int index) } } + Entry newEntry = Parser.parseCaloriesIn(input); } diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/parser/Parser.java new file mode 100644 index 0000000000..b6356568b4 --- /dev/null +++ b/src/main/java/seedu/lifetrack/parser/Parser.java @@ -0,0 +1,55 @@ +package seedu.lifetrack.parser; +import seedu.lifetrack.activity.Activity; +import seedu.lifetrack.calorielist.Entry; +import seedu.lifetrack.calories.Calorie; +import seedu.lifetrack.exceptions.InvalidInputException; + +public class Parser { + + + + /** + * Parses a string input to create an Entry object representing calorie intake. + * + * This method expects the input string to follow a specific format, where the + * date, time, activity description, and calorie count are separated by the + * delimiters 'd/', 't/', 'a/', and 'c/'. The method extracts these components + * and creates an Entry object containing an Activity and a Calorie object. + * If any part of the input is missing or empty, an InvalidInputException is thrown. + * + * @param input the input string to be parsed, containing date, time, activity, + * and calorie count information + * @return an Entry object representing calorie intake + * @throws InvalidInputException if the input string is missing components or + * contains empty fields + */ + public static Entry parseCaloriesIn(String input) throws InvalidInputException { + //splits string according to d/ , t/ , a/ , c/ keyword + String[] parts = input.split("d/|t/|a/|c/"); + + //parts length less than 5 means that not all split keywords were keyed in + if (parts.length < 5) { + throw new InvalidInputException(); + } + + //extracts date, time, activity, calories_in portion from input + String date = parts[1].trim(); + String time = parts[2].trim(); + String description = parts[3].trim(); + String strCalories = parts[4].trim(); + //ensures that all inputs are not empty + if (date.isEmpty() || time.isEmpty() || description.isEmpty() || strCalories.isEmpty()) { + throw new InvalidInputException(); + } + int calories = Integer.parseInt(strCalories); + + //create objects for Activity, Calorie + Activity activityToAdd = new Activity(date, time, description); + Calorie caloriesConsumed = new Calorie(calories, true); + + //create Object Entry to be returned + Entry newCalorieInEntry = new Entry(activityToAdd, caloriesConsumed); + return newCalorieInEntry; + } + +} From 02315f6f8df84b3dad45ae7245033d489c2bcb39 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:09:35 +0800 Subject: [PATCH 013/414] Include line to add newEntry data from parser into calorieArrayList --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 75265f3c51..2e278e8e18 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -1,11 +1,13 @@ package seedu.lifetrack.calorielist; import seedu.lifetrack.calories.Calorie; +import seedu.lifetrack.parser.Parser; import java.util.ArrayList; public class CalorieList { public ArrayList calorieArrayList; + public static ArrayList calorieArrayList; public CalorieList(){ calorieArrayList= new ArrayList<>(); @@ -32,4 +34,5 @@ public void deleteCalorie(int index) } Entry newEntry = Parser.parseCaloriesIn(input); + calorieArrayList.add(newEntry); } From 34592ea10c14344cebe63f4c534bb62f5d720c67 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:10:26 +0800 Subject: [PATCH 014/414] Include InvalidInputException handling --- .../java/seedu/lifetrack/calorielist/CalorieList.java | 11 ++++++++++- .../lifetrack/exceptions/InvalidInputException.java | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 2e278e8e18..8a061bece5 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -2,11 +2,11 @@ import seedu.lifetrack.calories.Calorie; import seedu.lifetrack.parser.Parser; +import seedu.lifetrack.exceptions.InvalidInputException; import java.util.ArrayList; public class CalorieList { - public ArrayList calorieArrayList; public static ArrayList calorieArrayList; public CalorieList(){ @@ -33,6 +33,15 @@ public void deleteCalorie(int index) } } + public static void calorieIn(String input) { + try { Entry newEntry = Parser.parseCaloriesIn(input); calorieArrayList.add(newEntry); + } catch (InvalidInputException e) { + System.out.println("Ensure you follow format with no missing inputs!:" + + " calories in d/DATE t/TIME a/ACTIVITY c/CALORIES_IN"); + } + + } + } diff --git a/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java new file mode 100644 index 0000000000..600bf74139 --- /dev/null +++ b/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java @@ -0,0 +1,7 @@ +package seedu.lifetrack.exceptions; + +public class InvalidInputException extends Exception { + public InvalidInputException() { + System.out.println("Please ensure that you have keyed in the correct format!"); + } +} From eb14cffbe68c0fab0d394e9cb83a56cd92e2562c Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:10:49 +0800 Subject: [PATCH 015/414] Add byeMessage method in main class --- src/main/java/seedu/lifetrack/LifeTrack.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 87a155365f..aa38cd6f27 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -2,6 +2,7 @@ import seedu.lifetrack.calorielist.CalorieList; import seedu.lifetrack.calories.Calorie; +import seedu.lifetrack.ui.Ui; import java.util.Scanner; @@ -27,5 +28,10 @@ public static void main(String[] args) { System.out.println("Hello " + in.nextLine()); CalorieList calorieList = new CalorieList(); Ui.readUserInput(); + byeMessage(); + } + + public static void byeMessage() { + System.out.println("Bye! See you again soon ^^"); } } From dd49d7efd1b89b702ddb61bd05e168e010d14f5e Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:11:14 +0800 Subject: [PATCH 016/414] Add javadoc in Class CalorieList for method calorieIn --- .../java/seedu/lifetrack/calorielist/CalorieList.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 8a061bece5..9cda763696 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -33,6 +33,17 @@ public void deleteCalorie(int index) } } + /** + * Parses a string input representing calorie intake and adds it to the calorie list. + * + * This method takes a string input representing calorie intake information and + * attempts to parse it using the parseCaloriesIn method from the Parser class. + * If the input format is incorrect or contains missing components, it catches + * the InvalidInputException and prints an error message. Otherwise, it adds + * the parsed Entry object to the calorieArrayList. + * + * @param input the input string containing date, time, activity, and calorie count + */ public static void calorieIn(String input) { try { Entry newEntry = Parser.parseCaloriesIn(input); From fe6a501cc3738d2fd3cef2b7a8ed7c3b760b3bf3 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:29:18 +0800 Subject: [PATCH 017/414] Fix formatting error and remove unused imports --- .../lifetrack/calorielist/CalorieList.java | 17 ++++++----------- src/main/java/seedu/lifetrack/ui/Ui.java | 1 - 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 9cda763696..d6497b2270 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -1,6 +1,5 @@ package seedu.lifetrack.calorielist; -import seedu.lifetrack.calories.Calorie; import seedu.lifetrack.parser.Parser; import seedu.lifetrack.exceptions.InvalidInputException; @@ -9,7 +8,7 @@ public class CalorieList { public static ArrayList calorieArrayList; - public CalorieList(){ + public CalorieList() { calorieArrayList= new ArrayList<>(); } @@ -17,18 +16,16 @@ public CalorieList(){ * Index should be in an integer from 1 to size of the list. * @param index the index of calorie record user want to delete */ - public void deleteCalorie(int index) - { - try{ - if(index>calorieArrayList.size()) - { + public void deleteCalorie(int index) { + try { + if(index > calorieArrayList.size()) { System.out.println("Sorry, this index is out of out of range. Please enter a valid index."); return; } this.calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 + calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); - } catch (IndexOutOfBoundsException e) - { + } catch (IndexOutOfBoundsException e) { System.out.println("Sorry, this index is invalid. Please enter a positive integer."); } } @@ -52,7 +49,5 @@ public static void calorieIn(String input) { System.out.println("Ensure you follow format with no missing inputs!:" + " calories in d/DATE t/TIME a/ACTIVITY c/CALORIES_IN"); } - } - } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 1f0ead8132..3a484255fb 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -1,7 +1,6 @@ package seedu.lifetrack.ui; import seedu.lifetrack.calorielist.CalorieList; -import seedu.lifetrack.calories.Calorie; import java.util.Scanner; From e734cc7c2d6cc251d2b7590aa5bd21d066d52671 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:30:39 +0800 Subject: [PATCH 018/414] Edit deleteCalorie method in Class CalorieList --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index d6497b2270..f2558a7d51 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -22,7 +22,7 @@ public void deleteCalorie(int index) { System.out.println("Sorry, this index is out of out of range. Please enter a valid index."); return; } - this.calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 +// this.calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { From b42ae27b08733902df9a63358978aae9ce2e9cc9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:33:37 +0800 Subject: [PATCH 019/414] Remove unused import and redundant lines --- src/main/java/seedu/lifetrack/LifeTrack.java | 6 ------ src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 1 - src/main/java/seedu/lifetrack/parser/Parser.java | 1 + src/main/java/seedu/lifetrack/ui/Ui.java | 1 - src/test/java/seedu/lifetrack/DukeTest.java | 1 - 5 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index aa38cd6f27..c4df5dd43d 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -1,7 +1,6 @@ package seedu.lifetrack; import seedu.lifetrack.calorielist.CalorieList; -import seedu.lifetrack.calories.Calorie; import seedu.lifetrack.ui.Ui; import java.util.Scanner; @@ -11,11 +10,6 @@ public class LifeTrack { * Main entry-point for the java.lifetrack.LifeTrack application. */ public static void main(String[] args) { -// String logo = " ____ _ \n" -// + "| _ \\ _ _| | _____ \n" -// + "| | | | | | | |/ / _ \\\n" -// + "| |_| | |_| | < __/\n" -// + "|____/ \\__,_|_|\\_\\___|\n"; String logo = "LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + "L I F E T R R A A C C K K \n" + "LLL I FFFF EEEE T RRRR AAAAA C KK \n" + diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index f2558a7d51..986b61bfb8 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -2,7 +2,6 @@ import seedu.lifetrack.parser.Parser; import seedu.lifetrack.exceptions.InvalidInputException; - import java.util.ArrayList; public class CalorieList { diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/parser/Parser.java index b6356568b4..5577b68af7 100644 --- a/src/main/java/seedu/lifetrack/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/parser/Parser.java @@ -1,4 +1,5 @@ package seedu.lifetrack.parser; + import seedu.lifetrack.activity.Activity; import seedu.lifetrack.calorielist.Entry; import seedu.lifetrack.calories.Calorie; diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 3a484255fb..bcc1de9457 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -1,7 +1,6 @@ package seedu.lifetrack.ui; import seedu.lifetrack.calorielist.CalorieList; - import java.util.Scanner; /** diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index 903db4b48d..98886ab812 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -1,7 +1,6 @@ package seedu.lifetrack; import static org.junit.jupiter.api.Assertions.assertTrue; - import org.junit.jupiter.api.Test; class DukeTest { From f552f5df1f9ecc2ba997afab534fee4f89e9ae23 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 02:51:16 +0800 Subject: [PATCH 020/414] Remove unnecessary line --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 986b61bfb8..74d216a68d 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -21,7 +21,6 @@ public void deleteCalorie(int index) { System.out.println("Sorry, this index is out of out of range. Please enter a valid index."); return; } -// this.calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { From 6a1a1b7b1cfda202537a1de47b97c4b6b9fd725e Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 09:34:42 +0800 Subject: [PATCH 021/414] Fix Could not find or load main class seedu.duke.LifeTrack error in Gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 57f81c0f7c..78b36ff434 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ test { } application { - mainClass.set("seedu.duke.LifeTrack") + mainClass.set("seedu.LifeTrack") } shadowJar { From 90b8bf3fbf06af3289dd6b49d3a1beb0c31a5468 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 10:03:38 +0800 Subject: [PATCH 022/414] Edit build.gradle to fix gradle error --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 78b36ff434..b0da4382a9 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ test { } application { - mainClass.set("seedu.LifeTrack") + mainClass.set("seedu.lifetrack.LifeTrack") } shadowJar { From c39506b2de8f4933466b1a87673aaed067b85c49 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 10:34:37 +0800 Subject: [PATCH 023/414] Fix error in Gradle Error: Exception in thread "main" java.util.NoSuchElementException: No line found at java.base/java.util.Scanner.nextLine(Scanner.java:1651) at seedu.lifetrack.ui.Ui.readUserInput(Ui.java:19) at seedu.lifetrack.LifeTrack.main(LifeTrack.java:24) --- src/main/java/seedu/lifetrack/ui/Ui.java | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index bcc1de9457..8b975fa2c8 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -14,16 +14,22 @@ */ public class Ui { public static void readUserInput() { + String line; Scanner in = new Scanner(System.in); - line = in.nextLine(); - while (!line.equalsIgnoreCase("bye")) { - if (line.trim().isEmpty()) { - System.out.println("Please enter a non empty Input!"); - } else if (line.startsWith("calories in")) { - CalorieList.calorieIn(line); - } + if(in.hasNextLine()) { line = in.nextLine(); + while (!line.equalsIgnoreCase("bye")) { + if (line.trim().isEmpty()) { + System.out.println("Please enter a non empty Input!"); + } else if (line.startsWith("calories in")) { + CalorieList.calorieIn(line); + } + + if(in.hasNextLine()) { + line = in.nextLine(); + } + } } } } From 21412f1055352c72dd4fca0483cfa94f6f390f68 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 10:49:10 +0800 Subject: [PATCH 024/414] Fix Gradle error relating to runtest.sh ./runtest.sh: line 14: dos2unix: command not found --- text-ui-test/EXPECTED.TXT | 9 --------- text-ui-test/input.txt | 1 - 2 files changed, 10 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..e69de29bb2 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +0,0 @@ -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 00af700e7c4bf238410805e7c26cca7368d5ee30 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 13 Mar 2024 11:01:58 +0800 Subject: [PATCH 025/414] Fix Gradle error due to runtest.sh Error: Exception in thread "main" java.util.NoSuchElementException: No line found at java.base/java.util.Scanner.nextLine(Scanner.java:1651) at seedu.lifetrack.LifeTrack.main(LifeTrack.java:22) ./runtest.sh: line 14: dos2unix: command not found --- src/main/java/seedu/lifetrack/LifeTrack.java | 6 +++--- text-ui-test/EXPECTED.TXT | 10 ++++++++++ text-ui-test/input.txt | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index c4df5dd43d..25f06eb71e 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -11,9 +11,9 @@ public class LifeTrack { */ public static void main(String[] args) { String logo = "LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + - "L I F E T R R A A C C K K \n" + - "LLL I FFFF EEEE T RRRR AAAAA C KK \n" + - "L I F E T R R A A C C K K \n" + + "L I F E T R R A A C C K K\n" + + "LLL I FFFF EEEE T RRRR AAAAA C KK\n" + + "L I F E T R R A A C C K K\n" + "LLLLL IIIII F EEEEE TTTT R R A A CCC K K\n"; System.out.println("Hello from\n" + logo); System.out.println("What is your name?"); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index e69de29bb2..56020b1c5c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -0,0 +1,10 @@ +Hello from +LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K +L I F E T R R A A C C K K +LLL I FFFF EEEE T RRRR AAAAA C KK +L I F E T R R A A C C K K +LLLLL IIIII F EEEEE TTTT R R A A CCC K K + +What is your name? +Hello Rex +Bye! See you again soon ^^ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..019748b3a5 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1 @@ +Rex \ No newline at end of file From 9b1c9b0a83170c684e81242a00ce70dcb5150772 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:20:00 +0800 Subject: [PATCH 026/414] Add caloric list function --- .../lifetrack/calorielist/CalorieList.java | 21 +++++++++++++++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 2 ++ 2 files changed, 23 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 74d216a68d..5bde5cbcf0 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -1,5 +1,7 @@ package seedu.lifetrack.calorielist; +import seedu.lifetrack.activity.Activity; +import seedu.lifetrack.calories.Calorie; import seedu.lifetrack.parser.Parser; import seedu.lifetrack.exceptions.InvalidInputException; import java.util.ArrayList; @@ -48,4 +50,23 @@ public static void calorieIn(String input) { " calories in d/DATE t/TIME a/ACTIVITY c/CALORIES_IN"); } } + + /** + * Prints the list of calorie entries along with its activity description. + * If the list is empty, it prints a message indicating that the list is empty. + * Otherwise, it prints each entry's activity description and calorie count. + */ + public static void printCalorieList() { + if (calorieArrayList.isEmpty()) { + System.out.println("Your caloric list is empty."); + } else { + System.out.println("Caloric List: "); + for (int i = 0; i < calorieArrayList.size(); i++) { + Entry entry = calorieArrayList.get(i); + Activity activity = entry.getActivity(); + Calorie calorie = entry.getCalorie(); + System.out.println((i + 1) + ". Activity: " + activity.getDescription() + ", Calories: " + calorie.getCalories()); + } + } + } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 8b975fa2c8..ed0031e67a 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -24,6 +24,8 @@ public static void readUserInput() { System.out.println("Please enter a non empty Input!"); } else if (line.startsWith("calories in")) { CalorieList.calorieIn(line); + } else if (line.startsWith("list")) { + CalorieList.printCalorieList(); } if(in.hasNextLine()) { From 9c9883f818ae0b30437c23ba09a403693928a938 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:29:41 +0800 Subject: [PATCH 027/414] Add testDeleteCalorie Junit test function --- src/test/java/seedu/lifetrack/DukeTest.java | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index 98886ab812..9c33ac3476 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -1,11 +1,42 @@ package seedu.lifetrack; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import seedu.lifetrack.activity.Activity; +import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.calorielist.Entry; +import seedu.lifetrack.calories.Calorie; + +import java.util.ArrayList; class DukeTest { @Test public void sampleTest() { assertTrue(true); } + + @Test + public void testDeleteCalorie_ValidIndex() { + CalorieList calorieList = new CalorieList(); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Running"),new Calorie(200,true))); + int initialSize = CalorieList.calorieArrayList.size(); + calorieList.deleteCalorie(1); + assertEquals(initialSize - 1, CalorieList.calorieArrayList.size()); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Running"),new Calorie(200,true))); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","13:00", "Eating"),new Calorie(200,false))); + initialSize = CalorieList.calorieArrayList.size(); + calorieList.deleteCalorie(2); + assertEquals(initialSize - 1, CalorieList.calorieArrayList.size()); + } + + @Test + public void testDeleteCalorie_InvalidIndex() { + CalorieList calorieList = new CalorieList(); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Running"),new Calorie(200,true))); + int initialSize = CalorieList.calorieArrayList.size(); + calorieList.deleteCalorie(2); // Index out of bounds + calorieList.deleteCalorie(-1); + assertEquals(initialSize, CalorieList.calorieArrayList.size()); + } } From 5fe3e3fbe33ab5b1ef243c141e92618178a3ea3c Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:30:02 +0800 Subject: [PATCH 028/414] Remove unused import --- src/test/java/seedu/lifetrack/DukeTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index 9c33ac3476..d1bd60b43e 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -8,7 +8,6 @@ import seedu.lifetrack.calorielist.Entry; import seedu.lifetrack.calories.Calorie; -import java.util.ArrayList; class DukeTest { @Test From 2908bfc413e765a501ea410c377d87281743e9e5 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 14 Mar 2024 22:35:53 +0800 Subject: [PATCH 029/414] cleanup code, include feature to add calorie outflow --- src/main/java/seedu/lifetrack/LifeTrack.java | 6 ++--- .../seedu/lifetrack/activity/Activity.java | 24 ------------------- .../lifetrack/calorielist/CalorieList.java | 6 ++--- .../seedu/lifetrack/calorielist/Entry.java | 16 ------------- .../seedu/lifetrack/calories/Calorie.java | 17 +------------ .../exceptions/InvalidInputException.java | 3 ++- .../java/seedu/lifetrack/parser/Parser.java | 8 +++---- src/main/java/seedu/lifetrack/ui/Ui.java | 6 ++--- 8 files changed, 14 insertions(+), 72 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 25f06eb71e..3d971c702e 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -10,17 +10,15 @@ public class LifeTrack { * Main entry-point for the java.lifetrack.LifeTrack application. */ public static void main(String[] args) { + CalorieList calorieList = new CalorieList(); String logo = "LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + "L I F E T R R A A C C K K\n" + "LLL I FFFF EEEE T RRRR AAAAA C KK\n" + "L I F E T R R A A C C K K\n" + "LLLLL IIIII F EEEEE TTTT R R A A CCC K K\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()); - CalorieList calorieList = new CalorieList(); + Ui.readUserInput(); byeMessage(); } diff --git a/src/main/java/seedu/lifetrack/activity/Activity.java b/src/main/java/seedu/lifetrack/activity/Activity.java index b4daebbcc8..df26a32976 100644 --- a/src/main/java/seedu/lifetrack/activity/Activity.java +++ b/src/main/java/seedu/lifetrack/activity/Activity.java @@ -7,32 +7,8 @@ public class Activity { private String description; public Activity(String date, String time,String description){ - this.date= date; - this.time= time; - this.description=description; - } - - public String getDate() { - return date; - } - - public void setDate(String date) { this.date = date; - } - - public String getTime() { - return time; - } - - public void setTime(String time) { this.time = time; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { this.description = description; } } diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 74d216a68d..d74542e5f2 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -5,6 +5,7 @@ import java.util.ArrayList; public class CalorieList { + public static ArrayList calorieArrayList; public CalorieList() { @@ -39,13 +40,12 @@ public void deleteCalorie(int index) { * * @param input the input string containing date, time, activity, and calorie count */ - public static void calorieIn(String input) { + public static void addEntry(String input) { try { Entry newEntry = Parser.parseCaloriesIn(input); calorieArrayList.add(newEntry); } catch (InvalidInputException e) { - System.out.println("Ensure you follow format with no missing inputs!:" + - " calories in d/DATE t/TIME a/ACTIVITY c/CALORIES_IN"); + System.out.println(e.getMessage()); } } } diff --git a/src/main/java/seedu/lifetrack/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calorielist/Entry.java index 9b47a3faf9..5c3f7defd7 100644 --- a/src/main/java/seedu/lifetrack/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/calorielist/Entry.java @@ -12,20 +12,4 @@ public Entry(Activity activity, Calorie calorie){ this.calorie = calorie; this.activity= activity; } - - public Activity getActivity() { - return activity; - } - - public void setActivity(Activity activity) { - this.activity = activity; - } - - public Calorie getCalorie() { - return calorie; - } - - public void setCalorie(Calorie calorie) { - this.calorie = calorie; - } } diff --git a/src/main/java/seedu/lifetrack/calories/Calorie.java b/src/main/java/seedu/lifetrack/calories/Calorie.java index d75b5ca1c0..7c3b02da82 100644 --- a/src/main/java/seedu/lifetrack/calories/Calorie.java +++ b/src/main/java/seedu/lifetrack/calories/Calorie.java @@ -4,24 +4,9 @@ public class Calorie { private int calories; private boolean isIntake; + public Calorie (int calories, boolean isIntake){ this.calories = calories; this.isIntake = isIntake; } - - public int getCalories() { - return calories; - } - - public void setCalories(int calories) { - this.calories = calories; - } - - public boolean isIntake() { - return isIntake; - } - - public void setIntake(boolean intake) { - isIntake = intake; - } } diff --git a/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java index 600bf74139..67e7de5a51 100644 --- a/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java @@ -1,7 +1,8 @@ package seedu.lifetrack.exceptions; public class InvalidInputException extends Exception { + public InvalidInputException() { - System.out.println("Please ensure that you have keyed in the correct format!"); + super("Please ensure that you have keyed in the correct format!"); } } diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/parser/Parser.java index 5577b68af7..cd64c37cfa 100644 --- a/src/main/java/seedu/lifetrack/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/parser/Parser.java @@ -7,8 +7,6 @@ public class Parser { - - /** * Parses a string input to create an Entry object representing calorie intake. * @@ -33,7 +31,8 @@ public static Entry parseCaloriesIn(String input) throws InvalidInputException { throw new InvalidInputException(); } - //extracts date, time, activity, calories_in portion from input + //extracts command, date, time, activity, calories_in portion from input + String command = parts[0].trim(); String date = parts[1].trim(); String time = parts[2].trim(); String description = parts[3].trim(); @@ -46,11 +45,10 @@ public static Entry parseCaloriesIn(String input) throws InvalidInputException { //create objects for Activity, Calorie Activity activityToAdd = new Activity(date, time, description); - Calorie caloriesConsumed = new Calorie(calories, true); + Calorie caloriesConsumed = new Calorie(calories, command == "calories in" ? true : false); //create Object Entry to be returned Entry newCalorieInEntry = new Entry(activityToAdd, caloriesConsumed); return newCalorieInEntry; } - } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 8b975fa2c8..173ccafa31 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -13,8 +13,8 @@ * information using the calorieIn method from the CalorieList class. */ public class Ui { - public static void readUserInput() { + public static void readUserInput() { String line; Scanner in = new Scanner(System.in); if(in.hasNextLine()) { @@ -22,8 +22,8 @@ public static void readUserInput() { while (!line.equalsIgnoreCase("bye")) { if (line.trim().isEmpty()) { System.out.println("Please enter a non empty Input!"); - } else if (line.startsWith("calories in")) { - CalorieList.calorieIn(line); + } else if (line.startsWith("calories in") || line.startsWith("calories out")) { + CalorieList.addEntry(line); } if(in.hasNextLine()) { From 8a0b795d0f7df215b3147a2c6512d0ff5c5e4f39 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:40:31 +0800 Subject: [PATCH 030/414] Fix gradle formatting --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 5bde5cbcf0..be116105fb 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -65,7 +65,8 @@ public static void printCalorieList() { Entry entry = calorieArrayList.get(i); Activity activity = entry.getActivity(); Calorie calorie = entry.getCalorie(); - System.out.println((i + 1) + ". Activity: " + activity.getDescription() + ", Calories: " + calorie.getCalories()); + System.out.println((i + 1) + ". Activity: " + activity.getDescription() + + ", Calories: " + calorie.getCalories()); } } } From 197143c1600d31fc46ebfe8dc5e220d72c042b4f Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:55:21 +0800 Subject: [PATCH 031/414] Fix gradle issue --- src/test/java/seedu/lifetrack/DukeTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index d1bd60b43e..11d8633bb4 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -16,23 +16,23 @@ public void sampleTest() { } @Test - public void testDeleteCalorie_ValidIndex() { + public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Running"),new Calorie(200,true))); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Run"),new Calorie(200,true))); int initialSize = CalorieList.calorieArrayList.size(); calorieList.deleteCalorie(1); assertEquals(initialSize - 1, CalorieList.calorieArrayList.size()); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Running"),new Calorie(200,true))); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","13:00", "Eating"),new Calorie(200,false))); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Run"),new Calorie(200,true))); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","13:00", "Eat"),new Calorie(200,false))); initialSize = CalorieList.calorieArrayList.size(); calorieList.deleteCalorie(2); assertEquals(initialSize - 1, CalorieList.calorieArrayList.size()); } @Test - public void testDeleteCalorie_InvalidIndex() { + public void testDeleteCalorieInvalidIndex() { CalorieList calorieList = new CalorieList(); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Running"),new Calorie(200,true))); + CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Run"),new Calorie(200,true))); int initialSize = CalorieList.calorieArrayList.size(); calorieList.deleteCalorie(2); // Index out of bounds calorieList.deleteCalorie(-1); From 9fde183288f6b6a3ef32eaf3d928d50c933296f1 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 14 Mar 2024 23:00:11 +0800 Subject: [PATCH 032/414] remove static from addEntry method, cleanup code --- src/main/java/seedu/lifetrack/LifeTrack.java | 5 +++-- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 6 +++--- src/main/java/seedu/lifetrack/parser/Parser.java | 5 ++--- src/main/java/seedu/lifetrack/ui/Ui.java | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 3d971c702e..3053ecda43 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -6,20 +6,21 @@ import java.util.Scanner; public class LifeTrack { + /** * Main entry-point for the java.lifetrack.LifeTrack application. */ public static void main(String[] args) { CalorieList calorieList = new CalorieList(); + Scanner in = new Scanner(System.in); String logo = "LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + "L I F E T R R A A C C K K\n" + "LLL I FFFF EEEE T RRRR AAAAA C KK\n" + "L I F E T R R A A C C K K\n" + "LLLLL IIIII F EEEEE TTTT R R A A CCC K K\n"; System.out.println("Hello from\n" + logo); - Scanner in = new Scanner(System.in); - Ui.readUserInput(); + Ui.readUserInput(calorieList); byeMessage(); } diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index d74542e5f2..395aac27ad 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -6,7 +6,7 @@ public class CalorieList { - public static ArrayList calorieArrayList; + public ArrayList calorieArrayList; public CalorieList() { calorieArrayList= new ArrayList<>(); @@ -16,7 +16,7 @@ public CalorieList() { * Index should be in an integer from 1 to size of the list. * @param index the index of calorie record user want to delete */ - public void deleteCalorie(int index) { + public void deleteEntry(int index) { try { if(index > calorieArrayList.size()) { System.out.println("Sorry, this index is out of out of range. Please enter a valid index."); @@ -40,7 +40,7 @@ public void deleteCalorie(int index) { * * @param input the input string containing date, time, activity, and calorie count */ - public static void addEntry(String input) { + public void addEntry(String input) { try { Entry newEntry = Parser.parseCaloriesIn(input); calorieArrayList.add(newEntry); diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/parser/Parser.java index cd64c37cfa..11c06b6990 100644 --- a/src/main/java/seedu/lifetrack/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/parser/Parser.java @@ -25,7 +25,6 @@ public class Parser { public static Entry parseCaloriesIn(String input) throws InvalidInputException { //splits string according to d/ , t/ , a/ , c/ keyword String[] parts = input.split("d/|t/|a/|c/"); - //parts length less than 5 means that not all split keywords were keyed in if (parts.length < 5) { throw new InvalidInputException(); @@ -48,7 +47,7 @@ public static Entry parseCaloriesIn(String input) throws InvalidInputException { Calorie caloriesConsumed = new Calorie(calories, command == "calories in" ? true : false); //create Object Entry to be returned - Entry newCalorieInEntry = new Entry(activityToAdd, caloriesConsumed); - return newCalorieInEntry; + Entry newEntry = new Entry(activityToAdd, caloriesConsumed); + return newEntry; } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 173ccafa31..8b2259fa3d 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -14,7 +14,7 @@ */ public class Ui { - public static void readUserInput() { + public static void readUserInput(CalorieList calorieList) { String line; Scanner in = new Scanner(System.in); if(in.hasNextLine()) { @@ -23,7 +23,7 @@ public static void readUserInput() { if (line.trim().isEmpty()) { System.out.println("Please enter a non empty Input!"); } else if (line.startsWith("calories in") || line.startsWith("calories out")) { - CalorieList.addEntry(line); + calorieList.addEntry(line); } if(in.hasNextLine()) { From 12e21bdc4f0b68bf143f7491c3402602567b363a Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 14 Mar 2024 23:48:30 +0800 Subject: [PATCH 033/414] fix bugs after merging PR #18 --- .../seedu/lifetrack/activity/Activity.java | 4 +++ .../lifetrack/calorielist/CalorieList.java | 8 +++-- .../seedu/lifetrack/calorielist/Entry.java | 8 +++++ .../seedu/lifetrack/calories/Calorie.java | 4 +++ src/main/java/seedu/lifetrack/ui/Ui.java | 2 +- src/test/java/seedu/lifetrack/DukeTest.java | 32 ++++++++----------- 6 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/main/java/seedu/lifetrack/activity/Activity.java b/src/main/java/seedu/lifetrack/activity/Activity.java index df26a32976..11d8f555dc 100644 --- a/src/main/java/seedu/lifetrack/activity/Activity.java +++ b/src/main/java/seedu/lifetrack/activity/Activity.java @@ -11,4 +11,8 @@ public Activity(String date, String time,String description){ this.time = time; this.description = description; } + + public String getDescription() { + return description; + } } diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index d82c585a2a..326f379112 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -8,7 +8,7 @@ public class CalorieList { - public ArrayList calorieArrayList; + private ArrayList calorieArrayList; public CalorieList() { calorieArrayList= new ArrayList<>(); @@ -56,7 +56,7 @@ public void addEntry(String input) { * If the list is empty, it prints a message indicating that the list is empty. * Otherwise, it prints each entry's activity description and calorie count. */ - public static void printCalorieList() { + public void printCalorieList() { if (calorieArrayList.isEmpty()) { System.out.println("Your caloric list is empty."); } else { @@ -70,4 +70,8 @@ public static void printCalorieList() { } } } + + public int getSize() { + return calorieArrayList.size(); + } } diff --git a/src/main/java/seedu/lifetrack/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calorielist/Entry.java index 5c3f7defd7..b24b19a868 100644 --- a/src/main/java/seedu/lifetrack/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/calorielist/Entry.java @@ -12,4 +12,12 @@ public Entry(Activity activity, Calorie calorie){ this.calorie = calorie; this.activity= activity; } + + public Activity getActivity() { + return activity; + } + + public Calorie getCalorie() { + return calorie; + } } diff --git a/src/main/java/seedu/lifetrack/calories/Calorie.java b/src/main/java/seedu/lifetrack/calories/Calorie.java index 7c3b02da82..8e9171546d 100644 --- a/src/main/java/seedu/lifetrack/calories/Calorie.java +++ b/src/main/java/seedu/lifetrack/calories/Calorie.java @@ -9,4 +9,8 @@ public Calorie (int calories, boolean isIntake){ this.calories = calories; this.isIntake = isIntake; } + + public int getCalories() { + return calories; + } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 37e90dbebd..67e8ffa8e2 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -25,7 +25,7 @@ public static void readUserInput(CalorieList calorieList) { } else if (line.startsWith("calories in") || line.startsWith("calories out")) { calorieList.addEntry(line); } else if (line.startsWith("list")) { - CalorieList.printCalorieList(); + calorieList.printCalorieList(); } if(in.hasNextLine()) { diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index 11d8633bb4..ef42009fd8 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -3,11 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; -import seedu.lifetrack.activity.Activity; import seedu.lifetrack.calorielist.CalorieList; -import seedu.lifetrack.calorielist.Entry; -import seedu.lifetrack.calories.Calorie; - class DukeTest { @Test @@ -18,24 +14,24 @@ public void sampleTest() { @Test public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Run"),new Calorie(200,true))); - int initialSize = CalorieList.calorieArrayList.size(); - calorieList.deleteCalorie(1); - assertEquals(initialSize - 1, CalorieList.calorieArrayList.size()); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Run"),new Calorie(200,true))); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","13:00", "Eat"),new Calorie(200,false))); - initialSize = CalorieList.calorieArrayList.size(); - calorieList.deleteCalorie(2); - assertEquals(initialSize - 1, CalorieList.calorieArrayList.size()); + calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); + int initialSize = calorieList.getSize(); + calorieList.deleteEntry(1); + assertEquals(initialSize - 1, calorieList.getSize()); + calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); + calorieList.addEntry("calories in d/2024-03-14 t/13:00 a/Eat c/200"); + initialSize = calorieList.getSize(); + calorieList.deleteEntry(2); + assertEquals(initialSize - 1, calorieList.getSize()); } @Test public void testDeleteCalorieInvalidIndex() { CalorieList calorieList = new CalorieList(); - CalorieList.calorieArrayList.add(new Entry(new Activity("2024-03-14","12:00", "Run"),new Calorie(200,true))); - int initialSize = CalorieList.calorieArrayList.size(); - calorieList.deleteCalorie(2); // Index out of bounds - calorieList.deleteCalorie(-1); - assertEquals(initialSize, CalorieList.calorieArrayList.size()); + calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); + int initialSize = calorieList.getSize(); + calorieList.deleteEntry(2); // Index out of bounds + calorieList.deleteEntry(-1); + assertEquals(initialSize, calorieList.getSize()); } } From a20aaab38b56d5d67dcdbe45f86feb56187e5fc4 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 14 Mar 2024 23:54:44 +0800 Subject: [PATCH 034/414] Add JUnit test for calorieIn method --- .../java/seedu/lifetrack/CalorieListTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/java/seedu/lifetrack/CalorieListTest.java diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java new file mode 100644 index 0000000000..5436f50778 --- /dev/null +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -0,0 +1,29 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Test; +import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.calorielist.Entry; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CalorieListTest { + + @Test + public void calorieInTest() { + // Test setup + CalorieList calorieList = new CalorieList(); + String validInput = "calories in d/2024-03-14 t/15:30 a/Eat burger c/369"; + final int INDEX0 = 0; + + // Call method to test + CalorieList.calorieIn(validInput); + + // Verify that the entry has been added to the list + assertEquals(1, CalorieList.calorieArrayList.size()); + Entry addedEntry = CalorieList.calorieArrayList.get(INDEX0); + assertEquals("2024-03-14", addedEntry.getActivity().getDate()); + assertEquals("15:30", addedEntry.getActivity().getTime()); + assertEquals("Eat burger", addedEntry.getActivity().getDescription()); + assertEquals(369, addedEntry.getCalorie().getCalories()); + } +} From bd2e54f5acb9df0ae47758dd8e4032c0620a6b00 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Fri, 15 Mar 2024 00:23:13 +0800 Subject: [PATCH 035/414] fix gradle IO redirection bug --- text-ui-test/EXPECTED.TXT | 2 -- text-ui-test/input.txt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 56020b1c5c..bb9cb9efb6 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,6 +5,4 @@ LLL I FFFF EEEE T RRRR AAAAA C KK L I F E T R R A A C C K K LLLLL IIIII F EEEEE TTTT R R A A CCC K K -What is your name? -Hello Rex Bye! See you again soon ^^ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 019748b3a5..0abaeaa993 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1 @@ -Rex \ No newline at end of file +bye \ No newline at end of file From b41f5037a05c090ccd916a39713535c3adb1f2a6 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 15 Mar 2024 00:48:44 +0800 Subject: [PATCH 036/414] clean branch --- src/main/java/seedu/lifetrack/LifeTrack.java | 7 +-- .../java/seedu/lifetrack/parser/Parser.java | 7 ++- src/main/java/seedu/lifetrack/ui/Ui.java | 53 +++++++++++++------ 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 25f06eb71e..919a558d93 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -5,6 +5,7 @@ import java.util.Scanner; + public class LifeTrack { /** * Main entry-point for the java.lifetrack.LifeTrack application. @@ -21,11 +22,7 @@ public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.println("Hello " + in.nextLine()); CalorieList calorieList = new CalorieList(); + Ui ui = new Ui(calorieList); Ui.readUserInput(); - byeMessage(); - } - - public static void byeMessage() { - System.out.println("Bye! See you again soon ^^"); } } diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/parser/Parser.java index 5577b68af7..11918d6c9b 100644 --- a/src/main/java/seedu/lifetrack/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/parser/Parser.java @@ -36,6 +36,10 @@ public static Entry parseCaloriesIn(String input) throws InvalidInputException { //extracts date, time, activity, calories_in portion from input String date = parts[1].trim(); String time = parts[2].trim(); + return getNewCalorieInEntry(parts, date, time); + } + + private static Entry getNewCalorieInEntry(String[] parts, String date, String time) throws InvalidInputException { String description = parts[3].trim(); String strCalories = parts[4].trim(); //ensures that all inputs are not empty @@ -49,8 +53,7 @@ public static Entry parseCaloriesIn(String input) throws InvalidInputException { Calorie caloriesConsumed = new Calorie(calories, true); //create Object Entry to be returned - Entry newCalorieInEntry = new Entry(activityToAdd, caloriesConsumed); - return newCalorieInEntry; + return new Entry(activityToAdd, caloriesConsumed); } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 8b975fa2c8..d458672e9e 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -1,11 +1,14 @@ package seedu.lifetrack.ui; import seedu.lifetrack.calorielist.CalorieList; + import java.util.Scanner; +import static seedu.lifetrack.calorielist.CalorieList.calorieIn; + /** * Reads user input from the console and processes it. - * + *

* This method continuously reads input from the console until the user * inputs "bye". For each input line, it checks if it's empty and prompts * the user to enter a non-empty input if it is. If the input line starts @@ -13,23 +16,39 @@ * information using the calorieIn method from the CalorieList class. */ public class Ui { - public static void readUserInput() { + static CalorieList calorieList; - String line; - Scanner in = new Scanner(System.in); - if(in.hasNextLine()) { - line = in.nextLine(); - while (!line.equalsIgnoreCase("bye")) { - if (line.trim().isEmpty()) { - System.out.println("Please enter a non empty Input!"); - } else if (line.startsWith("calories in")) { - CalorieList.calorieIn(line); - } - - if(in.hasNextLine()) { - line = in.nextLine(); - } + public Ui(CalorieList calorieList) { + Ui.calorieList = calorieList; + } + + public static void readUserInput() { + Scanner scanner = new Scanner(System.in); + String input; + do { + input = scanner.nextLine(); + printLine(); + if (input.trim().isEmpty()) { + printEmptyInputMessage(); + } else if (input.startsWith("calories in")) { + calorieIn(input); + } else if (input.startsWith("bye")) { + byeMessage(); } - } + printLine(); + } while (!input.equalsIgnoreCase("bye")); + scanner.close(); + } + + public static void byeMessage() { + System.out.println("Bye! See you again soon ^^"); + } + + public static void printEmptyInputMessage() { + System.out.println("Please enter a non-empty input!"); + } + + public static void printLine() { + System.out.println("\t -------------------------------------------------------------"); } } From 61a246ed136157fe4bd208068270003b0e48592b Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 15 Mar 2024 02:33:25 +0800 Subject: [PATCH 037/414] bye j-unit --- src/main/java/seedu/lifetrack/LifeTrack.java | 2 +- .../java/seedu/lifetrack/parser/Parser.java | 9 +++-- src/main/java/seedu/lifetrack/ui/Ui.java | 35 ++++++++---------- src/test/java/seedu/lifetrack/DukeTest.java | 6 ++++ src/test/java/seedu/lifetrack/InputTest.java | 36 +++++++++++++++++++ text-ui-test/EXPECTED.TXT | 2 ++ 6 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 src/test/java/seedu/lifetrack/InputTest.java diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index dd20a04032..ac87f1f259 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -20,7 +20,7 @@ public static void main(String[] args) { "L I F E T R R A A C C K K\n" + "LLLLL IIIII F EEEEE TTTT R R A A CCC K K\n"; System.out.println("Hello from\n" + logo); - + System.out.println("What do you want to do today?\n"); Ui.readUserInput(calorieList); byeMessage(); } diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/parser/Parser.java index 86c7c88973..7e49917b6b 100644 --- a/src/main/java/seedu/lifetrack/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/parser/Parser.java @@ -5,6 +5,8 @@ import seedu.lifetrack.calories.Calorie; import seedu.lifetrack.exceptions.InvalidInputException; +import java.util.Objects; + public class Parser { /** @@ -34,10 +36,11 @@ public static Entry parseCaloriesIn(String input) throws InvalidInputException { String command = parts[0].trim(); String date = parts[1].trim(); String time = parts[2].trim(); - return getNewCalorieInEntry(parts, date, time); + return getNewCalorieInEntry(parts, date, time, command); } - private static Entry getNewCalorieInEntry(String[] parts, String date, String time) throws InvalidInputException { + private static Entry getNewCalorieInEntry(String[] parts, String date, String time, String command) + throws InvalidInputException { String description = parts[3].trim(); String strCalories = parts[4].trim(); //ensures that all inputs are not empty @@ -48,7 +51,7 @@ private static Entry getNewCalorieInEntry(String[] parts, String date, String ti //create objects for Activity, Calorie Activity activityToAdd = new Activity(date, time, description); - Calorie caloriesConsumed = new Calorie(calories, command == "calories in" ? true : false); + Calorie caloriesConsumed = new Calorie(calories, Objects.equals(command, "calories in")); //create Object Entry to be returned return new Entry(activityToAdd, caloriesConsumed); diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 5885612b07..8134ae141b 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -4,8 +4,6 @@ import java.util.Scanner; -import static seedu.lifetrack.calorielist.CalorieList.calorieIn; - /** * Reads user input from the console and processes it. *

@@ -19,25 +17,20 @@ public class Ui { public static void readUserInput(CalorieList calorieList) { String line; - Scanner in = new Scanner(System.in); - if(in.hasNextLine()) { - line = in.nextLine(); - while (!line.equalsIgnoreCase("bye")) { - if (line.trim().isEmpty()) { - System.out.println("Please enter a non empty Input!"); - } else if (line.startsWith("calories in") || line.startsWith("calories out")) { - calorieList.addEntry(line); - } else if (line.startsWith("list")) { - calorieList.printCalorieList(); - } - - if(in.hasNextLine()) { - line = in.nextLine(); - } - } - printLine(); - } while (!input.equalsIgnoreCase("bye")); - scanner.close(); + do { + line = new Scanner(System.in).nextLine(); + handleUserInput(line, calorieList); + } while (!line.equalsIgnoreCase("bye")); + } + + public static void handleUserInput(String line, CalorieList calorieList) { + if (line.trim().isEmpty()) { + System.out.println("Please enter a non empty Input!"); + } else if (line.startsWith("calories in") || line.startsWith("calories out")) { + calorieList.addEntry(line); + } else if (line.startsWith("list")) { + calorieList.printCalorieList(); + } } public static void byeMessage() { diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index ef42009fd8..f992165391 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -2,8 +2,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.Test; import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.ui.Ui; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; class DukeTest { @Test @@ -34,4 +39,5 @@ public void testDeleteCalorieInvalidIndex() { calorieList.deleteEntry(-1); assertEquals(initialSize, calorieList.getSize()); } + } diff --git a/src/test/java/seedu/lifetrack/InputTest.java b/src/test/java/seedu/lifetrack/InputTest.java new file mode 100644 index 0000000000..c6dfc2890d --- /dev/null +++ b/src/test/java/seedu/lifetrack/InputTest.java @@ -0,0 +1,36 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.ui.Ui; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class InputTest { + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + + @BeforeEach + public void setUpStreams() { + System.setOut(new PrintStream(outContent)); + } + + @AfterEach + public void restoreStreams() { + System.setOut(originalOut); + } + + @Test + // Expect Empty String as function is exited + public void handleInputBye_printByeMessage() { + CalorieList calorieList = new CalorieList(); + String input = "bye"; + Ui.handleUserInput(input, calorieList); + assertEquals("", outContent.toString()); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index bb9cb9efb6..9dab287556 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,4 +5,6 @@ LLL I FFFF EEEE T RRRR AAAAA C KK L I F E T R R A A C C K K LLLLL IIIII F EEEEE TTTT R R A A CCC K K +What do you want to do today? + Bye! See you again soon ^^ From f8f29ecdcaa36c5bbb6ae2dcdb141745aa1a9643 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 15 Mar 2024 02:36:23 +0800 Subject: [PATCH 038/414] bye j-unit --- src/test/java/seedu/lifetrack/DukeTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index f992165391..b894df1962 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -5,10 +5,7 @@ import org.junit.jupiter.api.Test; import seedu.lifetrack.calorielist.CalorieList; -import seedu.lifetrack.ui.Ui; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; class DukeTest { @Test From 1b06b306b79dba7129e67cc4829d5f896734f674 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 15 Mar 2024 10:39:10 +0800 Subject: [PATCH 039/414] Change Junit test name for calorieIn method --- src/test/java/seedu/lifetrack/CalorieListTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 5436f50778..6738139c22 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -9,7 +9,8 @@ public class CalorieListTest { @Test - public void calorieInTest() { + public void calorieIn_validInput_entryAdded() { +// public void calorieInTest() { // Test setup CalorieList calorieList = new CalorieList(); String validInput = "calories in d/2024-03-14 t/15:30 a/Eat burger c/369"; From 3b121be3838458d5b056e6ba8e7c31eb9304daf8 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 15 Mar 2024 11:11:18 +0800 Subject: [PATCH 040/414] Add getter in Class Actitivity to access date and time --- src/main/java/seedu/lifetrack/activity/Activity.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/seedu/lifetrack/activity/Activity.java b/src/main/java/seedu/lifetrack/activity/Activity.java index 11d8f555dc..1a00ec47ea 100644 --- a/src/main/java/seedu/lifetrack/activity/Activity.java +++ b/src/main/java/seedu/lifetrack/activity/Activity.java @@ -12,6 +12,14 @@ public Activity(String date, String time,String description){ this.description = description; } + public String getDate() { + return date; + } + + public String getTime() { + return time; + } + public String getDescription() { return description; } From 4d37337a216f41cce8afa236dce1dc96f2f2ef7c Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 15 Mar 2024 11:11:58 +0800 Subject: [PATCH 041/414] Add getter for Entry in calorieArrayList --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 326f379112..3727060495 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -4,6 +4,8 @@ import seedu.lifetrack.calories.Calorie; import seedu.lifetrack.parser.Parser; import seedu.lifetrack.exceptions.InvalidInputException; + +import java.lang.reflect.Array; import java.util.ArrayList; public class CalorieList { @@ -14,6 +16,10 @@ public CalorieList() { calorieArrayList= new ArrayList<>(); } + public Entry getEntry(int index) { + return calorieArrayList.get(index); + } + /** * Index should be in an integer from 1 to size of the list. * @param index the index of calorie record user want to delete From c2a43f3178c4632dd7b66626cd4fd1152b862124 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 15 Mar 2024 11:13:09 +0800 Subject: [PATCH 042/414] Add addEntry test for valid inputs Test calorie input entry Test calorie ouput entry --- .../java/seedu/lifetrack/CalorieListTest.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 6738139c22..7d49c5bc3d 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -9,22 +9,31 @@ public class CalorieListTest { @Test - public void calorieIn_validInput_entryAdded() { -// public void calorieInTest() { + public void addEntry_validInput_entryAdded() { // Test setup CalorieList calorieList = new CalorieList(); - String validInput = "calories in d/2024-03-14 t/15:30 a/Eat burger c/369"; - final int INDEX0 = 0; + String validInputCalorieIn = "calories in d/2024-03-14 t/15:30 a/Eat burger c/369"; + String validInputCalorieOut = "calories out d/2024-03-15 t/14:00 a/run c/679"; // Call method to test - CalorieList.calorieIn(validInput); + calorieList.addEntry(validInputCalorieIn); + calorieList.addEntry(validInputCalorieOut); // Verify that the entry has been added to the list - assertEquals(1, CalorieList.calorieArrayList.size()); - Entry addedEntry = CalorieList.calorieArrayList.get(INDEX0); - assertEquals("2024-03-14", addedEntry.getActivity().getDate()); - assertEquals("15:30", addedEntry.getActivity().getTime()); - assertEquals("Eat burger", addedEntry.getActivity().getDescription()); - assertEquals(369, addedEntry.getCalorie().getCalories()); + assertEquals(2, calorieList.getSize()); + Entry firstEntry = calorieList.getEntry(0); + Entry secondEntry = calorieList.getEntry(1); + + // Check calories intake entry + assertEquals("2024-03-14", firstEntry.getActivity().getDate()); + assertEquals("15:30", firstEntry.getActivity().getTime()); + assertEquals("Eat burger", firstEntry.getActivity().getDescription()); + assertEquals(369, firstEntry.getCalorie().getCalories()); + + // Check calories outflow entry + assertEquals("2024-03-15", secondEntry.getActivity().getDate()); + assertEquals("14:00", secondEntry.getActivity().getTime()); + assertEquals("run", secondEntry.getActivity().getDescription()); + assertEquals(679, secondEntry.getCalorie().getCalories()); } } From d6245d804c7593e9c18bd4f27f9a083ec9b5fa81 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Fri, 15 Mar 2024 11:15:31 +0800 Subject: [PATCH 043/414] add JUnit tests for parseCaloriesInput method --- src/main/java/seedu/lifetrack/LifeTrack.java | 1 - .../lifetrack/calorielist/CalorieList.java | 5 ++-- .../java/seedu/lifetrack/parser/Parser.java | 15 ++++++----- src/test/java/seedu/lifetrack/InputTest.java | 2 +- .../{DukeTest.java => LifeTrackTest.java} | 27 ++++++++++++++----- 5 files changed, 33 insertions(+), 17 deletions(-) rename src/test/java/seedu/lifetrack/{DukeTest.java => LifeTrackTest.java} (62%) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index ac87f1f259..35b99ff527 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -5,7 +5,6 @@ import java.util.Scanner; - public class LifeTrack { /** diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 326f379112..ce48748877 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -1,8 +1,9 @@ package seedu.lifetrack.calorielist; +import static seedu.lifetrack.parser.Parser.parseCaloriesInput; + import seedu.lifetrack.activity.Activity; import seedu.lifetrack.calories.Calorie; -import seedu.lifetrack.parser.Parser; import seedu.lifetrack.exceptions.InvalidInputException; import java.util.ArrayList; @@ -44,7 +45,7 @@ public void deleteEntry(int index) { */ public void addEntry(String input) { try { - Entry newEntry = Parser.parseCaloriesIn(input); + Entry newEntry = parseCaloriesInput(input); calorieArrayList.add(newEntry); } catch (InvalidInputException e) { System.out.println(e.getMessage()); diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/parser/Parser.java index 7e49917b6b..98e6cae656 100644 --- a/src/main/java/seedu/lifetrack/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/parser/Parser.java @@ -24,7 +24,8 @@ public class Parser { * @throws InvalidInputException if the input string is missing components or * contains empty fields */ - public static Entry parseCaloriesIn(String input) throws InvalidInputException { + public static Entry parseCaloriesInput(String input) throws InvalidInputException { + //splits string according to d/ , t/ , a/ , c/ keyword String[] parts = input.split("d/|t/|a/|c/"); //parts length less than 5 means that not all split keywords were keyed in @@ -36,17 +37,19 @@ public static Entry parseCaloriesIn(String input) throws InvalidInputException { String command = parts[0].trim(); String date = parts[1].trim(); String time = parts[2].trim(); - return getNewCalorieInEntry(parts, date, time, command); - } - - private static Entry getNewCalorieInEntry(String[] parts, String date, String time, String command) - throws InvalidInputException { String description = parts[3].trim(); String strCalories = parts[4].trim(); + //ensures that all inputs are not empty if (date.isEmpty() || time.isEmpty() || description.isEmpty() || strCalories.isEmpty()) { throw new InvalidInputException(); } + return getNewCalorieEntry(command, date, time, description, strCalories); + } + + private static Entry getNewCalorieEntry(String command, String date, String time, + String description, String strCalories) throws InvalidInputException { + int calories = Integer.parseInt(strCalories); //create objects for Activity, Calorie diff --git a/src/test/java/seedu/lifetrack/InputTest.java b/src/test/java/seedu/lifetrack/InputTest.java index c6dfc2890d..8771813d52 100644 --- a/src/test/java/seedu/lifetrack/InputTest.java +++ b/src/test/java/seedu/lifetrack/InputTest.java @@ -27,7 +27,7 @@ public void restoreStreams() { @Test // Expect Empty String as function is exited - public void handleInputBye_printByeMessage() { + public void handleUserInput_inputBye_printByeMessage() { CalorieList calorieList = new CalorieList(); String input = "bye"; Ui.handleUserInput(input, calorieList); diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java similarity index 62% rename from src/test/java/seedu/lifetrack/DukeTest.java rename to src/test/java/seedu/lifetrack/LifeTrackTest.java index b894df1962..a4e5cad64b 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -1,17 +1,13 @@ package seedu.lifetrack; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.lifetrack.parser.Parser.parseCaloriesInput; import org.junit.jupiter.api.Test; import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.exceptions.InvalidInputException; - -class DukeTest { - @Test - public void sampleTest() { - assertTrue(true); - } +class LifeTrackTest { @Test public void testDeleteCalorieValidIndex() { @@ -37,4 +33,21 @@ public void testDeleteCalorieInvalidIndex() { assertEquals(initialSize, calorieList.getSize()); } + @Test + public void parseCaloriesInput_emptyFields_exceptionThrown() { + try { + parseCaloriesInput("calories in"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_incompleteFields_exceptionThrown() { + try { + parseCaloriesInput("calories in d/220224 t/"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + } + } } From 7be3b0f79d88a05b8bbdb96fd979da0bf93e55fd Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 15 Mar 2024 11:18:49 +0800 Subject: [PATCH 044/414] Remove unused import --- src/main/java/seedu/lifetrack/calorielist/CalorieList.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 3727060495..4a59c854ec 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -5,7 +5,6 @@ import seedu.lifetrack.parser.Parser; import seedu.lifetrack.exceptions.InvalidInputException; -import java.lang.reflect.Array; import java.util.ArrayList; public class CalorieList { From 96e5bb3de407e40d10c7fc28f0e5729a23bc24ae Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 15 Mar 2024 11:33:12 +0800 Subject: [PATCH 045/414] Add junit testing for printCalorieList --- .../lifetrack/calorielist/CalorieList.java | 2 +- src/test/java/seedu/lifetrack/DukeTest.java | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 326f379112..71f003ffdd 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -60,7 +60,7 @@ public void printCalorieList() { if (calorieArrayList.isEmpty()) { System.out.println("Your caloric list is empty."); } else { - System.out.println("Caloric List: "); + System.out.println("Caloric List:"); for (int i = 0; i < calorieArrayList.size(); i++) { Entry entry = calorieArrayList.get(i); Activity activity = entry.getActivity(); diff --git a/src/test/java/seedu/lifetrack/DukeTest.java b/src/test/java/seedu/lifetrack/DukeTest.java index b894df1962..ed8011ba56 100644 --- a/src/test/java/seedu/lifetrack/DukeTest.java +++ b/src/test/java/seedu/lifetrack/DukeTest.java @@ -6,6 +6,9 @@ import org.junit.jupiter.api.Test; import seedu.lifetrack.calorielist.CalorieList; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + class DukeTest { @Test @@ -37,4 +40,52 @@ public void testDeleteCalorieInvalidIndex() { assertEquals(initialSize, calorieList.getSize()); } + @Test + public void testPrintCalorieListEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.printCalorieList(); + System.setOut(System.out); + String expectedOutput = "Your caloric list is empty." + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintCalorieListNonEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in d/2024-03-14 t/12:00 a/Run c/200"); + calorieList.printCalorieList(); + System.setOut(System.out); + String expectedOutput = "Caloric List:" + lineSeparator + "1. Activity: Run, Calories: 200" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintCalorieListMultipleEntries() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in d/2024-03-14 t/12:00 a/Run c/200"); + calorieList.addEntry("calories out d/2024-03-14 t/13:00 a/Walk c/150"); + calorieList.addEntry("calories in d/2024-03-14 t/14:00 a/Eat c/500"); + calorieList.addEntry("calories out d/2024-03-14 t/15:00 a/Run c/250"); + calorieList.addEntry("calories in d/2024-03-14 t/16:00 a/Eat c/300"); + calorieList.printCalorieList(); + System.setOut(System.out); + String expectedOutput = "Caloric List:" + lineSeparator + + "1. Activity: Run, Calories: 200" + lineSeparator + + "2. Activity: Walk, Calories: 150" + lineSeparator + + "3. Activity: Eat, Calories: 500" + lineSeparator + + "4. Activity: Run, Calories: 250" + lineSeparator + + "5. Activity: Eat, Calories: 300" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + assertEquals(5, calorieList.getSize()); + } + } From 07ff9cc72d9dedd6b03752c80f5b33285830f8d0 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 15 Mar 2024 16:29:15 +0800 Subject: [PATCH 046/414] resolve --- src/test/java/seedu/lifetrack/LifeTrackTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index fbbfdd48bc..8c44233431 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -1,9 +1,5 @@ package seedu.lifetrack; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.lifetrack.parser.Parser.parseCaloriesInput; - import org.junit.jupiter.api.Test; import seedu.lifetrack.calorielist.CalorieList; import seedu.lifetrack.exceptions.InvalidInputException; @@ -11,8 +7,12 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.lifetrack.parser.Parser.parseCaloriesInput; -class DukeTest { + +class LifeTrackTest { @Test public void sampleTest() { assertTrue(true); @@ -59,6 +59,7 @@ public void parseCaloriesInput_incompleteFields_exceptionThrown() { assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); } } + @Test public void testPrintCalorieListEmpty() { String lineSeparator = System.lineSeparator(); From 2845428768fefd337a3d56fa0753f4b0e01c5056 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 15 Mar 2024 16:35:25 +0800 Subject: [PATCH 047/414] delete --- .../java/seedu/lifetrack/calorielist/CalorieList.java | 6 ++++-- src/main/java/seedu/lifetrack/ui/Ui.java | 2 ++ src/test/java/seedu/lifetrack/LifeTrackTest.java | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java index 07d3532e77..26c282204a 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calorielist/CalorieList.java @@ -11,6 +11,7 @@ public class CalorieList { private ArrayList calorieArrayList; + final int SIZE_OF_DELETE = 7; public CalorieList() { calorieArrayList= new ArrayList<>(); @@ -22,10 +23,11 @@ public Entry getEntry(int index) { /** * Index should be in an integer from 1 to size of the list. - * @param index the index of calorie record user want to delete + * @param line the string containing the index of calorie record user want to delete */ - public void deleteEntry(int index) { + public void deleteEntry(String line) { try { + int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); if(index > calorieArrayList.size()) { System.out.println("Sorry, this index is out of out of range. Please enter a valid index."); return; diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 8134ae141b..7994de4de5 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -30,6 +30,8 @@ public static void handleUserInput(String line, CalorieList calorieList) { calorieList.addEntry(line); } else if (line.startsWith("list")) { calorieList.printCalorieList(); + } else if (line.startsWith("delete")) { + calorieList.deleteEntry(line); } } diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 8c44233431..01057958eb 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -23,12 +23,12 @@ public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); int initialSize = calorieList.getSize(); - calorieList.deleteEntry(1); + calorieList.deleteEntry("delete 1"); assertEquals(initialSize - 1, calorieList.getSize()); calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); calorieList.addEntry("calories in d/2024-03-14 t/13:00 a/Eat c/200"); initialSize = calorieList.getSize(); - calorieList.deleteEntry(2); + calorieList.deleteEntry("delete 2"); assertEquals(initialSize - 1, calorieList.getSize()); } @@ -37,8 +37,8 @@ public void testDeleteCalorieInvalidIndex() { CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); int initialSize = calorieList.getSize(); - calorieList.deleteEntry(2); // Index out of bounds - calorieList.deleteEntry(-1); + calorieList.deleteEntry("delete 2"); // Index out of bounds + calorieList.deleteEntry("delete -1"); assertEquals(initialSize, calorieList.getSize()); } From f7d5491bf6d06fdea5a39c7b54fe5604f1282e5c Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Tue, 19 Mar 2024 22:55:04 +0800 Subject: [PATCH 048/414] changes --- src/main/java/seedu/lifetrack/LifeTrack.java | 2 +- .../{ => calories}/activity/Activity.java | 2 +- .../calorielist/CalorieList.java | 19 +++++++++---------- .../{ => calories}/calorielist/Entry.java | 4 ++-- .../exceptions/InvalidInputException.java | 2 +- .../lifetrack/{ => system}/parser/Parser.java | 8 ++++---- src/main/java/seedu/lifetrack/ui/Ui.java | 2 +- .../java/seedu/lifetrack/CalorieListTest.java | 4 ++-- src/test/java/seedu/lifetrack/InputTest.java | 2 +- .../java/seedu/lifetrack/LifeTrackTest.java | 6 +++--- 10 files changed, 25 insertions(+), 26 deletions(-) rename src/main/java/seedu/lifetrack/{ => calories}/activity/Activity.java (91%) rename src/main/java/seedu/lifetrack/{ => calories}/calorielist/CalorieList.java (85%) rename src/main/java/seedu/lifetrack/{ => calories}/calorielist/Entry.java (79%) rename src/main/java/seedu/lifetrack/{ => system}/exceptions/InvalidInputException.java (80%) rename src/main/java/seedu/lifetrack/{ => system}/parser/Parser.java (92%) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 35b99ff527..f9ad44e4f3 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -1,6 +1,6 @@ package seedu.lifetrack; -import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.ui.Ui; import java.util.Scanner; diff --git a/src/main/java/seedu/lifetrack/activity/Activity.java b/src/main/java/seedu/lifetrack/calories/activity/Activity.java similarity index 91% rename from src/main/java/seedu/lifetrack/activity/Activity.java rename to src/main/java/seedu/lifetrack/calories/activity/Activity.java index 1a00ec47ea..11ab3fb849 100644 --- a/src/main/java/seedu/lifetrack/activity/Activity.java +++ b/src/main/java/seedu/lifetrack/calories/activity/Activity.java @@ -1,4 +1,4 @@ -package seedu.lifetrack.activity; +package seedu.lifetrack.calories.activity; public class Activity { diff --git a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java similarity index 85% rename from src/main/java/seedu/lifetrack/calorielist/CalorieList.java rename to src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 26c282204a..5b77dc17eb 100644 --- a/src/main/java/seedu/lifetrack/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,17 +1,17 @@ -package seedu.lifetrack.calorielist; +package seedu.lifetrack.calories.calorielist; -import static seedu.lifetrack.parser.Parser.parseCaloriesInput; +import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; -import seedu.lifetrack.activity.Activity; +import seedu.lifetrack.calories.activity.Activity; import seedu.lifetrack.calories.Calorie; -import seedu.lifetrack.exceptions.InvalidInputException; +import seedu.lifetrack.system.exceptions.InvalidInputException; import java.util.ArrayList; public class CalorieList { private ArrayList calorieArrayList; - final int SIZE_OF_DELETE = 7; + private final int SIZE_OF_DELETE = 7; public CalorieList() { calorieArrayList= new ArrayList<>(); @@ -28,14 +28,13 @@ public Entry getEntry(int index) { public void deleteEntry(String line) { try { int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); - if(index > calorieArrayList.size()) { - System.out.println("Sorry, this index is out of out of range. Please enter a valid index."); - return; - } calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { - System.out.println("Sorry, this index is invalid. Please enter a positive integer."); + System.out.println("Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list."); + } catch (NumberFormatException e) { + System.out.println("Please enter a valid index!"); } } diff --git a/src/main/java/seedu/lifetrack/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java similarity index 79% rename from src/main/java/seedu/lifetrack/calorielist/Entry.java rename to src/main/java/seedu/lifetrack/calories/calorielist/Entry.java index b24b19a868..349b879e8e 100644 --- a/src/main/java/seedu/lifetrack/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java @@ -1,6 +1,6 @@ -package seedu.lifetrack.calorielist; +package seedu.lifetrack.calories.calorielist; -import seedu.lifetrack.activity.Activity; +import seedu.lifetrack.calories.activity.Activity; import seedu.lifetrack.calories.Calorie; public class Entry { diff --git a/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java similarity index 80% rename from src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java rename to src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index 67e7de5a51..74db52f3a4 100644 --- a/src/main/java/seedu/lifetrack/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -1,4 +1,4 @@ -package seedu.lifetrack.exceptions; +package seedu.lifetrack.system.exceptions; public class InvalidInputException extends Exception { diff --git a/src/main/java/seedu/lifetrack/parser/Parser.java b/src/main/java/seedu/lifetrack/system/parser/Parser.java similarity index 92% rename from src/main/java/seedu/lifetrack/parser/Parser.java rename to src/main/java/seedu/lifetrack/system/parser/Parser.java index 98e6cae656..705ad54fca 100644 --- a/src/main/java/seedu/lifetrack/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/system/parser/Parser.java @@ -1,9 +1,9 @@ -package seedu.lifetrack.parser; +package seedu.lifetrack.system.parser; -import seedu.lifetrack.activity.Activity; -import seedu.lifetrack.calorielist.Entry; +import seedu.lifetrack.calories.activity.Activity; +import seedu.lifetrack.calories.calorielist.Entry; import seedu.lifetrack.calories.Calorie; -import seedu.lifetrack.exceptions.InvalidInputException; +import seedu.lifetrack.system.exceptions.InvalidInputException; import java.util.Objects; diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 7994de4de5..c667d99ac9 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -1,6 +1,6 @@ package seedu.lifetrack.ui; -import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.CalorieList; import java.util.Scanner; diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 7d49c5bc3d..4b6bf119fa 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -1,8 +1,8 @@ package seedu.lifetrack; import org.junit.jupiter.api.Test; -import seedu.lifetrack.calorielist.CalorieList; -import seedu.lifetrack.calorielist.Entry; +import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.Entry; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/seedu/lifetrack/InputTest.java b/src/test/java/seedu/lifetrack/InputTest.java index 8771813d52..7c94d6f64e 100644 --- a/src/test/java/seedu/lifetrack/InputTest.java +++ b/src/test/java/seedu/lifetrack/InputTest.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.lifetrack.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.ui.Ui; import java.io.ByteArrayOutputStream; diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 01057958eb..84cd9c94fb 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -1,15 +1,15 @@ package seedu.lifetrack; import org.junit.jupiter.api.Test; -import seedu.lifetrack.calorielist.CalorieList; -import seedu.lifetrack.exceptions.InvalidInputException; +import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.system.exceptions.InvalidInputException; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.lifetrack.parser.Parser.parseCaloriesInput; +import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; class LifeTrackTest { From cd9d403b125ace44ddff0bc8e6ebaa04298cc2da Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:32:59 +0800 Subject: [PATCH 049/414] Add liquids in --- src/main/java/seedu/lifetrack/LifeTrack.java | 4 +- .../calories/calorielist/CalorieList.java | 4 +- .../java/seedu/lifetrack/liquids/Liquid.java | 20 ++++ .../lifetrack/liquids/beverage/Beverage.java | 20 ++++ .../lifetrack/liquids/liquidlist/Entry.java | 16 ++++ .../liquids/liquidlist/LiquidList.java | 95 +++++++++++++++++++ .../{Parser.java => ParserCalories.java} | 2 +- .../lifetrack/system/parser/ParserLiquid.java | 50 ++++++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 14 ++- src/test/java/seedu/lifetrack/InputTest.java | 4 +- .../java/seedu/lifetrack/LifeTrackTest.java | 2 +- 11 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/liquids/Liquid.java create mode 100644 src/main/java/seedu/lifetrack/liquids/beverage/Beverage.java create mode 100644 src/main/java/seedu/lifetrack/liquids/liquidlist/Entry.java create mode 100644 src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java rename src/main/java/seedu/lifetrack/system/parser/{Parser.java => ParserCalories.java} (98%) create mode 100644 src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index f9ad44e4f3..a7e0f55cad 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -1,6 +1,7 @@ package seedu.lifetrack; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.liquids.liquidlist.LiquidList; import seedu.lifetrack.ui.Ui; import java.util.Scanner; @@ -12,6 +13,7 @@ public class LifeTrack { */ public static void main(String[] args) { CalorieList calorieList = new CalorieList(); + LiquidList liquidList = new LiquidList(); Scanner in = new Scanner(System.in); String logo = "LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + "L I F E T R R A A C C K K\n" + @@ -20,7 +22,7 @@ public static void main(String[] args) { "LLLLL IIIII F EEEEE TTTT R R A A CCC K K\n"; System.out.println("Hello from\n" + logo); System.out.println("What do you want to do today?\n"); - Ui.readUserInput(calorieList); + Ui.readUserInput(calorieList, liquidList); byeMessage(); } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 5b77dc17eb..831e25f8ce 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,6 +1,6 @@ package seedu.lifetrack.calories.calorielist; -import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; +import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; import seedu.lifetrack.calories.activity.Activity; import seedu.lifetrack.calories.Calorie; @@ -11,7 +11,7 @@ public class CalorieList { private ArrayList calorieArrayList; - private final int SIZE_OF_DELETE = 7; + private final int SIZE_OF_DELETE = 16; public CalorieList() { calorieArrayList= new ArrayList<>(); diff --git a/src/main/java/seedu/lifetrack/liquids/Liquid.java b/src/main/java/seedu/lifetrack/liquids/Liquid.java new file mode 100644 index 0000000000..d78ae1dac7 --- /dev/null +++ b/src/main/java/seedu/lifetrack/liquids/Liquid.java @@ -0,0 +1,20 @@ +package seedu.lifetrack.liquids; + +public class Liquid { + private int liquids; + + private String beverage; + + public Liquid (int liquids, String beverage){ + this.liquids = liquids; + this.beverage = beverage; + } + + public int getVolume() { + return liquids; + } + + public String getBeverage() { + return beverage; + } +} diff --git a/src/main/java/seedu/lifetrack/liquids/beverage/Beverage.java b/src/main/java/seedu/lifetrack/liquids/beverage/Beverage.java new file mode 100644 index 0000000000..054b287e2f --- /dev/null +++ b/src/main/java/seedu/lifetrack/liquids/beverage/Beverage.java @@ -0,0 +1,20 @@ +package seedu.lifetrack.liquids.beverage; + +public class Beverage { + private String beverage; + private int volume; + + public Beverage(String beverage, int volume){ + this.beverage = beverage; + this.volume = volume; + + } + + public String getBeverage() { + return beverage; + } + + public int getVolume() { + return volume; + } +} diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/Entry.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/Entry.java new file mode 100644 index 0000000000..8677a47c09 --- /dev/null +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/Entry.java @@ -0,0 +1,16 @@ +package seedu.lifetrack.liquids.liquidlist; + +import seedu.lifetrack.liquids.beverage.Beverage; + +public class Entry { + + private Beverage beverage; + + public Entry(Beverage beverage){ + this.beverage= beverage; + } + + public Beverage getBeverage() { + return beverage; + } +} diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java new file mode 100644 index 0000000000..7dfba37343 --- /dev/null +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -0,0 +1,95 @@ +package seedu.lifetrack.liquids.liquidlist; + +import seedu.lifetrack.liquids.beverage.Beverage; +import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.parser.ParserLiquid; + +import java.util.ArrayList; + +/** + * Represents a list of liquid entries. + * Provides methods to add, delete, and print liquid entries. + */ +public class LiquidList { + + private ArrayList liquidArrayList; + private final int SIZE_OF_DELETE = 15; + + /** + * Constructs an empty LiquidList. + */ + public LiquidList() { + liquidArrayList = new ArrayList<>(); + } + + /** + * Retrieves the liquid entry at the specified index. + * + * @param index the index of the liquid entry to retrieve + * @return the liquid entry at the specified index + */ + public Entry getEntry(int index) { + assert index >= 0 && index < liquidArrayList.size() : "Index out of bounds"; + return liquidArrayList.get(index); + } + + /** + * Deletes the liquid entry indicated by the provided line. + * + * @param line the string containing the index of the liquid record to delete + */ + public void deleteEntry(String line) { + try { + int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); + assert index > 0 && index <= liquidArrayList.size() : "Invalid index"; + liquidArrayList.remove(index - 1); + System.out.println("Successfully delete the liquid record."); + } catch (IndexOutOfBoundsException e) { + System.out.println("Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list."); + } catch (NumberFormatException e) { + System.out.println("Please enter a valid index!"); + } + } + + /** + * Adds a new liquid entry based on the provided input. + * + * @param input the input string containing liquid entry information + */ + public void addEntry(String input) { + try { + Entry newEntry = ParserLiquid.parseLiquidInput(input); + liquidArrayList.add(newEntry); + } catch (InvalidInputException e) { + System.out.println(e.getMessage()); + } + } + + /** + * Prints the list of liquid entries. + * If the list is empty, prints a message indicating that the list is empty. + */ + public void printLiquidList() { + if (liquidArrayList.isEmpty()) { + System.out.println("Your liquid list is empty."); + } else { + System.out.println("Liquid List:"); + for (int i = 0; i < liquidArrayList.size(); i++) { + Entry entry = liquidArrayList.get(i); + Beverage beverage = entry.getBeverage(); + System.out.println((i + 1) + ". Beverage: " + beverage.getBeverage() + + ", Volume: " + beverage.getVolume()); + } + } + } + + /** + * Retrieves the size of the liquid list. + * + * @return the number of liquid entries in the list + */ + public int getSize() { + return liquidArrayList.size(); + } +} diff --git a/src/main/java/seedu/lifetrack/system/parser/Parser.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java similarity index 98% rename from src/main/java/seedu/lifetrack/system/parser/Parser.java rename to src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 705ad54fca..621e14e463 100644 --- a/src/main/java/seedu/lifetrack/system/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -7,7 +7,7 @@ import java.util.Objects; -public class Parser { +public class ParserCalories { /** * Parses a string input to create an Entry object representing calorie intake. diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java new file mode 100644 index 0000000000..1a91d453e1 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -0,0 +1,50 @@ +package seedu.lifetrack.system.parser; + +import seedu.lifetrack.liquids.beverage.Beverage; +import seedu.lifetrack.liquids.liquidlist.Entry; +import seedu.lifetrack.system.exceptions.InvalidInputException; + +public class ParserLiquid { + + /** + * Parses a string input to create a Liquid object representing liquid intake. + * + * This method expects the input string to follow a specific format, where the + * beverage name and quantity are separated by the delimiters 'b/' and 'v/'. + * The method extracts these components and creates a Liquid object. + * If any part of the input is missing or empty, an InvalidInputException is thrown. + * + * @param input the input string to be parsed, containing beverage name and quantity + * @return a Liquid object representing liquid intake + * @throws InvalidInputException if the input string is missing components or + * contains empty fields + */ + public static Entry parseLiquidInput(String input) throws InvalidInputException { + + // splits string according to b/ and v/ keywords + String[] parts = input.split("b/|v/"); + // parts length less than 3 means that not all split keywords were keyed in + if (parts.length < 3) { + throw new InvalidInputException(); + } + + // extracts beverage name and quantity portion from input + String beverageName = parts[1].trim(); + String volume = parts[2].trim(); + + // ensures that both inputs are not empty + if (beverageName.isEmpty() || volume.isEmpty()) { + throw new InvalidInputException(); + } + return getNewLiquidEntry(volume, beverageName); + } + private static Entry getNewLiquidEntry(String strVolume, String name) throws InvalidInputException { + int volume = Integer.parseInt(strVolume); + + //create objects for Beverage + Beverage liquidToAdd = new Beverage(name, volume); + + //create Object Entry to be returned + return new Entry(liquidToAdd); + } +} diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index c667d99ac9..7376469336 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -1,6 +1,7 @@ package seedu.lifetrack.ui; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.liquids.liquidlist.LiquidList; import java.util.Scanner; @@ -15,23 +16,28 @@ */ public class Ui { - public static void readUserInput(CalorieList calorieList) { + public static void readUserInput(CalorieList calorieList, LiquidList liquidList) { String line; do { line = new Scanner(System.in).nextLine(); - handleUserInput(line, calorieList); + handleUserInput(line, calorieList, liquidList); } while (!line.equalsIgnoreCase("bye")); } - public static void handleUserInput(String line, CalorieList calorieList) { + public static void handleUserInput(String line, CalorieList calorieList, LiquidList liquidList) { if (line.trim().isEmpty()) { System.out.println("Please enter a non empty Input!"); } else if (line.startsWith("calories in") || line.startsWith("calories out")) { calorieList.addEntry(line); + } else if (line.startsWith("liquids in")) { + liquidList.addEntry(line); } else if (line.startsWith("list")) { calorieList.printCalorieList(); - } else if (line.startsWith("delete")) { + liquidList.printLiquidList(); + } else if (line.startsWith("delete calories")) { calorieList.deleteEntry(line); + }else if (line.startsWith("delete liquids")) { + liquidList.deleteEntry(line); } } diff --git a/src/test/java/seedu/lifetrack/InputTest.java b/src/test/java/seedu/lifetrack/InputTest.java index 7c94d6f64e..7016e47350 100644 --- a/src/test/java/seedu/lifetrack/InputTest.java +++ b/src/test/java/seedu/lifetrack/InputTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.liquids.liquidlist.LiquidList; import seedu.lifetrack.ui.Ui; import java.io.ByteArrayOutputStream; @@ -29,8 +30,9 @@ public void restoreStreams() { // Expect Empty String as function is exited public void handleUserInput_inputBye_printByeMessage() { CalorieList calorieList = new CalorieList(); + LiquidList liquidList = new LiquidList(); String input = "bye"; - Ui.handleUserInput(input, calorieList); + Ui.handleUserInput(input, calorieList, liquidList); assertEquals("", outContent.toString()); } } diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 84cd9c94fb..46dfa96e39 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; +import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; class LifeTrackTest { From e2d6b2a59d9b9565acd5574088693152f56c70ed Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:36:55 +0800 Subject: [PATCH 050/414] Update junit testing for calories --- src/test/java/seedu/lifetrack/LifeTrackTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 46dfa96e39..5c0997b157 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -23,12 +23,12 @@ public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); int initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete 1"); + calorieList.deleteEntry("delete calories 1"); assertEquals(initialSize - 1, calorieList.getSize()); calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); calorieList.addEntry("calories in d/2024-03-14 t/13:00 a/Eat c/200"); initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete 2"); + calorieList.deleteEntry("delete calories 2"); assertEquals(initialSize - 1, calorieList.getSize()); } @@ -37,8 +37,8 @@ public void testDeleteCalorieInvalidIndex() { CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); int initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete 2"); // Index out of bounds - calorieList.deleteEntry("delete -1"); + calorieList.deleteEntry("delete calories 2"); // Index out of bounds + calorieList.deleteEntry("delete calories -1"); assertEquals(initialSize, calorieList.getSize()); } From f224557c675998b87f23f9078522751584adbde0 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:42:21 +0800 Subject: [PATCH 051/414] Add junit tests for liquids --- .../java/seedu/lifetrack/LifeTrackTest.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 5c0997b157..d48d9c7a68 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.Test; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.liquids.liquidlist.LiquidList; import seedu.lifetrack.system.exceptions.InvalidInputException; import java.io.ByteArrayOutputStream; @@ -10,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; +import static seedu.lifetrack.system.parser.ParserLiquid.parseLiquidInput; class LifeTrackTest { @@ -107,5 +109,85 @@ public void testPrintCalorieListMultipleEntries() { assertEquals(expectedOutput, outputStream.toString()); assertEquals(5, calorieList.getSize()); } + @Test + public void testDeleteLiquidValidIndex() { + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + int initialSize = liquidList.getSize(); + liquidList.deleteEntry("delete liquids 1"); + assertEquals(initialSize - 1, liquidList.getSize()); + } + + @Test + public void testDeleteLiquidInvalidIndex() { + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + int initialSize = liquidList.getSize(); + liquidList.deleteEntry("delete liquids 2"); // Index out of bounds + liquidList.deleteEntry("delete liquids -1"); + assertEquals(initialSize, liquidList.getSize()); + } + + @Test + public void parseLiquidInput_emptyFields_exceptionThrown() { + try { + parseLiquidInput("liquids in"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_incompleteFields_exceptionThrown() { + try { + parseLiquidInput("liquids in b/Milo"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + } + } + + @Test + public void testPrintLiquidListEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + LiquidList liquidList = new LiquidList(); + liquidList.printLiquidList(); + System.setOut(System.out); + String expectedOutput = "Your liquid list is empty." + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + @Test + public void testPrintLiquidListNonEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + liquidList.printLiquidList(); + System.setOut(System.out); + String expectedOutput = "Liquid List:" + lineSeparator + + "1. Beverage: Milo, Volume: 200" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintLiquidListMultipleEntries() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + liquidList.addEntry("liquids in b/Water v/300"); + liquidList.addEntry("liquids in b/Juice v/150"); + liquidList.printLiquidList(); + System.setOut(System.out); + String expectedOutput = "Liquid List:" + lineSeparator + + "1. Beverage: Milo, Volume: 200" + lineSeparator + + "2. Beverage: Water, Volume: 300" + lineSeparator + + "3. Beverage: Juice, Volume: 150" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + assertEquals(3, liquidList.getSize()); + } } From fd5aea845db2e593226a6f3a2cf0050382b4a500 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:49:27 +0800 Subject: [PATCH 052/414] Fix junit test --- src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 7dfba37343..787654b48b 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -41,7 +41,6 @@ public Entry getEntry(int index) { public void deleteEntry(String line) { try { int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); - assert index > 0 && index <= liquidArrayList.size() : "Invalid index"; liquidArrayList.remove(index - 1); System.out.println("Successfully delete the liquid record."); } catch (IndexOutOfBoundsException e) { From 7860b7791fd060757691277873adf3dd507cdfc4 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 20 Mar 2024 21:31:19 +0800 Subject: [PATCH 053/414] add Food, inputEntry and outputEntry classes, change calorieList functions accordingly --- src/main/java/seedu/lifetrack/LifeTrack.java | 2 +- .../seedu/lifetrack/calories/Activity.java | 8 ++ .../seedu/lifetrack/calories/Calorie.java | 16 --- .../java/seedu/lifetrack/calories/Food.java | 14 +++ .../lifetrack/calories/activity/Activity.java | 26 ---- .../calories/calorielist/CalorieList.java | 16 ++- .../lifetrack/calories/calorielist/Entry.java | 27 +++-- .../calories/calorielist/InputEntry.java | 17 +++ .../calories/calorielist/OutputEntry.java | 17 +++ .../java/seedu/lifetrack/system/Parser.java | 112 ++++++++++++++++++ .../seedu/lifetrack/{ui => system}/Ui.java | 2 +- .../seedu/lifetrack/system/parser/Parser.java | 62 ---------- .../java/seedu/lifetrack/CalorieListTest.java | 18 ++- src/test/java/seedu/lifetrack/InputTest.java | 2 +- .../java/seedu/lifetrack/LifeTrackTest.java | 24 ++-- 15 files changed, 213 insertions(+), 150 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/calories/Activity.java delete mode 100644 src/main/java/seedu/lifetrack/calories/Calorie.java create mode 100644 src/main/java/seedu/lifetrack/calories/Food.java delete mode 100644 src/main/java/seedu/lifetrack/calories/activity/Activity.java create mode 100644 src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java create mode 100644 src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java create mode 100644 src/main/java/seedu/lifetrack/system/Parser.java rename src/main/java/seedu/lifetrack/{ui => system}/Ui.java (98%) delete mode 100644 src/main/java/seedu/lifetrack/system/parser/Parser.java diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index f9ad44e4f3..9231d056bf 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -1,7 +1,7 @@ package seedu.lifetrack; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.ui.Ui; +import seedu.lifetrack.system.Ui; import java.util.Scanner; diff --git a/src/main/java/seedu/lifetrack/calories/Activity.java b/src/main/java/seedu/lifetrack/calories/Activity.java new file mode 100644 index 0000000000..5c3d0bd1ca --- /dev/null +++ b/src/main/java/seedu/lifetrack/calories/Activity.java @@ -0,0 +1,8 @@ +package seedu.lifetrack.calories; + +public class Activity { + + public Activity(){ + + } +} diff --git a/src/main/java/seedu/lifetrack/calories/Calorie.java b/src/main/java/seedu/lifetrack/calories/Calorie.java deleted file mode 100644 index 8e9171546d..0000000000 --- a/src/main/java/seedu/lifetrack/calories/Calorie.java +++ /dev/null @@ -1,16 +0,0 @@ -package seedu.lifetrack.calories; - -public class Calorie { - - private int calories; - private boolean isIntake; - - public Calorie (int calories, boolean isIntake){ - this.calories = calories; - this.isIntake = isIntake; - } - - public int getCalories() { - return calories; - } -} diff --git a/src/main/java/seedu/lifetrack/calories/Food.java b/src/main/java/seedu/lifetrack/calories/Food.java new file mode 100644 index 0000000000..f387a8ab72 --- /dev/null +++ b/src/main/java/seedu/lifetrack/calories/Food.java @@ -0,0 +1,14 @@ +package seedu.lifetrack.calories; + +public class Food { + + private int carbohydrates; + private int proteins; + private int fats; + + public Food(int carbohydrates, int proteins, int fats) { + this.carbohydrates = carbohydrates; + this.proteins = proteins; + this.fats = fats; + } +} diff --git a/src/main/java/seedu/lifetrack/calories/activity/Activity.java b/src/main/java/seedu/lifetrack/calories/activity/Activity.java deleted file mode 100644 index 11ab3fb849..0000000000 --- a/src/main/java/seedu/lifetrack/calories/activity/Activity.java +++ /dev/null @@ -1,26 +0,0 @@ -package seedu.lifetrack.calories.activity; - -public class Activity { - - private String date; - private String time; - private String description; - - public Activity(String date, String time,String description){ - this.date = date; - this.time = time; - this.description = description; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } - - public String getDescription() { - return description; - } -} diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 5b77dc17eb..f3e4eb99bf 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,13 +1,12 @@ package seedu.lifetrack.calories.calorielist; -import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; - -import seedu.lifetrack.calories.activity.Activity; -import seedu.lifetrack.calories.Calorie; +import seedu.lifetrack.calories.Activity; +import seedu.lifetrack.system.Parser; import seedu.lifetrack.system.exceptions.InvalidInputException; import java.util.ArrayList; + public class CalorieList { private ArrayList calorieArrayList; @@ -51,7 +50,7 @@ public void deleteEntry(String line) { */ public void addEntry(String input) { try { - Entry newEntry = parseCaloriesInput(input); + Entry newEntry = Parser.parseCaloriesInput(input); calorieArrayList.add(newEntry); } catch (InvalidInputException e) { System.out.println(e.getMessage()); @@ -70,10 +69,9 @@ public void printCalorieList() { System.out.println("Caloric List:"); for (int i = 0; i < calorieArrayList.size(); i++) { Entry entry = calorieArrayList.get(i); - Activity activity = entry.getActivity(); - Calorie calorie = entry.getCalorie(); - System.out.println((i + 1) + ". Activity: " + activity.getDescription() - + ", Calories: " + calorie.getCalories()); + String description = entry.getDescription(); + int calories = entry.getCalories(); + System.out.println((i + 1) + ". Description: " + description + ", Calories: " + calories); } } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java index 349b879e8e..cf719fd560 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java @@ -1,23 +1,26 @@ package seedu.lifetrack.calories.calorielist; -import seedu.lifetrack.calories.activity.Activity; -import seedu.lifetrack.calories.Calorie; +public abstract class Entry { -public class Entry { + private String description; + private int calories; + private String date; - private Activity activity; - private Calorie calorie; + public Entry(String description, int calories, String date){ + this.description = description; + this.calories = calories; + this.date = date; + } - public Entry(Activity activity, Calorie calorie){ - this.calorie = calorie; - this.activity= activity; + public String getDescription() { + return description; } - public Activity getActivity() { - return activity; + public int getCalories() { + return calories; } - public Calorie getCalorie() { - return calorie; + public String getDate() { + return date; } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java new file mode 100644 index 0000000000..cdfebc6db7 --- /dev/null +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -0,0 +1,17 @@ +package seedu.lifetrack.calories.calorielist; + +import seedu.lifetrack.calories.Food; + +public class InputEntry extends Entry { + + private Food food; + + public InputEntry(String description, int calories, String date) { + super(description, calories, date); + } + + public InputEntry(String description, int calories, String date, Food food) { + super(description, calories, date); + this.food = food; + } +} diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java new file mode 100644 index 0000000000..26b5424d34 --- /dev/null +++ b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java @@ -0,0 +1,17 @@ +package seedu.lifetrack.calories.calorielist; + +import seedu.lifetrack.calories.Activity; + +public class OutputEntry extends Entry { + + private Activity activity; + + public OutputEntry(String description, int calories, String date) { + super(description, calories, date); + } + + public OutputEntry(String description, int calories, String date, Activity activity) { + super(description, calories, date); + this.activity = activity; + } +} diff --git a/src/main/java/seedu/lifetrack/system/Parser.java b/src/main/java/seedu/lifetrack/system/Parser.java new file mode 100644 index 0000000000..6e65a4c560 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/Parser.java @@ -0,0 +1,112 @@ +package seedu.lifetrack.system; + +import seedu.lifetrack.calories.calorielist.Entry; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; +import seedu.lifetrack.calories.Activity; +import seedu.lifetrack.calories.Food; +import seedu.lifetrack.system.exceptions.InvalidInputException; + +public class Parser { + + private static final int CARBS_IDX = 0; + private static final int PROTEINS_IDX = 1; + private static final int FATS_IDX = 2; + + /** + * Parses a string input to create an Entry object representing calorie intake. + * + * This method expects the input string to follow a specific format, where the + * date, time, activity description, and calorie count are separated by the + * delimiters 'd/', 't/', 'a/', and 'c/'. The method extracts these components + * and creates an Entry object containing an Activity and a Calorie object. + * If any part of the input is missing or empty, an InvalidInputException is thrown. + * + * @param input the input string to be parsed, containing date, time, activity, + * and calorie count information + * @return an Entry object representing calorie intake + * @throws InvalidInputException if the input string is missing components or + * contains empty fields + */ + public static Entry parseCaloriesInput(String input) throws InvalidInputException { + + //check that desc/, c/ and date/ keywords exist in the correct order, else throw exception + int descriptionIndex = input.indexOf("desc/"); + int caloriesIndex = input.indexOf("c/"); + int dateIndex = input.indexOf("date/"); + int macrosIndex = input.indexOf("m/"); + if (descriptionIndex == -1 || caloriesIndex == -1 || dateIndex == -1 || + (!(descriptionIndex < caloriesIndex && caloriesIndex < dateIndex) && + macrosIndex != -1 && dateIndex < macrosIndex)) { + throw new InvalidInputException(); + } + + //extract command, description, calories, date and macronutrients from input + String[] parts = input.split("desc/|c/|date/|m/"); + String command = parts[0].trim(); + String description = parts[1].trim(); + String strCalories = parts[2].trim(); + String date = parts[3].trim(); + int[] macros; + + //check if optional macronutrients field was provided + if (parts.length == 5) { + String macroString = parts[4].trim(); + macros = getMacrosFromString(macroString); + } else { + macros = null; + } + + //check if the description, calories or date fields are empty + if (description.isEmpty() || strCalories.isEmpty() || date.isEmpty()) { + throw new InvalidInputException(); + } + + try { + int calories = Integer.parseInt(strCalories); + if (command == "calories out") { + return makeNewOutputEntry(description, calories, date); + } else if (macros == null) { + return makeNewInputEntry(description, calories, date); + } else { + return makeNewInputEntry(description, calories, date, macros); + } + } catch (NumberFormatException e) { + System.out.println("Please input only numbers into the calories field!"); + return null; + } + } + + private static int[] getMacrosFromString(String macroString) { + int[] macros = new int[3]; + try { + String[] macroParts = macroString.split(","); + int idx = 0; + for (String macro: macroParts) { + macros[idx] = Integer.parseInt(macro); + idx++; + } + } catch (NumberFormatException e) { + System.out.println("Please input only numbers into the macronutrients field!"); + } + return macros; + } + + private static Entry makeNewOutputEntry(String description, int calories, String date) { + Activity newActivity = new Activity(); + + return new OutputEntry(description, calories, date, newActivity); + } + + private static Entry makeNewInputEntry(String description, int calories, String date) { + + return new InputEntry(description, calories, date); + } + + private static Entry makeNewInputEntry(String description, int calories, String date, int[] foodMacros) { + + Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); + + return new InputEntry(description, calories, date, newFood); + } +} diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/system/Ui.java similarity index 98% rename from src/main/java/seedu/lifetrack/ui/Ui.java rename to src/main/java/seedu/lifetrack/system/Ui.java index c667d99ac9..0a52a65ad2 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/system/Ui.java @@ -1,4 +1,4 @@ -package seedu.lifetrack.ui; +package seedu.lifetrack.system; import seedu.lifetrack.calories.calorielist.CalorieList; diff --git a/src/main/java/seedu/lifetrack/system/parser/Parser.java b/src/main/java/seedu/lifetrack/system/parser/Parser.java deleted file mode 100644 index 705ad54fca..0000000000 --- a/src/main/java/seedu/lifetrack/system/parser/Parser.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.lifetrack.system.parser; - -import seedu.lifetrack.calories.activity.Activity; -import seedu.lifetrack.calories.calorielist.Entry; -import seedu.lifetrack.calories.Calorie; -import seedu.lifetrack.system.exceptions.InvalidInputException; - -import java.util.Objects; - -public class Parser { - - /** - * Parses a string input to create an Entry object representing calorie intake. - * - * This method expects the input string to follow a specific format, where the - * date, time, activity description, and calorie count are separated by the - * delimiters 'd/', 't/', 'a/', and 'c/'. The method extracts these components - * and creates an Entry object containing an Activity and a Calorie object. - * If any part of the input is missing or empty, an InvalidInputException is thrown. - * - * @param input the input string to be parsed, containing date, time, activity, - * and calorie count information - * @return an Entry object representing calorie intake - * @throws InvalidInputException if the input string is missing components or - * contains empty fields - */ - public static Entry parseCaloriesInput(String input) throws InvalidInputException { - - //splits string according to d/ , t/ , a/ , c/ keyword - String[] parts = input.split("d/|t/|a/|c/"); - //parts length less than 5 means that not all split keywords were keyed in - if (parts.length < 5) { - throw new InvalidInputException(); - } - - //extracts command, date, time, activity, calories_in portion from input - String command = parts[0].trim(); - String date = parts[1].trim(); - String time = parts[2].trim(); - String description = parts[3].trim(); - String strCalories = parts[4].trim(); - - //ensures that all inputs are not empty - if (date.isEmpty() || time.isEmpty() || description.isEmpty() || strCalories.isEmpty()) { - throw new InvalidInputException(); - } - return getNewCalorieEntry(command, date, time, description, strCalories); - } - - private static Entry getNewCalorieEntry(String command, String date, String time, - String description, String strCalories) throws InvalidInputException { - - int calories = Integer.parseInt(strCalories); - - //create objects for Activity, Calorie - Activity activityToAdd = new Activity(date, time, description); - Calorie caloriesConsumed = new Calorie(calories, Objects.equals(command, "calories in")); - - //create Object Entry to be returned - return new Entry(activityToAdd, caloriesConsumed); - } -} diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 4b6bf119fa..dd0885af07 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -12,8 +12,8 @@ public class CalorieListTest { public void addEntry_validInput_entryAdded() { // Test setup CalorieList calorieList = new CalorieList(); - String validInputCalorieIn = "calories in d/2024-03-14 t/15:30 a/Eat burger c/369"; - String validInputCalorieOut = "calories out d/2024-03-15 t/14:00 a/run c/679"; + String validInputCalorieIn = "calories in a/Eat burger c/369 d/2024-03-14"; + String validInputCalorieOut = "calories out a/run c/679 d/2024-03-15"; // Call method to test calorieList.addEntry(validInputCalorieIn); @@ -25,15 +25,13 @@ public void addEntry_validInput_entryAdded() { Entry secondEntry = calorieList.getEntry(1); // Check calories intake entry - assertEquals("2024-03-14", firstEntry.getActivity().getDate()); - assertEquals("15:30", firstEntry.getActivity().getTime()); - assertEquals("Eat burger", firstEntry.getActivity().getDescription()); - assertEquals(369, firstEntry.getCalorie().getCalories()); + assertEquals("2024-03-14", firstEntry.getDate()); + assertEquals("Eat burger", firstEntry.getDescription()); + assertEquals(369, firstEntry.getCalories()); // Check calories outflow entry - assertEquals("2024-03-15", secondEntry.getActivity().getDate()); - assertEquals("14:00", secondEntry.getActivity().getTime()); - assertEquals("run", secondEntry.getActivity().getDescription()); - assertEquals(679, secondEntry.getCalorie().getCalories()); + assertEquals("2024-03-15", secondEntry.getDate()); + assertEquals("run", secondEntry.getDescription()); + assertEquals(679, secondEntry.getCalories()); } } diff --git a/src/test/java/seedu/lifetrack/InputTest.java b/src/test/java/seedu/lifetrack/InputTest.java index 7c94d6f64e..aaf746005c 100644 --- a/src/test/java/seedu/lifetrack/InputTest.java +++ b/src/test/java/seedu/lifetrack/InputTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.ui.Ui; +import seedu.lifetrack.system.Ui; import java.io.ByteArrayOutputStream; import java.io.PrintStream; diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 84cd9c94fb..092b468933 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; +import static seedu.lifetrack.system.Parser.parseCaloriesInput; class LifeTrackTest { @@ -21,12 +21,12 @@ public void sampleTest() { @Test public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); + calorieList.addEntry("calories out a/Run c/200 d/2024-03-14"); int initialSize = calorieList.getSize(); calorieList.deleteEntry("delete 1"); assertEquals(initialSize - 1, calorieList.getSize()); - calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); - calorieList.addEntry("calories in d/2024-03-14 t/13:00 a/Eat c/200"); + calorieList.addEntry("calories out a/Run c/200 d/2024-03-14"); + calorieList.addEntry("calories in a/Eat c/200 d/2024-03-14"); initialSize = calorieList.getSize(); calorieList.deleteEntry("delete 2"); assertEquals(initialSize - 1, calorieList.getSize()); @@ -35,7 +35,7 @@ public void testDeleteCalorieValidIndex() { @Test public void testDeleteCalorieInvalidIndex() { CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out d/2024-03-14 t/12:00 a/Run c/200"); + calorieList.addEntry("calories out a/Run c/200 d/2024-03-14"); int initialSize = calorieList.getSize(); calorieList.deleteEntry("delete 2"); // Index out of bounds calorieList.deleteEntry("delete -1"); @@ -54,7 +54,7 @@ public void parseCaloriesInput_emptyFields_exceptionThrown() { @Test public void parseCaloriesInput_incompleteFields_exceptionThrown() { try { - parseCaloriesInput("calories in d/220224 t/"); + parseCaloriesInput("calories in d/220224"); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); } @@ -78,7 +78,7 @@ public void testPrintCalorieListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in d/2024-03-14 t/12:00 a/Run c/200"); + calorieList.addEntry("calories in a/Run c/200 d/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); String expectedOutput = "Caloric List:" + lineSeparator + "1. Activity: Run, Calories: 200" + lineSeparator; @@ -91,11 +91,11 @@ public void testPrintCalorieListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in d/2024-03-14 t/12:00 a/Run c/200"); - calorieList.addEntry("calories out d/2024-03-14 t/13:00 a/Walk c/150"); - calorieList.addEntry("calories in d/2024-03-14 t/14:00 a/Eat c/500"); - calorieList.addEntry("calories out d/2024-03-14 t/15:00 a/Run c/250"); - calorieList.addEntry("calories in d/2024-03-14 t/16:00 a/Eat c/300"); + calorieList.addEntry("calories in a/Run c/200 d/2024-03-14"); + calorieList.addEntry("calories out a/Walk c/150 d/2024-03-14"); + calorieList.addEntry("calories in a/Eat c/500 d/2024-03-14"); + calorieList.addEntry("calories out a/Run c/250 d/2024-03-14"); + calorieList.addEntry("calories in a/Eat c/300 d/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); String expectedOutput = "Caloric List:" + lineSeparator + From 884323ebbe454b123f143a844be4b53bed0046d7 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 20 Mar 2024 21:46:54 +0800 Subject: [PATCH 054/414] update JUnit Tests --- .../calories/calorielist/CalorieList.java | 4 +-- .../lifetrack/calories/calorielist/Entry.java | 5 +++ .../java/seedu/lifetrack/CalorieListTest.java | 4 +-- .../java/seedu/lifetrack/LifeTrackTest.java | 33 ++++++++++--------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index f3e4eb99bf..58a41bd4b3 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -69,9 +69,7 @@ public void printCalorieList() { System.out.println("Caloric List:"); for (int i = 0; i < calorieArrayList.size(); i++) { Entry entry = calorieArrayList.get(i); - String description = entry.getDescription(); - int calories = entry.getCalories(); - System.out.println((i + 1) + ". Description: " + description + ", Calories: " + calories); + System.out.println((i + 1) + ". " + entry); } } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java index cf719fd560..cc7c6c0cd7 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java @@ -23,4 +23,9 @@ public int getCalories() { public String getDate() { return date; } + + public String toString() { + return String.format("Date: " + date + ", Description: " + description + ", Calories: " + calories); + + } } diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index dd0885af07..b9dd48bf25 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -12,8 +12,8 @@ public class CalorieListTest { public void addEntry_validInput_entryAdded() { // Test setup CalorieList calorieList = new CalorieList(); - String validInputCalorieIn = "calories in a/Eat burger c/369 d/2024-03-14"; - String validInputCalorieOut = "calories out a/run c/679 d/2024-03-15"; + String validInputCalorieIn = "calories in desc/Eat burger c/369 date/2024-03-14"; + String validInputCalorieOut = "calories out desc/run c/679 date/2024-03-15"; // Call method to test calorieList.addEntry(validInputCalorieIn); diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 092b468933..7fed197a78 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -21,12 +21,12 @@ public void sampleTest() { @Test public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out a/Run c/200 d/2024-03-14"); + calorieList.addEntry("calories out desc/Run c/200 date/2024-03-14"); int initialSize = calorieList.getSize(); calorieList.deleteEntry("delete 1"); assertEquals(initialSize - 1, calorieList.getSize()); - calorieList.addEntry("calories out a/Run c/200 d/2024-03-14"); - calorieList.addEntry("calories in a/Eat c/200 d/2024-03-14"); + calorieList.addEntry("calories out desc/Run c/200 date/2024-03-14"); + calorieList.addEntry("calories in desc/Eat c/200 date/2024-03-14"); initialSize = calorieList.getSize(); calorieList.deleteEntry("delete 2"); assertEquals(initialSize - 1, calorieList.getSize()); @@ -35,7 +35,7 @@ public void testDeleteCalorieValidIndex() { @Test public void testDeleteCalorieInvalidIndex() { CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out a/Run c/200 d/2024-03-14"); + calorieList.addEntry("calories out desc/Run c/200 date/2024-03-14"); int initialSize = calorieList.getSize(); calorieList.deleteEntry("delete 2"); // Index out of bounds calorieList.deleteEntry("delete -1"); @@ -78,10 +78,11 @@ public void testPrintCalorieListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in a/Run c/200 d/2024-03-14"); + calorieList.addEntry("calories in desc/Run c/200 date/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "Caloric List:" + lineSeparator + "1. Activity: Run, Calories: 200" + lineSeparator; + String expectedOutput = "Caloric List:" + lineSeparator + + "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -91,19 +92,19 @@ public void testPrintCalorieListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in a/Run c/200 d/2024-03-14"); - calorieList.addEntry("calories out a/Walk c/150 d/2024-03-14"); - calorieList.addEntry("calories in a/Eat c/500 d/2024-03-14"); - calorieList.addEntry("calories out a/Run c/250 d/2024-03-14"); - calorieList.addEntry("calories in a/Eat c/300 d/2024-03-14"); + calorieList.addEntry("calories in desc/Run c/200 date/2024-03-14"); + calorieList.addEntry("calories out desc/Walk c/150 date/2024-03-14"); + calorieList.addEntry("calories in desc/Eat c/500 date/2024-03-14"); + calorieList.addEntry("calories out desc/Run c/250 date/2024-03-14"); + calorieList.addEntry("calories in desc/Eat c/300 date/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); String expectedOutput = "Caloric List:" + lineSeparator + - "1. Activity: Run, Calories: 200" + lineSeparator + - "2. Activity: Walk, Calories: 150" + lineSeparator + - "3. Activity: Eat, Calories: 500" + lineSeparator + - "4. Activity: Run, Calories: 250" + lineSeparator + - "5. Activity: Eat, Calories: 300" + lineSeparator; + "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator + + "2. Date: 2024-03-14, Description: Walk, Calories: 150" + lineSeparator + + "3. Date: 2024-03-14, Description: Eat, Calories: 500" + lineSeparator + + "4. Date: 2024-03-14, Description: Run, Calories: 250" + lineSeparator + + "5. Date: 2024-03-14, Description: Eat, Calories: 300" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(5, calorieList.getSize()); } From 2f65752695a04f5781843c468d8fc6078119aff5 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 20 Mar 2024 21:53:22 +0800 Subject: [PATCH 055/414] update Parser jdocs --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 5 ++--- src/main/java/seedu/lifetrack/system/Parser.java | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 58a41bd4b3..5ddf752e87 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,6 +1,5 @@ package seedu.lifetrack.calories.calorielist; -import seedu.lifetrack.calories.Activity; import seedu.lifetrack.system.Parser; import seedu.lifetrack.system.exceptions.InvalidInputException; @@ -10,7 +9,7 @@ public class CalorieList { private ArrayList calorieArrayList; - private final int SIZE_OF_DELETE = 7; + private final int DELETE_PADDING = 7; public CalorieList() { calorieArrayList= new ArrayList<>(); @@ -26,7 +25,7 @@ public Entry getEntry(int index) { */ public void deleteEntry(String line) { try { - int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); + int index = Integer.parseInt(line.substring(DELETE_PADDING).trim()); calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { diff --git a/src/main/java/seedu/lifetrack/system/Parser.java b/src/main/java/seedu/lifetrack/system/Parser.java index 6e65a4c560..78026a4c42 100644 --- a/src/main/java/seedu/lifetrack/system/Parser.java +++ b/src/main/java/seedu/lifetrack/system/Parser.java @@ -17,10 +17,10 @@ public class Parser { * Parses a string input to create an Entry object representing calorie intake. * * This method expects the input string to follow a specific format, where the - * date, time, activity description, and calorie count are separated by the - * delimiters 'd/', 't/', 'a/', and 'c/'. The method extracts these components - * and creates an Entry object containing an Activity and a Calorie object. - * If any part of the input is missing or empty, an InvalidInputException is thrown. + * description, calorie count, date and macronutrients are separated by the + * delimiters 'desc/', 'c/', 'date/', and 'm/'. The method extracts these components + * and creates either an InputEntry or OutputEntry object depending on the user command. + * If required inputs are missing or empty, an InvalidInputException is thrown. * * @param input the input string to be parsed, containing date, time, activity, * and calorie count information From bec58244a21e5592a01823e1fd0d0560ddbae694 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 20 Mar 2024 22:17:09 +0800 Subject: [PATCH 056/414] make edits after merging with upstream base --- .../lifetrack/calories/calorielist/CalorieList.java | 4 ++-- .../lifetrack/liquids/{beverage => }/Beverage.java | 2 +- .../liquidlist/{Entry.java => LiquidEntry.java} | 6 +++--- .../seedu/lifetrack/liquids/liquidlist/LiquidList.java | 10 +++++----- .../system/{Parser.java => parser/ParserCalories.java} | 4 ++-- .../seedu/lifetrack/system/parser/ParserLiquid.java | 10 +++++----- src/test/java/seedu/lifetrack/LifeTrackTest.java | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) rename src/main/java/seedu/lifetrack/liquids/{beverage => }/Beverage.java (88%) rename src/main/java/seedu/lifetrack/liquids/liquidlist/{Entry.java => LiquidEntry.java} (62%) rename src/main/java/seedu/lifetrack/system/{Parser.java => parser/ParserCalories.java} (98%) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 9ad42b0a53..a030387182 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,7 +1,7 @@ package seedu.lifetrack.calories.calorielist; -import seedu.lifetrack.system.Parser; import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.parser.ParserCalories; import java.util.ArrayList; @@ -48,7 +48,7 @@ public void deleteEntry(String line) { */ public void addEntry(String input) { try { - Entry newEntry = Parser.parseCaloriesInput(input); + Entry newEntry = ParserCalories.parseCaloriesInput(input); calorieArrayList.add(newEntry); } catch (InvalidInputException e) { System.out.println(e.getMessage()); diff --git a/src/main/java/seedu/lifetrack/liquids/beverage/Beverage.java b/src/main/java/seedu/lifetrack/liquids/Beverage.java similarity index 88% rename from src/main/java/seedu/lifetrack/liquids/beverage/Beverage.java rename to src/main/java/seedu/lifetrack/liquids/Beverage.java index 054b287e2f..97838f2c93 100644 --- a/src/main/java/seedu/lifetrack/liquids/beverage/Beverage.java +++ b/src/main/java/seedu/lifetrack/liquids/Beverage.java @@ -1,4 +1,4 @@ -package seedu.lifetrack.liquids.beverage; +package seedu.lifetrack.liquids; public class Beverage { private String beverage; diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/Entry.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java similarity index 62% rename from src/main/java/seedu/lifetrack/liquids/liquidlist/Entry.java rename to src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java index 8677a47c09..b812fecf96 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/Entry.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java @@ -1,12 +1,12 @@ package seedu.lifetrack.liquids.liquidlist; -import seedu.lifetrack.liquids.beverage.Beverage; +import seedu.lifetrack.liquids.Beverage; -public class Entry { +public class LiquidEntry { private Beverage beverage; - public Entry(Beverage beverage){ + public LiquidEntry(Beverage beverage){ this.beverage= beverage; } diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 787654b48b..4ad302d975 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -1,6 +1,6 @@ package seedu.lifetrack.liquids.liquidlist; -import seedu.lifetrack.liquids.beverage.Beverage; +import seedu.lifetrack.liquids.Beverage; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserLiquid; @@ -12,7 +12,7 @@ */ public class LiquidList { - private ArrayList liquidArrayList; + private ArrayList liquidArrayList; private final int SIZE_OF_DELETE = 15; /** @@ -28,7 +28,7 @@ public LiquidList() { * @param index the index of the liquid entry to retrieve * @return the liquid entry at the specified index */ - public Entry getEntry(int index) { + public LiquidEntry getEntry(int index) { assert index >= 0 && index < liquidArrayList.size() : "Index out of bounds"; return liquidArrayList.get(index); } @@ -58,7 +58,7 @@ public void deleteEntry(String line) { */ public void addEntry(String input) { try { - Entry newEntry = ParserLiquid.parseLiquidInput(input); + LiquidEntry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); } catch (InvalidInputException e) { System.out.println(e.getMessage()); @@ -75,7 +75,7 @@ public void printLiquidList() { } else { System.out.println("Liquid List:"); for (int i = 0; i < liquidArrayList.size(); i++) { - Entry entry = liquidArrayList.get(i); + LiquidEntry entry = liquidArrayList.get(i); Beverage beverage = entry.getBeverage(); System.out.println((i + 1) + ". Beverage: " + beverage.getBeverage() + ", Volume: " + beverage.getVolume()); diff --git a/src/main/java/seedu/lifetrack/system/Parser.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java similarity index 98% rename from src/main/java/seedu/lifetrack/system/Parser.java rename to src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 78026a4c42..c8310e3111 100644 --- a/src/main/java/seedu/lifetrack/system/Parser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -1,4 +1,4 @@ -package seedu.lifetrack.system; +package seedu.lifetrack.system.parser; import seedu.lifetrack.calories.calorielist.Entry; import seedu.lifetrack.calories.calorielist.InputEntry; @@ -7,7 +7,7 @@ import seedu.lifetrack.calories.Food; import seedu.lifetrack.system.exceptions.InvalidInputException; -public class Parser { +public class ParserCalories { private static final int CARBS_IDX = 0; private static final int PROTEINS_IDX = 1; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 1a91d453e1..61a86ae08a 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -1,7 +1,7 @@ package seedu.lifetrack.system.parser; -import seedu.lifetrack.liquids.beverage.Beverage; -import seedu.lifetrack.liquids.liquidlist.Entry; +import seedu.lifetrack.liquids.Beverage; +import seedu.lifetrack.liquids.liquidlist.LiquidEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; public class ParserLiquid { @@ -19,7 +19,7 @@ public class ParserLiquid { * @throws InvalidInputException if the input string is missing components or * contains empty fields */ - public static Entry parseLiquidInput(String input) throws InvalidInputException { + public static LiquidEntry parseLiquidInput(String input) throws InvalidInputException { // splits string according to b/ and v/ keywords String[] parts = input.split("b/|v/"); @@ -38,13 +38,13 @@ public static Entry parseLiquidInput(String input) throws InvalidInputException } return getNewLiquidEntry(volume, beverageName); } - private static Entry getNewLiquidEntry(String strVolume, String name) throws InvalidInputException { + private static LiquidEntry getNewLiquidEntry(String strVolume, String name) throws InvalidInputException { int volume = Integer.parseInt(strVolume); //create objects for Beverage Beverage liquidToAdd = new Beverage(name, volume); //create Object Entry to be returned - return new Entry(liquidToAdd); + return new LiquidEntry(liquidToAdd); } } diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index a2fa427442..c8bd4644da 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.lifetrack.system.Parser.parseCaloriesInput; +import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; import static seedu.lifetrack.system.parser.ParserLiquid.parseLiquidInput; From b60275754225e955bc4a004228d71674f9bd1b92 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 09:06:12 +0800 Subject: [PATCH 057/414] Add entry addedd successfully message to show on terminal --- .../java/seedu/lifetrack/calories/calorielist/CalorieList.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index a030387182..f9130dd9b0 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -50,6 +50,7 @@ public void addEntry(String input) { try { Entry newEntry = ParserCalories.parseCaloriesInput(input); calorieArrayList.add(newEntry); + System.out.println("New entry successfully added!"); } catch (InvalidInputException e) { System.out.println(e.getMessage()); } From 426eb360dae89c4a24b8a754df606d66c6c78665 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 09:41:10 +0800 Subject: [PATCH 058/414] Add new customised exception messages for adding calories entry --- .../system/exceptions/InvalidInputException.java | 4 ++-- .../seedu/lifetrack/system/parser/ParserCalories.java | 9 ++++++--- .../java/seedu/lifetrack/system/parser/ParserLiquid.java | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index 74db52f3a4..2f2eb3930f 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -2,7 +2,7 @@ public class InvalidInputException extends Exception { - public InvalidInputException() { - super("Please ensure that you have keyed in the correct format!"); + public InvalidInputException(String exceptionMessage) { + super(exceptionMessage); } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index c8310e3111..583efca8d3 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -37,8 +37,10 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio int macrosIndex = input.indexOf("m/"); if (descriptionIndex == -1 || caloriesIndex == -1 || dateIndex == -1 || (!(descriptionIndex < caloriesIndex && caloriesIndex < dateIndex) && - macrosIndex != -1 && dateIndex < macrosIndex)) { - throw new InvalidInputException(); + macrosIndex != -1 && dateIndex < macrosIndex)) { + throw new InvalidInputException("Please ensure that you have keyed in the correct format" + + " in the correct order!\n" + "Example input: " + + "calories in desc/DESCRIPTION c/INTEGER_CALORIES date/DATE"); } //extract command, description, calories, date and macronutrients from input @@ -59,7 +61,8 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio //check if the description, calories or date fields are empty if (description.isEmpty() || strCalories.isEmpty() || date.isEmpty()) { - throw new InvalidInputException(); + throw new InvalidInputException("Please ensure that input parameters are not empty!\n" + + "Example input: " + "calories in desc/DESCRIPTION c/INTEGER_CALORIES date/DATE"); } try { diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 61a86ae08a..90e9e4fa61 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -25,7 +25,7 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce String[] parts = input.split("b/|v/"); // parts length less than 3 means that not all split keywords were keyed in if (parts.length < 3) { - throw new InvalidInputException(); + throw new InvalidInputException("Please ensure that you have keyed in the correct format!"); } // extracts beverage name and quantity portion from input @@ -34,7 +34,7 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce // ensures that both inputs are not empty if (beverageName.isEmpty() || volume.isEmpty()) { - throw new InvalidInputException(); + throw new InvalidInputException("Please ensure that you have keyed in the correct format!"); } return getNewLiquidEntry(volume, beverageName); } From 30a129cba9f06d3e49b0b5ff4f66085fe664cd63 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 10:52:53 +0800 Subject: [PATCH 059/414] Add Exception handling for macros Case 1: Ensure all macro fields are filled up with integer Case 2: Handles case where spaces are typed with macros , eg: m/ 1 , 222 , 3 --- .../lifetrack/system/parser/ParserCalories.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 583efca8d3..554b3d04c2 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -51,10 +51,13 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio String date = parts[3].trim(); int[] macros; - //check if optional macronutrients field was provided if (parts.length == 5) { String macroString = parts[4].trim(); - macros = getMacrosFromString(macroString); + try { + macros = getMacrosFromString(macroString); + } catch (InvalidInputException e) { + throw new InvalidInputException(e.getMessage()); + } } else { macros = null; } @@ -80,15 +83,21 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio } } - private static int[] getMacrosFromString(String macroString) { + private static int[] getMacrosFromString(String macroString) throws InvalidInputException { int[] macros = new int[3]; try { String[] macroParts = macroString.split(","); int idx = 0; for (String macro: macroParts) { - macros[idx] = Integer.parseInt(macro); + macros[idx] = Integer.parseInt(macro.trim()); idx++; } +// Exception handling when user does not fill up values for macros + if (idx != 3) { + throw new InvalidInputException("Invalid input exception: " + + "Please ensure that all macronutrients fields are filled up. " + + "For example: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"); + } } catch (NumberFormatException e) { System.out.println("Please input only numbers into the macronutrients field!"); } From e84d786c6f98b7c5ac8cbb9bbcd724b9fad38bbe Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 11:26:14 +0800 Subject: [PATCH 060/414] Add exception handling for blank space input for macro Handle input: calories in desc/ eat yyy c/321 date/yesterday m/ ,0, 22 Throw exception for the input --- .../java/seedu/lifetrack/system/parser/ParserCalories.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 554b3d04c2..aae10503ab 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -89,6 +89,13 @@ private static int[] getMacrosFromString(String macroString) throws InvalidInput String[] macroParts = macroString.split(","); int idx = 0; for (String macro: macroParts) { + //Exception handling when user puts spaces in m/ + //EG m/123, , 123 + if (macro.trim().isEmpty()) { + throw new InvalidInputException("Invalid input exception: " + + "Please ensure that all macronutrients fields are filled up. " + + "For example: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"); + } macros[idx] = Integer.parseInt(macro.trim()); idx++; } From 6a20c8bd168e38102a423b8051f0ea2817e8f348 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 21 Mar 2024 12:24:32 +0800 Subject: [PATCH 061/414] add assertions for parseCaloriesInput --- .../java/seedu/lifetrack/system/parser/ParserCalories.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index c8310e3111..1e0d8cb037 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -40,9 +40,13 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio macrosIndex != -1 && dateIndex < macrosIndex)) { throw new InvalidInputException(); } + assert descriptionIndex != -1 : "The desc/ keyword should exist!"; + assert caloriesIndex != -1 : "The c/ keyword should exist!"; + assert dateIndex != -1 : "The date/ keyword should exist!"; //extract command, description, calories, date and macronutrients from input String[] parts = input.split("desc/|c/|date/|m/"); + assert parts.length >= 4 : "The desc/, c/, date/ fields must have been provided!"; String command = parts[0].trim(); String description = parts[1].trim(); String strCalories = parts[2].trim(); From ef1b2003a5a6070107a7e013518f13c1e5e6587b Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 12:43:56 +0800 Subject: [PATCH 062/414] Modify parseCaloriesInput method to take in description withour desc/ Update CalorieListTest to follow format --- .../lifetrack/system/parser/ParserCalories.java | 17 +++++++++++++++-- .../java/seedu/lifetrack/CalorieListTest.java | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index aae10503ab..296423270b 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -13,6 +13,8 @@ public class ParserCalories { private static final int PROTEINS_IDX = 1; private static final int FATS_IDX = 2; + private static final int CALORIES_OUT_PADDING = 12; + /** * Parses a string input to create an Entry object representing calorie intake. * @@ -49,10 +51,21 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio String description = parts[1].trim(); String strCalories = parts[2].trim(); String date = parts[3].trim(); + String[] parts = input.split("c/|date/|m/"); + String command = parts[0].substring(0, CALORIES_OUT_PADDING).trim(); + String description; + if (command.equals("calories out")) { + description = parts[0].substring(CALORIES_OUT_PADDING, caloriesIndex).trim(); + } else { + command = parts[0].substring(0, CALORIES_OUT_PADDING - 1).trim(); + description = parts[0].substring(CALORIES_OUT_PADDING - 1, caloriesIndex).trim(); + } + String strCalories = parts[1].trim(); + String date = parts[2].trim(); int[] macros; - if (parts.length == 5) { - String macroString = parts[4].trim(); + if (parts.length == 4) { + String macroString = parts[3].trim(); try { macros = getMacrosFromString(macroString); } catch (InvalidInputException e) { diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index b9dd48bf25..8ab458bb17 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -12,8 +12,8 @@ public class CalorieListTest { public void addEntry_validInput_entryAdded() { // Test setup CalorieList calorieList = new CalorieList(); - String validInputCalorieIn = "calories in desc/Eat burger c/369 date/2024-03-14"; - String validInputCalorieOut = "calories out desc/run c/679 date/2024-03-15"; + String validInputCalorieIn = "calories in Eat burger c/369 date/2024-03-14"; + String validInputCalorieOut = "calories out run c/679 date/2024-03-15"; // Call method to test calorieList.addEntry(validInputCalorieIn); From cde543f74c9a02c9b3bd3d6ca678f71f4626be63 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 12:44:49 +0800 Subject: [PATCH 063/414] Update exception messages and conditions to follow removal of desc/ keyword --- .../system/parser/ParserCalories.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 296423270b..4a2b84cdbf 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -32,25 +32,20 @@ public class ParserCalories { */ public static Entry parseCaloriesInput(String input) throws InvalidInputException { - //check that desc/, c/ and date/ keywords exist in the correct order, else throw exception - int descriptionIndex = input.indexOf("desc/"); + //check that c/ and date/ keywords exist in the correct order, else throw exception int caloriesIndex = input.indexOf("c/"); int dateIndex = input.indexOf("date/"); int macrosIndex = input.indexOf("m/"); - if (descriptionIndex == -1 || caloriesIndex == -1 || dateIndex == -1 || - (!(descriptionIndex < caloriesIndex && caloriesIndex < dateIndex) && - macrosIndex != -1 && dateIndex < macrosIndex)) { + if (caloriesIndex == -1 || dateIndex == -1 || + (!(caloriesIndex < dateIndex) && + macrosIndex != -1 && dateIndex < macrosIndex) || + dateIndex < caloriesIndex) { throw new InvalidInputException("Please ensure that you have keyed in the correct format" + " in the correct order!\n" + "Example input: " + - "calories in desc/DESCRIPTION c/INTEGER_CALORIES date/DATE"); + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE"); } //extract command, description, calories, date and macronutrients from input - String[] parts = input.split("desc/|c/|date/|m/"); - String command = parts[0].trim(); - String description = parts[1].trim(); - String strCalories = parts[2].trim(); - String date = parts[3].trim(); String[] parts = input.split("c/|date/|m/"); String command = parts[0].substring(0, CALORIES_OUT_PADDING).trim(); String description; @@ -78,12 +73,12 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio //check if the description, calories or date fields are empty if (description.isEmpty() || strCalories.isEmpty() || date.isEmpty()) { throw new InvalidInputException("Please ensure that input parameters are not empty!\n" + - "Example input: " + "calories in desc/DESCRIPTION c/INTEGER_CALORIES date/DATE"); + "Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE"); } try { int calories = Integer.parseInt(strCalories); - if (command == "calories out") { + if (command.equals("calories out")) { return makeNewOutputEntry(description, calories, date); } else if (macros == null) { return makeNewInputEntry(description, calories, date); From dea61b89bd5114eb61b2e2e14cc16d5b62fb3857 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 13:16:59 +0800 Subject: [PATCH 064/414] Add logic to handle wrong order of macronutrient input --- .../java/seedu/lifetrack/system/parser/ParserCalories.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 4a2b84cdbf..d0da2fa4d7 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -37,12 +37,11 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio int dateIndex = input.indexOf("date/"); int macrosIndex = input.indexOf("m/"); if (caloriesIndex == -1 || dateIndex == -1 || - (!(caloriesIndex < dateIndex) && - macrosIndex != -1 && dateIndex < macrosIndex) || - dateIndex < caloriesIndex) { + (!(caloriesIndex < dateIndex) && macrosIndex != -1 && dateIndex < macrosIndex) || + (macrosIndex != -1 && (macrosIndex < dateIndex || macrosIndex < caloriesIndex))) { throw new InvalidInputException("Please ensure that you have keyed in the correct format" + " in the correct order!\n" + "Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE"); + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); } //extract command, description, calories, date and macronutrients from input From 033560bba46c013cddc03a7403a0623a72b8e847 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 13:19:45 +0800 Subject: [PATCH 065/414] Add condition to handle case where date and calories are in wrong order --- .../java/seedu/lifetrack/system/parser/ParserCalories.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index d0da2fa4d7..233531de4c 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -38,7 +38,8 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio int macrosIndex = input.indexOf("m/"); if (caloriesIndex == -1 || dateIndex == -1 || (!(caloriesIndex < dateIndex) && macrosIndex != -1 && dateIndex < macrosIndex) || - (macrosIndex != -1 && (macrosIndex < dateIndex || macrosIndex < caloriesIndex))) { + (macrosIndex != -1 && (macrosIndex < dateIndex || macrosIndex < caloriesIndex)) || + (macrosIndex == -1 && (dateIndex < caloriesIndex))) { throw new InvalidInputException("Please ensure that you have keyed in the correct format" + " in the correct order!\n" + "Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); From 22a665e45f31c4be155dfa9174769f42a8b2663b Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 13:25:55 +0800 Subject: [PATCH 066/414] Add exception to handle calorie out command which contains macros --- .../seedu/lifetrack/system/parser/ParserCalories.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 233531de4c..ef9a027034 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -40,7 +40,8 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio (!(caloriesIndex < dateIndex) && macrosIndex != -1 && dateIndex < macrosIndex) || (macrosIndex != -1 && (macrosIndex < dateIndex || macrosIndex < caloriesIndex)) || (macrosIndex == -1 && (dateIndex < caloriesIndex))) { - throw new InvalidInputException("Please ensure that you have keyed in the correct format" + + throw new InvalidInputException("Invalid input exception:" + + "Please ensure that you have keyed in the correct format" + " in the correct order!\n" + "Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); } @@ -70,6 +71,12 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio macros = null; } + //throw exception if command is calories out, but macros is keyed in + if (command.equals("calories out") && macros != null) { + throw new InvalidInputException("Invalid input exception: " + + "Calorie output entry cannot have macros"); + } + //check if the description, calories or date fields are empty if (description.isEmpty() || strCalories.isEmpty() || date.isEmpty()) { throw new InvalidInputException("Please ensure that input parameters are not empty!\n" + From 67b65750f816eb45c5547a17922dd5e22533466e Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 13:37:44 +0800 Subject: [PATCH 067/414] Add condition to handle invalid inputs --- src/main/java/seedu/lifetrack/system/Ui.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/Ui.java b/src/main/java/seedu/lifetrack/system/Ui.java index ac4dab79e0..dd3112fa76 100644 --- a/src/main/java/seedu/lifetrack/system/Ui.java +++ b/src/main/java/seedu/lifetrack/system/Ui.java @@ -2,6 +2,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.system.exceptions.InvalidInputException; import java.util.Scanner; @@ -38,6 +39,10 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL calorieList.deleteEntry(line); }else if (line.startsWith("delete liquids")) { liquidList.deleteEntry(line); + } else { + System.out.println("Invalid Input: " + + "Please ensure that input starts with proper command\n" + + "Refer to user guide for more information"); } } From 29b7522effbbdfd8a3e76218115cb3d820b18449 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 21 Mar 2024 13:41:16 +0800 Subject: [PATCH 068/414] UI improvement, better classification of files and added help command --- src/main/java/seedu/lifetrack/LifeTrack.java | 15 +---- .../calories/calorielist/CalorieList.java | 22 ++++--- .../lifetrack/calories/calorielist/Entry.java | 5 ++ .../system/exceptions/ErrorMessages.java | 13 ++++ .../exceptions/InvalidInputException.java | 2 +- .../seedu/lifetrack/system/parser/Parser.java | 2 +- .../seedu/lifetrack/ui/CalorieListUi.java | 25 ++++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 62 +++++++++++++++---- 8 files changed, 112 insertions(+), 34 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java create mode 100644 src/main/java/seedu/lifetrack/ui/CalorieListUi.java diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index f9ad44e4f3..e5049168ba 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -13,18 +13,9 @@ public class LifeTrack { public static void main(String[] args) { CalorieList calorieList = new CalorieList(); Scanner in = new Scanner(System.in); - String logo = "LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + - "L I F E T R R A A C C K K\n" + - "LLL I FFFF EEEE T RRRR AAAAA C KK\n" + - "L I F E T R R A A C C K K\n" + - "LLLLL IIIII F EEEEE TTTT R R A A CCC K K\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What do you want to do today?\n"); + Ui.sayHello(); Ui.readUserInput(calorieList); - byeMessage(); - } - - public static void byeMessage() { - System.out.println("Bye! See you again soon ^^"); + Ui.byeMessage(); } + } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 5b77dc17eb..6124ceaa46 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,6 +1,10 @@ package seedu.lifetrack.calories.calorielist; + +import static seedu.lifetrack.system.exceptions.ErrorMessages.printIndexOutOfBoundsError; +import static seedu.lifetrack.system.exceptions.ErrorMessages.printNumberFormatError; import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; +import static seedu.lifetrack.ui.CalorieListUi.*; import seedu.lifetrack.calories.activity.Activity; import seedu.lifetrack.calories.Calorie; @@ -28,13 +32,13 @@ public Entry getEntry(int index) { public void deleteEntry(String line) { try { int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); + Entry toDelete = calorieArrayList.get(index-1); calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 - System.out.println("Successfully delete the calorie record."); + successfulDeletedMessage(toDelete); } catch (IndexOutOfBoundsException e) { - System.out.println("Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."); - } catch (NumberFormatException e) { - System.out.println("Please enter a valid index!"); + printIndexOutOfBoundsError(); + } catch ( NumberFormatException e) { + printNumberFormatError(); } } @@ -53,6 +57,7 @@ public void addEntry(String input) { try { Entry newEntry = parseCaloriesInput(input); calorieArrayList.add(newEntry); + printNewCalorieEntry(newEntry); } catch (InvalidInputException e) { System.out.println(e.getMessage()); } @@ -65,15 +70,14 @@ public void addEntry(String input) { */ public void printCalorieList() { if (calorieArrayList.isEmpty()) { - System.out.println("Your caloric list is empty."); + emptyListMessage(); } else { - System.out.println("Caloric List:"); + calorieListHeader(); for (int i = 0; i < calorieArrayList.size(); i++) { Entry entry = calorieArrayList.get(i); Activity activity = entry.getActivity(); Calorie calorie = entry.getCalorie(); - System.out.println((i + 1) + ". Activity: " + activity.getDescription() - + ", Calories: " + calorie.getCalories()); + System.out.println("\t " + (i + 1) + calorieArrayList.get(i).toString()); } } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java index 349b879e8e..a0c6da6da9 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java @@ -20,4 +20,9 @@ public Activity getActivity() { public Calorie getCalorie() { return calorie; } + + public static String toString(Entry toDelete){ + return "Activity: " + toDelete.getActivity().getDescription() + + ", Calories: " + toDelete.getCalorie().getCalories(); + } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java new file mode 100644 index 0000000000..30e2a8f292 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -0,0 +1,13 @@ +package seedu.lifetrack.system.exceptions; + +public class ErrorMessages { + public static void printIndexOutOfBoundsError(){ + System.out.println("\t Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list."); + } + + public static void printNumberFormatError(){ + System.out.println("\t Please enter a valid number within the command"); + } + +} diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index 74db52f3a4..6de80d6468 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -3,6 +3,6 @@ public class InvalidInputException extends Exception { public InvalidInputException() { - super("Please ensure that you have keyed in the correct format!"); + super("\t Please ensure that you have keyed in the correct format!"); } } diff --git a/src/main/java/seedu/lifetrack/system/parser/Parser.java b/src/main/java/seedu/lifetrack/system/parser/Parser.java index 705ad54fca..f87dd86471 100644 --- a/src/main/java/seedu/lifetrack/system/parser/Parser.java +++ b/src/main/java/seedu/lifetrack/system/parser/Parser.java @@ -48,7 +48,7 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio } private static Entry getNewCalorieEntry(String command, String date, String time, - String description, String strCalories) throws InvalidInputException { + String description, String strCalories) { int calories = Integer.parseInt(strCalories); diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java new file mode 100644 index 0000000000..0cc40b315c --- /dev/null +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -0,0 +1,25 @@ +package seedu.lifetrack.ui; + +import seedu.lifetrack.calories.calorielist.Entry; + +public class CalorieListUi { + + public static void successfulDeletedMessage(Entry toDelete) { + System.out.println("\t The following calorie record has been successfully deleted!"); + System.out.println("\t " + Entry.toString(toDelete)); + } + + public static void emptyListMessage() { + System.out.println("\t Your caloric list is empty. Add new entries to populate your list :)"); + } + + public static void calorieListHeader() { + System.out.println("\t Your Caloric List:"); + } + + public static void printNewCalorieEntry(Entry newEntry) { + System.out.println("\t The following entry has been added to your caloric list!"); + System.out.println("\t " + Entry.toString(newEntry)); + + } +} diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index c667d99ac9..4ad96ee113 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -15,6 +15,12 @@ */ public class Ui { + private static final String logo = "| IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + + "| I F E T R R A A C K K\n" + + "| I FFFF EEEE T RRRR AAAAA C KK\n" + + "| I F E T R R A A C K K\n" + + "|______ IIIII F EEEEE T R R A A CCC K K\n"; + public static void readUserInput(CalorieList calorieList) { String line; do { @@ -24,26 +30,60 @@ public static void readUserInput(CalorieList calorieList) { } public static void handleUserInput(String line, CalorieList calorieList) { - if (line.trim().isEmpty()) { - System.out.println("Please enter a non empty Input!"); - } else if (line.startsWith("calories in") || line.startsWith("calories out")) { - calorieList.addEntry(line); - } else if (line.startsWith("list")) { - calorieList.printCalorieList(); - } else if (line.startsWith("delete")) { - calorieList.deleteEntry(line); + if (!line.startsWith("bye")) { + printLine(); + line = line.trim().toLowerCase(); + if (line.isEmpty()) { + printEmptyInputMessage(); + } else if (line.startsWith("calories in") || line.startsWith("calories out")) { + calorieList.addEntry(line); + } else if (line.startsWith("list")) { + calorieList.printCalorieList(); + } else if (line.startsWith("delete")) { + calorieList.deleteEntry(line); + } else if (line.startsWith("help")) { + showHelp(); + } else { + handleUnknownInput(); + } + printLine(); } } + public static void sayHello() { + System.out.println("\t Hello from\n\n" + logo); + System.out.println("\t How can I help you today?"); + printLine(); + } + public static void byeMessage() { - System.out.println("Bye! See you again soon ^^"); + printLine(); + System.out.println("\t Bye! See you again soon ^^"); } public static void printEmptyInputMessage() { - System.out.println("Please enter a non-empty input!"); + System.out.println("\t Please enter a non-empty input!"); } public static void printLine() { - System.out.println("\t -------------------------------------------------------------"); + System.out.println("\t -----------------------------------------------------------------------------"); + } + + public static void handleUnknownInput() { + System.out.println("\t Oops! I've never seen this input before..."); + System.out.println("\t If you are unsure of the commands, use the help command for a quick recap :)"); + + } + + public static void showHelp() { + System.out.println("\t LifeTrack Command List:"); + System.out.println("\t - help: Displays a list of available commands and their descriptions."); + System.out.println("\t - calories in/out a/ c/ d/: " + + "Adds a calorie gaining/burning entry into the calories tracker."); + System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); + System.out.println("\t - calories delete : Deletes the entry at the specified index" + + " from the calorie list."); + System.out.println("\t - hydration add b/ v/ : Marks the task at the specified index as done."); + System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list.\""); } } From dc2583f77c4d0a9413ef4f6426c40fb57e8803c2 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 14:24:14 +0800 Subject: [PATCH 069/414] Edit test case to pass gradle Edit file to solve checkstyle error --- src/main/java/seedu/lifetrack/system/Ui.java | 5 --- .../system/parser/ParserCalories.java | 4 +-- .../java/seedu/lifetrack/LifeTrackTest.java | 36 ++++++++++++------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/Ui.java b/src/main/java/seedu/lifetrack/system/Ui.java index dd3112fa76..ac4dab79e0 100644 --- a/src/main/java/seedu/lifetrack/system/Ui.java +++ b/src/main/java/seedu/lifetrack/system/Ui.java @@ -2,7 +2,6 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.liquids.liquidlist.LiquidList; -import seedu.lifetrack.system.exceptions.InvalidInputException; import java.util.Scanner; @@ -39,10 +38,6 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL calorieList.deleteEntry(line); }else if (line.startsWith("delete liquids")) { liquidList.deleteEntry(line); - } else { - System.out.println("Invalid Input: " + - "Please ensure that input starts with proper command\n" + - "Refer to user guide for more information"); } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index ef9a027034..612a31138a 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -42,7 +42,7 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio (macrosIndex == -1 && (dateIndex < caloriesIndex))) { throw new InvalidInputException("Invalid input exception:" + "Please ensure that you have keyed in the correct format" + - " in the correct order!\n" + "Example input: " + + " in the correct order!" + "Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); } @@ -114,7 +114,7 @@ private static int[] getMacrosFromString(String macroString) throws InvalidInput macros[idx] = Integer.parseInt(macro.trim()); idx++; } -// Exception handling when user does not fill up values for macros + //Exception handling when user does not fill up values for macros if (idx != 3) { throw new InvalidInputException("Invalid input exception: " + "Please ensure that all macronutrients fields are filled up. " + diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index c8bd4644da..8c430f037c 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -23,12 +23,12 @@ public void sampleTest() { @Test public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out desc/Run c/200 date/2024-03-14"); + calorieList.addEntry("calories out Run c/200 date/2024-03-14"); int initialSize = calorieList.getSize(); calorieList.deleteEntry("delete calories 1"); assertEquals(initialSize - 1, calorieList.getSize()); - calorieList.addEntry("calories out desc/Run c/200 date/2024-03-14"); - calorieList.addEntry("calories in desc/Eat c/200 date/2024-03-14"); + calorieList.addEntry("calories out Run c/200 date/2024-03-14"); + calorieList.addEntry("calories in Eat c/200 date/2024-03-14"); initialSize = calorieList.getSize(); calorieList.deleteEntry("delete calories 2"); assertEquals(initialSize - 1, calorieList.getSize()); @@ -49,7 +49,9 @@ public void parseCaloriesInput_emptyFields_exceptionThrown() { try { parseCaloriesInput("calories in"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("Invalid input exception:" + "Please ensure that you have keyed in the correct format " + + "in the correct order!" + "Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); } } @@ -58,7 +60,9 @@ public void parseCaloriesInput_incompleteFields_exceptionThrown() { try { parseCaloriesInput("calories in d/220224"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("Invalid input exception:" + "Please ensure that you have keyed in the correct format " + + "in the correct order!" + "Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); } } @@ -80,10 +84,11 @@ public void testPrintCalorieListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in desc/Run c/200 date/2024-03-14"); + calorieList.addEntry("calories in Run c/200 date/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "Caloric List:" + lineSeparator + + String expectedOutput = "New entry successfully added!" + lineSeparator + + "Caloric List:" + lineSeparator + "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -94,14 +99,19 @@ public void testPrintCalorieListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in desc/Run c/200 date/2024-03-14"); - calorieList.addEntry("calories out desc/Walk c/150 date/2024-03-14"); - calorieList.addEntry("calories in desc/Eat c/500 date/2024-03-14"); - calorieList.addEntry("calories out desc/Run c/250 date/2024-03-14"); - calorieList.addEntry("calories in desc/Eat c/300 date/2024-03-14"); + calorieList.addEntry("calories in Run c/200 date/2024-03-14"); + calorieList.addEntry("calories out Walk c/150 date/2024-03-14"); + calorieList.addEntry("calories in Eat c/500 date/2024-03-14"); + calorieList.addEntry("calories out Run c/250 date/2024-03-14"); + calorieList.addEntry("calories in Eat c/300 date/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "Caloric List:" + lineSeparator + + String expectedOutput = "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "Caloric List:" + lineSeparator + "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator + "2. Date: 2024-03-14, Description: Walk, Calories: 150" + lineSeparator + "3. Date: 2024-03-14, Description: Eat, Calories: 500" + lineSeparator + From b97f39a3d3977e0ae44ea4a2b82bb8967dbfc645 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:31:33 +0800 Subject: [PATCH 070/414] add logger --- .../calories/calorielist/CalorieList.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 5b77dc17eb..9450a39f55 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -5,13 +5,14 @@ import seedu.lifetrack.calories.activity.Activity; import seedu.lifetrack.calories.Calorie; import seedu.lifetrack.system.exceptions.InvalidInputException; - +import java.util.logging.*; import java.util.ArrayList; public class CalorieList { private ArrayList calorieArrayList; private final int SIZE_OF_DELETE = 7; + private static Logger logr = Logger.getLogger(CalorieList.class.getName()); public CalorieList() { calorieArrayList= new ArrayList<>(); @@ -31,10 +32,10 @@ public void deleteEntry(String line) { calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { - System.out.println("Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."); + logr.log(Level.WARNING, "Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list.", e); } catch (NumberFormatException e) { - System.out.println("Please enter a valid index!"); + logr.log(Level.WARNING, "Please enter a valid index!", e); } } @@ -50,11 +51,12 @@ public void deleteEntry(String line) { * @param input the input string containing date, time, activity, and calorie count */ public void addEntry(String input) { + logr.setLevel(Level.WARNING); try { Entry newEntry = parseCaloriesInput(input); calorieArrayList.add(newEntry); } catch (InvalidInputException e) { - System.out.println(e.getMessage()); + logr.log(Level.WARNING, e.getMessage(), e); } } @@ -64,14 +66,17 @@ public void addEntry(String input) { * Otherwise, it prints each entry's activity description and calorie count. */ public void printCalorieList() { + logr.setLevel(Level.WARNING); if (calorieArrayList.isEmpty()) { - System.out.println("Your caloric list is empty."); + logr.log(Level.INFO,"Your caloric list is empty."); } else { System.out.println("Caloric List:"); for (int i = 0; i < calorieArrayList.size(); i++) { Entry entry = calorieArrayList.get(i); Activity activity = entry.getActivity(); Calorie calorie = entry.getCalorie(); + logr.log(Level.INFO,(i + 1) + ". Activity: " + activity.getDescription() + + ", Calories: " + calorie.getCalories()); System.out.println((i + 1) + ". Activity: " + activity.getDescription() + ", Calories: " + calorie.getCalories()); } From a8ec5fea868a692851697a7a96b5fb317ca0e6aa Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:52:31 +0800 Subject: [PATCH 071/414] add logger --- .../lifetrack/calories/calorielist/CalorieList.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index a030387182..02a1bc373c 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -2,13 +2,15 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserCalories; - +import java.util.logging.*; import java.util.ArrayList; public class CalorieList { private ArrayList calorieArrayList; private final int DELETE_PADDING = 16; + private static Logger logr = Logger.getLogger(CalorieList.class.getName()); + public CalorieList() { calorieArrayList= new ArrayList<>(); @@ -28,10 +30,10 @@ public void deleteEntry(String line) { calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { - System.out.println("Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."); + logr.log(Level.WARNING, "Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list.", e); } catch (NumberFormatException e) { - System.out.println("Please enter a valid index!"); + logr.log(Level.WARNING, "Please enter a valid index!", e); } } @@ -51,7 +53,7 @@ public void addEntry(String input) { Entry newEntry = ParserCalories.parseCaloriesInput(input); calorieArrayList.add(newEntry); } catch (InvalidInputException e) { - System.out.println(e.getMessage()); + logr.log(Level.WARNING, e.getMessage(), e); } } From 1bd0569c2396823ec739306a8c6913facbfd8bac Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:02:34 +0800 Subject: [PATCH 072/414] add logger to LiquidList.java --- .../lifetrack/liquids/liquidlist/LiquidList.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 4ad302d975..7760c93f56 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -1,9 +1,10 @@ package seedu.lifetrack.liquids.liquidlist; +import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.liquids.Beverage; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserLiquid; - +import java.util.logging.*; import java.util.ArrayList; /** @@ -14,6 +15,7 @@ public class LiquidList { private ArrayList liquidArrayList; private final int SIZE_OF_DELETE = 15; + private static Logger logr = Logger.getLogger(CalorieList.class.getName()); /** * Constructs an empty LiquidList. @@ -44,10 +46,10 @@ public void deleteEntry(String line) { liquidArrayList.remove(index - 1); System.out.println("Successfully delete the liquid record."); } catch (IndexOutOfBoundsException e) { - System.out.println("Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."); + logr.log(Level.WARNING, "Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list.", e); } catch (NumberFormatException e) { - System.out.println("Please enter a valid index!"); + logr.log(Level.WARNING, "Please enter a valid index!", e); } } @@ -61,7 +63,7 @@ public void addEntry(String input) { LiquidEntry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); } catch (InvalidInputException e) { - System.out.println(e.getMessage()); + logr.log(Level.WARNING, e.getMessage(), e); } } From de86dc8e0a91bd7852296cb69d7d9848438a3367 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:09:56 +0800 Subject: [PATCH 073/414] fix gradle --- .../java/seedu/lifetrack/calories/calorielist/CalorieList.java | 3 ++- .../java/seedu/lifetrack/liquids/liquidlist/LiquidList.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 02a1bc373c..058343a3b4 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -2,7 +2,8 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserCalories; -import java.util.logging.*; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.ArrayList; public class CalorieList { diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 7760c93f56..61a7a8dfae 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -4,8 +4,9 @@ import seedu.lifetrack.liquids.Beverage; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserLiquid; -import java.util.logging.*; import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Represents a list of liquid entries. From fdfd22536d6bd829f04d21ddb5c28c3e761fefee Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:13:59 +0800 Subject: [PATCH 074/414] fix gradle --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 4 ++-- .../java/seedu/lifetrack/liquids/liquidlist/LiquidList.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 058343a3b4..e82aebf29a 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -9,7 +9,7 @@ public class CalorieList { private ArrayList calorieArrayList; - private final int DELETE_PADDING = 16; + private final int DELETEPADDING = 16; private static Logger logr = Logger.getLogger(CalorieList.class.getName()); @@ -27,7 +27,7 @@ public Entry getEntry(int index) { */ public void deleteEntry(String line) { try { - int index = Integer.parseInt(line.substring(DELETE_PADDING).trim()); + int index = Integer.parseInt(line.substring(DELETEPADDING).trim()); calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 61a7a8dfae..be6cdd12cd 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -15,7 +15,7 @@ public class LiquidList { private ArrayList liquidArrayList; - private final int SIZE_OF_DELETE = 15; + private final int SIZEOFDELETE = 15; private static Logger logr = Logger.getLogger(CalorieList.class.getName()); /** @@ -43,7 +43,7 @@ public LiquidEntry getEntry(int index) { */ public void deleteEntry(String line) { try { - int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); + int index = Integer.parseInt(line.substring(SIZEOFDELETE).trim()); liquidArrayList.remove(index - 1); System.out.println("Successfully delete the liquid record."); } catch (IndexOutOfBoundsException e) { From 7fa771d94bb7c9c4c7766992fccac29b7705a5b3 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:17:25 +0800 Subject: [PATCH 075/414] fix gradle --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 8 ++++---- .../seedu/lifetrack/liquids/liquidlist/LiquidList.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index e82aebf29a..c0c6d1bed1 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -7,10 +7,10 @@ import java.util.ArrayList; public class CalorieList { - - private ArrayList calorieArrayList; - private final int DELETEPADDING = 16; private static Logger logr = Logger.getLogger(CalorieList.class.getName()); + private ArrayList calorieArrayList; + private final int deletePadding = 16; + public CalorieList() { @@ -27,7 +27,7 @@ public Entry getEntry(int index) { */ public void deleteEntry(String line) { try { - int index = Integer.parseInt(line.substring(DELETEPADDING).trim()); + int index = Integer.parseInt(line.substring(deletePadding).trim()); calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 System.out.println("Successfully delete the calorie record."); } catch (IndexOutOfBoundsException e) { diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index be6cdd12cd..b1066acaa8 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -13,10 +13,10 @@ * Provides methods to add, delete, and print liquid entries. */ public class LiquidList { - - private ArrayList liquidArrayList; - private final int SIZEOFDELETE = 15; private static Logger logr = Logger.getLogger(CalorieList.class.getName()); + private ArrayList liquidArrayList; + private final int sizeOfDelete = 15; + /** * Constructs an empty LiquidList. @@ -43,7 +43,7 @@ public LiquidEntry getEntry(int index) { */ public void deleteEntry(String line) { try { - int index = Integer.parseInt(line.substring(SIZEOFDELETE).trim()); + int index = Integer.parseInt(line.substring(sizeOfDelete).trim()); liquidArrayList.remove(index - 1); System.out.println("Successfully delete the liquid record."); } catch (IndexOutOfBoundsException e) { From c63cd9a3412e66b48c8416c8c6b8bf83f2b9b4d0 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 21 Mar 2024 15:40:05 +0800 Subject: [PATCH 076/414] Update Tests --- .../calories/calorielist/CalorieList.java | 7 +++- .../lifetrack/calories/calorielist/Entry.java | 6 +-- src/main/java/seedu/lifetrack/ui/Ui.java | 3 +- .../java/seedu/lifetrack/LifeTrackTest.java | 40 ++++++++++++++----- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 6124ceaa46..e4ecee2434 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -4,7 +4,10 @@ import static seedu.lifetrack.system.exceptions.ErrorMessages.printIndexOutOfBoundsError; import static seedu.lifetrack.system.exceptions.ErrorMessages.printNumberFormatError; import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; -import static seedu.lifetrack.ui.CalorieListUi.*; +import static seedu.lifetrack.ui.CalorieListUi.emptyListMessage; +import static seedu.lifetrack.ui.CalorieListUi.successfulDeletedMessage; +import static seedu.lifetrack.ui.CalorieListUi.printNewCalorieEntry; +import static seedu.lifetrack.ui.CalorieListUi.calorieListHeader; import seedu.lifetrack.calories.activity.Activity; import seedu.lifetrack.calories.Calorie; @@ -77,7 +80,7 @@ public void printCalorieList() { Entry entry = calorieArrayList.get(i); Activity activity = entry.getActivity(); Calorie calorie = entry.getCalorie(); - System.out.println("\t " + (i + 1) + calorieArrayList.get(i).toString()); + System.out.println("\t " + (i + 1) + ". " + Entry.toString(calorieArrayList.get(i))); } } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java index a0c6da6da9..b0730fe0f2 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java @@ -21,8 +21,8 @@ public Calorie getCalorie() { return calorie; } - public static String toString(Entry toDelete){ - return "Activity: " + toDelete.getActivity().getDescription() - + ", Calories: " + toDelete.getCalorie().getCalories(); + public static String toString(Entry toPrint){ + return "Activity: " + toPrint.getActivity().getDescription() + + ", Calories: " + toPrint.getCalorie().getCalories(); } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 4ad96ee113..fd14ee5ada 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -83,7 +83,8 @@ public static void showHelp() { System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); System.out.println("\t - calories delete : Deletes the entry at the specified index" + " from the calorie list."); - System.out.println("\t - hydration add b/ v/ : Marks the task at the specified index as done."); + System.out.println("\t - hydration add b/ v/ : " + + "Marks the task at the specified index as done."); System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list.\""); } } diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 84cd9c94fb..0f47a14b9c 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.Test; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.Entry; import seedu.lifetrack.system.exceptions.InvalidInputException; import java.io.ByteArrayOutputStream; @@ -13,6 +14,7 @@ class LifeTrackTest { + private final String addedEntryHeader = "\t The following entry has been added to your caloric list!"; @Test public void sampleTest() { assertTrue(true); @@ -47,7 +49,7 @@ public void parseCaloriesInput_emptyFields_exceptionThrown() { try { parseCaloriesInput("calories in"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("\t Please ensure that you have keyed in the correct format!", e.getMessage()); } } @@ -56,7 +58,7 @@ public void parseCaloriesInput_incompleteFields_exceptionThrown() { try { parseCaloriesInput("calories in d/220224 t/"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("\t Please ensure that you have keyed in the correct format!", e.getMessage()); } } @@ -68,7 +70,8 @@ public void testPrintCalorieListEmpty() { CalorieList calorieList = new CalorieList(); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "Your caloric list is empty." + lineSeparator; + String expectedOutput = "\t Your caloric list is empty. " + + "Add new entries to populate your list :)" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -81,7 +84,10 @@ public void testPrintCalorieListNonEmpty() { calorieList.addEntry("calories in d/2024-03-14 t/12:00 a/Run c/200"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "Caloric List:" + lineSeparator + "1. Activity: Run, Calories: 200" + lineSeparator; + String expectedOutput = addedEntryHeader + lineSeparator + + "\t " + Entry.toString(calorieList.getEntry(0)) + lineSeparator + + "\t Your Caloric List:" + lineSeparator + + "\t 1. Activity: Run, Calories: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -98,13 +104,25 @@ public void testPrintCalorieListMultipleEntries() { calorieList.addEntry("calories in d/2024-03-14 t/16:00 a/Eat c/300"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "Caloric List:" + lineSeparator + - "1. Activity: Run, Calories: 200" + lineSeparator + - "2. Activity: Walk, Calories: 150" + lineSeparator + - "3. Activity: Eat, Calories: 500" + lineSeparator + - "4. Activity: Run, Calories: 250" + lineSeparator + - "5. Activity: Eat, Calories: 300" + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); + StringBuilder expectedOutput = new StringBuilder(); + for (int i = 0; i < 5; i++) { + expectedOutput.append(addedEntryHeader) + .append(lineSeparator).append("\t ").append(Entry.toString(calorieList.getEntry(i))) + .append(lineSeparator); + } + expectedOutput.append("\t Your Caloric List:") + .append(lineSeparator) + .append("\t 1. Activity: Run, Calories: 200") + .append(lineSeparator) + .append("\t 2. Activity: Walk, Calories: 150") + .append(lineSeparator) + .append("\t 3. Activity: Eat, Calories: 500") + .append(lineSeparator) + .append("\t 4. Activity: Run, Calories: 250") + .append(lineSeparator) + .append("\t 5. Activity: Eat, Calories: 300") + .append(lineSeparator); + assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); } From 219231a24047ae7921cb21a34747e4f98562654a Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 15:54:55 +0800 Subject: [PATCH 077/414] Enable assertion for addEntry method --- build.gradle | 1 + .../java/seedu/lifetrack/calories/calorielist/CalorieList.java | 1 + 2 files changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index b0da4382a9..9fec70c1ae 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/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index f9130dd9b0..3abfe91f6b 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -47,6 +47,7 @@ public void deleteEntry(String line) { * @param input the input string containing date, time, activity, and calorie count */ public void addEntry(String input) { + assert (input.startsWith("calories in") || input.startsWith("calories out")) : "ensures that input is correct"; try { Entry newEntry = ParserCalories.parseCaloriesInput(input); calorieArrayList.add(newEntry); From bb37281911e9827dd3070b031a5611cd292fa1ca Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 21 Mar 2024 16:38:01 +0800 Subject: [PATCH 078/414] make ParserCalories more OOP-like, re-organised JUnit tests --- .../system/parser/ParserCalories.java | 137 +++++++----- .../java/seedu/lifetrack/CalorieListTest.java | 86 +++++++- .../java/seedu/lifetrack/LifeTrackTest.java | 204 ------------------ .../java/seedu/lifetrack/LiquidListTest.java | 76 +++++++ .../seedu/lifetrack/ParserCaloriesTest.java | 43 ++++ .../seedu/lifetrack/ParserLiquidTest.java | 28 +++ .../lifetrack/{InputTest.java => UITest.java} | 10 +- 7 files changed, 321 insertions(+), 263 deletions(-) delete mode 100644 src/test/java/seedu/lifetrack/LifeTrackTest.java create mode 100644 src/test/java/seedu/lifetrack/LiquidListTest.java create mode 100644 src/test/java/seedu/lifetrack/ParserCaloriesTest.java create mode 100644 src/test/java/seedu/lifetrack/ParserLiquidTest.java rename src/test/java/seedu/lifetrack/{InputTest.java => UITest.java} (97%) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 456113d9da..5eeb222b12 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -12,7 +12,6 @@ public class ParserCalories { private static final int CARBS_IDX = 0; private static final int PROTEINS_IDX = 1; private static final int FATS_IDX = 2; - private static final int CALORIES_OUT_PADDING = 12; /** @@ -27,88 +26,88 @@ public class ParserCalories { * @param input the input string to be parsed, containing date, time, activity, * and calorie count information * @return an Entry object representing calorie intake - * @throws InvalidInputException if the input string is missing components or - * contains empty fields + * @throws InvalidInputException if the input string is missing components or contains empty fields */ public static Entry parseCaloriesInput(String input) throws InvalidInputException { - - //check that c/ and date/ keywords exist in the correct order, else throw exception int caloriesIndex = input.indexOf("c/"); int dateIndex = input.indexOf("date/"); int macrosIndex = input.indexOf("m/"); - if (caloriesIndex == -1 || dateIndex == -1 || - (!(caloriesIndex < dateIndex) && macrosIndex != -1 && dateIndex < macrosIndex) || - (macrosIndex != -1 && (macrosIndex < dateIndex || macrosIndex < caloriesIndex)) || - (macrosIndex == -1 && (dateIndex < caloriesIndex))) { - throw new InvalidInputException("Invalid input exception:" + - "Please ensure that you have keyed in the correct format" + - " in the correct order!" + "Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); - } + + checkKeywordsExist(caloriesIndex, dateIndex); assert caloriesIndex != -1 : "The c/ keyword should exist!"; assert dateIndex != -1 : "The date/ keyword should exist!"; - //extract command, description, calories, date and macronutrients from input + checkKeywordsCorrectlyOrdered(caloriesIndex, dateIndex, macrosIndex); + assert caloriesIndex < dateIndex : "The c/ keyword must appear before date/ in the input!"; + + //extract command, description, calories, date from input String[] parts = input.split("c/|date/|m/"); - assert parts.length >= 3 : "The c/, date/ fields must have been provided!"; String command = parts[0].substring(0, CALORIES_OUT_PADDING).trim(); - String description; - if (command.equals("calories out")) { - description = parts[0].substring(CALORIES_OUT_PADDING, caloriesIndex).trim(); - } else { - command = parts[0].substring(0, CALORIES_OUT_PADDING - 1).trim(); - description = parts[0].substring(CALORIES_OUT_PADDING - 1, caloriesIndex).trim(); - } + String description = getDescriptionFromInput(input, command, caloriesIndex); String strCalories = parts[1].trim(); String date = parts[2].trim(); - int[] macros; - - if (parts.length == 4) { + + checkInputsAreNonEmpty(description, strCalories, date); + assert description != "" : "The description field should be a non-empty string!"; + assert strCalories != "" : "The calories field should be a non-empty string!"; + assert date != "" : "The date field should be a non-empty string!"; + + //extract macronutrients if user provided it in their input, otherwise initialise it as null + int[] macros = null; + if (macrosIndex != -1) { + if (command.equals("calories out")) { + throw new InvalidInputException("Invalid input exception: Calorie output entry cannot have macros"); + } String macroString = parts[3].trim(); try { - macros = getMacrosFromString(macroString); + macros = getMacrosFromInput(macroString); } catch (InvalidInputException e) { throw new InvalidInputException(e.getMessage()); } - } else { - macros = null; } - //throw exception if command is calories out, but macros is keyed in - if (command.equals("calories out") && macros != null) { - throw new InvalidInputException("Invalid input exception: " + - "Calorie output entry cannot have macros"); - } + //convert calories from string to integer + int calories = getIntegerCaloriesFromInput(strCalories); + checkCaloriesIsPositiveInteger(calories); + assert calories > 0 : "Calories value must be a positive integer!"; - //check if the description, calories or date fields are empty - if (description.isEmpty() || strCalories.isEmpty() || date.isEmpty()) { - throw new InvalidInputException("Please ensure that input parameters are not empty!\n" + - "Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE"); + if (command.equals("calories out")) { + return makeNewOutputEntry(description, calories, date); + } else if (macros == null) { + return makeNewInputEntry(description, calories, date); + } else { + return makeNewInputEntry(description, calories, date, macros); } + } + private static int getIntegerCaloriesFromInput(String strCalories) { + int calories = 0; try { - int calories = Integer.parseInt(strCalories); - if (command.equals("calories out")) { - return makeNewOutputEntry(description, calories, date); - } else if (macros == null) { - return makeNewInputEntry(description, calories, date); - } else { - return makeNewInputEntry(description, calories, date, macros); - } + calories = Integer.parseInt(strCalories); } catch (NumberFormatException e) { - System.out.println("Please input only numbers into the calories field!"); - return null; + System.out.println("Please input only positive integers into the calories field!"); } + return calories; } - private static int[] getMacrosFromString(String macroString) throws InvalidInputException { + private static String getDescriptionFromInput(String inputString, String command, int caloriesIndex) { + String description; + if (command.equals("calories out")) { + description = inputString.substring(CALORIES_OUT_PADDING, caloriesIndex).trim(); + } else { + command = inputString.substring(0, CALORIES_OUT_PADDING - 1).trim(); + description = inputString.substring(CALORIES_OUT_PADDING - 1, caloriesIndex).trim(); + } + return description; + } + + private static int[] getMacrosFromInput(String macroString) throws InvalidInputException { int[] macros = new int[3]; try { String[] macroParts = macroString.split(","); int idx = 0; for (String macro: macroParts) { - //Exception handling when user puts spaces in m/ - //EG m/123, , 123 + //throw exception if user inputs whitespace in the macros field i.e. m/123, ,123 if (macro.trim().isEmpty()) { throw new InvalidInputException("Invalid input exception: " + "Please ensure that all macronutrients fields are filled up. " + @@ -117,7 +116,7 @@ private static int[] getMacrosFromString(String macroString) throws InvalidInput macros[idx] = Integer.parseInt(macro.trim()); idx++; } - //Exception handling when user does not fill up values for macros + //throw exception if there are missing values in the macros field if (idx != 3) { throw new InvalidInputException("Invalid input exception: " + "Please ensure that all macronutrients fields are filled up. " + @@ -129,6 +128,40 @@ private static int[] getMacrosFromString(String macroString) throws InvalidInput return macros; } + private static void checkCaloriesIsPositiveInteger(int calories) throws InvalidInputException { + if (calories <= 0) { + throw new InvalidInputException("Please input only positive integers into the calories field!"); + } + } + + private static void checkInputsAreNonEmpty(String description, String strCalories, String date) + throws InvalidInputException { + //check if the description, calories or date fields are empty + if (description.isEmpty() || strCalories.isEmpty() || date.isEmpty()) { + throw new InvalidInputException("Please ensure that input parameters are not empty!\n" + + "Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE"); + } + } + + private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws InvalidInputException { + //check that c/ and date/ keywords exist in the input, else throw exception + if (caloriesIndex == -1 || dateIndex == -1) { + throw new InvalidInputException("Invalid input exception: Please ensure that you have keyed in " + + "the correct format in the correct order! Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); + } + } + + private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateIndex, int macrosIndex) + throws InvalidInputException { + if ((macrosIndex != -1 && !(caloriesIndex < dateIndex && dateIndex < macrosIndex)) || + (macrosIndex == -1 && !(caloriesIndex < dateIndex))) { + throw new InvalidInputException("Invalid input exception: Please ensure that you have keyed in " + + "the correct format in the correct order! Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); + } + } + private static Entry makeNewOutputEntry(String description, int calories, String date) { Activity newActivity = new Activity(); diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 8ab458bb17..4dc3adc286 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -1,11 +1,14 @@ package seedu.lifetrack; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.calories.calorielist.Entry; -import static org.junit.jupiter.api.Assertions.assertEquals; - public class CalorieListTest { @Test @@ -34,4 +37,83 @@ public void addEntry_validInput_entryAdded() { assertEquals("run", secondEntry.getDescription()); assertEquals(679, secondEntry.getCalories()); } + + @Test + public void testDeleteCalorieValidIndex() { + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories out Run c/200 date/2024-03-14"); + int initialSize = calorieList.getSize(); + calorieList.deleteEntry("delete calories 1"); + assertEquals(initialSize - 1, calorieList.getSize()); + calorieList.addEntry("calories out Run c/200 date/2024-03-14"); + calorieList.addEntry("calories in Eat c/200 date/2024-03-14"); + initialSize = calorieList.getSize(); + calorieList.deleteEntry("delete calories 2"); + assertEquals(initialSize - 1, calorieList.getSize()); + } + + @Test + public void testDeleteCalorieInvalidIndex() { + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories out Run c/200 date/2024-03-14"); + int initialSize = calorieList.getSize(); + calorieList.deleteEntry("delete calories 2"); // Index out of bounds + calorieList.deleteEntry("delete calories -1"); + assertEquals(initialSize, calorieList.getSize()); + } + + @Test + public void testPrintCalorieListEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.printCalorieList(); + System.setOut(System.out); + String expectedOutput = "Your caloric list is empty." + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintCalorieListNonEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in Run c/200 date/2024-03-14"); + calorieList.printCalorieList(); + System.setOut(System.out); + String expectedOutput = "New entry successfully added!" + lineSeparator + + "Caloric List:" + lineSeparator + + "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintCalorieListMultipleEntries() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in Run c/200 date/2024-03-14"); + calorieList.addEntry("calories out Walk c/150 date/2024-03-14"); + calorieList.addEntry("calories in Eat c/500 date/2024-03-14"); + calorieList.addEntry("calories out Run c/250 date/2024-03-14"); + calorieList.addEntry("calories in Eat c/300 date/2024-03-14"); + calorieList.printCalorieList(); + System.setOut(System.out); + String expectedOutput = "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "New entry successfully added!" + lineSeparator + + "Caloric List:" + lineSeparator + + "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator + + "2. Date: 2024-03-14, Description: Walk, Calories: 150" + lineSeparator + + "3. Date: 2024-03-14, Description: Eat, Calories: 500" + lineSeparator + + "4. Date: 2024-03-14, Description: Run, Calories: 250" + lineSeparator + + "5. Date: 2024-03-14, Description: Eat, Calories: 300" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + assertEquals(5, calorieList.getSize()); + } } diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java deleted file mode 100644 index 8c430f037c..0000000000 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ /dev/null @@ -1,204 +0,0 @@ -package seedu.lifetrack; - -import org.junit.jupiter.api.Test; -import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.liquids.liquidlist.LiquidList; -import seedu.lifetrack.system.exceptions.InvalidInputException; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; -import static seedu.lifetrack.system.parser.ParserLiquid.parseLiquidInput; - - -class LifeTrackTest { - @Test - public void sampleTest() { - assertTrue(true); - } - - @Test - public void testDeleteCalorieValidIndex() { - CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out Run c/200 date/2024-03-14"); - int initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete calories 1"); - assertEquals(initialSize - 1, calorieList.getSize()); - calorieList.addEntry("calories out Run c/200 date/2024-03-14"); - calorieList.addEntry("calories in Eat c/200 date/2024-03-14"); - initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete calories 2"); - assertEquals(initialSize - 1, calorieList.getSize()); - } - - @Test - public void testDeleteCalorieInvalidIndex() { - CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out desc/Run c/200 date/2024-03-14"); - int initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete calories 2"); // Index out of bounds - calorieList.deleteEntry("delete calories -1"); - assertEquals(initialSize, calorieList.getSize()); - } - - @Test - public void parseCaloriesInput_emptyFields_exceptionThrown() { - try { - parseCaloriesInput("calories in"); - } catch (InvalidInputException e) { - assertEquals("Invalid input exception:" + "Please ensure that you have keyed in the correct format " + - "in the correct order!" + "Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); - } - } - - @Test - public void parseCaloriesInput_incompleteFields_exceptionThrown() { - try { - parseCaloriesInput("calories in d/220224"); - } catch (InvalidInputException e) { - assertEquals("Invalid input exception:" + "Please ensure that you have keyed in the correct format " + - "in the correct order!" + "Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); - } - } - - @Test - public void testPrintCalorieListEmpty() { - String lineSeparator = System.lineSeparator(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outputStream)); - CalorieList calorieList = new CalorieList(); - calorieList.printCalorieList(); - System.setOut(System.out); - String expectedOutput = "Your caloric list is empty." + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); - } - - @Test - public void testPrintCalorieListNonEmpty() { - String lineSeparator = System.lineSeparator(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outputStream)); - CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in Run c/200 date/2024-03-14"); - calorieList.printCalorieList(); - System.setOut(System.out); - String expectedOutput = "New entry successfully added!" + lineSeparator + - "Caloric List:" + lineSeparator + - "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); - } - - @Test - public void testPrintCalorieListMultipleEntries() { - String lineSeparator = System.lineSeparator(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outputStream)); - CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in Run c/200 date/2024-03-14"); - calorieList.addEntry("calories out Walk c/150 date/2024-03-14"); - calorieList.addEntry("calories in Eat c/500 date/2024-03-14"); - calorieList.addEntry("calories out Run c/250 date/2024-03-14"); - calorieList.addEntry("calories in Eat c/300 date/2024-03-14"); - calorieList.printCalorieList(); - System.setOut(System.out); - String expectedOutput = "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "Caloric List:" + lineSeparator + - "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator + - "2. Date: 2024-03-14, Description: Walk, Calories: 150" + lineSeparator + - "3. Date: 2024-03-14, Description: Eat, Calories: 500" + lineSeparator + - "4. Date: 2024-03-14, Description: Run, Calories: 250" + lineSeparator + - "5. Date: 2024-03-14, Description: Eat, Calories: 300" + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); - assertEquals(5, calorieList.getSize()); - } - @Test - public void testDeleteLiquidValidIndex() { - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); - int initialSize = liquidList.getSize(); - liquidList.deleteEntry("delete liquids 1"); - assertEquals(initialSize - 1, liquidList.getSize()); - } - - @Test - public void testDeleteLiquidInvalidIndex() { - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); - int initialSize = liquidList.getSize(); - liquidList.deleteEntry("delete liquids 2"); // Index out of bounds - liquidList.deleteEntry("delete liquids -1"); - assertEquals(initialSize, liquidList.getSize()); - } - - @Test - public void parseLiquidInput_emptyFields_exceptionThrown() { - try { - parseLiquidInput("liquids in"); - } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); - } - } - - @Test - public void parseLiquidInput_incompleteFields_exceptionThrown() { - try { - parseLiquidInput("liquids in b/Milo"); - } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); - } - } - - @Test - public void testPrintLiquidListEmpty() { - String lineSeparator = System.lineSeparator(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outputStream)); - LiquidList liquidList = new LiquidList(); - liquidList.printLiquidList(); - System.setOut(System.out); - String expectedOutput = "Your liquid list is empty." + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); - } - - @Test - public void testPrintLiquidListNonEmpty() { - String lineSeparator = System.lineSeparator(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outputStream)); - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); - liquidList.printLiquidList(); - System.setOut(System.out); - String expectedOutput = "Liquid List:" + lineSeparator + - "1. Beverage: Milo, Volume: 200" + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); - } - - @Test - public void testPrintLiquidListMultipleEntries() { - String lineSeparator = System.lineSeparator(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outputStream)); - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); - liquidList.addEntry("liquids in b/Water v/300"); - liquidList.addEntry("liquids in b/Juice v/150"); - liquidList.printLiquidList(); - System.setOut(System.out); - String expectedOutput = "Liquid List:" + lineSeparator + - "1. Beverage: Milo, Volume: 200" + lineSeparator + - "2. Beverage: Water, Volume: 300" + lineSeparator + - "3. Beverage: Juice, Volume: 150" + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); - assertEquals(3, liquidList.getSize()); - } -} diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java new file mode 100644 index 0000000000..10137d07b1 --- /dev/null +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -0,0 +1,76 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import seedu.lifetrack.liquids.liquidlist.LiquidList; + +public class LiquidListTest { + + @Test + public void testDeleteLiquidValidIndex() { + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + int initialSize = liquidList.getSize(); + liquidList.deleteEntry("delete liquids 1"); + assertEquals(initialSize - 1, liquidList.getSize()); + } + + @Test + public void testDeleteLiquidInvalidIndex() { + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + int initialSize = liquidList.getSize(); + liquidList.deleteEntry("delete liquids 2"); // Index out of bounds + liquidList.deleteEntry("delete liquids -1"); + assertEquals(initialSize, liquidList.getSize()); + } + + @Test + public void testPrintLiquidListEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + LiquidList liquidList = new LiquidList(); + liquidList.printLiquidList(); + System.setOut(System.out); + String expectedOutput = "Your liquid list is empty." + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintLiquidListNonEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + liquidList.printLiquidList(); + System.setOut(System.out); + String expectedOutput = "Liquid List:" + lineSeparator + + "1. Beverage: Milo, Volume: 200" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintLiquidListMultipleEntries() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + LiquidList liquidList = new LiquidList(); + liquidList.addEntry("liquids in b/Milo v/200"); + liquidList.addEntry("liquids in b/Water v/300"); + liquidList.addEntry("liquids in b/Juice v/150"); + liquidList.printLiquidList(); + System.setOut(System.out); + String expectedOutput = "Liquid List:" + lineSeparator + + "1. Beverage: Milo, Volume: 200" + lineSeparator + + "2. Beverage: Water, Volume: 300" + lineSeparator + + "3. Beverage: Juice, Volume: 150" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + assertEquals(3, liquidList.getSize()); + } +} diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java new file mode 100644 index 0000000000..7a39cf640e --- /dev/null +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -0,0 +1,43 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import seedu.lifetrack.system.exceptions.InvalidInputException; +import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; + +class ParserCaloriesTest { + + @Test + public void parseCaloriesInput_missingKeywords_exceptionThrown() { + try { + parseCaloriesInput("calories in Running"); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception:" + " Please ensure that you have keyed in the correct format " + + "in the correct order!" + " Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_incompleteInput_exceptionThrown() { + try { + parseCaloriesInput("calories in Running date/220224"); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception:" + " Please ensure that you have keyed in the correct format " + + "in the correct order!" + " Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { + try { + parseCaloriesInput("calories in Running date/220224 c/123"); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception:" + " Please ensure that you have keyed in the correct format " + + "in the correct order!" + " Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); + } + } +} diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java new file mode 100644 index 0000000000..95638868c3 --- /dev/null +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -0,0 +1,28 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import seedu.lifetrack.system.exceptions.InvalidInputException; +import static seedu.lifetrack.system.parser.ParserLiquid.parseLiquidInput; + +public class ParserLiquidTest { + + @Test + public void parseLiquidInput_emptyFields_exceptionThrown() { + try { + parseLiquidInput("liquids in"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_incompleteFields_exceptionThrown() { + try { + parseLiquidInput("liquids in b/Milo"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + } + } +} diff --git a/src/test/java/seedu/lifetrack/InputTest.java b/src/test/java/seedu/lifetrack/UITest.java similarity index 97% rename from src/test/java/seedu/lifetrack/InputTest.java rename to src/test/java/seedu/lifetrack/UITest.java index 6c8af85eba..192e4ba9ba 100644 --- a/src/test/java/seedu/lifetrack/InputTest.java +++ b/src/test/java/seedu/lifetrack/UITest.java @@ -3,16 +3,16 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.system.Ui; -import seedu.lifetrack.liquids.liquidlist.LiquidList; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import static org.junit.jupiter.api.Assertions.assertEquals; +import seedu.lifetrack.system.Ui; +import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.liquids.liquidlist.LiquidList; -public class InputTest { +public class UITest { private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; From 98c5481f923686e3af778f126f3d01c215891084 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:06:11 +0800 Subject: [PATCH 079/414] Add exception for invalid liquid in input Handle exception when b/ or v/ not entered Handle excption when b/ and v/ are in wrong order --- .../lifetrack/system/parser/ParserLiquid.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 90e9e4fa61..aaa14fe2e9 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -20,6 +20,24 @@ public class ParserLiquid { * contains empty fields */ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputException { + //get index for b/ and v/ inputs + int beverageIndex = input.indexOf("b/"); + int volumeIndex = input.indexOf("v/"); + + // Handle exception when b/ or v/ not entered + if (beverageIndex == -1 || volumeIndex == -1) { + throw new InvalidInputException("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000"); + } + + //Handle exception when order of b/ and v/ is incorrect + if (volumeIndex < beverageIndex) { + throw new InvalidInputException("Invalid input exception: " + + "Please ensure that you have entered b/ before v/\n" + + "For example: liquids in b/Milo v/1000"); + } + // splits string according to b/ and v/ keywords String[] parts = input.split("b/|v/"); @@ -38,6 +56,7 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce } return getNewLiquidEntry(volume, beverageName); } + private static LiquidEntry getNewLiquidEntry(String strVolume, String name) throws InvalidInputException { int volume = Integer.parseInt(strVolume); From 3191a48a3cbedf9c7ff38983647ac0211bf5d315 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:07:26 +0800 Subject: [PATCH 080/414] Add JUnit test for ParserLiquidTest Throw exception if input contains 2 beverages Throw exception if input contains 2 volumes Throw exception if beverage is missing Throw exception if volume is missing Throw exception if order of Volume and Beverage is wrong --- .../seedu/lifetrack/ParserLiquidTest.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/test/java/seedu/lifetrack/ParserLiquidTest.java diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java new file mode 100644 index 0000000000..c3bd49f1ba --- /dev/null +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -0,0 +1,92 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Test; +import seedu.lifetrack.liquids.liquidlist.LiquidEntry; +import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.system.exceptions.InvalidInputException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.lifetrack.system.parser.ParserLiquid.parseLiquidInput; + +public class ParserLiquidTest { + + @Test + public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown() { + // setup test + LiquidList liquidList = new LiquidList(); + String invalidInput = "liquids in b/Milo b/1000"; + + // Call methods to test + try { + LiquidEntry entry = parseLiquidInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() { + // setup test + LiquidList liquidList = new LiquidList(); + String invalidInput = "liquids in v/Milo v/1000"; + + // Call methods to test + try { + LiquidEntry entry = parseLiquidInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_inputMissingBeverage_invalidInputExceptionThrown() { + // setup test + LiquidList liquidList = new LiquidList(); + String invalidInput = "liquids in v/1000"; + + // Call methods to test + try { + LiquidEntry entry = parseLiquidInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { + // setup test + LiquidList liquidList = new LiquidList(); + String invalidInput = "liquids in b/Milo"; + + // Call methods to test + try { + LiquidEntry entry = parseLiquidInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExceptionThrown() { + // setup test + LiquidList liquidList = new LiquidList(); + String invalidInput = "liquids in v/1000 b/milo"; + + // Call methods to test + try { + LiquidEntry entry = parseLiquidInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ before v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); + } + } +} From f55825c40fe1f217eb0214fc1f3726730a3e8518 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:07:54 +0800 Subject: [PATCH 081/414] Edit Junit test in Class LifeTrackTest to align with changes --- .../lifetrack/liquids/liquidlist/LiquidList.java | 1 + src/test/java/seedu/lifetrack/LifeTrackTest.java | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 4ad302d975..4fb0af385a 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -60,6 +60,7 @@ public void addEntry(String input) { try { LiquidEntry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); + System.out.println("Beverage has been successfully added"); } catch (InvalidInputException e) { System.out.println(e.getMessage()); } diff --git a/src/test/java/seedu/lifetrack/LifeTrackTest.java b/src/test/java/seedu/lifetrack/LifeTrackTest.java index 8c430f037c..91975d8bc5 100644 --- a/src/test/java/seedu/lifetrack/LifeTrackTest.java +++ b/src/test/java/seedu/lifetrack/LifeTrackTest.java @@ -144,7 +144,9 @@ public void parseLiquidInput_emptyFields_exceptionThrown() { try { parseLiquidInput("liquids in"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); } } @@ -153,7 +155,9 @@ public void parseLiquidInput_incompleteFields_exceptionThrown() { try { parseLiquidInput("liquids in b/Milo"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); } } @@ -178,7 +182,8 @@ public void testPrintLiquidListNonEmpty() { liquidList.addEntry("liquids in b/Milo v/200"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "Liquid List:" + lineSeparator + + String expectedOutput = "Beverage has been successfully added" + lineSeparator + + "Liquid List:" + lineSeparator + "1. Beverage: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -194,7 +199,10 @@ public void testPrintLiquidListMultipleEntries() { liquidList.addEntry("liquids in b/Juice v/150"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "Liquid List:" + lineSeparator + + String expectedOutput = "Beverage has been successfully added" + lineSeparator + + "Beverage has been successfully added" + lineSeparator + + "Beverage has been successfully added" + lineSeparator + + "Liquid List:" + lineSeparator + "1. Beverage: Milo, Volume: 200" + lineSeparator + "2. Beverage: Water, Volume: 300" + lineSeparator + "3. Beverage: Juice, Volume: 150" + lineSeparator; From e3ef30d1660d6bd9eedc4d2e0fc5fb0d33b21047 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:25:32 +0800 Subject: [PATCH 082/414] Add exception handling for Class ParserLiquid Handle exception when non integer values are keyed in for volume Handle exception when negative values are keyed in for volume --- .../lifetrack/system/parser/ParserLiquid.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index aaa14fe2e9..c5a76b17fb 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -48,18 +48,31 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce // extracts beverage name and quantity portion from input String beverageName = parts[1].trim(); - String volume = parts[2].trim(); + String strVolume = parts[2].trim(); // ensures that both inputs are not empty - if (beverageName.isEmpty() || volume.isEmpty()) { + if (beverageName.isEmpty() || strVolume.isEmpty()) { throw new InvalidInputException("Please ensure that you have keyed in the correct format!"); } + + int volume; + // Handle exception when non integer values are keyed in for volume + try { + volume = Integer.parseInt(strVolume); + } catch(NumberFormatException e) { + throw new InvalidInputException("Invalid input Exception: " + + "Please enter a positive integer value for volume"); + } + + // Handle exception when negative values are keyed in for volume + if (volume <= 0) { + throw new InvalidInputException("Invalid input Exception: " + + "Please enter a positive integer value for volume"); + } return getNewLiquidEntry(volume, beverageName); } - private static LiquidEntry getNewLiquidEntry(String strVolume, String name) throws InvalidInputException { - int volume = Integer.parseInt(strVolume); - + private static LiquidEntry getNewLiquidEntry(int volume, String name) throws InvalidInputException { //create objects for Beverage Beverage liquidToAdd = new Beverage(name, volume); From 55a82627c876cdb48cbaff9c4e56da88bdb70650 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:26:26 +0800 Subject: [PATCH 083/414] Add JUnit test for method parseLiquidInput Throw exception when non integer value is keyed in for volume Throw exception when negative value is keyed in for volume --- .../seedu/lifetrack/ParserLiquidTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index c3bd49f1ba..3b37522bb6 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -89,4 +89,35 @@ public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExc "For example: liquids in b/Milo v/1000", e.getMessage()); } } + + @Test + public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputExceptionThrown() { + // setup test + LiquidList liquidList = new LiquidList(); + String invalidInput = "liquids in b/Milo v/##s100"; + + // Call methods to test + try { + LiquidEntry entry = parseLiquidInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Invalid input Exception: " + + "Please enter a positive integer value for volume", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionThrown() { + // setup test + LiquidList liquidList = new LiquidList(); + String invalidInput = "liquids in b/Milo v/-1000"; + + // Call methods to test + try { + LiquidEntry entry = parseLiquidInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Invalid input Exception: " + + "Please enter a positive integer value for volume", e.getMessage()); + } + } + } From 7b95f1c243248ec208f7739b31ddeaa3b9d870cc Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:35:43 +0800 Subject: [PATCH 084/414] Add condition in method UI to handle invalid command --- src/main/java/seedu/lifetrack/system/Ui.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/Ui.java b/src/main/java/seedu/lifetrack/system/Ui.java index ac4dab79e0..c8470354fc 100644 --- a/src/main/java/seedu/lifetrack/system/Ui.java +++ b/src/main/java/seedu/lifetrack/system/Ui.java @@ -36,8 +36,11 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL liquidList.printLiquidList(); } else if (line.startsWith("delete calories")) { calorieList.deleteEntry(line); - }else if (line.startsWith("delete liquids")) { + } else if (line.startsWith("delete liquids")) { liquidList.deleteEntry(line); + } else { + System.out.println("Please enter a valid command.\n" + + "Refer to the user guide for more details"); } } From 938e957a957b86b0294cbbc94d550db4893bb406 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:52:36 +0800 Subject: [PATCH 085/414] Add condition to better handle invalid command entered by user --- src/main/java/seedu/lifetrack/system/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/Ui.java b/src/main/java/seedu/lifetrack/system/Ui.java index c8470354fc..84165658b1 100644 --- a/src/main/java/seedu/lifetrack/system/Ui.java +++ b/src/main/java/seedu/lifetrack/system/Ui.java @@ -38,7 +38,7 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL calorieList.deleteEntry(line); } else if (line.startsWith("delete liquids")) { liquidList.deleteEntry(line); - } else { + } else if (!line.startsWith("bye")) { System.out.println("Please enter a valid command.\n" + "Refer to the user guide for more details"); } From ad8e38ddb8a02af0d7d5d3fd58681d6692461324 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 21 Mar 2024 19:53:43 +0800 Subject: [PATCH 086/414] Add assertion for method ParseLiquidInput --- src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index c5a76b17fb..995d2b0743 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -31,6 +31,8 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce "For example: liquids in b/Milo v/1000"); } + assert (beverageIndex != -1 || volumeIndex != -1) : "ensures that beverage and volume has been keyed in"; + //Handle exception when order of b/ and v/ is incorrect if (volumeIndex < beverageIndex) { throw new InvalidInputException("Invalid input exception: " + From 341d91ee361d363919eec86ca48fd165e03eb25d Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 21 Mar 2024 21:33:18 +0800 Subject: [PATCH 087/414] fix JUnit tests expected input --- .../calories/calorielist/CalorieList.java | 2 +- .../system/parser/ParserCalories.java | 14 +++--- .../java/seedu/lifetrack/CalorieListTest.java | 43 ++++++++++++------- .../java/seedu/lifetrack/LiquidListTest.java | 14 +++--- .../seedu/lifetrack/ParserCaloriesTest.java | 15 ++++--- .../seedu/lifetrack/ParserLiquidTest.java | 8 ++-- text-ui-test/EXPECTED.TXT | 18 ++++---- 7 files changed, 66 insertions(+), 48 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 7a498b1989..e86ad1953e 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -16,7 +16,7 @@ public class CalorieList { private ArrayList calorieArrayList; - private final int SIZE_OF_DELETE = 7; + private final int SIZE_OF_DELETE = 16; public CalorieList() { calorieArrayList= new ArrayList<>(); diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 5eeb222b12..7186c255e4 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -146,9 +146,10 @@ private static void checkInputsAreNonEmpty(String description, String strCalorie private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws InvalidInputException { //check that c/ and date/ keywords exist in the input, else throw exception if (caloriesIndex == -1 || dateIndex == -1) { - throw new InvalidInputException("Invalid input exception: Please ensure that you have keyed in " + - "the correct format in the correct order! Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); + throw new InvalidInputException("\t Invalid input! \n" + + "\t Please ensure that you have keyed in the correct format" + + " in the correct order!\n" + "\t Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); } } @@ -156,9 +157,10 @@ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateInd throws InvalidInputException { if ((macrosIndex != -1 && !(caloriesIndex < dateIndex && dateIndex < macrosIndex)) || (macrosIndex == -1 && !(caloriesIndex < dateIndex))) { - throw new InvalidInputException("Invalid input exception: Please ensure that you have keyed in " + - "the correct format in the correct order! Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); + throw new InvalidInputException("\t Invalid input! \n" + + "\t Please ensure that you have keyed in the correct format" + + " in the correct order!\n" + "\t Example input: " + + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); } } diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 4dc3adc286..03191fb30d 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -11,6 +11,8 @@ public class CalorieListTest { + private final String addedEntryHeader = "\t The following entry has been added to your caloric list!"; + @Test public void addEntry_validInput_entryAdded() { // Test setup @@ -70,7 +72,8 @@ public void testPrintCalorieListEmpty() { CalorieList calorieList = new CalorieList(); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "Your caloric list is empty." + lineSeparator; + String expectedOutput = "\t Your caloric list is empty. " + + "Add new entries to populate your list :)" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -83,9 +86,10 @@ public void testPrintCalorieListNonEmpty() { calorieList.addEntry("calories in Run c/200 date/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "New entry successfully added!" + lineSeparator + - "Caloric List:" + lineSeparator + - "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; + String expectedOutput = addedEntryHeader + lineSeparator + + "\t " + calorieList.getEntry(0).toString() + lineSeparator + + "\t Your Caloric List:" + lineSeparator + + "\t 1. \t Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -102,18 +106,25 @@ public void testPrintCalorieListMultipleEntries() { calorieList.addEntry("calories in Eat c/300 date/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "New entry successfully added!" + lineSeparator + - "Caloric List:" + lineSeparator + - "1. Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator + - "2. Date: 2024-03-14, Description: Walk, Calories: 150" + lineSeparator + - "3. Date: 2024-03-14, Description: Eat, Calories: 500" + lineSeparator + - "4. Date: 2024-03-14, Description: Run, Calories: 250" + lineSeparator + - "5. Date: 2024-03-14, Description: Eat, Calories: 300" + lineSeparator; - assertEquals(expectedOutput, outputStream.toString()); + StringBuilder expectedOutput = new StringBuilder(); + for (int i = 0; i < 5; i++) { + expectedOutput.append(addedEntryHeader) + .append(lineSeparator).append("\t ").append(calorieList.getEntry(i).toString()) + .append(lineSeparator); + } + expectedOutput.append("\t Your Caloric List:") + .append(lineSeparator) + .append("\t 1. \t Date: 2024-03-14, Description: Run, Calories: 200") + .append(lineSeparator) + .append("\t 2. \t Date: 2024-03-14, Description: Walk, Calories: 150") + .append(lineSeparator) + .append("\t 3. \t Date: 2024-03-14, Description: Eat, Calories: 500") + .append(lineSeparator) + .append("\t 4. \t Date: 2024-03-14, Description: Run, Calories: 250") + .append(lineSeparator) + .append("\t 5. \t Date: 2024-03-14, Description: Eat, Calories: 300") + .append(lineSeparator); + assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); } } diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index 10137d07b1..dcfd0ea91d 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -37,7 +37,7 @@ public void testPrintLiquidListEmpty() { LiquidList liquidList = new LiquidList(); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "Your liquid list is empty." + lineSeparator; + String expectedOutput = "\t Your liquid list is empty." + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -50,8 +50,8 @@ public void testPrintLiquidListNonEmpty() { liquidList.addEntry("liquids in b/Milo v/200"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "Liquid List:" + lineSeparator + - "1. Beverage: Milo, Volume: 200" + lineSeparator; + String expectedOutput = "\t Liquid List:" + lineSeparator + + "\t 1. Beverage: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -66,10 +66,10 @@ public void testPrintLiquidListMultipleEntries() { liquidList.addEntry("liquids in b/Juice v/150"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "Liquid List:" + lineSeparator + - "1. Beverage: Milo, Volume: 200" + lineSeparator + - "2. Beverage: Water, Volume: 300" + lineSeparator + - "3. Beverage: Juice, Volume: 150" + lineSeparator; + String expectedOutput = "\t Liquid List:" + lineSeparator + + "\t 1. Beverage: Milo, Volume: 200" + lineSeparator + + "\t 2. Beverage: Water, Volume: 300" + lineSeparator + + "\t 3. Beverage: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, liquidList.getSize()); } diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index 7a39cf640e..f178291542 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -13,8 +13,9 @@ public void parseCaloriesInput_missingKeywords_exceptionThrown() { try { parseCaloriesInput("calories in Running"); } catch (InvalidInputException e) { - assertEquals("Invalid input exception:" + " Please ensure that you have keyed in the correct format " + - "in the correct order!" + " Example input: " + + assertEquals("\t Invalid input! \n" + + "\t Please ensure that you have keyed in the correct format" + + " in the correct order!\n" + "\t Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); } } @@ -24,8 +25,9 @@ public void parseCaloriesInput_incompleteInput_exceptionThrown() { try { parseCaloriesInput("calories in Running date/220224"); } catch (InvalidInputException e) { - assertEquals("Invalid input exception:" + " Please ensure that you have keyed in the correct format " + - "in the correct order!" + " Example input: " + + assertEquals("\t Invalid input! \n" + + "\t Please ensure that you have keyed in the correct format" + + " in the correct order!\n" + "\t Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); } } @@ -35,8 +37,9 @@ public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { try { parseCaloriesInput("calories in Running date/220224 c/123"); } catch (InvalidInputException e) { - assertEquals("Invalid input exception:" + " Please ensure that you have keyed in the correct format " + - "in the correct order!" + " Example input: " + + assertEquals("\t Invalid input! \n" + + "\t Please ensure that you have keyed in the correct format" + + " in the correct order!\n" + "\t Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); } } diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 95638868c3..ae61028d59 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -9,20 +9,20 @@ public class ParserLiquidTest { @Test - public void parseLiquidInput_emptyFields_exceptionThrown() { + public void parseLiquidInput_missingKeywords_exceptionThrown() { try { parseLiquidInput("liquids in"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("\t Please ensure that you have keyed in the correct format!", e.getMessage()); } } @Test - public void parseLiquidInput_incompleteFields_exceptionThrown() { + public void parseLiquidInput_incompleteInput_exceptionThrown() { try { parseLiquidInput("liquids in b/Milo"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + assertEquals("\t Please ensure that you have keyed in the correct format!", e.getMessage()); } } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 9dab287556..4d68f06f18 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,10 +1,12 @@ -Hello from -LLLLL IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K -L I F E T R R A A C C K K -LLL I FFFF EEEE T RRRR AAAAA C KK -L I F E T R R A A C C K K -LLLLL IIIII F EEEEE TTTT R R A A CCC K K + Hello from -What do you want to do today? +| IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K +| I F E T R R A A C K K +| I FFFF EEEE T RRRR AAAAA C KK +| I F E T R R A A C K K +|______ IIIII F EEEEE T R R A A CCC K K -Bye! See you again soon ^^ + How can I help you today? + ----------------------------------------------------------------------------- + ----------------------------------------------------------------------------- + Bye! See you again soon ^^ From a1da8e80a9cacf9992122c33ed7c1fffcd131c0a Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 21 Mar 2024 21:45:43 +0800 Subject: [PATCH 088/414] edit expected input for runtest file --- text-ui-test/EXPECTED.TXT | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 4d68f06f18..327e4a1e3d 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,4 +1,4 @@ - Hello from + Hello from | IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K | I F E T R R A A C K K @@ -6,7 +6,7 @@ | I F E T R R A A C K K |______ IIIII F EEEEE T R R A A CCC K K - How can I help you today? - ----------------------------------------------------------------------------- - ----------------------------------------------------------------------------- - Bye! See you again soon ^^ + How can I help you today? + ----------------------------------------------------------------------------- + ----------------------------------------------------------------------------- + Bye! See you again soon ^^ From 5be3890d9487ae889053a5979fb08e2d69511f95 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 21 Mar 2024 21:51:01 +0800 Subject: [PATCH 089/414] change tabs to spaces for hello and bye messages --- src/main/java/seedu/lifetrack/ui/Ui.java | 10 ++++++---- text-ui-test/EXPECTED.TXT | 10 +++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index ba0969d718..e6bf439948 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -16,6 +16,8 @@ */ public class Ui { + private static final String WHITESPACE = " "; + private static final String logo = "| IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + "| I F E T R R A A C K K\n" + "| I FFFF EEEE T RRRR AAAAA C KK\n" + @@ -76,14 +78,14 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL } public static void sayHello() { - System.out.println("\t Hello from\n\n" + logo); - System.out.println("\t How can I help you today?"); + System.out.println(WHITESPACE + "Hello from\n\n" + logo); + System.out.println(WHITESPACE + "How can I help you today?"); printLine(); } public static void byeMessage() { printLine(); - System.out.println("\t Bye! See you again soon ^^"); + System.out.println(WHITESPACE + "Bye! See you again soon ^^"); } public static void printEmptyInputMessage() { @@ -91,7 +93,7 @@ public static void printEmptyInputMessage() { } public static void printLine() { - System.out.println("\t -----------------------------------------------------------------------------"); + System.out.println(WHITESPACE + "-----------------------------------------------------------------------------"); } public static void handleUnknownInput() { diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 327e4a1e3d..4d68f06f18 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,4 +1,4 @@ - Hello from + Hello from | IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K | I F E T R R A A C K K @@ -6,7 +6,7 @@ | I F E T R R A A C K K |______ IIIII F EEEEE T R R A A CCC K K - How can I help you today? - ----------------------------------------------------------------------------- - ----------------------------------------------------------------------------- - Bye! See you again soon ^^ + How can I help you today? + ----------------------------------------------------------------------------- + ----------------------------------------------------------------------------- + Bye! See you again soon ^^ From c6a5070f98f5c6a17ee6b598bcba6f2fc6fd03b1 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 21 Mar 2024 21:52:51 +0800 Subject: [PATCH 090/414] fix checkstyle error --- src/main/java/seedu/lifetrack/ui/Ui.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index e6bf439948..4bccde74f2 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -93,7 +93,8 @@ public static void printEmptyInputMessage() { } public static void printLine() { - System.out.println(WHITESPACE + "-----------------------------------------------------------------------------"); + System.out.println(WHITESPACE + "-------------------------------------" + + "----------------------------------------"); } public static void handleUnknownInput() { From e2d4562b112b24bbc392df03e9665eac2cd04f1e Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 23:07:41 +0800 Subject: [PATCH 091/414] fix gradle --- .../java/seedu/lifetrack/calories/calorielist/CalorieList.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 129b831bfe..674b55b03a 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,7 +1,4 @@ package seedu.lifetrack.calories.calorielist; -import static seedu.lifetrack.system.parser.Parser.parseCaloriesInput; -import static seedu.lifetrack.system.exceptions.ErrorMessages.printIndexOutOfBoundsError; -import static seedu.lifetrack.system.exceptions.ErrorMessages.printNumberFormatError; import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; import static seedu.lifetrack.ui.CalorieListUi.emptyListMessage; import static seedu.lifetrack.ui.CalorieListUi.successfulDeletedMessage; From 263595399bbb30d7bc3d6a9d91467f3c0b19e895 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 23:12:54 +0800 Subject: [PATCH 092/414] add calories delete assert --- .../java/seedu/lifetrack/calories/calorielist/CalorieList.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 674b55b03a..8d4b8a2979 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -27,6 +27,8 @@ public Entry getEntry(int index) { * @param line the string containing the index of calorie record user want to delete */ public void deleteEntry(String line) { + assert (line.startsWith("calories delete") ) : "ensures that input is correct"; + try { int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); Entry toDelete = calorieArrayList.get(index-1); From a4f90a4ade39b6fd60c68229ce1211a78ed6d878 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 21 Mar 2024 23:18:22 +0800 Subject: [PATCH 093/414] fix test --- src/test/java/seedu/lifetrack/CalorieListTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 03191fb30d..92e0a136b7 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -45,12 +45,12 @@ public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories out Run c/200 date/2024-03-14"); int initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete calories 1"); + calorieList.deleteEntry("calories delete 1"); assertEquals(initialSize - 1, calorieList.getSize()); calorieList.addEntry("calories out Run c/200 date/2024-03-14"); calorieList.addEntry("calories in Eat c/200 date/2024-03-14"); initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete calories 2"); + calorieList.deleteEntry("calories delete 2"); assertEquals(initialSize - 1, calorieList.getSize()); } @@ -59,8 +59,8 @@ public void testDeleteCalorieInvalidIndex() { CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories out Run c/200 date/2024-03-14"); int initialSize = calorieList.getSize(); - calorieList.deleteEntry("delete calories 2"); // Index out of bounds - calorieList.deleteEntry("delete calories -1"); + calorieList.deleteEntry("calories delete 2"); // Index out of bounds + calorieList.deleteEntry("calories delete -1"); assertEquals(initialSize, calorieList.getSize()); } From 2f8cb899b3e2902c1630e0fc86e8a7f2f3f3f89f Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 21 Mar 2024 23:49:59 +0800 Subject: [PATCH 094/414] final --- META-INF/MANIFEST.MF | 3 +++ src/main/java/seedu/lifetrack/ui/Ui.java | 17 ++++++++++------- 2 files changed, 13 insertions(+), 7 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..1d2b1b8f1b --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.lifetrack.LifeTrack + diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 4bccde74f2..b8bb28b1bc 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -18,11 +18,14 @@ public class Ui { private static final String WHITESPACE = " "; - private static final String logo = "| IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K\n" + - "| I F E T R R A A C K K\n" + - "| I FFFF EEEE T RRRR AAAAA C KK\n" + - "| I F E T R R A A C K K\n" + - "|______ IIIII F EEEEE T R R A A CCC K K\n"; + private static final String logo = + "\n" + + ".____ .__ _____ ___________ __ \n" + + "| | |__|/ ____\\____ \\__ ___/___________ ____ | | __\n" + + "| | | \\ __\\/ __ \\ | | \\_ __ \\__ \\ _/ ___\\| |/ /\n" + + "| |___| || | \\ ___/ | | | | \\// __ \\\\ \\___| < \n" + + "|_______ \\__||__| \\___ > |____| |__| (____ /\\___ >__|_ \\\n" + + " \\/ \\/ \\/ \\/ \\/\n"; public static void readUserInput(CalorieList calorieList, LiquidList liquidList) { String line; @@ -111,8 +114,8 @@ public static void showHelp() { System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); System.out.println("\t - calories delete : Deletes the entry at the specified index" + " from the calorie list."); - System.out.println("\t - hydration add b/ v/ : " + + System.out.println("\t - liquids in b/ v/ : " + "Marks the task at the specified index as done."); - System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list.\""); + System.out.println("\t - liquids list: Displays all entries currently stored in the hydration list.\""); } } From 4ab554b6766210ba10cc3640b2f0247886fa17d6 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 21 Mar 2024 23:57:36 +0800 Subject: [PATCH 095/414] final --- text-ui-test/EXPECTED.TXT | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 4d68f06f18..1fe4f2b525 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,10 +1,12 @@ Hello from -| IIIII FFFFF EEEEE TTTTT RRRR AAA CCC K K -| I F E T R R A A C K K -| I FFFF EEEE T RRRR AAAAA C KK -| I F E T R R A A C K K -|______ IIIII F EEEEE T R R A A CCC K K + + .____ .__ _____ ___________ __ + | | |__|/ ____\____ \__ ___/___________ ____ | | __ + | | | \ __\/ __ \ | | \_ __ \__ \ _/ ___\| |/ / + | |___| || | \ ___/ | | | | \// __ \\ \___| < + |_______ \__||__| \___ > |____| |__| (____ /\___ >__|_ \ + \/ \/ \/ \/ \/ How can I help you today? ----------------------------------------------------------------------------- From 617b6d461d42cb296c67898a7495582fb331d5bf Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 22 Mar 2024 00:10:03 +0800 Subject: [PATCH 096/414] final --- text-ui-test/EXPECTED.TXT | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 1fe4f2b525..2f91cf0ff2 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,12 +1,12 @@ Hello from - .____ .__ _____ ___________ __ - | | |__|/ ____\____ \__ ___/___________ ____ | | __ - | | | \ __\/ __ \ | | \_ __ \__ \ _/ ___\| |/ / - | |___| || | \ ___/ | | | | \// __ \\ \___| < - |_______ \__||__| \___ > |____| |__| (____ /\___ >__|_ \ - \/ \/ \/ \/ \/ +.____ .__ _____ ___________ __ +| | |__|/ ____\____ \__ ___/___________ ____ | | __ +| | | \ __\/ __ \ | | \_ __ \__ \ _/ ___\| |/ / +| |___| || | \ ___/ | | | | \// __ \\ \___| < +|_______ \__||__| \___ > |____| |__| (____ /\___ >__|_ \ + \/ \/ \/ \/ \/ How can I help you today? ----------------------------------------------------------------------------- From 7a9062b0a554ff27dee6b0ac770861e0aaa9d6f2 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Fri, 22 Mar 2024 00:23:43 +0800 Subject: [PATCH 097/414] finalfrfr --- src/main/java/seedu/lifetrack/ui/Ui.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index b8bb28b1bc..227fbef9fd 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -20,10 +20,10 @@ public class Ui { private static final String logo = "\n" + - ".____ .__ _____ ___________ __ \n" + + ".____ .__ _____ ___________ __\n" + "| | |__|/ ____\\____ \\__ ___/___________ ____ | | __\n" + "| | | \\ __\\/ __ \\ | | \\_ __ \\__ \\ _/ ___\\| |/ /\n" + - "| |___| || | \\ ___/ | | | | \\// __ \\\\ \\___| < \n" + + "| |___| || | \\ ___/ | | | | \\// __ \\\\ \\___| <\n" + "|_______ \\__||__| \\___ > |____| |__| (____ /\\___ >__|_ \\\n" + " \\/ \\/ \\/ \\/ \\/\n"; From fc90e78a975186ceab7101e1b1215b8c1a2e0c90 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Fri, 22 Mar 2024 00:56:33 +0800 Subject: [PATCH 098/414] set authors for original writers of JUnit test code after re-org of test folder --- .../java/seedu/lifetrack/CalorieListTest.java | 4 ++- .../java/seedu/lifetrack/LiquidListTest.java | 2 ++ .../seedu/lifetrack/ParserLiquidTest.java | 34 +++++++++++++------ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 92e0a136b7..9fe7fa121c 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -39,7 +39,8 @@ public void addEntry_validInput_entryAdded() { assertEquals("run", secondEntry.getDescription()); assertEquals(679, secondEntry.getCalories()); } - + + //@@author a-wild-chocolate @Test public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); @@ -64,6 +65,7 @@ public void testDeleteCalorieInvalidIndex() { assertEquals(initialSize, calorieList.getSize()); } + //@@author shawnpong @Test public void testPrintCalorieListEmpty() { String lineSeparator = System.lineSeparator(); diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index ab3a2667b0..d676ab0b0f 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -1,3 +1,4 @@ +//@@author shawnpong package seedu.lifetrack; import org.junit.jupiter.api.Test; @@ -77,4 +78,5 @@ public void testPrintLiquidListMultipleEntries() { assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, liquidList.getSize()); } + //@@author } diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 6019d67a76..fef3d95b5a 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -1,3 +1,4 @@ +//@@author rexyyong package seedu.lifetrack; import org.junit.jupiter.api.Test; @@ -26,17 +27,6 @@ public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown } } - @Test - public void parseLiquidInput_missingKeywords_exceptionThrown() { - try { - parseLiquidInput("liquids in"); - } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); - } - } - @Test public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() { // setup test @@ -130,4 +120,26 @@ public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionTh "Please enter a positive integer value for volume", e.getMessage()); } } + + //@@author shawnpong + @Test + public void parseLiquidInput_missingKeywords_exceptionThrown() { + try { + parseLiquidInput("liquids in"); + } catch (InvalidInputException e) { + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); + } + } + + @Test + public void parseLiquidInput_incompleteInput_exceptionThrown() { + try { + parseLiquidInput("liquids in b/Milo"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); + } + } + //@@author } From 119f3de5d5bbc516bc15ec92788cc82cd03d10f4 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Fri, 22 Mar 2024 01:05:28 +0800 Subject: [PATCH 099/414] fix assertion for JUnit test for ParserLiquid --- src/test/java/seedu/lifetrack/ParserLiquidTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index fef3d95b5a..97c045fa44 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -138,8 +138,9 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { try { parseLiquidInput("liquids in b/Milo"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format!", e.getMessage()); - } + assertEquals("Invalid input exception: " + + "Please ensure that you have entered b/ and v/\n" + + "For example: liquids in b/Milo v/1000", e.getMessage()); } } //@@author } From 12326cb952419ced5272cc346309a46e161717e5 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Fri, 22 Mar 2024 11:22:46 +0800 Subject: [PATCH 100/414] add JUnit tests for ParserCalories, move error messages for parseCaloriesIn into new class --- src/main/java/seedu/lifetrack/LifeTrack.java | 1 + .../java/seedu/lifetrack/calories/Food.java | 12 +++ .../calories/calorielist/CalorieList.java | 4 +- .../calories/calorielist/InputEntry.java | 4 + .../system/exceptions/ErrorMessages.java | 8 ++ .../exceptions/InvalidInputException.java | 4 +- .../InvalidInputExceptionMessage.java | 40 ++++++++++ .../system/parser/ParserCalories.java | 40 +++++----- .../java/seedu/lifetrack/LiquidListTest.java | 1 - .../seedu/lifetrack/ParserCaloriesTest.java | 74 +++++++++++++++---- .../seedu/lifetrack/ParserLiquidTest.java | 26 ++----- 11 files changed, 156 insertions(+), 58 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 0dab84c905..1be48fbfd2 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -18,6 +18,7 @@ public static void main(String[] args) { Ui.sayHello(); Ui.readUserInput(calorieList,liquidList); Ui.byeMessage(); + in.close(); } } diff --git a/src/main/java/seedu/lifetrack/calories/Food.java b/src/main/java/seedu/lifetrack/calories/Food.java index f387a8ab72..fa950f112a 100644 --- a/src/main/java/seedu/lifetrack/calories/Food.java +++ b/src/main/java/seedu/lifetrack/calories/Food.java @@ -11,4 +11,16 @@ public Food(int carbohydrates, int proteins, int fats) { this.proteins = proteins; this.fats = fats; } + + public int getCarbohydrates() { + return carbohydrates; + } + + public int getProteins() { + return proteins; + } + + public int getFats() { + return fats; + } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 8d4b8a2979..9c5e449647 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -10,6 +10,7 @@ import java.util.ArrayList; public class CalorieList { + private static Logger logr = Logger.getLogger(CalorieList.class.getName()); private ArrayList calorieArrayList; private final int SIZE_OF_DELETE = 16; @@ -76,7 +77,8 @@ public void printCalorieList() { } else { calorieListHeader(); for (int i = 0; i < calorieArrayList.size(); i++) { - System.out.println("\t " + (i + 1) + ". " + calorieArrayList.get(i).toString()); + Entry entry = calorieArrayList.get(i); + System.out.println("\t " + (i + 1) + ". " + entry); } } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index cdfebc6db7..f93b4efa9e 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -14,4 +14,8 @@ public InputEntry(String description, int calories, String date, Food food) { super(description, calories, date); this.food = food; } + + public Food getFood() { + return food; + } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index 30e2a8f292..c0cae070a5 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -1,6 +1,7 @@ package seedu.lifetrack.system.exceptions; public class ErrorMessages { + public static void printIndexOutOfBoundsError(){ System.out.println("\t Sorry, this index is invalid. Please enter a positive integer " + "within the size of the list."); @@ -10,4 +11,11 @@ public static void printNumberFormatError(){ System.out.println("\t Please enter a valid number within the command"); } + public static String getIncorrectCaloriesInputMessage() { + return "\t Please input only positive integers into the calories field!"; + } + + public static String getIncorrectMacrosInputMessage() { + return "\t Please input only positive integers into the macronutrients field!"; + } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index 177a97ae68..e6be36cf56 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -2,6 +2,8 @@ public class InvalidInputException extends Exception { + public final String heythere = ""; + public InvalidInputException(){ super("\t Please ensure that you have keyed in the correct format!"); } @@ -9,6 +11,4 @@ public InvalidInputException(){ public InvalidInputException(String exception) { super(exception); } - - } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java new file mode 100644 index 0000000000..3301e21c7a --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -0,0 +1,40 @@ +package seedu.lifetrack.system.exceptions; + +public class InvalidInputExceptionMessage { + + private static final String HEADER = "\t Invalid input!\n"; + private static final String CALORIES_IN_INPUT = "\t Example input: calories in DESCRIPTION " + + "c/INTEGER_CALORIES date/DATE [m/MACROS]"; + private static final String CALORIES_OUT_INPUT = "\t Example input: calories out DESCRIPTION " + + "c/INTEGER_CALORIES date/DATE"; + private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; + + public static String getIncorrectOrderMessage() { + String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; + return HEADER + message + CALORIES_IN_INPUT; + } + + public static String getMissingKeywordsMessage() { + String message = "\t Please ensure that the compulsory keywords exist!\n"; + return HEADER + message + CALORIES_IN_INPUT; + } + + public static String getWhitespaceInInputMessage() { + String message = "\t Please ensure that there is no whitespace in your input!\n"; + return HEADER + message + CALORIES_IN_INPUT; + } + public static String getIncompleteMacrosMessage() { + String message = "\t Please ensure that all macronutrients fields are filled up!\n"; + return HEADER + message + MACROS_INPUT; + } + + public static String getWhitespaceInMacrosInputMessage() { + String message = "\t Please ensure that there is no whitespace in your macros input!\n"; + return HEADER + message + MACROS_INPUT; + } + + public static String getMacrosInCaloriesOutMessage() { + String message = "\t Calorie output entry cannot have macros!\n"; + return HEADER + message + CALORIES_OUT_INPUT; + } +} diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 7186c255e4..8435182589 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -6,6 +6,15 @@ import seedu.lifetrack.calories.Activity; import seedu.lifetrack.calories.Food; import seedu.lifetrack.system.exceptions.InvalidInputException; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectCaloriesInputMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectMacrosInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectOrderMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMissingKeywordsMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; + public class ParserCalories { @@ -32,7 +41,7 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio int caloriesIndex = input.indexOf("c/"); int dateIndex = input.indexOf("date/"); int macrosIndex = input.indexOf("m/"); - + checkKeywordsExist(caloriesIndex, dateIndex); assert caloriesIndex != -1 : "The c/ keyword should exist!"; assert dateIndex != -1 : "The date/ keyword should exist!"; @@ -56,7 +65,7 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio int[] macros = null; if (macrosIndex != -1) { if (command.equals("calories out")) { - throw new InvalidInputException("Invalid input exception: Calorie output entry cannot have macros"); + throw new InvalidInputException(getMacrosInCaloriesOutMessage()); } String macroString = parts[3].trim(); try { @@ -85,7 +94,7 @@ private static int getIntegerCaloriesFromInput(String strCalories) { try { calories = Integer.parseInt(strCalories); } catch (NumberFormatException e) { - System.out.println("Please input only positive integers into the calories field!"); + System.out.println(getIncorrectCaloriesInputMessage()); } return calories; } @@ -109,28 +118,24 @@ private static int[] getMacrosFromInput(String macroString) throws InvalidInputE for (String macro: macroParts) { //throw exception if user inputs whitespace in the macros field i.e. m/123, ,123 if (macro.trim().isEmpty()) { - throw new InvalidInputException("Invalid input exception: " + - "Please ensure that all macronutrients fields are filled up. " + - "For example: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"); + throw new InvalidInputException(getWhitespaceInMacrosInputMessage()); } macros[idx] = Integer.parseInt(macro.trim()); idx++; } //throw exception if there are missing values in the macros field if (idx != 3) { - throw new InvalidInputException("Invalid input exception: " + - "Please ensure that all macronutrients fields are filled up. " + - "For example: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"); + throw new InvalidInputException(getIncompleteMacrosMessage()); } } catch (NumberFormatException e) { - System.out.println("Please input only numbers into the macronutrients field!"); + System.out.println(getIncorrectMacrosInputMessage()); } return macros; } private static void checkCaloriesIsPositiveInteger(int calories) throws InvalidInputException { if (calories <= 0) { - throw new InvalidInputException("Please input only positive integers into the calories field!"); + throw new InvalidInputException(getIncorrectCaloriesInputMessage()); } } @@ -138,18 +143,14 @@ private static void checkInputsAreNonEmpty(String description, String strCalorie throws InvalidInputException { //check if the description, calories or date fields are empty if (description.isEmpty() || strCalories.isEmpty() || date.isEmpty()) { - throw new InvalidInputException("Please ensure that input parameters are not empty!\n" + - "Example input: " + "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE"); + throw new InvalidInputException(getWhitespaceInInputMessage()); } } private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws InvalidInputException { //check that c/ and date/ keywords exist in the input, else throw exception if (caloriesIndex == -1 || dateIndex == -1) { - throw new InvalidInputException("\t Invalid input! \n" + - "\t Please ensure that you have keyed in the correct format" + - " in the correct order!\n" + "\t Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); + throw new InvalidInputException(getMissingKeywordsMessage()); } } @@ -157,10 +158,7 @@ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateInd throws InvalidInputException { if ((macrosIndex != -1 && !(caloriesIndex < dateIndex && dateIndex < macrosIndex)) || (macrosIndex == -1 && !(caloriesIndex < dateIndex))) { - throw new InvalidInputException("\t Invalid input! \n" + - "\t Please ensure that you have keyed in the correct format" + - " in the correct order!\n" + "\t Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS"); + throw new InvalidInputException(getIncorrectOrderMessage()); } } diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index d676ab0b0f..03bd524509 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -78,5 +78,4 @@ public void testPrintLiquidListMultipleEntries() { assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, liquidList.getSize()); } - //@@author } diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index f178291542..6d62d62db8 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -5,42 +5,86 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectCaloriesInputMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectMacrosInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectOrderMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMissingKeywordsMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; class ParserCaloriesTest { @Test public void parseCaloriesInput_missingKeywords_exceptionThrown() { try { - parseCaloriesInput("calories in Running"); + parseCaloriesInput("calories in burger"); } catch (InvalidInputException e) { - assertEquals("\t Invalid input! \n" + - "\t Please ensure that you have keyed in the correct format" + - " in the correct order!\n" + "\t Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); + assertEquals(getMissingKeywordsMessage(), e.getMessage()); } } @Test public void parseCaloriesInput_incompleteInput_exceptionThrown() { try { - parseCaloriesInput("calories in Running date/220224"); + parseCaloriesInput("calories in burger c/ date/220224"); } catch (InvalidInputException e) { - assertEquals("\t Invalid input! \n" + - "\t Please ensure that you have keyed in the correct format" + - " in the correct order!\n" + "\t Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); + assertEquals(getWhitespaceInInputMessage(), e.getMessage()); } } @Test public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { try { - parseCaloriesInput("calories in Running date/220224 c/123"); + parseCaloriesInput("calories in burger date/220224 c/123"); } catch (InvalidInputException e) { - assertEquals("\t Invalid input! \n" + - "\t Please ensure that you have keyed in the correct format" + - " in the correct order!\n" + "\t Example input: " + - "calories in DESCRIPTION c/INTEGER_CALORIES date/DATE m/MACROS", e.getMessage()); + assertEquals(getIncorrectOrderMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_incorrectMacrosInput_exceptionThrown() { + try { + parseCaloriesInput("calories in burger c/123 date/220324 m/abc"); + } catch (InvalidInputException e) { + assertEquals(getIncorrectMacrosInputMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_incorrectCaloriesInput_exceptionThrown() { + try { + parseCaloriesInput("calories out Running c/abc date/220324"); + } catch (InvalidInputException e) { + assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_incompleteMacrosInput_exceptionThrown() { + try { + parseCaloriesInput("calories in burger c/123 date/220324 m/123,132"); + } catch (InvalidInputException e) { + assertEquals(getIncompleteMacrosMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_macrosInCaloriesOut_exceptionThrown() { + try { + parseCaloriesInput("calories out running c/123 date/220324 m/123,123,132"); + } catch (InvalidInputException e) { + assertEquals(getMacrosInCaloriesOutMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_whitespaceInMacrosInput_exceptionThrown() { + try { + parseCaloriesInput("calories in burger c/123 date/220324 m/123, ,132"); + } catch (InvalidInputException e) { + assertEquals(getWhitespaceInMacrosInputMessage(), e.getMessage()); } } } diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 97c045fa44..69fbe0bb7b 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -2,8 +2,6 @@ package seedu.lifetrack; import org.junit.jupiter.api.Test; -import seedu.lifetrack.liquids.liquidlist.LiquidEntry; -import seedu.lifetrack.liquids.liquidlist.LiquidList; import seedu.lifetrack.system.exceptions.InvalidInputException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -14,12 +12,11 @@ public class ParserLiquidTest { @Test public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown() { // setup test - LiquidList liquidList = new LiquidList(); String invalidInput = "liquids in b/Milo b/1000"; // Call methods to test try { - LiquidEntry entry = parseLiquidInput(invalidInput); + parseLiquidInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Invalid input exception: " + "Please ensure that you have entered b/ and v/\n" + @@ -30,12 +27,11 @@ public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown @Test public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() { // setup test - LiquidList liquidList = new LiquidList(); String invalidInput = "liquids in v/Milo v/1000"; // Call methods to test try { - LiquidEntry entry = parseLiquidInput(invalidInput); + parseLiquidInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Invalid input exception: " + "Please ensure that you have entered b/ and v/\n" + @@ -46,12 +42,11 @@ public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() @Test public void parseLiquidInput_inputMissingBeverage_invalidInputExceptionThrown() { // setup test - LiquidList liquidList = new LiquidList(); String invalidInput = "liquids in v/1000"; // Call methods to test try { - LiquidEntry entry = parseLiquidInput(invalidInput); + parseLiquidInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Invalid input exception: " + "Please ensure that you have entered b/ and v/\n" + @@ -62,12 +57,11 @@ public void parseLiquidInput_inputMissingBeverage_invalidInputExceptionThrown() @Test public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { // setup test - LiquidList liquidList = new LiquidList(); String invalidInput = "liquids in b/Milo"; // Call methods to test try { - LiquidEntry entry = parseLiquidInput(invalidInput); + parseLiquidInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Invalid input exception: " + "Please ensure that you have entered b/ and v/\n" + @@ -78,12 +72,11 @@ public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { @Test public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExceptionThrown() { // setup test - LiquidList liquidList = new LiquidList(); String invalidInput = "liquids in v/1000 b/milo"; // Call methods to test try { - LiquidEntry entry = parseLiquidInput(invalidInput); + parseLiquidInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Invalid input exception: " + "Please ensure that you have entered b/ before v/\n" + @@ -94,12 +87,11 @@ public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExc @Test public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputExceptionThrown() { // setup test - LiquidList liquidList = new LiquidList(); String invalidInput = "liquids in b/Milo v/##s100"; // Call methods to test try { - LiquidEntry entry = parseLiquidInput(invalidInput); + parseLiquidInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Invalid input Exception: " + "Please enter a positive integer value for volume", e.getMessage()); @@ -109,12 +101,11 @@ public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputException @Test public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionThrown() { // setup test - LiquidList liquidList = new LiquidList(); String invalidInput = "liquids in b/Milo v/-1000"; // Call methods to test try { - LiquidEntry entry = parseLiquidInput(invalidInput); + parseLiquidInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Invalid input Exception: " + "Please enter a positive integer value for volume", e.getMessage()); @@ -142,5 +133,4 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { "Please ensure that you have entered b/ and v/\n" + "For example: liquids in b/Milo v/1000", e.getMessage()); } } - //@@author -} +} \ No newline at end of file From f660977fb6270c9efcae61d54efcf59fc91b59b4 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Fri, 22 Mar 2024 11:31:35 +0800 Subject: [PATCH 101/414] fix gradle checkstyle error --- src/test/java/seedu/lifetrack/ParserLiquidTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 69fbe0bb7b..4c9d6724d1 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -133,4 +133,4 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { "Please ensure that you have entered b/ and v/\n" + "For example: liquids in b/Milo v/1000", e.getMessage()); } } -} \ No newline at end of file +} From a447d6ad220e73310258364179640a83e03d837a Mon Sep 17 00:00:00 2001 From: owx0130 Date: Fri, 22 Mar 2024 11:39:38 +0800 Subject: [PATCH 102/414] fix coding standard --- src/test/java/seedu/lifetrack/ParserLiquidTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 4c9d6724d1..ace58e989a 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -131,6 +131,7 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("Invalid input exception: " + "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); } + "For example: liquids in b/Milo v/1000", e.getMessage()); + } } } From 938ff119238ab9949ebf71345be6263841eba65c Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 24 Mar 2024 00:28:27 +0800 Subject: [PATCH 103/414] initial changes to liquid UI --- .gitignore | 2 +- src/main/java/seedu/lifetrack/LifeTrack.java | 1 + .../liquids/liquidlist/LiquidList.java | 27 ++++++++++------ .../java/seedu/lifetrack/ui/LiquidListUI.java | 32 +++++++++++++++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 10 ++++++ 5 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/ui/LiquidListUI.java diff --git a/.gitignore b/.gitignore index 2873e189e1..4a8db972fd 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ src/main/resources/docs/ bin/ /text-ui-test/ACTUAL.TXT -text-ui-test/EXPECTED-UNIX.TXT +text-ui-test/EXPECTED-UNIX.TXT \ No newline at end of file diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 1be48fbfd2..a1c0611922 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -16,6 +16,7 @@ public static void main(String[] args) { LiquidList liquidList = new LiquidList(); Scanner in = new Scanner(System.in); Ui.sayHello(); + assert true : "dummy assertion set to fail"; Ui.readUserInput(calorieList,liquidList); Ui.byeMessage(); in.close(); diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index ca18c30b52..18b3fc9eee 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -9,6 +9,14 @@ import java.util.logging.Level; import java.util.logging.Logger; +import static seedu.lifetrack.ui.LiquidListUI.deleteLogIndexMessage; +import static seedu.lifetrack.ui.LiquidListUI.deleteLogNumberMessage; +import static seedu.lifetrack.ui.LiquidListUI.deleteMessage; +import static seedu.lifetrack.ui.LiquidListUI.addEntryMessage; +import static seedu.lifetrack.ui.LiquidListUI.emptyListMessage; +import static seedu.lifetrack.ui.LiquidListUI.WHITESPACE; +import static seedu.lifetrack.ui.LiquidListUI.listHeader; + /** * Represents a list of liquid entries. * Provides methods to add, delete, and print liquid entries. @@ -16,7 +24,7 @@ public class LiquidList { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); private ArrayList liquidArrayList; - private final int SIZE_OF_DELETE = 15; + private final int DELETE_PADDING = 15; /** * Constructs an empty LiquidList. @@ -43,14 +51,13 @@ public LiquidEntry getEntry(int index) { */ public void deleteEntry(String line) { try { - int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); + int index = Integer.parseInt(line.substring(DELETE_PADDING).trim()); liquidArrayList.remove(index - 1); - System.out.println("\t Successfully delete the liquid record."); + deleteMessage(); } catch (IndexOutOfBoundsException e) { - logr.log(Level.WARNING, "Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list.", e); + logr.log(Level.WARNING, deleteLogIndexMessage(), e); } catch (NumberFormatException e) { - logr.log(Level.WARNING, "Please enter a valid index!", e); + logr.log(Level.WARNING, deleteLogNumberMessage(), e); } } @@ -63,7 +70,7 @@ public void addEntry(String input) { try { LiquidEntry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); - System.out.println("Beverage has been successfully added"); + addEntryMessage(); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); } @@ -75,13 +82,13 @@ public void addEntry(String input) { */ public void printLiquidList() { if (liquidArrayList.isEmpty()) { - System.out.println("\t Your liquid list is empty."); + emptyListMessage(); } else { - System.out.println("\t Liquid List:"); + listHeader(); for (int i = 0; i < liquidArrayList.size(); i++) { LiquidEntry entry = liquidArrayList.get(i); Beverage beverage = entry.getBeverage(); - System.out.println("\t " + (i + 1) + ". Beverage: " + beverage.getBeverage() + System.out.println(WHITESPACE + (i + 1) + ". Beverage: " + beverage.getBeverage() + ", Volume: " + beverage.getVolume()); } } diff --git a/src/main/java/seedu/lifetrack/ui/LiquidListUI.java b/src/main/java/seedu/lifetrack/ui/LiquidListUI.java new file mode 100644 index 0000000000..c2c43b331e --- /dev/null +++ b/src/main/java/seedu/lifetrack/ui/LiquidListUI.java @@ -0,0 +1,32 @@ +package seedu.lifetrack.ui; + +public class LiquidListUI { + + public static final String WHITESPACE = " "; + + public static void deleteMessage() { + System.out.println(WHITESPACE + "Successfully delete the liquid record."); + } + + public static String deleteLogIndexMessage() { + return "Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list."; + } + + public static String deleteLogNumberMessage() { + return "Please enter a valid index!"; + } + + public static void addEntryMessage() { + System.out.println(WHITESPACE + "Beverage has been successfully added"); + } + + public static void emptyListMessage() { + System.out.println(WHITESPACE + "Your liquid list is empty."); + System.out.println(WHITESPACE + "Populate your list with more entries :)"); + } + + public static void listHeader() { + System.out.println(WHITESPACE + "Your liquid List:"); + } +} diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 227fbef9fd..51ff681fc7 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -27,6 +27,11 @@ public class Ui { "|_______ \\__||__| \\___ > |____| |__| (____ /\\___ >__|_ \\\n" + " \\/ \\/ \\/ \\/ \\/\n"; + /** + * Reads in the input from the user + * @param calorieList list containing all entries pertinent to calories + * @param liquidList list containing all entries pertinent to liquids + */ public static void readUserInput(CalorieList calorieList, LiquidList liquidList) { String line; do { @@ -35,6 +40,11 @@ public static void readUserInput(CalorieList calorieList, LiquidList liquidList) } while (!line.equalsIgnoreCase("bye")); } + /** + * handles input from the user + * @param line input from the user + * @param calorieList list containing all entries pertinent to calories + */ public static void handleCaloriesInput(String line, CalorieList calorieList) { assert !line.startsWith("bye") : "exit the app"; if (line.startsWith("calories in") || line.startsWith("calories out")) { From acd5c09b808b60516d8736025587c2895e715f87 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 24 Mar 2024 00:39:30 +0800 Subject: [PATCH 104/414] initial changes to liquid UI tests --- .../java/seedu/lifetrack/LiquidListTest.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index 03bd524509..2cbac4f883 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.lifetrack.ui.LiquidListUI.WHITESPACE; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -38,7 +39,8 @@ public void testPrintLiquidListEmpty() { LiquidList liquidList = new LiquidList(); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "\t Your liquid list is empty." + lineSeparator; + String expectedOutput = WHITESPACE + "Your liquid list is empty." + lineSeparator + + WHITESPACE + "Populate your list with more entries :)" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -51,9 +53,9 @@ public void testPrintLiquidListNonEmpty() { liquidList.addEntry("liquids in b/Milo v/200"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "Beverage has been successfully added" + lineSeparator + - "\t Liquid List:" + lineSeparator + - "\t 1. Beverage: Milo, Volume: 200" + lineSeparator; + String expectedOutput = WHITESPACE+ "Beverage has been successfully added" + lineSeparator + + WHITESPACE+ "Your liquid List:" + lineSeparator + + WHITESPACE + "1. Beverage: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -68,13 +70,13 @@ public void testPrintLiquidListMultipleEntries() { liquidList.addEntry("liquids in b/Juice v/150"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = "Beverage has been successfully added" + lineSeparator + - "Beverage has been successfully added" + lineSeparator + - "Beverage has been successfully added" + lineSeparator + - "\t Liquid List:" + lineSeparator + - "\t 1. Beverage: Milo, Volume: 200" + lineSeparator + - "\t 2. Beverage: Water, Volume: 300" + lineSeparator + - "\t 3. Beverage: Juice, Volume: 150" + lineSeparator; + String expectedOutput = WHITESPACE + "Beverage has been successfully added" + lineSeparator + + WHITESPACE + "Beverage has been successfully added" + lineSeparator + + WHITESPACE + "Beverage has been successfully added" + lineSeparator + + WHITESPACE + "Your liquid List:" + lineSeparator + + WHITESPACE + "1. Beverage: Milo, Volume: 200" + lineSeparator + + WHITESPACE + "2. Beverage: Water, Volume: 300" + lineSeparator + + WHITESPACE + "3. Beverage: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, liquidList.getSize()); } From 03abd95d480c39d2f07094db7b42b6eceda0b820 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:55:18 +0800 Subject: [PATCH 105/414] fix logger --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 8 +++++--- .../seedu/lifetrack/liquids/liquidlist/LiquidList.java | 4 ++-- .../system/exceptions/InvalidInputException.java | 7 ++++++- src/main/java/seedu/lifetrack/ui/CalorieListUi.java | 8 ++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 9c5e449647..a8b0feacf2 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -4,6 +4,9 @@ import static seedu.lifetrack.ui.CalorieListUi.successfulDeletedMessage; import static seedu.lifetrack.ui.CalorieListUi.printNewCalorieEntry; import static seedu.lifetrack.ui.CalorieListUi.calorieListHeader; +import static seedu.lifetrack.ui.LiquidListUI.deleteLogIndexMessage; +import static seedu.lifetrack.ui.LiquidListUI.deleteLogNumberMessage; + import seedu.lifetrack.system.exceptions.InvalidInputException; import java.util.logging.Level; import java.util.logging.Logger; @@ -36,10 +39,9 @@ public void deleteEntry(String line) { calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 successfulDeletedMessage(toDelete); } catch (IndexOutOfBoundsException e) { - logr.log(Level.WARNING, "Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list.", e); + System.out.println(deleteLogIndexMessage()); } catch (NumberFormatException e) { - logr.log(Level.WARNING, "Please enter a valid index!", e); + System.out.println(deleteLogNumberMessage()); } } diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 18b3fc9eee..08c592538b 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -55,9 +55,9 @@ public void deleteEntry(String line) { liquidArrayList.remove(index - 1); deleteMessage(); } catch (IndexOutOfBoundsException e) { - logr.log(Level.WARNING, deleteLogIndexMessage(), e); + System.out.println(deleteLogIndexMessage()); } catch (NumberFormatException e) { - logr.log(Level.WARNING, deleteLogNumberMessage(), e); + System.out.println(deleteLogNumberMessage()); } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index e6be36cf56..89a0332988 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -1,11 +1,16 @@ package seedu.lifetrack.system.exceptions; +import seedu.lifetrack.calories.calorielist.CalorieList; +import java.util.logging.Level; +import java.util.logging.Logger; public class InvalidInputException extends Exception { public final String heythere = ""; - + private static Logger logr = Logger.getLogger(CalorieList.class.getName()); public InvalidInputException(){ super("\t Please ensure that you have keyed in the correct format!"); + logr.setLevel(Level.SEVERE); + logr.log(Level.WARNING,"\t Please ensure that you have keyed in the correct format!"); } public InvalidInputException(String exception) { diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index c5c5d72f4f..918a3f8057 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -13,6 +13,14 @@ public static void emptyListMessage() { System.out.println("\t Your caloric list is empty. Add new entries to populate your list :)"); } + public static String deleteLogIndexMessage() { + return "Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list."; + } + public static String deleteLogNumberMessage() { + return "Please enter a valid index!"; + } + public static void calorieListHeader() { System.out.println("\t Your Caloric List:"); } From 63f40e285fde7cc0c47678709a15769219678361 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:01:13 +0800 Subject: [PATCH 106/414] fix gradle problem --- .../lifetrack/system/exceptions/InvalidInputException.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index 89a0332988..d718e8bc0a 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -5,8 +5,9 @@ import java.util.logging.Logger; public class InvalidInputException extends Exception { - public final String heythere = ""; private static Logger logr = Logger.getLogger(CalorieList.class.getName()); + public final String heythere = ""; + public InvalidInputException(){ super("\t Please ensure that you have keyed in the correct format!"); logr.setLevel(Level.SEVERE); From 2bd1b1fe71fdba9175ea2a5e4beadc756a684369 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 26 Mar 2024 03:49:35 +0800 Subject: [PATCH 107/414] Add sleep feature --- src/main/java/seedu/lifetrack/LifeTrack.java | 4 +- .../java/seedu/lifetrack/sleep/Sleep.java | 30 +++++++ .../lifetrack/sleep/sleeplist/SleepList.java | 58 ++++++++++++++ .../system/exceptions/ErrorMessages.java | 7 ++ .../lifetrack/system/parser/ParserSleep.java | 39 +++++++++ .../java/seedu/lifetrack/ui/SleepListUi.java | 25 ++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 26 +++++- .../java/seedu/lifetrack/ParserSleepTest.java | 70 ++++++++++++++++ .../java/seedu/lifetrack/SleepListTest.java | 80 +++++++++++++++++++ src/test/java/seedu/lifetrack/UITest.java | 4 +- 10 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/sleep/Sleep.java create mode 100644 src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java create mode 100644 src/main/java/seedu/lifetrack/system/parser/ParserSleep.java create mode 100644 src/main/java/seedu/lifetrack/ui/SleepListUi.java create mode 100644 src/test/java/seedu/lifetrack/ParserSleepTest.java create mode 100644 src/test/java/seedu/lifetrack/SleepListTest.java diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index a1c0611922..91a2ff19a1 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -2,6 +2,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.sleep.sleeplist.SleepList; import seedu.lifetrack.ui.Ui; import java.util.Scanner; @@ -14,10 +15,11 @@ public class LifeTrack { public static void main(String[] args) { CalorieList calorieList = new CalorieList(); LiquidList liquidList = new LiquidList(); + SleepList sleepList = new SleepList(); Scanner in = new Scanner(System.in); Ui.sayHello(); assert true : "dummy assertion set to fail"; - Ui.readUserInput(calorieList,liquidList); + Ui.readUserInput(calorieList,liquidList,sleepList); Ui.byeMessage(); in.close(); } diff --git a/src/main/java/seedu/lifetrack/sleep/Sleep.java b/src/main/java/seedu/lifetrack/sleep/Sleep.java new file mode 100644 index 0000000000..5b56292fc3 --- /dev/null +++ b/src/main/java/seedu/lifetrack/sleep/Sleep.java @@ -0,0 +1,30 @@ +package seedu.lifetrack.sleep; + +public class Sleep { + private String date; + private double duration; + + /*** + * Sleep constructor: date can be empty. If date input is empty, automatically fill with N/A; + * date should be in format DDMMYY, duration should be a positive real number in hour unit. + * @param date + * @param duration + */ + public Sleep (String date, double duration){ + this.date = date.isEmpty() ? "N/A" : date; + this.duration = duration; + } + + public String getDate() { + return date; + } + + public double getDuration() { + return duration; + } + public String toString() { + // Show "N/A" if no date was provided + return "Date: " + (date == null || date.isEmpty() ? "N/A" : date) + + ", Duration: " + String.format("%.1f", duration) + " hours"; + } +} diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java new file mode 100644 index 0000000000..ff3e91baac --- /dev/null +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -0,0 +1,58 @@ +package seedu.lifetrack.sleep.sleeplist; + +import seedu.lifetrack.sleep.Sleep; +import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.parser.ParserSleep; +import seedu.lifetrack.ui.SleepListUi; +import java.util.ArrayList; +import static seedu.lifetrack.ui.SleepListUi.WHITESPACE; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; +import static seedu.lifetrack.ui.SleepListUi.deleteMessage; +import static seedu.lifetrack.ui.SleepListUi.sleepListHeader; + +public class SleepList { + private ArrayList sleepList; + public SleepList() { + this.sleepList=new ArrayList<>(); + } + + public Sleep getSleep(int index) { + assert index >= 0 && index < sleepList.size() : "Index out of bounds"; + return sleepList.get(index); + } + public void addSleep(String input) { + try { + Sleep newSleep = ParserSleep.parseSleepInput(input); + sleepList.add(newSleep); + SleepListUi.addEntryMessage(); + } catch (InvalidInputException e) { + System.out.println(getIncorrectSleepInputMessage()); + } + } + public void deleteSleep(String line) { + try { + int index = Integer.parseInt(line.split(" ")[2]) ; + sleepList.remove(index - 1); + deleteMessage(); + } catch (IndexOutOfBoundsException e) { + System.out.println(SleepListUi.deleteLogIndexMessage()); + } catch (NumberFormatException e) { + System.out.println(SleepListUi.deleteLogNumberMessage()); + } + } + public void printSleepList() { + if (this.sleepList.isEmpty()) { + SleepListUi.emptyListMessage(); + } else { + sleepListHeader(); + for (int i = 0; i < this.sleepList.size(); i++) { + System.out.println(WHITESPACE + (i + 1)+ ". " + getSleep(i).toString()); + } + } + } + public int getSize() { + return sleepList.size(); + } + + +} diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index c0cae070a5..7f644ecbf9 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -18,4 +18,11 @@ public static String getIncorrectCaloriesInputMessage() { public static String getIncorrectMacrosInputMessage() { return "\t Please input only positive integers into the macronutrients field!"; } + + public static String getIncorrectSleepInputMessage() { + return "\t Please input only positive real number into the sleep duration field!"; + } + public static String getIncorrectSleepDateInputMessage() { + return "\t Error: Date must be in DDMMYY format.!"; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java new file mode 100644 index 0000000000..0ab14af5ef --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -0,0 +1,39 @@ +package seedu.lifetrack.system.parser; + +import seedu.lifetrack.sleep.Sleep; +import seedu.lifetrack.system.exceptions.InvalidInputException; + +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepDateInputMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; + +public class ParserSleep { + public static Sleep parseSleepInput(String input) throws InvalidInputException { + try { + String date = "N/A"; // Default if no date is provided + double duration = 0; + String[] parts = input.split(" "); + for (String part : parts) { + if (part.startsWith("t/")) { + duration = Double.parseDouble(part.substring(2)); + if (duration < 0) { + throw new InvalidInputException(getIncorrectSleepInputMessage()); + } + } else if (part.startsWith("d/")) { + date = part.substring(2); + if (!date.matches("\\d{6}")) { + throw new InvalidInputException(getIncorrectSleepDateInputMessage()); + } + } + } + if (duration == 0) { + throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + + "sleep add t/ d/"); + } + return new Sleep(date, duration); + } catch (NumberFormatException e) { + throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + + "sleep add t/ d/"); + } + } + +} diff --git a/src/main/java/seedu/lifetrack/ui/SleepListUi.java b/src/main/java/seedu/lifetrack/ui/SleepListUi.java new file mode 100644 index 0000000000..187f622396 --- /dev/null +++ b/src/main/java/seedu/lifetrack/ui/SleepListUi.java @@ -0,0 +1,25 @@ +package seedu.lifetrack.ui; + +public class SleepListUi { + public static final String WHITESPACE = " "; + public static void addEntryMessage() { + System.out.println(WHITESPACE + "New sleep record has been successfully added."); + } + public static void deleteMessage() { + System.out.println(WHITESPACE + "Successfully delete the sleep record."); + } + public static String deleteLogNumberMessage() { + return "Please enter a valid index!"; + } + public static String deleteLogIndexMessage() { + return "Sorry, this index is invalid. Please enter a positive integer " + + "within the size of the list."; + } + public static void sleepListHeader() { + System.out.println(WHITESPACE +"Your Sleep List:"); + } + public static void emptyListMessage() { + System.out.println(WHITESPACE + "Your sleep list is empty."); + System.out.println(WHITESPACE + "Populate your list with more entries :)"); + } +} diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 51ff681fc7..ebdd4f78b1 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -2,6 +2,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.sleep.sleeplist.SleepList; import java.util.Scanner; @@ -32,11 +33,11 @@ public class Ui { * @param calorieList list containing all entries pertinent to calories * @param liquidList list containing all entries pertinent to liquids */ - public static void readUserInput(CalorieList calorieList, LiquidList liquidList) { + public static void readUserInput(CalorieList calorieList, LiquidList liquidList, SleepList sleepList) { String line; do { line = new Scanner(System.in).nextLine(); - handleUserInput(line, calorieList, liquidList); + handleUserInput(line, calorieList, liquidList, sleepList); } while (!line.equalsIgnoreCase("bye")); } @@ -70,8 +71,21 @@ public static void handleLiquidsInput(String line, LiquidList liquidsList) { handleUnknownInput(); } } + public static void handleSleepInput(String line, SleepList sleepList) { + assert !line.startsWith("bye") : "exit the app"; + if (line.startsWith("sleep add")) { + sleepList.addSleep(line); + } else if (line.startsWith("sleep list")) { + sleepList.printSleepList(); + } else if (line.startsWith("sleep delete")) { + sleepList.deleteSleep(line); + } else { + handleUnknownInput(); + } + } - public static void handleUserInput(String line, CalorieList calorieList, LiquidList liquidList) { + public static void handleUserInput(String line, CalorieList calorieList, + LiquidList liquidList, SleepList sleepList) { if (!line.startsWith("bye")) { printLine(); line = line.trim().toLowerCase(); @@ -83,6 +97,8 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL showHelp(); } else if (line.startsWith("liquids")) { handleLiquidsInput(line, liquidList); + } else if (line.startsWith("sleep")) { + handleSleepInput(line, sleepList); } else { handleUnknownInput(); } @@ -127,5 +143,9 @@ public static void showHelp() { System.out.println("\t - liquids in b/ v/ : " + "Marks the task at the specified index as done."); System.out.println("\t - liquids list: Displays all entries currently stored in the hydration list.\""); + System.out.println("\t - sleep add t/ d/.\""); + System.out.println("\t - sleep list: Displays all entries currently stored in the sleep list.\""); + System.out.println("\t - sleep delete : Deletes the entry at the specified index" + + " from the sleep list."); } } diff --git a/src/test/java/seedu/lifetrack/ParserSleepTest.java b/src/test/java/seedu/lifetrack/ParserSleepTest.java new file mode 100644 index 0000000000..ada658a5d1 --- /dev/null +++ b/src/test/java/seedu/lifetrack/ParserSleepTest.java @@ -0,0 +1,70 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Test; +import seedu.lifetrack.system.exceptions.InvalidInputException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.lifetrack.system.parser.ParserSleep.parseSleepInput; + +public class ParserSleepTest { + @Test + public void parseSleepInput_inputContains2Duration_invalidInputExceptionThrown() { + // setup test + String invalidInput = "sleep add t/8.0 t/9.2"; + // Call methods to test + try { + parseSleepInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format: " + + "sleep add t/ d/", e.getMessage()); + } + } + @Test + public void parseSleepInput_inputContains2Date_invalidInputExceptionThrown() { + // setup test + String invalidInput = "sleep add d/110324 d/280524"; + // Call methods to test + try { + parseSleepInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format: " + + "sleep add t/ d/", e.getMessage()); + } + } + @Test + public void parseSleepInput_inputMissingDuration_invalidInputExceptionThrown() { + // setup test + String invalidInput = "sleep add d/110324"; + // Call methods to test + try { + parseSleepInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format: " + + "sleep add t/ d/", e.getMessage()); + } + } + + @Test + public void parseSleepInput_inputNonPositiveValueForDuration_invalidInputExceptionThrown() { + // setup test + String invalidInput = "sleep add t/-2 d/110324"; + + // Call methods to test + try { + parseSleepInput(invalidInput); + } catch (InvalidInputException e) { + assertEquals("\t Please input only positive real number into the sleep duration field!" + , e.getMessage()); + } + } + @Test + public void parseLiquidInput_missingKeywords_exceptionThrown() { + + try { + parseSleepInput("sleep add"); + } catch (InvalidInputException e) { + assertEquals("Please ensure that you have keyed in the correct format: " + + "sleep add t/ d/", e.getMessage()); + } + } +} diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java new file mode 100644 index 0000000000..97f4b934ee --- /dev/null +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -0,0 +1,80 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Test; +import seedu.lifetrack.sleep.sleeplist.SleepList; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.lifetrack.ui.LiquidListUI.WHITESPACE; + +public class SleepListTest { + @Test + public void testDeleteSleepValidIndex(){ + SleepList sleepList = new SleepList(); + sleepList.addSleep("sleep add t/7.5 d/110324"); + sleepList.addSleep("sleep add t/8"); + int initialSize = sleepList.getSize(); + sleepList.deleteSleep("sleep delete 1"); + assertEquals(initialSize - 1, sleepList.getSize()); + } + @Test + public void testDeleteSleepInvalidIndex() { + SleepList sleepList = new SleepList(); + sleepList.addSleep("sleep add t/7.5 d/110324"); + sleepList.addSleep("sleep add t/8"); + int initialSize = sleepList.getSize(); + sleepList.deleteSleep("sleep delete 5"); // Index out of bounds + sleepList.deleteSleep("sleep delete -1"); + assertEquals(initialSize, sleepList.getSize()); + } + @Test + public void testPrintLiquidListEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + SleepList sleepList = new SleepList(); + sleepList.printSleepList(); + System.setOut(System.out); + String expectedOutput = WHITESPACE + "Your sleep list is empty." + lineSeparator + + WHITESPACE + "Populate your list with more entries :)" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + @Test + public void testPrintSleepListNonEmpty() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + SleepList sleepList = new SleepList(); + sleepList.addSleep("sleep add t/7.5 d/110324"); + sleepList.printSleepList(); + System.setOut(System.out); + String expectedOutput = WHITESPACE+ "New sleep record has been successfully added." + lineSeparator + + WHITESPACE+ "Your Sleep List:" + lineSeparator + + WHITESPACE + "1. Date: 110324, Duration: 7.5 hours" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + } + + @Test + public void testPrintSleepListMultipleEntries() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + SleepList sleepList = new SleepList(); + sleepList.addSleep("sleep add t/7.5 d/110324"); + sleepList.addSleep("sleep add t/8.0 d/280524"); + sleepList.addSleep("sleep add t/4.2"); + sleepList.printSleepList(); + System.setOut(System.out); + String expectedOutput = WHITESPACE + "New sleep record has been successfully added." + lineSeparator + + WHITESPACE + "New sleep record has been successfully added." + lineSeparator + + WHITESPACE + "New sleep record has been successfully added." + lineSeparator + + WHITESPACE + "Your Sleep List:" + lineSeparator + + WHITESPACE + "1. Date: 110324, Duration: 7.5 hours" + lineSeparator + + WHITESPACE + "2. Date: 280524, Duration: 8.0 hours" + lineSeparator + + WHITESPACE + "3. Date: N/A, Duration: 4.2 hours" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + assertEquals(3, sleepList.getSize()); + } +} diff --git a/src/test/java/seedu/lifetrack/UITest.java b/src/test/java/seedu/lifetrack/UITest.java index 7965e0143a..2ee545b61e 100644 --- a/src/test/java/seedu/lifetrack/UITest.java +++ b/src/test/java/seedu/lifetrack/UITest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.sleep.sleeplist.SleepList; import seedu.lifetrack.ui.Ui; import seedu.lifetrack.liquids.liquidlist.LiquidList; @@ -31,8 +32,9 @@ public void restoreStreams() { public void handleUserInput_inputBye_printByeMessage() { CalorieList calorieList = new CalorieList(); LiquidList liquidList = new LiquidList(); + SleepList sleepList = new SleepList(); String input = "bye"; - Ui.handleUserInput(input, calorieList, liquidList); + Ui.handleUserInput(input, calorieList, liquidList,sleepList); assertEquals("", outContent.toString()); } } From bc49ee7a07ff4d369ce3893201e05a7131604f0b Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:19:08 +0800 Subject: [PATCH 108/414] modify sleep add input command format --- .../lifetrack/system/parser/ParserSleep.java | 8 ++++---- .../java/seedu/lifetrack/ParserSleepTest.java | 12 ++++++------ src/test/java/seedu/lifetrack/SleepListTest.java | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 0ab14af5ef..445fd9d512 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -13,8 +13,8 @@ public static Sleep parseSleepInput(String input) throws InvalidInputException { double duration = 0; String[] parts = input.split(" "); for (String part : parts) { - if (part.startsWith("t/")) { - duration = Double.parseDouble(part.substring(2)); + if (part.matches("^-?\\d+(\\.\\d+)?$")) { + duration = Double.parseDouble(part); if (duration < 0) { throw new InvalidInputException(getIncorrectSleepInputMessage()); } @@ -27,12 +27,12 @@ public static Sleep parseSleepInput(String input) throws InvalidInputException { } if (duration == 0) { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + - "sleep add t/ d/"); + "sleep add d/"); } return new Sleep(date, duration); } catch (NumberFormatException e) { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + - "sleep add t/ d/"); + "sleep add d/"); } } diff --git a/src/test/java/seedu/lifetrack/ParserSleepTest.java b/src/test/java/seedu/lifetrack/ParserSleepTest.java index ada658a5d1..150b2a5156 100644 --- a/src/test/java/seedu/lifetrack/ParserSleepTest.java +++ b/src/test/java/seedu/lifetrack/ParserSleepTest.java @@ -10,13 +10,13 @@ public class ParserSleepTest { @Test public void parseSleepInput_inputContains2Duration_invalidInputExceptionThrown() { // setup test - String invalidInput = "sleep add t/8.0 t/9.2"; + String invalidInput = "sleep add 8.0 9.2"; // Call methods to test try { parseSleepInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add t/ d/", e.getMessage()); + "sleep add d/", e.getMessage()); } } @Test @@ -28,7 +28,7 @@ public void parseSleepInput_inputContains2Date_invalidInputExceptionThrown() { parseSleepInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add t/ d/", e.getMessage()); + "sleep add d/", e.getMessage()); } } @Test @@ -40,14 +40,14 @@ public void parseSleepInput_inputMissingDuration_invalidInputExceptionThrown() { parseSleepInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add t/ d/", e.getMessage()); + "sleep add d/", e.getMessage()); } } @Test public void parseSleepInput_inputNonPositiveValueForDuration_invalidInputExceptionThrown() { // setup test - String invalidInput = "sleep add t/-2 d/110324"; + String invalidInput = "sleep add -2 d/110324"; // Call methods to test try { @@ -64,7 +64,7 @@ public void parseLiquidInput_missingKeywords_exceptionThrown() { parseSleepInput("sleep add"); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add t/ d/", e.getMessage()); + "sleep add d/", e.getMessage()); } } } diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index 97f4b934ee..366eefc9f3 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -13,8 +13,8 @@ public class SleepListTest { @Test public void testDeleteSleepValidIndex(){ SleepList sleepList = new SleepList(); - sleepList.addSleep("sleep add t/7.5 d/110324"); - sleepList.addSleep("sleep add t/8"); + sleepList.addSleep("sleep add 7.5 d/110324"); + sleepList.addSleep("sleep add 8"); int initialSize = sleepList.getSize(); sleepList.deleteSleep("sleep delete 1"); assertEquals(initialSize - 1, sleepList.getSize()); @@ -22,8 +22,8 @@ public void testDeleteSleepValidIndex(){ @Test public void testDeleteSleepInvalidIndex() { SleepList sleepList = new SleepList(); - sleepList.addSleep("sleep add t/7.5 d/110324"); - sleepList.addSleep("sleep add t/8"); + sleepList.addSleep("sleep add 7.5 d/110324"); + sleepList.addSleep("sleep add 8"); int initialSize = sleepList.getSize(); sleepList.deleteSleep("sleep delete 5"); // Index out of bounds sleepList.deleteSleep("sleep delete -1"); @@ -47,7 +47,7 @@ public void testPrintSleepListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); SleepList sleepList = new SleepList(); - sleepList.addSleep("sleep add t/7.5 d/110324"); + sleepList.addSleep("sleep add 7.5 d/110324"); sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = WHITESPACE+ "New sleep record has been successfully added." + lineSeparator + @@ -62,9 +62,9 @@ public void testPrintSleepListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); SleepList sleepList = new SleepList(); - sleepList.addSleep("sleep add t/7.5 d/110324"); - sleepList.addSleep("sleep add t/8.0 d/280524"); - sleepList.addSleep("sleep add t/4.2"); + sleepList.addSleep("sleep add 7.5 d/110324"); + sleepList.addSleep("sleep add 8.0 d/280524"); + sleepList.addSleep("sleep add 4.2"); sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = WHITESPACE + "New sleep record has been successfully added." + lineSeparator + From d54a9c090d8d431c9c9a5e0726c956921dc95a62 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 26 Mar 2024 23:14:42 +0800 Subject: [PATCH 109/414] modify sleep add input command format in ui --- src/main/java/seedu/lifetrack/ui/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index ebdd4f78b1..dc8ea71986 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -143,7 +143,7 @@ public static void showHelp() { System.out.println("\t - liquids in b/ v/ : " + "Marks the task at the specified index as done."); System.out.println("\t - liquids list: Displays all entries currently stored in the hydration list.\""); - System.out.println("\t - sleep add t/ d/.\""); + System.out.println("\t - sleep add d/.\""); System.out.println("\t - sleep list: Displays all entries currently stored in the sleep list.\""); System.out.println("\t - sleep delete : Deletes the entry at the specified index" + " from the sleep list."); From de381b5fd8713be142f49fe276b59493f320316d Mon Sep 17 00:00:00 2001 From: Pongster <110764881+shawnpong@users.noreply.github.com> Date: Wed, 27 Mar 2024 01:05:57 +0800 Subject: [PATCH 110/414] Fixing junit tests --- .../java/seedu/lifetrack/liquids/Liquid.java | 20 ---- .../liquids/liquidlist/LiquidEntry.java | 21 +++- .../liquids/liquidlist/LiquidList.java | 9 +- .../system/exceptions/ErrorMessages.java | 4 + .../InvalidInputExceptionMessage.java | 17 +++ .../lifetrack/system/parser/ParserLiquid.java | 105 ++++++++++-------- .../java/seedu/lifetrack/ui/LiquidListUI.java | 6 +- src/main/java/seedu/lifetrack/ui/Ui.java | 14 +-- .../java/seedu/lifetrack/LiquidListTest.java | 32 +++--- .../seedu/lifetrack/ParserLiquidTest.java | 18 +-- 10 files changed, 130 insertions(+), 116 deletions(-) delete mode 100644 src/main/java/seedu/lifetrack/liquids/Liquid.java diff --git a/src/main/java/seedu/lifetrack/liquids/Liquid.java b/src/main/java/seedu/lifetrack/liquids/Liquid.java deleted file mode 100644 index d78ae1dac7..0000000000 --- a/src/main/java/seedu/lifetrack/liquids/Liquid.java +++ /dev/null @@ -1,20 +0,0 @@ -package seedu.lifetrack.liquids; - -public class Liquid { - private int liquids; - - private String beverage; - - public Liquid (int liquids, String beverage){ - this.liquids = liquids; - this.beverage = beverage; - } - - public int getVolume() { - return liquids; - } - - public String getBeverage() { - return beverage; - } -} diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java index b812fecf96..e582dd4dab 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java @@ -3,14 +3,23 @@ import seedu.lifetrack.liquids.Beverage; public class LiquidEntry { + private String description; + private int volume; + private String date; + public LiquidEntry(String description, int volume, String date){ + this.description = description; + this.volume = volume; + this.date = date; + } + public String getDescription() { + return description; + } - private Beverage beverage; - - public LiquidEntry(Beverage beverage){ - this.beverage= beverage; + public int getVolume() { + return volume; } - public Beverage getBeverage() { - return beverage; + public String getDate() { + return date; } } diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 08c592538b..279284cd7b 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -24,8 +24,7 @@ public class LiquidList { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); private ArrayList liquidArrayList; - private final int DELETE_PADDING = 15; - + private final int DELETE_PADDING = 16; /** * Constructs an empty LiquidList. */ @@ -67,6 +66,7 @@ public void deleteEntry(String line) { * @param input the input string containing liquid entry information */ public void addEntry(String input) { + assert (input.startsWith("hydration add") || input.startsWith("calories out")) : "ensures that input is correct"; try { LiquidEntry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); @@ -87,9 +87,8 @@ public void printLiquidList() { listHeader(); for (int i = 0; i < liquidArrayList.size(); i++) { LiquidEntry entry = liquidArrayList.get(i); - Beverage beverage = entry.getBeverage(); - System.out.println(WHITESPACE + (i + 1) + ". Beverage: " + beverage.getBeverage() - + ", Volume: " + beverage.getVolume()); + System.out.println(WHITESPACE + (i + 1) + ". Beverage: " + entry.getDescription() + + ", Volume: " + entry.getVolume() + ", Date: " + entry.getDate()); } } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index c0cae070a5..86be6ab193 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -15,6 +15,10 @@ public static String getIncorrectCaloriesInputMessage() { return "\t Please input only positive integers into the calories field!"; } + public static String getIncorrectVolumeInputMessage() { + return "\t Please input only positive integers into the volume field!"; + } + public static String getIncorrectMacrosInputMessage() { return "\t Please input only positive integers into the macronutrients field!"; } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 3301e21c7a..91a7bef7e0 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -8,6 +8,8 @@ public class InvalidInputExceptionMessage { private static final String CALORIES_OUT_INPUT = "\t Example input: calories out DESCRIPTION " + "c/INTEGER_CALORIES date/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; + private static final String HYDRATION_ADD_INPUT = "\t Example input: hydration add DESCRIPTION " + + "v/VOLUME date/DATE "; public static String getIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; @@ -37,4 +39,19 @@ public static String getMacrosInCaloriesOutMessage() { String message = "\t Calorie output entry cannot have macros!\n"; return HEADER + message + CALORIES_OUT_INPUT; } + + public static String getHydrationIncorrectOrderMessage() { + String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; + return HEADER + message + HYDRATION_ADD_INPUT; + } + + public static String getHydrationMissingKeywordsMessage() { + String message = "\t Please ensure that the compulsory keywords exist!\n"; + return HEADER + message + HYDRATION_ADD_INPUT; + } + + public static String getHydrationWhitespaceInInputMessage() { + String message = "\t Please ensure that there is no whitespace in your input!\n"; + return HEADER + message + HYDRATION_ADD_INPUT; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 995d2b0743..4b497edd48 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -1,10 +1,13 @@ package seedu.lifetrack.system.parser; -import seedu.lifetrack.liquids.Beverage; import seedu.lifetrack.liquids.liquidlist.LiquidEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectVolumeInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; + public class ParserLiquid { + private static final int HYDRATION_ADD_PADDING = 13; /** * Parses a string input to create a Liquid object representing liquid intake. @@ -20,65 +23,73 @@ public class ParserLiquid { * contains empty fields */ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputException { - //get index for b/ and v/ inputs - int beverageIndex = input.indexOf("b/"); + //get index for v/ and date/ inputs int volumeIndex = input.indexOf("v/"); + int dateIndex = input.indexOf("date/"); - // Handle exception when b/ or v/ not entered - if (beverageIndex == -1 || volumeIndex == -1) { - throw new InvalidInputException("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000"); - } + checkKeywordsExist(dateIndex, volumeIndex); + assert volumeIndex != -1 : "The v/ keyword should exist!"; + assert dateIndex != -1 : "The date/ keyword should exist!"; - assert (beverageIndex != -1 || volumeIndex != -1) : "ensures that beverage and volume has been keyed in"; + checkKeywordsCorrectlyOrdered(dateIndex, volumeIndex); + assert volumeIndex < dateIndex : "The v/ keyword must appear before date/ in the input!"; - //Handle exception when order of b/ and v/ is incorrect - if (volumeIndex < beverageIndex) { - throw new InvalidInputException("Invalid input exception: " + - "Please ensure that you have entered b/ before v/\n" + - "For example: liquids in b/Milo v/1000"); - } + String[] parts = input.split("v/|date/"); + String command = parts[0].substring(0, HYDRATION_ADD_PADDING).trim(); + String description = getDescriptionFromInput(input, volumeIndex); + String strVolume = parts[1].trim(); + String date = parts[2].trim(); + checkInputsAreNonEmpty(description, strVolume, date); + assert description != "" : "The description field should be a non-empty string!"; + assert strVolume != "" : "The volume field should be a non-empty string!"; + assert date != "" : "The date field should be a non-empty string!"; - // splits string according to b/ and v/ keywords - String[] parts = input.split("b/|v/"); - // parts length less than 3 means that not all split keywords were keyed in - if (parts.length < 3) { - throw new InvalidInputException("Please ensure that you have keyed in the correct format!"); - } - - // extracts beverage name and quantity portion from input - String beverageName = parts[1].trim(); - String strVolume = parts[2].trim(); + int volume = getIntegerVolumeFromInput(strVolume); + checkVolumeIsPositiveInteger(volume); + assert volume > 0 : "Volume value must be a positive integer!"; - // ensures that both inputs are not empty - if (beverageName.isEmpty() || strVolume.isEmpty()) { - throw new InvalidInputException("Please ensure that you have keyed in the correct format!"); - } + return makeNewInputEntry(description, volume, date); + } + private static LiquidEntry makeNewInputEntry(String description, int volume, String date) { - int volume; - // Handle exception when non integer values are keyed in for volume + return new LiquidEntry(description, volume, date); + } + private static int getIntegerVolumeFromInput(String strVolume) { + int volume = 0; try { volume = Integer.parseInt(strVolume); - } catch(NumberFormatException e) { - throw new InvalidInputException("Invalid input Exception: " + - "Please enter a positive integer value for volume"); + } catch (NumberFormatException e) { + System.out.println(getIncorrectVolumeInputMessage()); } - - // Handle exception when negative values are keyed in for volume + return volume; + } + private static void checkVolumeIsPositiveInteger(int volume) throws InvalidInputException { if (volume <= 0) { - throw new InvalidInputException("Invalid input Exception: " + - "Please enter a positive integer value for volume"); + throw new InvalidInputException(getIncorrectVolumeInputMessage()); } - return getNewLiquidEntry(volume, beverageName); } - - private static LiquidEntry getNewLiquidEntry(int volume, String name) throws InvalidInputException { - //create objects for Beverage - Beverage liquidToAdd = new Beverage(name, volume); - - //create Object Entry to be returned - return new LiquidEntry(liquidToAdd); + private static void checkInputsAreNonEmpty(String description, String strVolume, String date) + throws InvalidInputException { + //check if the description, calories or date fields are empty + if (description.isEmpty() || strVolume.isEmpty() || date.isEmpty()) { + throw new InvalidInputException(getHydrationWhitespaceInInputMessage()); + } + } + private static String getDescriptionFromInput(String inputString, int volumeIndex) { + String description; + description = inputString.substring(HYDRATION_ADD_PADDING, volumeIndex).trim(); + return description; + } + private static void checkKeywordsCorrectlyOrdered( int dateIndex, int volumeIndex) throws InvalidInputException { + if (!(volumeIndex < dateIndex)) { + throw new InvalidInputException(getHydrationIncorrectOrderMessage()); + } + } + private static void checkKeywordsExist(int dateIndex, int volumeIndex) throws InvalidInputException { + //check that v/ and date/ keywords exist in the input, else throw exception + if (dateIndex == -1 || volumeIndex == -1) { + throw new InvalidInputException(getHydrationMissingKeywordsMessage()); + } } } diff --git a/src/main/java/seedu/lifetrack/ui/LiquidListUI.java b/src/main/java/seedu/lifetrack/ui/LiquidListUI.java index c2c43b331e..cb217e0521 100644 --- a/src/main/java/seedu/lifetrack/ui/LiquidListUI.java +++ b/src/main/java/seedu/lifetrack/ui/LiquidListUI.java @@ -5,7 +5,7 @@ public class LiquidListUI { public static final String WHITESPACE = " "; public static void deleteMessage() { - System.out.println(WHITESPACE + "Successfully delete the liquid record."); + System.out.println(WHITESPACE + "Successfully delete the hydration record."); } public static String deleteLogIndexMessage() { @@ -22,11 +22,11 @@ public static void addEntryMessage() { } public static void emptyListMessage() { - System.out.println(WHITESPACE + "Your liquid list is empty."); + System.out.println(WHITESPACE + "Your hydration list is empty."); System.out.println(WHITESPACE + "Populate your list with more entries :)"); } public static void listHeader() { - System.out.println(WHITESPACE + "Your liquid List:"); + System.out.println(WHITESPACE + "Your hydration List:"); } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 51ff681fc7..edd217ad8b 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -60,11 +60,11 @@ public static void handleCaloriesInput(String line, CalorieList calorieList) { public static void handleLiquidsInput(String line, LiquidList liquidsList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("liquids in") || line.startsWith("liquids out")) { + if (line.startsWith("hydration add")) { liquidsList.addEntry(line); - } else if (line.startsWith("liquids list")) { + } else if (line.startsWith("hydration list")) { liquidsList.printLiquidList(); - } else if (line.startsWith("liquids delete")) { + } else if (line.startsWith("hydration delete")) { liquidsList.deleteEntry(line); } else { handleUnknownInput(); @@ -81,7 +81,7 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL handleCaloriesInput(line, calorieList); } else if (line.startsWith("help")) { showHelp(); - } else if (line.startsWith("liquids")) { + } else if (line.startsWith("hydration")) { handleLiquidsInput(line, liquidList); } else { handleUnknownInput(); @@ -119,13 +119,13 @@ public static void handleUnknownInput() { public static void showHelp() { System.out.println("\t LifeTrack Command List:"); System.out.println("\t - help: Displays a list of available commands and their descriptions."); - System.out.println("\t - calories in/out c/ d/: " + + System.out.println("\t - calories in/out c/ date/: " + "Adds a calorie gaining/burning entry into the calories tracker."); System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); System.out.println("\t - calories delete : Deletes the entry at the specified index" + " from the calorie list."); - System.out.println("\t - liquids in b/ v/ : " + + System.out.println("\t - hydration add v/ date/: " + "Marks the task at the specified index as done."); - System.out.println("\t - liquids list: Displays all entries currently stored in the hydration list.\""); + System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list.\""); } } diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index 2cbac4f883..b2bdd4b9dd 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -15,19 +15,19 @@ public class LiquidListTest { @Test public void testDeleteLiquidValidIndex() { LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); + liquidList.addEntry("hydration add Milo v/200 date/220224"); int initialSize = liquidList.getSize(); - liquidList.deleteEntry("delete liquids 1"); + liquidList.deleteEntry("hydration delete 1"); assertEquals(initialSize - 1, liquidList.getSize()); } @Test public void testDeleteLiquidInvalidIndex() { LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); + liquidList.addEntry("hydration add Milo v/200 date/220224"); int initialSize = liquidList.getSize(); - liquidList.deleteEntry("delete liquids 2"); // Index out of bounds - liquidList.deleteEntry("delete liquids -1"); + liquidList.deleteEntry("hydration delete 2"); // Index out of bounds + liquidList.deleteEntry("hydration delete -1"); assertEquals(initialSize, liquidList.getSize()); } @@ -39,7 +39,7 @@ public void testPrintLiquidListEmpty() { LiquidList liquidList = new LiquidList(); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = WHITESPACE + "Your liquid list is empty." + lineSeparator + String expectedOutput = WHITESPACE + "Your hydration list is empty." + lineSeparator + WHITESPACE + "Populate your list with more entries :)" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -50,12 +50,12 @@ public void testPrintLiquidListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); + liquidList.addEntry("hydration add Milo v/200 date/220224"); liquidList.printLiquidList(); System.setOut(System.out); String expectedOutput = WHITESPACE+ "Beverage has been successfully added" + lineSeparator + - WHITESPACE+ "Your liquid List:" + lineSeparator + - WHITESPACE + "1. Beverage: Milo, Volume: 200" + lineSeparator; + WHITESPACE+ "Your hydration List:" + lineSeparator + + WHITESPACE + "1. Beverage: Milo, Volume: 200, Date: 220224" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -65,18 +65,18 @@ public void testPrintLiquidListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); LiquidList liquidList = new LiquidList(); - liquidList.addEntry("liquids in b/Milo v/200"); - liquidList.addEntry("liquids in b/Water v/300"); - liquidList.addEntry("liquids in b/Juice v/150"); + liquidList.addEntry("hydration add Milo v/200 date/220224"); + liquidList.addEntry("hydration add Water v/300 date/220224"); + liquidList.addEntry("hydration add Juice v/150 date/220224"); liquidList.printLiquidList(); System.setOut(System.out); String expectedOutput = WHITESPACE + "Beverage has been successfully added" + lineSeparator + WHITESPACE + "Beverage has been successfully added" + lineSeparator + WHITESPACE + "Beverage has been successfully added" + lineSeparator + - WHITESPACE + "Your liquid List:" + lineSeparator + - WHITESPACE + "1. Beverage: Milo, Volume: 200" + lineSeparator + - WHITESPACE + "2. Beverage: Water, Volume: 300" + lineSeparator + - WHITESPACE + "3. Beverage: Juice, Volume: 150" + lineSeparator; + WHITESPACE + "Your hydration List:" + lineSeparator + + WHITESPACE + "1. Beverage: Milo, Volume: 200, Date: 220224" + lineSeparator + + WHITESPACE + "2. Beverage: Water, Volume: 300, Date: 220224" + lineSeparator + + WHITESPACE + "3. Beverage: Juice, Volume: 150, Date: 220224" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, liquidList.getSize()); } diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index ace58e989a..db0a58152c 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -5,6 +5,8 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; +import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; import static seedu.lifetrack.system.parser.ParserLiquid.parseLiquidInput; public class ParserLiquidTest { @@ -71,16 +73,10 @@ public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { @Test public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExceptionThrown() { - // setup test - String invalidInput = "liquids in v/1000 b/milo"; - - // Call methods to test try { - parseLiquidInput(invalidInput); + parseCaloriesInput("hydration add milo date/202232 v/123"); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ before v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals(getIncorrectOrderMessage(), e.getMessage()); } } @@ -116,11 +112,9 @@ public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionTh @Test public void parseLiquidInput_missingKeywords_exceptionThrown() { try { - parseLiquidInput("liquids in"); + parseCaloriesInput("hydration add milo"); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals(getHydrationMissingKeywordsMessage(), e.getMessage()); } } From 12d8054a3f9a0d0d84c36ce68e35e9d8981363ae Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 03:51:31 +0800 Subject: [PATCH 111/414] add file storage capability, integrate Entry classes for liquids and calories --- .gitignore | 4 +- .../{calories/calorielist => }/Entry.java | 16 ++-- src/main/java/seedu/lifetrack/LifeTrack.java | 18 +++-- .../calories/calorielist/CalorieList.java | 23 +++++- .../calories/calorielist/InputEntry.java | 28 ++++++- .../calories/calorielist/OutputEntry.java | 27 ++++++- .../seedu/lifetrack/liquids/Beverage.java | 20 ----- .../java/seedu/lifetrack/liquids/Liquid.java | 20 ----- .../liquids/liquidlist/LiquidEntry.java | 19 +++-- .../liquids/liquidlist/LiquidList.java | 39 +++++++--- .../system/exceptions/ErrorMessages.java | 8 ++ .../system/parser/ParserCalories.java | 3 +- .../lifetrack/system/parser/ParserLiquid.java | 9 +-- .../lifetrack/system/storage/FileHandler.java | 77 +++++++++++++++++++ .../seedu/lifetrack/ui/CalorieListUi.java | 2 +- .../java/seedu/lifetrack/CalorieListTest.java | 9 ++- .../java/seedu/lifetrack/LiquidListTest.java | 8 +- src/test/java/seedu/lifetrack/UITest.java | 3 +- 18 files changed, 235 insertions(+), 98 deletions(-) rename src/main/java/seedu/lifetrack/{calories/calorielist => }/Entry.java (59%) delete mode 100644 src/main/java/seedu/lifetrack/liquids/Beverage.java delete mode 100644 src/main/java/seedu/lifetrack/liquids/Liquid.java create mode 100644 src/main/java/seedu/lifetrack/system/storage/FileHandler.java diff --git a/.gitignore b/.gitignore index 4a8db972fd..1c2255e49c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ src/main/resources/docs/ bin/ /text-ui-test/ACTUAL.TXT -text-ui-test/EXPECTED-UNIX.TXT \ No newline at end of file +text-ui-test/EXPECTED-UNIX.TXT + +data/* \ No newline at end of file diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java b/src/main/java/seedu/lifetrack/Entry.java similarity index 59% rename from src/main/java/seedu/lifetrack/calories/calorielist/Entry.java rename to src/main/java/seedu/lifetrack/Entry.java index 7d16f76f9d..deb82ca011 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -1,14 +1,12 @@ -package seedu.lifetrack.calories.calorielist; +package seedu.lifetrack; public abstract class Entry { private String description; - private int calories; private String date; - public Entry(String description, int calories, String date){ + public Entry(String description, String date){ this.description = description; - this.calories = calories; this.date = date; } @@ -16,15 +14,15 @@ public String getDescription() { return description; } - public int getCalories() { - return calories; - } - public String getDate() { return date; } public String toString() { - return String.format("\t Date: " + date + ", Description: " + description + ", Calories: " + calories); + return String.format("\t Date: " + date + ", Description: " + description); + } + + public String toFileFriendlyString() { + return String.format(date + ";" + description); } } diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index a1c0611922..0730c8b84e 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -4,22 +4,26 @@ import seedu.lifetrack.liquids.liquidlist.LiquidList; import seedu.lifetrack.ui.Ui; -import java.util.Scanner; +import java.io.File; public class LifeTrack { + public static CalorieList calorieList; + public static LiquidList liquidList; + + public static void setup() { + new File("data/").mkdir(); + calorieList = new CalorieList("data/caloriesData.txt"); + liquidList = new LiquidList("data/liquidsData.txt"); + } + /** * Main entry-point for the java.lifetrack.LifeTrack application. */ public static void main(String[] args) { - CalorieList calorieList = new CalorieList(); - LiquidList liquidList = new LiquidList(); - Scanner in = new Scanner(System.in); + setup(); Ui.sayHello(); - assert true : "dummy assertion set to fail"; Ui.readUserInput(calorieList,liquidList); Ui.byeMessage(); - in.close(); } - } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index a8b0feacf2..449263d3a9 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -7,19 +7,37 @@ import static seedu.lifetrack.ui.LiquidListUI.deleteLogIndexMessage; import static seedu.lifetrack.ui.LiquidListUI.deleteLogNumberMessage; +import seedu.lifetrack.Entry; +import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.storage.FileHandler; + import java.util.logging.Level; import java.util.logging.Logger; +import java.io.FileNotFoundException; import java.util.ArrayList; public class CalorieList { + private final int SIZE_OF_DELETE = 16; + private static Logger logr = Logger.getLogger(CalorieList.class.getName()); private ArrayList calorieArrayList; - private final int SIZE_OF_DELETE = 16; + private FileHandler fileHandler; public CalorieList() { - calorieArrayList= new ArrayList<>(); + calorieArrayList = new ArrayList<>(); + fileHandler = new FileHandler("data/caloriesTestData.txt"); + } + + public CalorieList(String filePath) { + try { + fileHandler = new FileHandler(filePath); + calorieArrayList = fileHandler.getCalorieEntriesFromFile(); + } catch (FileNotFoundException e) { + calorieArrayList = new ArrayList<>(); + System.out.println(ErrorMessages.getFileNotFoundMessage()); + } } public Entry getEntry(int index) { @@ -62,6 +80,7 @@ public void addEntry(String input) { try { Entry newEntry = parseCaloriesInput(input); calorieArrayList.add(newEntry); + fileHandler.writeEntries(calorieArrayList); printNewCalorieEntry(newEntry); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index f93b4efa9e..13fbc3b59f 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -1,21 +1,45 @@ package seedu.lifetrack.calories.calorielist; +import seedu.lifetrack.Entry; import seedu.lifetrack.calories.Food; public class InputEntry extends Entry { private Food food; + private int calories; + private boolean doesFoodExist = false; public InputEntry(String description, int calories, String date) { - super(description, calories, date); + super(description, date); + this.calories = calories; } public InputEntry(String description, int calories, String date, Food food) { - super(description, calories, date); + super(description, date); this.food = food; + this.calories = calories; + this.doesFoodExist = true; } public Food getFood() { return food; } + + public int getCalories() { + return calories; + } + + public String toString() { + return String.format(super.toString() + ", Calories: " + calories + (doesFoodExist ? + " (C: " + food.getCarbohydrates() + + ", P: " + food.getProteins() + + ", F: " + food.getFats() + ")" + : "")); + } + + public String toFileFriendlyString() { + return String.format(super.toFileFriendlyString() + ";C_IN;" + calories + + (doesFoodExist ? ";" + food.getCarbohydrates() + ";" + food.getProteins() + ";" + food.getFats() + : "")); + } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java index 26b5424d34..c5eca8f818 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java @@ -1,17 +1,40 @@ package seedu.lifetrack.calories.calorielist; +import seedu.lifetrack.Entry; import seedu.lifetrack.calories.Activity; public class OutputEntry extends Entry { private Activity activity; + private int calories; + private boolean doesActivityExist = false; + public OutputEntry(String description, int calories, String date) { - super(description, calories, date); + super(description, date); + this.calories = calories; } public OutputEntry(String description, int calories, String date, Activity activity) { - super(description, calories, date); + super(description, date); this.activity = activity; + this.calories = calories; + this.doesActivityExist = true; + } + + public Activity getActivity() { + return activity; + } + + public int getCalories() { + return calories; + } + + public String toString() { + return String.format(super.toString() + ", Calories: " + calories); + } + + public String toFileFriendlyString() { + return String.format(super.toFileFriendlyString() + ";C_OUT;" + calories); } } diff --git a/src/main/java/seedu/lifetrack/liquids/Beverage.java b/src/main/java/seedu/lifetrack/liquids/Beverage.java deleted file mode 100644 index 97838f2c93..0000000000 --- a/src/main/java/seedu/lifetrack/liquids/Beverage.java +++ /dev/null @@ -1,20 +0,0 @@ -package seedu.lifetrack.liquids; - -public class Beverage { - private String beverage; - private int volume; - - public Beverage(String beverage, int volume){ - this.beverage = beverage; - this.volume = volume; - - } - - public String getBeverage() { - return beverage; - } - - public int getVolume() { - return volume; - } -} diff --git a/src/main/java/seedu/lifetrack/liquids/Liquid.java b/src/main/java/seedu/lifetrack/liquids/Liquid.java deleted file mode 100644 index d78ae1dac7..0000000000 --- a/src/main/java/seedu/lifetrack/liquids/Liquid.java +++ /dev/null @@ -1,20 +0,0 @@ -package seedu.lifetrack.liquids; - -public class Liquid { - private int liquids; - - private String beverage; - - public Liquid (int liquids, String beverage){ - this.liquids = liquids; - this.beverage = beverage; - } - - public int getVolume() { - return liquids; - } - - public String getBeverage() { - return beverage; - } -} diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java index b812fecf96..273964b276 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java @@ -1,16 +1,21 @@ package seedu.lifetrack.liquids.liquidlist; -import seedu.lifetrack.liquids.Beverage; +import seedu.lifetrack.Entry; -public class LiquidEntry { +public class LiquidEntry extends Entry { - private Beverage beverage; + private int volume; - public LiquidEntry(Beverage beverage){ - this.beverage= beverage; + public LiquidEntry(String description, int volume, String date){ + super(description, date); + this.volume= volume; } - public Beverage getBeverage() { - return beverage; + public int getVolume() { + return volume; + } + + public String toString() { + return String.format(super.toString() + ", Volume: " + volume); } } diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 08c592538b..b8ec509462 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -1,10 +1,13 @@ package seedu.lifetrack.liquids.liquidlist; +import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.liquids.Beverage; +import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserLiquid; +import seedu.lifetrack.system.storage.FileHandler; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; @@ -22,24 +25,37 @@ * Provides methods to add, delete, and print liquid entries. */ public class LiquidList { - private static Logger logr = Logger.getLogger(CalorieList.class.getName()); - private ArrayList liquidArrayList; + private final int DELETE_PADDING = 15; - /** - * Constructs an empty LiquidList. - */ + private static Logger logr = Logger.getLogger(CalorieList.class.getName()); + private ArrayList liquidArrayList; + private FileHandler fileHandler; + public LiquidList() { liquidArrayList = new ArrayList<>(); + fileHandler = new FileHandler("data/liquidsTestData.txt"); } + /** + * Constructs an empty LiquidList. + */ + public LiquidList(String filePath) { + try { + fileHandler = new FileHandler(filePath); + liquidArrayList = fileHandler.getCalorieEntriesFromFile(); + } catch (FileNotFoundException e) { + liquidArrayList = new ArrayList<>(); + System.out.println(ErrorMessages.getFileNotFoundMessage()); + } } + /** * Retrieves the liquid entry at the specified index. * * @param index the index of the liquid entry to retrieve * @return the liquid entry at the specified index */ - public LiquidEntry getEntry(int index) { + public Entry getEntry(int index) { assert index >= 0 && index < liquidArrayList.size() : "Index out of bounds"; return liquidArrayList.get(index); } @@ -68,8 +84,9 @@ public void deleteEntry(String line) { */ public void addEntry(String input) { try { - LiquidEntry newEntry = ParserLiquid.parseLiquidInput(input); + Entry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); + fileHandler.writeEntries(liquidArrayList); addEntryMessage(); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); @@ -86,10 +103,8 @@ public void printLiquidList() { } else { listHeader(); for (int i = 0; i < liquidArrayList.size(); i++) { - LiquidEntry entry = liquidArrayList.get(i); - Beverage beverage = entry.getBeverage(); - System.out.println(WHITESPACE + (i + 1) + ". Beverage: " + beverage.getBeverage() - + ", Volume: " + beverage.getVolume()); + Entry entry = liquidArrayList.get(i); + System.out.println(WHITESPACE + (i + 1) + ". " + entry); } } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index c0cae070a5..5c3862df76 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -18,4 +18,12 @@ public static String getIncorrectCaloriesInputMessage() { public static String getIncorrectMacrosInputMessage() { return "\t Please input only positive integers into the macronutrients field!"; } + + public static String getIOExceptionMessage() { + return "\t Unable to write to file!"; + } + + public static String getFileNotFoundMessage() { + return "\t No file found! The initialised list will be empty."; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 8435182589..3af9693492 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -1,6 +1,5 @@ package seedu.lifetrack.system.parser; -import seedu.lifetrack.calories.calorielist.Entry; import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.calories.Activity; @@ -15,6 +14,8 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMissingKeywordsMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; +import seedu.lifetrack.Entry; + public class ParserCalories { diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 995d2b0743..246e3f3319 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -1,6 +1,6 @@ package seedu.lifetrack.system.parser; -import seedu.lifetrack.liquids.Beverage; +import seedu.lifetrack.Entry; import seedu.lifetrack.liquids.liquidlist.LiquidEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; @@ -19,7 +19,7 @@ public class ParserLiquid { * @throws InvalidInputException if the input string is missing components or * contains empty fields */ - public static LiquidEntry parseLiquidInput(String input) throws InvalidInputException { + public static Entry parseLiquidInput(String input) throws InvalidInputException { //get index for b/ and v/ inputs int beverageIndex = input.indexOf("b/"); int volumeIndex = input.indexOf("v/"); @@ -74,11 +74,10 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce return getNewLiquidEntry(volume, beverageName); } - private static LiquidEntry getNewLiquidEntry(int volume, String name) throws InvalidInputException { + private static Entry getNewLiquidEntry(int volume, String name) throws InvalidInputException { //create objects for Beverage - Beverage liquidToAdd = new Beverage(name, volume); //create Object Entry to be returned - return new LiquidEntry(liquidToAdd); + return new LiquidEntry(name, volume, "111111"); } } diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java new file mode 100644 index 0000000000..5eaff11370 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -0,0 +1,77 @@ +package seedu.lifetrack.system.storage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +import seedu.lifetrack.Entry; +import seedu.lifetrack.calories.Food; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; +import seedu.lifetrack.system.exceptions.ErrorMessages; + +public class FileHandler { + + //calorie list constants + private static final int DATE_INDEX = 0; + private static final int DESCRIPTION_INDEX = 1; + private static final int ENTRY_TYPE_INDEX = 2; + private static final int CALORIES_INDEX = 3; + private static final int CARBOHYDRATES_INDEX = 4; + private static final int PROTEINS_INDEX = 5; + private static final int FATS_INDEX = 6; + + private String filePath; + + public FileHandler(String filePath) { + this.filePath = filePath; + } + + private void writeToFile(String textToAdd) throws IOException { + FileWriter fw = new FileWriter(filePath); + fw.write(textToAdd); + fw.close(); + } + + public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList entries = new ArrayList<>(); + String line = ""; + while (s.hasNext()) { + line = s.nextLine(); + String[] words = line.split(";"); + String date = words[DATE_INDEX]; + String description = words[DESCRIPTION_INDEX]; + int calories = Integer.parseInt(words[CALORIES_INDEX]); + String entryType = words[ENTRY_TYPE_INDEX]; + if (entryType.equals("C_IN") && words.length == 5) { + int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); + int proteins = Integer.parseInt(words[PROTEINS_INDEX]); + int fats = Integer.parseInt(words[FATS_INDEX]); + Food food = new Food(carbohydrates, proteins, fats); + entries.add(new InputEntry(description, calories, date, food)); + } else if (entryType.equals("C_IN")) { + entries.add(new InputEntry(description, calories, date)); + } else { + entries.add(new OutputEntry(description, calories, date)); + } + } + return entries; + } + + public void writeEntries(ArrayList entries) { + try { + String newData = ""; + for (Entry entry : entries) { + newData += entry.toFileFriendlyString() + System.lineSeparator(); + } + writeToFile(newData); + } catch (IOException e) { + System.out.println(ErrorMessages.getIOExceptionMessage()); + } + } +} diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 918a3f8057..a29eee8d06 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -1,6 +1,6 @@ package seedu.lifetrack.ui; -import seedu.lifetrack.calories.calorielist.Entry; +import seedu.lifetrack.Entry; public class CalorieListUi { diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 9fe7fa121c..55d7be88a9 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -7,7 +7,8 @@ import java.io.PrintStream; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.calories.calorielist.Entry; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; public class CalorieListTest { @@ -26,8 +27,8 @@ public void addEntry_validInput_entryAdded() { // Verify that the entry has been added to the list assertEquals(2, calorieList.getSize()); - Entry firstEntry = calorieList.getEntry(0); - Entry secondEntry = calorieList.getEntry(1); + InputEntry firstEntry = (InputEntry)calorieList.getEntry(0); + OutputEntry secondEntry = (OutputEntry)calorieList.getEntry(1); // Check calories intake entry assertEquals("2024-03-14", firstEntry.getDate()); @@ -129,4 +130,4 @@ public void testPrintCalorieListMultipleEntries() { assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); } -} +} \ No newline at end of file diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index 2cbac4f883..760957bb8a 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -55,7 +55,7 @@ public void testPrintLiquidListNonEmpty() { System.setOut(System.out); String expectedOutput = WHITESPACE+ "Beverage has been successfully added" + lineSeparator + WHITESPACE+ "Your liquid List:" + lineSeparator + - WHITESPACE + "1. Beverage: Milo, Volume: 200" + lineSeparator; + WHITESPACE + "1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -74,9 +74,9 @@ public void testPrintLiquidListMultipleEntries() { WHITESPACE + "Beverage has been successfully added" + lineSeparator + WHITESPACE + "Beverage has been successfully added" + lineSeparator + WHITESPACE + "Your liquid List:" + lineSeparator + - WHITESPACE + "1. Beverage: Milo, Volume: 200" + lineSeparator + - WHITESPACE + "2. Beverage: Water, Volume: 300" + lineSeparator + - WHITESPACE + "3. Beverage: Juice, Volume: 150" + lineSeparator; + WHITESPACE + "1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator + + WHITESPACE + "2. \t Date: 111111, Description: Water, Volume: 300" + lineSeparator + + WHITESPACE + "3. \t Date: 111111, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, liquidList.getSize()); } diff --git a/src/test/java/seedu/lifetrack/UITest.java b/src/test/java/seedu/lifetrack/UITest.java index 7965e0143a..ac7dab82d3 100644 --- a/src/test/java/seedu/lifetrack/UITest.java +++ b/src/test/java/seedu/lifetrack/UITest.java @@ -1,5 +1,5 @@ package seedu.lifetrack; - +/* import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,3 +36,4 @@ public void handleUserInput_inputBye_printByeMessage() { assertEquals("", outContent.toString()); } } +*/ \ No newline at end of file From 7b829543f7e7fadf1b99f0680da40ffab11c2680 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 27 Mar 2024 11:25:15 +0800 Subject: [PATCH 112/414] Add custom Hydration exception message for missing keywords and incorrect order --- .../exceptions/InvalidInputExceptionMessage.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 3301e21c7a..e5d1fd5742 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -9,6 +9,8 @@ public class InvalidInputExceptionMessage { "c/INTEGER_CALORIES date/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; + private static final String HYDRATION_IN_INPUT = "\t Example input: liquids in b/Milo v/1000" ; + public static String getIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; return HEADER + message + CALORIES_IN_INPUT; @@ -37,4 +39,14 @@ public static String getMacrosInCaloriesOutMessage() { String message = "\t Calorie output entry cannot have macros!\n"; return HEADER + message + CALORIES_OUT_INPUT; } + + public static String getHydrationMissingKeywordMessage() { + String message = "\t Please ensure that you have entered all keywords!\n"; + return HEADER + message + HYDRATION_IN_INPUT; + } + + public static String getHydrationIncorrectOrderMessage() { + String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; + return HEADER + message + HYDRATION_IN_INPUT; + } } From 0ea22e40d5b8d4e5bb351c67e5a104cf029809d2 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 27 Mar 2024 11:26:49 +0800 Subject: [PATCH 113/414] Edit exception in class ParserLiquid to use getHydrationMissingKeywordMessage() --- .../java/seedu/lifetrack/system/parser/ParserLiquid.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 995d2b0743..f4c1d0dee7 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -3,6 +3,7 @@ import seedu.lifetrack.liquids.Beverage; import seedu.lifetrack.liquids.liquidlist.LiquidEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage; public class ParserLiquid { @@ -26,9 +27,7 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce // Handle exception when b/ or v/ not entered if (beverageIndex == -1 || volumeIndex == -1) { - throw new InvalidInputException("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000"); + throw new InvalidInputException(InvalidInputExceptionMessage.getHydrationMissingKeywordMessage()); } assert (beverageIndex != -1 || volumeIndex != -1) : "ensures that beverage and volume has been keyed in"; From 4575b0d79ce13efb47d80f3059ac26c5d7e26e47 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 27 Mar 2024 11:27:14 +0800 Subject: [PATCH 114/414] Update test in ParserLiquidTest to follow exception messages --- .../seedu/lifetrack/ParserLiquidTest.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index ace58e989a..81c82bfaa6 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -18,9 +18,10 @@ public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown try { parseLiquidInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + } } @@ -33,9 +34,10 @@ public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() try { parseLiquidInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + } } @@ -48,9 +50,9 @@ public void parseLiquidInput_inputMissingBeverage_invalidInputExceptionThrown() try { parseLiquidInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); } } @@ -63,9 +65,10 @@ public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { try { parseLiquidInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + } } @@ -118,9 +121,9 @@ public void parseLiquidInput_missingKeywords_exceptionThrown() { try { parseLiquidInput("liquids in"); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); } } @@ -129,9 +132,10 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { try { parseLiquidInput("liquids in b/Milo"); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ and v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + } } } From c3287bf75e7fd0a4c2494f5621f932f10ce9a80e Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 27 Mar 2024 18:22:39 +0800 Subject: [PATCH 115/414] Update exception message for getHydrationIncorrectOrder Update JUnit test also --- .../system/exceptions/InvalidInputExceptionMessage.java | 2 +- .../java/seedu/lifetrack/system/parser/ParserLiquid.java | 4 +--- src/test/java/seedu/lifetrack/ParserLiquidTest.java | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index e5d1fd5742..ea72ec21ed 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -46,7 +46,7 @@ public static String getHydrationMissingKeywordMessage() { } public static String getHydrationIncorrectOrderMessage() { - String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; + String message = "\t Please ensure that you have keyed the input in the correct order!\n"; return HEADER + message + HYDRATION_IN_INPUT; } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index f4c1d0dee7..3f633db80b 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -34,9 +34,7 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce //Handle exception when order of b/ and v/ is incorrect if (volumeIndex < beverageIndex) { - throw new InvalidInputException("Invalid input exception: " + - "Please ensure that you have entered b/ before v/\n" + - "For example: liquids in b/Milo v/1000"); + throw new InvalidInputException(InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage()); } diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 81c82bfaa6..a9b50f445b 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -81,9 +81,9 @@ public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExc try { parseLiquidInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid input exception: " + - "Please ensure that you have entered b/ before v/\n" + - "For example: liquids in b/Milo v/1000", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that you have keyed the input in the correct order!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); } } From 8380f79386f4ba3a811f7e4dce9d3c18debea1b9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 27 Mar 2024 18:36:16 +0800 Subject: [PATCH 116/414] Update exception message for empty description or empty volume --- .../InvalidInputExceptionMessage.java | 5 ++++ .../lifetrack/system/parser/ParserLiquid.java | 8 +++---- .../seedu/lifetrack/ParserLiquidTest.java | 23 +++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index ea72ec21ed..170d977417 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -49,4 +49,9 @@ public static String getHydrationIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed the input in the correct order!\n"; return HEADER + message + HYDRATION_IN_INPUT; } + + public static String getHydrationEmptyDescriptionMessage() { + String message = "\t Please ensure that beverage and volume is not empty!\n"; + return HEADER + message + HYDRATION_IN_INPUT; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 3f633db80b..cf0403e2f9 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -37,12 +37,12 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce throw new InvalidInputException(InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage()); } - // splits string according to b/ and v/ keywords String[] parts = input.split("b/|v/"); - // parts length less than 3 means that not all split keywords were keyed in + + //ensures that v/DESCRIPTION is not empty if (parts.length < 3) { - throw new InvalidInputException("Please ensure that you have keyed in the correct format!"); + throw new InvalidInputException(InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage()); } // extracts beverage name and quantity portion from input @@ -51,7 +51,7 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce // ensures that both inputs are not empty if (beverageName.isEmpty() || strVolume.isEmpty()) { - throw new InvalidInputException("Please ensure that you have keyed in the correct format!"); + throw new InvalidInputException(InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage()); } int volume; diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index a9b50f445b..60c2f4cd9e 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -138,4 +138,27 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { } } + + @Test + public void parseLiquidInput_emptyBeverageName_exceptionThrown() { + try { + parseLiquidInput("liquids in b/ v/1000"); + } catch (InvalidInputException e) { + assertEquals("\t Invalid input!\n" + + "\t Please ensure that beverage and volume is not empty!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + + } + } + @Test + public void parseLiquidInput_emptyVolumeDescription_exceptionThrown() { + try { + parseLiquidInput("liquids in b/Milo v/ "); + } catch (InvalidInputException e) { + assertEquals("\t Invalid input!\n" + + "\t Please ensure that beverage and volume is not empty!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + + } + } } From 539fcf2a71f3acafd61cd5da7fd6fea52dd3f853 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 27 Mar 2024 18:40:10 +0800 Subject: [PATCH 117/414] Add exception message for non positive integer value of volume --- .../exceptions/InvalidInputExceptionMessage.java | 5 +++++ .../seedu/lifetrack/system/parser/ParserLiquid.java | 6 ++---- src/test/java/seedu/lifetrack/ParserLiquidTest.java | 10 ++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 170d977417..8553d0ad8c 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -54,4 +54,9 @@ public static String getHydrationEmptyDescriptionMessage() { String message = "\t Please ensure that beverage and volume is not empty!\n"; return HEADER + message + HYDRATION_IN_INPUT; } + + public static String getHydrationNonIntegerVolumeMessage() { + String message = "\t Please ensure that positive integer value is keyed in for volume!\n"; + return HEADER + message + HYDRATION_IN_INPUT; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index cf0403e2f9..fa007146a6 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -59,14 +59,12 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce try { volume = Integer.parseInt(strVolume); } catch(NumberFormatException e) { - throw new InvalidInputException("Invalid input Exception: " + - "Please enter a positive integer value for volume"); + throw new InvalidInputException(InvalidInputExceptionMessage.getHydrationNonIntegerVolumeMessage()); } // Handle exception when negative values are keyed in for volume if (volume <= 0) { - throw new InvalidInputException("Invalid input Exception: " + - "Please enter a positive integer value for volume"); + throw new InvalidInputException(InvalidInputExceptionMessage.getHydrationNonIntegerVolumeMessage()); } return getNewLiquidEntry(volume, beverageName); } diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 60c2f4cd9e..5d5424afea 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -96,8 +96,9 @@ public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputException try { parseLiquidInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid input Exception: " + - "Please enter a positive integer value for volume", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that positive integer value is keyed in for volume!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); } } @@ -110,8 +111,9 @@ public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionTh try { parseLiquidInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid input Exception: " + - "Please enter a positive integer value for volume", e.getMessage()); + assertEquals("\t Invalid input!\n" + + "\t Please ensure that positive integer value is keyed in for volume!\n" + + "\t Example input: liquids in b/Milo v/1000", e.getMessage()); } } From f7ba1f029e74930ef7ba082374d00a0af9ef3948 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 21:22:35 +0800 Subject: [PATCH 118/414] edit gitignore to ignore db files --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4a8db972fd..1c2255e49c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ src/main/resources/docs/ bin/ /text-ui-test/ACTUAL.TXT -text-ui-test/EXPECTED-UNIX.TXT \ No newline at end of file +text-ui-test/EXPECTED-UNIX.TXT + +data/* \ No newline at end of file From d4903082608046dc36853927a70d31e79b5ba468 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 21:39:24 +0800 Subject: [PATCH 119/414] fix different numbers of whitespace being printed on JUnit test vs reality --- .../calories/calorielist/CalorieList.java | 2 ++ .../liquids/liquidlist/LiquidList.java | 8 +++--- .../lifetrack/sleep/sleeplist/SleepList.java | 3 +-- .../java/seedu/lifetrack/ui/LiquidListUI.java | 12 ++++----- .../java/seedu/lifetrack/ui/SleepListUi.java | 12 ++++----- .../java/seedu/lifetrack/LiquidListTest.java | 25 +++++++++---------- .../java/seedu/lifetrack/SleepListTest.java | 25 +++++++++---------- 7 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 449263d3a9..d188aa2246 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -25,11 +25,13 @@ public class CalorieList { private ArrayList calorieArrayList; private FileHandler fileHandler; + //constructor for JUnit tests public CalorieList() { calorieArrayList = new ArrayList<>(); fileHandler = new FileHandler("data/caloriesTestData.txt"); } + //constructor for usage in terminal public CalorieList(String filePath) { try { fileHandler = new FileHandler(filePath); diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index b8ec509462..4ea05e20c7 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -17,7 +17,6 @@ import static seedu.lifetrack.ui.LiquidListUI.deleteMessage; import static seedu.lifetrack.ui.LiquidListUI.addEntryMessage; import static seedu.lifetrack.ui.LiquidListUI.emptyListMessage; -import static seedu.lifetrack.ui.LiquidListUI.WHITESPACE; import static seedu.lifetrack.ui.LiquidListUI.listHeader; /** @@ -32,14 +31,13 @@ public class LiquidList { private ArrayList liquidArrayList; private FileHandler fileHandler; + //constructor for JUnit tests public LiquidList() { liquidArrayList = new ArrayList<>(); fileHandler = new FileHandler("data/liquidsTestData.txt"); } - /** - * Constructs an empty LiquidList. - */ + //constructor for usage in terminal public LiquidList(String filePath) { try { fileHandler = new FileHandler(filePath); @@ -104,7 +102,7 @@ public void printLiquidList() { listHeader(); for (int i = 0; i < liquidArrayList.size(); i++) { Entry entry = liquidArrayList.get(i); - System.out.println(WHITESPACE + (i + 1) + ". " + entry); + System.out.println("\t " + (i + 1) + ". " + entry); } } } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index ff3e91baac..b5b6c8ce14 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -5,7 +5,6 @@ import seedu.lifetrack.system.parser.ParserSleep; import seedu.lifetrack.ui.SleepListUi; import java.util.ArrayList; -import static seedu.lifetrack.ui.SleepListUi.WHITESPACE; import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; import static seedu.lifetrack.ui.SleepListUi.deleteMessage; import static seedu.lifetrack.ui.SleepListUi.sleepListHeader; @@ -46,7 +45,7 @@ public void printSleepList() { } else { sleepListHeader(); for (int i = 0; i < this.sleepList.size(); i++) { - System.out.println(WHITESPACE + (i + 1)+ ". " + getSleep(i).toString()); + System.out.println("\t " + (i + 1)+ ". " + getSleep(i).toString()); } } } diff --git a/src/main/java/seedu/lifetrack/ui/LiquidListUI.java b/src/main/java/seedu/lifetrack/ui/LiquidListUI.java index c2c43b331e..bcf0c48df9 100644 --- a/src/main/java/seedu/lifetrack/ui/LiquidListUI.java +++ b/src/main/java/seedu/lifetrack/ui/LiquidListUI.java @@ -2,10 +2,8 @@ public class LiquidListUI { - public static final String WHITESPACE = " "; - public static void deleteMessage() { - System.out.println(WHITESPACE + "Successfully delete the liquid record."); + System.out.println("\t Successfully delete the liquid record."); } public static String deleteLogIndexMessage() { @@ -18,15 +16,15 @@ public static String deleteLogNumberMessage() { } public static void addEntryMessage() { - System.out.println(WHITESPACE + "Beverage has been successfully added"); + System.out.println("\t Beverage has been successfully added"); } public static void emptyListMessage() { - System.out.println(WHITESPACE + "Your liquid list is empty."); - System.out.println(WHITESPACE + "Populate your list with more entries :)"); + System.out.println("\t Your liquid list is empty."); + System.out.println("\t Populate your list with more entries :)"); } public static void listHeader() { - System.out.println(WHITESPACE + "Your liquid List:"); + System.out.println("\t Your liquid List:"); } } diff --git a/src/main/java/seedu/lifetrack/ui/SleepListUi.java b/src/main/java/seedu/lifetrack/ui/SleepListUi.java index 187f622396..25cf48b0fc 100644 --- a/src/main/java/seedu/lifetrack/ui/SleepListUi.java +++ b/src/main/java/seedu/lifetrack/ui/SleepListUi.java @@ -1,12 +1,12 @@ package seedu.lifetrack.ui; public class SleepListUi { - public static final String WHITESPACE = " "; + public static void addEntryMessage() { - System.out.println(WHITESPACE + "New sleep record has been successfully added."); + System.out.println("\t New sleep record has been successfully added."); } public static void deleteMessage() { - System.out.println(WHITESPACE + "Successfully delete the sleep record."); + System.out.println("\t Successfully delete the sleep record."); } public static String deleteLogNumberMessage() { return "Please enter a valid index!"; @@ -16,10 +16,10 @@ public static String deleteLogIndexMessage() { "within the size of the list."; } public static void sleepListHeader() { - System.out.println(WHITESPACE +"Your Sleep List:"); + System.out.println("\t Your Sleep List:"); } public static void emptyListMessage() { - System.out.println(WHITESPACE + "Your sleep list is empty."); - System.out.println(WHITESPACE + "Populate your list with more entries :)"); + System.out.println("\t Your sleep list is empty."); + System.out.println("\t Populate your list with more entries :)"); } } diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index 760957bb8a..fab7999261 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.lifetrack.ui.LiquidListUI.WHITESPACE; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -39,8 +38,8 @@ public void testPrintLiquidListEmpty() { LiquidList liquidList = new LiquidList(); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = WHITESPACE + "Your liquid list is empty." + lineSeparator - + WHITESPACE + "Populate your list with more entries :)" + lineSeparator; + String expectedOutput = "\t Your liquid list is empty." + lineSeparator + + "\t Populate your list with more entries :)" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -53,9 +52,9 @@ public void testPrintLiquidListNonEmpty() { liquidList.addEntry("liquids in b/Milo v/200"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = WHITESPACE+ "Beverage has been successfully added" + lineSeparator + - WHITESPACE+ "Your liquid List:" + lineSeparator + - WHITESPACE + "1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator; + String expectedOutput = "\t Beverage has been successfully added" + lineSeparator + + "\t Your liquid List:" + lineSeparator + + "\t 1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -70,13 +69,13 @@ public void testPrintLiquidListMultipleEntries() { liquidList.addEntry("liquids in b/Juice v/150"); liquidList.printLiquidList(); System.setOut(System.out); - String expectedOutput = WHITESPACE + "Beverage has been successfully added" + lineSeparator + - WHITESPACE + "Beverage has been successfully added" + lineSeparator + - WHITESPACE + "Beverage has been successfully added" + lineSeparator + - WHITESPACE + "Your liquid List:" + lineSeparator + - WHITESPACE + "1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator + - WHITESPACE + "2. \t Date: 111111, Description: Water, Volume: 300" + lineSeparator + - WHITESPACE + "3. \t Date: 111111, Description: Juice, Volume: 150" + lineSeparator; + String expectedOutput = "\t Beverage has been successfully added" + lineSeparator + + "\t Beverage has been successfully added" + lineSeparator + + "\t Beverage has been successfully added" + lineSeparator + + "\t Your liquid List:" + lineSeparator + + "\t 1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator + + "\t 2. \t Date: 111111, Description: Water, Volume: 300" + lineSeparator + + "\t 3. \t Date: 111111, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, liquidList.getSize()); } diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index 366eefc9f3..7b6329c815 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -7,7 +7,6 @@ import java.io.PrintStream; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.lifetrack.ui.LiquidListUI.WHITESPACE; public class SleepListTest { @Test @@ -37,8 +36,8 @@ public void testPrintLiquidListEmpty() { SleepList sleepList = new SleepList(); sleepList.printSleepList(); System.setOut(System.out); - String expectedOutput = WHITESPACE + "Your sleep list is empty." + lineSeparator - + WHITESPACE + "Populate your list with more entries :)" + lineSeparator; + String expectedOutput = "\t Your sleep list is empty." + lineSeparator + + "\t Populate your list with more entries :)" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @Test @@ -50,9 +49,9 @@ public void testPrintSleepListNonEmpty() { sleepList.addSleep("sleep add 7.5 d/110324"); sleepList.printSleepList(); System.setOut(System.out); - String expectedOutput = WHITESPACE+ "New sleep record has been successfully added." + lineSeparator + - WHITESPACE+ "Your Sleep List:" + lineSeparator + - WHITESPACE + "1. Date: 110324, Duration: 7.5 hours" + lineSeparator; + String expectedOutput = "\t New sleep record has been successfully added." + lineSeparator + + "\t Your Sleep List:" + lineSeparator + + "\t 1. Date: 110324, Duration: 7.5 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -67,13 +66,13 @@ public void testPrintSleepListMultipleEntries() { sleepList.addSleep("sleep add 4.2"); sleepList.printSleepList(); System.setOut(System.out); - String expectedOutput = WHITESPACE + "New sleep record has been successfully added." + lineSeparator + - WHITESPACE + "New sleep record has been successfully added." + lineSeparator + - WHITESPACE + "New sleep record has been successfully added." + lineSeparator + - WHITESPACE + "Your Sleep List:" + lineSeparator + - WHITESPACE + "1. Date: 110324, Duration: 7.5 hours" + lineSeparator + - WHITESPACE + "2. Date: 280524, Duration: 8.0 hours" + lineSeparator + - WHITESPACE + "3. Date: N/A, Duration: 4.2 hours" + lineSeparator; + String expectedOutput = "\t New sleep record has been successfully added." + lineSeparator + + "\t New sleep record has been successfully added." + lineSeparator + + "\t New sleep record has been successfully added." + lineSeparator + + "\t Your Sleep List:" + lineSeparator + + "\t 1. Date: 110324, Duration: 7.5 hours" + lineSeparator + + "\t 2. Date: 280524, Duration: 8.0 hours" + lineSeparator + + "\t 3. Date: N/A, Duration: 4.2 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, sleepList.getSize()); } From 0203362a8eddbc5a9361881663813ad20c8d834b Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 22:08:55 +0800 Subject: [PATCH 120/414] change Sleep class to SleepEntry which extends from Entry class, add storage functionality for sleep and liquids --- src/main/java/seedu/lifetrack/LifeTrack.java | 2 +- .../liquids/liquidlist/LiquidEntry.java | 4 ++ .../liquids/liquidlist/LiquidList.java | 5 +- .../{Sleep.java => sleeplist/SleepEntry.java} | 15 +++- .../lifetrack/sleep/sleeplist/SleepList.java | 44 +++++++++--- .../lifetrack/system/parser/ParserSleep.java | 7 +- .../lifetrack/system/storage/FileHandler.java | 72 ++++++++++++++++--- 7 files changed, 121 insertions(+), 28 deletions(-) rename src/main/java/seedu/lifetrack/sleep/{Sleep.java => sleeplist/SleepEntry.java} (70%) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 968e1a2383..291120653f 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -17,7 +17,7 @@ public static void setup() { new File("data/").mkdir(); calorieList = new CalorieList("data/caloriesData.txt"); liquidList = new LiquidList("data/liquidsData.txt"); - sleepList = new SleepList(); + sleepList = new SleepList("data/sleepData.txt"); } /** diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java index 273964b276..803695548c 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java @@ -18,4 +18,8 @@ public int getVolume() { public String toString() { return String.format(super.toString() + ", Volume: " + volume); } + + public String toFileFriendlyString() { + return String.format(super.toFileFriendlyString() + ";" + volume); + } } diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 4ea05e20c7..517f115f2c 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -41,11 +41,12 @@ public LiquidList() { public LiquidList(String filePath) { try { fileHandler = new FileHandler(filePath); - liquidArrayList = fileHandler.getCalorieEntriesFromFile(); + liquidArrayList = fileHandler.getLiquidEntriesFromFile(); } catch (FileNotFoundException e) { liquidArrayList = new ArrayList<>(); System.out.println(ErrorMessages.getFileNotFoundMessage()); - } } + } + } /** * Retrieves the liquid entry at the specified index. diff --git a/src/main/java/seedu/lifetrack/sleep/Sleep.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java similarity index 70% rename from src/main/java/seedu/lifetrack/sleep/Sleep.java rename to src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 5b56292fc3..bd79363bfa 100644 --- a/src/main/java/seedu/lifetrack/sleep/Sleep.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -1,6 +1,9 @@ -package seedu.lifetrack.sleep; +package seedu.lifetrack.sleep.sleeplist; + +import seedu.lifetrack.Entry; + +public class SleepEntry extends Entry { -public class Sleep { private String date; private double duration; @@ -10,7 +13,8 @@ public class Sleep { * @param date * @param duration */ - public Sleep (String date, double duration){ + public SleepEntry (double duration, String date){ + super("SLEEP", date); this.date = date.isEmpty() ? "N/A" : date; this.duration = duration; } @@ -22,9 +26,14 @@ public String getDate() { public double getDuration() { return duration; } + public String toString() { // Show "N/A" if no date was provided return "Date: " + (date == null || date.isEmpty() ? "N/A" : date) + ", Duration: " + String.format("%.1f", duration) + " hours"; } + + public String toFileFriendlyString() { + return String.format(super.toFileFriendlyString() + ";" + duration); + } } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index b5b6c8ce14..668090f13b 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -1,57 +1,79 @@ package seedu.lifetrack.sleep.sleeplist; -import seedu.lifetrack.sleep.Sleep; +import seedu.lifetrack.Entry; +import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserSleep; +import seedu.lifetrack.system.storage.FileHandler; import seedu.lifetrack.ui.SleepListUi; + +import java.io.FileNotFoundException; import java.util.ArrayList; import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; -import static seedu.lifetrack.ui.SleepListUi.deleteMessage; -import static seedu.lifetrack.ui.SleepListUi.sleepListHeader; public class SleepList { - private ArrayList sleepList; + + private ArrayList sleepList; + private FileHandler fileHandler; + + //constructor for JUnit tests public SleepList() { - this.sleepList=new ArrayList<>(); + sleepList = new ArrayList<>(); + fileHandler = new FileHandler("data/sleepTestData.txt"); + } + + //constructor for usage in terminal + public SleepList(String filePath) { + try { + fileHandler = new FileHandler(filePath); + sleepList = fileHandler.getSleepEntriesFromFile(); + } catch (FileNotFoundException e) { + sleepList = new ArrayList<>(); + System.out.println(ErrorMessages.getFileNotFoundMessage()); + } } - public Sleep getSleep(int index) { + public Entry getSleep(int index) { assert index >= 0 && index < sleepList.size() : "Index out of bounds"; return sleepList.get(index); } + public void addSleep(String input) { try { - Sleep newSleep = ParserSleep.parseSleepInput(input); + SleepEntry newSleep = ParserSleep.parseSleepInput(input); sleepList.add(newSleep); + fileHandler.writeEntries(sleepList); SleepListUi.addEntryMessage(); } catch (InvalidInputException e) { System.out.println(getIncorrectSleepInputMessage()); } } + public void deleteSleep(String line) { try { int index = Integer.parseInt(line.split(" ")[2]) ; sleepList.remove(index - 1); - deleteMessage(); + fileHandler.writeEntries(sleepList); + SleepListUi.deleteMessage(); } catch (IndexOutOfBoundsException e) { System.out.println(SleepListUi.deleteLogIndexMessage()); } catch (NumberFormatException e) { System.out.println(SleepListUi.deleteLogNumberMessage()); } } + public void printSleepList() { if (this.sleepList.isEmpty()) { SleepListUi.emptyListMessage(); } else { - sleepListHeader(); + SleepListUi.sleepListHeader(); for (int i = 0; i < this.sleepList.size(); i++) { System.out.println("\t " + (i + 1)+ ". " + getSleep(i).toString()); } } } + public int getSize() { return sleepList.size(); } - - } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 445fd9d512..653a3b07d0 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -1,13 +1,14 @@ package seedu.lifetrack.system.parser; -import seedu.lifetrack.sleep.Sleep; import seedu.lifetrack.system.exceptions.InvalidInputException; import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepDateInputMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; +import seedu.lifetrack.sleep.sleeplist.SleepEntry; + public class ParserSleep { - public static Sleep parseSleepInput(String input) throws InvalidInputException { + public static SleepEntry parseSleepInput(String input) throws InvalidInputException { try { String date = "N/A"; // Default if no date is provided double duration = 0; @@ -29,7 +30,7 @@ public static Sleep parseSleepInput(String input) throws InvalidInputException { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + "sleep add d/"); } - return new Sleep(date, duration); + return new SleepEntry(duration, date); } catch (NumberFormatException e) { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + "sleep add d/"); diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 5eaff11370..9af077c7d3 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -11,6 +11,8 @@ import seedu.lifetrack.calories.Food; import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; +import seedu.lifetrack.liquids.liquidlist.LiquidEntry; +import seedu.lifetrack.sleep.sleeplist.SleepEntry; import seedu.lifetrack.system.exceptions.ErrorMessages; public class FileHandler { @@ -36,6 +38,18 @@ private void writeToFile(String textToAdd) throws IOException { fw.close(); } + public void writeEntries(ArrayList entries) { + try { + String newData = ""; + for (Entry entry : entries) { + newData += entry.toFileFriendlyString() + System.lineSeparator(); + } + writeToFile(newData); + } catch (IOException e) { + System.out.println(ErrorMessages.getIOExceptionMessage()); + } + } + public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); @@ -63,15 +77,57 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException return entries; } - public void writeEntries(ArrayList entries) { - try { - String newData = ""; - for (Entry entry : entries) { - newData += entry.toFileFriendlyString() + System.lineSeparator(); + public ArrayList getLiquidEntriesFromFile() throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList entries = new ArrayList<>(); + String line = ""; + while (s.hasNext()) { + line = s.nextLine(); + String[] words = line.split(";"); + String date = words[DATE_INDEX]; + String description = words[DESCRIPTION_INDEX]; + int calories = Integer.parseInt(words[CALORIES_INDEX]); + String entryType = words[ENTRY_TYPE_INDEX]; + if (entryType.equals("C_IN") && words.length == 5) { + int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); + int proteins = Integer.parseInt(words[PROTEINS_INDEX]); + int fats = Integer.parseInt(words[FATS_INDEX]); + Food food = new Food(carbohydrates, proteins, fats); + entries.add(new InputEntry(description, calories, date, food)); + } else if (entryType.equals("C_IN")) { + entries.add(new InputEntry(description, calories, date)); + } else { + entries.add(new OutputEntry(description, calories, date)); + } + } + return entries; + } + + public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList entries = new ArrayList<>(); + String line = ""; + while (s.hasNext()) { + line = s.nextLine(); + String[] words = line.split(";"); + String date = words[DATE_INDEX]; + String description = words[DESCRIPTION_INDEX]; + int calories = Integer.parseInt(words[CALORIES_INDEX]); + String entryType = words[ENTRY_TYPE_INDEX]; + if (entryType.equals("C_IN") && words.length == 5) { + int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); + int proteins = Integer.parseInt(words[PROTEINS_INDEX]); + int fats = Integer.parseInt(words[FATS_INDEX]); + Food food = new Food(carbohydrates, proteins, fats); + entries.add(new InputEntry(description, calories, date, food)); + } else if (entryType.equals("C_IN")) { + entries.add(new InputEntry(description, calories, date)); + } else { + entries.add(new OutputEntry(description, calories, date)); } - writeToFile(newData); - } catch (IOException e) { - System.out.println(ErrorMessages.getIOExceptionMessage()); } + return entries; } } From e55c0f6e9988fad2f2ec3b6f2a358aba30782746 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 22:18:21 +0800 Subject: [PATCH 121/414] add functionality to read from existing text files for sleep and liquids --- .../lifetrack/sleep/sleeplist/SleepEntry.java | 2 +- .../lifetrack/sleep/sleeplist/SleepList.java | 5 ++- .../lifetrack/system/storage/FileHandler.java | 41 ++++++------------- .../java/seedu/lifetrack/SleepListTest.java | 8 ++-- 4 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index bd79363bfa..4366164059 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -29,7 +29,7 @@ public double getDuration() { public String toString() { // Show "N/A" if no date was provided - return "Date: " + (date == null || date.isEmpty() ? "N/A" : date) + + return "\t Date: " + (date == null || date.isEmpty() ? "N/A" : date) + ", Duration: " + String.format("%.1f", duration) + " hours"; } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 668090f13b..9d9b2dcdba 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -67,8 +67,9 @@ public void printSleepList() { SleepListUi.emptyListMessage(); } else { SleepListUi.sleepListHeader(); - for (int i = 0; i < this.sleepList.size(); i++) { - System.out.println("\t " + (i + 1)+ ". " + getSleep(i).toString()); + for (int i = 0; i < sleepList.size(); i++) { + Entry entry = sleepList.get(i); + System.out.println("\t " + (i + 1) + ". " + entry); } } } diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 9af077c7d3..37229b314b 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -17,15 +17,23 @@ public class FileHandler { - //calorie list constants + //general list constants private static final int DATE_INDEX = 0; private static final int DESCRIPTION_INDEX = 1; + + //calorie list constants private static final int ENTRY_TYPE_INDEX = 2; private static final int CALORIES_INDEX = 3; private static final int CARBOHYDRATES_INDEX = 4; private static final int PROTEINS_INDEX = 5; private static final int FATS_INDEX = 6; + //liquids list constants + private static final int VOLUME_INDEX = 2; + + //sleep list constants + private static final int DURATION_INDEX = 2; + private String filePath; public FileHandler(String filePath) { @@ -87,19 +95,8 @@ public ArrayList getLiquidEntriesFromFile() throws FileNotFoundException String[] words = line.split(";"); String date = words[DATE_INDEX]; String description = words[DESCRIPTION_INDEX]; - int calories = Integer.parseInt(words[CALORIES_INDEX]); - String entryType = words[ENTRY_TYPE_INDEX]; - if (entryType.equals("C_IN") && words.length == 5) { - int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); - int proteins = Integer.parseInt(words[PROTEINS_INDEX]); - int fats = Integer.parseInt(words[FATS_INDEX]); - Food food = new Food(carbohydrates, proteins, fats); - entries.add(new InputEntry(description, calories, date, food)); - } else if (entryType.equals("C_IN")) { - entries.add(new InputEntry(description, calories, date)); - } else { - entries.add(new OutputEntry(description, calories, date)); - } + int volume = Integer.parseInt(words[VOLUME_INDEX]); + entries.add(new LiquidEntry(description, volume, date)); } return entries; } @@ -113,20 +110,8 @@ public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { line = s.nextLine(); String[] words = line.split(";"); String date = words[DATE_INDEX]; - String description = words[DESCRIPTION_INDEX]; - int calories = Integer.parseInt(words[CALORIES_INDEX]); - String entryType = words[ENTRY_TYPE_INDEX]; - if (entryType.equals("C_IN") && words.length == 5) { - int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); - int proteins = Integer.parseInt(words[PROTEINS_INDEX]); - int fats = Integer.parseInt(words[FATS_INDEX]); - Food food = new Food(carbohydrates, proteins, fats); - entries.add(new InputEntry(description, calories, date, food)); - } else if (entryType.equals("C_IN")) { - entries.add(new InputEntry(description, calories, date)); - } else { - entries.add(new OutputEntry(description, calories, date)); - } + double duration = Double.parseDouble(words[DURATION_INDEX]); + entries.add(new SleepEntry(duration, date)); } return entries; } diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index 7b6329c815..5886a88b46 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -51,7 +51,7 @@ public void testPrintSleepListNonEmpty() { System.setOut(System.out); String expectedOutput = "\t New sleep record has been successfully added." + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. Date: 110324, Duration: 7.5 hours" + lineSeparator; + "\t 1. \t Date: 110324, Duration: 7.5 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -70,9 +70,9 @@ public void testPrintSleepListMultipleEntries() { "\t New sleep record has been successfully added." + lineSeparator + "\t New sleep record has been successfully added." + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. Date: 110324, Duration: 7.5 hours" + lineSeparator + - "\t 2. Date: 280524, Duration: 8.0 hours" + lineSeparator + - "\t 3. Date: N/A, Duration: 4.2 hours" + lineSeparator; + "\t 1. \t Date: 110324, Duration: 7.5 hours" + lineSeparator + + "\t 2. \t Date: 280524, Duration: 8.0 hours" + lineSeparator + + "\t 3. \t Date: N/A, Duration: 4.2 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, sleepList.getSize()); } From e8c75bff12023780eaa5549f2f043b9bc4ed2701 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 23:02:48 +0800 Subject: [PATCH 122/414] add some DG details for ParserCalories --- docs/DeveloperGuide.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..6afb84df3c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -5,9 +5,20 @@ {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} ## Design & implementation +### Parsing user input for caloric entries -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +This functionality is facilitated by `ParserCalories`. It implements one operation, namely: +- `ParserCalories#parseCaloriesInput(String)` +This operation is exposed in the `CalorieList` class as `CalorieList#addEntry(String)`. + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `calories in burger c/200 date/270324` in the terminal, +the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalories#parseCaloriesInput(String)`. + +- Step 2: Using `String.split()`, the method extracts information such as the description, number of calories, and date of entry. The obtained information is sent to the private method `ParserCalories#makeNewInputEntry(String, int, String)` to create a new entry of class `InputEntry` that extends `Entry`. + +- Step 3: The created `InputEntry` instance is added into the `ArrayList` attribute of the `CalorieList`. ## Product scope ### Target user profile From 992a90671f95165454022b6b8cb94f3a901e895c Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 23:15:59 +0800 Subject: [PATCH 123/414] fix checkstyle errors --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 4 ++-- .../java/seedu/lifetrack/liquids/liquidlist/LiquidList.java | 4 ++-- src/test/java/seedu/lifetrack/CalorieListTest.java | 2 +- src/test/java/seedu/lifetrack/UITest.java | 3 +-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index d188aa2246..5c115af9e8 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -19,9 +19,9 @@ public class CalorieList { - private final int SIZE_OF_DELETE = 16; - private static Logger logr = Logger.getLogger(CalorieList.class.getName()); + + private final int SIZE_OF_DELETE = 16; private ArrayList calorieArrayList; private FileHandler fileHandler; diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 517f115f2c..0a87b002db 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -25,9 +25,9 @@ */ public class LiquidList { - private final int DELETE_PADDING = 15; - private static Logger logr = Logger.getLogger(CalorieList.class.getName()); + + private final int DELETE_PADDING = 15; private ArrayList liquidArrayList; private FileHandler fileHandler; diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 55d7be88a9..61d17622af 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -130,4 +130,4 @@ public void testPrintCalorieListMultipleEntries() { assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); } -} \ No newline at end of file +} diff --git a/src/test/java/seedu/lifetrack/UITest.java b/src/test/java/seedu/lifetrack/UITest.java index ea056dcb2d..2ee545b61e 100644 --- a/src/test/java/seedu/lifetrack/UITest.java +++ b/src/test/java/seedu/lifetrack/UITest.java @@ -1,5 +1,5 @@ package seedu.lifetrack; -/* + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,4 +38,3 @@ public void handleUserInput_inputBye_printByeMessage() { assertEquals("", outContent.toString()); } } -*/ \ No newline at end of file From 283dc331e63bf9c06a04daddba344b794cde588f Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 23:45:24 +0800 Subject: [PATCH 124/414] account for absence of file storage for JUnit tests --- src/main/java/seedu/lifetrack/LifeTrack.java | 17 +++++------------ .../calories/calorielist/CalorieList.java | 10 ++++++++-- .../liquids/liquidlist/LiquidList.java | 10 ++++++++-- .../lifetrack/sleep/sleeplist/SleepList.java | 11 ++++++++--- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 291120653f..d98b9735bc 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -9,22 +9,15 @@ public class LifeTrack { - public static CalorieList calorieList; - public static LiquidList liquidList; - public static SleepList sleepList; - - public static void setup() { - new File("data/").mkdir(); - calorieList = new CalorieList("data/caloriesData.txt"); - liquidList = new LiquidList("data/liquidsData.txt"); - sleepList = new SleepList("data/sleepData.txt"); - } - + public static CalorieList calorieList = new CalorieList("data/caloriesData.txt"); + public static LiquidList liquidList = new LiquidList("data/liquidsData.txt"); + public static SleepList sleepList = new SleepList("data/sleepData.txt"); + /** * Main entry-point for the java.lifetrack.LifeTrack application. */ public static void main(String[] args) { - setup(); + new File("data/").mkdir(); Ui.sayHello(); Ui.readUserInput(calorieList,liquidList,sleepList); Ui.byeMessage(); diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 5c115af9e8..46a916f0df 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -28,7 +28,6 @@ public class CalorieList { //constructor for JUnit tests public CalorieList() { calorieArrayList = new ArrayList<>(); - fileHandler = new FileHandler("data/caloriesTestData.txt"); } //constructor for usage in terminal @@ -42,6 +41,12 @@ public CalorieList(String filePath) { } } + private void updateFile() { + if (fileHandler != null) { + fileHandler.writeEntries(calorieArrayList); + } + } + public Entry getEntry(int index) { return calorieArrayList.get(index); } @@ -57,6 +62,7 @@ public void deleteEntry(String line) { int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); Entry toDelete = calorieArrayList.get(index-1); calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 + updateFile(); successfulDeletedMessage(toDelete); } catch (IndexOutOfBoundsException e) { System.out.println(deleteLogIndexMessage()); @@ -82,7 +88,7 @@ public void addEntry(String input) { try { Entry newEntry = parseCaloriesInput(input); calorieArrayList.add(newEntry); - fileHandler.writeEntries(calorieArrayList); + updateFile(); printNewCalorieEntry(newEntry); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 0a87b002db..bf0f8c7ba6 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -34,7 +34,6 @@ public class LiquidList { //constructor for JUnit tests public LiquidList() { liquidArrayList = new ArrayList<>(); - fileHandler = new FileHandler("data/liquidsTestData.txt"); } //constructor for usage in terminal @@ -48,6 +47,12 @@ public LiquidList(String filePath) { } } + private void updateFile() { + if (fileHandler != null) { + fileHandler.writeEntries(liquidArrayList); + } + } + /** * Retrieves the liquid entry at the specified index. * @@ -68,6 +73,7 @@ public void deleteEntry(String line) { try { int index = Integer.parseInt(line.substring(DELETE_PADDING).trim()); liquidArrayList.remove(index - 1); + updateFile(); deleteMessage(); } catch (IndexOutOfBoundsException e) { System.out.println(deleteLogIndexMessage()); @@ -85,7 +91,7 @@ public void addEntry(String input) { try { Entry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); - fileHandler.writeEntries(liquidArrayList); + updateFile(); addEntryMessage(); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 9d9b2dcdba..5ec2ff161c 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -19,7 +19,6 @@ public class SleepList { //constructor for JUnit tests public SleepList() { sleepList = new ArrayList<>(); - fileHandler = new FileHandler("data/sleepTestData.txt"); } //constructor for usage in terminal @@ -33,6 +32,12 @@ public SleepList(String filePath) { } } + private void updateFile() { + if (fileHandler != null) { + fileHandler.writeEntries(sleepList); + } + } + public Entry getSleep(int index) { assert index >= 0 && index < sleepList.size() : "Index out of bounds"; return sleepList.get(index); @@ -42,7 +47,7 @@ public void addSleep(String input) { try { SleepEntry newSleep = ParserSleep.parseSleepInput(input); sleepList.add(newSleep); - fileHandler.writeEntries(sleepList); + updateFile(); SleepListUi.addEntryMessage(); } catch (InvalidInputException e) { System.out.println(getIncorrectSleepInputMessage()); @@ -53,7 +58,7 @@ public void deleteSleep(String line) { try { int index = Integer.parseInt(line.split(" ")[2]) ; sleepList.remove(index - 1); - fileHandler.writeEntries(sleepList); + updateFile(); SleepListUi.deleteMessage(); } catch (IndexOutOfBoundsException e) { System.out.println(SleepListUi.deleteLogIndexMessage()); From 100647d2897dc78790f67e830b3a291cddbd71df Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 27 Mar 2024 23:53:01 +0800 Subject: [PATCH 125/414] fix text-ui-test --- text-ui-test/EXPECTED.TXT | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 2f91cf0ff2..7110c6a8f3 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,3 +1,6 @@ + No file found! The initialised list will be empty. + No file found! The initialised list will be empty. + No file found! The initialised list will be empty. Hello from From 39f2dcd459cecb006a0184f70140ba889566af12 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Wed, 27 Mar 2024 23:56:12 +0800 Subject: [PATCH 126/414] Fix junit tests --- .../liquids/liquidlist/LiquidEntry.java | 2 - .../liquids/liquidlist/LiquidList.java | 4 +- .../InvalidInputExceptionMessage.java | 7 +-- .../lifetrack/system/parser/ParserLiquid.java | 17 +++--- src/main/java/seedu/lifetrack/ui/Ui.java | 2 +- .../seedu/lifetrack/ParserLiquidTest.java | 57 +++++++------------ 6 files changed, 36 insertions(+), 53 deletions(-) diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java index e582dd4dab..468f61f8c1 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java @@ -1,7 +1,5 @@ package seedu.lifetrack.liquids.liquidlist; -import seedu.lifetrack.liquids.Beverage; - public class LiquidEntry { private String description; private int volume; diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java index 279284cd7b..1d1e4bcb5b 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java @@ -1,7 +1,6 @@ package seedu.lifetrack.liquids.liquidlist; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.liquids.Beverage; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserLiquid; @@ -66,7 +65,8 @@ public void deleteEntry(String line) { * @param input the input string containing liquid entry information */ public void addEntry(String input) { - assert (input.startsWith("hydration add") || input.startsWith("calories out")) : "ensures that input is correct"; + assert (input.startsWith("hydration in")) + : "ensures that input is correct"; try { LiquidEntry newEntry = ParserLiquid.parseLiquidInput(input); liquidArrayList.add(newEntry); diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 9dc1b4d28a..70dcb52411 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -8,10 +8,7 @@ public class InvalidInputExceptionMessage { private static final String CALORIES_OUT_INPUT = "\t Example input: calories out DESCRIPTION " + "c/INTEGER_CALORIES date/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; - private static final String HYDRATION_ADD_INPUT = "\t Example input: hydration add DESCRIPTION " + - "v/VOLUME date/DATE "; - - private static final String HYDRATION_IN_INPUT = "\t Example input: liquids in b/Milo v/1000" ; + private static final String HYDRATION_IN_INPUT = "\t Example input: liquids in Milo v/1000 date/221024" ; public static String getIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; @@ -57,7 +54,7 @@ public static String getHydrationEmptyDescriptionMessage() { return HEADER + message + HYDRATION_IN_INPUT; } - public static String getHydrationNonIntegerVolumeMessage() { + public static String getHydrationNegativeIntegerVolumeMessage() { String message = "\t Please ensure that positive integer value is keyed in for volume!\n"; return HEADER + message + HYDRATION_IN_INPUT; } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java index 4b497edd48..a806776d02 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java @@ -4,10 +4,13 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectVolumeInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationMissingKeywordMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; public class ParserLiquid { - private static final int HYDRATION_ADD_PADDING = 13; + private static final int HYDRATION_IN_PADDING = 12; /** * Parses a string input to create a Liquid object representing liquid intake. @@ -35,7 +38,7 @@ public static LiquidEntry parseLiquidInput(String input) throws InvalidInputExce assert volumeIndex < dateIndex : "The v/ keyword must appear before date/ in the input!"; String[] parts = input.split("v/|date/"); - String command = parts[0].substring(0, HYDRATION_ADD_PADDING).trim(); + String command = parts[0].substring(0, HYDRATION_IN_PADDING).trim(); String description = getDescriptionFromInput(input, volumeIndex); String strVolume = parts[1].trim(); String date = parts[2].trim(); @@ -66,19 +69,19 @@ private static int getIntegerVolumeFromInput(String strVolume) { } private static void checkVolumeIsPositiveInteger(int volume) throws InvalidInputException { if (volume <= 0) { - throw new InvalidInputException(getIncorrectVolumeInputMessage()); + throw new InvalidInputException(getHydrationNegativeIntegerVolumeMessage()); } } private static void checkInputsAreNonEmpty(String description, String strVolume, String date) throws InvalidInputException { //check if the description, calories or date fields are empty if (description.isEmpty() || strVolume.isEmpty() || date.isEmpty()) { - throw new InvalidInputException(getHydrationWhitespaceInInputMessage()); + throw new InvalidInputException(getHydrationEmptyDescriptionMessage()); } } private static String getDescriptionFromInput(String inputString, int volumeIndex) { String description; - description = inputString.substring(HYDRATION_ADD_PADDING, volumeIndex).trim(); + description = inputString.substring(HYDRATION_IN_PADDING, volumeIndex).trim(); return description; } private static void checkKeywordsCorrectlyOrdered( int dateIndex, int volumeIndex) throws InvalidInputException { @@ -89,7 +92,7 @@ private static void checkKeywordsCorrectlyOrdered( int dateIndex, int volumeInde private static void checkKeywordsExist(int dateIndex, int volumeIndex) throws InvalidInputException { //check that v/ and date/ keywords exist in the input, else throw exception if (dateIndex == -1 || volumeIndex == -1) { - throw new InvalidInputException(getHydrationMissingKeywordsMessage()); + throw new InvalidInputException(getHydrationMissingKeywordMessage()); } } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 95e44eb7dd..74d6c92c57 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -61,7 +61,7 @@ public static void handleCaloriesInput(String line, CalorieList calorieList) { public static void handleLiquidsInput(String line, LiquidList liquidsList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("hydration add")) { + if (line.startsWith("hydration in")) { liquidsList.addEntry(line); } else if (line.startsWith("hydration list")) { liquidsList.printLiquidList(); diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserLiquidTest.java index 5d5424afea..b7bdd0cf74 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserLiquidTest.java @@ -12,7 +12,7 @@ public class ParserLiquidTest { @Test public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in b/Milo b/1000"; + String invalidInput = "liquids in Milo b/1000 b/1000"; // Call methods to test try { @@ -20,7 +20,7 @@ public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @@ -28,7 +28,7 @@ public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown @Test public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in v/Milo v/1000"; + String invalidInput = "liquids in Milo v/1000 v/1000"; // Call methods to test try { @@ -36,30 +36,15 @@ public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } - @Test - public void parseLiquidInput_inputMissingBeverage_invalidInputExceptionThrown() { - // setup test - String invalidInput = "liquids in v/1000"; - - // Call methods to test - try { - parseLiquidInput(invalidInput); - } catch (InvalidInputException e) { - assertEquals("\t Invalid input!\n" + - "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); - } - } - @Test public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in b/Milo"; + String invalidInput = "liquids in Milo date/221024"; // Call methods to test try { @@ -67,15 +52,15 @@ public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @Test - public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExceptionThrown() { + public void parseLiquidInput_inputWrongOrderDateBeforeVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in v/1000 b/milo"; + String invalidInput = "liquids in Milo date/221024 v/1000"; // Call methods to test try { @@ -83,14 +68,14 @@ public void parseLiquidInput_inputWrongOrderVolumeBeforeBeverage_invalidInputExc } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have keyed the input in the correct order!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @Test public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in b/Milo v/##s100"; + String invalidInput = "liquids in Milo v/##s100 date/221024"; // Call methods to test try { @@ -98,14 +83,14 @@ public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputException } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @Test public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in b/Milo v/-1000"; + String invalidInput = "liquids in Milo v/-1000 date/221024"; // Call methods to test try { @@ -113,7 +98,7 @@ public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionTh } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @@ -125,7 +110,7 @@ public void parseLiquidInput_missingKeywords_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @@ -136,7 +121,7 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @@ -144,22 +129,22 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { @Test public void parseLiquidInput_emptyBeverageName_exceptionThrown() { try { - parseLiquidInput("liquids in b/ v/1000"); + parseLiquidInput("liquids in v/1000"); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + - "\t Please ensure that beverage and volume is not empty!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } @Test public void parseLiquidInput_emptyVolumeDescription_exceptionThrown() { try { - parseLiquidInput("liquids in b/Milo v/ "); + parseLiquidInput("liquids in Milo v/ "); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + - "\t Please ensure that beverage and volume is not empty!\n" + - "\t Example input: liquids in b/Milo v/1000", e.getMessage()); + "\t Please ensure that you have entered all keywords!\n" + + "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); } } From 07c2e2c5fac3eb0a58a08833eed9a43f6176563b Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 00:33:28 +0800 Subject: [PATCH 127/414] fix whitespace issue with IO redirection --- .../java/seedu/lifetrack/system/exceptions/ErrorMessages.java | 4 +++- src/main/java/seedu/lifetrack/ui/Ui.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index 9c1b0e0584..14d6dbf1ac 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -2,6 +2,8 @@ public class ErrorMessages { + private static final String WHITESPACE = " "; + public static void printIndexOutOfBoundsError(){ System.out.println("\t Sorry, this index is invalid. Please enter a positive integer " + "within the size of the list."); @@ -24,7 +26,7 @@ public static String getIOExceptionMessage() { } public static String getFileNotFoundMessage() { - return "\t No file found! The initialised list will be empty."; + return WHITESPACE + "No file found! The initialised list will be empty."; } public static String getIncorrectSleepInputMessage() { diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index dc8ea71986..f9b29d71be 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -85,7 +85,7 @@ public static void handleSleepInput(String line, SleepList sleepList) { } public static void handleUserInput(String line, CalorieList calorieList, - LiquidList liquidList, SleepList sleepList) { + LiquidList liquidList, SleepList sleepList) { if (!line.startsWith("bye")) { printLine(); line = line.trim().toLowerCase(); From dbfc2e84f0e2815be472d168fc0f59da5845b0fe Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 01:47:27 +0800 Subject: [PATCH 128/414] reformat all Liquids related classes to Hydration --- src/main/java/seedu/lifetrack/LifeTrack.java | 6 +- .../calories/calorielist/CalorieList.java | 4 +- .../hydrationlist/HydrationEntry.java} | 6 +- .../hydrationlist/HydrationList.java} | 55 ++++++++--------- ...ParserLiquid.java => ParserHydration.java} | 18 ++++-- .../lifetrack/system/storage/FileHandler.java | 6 +- .../seedu/lifetrack/ui/CalorieListUi.java | 3 +- ...LiquidListUI.java => HydrationListUI.java} | 5 +- src/main/java/seedu/lifetrack/ui/Ui.java | 14 ++--- ...idListTest.java => HydrationListTest.java} | 61 ++++++++++--------- ...quidTest.java => ParserHydrationTest.java} | 44 ++++++------- src/test/java/seedu/lifetrack/UITest.java | 6 +- 12 files changed, 116 insertions(+), 112 deletions(-) rename src/main/java/seedu/lifetrack/{liquids/liquidlist/LiquidEntry.java => hydration/hydrationlist/HydrationEntry.java} (71%) rename src/main/java/seedu/lifetrack/{liquids/liquidlist/LiquidList.java => hydration/hydrationlist/HydrationList.java} (64%) rename src/main/java/seedu/lifetrack/system/parser/{ParserLiquid.java => ParserHydration.java} (92%) rename src/main/java/seedu/lifetrack/ui/{LiquidListUI.java => HydrationListUI.java} (89%) rename src/test/java/seedu/lifetrack/{LiquidListTest.java => HydrationListTest.java} (55%) rename src/test/java/seedu/lifetrack/{ParserLiquidTest.java => ParserHydrationTest.java} (73%) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index d98b9735bc..959f1cda39 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -1,7 +1,7 @@ package seedu.lifetrack; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.hydration.hydrationlist.HydrationList; import seedu.lifetrack.sleep.sleeplist.SleepList; import seedu.lifetrack.ui.Ui; @@ -10,7 +10,7 @@ public class LifeTrack { public static CalorieList calorieList = new CalorieList("data/caloriesData.txt"); - public static LiquidList liquidList = new LiquidList("data/liquidsData.txt"); + public static HydrationList hydrationList = new HydrationList("data/liquidsData.txt"); public static SleepList sleepList = new SleepList("data/sleepData.txt"); /** @@ -19,7 +19,7 @@ public class LifeTrack { public static void main(String[] args) { new File("data/").mkdir(); Ui.sayHello(); - Ui.readUserInput(calorieList,liquidList,sleepList); + Ui.readUserInput(calorieList,hydrationList,sleepList); Ui.byeMessage(); } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 46a916f0df..ae68f851f0 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -4,8 +4,8 @@ import static seedu.lifetrack.ui.CalorieListUi.successfulDeletedMessage; import static seedu.lifetrack.ui.CalorieListUi.printNewCalorieEntry; import static seedu.lifetrack.ui.CalorieListUi.calorieListHeader; -import static seedu.lifetrack.ui.LiquidListUI.deleteLogIndexMessage; -import static seedu.lifetrack.ui.LiquidListUI.deleteLogNumberMessage; +import static seedu.lifetrack.ui.CalorieListUi.deleteLogIndexMessage; +import static seedu.lifetrack.ui.CalorieListUi.deleteLogNumberMessage; import seedu.lifetrack.Entry; import seedu.lifetrack.system.exceptions.ErrorMessages; diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java similarity index 71% rename from src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java rename to src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java index 803695548c..f1adf72564 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidEntry.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java @@ -1,12 +1,12 @@ -package seedu.lifetrack.liquids.liquidlist; +package seedu.lifetrack.hydration.hydrationlist; import seedu.lifetrack.Entry; -public class LiquidEntry extends Entry { +public class HydrationEntry extends Entry { private int volume; - public LiquidEntry(String description, int volume, String date){ + public HydrationEntry(String description, int volume, String date){ super(description, date); this.volume= volume; } diff --git a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java similarity index 64% rename from src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java rename to src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index c715430837..20ea6367fb 100644 --- a/src/main/java/seedu/lifetrack/liquids/liquidlist/LiquidList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -1,10 +1,10 @@ -package seedu.lifetrack.liquids.liquidlist; +package seedu.lifetrack.hydration.hydrationlist; import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; -import seedu.lifetrack.system.parser.ParserLiquid; +import seedu.lifetrack.system.parser.ParserHydration; import seedu.lifetrack.system.storage.FileHandler; import java.io.FileNotFoundException; @@ -12,44 +12,44 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.lifetrack.ui.LiquidListUI.deleteLogIndexMessage; -import static seedu.lifetrack.ui.LiquidListUI.deleteLogNumberMessage; -import static seedu.lifetrack.ui.LiquidListUI.deleteMessage; -import static seedu.lifetrack.ui.LiquidListUI.addEntryMessage; -import static seedu.lifetrack.ui.LiquidListUI.emptyListMessage; -import static seedu.lifetrack.ui.LiquidListUI.listHeader; +import static seedu.lifetrack.ui.HydrationListUI.deleteLogIndexMessage; +import static seedu.lifetrack.ui.HydrationListUI.deleteLogNumberMessage; +import static seedu.lifetrack.ui.HydrationListUI.deleteMessage; +import static seedu.lifetrack.ui.HydrationListUI.addEntryMessage; +import static seedu.lifetrack.ui.HydrationListUI.emptyListMessage; +import static seedu.lifetrack.ui.HydrationListUI.listHeader; /** * Represents a list of liquid entries. * Provides methods to add, delete, and print liquid entries. */ -public class LiquidList { +public class HydrationList { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); private final int DELETE_PADDING = 15; - private ArrayList liquidArrayList; + private ArrayList hydrationArrayList; private FileHandler fileHandler; //constructor for JUnit tests - public LiquidList() { - liquidArrayList = new ArrayList<>(); + public HydrationList() { + hydrationArrayList = new ArrayList<>(); } //constructor for usage in terminal - public LiquidList(String filePath) { + public HydrationList(String filePath) { try { fileHandler = new FileHandler(filePath); - liquidArrayList = fileHandler.getLiquidEntriesFromFile(); + hydrationArrayList = fileHandler.getHydrationEntriesFromFile(); } catch (FileNotFoundException e) { - liquidArrayList = new ArrayList<>(); + hydrationArrayList = new ArrayList<>(); System.out.println(ErrorMessages.getFileNotFoundMessage()); } } private void updateFile() { if (fileHandler != null) { - fileHandler.writeEntries(liquidArrayList); + fileHandler.writeEntries(hydrationArrayList); } } @@ -60,8 +60,8 @@ private void updateFile() { * @return the liquid entry at the specified index */ public Entry getEntry(int index) { - assert index >= 0 && index < liquidArrayList.size() : "Index out of bounds"; - return liquidArrayList.get(index); + assert index >= 0 && index < hydrationArrayList.size() : "Index out of bounds"; + return hydrationArrayList.get(index); } /** @@ -72,7 +72,7 @@ public Entry getEntry(int index) { public void deleteEntry(String line) { try { int index = Integer.parseInt(line.substring(DELETE_PADDING).trim()); - liquidArrayList.remove(index - 1); + hydrationArrayList.remove(index - 1); updateFile(); deleteMessage(); } catch (IndexOutOfBoundsException e) { @@ -88,11 +88,10 @@ public void deleteEntry(String line) { * @param input the input string containing liquid entry information */ public void addEntry(String input) { - assert (input.startsWith("hydration in")) - : "ensures that input is correct"; + assert (input.startsWith("hydration in")) : "ensures that input is correct"; try { - Entry newEntry = ParserLiquid.parseLiquidInput(input); - liquidArrayList.add(newEntry); + Entry newEntry = ParserHydration.parseHydrationInput(input); + hydrationArrayList.add(newEntry); updateFile(); addEntryMessage(); } catch (InvalidInputException e) { @@ -104,13 +103,13 @@ public void addEntry(String input) { * Prints the list of liquid entries. * If the list is empty, prints a message indicating that the list is empty. */ - public void printLiquidList() { - if (liquidArrayList.isEmpty()) { + public void printHydrationList() { + if (hydrationArrayList.isEmpty()) { emptyListMessage(); } else { listHeader(); - for (int i = 0; i < liquidArrayList.size(); i++) { - Entry entry = liquidArrayList.get(i); + for (int i = 0; i < hydrationArrayList.size(); i++) { + Entry entry = hydrationArrayList.get(i); System.out.println("\t " + (i + 1) + ". " + entry); } } @@ -122,6 +121,6 @@ public void printLiquidList() { * @return the number of liquid entries in the list */ public int getSize() { - return liquidArrayList.size(); + return hydrationArrayList.size(); } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java similarity index 92% rename from src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java rename to src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index 89a93b1797..ffa3828449 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserLiquid.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -1,7 +1,7 @@ package seedu.lifetrack.system.parser; import seedu.lifetrack.Entry; -import seedu.lifetrack.liquids.liquidlist.LiquidEntry; +import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectVolumeInputMessage; @@ -10,7 +10,7 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationMissingKeywordMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; -public class ParserLiquid { +public class ParserHydration { private static final int HYDRATION_IN_PADDING = 12; @@ -27,8 +27,8 @@ public class ParserLiquid { * @throws InvalidInputException if the input string is missing components or * contains empty fields */ - public static Entry parseLiquidInput(String input) throws InvalidInputException { - //get index for b/ and v/ inputs + public static Entry parseHydrationInput(String input) throws InvalidInputException { + int volumeIndex = input.indexOf("v/"); int dateIndex = input.indexOf("date/"); @@ -55,10 +55,12 @@ public static Entry parseLiquidInput(String input) throws InvalidInputException return makeNewInputEntry(description, volume, date); } - private static LiquidEntry makeNewInputEntry(String description, int volume, String date) { - return new LiquidEntry(description, volume, date); + private static HydrationEntry makeNewInputEntry(String description, int volume, String date) { + + return new HydrationEntry(description, volume, date); } + private static int getIntegerVolumeFromInput(String strVolume) { int volume = 0; try { @@ -74,6 +76,7 @@ private static void checkVolumeIsPositiveInteger(int volume) throws InvalidInput throw new InvalidInputException(getHydrationNegativeIntegerVolumeMessage()); } } + private static void checkInputsAreNonEmpty(String description, String strVolume, String date) throws InvalidInputException { //check if the description, calories or date fields are empty @@ -81,16 +84,19 @@ private static void checkInputsAreNonEmpty(String description, String strVolume, throw new InvalidInputException(getHydrationEmptyDescriptionMessage()); } } + private static String getDescriptionFromInput(String inputString, int volumeIndex) { String description; description = inputString.substring(HYDRATION_IN_PADDING, volumeIndex).trim(); return description; } + private static void checkKeywordsCorrectlyOrdered( int dateIndex, int volumeIndex) throws InvalidInputException { if (!(volumeIndex < dateIndex)) { throw new InvalidInputException(getHydrationIncorrectOrderMessage()); } } + private static void checkKeywordsExist(int dateIndex, int volumeIndex) throws InvalidInputException { //check that v/ and date/ keywords exist in the input, else throw exception if (dateIndex == -1 || volumeIndex == -1) { diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 37229b314b..53ce788b72 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -11,7 +11,7 @@ import seedu.lifetrack.calories.Food; import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; -import seedu.lifetrack.liquids.liquidlist.LiquidEntry; +import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.sleep.sleeplist.SleepEntry; import seedu.lifetrack.system.exceptions.ErrorMessages; @@ -85,7 +85,7 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException return entries; } - public ArrayList getLiquidEntriesFromFile() throws FileNotFoundException { + public ArrayList getHydrationEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); ArrayList entries = new ArrayList<>(); @@ -96,7 +96,7 @@ public ArrayList getLiquidEntriesFromFile() throws FileNotFoundException String date = words[DATE_INDEX]; String description = words[DESCRIPTION_INDEX]; int volume = Integer.parseInt(words[VOLUME_INDEX]); - entries.add(new LiquidEntry(description, volume, date)); + entries.add(new HydrationEntry(description, volume, date)); } return entries; } diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index a29eee8d06..7eb394ce49 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -14,8 +14,7 @@ public static void emptyListMessage() { } public static String deleteLogIndexMessage() { - return "Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."; + return "Sorry, this index is invalid. Please enter a positive integer within the size of the list."; } public static String deleteLogNumberMessage() { return "Please enter a valid index!"; diff --git a/src/main/java/seedu/lifetrack/ui/LiquidListUI.java b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java similarity index 89% rename from src/main/java/seedu/lifetrack/ui/LiquidListUI.java rename to src/main/java/seedu/lifetrack/ui/HydrationListUI.java index bcf0c48df9..0a18bc365f 100644 --- a/src/main/java/seedu/lifetrack/ui/LiquidListUI.java +++ b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java @@ -1,14 +1,13 @@ package seedu.lifetrack.ui; -public class LiquidListUI { +public class HydrationListUI { public static void deleteMessage() { System.out.println("\t Successfully delete the liquid record."); } public static String deleteLogIndexMessage() { - return "Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."; + return "Sorry, this index is invalid. Please enter a positive integer within the size of the list."; } public static String deleteLogNumberMessage() { diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 7b38eef37c..a8bf4f6d62 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -1,7 +1,7 @@ package seedu.lifetrack.ui; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.hydration.hydrationlist.HydrationList; import seedu.lifetrack.sleep.sleeplist.SleepList; import java.util.Scanner; @@ -33,7 +33,7 @@ public class Ui { * @param calorieList list containing all entries pertinent to calories * @param liquidList list containing all entries pertinent to liquids */ - public static void readUserInput(CalorieList calorieList, LiquidList liquidList, SleepList sleepList) { + public static void readUserInput(CalorieList calorieList, HydrationList liquidList, SleepList sleepList) { String line; do { line = new Scanner(System.in).nextLine(); @@ -59,14 +59,14 @@ public static void handleCaloriesInput(String line, CalorieList calorieList) { } } - public static void handleLiquidsInput(String line, LiquidList liquidsList) { + public static void handleLiquidsInput(String line, HydrationList hydrationList) { assert !line.startsWith("bye") : "exit the app"; if (line.startsWith("hydration in")) { - liquidsList.addEntry(line); + hydrationList.addEntry(line); } else if (line.startsWith("hydration list")) { - liquidsList.printLiquidList(); + hydrationList.printHydrationList(); } else if (line.startsWith("hydration delete")) { - liquidsList.deleteEntry(line); + hydrationList.deleteEntry(line); } else { handleUnknownInput(); } @@ -85,7 +85,7 @@ public static void handleSleepInput(String line, SleepList sleepList) { } public static void handleUserInput(String line, CalorieList calorieList, - LiquidList liquidList, SleepList sleepList) { + HydrationList liquidList, SleepList sleepList) { if (!line.startsWith("bye")) { printLine(); line = line.trim().toLowerCase(); diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java similarity index 55% rename from src/test/java/seedu/lifetrack/LiquidListTest.java rename to src/test/java/seedu/lifetrack/HydrationListTest.java index 33a5ac32a9..c1eada5de3 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -2,41 +2,42 @@ package seedu.lifetrack; import org.junit.jupiter.api.Test; + +import seedu.lifetrack.hydration.hydrationlist.HydrationList; + import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import seedu.lifetrack.liquids.liquidlist.LiquidList; - -public class LiquidListTest { +public class HydrationListTest { @Test - public void testDeleteLiquidValidIndex() { - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("hydration add Milo v/200 date/220224"); - int initialSize = liquidList.getSize(); - liquidList.deleteEntry("hydration delete 1"); - assertEquals(initialSize - 1, liquidList.getSize()); + public void testDeleteHydrationValidIndex() { + HydrationList hydrationList = new HydrationList(); + hydrationList.addEntry("hydration add Milo v/200 date/220224"); + int initialSize = hydrationList.getSize(); + hydrationList.deleteEntry("hydration delete 1"); + assertEquals(initialSize - 1, hydrationList.getSize()); } @Test - public void testDeleteLiquidInvalidIndex() { - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("hydration add Milo v/200 date/220224"); - int initialSize = liquidList.getSize(); - liquidList.deleteEntry("hydration delete 2"); // Index out of bounds - liquidList.deleteEntry("hydration delete -1"); - assertEquals(initialSize, liquidList.getSize()); + public void testDeleteHydrationInvalidIndex() { + HydrationList hydrationList = new HydrationList(); + hydrationList.addEntry("hydration add Milo v/200 date/220224"); + int initialSize = hydrationList.getSize(); + hydrationList.deleteEntry("hydration delete 2"); // Index out of bounds + hydrationList.deleteEntry("hydration delete -1"); + assertEquals(initialSize, hydrationList.getSize()); } @Test - public void testPrintLiquidListEmpty() { + public void testPrintHydrationListEmpty() { String lineSeparator = System.lineSeparator(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); - LiquidList liquidList = new LiquidList(); - liquidList.printLiquidList(); + HydrationList hydrationList = new HydrationList(); + hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t Your liquid list is empty." + lineSeparator + "\t Populate your list with more entries :)" + lineSeparator; @@ -44,13 +45,13 @@ public void testPrintLiquidListEmpty() { } @Test - public void testPrintLiquidListNonEmpty() { + public void testPrintHydrationListNonEmpty() { String lineSeparator = System.lineSeparator(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("hydration add Milo v/200 date/220224"); - liquidList.printLiquidList(); + HydrationList hydrationList = new HydrationList(); + hydrationList.addEntry("hydration add Milo v/200 date/220224"); + hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t Beverage has been successfully added" + lineSeparator + "\t Your liquid List:" + lineSeparator + @@ -59,15 +60,15 @@ public void testPrintLiquidListNonEmpty() { } @Test - public void testPrintLiquidListMultipleEntries() { + public void testPrintHydrationListMultipleEntries() { String lineSeparator = System.lineSeparator(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); - LiquidList liquidList = new LiquidList(); - liquidList.addEntry("hydration add Milo v/200 date/220224"); - liquidList.addEntry("hydration add Water v/300 date/220224"); - liquidList.addEntry("hydration add Juice v/150 date/220224"); - liquidList.printLiquidList(); + HydrationList hydrationList = new HydrationList(); + hydrationList.addEntry("hydration add Milo v/200 date/220224"); + hydrationList.addEntry("hydration add Water v/300 date/220224"); + hydrationList.addEntry("hydration add Juice v/150 date/220224"); + hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t Beverage has been successfully added" + lineSeparator + "\t Beverage has been successfully added" + lineSeparator + @@ -77,6 +78,6 @@ public void testPrintLiquidListMultipleEntries() { "\t 2. \t Date: 111111, Description: Water, Volume: 300" + lineSeparator + "\t 3. \t Date: 111111, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); - assertEquals(3, liquidList.getSize()); + assertEquals(3, hydrationList.getSize()); } } diff --git a/src/test/java/seedu/lifetrack/ParserLiquidTest.java b/src/test/java/seedu/lifetrack/ParserHydrationTest.java similarity index 73% rename from src/test/java/seedu/lifetrack/ParserLiquidTest.java rename to src/test/java/seedu/lifetrack/ParserHydrationTest.java index b7bdd0cf74..20b3bbfa31 100644 --- a/src/test/java/seedu/lifetrack/ParserLiquidTest.java +++ b/src/test/java/seedu/lifetrack/ParserHydrationTest.java @@ -5,18 +5,18 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.lifetrack.system.parser.ParserLiquid.parseLiquidInput; +import static seedu.lifetrack.system.parser.ParserHydration.parseHydrationInput; -public class ParserLiquidTest { +public class ParserHydrationTest { @Test - public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown() { + public void parseHydrationInput_inputContains2Beverages_invalidInputExceptionThrown() { // setup test String invalidInput = "liquids in Milo b/1000 b/1000"; // Call methods to test try { - parseLiquidInput(invalidInput); + parseHydrationInput(invalidInput); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -26,13 +26,13 @@ public void parseLiquidInput_inputContains2Beverages_invalidInputExceptionThrown } @Test - public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() { + public void parseHydrationInput_inputContains2Volumes_invalidInputExceptionThrown() { // setup test String invalidInput = "liquids in Milo v/1000 v/1000"; // Call methods to test try { - parseLiquidInput(invalidInput); + parseHydrationInput(invalidInput); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -42,13 +42,13 @@ public void parseLiquidInput_inputContains2Volumes_invalidInputExceptionThrown() } @Test - public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { + public void parseHydrationInput_inputMissingVolume_invalidInputExceptionThrown() { // setup test String invalidInput = "liquids in Milo date/221024"; // Call methods to test try { - parseLiquidInput(invalidInput); + parseHydrationInput(invalidInput); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -58,13 +58,13 @@ public void parseLiquidInput_inputMissingVolume_invalidInputExceptionThrown() { } @Test - public void parseLiquidInput_inputWrongOrderDateBeforeVolume_invalidInputExceptionThrown() { + public void parseHydrationInput_inputWrongOrderDateBeforeVolume_invalidInputExceptionThrown() { // setup test String invalidInput = "liquids in Milo date/221024 v/1000"; // Call methods to test try { - parseLiquidInput(invalidInput); + parseHydrationInput(invalidInput); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have keyed the input in the correct order!\n" + @@ -73,13 +73,13 @@ public void parseLiquidInput_inputWrongOrderDateBeforeVolume_invalidInputExcepti } @Test - public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputExceptionThrown() { + public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExceptionThrown() { // setup test String invalidInput = "liquids in Milo v/##s100 date/221024"; // Call methods to test try { - parseLiquidInput(invalidInput); + parseHydrationInput(invalidInput); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + @@ -88,13 +88,13 @@ public void parseLiquidInput_inputNonIntegerValueForVolume_invalidInputException } @Test - public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionThrown() { + public void parseHydrationInput_inputNegativeValueForVolume_invalidInputExceptionThrown() { // setup test String invalidInput = "liquids in Milo v/-1000 date/221024"; // Call methods to test try { - parseLiquidInput(invalidInput); + parseHydrationInput(invalidInput); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + @@ -104,9 +104,9 @@ public void parseLiquidInput_inputNegativeValueForVolume_invalidInputExceptionTh //@@author shawnpong @Test - public void parseLiquidInput_missingKeywords_exceptionThrown() { + public void parseHydrationInput_missingKeywords_exceptionThrown() { try { - parseLiquidInput("liquids in"); + parseHydrationInput("liquids in"); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -115,9 +115,9 @@ public void parseLiquidInput_missingKeywords_exceptionThrown() { } @Test - public void parseLiquidInput_incompleteInput_exceptionThrown() { + public void parseHydrationInput_incompleteInput_exceptionThrown() { try { - parseLiquidInput("liquids in b/Milo"); + parseHydrationInput("liquids in b/Milo"); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -127,9 +127,9 @@ public void parseLiquidInput_incompleteInput_exceptionThrown() { } @Test - public void parseLiquidInput_emptyBeverageName_exceptionThrown() { + public void parseHydrationInput_emptyBeverageName_exceptionThrown() { try { - parseLiquidInput("liquids in v/1000"); + parseHydrationInput("liquids in v/1000"); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -138,9 +138,9 @@ public void parseLiquidInput_emptyBeverageName_exceptionThrown() { } } @Test - public void parseLiquidInput_emptyVolumeDescription_exceptionThrown() { + public void parseHydrationInput_emptyVolumeDescription_exceptionThrown() { try { - parseLiquidInput("liquids in Milo v/ "); + parseHydrationInput("liquids in Milo v/ "); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + diff --git a/src/test/java/seedu/lifetrack/UITest.java b/src/test/java/seedu/lifetrack/UITest.java index 2ee545b61e..f0cc779a53 100644 --- a/src/test/java/seedu/lifetrack/UITest.java +++ b/src/test/java/seedu/lifetrack/UITest.java @@ -6,9 +6,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.hydration.hydrationlist.HydrationList; import seedu.lifetrack.sleep.sleeplist.SleepList; import seedu.lifetrack.ui.Ui; -import seedu.lifetrack.liquids.liquidlist.LiquidList; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -31,10 +31,10 @@ public void restoreStreams() { // Expect Empty String as function is exited public void handleUserInput_inputBye_printByeMessage() { CalorieList calorieList = new CalorieList(); - LiquidList liquidList = new LiquidList(); + HydrationList hydrationList = new HydrationList(); SleepList sleepList = new SleepList(); String input = "bye"; - Ui.handleUserInput(input, calorieList, liquidList,sleepList); + Ui.handleUserInput(input, calorieList, hydrationList,sleepList); assertEquals("", outContent.toString()); } } From 23e75fb866de2eb024e214b0f4f1d6dd2cd98b34 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 02:20:08 +0800 Subject: [PATCH 129/414] standardize UI between lists --- src/main/java/seedu/lifetrack/LifeTrack.java | 2 +- .../calories/calorielist/CalorieList.java | 23 ++++++-------- .../hydrationlist/HydrationList.java | 25 ++++++--------- .../lifetrack/sleep/sleeplist/SleepList.java | 7 +++-- .../exceptions/InvalidInputException.java | 1 + .../system/parser/ParserCalories.java | 7 ++--- .../system/parser/ParserHydration.java | 10 +++--- .../lifetrack/system/parser/ParserSleep.java | 2 +- .../seedu/lifetrack/ui/CalorieListUi.java | 6 ++-- .../seedu/lifetrack/ui/HydrationListUI.java | 29 +++++++++-------- .../java/seedu/lifetrack/ui/SleepListUi.java | 31 ++++++++++++------- src/main/java/seedu/lifetrack/ui/Ui.java | 14 ++++----- 12 files changed, 79 insertions(+), 78 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index 959f1cda39..e9bc270a87 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -10,7 +10,7 @@ public class LifeTrack { public static CalorieList calorieList = new CalorieList("data/caloriesData.txt"); - public static HydrationList hydrationList = new HydrationList("data/liquidsData.txt"); + public static HydrationList hydrationList = new HydrationList("data/hydrationData.txt"); public static SleepList sleepList = new SleepList("data/sleepData.txt"); /** diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index ae68f851f0..242b11aeb7 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,16 +1,11 @@ package seedu.lifetrack.calories.calorielist; -import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; -import static seedu.lifetrack.ui.CalorieListUi.emptyListMessage; -import static seedu.lifetrack.ui.CalorieListUi.successfulDeletedMessage; -import static seedu.lifetrack.ui.CalorieListUi.printNewCalorieEntry; -import static seedu.lifetrack.ui.CalorieListUi.calorieListHeader; -import static seedu.lifetrack.ui.CalorieListUi.deleteLogIndexMessage; -import static seedu.lifetrack.ui.CalorieListUi.deleteLogNumberMessage; import seedu.lifetrack.Entry; import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.parser.ParserCalories; import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.ui.CalorieListUi; import java.util.logging.Level; import java.util.logging.Logger; @@ -63,11 +58,11 @@ public void deleteEntry(String line) { Entry toDelete = calorieArrayList.get(index-1); calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 updateFile(); - successfulDeletedMessage(toDelete); + CalorieListUi.successfulDeletedMessage(toDelete); } catch (IndexOutOfBoundsException e) { - System.out.println(deleteLogIndexMessage()); + System.out.println(CalorieListUi.deleteLogIndexMessage()); } catch (NumberFormatException e) { - System.out.println(deleteLogNumberMessage()); + System.out.println(CalorieListUi.deleteLogNumberMessage()); } } @@ -86,10 +81,10 @@ public void addEntry(String input) { assert (input.startsWith("calories in") || input.startsWith("calories out")) : "ensures that input is correct"; logr.setLevel(Level.WARNING); try { - Entry newEntry = parseCaloriesInput(input); + Entry newEntry = ParserCalories.parseCaloriesInput(input); calorieArrayList.add(newEntry); updateFile(); - printNewCalorieEntry(newEntry); + CalorieListUi.printNewCalorieEntry(newEntry); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); } @@ -102,9 +97,9 @@ public void addEntry(String input) { */ public void printCalorieList() { if (calorieArrayList.isEmpty()) { - emptyListMessage(); + CalorieListUi.emptyListMessage(); } else { - calorieListHeader(); + CalorieListUi.calorieListHeader(); for (int i = 0; i < calorieArrayList.size(); i++) { Entry entry = calorieArrayList.get(i); System.out.println("\t " + (i + 1) + ". " + entry); diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 20ea6367fb..ad858151aa 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -6,19 +6,13 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserHydration; import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.ui.HydrationListUI; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.lifetrack.ui.HydrationListUI.deleteLogIndexMessage; -import static seedu.lifetrack.ui.HydrationListUI.deleteLogNumberMessage; -import static seedu.lifetrack.ui.HydrationListUI.deleteMessage; -import static seedu.lifetrack.ui.HydrationListUI.addEntryMessage; -import static seedu.lifetrack.ui.HydrationListUI.emptyListMessage; -import static seedu.lifetrack.ui.HydrationListUI.listHeader; - /** * Represents a list of liquid entries. * Provides methods to add, delete, and print liquid entries. @@ -27,7 +21,7 @@ public class HydrationList { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); - private final int DELETE_PADDING = 15; + private final int DELETE_PADDING = 16; private ArrayList hydrationArrayList; private FileHandler fileHandler; @@ -72,13 +66,14 @@ public Entry getEntry(int index) { public void deleteEntry(String line) { try { int index = Integer.parseInt(line.substring(DELETE_PADDING).trim()); + Entry toDelete = hydrationArrayList.get(index - 1); hydrationArrayList.remove(index - 1); updateFile(); - deleteMessage(); + HydrationListUI.successfulDeletedMessage(toDelete); } catch (IndexOutOfBoundsException e) { - System.out.println(deleteLogIndexMessage()); + System.out.println(HydrationListUI.deleteLogIndexMessage()); } catch (NumberFormatException e) { - System.out.println(deleteLogNumberMessage()); + System.out.println(HydrationListUI.deleteLogNumberMessage()); } } @@ -88,12 +83,12 @@ public void deleteEntry(String line) { * @param input the input string containing liquid entry information */ public void addEntry(String input) { - assert (input.startsWith("hydration in")) : "ensures that input is correct"; + assert (input.startsWith("hydration add")) : "ensures that input is correct"; try { Entry newEntry = ParserHydration.parseHydrationInput(input); hydrationArrayList.add(newEntry); updateFile(); - addEntryMessage(); + HydrationListUI.printNewHydrationEntry(newEntry); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); } @@ -105,9 +100,9 @@ public void addEntry(String input) { */ public void printHydrationList() { if (hydrationArrayList.isEmpty()) { - emptyListMessage(); + HydrationListUI.emptyListMessage(); } else { - listHeader(); + HydrationListUI.hydrationListHeader(); for (int i = 0; i < hydrationArrayList.size(); i++) { Entry entry = hydrationArrayList.get(i); System.out.println("\t " + (i + 1) + ". " + entry); diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 5ec2ff161c..3a11f07f6d 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -45,10 +45,10 @@ public Entry getSleep(int index) { public void addSleep(String input) { try { - SleepEntry newSleep = ParserSleep.parseSleepInput(input); + Entry newSleep = ParserSleep.parseSleepInput(input); sleepList.add(newSleep); updateFile(); - SleepListUi.addEntryMessage(); + SleepListUi.printNewSleepEntry(newSleep); } catch (InvalidInputException e) { System.out.println(getIncorrectSleepInputMessage()); } @@ -57,9 +57,10 @@ public void addSleep(String input) { public void deleteSleep(String line) { try { int index = Integer.parseInt(line.split(" ")[2]) ; + Entry toDelete = sleepList.get(index-1); sleepList.remove(index - 1); updateFile(); - SleepListUi.deleteMessage(); + SleepListUi.successfulDeletedMessage(toDelete); } catch (IndexOutOfBoundsException e) { System.out.println(SleepListUi.deleteLogIndexMessage()); } catch (NumberFormatException e) { diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index d718e8bc0a..818ec5bcbc 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -3,6 +3,7 @@ import java.util.logging.Level; import java.util.logging.Logger; + public class InvalidInputException extends Exception { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 3af9693492..52948a1658 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -16,7 +16,6 @@ import seedu.lifetrack.Entry; - public class ParserCalories { private static final int CARBS_IDX = 0; @@ -40,18 +39,18 @@ public class ParserCalories { */ public static Entry parseCaloriesInput(String input) throws InvalidInputException { int caloriesIndex = input.indexOf("c/"); - int dateIndex = input.indexOf("date/"); + int dateIndex = input.indexOf("d/"); int macrosIndex = input.indexOf("m/"); checkKeywordsExist(caloriesIndex, dateIndex); assert caloriesIndex != -1 : "The c/ keyword should exist!"; - assert dateIndex != -1 : "The date/ keyword should exist!"; + assert dateIndex != -1 : "The d/ keyword should exist!"; checkKeywordsCorrectlyOrdered(caloriesIndex, dateIndex, macrosIndex); assert caloriesIndex < dateIndex : "The c/ keyword must appear before date/ in the input!"; //extract command, description, calories, date from input - String[] parts = input.split("c/|date/|m/"); + String[] parts = input.split("c/|d/|m/"); String command = parts[0].substring(0, CALORIES_OUT_PADDING).trim(); String description = getDescriptionFromInput(input, command, caloriesIndex); String strCalories = parts[1].trim(); diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index ffa3828449..c0dd75de63 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -12,7 +12,7 @@ public class ParserHydration { - private static final int HYDRATION_IN_PADDING = 12; + private static final int HYDRATION_ADD_PADDING = 13; /** * Parses a string input to create a Liquid object representing liquid intake. @@ -30,16 +30,16 @@ public class ParserHydration { public static Entry parseHydrationInput(String input) throws InvalidInputException { int volumeIndex = input.indexOf("v/"); - int dateIndex = input.indexOf("date/"); + int dateIndex = input.indexOf("d/"); checkKeywordsExist(dateIndex, volumeIndex); assert volumeIndex != -1 : "The v/ keyword should exist!"; - assert dateIndex != -1 : "The date/ keyword should exist!"; + assert dateIndex != -1 : "The d/ keyword should exist!"; checkKeywordsCorrectlyOrdered(dateIndex, volumeIndex); assert volumeIndex < dateIndex : "The v/ keyword must appear before date/ in the input!"; - String[] parts = input.split("v/|date/"); + String[] parts = input.split("v/|d/"); String description = getDescriptionFromInput(input, volumeIndex); String strVolume = parts[1].trim(); String date = parts[2].trim(); @@ -87,7 +87,7 @@ private static void checkInputsAreNonEmpty(String description, String strVolume, private static String getDescriptionFromInput(String inputString, int volumeIndex) { String description; - description = inputString.substring(HYDRATION_IN_PADDING, volumeIndex).trim(); + description = inputString.substring(HYDRATION_ADD_PADDING, volumeIndex).trim(); return description; } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 653a3b07d0..88366ad42e 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -8,6 +8,7 @@ import seedu.lifetrack.sleep.sleeplist.SleepEntry; public class ParserSleep { + public static SleepEntry parseSleepInput(String input) throws InvalidInputException { try { String date = "N/A"; // Default if no date is provided @@ -36,5 +37,4 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept "sleep add d/"); } } - } diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 7eb394ce49..7c36a6cd2c 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -14,10 +14,11 @@ public static void emptyListMessage() { } public static String deleteLogIndexMessage() { - return "Sorry, this index is invalid. Please enter a positive integer within the size of the list."; + return "\t Sorry, this index is invalid. Please enter a positive integer within the size of the list."; } + public static String deleteLogNumberMessage() { - return "Please enter a valid index!"; + return "\t Please enter a valid index!"; } public static void calorieListHeader() { @@ -27,6 +28,5 @@ public static void calorieListHeader() { public static void printNewCalorieEntry(Entry newEntry) { System.out.println("\t The following entry has been added to your caloric list!"); System.out.println("\t " + newEntry.toString()); - } } diff --git a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java index 0a18bc365f..9e3664b45f 100644 --- a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java +++ b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java @@ -1,29 +1,32 @@ package seedu.lifetrack.ui; +import seedu.lifetrack.Entry; + public class HydrationListUI { - public static void deleteMessage() { - System.out.println("\t Successfully delete the liquid record."); + public static void successfulDeletedMessage(Entry toDelete) { + System.out.println("\t The following hydration record has been successfully deleted!"); + System.out.println("\t " + toDelete.toString()); } - public static String deleteLogIndexMessage() { - return "Sorry, this index is invalid. Please enter a positive integer within the size of the list."; + public static void emptyListMessage() { + System.out.println("\t Your hydration list is empty. Add new entries to populate your list :)"); } - public static String deleteLogNumberMessage() { - return "Please enter a valid index!"; + public static String deleteLogIndexMessage() { + return "\t Sorry, this index is invalid. Please enter a positive integer within the size of the list."; } - public static void addEntryMessage() { - System.out.println("\t Beverage has been successfully added"); + public static String deleteLogNumberMessage() { + return "\t Please enter a valid index!"; } - public static void emptyListMessage() { - System.out.println("\t Your liquid list is empty."); - System.out.println("\t Populate your list with more entries :)"); + public static void hydrationListHeader() { + System.out.println("\t Your Hydration List:"); } - public static void listHeader() { - System.out.println("\t Your liquid List:"); + public static void printNewHydrationEntry(Entry newEntry) { + System.out.println("\t The following entry has been added to your hydration list!"); + System.out.println("\t " + newEntry.toString()); } } diff --git a/src/main/java/seedu/lifetrack/ui/SleepListUi.java b/src/main/java/seedu/lifetrack/ui/SleepListUi.java index 25cf48b0fc..9e4a0551e8 100644 --- a/src/main/java/seedu/lifetrack/ui/SleepListUi.java +++ b/src/main/java/seedu/lifetrack/ui/SleepListUi.java @@ -1,25 +1,32 @@ package seedu.lifetrack.ui; +import seedu.lifetrack.Entry; + public class SleepListUi { - public static void addEntryMessage() { - System.out.println("\t New sleep record has been successfully added."); - } - public static void deleteMessage() { - System.out.println("\t Successfully delete the sleep record."); + public static void successfulDeletedMessage(Entry toDelete) { + System.out.println("\t The following sleep record has been successfully deleted!"); + System.out.println("\t " + toDelete.toString()); } - public static String deleteLogNumberMessage() { - return "Please enter a valid index!"; + + public static void emptyListMessage() { + System.out.println("\t Your sleep list is empty. Add new entries to populate your list :)"); } + public static String deleteLogIndexMessage() { - return "Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."; + return "\t Sorry, this index is invalid. Please enter a positive integer within the size of the list."; } + + public static String deleteLogNumberMessage() { + return "\t Please enter a valid index!"; + } + public static void sleepListHeader() { System.out.println("\t Your Sleep List:"); } - public static void emptyListMessage() { - System.out.println("\t Your sleep list is empty."); - System.out.println("\t Populate your list with more entries :)"); + + public static void printNewSleepEntry(Entry newEntry) { + System.out.println("\t The following entry has been added to your sleep list!"); + System.out.println("\t " + newEntry.toString()); } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index a8bf4f6d62..7b2fcc0ce0 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -31,13 +31,13 @@ public class Ui { /** * Reads in the input from the user * @param calorieList list containing all entries pertinent to calories - * @param liquidList list containing all entries pertinent to liquids + * @param hydrationList list containing all entries pertinent to liquids */ - public static void readUserInput(CalorieList calorieList, HydrationList liquidList, SleepList sleepList) { + public static void readUserInput(CalorieList calorieList, HydrationList hydrationList, SleepList sleepList) { String line; do { line = new Scanner(System.in).nextLine(); - handleUserInput(line, calorieList, liquidList, sleepList); + handleUserInput(line, calorieList, hydrationList, sleepList); } while (!line.equalsIgnoreCase("bye")); } @@ -59,9 +59,9 @@ public static void handleCaloriesInput(String line, CalorieList calorieList) { } } - public static void handleLiquidsInput(String line, HydrationList hydrationList) { + public static void handleHydrationInput(String line, HydrationList hydrationList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("hydration in")) { + if (line.startsWith("hydration add")) { hydrationList.addEntry(line); } else if (line.startsWith("hydration list")) { hydrationList.printHydrationList(); @@ -85,7 +85,7 @@ public static void handleSleepInput(String line, SleepList sleepList) { } public static void handleUserInput(String line, CalorieList calorieList, - HydrationList liquidList, SleepList sleepList) { + HydrationList hydrationList, SleepList sleepList) { if (!line.startsWith("bye")) { printLine(); line = line.trim().toLowerCase(); @@ -96,7 +96,7 @@ public static void handleUserInput(String line, CalorieList calorieList, } else if (line.startsWith("help")) { showHelp(); } else if (line.startsWith("hydration")) { - handleLiquidsInput(line, liquidList); + handleHydrationInput(line, hydrationList); } else if (line.startsWith("sleep")) { handleSleepInput(line, sleepList); } else { From 30ff4e6ef50822e6769669a621078889740a9a7c Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 02:42:07 +0800 Subject: [PATCH 130/414] update all JUnit test cases to adhere to new input formats --- .../InvalidInputExceptionMessage.java | 10 +++-- .../system/parser/ParserCalories.java | 8 ++-- .../java/seedu/lifetrack/CalorieListTest.java | 22 +++++------ .../seedu/lifetrack/HydrationListTest.java | 39 +++++++++++-------- .../seedu/lifetrack/ParserCaloriesTest.java | 22 +++++------ .../seedu/lifetrack/ParserHydrationTest.java | 6 +-- .../java/seedu/lifetrack/SleepListTest.java | 16 +++++--- 7 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 70dcb52411..03064edfa2 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -10,13 +10,14 @@ public class InvalidInputExceptionMessage { private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; private static final String HYDRATION_IN_INPUT = "\t Example input: liquids in Milo v/1000 date/221024" ; - public static String getIncorrectOrderMessage() { - String message = "\t Please ensure that you have keyed in the correct format in the correct order!\n"; + //calories list related methods + public static String getCaloriesIncorrectOrderMessage() { + String message = "\t Please ensure that you have keyed the input in the correct order!\n"; return HEADER + message + CALORIES_IN_INPUT; } - public static String getMissingKeywordsMessage() { - String message = "\t Please ensure that the compulsory keywords exist!\n"; + public static String getCaloriesMissingKeywordsMessage() { + String message = "\t Please ensure that you have entered all keywords!\n"; return HEADER + message + CALORIES_IN_INPUT; } @@ -39,6 +40,7 @@ public static String getMacrosInCaloriesOutMessage() { return HEADER + message + CALORIES_OUT_INPUT; } + //hydration list related methods public static String getHydrationMissingKeywordMessage() { String message = "\t Please ensure that you have entered all keywords!\n"; return HEADER + message + HYDRATION_IN_INPUT; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 52948a1658..f990b3d022 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -10,8 +10,8 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectOrderMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMissingKeywordsMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesIncorrectOrderMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesMissingKeywordsMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; import seedu.lifetrack.Entry; @@ -150,7 +150,7 @@ private static void checkInputsAreNonEmpty(String description, String strCalorie private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws InvalidInputException { //check that c/ and date/ keywords exist in the input, else throw exception if (caloriesIndex == -1 || dateIndex == -1) { - throw new InvalidInputException(getMissingKeywordsMessage()); + throw new InvalidInputException(getCaloriesMissingKeywordsMessage()); } } @@ -158,7 +158,7 @@ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateInd throws InvalidInputException { if ((macrosIndex != -1 && !(caloriesIndex < dateIndex && dateIndex < macrosIndex)) || (macrosIndex == -1 && !(caloriesIndex < dateIndex))) { - throw new InvalidInputException(getIncorrectOrderMessage()); + throw new InvalidInputException(getCaloriesIncorrectOrderMessage()); } } diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 61d17622af..924d8f7de5 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -18,8 +18,8 @@ public class CalorieListTest { public void addEntry_validInput_entryAdded() { // Test setup CalorieList calorieList = new CalorieList(); - String validInputCalorieIn = "calories in Eat burger c/369 date/2024-03-14"; - String validInputCalorieOut = "calories out run c/679 date/2024-03-15"; + String validInputCalorieIn = "calories in Eat burger c/369 d/2024-03-14"; + String validInputCalorieOut = "calories out run c/679 d/2024-03-15"; // Call method to test calorieList.addEntry(validInputCalorieIn); @@ -45,12 +45,12 @@ public void addEntry_validInput_entryAdded() { @Test public void testDeleteCalorieValidIndex() { CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories out Run c/200 date/2024-03-14"); + calorieList.addEntry("calories out Run c/200 d/2024-03-14"); int initialSize = calorieList.getSize(); calorieList.deleteEntry("calories delete 1"); assertEquals(initialSize - 1, calorieList.getSize()); - calorieList.addEntry("calories out Run c/200 date/2024-03-14"); - calorieList.addEntry("calories in Eat c/200 date/2024-03-14"); + calorieList.addEntry("calories out Run c/200 d/2024-03-14"); + calorieList.addEntry("calories in Eat c/200 d/2024-03-14"); initialSize = calorieList.getSize(); calorieList.deleteEntry("calories delete 2"); assertEquals(initialSize - 1, calorieList.getSize()); @@ -86,7 +86,7 @@ public void testPrintCalorieListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in Run c/200 date/2024-03-14"); + calorieList.addEntry("calories in Run c/200 d/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); String expectedOutput = addedEntryHeader + lineSeparator + @@ -102,11 +102,11 @@ public void testPrintCalorieListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in Run c/200 date/2024-03-14"); - calorieList.addEntry("calories out Walk c/150 date/2024-03-14"); - calorieList.addEntry("calories in Eat c/500 date/2024-03-14"); - calorieList.addEntry("calories out Run c/250 date/2024-03-14"); - calorieList.addEntry("calories in Eat c/300 date/2024-03-14"); + calorieList.addEntry("calories in Run c/200 d/2024-03-14"); + calorieList.addEntry("calories out Walk c/150 d/2024-03-14"); + calorieList.addEntry("calories in Eat c/500 d/2024-03-14"); + calorieList.addEntry("calories out Run c/250 d/2024-03-14"); + calorieList.addEntry("calories in Eat c/300 d/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); StringBuilder expectedOutput = new StringBuilder(); diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index c1eada5de3..c815becf8c 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import seedu.lifetrack.hydration.hydrationlist.HydrationList; +import seedu.lifetrack.ui.HydrationListUI; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,7 +16,7 @@ public class HydrationListTest { @Test public void testDeleteHydrationValidIndex() { HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 date/220224"); + hydrationList.addEntry("hydration add Milo v/200 d/220224"); int initialSize = hydrationList.getSize(); hydrationList.deleteEntry("hydration delete 1"); assertEquals(initialSize - 1, hydrationList.getSize()); @@ -39,8 +40,8 @@ public void testPrintHydrationListEmpty() { HydrationList hydrationList = new HydrationList(); hydrationList.printHydrationList(); System.setOut(System.out); - String expectedOutput = "\t Your liquid list is empty." + lineSeparator - + "\t Populate your list with more entries :)" + lineSeparator; + String expectedOutput = "\t Your hydration list is empty. Add new entries to populate your list :)" + + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -50,12 +51,13 @@ public void testPrintHydrationListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 date/220224"); + hydrationList.addEntry("hydration add Milo v/200 d/220224"); hydrationList.printHydrationList(); System.setOut(System.out); - String expectedOutput = "\t Beverage has been successfully added" + lineSeparator + - "\t Your liquid List:" + lineSeparator + - "\t 1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator; + String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + + "\t \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator + + "\t Your Hydration List:" + lineSeparator + + "\t 1. \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -65,18 +67,21 @@ public void testPrintHydrationListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 date/220224"); - hydrationList.addEntry("hydration add Water v/300 date/220224"); - hydrationList.addEntry("hydration add Juice v/150 date/220224"); + hydrationList.addEntry("hydration add Milo v/200 d/220224"); + hydrationList.addEntry("hydration add Water v/300 d/220224"); + hydrationList.addEntry("hydration add Juice v/150 d/220224"); hydrationList.printHydrationList(); System.setOut(System.out); - String expectedOutput = "\t Beverage has been successfully added" + lineSeparator + - "\t Beverage has been successfully added" + lineSeparator + - "\t Beverage has been successfully added" + lineSeparator + - "\t Your liquid List:" + lineSeparator + - "\t 1. \t Date: 111111, Description: Milo, Volume: 200" + lineSeparator + - "\t 2. \t Date: 111111, Description: Water, Volume: 300" + lineSeparator + - "\t 3. \t Date: 111111, Description: Juice, Volume: 150" + lineSeparator; + String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + + "\t \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator + + "\t The following entry has been added to your hydration list!" + lineSeparator + + "\t \t Date: 220224, Description: Water, Volume: 300" + lineSeparator + + "\t The following entry has been added to your hydration list!" + lineSeparator + + "\t \t Date: 220224, Description: Juice, Volume: 150" + lineSeparator + + "\t Your Hydration List:" + lineSeparator + + "\t 1. \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator + + "\t 2. \t Date: 220224, Description: Water, Volume: 300" + lineSeparator + + "\t 3. \t Date: 220224, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, hydrationList.getSize()); } diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index 6d62d62db8..8651e915f2 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -10,8 +10,8 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectOrderMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMissingKeywordsMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesIncorrectOrderMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesMissingKeywordsMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; class ParserCaloriesTest { @@ -21,14 +21,14 @@ public void parseCaloriesInput_missingKeywords_exceptionThrown() { try { parseCaloriesInput("calories in burger"); } catch (InvalidInputException e) { - assertEquals(getMissingKeywordsMessage(), e.getMessage()); + assertEquals(getCaloriesMissingKeywordsMessage(), e.getMessage()); } } @Test public void parseCaloriesInput_incompleteInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/ date/220224"); + parseCaloriesInput("calories in burger c/ d/220224"); } catch (InvalidInputException e) { assertEquals(getWhitespaceInInputMessage(), e.getMessage()); } @@ -37,16 +37,16 @@ public void parseCaloriesInput_incompleteInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger date/220224 c/123"); + parseCaloriesInput("calories in burger d/220224 c/123"); } catch (InvalidInputException e) { - assertEquals(getIncorrectOrderMessage(), e.getMessage()); + assertEquals(getCaloriesIncorrectOrderMessage(), e.getMessage()); } } @Test public void parseCaloriesInput_incorrectMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 date/220324 m/abc"); + parseCaloriesInput("calories in burger c/123 d/220324 m/abc"); } catch (InvalidInputException e) { assertEquals(getIncorrectMacrosInputMessage(), e.getMessage()); } @@ -55,7 +55,7 @@ public void parseCaloriesInput_incorrectMacrosInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectCaloriesInput_exceptionThrown() { try { - parseCaloriesInput("calories out Running c/abc date/220324"); + parseCaloriesInput("calories out Running c/abc d/220324"); } catch (InvalidInputException e) { assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); } @@ -64,7 +64,7 @@ public void parseCaloriesInput_incorrectCaloriesInput_exceptionThrown() { @Test public void parseCaloriesInput_incompleteMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 date/220324 m/123,132"); + parseCaloriesInput("calories in burger c/123 d/220324 m/123,132"); } catch (InvalidInputException e) { assertEquals(getIncompleteMacrosMessage(), e.getMessage()); } @@ -73,7 +73,7 @@ public void parseCaloriesInput_incompleteMacrosInput_exceptionThrown() { @Test public void parseCaloriesInput_macrosInCaloriesOut_exceptionThrown() { try { - parseCaloriesInput("calories out running c/123 date/220324 m/123,123,132"); + parseCaloriesInput("calories out running c/123 d/220324 m/123,123,132"); } catch (InvalidInputException e) { assertEquals(getMacrosInCaloriesOutMessage(), e.getMessage()); } @@ -82,7 +82,7 @@ public void parseCaloriesInput_macrosInCaloriesOut_exceptionThrown() { @Test public void parseCaloriesInput_whitespaceInMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 date/220324 m/123, ,132"); + parseCaloriesInput("calories in burger c/123 d/220324 m/123, ,132"); } catch (InvalidInputException e) { assertEquals(getWhitespaceInMacrosInputMessage(), e.getMessage()); } diff --git a/src/test/java/seedu/lifetrack/ParserHydrationTest.java b/src/test/java/seedu/lifetrack/ParserHydrationTest.java index 20b3bbfa31..bc60639e52 100644 --- a/src/test/java/seedu/lifetrack/ParserHydrationTest.java +++ b/src/test/java/seedu/lifetrack/ParserHydrationTest.java @@ -60,7 +60,7 @@ public void parseHydrationInput_inputMissingVolume_invalidInputExceptionThrown() @Test public void parseHydrationInput_inputWrongOrderDateBeforeVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in Milo date/221024 v/1000"; + String invalidInput = "hydration add Milo d/221024 v/1000"; // Call methods to test try { @@ -75,7 +75,7 @@ public void parseHydrationInput_inputWrongOrderDateBeforeVolume_invalidInputExce @Test public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in Milo v/##s100 date/221024"; + String invalidInput = "hydration add Milo v/##s100 d/221024"; // Call methods to test try { @@ -90,7 +90,7 @@ public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExcept @Test public void parseHydrationInput_inputNegativeValueForVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "liquids in Milo v/-1000 date/221024"; + String invalidInput = "hydration add Milo v/-1000 d/221024"; // Call methods to test try { diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index 5886a88b46..5af7a22319 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -36,8 +36,8 @@ public void testPrintLiquidListEmpty() { SleepList sleepList = new SleepList(); sleepList.printSleepList(); System.setOut(System.out); - String expectedOutput = "\t Your sleep list is empty." + lineSeparator - + "\t Populate your list with more entries :)" + lineSeparator; + String expectedOutput = "\t Your sleep list is empty. Add new entries to populate your list :)" + + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @Test @@ -49,7 +49,8 @@ public void testPrintSleepListNonEmpty() { sleepList.addSleep("sleep add 7.5 d/110324"); sleepList.printSleepList(); System.setOut(System.out); - String expectedOutput = "\t New sleep record has been successfully added." + lineSeparator + + String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + + "\t \t Date: 110324, Duration: 7.5 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + "\t 1. \t Date: 110324, Duration: 7.5 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); @@ -66,9 +67,12 @@ public void testPrintSleepListMultipleEntries() { sleepList.addSleep("sleep add 4.2"); sleepList.printSleepList(); System.setOut(System.out); - String expectedOutput = "\t New sleep record has been successfully added." + lineSeparator + - "\t New sleep record has been successfully added." + lineSeparator + - "\t New sleep record has been successfully added." + lineSeparator + + String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + + "\t \t Date: 110324, Duration: 7.5 hours" + lineSeparator + + "\t The following entry has been added to your sleep list!" + lineSeparator + + "\t \t Date: 280524, Duration: 8.0 hours" + lineSeparator + + "\t The following entry has been added to your sleep list!" + lineSeparator + + "\t \t Date: N/A, Duration: 4.2 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + "\t 1. \t Date: 110324, Duration: 7.5 hours" + lineSeparator + "\t 2. \t Date: 280524, Duration: 8.0 hours" + lineSeparator + From 2c87f2f1360256463b5d459de32ca2657ab938ac Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 02:44:09 +0800 Subject: [PATCH 131/414] fix checkstyle error --- src/test/java/seedu/lifetrack/HydrationListTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index c815becf8c..1a2c121e63 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test; import seedu.lifetrack.hydration.hydrationlist.HydrationList; -import seedu.lifetrack.ui.HydrationListUI; import static org.junit.jupiter.api.Assertions.assertEquals; From 5f1516c64887cc56462e2eac5fa84124bed69d34 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 03:10:23 +0800 Subject: [PATCH 132/414] update help command output --- .../InvalidInputExceptionMessage.java | 6 ++--- src/main/java/seedu/lifetrack/ui/Ui.java | 27 ++++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 03064edfa2..ffc5bdac8b 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -4,11 +4,11 @@ public class InvalidInputExceptionMessage { private static final String HEADER = "\t Invalid input!\n"; private static final String CALORIES_IN_INPUT = "\t Example input: calories in DESCRIPTION " + - "c/INTEGER_CALORIES date/DATE [m/MACROS]"; + "c/INTEGER_CALORIES d/DATE [m/MACROS]"; private static final String CALORIES_OUT_INPUT = "\t Example input: calories out DESCRIPTION " + - "c/INTEGER_CALORIES date/DATE"; + "c/INTEGER_CALORIES d/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; - private static final String HYDRATION_IN_INPUT = "\t Example input: liquids in Milo v/1000 date/221024" ; + private static final String HYDRATION_IN_INPUT = "\t Example input: hydration add Milo v/1000 d/221024" ; //calories list related methods public static String getCaloriesIncorrectOrderMessage() { diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 7b2fcc0ce0..262b921539 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -135,18 +135,25 @@ public static void handleUnknownInput() { public static void showHelp() { System.out.println("\t LifeTrack Command List:"); System.out.println("\t - help: Displays a list of available commands and their descriptions."); - System.out.println("\t - calories in/out c/ date/: " + - "Adds a calorie gaining/burning entry into the calories tracker."); + printLine(); + System.out.println("\t - calories in c/ d/ " + + "m/[carbohydrates, proteins, fats]: Adds a calorie gaining entry into the calories tracker."); + System.out.println("\t - calories out c/ d/: " + + "Adds a calorie burning entry into the calories tracker."); System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); System.out.println("\t - calories delete : Deletes the entry at the specified index" + " from the calorie list."); - System.out.println("\t - hydration add v/ date/: " + - "Marks the task at the specified index as done."); - System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list.\""); - System.out.println("\t - liquids list: Displays all entries currently stored in the hydration list.\""); - System.out.println("\t - sleep add d/.\""); - System.out.println("\t - sleep list: Displays all entries currently stored in the sleep list.\""); - System.out.println("\t - sleep delete : Deletes the entry at the specified index" + - " from the sleep list."); + printLine(); + System.out.println("\t - hydration add v/ d/: " + + "Adds a hydration entry into the hydration tracker."); + System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list."); + System.out.println("\t - hydration delete : Deletes the hydration entry at the specified index " + + "from the hydration list."); + printLine(); + System.out.println("\t - sleep add d/: " + + "Adds a sleep entry into the sleep tracker."); + System.out.println("\t - sleep list: Displays all entries currently stored in the sleep list."); + System.out.println("\t - sleep delete : Deletes the entry at the specified index " + + "from the sleep list."); } } From fb08c38908c798c27f84191ffde55a336ad93720 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 03:16:26 +0800 Subject: [PATCH 133/414] update JUnit test expected input to reflect new changes in InvalidInputExceptionMessage --- .../seedu/lifetrack/ParserHydrationTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/seedu/lifetrack/ParserHydrationTest.java b/src/test/java/seedu/lifetrack/ParserHydrationTest.java index bc60639e52..3cb2ef12bc 100644 --- a/src/test/java/seedu/lifetrack/ParserHydrationTest.java +++ b/src/test/java/seedu/lifetrack/ParserHydrationTest.java @@ -20,7 +20,7 @@ public void parseHydrationInput_inputContains2Beverages_invalidInputExceptionThr } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -36,7 +36,7 @@ public void parseHydrationInput_inputContains2Volumes_invalidInputExceptionThrow } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -52,7 +52,7 @@ public void parseHydrationInput_inputMissingVolume_invalidInputExceptionThrown() } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -68,7 +68,7 @@ public void parseHydrationInput_inputWrongOrderDateBeforeVolume_invalidInputExce } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have keyed the input in the correct order!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -83,7 +83,7 @@ public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExcept } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -98,7 +98,7 @@ public void parseHydrationInput_inputNegativeValueForVolume_invalidInputExceptio } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -110,7 +110,7 @@ public void parseHydrationInput_missingKeywords_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -121,7 +121,7 @@ public void parseHydrationInput_incompleteInput_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -133,7 +133,7 @@ public void parseHydrationInput_emptyBeverageName_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } @@ -144,7 +144,7 @@ public void parseHydrationInput_emptyVolumeDescription_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: liquids in Milo v/1000 date/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); } } From 639e706dd33a1a7fc8b168471dc0735d66aa9cc3 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 28 Mar 2024 04:06:37 +0800 Subject: [PATCH 134/414] first update --- .../lifetrack/system/parser/ParserUser.java | 2 ++ src/main/java/seedu/lifetrack/user/User.java | 2 ++ .../lifetrack/user/usergoals/UserGoals.java | 28 +++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/main/java/seedu/lifetrack/system/parser/ParserUser.java create mode 100644 src/main/java/seedu/lifetrack/user/User.java create mode 100644 src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java new file mode 100644 index 0000000000..d85e7b8dec --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -0,0 +1,2 @@ +package seedu.lifetrack.system.parser;public class UserParses { +} diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java new file mode 100644 index 0000000000..494d4d22c4 --- /dev/null +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -0,0 +1,2 @@ +package seedu.lifetrack.user.usergoals;public class User { +} diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java new file mode 100644 index 0000000000..2177c1d3c5 --- /dev/null +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -0,0 +1,28 @@ +package seedu.lifetrack.usergoals; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + + +public class UserGoals { + + + public static void getGoals() { + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("https://fitness-api.p.rapidapi.com/fitness")) + .header("content-type", "application/x-www-form-urlencoded") + .header("X-RapidAPI-Key", "313560bcc6msh96f48210f860abep1be49djsn7c3a2058360d") + .header("X-RapidAPI-Host", "fitness-api.p.rapidapi.com") + .method("POST", HttpRequest.BodyPublishers.ofString("height=190&weight=80&age=30&gender=male&exercise=little&goal=maintenance")) + .build(); + HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(response.body()); + } catch (IOException | InterruptedException e) { + System.out.println("OOPS"); + } + } +} From 3209e334db4dcebe875addfb5136a718e9773957 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 28 Mar 2024 04:06:52 +0800 Subject: [PATCH 135/414] first update --- src/main/java/seedu/lifetrack/LifeTrack.java | 4 +- .../system/exceptions/ErrorMessages.java | 17 ++- .../lifetrack/system/parser/ParserUser.java | 94 ++++++++++++++- src/main/java/seedu/lifetrack/ui/Ui.java | 25 +++- src/main/java/seedu/lifetrack/user/User.java | 111 +++++++++++++++++- .../lifetrack/user/usergoals/UserGoals.java | 28 ++++- .../java/seedu/lifetrack/LiquidListTest.java | 1 - src/test/java/seedu/lifetrack/UITest.java | 4 +- 8 files changed, 265 insertions(+), 19 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index a1c0611922..90d36dc331 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -3,6 +3,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.liquids.liquidlist.LiquidList; import seedu.lifetrack.ui.Ui; +import seedu.lifetrack.user.User; import java.util.Scanner; @@ -15,9 +16,10 @@ public static void main(String[] args) { CalorieList calorieList = new CalorieList(); LiquidList liquidList = new LiquidList(); Scanner in = new Scanner(System.in); + User user = new User(); Ui.sayHello(); assert true : "dummy assertion set to fail"; - Ui.readUserInput(calorieList,liquidList); + Ui.readUserInput(calorieList,liquidList,user); Ui.byeMessage(); in.close(); } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index c0cae070a5..dcff60719e 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -2,12 +2,12 @@ public class ErrorMessages { - public static void printIndexOutOfBoundsError(){ + public static void printIndexOutOfBoundsError() { System.out.println("\t Sorry, this index is invalid. Please enter a positive integer " + "within the size of the list."); } - public static void printNumberFormatError(){ + public static void printNumberFormatError() { System.out.println("\t Please enter a valid number within the command"); } @@ -18,4 +18,17 @@ public static String getIncorrectCaloriesInputMessage() { public static String getIncorrectMacrosInputMessage() { return "\t Please input only positive integers into the macronutrients field!"; } + + public static String getInvalidNumberOfSetUpInputs() { + return "\t Sorry, this command is invalid. Please enter the setup command in the following format " + + "user setup {NAME} h/{HEIGHT} w/{WEIGHT} a/{AGE} s/{SEX} e/{ENERGY LEVELS} g/{GOAL}"; + } + + public static String getInvalidGoalNumberMessage() { + return "\t Invalid input for goal number. Please enter a number between 1 and 7."; + } + + public static String getInvalidExerciseLevelsNumberMessage() { + return "\t Invalid input for exercise level. Please enter a number between 1 and 5."; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index d85e7b8dec..af504343d0 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -1,2 +1,94 @@ -package seedu.lifetrack.system.parser;public class UserParses { +package seedu.lifetrack.system.parser; + +import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.user.User; + +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidExerciseLevelsNumberMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidNumberOfSetUpInputs; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGoalNumberMessage; + +public class ParserUser { + private static final int LABEL_SIZE = 2; + + public static void parseSetUp(String input, User user) throws InvalidInputException, NumberFormatException { + int heightIndex = input.indexOf("h/"); + int weightIndex = input.indexOf("w/"); + int ageIndex = input.indexOf("a/"); + int sexIndex = input.indexOf("s/"); + int exerciseLevelsIndex = input.indexOf("e/"); + int goalIndex = input.indexOf("g/"); + + if (heightIndex == -1 || weightIndex == -1 || ageIndex == -1 || sexIndex == -1 + || exerciseLevelsIndex == -1 || goalIndex == -1) { + throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); + } + checkSetUpInputsCorrectOrder(heightIndex, weightIndex, ageIndex, sexIndex, exerciseLevelsIndex, goalIndex); + + String[] parts = input.split("h/|w/|a/|s/|e/|g/"); + String name = parts[0].trim(); + int height = Integer.parseInt(parts[1].trim()); + int weight = Integer.parseInt(parts[2].trim()); + int age = Integer.parseInt(parts[3].trim()); + String sex = parts[4].trim().toLowerCase(); + String exerciseLevels = parseExerciseLevels(parts[5].trim()); + String goal = parseGoalIndex(parts[6].trim()); + user.setName(name); + user.setHeight(height); + user.setWeight(weight); + user.setAge(age); + user.setSex(sex); + user.setExerciseLevels(exerciseLevels); + user.setGoal(goal); + } + + private static String parseGoalIndex(String input) throws InvalidInputException { + try { + int goalNumber = Integer.parseInt(input); + if (goalNumber == 1) { + return "fatloss reckless"; + } else if (goalNumber == 2) { + return "fatloss aggressive"; + } else if (goalNumber == 3) { + return "fatloss moderate"; + } else if (goalNumber == 4) { + return "moderate"; + } else if (goalNumber == 5) { + return "bulking slow"; + } else if (goalNumber == 6) { + return "bulking normal"; + } else { + return "bulking aggressive"; + } + } catch (NumberFormatException e) { + throw new InvalidInputException(getInvalidGoalNumberMessage()); + } + } + + private static String parseExerciseLevels(String input) throws InvalidInputException { + try { + int levelInNumber = Integer.parseInt(input); + if (levelInNumber == 1) { + return "little"; + } else if (levelInNumber == 2) { + return "light"; + } else if (levelInNumber == 3) { + return "moderate"; + } else if (levelInNumber == 4) { + return "heavy"; + } else { + return "veryheavy"; + } + } catch (NumberFormatException e) { + throw new InvalidInputException(getInvalidExerciseLevelsNumberMessage()); + } + } + + private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightIndex, int ageIndex, int sexIndex, + int exerciseLevelsIndex, int goalIndex) + throws InvalidInputException { + if (!(heightIndex < weightIndex && weightIndex < ageIndex && sexIndex < exerciseLevelsIndex + && exerciseLevelsIndex < goalIndex)) { + throw new InvalidInputException(getInvalidExerciseLevelsNumberMessage()); + } + } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 51ff681fc7..00e0711de0 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -2,6 +2,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.user.User; import java.util.Scanner; @@ -29,20 +30,22 @@ public class Ui { /** * Reads in the input from the user + * * @param calorieList list containing all entries pertinent to calories - * @param liquidList list containing all entries pertinent to liquids + * @param liquidList list containing all entries pertinent to liquids */ - public static void readUserInput(CalorieList calorieList, LiquidList liquidList) { + public static void readUserInput(CalorieList calorieList, LiquidList liquidList, User user) { String line; do { line = new Scanner(System.in).nextLine(); - handleUserInput(line, calorieList, liquidList); + handleUserInput(line, calorieList, liquidList, user); } while (!line.equalsIgnoreCase("bye")); } /** - * handles input from the user - * @param line input from the user + * handles input from the user + * + * @param line input from the user * @param calorieList list containing all entries pertinent to calories */ public static void handleCaloriesInput(String line, CalorieList calorieList) { @@ -71,7 +74,7 @@ public static void handleLiquidsInput(String line, LiquidList liquidsList) { } } - public static void handleUserInput(String line, CalorieList calorieList, LiquidList liquidList) { + public static void handleUserInput(String line, CalorieList calorieList, LiquidList liquidList, User user) { if (!line.startsWith("bye")) { printLine(); line = line.trim().toLowerCase(); @@ -83,6 +86,8 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL showHelp(); } else if (line.startsWith("liquids")) { handleLiquidsInput(line, liquidList); + } else if (line.startsWith("user")) { + handleUserCommands(line, user); } else { handleUnknownInput(); } @@ -90,6 +95,14 @@ public static void handleUserInput(String line, CalorieList calorieList, LiquidL } } + public static void handleUserCommands(String line, User user) { + if (line.contains("setup")) { + user.setUp(line); + } else if (line.contains("progress")) { + user.getHealthInfo(); + } + } + public static void sayHello() { System.out.println(WHITESPACE + "Hello from\n\n" + logo); System.out.println(WHITESPACE + "How can I help you today?"); diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 494d4d22c4..de4ca39724 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -1,2 +1,111 @@ -package seedu.lifetrack.user.usergoals;public class User { +package seedu.lifetrack.user; + +import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.user.usergoals.UserGoals; + +import static seedu.lifetrack.system.parser.ParserUser.parseSetUp; + +public class User { + protected String name; + protected int height; + protected int weight; + protected int age; + protected String sex; + protected String exerciseLevels; + protected String goal; + protected int caloriesRequired; + + public User(String name, int height, int weight, int age, String sex, String exerciseLevels, String goal) { + this.name = name; + this.height = height; + this.weight = weight; + this.age = age; + this.sex = sex; + this.exerciseLevels = exerciseLevels; + this.goal = goal; + } + + public User(String name, int height, int weight, int age, String sex) { + this.name = name; + this.height = height; + this.weight = weight; + this.age = age; + this.sex = sex; + } + + public User() { + + } + + public void setUp(String line) { + try { + parseSetUp(line, this); + } catch (InvalidInputException e) { + System.out.println(e.getMessage()); + } + } + + public void setName(String name) { + this.name = name; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public void setAge(int age) { + this.age = age; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public void setExerciseLevels(String exerciseLevels) { + this.exerciseLevels = exerciseLevels; + } + + public void setGoal(String goal) { + this.goal = goal; + } + + public int getHeight() { + return height; + } + + public int getWeight() { + return weight; + } + + public int getAge() { + return age; + } + + public String getSex() { + return sex; + } + + public String getExerciseLevels() { + return exerciseLevels; + } + + public String getGoal() { + return goal; + } + + public void getHealthInfo() { + UserGoals.getHealthInfo(this); + } + + public void setCaloriesRequired(int caloriesRequired) { + this.caloriesRequired = caloriesRequired; + } + + public int getCaloriesRequired() { + return caloriesRequired; + } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 2177c1d3c5..b950e48299 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -1,4 +1,6 @@ -package seedu.lifetrack.usergoals; +package seedu.lifetrack.user.usergoals; + +import seedu.lifetrack.user.User; import java.io.IOException; import java.net.URI; @@ -6,21 +8,35 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; - public class UserGoals { + private static HttpResponse response; + private static final int JSON_HEADING_SIZE = 67; + private static final int CALORIES_LENGTH = 4; - public static void getGoals() { + public static void getHealthInfo(User user) { try { + System.out.println(System.getenv("KEY")); + String requestBody = "height=" + user.getHeight() + "&" + + "weight=" + user.getWeight() + "&" + + "age=" + user.getAge() + "&" + + "gender=" + user.getSex() + "&" + + "exercise=" + user.getExerciseLevels() + "&" + + "goal=" + user.getGoal().replace(" ", "_"); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://fitness-api.p.rapidapi.com/fitness")) .header("content-type", "application/x-www-form-urlencoded") .header("X-RapidAPI-Key", "313560bcc6msh96f48210f860abep1be49djsn7c3a2058360d") .header("X-RapidAPI-Host", "fitness-api.p.rapidapi.com") - .method("POST", HttpRequest.BodyPublishers.ofString("height=190&weight=80&age=30&gender=male&exercise=little&goal=maintenance")) + .method("POST", HttpRequest.BodyPublishers.ofString(requestBody)) .build(); - HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); - System.out.println(response.body()); + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandlers.ofString()); + int indexOfCalories = response.body().indexOf("neededEnergy") + JSON_HEADING_SIZE; + int calories = Integer.parseInt(response.body() + .substring(indexOfCalories, indexOfCalories + CALORIES_LENGTH)); + System.out.println("You should consume " + calories + " calories a day to hit your goals!"); + user.setCaloriesRequired(calories); } catch (IOException | InterruptedException e) { System.out.println("OOPS"); } diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java index 2cbac4f883..59bf380427 100644 --- a/src/test/java/seedu/lifetrack/LiquidListTest.java +++ b/src/test/java/seedu/lifetrack/LiquidListTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static seedu.lifetrack.ui.LiquidListUI.WHITESPACE; - import java.io.ByteArrayOutputStream; import java.io.PrintStream; diff --git a/src/test/java/seedu/lifetrack/UITest.java b/src/test/java/seedu/lifetrack/UITest.java index 7965e0143a..5941ef5458 100644 --- a/src/test/java/seedu/lifetrack/UITest.java +++ b/src/test/java/seedu/lifetrack/UITest.java @@ -8,6 +8,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.ui.Ui; import seedu.lifetrack.liquids.liquidlist.LiquidList; +import seedu.lifetrack.user.User; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -31,8 +32,9 @@ public void restoreStreams() { public void handleUserInput_inputBye_printByeMessage() { CalorieList calorieList = new CalorieList(); LiquidList liquidList = new LiquidList(); + User user = new User(); String input = "bye"; - Ui.handleUserInput(input, calorieList, liquidList); + Ui.handleUserInput(input, calorieList, liquidList,user); assertEquals("", outContent.toString()); } } From 5ad00db56427806f6702f075923d76df501176a4 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 28 Mar 2024 04:35:09 +0800 Subject: [PATCH 136/414] merged --- .../java/seedu/lifetrack/system/exceptions/ErrorMessages.java | 2 +- src/main/java/seedu/lifetrack/ui/Ui.java | 3 ++- src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java | 3 +-- src/test/java/seedu/lifetrack/LiquidListTest.java | 0 4 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 src/test/java/seedu/lifetrack/LiquidListTest.java diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index 1bda4eceb2..f01228c069 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -27,7 +27,7 @@ public static String getIncorrectMacrosInputMessage() { public static String getInvalidNumberOfSetUpInputs() { return "\t Sorry, this command is invalid. Please enter the setup command in the following format " + - "user setup {NAME} h/{HEIGHT} w/{WEIGHT} a/{AGE} s/{SEX} e/{ENERGY LEVELS} g/{GOAL}"; + "user setup {NAME} h/{HEIGHT} w/{WEIGHT} a/{AGE} s/{SEX} e/{EXERCISE LEVELS} g/{GOAL}"; } public static String getInvalidGoalNumberMessage() { diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index e42c9a1154..a349d647a7 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -35,7 +35,8 @@ public class Ui { * @param calorieList list containing all entries pertinent to calories * @param hydrationList list containing all entries pertinent to liquids */ - public static void readUserInput(CalorieList calorieList, HydrationList hydrationList, User user, SleepList sleepList) { + public static void readUserInput(CalorieList calorieList, HydrationList hydrationList, + User user, SleepList sleepList) { String line; do { line = new Scanner(System.in).nextLine(); diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index b950e48299..c5da3d12fe 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -16,7 +16,6 @@ public class UserGoals { public static void getHealthInfo(User user) { try { - System.out.println(System.getenv("KEY")); String requestBody = "height=" + user.getHeight() + "&" + "weight=" + user.getWeight() + "&" + "age=" + user.getAge() + "&" + @@ -35,7 +34,7 @@ public static void getHealthInfo(User user) { int indexOfCalories = response.body().indexOf("neededEnergy") + JSON_HEADING_SIZE; int calories = Integer.parseInt(response.body() .substring(indexOfCalories, indexOfCalories + CALORIES_LENGTH)); - System.out.println("You should consume " + calories + " calories a day to hit your goals!"); + System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); user.setCaloriesRequired(calories); } catch (IOException | InterruptedException e) { System.out.println("OOPS"); diff --git a/src/test/java/seedu/lifetrack/LiquidListTest.java b/src/test/java/seedu/lifetrack/LiquidListTest.java deleted file mode 100644 index e69de29bb2..0000000000 From 6ff95c5719e4eaaf2a505c88bf4ebadfa6f969c0 Mon Sep 17 00:00:00 2001 From: Rex <76645229+rexyyong@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:34:51 +0800 Subject: [PATCH 137/414] Add calories list description in developer guide --- docs/DeveloperGuide.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6afb84df3c..10c16081a1 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -20,6 +20,10 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie - Step 3: The created `InputEntry` instance is added into the `ArrayList` attribute of the `CalorieList`. +### List Calories record feature + +The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. + ## Product scope ### Target user profile From 9f8c219d1438a8ed6081ddc86cccf89d14185199 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 28 Mar 2024 13:52:44 +0800 Subject: [PATCH 138/414] Add PlantUML diagram for calories list feature --- docs/team/CaloriesListClassDiagram.puml | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/team/CaloriesListClassDiagram.puml diff --git a/docs/team/CaloriesListClassDiagram.puml b/docs/team/CaloriesListClassDiagram.puml new file mode 100644 index 0000000000..9d2c773291 --- /dev/null +++ b/docs/team/CaloriesListClassDiagram.puml @@ -0,0 +1,37 @@ +@startuml + +class LifeTrack { + + main(args: String[]): void +} + +package ui { + class Ui { + + readUserInput(calorieList: calories.CalorieList, hydrationList: hydration.HydrationList, user: user.User, sleepList: sleep.SleepList): void + + handleCaloriesInput(line: String, calorieList: calories.CalorieList): void + } +} + +package calories { + package calorielist { + class CalorieList { + - ArrayList calorieArrayList + + printCalorieList(): void + } + + class CalorieListUi { + + calorieListHeader(): void + + emptyListMessage(): void + } + } +} + + + +LifeTrack --> calories.calorielist.CalorieList +LifeTrack -[dotted]-> ui.Ui +ui.Ui -[dotted]-> calories.calorielist.CalorieList +CalorieList -[dotted]-> CalorieListUi + +@enduml + + From 27f6b16ba9e1d08fc837195a41f4d65e05e77114 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 28 Mar 2024 15:15:48 +0800 Subject: [PATCH 139/414] Add plantUML diagram to DeveloperGuide.md --- docs/DeveloperGuide.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 10c16081a1..9b4509375c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -23,7 +23,40 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie ### List Calories record feature The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. - +```plantuml +@startuml + +class LifeTrack { + + main(args: String[]): void +} + +package ui { + class Ui { + + readUserInput(calorieList: calories.CalorieList, hydrationList: hydration.HydrationList, user: user.User, sleepList: sleep.SleepList): void + + handleCaloriesInput(line: String, calorieList: calories.CalorieList): void + } +} + +package calories { + package calorielist { + class CalorieList { + - ArrayList calorieArrayList + + printCalorieList(): void + } + + class CalorieListUi { + + calorieListHeader(): void + + emptyListMessage(): void + } + } +} + +LifeTrack --> calories.calorielist.CalorieList +LifeTrack -[dotted]-> ui.Ui +ui.Ui -[dotted]-> calories.calorielist.CalorieList +CalorieList -[dotted]-> CalorieListUi +@enduml +``` ## Product scope ### Target user profile From a4138e9c9ba857c75d9683fea98cfc8fd4e500b7 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 28 Mar 2024 15:33:59 +0800 Subject: [PATCH 140/414] shift CaloriesListClassDiagram ourside of docs team folder --- docs/{team => }/CaloriesListClassDiagram.puml | 0 docs/DeveloperGuide.md | 2 ++ 2 files changed, 2 insertions(+) rename docs/{team => }/CaloriesListClassDiagram.puml (100%) diff --git a/docs/team/CaloriesListClassDiagram.puml b/docs/CaloriesListClassDiagram.puml similarity index 100% rename from docs/team/CaloriesListClassDiagram.puml rename to docs/CaloriesListClassDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 9b4509375c..ac18783028 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -23,6 +23,8 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie ### List Calories record feature The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. + +[//]: # (![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://)) ```plantuml @startuml From 75f52338fee30d1250202d99d8ddafd1a6b0b0df Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 28 Mar 2024 15:40:49 +0800 Subject: [PATCH 141/414] Enable rendering of uml diagram on github markdown --- docs/DeveloperGuide.md | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index ac18783028..aab32864bd 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -23,42 +23,8 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie ### List Calories record feature The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. +![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/DevGuideRex/docs/CaloriesListClassDiagram.puml) -[//]: # (![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://)) -```plantuml -@startuml - -class LifeTrack { - + main(args: String[]): void -} - -package ui { - class Ui { - + readUserInput(calorieList: calories.CalorieList, hydrationList: hydration.HydrationList, user: user.User, sleepList: sleep.SleepList): void - + handleCaloriesInput(line: String, calorieList: calories.CalorieList): void - } -} - -package calories { - package calorielist { - class CalorieList { - - ArrayList calorieArrayList - + printCalorieList(): void - } - - class CalorieListUi { - + calorieListHeader(): void - + emptyListMessage(): void - } - } -} - -LifeTrack --> calories.calorielist.CalorieList -LifeTrack -[dotted]-> ui.Ui -ui.Ui -[dotted]-> calories.calorielist.CalorieList -CalorieList -[dotted]-> CalorieListUi -@enduml -``` ## Product scope ### Target user profile From 33fc9caf51ea05b812f19044e492088c5485b07c Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 28 Mar 2024 15:59:54 +0800 Subject: [PATCH 142/414] Add description for calories list feature in developer guide --- docs/DeveloperGuide.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index aab32864bd..e3dd67a523 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -20,9 +20,14 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie - Step 3: The created `InputEntry` instance is added into the `ArrayList` attribute of the `CalorieList`. -### List Calories record feature +### Calories list feature The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. + +The `printCalorieList()` function iterates through the `calorieArrayList` and prints out the Entries according to its order in the Array List. + +The Class diagram for Calories list feature is shown below. Unrelated attributes and Classes were excluded. + ![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/DevGuideRex/docs/CaloriesListClassDiagram.puml) ## Product scope From f083ddb90d9177c61f18690018beda1bc26e5029 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:09:31 +0800 Subject: [PATCH 143/414] Add hydration DG --- docs/DeveloperGuide.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6afb84df3c..e1e7b20f8b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -20,6 +20,21 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie - Step 3: The created `InputEntry` instance is added into the `ArrayList` attribute of the `CalorieList`. +### Parsing user input for hydration entries + +This functionality is facilitated by `ParserHydration`. It implements one operation, namely: +- `ParserHydration#parseHydrationInput(String input)` + +This operation is exposed in the `HydrationList` class as `HydrationList#addEntry(String)`. + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `calories in Milo v/100 date/270324` in the terminal, + the string is sent to `HydrationList#addEntry(String)`, which calls `ParserHydration#parseHydrationInput(String)`. + +- Step 2: Using `String.split()`, the method extracts information such as the description, volume of beverage, and date of entry. The obtained information is sent to the private method `ParserHydration#makeNewInputEntry(String, int, String)` to create a new entry of class `HydrationEntry` that extends `Entry`. + +- Step 3: The created `HydrationEntry` instance is added into the `ArrayList` attribute of the `HydrationList`. + ## Product scope ### Target user profile From 898d88d97c234c878540f55401bb83020a99f3cb Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:10:55 +0800 Subject: [PATCH 144/414] hydration DG --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index e1e7b20f8b..d7a26d2786 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -6,7 +6,7 @@ ## Design & implementation ### Parsing user input for caloric entries - + This functionality is facilitated by `ParserCalories`. It implements one operation, namely: - `ParserCalories#parseCaloriesInput(String)` From 71a43c74e716eb416703a58913ea70dc0159bcb4 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 28 Mar 2024 17:20:08 +0800 Subject: [PATCH 145/414] DG --- docs/DeveloperGuide.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6afb84df3c..6fdac72784 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -20,6 +20,38 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie - Step 3: The created `InputEntry` instance is added into the `ArrayList` attribute of the `CalorieList`. + +### Calculating calorie requirements based on a user's goals + +#### Implementation + +This functionality is facilitated by `UserGoals`. It implements one operation, namely: +- `UserGoals#getHealthInfo(User)` + +This operation is exposed in the `User` class as `User#getHealhInfo()`. + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `user progress` in the terminal, + the string is sent to `User#getHealthInfo()`, which calls `UserGoals#getHealthInfo(User)`. + +- Step 2: The method retrieves the user's information such as his height, weight, age, gender, exercise levels and intended goal. + +- Step 3: Using these information, the method creates a `requestBody` `String`. + +- Step 4: The created `requestBody` is used to send a `HttpRequest` to RapidAPI's Fitness API, and the response is parsed to determine the number of calories a user needs to consume according to their personal goals. + +- Step 5: This value is set to `User.caloriesRequired` by `User#setCaloriesRequired(int)`. + +#### Design considerations + +- **Alternative 1 (current choice):** Uses an API to get the calories needed + - Pros: No need to figure out the optimal algorithm + - Cons: Need to parse response to sieve out necessary information + +- **Alternative 2:** Uses an algorithm to find the number of calories needed + - Pros: Not dependent on external APIs + - Cons: Need to come up with an algorithm to use + ## Product scope ### Target user profile From 7883eda91b440ff186bf1b5f1feaeb498a9ab584 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 17:51:54 +0800 Subject: [PATCH 146/414] implement file storage for User --- src/main/java/seedu/lifetrack/LifeTrack.java | 2 +- .../lifetrack/system/parser/ParserUser.java | 4 +- .../lifetrack/system/storage/FileHandler.java | 36 ++++++++++++ src/main/java/seedu/lifetrack/user/User.java | 58 ++++++++++++++++--- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/src/main/java/seedu/lifetrack/LifeTrack.java b/src/main/java/seedu/lifetrack/LifeTrack.java index cd6bb04e30..5bb315ccde 100644 --- a/src/main/java/seedu/lifetrack/LifeTrack.java +++ b/src/main/java/seedu/lifetrack/LifeTrack.java @@ -13,7 +13,7 @@ public class LifeTrack { public static CalorieList calorieList = new CalorieList("data/caloriesData.txt"); public static HydrationList hydrationList = new HydrationList("data/hydrationData.txt"); public static SleepList sleepList = new SleepList("data/sleepData.txt"); - public static User user = new User(); + public static User user = new User("data/userData.txt"); /** * Main entry-point for the java.lifetrack.LifeTrack application. */ diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index af504343d0..57f5244d90 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -8,7 +8,6 @@ import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGoalNumberMessage; public class ParserUser { - private static final int LABEL_SIZE = 2; public static void parseSetUp(String input, User user) throws InvalidInputException, NumberFormatException { int heightIndex = input.indexOf("h/"); @@ -84,8 +83,7 @@ private static String parseExerciseLevels(String input) throws InvalidInputExcep } private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightIndex, int ageIndex, int sexIndex, - int exerciseLevelsIndex, int goalIndex) - throws InvalidInputException { + int exerciseLevelsIndex, int goalIndex) throws InvalidInputException { if (!(heightIndex < weightIndex && weightIndex < ageIndex && sexIndex < exerciseLevelsIndex && exerciseLevelsIndex < goalIndex)) { throw new InvalidInputException(getInvalidExerciseLevelsNumberMessage()); diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 53ce788b72..0340186ad6 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -14,6 +14,7 @@ import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.sleep.sleeplist.SleepEntry; import seedu.lifetrack.system.exceptions.ErrorMessages; +import seedu.lifetrack.user.User; public class FileHandler { @@ -34,6 +35,16 @@ public class FileHandler { //sleep list constants private static final int DURATION_INDEX = 2; + //user data constants + private static final int NAME_INDEX = 0; + private static final int HEIGHT_INDEX = 1; + private static final int WEIGHT_INDEX = 2; + private static final int AGE_INDEX = 3; + private static final int SEX_INDEX = 4; + private static final int EXERCISE_INDEX = 5; + private static final int GOAL_INDEX = 6; + private static final int REQ_CAL_INDEX = 7; + private String filePath; public FileHandler(String filePath) { @@ -46,6 +57,14 @@ private void writeToFile(String textToAdd) throws IOException { fw.close(); } + public void writeUserData(User user) { + try { + writeToFile(user.toFileFriendlyString()); + } catch (IOException e) { + System.out.println(ErrorMessages.getIOExceptionMessage()); + } + } + public void writeEntries(ArrayList entries) { try { String newData = ""; @@ -115,4 +134,21 @@ public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { } return entries; } + + public ArrayList getUserDataFromFile() throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList data = new ArrayList<>(); + String line = s.nextLine(); + String[] words = line.split(";"); + data.add(words[NAME_INDEX]); + data.add(words[HEIGHT_INDEX]); + data.add(words[WEIGHT_INDEX]); + data.add(words[AGE_INDEX]); + data.add(words[SEX_INDEX]); + data.add(words[EXERCISE_INDEX]); + data.add(words[GOAL_INDEX]); + data.add(words[REQ_CAL_INDEX]); + return data; + } } diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index de4ca39724..ebd0fdb261 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -1,19 +1,36 @@ package seedu.lifetrack.user; +import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.storage.FileHandler; import seedu.lifetrack.user.usergoals.UserGoals; import static seedu.lifetrack.system.parser.ParserUser.parseSetUp; +import java.io.FileNotFoundException; +import java.util.ArrayList; + public class User { - protected String name; - protected int height; - protected int weight; - protected int age; - protected String sex; - protected String exerciseLevels; - protected String goal; - protected int caloriesRequired; + + private FileHandler fileHandler; + private String name; + private int height; + private int weight; + private int age; + private String sex; + private String exerciseLevels; + private String goal; + private int caloriesRequired; + + //user data constants + private static final int NAME_INDEX = 0; + private static final int HEIGHT_INDEX = 1; + private static final int WEIGHT_INDEX = 2; + private static final int AGE_INDEX = 3; + private static final int SEX_INDEX = 4; + private static final int EXERCISE_INDEX = 5; + private static final int GOAL_INDEX = 6; + private static final int REQ_CAL_INDEX = 7; public User(String name, int height, int weight, int age, String sex, String exerciseLevels, String goal) { this.name = name; @@ -33,13 +50,33 @@ public User(String name, int height, int weight, int age, String sex) { this.sex = sex; } + //constructor for JUnit tests public User() { } + //constructor for usage in terminal + public User(String filePath) { + try { + fileHandler = new FileHandler(filePath); + ArrayList data = fileHandler.getUserDataFromFile(); + name = data.get(NAME_INDEX); + height = Integer.parseInt(data.get(HEIGHT_INDEX)); + weight = Integer.parseInt(data.get(WEIGHT_INDEX)); + age = Integer.parseInt(data.get(AGE_INDEX)); + sex = data.get(SEX_INDEX); + exerciseLevels = data.get(EXERCISE_INDEX); + goal = data.get(GOAL_INDEX); + caloriesRequired = Integer.parseInt(data.get(REQ_CAL_INDEX)); + } catch (FileNotFoundException e) { + System.out.println(ErrorMessages.getFileNotFoundMessage()); + } + } + public void setUp(String line) { try { parseSetUp(line, this); + fileHandler.writeUserData(this); } catch (InvalidInputException e) { System.out.println(e.getMessage()); } @@ -108,4 +145,9 @@ public void setCaloriesRequired(int caloriesRequired) { public int getCaloriesRequired() { return caloriesRequired; } + + public String toFileFriendlyString() { + return String.format(name + ";" + height + ";" + weight + ";" + age + ";" + sex + ";" + + exerciseLevels + ";" + goal + ";" + caloriesRequired); + } } From 76b249543369394cb1fd65aef74fda897cfc2c1a Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 17:59:30 +0800 Subject: [PATCH 147/414] remove static from constants in User --- src/main/java/seedu/lifetrack/user/User.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index ebd0fdb261..136b0d89c7 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -23,14 +23,14 @@ public class User { private int caloriesRequired; //user data constants - private static final int NAME_INDEX = 0; - private static final int HEIGHT_INDEX = 1; - private static final int WEIGHT_INDEX = 2; - private static final int AGE_INDEX = 3; - private static final int SEX_INDEX = 4; - private static final int EXERCISE_INDEX = 5; - private static final int GOAL_INDEX = 6; - private static final int REQ_CAL_INDEX = 7; + private final int NAME_INDEX = 0; + private final int HEIGHT_INDEX = 1; + private final int WEIGHT_INDEX = 2; + private final int AGE_INDEX = 3; + private final int SEX_INDEX = 4; + private final int EXERCISE_INDEX = 5; + private final int GOAL_INDEX = 6; + private final int REQ_CAL_INDEX = 7; public User(String name, int height, int weight, int age, String sex, String exerciseLevels, String goal) { this.name = name; From c49f9cf0c15aeac29daea0e7539397879329ea33 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 28 Mar 2024 18:04:02 +0800 Subject: [PATCH 148/414] fix text-ui-test --- 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 7110c6a8f3..14048a91a8 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,6 +1,7 @@ No file found! The initialised list will be empty. No file found! The initialised list will be empty. No file found! The initialised list will be empty. + No file found! The initialised list will be empty. Hello from From caa88bc413ffb0cf95da27d58d73a044a232a1bb Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:43:27 +0800 Subject: [PATCH 149/414] add delete calories feature into DeveloperGuide --- docs/DeveloperGuide.md | 26 +++++++++++++++++++++++--- docs/caloriesDeleteUML.jpg | Bin 0 -> 52154 bytes 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 docs/caloriesDeleteUML.jpg diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 31acbda738..545b5bbb1a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -20,7 +20,7 @@ the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalorie - Step 3: The created `InputEntry` instance is added into the `ArrayList` attribute of the `CalorieList`. -### Calculating calorie requirements based on a user's goals +### Calculating calorie requirements based on a user`s goals #### Implementation @@ -33,11 +33,11 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 1: When the user inputs the command `user progress` in the terminal, the string is sent to `User#getHealthInfo()`, which calls `UserGoals#getHealthInfo(User)`. -- Step 2: The method retrieves the user's information such as his height, weight, age, gender, exercise levels and intended goal. +- Step 2: The method retrieves the user`s information such as his height, weight, age, gender, exercise levels and intended goal. - Step 3: Using these information, the method creates a `requestBody` `String`. -- Step 4: The created `requestBody` is used to send a `HttpRequest` to RapidAPI's Fitness API, and the response is parsed to determine the number of calories a user needs to consume according to their personal goals. +- Step 4: The created `requestBody` is used to send a `HttpRequest` to RapidAPI`s Fitness API, and the response is parsed to determine the number of calories a user needs to consume according to their personal goals. - Step 5: This value is set to `User.caloriesRequired` by `User#setCaloriesRequired(int)`. @@ -76,6 +76,26 @@ The Class diagram for Calories list feature is shown below. Unrelated attributes ![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/DevGuideRex/docs/CaloriesListClassDiagram.puml) +### Calories delete feature + +The `calories delete` feature can delete the calories record at specific index of calorie list. This functionality is facilitated by `CaloriesList`. It implements one operation, namely: +- `deleteEntry(String line)` + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `calories delete INDEX` in the terminal, the string is sent to `Ui#handleUserInput()`, which will call `Ui#handleCaloriesInput()`. + +- Step 2: After the `Ui#handleCaloriesInput()` matching `delete calories` key word, the string will be passed into deleteEntry(String line) to execute delete process. + +- Step 3: The string will be divided to two substrings according to the command syntax. Index will be tried to get from the second substring by `Integer.parseInt()`. + +- Step 4: The calories record (`Entry`) stored in the `ArrayList caloriesList` will be deleted by calling `calorieArrayList.remove((index-1));` and a successful deleting message will be shown in terminal by calling `CalorieListUi#successfulDeletedMessage(toDelete)` + +- Step 5: The latest calories list will be updated to saving file by calling `CalorieList#updateFile()`. + +The Class diagram for Calories delete feature is shown below: + +![CaloriesDeleteClassDiagram](docs/caloriesDeleteUML.jpg) + ## Product scope ### Target user profile diff --git a/docs/caloriesDeleteUML.jpg b/docs/caloriesDeleteUML.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c36887bc8542f8c1b4818e5ec9b69db92dc1fc05 GIT binary patch literal 52154 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 F{{YJDXKDZd literal 0 HcmV?d00001 From 706c7d11de44452eef39cca650ed122b1712d95b Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:45:45 +0800 Subject: [PATCH 150/414] Update DeveloperGuide.md fix img path --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 545b5bbb1a..396d54b284 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -94,7 +94,7 @@ Given below is an example usage scenario and how this mechanism behaves at every The Class diagram for Calories delete feature is shown below: -![CaloriesDeleteClassDiagram](docs/caloriesDeleteUML.jpg) +![CaloriesDeleteClassDiagram]([docs/caloriesDeleteUML.jpg](https://github.com/a-wild-chocolate/tp/blob/master/docs/caloriesDeleteUML.jpg)) ## Product scope ### Target user profile From 0e297319de93414722ef471aa31956af5796d78e Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:46:27 +0800 Subject: [PATCH 151/414] Update DeveloperGuide.md --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 396d54b284..e6f17d726e 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -94,7 +94,7 @@ Given below is an example usage scenario and how this mechanism behaves at every The Class diagram for Calories delete feature is shown below: -![CaloriesDeleteClassDiagram]([docs/caloriesDeleteUML.jpg](https://github.com/a-wild-chocolate/tp/blob/master/docs/caloriesDeleteUML.jpg)) +![CaloriesDeleteClassDiagram](https://github.com/a-wild-chocolate/tp/blob/master/docs/caloriesDeleteUML.jpg) ## Product scope ### Target user profile From 45b021e2c9376374f8e1ed7847fb67641d4e273b Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:28:51 +0800 Subject: [PATCH 152/414] Add Date format for Class ParserCalories --- .../system/parser/ParserCalories.java | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index f990b3d022..ded1acbc06 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -16,6 +16,9 @@ import seedu.lifetrack.Entry; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + public class ParserCalories { private static final int CARBS_IDX = 0; @@ -47,19 +50,27 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio assert dateIndex != -1 : "The d/ keyword should exist!"; checkKeywordsCorrectlyOrdered(caloriesIndex, dateIndex, macrosIndex); - assert caloriesIndex < dateIndex : "The c/ keyword must appear before date/ in the input!"; + assert caloriesIndex < dateIndex : "The c/ keyword must appear before strDate/ in the input!"; - //extract command, description, calories, date from input + //extract command, description, calories, strDate from input String[] parts = input.split("c/|d/|m/"); String command = parts[0].substring(0, CALORIES_OUT_PADDING).trim(); String description = getDescriptionFromInput(input, command, caloriesIndex); String strCalories = parts[1].trim(); - String date = parts[2].trim(); - - checkInputsAreNonEmpty(description, strCalories, date); + + // Try catch here is needed because if i input , calories in chicken c/1000 d/ , code fails + // code fails because index out of bounds occurs due to parts[2].trim() + String strDate = null; + try { + strDate = parts[2].trim(); + } catch (ArrayIndexOutOfBoundsException e) { + throw new InvalidInputException(getWhitespaceInInputMessage()); + } + + checkInputsAreNonEmpty(description, strCalories, strDate); assert description != "" : "The description field should be a non-empty string!"; assert strCalories != "" : "The calories field should be a non-empty string!"; - assert date != "" : "The date field should be a non-empty string!"; + assert strDate != "" : "The strDate field should be a non-empty string!"; //extract macronutrients if user provided it in their input, otherwise initialise it as null int[] macros = null; @@ -80,6 +91,16 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio checkCaloriesIsPositiveInteger(calories); assert calories > 0 : "Calories value must be a positive integer!"; + //@@author rexyyong + // Convert strDate from type String to date of type LocalDate + LocalDate date = null; + try { + date = getLocalDateFromInput(strDate); + } catch (DateTimeParseException e) { + throw new InvalidInputException("Invalid date format"); + } + //@@author + if (command.equals("calories out")) { return makeNewOutputEntry(description, calories, date); } else if (macros == null) { @@ -89,6 +110,13 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio } } + //@@author rexyyong + public static LocalDate getLocalDateFromInput(String strDate) throws DateTimeParseException { + LocalDate date = LocalDate.parse(strDate); + return date; + } + //@@author + private static int getIntegerCaloriesFromInput(String strCalories) { int calories = 0; try { @@ -162,18 +190,18 @@ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateInd } } - private static Entry makeNewOutputEntry(String description, int calories, String date) { + private static Entry makeNewOutputEntry(String description, int calories, LocalDate date) { Activity newActivity = new Activity(); return new OutputEntry(description, calories, date, newActivity); } - private static Entry makeNewInputEntry(String description, int calories, String date) { + private static Entry makeNewInputEntry(String description, int calories, LocalDate date) { return new InputEntry(description, calories, date); } - private static Entry makeNewInputEntry(String description, int calories, String date, int[] foodMacros) { + private static Entry makeNewInputEntry(String description, int calories, LocalDate date, int[] foodMacros) { Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); From 5e8bcf04fa130219f638cf91e618ae106bec950e Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:29:44 +0800 Subject: [PATCH 153/414] Edit Entry Classess to accomodate to Date Format --- src/main/java/seedu/lifetrack/Entry.java | 8 +++++--- .../lifetrack/calories/calorielist/InputEntry.java | 6 ++++-- .../lifetrack/calories/calorielist/OutputEntry.java | 6 ++++-- .../hydration/hydrationlist/HydrationEntry.java | 4 +++- .../seedu/lifetrack/sleep/sleeplist/SleepEntry.java | 13 +++++++------ 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index deb82ca011..e8b8375f8f 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -1,11 +1,13 @@ package seedu.lifetrack; +import java.time.LocalDate; + public abstract class Entry { private String description; - private String date; + private LocalDate date; - public Entry(String description, String date){ + public Entry(String description, LocalDate date){ this.description = description; this.date = date; } @@ -14,7 +16,7 @@ public String getDescription() { return description; } - public String getDate() { + public LocalDate getDate() { return date; } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index 13fbc3b59f..c6263647a2 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -3,18 +3,20 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.Food; +import java.time.LocalDate; + public class InputEntry extends Entry { private Food food; private int calories; private boolean doesFoodExist = false; - public InputEntry(String description, int calories, String date) { + public InputEntry(String description, int calories, LocalDate date) { super(description, date); this.calories = calories; } - public InputEntry(String description, int calories, String date, Food food) { + public InputEntry(String description, int calories, LocalDate date, Food food) { super(description, date); this.food = food; this.calories = calories; diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java index c5eca8f818..2ac4ef8e24 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java @@ -3,6 +3,8 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.Activity; +import java.time.LocalDate; + public class OutputEntry extends Entry { private Activity activity; @@ -10,12 +12,12 @@ public class OutputEntry extends Entry { private boolean doesActivityExist = false; - public OutputEntry(String description, int calories, String date) { + public OutputEntry(String description, int calories, LocalDate date) { super(description, date); this.calories = calories; } - public OutputEntry(String description, int calories, String date, Activity activity) { + public OutputEntry(String description, int calories, LocalDate date, Activity activity) { super(description, date); this.activity = activity; this.calories = calories; diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java index f1adf72564..617ccfc415 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java @@ -2,11 +2,13 @@ import seedu.lifetrack.Entry; +import java.time.LocalDate; + public class HydrationEntry extends Entry { private int volume; - public HydrationEntry(String description, int volume, String date){ + public HydrationEntry(String description, int volume, LocalDate date){ super(description, date); this.volume= volume; } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 4366164059..96e24aa4ba 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -2,9 +2,11 @@ import seedu.lifetrack.Entry; +import java.time.LocalDate; + public class SleepEntry extends Entry { - private String date; + private LocalDate date; private double duration; /*** @@ -13,13 +15,13 @@ public class SleepEntry extends Entry { * @param date * @param duration */ - public SleepEntry (double duration, String date){ + public SleepEntry (double duration, LocalDate date){ super("SLEEP", date); - this.date = date.isEmpty() ? "N/A" : date; + this.date = date; this.duration = duration; } - public String getDate() { + public LocalDate getDate() { return date; } @@ -28,8 +30,7 @@ public double getDuration() { } public String toString() { - // Show "N/A" if no date was provided - return "\t Date: " + (date == null || date.isEmpty() ? "N/A" : date) + + return "\t Date: " + date + ", Duration: " + String.format("%.1f", duration) + " hours"; } From 37dbe8d4f30b39328ebe506ef300c08873af0b72 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:30:01 +0800 Subject: [PATCH 154/414] Add Date format to Class Hydration --- .../system/parser/ParserHydration.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index c0dd75de63..f1b90359d4 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -4,6 +4,9 @@ import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectVolumeInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage; @@ -42,25 +45,42 @@ public static Entry parseHydrationInput(String input) throws InvalidInputExcepti String[] parts = input.split("v/|d/"); String description = getDescriptionFromInput(input, volumeIndex); String strVolume = parts[1].trim(); - String date = parts[2].trim(); + String strDate = parts[2].trim(); - checkInputsAreNonEmpty(description, strVolume, date); + checkInputsAreNonEmpty(description, strVolume, strDate); assert description != "" : "The description field should be a non-empty string!"; assert strVolume != "" : "The volume field should be a non-empty string!"; - assert date != "" : "The date field should be a non-empty string!"; + assert strDate != "" : "The date field should be a non-empty string!"; int volume = getIntegerVolumeFromInput(strVolume); checkVolumeIsPositiveInteger(volume); assert volume > 0 : "Volume value must be a positive integer!"; + //@@author rexyyong + // Convert strDate from type String to date of type LocalDate + LocalDate date = null; + try { + date = getLocalDateFromInput(strDate); + } catch (DateTimeParseException e) { + throw new InvalidInputException("Invalid date format"); + } + //@@author + return makeNewInputEntry(description, volume, date); } - private static HydrationEntry makeNewInputEntry(String description, int volume, String date) { + private static HydrationEntry makeNewInputEntry(String description, int volume, LocalDate date) { return new HydrationEntry(description, volume, date); } + //@@author rexyyong + public static LocalDate getLocalDateFromInput(String strDate) throws DateTimeParseException { + LocalDate date = LocalDate.parse(strDate); + return date; + } + //@@author + private static int getIntegerVolumeFromInput(String strVolume) { int volume = 0; try { From 46f52be2a6508361f1118ad87b3b0e5c347c1dde Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:30:15 +0800 Subject: [PATCH 155/414] Add Date format to Class Sleep --- .../lifetrack/system/parser/ParserSleep.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 88366ad42e..e9a5a63354 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -2,16 +2,19 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepDateInputMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; import seedu.lifetrack.sleep.sleeplist.SleepEntry; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + public class ParserSleep { public static SleepEntry parseSleepInput(String input) throws InvalidInputException { try { - String date = "N/A"; // Default if no date is provided + String strDate = "N/A"; // Default if no strDate is provided + LocalDate date = null; double duration = 0; String[] parts = input.split(" "); for (String part : parts) { @@ -21,20 +24,33 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept throw new InvalidInputException(getIncorrectSleepInputMessage()); } } else if (part.startsWith("d/")) { - date = part.substring(2); - if (!date.matches("\\d{6}")) { - throw new InvalidInputException(getIncorrectSleepDateInputMessage()); - } + strDate = part.substring(2); } } if (duration == 0) { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + - "sleep add d/"); + "sleep add d/"); + } + + //@@author rexyyong + // Parse str date to date of type LocalDate + try { + date = getLocalDateFromInput(strDate); + } catch (DateTimeParseException e) { + throw new InvalidInputException("Invalid date format"); } + //@@author return new SleepEntry(duration, date); } catch (NumberFormatException e) { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + "sleep add d/"); } } + + //@@author rexyyong + public static LocalDate getLocalDateFromInput(String strDate) throws DateTimeParseException { + LocalDate date = LocalDate.parse(strDate); + return date; + } + //@@author } From adba4844859f356fac88bb9942a60d211dcb7a65 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:30:47 +0800 Subject: [PATCH 156/414] Edit Class FileHandler to accomodate to LocalDate format --- .../java/seedu/lifetrack/system/storage/FileHandler.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 0340186ad6..bc4f9d5fbd 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -4,6 +4,7 @@ import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Scanner; @@ -85,7 +86,7 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); - String date = words[DATE_INDEX]; + LocalDate date = LocalDate.parse(words[DATE_INDEX]); String description = words[DESCRIPTION_INDEX]; int calories = Integer.parseInt(words[CALORIES_INDEX]); String entryType = words[ENTRY_TYPE_INDEX]; @@ -112,7 +113,7 @@ public ArrayList getHydrationEntriesFromFile() throws FileNotFoundExcepti while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); - String date = words[DATE_INDEX]; + LocalDate date = LocalDate.parse(words[DATE_INDEX]); String description = words[DESCRIPTION_INDEX]; int volume = Integer.parseInt(words[VOLUME_INDEX]); entries.add(new HydrationEntry(description, volume, date)); @@ -128,7 +129,7 @@ public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); - String date = words[DATE_INDEX]; + LocalDate date = LocalDate.parse(words[DATE_INDEX]); double duration = Double.parseDouble(words[DURATION_INDEX]); entries.add(new SleepEntry(duration, date)); } From c2028677d660f88ee7ef91b8c3e3541c89af1cab Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:31:05 +0800 Subject: [PATCH 157/414] Edit Unit tests to accomodate to LocalDate format --- .../java/seedu/lifetrack/CalorieListTest.java | 8 ++++-- .../seedu/lifetrack/HydrationListTest.java | 28 +++++++++---------- .../seedu/lifetrack/ParserCaloriesTest.java | 2 +- .../seedu/lifetrack/ParserHydrationTest.java | 20 ++++++------- .../java/seedu/lifetrack/ParserSleepTest.java | 9 +++--- .../java/seedu/lifetrack/SleepListTest.java | 28 +++++++++---------- 6 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 924d8f7de5..1a629fd483 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -2,9 +2,11 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.time.LocalDate; import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.calories.calorielist.InputEntry; @@ -31,12 +33,14 @@ public void addEntry_validInput_entryAdded() { OutputEntry secondEntry = (OutputEntry)calorieList.getEntry(1); // Check calories intake entry - assertEquals("2024-03-14", firstEntry.getDate()); + LocalDate dateIntake = LocalDate.parse("2024-03-14"); + assertTrue(firstEntry.getDate().isEqual(dateIntake)); assertEquals("Eat burger", firstEntry.getDescription()); assertEquals(369, firstEntry.getCalories()); // Check calories outflow entry - assertEquals("2024-03-15", secondEntry.getDate()); + LocalDate dateOutflow = LocalDate.parse("2024-03-15"); + assertTrue(secondEntry.getDate().isEqual(dateOutflow)); assertEquals("run", secondEntry.getDescription()); assertEquals(679, secondEntry.getCalories()); } diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index 1a2c121e63..fdc2164131 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -15,7 +15,7 @@ public class HydrationListTest { @Test public void testDeleteHydrationValidIndex() { HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 d/220224"); + hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); int initialSize = hydrationList.getSize(); hydrationList.deleteEntry("hydration delete 1"); assertEquals(initialSize - 1, hydrationList.getSize()); @@ -24,7 +24,7 @@ public void testDeleteHydrationValidIndex() { @Test public void testDeleteHydrationInvalidIndex() { HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 date/220224"); + hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); int initialSize = hydrationList.getSize(); hydrationList.deleteEntry("hydration delete 2"); // Index out of bounds hydrationList.deleteEntry("hydration delete -1"); @@ -50,13 +50,13 @@ public void testPrintHydrationListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 d/220224"); + hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator + + "\t \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator; + "\t 1. \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -66,21 +66,21 @@ public void testPrintHydrationListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 d/220224"); - hydrationList.addEntry("hydration add Water v/300 d/220224"); - hydrationList.addEntry("hydration add Juice v/150 d/220224"); + hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); + hydrationList.addEntry("hydration add Water v/300 d/2024-02-22"); + hydrationList.addEntry("hydration add Juice v/150 d/2024-02-22"); hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator + + "\t \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 220224, Description: Water, Volume: 300" + lineSeparator + + "\t \t Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 220224, Description: Juice, Volume: 150" + lineSeparator + + "\t \t Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Date: 220224, Description: Milo, Volume: 200" + lineSeparator + - "\t 2. \t Date: 220224, Description: Water, Volume: 300" + lineSeparator + - "\t 3. \t Date: 220224, Description: Juice, Volume: 150" + lineSeparator; + "\t 1. \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t 2. \t Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t 3. \t Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, hydrationList.getSize()); } diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index 8651e915f2..fcc732af5e 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -46,7 +46,7 @@ public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 d/220324 m/abc"); + parseCaloriesInput("calories in burger c/123 d/2024-03-22 m/abc"); } catch (InvalidInputException e) { assertEquals(getIncorrectMacrosInputMessage(), e.getMessage()); } diff --git a/src/test/java/seedu/lifetrack/ParserHydrationTest.java b/src/test/java/seedu/lifetrack/ParserHydrationTest.java index 3cb2ef12bc..0ad814bc61 100644 --- a/src/test/java/seedu/lifetrack/ParserHydrationTest.java +++ b/src/test/java/seedu/lifetrack/ParserHydrationTest.java @@ -20,7 +20,7 @@ public void parseHydrationInput_inputContains2Beverages_invalidInputExceptionThr } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -36,7 +36,7 @@ public void parseHydrationInput_inputContains2Volumes_invalidInputExceptionThrow } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -52,7 +52,7 @@ public void parseHydrationInput_inputMissingVolume_invalidInputExceptionThrown() } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -68,7 +68,7 @@ public void parseHydrationInput_inputWrongOrderDateBeforeVolume_invalidInputExce } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have keyed the input in the correct order!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -83,7 +83,7 @@ public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExcept } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -98,7 +98,7 @@ public void parseHydrationInput_inputNegativeValueForVolume_invalidInputExceptio } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -110,7 +110,7 @@ public void parseHydrationInput_missingKeywords_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -121,7 +121,7 @@ public void parseHydrationInput_incompleteInput_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -133,7 +133,7 @@ public void parseHydrationInput_emptyBeverageName_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -144,7 +144,7 @@ public void parseHydrationInput_emptyVolumeDescription_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/221024", e.getMessage()); + "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); } } diff --git a/src/test/java/seedu/lifetrack/ParserSleepTest.java b/src/test/java/seedu/lifetrack/ParserSleepTest.java index 150b2a5156..14127898ff 100644 --- a/src/test/java/seedu/lifetrack/ParserSleepTest.java +++ b/src/test/java/seedu/lifetrack/ParserSleepTest.java @@ -15,8 +15,7 @@ public void parseSleepInput_inputContains2Duration_invalidInputExceptionThrown() try { parseSleepInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add d/", e.getMessage()); + assertEquals("Invalid date format", e.getMessage()); } } @Test @@ -28,7 +27,7 @@ public void parseSleepInput_inputContains2Date_invalidInputExceptionThrown() { parseSleepInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add d/", e.getMessage()); + "sleep add d/", e.getMessage()); } } @Test @@ -40,7 +39,7 @@ public void parseSleepInput_inputMissingDuration_invalidInputExceptionThrown() { parseSleepInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add d/", e.getMessage()); + "sleep add d/", e.getMessage()); } } @@ -64,7 +63,7 @@ public void parseLiquidInput_missingKeywords_exceptionThrown() { parseSleepInput("sleep add"); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add d/", e.getMessage()); + "sleep add d/", e.getMessage()); } } } diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index 5af7a22319..f4eb5a947d 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -12,8 +12,8 @@ public class SleepListTest { @Test public void testDeleteSleepValidIndex(){ SleepList sleepList = new SleepList(); - sleepList.addSleep("sleep add 7.5 d/110324"); - sleepList.addSleep("sleep add 8"); + sleepList.addSleep("sleep add 7.5 d/2024-11-03"); + sleepList.addSleep("sleep add 8 d/2024-12-10"); int initialSize = sleepList.getSize(); sleepList.deleteSleep("sleep delete 1"); assertEquals(initialSize - 1, sleepList.getSize()); @@ -46,13 +46,13 @@ public void testPrintSleepListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); SleepList sleepList = new SleepList(); - sleepList.addSleep("sleep add 7.5 d/110324"); + sleepList.addSleep("sleep add 7.5 d/2024-03-11"); sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Date: 110324, Duration: 7.5 hours" + lineSeparator + + "\t \t Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. \t Date: 110324, Duration: 7.5 hours" + lineSeparator; + "\t 1. \t Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -62,21 +62,21 @@ public void testPrintSleepListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); SleepList sleepList = new SleepList(); - sleepList.addSleep("sleep add 7.5 d/110324"); - sleepList.addSleep("sleep add 8.0 d/280524"); - sleepList.addSleep("sleep add 4.2"); + sleepList.addSleep("sleep add 7.5 d/2024-03-11"); + sleepList.addSleep("sleep add 8.0 d/2024-05-28"); + sleepList.addSleep("sleep add 4.2 d/2024-06-15"); sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Date: 110324, Duration: 7.5 hours" + lineSeparator + + "\t \t Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Date: 280524, Duration: 8.0 hours" + lineSeparator + + "\t \t Date: 2024-05-28, Duration: 8.0 hours" + lineSeparator + "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Date: N/A, Duration: 4.2 hours" + lineSeparator + + "\t \t Date: 2024-06-15, Duration: 4.2 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. \t Date: 110324, Duration: 7.5 hours" + lineSeparator + - "\t 2. \t Date: 280524, Duration: 8.0 hours" + lineSeparator + - "\t 3. \t Date: N/A, Duration: 4.2 hours" + lineSeparator; + "\t 1. \t Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + + "\t 2. \t Date: 2024-05-28, Duration: 8.0 hours" + lineSeparator + + "\t 3. \t Date: 2024-06-15, Duration: 4.2 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, sleepList.getSize()); } From 07ab5eb002bd694862614be21b72cb1872402019 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:31:31 +0800 Subject: [PATCH 158/414] Edot Exception messages to follow LocalDate format --- .../java/seedu/lifetrack/system/exceptions/ErrorMessages.java | 2 +- .../system/exceptions/InvalidInputExceptionMessage.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index f01228c069..55e2855e94 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -51,6 +51,6 @@ public static String getIncorrectSleepInputMessage() { } public static String getIncorrectSleepDateInputMessage() { - return "\t Error: Date must be in DDMMYY format.!"; + return "\t Error: Date must be in YYYY-MM-DD format.!"; } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index ffc5bdac8b..e814877c0e 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -8,7 +8,7 @@ public class InvalidInputExceptionMessage { private static final String CALORIES_OUT_INPUT = "\t Example input: calories out DESCRIPTION " + "c/INTEGER_CALORIES d/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; - private static final String HYDRATION_IN_INPUT = "\t Example input: hydration add Milo v/1000 d/221024" ; + private static final String HYDRATION_IN_INPUT = "\t Example input: hydration add Milo v/1000 d/2024-04-19" ; //calories list related methods public static String getCaloriesIncorrectOrderMessage() { From dbece8b68453649fdd1681d8412d71b2f8d83cc9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 29 Mar 2024 22:31:49 +0800 Subject: [PATCH 159/414] Edit Class UI to follwo LocalDate format --- src/main/java/seedu/lifetrack/ui/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index a349d647a7..e202481e49 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -163,7 +163,7 @@ public static void showHelp() { System.out.println("\t - hydration delete : Deletes the hydration entry at the specified index " + "from the hydration list."); printLine(); - System.out.println("\t - sleep add d/: " + + System.out.println("\t - sleep add d/: " + "Adds a sleep entry into the sleep tracker."); System.out.println("\t - sleep list: Displays all entries currently stored in the sleep list."); System.out.println("\t - sleep delete : Deletes the entry at the specified index " + From b991fa94f42c605655d709bdaf57dd975cd4fa86 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 31 Mar 2024 19:37:41 +0800 Subject: [PATCH 160/414] added initial checks n javadocs --- .../InvalidInputExceptionMessage.java | 21 +++++- .../lifetrack/system/parser/ParserUser.java | 71 +++++++++++++++++-- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index ffc5bdac8b..7a539f6c72 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -8,7 +8,8 @@ public class InvalidInputExceptionMessage { private static final String CALORIES_OUT_INPUT = "\t Example input: calories out DESCRIPTION " + "c/INTEGER_CALORIES d/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; - private static final String HYDRATION_IN_INPUT = "\t Example input: hydration add Milo v/1000 d/221024" ; + private static final String HYDRATION_IN_INPUT = "\t Example input: hydration add Milo v/1000 d/221024"; + private static final String USER_SETUP_INPUT = "\t Example input: user set up Tom h/170 w/80 a/25 s/male e/4 g/3"; //calories list related methods public static String getCaloriesIncorrectOrderMessage() { @@ -25,6 +26,7 @@ public static String getWhitespaceInInputMessage() { String message = "\t Please ensure that there is no whitespace in your input!\n"; return HEADER + message + CALORIES_IN_INPUT; } + public static String getIncompleteMacrosMessage() { String message = "\t Please ensure that all macronutrients fields are filled up!\n"; return HEADER + message + MACROS_INPUT; @@ -34,7 +36,7 @@ public static String getWhitespaceInMacrosInputMessage() { String message = "\t Please ensure that there is no whitespace in your macros input!\n"; return HEADER + message + MACROS_INPUT; } - + public static String getMacrosInCaloriesOutMessage() { String message = "\t Calorie output entry cannot have macros!\n"; return HEADER + message + CALORIES_OUT_INPUT; @@ -60,4 +62,19 @@ public static String getHydrationNegativeIntegerVolumeMessage() { String message = "\t Please ensure that positive integer value is keyed in for volume!\n"; return HEADER + message + HYDRATION_IN_INPUT; } + + // User related Messages + public static String getOutOfGoalRangeMessage() { + return "\t Please key in a number between 1 and 7! 1 being reckless fatloss " + + "and 7 being aggressive bulking"; + } + + public static String getOutOfExerciseLevelsRangeMessage() { + return "\t Please key in a number between 1 and 5! 1 being little exercise done per week and 5 being" + + " very heavy levels of exercise."; + } + + public static String getEmptyUserSetupInputMessage() { + return HEADER + "\t Please key in the relevant user fields!\n" + USER_SETUP_INPUT; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 57f5244d90..75dd0a96a0 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -6,10 +6,27 @@ import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidExerciseLevelsNumberMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidNumberOfSetUpInputs; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGoalNumberMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; +/** + * Utility Class to parse the commands made with regard to the User class. + */ public class ParserUser { + private static final int LENGTH_OF_SETUP_COMMAND = "user setup".length(); + /** + * Parses the input from user to sieve out name, height, weight, age, gender, exercise levels and goals of the user + * and sets them accordingly in the User class. + * + * @param input input String from the user + * @param user current user of the app + * @throws InvalidInputException if user does not input all the information in the correct order and format + * @throws NumberFormatException if user does not input a number for exercise levels and goals + */ public static void parseSetUp(String input, User user) throws InvalidInputException, NumberFormatException { + checkEmptyInput(input); int heightIndex = input.indexOf("h/"); int weightIndex = input.indexOf("w/"); int ageIndex = input.indexOf("a/"); @@ -24,13 +41,17 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept checkSetUpInputsCorrectOrder(heightIndex, weightIndex, ageIndex, sexIndex, exerciseLevelsIndex, goalIndex); String[] parts = input.split("h/|w/|a/|s/|e/|g/"); - String name = parts[0].trim(); + if (parts.length != 7){ + throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); + } + String name = parts[0].substring(LENGTH_OF_SETUP_COMMAND).trim(); int height = Integer.parseInt(parts[1].trim()); int weight = Integer.parseInt(parts[2].trim()); int age = Integer.parseInt(parts[3].trim()); String sex = parts[4].trim().toLowerCase(); String exerciseLevels = parseExerciseLevels(parts[5].trim()); String goal = parseGoalIndex(parts[6].trim()); + user.setName(name); user.setHeight(height); user.setWeight(weight); @@ -40,6 +61,13 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept user.setGoal(goal); } + /** + * Parses the user's goal input for an Integer and assigns the String equivalent of it + * + * @param input user's goal input + * @return String equivalent of User's goals + * @throws InvalidInputException if the goal input is not an integer between 1 and 7 + */ private static String parseGoalIndex(String input) throws InvalidInputException { try { int goalNumber = Integer.parseInt(input); @@ -55,14 +83,22 @@ private static String parseGoalIndex(String input) throws InvalidInputException return "bulking slow"; } else if (goalNumber == 6) { return "bulking normal"; - } else { + } else if (goalNumber == 7) { return "bulking aggressive"; + } else { + throw new InvalidInputException(getOutOfGoalRangeMessage()); } } catch (NumberFormatException e) { throw new InvalidInputException(getInvalidGoalNumberMessage()); } } + /** + * Parses the user's exercise levels input for an Integer and assigns the String equivalent of it + * @param input user's exercise levels input + * @return String equivalent of User's exercise levels + * @throws InvalidInputException if the goal input is not an integer between 1 and 5 + */ private static String parseExerciseLevels(String input) throws InvalidInputException { try { int levelInNumber = Integer.parseInt(input); @@ -74,19 +110,44 @@ private static String parseExerciseLevels(String input) throws InvalidInputExcep return "moderate"; } else if (levelInNumber == 4) { return "heavy"; - } else { + } else if (levelInNumber == 5) { return "veryheavy"; + } else { + throw new InvalidInputException(getOutOfExerciseLevelsRangeMessage()); } } catch (NumberFormatException e) { throw new InvalidInputException(getInvalidExerciseLevelsNumberMessage()); } } + /** + * Ensures that the input given by the user is in the correct order + * @param heightIndex Index of the input where user's height is specified + * @param weightIndex Index of the input where user's weight is specified + * @param ageIndex Index of the input where user's age is specified + * @param sexIndex Index of the input where user's gender is specified + * @param exerciseLevelsIndex Index of the input where user's exercise levels is specified in Integer form + * @param goalIndex Index of the input where user's goal is specified in Integer form + * @throws InvalidInputException if the order of the inputs is not correct. The input should be in this order: + * height, weight, age, gender, exercise levels and goal. + */ private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightIndex, int ageIndex, int sexIndex, - int exerciseLevelsIndex, int goalIndex) throws InvalidInputException { + int exerciseLevelsIndex, int goalIndex) + throws InvalidInputException { if (!(heightIndex < weightIndex && weightIndex < ageIndex && sexIndex < exerciseLevelsIndex && exerciseLevelsIndex < goalIndex)) { - throw new InvalidInputException(getInvalidExerciseLevelsNumberMessage()); + throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); + } + } + + /** + * Checks if User Setup command is empty + * @param input input from user + * @throws InvalidInputException if the command is empty + */ + private static void checkEmptyInput(String input) throws InvalidInputException { + if (input.substring(LENGTH_OF_SETUP_COMMAND).trim().isEmpty()){ + throw new InvalidInputException(getEmptyUserSetupInputMessage()); } } } From ab854a519dd7c6f57d2c572b6dfb3545a2932551 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Mon, 1 Apr 2024 14:27:25 +0800 Subject: [PATCH 161/414] moe exceptions --- src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index c5da3d12fe..51662d45c0 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -37,7 +37,7 @@ public static void getHealthInfo(User user) { System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); user.setCaloriesRequired(calories); } catch (IOException | InterruptedException e) { - System.out.println("OOPS"); + System.out.println("You "); } } } From bd82a9c55b9e245e896b10734aa9b2137c45a301 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Mon, 1 Apr 2024 14:33:40 +0800 Subject: [PATCH 162/414] more exceptions --- src/main/java/seedu/lifetrack/system/parser/ParserUser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 75dd0a96a0..f29a56113d 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -129,7 +129,7 @@ private static String parseExerciseLevels(String input) throws InvalidInputExcep * @param exerciseLevelsIndex Index of the input where user's exercise levels is specified in Integer form * @param goalIndex Index of the input where user's goal is specified in Integer form * @throws InvalidInputException if the order of the inputs is not correct. The input should be in this order: - * height, weight, age, gender, exercise levels and goal. + * height, weight, age, gender, exercise levels and goal. */ private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightIndex, int ageIndex, int sexIndex, int exerciseLevelsIndex, int goalIndex) From 6e927b933023d57220a16a0f0cc0975d149e0f9e Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:46:16 +0800 Subject: [PATCH 163/414] Add progress bar calories --- .../calories/calorielist/CalorieList.java | 7 +++++ .../calories/calorielist/InputEntry.java | 4 +-- src/main/java/seedu/lifetrack/ui/Ui.java | 1 + src/main/java/seedu/lifetrack/user/User.java | 4 +++ .../lifetrack/user/usergoals/UserGoals.java | 27 ++++++++++++++++++- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 242b11aeb7..5faaeef1bb 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -110,4 +110,11 @@ public void printCalorieList() { public int getSize() { return calorieArrayList.size(); } + public int getCaloriesConsumed() { + int totalCalories = 0; + for (Entry entry : calorieArrayList) { + totalCalories += InputEntry.getCalories(); + } + return totalCalories; + } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index c6263647a2..4a8fddb2d8 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -8,7 +8,7 @@ public class InputEntry extends Entry { private Food food; - private int calories; + private static int calories; private boolean doesFoodExist = false; public InputEntry(String description, int calories, LocalDate date) { @@ -27,7 +27,7 @@ public Food getFood() { return food; } - public int getCalories() { + public static int getCalories() { return calories; } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index e202481e49..1d694942ea 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -116,6 +116,7 @@ public static void handleUserCommands(String line, User user) { user.setUp(line); } else if (line.contains("progress")) { user.getHealthInfo(); + user.getProgressBar(); } } diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 136b0d89c7..99452b2605 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -150,4 +150,8 @@ public String toFileFriendlyString() { return String.format(name + ";" + height + ";" + weight + ";" + age + ";" + sex + ";" + exerciseLevels + ";" + goal + ";" + caloriesRequired); } + + public void getProgressBar() { + UserGoals.getProgressBar(this); + } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 51662d45c0..d180e3bf7c 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -8,6 +8,8 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import static seedu.lifetrack.LifeTrack.calorieList; + public class UserGoals { private static HttpResponse response; @@ -34,10 +36,33 @@ public static void getHealthInfo(User user) { int indexOfCalories = response.body().indexOf("neededEnergy") + JSON_HEADING_SIZE; int calories = Integer.parseInt(response.body() .substring(indexOfCalories, indexOfCalories + CALORIES_LENGTH)); - System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); + System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); user.setCaloriesRequired(calories); } catch (IOException | InterruptedException e) { System.out.println("You "); } } + public static void getProgressBar(User user) { + int caloriesRequired = user.getCaloriesRequired(); + int caloriesConsumed = calorieList.getCaloriesConsumed(); + double progress = (double) caloriesConsumed / caloriesRequired; + int width = 50; + + int progressWidth = (int) (width * progress); + StringBuilder progressBar = new StringBuilder("["); + for (int i = 0; i < width; i++) { + if (i < progressWidth) { + progressBar.append("="); + } else { + progressBar.append(" "); + } + } + progressBar.append("] "); + + // Calculate percentage + int percentage = (int) (progress * 100); + + // Print progress bar and percentage + System.out.printf("%s %d%%\n", progressBar.toString(), percentage); + } } From 4fec89640aa8cfa0898f9e8b1a6c9aa105136c46 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 11:45:10 +0800 Subject: [PATCH 164/414] Added userguide --- docs/UserGuide.md | 135 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..8c96d076b4 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -11,9 +11,140 @@ 1. Ensure that you have Java 11 or above installed. 1. Down the latest version of `Duke` from [here](http://link.to/duke). -## Features +[//]: # (## Features ) + +## General + +### Viewing help: `help` +Shows a help message listing the commands available in the application. + +**Format:** +`help` + +### Exiting the program: `bye` + +Exits the program. + +**Format:** +`bye` + +## Calories Tracker + +### Input calorie intake: `calories in` +Adds a calorie gaining activity into the calories tracker. + +**Format:** +`calories in DESCRIPTION c/CALORIES_IN d/DATE` + +* The calories must be a positive integer 1, 2, 3, …, measured in kcal. +* The time indicated should follow the 24-hour system. +* The date provided should be of the form YYYY-MM-DD. + +**Examples:** +* `calories in eat chicken rice c/678 d/2022-02-24` +* `calories in drink liho milk tea c/430 d/1022-03-25` + + +### Input calorie loss: `calories out` +Adds a calorie burning activity into the calories tracker. + +**Format:** +`calories out DESCRIPTION c/CALORIES_OUT d/DATE` + +* The calories must be a positive integer 1, 2, 3, …, measured in kcal. +* The time indicated should follow the 24-hour system. +* The date provided should be of the form YYYY-MM-DD. + +**Examples:** + +* `calories out Run around NUS c/678 d/2022-02-24` +* `calories out Walk to i3 building c/67 d/2022-03-25` + +### Listing calorie items: `calories list` +Shows a list of all activities in the calories tracker. + +**Format:** +`calories list` + +### Deleting a calorie item: `calories delete` +Deletes the specified activity from the calories tracker. + +**Format:** +`calories delete INDEX` + +**Examples:** + +* `calories list` followed by `calories delete 2` deletes the 2nd activity in the calories tracker. + +## Hydration Tracker + +### Input hydration intake: `hydration add` +Adds a hydration record into the hydration tracker. + +**Format:** +`hydration add DESCRIPTION v/VOLUME d/DATE` + +* The volume must be a positive integer 1, 2, 3, …, measured in milliliters. +* The time indicated should follow the 24-hour system. +* The date provided should be of the form YYYY-MM-DD. + +**Examples:** +* `hydration add Milo v/1000 d/2022-03-25` +* `hydration add Tea v/200 d/2022-02-05` + +### Listing hydration items: `hydration list` +Show the list of all hydration records in the hydration tracker. + +**Format:** +`hydration list` + +### Deleting a hydration item: `hydration delete` +Deletes the hydration record according to the index. + +**Format:** +`hydration delete INDEX` + +Delete the drinking water record at the specific index. The index refers to the index number shown in the displayed Hydration list. The index must be a positive integer 1, 2, 3, …​ + +**Examples:** +* `hydration list` followed by `hydration delete 2` deletes the 2nd hydration record from the hydration tracker. + +## Sleep Tracker + +### Input sleeping hours: `sleep add` +Adds a sleep record into the sleep tracker. + +**Format:** +`sleep add DURATION d/DATE` +* The duration provided must be a positive real number. +* The time indicated should follow the 24-hour system. +* The date provided should be of the form YYYY-MM-DD. + +**Notes about the command format:** +Including the DATE is optional. However, the DURATION must be included! + +**Examples:** +* `sleep add 7.5 d/110324` +* `sleep add 8` + +### Listing sleep records: `sleep list` +Show the list of all sleep records in the sleep tracker. + +**Format:** +`sleep list` + +### Deleting a sleep record: `sleep delete` +Deletes the sleep record according to the index. + +**Format:** +`sleep delete INDEX` +* Delete the sleep record at the specific index. +* The index refers to the index number shown in the displayed sleeping records list. +* The index must be a positive integer 1, 2, 3, …​ + +**Examples:** +* `list` followed by `sleep delete 2` deletes the 2nd sleep record from the sleep tracker. -{Give detailed description of each feature} ### Adding a todo: `todo` Adds a new item to the list of todo items. From 7d61600886fa1e31268fa1fd0d87f9ffef7d1970 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:27:53 +0800 Subject: [PATCH 165/414] Progress bar finishing touches --- docs/UserGuide.md | 8 ++--- .../calories/calorielist/CalorieList.java | 6 ++-- .../calories/calorielist/InputEntry.java | 6 ++-- .../hydrationlist/HydrationEntry.java | 4 +++ .../hydrationlist/HydrationList.java | 10 +++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 5 ++-- src/main/java/seedu/lifetrack/user/User.java | 14 +++++++-- .../lifetrack/user/usergoals/UserGoals.java | 30 +++++++++++++++++-- 8 files changed, 67 insertions(+), 16 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 8c96d076b4..8124ff6661 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -78,19 +78,19 @@ Deletes the specified activity from the calories tracker. ## Hydration Tracker -### Input hydration intake: `hydration add` +### Input hydration intake: `hydration in` Adds a hydration record into the hydration tracker. **Format:** -`hydration add DESCRIPTION v/VOLUME d/DATE` +`hydration in DESCRIPTION v/VOLUME d/DATE` * The volume must be a positive integer 1, 2, 3, …, measured in milliliters. * The time indicated should follow the 24-hour system. * The date provided should be of the form YYYY-MM-DD. **Examples:** -* `hydration add Milo v/1000 d/2022-03-25` -* `hydration add Tea v/200 d/2022-02-05` +* `hydration in Milo v/1000 d/2022-03-25` +* `hydration in Tea v/200 d/2022-02-05` ### Listing hydration items: `hydration list` Show the list of all hydration records in the hydration tracker. diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 5faaeef1bb..48a2d80fbe 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -46,6 +46,7 @@ public Entry getEntry(int index) { return calorieArrayList.get(index); } + /** * Index should be in an integer from 1 to size of the list. * @param line the string containing the index of calorie record user want to delete @@ -112,8 +113,9 @@ public int getSize() { } public int getCaloriesConsumed() { int totalCalories = 0; - for (Entry entry : calorieArrayList) { - totalCalories += InputEntry.getCalories(); + for (int i = 0; i < calorieArrayList.size(); i++) { + InputEntry tempEntry = (InputEntry) calorieArrayList.get(i); + totalCalories += tempEntry.getCalories(); } return totalCalories; } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index 4a8fddb2d8..247e605efe 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -8,7 +8,7 @@ public class InputEntry extends Entry { private Food food; - private static int calories; + private int calories; private boolean doesFoodExist = false; public InputEntry(String description, int calories, LocalDate date) { @@ -27,8 +27,8 @@ public Food getFood() { return food; } - public static int getCalories() { - return calories; + public int getCalories() { + return this.calories; } public String toString() { diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java index 617ccfc415..e06482a71c 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java @@ -13,6 +13,10 @@ public HydrationEntry(String description, int volume, LocalDate date){ this.volume= volume; } + public int getHydration() { + return volume; + } + public int getVolume() { return volume; } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index ad858151aa..28bcc2a839 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -2,6 +2,7 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserHydration; @@ -109,6 +110,15 @@ public void printHydrationList() { } } } + public int getHydrationConsumed() { + int totalHydration = 0; + for (int i = 0; i < hydrationArrayList.size(); i++) { + HydrationEntry tempEntry = (HydrationEntry) hydrationArrayList.get(i); + totalHydration += tempEntry.getHydration(); + } + return totalHydration; + } + /** * Retrieves the size of the liquid list. diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 1d694942ea..d4db3d4707 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -64,7 +64,7 @@ public static void handleCaloriesInput(String line, CalorieList calorieList) { public static void handleHydrationInput(String line, HydrationList hydrationList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("hydration add")) { + if (line.startsWith("hydration in")) { hydrationList.addEntry(line); } else if (line.startsWith("hydration list")) { hydrationList.printHydrationList(); @@ -116,7 +116,8 @@ public static void handleUserCommands(String line, User user) { user.setUp(line); } else if (line.contains("progress")) { user.getHealthInfo(); - user.getProgressBar(); + user.getCaloriesProgressBar(); + user.getHydrationProgressBar(); } } diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 99452b2605..129e89abc6 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -22,6 +22,8 @@ public class User { private String goal; private int caloriesRequired; + private int hydrationRequired = 2000; + //user data constants private final int NAME_INDEX = 0; private final int HEIGHT_INDEX = 1; @@ -146,12 +148,20 @@ public int getCaloriesRequired() { return caloriesRequired; } + public int getHydrationRequired() { + return hydrationRequired; + } + public String toFileFriendlyString() { return String.format(name + ";" + height + ";" + weight + ";" + age + ";" + sex + ";" + exerciseLevels + ";" + goal + ";" + caloriesRequired); } - public void getProgressBar() { - UserGoals.getProgressBar(this); + public void getCaloriesProgressBar() { + UserGoals.getCaloriesProgressBar(this); + } + + public void getHydrationProgressBar() { + UserGoals.getHydrationProgressBar(this); } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index d180e3bf7c..243b4b2b4e 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -9,6 +9,7 @@ import java.net.http.HttpResponse; import static seedu.lifetrack.LifeTrack.calorieList; +import static seedu.lifetrack.LifeTrack.hydrationList; public class UserGoals { @@ -42,7 +43,7 @@ public static void getHealthInfo(User user) { System.out.println("You "); } } - public static void getProgressBar(User user) { + public static void getCaloriesProgressBar(User user) { int caloriesRequired = user.getCaloriesRequired(); int caloriesConsumed = calorieList.getCaloriesConsumed(); double progress = (double) caloriesConsumed / caloriesRequired; @@ -59,10 +60,33 @@ public static void getProgressBar(User user) { } progressBar.append("] "); - // Calculate percentage int percentage = (int) (progress * 100); - // Print progress bar and percentage + System.out.printf("Calories:\n"); + System.out.printf("You have consumed " + caloriesConsumed + " out of your goal of " + caloriesRequired + " so far.\n"); + System.out.printf("%s %d%%\n", progressBar.toString(), percentage); + } + + public static void getHydrationProgressBar(User user) { + int hydrationRequired = user.getHydrationRequired(); + int hydrationConsumed = hydrationList.getHydrationConsumed(); + double progress = (double) hydrationConsumed / hydrationRequired; + int width = 50; + + int progressWidth = (int) (width * progress); + StringBuilder progressBar = new StringBuilder("["); + for (int i = 0; i < width; i++) { + if (i < progressWidth) { + progressBar.append("="); + } else { + progressBar.append(" "); + } + } + progressBar.append("] "); + + int percentage = (int) (progress * 100); + System.out.printf("Hydration:\n"); + System.out.printf("You have consumed " + hydrationConsumed + " out of your goal of " + hydrationRequired + " so far.\n"); System.out.printf("%s %d%%\n", progressBar.toString(), percentage); } } From ccb1e3766a183a9834e53197e17d866bb1a24be7 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:34:30 +0800 Subject: [PATCH 166/414] Checkstyle resolve --- .../lifetrack/hydration/hydrationlist/HydrationList.java | 1 - .../java/seedu/lifetrack/user/usergoals/UserGoals.java | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 28bcc2a839..dce48f0482 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -2,7 +2,6 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserHydration; diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 243b4b2b4e..aec74c2f21 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -37,7 +37,7 @@ public static void getHealthInfo(User user) { int indexOfCalories = response.body().indexOf("neededEnergy") + JSON_HEADING_SIZE; int calories = Integer.parseInt(response.body() .substring(indexOfCalories, indexOfCalories + CALORIES_LENGTH)); - System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); + System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); user.setCaloriesRequired(calories); } catch (IOException | InterruptedException e) { System.out.println("You "); @@ -63,7 +63,8 @@ public static void getCaloriesProgressBar(User user) { int percentage = (int) (progress * 100); System.out.printf("Calories:\n"); - System.out.printf("You have consumed " + caloriesConsumed + " out of your goal of " + caloriesRequired + " so far.\n"); + System.out.printf("You have consumed " + caloriesConsumed + " out of your goal of " + + caloriesRequired + " so far.\n"); System.out.printf("%s %d%%\n", progressBar.toString(), percentage); } @@ -86,7 +87,8 @@ public static void getHydrationProgressBar(User user) { int percentage = (int) (progress * 100); System.out.printf("Hydration:\n"); - System.out.printf("You have consumed " + hydrationConsumed + " out of your goal of " + hydrationRequired + " so far.\n"); + System.out.printf("You have consumed " + hydrationConsumed + " out of your goal of " + + hydrationRequired + " so far.\n"); System.out.printf("%s %d%%\n", progressBar.toString(), percentage); } } From ac4710a477e1a5bed342d3a09ab03b369ee45c1f Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:14:17 +0800 Subject: [PATCH 167/414] Add developer guide for Hydration and Hydration List UML --- docs/DeveloperGuide.md | 58 +++++++++++++----- docs/HydrationListClassDiagram.png | Bin 0 -> 39775 bytes docs/HydrationListClassDiagram.puml | 33 ++++++++++ .../system/parser/ParserHydration.java | 10 +-- .../java/seedu/lifetrack/CalorieListTest.java | 4 +- .../seedu/lifetrack/HydrationListTest.java | 46 ++++++++++++++ 6 files changed, 131 insertions(+), 20 deletions(-) create mode 100644 docs/HydrationListClassDiagram.png create mode 100644 docs/HydrationListClassDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index e6f17d726e..afa902aa25 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -51,21 +51,6 @@ Given below is an example usage scenario and how this mechanism behaves at every - Pros: Not dependent on external APIs - Cons: Need to come up with an algorithm to use -### Parsing user input for hydration entries - -This functionality is facilitated by `ParserHydration`. It implements one operation, namely: -- `ParserHydration#parseHydrationInput(String input)` - -This operation is exposed in the `HydrationList` class as `HydrationList#addEntry(String)`. - -Given below is an example usage scenario and how this mechanism behaves at every step: -- Step 1: When the user inputs the command `calories in Milo v/100 date/270324` in the terminal, - the string is sent to `HydrationList#addEntry(String)`, which calls `ParserHydration#parseHydrationInput(String)`. - -- Step 2: Using `String.split()`, the method extracts information such as the description, volume of beverage, and date of entry. The obtained information is sent to the private method `ParserHydration#makeNewInputEntry(String, int, String)` to create a new entry of class `HydrationEntry` that extends `Entry`. - -- Step 3: The created `HydrationEntry` instance is added into the `ArrayList` attribute of the `HydrationList`. - ### Calories list feature The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. @@ -96,6 +81,49 @@ The Class diagram for Calories delete feature is shown below: ![CaloriesDeleteClassDiagram](https://github.com/a-wild-chocolate/tp/blob/master/docs/caloriesDeleteUML.jpg) +### Parsing user input for hydration entries + +This functionality is facilitated by `ParserHydration`. It implements one operation, namely: +- `ParserHydration#parseHydrationInput(String input)` + +This operation is exposed in the `HydrationList` class as `HydrationList#addEntry(String)`. + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `hydration in Milo v/100 d/2022-01-02` in the terminal, + the string is sent to `HydrationList#addEntry(String)`, which calls `ParserHydration#parseHydrationInput(String)`. + +- Step 2: Using `String.split()`, the method extracts information such as the description, volume of beverage, and date of entry. The obtained information is sent to the private method `ParserHydration#makeNewInputEntry(String, int, String)` to create a new entry of class `HydrationEntry` that extends `Entry`. + +- Step 3: The created `HydrationEntry` instance is added into the `ArrayList` attribute of the `HydrationList`. + +### Hydration list feature + +The `hydration list` feature lists out the record of all the Hydration data that the user has keyed in. The Hydration data are all stored into a `ArrayList hydrationArrayList` attribute of the `HydrationList` Class. Hydration data are printed when the `printHydrationList()` function is called. + +The `printHydrationList()` function iterates through the `hydrationArrayList` and prints out the Entries according to its order in the Array List. + +The Class diagram for Hydration list feature is shown below. Unrelated attributes and Classes were excluded. + +![HydrationListClassDiagram.png](HydrationListClassDiagram.png) + +### Hydration delete feature + +The `hydration delete` feature can delete the hydration record at specific index of hydration list. This functionality is facilitated by `HydrationList`. It implements one operation, namely: +- `deleteEntry(String line)` + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `hydration delete INDEX` in the terminal, the string is sent to `Ui#handleUserInput()`, which will call `Ui#handleHydrationInput()`. + +- Step 2: After the `Ui#handleHydrationInput()` matching `delete hydration` key word, the string will be passed into deleteEntry(String line) to execute delete process. + +- Step 3: The string will be divided to two substrings according to the command syntax. Index will be tried to get from the second substring by `Integer.parseInt()`. + +- Step 4: The hydration record (`Entry`) stored in the `ArrayList hydrationList` will be deleted by calling `hydrationArrayList.remove((index-1));` and a successful deleting message will be shown in terminal by calling `HydrationListUi#successfulDeletedMessage(toDelete)` + +- Step 5: The latest hydration list will be updated to saving file by calling `HydrationList#updateFile()`. + +The Class diagram for Hydration delete feature is shown below: + ## Product scope ### Target user profile diff --git a/docs/HydrationListClassDiagram.png b/docs/HydrationListClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..21168646bf4f874cc32c1a068be47ac15bba9067 GIT binary patch literal 39775 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 hydrationArrayList + + printHydrationList(): void + } + + class HydrationListUi { + + hydrationListHeader(): void + + emptyListMessage(): void + } + } +} + +LifeTrack --> hydration.hydrationlist.HydrationList +LifeTrack -[dotted]-> ui.Ui +ui.Ui -[dotted]-> hydration.hydrationlist.HydrationList +HydrationList -[dotted]-> HydrationListUi + +@enduml diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index f1b90359d4..d32683a5cd 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -14,7 +14,9 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; public class ParserHydration { - + private static final int CARBS_IDX = 0; + private static final int VOLUME_IDX = 1; + private static final int DATE_IDX = 2; private static final int HYDRATION_ADD_PADDING = 13; /** @@ -44,8 +46,8 @@ public static Entry parseHydrationInput(String input) throws InvalidInputExcepti String[] parts = input.split("v/|d/"); String description = getDescriptionFromInput(input, volumeIndex); - String strVolume = parts[1].trim(); - String strDate = parts[2].trim(); + String strVolume = parts[VOLUME_IDX].trim(); + String strDate = parts[DATE_IDX].trim(); checkInputsAreNonEmpty(description, strVolume, strDate); assert description != "" : "The description field should be a non-empty string!"; @@ -112,7 +114,7 @@ private static String getDescriptionFromInput(String inputString, int volumeInde } private static void checkKeywordsCorrectlyOrdered( int dateIndex, int volumeIndex) throws InvalidInputException { - if (!(volumeIndex < dateIndex)) { + if (volumeIndex >= dateIndex) { throw new InvalidInputException(getHydrationIncorrectOrderMessage()); } } diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 1a629fd483..94bcbc5396 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -70,7 +70,7 @@ public void testDeleteCalorieInvalidIndex() { assertEquals(initialSize, calorieList.getSize()); } - //@@author shawnpong +// @@author shawnpong @Test public void testPrintCalorieListEmpty() { String lineSeparator = System.lineSeparator(); @@ -134,4 +134,6 @@ public void testPrintCalorieListMultipleEntries() { assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); } + + } diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index fdc2164131..4a26794ec1 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -84,4 +84,50 @@ public void testPrintHydrationListMultipleEntries() { assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, hydrationList.getSize()); } + @Test + public void testAddEntry() { + HydrationList hydrationList = new HydrationList(); + int initialSize = hydrationList.getSize(); + hydrationList.addEntry("hydration add Water v/250 d/2024-02-23"); + assertEquals(initialSize + 1, hydrationList.getSize()); + } + + @Test + public void testDeleteEntryFromEmptyList() { + HydrationList hydrationList = new HydrationList(); + int initialSize = hydrationList.getSize(); + hydrationList.deleteEntry("hydration delete 1"); + assertEquals(initialSize, hydrationList.getSize()); + } + + @Test + public void testDeleteEntryWithInvalidFormat() { + HydrationList hydrationList = new HydrationList(); + hydrationList.addEntry("hydration add Water v/250 d/2024-02-23"); + int initialSize = hydrationList.getSize(); + // Try to delete with invalid format, should not affect the list + hydrationList.deleteEntry("delete 1"); + assertEquals(initialSize, hydrationList.getSize()); + } + + @Test + public void testPrintHydrationListWithMultipleEntries() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + HydrationList hydrationList = new HydrationList(); + hydrationList.addEntry("hydration add Coffee v/150 d/2024-02-22"); + hydrationList.addEntry("hydration add Tea v/200 d/2024-02-22"); + hydrationList.printHydrationList(); + System.setOut(System.out); + String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + + "\t \t Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t The following entry has been added to your hydration list!" + lineSeparator + + "\t \t Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + + "\t Your Hydration List:" + lineSeparator + + "\t 1. \t Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t 2. \t Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; + assertEquals(expectedOutput, outputStream.toString()); + assertEquals(2, hydrationList.getSize()); + } } From df4445e1909d6b3f76911f64f9e409ef483530df Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:21:06 +0800 Subject: [PATCH 168/414] Checkstyle erros --- .../java/seedu/lifetrack/system/parser/ParserHydration.java | 1 - src/main/java/seedu/lifetrack/ui/Ui.java | 6 +++--- src/test/java/seedu/lifetrack/CalorieListTest.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index d32683a5cd..300297b772 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -14,7 +14,6 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; public class ParserHydration { - private static final int CARBS_IDX = 0; private static final int VOLUME_IDX = 1; private static final int DATE_IDX = 2; private static final int HYDRATION_ADD_PADDING = 13; diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index d4db3d4707..12f2e11246 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -151,15 +151,15 @@ public static void showHelp() { System.out.println("\t LifeTrack Command List:"); System.out.println("\t - help: Displays a list of available commands and their descriptions."); printLine(); - System.out.println("\t - calories in c/ d/ " + + System.out.println("\t - calories in c/ d/ " + "m/[carbohydrates, proteins, fats]: Adds a calorie gaining entry into the calories tracker."); - System.out.println("\t - calories out c/ d/: " + + System.out.println("\t - calories out c/ d/: " + "Adds a calorie burning entry into the calories tracker."); System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); System.out.println("\t - calories delete : Deletes the entry at the specified index" + " from the calorie list."); printLine(); - System.out.println("\t - hydration add v/ d/: " + + System.out.println("\t - hydration in v/ d/: " + "Adds a hydration entry into the hydration tracker."); System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list."); System.out.println("\t - hydration delete : Deletes the hydration entry at the specified index " + diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 94bcbc5396..25ef93c05a 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -70,7 +70,7 @@ public void testDeleteCalorieInvalidIndex() { assertEquals(initialSize, calorieList.getSize()); } -// @@author shawnpong +// @@author shawnpong @Test public void testPrintCalorieListEmpty() { String lineSeparator = System.lineSeparator(); From 0c9b8dc87ac8e7c0500e911bce26152da6d34003 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:23:55 +0800 Subject: [PATCH 169/414] Checkstyle --- src/test/java/seedu/lifetrack/CalorieListTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 25ef93c05a..9fcab0b7aa 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -70,7 +70,7 @@ public void testDeleteCalorieInvalidIndex() { assertEquals(initialSize, calorieList.getSize()); } -// @@author shawnpong +// @@author shawnpong @Test public void testPrintCalorieListEmpty() { String lineSeparator = System.lineSeparator(); From 2ab6bf3416a9be88cb655b43f3a428feac6da21f Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:26:15 +0800 Subject: [PATCH 170/414] Indentation error --- src/test/java/seedu/lifetrack/CalorieListTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 9fcab0b7aa..b41f3e1eb0 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -70,7 +70,7 @@ public void testDeleteCalorieInvalidIndex() { assertEquals(initialSize, calorieList.getSize()); } -// @@author shawnpong + //@@author shawnpong @Test public void testPrintCalorieListEmpty() { String lineSeparator = System.lineSeparator(); From e65a63cfc19ce0fbcc579a9b1f58ea7cf356b8a1 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:50:46 +0800 Subject: [PATCH 171/414] error handling --- .../hydrationlist/HydrationList.java | 12 +++- .../system/parser/ParserHydration.java | 64 +++++++++++++++++-- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index dce48f0482..c1f0cb385e 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -41,6 +41,9 @@ public HydrationList(String filePath) { } } + /** + * Updates the file with the current list of hydration entries. + */ private void updateFile() { if (fileHandler != null) { fileHandler.writeEntries(hydrationArrayList); @@ -52,9 +55,9 @@ private void updateFile() { * * @param index the index of the liquid entry to retrieve * @return the liquid entry at the specified index + * @throws IndexOutOfBoundsException if the index is out of range */ public Entry getEntry(int index) { - assert index >= 0 && index < hydrationArrayList.size() : "Index out of bounds"; return hydrationArrayList.get(index); } @@ -109,6 +112,12 @@ public void printHydrationList() { } } } + + /** + * Retrieves the total amount of hydration consumed. + * + * @return the total amount of hydration consumed + */ public int getHydrationConsumed() { int totalHydration = 0; for (int i = 0; i < hydrationArrayList.size(); i++) { @@ -118,7 +127,6 @@ public int getHydrationConsumed() { return totalHydration; } - /** * Retrieves the size of the liquid list. * diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index 300297b772..44abc9d9c4 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -1,3 +1,4 @@ +//@@author shawnpong package seedu.lifetrack.system.parser; import seedu.lifetrack.Entry; @@ -70,18 +71,37 @@ public static Entry parseHydrationInput(String input) throws InvalidInputExcepti return makeNewInputEntry(description, volume, date); } + /** + * Creates a new HydrationEntry object with the specified description, volume, and date. + * + * @param description the description of the hydration entry + * @param volume the volume of liquid intake + * @param date the date of the hydration entry + * @return a new HydrationEntry object with the specified attributes + */ private static HydrationEntry makeNewInputEntry(String description, int volume, LocalDate date) { - return new HydrationEntry(description, volume, date); } + /** + * Parses a string representation of a date and returns a LocalDate object. + * + * @param strDate the string representation of the date + * @return a LocalDate object representing the parsed date + * @throws DateTimeParseException if the input string cannot be parsed into a valid date + */ //@@author rexyyong public static LocalDate getLocalDateFromInput(String strDate) throws DateTimeParseException { LocalDate date = LocalDate.parse(strDate); return date; } //@@author - + /** + * Parses the volume from a string and returns the integer value. + * + * @param strVolume the string representation of the volume + * @return the integer value of the volume + */ private static int getIntegerVolumeFromInput(String strVolume) { int volume = 0; try { @@ -92,36 +112,70 @@ private static int getIntegerVolumeFromInput(String strVolume) { return volume; } + /** + * Checks if the given volume is a positive integer. + * + * @param volume the volume value to be checked + * @throws InvalidInputException if the volume is not a positive integer + */ private static void checkVolumeIsPositiveInteger(int volume) throws InvalidInputException { if (volume <= 0) { throw new InvalidInputException(getHydrationNegativeIntegerVolumeMessage()); } } + /** + * Checks if the description, volume, and date fields are non-empty. + * + * @param description the description of the hydration entry + * @param strVolume the string representation of the volume + * @param date the string representation of the date + * @throws InvalidInputException if any of the fields are empty + */ private static void checkInputsAreNonEmpty(String description, String strVolume, String date) throws InvalidInputException { - //check if the description, calories or date fields are empty if (description.isEmpty() || strVolume.isEmpty() || date.isEmpty()) { throw new InvalidInputException(getHydrationEmptyDescriptionMessage()); } } + /** + * Extracts the description from the input string. + * + * @param inputString the input string containing description, volume, and date + * @param volumeIndex the index of the 'v/' delimiter + * @return the description extracted from the input string + */ private static String getDescriptionFromInput(String inputString, int volumeIndex) { String description; description = inputString.substring(HYDRATION_ADD_PADDING, volumeIndex).trim(); return description; } - private static void checkKeywordsCorrectlyOrdered( int dateIndex, int volumeIndex) throws InvalidInputException { + /** + * Checks if the 'v/' and 'd/' keywords are in the correct order in the input string. + * + * @param dateIndex the index of the 'd/' keyword + * @param volumeIndex the index of the 'v/' keyword + * @throws InvalidInputException if the keywords are in the incorrect order + */ + private static void checkKeywordsCorrectlyOrdered(int dateIndex, int volumeIndex) throws InvalidInputException { if (volumeIndex >= dateIndex) { throw new InvalidInputException(getHydrationIncorrectOrderMessage()); } } + /** + * Checks if the 'v/' and 'd/' keywords exist in the input string. + * + * @param dateIndex the index of the 'd/' keyword + * @param volumeIndex the index of the 'v/' keyword + * @throws InvalidInputException if any of the keywords are missing + */ private static void checkKeywordsExist(int dateIndex, int volumeIndex) throws InvalidInputException { - //check that v/ and date/ keywords exist in the input, else throw exception if (dateIndex == -1 || volumeIndex == -1) { throw new InvalidInputException(getHydrationMissingKeywordMessage()); } } + } From 3b3ca721ce0293a3ce02a4b41408465f3e8c209e Mon Sep 17 00:00:00 2001 From: owx0130 Date: Tue, 2 Apr 2024 15:17:53 +0800 Subject: [PATCH 172/414] tidy progress bar output, add file update for calories and hydration progress --- .../lifetrack/system/parser/ParserUser.java | 3 +-- src/main/java/seedu/lifetrack/ui/Ui.java | 11 ++++++--- src/main/java/seedu/lifetrack/user/User.java | 23 ++++--------------- .../lifetrack/user/usergoals/UserGoals.java | 16 ++++++------- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index f29a56113d..8776c979cb 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -132,8 +132,7 @@ private static String parseExerciseLevels(String input) throws InvalidInputExcep * height, weight, age, gender, exercise levels and goal. */ private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightIndex, int ageIndex, int sexIndex, - int exerciseLevelsIndex, int goalIndex) - throws InvalidInputException { + int exerciseLevelsIndex, int goalIndex) throws InvalidInputException { if (!(heightIndex < weightIndex && weightIndex < ageIndex && sexIndex < exerciseLevelsIndex && exerciseLevelsIndex < goalIndex)) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index d4db3d4707..710888bf37 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -36,7 +36,7 @@ public class Ui { * @param hydrationList list containing all entries pertinent to liquids */ public static void readUserInput(CalorieList calorieList, HydrationList hydrationList, - User user, SleepList sleepList) { + User user, SleepList sleepList) { String line; do { line = new Scanner(System.in).nextLine(); @@ -88,7 +88,7 @@ public static void handleSleepInput(String line, SleepList sleepList) { } public static void handleUserInput(String line, CalorieList calorieList, HydrationList hydrationList, - User user ,SleepList sleepList) { + User user ,SleepList sleepList) { if (!line.startsWith("bye")) { printLine(); line = line.trim().toLowerCase(); @@ -159,7 +159,7 @@ public static void showHelp() { System.out.println("\t - calories delete : Deletes the entry at the specified index" + " from the calorie list."); printLine(); - System.out.println("\t - hydration add v/ d/: " + + System.out.println("\t - hydration add v/ d/: " + "Adds a hydration entry into the hydration tracker."); System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list."); System.out.println("\t - hydration delete : Deletes the hydration entry at the specified index " + @@ -170,5 +170,10 @@ public static void showHelp() { System.out.println("\t - sleep list: Displays all entries currently stored in the sleep list."); System.out.println("\t - sleep delete : Deletes the entry at the specified index " + "from the sleep list."); + printLine(); + System.out.println("\t - user setup h/ w/ a/ s/ e/ " + + "g/: Create a new user, or edit an existing one."); + System.out.println("\t - user progress: Display calories and hydration progress towards the daily " + + "requirement."); } } diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 129e89abc6..6f9b062cdb 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -20,8 +20,8 @@ public class User { private String sex; private String exerciseLevels; private String goal; - private int caloriesRequired; + private int caloriesRequired; private int hydrationRequired = 2000; //user data constants @@ -34,24 +34,6 @@ public class User { private final int GOAL_INDEX = 6; private final int REQ_CAL_INDEX = 7; - public User(String name, int height, int weight, int age, String sex, String exerciseLevels, String goal) { - this.name = name; - this.height = height; - this.weight = weight; - this.age = age; - this.sex = sex; - this.exerciseLevels = exerciseLevels; - this.goal = goal; - } - - public User(String name, int height, int weight, int age, String sex) { - this.name = name; - this.height = height; - this.weight = weight; - this.age = age; - this.sex = sex; - } - //constructor for JUnit tests public User() { @@ -78,6 +60,7 @@ public User(String filePath) { public void setUp(String line) { try { parseSetUp(line, this); + getHealthInfo(); fileHandler.writeUserData(this); } catch (InvalidInputException e) { System.out.println(e.getMessage()); @@ -159,9 +142,11 @@ public String toFileFriendlyString() { public void getCaloriesProgressBar() { UserGoals.getCaloriesProgressBar(this); + fileHandler.writeUserData(this); } public void getHydrationProgressBar() { UserGoals.getHydrationProgressBar(this); + fileHandler.writeUserData(this); } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index aec74c2f21..82e7f042fa 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -13,7 +13,6 @@ public class UserGoals { - private static HttpResponse response; private static final int JSON_HEADING_SIZE = 67; private static final int CALORIES_LENGTH = 4; @@ -37,12 +36,13 @@ public static void getHealthInfo(User user) { int indexOfCalories = response.body().indexOf("neededEnergy") + JSON_HEADING_SIZE; int calories = Integer.parseInt(response.body() .substring(indexOfCalories, indexOfCalories + CALORIES_LENGTH)); - System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); + System.out.println("\t You should consume " + calories + " calories a day to hit your goals!\n"); user.setCaloriesRequired(calories); } catch (IOException | InterruptedException e) { System.out.println("You "); } } + public static void getCaloriesProgressBar(User user) { int caloriesRequired = user.getCaloriesRequired(); int caloriesConsumed = calorieList.getCaloriesConsumed(); @@ -62,10 +62,10 @@ public static void getCaloriesProgressBar(User user) { int percentage = (int) (progress * 100); - System.out.printf("Calories:\n"); - System.out.printf("You have consumed " + caloriesConsumed + " out of your goal of " + System.out.printf("\t Calories:\n"); + System.out.printf("\t You have consumed " + caloriesConsumed + " out of your goal of " + caloriesRequired + " so far.\n"); - System.out.printf("%s %d%%\n", progressBar.toString(), percentage); + System.out.printf("\t %s %d%%\n\n", progressBar.toString(), percentage); } public static void getHydrationProgressBar(User user) { @@ -86,9 +86,9 @@ public static void getHydrationProgressBar(User user) { progressBar.append("] "); int percentage = (int) (progress * 100); - System.out.printf("Hydration:\n"); - System.out.printf("You have consumed " + hydrationConsumed + " out of your goal of " + System.out.printf("\t Hydration:\n"); + System.out.printf("\t You have consumed " + hydrationConsumed + " out of your goal of " + hydrationRequired + " so far.\n"); - System.out.printf("%s %d%%\n", progressBar.toString(), percentage); + System.out.printf("\t %s %d%%\n", progressBar.toString(), percentage); } } From 61257a5d821bb2ea977ac9760ca2f5f3fede3bf8 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Tue, 2 Apr 2024 15:23:45 +0800 Subject: [PATCH 173/414] print hydration goal message --- src/main/java/seedu/lifetrack/user/User.java | 2 -- src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 6f9b062cdb..775124e34f 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -142,11 +142,9 @@ public String toFileFriendlyString() { public void getCaloriesProgressBar() { UserGoals.getCaloriesProgressBar(this); - fileHandler.writeUserData(this); } public void getHydrationProgressBar() { UserGoals.getHydrationProgressBar(this); - fileHandler.writeUserData(this); } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 82e7f042fa..7097e0b156 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -36,7 +36,9 @@ public static void getHealthInfo(User user) { int indexOfCalories = response.body().indexOf("neededEnergy") + JSON_HEADING_SIZE; int calories = Integer.parseInt(response.body() .substring(indexOfCalories, indexOfCalories + CALORIES_LENGTH)); - System.out.println("\t You should consume " + calories + " calories a day to hit your goals!\n"); + System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); + System.out.println("\t You should drink " + user.getHydrationRequired() + "ml of water a day " + + "to hit your goals!"); user.setCaloriesRequired(calories); } catch (IOException | InterruptedException e) { System.out.println("You "); From e163181d2916ec006d2222338bcd76d86ec08cab Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:42:55 +0800 Subject: [PATCH 174/414] Add sequence diagram for hydration delete --- docs/HydrationDeleteDiagram.puml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/HydrationDeleteDiagram.puml diff --git a/docs/HydrationDeleteDiagram.puml b/docs/HydrationDeleteDiagram.puml new file mode 100644 index 0000000000..e69de29bb2 From 27ee869657401e187a05da335b0c77657cf467ef Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:43:07 +0800 Subject: [PATCH 175/414] Add sequence diagram --- docs/DeveloperGuide.md | 10 +++++++++- docs/HydrationDeleteDiagram.png | Bin 0 -> 35846 bytes docs/HydrationDeleteDiagram.puml | 31 +++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 docs/HydrationDeleteDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index afa902aa25..e950ba4833 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -96,6 +96,13 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 3: The created `HydrationEntry` instance is added into the `ArrayList` attribute of the `HydrationList`. +### Calculating hydration requirements for each user + +#### Design Considerations + +**General Health Guidelines:** The recommended daily intake of water for an average adult is around 8 glasses or approximately 2000 milliliters. This guideline is commonly recommended by health authorities and organizations such as HealthHub. + +**Ease of Implementation:** Setting a standard hydration requirement simplifies the tracking process for users. It provides a clear goal to strive for, making it easier for individuals to monitor and maintain their hydration levels consistently. ### Hydration list feature The `hydration list` feature lists out the record of all the Hydration data that the user has keyed in. The Hydration data are all stored into a `ArrayList hydrationArrayList` attribute of the `HydrationList` Class. Hydration data are printed when the `printHydrationList()` function is called. @@ -122,7 +129,8 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: The latest hydration list will be updated to saving file by calling `HydrationList#updateFile()`. -The Class diagram for Hydration delete feature is shown below: +The Sequence diagram for Hydration delete feature is shown below: +![HydrationDeleteDiagram.png](HydrationDeleteDiagram.png) ## Product scope ### Target user profile diff --git a/docs/HydrationDeleteDiagram.png b/docs/HydrationDeleteDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..26043bb76d822a33a340f62d4defe1f78bf825b0 GIT binary patch literal 35846 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 entries)" as WriteEntries + +User -> UI : Enters command +activate UI +UI -> HydrationInput : Sends command for hydration input handling +activate HydrationInput +HydrationInput -> DeleteEntry : Calls delete entry method +activate DeleteEntry +DeleteEntry -> UpdateFile : Deletes entry +activate UpdateFile +UpdateFile -> WriteEntries : Updates file with new entries +activate WriteEntries +WriteEntries --> UpdateFile : Updates file +deactivate WriteEntries +UpdateFile --> DeleteEntry : File updated +deactivate UpdateFile +DeleteEntry --> HydrationInput : Entry deleted +deactivate DeleteEntry +HydrationInput --> UI : Sends success message +deactivate HydrationInput +UI -> User : Displays success message +deactivate UI + +@enduml From cd9f9f4c6175798aa6d62c121969be0bb0959a71 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Tue, 2 Apr 2024 18:37:36 +0800 Subject: [PATCH 176/414] edit add calorie entries feature in DG --- docs/CaloriesAddEntrySeqDiagram.png | Bin 0 -> 23922 bytes docs/CaloriesAddEntrySeqDiagram.puml | 31 +++++++++++++++++++++++++++ docs/DeveloperGuide.md | 30 +++++++++++++++++++------- 3 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 docs/CaloriesAddEntrySeqDiagram.png create mode 100644 docs/CaloriesAddEntrySeqDiagram.puml diff --git a/docs/CaloriesAddEntrySeqDiagram.png b/docs/CaloriesAddEntrySeqDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..c0921c36188e55fc53eab89244be0199299c7ee7 GIT binary patch literal 23922 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 UI: Input "calories in" or "calories out" command +activate UI + +UI -> UI: handleUserInput() +activate UI + +UI -> UI: handleCaloriesInput() +activate UI + +UI -> CalorieList: addEntry() +activate CalorieList + +CalorieList -> ParserCalories: parseCaloriesInput() +activate ParserCalories +return entry + +CalorieList -> CalorieList: calorieArrayList.add(entry) +activate CalorieList +return + +CalorieList -> CalorieList: updateFile() +activate CalorieList +return + +return +return +return +return + +@enduml \ No newline at end of file diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index e950ba4833..934b35df11 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -5,20 +5,34 @@ {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} ## Design & implementation -### Parsing user input for caloric entries - -This functionality is facilitated by `ParserCalories`. It implements one operation, namely: +### Adding calorie entries feature + +#### Implementation + +This functionality is facilitated by `UI`, `CalorieList`, `FileHandler` and `ParserCalories`. It implements one operation, namely: +- `UI#handleCaloriesInput(String, CalorieList)` +- `CalorieList#addEntry(String)` - `ParserCalories#parseCaloriesInput(String)` +- `FileHandler#updateFile()` -This operation is exposed in the `CalorieList` class as `CalorieList#addEntry(String)`. +This feature is activated when the user inputs a `calories in` or `calories out` command in the terminal. Given below is an example usage scenario and how this mechanism behaves at every step: -- Step 1: When the user inputs the command `calories in burger c/200 date/270324` in the terminal, -the string is sent to `CalorieList#addEntry(String)`, which calls `ParserCalories#parseCaloriesInput(String)`. -- Step 2: Using `String.split()`, the method extracts information such as the description, number of calories, and date of entry. The obtained information is sent to the private method `ParserCalories#makeNewInputEntry(String, int, String)` to create a new entry of class `InputEntry` that extends `Entry`. +- Step 1: When the user inputs the command `calories in burger c/200 d/2024-02-02` in the terminal, +the string is sent to `UI#handleCaloriesInput(String, CalorieList)`, which calls `CalorieList#addEntry(String)`. + +- Step 2: Inside `CalorieList#addEntry(String)`, the function `ParserCalories#parseCaloriesInput(String)` is then called to extract information such as the description, number of calories, and date of entry. + +- Step 3: The obtained information is sent to the private method `ParserCalories#makeNewInputEntry(String, int, String)` to create a new entry of class `InputEntry` that extends `Entry`. An `Entry` object is then returned to the caller, `CalorieList#addEntry(String)`. + +- Step 4: The returned `Entry` object is added into the `calorieArrayList` member of type `ArrayList` in the `CalorieList`, via the `ArrayList.add()` method. + +- Step 5: `FileHandler#updateFile()` is then called to update the data file with the new entry in the `CalorieList`. + +The sequence diagram for this feature is shown below: -- Step 3: The created `InputEntry` instance is added into the `ArrayList` attribute of the `CalorieList`. +![CaloriesAddEntrySeqDiagram](CaloriesAddEntrySeqDiagram.png) ### Calculating calorie requirements based on a user`s goals From 87ca845e7c3464fb8976c2521002d2dc56f58222 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Tue, 2 Apr 2024 18:47:52 +0800 Subject: [PATCH 177/414] add expected output for help command --- docs/UserGuide.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 8124ff6661..bd2efd33d8 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -21,6 +21,29 @@ Shows a help message listing the commands available in the application. **Format:** `help` +#### Expected Output + + ----------------------------------------------------------------------------- + LifeTrack Command List: + - help: Displays a list of available commands and their descriptions. + ----------------------------------------------------------------------------- + - calories in c/ d/ m/[carbohydrates, proteins, fats]: Adds a calorie gaining entry into the calories tracker. + - calories out c/ d/: Adds a calorie burning entry into the calories tracker. + - calories list: Displays all entries currently stored in the calorie list. + - calories delete : Deletes the entry at the specified index from the calorie list. + ----------------------------------------------------------------------------- + - hydration in v/ d/: Adds a hydration entry into the hydration tracker. + - hydration list: Displays all entries currently stored in the hydration list. + - hydration delete : Deletes the hydration entry at the specified index from the hydration list. + ----------------------------------------------------------------------------- + - sleep add d/: Adds a sleep entry into the sleep tracker. + - sleep list: Displays all entries currently stored in the sleep list. + - sleep delete : Deletes the entry at the specified index from the sleep list. + ----------------------------------------------------------------------------- + - user setup h/ w/ a/ s/ e/ g/: Create a new user, or edit an existing one. + - user progress: Display calories and hydration progress towards the daily requirement. + ----------------------------------------------------------------------------- + ### Exiting the program: `bye` Exits the program. From 60998919e6954670f43956082193e542a99ae62f Mon Sep 17 00:00:00 2001 From: owx0130 Date: Tue, 2 Apr 2024 18:51:19 +0800 Subject: [PATCH 178/414] add expected output for bye command --- docs/UserGuide.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index bd2efd33d8..3bafa3f26e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -51,6 +51,11 @@ Exits the program. **Format:** `bye` +#### Expected Output + + ----------------------------------------------------------------------------- + Bye! See you again soon ^^ + ## Calories Tracker ### Input calorie intake: `calories in` From 0bd347e84376abfab68ae9a3eb1e997a60c70ebb Mon Sep 17 00:00:00 2001 From: owx0130 Date: Tue, 2 Apr 2024 18:54:51 +0800 Subject: [PATCH 179/414] add short introduction --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3bafa3f26e..c8c171938f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,7 +2,7 @@ ## Introduction -{Give a product intro} +LifeTrack is a desktop app for students to track their health data, optimized for use via a Command Line Interface (CLI). It tracks calories, hydration and sleep data for the user, while also providing daily recommendations for calorie and hydration intake, based on the user's build and gender, as well as their body goals and activity levels. ## Quick Start From 59f5c47d10bb84663afa08252d9f69feabeed1e7 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 10:29:48 +0800 Subject: [PATCH 180/414] Javadocs for Class CalorieLIst --- .../calories/calorielist/CalorieList.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 48a2d80fbe..648cccf1a9 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -15,7 +15,7 @@ public class CalorieList { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); - + private final int SIZE_OF_DELETE = 16; private ArrayList calorieArrayList; private FileHandler fileHandler; @@ -25,7 +25,12 @@ public CalorieList() { calorieArrayList = new ArrayList<>(); } - //constructor for usage in terminal + /** + * Constructs a new CalorieList object using a file path. + * This constructor is intended for usage in a terminal environment. + * + * @param filePath the path to the file containing calorie entries + */ public CalorieList(String filePath) { try { fileHandler = new FileHandler(filePath); @@ -36,6 +41,10 @@ public CalorieList(String filePath) { } } + /** + * Updates the file with the current list of calorie entries. + * If the file handler is not initialized, no action is taken. + */ private void updateFile() { if (fileHandler != null) { fileHandler.writeEntries(calorieArrayList); @@ -48,7 +57,9 @@ public Entry getEntry(int index) { /** + * Deletes a calorie entry from the list based on the provided index. * Index should be in an integer from 1 to size of the list. + * * @param line the string containing the index of calorie record user want to delete */ public void deleteEntry(String line) { @@ -108,9 +119,20 @@ public void printCalorieList() { } } + /** + * Returns the size of the list of calorie entries. + * + * @return the number of calorie entries in the list + */ public int getSize() { return calorieArrayList.size(); } + + /** + * Calculates and returns the total number of calories consumed from all entries in the list. + * + * @return the total number of calories consumed + */ public int getCaloriesConsumed() { int totalCalories = 0; for (int i = 0; i < calorieArrayList.size(); i++) { From 58541187ec96a312b4af98cb01e91e212c9d1c52 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 10:36:50 +0800 Subject: [PATCH 181/414] Add javadocs for Class ParserCalories --- .../system/parser/ParserCalories.java | 87 ++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index ded1acbc06..79c5b6f2c1 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -44,7 +44,7 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio int caloriesIndex = input.indexOf("c/"); int dateIndex = input.indexOf("d/"); int macrosIndex = input.indexOf("m/"); - + checkKeywordsExist(caloriesIndex, dateIndex); assert caloriesIndex != -1 : "The c/ keyword should exist!"; assert dateIndex != -1 : "The d/ keyword should exist!"; @@ -111,12 +111,25 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio } //@@author rexyyong + /** + * Parses a string representation of a date and returns a LocalDate object. + * + * @param strDate the string representation of the date + * @return the LocalDate object parsed from the input string + * @throws DateTimeParseException if the input string cannot be parsed as a valid date + */ public static LocalDate getLocalDateFromInput(String strDate) throws DateTimeParseException { LocalDate date = LocalDate.parse(strDate); return date; } //@@author + /** + * Parses a string representation of calories and returns an integer value. + * + * @param strCalories the string representation of calories + * @return the integer value of calories parsed from the input string + */ private static int getIntegerCaloriesFromInput(String strCalories) { int calories = 0; try { @@ -127,6 +140,14 @@ private static int getIntegerCaloriesFromInput(String strCalories) { return calories; } + /** + * Extracts the description from the input string based on the command and the index of calories. + * + * @param inputString the input string containing the description + * @param command the command string indicating the type of entry ("calories in" or "calories out") + * @param caloriesIndex the index of the "calories" keyword in the input string + * @return the description extracted from the input string + */ private static String getDescriptionFromInput(String inputString, String command, int caloriesIndex) { String description; if (command.equals("calories out")) { @@ -138,6 +159,14 @@ private static String getDescriptionFromInput(String inputString, String command return description; } + /** + * Parses a string containing macros (carbs, proteins, fats) and returns an array of integers. + * The string should be in the format "carbs,proteins,fats". + * + * @param macroString the string containing macros separated by commas + * @return an array of integers representing macros [carbs, proteins, fats] + * @throws InvalidInputException if the input string is not in the correct format or contains invalid values + */ private static int[] getMacrosFromInput(String macroString) throws InvalidInputException { int[] macros = new int[3]; try { @@ -161,12 +190,26 @@ private static int[] getMacrosFromInput(String macroString) throws InvalidInputE return macros; } + /** + * Checks if the calorie value is a positive integer. + * + * @param calories the calorie value to check + * @throws InvalidInputException if the calorie value is not a positive integer + */ private static void checkCaloriesIsPositiveInteger(int calories) throws InvalidInputException { if (calories <= 0) { throw new InvalidInputException(getIncorrectCaloriesInputMessage()); } } + /** + * Checks if the inputs (description, calories, and date) are non-empty strings. + * + * @param description the description of the entry + * @param strCalories the string representation of calories + * @param date the date of the entry + * @throws InvalidInputException if any of the inputs are empty strings + */ private static void checkInputsAreNonEmpty(String description, String strCalories, String date) throws InvalidInputException { //check if the description, calories or date fields are empty @@ -175,6 +218,13 @@ private static void checkInputsAreNonEmpty(String description, String strCalorie } } + /** + * Checks if the keywords for calories and date exist in the input string. + * + * @param caloriesIndex the index of the "c/" keyword in the input + * @param dateIndex the index of the "date/" keyword in the input + * @throws InvalidInputException if the keywords are missing + */ private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws InvalidInputException { //check that c/ and date/ keywords exist in the input, else throw exception if (caloriesIndex == -1 || dateIndex == -1) { @@ -182,25 +232,58 @@ private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws } } + /** + * Checks if the keywords for calories and date are correctly ordered in the input string. + * + * @param caloriesIndex the index of the "c/" keyword in the input + * @param dateIndex the index of the "date/" keyword in the input + * @param macrosIndex the index of the "macros/" keyword in the input + * @throws InvalidInputException if the keywords are not correctly ordered + */ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateIndex, int macrosIndex) - throws InvalidInputException { + throws InvalidInputException { if ((macrosIndex != -1 && !(caloriesIndex < dateIndex && dateIndex < macrosIndex)) || (macrosIndex == -1 && !(caloriesIndex < dateIndex))) { throw new InvalidInputException(getCaloriesIncorrectOrderMessage()); } } + /** + * Creates a new output entry with the given description, calories, and date. + * + * @param description the description of the entry + * @param calories the number of calories + * @param date the date of the entry + * @return a new OutputEntry object + */ private static Entry makeNewOutputEntry(String description, int calories, LocalDate date) { Activity newActivity = new Activity(); return new OutputEntry(description, calories, date, newActivity); } + /** + * Creates a new input entry with the given description, calories, and date. + * + * @param description the description of the entry + * @param calories the number of calories + * @param date the date of the entry + * @return a new InputEntry object + */ private static Entry makeNewInputEntry(String description, int calories, LocalDate date) { return new InputEntry(description, calories, date); } + /** + * Creates a new input entry with the given description, calories, date, and food macros. + * + * @param description the description of the entry + * @param calories the number of calories + * @param date the date of the entry + * @param foodMacros an array containing food macros (carbs, proteins, fats) + * @return a new InputEntry object with food macros + */ private static Entry makeNewInputEntry(String description, int calories, LocalDate date, int[] foodMacros) { Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); From 60f89dab0f7fa5f6c341b3a01beb40702596d0e6 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 10:38:09 +0800 Subject: [PATCH 182/414] Add javadocs for Class InvalidInputException --- .../system/exceptions/InvalidInputException.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index 818ec5bcbc..cda9303d4f 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -9,12 +9,21 @@ public class InvalidInputException extends Exception { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); public final String heythere = ""; + /** + * Constructs a new InvalidInputException with a default error message. + * The default message provides guidance on ensuring correct input format. + */ public InvalidInputException(){ super("\t Please ensure that you have keyed in the correct format!"); logr.setLevel(Level.SEVERE); logr.log(Level.WARNING,"\t Please ensure that you have keyed in the correct format!"); } + /** + * Constructs a new InvalidInputException with a custom error message. + * + * @param exception the custom error message describing the specific input error + */ public InvalidInputException(String exception) { super(exception); } From 73b5fa795bee905aa9ac47b917daa51a2951a527 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 10:39:13 +0800 Subject: [PATCH 183/414] Add Javadocs for Class ErrorMessages --- .../java/seedu/lifetrack/system/exceptions/ErrorMessages.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index 55e2855e94..62aecb0b14 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -1,5 +1,8 @@ package seedu.lifetrack.system.exceptions; +/** + * Utility class for managing error messages related to the application. + */ public class ErrorMessages { private static final String WHITESPACE = " "; From 23928590057e06cc28356b400da5e2e0e1e86d62 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 10:42:22 +0800 Subject: [PATCH 184/414] Add Javadocs for Class InvalidInputExceptionMessages --- .../system/exceptions/InvalidInputExceptionMessage.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index fb4fe4ab57..40c39c417a 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -1,5 +1,8 @@ package seedu.lifetrack.system.exceptions; +/** + * Utility class for managing error messages related to invalid input exceptions. + */ public class InvalidInputExceptionMessage { private static final String HEADER = "\t Invalid input!\n"; From 8a5ef76eb7dcfa329d56bf60de7ba9cd024cddb9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 10:45:50 +0800 Subject: [PATCH 185/414] Add javadocs for Class InputEntry --- .../calories/calorielist/InputEntry.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index 247e605efe..884396da33 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -5,17 +5,37 @@ import java.time.LocalDate; +/** + * Represents an entry for calories intake. + * Extends the Entry class and includes additional fields and methods specific to input entries. + */ public class InputEntry extends Entry { private Food food; private int calories; private boolean doesFoodExist = false; + /** + * Constructs a new InputEntry object with the given description, calories, and date. + * + * @param description the description of the entry + * @param calories the number of calories consumed + * @param date the date of the entry + */ public InputEntry(String description, int calories, LocalDate date) { super(description, date); this.calories = calories; } + /** + * Constructs a new InputEntry object with the given description, calories, date, + * and food details with macronutrients. + * + * @param description the description of the entry + * @param calories the number of calories consumed + * @param date the date of the entry + * @param food the food details with macronutrients associated with the entry + */ public InputEntry(String description, int calories, LocalDate date, Food food) { super(description, date); this.food = food; From e59110d4a388dab7a3ef91d8a385df2fa4675af9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 10:47:28 +0800 Subject: [PATCH 186/414] Add jacadocs for Class OutputEntry --- .../calories/calorielist/OutputEntry.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java index 2ac4ef8e24..fb98d753db 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java @@ -5,18 +5,36 @@ import java.time.LocalDate; +/** + * Represents an entry for calories output. + * Extends the Entry class and includes additional fields and methods specific to output entries. + */ public class OutputEntry extends Entry { private Activity activity; private int calories; private boolean doesActivityExist = false; - + /** + * Constructs a new OutputEntry object with the given description, calories, and date. + * + * @param description the description of the entry + * @param calories the number of calories burnt + * @param date the date of the entry + */ public OutputEntry(String description, int calories, LocalDate date) { super(description, date); this.calories = calories; } + /** + * Constructs a new OutputEntry object with the given description, calories, date, and activity details. + * + * @param description the description of the entry + * @param calories the number of calories burnt + * @param date the date of the entry + * @param activity the activity details associated with the entry + */ public OutputEntry(String description, int calories, LocalDate date, Activity activity) { super(description, date); this.activity = activity; From 967923778e3e9ba55e2bc879f930bedf325c08c9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 15:45:13 +0800 Subject: [PATCH 187/414] Edit user UserGuide Add quick links Add calories in details Add calories out details Add calories list details Add command summary --- docs/UserGuide.md | 76 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c8c171938f..a62e10effd 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -4,6 +4,27 @@ LifeTrack is a desktop app for students to track their health data, optimized for use via a Command Line Interface (CLI). It tracks calories, hydration and sleep data for the user, while also providing daily recommendations for calorie and hydration intake, based on the user's build and gender, as well as their body goals and activity levels. +## Quick links +- [Quick Start](#quick-start) +- [General](#general) + - [help](#viewing-help-help) + - [bye](#exiting-the-program-bye) +- [Calories Tracker](#calories-tracker) + - [Input calories Intake](#input-calorie-intake-calories-in) + - [Input calorie loss](#input-calorie-loss-calories-out) + - [Listing calorie items](#listing-calorie-items-calories-list) + - [Deleting a calorie item](#deleting-a-calorie-item-calories-delete) +- [Hydration Tracker](#hydration-tracker) + - [Input hydration intake](#input-hydration-intake-hydration-in) + - [Listing hydration items](#listing-hydration-items-hydration-list) + - [Deleting a hydration item](#deleting-a-hydration-item-hydration-delete) +- [Sleep Tracker](#sleep-tracker) + - [Input sleeping hours](#input-sleeping-hours-sleep-add) + - [Listing sleep records](#listing-sleep-records-sleep-list) + - [Deleting a sleep record](#deleting-a-sleep-record-sleep-delete) +- [FAQ](#faq) +- [Command Summary](#command-summary) + ## Quick Start {Give steps to get started quickly} @@ -60,40 +81,58 @@ Exits the program. ### Input calorie intake: `calories in` Adds a calorie gaining activity into the calories tracker. +Macronutrients such as Carbohydrates, Proteins and Fats can be included if needed. **Format:** -`calories in DESCRIPTION c/CALORIES_IN d/DATE` +`calories in DESCRIPTION c/CALORIES d/DATE [m/CARBOHYDRATES,PROTEIN,FATS]` -* The calories must be a positive integer 1, 2, 3, …, measured in kcal. -* The time indicated should follow the 24-hour system. -* The date provided should be of the form YYYY-MM-DD. +* The `DESCRIPTION` refers to the food that the person consumed. +* The `CALORIES` must be a positive integer 1, 2, 3, …, measured in kcal. +* The `DATE` provided should be of the form YYYY-MM-DD, such as 2024-03-04. +* Macronutrients field including `CARBOHYDRATES`, `PROTEINS` and `FATS` is optional. The macronutrients must be a positive integer 1, 2, 3, measured in grams. **Examples:** -* `calories in eat chicken rice c/678 d/2022-02-24` -* `calories in drink liho milk tea c/430 d/1022-03-25` +* `calories in chicken rice c/678 d/2022-02-24` +* `calories in hamburger c/983 d/2024-04-03` +* `calories in cai png c/543 d/2024-04-13 m/200, 150, 100` +* `calories in drink liho milk tea c/200 d/2024-04-25 m/50, 20, 10` ### Input calorie loss: `calories out` Adds a calorie burning activity into the calories tracker. **Format:** -`calories out DESCRIPTION c/CALORIES_OUT d/DATE` +`calories out DESCRIPTION c/CALORIES d/DATE` -* The calories must be a positive integer 1, 2, 3, …, measured in kcal. -* The time indicated should follow the 24-hour system. -* The date provided should be of the form YYYY-MM-DD. +* The `DESCRIPTION` refers to any activity that resulted in loss of calories. +* The `CALORIES` must be a positive integer 1, 2, 3, …, measured in kcal. +* The `DATE` provided should be of the form YYYY-MM-DD such as 2024-04-03. **Examples:** * `calories out Run around NUS c/678 d/2022-02-24` * `calories out Walk to i3 building c/67 d/2022-03-25` +* `calories out go gym c/300 d/2024-04-03` ### Listing calorie items: `calories list` -Shows a list of all activities in the calories tracker. +Shows a list of all activities in the calories tracker. Includes both calories in and out. **Format:** `calories list` +#### Sample output + ----------------------------------------------------------------------------- + Your Caloric List: + 1. Date: 2024-06-15, Description: chicken, Calories: 1000 + 2. Date: 2024-06-15, Description: chicken, Calories: 1000 + 3. Date: 2024-05-15, Description: chicken, Calories: 1000 + 4. Date: 2023-03-01, Description: taco, Calories: 1 + 5. Date: 2024-04-03, Description: burger, Calories: 100 + 6. Date: 2024-04-03, Description: cai png, Calories: 1000 + 7. Date: 2024-04-03, Description: cai png, Calories: 500 + 8. Date: 2024-04-13, Description: liho milk tea, Calories: 200 + ----------------------------------------------------------------------------- + ### Deleting a calorie item: `calories delete` Deletes the specified activity from the calories tracker. @@ -196,6 +235,17 @@ Example of usage: ## Command Summary -{Give a 'cheat sheet' of commands here} - +| Action | Format, Examples | +|------------------------|----------------------------------------------------------------------------| +| Help | `help` | +| Add calories intake | `calories in DESCRIPTION c/CALORIES d/DATE [m/CARBOHYDRATES,PROTEIN,FATS]` | +| Add calories outflow | `calories out DESCRIPTION c/CALORIES d/DATE` | +| List calories | `calories list` | +| Delete calories entry | `calories delete INDEX` | +| Add hydration intake | `hydration in DESCRIPTION v/VOLUME d/DATE` | +| List hydration | `hydration list` | +| Delete hydration entry | `hydration delete INDEX` | +| Add sleep | `sleep add DURATION d/DATE` | +| List sleep | `sleep list` | +| Delete sleep entry | `sleep delete INDEX` | * Add todo `todo n/TODO_NAME d/DEADLINE` From f7ae93798b799d35ddd16844199959fc5ffda2b5 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 18:16:27 +0800 Subject: [PATCH 188/414] Edit developer guide --- docs/DeveloperGuide.md | 3 ++- docs/team/CaloriesListSequenceDiagram.puml | 27 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 docs/team/CaloriesListSequenceDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 934b35df11..7d476e6645 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -71,9 +71,10 @@ The `calories list` feature lists out the record of all the Calories data that t The `printCalorieList()` function iterates through the `calorieArrayList` and prints out the Entries according to its order in the Array List. -The Class diagram for Calories list feature is shown below. Unrelated attributes and Classes were excluded. +The Class diagram and sequence diagram for Calories list feature is shown below. Unrelated attributes and Classes were excluded. ![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/DevGuideRex/docs/CaloriesListClassDiagram.puml) +![CaloriesListSequenceDiagram]() ### Calories delete feature diff --git a/docs/team/CaloriesListSequenceDiagram.puml b/docs/team/CaloriesListSequenceDiagram.puml new file mode 100644 index 0000000000..3d99b22c68 --- /dev/null +++ b/docs/team/CaloriesListSequenceDiagram.puml @@ -0,0 +1,27 @@ +@startuml +actor Bob + +Bob -> UI: Input "calories list" command +activate UI + +UI -> UI: handleUserInput(input, calorieList, .....) +activate UI + +UI -> UI: handleCaloriesInput(input, calorieList, .....) +activate UI + +UI -> CalorieList: printCalorieList() +activate CalorieList + +loop calorieArrayList.size() +CalorieList -> CalorieList : calorieArrayList.get(i) +activate CalorieList +end + +return +return +return +return +return + +@enduml \ No newline at end of file From e83e6abad7220fe08db9dbeab48cc6738e837af6 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 18:19:31 +0800 Subject: [PATCH 189/414] shift sequence diagram puml file under docs --- docs/{team => }/CaloriesListSequenceDiagram.puml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{team => }/CaloriesListSequenceDiagram.puml (100%) diff --git a/docs/team/CaloriesListSequenceDiagram.puml b/docs/CaloriesListSequenceDiagram.puml similarity index 100% rename from docs/team/CaloriesListSequenceDiagram.puml rename to docs/CaloriesListSequenceDiagram.puml From da2cc1703c24f4833b653abc7faa128e89d3cb08 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 18:21:37 +0800 Subject: [PATCH 190/414] Display Sequence diagram on DG --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 7d476e6645..518d1ca3e6 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -74,7 +74,7 @@ The `printCalorieList()` function iterates through the `calorieArrayList` and pr The Class diagram and sequence diagram for Calories list feature is shown below. Unrelated attributes and Classes were excluded. ![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/DevGuideRex/docs/CaloriesListClassDiagram.puml) -![CaloriesListSequenceDiagram]() +![CaloriesListSequenceDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/RexDG/docs/CaloriesListSequenceDiagram.puml) ### Calories delete feature From bba272aa9b1114481f668f5c0043f3844cbebad3 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 18:28:35 +0800 Subject: [PATCH 191/414] Add actor in plantUML for wei xiang --- docs/CaloriesAddEntrySeqDiagram.puml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CaloriesAddEntrySeqDiagram.puml b/docs/CaloriesAddEntrySeqDiagram.puml index 303e6e00a9..a2aee2cfaa 100644 --- a/docs/CaloriesAddEntrySeqDiagram.puml +++ b/docs/CaloriesAddEntrySeqDiagram.puml @@ -1,5 +1,6 @@ @startuml -User -> UI: Input "calories in" or "calories out" command +actor Bob +Bob -> UI: Input "calories in" or "calories out" command activate UI UI -> UI: handleUserInput() From 6fb6e355c75119fefbb28d7180450b1831598caf Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 3 Apr 2024 18:30:35 +0800 Subject: [PATCH 192/414] Show sequence diagram for CaloriesAddEntry --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 518d1ca3e6..9dee9d4f85 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -32,7 +32,7 @@ the string is sent to `UI#handleCaloriesInput(String, CalorieList)`, which calls The sequence diagram for this feature is shown below: -![CaloriesAddEntrySeqDiagram](CaloriesAddEntrySeqDiagram.png) +![CaloriesAddEntrySeqDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/RexDG/docs/CaloriesAddEntrySeqDiagram.puml) ### Calculating calorie requirements based on a user`s goals From caf4f53fed75e88dc8872cb1b73112d153ebd30d Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Wed, 3 Apr 2024 23:04:57 +0800 Subject: [PATCH 193/414] update DeveloperGuide: Sleep and sequence diagram --- docs/DeveloperGuide.md | 55 +++++++++++++++++++ .../lifetrack/sleep/sleeplist/SleepList.java | 2 +- .../lifetrack/system/parser/ParserSleep.java | 3 +- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 9dee9d4f85..c2f05d111d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -118,6 +118,7 @@ Given below is an example usage scenario and how this mechanism behaves at every **General Health Guidelines:** The recommended daily intake of water for an average adult is around 8 glasses or approximately 2000 milliliters. This guideline is commonly recommended by health authorities and organizations such as HealthHub. **Ease of Implementation:** Setting a standard hydration requirement simplifies the tracking process for users. It provides a clear goal to strive for, making it easier for individuals to monitor and maintain their hydration levels consistently. + ### Hydration list feature The `hydration list` feature lists out the record of all the Hydration data that the user has keyed in. The Hydration data are all stored into a `ArrayList hydrationArrayList` attribute of the `HydrationList` Class. Hydration data are printed when the `printHydrationList()` function is called. @@ -147,6 +148,60 @@ Given below is an example usage scenario and how this mechanism behaves at every The Sequence diagram for Hydration delete feature is shown below: ![HydrationDeleteDiagram.png](HydrationDeleteDiagram.png) +### Parsing user input for sleep entries + +This functionality is facilitated by `ParserSleep`. It implements one operation, namely: +- `ParserSleep#parseSleepInput(String input)` + +This operation is exposed in the `SleepList` class as `SleepList#addSleep(String)`. + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `sleep add 7.5 d/2022-01-02` in the terminal, + the string is sent to `SleepList#addEntry(String)`, which calls `ParserSleep#parseSleepInput(String)`. + +- Step 2: Using `String.split()`, the method extracts information such as the duration and date of entry. + +- Step 3: It will create a new entry of class `SleepEntry` that extends `Entry`based on the information. + +- Step 4: The created `SleepEntry` instance is added into the `ArrayList sleepList` attribute of the `SleepList`. + +The sequence diagram for ParserSleep feature is shown below. Unrelated attributes and Classes were excluded. + + +### Sleep list feature + +The `sleep list` feature lists out the record of all the sleep data that the user has keyed in. The sleep data are all stored into a `ArrayList sleepList` attribute of the `SleepList` Class. Sleep data are printed when the `printSleepList()` function is called. + +The `printSleepList()` function iterates through the `sleepList` and each entry will call `SleepEntry#toString()` to return its information string to be printed. + +The Sequence diagram for Sleep list feature is shown below. Unrelated attributes and Classes were excluded. + +### Sleep delete feature + +The `sleep delete` feature can delete the sleep record at specific index of sleep list. This functionality is facilitated by `SleepList`. It implements one operation, namely: +- `deleteSleep(String line)` + +Given below is an example usage scenario and how this mechanism behaves at every step: +- Step 1: When the user inputs the command `sleep delete INDEX` in the terminal, the string is sent to `Ui#handleUserInput()`, which will call `Ui#handleSleepInput()`. + +- Step 2: After the `Ui#handleSleepInput()` matching `sleep delete` key word, the string will be passed into deleteSleep(String line) to execute delete process. + +- Step 3: The string will be divided to two substrings according to the command syntax. Index will be tried to get from the second substring by `Integer.parseInt()`. + +- Step 4: The sleep record (`Entry`) stored in the `ArrayList sleepList` will be deleted by calling `sleepArrayList.remove((index-1));` and a successful deleting message will be shown in terminal by calling `SleepListUi#successfulDeletedMessage(toDelete)` + +- Step 5: The latest sleep list will be updated to saving file by calling `SleepList#updateFile()`. + +The Sequence diagram for Sleep delete feature is shown below: + +### Calculating sleep requirements for each user (Planning) + +#### Design Considerations + +**General Health Guidelines:** The recommended daily sleep duration for an average adult is around 7.5 hours. However, sleep standard is different for different individual with different healthy status. This feature will calculate recommend sleep duration by formula based on user health information. + +**Ease of Implementation:** Setting a standard sleep requirement simplifies the tracking process for users. It provides a clear goal to strive for, making it easier for individuals to monitor and maintain their sleep time levels consistently. + ## Product scope ### Target user profile diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 3a11f07f6d..93b9632ad6 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -56,7 +56,7 @@ public void addSleep(String input) { public void deleteSleep(String line) { try { - int index = Integer.parseInt(line.split(" ")[2]) ; + int index = Integer.parseInt(line.split(" ")[2]) ; //User input format: sleep delete INDEX, here get index Entry toDelete = sleepList.get(index-1); sleepList.remove(index - 1); updateFile(); diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index e9a5a63354..b687525a33 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -9,6 +9,7 @@ import java.time.LocalDate; import java.time.format.DateTimeParseException; +//@@author a-wild-chocloate public class ParserSleep { public static SleepEntry parseSleepInput(String input) throws InvalidInputException { @@ -31,7 +32,7 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + "sleep add d/"); } - + //@author //@@author rexyyong // Parse str date to date of type LocalDate try { From 131615267e3e195245e9ac049cb1d509530a2127 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 4 Apr 2024 02:26:40 +0800 Subject: [PATCH 194/414] update DeveloperGuide: Sleep add --- docs/DeveloperGuide.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index c2f05d111d..b60e4cec44 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -148,6 +148,34 @@ Given below is an example usage scenario and how this mechanism behaves at every The Sequence diagram for Hydration delete feature is shown below: ![HydrationDeleteDiagram.png](HydrationDeleteDiagram.png) +### Adding sleep entries feature + +#### Implementation + +This functionality is facilitated by `UI`, `SleepList`, `FileHandler` and `ParserSleep`. It implements one operation, namely: +- `UI#handleSleepInput(String,SleepList)` +- `SleepList#addEntry(String)` +- `ParserSleep#parseSleepInput(String)` +- `FileHandler#updateFile()` + +This feature is activated when the user inputs a `sleep add` command in the terminal. + +Given below is an example usage scenario and how this mechanism behaves at every step: + +- Step 1: When the user inputs the command `sleep add 7.5 d/2022-01-02` in the terminal, + the string is sent to `UI#handleSleepInput(String, SleepList)`, which calls `SleepList#addSleep(String)`. + +- Step 2: Inside `SleepList#addSleep(String)`, the function `ParserSleep#parseSleepInput(String)` is then called to extract information such as the duration and date of entry. + +- Step 3: It will create a new entry of class `SleepEntry` that extends `Entry`based on the information. + +- Step 4: The created `SleepEntry` instance is added into the `ArrayList sleepList` attribute of the `SleepList`. + +- Step 5: `FileHandler#updateFile()` is then called to update the data file with the new entry in the `SleepList`. + +The sequence diagram for this feature is shown below: + + ### Parsing user input for sleep entries This functionality is facilitated by `ParserSleep`. It implements one operation, namely: From 10e64f666c4c61faf6e5ef57374a094e64ff408a Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 4 Apr 2024 03:05:37 +0800 Subject: [PATCH 195/414] Add files via upload --- docs/SleepListSeqDiagram.jpg | Bin 0 -> 43952 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/SleepListSeqDiagram.jpg diff --git a/docs/SleepListSeqDiagram.jpg b/docs/SleepListSeqDiagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..109b48bb7ace6a250ecaabe466ee8e01803c7512 GIT binary patch literal 43952 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 uvGm0|yB- Date: Thu, 4 Apr 2024 03:08:07 +0800 Subject: [PATCH 196/414] Add files via upload --- docs/SleepAddSeqDiagram.jpg | Bin 0 -> 69189 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/SleepAddSeqDiagram.jpg diff --git a/docs/SleepAddSeqDiagram.jpg b/docs/SleepAddSeqDiagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c187a8ce7e3f4c0514b032cd9f217559639bac82 GIT binary patch literal 69189 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�$R5wV{ih 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 literal 0 HcmV?d00001 From a6602c3ffdd35168b0c3be6b852844783826eb64 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 4 Apr 2024 03:26:24 +0800 Subject: [PATCH 197/414] Delete docs/SleepListSeqDiagram.jpg --- docs/SleepListSeqDiagram.jpg | Bin 43952 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/SleepListSeqDiagram.jpg diff --git a/docs/SleepListSeqDiagram.jpg b/docs/SleepListSeqDiagram.jpg deleted file mode 100644 index 109b48bb7ace6a250ecaabe466ee8e01803c7512..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43952 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 uvGm0|yB- Date: Thu, 4 Apr 2024 03:26:33 +0800 Subject: [PATCH 198/414] Delete docs/SleepAddSeqDiagram.jpg --- docs/SleepAddSeqDiagram.jpg | Bin 69189 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/SleepAddSeqDiagram.jpg diff --git a/docs/SleepAddSeqDiagram.jpg b/docs/SleepAddSeqDiagram.jpg deleted file mode 100644 index c187a8ce7e3f4c0514b032cd9f217559639bac82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69189 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�$R5wV{ih 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 From f9a94746e763ddf54a4309583b71e0fdbabdb141 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Thu, 4 Apr 2024 03:27:13 +0800 Subject: [PATCH 199/414] Add files via upload --- docs/SleepAddSeqDiagram.jpg | Bin 0 -> 68509 bytes docs/SleepDeleteSeqDiagram.jpg | Bin 0 -> 53610 bytes docs/SleepListSeqDiagram.jpg | Bin 0 -> 43751 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/SleepAddSeqDiagram.jpg create mode 100644 docs/SleepDeleteSeqDiagram.jpg create mode 100644 docs/SleepListSeqDiagram.jpg diff --git a/docs/SleepAddSeqDiagram.jpg b/docs/SleepAddSeqDiagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2ca47c719ea475976ddc7601b5fcab5c8c1376d2 GIT binary patch literal 68509 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 E00Y#Cv;Y7A literal 0 HcmV?d00001 diff --git a/docs/SleepDeleteSeqDiagram.jpg b/docs/SleepDeleteSeqDiagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bf521746cb7734eec23fa6b7171b5fa3e98f2b29 GIT binary patch literal 53610 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

4*f zi3%;1gMZ`q>FhF6i5T=HuVBjYT~?s;s~aY}4)c3@gE?{?;9bU1Zle1SS^V|jKZ6;j zC)e(z&H zSlbE4*Djo#$NRuH5=_hAZ>26GK`4QX8njNd_DRe%ty6UOz?v(6xwo zKQz<|%?K6wM^|V6ivJ1PzZoh-OxF;_@D0h-c{9?p8^m!S$EFYfkH-oKfc#2Rla>gX zJAQTqKpdmVubf~*E%sk_XK((q*#G1^35N0+d=y%R?*_3F02}+K4MD4Fr1+A~oMUBd z4gp}0J~}+UQD<=C244gxVGHf>zpQXZxCyfim=`vx7^hx^E;@Ff%PQgnTiPfF&))5V zOe`d9*RpyrYe_!&ZpjXA{%YSDWMcU0gtwCMr8)NnB|F-udR*O z**h|RvYor?LlUP$b?4zheXYB3M^5!_c!PGl{ZGoQQWwkn#UU=^p;lHly$&?rE_BPh zVbiUKEh@#n7NUzE?gUdE!a27B8VXkW56w8eU6XXa=sX8hG(&p4XjoGO6KD48NL^Pe zD7R1Yw2Pp5x-ZN}-K+URX(vR6maJ^j2mr|Jn!M3Py1aBJ8k^S~AnB`=l6jE^vqA9o zEOa(={wjh1cvfDy2bcQR5YP?vAK-K4)=2dfKF>!2k)vfxXJlw=uH0ZdUaz-L#py&6 z?Xz#;+L2+fke~2Hs<9?L#jH=A90{K^^X6PP*o( z>CKFS_$t-AQh5@-ZfKR)%x%%1<{e*I-00$5nY!;f&6Eiqn_tyO$BQ?XmY-~uO$R=` z(tmbqJ2BIuQRO=9g->Z{Tqt5iRuf6Mi`At9`$=_ zjVf@J@oz2}WH1eBsXfK?QVy`%F-|^yr2V6`E}ZM*Zi}-Foy|IWW8cOt*qyi#YuCQh z17=>>*w+=V-h?#;=H?4m((sbha#8SlOPe8yj^de1L6~8c)ztY|1D-q_d-lF}QJvDd zV))4BA;dC2LkjWgrMI4!^q{kJ+*$t{pmU-pgYj<7PLbE)8wc68_`&LX->Bm-^&#=p zT>Ks5Ku>hn^!6OL-i{>!U{v?hUu0BjS15jpRj80UcWR4HEc3z-+?5xJ^`h^N{l^V* zcCte}9T6bVfWcR>5785AqF6lwK-6F{=ZsZh?_7GYWQ+Tk>Y@xT3lt9DzBk@-*m$S> zGvr`q{CpoS(+ezf0PRZ`91wjJ(UjSjv$6lgi2nnuB|*V!lyHfYplwI=I6mjI#P_XX z&=b#Jmnz{CeT(JiAak>~b)o1wk0;dMrb8W}{O;D@?b87r!j0EiJ9-j)YaST%7Y z5fWC&CQQVOJGG>;Ghfuyp7B*^_c%YYS~e@&MPA016XDO>!0P$1^76|y7_(^qW({{r zousgt&0@vOBC8UsJE5-6lDXKUNo(zXT)P3Zg>trLD7EL%n?pTs$F{q=#D3Mglz>hu(v} zrJPNSy78+(0cFOW69J<0sd{;_e zL455(W6LzZ5&+c+ogfw%&zOQUrt3KoabW+B=YI7lZb!QOP_?nymd9#cw9?%E+HICc z(J3CO7DC@+b*_+{l(K8y9Nw+Y;{NgDbu@p;|hXMoFDW>O&LKsnVG? zY4??vja@>wGMC&x*I(>WPiO|{YC*NP_oVBA?Jb;>Q(MV%vm0|n@=vN9rsu1EM(cvO z5T+QAs0tKgx2_?XZY8Cx!&aa}2T&tHw`RAv3zN<>CB8-IztLXZeQ*`W`#kn1C0q-Z zgaIwdL?6K;fYlM?jAkDfl4mcgb8k54tVd&zQZ>!e+xmMns%V8uu` zHYJvF=<#FotVi%LGh4NKbasLxITnqZ5{n!anV_~mLn(x(3YC;RrnXd{@ zBAOc)MLW*nHZdsSi0JoUnnrM3C43r0V`8-GNgMz6q;xkTowc#D=fhH;!`!?qI`Avt zh=^E0TU>!0uBTl&7(MG0tdGVa=dkj@@mO3hWp*o?d{EE|q7g1g>JkGtr+∾KnPe1 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 Date: Thu, 4 Apr 2024 03:34:51 +0800 Subject: [PATCH 200/414] Update DeveloperGuide.md --- docs/DeveloperGuide.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index b60e4cec44..1ff59789b6 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -174,7 +174,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: `FileHandler#updateFile()` is then called to update the data file with the new entry in the `SleepList`. The sequence diagram for this feature is shown below: - +![SleepAddSeqDiagram](https://github.com/a-wild-chocolate/tp/blob/f9a94746e763ddf54a4309583b71e0fdbabdb141/docs/SleepAddSeqDiagram.jpg) ### Parsing user input for sleep entries @@ -193,8 +193,6 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 4: The created `SleepEntry` instance is added into the `ArrayList sleepList` attribute of the `SleepList`. -The sequence diagram for ParserSleep feature is shown below. Unrelated attributes and Classes were excluded. - ### Sleep list feature @@ -203,6 +201,8 @@ The `sleep list` feature lists out the record of all the sleep data that the use The `printSleepList()` function iterates through the `sleepList` and each entry will call `SleepEntry#toString()` to return its information string to be printed. The Sequence diagram for Sleep list feature is shown below. Unrelated attributes and Classes were excluded. +![SleepListSeqDiagram](https://github.com/a-wild-chocolate/tp/blob/f9a94746e763ddf54a4309583b71e0fdbabdb141/docs/SleepListSeqDiagram.jpg) + ### Sleep delete feature @@ -221,6 +221,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: The latest sleep list will be updated to saving file by calling `SleepList#updateFile()`. The Sequence diagram for Sleep delete feature is shown below: +![SleepDeleteDiagram.jpg](https://github.com/a-wild-chocolate/tp/blob/f9a94746e763ddf54a4309583b71e0fdbabdb141/docs/SleepDeleteSeqDiagram.jpg) ### Calculating sleep requirements for each user (Planning) From 3c362697d96de60bdef11cd520b6da20e65d3db3 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 4 Apr 2024 14:32:36 +0800 Subject: [PATCH 201/414] replaced API with manual calculation --- .../system/exceptions/ErrorMessages.java | 2 +- .../InvalidInputExceptionMessage.java | 6 +- .../lifetrack/system/parser/ParserUser.java | 67 ++++++-------- src/main/java/seedu/lifetrack/ui/UserUi.java | 28 ++++++ src/main/java/seedu/lifetrack/user/User.java | 23 +++-- .../lifetrack/user/usergoals/UserGoals.java | 91 ++++++++++--------- 6 files changed, 118 insertions(+), 99 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/ui/UserUi.java diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index 62aecb0b14..f240245f96 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -34,7 +34,7 @@ public static String getInvalidNumberOfSetUpInputs() { } public static String getInvalidGoalNumberMessage() { - return "\t Invalid input for goal number. Please enter a number between 1 and 7."; + return "\t Invalid input for goal number. Please enter a number between 1 and 5."; } public static String getInvalidExerciseLevelsNumberMessage() { diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 40c39c417a..adf4f61aec 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -68,13 +68,13 @@ public static String getHydrationNegativeIntegerVolumeMessage() { // User related Messages public static String getOutOfGoalRangeMessage() { - return "\t Please key in a number between 1 and 7! 1 being reckless fatloss " + - "and 7 being aggressive bulking"; + return "\t Please key in a number between 1 and 5! 1 being quick fat loss " + + "and 5 being quick bulking"; } public static String getOutOfExerciseLevelsRangeMessage() { return "\t Please key in a number between 1 and 5! 1 being little exercise done per week and 5 being" + - " very heavy levels of exercise."; + " very heavy levels of exercise done per week."; } public static String getEmptyUserSetupInputMessage() { diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 8776c979cb..f9f4d22e46 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -4,11 +4,12 @@ import seedu.lifetrack.user.User; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidExerciseLevelsNumberMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidNumberOfSetUpInputs; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGoalNumberMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidNumberOfSetUpInputs; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; +import static seedu.lifetrack.ui.UserUi.printUserSetUpComplete; /** * Utility Class to parse the commands made with regard to the User class. @@ -41,7 +42,7 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept checkSetUpInputsCorrectOrder(heightIndex, weightIndex, ageIndex, sexIndex, exerciseLevelsIndex, goalIndex); String[] parts = input.split("h/|w/|a/|s/|e/|g/"); - if (parts.length != 7){ + if (parts.length != 7) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); } String name = parts[0].substring(LENGTH_OF_SETUP_COMMAND).trim(); @@ -49,8 +50,8 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept int weight = Integer.parseInt(parts[2].trim()); int age = Integer.parseInt(parts[3].trim()); String sex = parts[4].trim().toLowerCase(); - String exerciseLevels = parseExerciseLevels(parts[5].trim()); - String goal = parseGoalIndex(parts[6].trim()); + int exerciseLevels = parseExerciseLevels(parts[5].trim()); + int goal = parseGoalIndex(parts[6].trim()); user.setName(name); user.setHeight(height); @@ -59,6 +60,7 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept user.setSex(sex); user.setExerciseLevels(exerciseLevels); user.setGoal(goal); + printUserSetUpComplete(user.getName()); } /** @@ -68,26 +70,14 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept * @return String equivalent of User's goals * @throws InvalidInputException if the goal input is not an integer between 1 and 7 */ - private static String parseGoalIndex(String input) throws InvalidInputException { + private static int parseGoalIndex(String input) throws InvalidInputException { try { int goalNumber = Integer.parseInt(input); - if (goalNumber == 1) { - return "fatloss reckless"; - } else if (goalNumber == 2) { - return "fatloss aggressive"; - } else if (goalNumber == 3) { - return "fatloss moderate"; - } else if (goalNumber == 4) { - return "moderate"; - } else if (goalNumber == 5) { - return "bulking slow"; - } else if (goalNumber == 6) { - return "bulking normal"; - } else if (goalNumber == 7) { - return "bulking aggressive"; - } else { + if (goalNumber > 5 || goalNumber < 1) { + throw new InvalidInputException(getOutOfGoalRangeMessage()); } + return goalNumber; } catch (NumberFormatException e) { throw new InvalidInputException(getInvalidGoalNumberMessage()); } @@ -95,26 +85,18 @@ private static String parseGoalIndex(String input) throws InvalidInputException /** * Parses the user's exercise levels input for an Integer and assigns the String equivalent of it + * * @param input user's exercise levels input * @return String equivalent of User's exercise levels * @throws InvalidInputException if the goal input is not an integer between 1 and 5 */ - private static String parseExerciseLevels(String input) throws InvalidInputException { + private static int parseExerciseLevels(String input) throws InvalidInputException { try { int levelInNumber = Integer.parseInt(input); - if (levelInNumber == 1) { - return "little"; - } else if (levelInNumber == 2) { - return "light"; - } else if (levelInNumber == 3) { - return "moderate"; - } else if (levelInNumber == 4) { - return "heavy"; - } else if (levelInNumber == 5) { - return "veryheavy"; - } else { + if (levelInNumber < 1 || levelInNumber > 5) { throw new InvalidInputException(getOutOfExerciseLevelsRangeMessage()); } + return levelInNumber; } catch (NumberFormatException e) { throw new InvalidInputException(getInvalidExerciseLevelsNumberMessage()); } @@ -122,17 +104,19 @@ private static String parseExerciseLevels(String input) throws InvalidInputExcep /** * Ensures that the input given by the user is in the correct order - * @param heightIndex Index of the input where user's height is specified - * @param weightIndex Index of the input where user's weight is specified - * @param ageIndex Index of the input where user's age is specified - * @param sexIndex Index of the input where user's gender is specified + * + * @param heightIndex Index of the input where user's height is specified + * @param weightIndex Index of the input where user's weight is specified + * @param ageIndex Index of the input where user's age is specified + * @param sexIndex Index of the input where user's gender is specified * @param exerciseLevelsIndex Index of the input where user's exercise levels is specified in Integer form - * @param goalIndex Index of the input where user's goal is specified in Integer form + * @param goalIndex Index of the input where user's goal is specified in Integer form * @throws InvalidInputException if the order of the inputs is not correct. The input should be in this order: - * height, weight, age, gender, exercise levels and goal. + * height, weight, age, gender, exercise levels and goal. */ private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightIndex, int ageIndex, int sexIndex, - int exerciseLevelsIndex, int goalIndex) throws InvalidInputException { + int exerciseLevelsIndex, + int goalIndex) throws InvalidInputException { if (!(heightIndex < weightIndex && weightIndex < ageIndex && sexIndex < exerciseLevelsIndex && exerciseLevelsIndex < goalIndex)) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); @@ -141,11 +125,12 @@ private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightInde /** * Checks if User Setup command is empty + * * @param input input from user * @throws InvalidInputException if the command is empty */ private static void checkEmptyInput(String input) throws InvalidInputException { - if (input.substring(LENGTH_OF_SETUP_COMMAND).trim().isEmpty()){ + if (input.substring(LENGTH_OF_SETUP_COMMAND).trim().isEmpty()) { throw new InvalidInputException(getEmptyUserSetupInputMessage()); } } diff --git a/src/main/java/seedu/lifetrack/ui/UserUi.java b/src/main/java/seedu/lifetrack/ui/UserUi.java new file mode 100644 index 0000000000..43300c43f7 --- /dev/null +++ b/src/main/java/seedu/lifetrack/ui/UserUi.java @@ -0,0 +1,28 @@ +package seedu.lifetrack.ui; + +public class UserUi { + + public static void printUserCaloriesRequired(int caloriesRequired) { + System.out.println("\t You need to consume " + caloriesRequired + " calories per day to hit your goals!"); + } + + public static void printUserCalorieProgress(int caloriesConsumed, int caloriesRequired, String progressBar, + int percentage) { + System.out.print("\t Calories:\n"); + System.out.printf("\t You have consumed " + caloriesConsumed + " out of your goal of " + + caloriesRequired + " so far.\n"); + System.out.printf("\t %s %d%%\n\n", progressBar, percentage); + } + + public static void printUserHydrationProgress(int hydrationConsumed, int hydrationRequired, String progressBar, + int percentage) { + System.out.print("\t Hydration:\n"); + System.out.printf("\t You have consumed " + hydrationConsumed + " out of your goal of " + + hydrationRequired + " so far.\n"); + System.out.printf("\t %s %d%%\n", progressBar, percentage); + } + + public static void printUserSetUpComplete(String name){ + System.out.println("\t Hello, " + name + "! Thank you for completing the setup :)"); + } +} diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 775124e34f..43a6303fdf 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -5,11 +5,11 @@ import seedu.lifetrack.system.storage.FileHandler; import seedu.lifetrack.user.usergoals.UserGoals; -import static seedu.lifetrack.system.parser.ParserUser.parseSetUp; - import java.io.FileNotFoundException; import java.util.ArrayList; +import static seedu.lifetrack.system.parser.ParserUser.parseSetUp; + public class User { private FileHandler fileHandler; @@ -18,8 +18,8 @@ public class User { private int weight; private int age; private String sex; - private String exerciseLevels; - private String goal; + private int exerciseLevels; + private int goal; private int caloriesRequired; private int hydrationRequired = 2000; @@ -49,8 +49,8 @@ public User(String filePath) { weight = Integer.parseInt(data.get(WEIGHT_INDEX)); age = Integer.parseInt(data.get(AGE_INDEX)); sex = data.get(SEX_INDEX); - exerciseLevels = data.get(EXERCISE_INDEX); - goal = data.get(GOAL_INDEX); + exerciseLevels = Integer.parseInt(data.get(EXERCISE_INDEX)); + goal = Integer.parseInt(data.get(GOAL_INDEX)); caloriesRequired = Integer.parseInt(data.get(REQ_CAL_INDEX)); } catch (FileNotFoundException e) { System.out.println(ErrorMessages.getFileNotFoundMessage()); @@ -87,14 +87,17 @@ public void setSex(String sex) { this.sex = sex; } - public void setExerciseLevels(String exerciseLevels) { + public void setExerciseLevels(int exerciseLevels) { this.exerciseLevels = exerciseLevels; } - public void setGoal(String goal) { + public void setGoal(int goal) { this.goal = goal; } + public String getName() { + return name; + } public int getHeight() { return height; } @@ -111,11 +114,11 @@ public String getSex() { return sex; } - public String getExerciseLevels() { + public int getExerciseLevels() { return exerciseLevels; } - public String getGoal() { + public int getGoal() { return goal; } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 7097e0b156..b3b193c934 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -2,49 +2,59 @@ import seedu.lifetrack.user.User; -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; - import static seedu.lifetrack.LifeTrack.calorieList; import static seedu.lifetrack.LifeTrack.hydrationList; +import static seedu.lifetrack.ui.UserUi.printUserCaloriesRequired; +import static seedu.lifetrack.ui.UserUi.printUserCalorieProgress; +import static seedu.lifetrack.ui.UserUi.printUserHydrationProgress; public class UserGoals { - - private static final int JSON_HEADING_SIZE = 67; - private static final int CALORIES_LENGTH = 4; + private static final int BMR_WEIGHT_MULTIPLIER = 10; + private static final double BMR_HEIGHT_MULTIPLIER = 6.25; + private static final int BMR_AGE_MULTIPLIER = 5; + private static final int BMR_MALE_MODIFIER = 5; + private static final int BMR_FEMALE_MODIFIER = -161; public static void getHealthInfo(User user) { - try { - String requestBody = "height=" + user.getHeight() + "&" + - "weight=" + user.getWeight() + "&" + - "age=" + user.getAge() + "&" + - "gender=" + user.getSex() + "&" + - "exercise=" + user.getExerciseLevels() + "&" + - "goal=" + user.getGoal().replace(" ", "_"); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("https://fitness-api.p.rapidapi.com/fitness")) - .header("content-type", "application/x-www-form-urlencoded") - .header("X-RapidAPI-Key", "313560bcc6msh96f48210f860abep1be49djsn7c3a2058360d") - .header("X-RapidAPI-Host", "fitness-api.p.rapidapi.com") - .method("POST", HttpRequest.BodyPublishers.ofString(requestBody)) - .build(); - HttpResponse response = HttpClient.newHttpClient() - .send(request, HttpResponse.BodyHandlers.ofString()); - int indexOfCalories = response.body().indexOf("neededEnergy") + JSON_HEADING_SIZE; - int calories = Integer.parseInt(response.body() - .substring(indexOfCalories, indexOfCalories + CALORIES_LENGTH)); - System.out.println("\t You should consume " + calories + " calories a day to hit your goals!"); - System.out.println("\t You should drink " + user.getHydrationRequired() + "ml of water a day " + - "to hit your goals!"); - user.setCaloriesRequired(calories); - } catch (IOException | InterruptedException e) { - System.out.println("You "); + double rawBMR = BMR_WEIGHT_MULTIPLIER * user.getWeight() + BMR_HEIGHT_MULTIPLIER * user.getHeight() + + BMR_AGE_MULTIPLIER * user.getAge(); + String gender = user.getSex(); + int genderBMRModifier = gender.equals("male") ? BMR_MALE_MODIFIER : BMR_FEMALE_MODIFIER; + int exerciseLevel = user.getExerciseLevels(); + double rawAMR = getAMR(rawBMR + genderBMRModifier, exerciseLevel); + int goal = user.getGoal(); + int caloriesRequired = adjustAMRWithGoal(rawAMR,goal); + printUserCaloriesRequired(caloriesRequired); + user.setCaloriesRequired(caloriesRequired); + } + + private static int adjustAMRWithGoal(double rawAMR, int goal) { + if (goal == 1){ + rawAMR *= 0.8; + } else if (goal == 2){ + rawAMR *= 0.9; + } else if (goal == 4) { + rawAMR *= 1.1; + } else if (goal == 5) { + rawAMR*=1.2; } + return (int) rawAMR; } - + private static double getAMR(double calories, int exerciseLevel) { + if (exerciseLevel == 1) { + calories *= 1.2; + } else if (exerciseLevel == 2) { + calories *= 1.375; + } else if (exerciseLevel == 3) { + calories *= 1.55; + } else if (exerciseLevel == 4) { + calories *= 1.725; + } else { + calories *= 1.9; + } + return calories; + } + public static void getCaloriesProgressBar(User user) { int caloriesRequired = user.getCaloriesRequired(); int caloriesConsumed = calorieList.getCaloriesConsumed(); @@ -63,11 +73,7 @@ public static void getCaloriesProgressBar(User user) { progressBar.append("] "); int percentage = (int) (progress * 100); - - System.out.printf("\t Calories:\n"); - System.out.printf("\t You have consumed " + caloriesConsumed + " out of your goal of " - + caloriesRequired + " so far.\n"); - System.out.printf("\t %s %d%%\n\n", progressBar.toString(), percentage); + printUserCalorieProgress(caloriesConsumed,caloriesRequired,progressBar.toString(), percentage); } public static void getHydrationProgressBar(User user) { @@ -88,9 +94,6 @@ public static void getHydrationProgressBar(User user) { progressBar.append("] "); int percentage = (int) (progress * 100); - System.out.printf("\t Hydration:\n"); - System.out.printf("\t You have consumed " + hydrationConsumed + " out of your goal of " - + hydrationRequired + " so far.\n"); - System.out.printf("\t %s %d%%\n", progressBar.toString(), percentage); + printUserHydrationProgress(hydrationConsumed,hydrationRequired,progressBar.toString(),percentage); } } From c50acaa165bc4787d560408ca1651b22cd80f1f3 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 4 Apr 2024 15:33:30 +0800 Subject: [PATCH 202/414] exception handling --- .../system/exceptions/ErrorMessages.java | 24 ++++++ .../lifetrack/system/parser/ParserUser.java | 80 ++++++++++++++++++- src/main/java/seedu/lifetrack/ui/Ui.java | 14 +++- src/main/java/seedu/lifetrack/ui/UserUi.java | 14 ++-- .../lifetrack/user/usergoals/UserGoals.java | 3 +- 5 files changed, 122 insertions(+), 13 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index f240245f96..a0b1e6f218 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -56,4 +56,28 @@ public static String getIncorrectSleepInputMessage() { public static String getIncorrectSleepDateInputMessage() { return "\t Error: Date must be in YYYY-MM-DD format.!"; } + + public static String getHeightOutOfRangeMessage() { + return "\t Please enter a valid height!"; + } + public static String getInvalidHeightNumberMessage(){ + return "\t Please enter your height(in cm) as an integer!"; + } + public static String getWeightOutOfRangeMessage(){ + return "\t Please enter a valid weight!"; + } + public static String getInvalidWeightNumberMessage(){ + return "\t Please enter your weight(in kg) as an integer!"; + } + public static String getUnderAgeMessage(){ + return "\t You are too young to use this app :("; + } + + public static String getAgeOutOfRangeMessage(){ + return "\t Please enter a valid age!"; + } + + public static String getInvalidAgeNumberMessage(){ + return "\t Please enter your age as an integer!"; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index f9f4d22e46..d32969b91b 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -3,9 +3,16 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.user.User; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getAgeOutOfRangeMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getHeightOutOfRangeMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidAgeNumberMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidExerciseLevelsNumberMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGoalNumberMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidHeightNumberMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidNumberOfSetUpInputs; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidWeightNumberMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getUnderAgeMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getWeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; @@ -46,9 +53,9 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); } String name = parts[0].substring(LENGTH_OF_SETUP_COMMAND).trim(); - int height = Integer.parseInt(parts[1].trim()); - int weight = Integer.parseInt(parts[2].trim()); - int age = Integer.parseInt(parts[3].trim()); + int height = parseHeightIndex(parts[1].trim()); + int weight = parseWeightIndex(parts[2].trim()); + int age = parseAgeIndex(parts[3].trim()); String sex = parts[4].trim().toLowerCase(); int exerciseLevels = parseExerciseLevels(parts[5].trim()); int goal = parseGoalIndex(parts[6].trim()); @@ -60,7 +67,72 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept user.setSex(sex); user.setExerciseLevels(exerciseLevels); user.setGoal(goal); - printUserSetUpComplete(user.getName()); + user.getHealthInfo(); + int caloriesRequired = user.getCaloriesRequired(); + printUserSetUpComplete(user.getName(), caloriesRequired); + } + + /** + * Parses the user's height input for an Integer + * + * @param input user's height input + * @return user's height as an integer + * @throws InvalidInputException if the height input is not an integer or if the user's height is not between + * 90 and 225 cm + */ + private static int parseHeightIndex(String input) throws InvalidInputException { + try { + int height = Integer.parseInt(input); + if (height < 90 || height > 225){ + throw new InvalidInputException(getHeightOutOfRangeMessage()); + } + return height; + } catch (NumberFormatException e) { + throw new InvalidInputException(getInvalidHeightNumberMessage()); + } + } + + /** + * Parses the user's weight input for an Integer + * + * @param input user's weight input + * @return user's weight as an integer + * @throws InvalidInputException if the weight input is not an integer or if the user's weight is not between + * 30 and 200 kg + */ + private static int parseWeightIndex(String input) throws InvalidInputException { + try { + int weight = Integer.parseInt(input); + if (weight<30 || weight > 200){ + throw new InvalidInputException(getWeightOutOfRangeMessage()); + } + return weight; + } catch (NumberFormatException e){ + throw new InvalidInputException(getInvalidWeightNumberMessage()); + } + } + + /** + * Parses the user's age input for an Integer + * + * @param input user's age input + * @return user's age as an integer + * @throws InvalidInputException if the age input is not an integer or if the user's age is not between + * 13 and 110 years old + */ + private static int parseAgeIndex(String input) throws InvalidInputException{ + try{ + int age = Integer.parseInt(input); + if(age <13 && age > 0){ + throw new InvalidInputException(getUnderAgeMessage()); + } + if (age <0 || age > 110) { + throw new InvalidInputException(getAgeOutOfRangeMessage()); + } + return age; + } catch (NumberFormatException e){ + throw new InvalidInputException(getInvalidAgeNumberMessage()); + } } /** diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index e0fb4ea4b2..3830a9f83b 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -7,6 +7,8 @@ import java.util.Scanner; +import static seedu.lifetrack.ui.UserUi.printNoUserYetMessage; + /** * Reads user input from the console and processes it. *

@@ -115,12 +117,20 @@ public static void handleUserCommands(String line, User user) { if (line.contains("setup")) { user.setUp(line); } else if (line.contains("progress")) { - user.getHealthInfo(); + handleUserProgress(user); + } else { + handleUnknownInput(); + } + } + + private static void handleUserProgress(User user) { + if (user.getName() == null){ + printNoUserYetMessage(); + } else { user.getCaloriesProgressBar(); user.getHydrationProgressBar(); } } - public static void sayHello() { System.out.println(WHITESPACE + "Hello from\n\n" + logo); System.out.println(WHITESPACE + "How can I help you today?"); diff --git a/src/main/java/seedu/lifetrack/ui/UserUi.java b/src/main/java/seedu/lifetrack/ui/UserUi.java index 43300c43f7..963a513edc 100644 --- a/src/main/java/seedu/lifetrack/ui/UserUi.java +++ b/src/main/java/seedu/lifetrack/ui/UserUi.java @@ -9,20 +9,24 @@ public static void printUserCaloriesRequired(int caloriesRequired) { public static void printUserCalorieProgress(int caloriesConsumed, int caloriesRequired, String progressBar, int percentage) { System.out.print("\t Calories:\n"); - System.out.printf("\t You have consumed " + caloriesConsumed + " out of your goal of " - + caloriesRequired + " so far.\n"); + System.out.printf("\t You have consumed " + caloriesConsumed + "calories out of your goal of " + + caloriesRequired + "calories so far.\n"); System.out.printf("\t %s %d%%\n\n", progressBar, percentage); } public static void printUserHydrationProgress(int hydrationConsumed, int hydrationRequired, String progressBar, int percentage) { System.out.print("\t Hydration:\n"); - System.out.printf("\t You have consumed " + hydrationConsumed + " out of your goal of " - + hydrationRequired + " so far.\n"); + System.out.printf("\t You have consumed " + hydrationConsumed + "ml out of your goal of " + + hydrationRequired + "ml so far.\n"); System.out.printf("\t %s %d%%\n", progressBar, percentage); } - public static void printUserSetUpComplete(String name){ + public static void printUserSetUpComplete(String name, int caloriesRequired){ System.out.println("\t Hello, " + name + "! Thank you for completing the setup :)"); + printUserCaloriesRequired(caloriesRequired); + } + public static void printNoUserYetMessage(){ + System.out.println("\t\t Please set up your profile first!"); } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index b3b193c934..cc558aad95 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -17,14 +17,13 @@ public class UserGoals { public static void getHealthInfo(User user) { double rawBMR = BMR_WEIGHT_MULTIPLIER * user.getWeight() + BMR_HEIGHT_MULTIPLIER * user.getHeight() - + BMR_AGE_MULTIPLIER * user.getAge(); + - BMR_AGE_MULTIPLIER * user.getAge(); String gender = user.getSex(); int genderBMRModifier = gender.equals("male") ? BMR_MALE_MODIFIER : BMR_FEMALE_MODIFIER; int exerciseLevel = user.getExerciseLevels(); double rawAMR = getAMR(rawBMR + genderBMRModifier, exerciseLevel); int goal = user.getGoal(); int caloriesRequired = adjustAMRWithGoal(rawAMR,goal); - printUserCaloriesRequired(caloriesRequired); user.setCaloriesRequired(caloriesRequired); } From f6379425d9815ccbe7a64da9cb733e2f947846a2 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 4 Apr 2024 15:41:24 +0800 Subject: [PATCH 203/414] checkstyle --- src/main/java/seedu/lifetrack/system/parser/ParserUser.java | 6 +++--- src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index d32969b91b..cbd117923e 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -78,7 +78,7 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept * @param input user's height input * @return user's height as an integer * @throws InvalidInputException if the height input is not an integer or if the user's height is not between - * 90 and 225 cm + * 90 and 225 cm */ private static int parseHeightIndex(String input) throws InvalidInputException { try { @@ -98,7 +98,7 @@ private static int parseHeightIndex(String input) throws InvalidInputException { * @param input user's weight input * @return user's weight as an integer * @throws InvalidInputException if the weight input is not an integer or if the user's weight is not between - * 30 and 200 kg + * 30 and 200 kg */ private static int parseWeightIndex(String input) throws InvalidInputException { try { @@ -118,7 +118,7 @@ private static int parseWeightIndex(String input) throws InvalidInputException { * @param input user's age input * @return user's age as an integer * @throws InvalidInputException if the age input is not an integer or if the user's age is not between - * 13 and 110 years old + * 13 and 110 years old */ private static int parseAgeIndex(String input) throws InvalidInputException{ try{ diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index cc558aad95..a06678b51c 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -4,7 +4,6 @@ import static seedu.lifetrack.LifeTrack.calorieList; import static seedu.lifetrack.LifeTrack.hydrationList; -import static seedu.lifetrack.ui.UserUi.printUserCaloriesRequired; import static seedu.lifetrack.ui.UserUi.printUserCalorieProgress; import static seedu.lifetrack.ui.UserUi.printUserHydrationProgress; From 4e2bef2a7bbd7bc46d397aa4845aabfd13ff4d36 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 4 Apr 2024 17:01:37 +0800 Subject: [PATCH 204/414] UG --- docs/DeveloperGuide.md | 26 +++--- docs/UserGuide.md | 95 +++++++++++++++----- src/main/java/seedu/lifetrack/ui/UserUi.java | 4 +- 3 files changed, 89 insertions(+), 36 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 1ff59789b6..77452bbc3c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -44,27 +44,31 @@ This functionality is facilitated by `UserGoals`. It implements one operation, n This operation is exposed in the `User` class as `User#getHealhInfo()`. Given below is an example usage scenario and how this mechanism behaves at every step: -- Step 1: When the user inputs the command `user progress` in the terminal, - the string is sent to `User#getHealthInfo()`, which calls `UserGoals#getHealthInfo(User)`. +- Step 1: When the user inputs the command `user setup` in the terminal, + the input string is sent to `Ui#handleUserCommands(User,String)`, which calls `User#setup(String)`. -- Step 2: The method retrieves the user`s information such as his height, weight, age, gender, exercise levels and intended goal. +- Step 2: `User#setup(String)` calls `ParserUser#parseSetUp(String, User)` which parses the user's information such as + his height, weight, age, gender, exercise levels and intended goal and sets these information to the `User`. -- Step 3: Using these information, the method creates a `requestBody` `String`. +- Step 3: `User#setup(String)` then calls `User#getHealthInfo()` which calls `UserGoals#getHealthInfo(User)`. -- Step 4: The created `requestBody` is used to send a `HttpRequest` to RapidAPI`s Fitness API, and the response is parsed to determine the number of calories a user needs to consume according to their personal goals. +- Step 4: This method calculates the basal metabolism rate of the `user` using the user's height, weight, age and gender. -- Step 5: This value is set to `User.caloriesRequired` by `User#setCaloriesRequired(int)`. +- Step 5: Using the `user`'s exercise levels, the method calculates his active metabolism rate, and finally calculates the calories required by the user to achieve their goals based on their input. -#### Design considerations +- Step 6: This value is set to `User.caloriesRequired` by `User#setCaloriesRequired(int)`. -- **Alternative 1 (current choice):** Uses an API to get the calories needed - - Pros: No need to figure out the optimal algorithm - - Cons: Need to parse response to sieve out necessary information -- **Alternative 2:** Uses an algorithm to find the number of calories needed +#### Design considerations + +- **Alternative 1 (current choice):** Uses an algorithm to find the number of calories needed - Pros: Not dependent on external APIs - Cons: Need to come up with an algorithm to use +- **Alternative 2 :** Uses an API to get the calories needed + - Pros: No need to figure out the optimal algorithm + - Cons: Need to parse response to sieve out necessary information + ### Calories list feature The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. diff --git a/docs/UserGuide.md b/docs/UserGuide.md index a62e10effd..ec79667df6 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -22,6 +22,9 @@ LifeTrack is a desktop app for students to track their health data, optimized fo - [Input sleeping hours](#input-sleeping-hours-sleep-add) - [Listing sleep records](#listing-sleep-records-sleep-list) - [Deleting a sleep record](#deleting-a-sleep-record-sleep-delete) +- [User Profile](#user-profile) + - [Set Up User Profile](#set-up-user-profile-user-setup) + - [Check User's daily calories and hydration consumption](#check-your-daily-calories-and-hydration-consumption-user-progress) - [FAQ](#faq) - [Command Summary](#command-summary) @@ -30,7 +33,7 @@ LifeTrack is a desktop app for students to track their health data, optimized fo {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). +2. Download the latest version of `LifeTrack` from [here](http://link.to/duke). [//]: # (## Features ) @@ -212,20 +215,64 @@ Deletes the sleep record according to the index. **Examples:** * `list` followed by `sleep delete 2` deletes the 2nd sleep record from the sleep tracker. +## User Profile -### Adding a todo: `todo` -Adds a new item to the list of todo items. +### Set up user profile: `user setup` +Creates/edits an existing user profile. -Format: `todo n/TODO_NAME d/DEADLINE` +**Format:** +`user setup NAME h/HEIGHT w/WEIGHT a/AGE s/GENDER e/EXERCISE LEVELS g/BODY GOAL` +* The height provided must be an integer between 90 and 225 cms. +* The weight provided must be an integer between 30 and 200 kgs. +* The age provided must be an integer between 13 and 110 years old. +* The gender provided must be either `male` or `female`. +* The exercise levels provided must be an integer between 1 and 5. +* The body goal provided must be an integer between 1 and 5. + +**Notes about the command format:** + +| Exercise Level Input | Corresponding Exercise Levels | +|:--------------------:|:-----------------------------:| +| 1 | Sedentary | +| 2 | Lightly Active | +| 3 | Moderately Active | +| 4 | Very Active | +| 5 | Extremely Active | + +| Body Goal Input | Corresponding Goal | +|:---------------:|:--------------------:| +| 1 | Quick Weight Loss | +| 2 | Moderate Weight Loss | +| 3 | Maintain Weight | +| 4 | Moderate Weight Gain | +| 5 | Quick Weight Gain | + + +**Examples:** +* `user setup Tom h/180 w/80 a/25 s/male e/3 g/2` +* `user setup Jane h/163 w/54 a/23 s/female e/2 g/3` + +### Check your daily calories and hydration consumption: `user progress` +Displays a progress bar to show the percentage of calories and hydration you have consumed. -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +**Format:** +`user progress` -Example of usage: +**Notes about the command:** +If you have not set your user up beforehand, this command will prompt you to do so instead. -`todo n/Write the rest of the User Guide d/next week` +#### Sample Output + + ----------------------------------------------------------------------------- + Calories: + You have consumed 350 calories out of your goal of 2140 calories so far. + [======== ] 16% + + Hydration: + You have consumed 200ml out of your goal of 2000ml so far. + [===== ] 10% + ----------------------------------------------------------------------------- -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` ## FAQ @@ -235,17 +282,19 @@ Example of usage: ## Command Summary -| Action | Format, Examples | -|------------------------|----------------------------------------------------------------------------| -| Help | `help` | -| Add calories intake | `calories in DESCRIPTION c/CALORIES d/DATE [m/CARBOHYDRATES,PROTEIN,FATS]` | -| Add calories outflow | `calories out DESCRIPTION c/CALORIES d/DATE` | -| List calories | `calories list` | -| Delete calories entry | `calories delete INDEX` | -| Add hydration intake | `hydration in DESCRIPTION v/VOLUME d/DATE` | -| List hydration | `hydration list` | -| Delete hydration entry | `hydration delete INDEX` | -| Add sleep | `sleep add DURATION d/DATE` | -| List sleep | `sleep list` | -| Delete sleep entry | `sleep delete INDEX` | -* Add todo `todo n/TODO_NAME d/DEADLINE` +| Action | Format, Examples | +|------------------------|----------------------------------------------------------------------------------| +| Help | `help` | +| Add calories intake | `calories in DESCRIPTION c/CALORIES d/DATE [m/CARBOHYDRATES,PROTEIN,FATS]` | +| Add calories outflow | `calories out DESCRIPTION c/CALORIES d/DATE` | +| List calories | `calories list` | +| Delete calories entry | `calories delete INDEX` | +| Add hydration intake | `hydration in DESCRIPTION v/VOLUME d/DATE` | +| List hydration | `hydration list` | +| Delete hydration entry | `hydration delete INDEX` | +| Add sleep | `sleep add DURATION d/DATE` | +| List sleep | `sleep list` | +| Delete sleep entry | `sleep delete INDEX` | +| Set Up User Profile | `user setup NAME h/HEIGHT w/WEIGHT a/AGE s/GENDER e/EXERCISE LEVELS g/BODY GOAL` | +| Check User Progress | `user progress` | + diff --git a/src/main/java/seedu/lifetrack/ui/UserUi.java b/src/main/java/seedu/lifetrack/ui/UserUi.java index 963a513edc..d27461ba4a 100644 --- a/src/main/java/seedu/lifetrack/ui/UserUi.java +++ b/src/main/java/seedu/lifetrack/ui/UserUi.java @@ -9,8 +9,8 @@ public static void printUserCaloriesRequired(int caloriesRequired) { public static void printUserCalorieProgress(int caloriesConsumed, int caloriesRequired, String progressBar, int percentage) { System.out.print("\t Calories:\n"); - System.out.printf("\t You have consumed " + caloriesConsumed + "calories out of your goal of " - + caloriesRequired + "calories so far.\n"); + System.out.printf("\t You have consumed " + caloriesConsumed + " calories out of your goal of " + + caloriesRequired + " calories so far.\n"); System.out.printf("\t %s %d%%\n\n", progressBar, percentage); } From 994cc519422d9fad78ef4436c7dc95f6bbc88fc8 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 4 Apr 2024 17:33:51 +0800 Subject: [PATCH 205/414] Seq diagram --- docs/DeveloperGuide.md | 3 ++ docs/UserCalculateCaloriesSeqDiagram.png | Bin 0 -> 36942 bytes docs/UserCalculateCaloriesSeqDiagram.puml | 36 +++++++++++++++++++ src/main/java/seedu/lifetrack/user/User.java | 1 - 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 docs/UserCalculateCaloriesSeqDiagram.png create mode 100644 docs/UserCalculateCaloriesSeqDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 77452bbc3c..bcbc1d5d0c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -58,6 +58,9 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 6: This value is set to `User.caloriesRequired` by `User#setCaloriesRequired(int)`. +The Sequence Diagram for the above-mentioned process is as follows: +![Sequence Diagram](UserCalculateCaloriesSeqDiagram.png) + #### Design considerations diff --git a/docs/UserCalculateCaloriesSeqDiagram.png b/docs/UserCalculateCaloriesSeqDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..20c67051bfdd2564573fe0fe6b0455f9050a1f1e GIT binary patch literal 36942 zcmce;cRbc@8wYHM>>aX|y%LISnHg7BBD-XSjBMGnjL6Oixw1pa%$Agym7Qd7vc1Qp z`+lCD`+45^$NN`(xW?~y{*LoFzUvgGsjf(fPm7O+hDNBYB#%Ht!%Rg(!(hb4fS)`I zke7#lxSSRAoz3kX+-)uII-@CCKCpaf>TGGjWaiFfbd{gq!PfMFvx}WApSiuA z>z58jG&JfBzf}9X`h`^-1gvjYesb;MGm;3on?oFhUtT1UPoJu-M}k-0ec% zC|BS9^|Hrzl}AZ4tEcJi#21Oxnr}-)h2*V!ud!mT*f}7%Cxb3hC$^HdzTP2dF zM!i6n?vKp%7twY+5=!vgNGK~t+6q>bsBGSS?K0l7T*qVEdP_NIZy|L5z6xQy>+R~V zSu=C#h;V`1aSzVt#~&X(YGlaOmnaX+7b~%o7Vx@u-SmQr9osN=t;cFeXBcX()tFT#JI`v4~e&}+!vm^K6Xj%K?2Q)oz>@Qc7x>` z*ArD2BfZw29@!;^UY~5_zQ`TER(j#`xrtSLw?f+c<*${lBuaeV`LH)RqjFWPeNB1o zK90Eop7B*zi>nE*Lmjk#?ceiG#Mg1tT5~`M^hylXD0&I+qV>0J3%KK7Oh-d|jHWCv zqwRKQDea6tg<@0NWkJCk=G2-bpY9EYnnl&NQeED;)6-0L*?Or|vDDO?(sNa1>HKZ^ zC;l8+rJ14EhBQeuR}Sh$i&H)`*bZzy{jqXfe~Hq?FSyiME(JpY6IjUTZV>fQ&C+ zUp)=~oah5tXXo;V=TE;;wjfFuS?hDOf4CQM!$sFXR?fc3#=ZXe>ATsa402qjn?JsL za81zV*51Pf?e=g=?l>E=-xq5sti1d+Ur(=4<*)&>MF_t9XIDD+;KkFcY)-a{H87qC z(kmJj)zR;isce0(Yk;fb(W+#+sQrtF-hW+q*af7~Tid-HRnlC1o2pdtM<2VoG~P?1 zJ|35OK>k_(c&+E7o~$3J->M}EZNsH@Y@eOY^u%_(bdMF#at?G<3^=sEu-%@s~-JI7XKwxS9iA9s5-Y( z{`6H!LK|NY+zr2aPS-V>0wd@5@()S;cUj0G*-x)gg!o@CQdU{cy^)gpy7!Hxs%0D= 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 h3UQHcfOc2Sz%G_ z&o0~S&|Y3>BqGIA4)V18l8W6_yr$J+Fx493oaqev+kgGa&D>7!10wvh5rGRb8=7$A z>Ls@w%bcJ^6BBF5n|ZB_zJIm5QLFrnDm-1SJ>gOO$l8hIc-1?LkG*COyVC0?8-wym z-6uaN3uE1A>dllBExpf}jtvcXO`WEG%|)AzJp3?S_d|M5^Q$h_{ft>Wcbw65 zK3JmV((yPLJJQizd(IkS^OXL|S!zCf%wUp`z|oD(Z^lK2E!nR#^<(14 z3~!&DoQz>rB%&3@MY7C}zIOs1F;-zi&62=plpGyh0&KP+;Eet7n;+@0O@ z5GfLD>=n93C=i;$%89HfYau{ZY`4%O$EqLMw?>`=KE3+=JEkg7Oa_(&e&dyynf`)X zWhaL}r9ydbzqO5GxE9R}bNgUt`B|~LEiRyI859@e)wJfU@V!}NcqK6(_u$|_&cV3e zhY;!F;^MtIOUU927Yt+~f`Iq-+f*^fta83m-v0#4H>#ZHzody@Yy>KVMC9NT6NmKc zA`uGmPw7O9p!c!B4c_yjFg0`1w6?=V@iZ$xgevkV1+MOen4@eUqm&N;@=0W5V867VSSQo`RO^_DD@OjsPJE2TGq>C|9J!Cq@?IE+iPoi z1{KzT75)AF3E~XIgZ`idZSf;|#dnhGUWhO=PesP;?z)kZQ&PH|9D9@c>~GD(!;z;! zKfO-1l)LCi1r!ydTw7lkT=ufzzjXH$bGL5IUs6-NbZ~pNQyF2+D}*9M<1@`U&&7n` zsS^<$Pn0I4Gn|T{CBbn&H#n86)=DYUGUO!gFk_9 z%&^KC`R0dxLk$$E=ZzIk2QA?wglb%KOAD|5q_}x&CoqweQWJ%Orzmo@NJ2|{)oFGK zdGlW4%Gn71q4#xlqwk?6*sdsknlbkBy31==scGru)AgGViWpmKq)Qe?vJheZsw-@| zri9C^+Y%FUt9Il;UBAjngr6AG=)qf}Urfw_`jm{k{-T4(6S=>^trxKkDUtLvr1<$N_-wyGNCXp=&XC*H4s7OPocKUfv z3`{Yw1(M`Xw>K5sv3u1|<}^9}&ZY7U9&__+u?>NIy>FireoIrb2;BcxBh1~AFr*tY za~(Ew zM3Cuk@sMNnz9-3-ZLU9|{aRw$biXf`osCVxW6Nr5u4nUSm;GqvI)D8k898}Cr_{-z zle~POe$S^*iz{Q*I3;kiq2VDW~^n2@*Z@{1C4fu`gA|oT|g)DI2VjtB$ zT798Z<+b;)>%}$FE00zTwb29xz7x`k2-^%S!7AB*5;1)L5PQ8H$DFDH>RTy|W!A<4u-uf9T~^Gq^^-e$4cb0~sG5Z`J?tw%g^O7`)Sq`U?m0)|g>>gqY$ZhbD4B=ELN3)5r zfvD)aTF+my`Z4?8?CVXP(tVHLr>8So3z>XC(?{x4uT+|~;9JP`wLU!`A*(65MXRi- zJYP(&O<6@xMuyQ7*udI4IQR~LI)}xtm9Z-iXT%&P(HhgeHt^UE=6YTh6&2;ECR}GL zmxTV*UVp#uLOHD@A?Edn*LuZ1OGASFH4lGu;Jobd@bJjO)JVIkldVL$gyH<@Rl3jq z_b4eGEUa>?o-CtkSDf!RS%#?F2i`mVgp#^xeV*{oYUNh31Bq0gfX899(or)X*k+B- z5v?H9Ss?3mMa7hb`&m(p90Er9@Q8-!DM&1{9GX2nJw>hiIXZD8N&F+)QC0kuo4><^ z$8Pe2U$?khSssH5c{qWWm)B%7&hYm$VJf_%dd;IX?w6pLPq;_0`U!pT#1(fxr-%aN zS5{Jb9KgaP)R`tuO(^5{{^1WYL5J>-&n~h)&Xq|cypH76D@s)g>dC^uHLP|eWO@2w z`P;jurY5ajSf1wPd*v9lyveR}-I+p`9h1|j>FMa`m^B2&iTJNuElfyA2n8z9o+xOZ zp=<2D9Un*NPe)41Ja}{H#W@7`fXCLH*2(b^Xs#)O=B>)lE-=?YO|CV*7MgUAHdy~r zP2X3R9Tzkaj&;LD3`jb$i#FKTyC}-mIL-QUM$AX;hSA zC+t`3`oI%PE^Q@=8;487m)6-UsgReAY<_gabC3)A`S}S*HACraSFhHQ@2yVM*F!G} zSXVEUwKg|1qvFwLYE*sB^D$jY3ZwA$JHP>1q4Iu!O)k+4QX<5mS!%t!9;S_(7d_&S zeKtH11T|R>g`qeQ<}@VtX5bOxd`}zEEi}l)`0**>*XAbqIna;?>byNE^j~Xpl0M7p z7qK1E%uZI}^?I~4RLTO4_w6*pds*uqb7Qad7Wh1Z)|9rq-s<0TN-3P>R?ZGe11N1E zUF=7)zPMx8mwN+xBkZ4V1-C9)QT1C778ym$&L`-HnN?A}{aWMhCJWIELtG!;QI zyfj?abc2{)Ofz4%5CGgcvS$Wp-t>ZISOd#FFVFCh2s(5C;AwQ!dIFIDLn2}8wXy8`QHKT&5D)UeAfNP(*Z zzfUSFbCQ<+aG+!dm!+V$ySr=gG4==0Jdt<oZbwK=%qU_@dTN?ejviS4;Qtq@wFq@WR=z#!qiVF7>*=lhtAFnJn}4_gR< zlK*1FA;&EaA(a#0>NEmEea+o+pfCM^EsVUz!oswTpp%t)Zc@ z7t#CMJvh6Wvtu+-;JG?}ZJt?AoRy7D-eaKgy5At^>)(cl-@|}~g6^;&A9q=O7Kif( zSgPJbBYvT zCJ;!`(}Mb8@H)erK+w_d)L{=pxlwHHpr$qfez+zfK`UZg>AGwX^hqTzF<*rYg?p4X zq>(Pp{s93w_XxB#1@+?DP`*d>4B&j>OtT^`qa_;5i$TlV8MF_t}8x`&ei#Brso zjer~AD|j2heYIaY^;MY5_;A$KuLKTe{qtF0Q|$q4G%hibp;$2onaj?ndjhg1qB83G(IO^h6d?5+j)+;0;VRTfC<{b=Qz%r#dm5+%4~<< zxGs-)Z-3RxLp-~11HhUifI*dbt}r1jH^2noL?m7Fbk(+994HKJc9oNrmB+z-06vnQyGLUv*5Uj!`fiMjF_C7 zBBi7ZTKnLKsg(qk!m{~VCm;GP{}{5+s9J=Airw64zBi}Z@p~(@c;;c?(X!S+)UQV| zc+dB;w^t2K1AVAF-nR)@Qn!5@l01BNBnu)ZR*%^ACUjnoU{S zA}FP`rKRCKIT;yA9e8ye@!V&K8B(Df;0C3wnPXV$qoRoAonXZ#yf&YNzIRm`w@oWBIccz?zPkDnWI~r{+86}bX()iCyfAccKlWDdvXlu1l9Eq4-X}-T?31l(A##XB(q)0W`IX& zdE>_?@VRc2I{jKPdfyL2F4v^N|C5D;*RBI>jtSqBqi=S!va+(huQd1@yKgA*F5RR6 zvvLtey7FS<3+9UZz1gHmKM>X42d<>IvXx>0m&;nS+e-q=NfNRIHF0TWh0jb=FsPqF z!mUIbiD3C^Vk@cg^y$;*W?TM0`TyjzR=;m$n=yS*!=fCC%XXJ%0-+t22kg| zK8?E>Da@E_7%w$_FfCAI3$iy7K~LMJK-yb23! zPlH$L=VJ(MwigFAs{3{GG-XEbU*FRgl#q}Bv^YJxSX_V1mHpQHU z^?GD`il{vlB0dEN(~Gzuo2((&3UJjG0Q=j0p){K{l7%^aj}HN-gD^rP?#g4v)KSs} z<@Rz0G)9JN9@^FYRaI44M1YnRJC6>2#c0FZ0Uunp8==?>>d%sFe+xV#kd%ZNAG5p^ zo;OPYzl868v+Vz+m22rsT8`txFvJ!)?0~n>HtA(i< z+(n9YeXYQ$g5H-H0%Jo%`bq$~2jjW*o}L${qNcw1Lke(#rY1?;`LEl}55B|M%DfKy5sK<|F|fH6jUCS^sVV z{jb^BG1m+%DSFWU&o)}N#bbmG!~a_9I08-U&jJ6dykpT-3f=#_Lr_C*pe8LsNE&&W z_#Xq`kpp#Koaaz3DcmXC0j(BlaAQN!Q=KTV{Z41#sPOiLo^~yHdHG0&YXEI}r>3Sl z%6|p?PF`T;A$E9nULJ?B@dP}}fjq63u;10y z)nSEqY2eX!-R0z{m~Hsyb`lsYwm?yZ)!Pq$ep$A_$bPGtDZ8P=6Pll&Ut=cE`p@;v zMR1>RTvU8YUsqR`1H;OwsyroyE?WdTYdn|Eo8B}7v;TS~U=jqs>*^l>-~dDK#fukL zK%5(?$QR=7`R^qk*Rf`A{zeS0#mdr>#Jw-{?x*l6GzXr-jYQrGjP(}=fJ-Cle@9+U zpNhK%l2ZS>RQM^5rGc5xN)_{4(c(6(>)&{uA}52axa4;HZ(;5)6>Sb9XW+}9_yy2s zGK3(`{qwd#C;ewZ`6F!Z{aH$Y&cfh(P|+PFm0%do|7ZUfGRu5ck}%<>%+OToQ-m&?m@^?H(NHK?hE{ zI|K4L6qo;3Wx=62s1}P`4L$Mh2>a=L^&OH>Vv1!#z# z+Y4W_6wc23{M=doHdO(=6X*MpNngd+P&G*jpu?_kccFr$wvg7?(5g^rG;Nb?(8G<}n^1$4T3=JQQ zR-zl*PSjoNupKGC53wa`UV}he=<(p@3#NYk{J9XGM<_VXx()u=z=#7}NTXGkVeL8q zULq0fhlP5@3~GE}P8{x*hJ+{{?QfUDjpVnrv@p*DU>s5jz#-h2{k*um{3H>chhDxe zFED<6ZqPlUKg4vP$Hc_!R)Z|2r>95pV`W9sbxFUO;QUpJuX20X_!KZ{wLmKYHXTmY z5D*itoACg47l1hsR-IrJE4+52F^BOCs%-<8l>x{Org?K5{UlEmUUq9~x>_uLd&j?{ z04SA|GkXU}G+-RIef+PLZtE;&fIA=uc;V|?aG>D}VkPna$7(5rQ;=2-0~iF5X;kY$ ziN1cg2&5s?8Rum`h$~-BYATX0+(^FH^}=Oo$Pq-{(6I1usD%VWYmlCX>wSH-d;s2- znjsJo8#aSQyk?Ubk5)z-TusXX?#Xuo;)e0&zPDki#2ZQe_6Y&UnRUb!j3EcGpFt1^ z6Ocb=lP2j+iw%7e+XfZt0+pCdgL6_%CZ6Cr(r)-oPJ`P4l_i6?D}kx@JZN)5G$~9f z5m8ZHV3vY_)vYy$l{v<1P%iz_=j7$7P|QIm z`2>MP&@vSuK#}$X0m;HC(5PL75OrRAuu#%L%uhFp#~=|Z0#XI{{X}#kYRwj3 zQg2C!p+A8_E^XkbfVC8)VZ@V8iq;EQPZ|Uk)?ngwjDAH6gNvA%h=}Oyc~5!oms_uYhLs=;6bK?#w0-kebPH*F4L$Eiiq2eCFuqUq1e0&;r$0rN{rI zQs~U+*c?h4ZIEXTOv48n3cz;T@tRw)2J@;XkUfy$);g1!nE{XdA(B@+uN&mT!wC%5 zKp~&qsgPdQx04|02FcJyir)IQ{O!!FS_B^4*KVSm^Q}9usiE%#MPZ&HqPWf{oiKpBskjg3P~Z?d zih<^<6h=j9yuAKIz7ynxjL6{T(@hj&LmT->Bh&jK5+!O0>sbV|hW|7}?a|)m&!3{B zqqpY!zOSOfhu_kHytd>{)VM!`L<2~Cw2{Ojaa=n1vAhW*pmk^Mi9Q$=pb4V+$^k&x z*0$i;vpy&>(!I4QxqJ71W@Ipx<%nFp3bt_nw9guJuFUUw0myzF9Gt8vup}QH+v;U>_}R!^$%0NStxLF6>K?;n3Pnh#6o5*~=MGINym|*pS$e3+s$#mV-FW8;g&S0&sD} z+y!n4k~=`v>w8eYqBIJ963ELm>B!H(H=%C=nG2yUGOi!jwkDM>ged{?-Yw^Z%9{~v ziF^bM`CyRJ3jzoMwX|Np$Ha@_5j4hLMHFu111tT8zNYmv79O4@pzIy`s*e*^f*W&Hk`;%Izl@{6NJ<*^U2u zRDw<126{6}?&ZHzpeE4x4}A7d+=7}?<*J2cWt)2Y%)XC!wEX_qec(!3$yWC-c)rrj zF_S0yvqf!!{YvrM+sn2|ODA{Ri_rrr>Wo3=WTlG_E38nuN zKcjZYClw(3&0wa$<_ri3h`+xY^1oiDs%&RsVgd?G%td&DF!;1C)QfUavOfIh#2LS7 z4R!zu8Uxp);J-8a`Sa(ou`$}~HkySrF@I7;z#{BKLBj|-O3KUdfo4#+Jo?=qm0&Oy zT9o5_J%x(RPf?YTr?jkhf^}uHo*lm2!EovgS$l?li+^QRRmW>(6m?NNmFV~?-o*Jn zuE@&D0ufU)OCFDyn3%azU<_)RqwF+lU2_I?&t*BfSk0FzK;7Kl+UkUA1bAF~n$D(b z{_W|?Q^S3*V_;&|*3>+lX`fnI@!6PBR^U<=`SX7NMyUQd*}6>QH*Z%t-$!9ytH6Ie z@WwX??vhqt3Rc>wKXf=0jx7o9Cp-VoAN~{Eg7rJCiSq3ONn&GB%kh8re?br-J<9lc zuvH4v{$vyXLVp|;t~h^|9!y89|8FS!_cZx!J|Kipi~HyQzr~KgfNX1SmT=#YPm)GG zDabOY3dbBG7FSon{Pn>C)gziq=QZ$P&X1oyfpn|od++EX4Cd<#fGKUhz6LPHZ7Brr zlwBf4i8!K*WJXFpe`|^as~i=eLFRP<3?YSB(F(XkaaTe~|19%a)xtYccC1wt!M5cmlhC2L?NatH_s zktx%O*ghsyrRx9_$K()BwOiaEWzr_KtC~lqCY%fCLbcjMQ!8uT~FPSxmf?KLxeCPZz zKJp0cC&0kf;1+|O31Ix}l`{l1lgrDzcHFS?00E+ZYovGnnq!2mdLN&HG6mJtD~An2 zGf)PZnIfa=X5Me0-FE@P>`c1E(gc$WyfdH-L+8a^o7>u0aS)5s(+{Vc@d{jK+DT)U z!H(`WBPGJa^Fxm)@g_fa?md`tAR$;{z>b&}KnXj*h^St+XN zH#IE{Hl*Y3nyKZ6)Sq1n9^}GypGekOPPhQhHG`$60QRaYD=9b;C`$_-pYC0u|1l>i zCo32j7=RTy2u&|6jFg(Ut*tpIi2S)(Q7vRMd!VJ|2Y2k!%;%KB;o)0Kb`VEvHu?q# ztg^bAS`|a7c65NUDZ#~;WC4HZ~^BOBe*0_D>{5Xch0-yLf!_U6JoDmR4c&v5jEOjha5)aj+8 zG)quC9;5WDkHM@qv3KnLQaAqf#ecjDx77SMqWJxW-&6JPFD4Oh{d?-cV36*J837qM zfkTT0KxW(P{^qxlSvQC0uopePel2F(5r zVGuYZPw(Z=fg_C{Gt%AN4g0-8m-KkEO9}wTE48Go&aLOLFQIi4`Js|=+`4Knw84d% z@Hx1_z={%KVH=|I7E{1$05)?u!RC&4w7AimzkE7w3eT?L82)wAyu{AI}$M zNw`Buft2++Q!eZ;X!)|8s74AVIAC~adyo(W^7PX4*9tVOxQ<(OURO4VX#^(0M%t}} z)(gx{1qr?LPN`WCZb)F@7Bp30mB6QVE5V|H49M2f@FoZy%F4^IiM1x736_KbG!AF; zqQ7)E68EJCXwdtfa8gZ~>R(j~dej*>AaZKyI-!!-liog&fF-#OJpw2OZfD<;KLHQhzTDV+TM@?l2Fxf)B@Zk`CENJ{+ zUR>jTqoAnx3c}Yw6gFHy8z1?P(i4!gHce;o`rb+itOiVa2t7@ktEmz6Z$gkpgeC`$ zcsSyG_a1<*22$A$xNCshBO@X_LH2=;3PgqRZZ-tRWUs=7;_29dH~^U~EpA)f7@%%T zqwmYwq0y^|<;ygEKND@BR2F*0=TOX+CLU!tPQF2?m$PtiD503$$H)I!?2vy#rAY!! z^T1()=C}sKo|dp-N2HEIoqoQytf6&8>? zv|(1pl>ll7y*M)v;1sN54r~p83h+{fr|CT}6j zxGpdM;TGvI%m!!$;Ytn=em4RJ&ppYY@qP= zBS`9nj=r@^;kXAfnQWXNMA;^Mq;cIfSXfyh?ZuHg(@g8{`3fC=(kKUH zxU$^3#NMF_8$F<4@ufIT;J?EYCFWlptA2-)lshp{nT?g{>E0}b2;W)**MBrfm=f~n zHE*T;FyK*w*2oF^HT1d`DSqLWn9ItiG1v>x{n1c{$vC4MZ>!oxRWU#705=s|Hk zY#3p1Mriyrt_`_hLV7U)6rZ~#Dk?f;2ip?v6P54KOxxCu{cR@vJ)&xe|23hjs(c{n zg$iWyac2rGBH2h)LB&To0;kheFa@TFr<+@Uv9T{WX7~y5no0llafyns>RXY1_*`W# zRLDvxPz=F-fV~QiN1je?Gac=_*W%d@pvU2-VR-Ex{7;>2vt4|HB7KZ>ZXVWC|;R3j*FF2(2M{ z0g=IvvZGeJeu1|Q1Cr7A5KLghq}(SE(#yocl9iJSAv({cL3_=^8LTpi>yL3@;wqoB zVe83GOiToMbN)NZpMXaMqIBV{vg^+m&qw2@LW6~Hy#kwo-wj4F2Wc<)>h8T>u7}ZO zD8Qi48g>D2+7bwEy=@>kp5rsBl7NG& zNd-?(#VXJNvNYK5BJW+?# z1Z-gD#uJau{C__0kNgz?Py=6vIzZ|Tm8^jn*199qphHl7Ssf!l2NGN?fiYS6xQ`K=>ga zXdd$eP8n;`=SwQF25JN#k2V;<^d-G?W&>*NJUMTN;uJ)e;JA7tG{Bs~R5%3#-~}L5 zTR)2#C<7;ln;N7=`lVS8H^V@daKjVv+an<3WGRJezI!88Y#n;zXf3!iTe`1Z+SE$#}&`!WX7FPkmst+xE@u*i)3cc42wh8xY=-3~3mdCXtF<5+FLq z#BgU*%?xRD=cn2jVBTOS2kXqp=FSl={nC&+a)y>YM@9bx1dX&fCy0;^t%el^)x}zS zLdpY>O?W#PPvJ_2Rx*#}^)c*{1kG`P20ReBQUZs5pFgyU98Y| z3wwwob%@X+UP8Tb1-Lw)Mdu5%CeFUL?)~ z1c&6=g%$m+g|BDHugs>Ptf6~1a;Ti>79Jx9nVyZn2_6}nwNmg8AlxjY28AJz-(UrU zEA$-2IL^hU(csva@GvY>wLq!kUGp$9b^&GM8aLATm<61JJ@CP@qcXrgHX{c|^qins zV1pGJ40zyPZT(NVpwKj!D*!RaJ_z$W+}kv-6E#w;I)7I2W74II^c3u3SFU8@u^{0R z^0aRI24!aaOancK~Jhuykb9|6{z7c0+N3#jM=BPTflkb3W9EcVr)YyjP0y`0%@ zyNoGv@sVbtBA|Oy)hsiBvT^k4Xy#2tis?-^wUh(2bWs_zrJM93R({$%~ork(J?@|ydrEJOUI)p7oM$lSa_V7&0I**a_xyTU4 zftFw<4Ke<1;^1Z?+o1lZw3oTmf}LA>3vY`C_IPE6-pewZYHM@&rAy~SgE9N)2JsNmZP4-P?!Il5%?Jb-E+Dmr{)P3ng1PoGf zY2-A&f%NtALGU}oL2+A+wB^7}HJ(0&!FwDidK=xp`3fA8hUnZ#ka} z7}VVC1A*VL?C~zTnZWReBmrc^d-6@J3k-tSCaPS{QdtV9EQ6~<@;IsCY>Tcy>GsMP z9lDxquLjA&U~yC!RbSNBXYt2D+A#a}ae)|S$b*faSPI9`NH#z5*Gpr|bJlU(WvaH+UN>|LeTCiqWTTAkvdmFV6x#Wq&)3{H zupR~<%XhxD`=;`ohZEsg?zQ(L!74(V>2X)X2w`h4D)&}80e>5SHI)WT&vS@~qtIHX zgB?54u(XJcwkLFhAcm2e3)~Sx=|TIr4mo~{fiMl0NDZ;iku4Hpp#p#2_nz2F9j;-P zlO3VGqBvmbseq&^?v(&cWn!D@aM^N$$6rAu<@0VvkNhqp@qh>u{_a5?b1CKl!-aPFU4siqzN95^F0zTCW?0 zMLFok;GaBaP-)Ez%D*xHgJZhssX2)w=z(gfai<> z>sulixR1aPe&64y)vW-2PaqXdq=q9CouNwAjUXR6sfe$ps&lT&)}@q&siC$Y1CS#Y zmzH?UFfcGCS#Xm2W##2x;dOU{+^Q;CN^m{J@2@2Jv+&#bK*`-sPe>|GK6D^jalUk}8nvLZ20#O@^<#*eMF%01& zFMA(KKQ@YV$uYJ_9w0$PM5x6WfU7kh;p4|v!I6n-Eta4SOqAUtHe*VFX+WlsslO!D zKdNo*i0w}pW-F%N9L3{EWTw$T;YgFR!au)`sm;)Gw>>%&CX3h%iTS$G@H>0st$Kax zJ;4n~WJEQL^_<8?5Vrc|0Vu8PhSO(4`0__u7j~+r3j2>21^NHMrp~G?Lnexag=Nx? zW=_x-^oVUBs}4-_zpf?rfMp7QdIhu&NUy!R`#Cw@in|S^KPb! zTpQ)fsF2zgJ10hLe>lxeBpmDLN)&vlM47?Zd|n*=!Y-Tz0eV38?Q`mtO%ON)=JCrY z4ngmqBYfX)#+`RDQhszzE+PC3GhlWWp^T=SRV+pcH;3hs%gWZ72bzR2SYZ?h?jy}k zZEG;yIelj^sjrYw&%0X&tkX?FX2!Hbv9xkmW{ir@kYj8|PQ9PeHwOc9;2BFXZHvFEo zqahx$r6r(#ef>eedNRpasCsKlyc-`b^|R@GOy&xpMrTjivl)aLPc>?oR%gG0&V(}? z3BI0LY=EMf13w}g_pVH*en}oksV2JD--5$_HX%NHz+~$SvE(k=O;R?pR-CbB0>Al7 zAVVjXYomVknu^!JWT9mg=Y9ZPrtluIW66Vy->b_ns3ks*C2Xy}nYbzN3`8BGvVuIc z+4s-}q-y{Iu|Jq$g0oL$Ci%+Bh4TgoLQ>Eu)ZI`qHNm!v>=oVA9JIM=!sPIrTZyuO z-{@{l0f21biAc4&z!lILttv)>Mg1ZqjOrS}@`nm6H6oA4k{2sE)L$_kz!`TCwPu&hPGA*&O29+oq5oEh3X=m?8`=|%1(yo^;0#_|n zlvg+G%6M%?Z5|<}ttmts8XQR{s&43}gx&aX=H0g*sW}Y`z!bXt2IaU}as57orL0uSH>@;Ko+t98NvRC8 z@i2+7ZKS?2;luRe*$SwFptY5@ack$n-^NOnx8X=ID2J1JDq{T2kFeLIl&u|213Kq~ z>3@pRnwy(Lz%UVxhH{9zo)clKGica*`bh*yYqbh75Se`fjVQD-bMnoX>9P7-t+Q;c`Z@4)QM{lTL1clRM55|(amejHur9>@U= zrpctH=b%qR%9LAQVbrN{3uiUv4k_9g#jgv37aJ~-JSLT01*}iNR@zzHk^xk;6~+KysWhc#zxliF1L>n8$U_LqNYD? z0E`(IIDH3=bNuORI7wNA-hHx$*+-oi$Kn%cF5YM2UNed#2$Shz z#hD!}yQdKs(5P@8&U+JJCj?^VK7_OcJy*1>;{kb)oUSHgxt7PX(kxMe)`V`;;;F~b z+2IgCLk^KEIv1-E7so&{1l+e=-oJY%%9lgXIRV=T5aWzxR3OxhisAH20VQAK%?)Qq6A!IUdbzmbc|-oZDP2CUxeGs?@QV{)rDy zFMd8kXUg7I-}ORC^ua4&Qg6GZ4(O~?K*GO0(PE=(ulL1bpZAj&jIZ0wUBn^fKJ;wS zDfesa>-HTsSastQs(;G#d*B1CP=B#n`k~*@q3Zvea&zOp%RGWu!XsidnD9!qB5 z`n>PkZKcS9oLK=sQ%JBla$Re8oCaHmz=dzCZZtjEDKVzFif=Jc^W_S!M!R;EVk~3a z68X83G`Uc|Ul5!ORywj+MDPMr20PZDb#w%d$ARvQ?``tZUp5FxNl5`w2g~dk*A*$x zgbQvl*jChyixzk2$GrUFeU4~B8HoRE{OwtyisRQ0N+1#*T>WOzxA}9PRa{(I^cu1J z<;C1yYZ=%Ve9}Bge=~4K^@YEGx^U}lu70Lpcy06#fLcVCIrRLpLp#2G{VE9f0xs{R zTc^e-h$t-xnF7B{YPO+&gJ28x++C4g|z z9^nWk5uX=v$0L+kAtcpJ#Ix=irc9rp34UGMo3>}~v)PsI6EOIMhsH%MJ={w2M2`DY#l3*N37-LZP~QGG(_SHSmSdI&(B-L@pX6@)Wl$rF7~Q0p#ut6#R7>H zT6$xAsLqCmhiAn3<1@yit1vw`e-#LR6uIxB$0<8o(Pf0?JY)tCPAP}CbalCO(uEvv zFi9m}vesb49Zy3!!}Ek-KVoptok{@b4!(knXYrhi3|S^93j&3&&v)ZK4;KH?MW{^w zYn^@qT=BZA+1S$YP+z}(g}e_UT%L7kT|o2#Mv3!iAHTwOWkQQXQ86FD-14}+iHtz4 zIayd9x{UUL?Yd-bwElizlHkz730(_)9Jy;}H~P$a8gs8lBeA>6=Rbs+DgJl$pv&aA zw8nch#GwV@g%K)bXnM|@e+C8oINn|Jd8YoeNa8*i?Evt{+S+>d+=n1+0Zx9ItEWj>&H3)HE{I_YtBG|LQd1JHkpVV>>L@;YJ) zWn+{mP*d||d!F_yOQJSsLnq_k|?oBndnI68K*1W|^Cah?l_S(d3#vrb&7eYgbjda=Q`_ z<=}j>N_wRfB^X< z(W>DWrb%dN?&+MWA^IBi=A|&ezvFpbjC0uJ<*Ri@%0M%OKufx z>ctBe+FnPv0m{aP=uk--yLqUQ{DrGRC27up+hjkt#K6;e+(16fv2L&__iA3^@K@Co*n>)UYw%t~zmfWdNA-Wq`X@Irkz?MKdf(6Md zgnk4Y!z3_dF!sge@|4_+kgZlmSj|Tng-XyK`0CXwb7z$#=PP-tRzL|rD(7=9FD!)0 zgc87i#Q&NB>-n2Zlf({opO&eTBaQQfC4`szOxzB*nx~dzXk&Fz=&I3bK`J$14tgix z3ZtEizyr*3=w1a^xN`ke0|*cL`ubqba4x#yBD86JXdwkFlRQ-bjW-W%xo3TiDbLXLfh&SLEWvT)q|qo&Yf4gj=+9|yhXDrIDZo_+0Fc? z@&+n2L&%nyu;^IpF>g^n8-qTqBG)_pNKG$4Qm2pYh7 zNiGK)8ygo_k7-&lXhVd#9E>L=$f6sUqtL+Y92zeTyH}7}wA$li#g8bO^GEiQue;+e z+rlHaf~u{ckcXWH;8jV~)}~EAjpTl!0rSYh#6)IR#st1m>CWYP_Fy?H>nCeNg5rk^ zEIgD%l0#slP2BU+h3zv#l@s-@UsD?`Wu=9ME5yxr9XWEu!h-5)RAf>%_MAitKb`C( zqbw+>BJvLp#cGWuw6q|8mgQI! zTd7_>Yn#{f-G&b9^M2=|ndj0L-d}^8Dv}eTV`CSu-dZ|lM`0kP}@O<^Ng{pPlKvcQKRO=(F6-I!p#nU)IsNIz1sI zGBe3F=8vtfB0?IOUIOzOJ*XZ$DoG1jXrvDY(=)kjSyR-zMD2GT#0FVIVR-W3zk8WK zfBeUCylO=HD*V?|-aQ$)RaltlsM}1`pJ9-k7V)oJH;L5~u6cUHzK0(Wh}Ce(*25!# zSR#?oF>aNLZIko8GXc)<-J-`XD4@d%REU;kqvP5I9XGWxq*=aytzaD~GNHK#prf!9 zy`R!pw+)W%VW^V(ih*=IfSOW50K=RO#DM?W;f2TOJ5*KUrUJyI_W|&|R*U{( zb=mm01icL{KV!AWC`V}<7gw9P<*FnN>tmoh-rT$Bba@bN+V^p3d24Vj%8}G|i=&)_ z8v1AG#XhwcFQu&VwGm)|Kw~um(vl$G^1gE@!a96-!ce2pKs4h~}y~rRvu-MP$ z7|;6 zncm;E(*B~yIESvT?h!@ooIR^O30aH2V&NV!hB@nIM%lBMT(WxY%(A{#i~i6Cm;SOI zgB0lF8As|rBN)V%ZmB5NpP2n14gr$1v|&7%*B1S|NUtAh(X*Nahv<6R$0Og#(@`{h zO$F>6G?BnlGdg>*+(YEch#eC6S!umF{azstj5CtIQ!jxUGCtNc6*w{8q3z~dtF>!a zA{w*C<}w>9c^v}xfug*r@Z|)*`;%zGXr*wcvc4rEha{_wjKuD{R(ryr5Trw6b3SF8 zoNqg>)uPJ`>cILF+HUF<;0=<^dmcA?4Gau))kk5i14b`lrFpr;h3~Len~oT|p-enl zp($1r)W^w<*ceL+`NHSq>>gcV#fgE+&X)@SmIqu4paz)k4&KaPH)!iYrBXru(#_Jp zjIAiZ+?o%kgp{C4CCMcS)YQlMb69TwGL)>ejhCpj^DaBOK`Ccnx>OI0RCvQHmoICC zgWj8r$XVAF{|Vq3jMp>NMbw4LK^q&b(KzP*{bDOiFl7+lL7{H#%iw^3RxhLBMdz*? z#Z|!3Ml)JSz5^MroXOp>MfgF{C{n8o7dj zKe#JC^mKH4kaWcf{^i1CszS(<&|93HM7IFYf}TlRkN`m?*wc>!hg{1yr2{p|*l^d{ zdR3_2;w9grtk4$l9u3jmx}@`J#1HQAkBN?keEjx)buYKVL>0c70D1IAElddLzFHN8 zzy{@-b=Ief$}(0Lma7&!D~_To8= zj2@RRrJbnh!vfi~FT>-;jB{)ehr9khJ-dNE!NJmJzaEfg0!c!t1CjaS2fMxs&qPrN zt!Uh+ZEcbK652!7KW;#FKzjM&h00*inbRzPed)7sp8{nG!jIqL z2LvY1H2&|O92ljReY;qEz~J|X<5~UCh2GaOkHLdVV?by$t}(rcXRC-LV%z<%KCz2} zw0=Ljm)?jD;_QN_pz<9LYU33bx9hff_rMHZ`M&8|Z{*aej{WA{x~7ON=#eZDLA&4-BDAALa7o~y3dfei{voeB>a3dd8TBcoMMuq{dfE3{ zY|EBC4z}=|hJmln@tR0yx%lXEqw1CDFw4r=bgn~MR%(ECQi>FVYrcB}x4YUle%?_}K zew^?EVM#z*hw$4eEeN0dlhB4eh;(jePQ0>@Rg-hC{BX(Q{jQ^}2*ZjbcWY|;o!z@y zqi-%Fa|UZBSaVdX9Yx2#K3~ka zNDYl?e7nF(AE>o_kQtSZ5dw>7PeM`eE89FjYLd>q3r3NV(<%<+$V;;P9w$Vj&{pQh ziul2XPJN5zAaCZ1GjsBbm&8?b-QZTtLog8@YEzr0eqrA^W*)2fNC)6m6m_rzCCCZk z&JcOC0)e+ZneQ0?r&i;Hg-BvG|6}p!(LJ4eGSzYm2SF>s7J+YgkGS)}ez`RvTaIsr zCfgahLVu~a?+wb%5MAGDSxSr^poAC&QzC^)DMu1}^f?#L2x=HgBhK+&Ld(Jki^lTL+~r;h|n0npe>-a zkz2v#ECO;q25hNTuUaG{JiNS-Si5-80Dji6MzXND?`4!si5kGbUXF!)5=D{lt4SIrz0-Fn2o0j zI01vGKv4ieXz*dWd3N}40Qc1{jmvaqkE6<0p7Y!?MIB=$@87SnY!6*S1w|&fxtDey zfx5uY(4ZPJ>!tQF1gQK@`z=+bn;XI?NyoW z8bRc8^co>~iHIoWo=fG4>ar0ipVhA}Ryb;qqCD&ieO|+@yLRsmc$jzc;{|7F_igv5 zqtRb>fAogY@fwKiDGt3qj0glBy48z}J!L5jcS+Mp+6i~6M*tG?561NP_M=#5g3JKX zLO_9-%HLs`1&FGDV7>dSX@oEZK1+WF_0XqvO-(dS0nkn z=E6e&3kYt6^%Kux5rc8Qvte%L9`u@S-KyW;wvps6F#TvV=$c4`D+}TK3-##n!59|r z)smtzGPNI1N`-0FTuNqiub+-`x(a@UeF`c`b%3}Ufgb{BM`W7CMzZ}C{NMT!gb7dr zu(Cg9wueJqn9Y=#)cAM2J+7AI3kjOrw{iB3dN+wQXY22eHJ#)zE~k;)IsV9^{~!2B z8<;xS1uIvstUYh}AAg{rsjeP}<8|QgD=I5#hx4b+;=Q{gzBD;C}JewU-w%l{7jX8jMiDTq|iNycfyV`W|nX)B6>U$ef<6Tn9<36BoD#N zW;eR3d5fl@pz>z1gd&$4%*KzF8$a6Xeu6)?_-<37v|fb+s1noG=Qn6?9YtPMRrNSK z1IgC+&YdpT`{~F$;5oy#TCI9bciQ}K^o>ZfP(a8?I_UD{%lZlW-DvY`m!PTsfb9S) ztx=4&sEXwVc5+c$QSidQP?bWn9p$>hOh8S5SvDir4+Va3riTm`Wlbl5zrd?${E`5x zGSZ(e`y)R5?U7%TLvP(W`0hai=>X5)uLGnqJY30Q*0TB7Y=&x%C98y|( z^sO2<5^q>g(DW*A%p&DXF9>1@56I6%x*J)EbOQ@!xZzAhBFD02bhKa~-`<}sN?LPl z`mW2C&AioJZW>k*=iR}pwrAT&UkOy z_E~2Tb!-w%(bCoR-GHV-zfriwqH!{N@GY^U_x>r$NnF=|;-xhc}0R=Noq-~9cT5te3|JoY{Jx2423 ze&cO4oD*M%8(%`}G#$#B9rp&fC293q%E6rsJCf460Q`a5 z`vsT-5s}FLlMfe4+PZ`O%m=0+tqUw8ZpW5e5co%?hu)11A|hpnO-DGfQ6FyliF&$t z84a$w2tTg|k$n#j^>Ph5LR87=*%`lqjkQ>thi>W$7AFz=aDJ+TQZW zkuE(g=L*1e)+i+TA1$SAe9=zbD?XFOBB2Z4a4Q+j>vt{7UN*Cn#@G93r_PKnQeA75 z3VN>2}oU6X9j%6QUmY?@n@1C3t(iS_td?cq2sW0DJXq$oH*{a za4jZeWG%*x^aErkk#zh=|21p>c$<;FCTvrH+Gf|krIgT*-+(eNFN`%z^$MFd*kxZw zfKM~ER3;i0Xia1A=4Z!&O*8f#fA@2a#eaKUj3>fL;=ziijlnFEd*pQf{pUxeKZ{6B zkwhgE?KtY{>YKTndByu;{+kUknp4Frmh*KiL#^kxUmupIlsQ?h-gS?g79J@FzvO+B z>EikW<2+pXfWgnZAX0vhS&CdrTZK||t*VCwsvfnfg$sUrcZ}X64f?Y+fBwm#0IF3( z7R_Oa?X$mTU^^E0%p2)nGa`{#ZsRR=*AUj}r^DLz^yAG#@LKCI1ArVVD=TsK4jF+R zu`BnT|FkxjM4G>g81slkX1x&(8s;1YKszZ8t=P&YRbd1GY1e(0{`NpW_h*X)B{d}l zolh0u9zrs~P#0jtc0r>R1^+%Iu}%Ay(Uz{;b=AwuK_(0%3wyWO%tEy;N^NmBZ_A|u zG1AE1H4)}z)0=k1jatC;b4)cO36b?(8XCT%j~`1J#{TVGj%M#?PuRce%X9sl@h5+e z)<#OI>1(@YfR zf>41pvY2@GRnwb*2zir{M8sM$klZ6!X7&(VG||95KSGb0M4GjfxOyc`gJ)cI6EP7k zuwn{^?DqwIsH$(D{yQSrI6sk-|Nqj(P6h6;^0$GXDTV#HRfysNe%^EQw8bNl?h3+g zUHwM2m*V&zS8@7YB!X^JjU>_<9hmVKs47GsQciBoyq_O8&PXCLl>gioyXj69E7#8n z-D*`KdV%53A)Ky6y%919<5_0-+c$W8l~@z|%S=khz-ZSGkp5idZ}A8Bwr`_Y@x3`{#-ok9FRuZD3a4$ff(`GYXk@Un=oQPzb+u@%u?331o%!}LU^F8!P z0zJ3f!OX_pfmcYun6;QDTleq`=A1xb1-f+TwOF;q!Uq+S?kq+z9v-Fa`Pd#@7{AYi zITwE|2NjT&oSY)kUrtJa+;A&6IL>rAiS$JA=ad$TP(bwHXh9!yytxVr#W%)1>efU| ziYA*2@7MdX`p<`-_GK+>G;p{NLDCo*H`9UzWU{dE*kIYgLdD-Mo-qy-L1AG_pdhFt zV8c|4l&EwUrHKWM_cspo1VMRFx%J zZswwCI5~a4t8@|VdrH-8yT4;Lwkgb5P1r5FXa#!5HG%NQ!;Q{yD`krwnJf>xheKW< z#EBX+Wgbj8)1Id)Nt7BuFseItKu_n}R9LqfF~P)lYI%`JG3;K;8^GPeS89|1mQb(O zO&f~ROO{c_B_7X*sSW5BMO_@=D#)u<^Q0HVR%W8PecYu>(0F-SQj1qIkjArT&-#2B z4vgnQutz!|(-h@^)=*3c6q#?`KZR8-3i|Q^*sl>i-SlJc+SfeLkUT}atvtjHZQ;*Uw&&9}1)4<>uKA>*n zd6Pd(9ys;KlfnDB^Ikx>L2_?hf)IoBP!2dFi4@rGOz%^8xb0Q0jeY&aU%q0-&R&jm zcW&x@JnuIT{*`e!uj;p33s62X_nb>;=9=@HYhXU+QILVJm*KU83hLi z!}D;~+A;_J%C@&v!C_ta-JYJ4bXJfJEhBwKJ( zI6C#{NA1G}S{gfV-?MWKyeHXoXY$o*h5hx{KeU`HR1ryIWi^MN5)QBeY-!?8H3DyE8l(l@9&(wdya70g|c#^<}oLlmP73A~Thk2!r zFAAo5=$q8<>2;HI3_BuFJMnBWgyc)cuJrao^jngaF`ML>4u+RwL|K<)?r58F?b@lb zk#BlA)frD)9Es<-YQ*gLCFyK0cV2$%wWV;7Y->B3&uvX@QlU@wfWl>&>jLTd@Gnn> za3!L;C-Wn>eu#7c_;XMcZVE7;K|f-!ev^6WYILLdFtj^ZXbkK$YXUk*%4m!?^?b7J z*B2_?y~z-wJ@x5@b8oKX`t=l4rr}onJ&@t9*mOt*kUPPYKwuty|GvT?T@Owgz1OkH04^n<(0?>5VIPUVL;QYG=fIMjI!hqq&wnAfnesLBu?Q9RQ~-n(9)H8 zxL}b)L_mOjgMBrpg3G>0G&$a<+}o4r^j*L3AF`#XN|Fb{tcQ2%Be!lS6*AZ)Asw~^OrH0-vJK9(3n6*qn+@~c zPEHgwXtIg!^7M@VlzZPsZ-3O#V6^~IfVESfn!M;dY{BY-F;GM~tR}gd&Pp5dJ4}a!2hY~@ zz;<8Ik^<&&jnuhKrs_?8?5TI$m#RQEma@GL?VX30E)B~C`3sT{J-x8J(enF7Z1N2P z8Pwd5mscMv`~D%+CcR{f+oWwriB?#a;{$yia7Ar=uD9i#E2!6;oalW$Io@8ETRmV^ zY)J<-It9l^hHFM!)X~0t&b|LM4kL)tdhfA_q+6rC`HDE>>1&;JwpH`1-kfTBFzeEw zJ&7aiy{*z{bEJU?cEihS$iz1|n5aogb9iOUmb9ftDp!{}%mI#XO+EaZfQnbG{4ZD|lPtS>``ER<2^!@QAC zYosT#)Y{7@b`Gs;?@lrvj^ppmpSsqS+$G z9_o=_SIuZBbzJvC)|Nv1BYiC^HG`7WGqu9>hpwfKgRFm##b9%94Hn;2&$yXol ztP&`*R{&$TR8wr>N}1EAGi{YRH$dEDHKJVzkraI!wvS|UZ$)qm^<9N^_b;dUj{{az z!@=v5biLgAdk(`50neWc%l8#FSLBJCkC$}ZINN+vsIzG2@;p>Bd@W-(bkXBN6XW)W zwrB3OCTAab$|U`zIL2Y*Yqd>&uK%8}e+GLdw!`@r+z1xDCv?nauf44~2;-j+MXw8s zl@3h1aNTJ19W-@yu9&5t@JVaK-T}_j7+-bKmhpJ~$YfiY@%_xr2CnWY<4@<+slNqY zt%xE)_k<~Zi}TQx7>x#=e7=8HZRsqR)zl8&EMA7w%Vm2W>&2g|e=M1xfAdzeM*mf# z-q4S^#*u7JFhoUq*!Oo%qUccA`=YN^vgw3@08uTtu}VTKN!+e&%y!mpJJoKpN!FfR zV+mrbs0qMmIDdFlAXCDZd$w}7Wd3mDIL`ka>>lgG>PoOL4q6;Au*nylIl=hk{6F&%8m(IB&R=$zBG4!x65ML|{m zR`2aLPouEv9JW35;%5ry_Ule5+k79$Y;touQW1UW^JixiCs3i_8jmx{ysNEX$a6*P zN|dYAW_4eO-pRC(ru1&K5zeOj`+QB_5N9H7B&tj~U4F;$>C;8QqaQAM^dtm~uc(SC z;G{K5DkpVFFQL7G5y|`Zs>QkY=AcR6c>jcPhU-1AHJ9r!5c}@TmbOqEoR7X2 zW)3u-)h+)B=T8=&6DWbSonef|-qF4LPzhTF6;Hp)D|fEiyVk9vFzoZvqC+2K_cO~v zpK2U4F$h*um8}pR>kJ}T#xh|`Qp$52uUD8j%ADy@qj(H{$xu~M(Ritmqjj4_>{z*w zo_U}kd3%`FQCq3)^b$My3_@0o?I@s@deV2Xus94QXJ569!x)c&D8Ky zp8V!5=A|mjX^_St*MTXd$mQ5_N+RE}f4I|tG4n;*@&28Z&Nu|}Zi7|&5+S~}I$P@> zt=$`_9lBM`Ri|PgWePTSi#K&JZ)KxiUgQv3YQO;L@fX7Rc3~Tj78`&2CJqI%SZIp` z9;TPy8O?JFzUA!eHlVvxB`MSPx{LDU;Yn ze%7oXy2{|7vR379pjH#WKV6tU=}1a+Q+Z`Pv1OIFPXK zSVXr#%;W<%k+W^VZlHI(>L2QAoVe&|WPf`zKVObP)_`e9yIw2%+-tR4s_lgP5-u1$ zKKMKtfQEW@iFH(D{LX6E$9RyG=(0f-5xH&@X>Czd$*p%9&2amzt(#huc9w~SXtOeQwAuZjHzidKotaYjyB#eIZMeq-`b zXKUJ7runq#jG5_h!mJ}W!!WLI>Er(LiuH-D`5>>0m`%p4qqK&A@L7fR{kVzFuZZJE zR&@rX*v{|F<=C7%^6Bm>i5|aTP)4%NLf__&Rr2t(R?0=b8#AXmzD6K{@)CzcCytRD za_$u^8*KLSj>yH~DBb1{?{Y-x-^Ye4wz=8Y``Bev$FV-XdVq!2RxCnkm3(bp#^uwb z+*0mask3vZ$II)A+PmckGxFt4aN7N-&B=$#<)O*WCxIWf$Pd@orFEF~W+l`MM2rs~ z&PS@W)i0Kc_$B|296xtm`nf$u8Yw3RJ})?4-PQq*9Vn!i?mF?Rf@13hD8p}qe`nUj zkS-+O9c0$FM91%!O}JLOT=*TRL`9T75et(wtk#wd=Y^$>ac0o^!^g{X zV|sl298V9(KYBzvLQA{gg@tA%3h5dhorhVE6{lR>3`d6QrUIh_XWts|moex)>o*dp z?E2w+ev!y{fud7vW_ZoOjoP%S$j|GOwV`zhMMrnqx{VDfZyu;JFm5$qlzx?E zzq;^g{4uN6eJv{x{pdW{|9lZ=R=sVBh;Dxms2k=M>&znclf#6Bq}h|4zkj&s4P}N0 z4XpZpKtTDtwU0-Q_6H?g(GU63EY~tD7sp=~qGI(CX?s=Lspd1b6rZKkvC8ZU6M;j9 zpGRv1H)kQnkWNYvI8|w)%~dGf;Cy9z=WAJGnZ9?P6q+;4S1l`4g7q!sGLmM}r>ncD zc)PS+)@#4x$tsDuf40eYk(bH^Ot5ZT;&J45GTr)YyzG0*{ z`$0YPnO?1_k>;a4-v4OY$!m6wDwaz4pP#bR;)DGF854}{7p{TNf^}H?t?-yWJ?!b3iB0L`uF36UBla1rWR#TX#@BZpu9b2 znp(g5KNwE4|I&l@i9Ol!8^DBNL56x`4dq(x)TT-8j|JSXUlkNsW?YG3p|JsbbYJpH z^Hs%C@>s!rEs5{iXgqNZMyH{J^FB~od4RjFP(lDbg%0B_y}2z?ka=Ah9!Haje1}Yh zwDAI!pE3oeL0j9%CZ@*9K#`pL@A`gEVIn~P;^WI{&RyHvF<4#jtz(|PDoI@` z*=8e8L50tC8<~XUAMrk=FRuCJyfz;Sy}!Fe0^+(|D{0|ZP6z@E%>P8q*NPU;9d1-} z(MnoadPjO}aj)fs`qrQ@i(zKPedubavvE85BSo!Dw;D5l9Uhg-NzzH<=T8a|Z=ZN3 zXUsO4cDUd?a#S@UN8PmcM~+%!K{q!y_ge9dVy(Dn)S#~|G&NA9SH>Iy~I zKG0Qqp7X6PZcAC(?fQ`$sauHDsp8c(yT0a$F}e84O7fhSWq0TAuFtf5c4@l|erXxI zq$8B+W!jIe^z;t8KE~H;B_iXe8G9VdkSaIj|FSW+psjquqNQ*Xgf=U! zk>Hb-dCI8(shDg|G_=vay>YPDtylWsvrt6$kS+3d;KwC*Sz4tj4I^>@SV+s35a>8* zgZF;}OQ-={COyH7;DUYolgYAZFO)Jx`1D4!k5~3>4?8{=w8-4i&HX9(|8y@d0-zb5Dz$*a0$ym za~|Rgi#`^jF#;o5C2C)TLfj19k9dHCTPWh2H$#a79EcdskuuNNO24_Xxnx*?EFjLpmdUI?_LyF#@A0|G+)bqs8w7Kn^^6yh5! j0+PPc?`z@D`I}mN!i*y){ literal 0 HcmV?d00001 diff --git a/docs/UserCalculateCaloriesSeqDiagram.puml b/docs/UserCalculateCaloriesSeqDiagram.puml new file mode 100644 index 0000000000..92943b25ff --- /dev/null +++ b/docs/UserCalculateCaloriesSeqDiagram.puml @@ -0,0 +1,36 @@ +'https://plantuml.com/sequence-diagram + +@startuml +actor Tom +Tom -> UI: Input "user setup" command +activate UI + +UI -> UI: handleUserInput() +activate UI + +UI -> UI: handleUserCommands() +activate UI + +UI -> User : setUp() +activate User + +User -> ParserUser: parseSetUp() +activate ParserUser + +ParserUser -> User: setName(),setHeight(),setWeight(),setAge(),setSex(),setExerciseLevels(),setGoal() +ParserUser -> User: getHealthInfo() +activate User + +User -> UserGoals : getHealthInfo(user) +activate UserGoals +return caloriesRequired + +return +return +return +return +return +return + + +@enduml \ No newline at end of file diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 43a6303fdf..813b426958 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -60,7 +60,6 @@ public User(String filePath) { public void setUp(String line) { try { parseSetUp(line, this); - getHealthInfo(); fileHandler.writeUserData(this); } catch (InvalidInputException e) { System.out.println(e.getMessage()); From 7a9372e72e98d82b6c6776e9ae30e648d2b564ca Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 4 Apr 2024 22:39:53 +0800 Subject: [PATCH 206/414] added newlines to overflowing text --- docs/UserGuide.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index ec79667df6..47fb01e57c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -51,12 +51,15 @@ Shows a help message listing the commands available in the application. LifeTrack Command List: - help: Displays a list of available commands and their descriptions. ----------------------------------------------------------------------------- - - calories in c/ d/ m/[carbohydrates, proteins, fats]: Adds a calorie gaining entry into the calories tracker. - - calories out c/ d/: Adds a calorie burning entry into the calories tracker. + - calories in c/ d/ m/[carbohydrates, proteins, fats] + : Adds a calorie gaining entry into the calories tracker. + - calories out c/ d/: Adds a calorie burning + entry into the calories tracker. - calories list: Displays all entries currently stored in the calorie list. - calories delete : Deletes the entry at the specified index from the calorie list. ----------------------------------------------------------------------------- - - hydration in v/ d/: Adds a hydration entry into the hydration tracker. + - hydration in v/ d/: Adds a hydration + entry into the hydration tracker. - hydration list: Displays all entries currently stored in the hydration list. - hydration delete : Deletes the hydration entry at the specified index from the hydration list. ----------------------------------------------------------------------------- @@ -64,7 +67,8 @@ Shows a help message listing the commands available in the application. - sleep list: Displays all entries currently stored in the sleep list. - sleep delete : Deletes the entry at the specified index from the sleep list. ----------------------------------------------------------------------------- - - user setup h/ w/ a/ s/ e/ g/: Create a new user, or edit an existing one. + - user setup h/ w/ a/ s/ e/ g/: + Create a new user, or edit an existing one. - user progress: Display calories and hydration progress towards the daily requirement. ----------------------------------------------------------------------------- From dd056ea0e34c98de004b41bd022224352afaf4c0 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 4 Apr 2024 22:54:31 +0800 Subject: [PATCH 207/414] added future plans to UG --- docs/UserGuide.md | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 47fb01e57c..a7f596711b 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -45,7 +45,7 @@ Shows a help message listing the commands available in the application. **Format:** `help` -#### Expected Output +#### Expected output ----------------------------------------------------------------------------- LifeTrack Command List: @@ -79,7 +79,7 @@ Exits the program. **Format:** `bye` -#### Expected Output +#### Expected output ----------------------------------------------------------------------------- Bye! See you again soon ^^ @@ -127,7 +127,7 @@ Shows a list of all activities in the calories tracker. Includes both calories i **Format:** `calories list` -#### Sample output +#### Expected output ----------------------------------------------------------------------------- Your Caloric List: 1. Date: 2024-06-15, Description: chicken, Calories: 1000 @@ -265,7 +265,7 @@ Displays a progress bar to show the percentage of calories and hydration you hav **Notes about the command:** If you have not set your user up beforehand, this command will prompt you to do so instead. -#### Sample Output +#### Expected output ----------------------------------------------------------------------------- Calories: @@ -284,6 +284,24 @@ If you have not set your user up beforehand, this command will prompt you to do **A**: {your answer here} +## Coming soon + +### Calorie lists to show calorie intakes and outflow separately + +When the user inputs `calorie list` into the terminal, they should be able to see the calorie lists for their calorie intakes and outflow separately. + +This upcoming feature will be implemented by allowing the `CalorieList` class to have two `ArrayList` members that store the calorie inflows and outflows separately. + +### Calorie and hydration progress to be calculated based on date + +The current implementation of the calorie and hydration progress calculates the calories/hydration consumed based on the total number of entries in the list, irregardless of the date. + +We want to be able to provide accurate representations of users meeting their daily calorie and hydration needs, so this feature will be updated very soon to accommodate that. + +### Edit user details + +The user should be able to quickly edit their details without having to run the `user setup` command again, as they may only need to change a few details for their account. + ## Command Summary | Action | Format, Examples | From 021e1296b1ec5d33d791435be54ef635abcd1e0e Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 4 Apr 2024 23:14:48 +0800 Subject: [PATCH 208/414] remove help command expected output, add more planned features --- docs/UserGuide.md | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index a7f596711b..7367c79e85 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -45,33 +45,6 @@ Shows a help message listing the commands available in the application. **Format:** `help` -#### Expected output - - ----------------------------------------------------------------------------- - LifeTrack Command List: - - help: Displays a list of available commands and their descriptions. - ----------------------------------------------------------------------------- - - calories in c/ d/ m/[carbohydrates, proteins, fats] - : Adds a calorie gaining entry into the calories tracker. - - calories out c/ d/: Adds a calorie burning - entry into the calories tracker. - - calories list: Displays all entries currently stored in the calorie list. - - calories delete : Deletes the entry at the specified index from the calorie list. - ----------------------------------------------------------------------------- - - hydration in v/ d/: Adds a hydration - entry into the hydration tracker. - - hydration list: Displays all entries currently stored in the hydration list. - - hydration delete : Deletes the hydration entry at the specified index from the hydration list. - ----------------------------------------------------------------------------- - - sleep add d/: Adds a sleep entry into the sleep tracker. - - sleep list: Displays all entries currently stored in the sleep list. - - sleep delete : Deletes the entry at the specified index from the sleep list. - ----------------------------------------------------------------------------- - - user setup h/ w/ a/ s/ e/ g/: - Create a new user, or edit an existing one. - - user progress: Display calories and hydration progress towards the daily requirement. - ----------------------------------------------------------------------------- - ### Exiting the program: `bye` Exits the program. @@ -298,6 +271,10 @@ The current implementation of the calorie and hydration progress calculates the We want to be able to provide accurate representations of users meeting their daily calorie and hydration needs, so this feature will be updated very soon to accommodate that. +### Reduce reliance on network features + +As of current, calorie requirements for users is calculated from an external API. However, to reduce the reliance on having a strong network connection for our application to work well, we are implementing a failsafe to calculate calorie requirements even if the network connection is not good. + ### Edit user details The user should be able to quickly edit their details without having to run the `user setup` command again, as they may only need to change a few details for their account. From 4926c1d9bd99d9b3ef8707fd1ed9fee9408a09e4 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:08:51 +0800 Subject: [PATCH 209/414] Update UserGuide.md --- docs/UserGuide.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7367c79e85..ee928b75bb 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -167,12 +167,10 @@ Adds a sleep record into the sleep tracker. * The time indicated should follow the 24-hour system. * The date provided should be of the form YYYY-MM-DD. -**Notes about the command format:** -Including the DATE is optional. However, the DURATION must be included! **Examples:** -* `sleep add 7.5 d/110324` -* `sleep add 8` +* `sleep add 7.5 d/2024-03-11` + ### Listing sleep records: `sleep list` Show the list of all sleep records in the sleep tracker. From 00ba507b4d38d970fdf09c928294a14820da030e Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:37:43 +0800 Subject: [PATCH 210/414] Added hydration list --- docs/UserGuide.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index ee928b75bb..99810fe7c2 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -144,6 +144,15 @@ Show the list of all hydration records in the hydration tracker. **Format:** `hydration list` +#### Expected output + ----------------------------------------------------------------------------- + Your Hydration List: + 1. Date: 2022-01-01, Description: teh peng, Volume: 100 + 2. Date: 2022-01-01, Description: milo, Volume: 100 + 3. Date: 2022-01-02, Description: water, Volume: 100 + 4. Date: 2022-01-02, Description: milo, Volume: 200 + 5. Date: 2022-01-01, Description: milo, Volume: 100 + ----------------------------------------------------------------------------- ### Deleting a hydration item: `hydration delete` Deletes the hydration record according to the index. From a3dd9ca606e20942453b13acbb3657a23db94490 Mon Sep 17 00:00:00 2001 From: RexYong Date: Mon, 8 Apr 2024 17:43:25 +0800 Subject: [PATCH 211/414] Add authorship tag to Class CalorieListUI, CalorieList, InputEntry, OutputEntry --- .../java/seedu/lifetrack/calories/calorielist/CalorieList.java | 1 + .../java/seedu/lifetrack/calories/calorielist/InputEntry.java | 1 + .../java/seedu/lifetrack/calories/calorielist/OutputEntry.java | 1 + src/main/java/seedu/lifetrack/ui/CalorieListUi.java | 1 + 4 files changed, 4 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 648cccf1a9..7a9e0bdef4 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -1,3 +1,4 @@ +//@@author rexyyong package seedu.lifetrack.calories.calorielist; import seedu.lifetrack.Entry; diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index 884396da33..de20977656 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -1,3 +1,4 @@ +//@@author rexyyong package seedu.lifetrack.calories.calorielist; import seedu.lifetrack.Entry; diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java index fb98d753db..0b3e34b663 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java @@ -1,3 +1,4 @@ +//@@author rexyyong package seedu.lifetrack.calories.calorielist; import seedu.lifetrack.Entry; diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 7c36a6cd2c..809f44005d 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -1,3 +1,4 @@ +//@@author rexyyong package seedu.lifetrack.ui; import seedu.lifetrack.Entry; From 2c822057e32a3384fce77bfef69e559674fd0843 Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:15:06 +0800 Subject: [PATCH 212/414] Add lastEntryID member into Class Entry, InputEntry, OutputEntry --- src/main/java/seedu/lifetrack/Entry.java | 8 +++++--- .../seedu/lifetrack/calories/calorielist/InputEntry.java | 8 ++++---- .../seedu/lifetrack/calories/calorielist/OutputEntry.java | 8 ++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index e8b8375f8f..5b921b3147 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -6,8 +6,10 @@ public abstract class Entry { private String description; private LocalDate date; + private int lastEntryID; - public Entry(String description, LocalDate date){ + public Entry(int lastEntryID, String description, LocalDate date){ + this.lastEntryID = lastEntryID; this.description = description; this.date = date; } @@ -21,10 +23,10 @@ public LocalDate getDate() { } public String toString() { - return String.format("\t Date: " + date + ", Description: " + description); + return String.format("\t EntryID: " + lastEntryID + ", Date: " + date + ", Description: " + description); } public String toFileFriendlyString() { - return String.format(date + ";" + description); + return String.format(lastEntryID + ";" + date + ";" + description); } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java index de20977656..1432c2a4e8 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/InputEntry.java @@ -23,8 +23,8 @@ public class InputEntry extends Entry { * @param calories the number of calories consumed * @param date the date of the entry */ - public InputEntry(String description, int calories, LocalDate date) { - super(description, date); + public InputEntry(int lastEntryID, String description, int calories, LocalDate date) { + super(lastEntryID, description, date); this.calories = calories; } @@ -37,8 +37,8 @@ public InputEntry(String description, int calories, LocalDate date) { * @param date the date of the entry * @param food the food details with macronutrients associated with the entry */ - public InputEntry(String description, int calories, LocalDate date, Food food) { - super(description, date); + public InputEntry(int lastEntryID, String description, int calories, LocalDate date, Food food) { + super(lastEntryID, description, date); this.food = food; this.calories = calories; this.doesFoodExist = true; diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java index 0b3e34b663..7f272de28e 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java @@ -23,8 +23,8 @@ public class OutputEntry extends Entry { * @param calories the number of calories burnt * @param date the date of the entry */ - public OutputEntry(String description, int calories, LocalDate date) { - super(description, date); + public OutputEntry(int lastEntryID, String description, int calories, LocalDate date) { + super(lastEntryID, description, date); this.calories = calories; } @@ -36,8 +36,8 @@ public OutputEntry(String description, int calories, LocalDate date) { * @param date the date of the entry * @param activity the activity details associated with the entry */ - public OutputEntry(String description, int calories, LocalDate date, Activity activity) { - super(description, date); + public OutputEntry(int lastEntryID, String description, int calories, LocalDate date, Activity activity) { + super(lastEntryID, description, date); this.activity = activity; this.calories = calories; this.doesActivityExist = true; From 71283d6a5396e6c41ea7b275294a38ee0f6536f9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:15:55 +0800 Subject: [PATCH 213/414] Add ways for lastEntryID to be incremented and included in Class ParserCalories --- .../system/parser/ParserCalories.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 79c5b6f2c1..2ae146cff3 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -40,7 +40,7 @@ public class ParserCalories { * @return an Entry object representing calorie intake * @throws InvalidInputException if the input string is missing components or contains empty fields */ - public static Entry parseCaloriesInput(String input) throws InvalidInputException { + public static Entry parseCaloriesInput(String input, int lastEntryID) throws InvalidInputException { int caloriesIndex = input.indexOf("c/"); int dateIndex = input.indexOf("d/"); int macrosIndex = input.indexOf("m/"); @@ -102,11 +102,14 @@ public static Entry parseCaloriesInput(String input) throws InvalidInputExceptio //@@author if (command.equals("calories out")) { - return makeNewOutputEntry(description, calories, date); + lastEntryID++; + return makeNewOutputEntry(lastEntryID, description, calories, date); } else if (macros == null) { - return makeNewInputEntry(description, calories, date); + lastEntryID++; + return makeNewInputEntry(lastEntryID, description, calories, date); } else { - return makeNewInputEntry(description, calories, date, macros); + lastEntryID++; + return makeNewInputEntry(lastEntryID, description, calories, date, macros); } } @@ -256,10 +259,10 @@ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateInd * @param date the date of the entry * @return a new OutputEntry object */ - private static Entry makeNewOutputEntry(String description, int calories, LocalDate date) { + private static Entry makeNewOutputEntry(int lastEntryID, String description, int calories, LocalDate date) { Activity newActivity = new Activity(); - return new OutputEntry(description, calories, date, newActivity); + return new OutputEntry(lastEntryID, description, calories, date, newActivity); } /** @@ -270,9 +273,9 @@ private static Entry makeNewOutputEntry(String description, int calories, LocalD * @param date the date of the entry * @return a new InputEntry object */ - private static Entry makeNewInputEntry(String description, int calories, LocalDate date) { + private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date) { - return new InputEntry(description, calories, date); + return new InputEntry(lastEntryID, description, calories, date); } /** @@ -284,10 +287,10 @@ private static Entry makeNewInputEntry(String description, int calories, LocalDa * @param foodMacros an array containing food macros (carbs, proteins, fats) * @return a new InputEntry object with food macros */ - private static Entry makeNewInputEntry(String description, int calories, LocalDate date, int[] foodMacros) { + private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date, int[] foodMacros) { Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); - return new InputEntry(description, calories, date, newFood); + return new InputEntry(lastEntryID, description, calories, date, newFood); } } From f1ca3c4f38900959de9be23402199b1d70d9860f Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:16:47 +0800 Subject: [PATCH 214/414] Add lastEntryID member into Class CalorieList --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 7a9e0bdef4..8d7a95cb0d 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -20,6 +20,7 @@ public class CalorieList { private final int SIZE_OF_DELETE = 16; private ArrayList calorieArrayList; private FileHandler fileHandler; + private int lastEntryID; //constructor for JUnit tests public CalorieList() { @@ -94,10 +95,11 @@ public void addEntry(String input) { assert (input.startsWith("calories in") || input.startsWith("calories out")) : "ensures that input is correct"; logr.setLevel(Level.WARNING); try { - Entry newEntry = ParserCalories.parseCaloriesInput(input); + Entry newEntry = ParserCalories.parseCaloriesInput(input, lastEntryID); calorieArrayList.add(newEntry); updateFile(); CalorieListUi.printNewCalorieEntry(newEntry); + lastEntryID ++; } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); } From df576c80a5c077a8327dbdee6fe6cebc25e4d4ab Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:17:33 +0800 Subject: [PATCH 215/414] Add preliminary method for lastEntryID to be initialised --- .../calories/calorielist/CalorieList.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 8d7a95cb0d..f222c69948 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -37,6 +37,8 @@ public CalorieList(String filePath) { try { fileHandler = new FileHandler(filePath); calorieArrayList = fileHandler.getCalorieEntriesFromFile(); + // Initialize lastEntryID from stored data or default to 0 if not available + this.lastEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { calorieArrayList = new ArrayList<>(); System.out.println(ErrorMessages.getFileNotFoundMessage()); @@ -144,4 +146,20 @@ public int getCaloriesConsumed() { } return totalCalories; } + + private int loadLastEntryID() { + // Load lastEntryID from file, if file doesn't exist or error occurs, return 0 +// try (BufferedReader reader = new BufferedReader(new FileReader("lastEntryID.txt"))) { +// String line = reader.readLine(); +// if (line != null) { +// return Integer.parseInt(line); +// } +// } catch (IOException | NumberFormatException e) { +// // Handle exception +// e.printStackTrace(); +// } + return 0; // Default value if file doesn't exist or error occurs + } + + } From 8260e0ddfe5912fdeac483677e0377fb27d6820c Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:17:57 +0800 Subject: [PATCH 216/414] Update Class ParserCaloriesTest to fit new member, lastEntryID --- .../java/seedu/lifetrack/ParserCaloriesTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index fcc732af5e..d77522386f 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -19,7 +19,7 @@ class ParserCaloriesTest { @Test public void parseCaloriesInput_missingKeywords_exceptionThrown() { try { - parseCaloriesInput("calories in burger"); + parseCaloriesInput("calories in burger", 0); } catch (InvalidInputException e) { assertEquals(getCaloriesMissingKeywordsMessage(), e.getMessage()); } @@ -28,7 +28,7 @@ public void parseCaloriesInput_missingKeywords_exceptionThrown() { @Test public void parseCaloriesInput_incompleteInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/ d/220224"); + parseCaloriesInput("calories in burger c/ d/220224", 0); } catch (InvalidInputException e) { assertEquals(getWhitespaceInInputMessage(), e.getMessage()); } @@ -37,7 +37,7 @@ public void parseCaloriesInput_incompleteInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger d/220224 c/123"); + parseCaloriesInput("calories in burger d/220224 c/123", 0); } catch (InvalidInputException e) { assertEquals(getCaloriesIncorrectOrderMessage(), e.getMessage()); } @@ -46,7 +46,7 @@ public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 d/2024-03-22 m/abc"); + parseCaloriesInput("calories in burger c/123 d/2024-03-22 m/abc", 0); } catch (InvalidInputException e) { assertEquals(getIncorrectMacrosInputMessage(), e.getMessage()); } @@ -55,7 +55,7 @@ public void parseCaloriesInput_incorrectMacrosInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectCaloriesInput_exceptionThrown() { try { - parseCaloriesInput("calories out Running c/abc d/220324"); + parseCaloriesInput("calories out Running c/abc d/220324", 0); } catch (InvalidInputException e) { assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); } @@ -64,7 +64,7 @@ public void parseCaloriesInput_incorrectCaloriesInput_exceptionThrown() { @Test public void parseCaloriesInput_incompleteMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 d/220324 m/123,132"); + parseCaloriesInput("calories in burger c/123 d/220324 m/123,132", 0); } catch (InvalidInputException e) { assertEquals(getIncompleteMacrosMessage(), e.getMessage()); } @@ -73,7 +73,7 @@ public void parseCaloriesInput_incompleteMacrosInput_exceptionThrown() { @Test public void parseCaloriesInput_macrosInCaloriesOut_exceptionThrown() { try { - parseCaloriesInput("calories out running c/123 d/220324 m/123,123,132"); + parseCaloriesInput("calories out running c/123 d/220324 m/123,123,132", 0); } catch (InvalidInputException e) { assertEquals(getMacrosInCaloriesOutMessage(), e.getMessage()); } @@ -82,7 +82,7 @@ public void parseCaloriesInput_macrosInCaloriesOut_exceptionThrown() { @Test public void parseCaloriesInput_whitespaceInMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 d/220324 m/123, ,132"); + parseCaloriesInput("calories in burger c/123 d/220324 m/123, ,132", 0); } catch (InvalidInputException e) { assertEquals(getWhitespaceInMacrosInputMessage(), e.getMessage()); } From ad0bfe666e2a490cfc8367cff8ce2fb0f731ec09 Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:18:42 +0800 Subject: [PATCH 217/414] Edit Class FileHandler to handle new input lastEntryID for calories, hydration and sleep --- .../lifetrack/system/storage/FileHandler.java | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index bc4f9d5fbd..d60679a501 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -20,18 +20,19 @@ public class FileHandler { //general list constants - private static final int DATE_INDEX = 0; - private static final int DESCRIPTION_INDEX = 1; + private static final int LASTENTRYID_INDEX = 0; + private static final int DATE_INDEX = 1; + private static final int DESCRIPTION_INDEX = 2; //calorie list constants - private static final int ENTRY_TYPE_INDEX = 2; - private static final int CALORIES_INDEX = 3; - private static final int CARBOHYDRATES_INDEX = 4; - private static final int PROTEINS_INDEX = 5; - private static final int FATS_INDEX = 6; + private static final int ENTRY_TYPE_INDEX = 3; + private static final int CALORIES_INDEX = 4; + private static final int CARBOHYDRATES_INDEX = 5; + private static final int PROTEINS_INDEX = 6; + private static final int FATS_INDEX = 7; //liquids list constants - private static final int VOLUME_INDEX = 2; + private static final int VOLUME_INDEX = 3; //sleep list constants private static final int DURATION_INDEX = 2; @@ -86,20 +87,21 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); + int lastEntryID = Integer.parseInt(words[LASTENTRYID_INDEX]); LocalDate date = LocalDate.parse(words[DATE_INDEX]); String description = words[DESCRIPTION_INDEX]; int calories = Integer.parseInt(words[CALORIES_INDEX]); String entryType = words[ENTRY_TYPE_INDEX]; - if (entryType.equals("C_IN") && words.length == 5) { + if (entryType.equals("C_IN") && words.length == 8) { int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); int proteins = Integer.parseInt(words[PROTEINS_INDEX]); int fats = Integer.parseInt(words[FATS_INDEX]); Food food = new Food(carbohydrates, proteins, fats); - entries.add(new InputEntry(description, calories, date, food)); + entries.add(new InputEntry(lastEntryID, description, calories, date, food)); } else if (entryType.equals("C_IN")) { - entries.add(new InputEntry(description, calories, date)); + entries.add(new InputEntry(lastEntryID, description, calories, date)); } else { - entries.add(new OutputEntry(description, calories, date)); + entries.add(new OutputEntry(lastEntryID, description, calories, date)); } } return entries; @@ -113,10 +115,11 @@ public ArrayList getHydrationEntriesFromFile() throws FileNotFoundExcepti while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); + int lastHydrationEntryID = Integer.parseInt(words[LASTENTRYID_INDEX]); LocalDate date = LocalDate.parse(words[DATE_INDEX]); String description = words[DESCRIPTION_INDEX]; int volume = Integer.parseInt(words[VOLUME_INDEX]); - entries.add(new HydrationEntry(description, volume, date)); + entries.add(new HydrationEntry(lastHydrationEntryID, description, volume, date)); } return entries; } @@ -129,9 +132,10 @@ public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); + int lastSleepEntryID = Integer.parseInt(words[LASTENTRYID_INDEX]); LocalDate date = LocalDate.parse(words[DATE_INDEX]); double duration = Double.parseDouble(words[DURATION_INDEX]); - entries.add(new SleepEntry(duration, date)); + entries.add(new SleepEntry(lastSleepEntryID, duration, date)); } return entries; } From 90d8601619d013d80a7bebd19a4d907920e0ff47 Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:19:01 +0800 Subject: [PATCH 218/414] Add lastHydrationEntryID into hydration Classes --- .../hydrationlist/HydrationEntry.java | 4 ++-- .../hydrationlist/HydrationList.java | 18 ++++++++++++++++- .../system/parser/ParserHydration.java | 10 +++++----- .../seedu/lifetrack/ParserHydrationTest.java | 20 +++++++++---------- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java index e06482a71c..53341ac8e3 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java @@ -8,8 +8,8 @@ public class HydrationEntry extends Entry { private int volume; - public HydrationEntry(String description, int volume, LocalDate date){ - super(description, date); + public HydrationEntry(int lastHydrationEntryID, String description, int volume, LocalDate date){ + super(lastHydrationEntryID, description, date); this.volume= volume; } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index c1f0cb385e..809154f881 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -24,6 +24,7 @@ public class HydrationList { private final int DELETE_PADDING = 16; private ArrayList hydrationArrayList; private FileHandler fileHandler; + private int lastHydrationEntryID; //constructor for JUnit tests public HydrationList() { @@ -35,6 +36,7 @@ public HydrationList(String filePath) { try { fileHandler = new FileHandler(filePath); hydrationArrayList = fileHandler.getHydrationEntriesFromFile(); + this.lastHydrationEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { hydrationArrayList = new ArrayList<>(); System.out.println(ErrorMessages.getFileNotFoundMessage()); @@ -88,7 +90,7 @@ public void deleteEntry(String line) { public void addEntry(String input) { assert (input.startsWith("hydration add")) : "ensures that input is correct"; try { - Entry newEntry = ParserHydration.parseHydrationInput(input); + Entry newEntry = ParserHydration.parseHydrationInput(input, lastHydrationEntryID); hydrationArrayList.add(newEntry); updateFile(); HydrationListUI.printNewHydrationEntry(newEntry); @@ -135,4 +137,18 @@ public int getHydrationConsumed() { public int getSize() { return hydrationArrayList.size(); } + + private int loadLastEntryID() { + // Load lastEntryID from file, if file doesn't exist or error occurs, return 0 +// try (BufferedReader reader = new BufferedReader(new FileReader("lastEntryID.txt"))) { +// String line = reader.readLine(); +// if (line != null) { +// return Integer.parseInt(line); +// } +// } catch (IOException | NumberFormatException e) { +// // Handle exception +// e.printStackTrace(); +// } + return 0; // Default value if file doesn't exist or error occurs + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index 44abc9d9c4..4dd58e9478 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -32,7 +32,7 @@ public class ParserHydration { * @throws InvalidInputException if the input string is missing components or * contains empty fields */ - public static Entry parseHydrationInput(String input) throws InvalidInputException { + public static Entry parseHydrationInput(String input, int lastHydrationEntryID) throws InvalidInputException { int volumeIndex = input.indexOf("v/"); int dateIndex = input.indexOf("d/"); @@ -67,8 +67,8 @@ public static Entry parseHydrationInput(String input) throws InvalidInputExcepti throw new InvalidInputException("Invalid date format"); } //@@author - - return makeNewInputEntry(description, volume, date); + lastHydrationEntryID++; + return makeNewInputEntry(lastHydrationEntryID, description, volume, date); } /** @@ -79,8 +79,8 @@ public static Entry parseHydrationInput(String input) throws InvalidInputExcepti * @param date the date of the hydration entry * @return a new HydrationEntry object with the specified attributes */ - private static HydrationEntry makeNewInputEntry(String description, int volume, LocalDate date) { - return new HydrationEntry(description, volume, date); + private static HydrationEntry makeNewInputEntry(int lastHydrationEntryID, String description, int volume, LocalDate date) { + return new HydrationEntry(lastHydrationEntryID, description, volume, date); } /** diff --git a/src/test/java/seedu/lifetrack/ParserHydrationTest.java b/src/test/java/seedu/lifetrack/ParserHydrationTest.java index 0ad814bc61..91f7d2865e 100644 --- a/src/test/java/seedu/lifetrack/ParserHydrationTest.java +++ b/src/test/java/seedu/lifetrack/ParserHydrationTest.java @@ -16,7 +16,7 @@ public void parseHydrationInput_inputContains2Beverages_invalidInputExceptionThr // Call methods to test try { - parseHydrationInput(invalidInput); + parseHydrationInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -32,7 +32,7 @@ public void parseHydrationInput_inputContains2Volumes_invalidInputExceptionThrow // Call methods to test try { - parseHydrationInput(invalidInput); + parseHydrationInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -48,7 +48,7 @@ public void parseHydrationInput_inputMissingVolume_invalidInputExceptionThrown() // Call methods to test try { - parseHydrationInput(invalidInput); + parseHydrationInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -64,7 +64,7 @@ public void parseHydrationInput_inputWrongOrderDateBeforeVolume_invalidInputExce // Call methods to test try { - parseHydrationInput(invalidInput); + parseHydrationInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have keyed the input in the correct order!\n" + @@ -79,7 +79,7 @@ public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExcept // Call methods to test try { - parseHydrationInput(invalidInput); + parseHydrationInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + @@ -94,7 +94,7 @@ public void parseHydrationInput_inputNegativeValueForVolume_invalidInputExceptio // Call methods to test try { - parseHydrationInput(invalidInput); + parseHydrationInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + @@ -106,7 +106,7 @@ public void parseHydrationInput_inputNegativeValueForVolume_invalidInputExceptio @Test public void parseHydrationInput_missingKeywords_exceptionThrown() { try { - parseHydrationInput("liquids in"); + parseHydrationInput("liquids in", 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -117,7 +117,7 @@ public void parseHydrationInput_missingKeywords_exceptionThrown() { @Test public void parseHydrationInput_incompleteInput_exceptionThrown() { try { - parseHydrationInput("liquids in b/Milo"); + parseHydrationInput("liquids in b/Milo", 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -129,7 +129,7 @@ public void parseHydrationInput_incompleteInput_exceptionThrown() { @Test public void parseHydrationInput_emptyBeverageName_exceptionThrown() { try { - parseHydrationInput("liquids in v/1000"); + parseHydrationInput("liquids in v/1000", 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + @@ -140,7 +140,7 @@ public void parseHydrationInput_emptyBeverageName_exceptionThrown() { @Test public void parseHydrationInput_emptyVolumeDescription_exceptionThrown() { try { - parseHydrationInput("liquids in Milo v/ "); + parseHydrationInput("liquids in Milo v/ ", 0); } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + From a3ff4dec9d8ce29da038fb6a5d3294c677611fdf Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:19:11 +0800 Subject: [PATCH 219/414] Add lastSleepEntryID into sleep classes --- .../lifetrack/sleep/sleeplist/SleepEntry.java | 5 +++-- .../lifetrack/sleep/sleeplist/SleepList.java | 18 +++++++++++++++++- .../lifetrack/system/parser/ParserSleep.java | 5 +++-- .../java/seedu/lifetrack/ParserSleepTest.java | 10 +++++----- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 96e24aa4ba..f6d8415cbe 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -8,6 +8,7 @@ public class SleepEntry extends Entry { private LocalDate date; private double duration; + private int sleepEntryID; /*** * Sleep constructor: date can be empty. If date input is empty, automatically fill with N/A; @@ -15,8 +16,8 @@ public class SleepEntry extends Entry { * @param date * @param duration */ - public SleepEntry (double duration, LocalDate date){ - super("SLEEP", date); + public SleepEntry (int sleepEntryID, double duration, LocalDate date){ + super(sleepEntryID, "SLEEP", date); this.date = date; this.duration = duration; } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 93b9632ad6..c434f53e3c 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -15,6 +15,7 @@ public class SleepList { private ArrayList sleepList; private FileHandler fileHandler; + private int lastSleepEntryID; //constructor for JUnit tests public SleepList() { @@ -26,6 +27,7 @@ public SleepList(String filePath) { try { fileHandler = new FileHandler(filePath); sleepList = fileHandler.getSleepEntriesFromFile(); + this.lastSleepEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { sleepList = new ArrayList<>(); System.out.println(ErrorMessages.getFileNotFoundMessage()); @@ -45,7 +47,7 @@ public Entry getSleep(int index) { public void addSleep(String input) { try { - Entry newSleep = ParserSleep.parseSleepInput(input); + Entry newSleep = ParserSleep.parseSleepInput(input, lastSleepEntryID); sleepList.add(newSleep); updateFile(); SleepListUi.printNewSleepEntry(newSleep); @@ -83,4 +85,18 @@ public void printSleepList() { public int getSize() { return sleepList.size(); } + + private int loadLastEntryID() { + // Load lastEntryID from file, if file doesn't exist or error occurs, return 0 +// try (BufferedReader reader = new BufferedReader(new FileReader("lastEntryID.txt"))) { +// String line = reader.readLine(); +// if (line != null) { +// return Integer.parseInt(line); +// } +// } catch (IOException | NumberFormatException e) { +// // Handle exception +// e.printStackTrace(); +// } + return 0; // Default value if file doesn't exist or error occurs + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index b687525a33..2bfa0c71a2 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -12,7 +12,7 @@ //@@author a-wild-chocloate public class ParserSleep { - public static SleepEntry parseSleepInput(String input) throws InvalidInputException { + public static SleepEntry parseSleepInput(String input, int lastSleepEntryID) throws InvalidInputException { try { String strDate = "N/A"; // Default if no strDate is provided LocalDate date = null; @@ -40,8 +40,9 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept } catch (DateTimeParseException e) { throw new InvalidInputException("Invalid date format"); } + lastSleepEntryID++; //@@author - return new SleepEntry(duration, date); + return new SleepEntry(lastSleepEntryID, duration, date); } catch (NumberFormatException e) { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + "sleep add d/"); diff --git a/src/test/java/seedu/lifetrack/ParserSleepTest.java b/src/test/java/seedu/lifetrack/ParserSleepTest.java index 14127898ff..7162533a6b 100644 --- a/src/test/java/seedu/lifetrack/ParserSleepTest.java +++ b/src/test/java/seedu/lifetrack/ParserSleepTest.java @@ -13,7 +13,7 @@ public void parseSleepInput_inputContains2Duration_invalidInputExceptionThrown() String invalidInput = "sleep add 8.0 9.2"; // Call methods to test try { - parseSleepInput(invalidInput); + parseSleepInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("Invalid date format", e.getMessage()); } @@ -24,7 +24,7 @@ public void parseSleepInput_inputContains2Date_invalidInputExceptionThrown() { String invalidInput = "sleep add d/110324 d/280524"; // Call methods to test try { - parseSleepInput(invalidInput); + parseSleepInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + "sleep add d/", e.getMessage()); @@ -36,7 +36,7 @@ public void parseSleepInput_inputMissingDuration_invalidInputExceptionThrown() { String invalidInput = "sleep add d/110324"; // Call methods to test try { - parseSleepInput(invalidInput); + parseSleepInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + "sleep add d/", e.getMessage()); @@ -50,7 +50,7 @@ public void parseSleepInput_inputNonPositiveValueForDuration_invalidInputExcepti // Call methods to test try { - parseSleepInput(invalidInput); + parseSleepInput(invalidInput, 0); } catch (InvalidInputException e) { assertEquals("\t Please input only positive real number into the sleep duration field!" , e.getMessage()); @@ -60,7 +60,7 @@ public void parseSleepInput_inputNonPositiveValueForDuration_invalidInputExcepti public void parseLiquidInput_missingKeywords_exceptionThrown() { try { - parseSleepInput("sleep add"); + parseSleepInput("sleep add", 0); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + "sleep add d/", e.getMessage()); From 3892a1a65987dc8c9077f71461d9af2cd8ff478a Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 00:32:27 +0800 Subject: [PATCH 220/414] Add method to allow caloriesEntryID to be incremented correctly between sessions --- .../calories/calorielist/CalorieList.java | 2 +- .../lifetrack/system/storage/FileHandler.java | 29 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index f222c69948..6d66d6297c 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -158,7 +158,7 @@ private int loadLastEntryID() { // // Handle exception // e.printStackTrace(); // } - return 0; // Default value if file doesn't exist or error occurs + return FileHandler.getMaxCaloriesID(); // Default value if file doesn't exist or error occurs } diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index d60679a501..8fd9d93ab6 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -19,8 +19,11 @@ public class FileHandler { + //public member for lastEntryID calories + public static int maxCaloriesID = 0; + //general list constants - private static final int LASTENTRYID_INDEX = 0; + private static final int ENTRYID_INDEX = 0; private static final int DATE_INDEX = 1; private static final int DESCRIPTION_INDEX = 2; @@ -49,6 +52,10 @@ public class FileHandler { private String filePath; + public static int getMaxCaloriesID() { + return maxCaloriesID; + } + public FileHandler(String filePath) { this.filePath = filePath; } @@ -87,7 +94,8 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); - int lastEntryID = Integer.parseInt(words[LASTENTRYID_INDEX]); + int entryID = Integer.parseInt(words[ENTRYID_INDEX]); + calculateMaxCaloriesEntry(entryID); LocalDate date = LocalDate.parse(words[DATE_INDEX]); String description = words[DESCRIPTION_INDEX]; int calories = Integer.parseInt(words[CALORIES_INDEX]); @@ -97,16 +105,23 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException int proteins = Integer.parseInt(words[PROTEINS_INDEX]); int fats = Integer.parseInt(words[FATS_INDEX]); Food food = new Food(carbohydrates, proteins, fats); - entries.add(new InputEntry(lastEntryID, description, calories, date, food)); + entries.add(new InputEntry(entryID, description, calories, date, food)); } else if (entryType.equals("C_IN")) { - entries.add(new InputEntry(lastEntryID, description, calories, date)); + entries.add(new InputEntry(entryID, description, calories, date)); } else { - entries.add(new OutputEntry(lastEntryID, description, calories, date)); + entries.add(new OutputEntry(entryID, description, calories, date)); } } return entries; } + // Method calculates the max calories entry ID + public void calculateMaxCaloriesEntry(int entryID) { + if (entryID > maxCaloriesID) { + maxCaloriesID = entryID; + } + } + public ArrayList getHydrationEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); @@ -115,7 +130,7 @@ public ArrayList getHydrationEntriesFromFile() throws FileNotFoundExcepti while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); - int lastHydrationEntryID = Integer.parseInt(words[LASTENTRYID_INDEX]); + int lastHydrationEntryID = Integer.parseInt(words[ENTRYID_INDEX]); LocalDate date = LocalDate.parse(words[DATE_INDEX]); String description = words[DESCRIPTION_INDEX]; int volume = Integer.parseInt(words[VOLUME_INDEX]); @@ -132,7 +147,7 @@ public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { while (s.hasNext()) { line = s.nextLine(); String[] words = line.split(";"); - int lastSleepEntryID = Integer.parseInt(words[LASTENTRYID_INDEX]); + int lastSleepEntryID = Integer.parseInt(words[ENTRYID_INDEX]); LocalDate date = LocalDate.parse(words[DATE_INDEX]); double duration = Double.parseDouble(words[DURATION_INDEX]); entries.add(new SleepEntry(lastSleepEntryID, duration, date)); From e83a6211d71611305200d60668afb76164aebad3 Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 01:33:13 +0800 Subject: [PATCH 221/414] Add function to delete according to entryID instead of arrayList index --- src/main/java/seedu/lifetrack/Entry.java | 5 ++++ .../calories/calorielist/CalorieList.java | 28 +++++++++++++++---- .../seedu/lifetrack/ui/CalorieListUi.java | 4 +++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index 5b921b3147..31df7560b2 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -6,6 +6,7 @@ public abstract class Entry { private String description; private LocalDate date; + private int lastEntryID; public Entry(int lastEntryID, String description, LocalDate date){ @@ -22,6 +23,10 @@ public LocalDate getDate() { return date; } + public int getLastEntryID() { + return lastEntryID; + } + public String toString() { return String.format("\t EntryID: " + lastEntryID + ", Date: " + date + ", Description: " + description); } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 6d66d6297c..f6047f8dda 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -18,6 +18,9 @@ public class CalorieList { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); private final int SIZE_OF_DELETE = 16; + + //constant for finding entry index from entryID + private final int NO_INDEX_FOUND = -1; private ArrayList calorieArrayList; private FileHandler fileHandler; private int lastEntryID; @@ -70,11 +73,16 @@ public void deleteEntry(String line) { assert (line.startsWith("calories delete") ) : "ensures that input is correct"; try { - int index = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); - Entry toDelete = calorieArrayList.get(index-1); - calorieArrayList.remove((index-1)); // transfer to scope 0 to size-1 - updateFile(); - CalorieListUi.successfulDeletedMessage(toDelete); + int entryID = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); + int index = getIndexFromEntryID(entryID); + if (index == NO_INDEX_FOUND) { + CalorieListUi.unsuccessfulDeletedMessage(entryID); + } else { + Entry toDelete = calorieArrayList.get(index); + calorieArrayList.remove((index)); + CalorieListUi.successfulDeletedMessage(toDelete); + updateFile(); + } } catch (IndexOutOfBoundsException e) { System.out.println(CalorieListUi.deleteLogIndexMessage()); } catch (NumberFormatException e) { @@ -82,6 +90,16 @@ public void deleteEntry(String line) { } } + //method that returns index of entry in arrayList according to lastEntryID + public int getIndexFromEntryID(int lastEntryID) { + for (int i = 0; i < calorieArrayList.size(); i++) { + if (calorieArrayList.get(i).getLastEntryID() == lastEntryID) { + return i; + } + } + return NO_INDEX_FOUND; + } + /** * Parses a string input representing calorie intake and adds it to the calorie list. * diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 809f44005d..9ce968db12 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -9,6 +9,10 @@ public static void successfulDeletedMessage(Entry toDelete) { System.out.println("\t The following calorie record has been successfully deleted!"); System.out.println("\t " + toDelete.toString()); } + public static void unsuccessfulDeletedMessage(int entryID) { + System.out.println("\t The following calorie record corresponding to entry ID " + entryID + " could " + + "not be found"); + } public static void emptyListMessage() { System.out.println("\t Your caloric list is empty. Add new entries to populate your list :)"); From bf17392f8bdaa4dca84740818c52f379b794a97a Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 01:48:43 +0800 Subject: [PATCH 222/414] Fix checkstyle errors --- .../lifetrack/calories/calorielist/CalorieList.java | 11 +---------- .../hydration/hydrationlist/HydrationList.java | 10 ---------- .../seedu/lifetrack/sleep/sleeplist/SleepList.java | 10 ---------- .../seedu/lifetrack/system/parser/ParserCalories.java | 3 ++- .../lifetrack/system/parser/ParserHydration.java | 3 ++- .../seedu/lifetrack/system/storage/FileHandler.java | 8 ++++---- 6 files changed, 9 insertions(+), 36 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index f6047f8dda..84bb2b14a1 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -165,17 +165,8 @@ public int getCaloriesConsumed() { return totalCalories; } + //method that loads LastEntryID from txt file private int loadLastEntryID() { - // Load lastEntryID from file, if file doesn't exist or error occurs, return 0 -// try (BufferedReader reader = new BufferedReader(new FileReader("lastEntryID.txt"))) { -// String line = reader.readLine(); -// if (line != null) { -// return Integer.parseInt(line); -// } -// } catch (IOException | NumberFormatException e) { -// // Handle exception -// e.printStackTrace(); -// } return FileHandler.getMaxCaloriesID(); // Default value if file doesn't exist or error occurs } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 809154f881..22c0bb50fb 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -139,16 +139,6 @@ public int getSize() { } private int loadLastEntryID() { - // Load lastEntryID from file, if file doesn't exist or error occurs, return 0 -// try (BufferedReader reader = new BufferedReader(new FileReader("lastEntryID.txt"))) { -// String line = reader.readLine(); -// if (line != null) { -// return Integer.parseInt(line); -// } -// } catch (IOException | NumberFormatException e) { -// // Handle exception -// e.printStackTrace(); -// } return 0; // Default value if file doesn't exist or error occurs } } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index c434f53e3c..4d2a45f676 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -87,16 +87,6 @@ public int getSize() { } private int loadLastEntryID() { - // Load lastEntryID from file, if file doesn't exist or error occurs, return 0 -// try (BufferedReader reader = new BufferedReader(new FileReader("lastEntryID.txt"))) { -// String line = reader.readLine(); -// if (line != null) { -// return Integer.parseInt(line); -// } -// } catch (IOException | NumberFormatException e) { -// // Handle exception -// e.printStackTrace(); -// } return 0; // Default value if file doesn't exist or error occurs } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 2ae146cff3..154f215288 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -287,7 +287,8 @@ private static Entry makeNewInputEntry(int lastEntryID, String description, int * @param foodMacros an array containing food macros (carbs, proteins, fats) * @return a new InputEntry object with food macros */ - private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date, int[] foodMacros) { + private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date, + int[] foodMacros) { Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index 4dd58e9478..cfbf11c0c4 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -79,7 +79,8 @@ public static Entry parseHydrationInput(String input, int lastHydrationEntryID) * @param date the date of the hydration entry * @return a new HydrationEntry object with the specified attributes */ - private static HydrationEntry makeNewInputEntry(int lastHydrationEntryID, String description, int volume, LocalDate date) { + private static HydrationEntry makeNewInputEntry(int lastHydrationEntryID, String description, int volume, + LocalDate date) { return new HydrationEntry(lastHydrationEntryID, description, volume, date); } diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 8fd9d93ab6..08456b3b35 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -52,14 +52,14 @@ public class FileHandler { private String filePath; - public static int getMaxCaloriesID() { - return maxCaloriesID; - } - public FileHandler(String filePath) { this.filePath = filePath; } + public static int getMaxCaloriesID() { + return maxCaloriesID; + } + private void writeToFile(String textToAdd) throws IOException { FileWriter fw = new FileWriter(filePath); fw.write(textToAdd); From 27eb1d5bbc843870ab22f2abf74f86a7b3d61b08 Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 01:49:03 +0800 Subject: [PATCH 223/414] Fix JUnit test error for CalorieListTest and HydrationListTest --- .../java/seedu/lifetrack/CalorieListTest.java | 12 +++++----- .../seedu/lifetrack/HydrationListTest.java | 24 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index b41f3e1eb0..8847dcd0ac 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -96,7 +96,7 @@ public void testPrintCalorieListNonEmpty() { String expectedOutput = addedEntryHeader + lineSeparator + "\t " + calorieList.getEntry(0).toString() + lineSeparator + "\t Your Caloric List:" + lineSeparator + - "\t 1. \t Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; + "\t 1. \t EntryID: 1, Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -121,15 +121,15 @@ public void testPrintCalorieListMultipleEntries() { } expectedOutput.append("\t Your Caloric List:") .append(lineSeparator) - .append("\t 1. \t Date: 2024-03-14, Description: Run, Calories: 200") + .append("\t 1. \t EntryID: 1, Date: 2024-03-14, Description: Run, Calories: 200") .append(lineSeparator) - .append("\t 2. \t Date: 2024-03-14, Description: Walk, Calories: 150") + .append("\t 2. \t EntryID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") .append(lineSeparator) - .append("\t 3. \t Date: 2024-03-14, Description: Eat, Calories: 500") + .append("\t 3. \t EntryID: 3, Date: 2024-03-14, Description: Eat, Calories: 500") .append(lineSeparator) - .append("\t 4. \t Date: 2024-03-14, Description: Run, Calories: 250") + .append("\t 4. \t EntryID: 4, Date: 2024-03-14, Description: Run, Calories: 250") .append(lineSeparator) - .append("\t 5. \t Date: 2024-03-14, Description: Eat, Calories: 300") + .append("\t 5. \t EntryID: 5, Date: 2024-03-14, Description: Eat, Calories: 300") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index 4a26794ec1..f30d2078d3 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -54,9 +54,9 @@ public void testPrintHydrationListNonEmpty() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator; + "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -72,15 +72,15 @@ public void testPrintHydrationListMultipleEntries() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t \t EntryID: 1, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + + "\t \t EntryID: 1, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + - "\t 2. \t Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + - "\t 3. \t Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; + "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t 2. \t EntryID: 1, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t 3. \t EntryID: 1, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, hydrationList.getSize()); } @@ -121,12 +121,12 @@ public void testPrintHydrationListWithMultipleEntries() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t \t EntryID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + + "\t \t EntryID: 1, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + - "\t 2. \t Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; + "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t 2. \t EntryID: 1, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(2, hydrationList.getSize()); } From 1db6191ea097b54f7989bdff91fd7636218d7e4a Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 15:20:52 +0800 Subject: [PATCH 224/414] Add feature to print calories list according to calorie outflow and inflow --- .../calories/calorielist/CalorieList.java | 29 +++++++++++++++++-- .../seedu/lifetrack/ui/CalorieListUi.java | 10 +++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 84bb2b14a1..3ccaebab7f 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -135,13 +135,36 @@ public void printCalorieList() { CalorieListUi.emptyListMessage(); } else { CalorieListUi.calorieListHeader(); - for (int i = 0; i < calorieArrayList.size(); i++) { - Entry entry = calorieArrayList.get(i); - System.out.println("\t " + (i + 1) + ". " + entry); + printCalorieInflow(); + printCalorieOutflow(); + } + } + + public void printCalorieInflow() { + CalorieListUi.inputCalorieListHeader(); + int serialNumber = 1; + for (Entry value : calorieArrayList) { + if (value instanceof InputEntry) { + Entry entry = value; + System.out.println("\t " + serialNumber + ". " + entry); + serialNumber++; + } + } + } + + public void printCalorieOutflow() { + CalorieListUi.outputCalorieListHeader(); + int serialNumber = 1; + for (Entry value : calorieArrayList) { + if (value instanceof OutputEntry) { + Entry entry = value; + System.out.println("\t " + serialNumber + ". " + entry); + serialNumber++; } } } + /** * Returns the size of the list of calorie entries. * diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 9ce968db12..b552deb00d 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -30,6 +30,16 @@ public static void calorieListHeader() { System.out.println("\t Your Caloric List:"); } + public static void outputCalorieListHeader() { + System.out.println(""); + System.out.println("\t Your Caloric outflow list:"); + } + + public static void inputCalorieListHeader() { + System.out.println(""); + System.out.println("\t Your Caloric Inflow List:"); + } + public static void printNewCalorieEntry(Entry newEntry) { System.out.println("\t The following entry has been added to your caloric list!"); System.out.println("\t " + newEntry.toString()); From 74c9c418ba0f7af424aecd5e00a64d960024a0c2 Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 15:21:41 +0800 Subject: [PATCH 225/414] Edit CalkorieListTest --- .../java/seedu/lifetrack/CalorieListTest.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 8847dcd0ac..cda5da0f99 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -90,13 +90,15 @@ public void testPrintCalorieListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in Run c/200 d/2024-03-14"); + calorieList.addEntry("calories in burger king c/200 d/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); String expectedOutput = addedEntryHeader + lineSeparator + "\t " + calorieList.getEntry(0).toString() + lineSeparator + - "\t Your Caloric List:" + lineSeparator + - "\t 1. \t EntryID: 1, Date: 2024-03-14, Description: Run, Calories: 200" + lineSeparator; + "\t Your Caloric List:" + lineSeparator + lineSeparator + + "\t Your Caloric Inflow List:" + lineSeparator + + "\t 1. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200" + lineSeparator + + lineSeparator + "\t Your Caloric outflow list:" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -106,11 +108,11 @@ public void testPrintCalorieListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); CalorieList calorieList = new CalorieList(); - calorieList.addEntry("calories in Run c/200 d/2024-03-14"); + calorieList.addEntry("calories in burger king c/200 d/2024-03-14"); calorieList.addEntry("calories out Walk c/150 d/2024-03-14"); - calorieList.addEntry("calories in Eat c/500 d/2024-03-14"); + calorieList.addEntry("calories in acai c/500 d/2024-03-14"); calorieList.addEntry("calories out Run c/250 d/2024-03-14"); - calorieList.addEntry("calories in Eat c/300 d/2024-03-14"); + calorieList.addEntry("calories in commhall dinner c/300 d/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); StringBuilder expectedOutput = new StringBuilder(); @@ -121,15 +123,21 @@ public void testPrintCalorieListMultipleEntries() { } expectedOutput.append("\t Your Caloric List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 1, Date: 2024-03-14, Description: Run, Calories: 200") .append(lineSeparator) - .append("\t 2. \t EntryID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") + .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 3. \t EntryID: 3, Date: 2024-03-14, Description: Eat, Calories: 500") + .append("\t 1. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") .append(lineSeparator) - .append("\t 4. \t EntryID: 4, Date: 2024-03-14, Description: Run, Calories: 250") + .append("\t 2. \t EntryID: 3, Date: 2024-03-14, Description: acai, Calories: 500") .append(lineSeparator) - .append("\t 5. \t EntryID: 5, Date: 2024-03-14, Description: Eat, Calories: 300") + .append("\t 3. \t EntryID: 5, Date: 2024-03-14, Description: commhall dinner, Calories: 300") + .append(lineSeparator) + .append(lineSeparator) + .append("\t Your Caloric outflow list:") + .append(lineSeparator) + .append("\t 1. \t EntryID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") + .append(lineSeparator) + .append("\t 2. \t EntryID: 4, Date: 2024-03-14, Description: Run, Calories: 250") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); From 12f3a2f69ffd892745acaf19fbe5ad8daef6ffc3 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:10:21 +0800 Subject: [PATCH 226/414] Add support for entryID for hydration --- .../calories/calorielist/CalorieList.java | 2 -- .../hydrationlist/HydrationList.java | 32 ++++++++++++++----- .../InvalidInputExceptionMessage.java | 2 +- .../lifetrack/system/storage/FileHandler.java | 12 ++++++- .../seedu/lifetrack/ui/HydrationListUI.java | 4 +++ 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 84bb2b14a1..04cf5d841a 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -169,6 +169,4 @@ public int getCaloriesConsumed() { private int loadLastEntryID() { return FileHandler.getMaxCaloriesID(); // Default value if file doesn't exist or error occurs } - - } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 22c0bb50fb..dc8a9ce9a3 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -21,7 +21,9 @@ public class HydrationList { private static Logger logr = Logger.getLogger(CalorieList.class.getName()); - private final int DELETE_PADDING = 16; + private final int SIZE_OF_DELETE = 16; + + private final int NO_INDEX_FOUND = -1; private ArrayList hydrationArrayList; private FileHandler fileHandler; private int lastHydrationEntryID; @@ -70,17 +72,30 @@ public Entry getEntry(int index) { */ public void deleteEntry(String line) { try { - int index = Integer.parseInt(line.substring(DELETE_PADDING).trim()); - Entry toDelete = hydrationArrayList.get(index - 1); - hydrationArrayList.remove(index - 1); - updateFile(); - HydrationListUI.successfulDeletedMessage(toDelete); + int entryID = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); + int index = getIndexFromEntryID(entryID); + if (index == NO_INDEX_FOUND) { + HydrationListUI.unsuccessfulDeletedMessage(entryID); + } else { + Entry toDelete = hydrationArrayList.get(index); + hydrationArrayList.remove((index)); + HydrationListUI.successfulDeletedMessage(toDelete); + updateFile(); + } } catch (IndexOutOfBoundsException e) { System.out.println(HydrationListUI.deleteLogIndexMessage()); } catch (NumberFormatException e) { System.out.println(HydrationListUI.deleteLogNumberMessage()); } } + public int getIndexFromEntryID(int lastEntryID) { + for (int i = 0; i < hydrationArrayList.size(); i++) { + if (hydrationArrayList.get(i).getLastEntryID() == lastEntryID) { + return i; + } + } + return NO_INDEX_FOUND; + } /** * Adds a new liquid entry based on the provided input. @@ -88,7 +103,8 @@ public void deleteEntry(String line) { * @param input the input string containing liquid entry information */ public void addEntry(String input) { - assert (input.startsWith("hydration add")) : "ensures that input is correct"; + assert (input.startsWith("hydration in")) : "ensures that input is correct"; + logr.setLevel(Level.WARNING); try { Entry newEntry = ParserHydration.parseHydrationInput(input, lastHydrationEntryID); hydrationArrayList.add(newEntry); @@ -139,6 +155,6 @@ public int getSize() { } private int loadLastEntryID() { - return 0; // Default value if file doesn't exist or error occurs + return FileHandler.getMaxHydrationID(); } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index adf4f61aec..c1d28a0049 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -11,7 +11,7 @@ public class InvalidInputExceptionMessage { private static final String CALORIES_OUT_INPUT = "\t Example input: calories out DESCRIPTION " + "c/INTEGER_CALORIES d/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; - private static final String HYDRATION_IN_INPUT = "\t Example input: hydration add Milo v/1000 d/2024-04-19" ; + private static final String HYDRATION_IN_INPUT = "\t Example input: hydration in Milo v/1000 d/2024-04-19" ; private static final String USER_SETUP_INPUT = "\t Example input: user set up Tom h/170 w/80 a/25 s/male e/4 g/3"; //calories list related methods diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 08456b3b35..6541cbc067 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -21,7 +21,7 @@ public class FileHandler { //public member for lastEntryID calories public static int maxCaloriesID = 0; - + public static int maxHydrationID = 0; //general list constants private static final int ENTRYID_INDEX = 0; private static final int DATE_INDEX = 1; @@ -60,6 +60,10 @@ public static int getMaxCaloriesID() { return maxCaloriesID; } + public static int getMaxHydrationID() { + return maxHydrationID; + } + private void writeToFile(String textToAdd) throws IOException { FileWriter fw = new FileWriter(filePath); fw.write(textToAdd); @@ -122,6 +126,12 @@ public void calculateMaxCaloriesEntry(int entryID) { } } + public void calculateMaxHydrationEntry(int entryID) { + if (entryID > maxHydrationID) { + maxHydrationID = entryID; + } + } + public ArrayList getHydrationEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); diff --git a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java index 9e3664b45f..a763052ae4 100644 --- a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java +++ b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java @@ -8,6 +8,10 @@ public static void successfulDeletedMessage(Entry toDelete) { System.out.println("\t The following hydration record has been successfully deleted!"); System.out.println("\t " + toDelete.toString()); } + public static void unsuccessfulDeletedMessage(int entryID) { + System.out.println("\t The following hydration record corresponding to entry ID " + entryID + " could " + + "not be found"); + } public static void emptyListMessage() { System.out.println("\t Your hydration list is empty. Add new entries to populate your list :)"); From 0da08e959ca22e1edf035c486d48880ebbc4e4da Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:17:33 +0800 Subject: [PATCH 227/414] Fix junit tests for hydration --- .../hydrationlist/HydrationList.java | 1 + .../seedu/lifetrack/HydrationListTest.java | 32 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index dc8a9ce9a3..39538c1e83 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -110,6 +110,7 @@ public void addEntry(String input) { hydrationArrayList.add(newEntry); updateFile(); HydrationListUI.printNewHydrationEntry(newEntry); + lastHydrationEntryID ++; } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); } diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index f30d2078d3..ca858eb6a6 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -15,7 +15,7 @@ public class HydrationListTest { @Test public void testDeleteHydrationValidIndex() { HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); + hydrationList.addEntry("hydration in Milo v/200 d/2024-02-22"); int initialSize = hydrationList.getSize(); hydrationList.deleteEntry("hydration delete 1"); assertEquals(initialSize - 1, hydrationList.getSize()); @@ -24,7 +24,7 @@ public void testDeleteHydrationValidIndex() { @Test public void testDeleteHydrationInvalidIndex() { HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); + hydrationList.addEntry("hydration in Milo v/200 d/2024-02-22"); int initialSize = hydrationList.getSize(); hydrationList.deleteEntry("hydration delete 2"); // Index out of bounds hydrationList.deleteEntry("hydration delete -1"); @@ -50,7 +50,7 @@ public void testPrintHydrationListNonEmpty() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); + hydrationList.addEntry("hydration in Milo v/200 d/2024-02-22"); hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + @@ -66,21 +66,21 @@ public void testPrintHydrationListMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Milo v/200 d/2024-02-22"); - hydrationList.addEntry("hydration add Water v/300 d/2024-02-22"); - hydrationList.addEntry("hydration add Juice v/150 d/2024-02-22"); + hydrationList.addEntry("hydration in Milo v/200 d/2024-02-22"); + hydrationList.addEntry("hydration in Water v/300 d/2024-02-22"); + hydrationList.addEntry("hydration in Juice v/150 d/2024-02-22"); hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + "\t \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 1, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t \t EntryID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 1, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + + "\t \t EntryID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + "\t Your Hydration List:" + lineSeparator + "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + - "\t 2. \t EntryID: 1, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + - "\t 3. \t EntryID: 1, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; + "\t 2. \t EntryID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t 3. \t EntryID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, hydrationList.getSize()); } @@ -88,7 +88,7 @@ public void testPrintHydrationListMultipleEntries() { public void testAddEntry() { HydrationList hydrationList = new HydrationList(); int initialSize = hydrationList.getSize(); - hydrationList.addEntry("hydration add Water v/250 d/2024-02-23"); + hydrationList.addEntry("hydration in Water v/250 d/2024-02-23"); assertEquals(initialSize + 1, hydrationList.getSize()); } @@ -103,7 +103,7 @@ public void testDeleteEntryFromEmptyList() { @Test public void testDeleteEntryWithInvalidFormat() { HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Water v/250 d/2024-02-23"); + hydrationList.addEntry("hydration in Water v/250 d/2024-02-23"); int initialSize = hydrationList.getSize(); // Try to delete with invalid format, should not affect the list hydrationList.deleteEntry("delete 1"); @@ -116,17 +116,17 @@ public void testPrintHydrationListWithMultipleEntries() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration add Coffee v/150 d/2024-02-22"); - hydrationList.addEntry("hydration add Tea v/200 d/2024-02-22"); + hydrationList.addEntry("hydration in Coffee v/150 d/2024-02-22"); + hydrationList.addEntry("hydration in Tea v/200 d/2024-02-22"); hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + "\t \t EntryID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 1, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + + "\t \t EntryID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + - "\t 2. \t EntryID: 1, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; + "\t 2. \t EntryID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(2, hydrationList.getSize()); } From d1819bf0774d268c360691bcdc35d743e2316270 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:04:56 +0800 Subject: [PATCH 228/414] Junit --- .../seedu/lifetrack/ParserHydrationTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/java/seedu/lifetrack/ParserHydrationTest.java b/src/test/java/seedu/lifetrack/ParserHydrationTest.java index 91f7d2865e..9672a9007c 100644 --- a/src/test/java/seedu/lifetrack/ParserHydrationTest.java +++ b/src/test/java/seedu/lifetrack/ParserHydrationTest.java @@ -20,7 +20,7 @@ public void parseHydrationInput_inputContains2Beverages_invalidInputExceptionThr } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -36,7 +36,7 @@ public void parseHydrationInput_inputContains2Volumes_invalidInputExceptionThrow } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -52,7 +52,7 @@ public void parseHydrationInput_inputMissingVolume_invalidInputExceptionThrown() } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -68,14 +68,14 @@ public void parseHydrationInput_inputWrongOrderDateBeforeVolume_invalidInputExce } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have keyed the input in the correct order!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @Test public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExceptionThrown() { // setup test - String invalidInput = "hydration add Milo v/##s100 d/221024"; + String invalidInput = "hydration in Milo v/##s100 d/221024"; // Call methods to test try { @@ -83,7 +83,7 @@ public void parseHydrationInput_inputNonIntegerValueForVolume_invalidInputExcept } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -98,7 +98,7 @@ public void parseHydrationInput_inputNegativeValueForVolume_invalidInputExceptio } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that positive integer value is keyed in for volume!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -110,7 +110,7 @@ public void parseHydrationInput_missingKeywords_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -121,7 +121,7 @@ public void parseHydrationInput_incompleteInput_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -133,7 +133,7 @@ public void parseHydrationInput_emptyBeverageName_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } @@ -144,7 +144,7 @@ public void parseHydrationInput_emptyVolumeDescription_exceptionThrown() { } catch (InvalidInputException e) { assertEquals("\t Invalid input!\n" + "\t Please ensure that you have entered all keywords!\n" + - "\t Example input: hydration add Milo v/1000 d/2024-04-19", e.getMessage()); + "\t Example input: hydration in Milo v/1000 d/2024-04-19", e.getMessage()); } } From 32650666828690ef93d66c0916bfc6381c0ce5b2 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:20:34 +0800 Subject: [PATCH 229/414] Fix UG --- docs/UserGuide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 99810fe7c2..86b86f8328 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -131,9 +131,9 @@ Adds a hydration record into the hydration tracker. **Format:** `hydration in DESCRIPTION v/VOLUME d/DATE` -* The volume must be a positive integer 1, 2, 3, …, measured in milliliters. -* The time indicated should follow the 24-hour system. -* The date provided should be of the form YYYY-MM-DD. +* The `DESCRIPTION` refers to the food that the person consumed. +* The `VOLUME` must be a positive integer 1, 2, 3, …, measured in milliliters. +* The `DATE` provided should be of the form YYYY-MM-DD, such as 2024-03-04. **Examples:** * `hydration in Milo v/1000 d/2022-03-25` From 67976dc0bc66c9ae2c68443a3ebb3696031b9b31 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 9 Apr 2024 20:42:04 +0800 Subject: [PATCH 230/414] fixed sleep add bugs added authority --- .../calories/calorielist/CalorieList.java | 5 +- .../hydrationlist/HydrationList.java | 2 + .../lifetrack/sleep/sleeplist/SleepEntry.java | 2 + .../lifetrack/sleep/sleeplist/SleepList.java | 5 +- .../system/exceptions/ErrorMessages.java | 8 ++ .../exceptions/InvalidInputException.java | 3 - .../InvalidInputExceptionMessage.java | 6 + .../lifetrack/system/parser/ParserSleep.java | 109 ++++++++++++------ .../seedu/lifetrack/ui/CalorieListUi.java | 4 +- .../java/seedu/lifetrack/ui/SleepListUi.java | 3 + src/main/java/seedu/lifetrack/ui/Ui.java | 2 + 11 files changed, 104 insertions(+), 45 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 648cccf1a9..e6d94d036f 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -62,6 +62,7 @@ public Entry getEntry(int index) { * * @param line the string containing the index of calorie record user want to delete */ + //@@author a-wild-chocolate public void deleteEntry(String line) { assert (line.startsWith("calories delete") ) : "ensures that input is correct"; @@ -77,6 +78,7 @@ public void deleteEntry(String line) { System.out.println(CalorieListUi.deleteLogNumberMessage()); } } + //@@author /** * Parses a string input representing calorie intake and adds it to the calorie list. @@ -91,7 +93,7 @@ public void deleteEntry(String line) { */ public void addEntry(String input) { assert (input.startsWith("calories in") || input.startsWith("calories out")) : "ensures that input is correct"; - logr.setLevel(Level.WARNING); + logr.setLevel(Level.SEVERE); try { Entry newEntry = ParserCalories.parseCaloriesInput(input); calorieArrayList.add(newEntry); @@ -99,6 +101,7 @@ public void addEntry(String input) { CalorieListUi.printNewCalorieEntry(newEntry); } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); + System.out.println(e.getMessage()); } } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index c1f0cb385e..33d9ecebc4 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -87,12 +87,14 @@ public void deleteEntry(String line) { */ public void addEntry(String input) { assert (input.startsWith("hydration add")) : "ensures that input is correct"; + logr.setLevel(Level.SEVERE); try { Entry newEntry = ParserHydration.parseHydrationInput(input); hydrationArrayList.add(newEntry); updateFile(); HydrationListUI.printNewHydrationEntry(newEntry); } catch (InvalidInputException e) { + System.out.println(e.getMessage()); logr.log(Level.WARNING, e.getMessage(), e); } } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 96e24aa4ba..732e5e072a 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -1,3 +1,4 @@ +//@@author a-wild-chocolate package seedu.lifetrack.sleep.sleeplist; import seedu.lifetrack.Entry; @@ -38,3 +39,4 @@ public String toFileFriendlyString() { return String.format(super.toFileFriendlyString() + ";" + duration); } } +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 93b9632ad6..2d491ce0be 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -1,3 +1,4 @@ +//@@author a-wild-chocolate package seedu.lifetrack.sleep.sleeplist; import seedu.lifetrack.Entry; @@ -50,7 +51,7 @@ public void addSleep(String input) { updateFile(); SleepListUi.printNewSleepEntry(newSleep); } catch (InvalidInputException e) { - System.out.println(getIncorrectSleepInputMessage()); + System.out.println(e.getMessage()); } } @@ -83,4 +84,6 @@ public void printSleepList() { public int getSize() { return sleepList.size(); } + } +//@@author diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index a0b1e6f218..8749910565 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -80,4 +80,12 @@ public static String getAgeOutOfRangeMessage(){ public static String getInvalidAgeNumberMessage(){ return "\t Please enter your age as an integer!"; } + public static String getTooLongSleepDurationMessage() + { + return "\t Please enter a sleep duration less than 24 hours."; + } + public static String getInvalidSleepDateMessage() + { + return "\t Please enter a valid date!"; + } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index cda9303d4f..be08f56bc9 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -6,7 +6,6 @@ public class InvalidInputException extends Exception { - private static Logger logr = Logger.getLogger(CalorieList.class.getName()); public final String heythere = ""; /** @@ -15,8 +14,6 @@ public class InvalidInputException extends Exception { */ public InvalidInputException(){ super("\t Please ensure that you have keyed in the correct format!"); - logr.setLevel(Level.SEVERE); - logr.log(Level.WARNING,"\t Please ensure that you have keyed in the correct format!"); } /** diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index adf4f61aec..9982d453c6 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -12,6 +12,7 @@ public class InvalidInputExceptionMessage { "c/INTEGER_CALORIES d/DATE"; private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; private static final String HYDRATION_IN_INPUT = "\t Example input: hydration add Milo v/1000 d/2024-04-19" ; + private static final String SLEEP_IN_INPUT = "\t Example input: sleep add 7.5 d/2024-03-11" ; private static final String USER_SETUP_INPUT = "\t Example input: user set up Tom h/170 w/80 a/25 s/male e/4 g/3"; //calories list related methods @@ -51,6 +52,11 @@ public static String getHydrationMissingKeywordMessage() { return HEADER + message + HYDRATION_IN_INPUT; } + public static String getSleepMissingKeywordMessage() { + String message = "\t Please ensure that you have entered all keywords!\n"; + return HEADER + message + SLEEP_IN_INPUT; + } + public static String getHydrationIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed the input in the correct order!\n"; return HEADER + message + HYDRATION_IN_INPUT; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index b687525a33..20001de3d4 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -1,57 +1,90 @@ +//@@author a-wild-chocolate package seedu.lifetrack.system.parser; import seedu.lifetrack.system.exceptions.InvalidInputException; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; - import seedu.lifetrack.sleep.sleeplist.SleepEntry; import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; -//@@author a-wild-chocloate +import static seedu.lifetrack.system.exceptions.ErrorMessages.*; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; + public class ParserSleep { + private static final int DURATION_IDX = 0; + private static final int DATE_IDX = 1; + private static final int STRING_PARTS_LEN = 2; + private static final String SLEEP_HEADER = "sleep add "; + private static final String DATE_ICON = "d/"; + public static SleepEntry parseSleepInput(String input) throws InvalidInputException { + LocalDate date = null; + double duration = 0; + int dateIndex = input.indexOf(DATE_ICON); + + checkKeywordsExist(dateIndex); + assert dateIndex != -1 : "The d/ keyword should exist!"; + + String[] parts = input.substring(SLEEP_HEADER.length()).split(DATE_ICON); + checkValidFormat(parts.length); + + String strDate = parts[DATE_IDX].trim(); + String strDuration = parts[DURATION_IDX].trim(); + System.out.println(strDate); + + duration = parseDuration(strDuration); + date = parseDate(strDate); + + SleepEntry newSleep = new SleepEntry(duration, date); + + + return newSleep; + } + + private static double parseDuration(String durationStr) throws InvalidInputException + { + double duration; + try { - String strDate = "N/A"; // Default if no strDate is provided - LocalDate date = null; - double duration = 0; - String[] parts = input.split(" "); - for (String part : parts) { - if (part.matches("^-?\\d+(\\.\\d+)?$")) { - duration = Double.parseDouble(part); - if (duration < 0) { - throw new InvalidInputException(getIncorrectSleepInputMessage()); - } - } else if (part.startsWith("d/")) { - strDate = part.substring(2); - } - } - if (duration == 0) { - throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + - "sleep add d/"); - } - //@author - //@@author rexyyong - // Parse str date to date of type LocalDate - try { - date = getLocalDateFromInput(strDate); - } catch (DateTimeParseException e) { - throw new InvalidInputException("Invalid date format"); - } - //@@author - return new SleepEntry(duration, date); + duration = Double.parseDouble(durationStr); } catch (NumberFormatException e) { - throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + - "sleep add d/"); + throw new InvalidInputException(getIncorrectSleepInputMessage()); + } + + if (duration <= 0) { + throw new InvalidInputException(getIncorrectSleepInputMessage()); + } + else if (duration >= 24) { + throw new InvalidInputException(getTooLongSleepDurationMessage()); + } + + return duration; +} + + + private static LocalDate parseDate(String strDate) throws InvalidInputException { + try { + return LocalDate.parse(strDate, DateTimeFormatter.ISO_LOCAL_DATE); + } catch (DateTimeParseException e) { + throw new InvalidInputException("\tInvalid date! Please enter a valid date in format YYYY-MM-DD."); + } + } + private static void checkKeywordsExist(int dateIndex) throws InvalidInputException { + if (dateIndex == -1 ) { + throw new InvalidInputException(getSleepMissingKeywordMessage()); } } - //@@author rexyyong - public static LocalDate getLocalDateFromInput(String strDate) throws DateTimeParseException { - LocalDate date = LocalDate.parse(strDate); - return date; + private static void checkValidFormat(int length) throws InvalidInputException { + if (length != STRING_PARTS_LEN) { + throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + + "sleep add d/"); + } } - //@@author + + } +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 7c36a6cd2c..b6b4bedf54 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -12,7 +12,7 @@ public static void successfulDeletedMessage(Entry toDelete) { public static void emptyListMessage() { System.out.println("\t Your caloric list is empty. Add new entries to populate your list :)"); } - + //@@author a-wild-chocolate public static String deleteLogIndexMessage() { return "\t Sorry, this index is invalid. Please enter a positive integer within the size of the list."; } @@ -20,7 +20,7 @@ public static String deleteLogIndexMessage() { public static String deleteLogNumberMessage() { return "\t Please enter a valid index!"; } - + //@@author public static void calorieListHeader() { System.out.println("\t Your Caloric List:"); } diff --git a/src/main/java/seedu/lifetrack/ui/SleepListUi.java b/src/main/java/seedu/lifetrack/ui/SleepListUi.java index 9e4a0551e8..b6e83337b1 100644 --- a/src/main/java/seedu/lifetrack/ui/SleepListUi.java +++ b/src/main/java/seedu/lifetrack/ui/SleepListUi.java @@ -1,3 +1,4 @@ +//@@author a-wild-chocolate package seedu.lifetrack.ui; import seedu.lifetrack.Entry; @@ -24,9 +25,11 @@ public static String deleteLogNumberMessage() { public static void sleepListHeader() { System.out.println("\t Your Sleep List:"); } + //@@author public static void printNewSleepEntry(Entry newEntry) { System.out.println("\t The following entry has been added to your sleep list!"); System.out.println("\t " + newEntry.toString()); } } +//@@author diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 3830a9f83b..fe939e7512 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -76,6 +76,7 @@ public static void handleHydrationInput(String line, HydrationList hydrationList handleUnknownInput(); } } + //@@author a-wild-chocolate public static void handleSleepInput(String line, SleepList sleepList) { assert !line.startsWith("bye") : "exit the app"; if (line.startsWith("sleep add")) { @@ -88,6 +89,7 @@ public static void handleSleepInput(String line, SleepList sleepList) { handleUnknownInput(); } } + //@@author public static void handleUserInput(String line, CalorieList calorieList, HydrationList hydrationList, User user ,SleepList sleepList) { From 5dc8fb661ce3fcdab3646bbc62d4f1bb6f5bd69d Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 9 Apr 2024 20:47:57 +0800 Subject: [PATCH 231/414] debug gradle errors --- .../lifetrack/sleep/sleeplist/SleepEntry.java | 2 +- .../lifetrack/sleep/sleeplist/SleepList.java | 1 - .../system/exceptions/ErrorMessages.java | 6 ++---- .../exceptions/InvalidInputException.java | 3 --- .../lifetrack/system/parser/ParserSleep.java | 21 ++++++++----------- 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 732e5e072a..68d8d72847 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -39,4 +39,4 @@ public String toFileFriendlyString() { return String.format(super.toFileFriendlyString() + ";" + duration); } } -//@@author \ No newline at end of file +//@@author diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 2d491ce0be..c53284eb1c 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -10,7 +10,6 @@ import java.io.FileNotFoundException; import java.util.ArrayList; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; public class SleepList { diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index 8749910565..fd6e379368 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -80,12 +80,10 @@ public static String getAgeOutOfRangeMessage(){ public static String getInvalidAgeNumberMessage(){ return "\t Please enter your age as an integer!"; } - public static String getTooLongSleepDurationMessage() - { + public static String getTooLongSleepDurationMessage() { return "\t Please enter a sleep duration less than 24 hours."; } - public static String getInvalidSleepDateMessage() - { + public static String getInvalidSleepDateMessage() { return "\t Please enter a valid date!"; } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index be08f56bc9..ef2335b42a 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -1,8 +1,5 @@ package seedu.lifetrack.system.exceptions; -import seedu.lifetrack.calories.calorielist.CalorieList; -import java.util.logging.Level; -import java.util.logging.Logger; public class InvalidInputException extends Exception { diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 20001de3d4..96466e00a5 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -9,8 +9,9 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; -import static seedu.lifetrack.system.exceptions.ErrorMessages.*; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getTooLongSleepDurationMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getSleepMissingKeywordMessage; public class ParserSleep { private static final int DURATION_IDX = 0; @@ -44,9 +45,8 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept return newSleep; } - private static double parseDuration(String durationStr) throws InvalidInputException - { - double duration; + private static double parseDuration(String durationStr) throws InvalidInputException { + double duration=0; try { duration = Double.parseDouble(durationStr); @@ -56,13 +56,11 @@ private static double parseDuration(String durationStr) throws InvalidInputExcep if (duration <= 0) { throw new InvalidInputException(getIncorrectSleepInputMessage()); - } - else if (duration >= 24) { + } else if (duration >= 24) { throw new InvalidInputException(getTooLongSleepDurationMessage()); } - return duration; -} + } private static LocalDate parseDate(String strDate) throws InvalidInputException { @@ -72,6 +70,7 @@ private static LocalDate parseDate(String strDate) throws InvalidInputException throw new InvalidInputException("\tInvalid date! Please enter a valid date in format YYYY-MM-DD."); } } + private static void checkKeywordsExist(int dateIndex) throws InvalidInputException { if (dateIndex == -1 ) { throw new InvalidInputException(getSleepMissingKeywordMessage()); @@ -84,7 +83,5 @@ private static void checkValidFormat(int length) throws InvalidInputException { "sleep add d/"); } } - - } -//@@author \ No newline at end of file +//@@author From 8a6375b91f83d3c82cf897aafc4a263552c74b8e Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 9 Apr 2024 22:43:24 +0800 Subject: [PATCH 232/414] merge the current version --- .../java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java | 9 ++++++++- .../java/seedu/lifetrack/sleep/sleeplist/SleepList.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 393b999dac..52f10cc777 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -10,6 +10,7 @@ public class SleepEntry extends Entry { private LocalDate date; private double duration; private int sleepEntryID; + private static int sleepEntryNum=0; /*** * Sleep constructor: date can be empty. If date input is empty, automatically fill with N/A; @@ -17,10 +18,16 @@ public class SleepEntry extends Entry { * @param date * @param duration */ - public SleepEntry (int sleepEntryID, double duration, LocalDate date){ + public SleepEntry (double duration, LocalDate date){ + super(sleepEntryNum++, "SLEEP", date); + this.date = date; + this.duration = duration; + } + public SleepEntry (int sleepEntryID,double duration, LocalDate date){ super(sleepEntryID, "SLEEP", date); this.date = date; this.duration = duration; + this.sleepEntryID=sleepEntryID; } public LocalDate getDate() { diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 60b5ecf4f3..ba793ee5ec 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -47,7 +47,7 @@ public Entry getSleep(int index) { public void addSleep(String input) { try { - Entry newSleep = ParserSleep.parseSleepInput(input, lastSleepEntryID); + Entry newSleep = ParserSleep.parseSleepInput(input); sleepList.add(newSleep); updateFile(); SleepListUi.printNewSleepEntry(newSleep); From 0f4e2a7ae04f27b426be7459369caefd3365fe27 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 9 Apr 2024 22:53:45 +0800 Subject: [PATCH 233/414] debug FileHandler --- src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java | 3 ++- src/main/java/seedu/lifetrack/system/parser/ParserSleep.java | 1 - src/main/java/seedu/lifetrack/system/storage/FileHandler.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 52f10cc777..0eb194d045 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -27,7 +27,6 @@ public SleepEntry (int sleepEntryID,double duration, LocalDate date){ super(sleepEntryID, "SLEEP", date); this.date = date; this.duration = duration; - this.sleepEntryID=sleepEntryID; } public LocalDate getDate() { @@ -38,6 +37,8 @@ public double getDuration() { return duration; } + public int getSleepEntryID() {return sleepEntryID;} + public String toString() { return "\t Date: " + date + ", Duration: " + String.format("%.1f", duration) + " hours"; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 96466e00a5..53a4a64dff 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -34,7 +34,6 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept String strDate = parts[DATE_IDX].trim(); String strDuration = parts[DURATION_IDX].trim(); - System.out.println(strDate); duration = parseDuration(strDuration); date = parseDate(strDate); diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 6541cbc067..ebcaa35831 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -38,7 +38,7 @@ public class FileHandler { private static final int VOLUME_INDEX = 3; //sleep list constants - private static final int DURATION_INDEX = 2; + private static final int DURATION_INDEX = 3; //user data constants private static final int NAME_INDEX = 0; From d5ddf0fa47db0659cebc913cc3e6874aab8290fe Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 9 Apr 2024 22:56:47 +0800 Subject: [PATCH 234/414] debug sleep id --- src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 0eb194d045..0ffc9ad1e7 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -25,6 +25,9 @@ public SleepEntry (double duration, LocalDate date){ } public SleepEntry (int sleepEntryID,double duration, LocalDate date){ super(sleepEntryID, "SLEEP", date); + if(sleepEntryNum Date: Tue, 9 Apr 2024 22:57:25 +0800 Subject: [PATCH 235/414] debug sleep id --- src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 0ffc9ad1e7..62398552dc 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -25,7 +25,7 @@ public SleepEntry (double duration, LocalDate date){ } public SleepEntry (int sleepEntryID,double duration, LocalDate date){ super(sleepEntryID, "SLEEP", date); - if(sleepEntryNum Date: Tue, 9 Apr 2024 23:02:48 +0800 Subject: [PATCH 236/414] fix gradle bug --- .../java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 62398552dc..a418bfafda 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -7,10 +7,11 @@ public class SleepEntry extends Entry { + private static int sleepEntryNum=0; private LocalDate date; private double duration; private int sleepEntryID; - private static int sleepEntryNum=0; + /*** * Sleep constructor: date can be empty. If date input is empty, automatically fill with N/A; @@ -40,7 +41,9 @@ public double getDuration() { return duration; } - public int getSleepEntryID() {return sleepEntryID;} + public int getSleepEntryID() { + return sleepEntryID; + } public String toString() { return "\t Date: " + date + From 17cee78200c359d2a0ab4d51fb9322b68815a8ee Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 23:10:29 +0800 Subject: [PATCH 237/414] Add sorting feature when new entry added --- .../calories/calorielist/CalorieList.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 3ccaebab7f..cbfb515f09 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -8,6 +8,8 @@ import seedu.lifetrack.system.storage.FileHandler; import seedu.lifetrack.ui.CalorieListUi; +import java.util.Collections; +import java.util.Comparator; import java.util.logging.Level; import java.util.logging.Logger; import java.io.FileNotFoundException; @@ -120,6 +122,11 @@ public void addEntry(String input) { updateFile(); CalorieListUi.printNewCalorieEntry(newEntry); lastEntryID ++; + //only sort if newly added date is earlier than date in final entry before adding entry + if (calorieArrayList.size() > 1 && + calorieArrayList.get(calorieArrayList.size() - 2).getDate().compareTo(newEntry.getDate()) > 0 ) { + sortEntriesByDate(); + } } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); } @@ -193,5 +200,15 @@ private int loadLastEntryID() { return FileHandler.getMaxCaloriesID(); // Default value if file doesn't exist or error occurs } + //Method to sort entries by date + public void sortEntriesByDate() { + Collections.sort(calorieArrayList, new Comparator() { + @Override + public int compare(Entry entry1, Entry entry2) { + return entry1.getDate().compareTo(entry2.getDate()); + } + }); + } + } From aee09ebe4070cf205d71025f58a0557543a7d60f Mon Sep 17 00:00:00 2001 From: RexYong Date: Tue, 9 Apr 2024 23:23:03 +0800 Subject: [PATCH 238/414] Add javadocs for CLass CalorieList --- .../calories/calorielist/CalorieList.java | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index cbfb515f09..5d27a9700e 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -27,7 +27,10 @@ public class CalorieList { private FileHandler fileHandler; private int lastEntryID; - //constructor for JUnit tests + /** + * Constructs a new CalorieList object. + * This constructor is used for JUnit tests and initializes an empty list of calorie entries. + */ public CalorieList() { calorieArrayList = new ArrayList<>(); } @@ -66,10 +69,10 @@ public Entry getEntry(int index) { /** - * Deletes a calorie entry from the list based on the provided index. - * Index should be in an integer from 1 to size of the list. + * Deletes a calorie entry from the list based on the provided entryID. + * entryID should be in an integer from 1 to size of the list, and be present in the calorieArrayList. * - * @param line the string containing the index of calorie record user want to delete + * @param line the string containing the entryID of calorie record user want to delete */ public void deleteEntry(String line) { assert (line.startsWith("calories delete") ) : "ensures that input is correct"; @@ -92,10 +95,15 @@ public void deleteEntry(String line) { } } - //method that returns index of entry in arrayList according to lastEntryID - public int getIndexFromEntryID(int lastEntryID) { + /** + * Returns the index of the entry in the calorie ArrayList based on the given entry ID. + * + * @param entryID the entry ID to search for in the list + * @return the index of the entry with the specified entry ID, or NO_INDEX_FOUND if not found + */ + public int getIndexFromEntryID(int entryID) { for (int i = 0; i < calorieArrayList.size(); i++) { - if (calorieArrayList.get(i).getLastEntryID() == lastEntryID) { + if (calorieArrayList.get(i).getLastEntryID() == entryID) { return i; } } @@ -106,10 +114,12 @@ public int getIndexFromEntryID(int lastEntryID) { * Parses a string input representing calorie intake and adds it to the calorie list. * * This method takes a string input representing calorie intake information and - * attempts to parse it using the parseCaloriesIn method from the Parser class. + * attempts to parse it using the parseCaloriesInput method from the ParserCalories class. * If the input format is incorrect or contains missing components, it catches * the InvalidInputException and prints an error message. Otherwise, it adds * the parsed Entry object to the calorieArrayList. + * Additionally, if the date of the newly added entry is earlier than the date of the final + * entry before adding the new entry, the list is sorted by date. * * @param input the input string containing date, time, activity, and calorie count */ @@ -136,6 +146,7 @@ public void addEntry(String input) { * Prints the list of calorie entries along with its activity description. * If the list is empty, it prints a message indicating that the list is empty. * Otherwise, it prints each entry's activity description and calorie count. + * It prints calorieInflow entries, followed by calorieOutflow entries. */ public void printCalorieList() { if (calorieArrayList.isEmpty()) { @@ -147,6 +158,10 @@ public void printCalorieList() { } } + /** + * Prints the list of calorie entries representing calorie intake along with their activity descriptions. + * This method prints each entry's activity description and calorie count if it represents calorie intake. + */ public void printCalorieInflow() { CalorieListUi.inputCalorieListHeader(); int serialNumber = 1; @@ -159,6 +174,10 @@ public void printCalorieInflow() { } } + /** + * Prints the list of calorie entries representing calorie expenditure along with their activity descriptions. + * This method prints each entry's activity description and calorie count if it represents calorie expenditure. + */ public void printCalorieOutflow() { CalorieListUi.outputCalorieListHeader(); int serialNumber = 1; @@ -195,12 +214,23 @@ public int getCaloriesConsumed() { return totalCalories; } - //method that loads LastEntryID from txt file + /** + * Loads the last entry ID from a text file. + * This method retrieves the last entry ID from the file using the FileHandler.getMaxCaloriesID method. + * If the file doesn't exist or an error occurs during the process, it returns a default value. + * + * @return the last entry ID loaded from the file, or a default value if the file doesn't exist or an error occurs + */ private int loadLastEntryID() { return FileHandler.getMaxCaloriesID(); // Default value if file doesn't exist or error occurs } - //Method to sort entries by date + /** + * Sorts the list of calorie entries by date. + * This method uses the Collections.sort(List, Comparator) method to sort the list of + * calorie entries in ascending order based on their dates. It provides a custom comparator that + * compares the dates of two entries and returns the result of the comparison. + */ public void sortEntriesByDate() { Collections.sort(calorieArrayList, new Comparator() { @Override @@ -209,6 +239,4 @@ public int compare(Entry entry1, Entry entry2) { } }); } - - } From 7624d9e78cebaad4fdc6448fe23873052eaab5fd Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 9 Apr 2024 23:26:50 +0800 Subject: [PATCH 239/414] fix test bug --- .../system/exceptions/ErrorMessages.java | 2 +- .../lifetrack/system/parser/ParserSleep.java | 7 ++--- .../java/seedu/lifetrack/ParserSleepTest.java | 27 ++++++++++--------- .../java/seedu/lifetrack/SleepListTest.java | 2 +- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index fd6e379368..e2c34e8a2a 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -50,7 +50,7 @@ public static String getFileNotFoundMessage() { } public static String getIncorrectSleepInputMessage() { - return "\t Please input only positive real number into the sleep duration field!"; + return "\t Please input one positive real number into the sleep duration field!"; } public static String getIncorrectSleepDateInputMessage() { diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 53a4a64dff..4ec3994c84 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -30,7 +30,7 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept assert dateIndex != -1 : "The d/ keyword should exist!"; String[] parts = input.substring(SLEEP_HEADER.length()).split(DATE_ICON); - checkValidFormat(parts.length); + checkValidFormat(parts); String strDate = parts[DATE_IDX].trim(); String strDuration = parts[DURATION_IDX].trim(); @@ -76,11 +76,12 @@ private static void checkKeywordsExist(int dateIndex) throws InvalidInputExcepti } } - private static void checkValidFormat(int length) throws InvalidInputException { - if (length != STRING_PARTS_LEN) { + private static void checkValidFormat(String[] parts) throws InvalidInputException { + if (parts.length != STRING_PARTS_LEN || parts[DURATION_IDX].isEmpty()) { throw new InvalidInputException("Please ensure that you have keyed in the correct format: " + "sleep add d/"); } + } } //@@author diff --git a/src/test/java/seedu/lifetrack/ParserSleepTest.java b/src/test/java/seedu/lifetrack/ParserSleepTest.java index 7162533a6b..b210a6cc53 100644 --- a/src/test/java/seedu/lifetrack/ParserSleepTest.java +++ b/src/test/java/seedu/lifetrack/ParserSleepTest.java @@ -13,18 +13,19 @@ public void parseSleepInput_inputContains2Duration_invalidInputExceptionThrown() String invalidInput = "sleep add 8.0 9.2"; // Call methods to test try { - parseSleepInput(invalidInput, 0); + parseSleepInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("Invalid date format", e.getMessage()); + assertEquals("\t Invalid input!\n"+"\t Please ensure that you have entered all keywords!\n"+ + "\t Example input: sleep add 7.5 d/2024-03-11", e.getMessage()); } } @Test public void parseSleepInput_inputContains2Date_invalidInputExceptionThrown() { // setup test - String invalidInput = "sleep add d/110324 d/280524"; + String invalidInput = "sleep add d/2024-12-12 d/2024-11-11"; // Call methods to test try { - parseSleepInput(invalidInput, 0); + parseSleepInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + "sleep add d/", e.getMessage()); @@ -33,10 +34,10 @@ public void parseSleepInput_inputContains2Date_invalidInputExceptionThrown() { @Test public void parseSleepInput_inputMissingDuration_invalidInputExceptionThrown() { // setup test - String invalidInput = "sleep add d/110324"; + String invalidInput = "sleep add d/2024-02-11"; // Call methods to test try { - parseSleepInput(invalidInput, 0); + parseSleepInput(invalidInput); } catch (InvalidInputException e) { assertEquals("Please ensure that you have keyed in the correct format: " + "sleep add d/", e.getMessage()); @@ -46,24 +47,24 @@ public void parseSleepInput_inputMissingDuration_invalidInputExceptionThrown() { @Test public void parseSleepInput_inputNonPositiveValueForDuration_invalidInputExceptionThrown() { // setup test - String invalidInput = "sleep add -2 d/110324"; + String invalidInput = "sleep add -2 d/2024-03-11"; // Call methods to test try { - parseSleepInput(invalidInput, 0); + parseSleepInput(invalidInput); } catch (InvalidInputException e) { - assertEquals("\t Please input only positive real number into the sleep duration field!" + assertEquals("\t Please input one positive real number into the sleep duration field!" , e.getMessage()); } } @Test - public void parseLiquidInput_missingKeywords_exceptionThrown() { + public void parseSleepInput_missingKeywords_exceptionThrown() { try { - parseSleepInput("sleep add", 0); + parseSleepInput("sleep add"); } catch (InvalidInputException e) { - assertEquals("Please ensure that you have keyed in the correct format: " + - "sleep add d/", e.getMessage()); + assertEquals("\t Invalid input!\n"+"\t Please ensure that you have entered all keywords!\n"+ + "\t Example input: sleep add 7.5 d/2024-03-11", e.getMessage()); } } } diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index f4eb5a947d..81226393b9 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -29,7 +29,7 @@ public void testDeleteSleepInvalidIndex() { assertEquals(initialSize, sleepList.getSize()); } @Test - public void testPrintLiquidListEmpty() { + public void testPrintSleepListEmpty() { String lineSeparator = System.lineSeparator(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); From 05fc34aa5d68ccca5c837daf9afb4bf3cee5ac3d Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Tue, 9 Apr 2024 23:45:15 +0800 Subject: [PATCH 240/414] fix test bug --- .../seedu/lifetrack/hydration/hydrationlist/HydrationList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 0066aa2bb5..ade1eb8b8e 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -103,7 +103,7 @@ public int getIndexFromEntryID(int lastEntryID) { * @param input the input string containing liquid entry information */ public void addEntry(String input) { - assert (input.startsWith("hydration add")) : "ensures that input is correct"; + assert (input.startsWith("hydration in")) : "ensures that input is correct"; logr.setLevel(Level.SEVERE); try { Entry newEntry = ParserHydration.parseHydrationInput(input, lastHydrationEntryID); From 3fe22c3d2b6f2bd2cc28fb61e91eafaccc28c088 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 00:08:59 +0800 Subject: [PATCH 241/414] Add JUnit for checking sorting in class CalorieList --- .../seedu/lifetrack/ui/CalorieListUi.java | 4 +- .../java/seedu/lifetrack/CalorieListTest.java | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index b552deb00d..5fec5b582c 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -32,12 +32,12 @@ public static void calorieListHeader() { public static void outputCalorieListHeader() { System.out.println(""); - System.out.println("\t Your Caloric outflow list:"); + System.out.println("\t Your caloric outflow list:"); } public static void inputCalorieListHeader() { System.out.println(""); - System.out.println("\t Your Caloric Inflow List:"); + System.out.println("\t Your caloric inflow list:"); } public static void printNewCalorieEntry(Entry newEntry) { diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index cda5da0f99..89ac452141 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.lifetrack.system.parser.ParserHydration.parseHydrationInput; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -11,6 +12,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; +import seedu.lifetrack.system.exceptions.InvalidInputException; public class CalorieListTest { @@ -143,5 +145,49 @@ public void testPrintCalorieListMultipleEntries() { assertEquals(5, calorieList.getSize()); } + @Test + public void testAddEntry_addDifferentDates_datesSortedCorrectly() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in burger king c/200 d/2024-03-14"); + calorieList.addEntry("calories out Walk c/150 d/2022-02-22"); + calorieList.addEntry("calories in acai c/500 d/2022-02-22"); + calorieList.addEntry("calories out Run c/250 d/2024-03-14"); + calorieList.addEntry("calories in commhall dinner c/300 d/2021-01-11"); + calorieList.addEntry("calories out play pool c/69 d/2021-01-11"); + calorieList.printCalorieList(); + System.setOut(System.out); + StringBuilder expectedOutput = new StringBuilder(); + for (int i = 1; i < 7; i++) { + expectedOutput.append(addedEntryHeader) + .append(lineSeparator).append("\t ") + .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) + .append(lineSeparator); + } + expectedOutput.append("\t Your Caloric List:") + .append(lineSeparator) + .append(lineSeparator) + .append("\t Your caloric inflow list:") + .append(lineSeparator) + .append("\t 1. \t EntryID: 5, Date: 2021-01-11, Description: commhall dinner, Calories: 300") + .append(lineSeparator) + .append("\t 2. \t EntryID: 3, Date: 2022-02-22, Description: acai, Calories: 500") + .append(lineSeparator) + .append("\t 3. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") + .append(lineSeparator) + .append(lineSeparator) + .append("\t Your caloric outflow list:") + .append(lineSeparator) + .append("\t 1. \t EntryID: 6, Date: 2021-01-11, Description: play pool, Calories: 69") + .append(lineSeparator) + .append("\t 2. \t EntryID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append(lineSeparator) + .append("\t 3. \t EntryID: 4, Date: 2024-03-14, Description: Run, Calories: 250") + .append(lineSeparator); + assertEquals(expectedOutput.toString(), outputStream.toString()); + assertEquals(6, calorieList.getSize()); + } } From a33693a3cc670b16790cb8969eeb2c6a0fd2a2f6 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 00:11:17 +0800 Subject: [PATCH 242/414] Fix checkstyle erorr in CLass CalorieListTest --- src/main/java/seedu/lifetrack/ui/CalorieListUi.java | 4 ++-- src/test/java/seedu/lifetrack/CalorieListTest.java | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 5fec5b582c..e4b0db6ba8 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -32,12 +32,12 @@ public static void calorieListHeader() { public static void outputCalorieListHeader() { System.out.println(""); - System.out.println("\t Your caloric outflow list:"); + System.out.println("\t Your Caloric Outflow List:"); } public static void inputCalorieListHeader() { System.out.println(""); - System.out.println("\t Your caloric inflow list:"); + System.out.println("\t Your Caloric Inflow List:"); } public static void printNewCalorieEntry(Entry newEntry) { diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 89ac452141..2f3d3455cb 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.lifetrack.system.parser.ParserHydration.parseHydrationInput; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -12,7 +11,6 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; -import seedu.lifetrack.system.exceptions.InvalidInputException; public class CalorieListTest { @@ -100,7 +98,7 @@ public void testPrintCalorieListNonEmpty() { "\t Your Caloric List:" + lineSeparator + lineSeparator + "\t Your Caloric Inflow List:" + lineSeparator + "\t 1. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200" + lineSeparator + - lineSeparator + "\t Your Caloric outflow list:" + lineSeparator; + lineSeparator + "\t Your Caloric Outflow List:" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -135,7 +133,7 @@ public void testPrintCalorieListMultipleEntries() { .append("\t 3. \t EntryID: 5, Date: 2024-03-14, Description: commhall dinner, Calories: 300") .append(lineSeparator) .append(lineSeparator) - .append("\t Your Caloric outflow list:") + .append("\t Your Caloric Outflow List:") .append(lineSeparator) .append("\t 1. \t EntryID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") .append(lineSeparator) @@ -169,7 +167,7 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { expectedOutput.append("\t Your Caloric List:") .append(lineSeparator) .append(lineSeparator) - .append("\t Your caloric inflow list:") + .append("\t Your Caloric Inflow List:") .append(lineSeparator) .append("\t 1. \t EntryID: 5, Date: 2021-01-11, Description: commhall dinner, Calories: 300") .append(lineSeparator) @@ -178,7 +176,7 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { .append("\t 3. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") .append(lineSeparator) .append(lineSeparator) - .append("\t Your caloric outflow list:") + .append("\t Your Caloric Outflow List:") .append(lineSeparator) .append("\t 1. \t EntryID: 6, Date: 2021-01-11, Description: play pool, Calories: 69") .append(lineSeparator) From 1b2998f9692986c14060e5a7111bd6ce3543b96e Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 00:25:58 +0800 Subject: [PATCH 243/414] Add JUnit test for deleting entries based on entryID instead of index --- .../java/seedu/lifetrack/CalorieListTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 2f3d3455cb..97655139c9 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -15,6 +15,7 @@ public class CalorieListTest { private final String addedEntryHeader = "\t The following entry has been added to your caloric list!"; + private final String deleteEntryHeader = "\t The following calorie record has been successfully deleted!"; @Test public void addEntry_validInput_entryAdded() { @@ -188,4 +189,45 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { assertEquals(6, calorieList.getSize()); } + @Test + public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in burger king c/200 d/2022-02-22"); + calorieList.addEntry("calories out Walk c/150 d/2022-02-22"); + calorieList.addEntry("calories in acai c/500 d/2022-02-22"); + + System.setOut(System.out); + StringBuilder expectedOutput = new StringBuilder(); + for (int i = 1; i < 4; i++) { + expectedOutput.append(addedEntryHeader) + .append(lineSeparator).append("\t ") + .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) + .append(lineSeparator); + } + expectedOutput.append(deleteEntryHeader) + .append(lineSeparator).append("\t ") + .append(calorieList.getEntry(calorieList.getIndexFromEntryID(3)).toString()) + .append(lineSeparator); + + calorieList.deleteEntry("calories delete 3"); + calorieList.printCalorieList(); + + expectedOutput.append("\t Your Caloric List:") + .append(lineSeparator) + .append(lineSeparator) + .append("\t Your Caloric Inflow List:") + .append(lineSeparator) + .append("\t 1. \t EntryID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") + .append(lineSeparator) + .append(lineSeparator) + .append("\t Your Caloric Outflow List:") + .append(lineSeparator) + .append("\t 1. \t EntryID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append(lineSeparator); + assertEquals(expectedOutput.toString(), outputStream.toString()); + assertEquals(2, calorieList.getSize()); + } } From 525ccec814ce2ce90bf22d9044b71c3a319d6655 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 10 Apr 2024 15:45:28 +0800 Subject: [PATCH 244/414] add authorship, fix empty input after "m/" bug --- src/main/java/seedu/lifetrack/Entry.java | 1 + src/main/java/seedu/lifetrack/calories/Activity.java | 1 + src/main/java/seedu/lifetrack/calories/Food.java | 1 + .../lifetrack/system/exceptions/ErrorMessages.java | 1 + .../system/exceptions/InvalidInputException.java | 1 + .../exceptions/InvalidInputExceptionMessage.java | 6 ++++++ .../lifetrack/system/parser/ParserCalories.java | 12 ++++++++---- .../seedu/lifetrack/system/storage/FileHandler.java | 1 + 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index 31df7560b2..0b4f7f5074 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack; import java.time.LocalDate; diff --git a/src/main/java/seedu/lifetrack/calories/Activity.java b/src/main/java/seedu/lifetrack/calories/Activity.java index 5c3d0bd1ca..f89a9f65cf 100644 --- a/src/main/java/seedu/lifetrack/calories/Activity.java +++ b/src/main/java/seedu/lifetrack/calories/Activity.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.calories; public class Activity { diff --git a/src/main/java/seedu/lifetrack/calories/Food.java b/src/main/java/seedu/lifetrack/calories/Food.java index fa950f112a..fcd01bb02f 100644 --- a/src/main/java/seedu/lifetrack/calories/Food.java +++ b/src/main/java/seedu/lifetrack/calories/Food.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.calories; public class Food { diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index e2c34e8a2a..cf31608e49 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.exceptions; /** diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index ef2335b42a..187f8c4575 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.exceptions; diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 6158119a8d..922bb0f974 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.exceptions; /** @@ -31,6 +32,11 @@ public static String getWhitespaceInInputMessage() { return HEADER + message + CALORIES_IN_INPUT; } + public static String getEmptyMacrosMessage() { + String message = "\t Your macronutrients field is empty!\n"; + return HEADER + message + MACROS_INPUT; + } + public static String getIncompleteMacrosMessage() { String message = "\t Please ensure that all macronutrients fields are filled up!\n"; return HEADER + message + MACROS_INPUT; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 154f215288..5bd44496ec 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.parser; import seedu.lifetrack.calories.calorielist.InputEntry; @@ -12,6 +13,7 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesIncorrectOrderMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesMissingKeywordsMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; import seedu.lifetrack.Entry; @@ -58,7 +60,7 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv String description = getDescriptionFromInput(input, command, caloriesIndex); String strCalories = parts[1].trim(); - // Try catch here is needed because if i input , calories in chicken c/1000 d/ , code fails + // Try catch here is needed because if user inputs "calories in chicken c/1000 d/ "", // code fails because index out of bounds occurs due to parts[2].trim() String strDate = null; try { @@ -78,11 +80,13 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv if (command.equals("calories out")) { throw new InvalidInputException(getMacrosInCaloriesOutMessage()); } - String macroString = parts[3].trim(); try { + String macroString = parts[3].trim(); macros = getMacrosFromInput(macroString); } catch (InvalidInputException e) { throw new InvalidInputException(e.getMessage()); + } catch (ArrayIndexOutOfBoundsException e) { + throw new InvalidInputException(getEmptyMacrosMessage()); } } @@ -99,7 +103,7 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv } catch (DateTimeParseException e) { throw new InvalidInputException("Invalid date format"); } - //@@author + //@@author owx0130 if (command.equals("calories out")) { lastEntryID++; @@ -125,7 +129,7 @@ public static LocalDate getLocalDateFromInput(String strDate) throws DateTimePar LocalDate date = LocalDate.parse(strDate); return date; } - //@@author + //@@author owx0130 /** * Parses a string representation of calories and returns an integer value. diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index ebcaa35831..4c0bfb2662 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.storage; import java.io.File; From da989f3669fc0442d32ec7fab4879d055070051d Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 10 Apr 2024 15:47:01 +0800 Subject: [PATCH 245/414] fix help message for user setup bug --- .../system/exceptions/InvalidInputExceptionMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 922bb0f974..feb4bcc8f7 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -14,7 +14,7 @@ public class InvalidInputExceptionMessage { private static final String MACROS_INPUT = "\t Example input: ....... m/CARBS_INT, PROTEIN_INT, FATS_INT"; private static final String SLEEP_IN_INPUT = "\t Example input: sleep add 7.5 d/2024-03-11" ; private static final String HYDRATION_IN_INPUT = "\t Example input: hydration in Milo v/1000 d/2024-04-19" ; - private static final String USER_SETUP_INPUT = "\t Example input: user set up Tom h/170 w/80 a/25 s/male e/4 g/3"; + private static final String USER_SETUP_INPUT = "\t Example input: user setup Tom h/170 w/80 a/25 s/male e/4 g/3"; //calories list related methods public static String getCaloriesIncorrectOrderMessage() { From e16e993c3222afca9e035f5906813bb29941d77c Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 10 Apr 2024 15:54:45 +0800 Subject: [PATCH 246/414] fix help message indentations --- src/main/java/seedu/lifetrack/ui/Ui.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index fe939e7512..430f7eae78 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -164,15 +164,15 @@ public static void showHelp() { System.out.println("\t - help: Displays a list of available commands and their descriptions."); printLine(); System.out.println("\t - calories in c/ d/ " + - "m/[carbohydrates, proteins, fats]: Adds a calorie gaining entry into the calories tracker."); - System.out.println("\t - calories out c/ d/: " + - "Adds a calorie burning entry into the calories tracker."); + "m/[carbohydrates, proteins, fats]:\n" + "\t Adds a calorie gaining entry into the calories tracker."); + System.out.println("\t - calories out c/ d/:\n" + + "\t Adds a calorie burning entry into the calories tracker."); System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); System.out.println("\t - calories delete : Deletes the entry at the specified index" + " from the calorie list."); printLine(); - System.out.println("\t - hydration in v/ d/: " + - "Adds a hydration entry into the hydration tracker."); + System.out.println("\t - hydration in v/ d/:\n" + + "\t Adds a hydration entry into the hydration tracker."); System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list."); System.out.println("\t - hydration delete : Deletes the hydration entry at the specified index " + "from the hydration list."); @@ -184,7 +184,7 @@ public static void showHelp() { "from the sleep list."); printLine(); System.out.println("\t - user setup h/ w/ a/ s/ e/ " + - "g/: Create a new user, or edit an existing one."); + "g/:\n" + "\t Create a new user, or edit an existing one."); System.out.println("\t - user progress: Display calories and hydration progress towards the daily " + "requirement."); } From 3bebe5f1b3411e582f6cde7bac11acffe6ebd4b6 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Wed, 10 Apr 2024 15:56:11 +0800 Subject: [PATCH 247/414] add index into sleep print list --- .../lifetrack/sleep/sleeplist/SleepEntry.java | 4 +++- .../lifetrack/sleep/sleeplist/SleepList.java | 19 +++++++++++------ .../java/seedu/lifetrack/SleepListTest.java | 21 ++++++++++--------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index a418bfafda..749ccd2263 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -23,6 +23,7 @@ public SleepEntry (double duration, LocalDate date){ super(sleepEntryNum++, "SLEEP", date); this.date = date; this.duration = duration; + this.sleepEntryID=sleepEntryNum-1; } public SleepEntry (int sleepEntryID,double duration, LocalDate date){ super(sleepEntryID, "SLEEP", date); @@ -31,6 +32,7 @@ public SleepEntry (int sleepEntryID,double duration, LocalDate date){ } this.date = date; this.duration = duration; + this.sleepEntryID=sleepEntryID; } public LocalDate getDate() { @@ -46,7 +48,7 @@ public int getSleepEntryID() { } public String toString() { - return "\t Date: " + date + + return "\t Sleep ID: " +this.sleepEntryID+", Date: " + date + ", Duration: " + String.format("%.1f", duration) + " hours"; } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index ba793ee5ec..a7d8acc3f2 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -13,6 +13,7 @@ public class SleepList { + private static int DELETE_IDX = 2; private ArrayList sleepList; private FileHandler fileHandler; private int lastSleepEntryID; @@ -58,12 +59,18 @@ public void addSleep(String input) { public void deleteSleep(String line) { try { - int index = Integer.parseInt(line.split(" ")[2]) ; //User input format: sleep delete INDEX, here get index - Entry toDelete = sleepList.get(index-1); - sleepList.remove(index - 1); - updateFile(); - SleepListUi.successfulDeletedMessage(toDelete); - } catch (IndexOutOfBoundsException e) { + int index = Integer.parseInt(line.split(" ")[DELETE_IDX]) ; //User input format: sleep delete ID, here get index + for(int i=0; i Date: Wed, 10 Apr 2024 15:56:14 +0800 Subject: [PATCH 248/414] add index into sleep print list --- src/main/java/seedu/lifetrack/ui/SleepListUi.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/SleepListUi.java b/src/main/java/seedu/lifetrack/ui/SleepListUi.java index b6e83337b1..4157cbcc51 100644 --- a/src/main/java/seedu/lifetrack/ui/SleepListUi.java +++ b/src/main/java/seedu/lifetrack/ui/SleepListUi.java @@ -15,7 +15,7 @@ public static void emptyListMessage() { } public static String deleteLogIndexMessage() { - return "\t Sorry, this index is invalid. Please enter a positive integer within the size of the list."; + return "\t Sorry, this index is invalid. Please enter a positive integer within the index of the list."; } public static String deleteLogNumberMessage() { @@ -25,7 +25,6 @@ public static String deleteLogNumberMessage() { public static void sleepListHeader() { System.out.println("\t Your Sleep List:"); } - //@@author public static void printNewSleepEntry(Entry newEntry) { System.out.println("\t The following entry has been added to your sleep list!"); From 4f0262ade425a3f9661e332f96c76c89cdab2f78 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Wed, 10 Apr 2024 15:57:25 +0800 Subject: [PATCH 249/414] fix gradle error --- .../java/seedu/lifetrack/sleep/sleeplist/SleepList.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index a7d8acc3f2..05fb072641 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -59,12 +59,10 @@ public void addSleep(String input) { public void deleteSleep(String line) { try { - int index = Integer.parseInt(line.split(" ")[DELETE_IDX]) ; //User input format: sleep delete ID, here get index - for(int i=0; i Date: Wed, 10 Apr 2024 15:58:51 +0800 Subject: [PATCH 250/414] fix broken quick start link in UG --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 86b86f8328..b66dd16e3f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -33,7 +33,7 @@ LifeTrack is a desktop app for students to track their health data, optimized fo {Give steps to get started quickly} 1. Ensure that you have Java 11 or above installed. -2. Download the latest version of `LifeTrack` from [here](http://link.to/duke). +2. Download the latest version of `LifeTrack` from [here](https://github.com/AY2324S2-CS2113-F15-2/tp/releases). [//]: # (## Features ) From 6155950658287dcefd43e60abe0262e8ec08033c Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 16:00:47 +0800 Subject: [PATCH 251/414] Add test that checks that entryID increments properly for calorieList --- .../java/seedu/lifetrack/CalorieListTest.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 97655139c9..c8de5e94e4 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -149,6 +149,7 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { String lineSeparator = System.lineSeparator(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories in burger king c/200 d/2024-03-14"); calorieList.addEntry("calories out Walk c/150 d/2022-02-22"); @@ -157,14 +158,19 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { calorieList.addEntry("calories in commhall dinner c/300 d/2021-01-11"); calorieList.addEntry("calories out play pool c/69 d/2021-01-11"); calorieList.printCalorieList(); + System.setOut(System.out); StringBuilder expectedOutput = new StringBuilder(); + + // expected output string for adding entries for (int i = 1; i < 7; i++) { expectedOutput.append(addedEntryHeader) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) .append(lineSeparator); } + + // expected output string for printing calorie list expectedOutput.append("\t Your Caloric List:") .append(lineSeparator) .append(lineSeparator) @@ -194,6 +200,7 @@ public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() String lineSeparator = System.lineSeparator(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); + CalorieList calorieList = new CalorieList(); calorieList.addEntry("calories in burger king c/200 d/2022-02-22"); calorieList.addEntry("calories out Walk c/150 d/2022-02-22"); @@ -201,12 +208,14 @@ public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() System.setOut(System.out); StringBuilder expectedOutput = new StringBuilder(); + // expected output string for adding entries for (int i = 1; i < 4; i++) { expectedOutput.append(addedEntryHeader) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) .append(lineSeparator); } + // expected output string for deleting entry expectedOutput.append(deleteEntryHeader) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(3)).toString()) @@ -215,6 +224,7 @@ public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() calorieList.deleteEntry("calories delete 3"); calorieList.printCalorieList(); + // expected output string for printing calories list expectedOutput.append("\t Your Caloric List:") .append(lineSeparator) .append(lineSeparator) @@ -230,4 +240,62 @@ public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(2, calorieList.getSize()); } + + @Test + public void testAddEntry_addAndDeleteEntries_entryIDIncrementsProperly() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in burger king c/200 d/2022-02-22"); + calorieList.addEntry("calories out Walk c/150 d/2022-02-22"); + calorieList.addEntry("calories in acai c/500 d/2022-02-22"); + + System.setOut(System.out); + StringBuilder expectedOutput = new StringBuilder(); + // expected output string for first 3 added entries + for (int i = 1; i < 4; i++) { + expectedOutput.append(addedEntryHeader) + .append(lineSeparator).append("\t ") + .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) + .append(lineSeparator); + } + + // expected output string for first deleted entry + expectedOutput.append(deleteEntryHeader) + .append(lineSeparator).append("\t ") + .append(calorieList.getEntry(calorieList.getIndexFromEntryID(3)).toString()) + .append(lineSeparator); + + calorieList.deleteEntry("calories delete 3"); + calorieList.addEntry("calories in yong tau foo c/688 d/2022-02-22 m/10,10,10"); + + // expected output string for fourth added entry + expectedOutput.append(addedEntryHeader) + .append(lineSeparator).append("\t ") + .append(calorieList.getEntry(calorieList.getIndexFromEntryID(4)).toString()) + .append(lineSeparator); + + calorieList.printCalorieList(); + + // expected output string for printing calorie list + expectedOutput.append("\t Your Caloric List:") + .append(lineSeparator) + .append(lineSeparator) + .append("\t Your Caloric Inflow List:") + .append(lineSeparator) + .append("\t 1. \t EntryID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") + .append(lineSeparator) + .append("\t 2. \t EntryID: 4, Date: 2022-02-22, Description: yong tau foo, Calories: 688 " + + "(C: 10, P: 10, F: 10)") + .append(lineSeparator) + .append(lineSeparator) + .append("\t Your Caloric Outflow List:") + .append(lineSeparator) + .append("\t 1. \t EntryID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append(lineSeparator); + assertEquals(expectedOutput.toString(), outputStream.toString()); + assertEquals(3, calorieList.getSize()); + } } From 19aeff3a0cfc8b17ad8df6c2b25e9e1c07dd62a4 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:02:07 +0800 Subject: [PATCH 252/414] edit user guide for sleep delete --- docs/UserGuide.md | 12 ++++++------ src/main/java/seedu/lifetrack/ui/SleepListUi.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 86b86f8328..35a96b596e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -188,16 +188,16 @@ Show the list of all sleep records in the sleep tracker. `sleep list` ### Deleting a sleep record: `sleep delete` -Deletes the sleep record according to the index. +Deletes the sleep record according to the sleep id. **Format:** -`sleep delete INDEX` -* Delete the sleep record at the specific index. -* The index refers to the index number shown in the displayed sleeping records list. -* The index must be a positive integer 1, 2, 3, …​ +`sleep delete SLEEPID` +* Delete the sleep record with specific id. +* The id refers to the id number shown in the displayed sleeping records list. +* The id must be a positive integer 1, 2, 3, …​ **Examples:** -* `list` followed by `sleep delete 2` deletes the 2nd sleep record from the sleep tracker. +* `list` followed by `sleep delete 2` deletes the sleep record with id 2 from the sleep tracker. ## User Profile diff --git a/src/main/java/seedu/lifetrack/ui/SleepListUi.java b/src/main/java/seedu/lifetrack/ui/SleepListUi.java index 4157cbcc51..dd19701b35 100644 --- a/src/main/java/seedu/lifetrack/ui/SleepListUi.java +++ b/src/main/java/seedu/lifetrack/ui/SleepListUi.java @@ -15,7 +15,7 @@ public static void emptyListMessage() { } public static String deleteLogIndexMessage() { - return "\t Sorry, this index is invalid. Please enter a positive integer within the index of the list."; + return "\t Sorry, this index is invalid. Please enter a positive integer within the sleep id of the list."; } public static String deleteLogNumberMessage() { From d4fafcca9335db9a16b1e89a0711f70cafe089ca Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:15:09 +0800 Subject: [PATCH 253/414] fix UG --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 35a96b596e..77132a6f74 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -173,7 +173,7 @@ Adds a sleep record into the sleep tracker. **Format:** `sleep add DURATION d/DATE` * The duration provided must be a positive real number. -* The time indicated should follow the 24-hour system. +* The duration should not exceed 24 hours. * The date provided should be of the form YYYY-MM-DD. From cc39b616479459fb8efa354090e9d36a85a35b97 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 10 Apr 2024 16:25:37 +0800 Subject: [PATCH 254/414] fix invalid date exception message printing --- .../lifetrack/calories/calorielist/CalorieList.java | 1 - .../lifetrack/hydration/hydrationlist/HydrationList.java | 1 - .../java/seedu/lifetrack/sleep/sleeplist/SleepList.java | 1 - .../seedu/lifetrack/system/exceptions/ErrorMessages.java | 9 --------- .../system/exceptions/InvalidInputExceptionMessage.java | 6 ++++++ .../seedu/lifetrack/system/parser/ParserCalories.java | 9 ++++----- .../seedu/lifetrack/system/parser/ParserHydration.java | 3 ++- .../java/seedu/lifetrack/system/parser/ParserSleep.java | 3 ++- src/main/java/seedu/lifetrack/user/User.java | 1 - 9 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 0bd003db70..b274af979a 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -44,7 +44,6 @@ public CalorieList(String filePath) { this.lastEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { calorieArrayList = new ArrayList<>(); - System.out.println(ErrorMessages.getFileNotFoundMessage()); } } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index ade1eb8b8e..2cc6b5c63a 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -41,7 +41,6 @@ public HydrationList(String filePath) { this.lastHydrationEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { hydrationArrayList = new ArrayList<>(); - System.out.println(ErrorMessages.getFileNotFoundMessage()); } } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index ba793ee5ec..9cf13c7039 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -30,7 +30,6 @@ public SleepList(String filePath) { this.lastSleepEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { sleepList = new ArrayList<>(); - System.out.println(ErrorMessages.getFileNotFoundMessage()); } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index cf31608e49..584ddd1e00 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -8,15 +8,6 @@ public class ErrorMessages { private static final String WHITESPACE = " "; - public static void printIndexOutOfBoundsError(){ - System.out.println("\t Sorry, this index is invalid. Please enter a positive integer " + - "within the size of the list."); - } - - public static void printNumberFormatError() { - System.out.println("\t Please enter a valid number within the command"); - } - public static String getIncorrectCaloriesInputMessage() { return "\t Please input only positive integers into the calories field!"; } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index feb4bcc8f7..4785df0830 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -16,6 +16,12 @@ public class InvalidInputExceptionMessage { private static final String HYDRATION_IN_INPUT = "\t Example input: hydration in Milo v/1000 d/2024-04-19" ; private static final String USER_SETUP_INPUT = "\t Example input: user setup Tom h/170 w/80 a/25 s/male e/4 g/3"; + //general error messages + public static String getInvalidDateMessage() { + String message = "\t Invalid date! Please enter a valid date in format YYYY-MM-DD."; + return message; + } + //calories list related methods public static String getCaloriesIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed the input in the correct order!\n"; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 5bd44496ec..1d7b53bb49 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -10,6 +10,7 @@ import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectMacrosInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesIncorrectOrderMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesMissingKeywordsMessage; @@ -101,18 +102,16 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv try { date = getLocalDateFromInput(strDate); } catch (DateTimeParseException e) { - throw new InvalidInputException("Invalid date format"); + throw new InvalidInputException(getInvalidDateMessage()); } //@@author owx0130 + lastEntryID++; if (command.equals("calories out")) { - lastEntryID++; return makeNewOutputEntry(lastEntryID, description, calories, date); } else if (macros == null) { - lastEntryID++; return makeNewInputEntry(lastEntryID, description, calories, date); } else { - lastEntryID++; return makeNewInputEntry(lastEntryID, description, calories, date, macros); } } @@ -192,7 +191,7 @@ private static int[] getMacrosFromInput(String macroString) throws InvalidInputE throw new InvalidInputException(getIncompleteMacrosMessage()); } } catch (NumberFormatException e) { - System.out.println(getIncorrectMacrosInputMessage()); + throw new InvalidInputException(getIncorrectMacrosInputMessage()); } return macros; } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index cfbf11c0c4..c6676512e0 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -13,6 +13,7 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationMissingKeywordMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; public class ParserHydration { private static final int VOLUME_IDX = 1; @@ -64,7 +65,7 @@ public static Entry parseHydrationInput(String input, int lastHydrationEntryID) try { date = getLocalDateFromInput(strDate); } catch (DateTimeParseException e) { - throw new InvalidInputException("Invalid date format"); + throw new InvalidInputException(getInvalidDateMessage()); } //@@author lastHydrationEntryID++; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index 4ec3994c84..efcaaee7c1 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -11,6 +11,7 @@ import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getTooLongSleepDurationMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getSleepMissingKeywordMessage; public class ParserSleep { @@ -66,7 +67,7 @@ private static LocalDate parseDate(String strDate) throws InvalidInputException try { return LocalDate.parse(strDate, DateTimeFormatter.ISO_LOCAL_DATE); } catch (DateTimeParseException e) { - throw new InvalidInputException("\tInvalid date! Please enter a valid date in format YYYY-MM-DD."); + throw new InvalidInputException(getInvalidDateMessage()); } } diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 813b426958..5cad629082 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -53,7 +53,6 @@ public User(String filePath) { goal = Integer.parseInt(data.get(GOAL_INDEX)); caloriesRequired = Integer.parseInt(data.get(REQ_CAL_INDEX)); } catch (FileNotFoundException e) { - System.out.println(ErrorMessages.getFileNotFoundMessage()); } } From 1ded088ac829cf30d6fbb741c636df9a26041c1a Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 16:31:19 +0800 Subject: [PATCH 255/414] Add JUnit test for invalidInputs for deleteEntry method --- .../java/seedu/lifetrack/CalorieListTest.java | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index c8de5e94e4..eee7d9b044 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -14,8 +14,12 @@ public class CalorieListTest { - private final String addedEntryHeader = "\t The following entry has been added to your caloric list!"; - private final String deleteEntryHeader = "\t The following calorie record has been successfully deleted!"; + private final String ADDED_ENTRY_HEADER = "\t The following entry has been added to your caloric list!"; + private final String DELETE_ENTRY_HEADER = "\t The following calorie record has been successfully deleted!"; + + private final String DELETE_ENTRY_INVALID_INPUT = "\t Please enter a valid positive integer " + + "for the entryID you wish to delete.\n" + + "\t Example input: calories delete ENTRY_ID"; @Test public void addEntry_validInput_entryAdded() { @@ -94,7 +98,7 @@ public void testPrintCalorieListNonEmpty() { calorieList.addEntry("calories in burger king c/200 d/2024-03-14"); calorieList.printCalorieList(); System.setOut(System.out); - String expectedOutput = addedEntryHeader + lineSeparator + + String expectedOutput = ADDED_ENTRY_HEADER + lineSeparator + "\t " + calorieList.getEntry(0).toString() + lineSeparator + "\t Your Caloric List:" + lineSeparator + lineSeparator + "\t Your Caloric Inflow List:" + lineSeparator + @@ -118,7 +122,7 @@ public void testPrintCalorieListMultipleEntries() { System.setOut(System.out); StringBuilder expectedOutput = new StringBuilder(); for (int i = 0; i < 5; i++) { - expectedOutput.append(addedEntryHeader) + expectedOutput.append(ADDED_ENTRY_HEADER) .append(lineSeparator).append("\t ").append(calorieList.getEntry(i).toString()) .append(lineSeparator); } @@ -164,7 +168,7 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { // expected output string for adding entries for (int i = 1; i < 7; i++) { - expectedOutput.append(addedEntryHeader) + expectedOutput.append(ADDED_ENTRY_HEADER) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) .append(lineSeparator); @@ -210,13 +214,13 @@ public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() StringBuilder expectedOutput = new StringBuilder(); // expected output string for adding entries for (int i = 1; i < 4; i++) { - expectedOutput.append(addedEntryHeader) + expectedOutput.append(ADDED_ENTRY_HEADER) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) .append(lineSeparator); } // expected output string for deleting entry - expectedOutput.append(deleteEntryHeader) + expectedOutput.append(DELETE_ENTRY_HEADER) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(3)).toString()) .append(lineSeparator); @@ -256,14 +260,14 @@ public void testAddEntry_addAndDeleteEntries_entryIDIncrementsProperly() { StringBuilder expectedOutput = new StringBuilder(); // expected output string for first 3 added entries for (int i = 1; i < 4; i++) { - expectedOutput.append(addedEntryHeader) + expectedOutput.append(ADDED_ENTRY_HEADER) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) .append(lineSeparator); } // expected output string for first deleted entry - expectedOutput.append(deleteEntryHeader) + expectedOutput.append(DELETE_ENTRY_HEADER) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(3)).toString()) .append(lineSeparator); @@ -272,7 +276,7 @@ public void testAddEntry_addAndDeleteEntries_entryIDIncrementsProperly() { calorieList.addEntry("calories in yong tau foo c/688 d/2022-02-22 m/10,10,10"); // expected output string for fourth added entry - expectedOutput.append(addedEntryHeader) + expectedOutput.append(ADDED_ENTRY_HEADER) .append(lineSeparator).append("\t ") .append(calorieList.getEntry(calorieList.getIndexFromEntryID(4)).toString()) .append(lineSeparator); @@ -298,4 +302,42 @@ public void testAddEntry_addAndDeleteEntries_entryIDIncrementsProperly() { assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(3, calorieList.getSize()); } + + + @Test + public void testDeleteEntry_invalidInputs_exceptionThrown() { + String lineSeparator = System.lineSeparator(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + + CalorieList calorieList = new CalorieList(); + calorieList.addEntry("calories in burger king c/200 d/2022-02-22"); + calorieList.addEntry("calories out Walk c/150 d/2022-02-22"); + calorieList.addEntry("calories in acai c/500 d/2022-02-22"); + + System.setOut(System.out); + StringBuilder expectedOutput = new StringBuilder(); + // expected output string for adding entries + for (int i = 1; i < 4; i++) { + expectedOutput.append(ADDED_ENTRY_HEADER) + .append(lineSeparator).append("\t ") + .append(calorieList.getEntry(calorieList.getIndexFromEntryID(i)).toString()) + .append(lineSeparator); + } + + // Invalid Inputs for calories delete + calorieList.deleteEntry("calories delete "); + calorieList.deleteEntry("calories delete"); + calorieList.deleteEntry("calories delete a b"); + calorieList.deleteEntry("calories delete 1 2"); + calorieList.deleteEntry("calories delete $12"); + + // expected output string for invalid entry inputs + for (int i = 0; i < 5; i++) { + expectedOutput.append(DELETE_ENTRY_INVALID_INPUT).append(lineSeparator); + } + + assertEquals(expectedOutput.toString(), outputStream.toString()); + assertEquals(3, calorieList.getSize()); + } } From 0b72d55757747b2dfbe3bd5d688325f035f205fb Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 16:31:42 +0800 Subject: [PATCH 256/414] Add nicer message for delete entry exception --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 2 +- src/main/java/seedu/lifetrack/ui/CalorieListUi.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 5d27a9700e..f201b0a198 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -89,7 +89,7 @@ public void deleteEntry(String line) { updateFile(); } } catch (IndexOutOfBoundsException e) { - System.out.println(CalorieListUi.deleteLogIndexMessage()); + System.out.println(CalorieListUi.deleteLogNumberMessage()); } catch (NumberFormatException e) { System.out.println(CalorieListUi.deleteLogNumberMessage()); } diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index e4b0db6ba8..3457ce7934 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -4,6 +4,8 @@ import seedu.lifetrack.Entry; public class CalorieListUi { + private static final String CALORIES_DELETE_SAMPLE_INPUT = "\t Example input: calories delete ENTRY_ID"; + public static void successfulDeletedMessage(Entry toDelete) { System.out.println("\t The following calorie record has been successfully deleted!"); @@ -23,7 +25,8 @@ public static String deleteLogIndexMessage() { } public static String deleteLogNumberMessage() { - return "\t Please enter a valid index!"; + return "\t Please enter a valid positive integer for the entryID you wish to delete.\n" + + CALORIES_DELETE_SAMPLE_INPUT; } public static void calorieListHeader() { From 68d050634449702a440cb5830ada7131c59082df Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 10 Apr 2024 16:37:36 +0800 Subject: [PATCH 257/414] remove ErrorMessage class, --- .../calories/calorielist/CalorieList.java | 1 - .../hydrationlist/HydrationList.java | 1 - .../lifetrack/sleep/sleeplist/SleepList.java | 1 - .../system/exceptions/ErrorMessages.java | 81 ------------------- .../InvalidInputExceptionMessage.java | 78 ++++++++++++++++-- .../system/parser/ParserCalories.java | 4 +- .../system/parser/ParserHydration.java | 2 +- .../lifetrack/system/parser/ParserSleep.java | 4 +- .../lifetrack/system/parser/ParserUser.java | 20 ++--- .../lifetrack/system/storage/FileHandler.java | 9 ++- src/main/java/seedu/lifetrack/user/User.java | 1 - .../seedu/lifetrack/ParserCaloriesTest.java | 4 +- text-ui-test/EXPECTED.TXT | 4 - 13 files changed, 93 insertions(+), 117 deletions(-) delete mode 100644 src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index b274af979a..2ffd62d219 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -2,7 +2,6 @@ package seedu.lifetrack.calories.calorielist; import seedu.lifetrack.Entry; -import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserCalories; import seedu.lifetrack.system.storage.FileHandler; diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 2cc6b5c63a..af6006eee8 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -2,7 +2,6 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserHydration; import seedu.lifetrack.system.storage.FileHandler; diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 9cf13c7039..b0ec76c5b1 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -2,7 +2,6 @@ package seedu.lifetrack.sleep.sleeplist; import seedu.lifetrack.Entry; -import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserSleep; import seedu.lifetrack.system.storage.FileHandler; diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java deleted file mode 100644 index 584ddd1e00..0000000000 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ /dev/null @@ -1,81 +0,0 @@ -//@@author owx0130 -package seedu.lifetrack.system.exceptions; - -/** - * Utility class for managing error messages related to the application. - */ -public class ErrorMessages { - - private static final String WHITESPACE = " "; - - public static String getIncorrectCaloriesInputMessage() { - return "\t Please input only positive integers into the calories field!"; - } - - public static String getIncorrectVolumeInputMessage() { - return "\t Please input only positive integers into the volume field!"; - } - - public static String getIncorrectMacrosInputMessage() { - return "\t Please input only positive integers into the macronutrients field!"; - } - - public static String getInvalidNumberOfSetUpInputs() { - return "\t Sorry, this command is invalid. Please enter the setup command in the following format " + - "user setup {NAME} h/{HEIGHT} w/{WEIGHT} a/{AGE} s/{SEX} e/{EXERCISE LEVELS} g/{GOAL}"; - } - - public static String getInvalidGoalNumberMessage() { - return "\t Invalid input for goal number. Please enter a number between 1 and 5."; - } - - public static String getInvalidExerciseLevelsNumberMessage() { - return "\t Invalid input for exercise level. Please enter a number between 1 and 5."; - } - - public static String getIOExceptionMessage() { - return "\t Unable to write to file!"; - } - - public static String getFileNotFoundMessage() { - return WHITESPACE + "No file found! The initialised list will be empty."; - } - - public static String getIncorrectSleepInputMessage() { - return "\t Please input one positive real number into the sleep duration field!"; - } - - public static String getIncorrectSleepDateInputMessage() { - return "\t Error: Date must be in YYYY-MM-DD format.!"; - } - - public static String getHeightOutOfRangeMessage() { - return "\t Please enter a valid height!"; - } - public static String getInvalidHeightNumberMessage(){ - return "\t Please enter your height(in cm) as an integer!"; - } - public static String getWeightOutOfRangeMessage(){ - return "\t Please enter a valid weight!"; - } - public static String getInvalidWeightNumberMessage(){ - return "\t Please enter your weight(in kg) as an integer!"; - } - public static String getUnderAgeMessage(){ - return "\t You are too young to use this app :("; - } - - public static String getAgeOutOfRangeMessage(){ - return "\t Please enter a valid age!"; - } - - public static String getInvalidAgeNumberMessage(){ - return "\t Please enter your age as an integer!"; - } - public static String getTooLongSleepDurationMessage() { - return "\t Please enter a sleep duration less than 24 hours."; - } - public static String getInvalidSleepDateMessage() { - return "\t Please enter a valid date!"; - } -} diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 4785df0830..26c5531295 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -22,12 +22,16 @@ public static String getInvalidDateMessage() { return message; } - //calories list related methods + //calories list related messages public static String getCaloriesIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed the input in the correct order!\n"; return HEADER + message + CALORIES_IN_INPUT; } + public static String getIncorrectCaloriesInputMessage() { + return "\t Please input only positive integers into the calories field!"; + } + public static String getCaloriesMissingKeywordsMessage() { String message = "\t Please ensure that you have entered all keywords!\n"; return HEADER + message + CALORIES_IN_INPUT; @@ -43,6 +47,10 @@ public static String getEmptyMacrosMessage() { return HEADER + message + MACROS_INPUT; } + public static String getIncorrectMacrosInputMessage() { + return "\t Please input only positive integers into the macronutrients field!"; + } + public static String getIncompleteMacrosMessage() { String message = "\t Please ensure that all macronutrients fields are filled up!\n"; return HEADER + message + MACROS_INPUT; @@ -58,17 +66,12 @@ public static String getMacrosInCaloriesOutMessage() { return HEADER + message + CALORIES_OUT_INPUT; } - //hydration list related methods + //hydration list related messages public static String getHydrationMissingKeywordMessage() { String message = "\t Please ensure that you have entered all keywords!\n"; return HEADER + message + HYDRATION_IN_INPUT; } - public static String getSleepMissingKeywordMessage() { - String message = "\t Please ensure that you have entered all keywords!\n"; - return HEADER + message + SLEEP_IN_INPUT; - } - public static String getHydrationIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed the input in the correct order!\n"; return HEADER + message + HYDRATION_IN_INPUT; @@ -84,7 +87,25 @@ public static String getHydrationNegativeIntegerVolumeMessage() { return HEADER + message + HYDRATION_IN_INPUT; } - // User related Messages + public static String getIncorrectVolumeInputMessage() { + return "\t Please input only positive integers into the volume field!"; + } + + //sleep list related messages + public static String getSleepMissingKeywordMessage() { + String message = "\t Please ensure that you have entered all keywords!\n"; + return HEADER + message + SLEEP_IN_INPUT; + } + + public static String getIncorrectSleepInputMessage() { + return "\t Please input one positive real number into the sleep duration field!"; + } + + public static String getTooLongSleepDurationMessage() { + return "\t Please enter a sleep duration less than 24 hours."; + } + + //user related messages public static String getOutOfGoalRangeMessage() { return "\t Please key in a number between 1 and 5! 1 being quick fat loss " + "and 5 being quick bulking"; @@ -98,4 +119,45 @@ public static String getOutOfExerciseLevelsRangeMessage() { public static String getEmptyUserSetupInputMessage() { return HEADER + "\t Please key in the relevant user fields!\n" + USER_SETUP_INPUT; } + + public static String getInvalidNumberOfSetUpInputs() { + return "\t Sorry, this command is invalid. Please enter the setup command in the following format " + + "user setup {NAME} h/{HEIGHT} w/{WEIGHT} a/{AGE} s/{SEX} e/{EXERCISE LEVELS} g/{GOAL}"; + } + + public static String getInvalidGoalNumberMessage() { + return "\t Invalid input for goal number. Please enter a number between 1 and 5."; + } + + public static String getInvalidExerciseLevelsNumberMessage() { + return "\t Invalid input for exercise level. Please enter a number between 1 and 5."; + } + + public static String getHeightOutOfRangeMessage() { + return "\t Please enter a valid height!"; + } + + public static String getInvalidHeightNumberMessage(){ + return "\t Please enter your height(in cm) as an integer!"; + } + + public static String getWeightOutOfRangeMessage(){ + return "\t Please enter a valid weight!"; + } + + public static String getInvalidWeightNumberMessage(){ + return "\t Please enter your weight(in kg) as an integer!"; + } + + public static String getUnderAgeMessage(){ + return "\t You are too young to use this app :("; + } + + public static String getAgeOutOfRangeMessage(){ + return "\t Please enter a valid age!"; + } + + public static String getInvalidAgeNumberMessage(){ + return "\t Please enter your age as an integer!"; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 1d7b53bb49..f4daa7eb2a 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -6,8 +6,8 @@ import seedu.lifetrack.calories.Activity; import seedu.lifetrack.calories.Food; import seedu.lifetrack.system.exceptions.InvalidInputException; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectCaloriesInputMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectMacrosInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectCaloriesInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectMacrosInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index c6676512e0..df2afe03d6 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -8,7 +8,7 @@ import java.time.LocalDate; import java.time.format.DateTimeParseException; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectVolumeInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectVolumeInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationMissingKeywordMessage; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index efcaaee7c1..97ebfaa3ac 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -9,8 +9,8 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectSleepInputMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getTooLongSleepDurationMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectSleepInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getTooLongSleepDurationMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getSleepMissingKeywordMessage; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index cbd117923e..cd7dc9d88a 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -3,16 +3,16 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.user.User; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getAgeOutOfRangeMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getHeightOutOfRangeMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidAgeNumberMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidExerciseLevelsNumberMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGoalNumberMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidHeightNumberMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidNumberOfSetUpInputs; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidWeightNumberMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getUnderAgeMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getWeightOutOfRangeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getAgeOutOfRangeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHeightOutOfRangeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidAgeNumberMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidExerciseLevelsNumberMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidGoalNumberMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidHeightNumberMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidNumberOfSetUpInputs; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidWeightNumberMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnderAgeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 4c0bfb2662..a6c882c827 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -15,7 +15,6 @@ import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.sleep.sleeplist.SleepEntry; -import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.user.User; public class FileHandler { @@ -23,6 +22,7 @@ public class FileHandler { //public member for lastEntryID calories public static int maxCaloriesID = 0; public static int maxHydrationID = 0; + //general list constants private static final int ENTRYID_INDEX = 0; private static final int DATE_INDEX = 1; @@ -51,6 +51,9 @@ public class FileHandler { private static final int GOAL_INDEX = 6; private static final int REQ_CAL_INDEX = 7; + //error message for IO exception + private static final String message = "\t Unable to write to file!"; + private String filePath; public FileHandler(String filePath) { @@ -75,7 +78,7 @@ public void writeUserData(User user) { try { writeToFile(user.toFileFriendlyString()); } catch (IOException e) { - System.out.println(ErrorMessages.getIOExceptionMessage()); + System.out.println(message); } } @@ -87,7 +90,7 @@ public void writeEntries(ArrayList entries) { } writeToFile(newData); } catch (IOException e) { - System.out.println(ErrorMessages.getIOExceptionMessage()); + System.out.println(message); } } diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 5cad629082..462c56cf6d 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -1,6 +1,5 @@ package seedu.lifetrack.user; -import seedu.lifetrack.system.exceptions.ErrorMessages; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.storage.FileHandler; import seedu.lifetrack.user.usergoals.UserGoals; diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index d77522386f..3e27f0c024 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -5,8 +5,8 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectCaloriesInputMessage; -import static seedu.lifetrack.system.exceptions.ErrorMessages.getIncorrectMacrosInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectCaloriesInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectMacrosInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 14048a91a8..2f91cf0ff2 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,3 @@ - No file found! The initialised list will be empty. - No file found! The initialised list will be empty. - No file found! The initialised list will be empty. - No file found! The initialised list will be empty. Hello from From b5e4ad148bbe6de18f792c45c74227d441ed81ad Mon Sep 17 00:00:00 2001 From: owx0130 Date: Wed, 10 Apr 2024 16:42:58 +0800 Subject: [PATCH 258/414] fix checkstyle error --- src/main/java/seedu/lifetrack/user/User.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 462c56cf6d..57a4445bd2 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -52,6 +52,7 @@ public User(String filePath) { goal = Integer.parseInt(data.get(GOAL_INDEX)); caloriesRequired = Integer.parseInt(data.get(REQ_CAL_INDEX)); } catch (FileNotFoundException e) { + return; } } From bf6694aee89c27bf49eca84c59d4c9e1793b61cd Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 17:16:28 +0800 Subject: [PATCH 259/414] Add own image into AboutUs.md --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 8b2c42767b..a3428887ca 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,5 +4,5 @@ Display | Name | Github Profile | Portfoli --------|:--------------:|:------------------------------------------:|:---------: ![](https://avatars.githubusercontent.com/u/64789669?v=4) | Paturi Karthik | [Github](https://github.com/paturikarthik) | [Portfolio](docs/team/paturikarthik.md) ![](https://avatars.githubusercontent.com/u/110764881?s=400&u=f41e3f40315f394bd71538063882c06bcfa2b624&v=4) | Shawn Pong | [Github](https://github.com/shawnpong) | [Portfolio](docs/team/shawnpong.md) -![](https://drive.google.com/file/d/1BM0lQP13brp_vlVJsgHYzQHSWaREHrnP/view?usp=drive_link) | Rex Yong Jin Xiang | [Github](https://github.com/rexyyong) | [Portfolio](docs/team/johndoe.md) +![](https://avatars.githubusercontent.com/u/76645229?s=400&u=235aba3bc4b5de57d0a76e24506f094e28734637&v=4) | Rex Yong Jin Xiang | [Github](https://github.com/rexyyong) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Yanyu | [Github](https://github.com/a-wild-chocolate/) | [Portfolio](docs/team/johndoe.md) From f03e1b16c16bc2792cc67bd51df71e8548ff9f7c Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 17:44:29 +0800 Subject: [PATCH 260/414] Edit userguide for bug fixing and new features --- docs/UserGuide.md | 58 ++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 95f94fec2e..939ad84587 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -67,9 +67,9 @@ Macronutrients such as Carbohydrates, Proteins and Fats can be included if neede `calories in DESCRIPTION c/CALORIES d/DATE [m/CARBOHYDRATES,PROTEIN,FATS]` * The `DESCRIPTION` refers to the food that the person consumed. -* The `CALORIES` must be a positive integer 1, 2, 3, …, measured in kcal. +* The `CALORIES` must be a positive **integer** 1, 2, 3, …, measured in kcal. * The `DATE` provided should be of the form YYYY-MM-DD, such as 2024-03-04. -* Macronutrients field including `CARBOHYDRATES`, `PROTEINS` and `FATS` is optional. The macronutrients must be a positive integer 1, 2, 3, measured in grams. +* Macronutrients field including `CARBOHYDRATES`, `PROTEINS` and `FATS` is optional. The macronutrients must be a positive **integer** 1, 2, 3, measured in grams. **Examples:** * `calories in chicken rice c/678 d/2022-02-24` @@ -85,7 +85,7 @@ Adds a calorie burning activity into the calories tracker. `calories out DESCRIPTION c/CALORIES d/DATE` * The `DESCRIPTION` refers to any activity that resulted in loss of calories. -* The `CALORIES` must be a positive integer 1, 2, 3, …, measured in kcal. +* The `CALORIES` must be a positive **integer** 1, 2, 3, …, measured in kcal. * The `DATE` provided should be of the form YYYY-MM-DD such as 2024-04-03. **Examples:** @@ -95,7 +95,8 @@ Adds a calorie burning activity into the calories tracker. * `calories out go gym c/300 d/2024-04-03` ### Listing calorie items: `calories list` -Shows a list of all activities in the calories tracker. Includes both calories in and out. +Shows a list of all activities in the calories tacker. Calories inflow and outflow are displayed separately. +All entries are sorted by date, in ascending order, from earlier dates to present dates. **Format:** `calories list` @@ -103,25 +104,27 @@ Shows a list of all activities in the calories tracker. Includes both calories i #### Expected output ----------------------------------------------------------------------------- Your Caloric List: - 1. Date: 2024-06-15, Description: chicken, Calories: 1000 - 2. Date: 2024-06-15, Description: chicken, Calories: 1000 - 3. Date: 2024-05-15, Description: chicken, Calories: 1000 - 4. Date: 2023-03-01, Description: taco, Calories: 1 - 5. Date: 2024-04-03, Description: burger, Calories: 100 - 6. Date: 2024-04-03, Description: cai png, Calories: 1000 - 7. Date: 2024-04-03, Description: cai png, Calories: 500 - 8. Date: 2024-04-13, Description: liho milk tea, Calories: 200 + + Your Caloric Inflow List: + 1. EntryID: 2, Date: 2024-02-02, Description: commhall dinner, Calories: 6969 (C: 69, P: 69, F: 69) + 2. EntryID: 1, Date: 2024-04-09, Description: wingstop, Calories: 1000 (C: 100, P: 100, F: 100) + + Your Caloric Outflow List: + 1. EntryID: 3, Date: 2024-01-01, Description: gym, Calories: 2000 + 2. EntryID: 4, Date: 2024-04-09, Description: sprint, Calories: 400 ----------------------------------------------------------------------------- + ### Deleting a calorie item: `calories delete` -Deletes the specified activity from the calories tracker. +Deletes the specified entry from the calories tracker according to the `ENTRYID`. **Format:** -`calories delete INDEX` +`calories delete ENTRYID` +* The `ENTRYID` must be a positive **integer** 1, 2, 3 and so on. **Examples:** -* `calories list` followed by `calories delete 2` deletes the 2nd activity in the calories tracker. +* `calories list` followed by `calories delete 2` deletes the entry with `ENTRYID` 2 in the calories tracker. ## Hydration Tracker @@ -155,15 +158,14 @@ Show the list of all hydration records in the hydration tracker. ----------------------------------------------------------------------------- ### Deleting a hydration item: `hydration delete` -Deletes the hydration record according to the index. +Deletes the specified hydration entry according to the `ENTRYID`. **Format:** -`hydration delete INDEX` - -Delete the drinking water record at the specific index. The index refers to the index number shown in the displayed Hydration list. The index must be a positive integer 1, 2, 3, …​ +`hydration delete ENTRYID` +* The `ENTRYID` must be a positive **integer** 1, 2, 3 and so on. **Examples:** -* `hydration list` followed by `hydration delete 2` deletes the 2nd hydration record from the hydration tracker. +* `hydration list` followed by `hydration delete 2` deletes the entry with `ENTRYID` 2 in the hydration tracker. ## Sleep Tracker @@ -188,16 +190,16 @@ Show the list of all sleep records in the sleep tracker. `sleep list` ### Deleting a sleep record: `sleep delete` -Deletes the sleep record according to the sleep id. +Deletes the specified sleep entry according to the `SLEEPID`. **Format:** `sleep delete SLEEPID` -* Delete the sleep record with specific id. -* The id refers to the id number shown in the displayed sleeping records list. -* The id must be a positive integer 1, 2, 3, …​ +* Delete the sleep record with specified `SLEEPID`. +* The `SLEEPID` refers to the id number shown in the displayed sleeping records list. +* The `SLEEPID` must be a positive integer 1, 2, 3, …​ **Examples:** -* `list` followed by `sleep delete 2` deletes the sleep record with id 2 from the sleep tracker. +* `list` followed by `sleep delete 2` deletes the sleep record with `SLEEPID` 2 from the sleep tracker. ## User Profile @@ -294,13 +296,13 @@ The user should be able to quickly edit their details without having to run the | Add calories intake | `calories in DESCRIPTION c/CALORIES d/DATE [m/CARBOHYDRATES,PROTEIN,FATS]` | | Add calories outflow | `calories out DESCRIPTION c/CALORIES d/DATE` | | List calories | `calories list` | -| Delete calories entry | `calories delete INDEX` | +| Delete calories entry | `calories delete ENTRYID` | | Add hydration intake | `hydration in DESCRIPTION v/VOLUME d/DATE` | | List hydration | `hydration list` | -| Delete hydration entry | `hydration delete INDEX` | +| Delete hydration entry | `hydration delete ENTRYID` | | Add sleep | `sleep add DURATION d/DATE` | | List sleep | `sleep list` | -| Delete sleep entry | `sleep delete INDEX` | +| Delete sleep entry | `sleep delete SLEEPID` | | Set Up User Profile | `user setup NAME h/HEIGHT w/WEIGHT a/AGE s/GENDER e/EXERCISE LEVELS g/BODY GOAL` | | Check User Progress | `user progress` | From 7e03d8840a2081fb4463138f0cd2a32cb4d6fbdf Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 17:51:21 +0800 Subject: [PATCH 261/414] Add message to inform user that list is empty if they try to delete from empty calorie list --- .../seedu/lifetrack/calories/calorielist/CalorieList.java | 4 +++- src/main/java/seedu/lifetrack/ui/CalorieListUi.java | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 8bfd4533aa..3dfe45c8f5 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -79,7 +79,9 @@ public void deleteEntry(String line) { try { int entryID = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); int index = getIndexFromEntryID(entryID); - if (index == NO_INDEX_FOUND) { + if (calorieArrayList.isEmpty()) { + CalorieListUi.emptyCalorieList();; + } else if (index == NO_INDEX_FOUND) { CalorieListUi.unsuccessfulDeletedMessage(entryID); } else { Entry toDelete = calorieArrayList.get(index); diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 613fdd68db..986f5aee54 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -16,6 +16,10 @@ public static void unsuccessfulDeletedMessage(int entryID) { "not be found"); } + public static void emptyCalorieList() { + System.out.println("\t There is nothing to delete as the calorie list is empty!"); + } + public static void emptyListMessage() { System.out.println("\t Your caloric list is empty. Add new entries to populate your list :)"); } From 9f7ca65e7bb28f6b7fe328ca3233ebd8841211e2 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 17:55:01 +0800 Subject: [PATCH 262/414] Add nicer message when entryID for calories delete cannot be found --- src/main/java/seedu/lifetrack/ui/CalorieListUi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 986f5aee54..dcb56217a9 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -13,7 +13,7 @@ public static void successfulDeletedMessage(Entry toDelete) { } public static void unsuccessfulDeletedMessage(int entryID) { System.out.println("\t The following calorie record corresponding to entry ID " + entryID + " could " + - "not be found"); + "not be found\n" + "\t Refer to calories list to see valid entry IDs."); } public static void emptyCalorieList() { From dbc21be3924c1f80e4249d02c400f653ef416a38 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 18:15:18 +0800 Subject: [PATCH 263/414] Fix bug where entries with date later than current date can be added --- .../system/exceptions/InvalidInputExceptionMessage.java | 7 +++++++ .../java/seedu/lifetrack/system/parser/ParserCalories.java | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 26c5531295..d364dca1b1 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -1,6 +1,8 @@ //@@author owx0130 package seedu.lifetrack.system.exceptions; +import java.time.LocalDate; + /** * Utility class for managing error messages related to invalid input exceptions. */ @@ -22,6 +24,11 @@ public static String getInvalidDateMessage() { return message; } + public static String getDateLaterThanPresentDateMessage() { + String message = "\t Invalid date! Please enter a date that is not later than today's date: " + LocalDate.now(); + return message; + } + //calories list related messages public static String getCaloriesIncorrectOrderMessage() { String message = "\t Please ensure that you have keyed the input in the correct order!\n"; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index f4daa7eb2a..b2e27d1a03 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -16,6 +16,7 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesMissingKeywordsMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getDateLaterThanPresentDateMessage; import seedu.lifetrack.Entry; @@ -101,6 +102,10 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv LocalDate date = null; try { date = getLocalDateFromInput(strDate); + + if (date.isAfter(LocalDate.now())) { + throw new InvalidInputException(getDateLaterThanPresentDateMessage()); + } } catch (DateTimeParseException e) { throw new InvalidInputException(getInvalidDateMessage()); } From 811edf9e443b708c2c6fb7fec442a344197f42c0 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Wed, 10 Apr 2024 20:40:49 +0800 Subject: [PATCH 264/414] fix sleep add cannot add future date and delete msg for empty list --- .../lifetrack/sleep/sleeplist/SleepList.java | 5 +++++ .../lifetrack/system/parser/ParserSleep.java | 4 ++++ .../java/seedu/lifetrack/SleepListTest.java | 22 +++++++++---------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 099bb67d9a..29db88e20f 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -58,6 +58,11 @@ public void addSleep(String input) { public void deleteSleep(String line) { try { int index = Integer.parseInt(line.split(" ")[DELETE_IDX]) ; //User input format: sleep delete ID + if(sleepList.isEmpty()){ + System.out.println("Sorry, there is no sleep record in the sleep list. " + + "You cannot delete sleep entry."); + return; + } for(int i=0; i Date: Wed, 10 Apr 2024 21:30:45 +0800 Subject: [PATCH 265/414] Add PPP for rex --- docs/team/rexyyong.md | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/team/rexyyong.md diff --git a/docs/team/rexyyong.md b/docs/team/rexyyong.md new file mode 100644 index 0000000000..c0cb6a172c --- /dev/null +++ b/docs/team/rexyyong.md @@ -0,0 +1,62 @@ +# Rex Yong's Project Portfolio Page +## Project: LifeTrack +LifeTrack is a desktop app for students to track their health data, +optimized for use via a Command Line Interface (CLI). +It tracks calories, hydration and sleep data for the user, +while also providing daily recommendations for calorie and hydration intake, +based on the user's build and gender, as well as their body goals and activity levels. + +The program was created using Java. Version control was done using Git. + +## My contributions to the project + +### New features added and enhancements to existing features +1. **Added the ability to add entries for calories intake. [PR #12](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/12)** + * What it does: Allows users to add records relating to their calories intake, which includes + `DESCRIPTION` of food, amount of `CALORIES`, as well as `DATE`. + * Testing: Added JUnit tests for the feature as well. + +2. **Added date format using Class LocalDate for Class Calories, Hydration and Sleep. [PR #83](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/83)** + * What it does: Allows users to key in dates into their entries. + * Justification: By using the Java LocalDate Class, it allows methods to check if dates keyed + in by users is valid. It also allows easier comparison of dates among entries. + * Highlights: This enhancement affects existing commands and commands to be added in future. It required + the editing of many other classes to change the `DATE` from type String to type LocalDate. + * Testing: Edited JUnit tests to follow LocalDate format. + +3. **Added `entryID` for calories entries in calories list. [PR #153](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/153)** + * What it does: Ensures that all entries added by users will be tagged to a unique `entryID`. + * Justification: This allows users to remove calories entries based on the `entryID`, instead of + removing entries based on the index of entry in the array list. + * Highlights: This enhancement affects existing commands and commands to be added in future. It required + the editing of many other classes to ensure that all entries will be given an `entryID`. + +4. **Added feature to sort entries by `DATE`. [PR #161](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/161)** + * What it does: Ensures that dates of entries are sorted in ascending order. + * Justification: So that user can see the breakdown of calories entries grouped by `DATE`. + +5. **Added feature to print calories list grouped by calories in and calories out. [PR #154](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/154)** + * What it does: Prints the calories list grouped by calories in and calories out data. + * Justification: So that user can distinguish which entries are for calories in and calories out. + +### Contributions to exception handling +* Added robust exception handling for calories Parser, in order to ensure that program does not crash + when users type the wrong command to add, delete or list calories entries. [PR #43](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/43) +* Added robust exception handling for hydration features, in order to ensure that program does not crash + when suers type the wrong command to add, delete or list hydration entries. [PR #50](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/50) + +### Contributions to documentation +* **Developer guide** + * Added implementation details for `calories list` feature. [PR #74](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/74) +* **User guide** + * Added documentation for features `calories in`, `calories out`, `calories list`. + Also added quick links as well as command summary. [PR #100](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/100) + +### Contributions to project management +* In charge of organising and documenting project meeting minutes and scheduling project meetings. + * Click [here](https://docs.google.com/document/d/1hQchbh4mrso-WWNApsfkhvX7QF_kqfvNnIorwwQwjzU/edit) + to view the meeting minutes document. + +### Code contributed +* (Clink RepoSense link and search for rexyyong to see my code contributed: +[RepoSense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=rexyyong&tabRepo=AY2324S2-CS2113-F15-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) \ No newline at end of file From 48a14818c766f9e339640f6f37c9edcb15dffd27 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Wed, 10 Apr 2024 22:04:44 +0800 Subject: [PATCH 266/414] fix bugs from PE-D --- docs/UserGuide.md | 2 +- .../calories/calorielist/CalorieList.java | 11 +- .../system/exceptions/ErrorMessages.java | 32 +++-- .../InvalidInputExceptionMessage.java | 14 ++ .../lifetrack/system/parser/ParserUser.java | 123 +++++++++++++++--- src/main/java/seedu/lifetrack/ui/Ui.java | 27 ++-- src/main/java/seedu/lifetrack/user/User.java | 11 ++ .../lifetrack/user/usergoals/UserGoals.java | 9 +- 8 files changed, 187 insertions(+), 42 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 86b86f8328..507344797f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -208,7 +208,7 @@ Creates/edits an existing user profile. `user setup NAME h/HEIGHT w/WEIGHT a/AGE s/GENDER e/EXERCISE LEVELS g/BODY GOAL` * The height provided must be an integer between 90 and 225 cms. * The weight provided must be an integer between 30 and 200 kgs. -* The age provided must be an integer between 13 and 110 years old. +* The age provided must be an integer between 13 and 30 years old. * The gender provided must be either `male` or `female`. * The exercise levels provided must be an integer between 1 and 5. * The body goal provided must be an integer between 1 and 5. diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 398a75ef5f..45391bb58b 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -181,9 +181,14 @@ public int getSize() { */ public int getCaloriesConsumed() { int totalCalories = 0; - for (int i = 0; i < calorieArrayList.size(); i++) { - InputEntry tempEntry = (InputEntry) calorieArrayList.get(i); - totalCalories += tempEntry.getCalories(); + for (Entry entry : calorieArrayList) { + if (entry instanceof InputEntry) { + InputEntry tempEntry = (InputEntry) entry; + totalCalories += tempEntry.getCalories(); + } else if (entry instanceof OutputEntry) { + OutputEntry tempEntry = (OutputEntry) entry; + totalCalories -= tempEntry.getCalories(); + } } return totalCalories; } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java index a0b1e6f218..fa19adca6f 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/ErrorMessages.java @@ -7,7 +7,7 @@ public class ErrorMessages { private static final String WHITESPACE = " "; - public static void printIndexOutOfBoundsError(){ + public static void printIndexOutOfBoundsError() { System.out.println("\t Sorry, this index is invalid. Please enter a positive integer " + "within the size of the list."); } @@ -60,24 +60,40 @@ public static String getIncorrectSleepDateInputMessage() { public static String getHeightOutOfRangeMessage() { return "\t Please enter a valid height!"; } - public static String getInvalidHeightNumberMessage(){ + + public static String getInvalidHeightNumberMessage() { return "\t Please enter your height(in cm) as an integer!"; } - public static String getWeightOutOfRangeMessage(){ + + public static String getWeightOutOfRangeMessage() { return "\t Please enter a valid weight!"; } - public static String getInvalidWeightNumberMessage(){ + + public static String getInvalidWeightNumberMessage() { return "\t Please enter your weight(in kg) as an integer!"; } - public static String getUnderAgeMessage(){ + + public static String getUnderAgeMessage() { return "\t You are too young to use this app :("; } - public static String getAgeOutOfRangeMessage(){ - return "\t Please enter a valid age!"; + public static String getAgeOutOfRangeMessage() { + return "\t Please enter a valid age between 13 and 30!"; } - public static String getInvalidAgeNumberMessage(){ + public static String getInvalidAgeNumberMessage() { return "\t Please enter your age as an integer!"; } + + public static String getEmptyGenderInputMessage() { + return "\t Please enter your gender!"; + } + + public static String getInvalidGenderInputMessage() { + return "\t Please enter either male or female as your gender!"; + } + + public static String getEmptyNameInputMessage() { + return "\t Please enter a non-empty name!"; + } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index c1d28a0049..edbc4a723d 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -80,4 +80,18 @@ public static String getOutOfExerciseLevelsRangeMessage() { public static String getEmptyUserSetupInputMessage() { return HEADER + "\t Please key in the relevant user fields!\n" + USER_SETUP_INPUT; } + public static String getEmptyUserUpdateInputMessage() { + return "\t Please enter your name!"; + } + public static String getUnknownUpdateMesssage() { + return "\t Oops, I've not seen this command before!\n" + "Here are a list of possible update commands:\n" + + "1) user update name \n"+ + "2) user update height \n" + + "3) user update weight \n" + + "4) user update age \n" + + "5) user update sex \n" + + "6) user update exercise levels \n" + + "7) user update goal "; + } } + diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index cbd117923e..c8d4fa646c 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -4,9 +4,12 @@ import seedu.lifetrack.user.User; import static seedu.lifetrack.system.exceptions.ErrorMessages.getAgeOutOfRangeMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getEmptyGenderInputMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getEmptyNameInputMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getHeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidAgeNumberMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidExerciseLevelsNumberMessage; +import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGenderInputMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidGoalNumberMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidHeightNumberMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getInvalidNumberOfSetUpInputs; @@ -14,8 +17,10 @@ import static seedu.lifetrack.system.exceptions.ErrorMessages.getUnderAgeMessage; import static seedu.lifetrack.system.exceptions.ErrorMessages.getWeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserUpdateInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnknownUpdateMesssage; import static seedu.lifetrack.ui.UserUi.printUserSetUpComplete; /** @@ -23,6 +28,21 @@ */ public class ParserUser { private static final int LENGTH_OF_SETUP_COMMAND = "user setup".length(); + private static final int USER_INPUT_NAME_INDEX = 0; + private static final int USER_INPUT_HEIGHT_INDEX = 1; + private static final int USER_INPUT_WEIGHT_INDEX = 2; + private static final int USER_INPUT_AGE_INDEX = 3; + private static final int USER_INPUT_GENDER_INDEX = 4; + private static final int USER_INPUT_EXERCISE_LEVELS_INDEX = 5; + private static final int USER_INPUT_GOAL_INDEX = 6; + private static final int LENGTH_OF_UPDATE_COMMAND = "user update".length(); + private static final int LENGTH_OF_NAME = "name".length(); + private static final int LENGTH_OF_HEIGHT = "height".length(); + private static final int LENGTH_OF_WEIGHT = "weight".length(); + private static final int LENGTH_OF_AGE = "age".length(); + private static final int LENGTH_OF_SEX = "sex".length(); + private static final int LENGTH_OF_EXERCISE_LEVELS = "exercise levels".length(); + private static final int LENGTH_OF_GOAL = "goal".length(); /** * Parses the input from user to sieve out name, height, weight, age, gender, exercise levels and goals of the user @@ -52,13 +72,13 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept if (parts.length != 7) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); } - String name = parts[0].substring(LENGTH_OF_SETUP_COMMAND).trim(); - int height = parseHeightIndex(parts[1].trim()); - int weight = parseWeightIndex(parts[2].trim()); - int age = parseAgeIndex(parts[3].trim()); - String sex = parts[4].trim().toLowerCase(); - int exerciseLevels = parseExerciseLevels(parts[5].trim()); - int goal = parseGoalIndex(parts[6].trim()); + String name = parseName(parts[USER_INPUT_NAME_INDEX].substring(LENGTH_OF_SETUP_COMMAND).trim()); + int height = parseHeightIndex(parts[USER_INPUT_HEIGHT_INDEX].trim()); + int weight = parseWeightIndex(parts[USER_INPUT_WEIGHT_INDEX].trim()); + int age = parseAgeIndex(parts[USER_INPUT_AGE_INDEX].trim()); + String sex = parseGenderIndex(parts[USER_INPUT_GENDER_INDEX].trim().toLowerCase()); + int exerciseLevels = parseExerciseLevels(parts[USER_INPUT_EXERCISE_LEVELS_INDEX].trim()); + int goal = parseGoalIndex(parts[USER_INPUT_GOAL_INDEX].trim()); user.setName(name); user.setHeight(height); @@ -72,18 +92,25 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept printUserSetUpComplete(user.getName(), caloriesRequired); } + private static String parseName(String input) throws InvalidInputException { + if (input.isEmpty()) { + throw new InvalidInputException(getEmptyNameInputMessage()); + } + return input; + } + /** * Parses the user's height input for an Integer * * @param input user's height input * @return user's height as an integer * @throws InvalidInputException if the height input is not an integer or if the user's height is not between - * 90 and 225 cm + * 90 and 225 cm */ private static int parseHeightIndex(String input) throws InvalidInputException { try { int height = Integer.parseInt(input); - if (height < 90 || height > 225){ + if (height < 90 || height > 225) { throw new InvalidInputException(getHeightOutOfRangeMessage()); } return height; @@ -92,22 +119,46 @@ private static int parseHeightIndex(String input) throws InvalidInputException { } } + /** + * Parses the user's gender input for a String + * + * @param input user's gender input + * @return user's gender as a string + * @throws InvalidInputException if the gender input is empty or if it is not male/m or female/f + */ + private static String parseGenderIndex(String input) throws InvalidInputException { + try { + if (input.isEmpty()) { + throw new InvalidInputException(getEmptyGenderInputMessage()); + } + if (input.equals("male") || input.equals("m")) { + return "male"; + } + if (input.equals("female") || input.equals("f")) { + return "female"; + } + throw new InvalidInputException(getInvalidGenderInputMessage()); + } catch (NullPointerException e) { + throw new InvalidInputException(getEmptyUserSetupInputMessage()); + } + } + /** * Parses the user's weight input for an Integer * * @param input user's weight input * @return user's weight as an integer * @throws InvalidInputException if the weight input is not an integer or if the user's weight is not between - * 30 and 200 kg + * 30 and 200 kg */ private static int parseWeightIndex(String input) throws InvalidInputException { try { int weight = Integer.parseInt(input); - if (weight<30 || weight > 200){ + if (weight < 30 || weight > 200) { throw new InvalidInputException(getWeightOutOfRangeMessage()); } return weight; - } catch (NumberFormatException e){ + } catch (NumberFormatException e) { throw new InvalidInputException(getInvalidWeightNumberMessage()); } } @@ -118,19 +169,19 @@ private static int parseWeightIndex(String input) throws InvalidInputException { * @param input user's age input * @return user's age as an integer * @throws InvalidInputException if the age input is not an integer or if the user's age is not between - * 13 and 110 years old + * 13 and 30 years old */ - private static int parseAgeIndex(String input) throws InvalidInputException{ - try{ + private static int parseAgeIndex(String input) throws InvalidInputException { + try { int age = Integer.parseInt(input); - if(age <13 && age > 0){ + if (age < 13 && age > 0) { throw new InvalidInputException(getUnderAgeMessage()); } - if (age <0 || age > 110) { + if (age < 0 || age > 30) { throw new InvalidInputException(getAgeOutOfRangeMessage()); } return age; - } catch (NumberFormatException e){ + } catch (NumberFormatException e) { throw new InvalidInputException(getInvalidAgeNumberMessage()); } } @@ -206,4 +257,40 @@ private static void checkEmptyInput(String input) throws InvalidInputException { throw new InvalidInputException(getEmptyUserSetupInputMessage()); } } + + public static void parseUpdate(String input, User user) throws InvalidInputException { + checkEmptyUpdateInput(input); + String fieldToUpdate = input.substring(LENGTH_OF_UPDATE_COMMAND).trim(); + if (fieldToUpdate.startsWith("name ")) { + String name = parseName(fieldToUpdate.substring(LENGTH_OF_NAME).trim()); + user.setName(name); + } else if (fieldToUpdate.startsWith("height ")) { + int height = parseHeightIndex(fieldToUpdate.substring(LENGTH_OF_HEIGHT).trim()); + user.setHeight(height); + } else if (fieldToUpdate.startsWith("weight ")) { + int weight = parseWeightIndex(fieldToUpdate.substring(LENGTH_OF_WEIGHT).trim()); + user.setWeight(weight); + } else if (fieldToUpdate.startsWith("age ")) { + int age = parseAgeIndex(fieldToUpdate.substring(LENGTH_OF_AGE).trim()); + user.setAge(age); + } else if (fieldToUpdate.startsWith("exercise levels ")) { + int level = parseExerciseLevels(fieldToUpdate.substring(LENGTH_OF_EXERCISE_LEVELS).trim()); + user.setExerciseLevels(level); + } else if (fieldToUpdate.startsWith("goal ")){ + int goal = parseGoalIndex(fieldToUpdate.substring(LENGTH_OF_GOAL).trim()); + user.setGoal(goal); + } else if (fieldToUpdate.startsWith("sex ")) { + String sex = parseGenderIndex(fieldToUpdate.substring(LENGTH_OF_SEX).trim()); + user.setSex(sex); + } else { + throw new InvalidInputException(getUnknownUpdateMesssage()); + } + } + + private static void checkEmptyUpdateInput(String input) throws InvalidInputException { + if (input.substring(LENGTH_OF_UPDATE_COMMAND).trim().isEmpty()) { + throw new InvalidInputException(getEmptyUserUpdateInputMessage()); + } + } + } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 3830a9f83b..58e93796e6 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -1,3 +1,4 @@ +// @@author paturikarthik package seedu.lifetrack.ui; import seedu.lifetrack.calories.calorielist.CalorieList; @@ -34,11 +35,11 @@ public class Ui { /** * Reads in the input from the user * - * @param calorieList list containing all entries pertinent to calories + * @param calorieList list containing all entries pertinent to calories * @param hydrationList list containing all entries pertinent to liquids */ public static void readUserInput(CalorieList calorieList, HydrationList hydrationList, - User user, SleepList sleepList) { + User user, SleepList sleepList) { String line; do { line = new Scanner(System.in).nextLine(); @@ -47,8 +48,9 @@ public static void readUserInput(CalorieList calorieList, HydrationList hydratio } /** - * handles input from the user - * @param line input from the user + * handles input from the user + * + * @param line input from the user * @param calorieList list containing all entries pertinent to calories */ public static void handleCaloriesInput(String line, CalorieList calorieList) { @@ -76,9 +78,10 @@ public static void handleHydrationInput(String line, HydrationList hydrationList handleUnknownInput(); } } + public static void handleSleepInput(String line, SleepList sleepList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("sleep add")) { + if (line.startsWith("sleep add")) { sleepList.addSleep(line); } else if (line.startsWith("sleep list")) { sleepList.printSleepList(); @@ -90,8 +93,8 @@ public static void handleSleepInput(String line, SleepList sleepList) { } public static void handleUserInput(String line, CalorieList calorieList, HydrationList hydrationList, - User user ,SleepList sleepList) { - if (!line.startsWith("bye")) { + User user, SleepList sleepList) { + if (!line.trim().equalsIgnoreCase("bye")) { printLine(); line = line.trim().toLowerCase(); if (line.isEmpty()) { @@ -114,23 +117,26 @@ public static void handleUserInput(String line, CalorieList calorieList, Hydrati } public static void handleUserCommands(String line, User user) { - if (line.contains("setup")) { + if (line.startsWith("user setup")) { user.setUp(line); - } else if (line.contains("progress")) { + } else if (line.startsWith("user progress")) { handleUserProgress(user); + } else if (line.startsWith("user update")) { + user.update(line); } else { handleUnknownInput(); } } private static void handleUserProgress(User user) { - if (user.getName() == null){ + if (user.getName() == null) { printNoUserYetMessage(); } else { user.getCaloriesProgressBar(); user.getHydrationProgressBar(); } } + public static void sayHello() { System.out.println(WHITESPACE + "Hello from\n\n" + logo); System.out.println(WHITESPACE + "How can I help you today?"); @@ -187,3 +193,4 @@ public static void showHelp() { "requirement."); } } +//@@author diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 813b426958..b85c0baa60 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -1,3 +1,4 @@ +//@@ author paturikarthik package seedu.lifetrack.user; import seedu.lifetrack.system.exceptions.ErrorMessages; @@ -9,6 +10,7 @@ import java.util.ArrayList; import static seedu.lifetrack.system.parser.ParserUser.parseSetUp; +import static seedu.lifetrack.system.parser.ParserUser.parseUpdate; public class User { @@ -66,6 +68,15 @@ public void setUp(String line) { } } + public void update(String line) { + try { + parseUpdate(line, this); + fileHandler.writeUserData(this); + } catch (InvalidInputException e) { + System.out.println(e.getMessage()); + } + } + public void setName(String name) { this.name = name; } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index a06678b51c..727d1fa972 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -1,3 +1,4 @@ +//@@ author paturikarthik package seedu.lifetrack.user.usergoals; import seedu.lifetrack.user.User; @@ -13,6 +14,7 @@ public class UserGoals { private static final int BMR_AGE_MULTIPLIER = 5; private static final int BMR_MALE_MODIFIER = 5; private static final int BMR_FEMALE_MODIFIER = -161; + private static final int PROGRESS_BAR_WIDTH = 50; public static void getHealthInfo(User user) { double rawBMR = BMR_WEIGHT_MULTIPLIER * user.getWeight() + BMR_HEIGHT_MULTIPLIER * user.getHeight() @@ -56,8 +58,11 @@ private static double getAMR(double calories, int exerciseLevel) { public static void getCaloriesProgressBar(User user) { int caloriesRequired = user.getCaloriesRequired(); int caloriesConsumed = calorieList.getCaloriesConsumed(); + if (caloriesConsumed < 0){ + caloriesConsumed = 0; + } double progress = (double) caloriesConsumed / caloriesRequired; - int width = 50; + int width = PROGRESS_BAR_WIDTH; int progressWidth = (int) (width * progress); StringBuilder progressBar = new StringBuilder("["); @@ -78,7 +83,7 @@ public static void getHydrationProgressBar(User user) { int hydrationRequired = user.getHydrationRequired(); int hydrationConsumed = hydrationList.getHydrationConsumed(); double progress = (double) hydrationConsumed / hydrationRequired; - int width = 50; + int width = PROGRESS_BAR_WIDTH; int progressWidth = (int) (width * progress); StringBuilder progressBar = new StringBuilder("["); From 0984ed347057f27ed54274d5ece6b897c6fd71e8 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 23:11:31 +0800 Subject: [PATCH 267/414] Modify code such that calories is Calories ID, hydration is Hydration id --- src/main/java/seedu/lifetrack/Entry.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index 0b4f7f5074..3f2d856782 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -1,6 +1,10 @@ //@@author owx0130 package seedu.lifetrack; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; +import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; + import java.time.LocalDate; public abstract class Entry { @@ -29,7 +33,13 @@ public int getLastEntryID() { } public String toString() { - return String.format("\t EntryID: " + lastEntryID + ", Date: " + date + ", Description: " + description); + if(this instanceof InputEntry) { + return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + ", Description: " + description); + } else if (this instanceof OutputEntry) { + return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + ", Description: " + description); + } else { + return String.format("\t Hydration ID: " + lastEntryID + ", Date: " + date + ", Description: " + description); + } } public String toFileFriendlyString() { From 627b1bcc78f22459675ec2d6b8618b63dcca571a Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 23:16:08 +0800 Subject: [PATCH 268/414] Fix checkstyle and JUnit test issues --- src/main/java/seedu/lifetrack/Entry.java | 10 +++--- .../java/seedu/lifetrack/CalorieListTest.java | 34 +++++++++---------- .../seedu/lifetrack/HydrationListTest.java | 24 ++++++------- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index 3f2d856782..25e6200065 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -3,7 +3,6 @@ import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; -import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import java.time.LocalDate; @@ -34,11 +33,14 @@ public int getLastEntryID() { public String toString() { if(this instanceof InputEntry) { - return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + ", Description: " + description); + return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + + ", Description: " + description); } else if (this instanceof OutputEntry) { - return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + ", Description: " + description); + return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + ", " + + "Description: " + description); } else { - return String.format("\t Hydration ID: " + lastEntryID + ", Date: " + date + ", Description: " + description); + return String.format("\t Hydration ID: " + lastEntryID + ", Date: " + date + ", " + + "Description: " + description); } } diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index eee7d9b044..754ea89160 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -102,7 +102,7 @@ public void testPrintCalorieListNonEmpty() { "\t " + calorieList.getEntry(0).toString() + lineSeparator + "\t Your Caloric List:" + lineSeparator + lineSeparator + "\t Your Caloric Inflow List:" + lineSeparator + - "\t 1. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200" + lineSeparator + + "\t 1. \t Calories ID: 1, Date: 2024-03-14, Description: burger king, Calories: 200" + lineSeparator + lineSeparator + "\t Your Caloric Outflow List:" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -131,18 +131,18 @@ public void testPrintCalorieListMultipleEntries() { .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") + .append("\t 1. \t Calories ID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") .append(lineSeparator) - .append("\t 2. \t EntryID: 3, Date: 2024-03-14, Description: acai, Calories: 500") + .append("\t 2. \t Calories ID: 3, Date: 2024-03-14, Description: acai, Calories: 500") .append(lineSeparator) - .append("\t 3. \t EntryID: 5, Date: 2024-03-14, Description: commhall dinner, Calories: 300") + .append("\t 3. \t Calories ID: 5, Date: 2024-03-14, Description: commhall dinner, Calories: 300") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") + .append("\t 1. \t Calories ID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") .append(lineSeparator) - .append("\t 2. \t EntryID: 4, Date: 2024-03-14, Description: Run, Calories: 250") + .append("\t 2. \t Calories ID: 4, Date: 2024-03-14, Description: Run, Calories: 250") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); @@ -180,20 +180,20 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 5, Date: 2021-01-11, Description: commhall dinner, Calories: 300") + .append("\t 1. \t Calories ID: 5, Date: 2021-01-11, Description: commhall dinner, Calories: 300") .append(lineSeparator) - .append("\t 2. \t EntryID: 3, Date: 2022-02-22, Description: acai, Calories: 500") + .append("\t 2. \t Calories ID: 3, Date: 2022-02-22, Description: acai, Calories: 500") .append(lineSeparator) - .append("\t 3. \t EntryID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") + .append("\t 3. \t Calories ID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 6, Date: 2021-01-11, Description: play pool, Calories: 69") + .append("\t 1. \t Calories ID: 6, Date: 2021-01-11, Description: play pool, Calories: 69") .append(lineSeparator) - .append("\t 2. \t EntryID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append("\t 2. \t Calories ID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") .append(lineSeparator) - .append("\t 3. \t EntryID: 4, Date: 2024-03-14, Description: Run, Calories: 250") + .append("\t 3. \t Calories ID: 4, Date: 2024-03-14, Description: Run, Calories: 250") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(6, calorieList.getSize()); @@ -234,12 +234,12 @@ public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") + .append("\t 1. \t Calories ID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append("\t 1. \t Calories ID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(2, calorieList.getSize()); @@ -289,15 +289,15 @@ public void testAddEntry_addAndDeleteEntries_entryIDIncrementsProperly() { .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") + .append("\t 1. \t Calories ID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") .append(lineSeparator) - .append("\t 2. \t EntryID: 4, Date: 2022-02-22, Description: yong tau foo, Calories: 688 " + + .append("\t 2. \t Calories ID: 4, Date: 2022-02-22, Description: yong tau foo, Calories: 688 " + "(C: 10, P: 10, F: 10)") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t EntryID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append("\t 1. \t Calories ID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(3, calorieList.getSize()); diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index ca858eb6a6..f4c23599db 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -54,9 +54,9 @@ public void testPrintHydrationListNonEmpty() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator; + "\t 1. \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -72,15 +72,15 @@ public void testPrintHydrationListMultipleEntries() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t \t Hydration ID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + + "\t \t Hydration ID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + - "\t 2. \t EntryID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + - "\t 3. \t EntryID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; + "\t 1. \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t 2. \t Hydration ID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t 3. \t Hydration ID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, hydrationList.getSize()); } @@ -121,12 +121,12 @@ public void testPrintHydrationListWithMultipleEntries() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t \t Hydration ID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t EntryID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + + "\t \t Hydration ID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t EntryID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + - "\t 2. \t EntryID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; + "\t 1. \t Hydration ID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t 2. \t Hydration ID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(2, hydrationList.getSize()); } From 5bd011744a028129e9abfdd14bc6d21e6d77ef3c Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 23:25:26 +0800 Subject: [PATCH 269/414] Change Calories ID to caloriesID. Change Hydration ID to hydrationID --- src/main/java/seedu/lifetrack/Entry.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index 25e6200065..98cac21dab 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -33,13 +33,13 @@ public int getLastEntryID() { public String toString() { if(this instanceof InputEntry) { - return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + + return String.format("\t caloriesID: " + lastEntryID + ", Date: " + date + ", Description: " + description); } else if (this instanceof OutputEntry) { - return String.format("\t Calories ID: " + lastEntryID + ", Date: " + date + ", " + + return String.format("\t caloriesID: " + lastEntryID + ", Date: " + date + ", " + "Description: " + description); } else { - return String.format("\t Hydration ID: " + lastEntryID + ", Date: " + date + ", " + + return String.format("\t hydrationID: " + lastEntryID + ", Date: " + date + ", " + "Description: " + description); } } From 5dcdc8c17de9647f432ff47a0bbb7cb9eb144abd Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 23:25:53 +0800 Subject: [PATCH 270/414] Update CalorieListTest and HydrationListTest to match changes --- .../java/seedu/lifetrack/CalorieListTest.java | 34 +++++++++---------- .../seedu/lifetrack/HydrationListTest.java | 24 ++++++------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index 754ea89160..ea9b45456c 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -102,7 +102,7 @@ public void testPrintCalorieListNonEmpty() { "\t " + calorieList.getEntry(0).toString() + lineSeparator + "\t Your Caloric List:" + lineSeparator + lineSeparator + "\t Your Caloric Inflow List:" + lineSeparator + - "\t 1. \t Calories ID: 1, Date: 2024-03-14, Description: burger king, Calories: 200" + lineSeparator + + "\t 1. \t caloriesID: 1, Date: 2024-03-14, Description: burger king, Calories: 200" + lineSeparator + lineSeparator + "\t Your Caloric Outflow List:" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -131,18 +131,18 @@ public void testPrintCalorieListMultipleEntries() { .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") + .append("\t 1. \t caloriesID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") .append(lineSeparator) - .append("\t 2. \t Calories ID: 3, Date: 2024-03-14, Description: acai, Calories: 500") + .append("\t 2. \t caloriesID: 3, Date: 2024-03-14, Description: acai, Calories: 500") .append(lineSeparator) - .append("\t 3. \t Calories ID: 5, Date: 2024-03-14, Description: commhall dinner, Calories: 300") + .append("\t 3. \t caloriesID: 5, Date: 2024-03-14, Description: commhall dinner, Calories: 300") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") + .append("\t 1. \t caloriesID: 2, Date: 2024-03-14, Description: Walk, Calories: 150") .append(lineSeparator) - .append("\t 2. \t Calories ID: 4, Date: 2024-03-14, Description: Run, Calories: 250") + .append("\t 2. \t caloriesID: 4, Date: 2024-03-14, Description: Run, Calories: 250") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(5, calorieList.getSize()); @@ -180,20 +180,20 @@ public void testAddEntry_addDifferentDates_datesSortedCorrectly() { .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 5, Date: 2021-01-11, Description: commhall dinner, Calories: 300") + .append("\t 1. \t caloriesID: 5, Date: 2021-01-11, Description: commhall dinner, Calories: 300") .append(lineSeparator) - .append("\t 2. \t Calories ID: 3, Date: 2022-02-22, Description: acai, Calories: 500") + .append("\t 2. \t caloriesID: 3, Date: 2022-02-22, Description: acai, Calories: 500") .append(lineSeparator) - .append("\t 3. \t Calories ID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") + .append("\t 3. \t caloriesID: 1, Date: 2024-03-14, Description: burger king, Calories: 200") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 6, Date: 2021-01-11, Description: play pool, Calories: 69") + .append("\t 1. \t caloriesID: 6, Date: 2021-01-11, Description: play pool, Calories: 69") .append(lineSeparator) - .append("\t 2. \t Calories ID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append("\t 2. \t caloriesID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") .append(lineSeparator) - .append("\t 3. \t Calories ID: 4, Date: 2024-03-14, Description: Run, Calories: 250") + .append("\t 3. \t caloriesID: 4, Date: 2024-03-14, Description: Run, Calories: 250") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(6, calorieList.getSize()); @@ -234,12 +234,12 @@ public void testDeleteEntry_deleteUsingEntryID_correctlyDeletesBasedOnEntryID() .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") + .append("\t 1. \t caloriesID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append("\t 1. \t caloriesID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(2, calorieList.getSize()); @@ -289,15 +289,15 @@ public void testAddEntry_addAndDeleteEntries_entryIDIncrementsProperly() { .append(lineSeparator) .append("\t Your Caloric Inflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") + .append("\t 1. \t caloriesID: 1, Date: 2022-02-22, Description: burger king, Calories: 200") .append(lineSeparator) - .append("\t 2. \t Calories ID: 4, Date: 2022-02-22, Description: yong tau foo, Calories: 688 " + + .append("\t 2. \t caloriesID: 4, Date: 2022-02-22, Description: yong tau foo, Calories: 688 " + "(C: 10, P: 10, F: 10)") .append(lineSeparator) .append(lineSeparator) .append("\t Your Caloric Outflow List:") .append(lineSeparator) - .append("\t 1. \t Calories ID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") + .append("\t 1. \t caloriesID: 2, Date: 2022-02-22, Description: Walk, Calories: 150") .append(lineSeparator); assertEquals(expectedOutput.toString(), outputStream.toString()); assertEquals(3, calorieList.getSize()); diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index f4c23599db..e17a7287d7 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -54,9 +54,9 @@ public void testPrintHydrationListNonEmpty() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t \t hydrationID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator; + "\t 1. \t hydrationID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -72,15 +72,15 @@ public void testPrintHydrationListMultipleEntries() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t \t hydrationID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Hydration ID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t \t hydrationID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Hydration ID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + + "\t \t hydrationID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Hydration ID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + - "\t 2. \t Hydration ID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + - "\t 3. \t Hydration ID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; + "\t 1. \t hydrationID: 1, Date: 2024-02-22, Description: Milo, Volume: 200" + lineSeparator + + "\t 2. \t hydrationID: 2, Date: 2024-02-22, Description: Water, Volume: 300" + lineSeparator + + "\t 3. \t hydrationID: 3, Date: 2024-02-22, Description: Juice, Volume: 150" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, hydrationList.getSize()); } @@ -121,12 +121,12 @@ public void testPrintHydrationListWithMultipleEntries() { hydrationList.printHydrationList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Hydration ID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t \t hydrationID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + "\t The following entry has been added to your hydration list!" + lineSeparator + - "\t \t Hydration ID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + + "\t \t hydrationID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator + "\t Your Hydration List:" + lineSeparator + - "\t 1. \t Hydration ID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + - "\t 2. \t Hydration ID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; + "\t 1. \t hydrationID: 1, Date: 2024-02-22, Description: Coffee, Volume: 150" + lineSeparator + + "\t 2. \t hydrationID: 2, Date: 2024-02-22, Description: Tea, Volume: 200" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(2, hydrationList.getSize()); } From 5a41231722ce6c708645398ade2ac8b1dadf3b79 Mon Sep 17 00:00:00 2001 From: RexYong Date: Wed, 10 Apr 2024 23:26:08 +0800 Subject: [PATCH 271/414] Update Userguide to match changes to caloriesID and hydrationID --- docs/UserGuide.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index b472bd0cc9..6594f2ec7e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -106,25 +106,29 @@ All entries are sorted by date, in ascending order, from earlier dates to presen Your Caloric List: Your Caloric Inflow List: - 1. EntryID: 2, Date: 2024-02-02, Description: commhall dinner, Calories: 6969 (C: 69, P: 69, F: 69) - 2. EntryID: 1, Date: 2024-04-09, Description: wingstop, Calories: 1000 (C: 100, P: 100, F: 100) + 1. caloriesID: 1, Date: 2024-04-09, Description: wingstop, Calories: 1000 (C: 100, P: 100, F: 100) + 2. caloriesID: 2, Date: 2024-04-09, Description: wingstop, Calories: 1000 (C: 100, P: 100, F: 100) + 3. caloriesID: 3, Date: 2024-04-09, Description: wingstop, Calories: 1000 (C: 100, P: 100, F: 100) Your Caloric Outflow List: - 1. EntryID: 3, Date: 2024-01-01, Description: gym, Calories: 2000 - 2. EntryID: 4, Date: 2024-04-09, Description: sprint, Calories: 400 + 1. caloriesID: 7, Date: 2024-04-10, Description: basketball, Calories: 1000 + 2. caloriesID: 9, Date: 2024-04-10, Description: run, Calories: 200 ----------------------------------------------------------------------------- + + + ### Deleting a calorie item: `calories delete` -Deletes the specified entry from the calories tracker according to the `ENTRYID`. +Deletes the specified calories ID entry from the calories tracker according to the `CALORIESID`. **Format:** -`calories delete ENTRYID` -* The `ENTRYID` must be a positive **integer** 1, 2, 3 and so on. +`calories delete CALORIESID` +* The `CALORIESID` must be a positive **integer** 1, 2, 3 and so on. **Examples:** -* `calories list` followed by `calories delete 2` deletes the entry with `ENTRYID` 2 in the calories tracker. +* `calories list` followed by `calories delete 2` deletes the entry with `CALORIESID` 2 in the calories tracker. ## Hydration Tracker @@ -150,22 +154,20 @@ Show the list of all hydration records in the hydration tracker. #### Expected output ----------------------------------------------------------------------------- Your Hydration List: - 1. Date: 2022-01-01, Description: teh peng, Volume: 100 - 2. Date: 2022-01-01, Description: milo, Volume: 100 - 3. Date: 2022-01-02, Description: water, Volume: 100 - 4. Date: 2022-01-02, Description: milo, Volume: 200 - 5. Date: 2022-01-01, Description: milo, Volume: 100 + 1. hydrationID: 1, Date: 2024-04-10, Description: milo, Volume: 100 + 2. hydrationID: 2, Date: 2024-04-10, Description: coke, Volume: 1000 ----------------------------------------------------------------------------- + ### Deleting a hydration item: `hydration delete` -Deletes the specified hydration entry according to the `ENTRYID`. +Deletes the specified hydration entry according to the `HYDRATIONID`. **Format:** -`hydration delete ENTRYID` -* The `ENTRYID` must be a positive **integer** 1, 2, 3 and so on. +`hydration delete HYDRATIONID` +* The `HYDRATIONID` must be a positive **integer** 1, 2, 3 and so on. **Examples:** -* `hydration list` followed by `hydration delete 2` deletes the entry with `ENTRYID` 2 in the hydration tracker. +* `hydration list` followed by `hydration delete 2` deletes the entry with `HYDRATIONID` 2 in the hydration tracker. ## Sleep Tracker From b1dd2f552430701cd00145c4fcab3d66d692a2f7 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 11 Apr 2024 00:06:02 +0800 Subject: [PATCH 272/414] add first draft of PPP --- docs/team/owx0130.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/team/owx0130.md diff --git a/docs/team/owx0130.md b/docs/team/owx0130.md new file mode 100644 index 0000000000..7aee32923e --- /dev/null +++ b/docs/team/owx0130.md @@ -0,0 +1,37 @@ +# Ong Wei Xiang's Project Portfolio Page + +## Project: LifeTrack + +LifeTrack is a desktop app for students to track their health data, +optimized for use via a Command Line Interface (CLI). +It tracks calories, hydration and sleep data for the user, +while also providing daily recommendations for calorie and hydration intake, +based on the user's build and gender, as well as their body goals and activity levels. + +Given below are my contributions to the project. + +- **New Feature**: Added the ability to read and write from a data file. + - What it does: allows the application to store caloric/hydration/sleep entries that were input previously. Data is retrieved from a data file whenever the application is relaunched. + - Justification: the user should be able to compare against all their previous calorie/hydration/sleep entries to monitor their health which is the very purpose of the application. Additionally, it is exceedingly inconvenient to have the application open all the time if there was no form of storage available, thus this capability is also a huge boost to the user's quality of life while using the application. + - Highlights: This enhancement required an in-depth analysis of design alternatives, such as choosing a suitable delimiter when storing data. In this case, the delimiter needed to be a seldom used character to ensure that it does not show up too often in user input. The implementation was also challenging as there were many alternatives, including when and where to update the data file. + +- **Code contributed**: [Reposense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=owx0130&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +- **Project management**: + - Managed releases `v1.0`, `v1.1` and `v2.0` on GitHub + - Maintaining the issue tracker i.e. closing completed issues, labelling and assigning issues appropriately + +- **Enhancements to existing features**: + - Added capability for `calories in` inputs to take in an optional argument for macronutrients ([PR #39](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/39)) + - Reorganized all exception messages into a new class for ease of viewing and editing ([PR #56](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/56), [PR #160](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/160)) + - Created `InputEntry` and `OutputEntry` classes to inherit the original `Entry` class to differentiate between a calorie input and output entry ([PR #39](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/39)) + - Reorganized the `ParserCalories` class by extracting methods to make it more OOP-like ([PR #48](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/48)) + - Reorganized JUnit test cases into individual classes for ease of access ([PR #48](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/48)) + - Added JUnit tests for existing features ([PR #26](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/26), [PR #56](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/56)) + +- **Documentation**: + - Developer Guide: + - Added implementation details of the `Adding calorie entries feature`. + +- **Community**: + - PRs reviewed: [#38](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/38), [#43](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/43), [#50](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/50), [#60](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/60), [#67](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/67), [#83](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/83), [#98](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/98) \ No newline at end of file From dfd8fd33af139feb23043e785326e400839adc72 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 11 Apr 2024 00:16:39 +0800 Subject: [PATCH 273/414] edit PPP --- docs/team/owx0130.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/team/owx0130.md b/docs/team/owx0130.md index 7aee32923e..477ad5fe70 100644 --- a/docs/team/owx0130.md +++ b/docs/team/owx0130.md @@ -30,6 +30,8 @@ Given below are my contributions to the project. - Added JUnit tests for existing features ([PR #26](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/26), [PR #56](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/56)) - **Documentation**: + - User Guide: + - Added to `Introduction` and `Quick Start` sections. - Developer Guide: - Added implementation details of the `Adding calorie entries feature`. From 110816b52c14f9f0bb2d3acea66aa20a037e678c Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 11 Apr 2024 00:47:02 +0800 Subject: [PATCH 274/414] update PPP documentation section, add FAQ to UG --- docs/UserGuide.md | 14 +++++++++----- docs/team/owx0130.md | 6 ++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index b472bd0cc9..957622ef7f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -30,10 +30,10 @@ LifeTrack is a desktop app for students to track their health data, optimized fo ## Quick Start -{Give steps to get started quickly} - 1. Ensure that you have Java 11 or above installed. -2. Download the latest version of `LifeTrack` from [here](https://github.com/AY2324S2-CS2113-F15-2/tp/releases). +2. Download the latest version of `LifeTrack` from [here](https://github.com/AY2324S2-CS2113-F15-2/tp/releases). You may move the JAR file to anywhere in your computer if you wish. +3. Open a terminal window and change directories to where the JAR file is located. +4. Run the command `java -jar tp.jar` and the application will start running. [//]: # (## Features ) @@ -262,9 +262,13 @@ If you have not set your user up beforehand, this command will prompt you to do ## FAQ -**Q**: How do I transfer my data to another computer? +**Q**: How do I transfer my data to another computer? + +**A**: In the same directory as where the JAR file is located, the application will automatically create a `/data` directory which stores all the data files required for the application. Simply copy the entire directory and its contents to your new computer and ensure that it is in the same directory as your JAR file, then run the application as per normal. + +**Q**: Why must I input integers for my calories when it is a continuous variable? -**A**: {your answer here} +**A**: Although calories is technically a continuous variable, we chose to only take in integer inputs in our application as the difference is just not that significant, i.e. users can just round up values that have decimal values of 0.5 and above, and round down any values below that. An average human will have calorie intake in the thousands daily, thus such a small inaccuracy is insignificant in comparison. An `int` is also much easier to work with than `float`, which is why we chose to only use the former. ## Coming soon diff --git a/docs/team/owx0130.md b/docs/team/owx0130.md index 477ad5fe70..ca430273c4 100644 --- a/docs/team/owx0130.md +++ b/docs/team/owx0130.md @@ -31,9 +31,11 @@ Given below are my contributions to the project. - **Documentation**: - User Guide: - - Added to `Introduction` and `Quick Start` sections. + - Added to `Introduction` ([PR #97](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/97)) + - Added to `Quick Start` section ([PR #160](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/160), [PR #170](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/170)) + - Added relevant FAQs ([PR #170](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/170)) - Developer Guide: - - Added implementation details of the `Adding calorie entries feature`. + - Added implementation details of the `Adding calorie entries feature` ([PR #70](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/70), [PR #96](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/96)) - **Community**: - PRs reviewed: [#38](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/38), [#43](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/43), [#50](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/50), [#60](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/60), [#67](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/67), [#83](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/83), [#98](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/98) \ No newline at end of file From 5ac858084a382d4a60387084adceb8e7f058c888 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 11 Apr 2024 03:19:25 +0800 Subject: [PATCH 275/414] Update Function --- .../InvalidInputExceptionMessage.java | 23 +++++--- .../lifetrack/system/parser/ParserUser.java | 51 +++++++++++++++-- src/main/java/seedu/lifetrack/ui/Ui.java | 15 ++++- src/main/java/seedu/lifetrack/ui/UserUi.java | 57 +++++++++++++++++-- src/main/java/seedu/lifetrack/user/User.java | 29 ++++++++++ 5 files changed, 155 insertions(+), 20 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 500262a01c..0622215807 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -17,6 +17,13 @@ public class InvalidInputExceptionMessage { private static final String SLEEP_IN_INPUT = "\t Example input: sleep add 7.5 d/2024-03-11" ; private static final String HYDRATION_IN_INPUT = "\t Example input: hydration in Milo v/1000 d/2024-04-19" ; private static final String USER_SETUP_INPUT = "\t Example input: user setup Tom h/170 w/80 a/25 s/male e/4 g/3"; + private static final String USER_UPDATE_FIELDS = "\t 1) user update name \n"+ + "\t 2) user update height \n" + + "\t 3) user update weight \n" + + "\t 4) user update age \n" + + "\t 5) user update sex \n" + + "\t 6) user update exercise levels \n" + + "\t 7) user update goal "; //general error messages public static String getInvalidDateMessage() { @@ -170,15 +177,9 @@ public static String getInvalidAgeNumberMessage(){ public static String getEmptyUserUpdateInputMessage() { return "\t Please enter your name!"; } - public static String getUnknownUpdateMesssage() { - return "\t Oops, I've not seen this command before!\n" + "Here are a list of possible update commands:\n" + - "1) user update name \n"+ - "2) user update height \n" + - "3) user update weight \n" + - "4) user update age \n" + - "5) user update sex \n" + - "6) user update exercise levels \n" + - "7) user update goal "; + public static String getUnknownUpdateMessage() { + return "\t Oops, I've not seen this command before!\n" + + "\t Here are a list of possible update commands:\n" + USER_UPDATE_FIELDS; } public static String getEmptyGenderInputMessage() { @@ -192,4 +193,8 @@ public static String getInvalidGenderInputMessage() { public static String getEmptyNameInputMessage() { return "\t Please enter a non-empty name!"; } + + public static String getEmptyUserUpdateFieldMessage(){ + return "\t Please include the value you would like to update the field to as such:\n" + USER_UPDATE_FIELDS; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 3463318560..09bcd8f724 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -3,9 +3,12 @@ import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.user.User; +import java.util.Objects; + import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getAgeOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyGenderInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyNameInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserUpdateFieldMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidAgeNumberMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidExerciseLevelsNumberMessage; @@ -15,12 +18,21 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidNumberOfSetUpInputs; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidWeightNumberMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnderAgeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnknownUpdateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserUpdateInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnknownUpdateMesssage; + +import static seedu.lifetrack.ui.UserUi.printNewUserAge; +import static seedu.lifetrack.ui.UserUi.printNewUserExerciseLevels; +import static seedu.lifetrack.ui.UserUi.printNewUserGoal; +import static seedu.lifetrack.ui.UserUi.printNewUserHeight; +import static seedu.lifetrack.ui.UserUi.printNewUserName; +import static seedu.lifetrack.ui.UserUi.printNewUserSex; +import static seedu.lifetrack.ui.UserUi.printNewUserWeight; +import static seedu.lifetrack.ui.UserUi.printUserCaloriesRequired; import static seedu.lifetrack.ui.UserUi.printUserSetUpComplete; /** @@ -88,8 +100,7 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept user.setExerciseLevels(exerciseLevels); user.setGoal(goal); user.getHealthInfo(); - int caloriesRequired = user.getCaloriesRequired(); - printUserSetUpComplete(user.getName(), caloriesRequired); + printUserSetUpComplete(user); } private static String parseName(String input) throws InvalidInputException { @@ -261,29 +272,56 @@ private static void checkEmptyInput(String input) throws InvalidInputException { public static void parseUpdate(String input, User user) throws InvalidInputException { checkEmptyUpdateInput(input); String fieldToUpdate = input.substring(LENGTH_OF_UPDATE_COMMAND).trim(); + checkEmptyUpdateField(fieldToUpdate); if (fieldToUpdate.startsWith("name ")) { String name = parseName(fieldToUpdate.substring(LENGTH_OF_NAME).trim()); user.setName(name); + assert Objects.equals(user.getName(), name); + printNewUserName(name); } else if (fieldToUpdate.startsWith("height ")) { int height = parseHeightIndex(fieldToUpdate.substring(LENGTH_OF_HEIGHT).trim()); user.setHeight(height); + assert Objects.equals(user.getHeight(),height); + printNewUserHeight(height); + user.getHealthInfo(); + printUserCaloriesRequired(user.getCaloriesRequired()); } else if (fieldToUpdate.startsWith("weight ")) { int weight = parseWeightIndex(fieldToUpdate.substring(LENGTH_OF_WEIGHT).trim()); user.setWeight(weight); + assert Objects.equals(user.getWeight(), weight); + printNewUserWeight(weight); + user.getHealthInfo(); + printUserCaloriesRequired(user.getCaloriesRequired()); } else if (fieldToUpdate.startsWith("age ")) { int age = parseAgeIndex(fieldToUpdate.substring(LENGTH_OF_AGE).trim()); user.setAge(age); + assert Objects.equals(user.getAge(),age); + printNewUserAge(age); + user.getHealthInfo(); + printUserCaloriesRequired(user.getCaloriesRequired()); } else if (fieldToUpdate.startsWith("exercise levels ")) { int level = parseExerciseLevels(fieldToUpdate.substring(LENGTH_OF_EXERCISE_LEVELS).trim()); user.setExerciseLevels(level); + assert Objects.equals(user.getExerciseLevels(),level); + printNewUserExerciseLevels(user , level); + user.getHealthInfo(); + printUserCaloriesRequired(user.getCaloriesRequired()); } else if (fieldToUpdate.startsWith("goal ")){ int goal = parseGoalIndex(fieldToUpdate.substring(LENGTH_OF_GOAL).trim()); user.setGoal(goal); + assert Objects.equals(user.getGoal(),goal); + printNewUserGoal(user, goal); + user.getHealthInfo(); + printUserCaloriesRequired(user.getCaloriesRequired()); } else if (fieldToUpdate.startsWith("sex ")) { String sex = parseGenderIndex(fieldToUpdate.substring(LENGTH_OF_SEX).trim()); user.setSex(sex); + assert Objects.equals(user.getSex(),sex); + printNewUserSex(sex); + user.getHealthInfo(); + printUserCaloriesRequired(user.getCaloriesRequired()); } else { - throw new InvalidInputException(getUnknownUpdateMesssage()); + throw new InvalidInputException(getUnknownUpdateMessage()); } } @@ -292,5 +330,10 @@ private static void checkEmptyUpdateInput(String input) throws InvalidInputExcep throw new InvalidInputException(getEmptyUserUpdateInputMessage()); } } + private static void checkEmptyUpdateField(String input) throws InvalidInputException { + if (input.trim().isEmpty()) { + throw new InvalidInputException(getEmptyUserUpdateFieldMessage()); + } + } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index d6df6af6a0..3b12a96113 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -9,6 +9,7 @@ import java.util.Scanner; import static seedu.lifetrack.ui.UserUi.printNoUserYetMessage; +import static seedu.lifetrack.ui.UserUi.printUserDetails; /** * Reads user input from the console and processes it. @@ -35,7 +36,7 @@ public class Ui { /** * Reads in the input from the user * - * @param calorieList list containing all entries pertinent to calories + * @param calorieList list containing all entries pertinent to calories * @param hydrationList list containing all entries pertinent to liquids */ public static void readUserInput(CalorieList calorieList, HydrationList hydrationList, @@ -126,7 +127,17 @@ public static void handleUserCommands(String line, User user) { } else if (line.startsWith("user progress")) { handleUserProgress(user); } else if (line.startsWith("user update")) { - user.update(line); + if (user.getName() == null) { + printNoUserYetMessage(); + } else { + user.update(line); + } + } else if (line.startsWith("user details")) { + if (user.getName() == null) { + printNoUserYetMessage(); + } else { + printUserDetails(user); + } } else { handleUnknownInput(); } diff --git a/src/main/java/seedu/lifetrack/ui/UserUi.java b/src/main/java/seedu/lifetrack/ui/UserUi.java index d27461ba4a..7a393fa2bf 100644 --- a/src/main/java/seedu/lifetrack/ui/UserUi.java +++ b/src/main/java/seedu/lifetrack/ui/UserUi.java @@ -1,6 +1,9 @@ package seedu.lifetrack.ui; +import seedu.lifetrack.user.User; + public class UserUi { + private final static String CHANGE_MADE_MESSAGE = "\t The following change has been made:\n"; public static void printUserCaloriesRequired(int caloriesRequired) { System.out.println("\t You need to consume " + caloriesRequired + " calories per day to hit your goals!"); @@ -22,11 +25,55 @@ public static void printUserHydrationProgress(int hydrationConsumed, int hydrati System.out.printf("\t %s %d%%\n", progressBar, percentage); } - public static void printUserSetUpComplete(String name, int caloriesRequired){ - System.out.println("\t Hello, " + name + "! Thank you for completing the setup :)"); - printUserCaloriesRequired(caloriesRequired); + public static void printUserSetUpComplete(User user) { + System.out.println("\t Hello, " + user.getName() + "! Thank you for completing the setup :)"); + printUserCaloriesRequired(user.getCaloriesRequired()); + printUserDetails(user); + } + + public static void printNoUserYetMessage() { + System.out.println("\t Please set up your profile first!"); + } + + public static void printNewUserName(String name) { + System.out.println(CHANGE_MADE_MESSAGE + "\t Name: " + name); + } + + public static void printNewUserAge(int age) { + System.out.println(CHANGE_MADE_MESSAGE + "\t Age: " + age); + } + + public static void printNewUserHeight(int height) { + System.out.println(CHANGE_MADE_MESSAGE + "\t Height: " + height); } - public static void printNoUserYetMessage(){ - System.out.println("\t\t Please set up your profile first!"); + + public static void printNewUserWeight(int weight) { + System.out.println(CHANGE_MADE_MESSAGE + "\t Weight: " + weight); + } + + public static void printNewUserSex(String sex) { + System.out.println(CHANGE_MADE_MESSAGE + "\t Sex: " + sex); + } + + public static void printNewUserExerciseLevels(User user, int level) { + System.out.println(CHANGE_MADE_MESSAGE + "\t Exercise Levels: " + level + " out of 5 (" + + user.getExerciseLevelAsString() + ")"); + } + + public static void printNewUserGoal(User user, int goal) { + System.out.println(CHANGE_MADE_MESSAGE + "\t Goal: " + goal + " out of 5 (" + + user.getGoalAsString() + ")"); + } + + public static void printUserDetails(User user) { + System.out.println("\t User details:\n" + + "\t Name: " + user.getName() + "\n" + + "\t Height: " + user.getHeight() + "\n" + + "\t Weight: " + user.getWeight() + "\n" + + "\t Age: " + user.getAge() + "\n" + + "\t Sex: " + user.getSex() + "\n" + + "\t Exercise Levels: " + user.getExerciseLevels() + " out of 5 (" + + user.getExerciseLevelAsString() + ")" + "\n" + + "\t Goal: " + user.getGoal() + " out of 5 (" + user.getGoalAsString() + ")" + "\n"); } } diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 3a2601da8d..305c49ffa9 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -107,6 +107,7 @@ public void setGoal(int goal) { public String getName() { return name; } + public int getHeight() { return height; } @@ -159,4 +160,32 @@ public void getCaloriesProgressBar() { public void getHydrationProgressBar() { UserGoals.getHydrationProgressBar(this); } + + public String getExerciseLevelAsString() { + if (exerciseLevels == 1) { + return "Sedentary"; + } else if (exerciseLevels == 2) { + return "Lightly Active"; + } else if (exerciseLevels == 5) { + return "Extremely Active"; + } else if (exerciseLevels == 4) { + return "Very Active"; + } else { + return "Moderately Active"; + } + } + + public String getGoalAsString() { + if (goal == 1) { + return "Quick Weight Loss"; + } else if (goal == 2) { + return "Moderate Weight Loss"; + } else if (goal == 5) { + return "Quick Weight Gain"; + } else if (goal == 4) { + return "Moderate Weight Gain"; + } else { + return "Maintain Weight"; + } + } } From c8ea368db14f7d4ac17259a50c21cddd5a1d0d63 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 11 Apr 2024 03:28:26 +0800 Subject: [PATCH 276/414] javadocs --- .../lifetrack/system/parser/ParserUser.java | 20 +++++++++++++++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 4 +++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 09bcd8f724..34b00d5f41 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -269,6 +269,15 @@ private static void checkEmptyInput(String input) throws InvalidInputException { } } + /** + * Parses "user update" command to update the relevant field of the user. Recalculates the user's calorific goal as + * well. + * + * @param input input from the user + * @param user current User + * @throws InvalidInputException if the command is empty, the update field is empty, if the field given to update + * is unknown or if the value to update is not correct. + */ public static void parseUpdate(String input, User user) throws InvalidInputException { checkEmptyUpdateInput(input); String fieldToUpdate = input.substring(LENGTH_OF_UPDATE_COMMAND).trim(); @@ -325,11 +334,22 @@ public static void parseUpdate(String input, User user) throws InvalidInputExcep } } + /** + * Checks if the "user update" command is empty + * @param input input from the user + * @throws InvalidInputException if the command is empty + */ private static void checkEmptyUpdateInput(String input) throws InvalidInputException { if (input.substring(LENGTH_OF_UPDATE_COMMAND).trim().isEmpty()) { throw new InvalidInputException(getEmptyUserUpdateInputMessage()); } } + + /** + * Checks if the field to update is empty + * @param input input from the user + * @throws InvalidInputException if the field is empty + */ private static void checkEmptyUpdateField(String input) throws InvalidInputException { if (input.trim().isEmpty()) { throw new InvalidInputException(getEmptyUserUpdateFieldMessage()); diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 3b12a96113..b343c6f69a 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -206,7 +206,9 @@ public static void showHelp() { "g/:\n" + "\t Create a new user, or edit an existing one."); System.out.println("\t - user progress: Display calories and hydration progress towards the daily " + "requirement."); - System.out.println("\t - user update name/height/weight/age/sex/exercise levels/goal "); + System.out.println("\t - user update name/height/weight/age/sex/exercise levels/goal : " + + "updates the corresponding field of the user."); + System.out.println("\t - user details: prints the details of the user."); } } //@@author From a527255223ac746a1ea8bcad68204b63508694ac Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 11 Apr 2024 03:32:15 +0800 Subject: [PATCH 277/414] gradle error --- src/main/java/seedu/lifetrack/ui/UserUi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/ui/UserUi.java b/src/main/java/seedu/lifetrack/ui/UserUi.java index 7a393fa2bf..e63b1bad82 100644 --- a/src/main/java/seedu/lifetrack/ui/UserUi.java +++ b/src/main/java/seedu/lifetrack/ui/UserUi.java @@ -3,7 +3,7 @@ import seedu.lifetrack.user.User; public class UserUi { - private final static String CHANGE_MADE_MESSAGE = "\t The following change has been made:\n"; + private static final String CHANGE_MADE_MESSAGE = "\t The following change has been made:\n"; public static void printUserCaloriesRequired(int caloriesRequired) { System.out.println("\t You need to consume " + caloriesRequired + " calories per day to hit your goals!"); From 59ba782f2560063cd579e3d11ee2b6d8dc40dae8 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 11 Apr 2024 13:16:34 +0800 Subject: [PATCH 278/414] Update Developer guide Calories list feature --- docs/DeveloperGuide.md | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index bcbc1d5d0c..31b16895c7 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -74,14 +74,38 @@ The Sequence Diagram for the above-mentioned process is as follows: ### Calories list feature -The `calories list` feature lists out the record of all the Calories data that the user has keyed in. The Calories data are all stored into a `ArrayList calorieArrayList` attribute of the `CalorieList` Class. Calories data are printed when the `printCalorieList()` function is called. +#### Implementation + +This functionality is facilitated by `UI` and `CalorieList` Classes. It implements the following +operation, namely: +- `UI#handleUserInput(String, CalorieList)` +- `UI#handleCaloriesInput(String, CalorieList)` +- `CalorieList#printCalorieList()` +- `CalorieList#printCalorieInflow()` +- `CalorieList#printCalorieOutflow()` + +This feature is activated when the user inputs `calories list` command in the terminal. + +Given below is an example usage scenario and how this mechanism behaves at every step: + +- Step 1: When the user inputs the command `calories list` in the terminal, + the string is sent to `UI#handleUserInput(String, CalorieList)` and +`UI#handleCaloriesInput(String, CalorieList)`, which calls `CalorieList#printCalorieList()`. + +- Step 2: Inside `CalorieList#printCalorieList()`, the function `CalorieList#printCalorieInflow()` +is then called. Entries that are classified under `InputEntry` will be printed out. + +- Step 3: Inside `CalorieList#printCalorieList()`, the function `CalorieList#printCalorieInflow()` + is then called. Entries that are classified under `OuputEntry` will be printed out. -The `printCalorieList()` function iterates through the `calorieArrayList` and prints out the Entries according to its order in the Array List. +- Step 4: After printing out both the Input Entries and Output Entries, the program returns and awaits +the next command typed in by user. -The Class diagram and sequence diagram for Calories list feature is shown below. Unrelated attributes and Classes were excluded. +The class and sequence diagram for this feature is shown below: +Unrelated attributes and Classes were excluded. -![CaloriesListClassDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/DevGuideRex/docs/CaloriesListClassDiagram.puml) -![CaloriesListSequenceDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/RexDG/docs/CaloriesListSequenceDiagram.puml) +![CaloriesListClassDiagram.png](diagrams%2FCaloriesListClassDiagram.png) +![CaloriesListSequenceDiagram.png](diagrams%2FCaloriesListSequenceDiagram.png) ### Calories delete feature From 6f876f76a279cfe8e9c38a766d79f262f63101d3 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 11 Apr 2024 13:16:55 +0800 Subject: [PATCH 279/414] Update seqeunce diagram and class diagram for Calories list feature --- docs/CaloriesListClassDiagram.puml | 22 +++++++++++------- docs/CaloriesListSequenceDiagram.puml | 18 ++++++++++---- docs/diagrams/CaloriesListClassDiagram.png | Bin 0 -> 26165 bytes docs/diagrams/CaloriesListSequenceDiagram.png | Bin 0 -> 33070 bytes 4 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 docs/diagrams/CaloriesListClassDiagram.png create mode 100644 docs/diagrams/CaloriesListSequenceDiagram.png diff --git a/docs/CaloriesListClassDiagram.puml b/docs/CaloriesListClassDiagram.puml index 9d2c773291..11033051ca 100644 --- a/docs/CaloriesListClassDiagram.puml +++ b/docs/CaloriesListClassDiagram.puml @@ -1,14 +1,23 @@ @startuml +skinparam classAttributeIconSize 0 + class LifeTrack { - + main(args: String[]): void + +main(args: String[]): void } package ui { class Ui { - + readUserInput(calorieList: calories.CalorieList, hydrationList: hydration.HydrationList, user: user.User, sleepList: sleep.SleepList): void + + readUserInput(calorieList: calories.CalorieList, ...... ) : void + + handleUserInput(line: String, calorieLIst: CalorieList, ......): void + handleCaloriesInput(line: String, calorieList: calories.CalorieList): void } + class CalorieListUi { + + calorieListHeader(): void + + emptyListMessage(): void + + inputCalorieListHeader(): void + + outputCalorieListHeader(): void + } } package calories { @@ -16,17 +25,14 @@ package calories { class CalorieList { - ArrayList calorieArrayList + printCalorieList(): void + + printCalorieInflow(): void + + printCalorieOutflow(): void } - class CalorieListUi { - + calorieListHeader(): void - + emptyListMessage(): void - } } } - - +hide circle LifeTrack --> calories.calorielist.CalorieList LifeTrack -[dotted]-> ui.Ui ui.Ui -[dotted]-> calories.calorielist.CalorieList diff --git a/docs/CaloriesListSequenceDiagram.puml b/docs/CaloriesListSequenceDiagram.puml index 3d99b22c68..fc1eddc5d3 100644 --- a/docs/CaloriesListSequenceDiagram.puml +++ b/docs/CaloriesListSequenceDiagram.puml @@ -4,21 +4,31 @@ actor Bob Bob -> UI: Input "calories list" command activate UI -UI -> UI: handleUserInput(input, calorieList, .....) +UI -> UI: handleUserInput(String input, CalorieList calorieList, .....) activate UI -UI -> UI: handleCaloriesInput(input, calorieList, .....) +UI -> UI: handleCaloriesInput(String input, CalorieList calorieList, .....) activate UI UI -> CalorieList: printCalorieList() activate CalorieList -loop calorieArrayList.size() -CalorieList -> CalorieList : calorieArrayList.get(i) +CalorieList -> CalorieList: printCalorieInflow() activate CalorieList + +loop calorieArrayList.size() +CalorieList -> CalorieList : print Calorie Inflow entries end +return +CalorieList -> CalorieList: printCalorieOutflow() +activate CalorieList + +loop calorieArrayList.size() +CalorieList -> CalorieList : print Calorie Outflow entries +end return + return return return diff --git a/docs/diagrams/CaloriesListClassDiagram.png b/docs/diagrams/CaloriesListClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..14be055a91c9c67cd934aa7977af7439da1337ea GIT binary patch literal 26165 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 HTHX8)$dFWu literal 0 HcmV?d00001 diff --git a/docs/diagrams/CaloriesListSequenceDiagram.png b/docs/diagrams/CaloriesListSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..2386758f1388e919bcfd5201f44f1f04baf5567c GIT binary patch literal 33070 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

    4%Dh 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 ITDy+_5204r#Q*>R literal 0 HcmV?d00001 From cabb74ff7d6d6066bdffa233d75aaa5b1551e86d Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 11 Apr 2024 13:47:06 +0800 Subject: [PATCH 280/414] Add design considerations in developer guid for calories list feature --- docs/DeveloperGuide.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 31b16895c7..7ce589076c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -107,6 +107,19 @@ Unrelated attributes and Classes were excluded. ![CaloriesListClassDiagram.png](diagrams%2FCaloriesListClassDiagram.png) ![CaloriesListSequenceDiagram.png](diagrams%2FCaloriesListSequenceDiagram.png) +#### Design considerations +- **Alternative 1 (current choice):** Use one arrayList and use instanceof to print out +the Input Entries and Output Entries. + - Pros: Only 1 arrayList is required. + - Cons: Need to loop the same arrayList twice, which is less efficient. Furthermore, + when deleting entries from arrayList, we need to use a unique entryID instead of using the + index of entry in the arrayList. + +- **Alternative 2 :** Use 2 arraylists. One for Input Entries and one for Output Entries. + - Pros: More efficient, as each arrayList only needs to be looped through one time. + Furthermore, we can directly delete entries based on index, and no unique entryID field will be required. + - Cons: Troublesome to implement. Requires a lot of changes to current Class structure and code structure. + ### Calories delete feature The `calories delete` feature can delete the calories record at specific index of calorie list. This functionality is facilitated by `CaloriesList`. It implements one operation, namely: From c87fb52d18137919a6621a1521fe93dd0c107f10 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 11 Apr 2024 15:44:49 +0800 Subject: [PATCH 281/414] remove Activity class --- .../seedu/lifetrack/calories/Activity.java | 9 -------- .../calories/calorielist/OutputEntry.java | 22 ------------------- .../system/parser/ParserCalories.java | 5 +---- 3 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 src/main/java/seedu/lifetrack/calories/Activity.java diff --git a/src/main/java/seedu/lifetrack/calories/Activity.java b/src/main/java/seedu/lifetrack/calories/Activity.java deleted file mode 100644 index f89a9f65cf..0000000000 --- a/src/main/java/seedu/lifetrack/calories/Activity.java +++ /dev/null @@ -1,9 +0,0 @@ -//@@author owx0130 -package seedu.lifetrack.calories; - -public class Activity { - - public Activity(){ - - } -} diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java index 7f272de28e..ff91ad2d84 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/OutputEntry.java @@ -2,7 +2,6 @@ package seedu.lifetrack.calories.calorielist; import seedu.lifetrack.Entry; -import seedu.lifetrack.calories.Activity; import java.time.LocalDate; @@ -12,9 +11,7 @@ */ public class OutputEntry extends Entry { - private Activity activity; private int calories; - private boolean doesActivityExist = false; /** * Constructs a new OutputEntry object with the given description, calories, and date. @@ -28,25 +25,6 @@ public OutputEntry(int lastEntryID, String description, int calories, LocalDate this.calories = calories; } - /** - * Constructs a new OutputEntry object with the given description, calories, date, and activity details. - * - * @param description the description of the entry - * @param calories the number of calories burnt - * @param date the date of the entry - * @param activity the activity details associated with the entry - */ - public OutputEntry(int lastEntryID, String description, int calories, LocalDate date, Activity activity) { - super(lastEntryID, description, date); - this.activity = activity; - this.calories = calories; - this.doesActivityExist = true; - } - - public Activity getActivity() { - return activity; - } - public int getCalories() { return calories; } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index b2e27d1a03..54b1cd1e65 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -3,7 +3,6 @@ import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; -import seedu.lifetrack.calories.Activity; import seedu.lifetrack.calories.Food; import seedu.lifetrack.system.exceptions.InvalidInputException; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectCaloriesInputMessage; @@ -268,9 +267,7 @@ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateInd * @return a new OutputEntry object */ private static Entry makeNewOutputEntry(int lastEntryID, String description, int calories, LocalDate date) { - Activity newActivity = new Activity(); - - return new OutputEntry(lastEntryID, description, calories, date, newActivity); + return new OutputEntry(lastEntryID, description, calories, date); } /** From 67f31d87d59a26beacf1a53da5da565eaa196aed Mon Sep 17 00:00:00 2001 From: owx0130 Date: Thu, 11 Apr 2024 15:48:10 +0800 Subject: [PATCH 282/414] close all scanner objects in FileHandler --- src/main/java/seedu/lifetrack/system/storage/FileHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index a6c882c827..1361f39522 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -120,6 +120,7 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException entries.add(new OutputEntry(entryID, description, calories, date)); } } + s.close(); return entries; } @@ -150,6 +151,7 @@ public ArrayList getHydrationEntriesFromFile() throws FileNotFoundExcepti int volume = Integer.parseInt(words[VOLUME_INDEX]); entries.add(new HydrationEntry(lastHydrationEntryID, description, volume, date)); } + s.close(); return entries; } @@ -166,6 +168,7 @@ public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { double duration = Double.parseDouble(words[DURATION_INDEX]); entries.add(new SleepEntry(lastSleepEntryID, duration, date)); } + s.close(); return entries; } @@ -183,6 +186,7 @@ public ArrayList getUserDataFromFile() throws FileNotFoundException { data.add(words[EXERCISE_INDEX]); data.add(words[GOAL_INDEX]); data.add(words[REQ_CAL_INDEX]); + s.close(); return data; } } From 1bd6a466e7b355431a0e64dc6cc0d86c40062522 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:27:06 +0800 Subject: [PATCH 283/414] authorship --- .../seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java | 1 + .../seedu/lifetrack/hydration/hydrationlist/HydrationList.java | 1 + src/main/java/seedu/lifetrack/ui/HydrationListUI.java | 1 + 3 files changed, 3 insertions(+) diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java index 53341ac8e3..693616d028 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationEntry.java @@ -1,3 +1,4 @@ +//@@author shawnpong package seedu.lifetrack.hydration.hydrationlist; import seedu.lifetrack.Entry; diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index af6006eee8..adde359be0 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -1,3 +1,4 @@ +//@@author shawnpong package seedu.lifetrack.hydration.hydrationlist; import seedu.lifetrack.Entry; diff --git a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java index a763052ae4..2b14af432e 100644 --- a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java +++ b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java @@ -1,3 +1,4 @@ +//@@author shawnpong package seedu.lifetrack.ui; import seedu.lifetrack.Entry; From 31695eaae7114602ab92a80504ae4fcb791f10a7 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:33:31 +0800 Subject: [PATCH 284/414] DG --- docs/HydrationListClassDiagram.puml | 21 ++++++++++++--------- docs/team/shawnpong.md | 23 ++++++++++++++++++++++- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/docs/HydrationListClassDiagram.puml b/docs/HydrationListClassDiagram.puml index b19db9a635..59ab4e445f 100644 --- a/docs/HydrationListClassDiagram.puml +++ b/docs/HydrationListClassDiagram.puml @@ -1,30 +1,33 @@ @startuml +skinparam classAttributeIconSize 0 + class LifeTrack { + main(args: String[]): void } package ui { class Ui { - + readUserInput(calorieList: calories.CalorieList, hydrationList: hydration.HydrationList, user: user.User, sleepList: sleep.SleepList): void + + readUserInput(hydrationList: hydration.HydrationList, ...... ) : void + + handleUserInput(line: String, hydrationList: HydrationList, ......): void + handleHydrationInput(line: String, hydrationList: hydration.HydrationList): void } + class HydrationListUi { + + hydrationListHeader(): void + + emptyListMessage(): void + + inputHydrationListHeader(): void + } } package hydration { package hydrationlist { class HydrationList { - - ArrayList hydrationArrayList - + printHydrationList(): void - } - - class HydrationListUi { - + hydrationListHeader(): void - + emptyListMessage(): void - } + - ArrayList hydrationArrayList + + printHydrationList(): void } } +hide circle LifeTrack --> hydration.hydrationlist.HydrationList LifeTrack -[dotted]-> ui.Ui ui.Ui -[dotted]-> hydration.hydrationlist.HydrationList diff --git a/docs/team/shawnpong.md b/docs/team/shawnpong.md index 15b8fa817c..8d08de2413 100644 --- a/docs/team/shawnpong.md +++ b/docs/team/shawnpong.md @@ -1 +1,22 @@ -this is my profile :D \ No newline at end of file +# Shawn Pong's Project Portfolio Page +## Project: LifeTrack +LifeTrack is a desktop app for students to track their health data, +optimized for use via a Command Line Interface (CLI). +It tracks calories, hydration and sleep data for the user, +while also providing daily recommendations for calorie and hydration intake, +based on the user's build and gender, as well as their body goals and activity levels. + +The program was created using Java. Version control was done using Git. + +## My contributions to the project + +### New features added and enhancements to existing features + +### Contributions to exception handling + +### Contributions to documentation + +### Contributions to project management + + +### Code contributed From ed9c89a5669252d25b100efaa1257fddc49d3672 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Thu, 11 Apr 2024 20:22:14 +0800 Subject: [PATCH 285/414] Karthik-PPP --- docs/team/paturikarthik.md | 53 ++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/team/paturikarthik.md b/docs/team/paturikarthik.md index aeb15c98d7..584406dff0 100644 --- a/docs/team/paturikarthik.md +++ b/docs/team/paturikarthik.md @@ -1,7 +1,50 @@ -# Paturi Karthik - Project Portfolio Page +# Paturi Karthik's Project Portfolio Page +## Project: LifeTrack +LifeTrack is a desktop app for students to track their health data, +optimized for use via a Command Line Interface (CLI). +It tracks calories, hydration and sleep data for the user, +while also providing daily recommendations for calorie and hydration intake, +based on the user's build and gender, as well as their body goals and activity levels. -## Overview -Hello +The program was created using Java. Version control was done using Git. -### Summary of Contributions -Hello \ No newline at end of file +## My contributions to the project + +### New features added and enhancements to existing features +1. **Added a `User` Class to store the details of the user of the app and calculate their daily calorie goal. [PR #73](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/73)** + * What it does: Allows users to set up their details such as their `NAME`, `HEIGHT`, `WEIGHT`, `AGE`, `SEX`, `EXERCISE LEVELS` and `GOALS`. + Using, these information, the user's calorie goal is also calculated and stored in `caloriesRequired`. Using the `user progress` command, users can track the percentage of calories and hydration they have consumed for the day. [PR #X](xxxx) + * Testing: Add JUnit tests for the feature (future plans). + +2. **Added `user update` and `user details` functions for users to quickly glance at and update their details. [PR #171](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/171)** + * What it does: `user update` allows users to update their details one field at a time, while `user details` allows user to view all their details at once. + * Testing: Add JUnit tests for the feature (future plans). + +3. **Added `UI` class for overall handling of user commands. [PR #52](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/52), [PR #58](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/58)** + * What it does: Redirects commands from user to the relevant methods. + * Justification: Having a separate class for `UI` ensures that the code is well factored out and that changes to the UI do not need to be made in multiple files. . + +4. **Added `UserUI` class to handle all System messages regarding the `User` class. [PR #108](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/108)** + * What it does: Ensures that all messages from the System are factored out and consolidated into a single class for code clarity. + +5. **Added a `search` feature to sieve through `calorieList` and `hydrationList` to find entries based on a given keyword.(future plans)** + * What it does: Prints the calories list/ hydration list based on the keyword that was searched. + * Justification: So that users have an easier time finding certain entries from the lists, instead of looking through the full list. + +### Contributions to exception handling +* Added robust exception handling for `ParserUser`, in order to ensure that program does not crash + when users give incomplete commands, invalid commands, missing fields, impractical entries or entries which are out of the range of LifeTrack. [PR #89](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/89) +* Contributed to the robust exception handling for calorie features, in order to ensure that program does not crash + when users type the wrong command to add calorie entries. [PR #33](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/33) + +### Contributions to documentation +* **Developer guide** + * Added implementation details for `User` class and `user progress` feature. [PR #109](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/109) +* **User guide** + * Added documentation for features `user setup`, `user progress` and `help`. [PR #109](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/109) + +### Contributions to project management +* In charge of organising and factoring out code written to ensure high levels of OOP, as well as one of 2 designation PR reviewers to maintain the Github repository. + +### Code contributed +* [RepoSense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=paturikarthik&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=paturikarthik&tabRepo=AY2324S2-CS2113-F15-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) \ No newline at end of file From 711a4f897234e625dfa40a7b0a543ffcaadb9896 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 11 Apr 2024 23:39:57 +0800 Subject: [PATCH 286/414] Add architecture diagram for developer guide --- docs/ArchitectureDiagram.puml | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/ArchitectureDiagram.puml diff --git a/docs/ArchitectureDiagram.puml b/docs/ArchitectureDiagram.puml new file mode 100644 index 0000000000..aae729c17c --- /dev/null +++ b/docs/ArchitectureDiagram.puml @@ -0,0 +1,38 @@ +@startuml + + +actor bob +rectangle { + rectangle Ui + rectangle Main + rectangle calories + rectangle system.parser + rectangle hydration.hydrationlist + rectangle sleep.sleeplist + rectangle system.storage + rectangle Entry +} + +bob --[dotted]> Ui +Main -[dotted]> Ui +Main ---> sleep.sleeplist +Main ---> hydration.hydrationlist +Main ---> calories + +calories -[dotted]---> system.parser +calories ----> Entry +calories --[dotted]> system.storage + +hydration.hydrationlist ---[dotted]-> system.parser +hydration.hydrationlist -> Entry +hydration.hydrationlist --[dotted]> system.storage + +sleep.sleeplist --[dotted]--> system.parser +sleep.sleeplist -> Entry +sleep.sleeplist --[dotted]> system.storage + +system.storage -[dotted]-> Entry + +hide circle + +@enduml From 67e86b9203019d7916be0dddccb21bcb6927b92a Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 11 Apr 2024 23:55:30 +0800 Subject: [PATCH 287/414] Add quick links for developer guide --- docs/DeveloperGuide.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 7ce589076c..53c39f7b13 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,5 +1,29 @@ # Developer Guide +## Quick links +* [Acknowledgements](#acknowledgements) +* [Design](#design) + * [Architecture](#architecture) +* [Implementation](#implementation) + * [Adding calorie entries feature](#adding-calorie-entries-feature) + * [Calculating calorie requirements based on user's goals](#calculating-calorie-requirements-based-on-a-users-goals) + * [Calories list feature](#calories-list-feature) + * [Calories delete feature](#calories-delete-feature) + * [Parsing user input for hydration entries](#parsing-user-input-for-hydration-entries) + * [Calculating hydration requirements for each user](#calculating-hydration-requirements-for-each-user) + * [Hydration list feature](#hydration-list-feature) + * [Hydration delete feature](#hydration-delete-feature) + * [Adding sleep entries feature](#adding-sleep-entries-feature) + * [Parsing user input for sleep entries](#parsing-user-input-for-sleep-entries) + * [Sleep list feature](#sleep-list-feature) + * [Sleep delete feature](#sleep-delete-feature) + * [Calculating sleep requirements for each user (Planning)](#calculating-sleep-requirements-for-each-user-planning) +* [Product scope](#product-scope) +* [User Stories](#user-stories) +* [Non-Functional Requirements](#non-functional-requirements) +* [Glossary](#glossary) +* [Instructions for manual testing](#instructions-for-manual-testing) + ## Acknowledgements {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} From 993d05e7072ffebbaf3a1ccd0d14f0860f801c33 Mon Sep 17 00:00:00 2001 From: RexYong Date: Thu, 11 Apr 2024 23:55:50 +0800 Subject: [PATCH 288/414] Add architecture portion of developer guide --- docs/DeveloperGuide.md | 24 +++++++++++++++++++++++- docs/diagrams/ArchitectureDiagram.png | Bin 0 -> 27201 bytes 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 docs/diagrams/ArchitectureDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 53c39f7b13..fdfe52886b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -27,8 +27,30 @@ ## Acknowledgements {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +## Design -## Design & implementation +### Architecture +![ArchitectureDiagram.png](diagrams%2FArchitectureDiagram.png) + +The **Architecture Diagram** given above explains the high-level design of the LifeTrack app. + +Given below is a quick overview of main components and how they interact with each other. + +**Main components of the architecture** + +`Main` (consisting of class `Lifetrack`) is in charge of the app launch and shut down. +* At app launch,v it initializes the other components in the correct sequence, and connects them up with each other. + +The bulk of the Lifetrack's work is done by the following four components: +* `Ui`: The UI of Lifetrack app. +* `calories`: The component in charge of the caloric entries of the Lifetrack app. +* `hydration.hydrationlist`: The component in charge of the hydration entries of the Lifetrack app. +* `sleep.sleeplist`: The component in charge of the sleep entries of the Lifetrack app. +* `system.storage`: The component in charge of reding data from, and writes date to the hard disk. +* `Entry`: The class in charge of all the data entries. +* `system.parser`: The component in charge of parsing all commands keyed in by user. + +## Implementation ### Adding calorie entries feature #### Implementation diff --git a/docs/diagrams/ArchitectureDiagram.png b/docs/diagrams/ArchitectureDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..974f79f90bb878c8a4276a5ee9d966990243cf4b GIT binary patch literal 27201 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%0DjpCFHdCIJfEH`=d6wLO6lxIiFn zp!u2`8#_8yTO8fOT1DVlbbe6qD7gzfEL1Cx5>asV76tzd=HTG4>dtBd|1rpO!Mp;` zBt!B=VtdYQt&SNZYFOjM9b1@cMzS+ANr7@g=B54A1M43g4xY@8jz@yLyoH0m$?h|= zumtVj#-izov@ZVyL{!(b{Fk9l<~`99$rJ zWZm|%cB@**Za5B^Oq73`CX?OimiEJk0~Id6-4SBsSvR55(4_Wha)siVl?v_|RJw-8 z`kwdzcYwXY7o`wj{`4tFpGYQmoQiqwDENuM1cF_>Wzpv4?tV{BZk>?#jEPk(M0cAZ zaU(k=C8Z0@oGi}Cq)Pfcl+)R|W8^VIDdKyz$lHKJ;=@|E5-Z43%V z9kT6#qT*EvPkxLQ`LalZsi@}|;DYeM8M$`3b~c05=*K9Aygyef1X;o(DA-qDFV_@B0-DlI<+!hTVCWJsfw`b4R<&8gCNT*K!u4|c5$V*ZfI+IQlac(Pbp|TIcor(9qKGm!iR;G11f8H=MT8W6`=j$=Nqt8p&jyJ$r_TVC{ToelZK- zqyG$kP(n~dBpxX+0~;1}G*tCo7E{k@3Poa@ne_DZ==5N7oMr^%axXwKhZ;AvWYOO4 z?xESgzRAp8^`RhEKLHMLO4-{K!!2@D@iQD+H z73^!7KfXRAAZ?d3b)p%B(0tQwk_^;h4BCG8=-F$(|C|y+#NaiZf?f0)20TdQ|H8Zx*R#nr6O#r3+rQ~Bv z0EW@^O9#ZnI{Ph9gQ)GWx6+{xUi`9&?zP}gi)?XWy>X)h9R|b&X(*13v-kew3d<44 zXa0I-=zTl|9o@&)z1SY7jJHd-fH|ka-9R;~%UhJb{+NM)@h!w&bw$29m>~6eA|Ai;ED=n8#mJ>+ zrKYA9g_P7*zI*riOc*QpxO)OYYJPp?_H$%DFGNJD@BT|9OAv2NME;MutL2rI*7KkD zTEWu+E?N(SE|A*brM(X61r97MFL3)b4P#&apNL& zR$VYDh?SQygE@tTYq%215dr9B5DO0dI@+(2kifx}w=6QDr$eN*_kyGwoGrHu%HJSu zqvPYDOiCNY$N(!1nH&0xcG|CYL|WaW?o*_l)N-ndif>ncw*PY&c#AFPl^y_~)a+k5uRkqDrrI)C)zSq^&^;I6n-}^#$7Z07JaHW zcme(&C(hv}>ALBy376ESlM9_v&uE_4H8 zoP$0O%h3$72jenk8Fpon`KvR1vT+IWRRNHqf2C*z=f>WC(ny}TvSSx+RTmu@w!z8C zX$>KQsov*6u)X>##TEV|#YMrrt?+RJ;dClpO&DM^(Igo`C1&W7b^+wO^(Ms{FnO^G&C?Im zeIU34&{xt&B9Rk3iC%y20Ys>4!uI}11f#e(7CN#;>@^<)gW<-v*F6v|H#v8DuYGu}4ZzApP~kKp56**OCcdzIZ{t2y*Y4_jS&&-JE7yClEi7NNE8+dm)gL|g&BNNaiwdNJAaTjAD|U*@Hv<=AQ+~M11+3~;Y%O^ zKTL_*e+he4Fhl?TeIlPr85tQJbzl{*-tN;6_Gmqm)5M#NG0~xm`1JSx78J6Xwq8)#mLC#579GElyw~+rz;4(`7aBxB3npU7&k_ zXy&J$3EQD~n|Ag#b=uABe2;hkEs;@#D+k%#B+*)Q>4PCkzz*3p^7vN6`@KkGjfhHM z?Vz#u10TV8IVYacQ=KOJFrTaz$NLPq%=_r8KVbL`veAr09D-=85lAFOz4zqoAO`0=mbI z+K|2mnDUJVE~Db`5`(#ww4qI+YL0P*b1n`5&MiGlCeP}12WRq)s}VpI!9+*Lu`Rs` z$F)uqQFwgxVyC>i@1)~ z1eOAP%Yc?f({w-sNX4~-R@Z8aarRMnXjMFVVd8k>B7GK69B&uT%A2~a41bzEz7H%1 zxN^xc=)|LJ_gr!~V5^`8W~D%unjw8$|KrDIB-cN{1=uf`seQB9A!G{9hmSj>BH)2T zftM{RSt&z(s$XOKolE32y*wDqZn*KgjGw+*3HIs@!K zNPaX6b2t~Hf^=LnD;wJjj2$Ns$`(*BupDp8%Y~h&k8lJ>L_|a}(ihK*Q8~X0V_XA( zc@6y#cutsxiC6hiO~j@g^gHBvAV58se*B3nqa-9_xHT4&4i10w4eM@bn;|K~L`#5> z{}i0#5*5`B4o0KtfnNOapijhgX#j^pF8TZ95I{fh&|z*G{BR8ha~e0dL>Cli9@y(D zI?lO&?0cKWyALp?Dc9HE@K=B9g_d@eEi-^%JwTUlJoq|zqm^Vc@u;0>q^ym?^ zA|41580CPwN?%*lJP$e;aKM@6CTSu7o`79&+P6;UF(_9@%uOl%1{evOW*VUgINk%A z?b^EQ3a{NgJvC8-7F6nI0&`0&$}D@)l$gP1kDUQr3~=(pu9xdkgF`gpR=_gMn;aj% z0HaHkln9T%N0729L%OWbOEU#YXyl6%E2bGoGxTk}jDzVRRdHpIB>jW;aP<>WX=ZL7 zc2KK&`NB2HkNF>(ZQoDlzXNU=$0DepIN(~em-DliLwHMMfWH8M^ZD~<_W++@vTMH6 zjTenvsD6N`>oj{FBN~(_gn|jyRHT_b$Y|?RKPt=1)!Sc}J%jllTfQfaZ~iI{K1s(y zZ03}(9N>J~M4#{K>H>-sj%%ltRw2(SO}~g!9X%+WEdl>@F^rr1oNkJe+MB)%L>(k{ zz>DkX%W(ZmFfDM&XYM`;^Y!b1uG|p^@F*%RI+=}Jh(||9iHGeM{%OG99Ja8`fNfJ_ zMkuZHa&6C37{z=uweeO?PVW7xV(wG$rb5x3K`aAP?lToGG<__>CMK2`8NI=O`!)=j zRb#LAoHnuI8w^|Q$({OcUjIx0s2k6nrKUVhu*hEvU-!cZ*I&m?It3)aYd+GJy>KEZ z$C<^2R}2y~yAsHnbC+*69&OhH$gHZWnprvIxGo^jyAaVS9~Ua4Pw1j*R>3v~h-kTH zH3c0Q94u;#S%kZb+{|ywla)4L8DNT&#gAhPn+_h1=X+A#Ok{d^h}Zk;0#s@@2(&kO^>8A2Q7RHy!&- zN0PrnJ~ork!8s~#^6bT>rnWywqao5-2Zp_6|H7rvSFgmu-Hv%K`U>pCGtDs&qu{Is zSrTXaPt%2m2;>2F(HY*BBB!7LEQ!dk-9$&+n`*!V1z0YD-?Fy4Em{c_ne6O2xM+{M zYmnGI6t`iyKmj)}auFrcsszKXTwfR=kkl{{7K<#I1>zN#D-mrK9-B9}={yk3z|X(K ztR_`RI1E@8;Th_02qxuBhvUON5cV67#BSAFKx(>t85p=lIqh*o{ul4P&#|%8m_hTk z@o!K|CVCfvE87Hq2#Z#Np&#cJ>$@c6S&<91bYJrri$IQ5bl<_>?#B&m!00W3 z#pPOUCD^Dqhk^%!5SWlRQ&uOBWPrB@$|-X{{?o^gt^NIO;KIXx`(iUzZP=%E8ga&i zBNJ?8gJZ?+JH^8}bp_jE20kZ8yFmKPAwJ!`d)E_Qb0p`BgvZ=3m(Yy>i22VM~J8k!N)c!9s%#Nd6wfjBBDb37~_sN&`J61FJxn_ zK0sOr4Fz7&1cQA=P?}jdIm0TLptR4BI&yGv!E-5%lz1!w;~7PMuWF!iB-T3ej!J>C zff8$^kqf+5;418d#Q-wNaYVc=7|9~9C9_apyC!AU48CQFI>Y2;>|!m1*g_QI^?_kX za4>pvM+w3xq52et2#(f}>Kq#Kf{q5JMWAD*6nm_E7r@XpaN1!8nWdy^Ae?bZz6>$g zSD00o&x$ZA&ujx5SW#KYc_QYeqoV_44)8A?1Oqu>5Cy^zYZV5<#L(3rY3GQveyJpj zBG9rFw#*Zl25{jwknZ0qo?~DFLI7w0N8{s$TiS1Fw1c zWcmZd7Sz}&B7 z{_w9LNiWcOzK>vEyw3-z0Kyt8Be=zsUy4!5tOzZIw=Pg_mg!c}1HG5+-#gw5~0SEJy% zp?7`Yhlz}WMY-xKc=k9)Wkl8>e&93M7eT@b6E1quXA5wXZllfJ)wML4mHNKfDAr^D zuvk7$R2otvI6ysmFH)??;3hifJ7p(WDNSHVi@9J-`N7=ksuWOpqiHW-77S;w=U^i; zS;PZqZ_)QL{)n3Sb2r2v?zJ;UF;}+-d;E&k!>G7GSE|eF-TOy4>p9WDzXJnB4WMi< z*JLIf;UJIVF53#Y8A?Qv1qLuyrO~^tcmX%mzLr0zL7sI1nv7wfDhHj4jB}xzzzwfs z(MZT{CEJ`eU=V>qWCL`95F742SbG~YSoBBuP$fk?9=VNgvdGInn5n@OPU;ab33>qK z5I(58>?=q|GxZ;6AcRU?2M7oV0J68|g>rIprwE}KFwZr~vtHu{d}wGnyajxJK-#Qu zn!A8O6K*frNZi{3E(I85e%sLVh>nes5<9IG)8y}r8 z9VI17*>4GtB|>^WYU~Rc)N^~?GE z-`xATZ|;lxI-kAwZ}>jXde*bns+H{!00Xx9r@OaA!;3YTJ+3^;4?7Hi{22=iYOB-L>QBEHyt}e0i*xt=>}qO z088t9gxgpXC*ta)hyrts(HXcjCt*CsGPO5#4XCE*9(qv;i7|AZa+*H%KKg^=llbVQ zBwbA{pZD8TezKD(&%CBAyUzSJ`aU8GRZ^8eCtA`4K|w*2pT*Sdn1rR( zpdst5IGqhe19}Gx!_P1MG)azO6?q%Bbry!m8Hxm&-_N-@ldns*6L9{Ynvb(8YPPqx zpP?iR>$C+ga#7dvVF&1{e+Oc$-`!Dy%&S#HY%DHQ(2TmKg4a_-Vu2_je7R`i(lBmR#74aPutD&fPpbo{br~ z--6Rvm-Tc7XCBTsPUNS=@%>N$v?aedr)O!IZ2PMfArSBGEjIB7OV?^DZzchZO(kmxi@0^PE>#8O@W& z9zdCa@HB5@C#uWahS#fA?*4nYSW$h%@*nqq`I0rw5wU%zm>6_!rpx{#V3gz<2^(^t z=WfgLd7QX%$^BQ$To$9>{vo2Xe-KjxKsy13StZXlupWjMbiAW%OY>j;P!W|ofu^d3 zM-`gsa{6kPpq#iwC4Xd%=t_OLjDC(b7hZ1GkKy8zUA_()6tYoV7I+Gv@S0y*%3bhv z(%%-8QwMNW0nvE;A*T2it)zCnO-xKr{k+0QM@ONFJ8vs`dK`pvVZ^J(B*iZss<*ES z>lEK9OvS^YDVu5mJgzmLQkM}jbETHu3k@A)y+%0cdtQ74JogDaAn>?4w?$K1ThVuJ z_Y)dtjZw_)O#)qL5(u*EO(m3l6TMooj?}6H`Qr9)u*N+ZKkV3)N;+FNElF6DK;06XsA}9xA900eA*}y=d;_{Xs`fFhGYO~=FpU0f7-yp za{2EYr(*9D_n}Q0qPS9sP^O39?Nm^>3awVTAiH+ z7_(D10gCRoV%R9JsPOs0hJy-s%OP%N^`r2U{!j#L+p%MZPvFa~LJpGe@-iv#UC*|j zRQ~343(P``LXqE)#PxG3iy99wt7}J zHtgg|hozF~OxN1bjL_hQ#>O7T#@2$ZQ%Eyz4ZrtSsaubw~g6JODLw@p{f8gBo57Nd38Yn^x04hTLAW zyu4iQv|dO^NY^SwAG)z!MUKts5d2B{ab*n!rDQy=x?ZJ;NW`djWg#B{Vr!^FCk^%P zD(Ph$8Gj5URMQ-|S>=I3778Ae4Z{QhwciKSnsj2P1h1T96X*(`HU+9QkgHAG#W83P z9IrpAHXc<~hI9D>oP4+`AL{FI@@uhKrfO)K7l9>puP+T0P8?2mROm$COkL2@{Dy3aEB5T?K9(Aa_|;1Y z1?9{pK&c!4)A&Ia2pGYAb{n3U?GhHY#!{)=X$X+d_*!jN{zHV}?6;z>>0q{@Y=ky| z_Tf}>h!ihbch6}Q(ar{o`uci=1afe4+FDT4@REFs4tMlR?ar@Y*l?;M_3&3L>)-V> z7eR3V+U!|hA)w&r49(4-q_O}`ue%?Hf1 zG=8hyS$cZghV|(!;=R*S9^kqVSZI3~_BSx4;{YgOp>&*TFv^4R!2!xj9$e_dc9dy~ zqtLCt24Dtq9blN2L*y}u?3-38s@SrAW@2Vew`*QpAag^%O*(wo4;mb`WLQMnK?&p^ z{xg8!6hM-_5A@4@-_TGI@Jr!|+WkcJ2eca0H278rV%txK1_w{i&5;EH@Jwz$ISvjH zE`hUI@*O6eMVHi{M1u0*>zkG|dZ!tbV~IQy^1oxZsyk=0_R+CUXv_x9d$Y2#a814_ zVHzIvf32~hSgjiN5jDZ-s^1hVacZZ|OH?bgrj2apcXp$W;B$TfR{Z<-7hqZguJ?!t z>{`QC%gc(wMKG$&t?rkeWIVS4c zE5Sm{im3P-*|5Bw7#O%TJEHsOj{u&R*-r5P;1GP%AvAFZ*$*BV?9l171CClV^i=rb zXuqrpCa@#ct)INSj{|#DvNka-uEPK~XdDW6ne&%$wnW|&PjpAKlIadK2f^5{v<=7|&YQ$3!k9}B1R#xZgv{RpiA z>V%S8-}{8N?%8YoBD&ky_^?$(hB?DT5eAJ9SclzH9Tl6Tz43-N-M zzg|P82w7}H?zP{khUaNYRV&L&x205CmHP6}D9SHJe=lu8(q(^Ts~$nJ)wvF1|2i6T z6d`*?9D<-Dj2^xU(I5gzz#Zd#5jjxDjWbDcT>M zp320y`O&)Kr|UvVH}ot2W-wbLA;+pd#4S$0aUGYaDFbJEnwma~C?gkNl(vMJx*2s1 z{Z%EmgMZIX)xNvz*=VEapxrmPvfzK{;q1o=&pH3br3u9{Up?Kk#>QJD`C40yn3z!r82CeSTRPWmDSh!SB{6UXu z_zUX$mz~*o(M0&>A&y1bAk0SkpUpMYhXBlQI?0E9u!cp`z<$~|xN3xF-LH;nfa8%R0eXIIkd6(C zht;l^MN-|wj5u#pUlN?HEp4#4CT2oL@k=bAh*Xuvs%i;_iFg z0sT^I(cs|&Ml7}=x6+PeOYAl+pc9fFazj3srAP#V2%fLss`5-fPE7}ulf*l0jG3{I z5$l&pIg7!gKllbGO+4I?qzme4Ts!=e>{oF*l8s~B`B)X~Gq4h0zllHiSu>JMreQFw zNg4UP>kSxCjze=edYPR=+F)WTO!gI4&jt<0L^y+$$BErJTc zCwZKN$|LypOU}*CHSG?T&`95HoIp2W1M*cr2)QKKFQ+QGyfQkT6S4TYNW~6=9?9JQ zsqu#F0g)&-VL7f#_ZTfJO0ukPTu(n;VSD4+Xp_hV76W}9%@_$8nd5?LI#Jf9bak^G zW0+fV&bhRSS*kUwmSmgvkG9jI7Js#}vHhB+hq zUDIfqp86n~?2f;HJPiZEUhIU1GYpFkrS!jL7P!hc7j=*dfcHUI( zZn`PX3ZD8+sROD#C8YhTnSp_gYttp95MF^uFH(C7F9Xh5^Tv{HfeaGYsR2C~7pw&? z$jZv5QL?R+E;Dr7vLT+t^UkZ9TN_1=LTfg1lVM{Mp~bU)y8c%^qB4+Z>U6SXp_wn_ zbu(>DR9=4T_%8$xch6oF!3v$>&zh&6$JZ7Ny4g1-yLc7xOK<7xxSaWb(*Yzxgz;R? zsVo|eHqCc85L0v~xh@gw2s98!3@-f20X>s*bOtfcQ$IOYb;ewjhm!3=0t;cf?54JC z2$Oje`vQ7QK7$Zz%q~Jps=3_ig?*e1H@@NXy$b#=R340;-&-kF+Hl9MN|d=}K$|2w zxq*k_{5vwoWQn?}nV$r?R-iv!Sh`cL=2x8fiBRGbcUW*`h$d*fzz3cG{Xs#YH;e3A zH~cimA6DWXMYtJ6#yRu#2WWF;y!q2stA}F`b4#@Djvm*$H;d*Cb?z$G98)zbJRh_q zZ4k+A6(Zq&{Dt@l?E~Temmd+Y$py8!bIfLZQ>#vZEK~iR=&0WxzAIR~Xy&yb^#P7} z(*b!AT#V{J+`DWR^!Ke^@j8xMWm;K0cXa)t;+*4!143g8{KU7eUfa!JwXnw;?IByF zu<-Ng^Z$wD1$zwp?;W`dyrpGih-h974Gp5iHH_0x7NWrDneT9W=FKAu7`7o9nY;7*XljwIYrx0Qw(RxBk$H*P5(3oYRX?`>zBi1n)w=-bVXB0K zp~CvE=X`HeRGrdpX=y@fTKf|VYeb%?ncwrlw$#bVNjN7`e&QS%QVcz4B z(Wd{E;J1oFEVqIH7~PZc4gLMQxeOlb=33Y6_EdMWXji}QaSgsD_BYlpE_ngDQWjwg zP|Y^0JiO~^Y<&Ou_$xl%5|?gr7wZ7Zh17K5tu<9S}#|8U_= zox^Wv`(~Ri7$F?S7h3~I9pvV87M`>JbM<=<(tRf6NjUMdGc&QjuRJ~=aC*V%l$`R? zZI(Ll*r$pOZYX_kI!ZZXXn6D7();^TRYEKIGue5iNCf8 zSsPouQQm+Q@ui<8W>z0NxwFJI#ZztK^}*xu0o&vpBd1n8!bcqXB41^%1n!>0)OWBA zU4Hgm%r4_4=(;O6?Eek1@)fROJ5$rOnA&WuSo$=|pA03Sw{@s?_+ zLcT{=VQC>P&6*qV0iyszyH~WT828%$l!(#PLojzd&kH~m(>)xp5#W&%enHXI`ck`t z`638&AtVnW_^d$jAEG)sRP7&|etpaK>S=M*SO)_)#_?B3UB&!X_L>c^oco-4%qC?E zakAGfR8V*BTfTw+|3wxiuR(P9&J_2p)b@65lx#cnjWvTr-+av`P4uCQ7x`$Y>xBi3Xx}VuQX8lgQ3>;^ZK9&NW=8 z)HX2Dhe(J71<}2UWmi+XZmYFHL_~!o8WhQ%`oI0TiZJObk^!KraxrHT36djZC~%Fp z4#tOiw*u&HCnl0r!VXkN^Mx literal 0 HcmV?d00001 From d05a36a7dfd27d84df768913019cd5bb2b1eb45e Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 12 Apr 2024 00:25:13 +0800 Subject: [PATCH 289/414] Hydration List --- docs/CaloriesListClassDiagram.png | Bin 0 -> 26165 bytes docs/HydrationListClassDiagram.png | Bin 39775 -> 26742 bytes docs/HydrationListClassDiagram.puml | 3 ++- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 docs/CaloriesListClassDiagram.png diff --git a/docs/CaloriesListClassDiagram.png b/docs/CaloriesListClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..14be055a91c9c67cd934aa7977af7439da1337ea GIT binary patch literal 26165 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 HTHX8)$dFWu literal 0 HcmV?d00001 diff --git a/docs/HydrationListClassDiagram.png b/docs/HydrationListClassDiagram.png index 21168646bf4f874cc32c1a068be47ac15bba9067..972cdaa195a7ba34fd42d7419657fb50e8008c98 100644 GIT binary patch literal 26742 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 literal 39775 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 hydration.hydrationlist.HydrationList LifeTrack -[dotted]-> ui.Ui ui.Ui -[dotted]-> hydration.hydrationlist.HydrationList From 59f762f9c016aa6edaa901a516fd3edcdef0e28e Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:54:22 +0800 Subject: [PATCH 290/414] Fixed userprogress to only show current day consumption --- .../calories/calorielist/CalorieList.java | 18 +++++++ .../hydrationlist/HydrationList.java | 51 +++++++++++++++++-- .../system/parser/ParserHydration.java | 10 ++-- .../seedu/lifetrack/ui/HydrationListUI.java | 4 ++ .../lifetrack/user/usergoals/UserGoals.java | 4 +- 5 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index c43ad75807..064c3e763c 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -7,6 +7,7 @@ import seedu.lifetrack.system.storage.FileHandler; import seedu.lifetrack.ui.CalorieListUi; +import java.time.LocalDate; import java.util.Collections; import java.util.Comparator; import java.util.logging.Level; @@ -222,6 +223,23 @@ public int getCaloriesConsumed() { return totalCalories; } + public int getCaloriesConsumedCurrentDay() { + int totalCalories = 0; + for (Entry entry : calorieArrayList) { + if (entry.getDate().isEqual(LocalDate.now())) { + if (entry instanceof InputEntry) { + InputEntry tempEntry = (InputEntry) entry; + totalCalories += tempEntry.getCalories(); + } else if (entry instanceof OutputEntry) { + OutputEntry tempEntry = (OutputEntry) entry; + totalCalories -= tempEntry.getCalories(); + } + } + } + return totalCalories; + } + + /** * Loads the last entry ID from a text file. * This method retrieves the last entry ID from the file using the FileHandler.getMaxCaloriesID method. diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index adde359be0..0f4afb5b67 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -3,13 +3,19 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserHydration; import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.ui.CalorieListUi; import seedu.lifetrack.ui.HydrationListUI; import java.io.FileNotFoundException; +import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.logging.Level; import java.util.logging.Logger; @@ -70,10 +76,13 @@ public Entry getEntry(int index) { * @param line the string containing the index of the liquid record to delete */ public void deleteEntry(String line) { + assert (line.startsWith("hydration delete") ) : "ensures that input is correct"; try { int entryID = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); int index = getIndexFromEntryID(entryID); - if (index == NO_INDEX_FOUND) { + if (hydrationArrayList.isEmpty()) { + HydrationListUI.emptyHydrationList(); + } else if (index == NO_INDEX_FOUND) { HydrationListUI.unsuccessfulDeletedMessage(entryID); } else { Entry toDelete = hydrationArrayList.get(index); @@ -110,11 +119,25 @@ public void addEntry(String input) { updateFile(); HydrationListUI.printNewHydrationEntry(newEntry); lastHydrationEntryID ++; + if (hydrationArrayList.size() > 1 && + hydrationArrayList.get(hydrationArrayList.size() - 2).getDate().compareTo(newEntry.getDate()) > 0 ) { + sortEntriesByDate(); + } } catch (InvalidInputException e) { logr.log(Level.WARNING, e.getMessage(), e); + System.out.println(e.getMessage()); } } + public void sortEntriesByDate() { + Collections.sort(hydrationArrayList, new Comparator() { + @Override + public int compare(Entry entry1, Entry entry2) { + return entry1.getDate().compareTo(entry2.getDate()); + } + }); + } + /** * Prints the list of liquid entries. * If the list is empty, prints a message indicating that the list is empty. @@ -124,9 +147,17 @@ public void printHydrationList() { HydrationListUI.emptyListMessage(); } else { HydrationListUI.hydrationListHeader(); - for (int i = 0; i < hydrationArrayList.size(); i++) { - Entry entry = hydrationArrayList.get(i); - System.out.println("\t " + (i + 1) + ". " + entry); + printHydrationInflow(); + } + } + + public void printHydrationInflow() { + int serialNumber = 1; + for (Entry value : hydrationArrayList) { + if (value instanceof HydrationEntry) { + Entry entry = value; + System.out.println("\t " + serialNumber + ". " + entry); + serialNumber++; } } } @@ -145,6 +176,18 @@ public int getHydrationConsumed() { return totalHydration; } + public int getHydrationConsumedCurrentDay() { + int totalHydration = 0; + for (Entry entry : hydrationArrayList) { + if (entry.getDate().isEqual(LocalDate.now())) { + HydrationEntry tempEntry = (HydrationEntry) entry; + totalHydration += tempEntry.getHydration(); + } + } + return totalHydration; + } + + /** * Retrieves the size of the liquid list. * diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index df2afe03d6..75197840b0 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -8,12 +8,7 @@ import java.time.LocalDate; import java.time.format.DateTimeParseException; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectVolumeInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationMissingKeywordMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; public class ParserHydration { private static final int VOLUME_IDX = 1; @@ -64,6 +59,9 @@ public static Entry parseHydrationInput(String input, int lastHydrationEntryID) LocalDate date = null; try { date = getLocalDateFromInput(strDate); + if (date.isAfter(LocalDate.now())) { + throw new InvalidInputException(getDateLaterThanPresentDateMessage()); + } } catch (DateTimeParseException e) { throw new InvalidInputException(getInvalidDateMessage()); } diff --git a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java index 2b14af432e..0089846b49 100644 --- a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java +++ b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java @@ -34,4 +34,8 @@ public static void printNewHydrationEntry(Entry newEntry) { System.out.println("\t The following entry has been added to your hydration list!"); System.out.println("\t " + newEntry.toString()); } + + public static void emptyHydrationList() { + System.out.println("\t There is nothing to delete as the hydration list is empty!"); + } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 9f70d4bdb1..824f19ca36 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -57,7 +57,7 @@ private static double getAMR(double calories, int exerciseLevel) { public static void getCaloriesProgressBar(User user) { int caloriesRequired = user.getCaloriesRequired(); - int caloriesConsumed = calorieList.getCaloriesConsumed(); + int caloriesConsumed = calorieList.getCaloriesConsumedCurrentDay(); if (caloriesConsumed < 0){ caloriesConsumed = 0; } @@ -81,7 +81,7 @@ public static void getCaloriesProgressBar(User user) { public static void getHydrationProgressBar(User user) { int hydrationRequired = user.getHydrationRequired(); - int hydrationConsumed = hydrationList.getHydrationConsumed(); + int hydrationConsumed = hydrationList.getHydrationConsumedCurrentDay(); double progress = (double) hydrationConsumed / hydrationRequired; int width = PROGRESS_BAR_WIDTH; From f3c43f964b781161d1eb98569eb8153eea71fe6d Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:28:15 +0800 Subject: [PATCH 291/414] Added PPP --- docs/team/shawnpong.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/team/shawnpong.md b/docs/team/shawnpong.md index 8d08de2413..692b95bded 100644 --- a/docs/team/shawnpong.md +++ b/docs/team/shawnpong.md @@ -15,8 +15,13 @@ The program was created using Java. Version control was done using Git. ### Contributions to exception handling ### Contributions to documentation - +* **Developer guide** + * Added implementation details for `Hydration` class. [PR #93](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/93) +* **User guide** + * Converted User guide from word document to Markdown format for all existing features. + * Added documentation for features `hydration in`, `hydration delete`, `hydration list`. [PR #91](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/91) ### Contributions to project management ### Code contributed +* [Reposense Link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=shawnpong&sort=groupTitle&sortWithin=totalCommits%20dsc&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=false) From cbd853dd2193a3cc29881b4903ef7584736bbfb7 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:35:34 +0800 Subject: [PATCH 292/414] Fix junit hydration --- src/test/java/seedu/lifetrack/HydrationListTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/test/java/seedu/lifetrack/HydrationListTest.java b/src/test/java/seedu/lifetrack/HydrationListTest.java index e17a7287d7..6661d77adf 100644 --- a/src/test/java/seedu/lifetrack/HydrationListTest.java +++ b/src/test/java/seedu/lifetrack/HydrationListTest.java @@ -100,16 +100,6 @@ public void testDeleteEntryFromEmptyList() { assertEquals(initialSize, hydrationList.getSize()); } - @Test - public void testDeleteEntryWithInvalidFormat() { - HydrationList hydrationList = new HydrationList(); - hydrationList.addEntry("hydration in Water v/250 d/2024-02-23"); - int initialSize = hydrationList.getSize(); - // Try to delete with invalid format, should not affect the list - hydrationList.deleteEntry("delete 1"); - assertEquals(initialSize, hydrationList.getSize()); - } - @Test public void testPrintHydrationListWithMultipleEntries() { String lineSeparator = System.lineSeparator(); From 2910db336b2e9c8e1ad147ceb5252c8f0d31d8ea Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:44:56 +0800 Subject: [PATCH 293/414] Fix import gradle errors --- .../lifetrack/hydration/hydrationlist/HydrationList.java | 5 +---- .../seedu/lifetrack/system/parser/ParserHydration.java | 8 +++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 0f4afb5b67..e9cbd48156 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -3,12 +3,9 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; -import seedu.lifetrack.calories.calorielist.InputEntry; -import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserHydration; import seedu.lifetrack.system.storage.FileHandler; -import seedu.lifetrack.ui.CalorieListUi; import seedu.lifetrack.ui.HydrationListUI; import java.io.FileNotFoundException; @@ -120,7 +117,7 @@ public void addEntry(String input) { HydrationListUI.printNewHydrationEntry(newEntry); lastHydrationEntryID ++; if (hydrationArrayList.size() > 1 && - hydrationArrayList.get(hydrationArrayList.size() - 2).getDate().compareTo(newEntry.getDate()) > 0 ) { + hydrationArrayList.get(hydrationArrayList.size() - 2).getDate().compareTo(newEntry.getDate()) > 0 ) { sortEntriesByDate(); } } catch (InvalidInputException e) { diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index 75197840b0..8d4c8e66cf 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -8,7 +8,13 @@ import java.time.LocalDate; import java.time.format.DateTimeParseException; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationIncorrectOrderMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationMissingKeywordMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getDateLaterThanPresentDateMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectVolumeInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; public class ParserHydration { private static final int VOLUME_IDX = 1; From a8066531acdf6ab0faf9f3016faef3a9708e4fed Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:28:25 +0800 Subject: [PATCH 294/414] Add more to PPP --- docs/team/shawnpong.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/team/shawnpong.md b/docs/team/shawnpong.md index 692b95bded..c7d4d0cc97 100644 --- a/docs/team/shawnpong.md +++ b/docs/team/shawnpong.md @@ -12,14 +12,37 @@ The program was created using Java. Version control was done using Git. ### New features added and enhancements to existing features +1. **Added the ability to add entries for hydration intake.** [PR #71](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/71) + * What it does: Allows users to add records relating to their hydration intake, which includes `DESCRIPTION` of + drink, `VOLUME` of drink, as well as `DATE`. + * Testing: Added JUnit tests for the feature as well. + +2. **Added functionality for users to view their caloric and hydration goals** [PR #90](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/90) + * What it does: Allows users to view their progress with a progress bar with `user progress` function. + * Justification: This lets users easily view their progress with an easy to comprehend progress bar, so users can + know how much more calories or hydration to consume. + +3. **Added feature for users to view caloric and hydration goals for current day** [PR #180](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/181) + * What it does: Users can now view their current daily caloric and hydration intake with `user progress` function, + instead of the cumulative intake. + * Justification: As this is a lifestyle tracker, the intent is for users to use it on a day-to-day basis, tracking + their current daily instake against their goals. They are still able to view their previous history of consumption + with `calories list` and `hydration list`. + ### Contributions to exception handling +* Added robust exception handling for hydration class and lists, in order to ensure that program does not crash + when users type the wrong command to add, delete or list hydration entries. [PR #38](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/38) +* Added robust exception handling for ParserHydration, in order to ensure that program does not crash + when users enter invalid commands [PR #71](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/71) ### Contributions to documentation * **Developer guide** * Added implementation details for `Hydration` class. [PR #93](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/93) + * Ensure consistent formatting throughout developer guide. * **User guide** * Converted User guide from word document to Markdown format for all existing features. - * Added documentation for features `hydration in`, `hydration delete`, `hydration list`. [PR #91](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/91) + * Added documentation for features `hydration in`, `hydration delete`, `hydration list`. [PR #91](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/91) + * Ensure consistent formatting throughout user guide. ### Contributions to project management From f4133d3c78adbc4507cb25c9f5dab61bb0e1a07f Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:29:34 +0800 Subject: [PATCH 295/414] Fix typo for rex --- docs/team/rexyyong.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/team/rexyyong.md b/docs/team/rexyyong.md index c0cb6a172c..87deb96ef0 100644 --- a/docs/team/rexyyong.md +++ b/docs/team/rexyyong.md @@ -43,7 +43,7 @@ The program was created using Java. Version control was done using Git. * Added robust exception handling for calories Parser, in order to ensure that program does not crash when users type the wrong command to add, delete or list calories entries. [PR #43](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/43) * Added robust exception handling for hydration features, in order to ensure that program does not crash - when suers type the wrong command to add, delete or list hydration entries. [PR #50](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/50) + when users type the wrong command to add, delete or list hydration entries. [PR #50](https://github.com/AY2324S2-CS2113-F15-2/tp/pull/50) ### Contributions to documentation * **Developer guide** From d3c705912f8bf6a847d0edd8a7b964d47f36d5cb Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 12 Apr 2024 22:22:31 +0800 Subject: [PATCH 296/414] Shift all images into assets folder --- docs/assets/ArchitectureDiagram.png | Bin 0 -> 36807 bytes docs/assets/CaloriesAddEntrySeqDiagram.png | Bin 0 -> 23922 bytes .../CaloriesListClassDiagram.png | Bin .../CaloriesListSequenceDiagram.png | Bin docs/assets/HydrationDeleteDiagram.png | Bin 0 -> 35846 bytes docs/{ => assets}/HydrationListClassDiagram.png | Bin docs/{ => assets}/SleepAddSeqDiagram.jpg | Bin docs/{ => assets}/SleepDeleteSeqDiagram.jpg | Bin docs/{ => assets}/SleepListSeqDiagram.jpg | Bin .../UserCalculateCaloriesSeqDiagram.png | Bin docs/assets/calories.png | Bin 0 -> 13293 bytes docs/assets/caloriesDeleteUML.jpg | Bin 0 -> 52154 bytes docs/assets/hydration.png | Bin 0 -> 11783 bytes docs/assets/sleep.png | Bin 0 -> 11235 bytes docs/diagrams/ArchitectureDiagram.png | Bin 27201 -> 0 bytes 15 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/ArchitectureDiagram.png create mode 100644 docs/assets/CaloriesAddEntrySeqDiagram.png rename docs/{diagrams => assets}/CaloriesListClassDiagram.png (100%) rename docs/{diagrams => assets}/CaloriesListSequenceDiagram.png (100%) create mode 100644 docs/assets/HydrationDeleteDiagram.png rename docs/{ => assets}/HydrationListClassDiagram.png (100%) rename docs/{ => assets}/SleepAddSeqDiagram.jpg (100%) rename docs/{ => assets}/SleepDeleteSeqDiagram.jpg (100%) rename docs/{ => assets}/SleepListSeqDiagram.jpg (100%) rename docs/{ => assets}/UserCalculateCaloriesSeqDiagram.png (100%) create mode 100644 docs/assets/calories.png create mode 100644 docs/assets/caloriesDeleteUML.jpg create mode 100644 docs/assets/hydration.png create mode 100644 docs/assets/sleep.png delete mode 100644 docs/diagrams/ArchitectureDiagram.png diff --git a/docs/assets/ArchitectureDiagram.png b/docs/assets/ArchitectureDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..f54d12e1e4c8209305f925f96d9a7cddd921e39b GIT binary patch literal 36807 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 F{{YJDXKDZd literal 0 HcmV?d00001 diff --git a/docs/assets/hydration.png b/docs/assets/hydration.png new file mode 100644 index 0000000000000000000000000000000000000000..45244a9cb0e5a01a0a0b39dfb6369d836637f1a2 GIT binary patch literal 11783 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/ArchitectureDiagram.png b/docs/diagrams/ArchitectureDiagram.png deleted file mode 100644 index 974f79f90bb878c8a4276a5ee9d966990243cf4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27201 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%0DjpCFHdCIJfEH`=d6wLO6lxIiFn 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 From 2f4ac09bed56253e8182ae6722cb5597b5bf8f81 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 12 Apr 2024 22:23:07 +0800 Subject: [PATCH 297/414] Add calories puml file --- docs/diagrams/calories.puml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/diagrams/calories.puml diff --git a/docs/diagrams/calories.puml b/docs/diagrams/calories.puml new file mode 100644 index 0000000000..06c6322b4d --- /dev/null +++ b/docs/diagrams/calories.puml @@ -0,0 +1,26 @@ +@startuml + +rectangle CalorieList +rectangle InputEntry +rectangle OutputEntry +rectangle Food + +rectangle Entry +rectangle FileHandler +rectangle Ui +rectangle ParserCalories + +CalorieList -> "*" Entry +CalorieList --> "1" FileHandler +CalorieList --[dotted]> ParserCalories + +Ui -[dotted]> CalorieList + +InputEntry --|> Entry +OutputEntry --|> Entry +InputEntry --> "0..1" Food + +ParserCalories -[dotted]-> InputEntry +ParserCalories -[dotted]-> OutputEntry + +@enduml \ No newline at end of file From 979c7f4645c56d319812a293aa7c74ba2a5d53f9 Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 12 Apr 2024 22:23:28 +0800 Subject: [PATCH 298/414] Add hydration, sleep, user puml diagrams --- docs/diagrams/hydration.puml | 21 +++++++++++++++++++++ docs/diagrams/sleep.puml | 21 +++++++++++++++++++++ docs/diagrams/user.puml | 24 ++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 docs/diagrams/hydration.puml create mode 100644 docs/diagrams/sleep.puml create mode 100644 docs/diagrams/user.puml diff --git a/docs/diagrams/hydration.puml b/docs/diagrams/hydration.puml new file mode 100644 index 0000000000..68a74558ab --- /dev/null +++ b/docs/diagrams/hydration.puml @@ -0,0 +1,21 @@ +@startuml + +rectangle HydrationList +rectangle HydrationEntry + +rectangle Entry +rectangle FileHandler +rectangle Ui +rectangle ParserHydration + +HydrationList --> "*" Entry +HydrationList ---> "1" FileHandler +HydrationList --[dotted]> ParserHydration + +Ui -[dotted]> HydrationList + +HydrationEntry --|> Entry + +ParserHydration -[dotted]-> HydrationEntry + +@enduml \ No newline at end of file diff --git a/docs/diagrams/sleep.puml b/docs/diagrams/sleep.puml new file mode 100644 index 0000000000..0b7eb8b91c --- /dev/null +++ b/docs/diagrams/sleep.puml @@ -0,0 +1,21 @@ +@startuml + +rectangle SleepList +rectangle SleepEntry + +rectangle Entry +rectangle FileHandler +rectangle Ui +rectangle ParserSleep + +SleepList --> "*" Entry +SleepList ---> "1" FileHandler +SleepList --[dotted]> ParserSleep + +Ui -[dotted]> SleepList + +SleepEntry --|> Entry + +ParserSleep -[dotted]-> SleepEntry + +@enduml \ No newline at end of file diff --git a/docs/diagrams/user.puml b/docs/diagrams/user.puml new file mode 100644 index 0000000000..7e0f15b938 --- /dev/null +++ b/docs/diagrams/user.puml @@ -0,0 +1,24 @@ +@startuml + +rectangle User + +rectangle FileHandler + +rectangle ParserUser + +rectangle UserGoals + +rectangle HydrationList +rectangle CaloriesList +rectangle SleepList + +User ---> "1" FileHandler +User --[dotted]> ParserUser + +User <-[dotted]> UserGoals + +UserGoals --[dotted]> HydrationList +UserGoals --[dotted]> CaloriesList +UserGoals --[dotted]> SleepList + +@enduml \ No newline at end of file From 4bb87a3b29eda70c07c3b060b18b022501ef9e2d Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 12 Apr 2024 22:23:52 +0800 Subject: [PATCH 299/414] Shift all puml files into diagrams folder --- docs/HydrationDeleteDiagram.png | Bin 35846 -> 0 bytes docs/caloriesDeleteUML.jpg | Bin 52154 -> 0 bytes docs/diagrams/ArchitectureDiagram.puml | 42 ++++++++++++++++++ docs/diagrams/CaloriesAddEntrySeqDiagram.puml | 32 +++++++++++++ .../CaloriesListClassDiagram.puml | 0 .../CaloriesListSequenceDiagram.puml | 0 .../HydrationDeleteDiagram.puml | 0 .../HydrationListClassDiagram.puml | 0 .../UserCalculateCaloriesSeqDiagram.puml | 0 9 files changed, 74 insertions(+) delete mode 100644 docs/HydrationDeleteDiagram.png delete mode 100644 docs/caloriesDeleteUML.jpg create mode 100644 docs/diagrams/ArchitectureDiagram.puml create mode 100644 docs/diagrams/CaloriesAddEntrySeqDiagram.puml rename docs/{ => diagrams}/CaloriesListClassDiagram.puml (100%) rename docs/{ => diagrams}/CaloriesListSequenceDiagram.puml (100%) rename docs/{ => diagrams}/HydrationDeleteDiagram.puml (100%) rename docs/{ => diagrams}/HydrationListClassDiagram.puml (100%) rename docs/{ => diagrams}/UserCalculateCaloriesSeqDiagram.puml (100%) diff --git a/docs/HydrationDeleteDiagram.png b/docs/HydrationDeleteDiagram.png deleted file mode 100644 index 26043bb76d822a33a340f62d4defe1f78bf825b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35846 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 F{{YJDXKDZd diff --git a/docs/diagrams/ArchitectureDiagram.puml b/docs/diagrams/ArchitectureDiagram.puml new file mode 100644 index 0000000000..51c2f8adfa --- /dev/null +++ b/docs/diagrams/ArchitectureDiagram.puml @@ -0,0 +1,42 @@ +@startuml + + +actor bob +rectangle { + rectangle Ui + rectangle Main + rectangle calories + rectangle system.parser + rectangle hydration.hydrationlist + rectangle sleep.sleeplist + rectangle system.storage + rectangle Entry +} + +bob --[dotted]> Ui +Main -[dotted]> Ui +Main ----> sleep.sleeplist +Main ----> hydration.hydrationlist +Main ----> calories + +Ui -[dotted]> calories +Ui -[dotted]> hydration.hydrationlist +Ui -[dotted]> sleep.sleeplist + +calories -[dotted]---> system.parser +calories ----> Entry +calories --[dotted]> system.storage + +hydration.hydrationlist ---[dotted]-> system.parser +hydration.hydrationlist -> Entry +hydration.hydrationlist --[dotted]> system.storage + +sleep.sleeplist --[dotted]--> system.parser +sleep.sleeplist -> Entry +sleep.sleeplist --[dotted]> system.storage + +system.storage -[dotted]-> Entry + +hide circle + +@enduml diff --git a/docs/diagrams/CaloriesAddEntrySeqDiagram.puml b/docs/diagrams/CaloriesAddEntrySeqDiagram.puml new file mode 100644 index 0000000000..a2aee2cfaa --- /dev/null +++ b/docs/diagrams/CaloriesAddEntrySeqDiagram.puml @@ -0,0 +1,32 @@ +@startuml +actor Bob +Bob -> UI: Input "calories in" or "calories out" command +activate UI + +UI -> UI: handleUserInput() +activate UI + +UI -> UI: handleCaloriesInput() +activate UI + +UI -> CalorieList: addEntry() +activate CalorieList + +CalorieList -> ParserCalories: parseCaloriesInput() +activate ParserCalories +return entry + +CalorieList -> CalorieList: calorieArrayList.add(entry) +activate CalorieList +return + +CalorieList -> CalorieList: updateFile() +activate CalorieList +return + +return +return +return +return + +@enduml \ No newline at end of file diff --git a/docs/CaloriesListClassDiagram.puml b/docs/diagrams/CaloriesListClassDiagram.puml similarity index 100% rename from docs/CaloriesListClassDiagram.puml rename to docs/diagrams/CaloriesListClassDiagram.puml diff --git a/docs/CaloriesListSequenceDiagram.puml b/docs/diagrams/CaloriesListSequenceDiagram.puml similarity index 100% rename from docs/CaloriesListSequenceDiagram.puml rename to docs/diagrams/CaloriesListSequenceDiagram.puml diff --git a/docs/HydrationDeleteDiagram.puml b/docs/diagrams/HydrationDeleteDiagram.puml similarity index 100% rename from docs/HydrationDeleteDiagram.puml rename to docs/diagrams/HydrationDeleteDiagram.puml diff --git a/docs/HydrationListClassDiagram.puml b/docs/diagrams/HydrationListClassDiagram.puml similarity index 100% rename from docs/HydrationListClassDiagram.puml rename to docs/diagrams/HydrationListClassDiagram.puml diff --git a/docs/UserCalculateCaloriesSeqDiagram.puml b/docs/diagrams/UserCalculateCaloriesSeqDiagram.puml similarity index 100% rename from docs/UserCalculateCaloriesSeqDiagram.puml rename to docs/diagrams/UserCalculateCaloriesSeqDiagram.puml From fd7b4d6fb1b2ba53f0fe5100b79cc8ba61b5e22c Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 12 Apr 2024 22:24:23 +0800 Subject: [PATCH 300/414] Edit developer guide --- docs/AboutUs.md | 2 +- docs/ArchitectureDiagram.puml | 38 ------------------------ docs/CaloriesAddEntrySeqDiagram.png | Bin 23922 -> 0 bytes docs/CaloriesAddEntrySeqDiagram.puml | 32 -------------------- docs/DeveloperGuide.md | 42 ++++++++++----------------- 5 files changed, 17 insertions(+), 97 deletions(-) delete mode 100644 docs/ArchitectureDiagram.puml delete mode 100644 docs/CaloriesAddEntrySeqDiagram.png delete mode 100644 docs/CaloriesAddEntrySeqDiagram.puml diff --git a/docs/AboutUs.md b/docs/AboutUs.md index a3428887ca..bd78557efc 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,4 +5,4 @@ Display | Name | Github Profile | Portfoli ![](https://avatars.githubusercontent.com/u/64789669?v=4) | Paturi Karthik | [Github](https://github.com/paturikarthik) | [Portfolio](docs/team/paturikarthik.md) ![](https://avatars.githubusercontent.com/u/110764881?s=400&u=f41e3f40315f394bd71538063882c06bcfa2b624&v=4) | Shawn Pong | [Github](https://github.com/shawnpong) | [Portfolio](docs/team/shawnpong.md) ![](https://avatars.githubusercontent.com/u/76645229?s=400&u=235aba3bc4b5de57d0a76e24506f094e28734637&v=4) | Rex Yong Jin Xiang | [Github](https://github.com/rexyyong) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Yanyu | [Github](https://github.com/a-wild-chocolate/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Yanyu | [Github](https://github.com/a-wild-chocolate/) | [Portfolio](docs/team/johndoe.md) \ No newline at end of file diff --git a/docs/ArchitectureDiagram.puml b/docs/ArchitectureDiagram.puml deleted file mode 100644 index aae729c17c..0000000000 --- a/docs/ArchitectureDiagram.puml +++ /dev/null @@ -1,38 +0,0 @@ -@startuml - - -actor bob -rectangle { - rectangle Ui - rectangle Main - rectangle calories - rectangle system.parser - rectangle hydration.hydrationlist - rectangle sleep.sleeplist - rectangle system.storage - rectangle Entry -} - -bob --[dotted]> Ui -Main -[dotted]> Ui -Main ---> sleep.sleeplist -Main ---> hydration.hydrationlist -Main ---> calories - -calories -[dotted]---> system.parser -calories ----> Entry -calories --[dotted]> system.storage - -hydration.hydrationlist ---[dotted]-> system.parser -hydration.hydrationlist -> Entry -hydration.hydrationlist --[dotted]> system.storage - -sleep.sleeplist --[dotted]--> system.parser -sleep.sleeplist -> Entry -sleep.sleeplist --[dotted]> system.storage - -system.storage -[dotted]-> Entry - -hide circle - -@enduml diff --git a/docs/CaloriesAddEntrySeqDiagram.png b/docs/CaloriesAddEntrySeqDiagram.png deleted file mode 100644 index c0921c36188e55fc53eab89244be0199299c7ee7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23922 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 UI: Input "calories in" or "calories out" command -activate UI - -UI -> UI: handleUserInput() -activate UI - -UI -> UI: handleCaloriesInput() -activate UI - -UI -> CalorieList: addEntry() -activate CalorieList - -CalorieList -> ParserCalories: parseCaloriesInput() -activate ParserCalories -return entry - -CalorieList -> CalorieList: calorieArrayList.add(entry) -activate CalorieList -return - -CalorieList -> CalorieList: updateFile() -activate CalorieList -return - -return -return -return -return - -@enduml \ No newline at end of file diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index fdfe52886b..99509cf4c9 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -29,26 +29,17 @@ {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} ## Design -### Architecture -![ArchitectureDiagram.png](diagrams%2FArchitectureDiagram.png) +### calories component +Here's a (partial) class diagram o the `calories` component. -The **Architecture Diagram** given above explains the high-level design of the LifeTrack app. +![calories.png](assets%2Fcalories.png) -Given below is a quick overview of main components and how they interact with each other. +The sequence diagram bellow illustrates the interactions within the `calories` component, taking +`calories in donut c/1000 d/2024-04-10` call as an example. -**Main components of the architecture** +How the `calories` component works: +1. When the user keys in the `calories in` command, -`Main` (consisting of class `Lifetrack`) is in charge of the app launch and shut down. -* At app launch,v it initializes the other components in the correct sequence, and connects them up with each other. - -The bulk of the Lifetrack's work is done by the following four components: -* `Ui`: The UI of Lifetrack app. -* `calories`: The component in charge of the caloric entries of the Lifetrack app. -* `hydration.hydrationlist`: The component in charge of the hydration entries of the Lifetrack app. -* `sleep.sleeplist`: The component in charge of the sleep entries of the Lifetrack app. -* `system.storage`: The component in charge of reding data from, and writes date to the hard disk. -* `Entry`: The class in charge of all the data entries. -* `system.parser`: The component in charge of parsing all commands keyed in by user. ## Implementation ### Adding calorie entries feature @@ -105,7 +96,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 6: This value is set to `User.caloriesRequired` by `User#setCaloriesRequired(int)`. The Sequence Diagram for the above-mentioned process is as follows: -![Sequence Diagram](UserCalculateCaloriesSeqDiagram.png) +![Sequence Diagram](assets/UserCalculateCaloriesSeqDiagram.png) #### Design considerations @@ -150,8 +141,8 @@ the next command typed in by user. The class and sequence diagram for this feature is shown below: Unrelated attributes and Classes were excluded. -![CaloriesListClassDiagram.png](diagrams%2FCaloriesListClassDiagram.png) -![CaloriesListSequenceDiagram.png](diagrams%2FCaloriesListSequenceDiagram.png) +![CaloriesListClassDiagram.png](assets%2FCaloriesListClassDiagram.png) +![CaloriesListSequenceDiagram.png](assets%2FCaloriesListSequenceDiagram.png) #### Design considerations - **Alternative 1 (current choice):** Use one arrayList and use instanceof to print out @@ -184,7 +175,7 @@ Given below is an example usage scenario and how this mechanism behaves at every The Class diagram for Calories delete feature is shown below: -![CaloriesDeleteClassDiagram](https://github.com/a-wild-chocolate/tp/blob/master/docs/caloriesDeleteUML.jpg) +![CaloriesDeleteClassDiagram](assets%2FcaloriesDeleteUML.jpg) ### Parsing user input for hydration entries @@ -217,7 +208,7 @@ The `printHydrationList()` function iterates through the `hydrationArrayList` an The Class diagram for Hydration list feature is shown below. Unrelated attributes and Classes were excluded. -![HydrationListClassDiagram.png](HydrationListClassDiagram.png) +![HydrationListClassDiagram.png](assets/HydrationListClassDiagram.png) ### Hydration delete feature @@ -236,7 +227,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: The latest hydration list will be updated to saving file by calling `HydrationList#updateFile()`. The Sequence diagram for Hydration delete feature is shown below: -![HydrationDeleteDiagram.png](HydrationDeleteDiagram.png) +![HydrationDeleteDiagram.png](assets/HydrationDeleteDiagram.png) ### Adding sleep entries feature @@ -264,7 +255,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: `FileHandler#updateFile()` is then called to update the data file with the new entry in the `SleepList`. The sequence diagram for this feature is shown below: -![SleepAddSeqDiagram](https://github.com/a-wild-chocolate/tp/blob/f9a94746e763ddf54a4309583b71e0fdbabdb141/docs/SleepAddSeqDiagram.jpg) +![SleepAddSeqDiagram.jpg](assets%2FSleepAddSeqDiagram.jpg) ### Parsing user input for sleep entries @@ -291,8 +282,7 @@ The `sleep list` feature lists out the record of all the sleep data that the use The `printSleepList()` function iterates through the `sleepList` and each entry will call `SleepEntry#toString()` to return its information string to be printed. The Sequence diagram for Sleep list feature is shown below. Unrelated attributes and Classes were excluded. -![SleepListSeqDiagram](https://github.com/a-wild-chocolate/tp/blob/f9a94746e763ddf54a4309583b71e0fdbabdb141/docs/SleepListSeqDiagram.jpg) - +![SleepListSeqDiagram.jpg](assets%2FSleepListSeqDiagram.jpg) ### Sleep delete feature @@ -311,7 +301,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: The latest sleep list will be updated to saving file by calling `SleepList#updateFile()`. The Sequence diagram for Sleep delete feature is shown below: -![SleepDeleteDiagram.jpg](https://github.com/a-wild-chocolate/tp/blob/f9a94746e763ddf54a4309583b71e0fdbabdb141/docs/SleepDeleteSeqDiagram.jpg) +![SleepDeleteSeqDiagram.jpg](assets%2FSleepDeleteSeqDiagram.jpg) ### Calculating sleep requirements for each user (Planning) From b86a01253eba3d550bd669749e2c1111ed15818e Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 12 Apr 2024 22:32:33 +0800 Subject: [PATCH 301/414] Edit devloper guide to include png of hydration, sleep and user class diagram --- docs/DeveloperGuide.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 99509cf4c9..6c9683bcd4 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -40,6 +40,14 @@ The sequence diagram bellow illustrates the interactions within the `calories` c How the `calories` component works: 1. When the user keys in the `calories in` command, +### hydration component +![hydration.png](assets%2Fhydration.png) + +### sleep component +![sleep.png](assets%2Fsleep.png) + +### user component +![user.png](assets%2Fuser.png) ## Implementation ### Adding calorie entries feature From 3a9493736515af0a8f0f7acbe60ba3460e80388b Mon Sep 17 00:00:00 2001 From: RexYong Date: Fri, 12 Apr 2024 22:32:57 +0800 Subject: [PATCH 302/414] Add user.png image --- docs/assets/user.png | Bin 0 -> 10399 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/user.png diff --git a/docs/assets/user.png b/docs/assets/user.png new file mode 100644 index 0000000000000000000000000000000000000000..c4c4104377b586e16aea917ab096898a6dd8a90d GIT binary patch literal 10399 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

    1TZW_z}v$2KQxp)($CHvbpba`Ij#!FewdG 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 Jm7GP;e*>$a>6QQh literal 0 HcmV?d00001 From 6122fa680b5cf94c65a12de67474eee92c2374ec Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sat, 13 Apr 2024 04:04:02 +0800 Subject: [PATCH 303/414] summary --- .../calories/calorielist/CalorieList.java | 10 +- .../hydrationlist/HydrationList.java | 17 +- .../lifetrack/sleep/sleeplist/SleepList.java | 15 ++ .../lifetrack/system/parser/ParserUser.java | 1 + src/main/java/seedu/lifetrack/ui/Ui.java | 1 + src/main/java/seedu/lifetrack/ui/UserUi.java | 53 +++++- src/main/java/seedu/lifetrack/user/User.java | 7 + .../lifetrack/user/usergoals/UserGoals.java | 179 +++++++++++++++--- 8 files changed, 232 insertions(+), 51 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 064c3e763c..e3976273e3 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -90,9 +90,7 @@ public void deleteEntry(String line) { CalorieListUi.successfulDeletedMessage(toDelete); updateFile(); } - } catch (IndexOutOfBoundsException e) { - System.out.println(CalorieListUi.deleteLogNumberMessage()); - } catch (NumberFormatException e) { + } catch (IndexOutOfBoundsException | NumberFormatException e) { System.out.println(CalorieListUi.deleteLogNumberMessage()); } } @@ -209,13 +207,13 @@ public int getSize() { * * @return the total number of calories consumed */ - public int getCaloriesConsumed() { + public int getCaloriesConsumed(LocalDate date) { int totalCalories = 0; for (Entry entry : calorieArrayList) { - if (entry instanceof InputEntry) { + if (entry instanceof InputEntry && entry.getDate().isEqual(date)) { InputEntry tempEntry = (InputEntry) entry; totalCalories += tempEntry.getCalories(); - } else if (entry instanceof OutputEntry) { + } else if (entry instanceof OutputEntry && entry.getDate().isEqual(date)) { OutputEntry tempEntry = (OutputEntry) entry; totalCalories -= tempEntry.getCalories(); } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index e9cbd48156..3d5b52de68 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -73,7 +73,7 @@ public Entry getEntry(int index) { * @param line the string containing the index of the liquid record to delete */ public void deleteEntry(String line) { - assert (line.startsWith("hydration delete") ) : "ensures that input is correct"; + assert (line.startsWith("hydration delete")) : "ensures that input is correct"; try { int entryID = Integer.parseInt(line.substring(SIZE_OF_DELETE).trim()); int index = getIndexFromEntryID(entryID); @@ -93,6 +93,7 @@ public void deleteEntry(String line) { System.out.println(HydrationListUI.deleteLogNumberMessage()); } } + public int getIndexFromEntryID(int lastEntryID) { for (int i = 0; i < hydrationArrayList.size(); i++) { if (hydrationArrayList.get(i).getLastEntryID() == lastEntryID) { @@ -115,9 +116,9 @@ public void addEntry(String input) { hydrationArrayList.add(newEntry); updateFile(); HydrationListUI.printNewHydrationEntry(newEntry); - lastHydrationEntryID ++; + lastHydrationEntryID++; if (hydrationArrayList.size() > 1 && - hydrationArrayList.get(hydrationArrayList.size() - 2).getDate().compareTo(newEntry.getDate()) > 0 ) { + hydrationArrayList.get(hydrationArrayList.size() - 2).getDate().compareTo(newEntry.getDate()) > 0) { sortEntriesByDate(); } } catch (InvalidInputException e) { @@ -164,11 +165,13 @@ public void printHydrationInflow() { * * @return the total amount of hydration consumed */ - public int getHydrationConsumed() { + public int getHydrationConsumed(LocalDate date) { int totalHydration = 0; - for (int i = 0; i < hydrationArrayList.size(); i++) { - HydrationEntry tempEntry = (HydrationEntry) hydrationArrayList.get(i); - totalHydration += tempEntry.getHydration(); + for (Entry entry : hydrationArrayList) { + if (entry.getDate().isEqual(date)) { + HydrationEntry tempEntry = (HydrationEntry) entry; + totalHydration += tempEntry.getHydration(); + } } return totalHydration; } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 29db88e20f..1e0dd6e0d0 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -2,12 +2,16 @@ package seedu.lifetrack.sleep.sleeplist; import seedu.lifetrack.Entry; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; +import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserSleep; import seedu.lifetrack.system.storage.FileHandler; import seedu.lifetrack.ui.SleepListUi; import java.io.FileNotFoundException; +import java.time.LocalDate; import java.util.ArrayList; public class SleepList { @@ -97,5 +101,16 @@ public int getSize() { private int loadLastEntryID() { return 0; // Default value if file doesn't exist or error occurs } + + public int getSleepConsumed(LocalDate date) { + double totalSleep = 0; + for (Entry entry : sleepList) { + if (entry.getDate().isEqual(date)) { + SleepEntry tempEntry = (SleepEntry) entry; + totalSleep += tempEntry.getDuration(); + } + } + return (int) totalSleep; + } } //@@author diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 34b00d5f41..8b2e4cd677 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -1,3 +1,4 @@ +//@@author paturikarthik package seedu.lifetrack.system.parser; import seedu.lifetrack.system.exceptions.InvalidInputException; diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index b343c6f69a..6ef599724e 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -149,6 +149,7 @@ private static void handleUserProgress(User user) { } else { user.getCaloriesProgressBar(); user.getHydrationProgressBar(); + user.getSleepProgressBar(); } } diff --git a/src/main/java/seedu/lifetrack/ui/UserUi.java b/src/main/java/seedu/lifetrack/ui/UserUi.java index e63b1bad82..adb36b2ec7 100644 --- a/src/main/java/seedu/lifetrack/ui/UserUi.java +++ b/src/main/java/seedu/lifetrack/ui/UserUi.java @@ -4,25 +4,64 @@ public class UserUi { private static final String CHANGE_MADE_MESSAGE = "\t The following change has been made:\n"; + private static final int INDEX_OF_TODAY = 0; + private static final int INDEX_OF_YESTERDAY = 1; + private static final int INDEX_OF_DAY_BEFORE = 2; + private static final String UNDERLINE = "\t ----------"; public static void printUserCaloriesRequired(int caloriesRequired) { System.out.println("\t You need to consume " + caloriesRequired + " calories per day to hit your goals!"); } public static void printUserCalorieProgress(int caloriesConsumed, int caloriesRequired, String progressBar, - int percentage) { - System.out.print("\t Calories:\n"); + int percentage, int date) { + String dateInString; + if (date == INDEX_OF_TODAY) { + System.out.print("\t Calories:\n"); + System.out.println(UNDERLINE); + dateInString = "today"; + } else if (date == INDEX_OF_YESTERDAY) { + dateInString = "yesterday"; + } else { + dateInString = "on the day before yesterday"; + } System.out.printf("\t You have consumed " + caloriesConsumed + " calories out of your goal of " - + caloriesRequired + " calories so far.\n"); + + caloriesRequired + " calories " + dateInString + ".\n"); System.out.printf("\t %s %d%%\n\n", progressBar, percentage); } public static void printUserHydrationProgress(int hydrationConsumed, int hydrationRequired, String progressBar, - int percentage) { - System.out.print("\t Hydration:\n"); + int percentage, int date) { + String dateInString; + if (date == INDEX_OF_TODAY) { + System.out.print("\t Hydration:\n"); + System.out.println(UNDERLINE); + dateInString = "today"; + } else if (date == INDEX_OF_YESTERDAY) { + dateInString = "yesterday"; + } else { + dateInString = "on the day before yesterday"; + } System.out.printf("\t You have consumed " + hydrationConsumed + "ml out of your goal of " - + hydrationRequired + "ml so far.\n"); - System.out.printf("\t %s %d%%\n", progressBar, percentage); + + hydrationRequired + "ml " + dateInString + ".\n"); + System.out.printf("\t %s %d%%\n\n", progressBar, percentage); + } + + public static void printUserSleepProgress(int sleepConsumed, int sleepRequired, String progressBar, + int percentage, int date) { + String dateInString; + if (date == INDEX_OF_TODAY) { + System.out.print("\t Sleep:\n"); + System.out.println(UNDERLINE); + dateInString = "today"; + } else if (date == INDEX_OF_YESTERDAY) { + dateInString = "yesterday"; + } else { + dateInString = "on the day before yesterday"; + } + System.out.printf("\t You have slept for " + sleepConsumed + "hrs out of your goal of " + + sleepRequired + "hrs " + dateInString + ".\n"); + System.out.printf("\t %s %d%%\n\n", progressBar, percentage); } public static void printUserSetUpComplete(User user) { diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 305c49ffa9..b871d396c8 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -24,6 +24,7 @@ public class User { private int caloriesRequired; private int hydrationRequired = 2000; + private int sleepRequired = 7; //user data constants private final int NAME_INDEX = 0; @@ -147,6 +148,9 @@ public int getCaloriesRequired() { public int getHydrationRequired() { return hydrationRequired; } + public int getSleepRequired() { + return sleepRequired; + } public String toFileFriendlyString() { return String.format(name + ";" + height + ";" + weight + ";" + age + ";" + sex + ";" + @@ -160,6 +164,9 @@ public void getCaloriesProgressBar() { public void getHydrationProgressBar() { UserGoals.getHydrationProgressBar(this); } + public void getSleepProgressBar() { + UserGoals.getSleepProgressBar(this); + } public String getExerciseLevelAsString() { if (exerciseLevels == 1) { diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 824f19ca36..2a5a09352d 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -3,10 +3,14 @@ import seedu.lifetrack.user.User; +import java.time.LocalDate; + import static seedu.lifetrack.LifeTrack.calorieList; import static seedu.lifetrack.LifeTrack.hydrationList; +import static seedu.lifetrack.LifeTrack.sleepList; import static seedu.lifetrack.ui.UserUi.printUserCalorieProgress; import static seedu.lifetrack.ui.UserUi.printUserHydrationProgress; +import static seedu.lifetrack.ui.UserUi.printUserSleepProgress; public class UserGoals { private static final int BMR_WEIGHT_MULTIPLIER = 10; @@ -15,6 +19,12 @@ public class UserGoals { private static final int BMR_MALE_MODIFIER = 5; private static final int BMR_FEMALE_MODIFIER = -161; private static final int PROGRESS_BAR_WIDTH = 50; + private static final int INDEX_OF_TODAY = 0; + private static final int INDEX_OF_YESTERDAY = 1; + private static final int INDEX_OF_DAY_BEFORE = 2; + private static final int NUMBER_OF_DAYS_TO_TRACK = 3; + private static final int NUMBER_OF_DAYS_TO_YESTERDAY = -1; + private static final int NUMBER_OF_DAYS_TO_DAY_BEFORE = -2; public static void getHealthInfo(User user) { double rawBMR = BMR_WEIGHT_MULTIPLIER * user.getWeight() + BMR_HEIGHT_MULTIPLIER * user.getHeight() @@ -24,22 +34,23 @@ public static void getHealthInfo(User user) { int exerciseLevel = user.getExerciseLevels(); double rawAMR = getAMR(rawBMR + genderBMRModifier, exerciseLevel); int goal = user.getGoal(); - int caloriesRequired = adjustAMRWithGoal(rawAMR,goal); + int caloriesRequired = adjustAMRWithGoal(rawAMR, goal); user.setCaloriesRequired(caloriesRequired); } private static int adjustAMRWithGoal(double rawAMR, int goal) { - if (goal == 1){ + if (goal == 1) { rawAMR *= 0.8; - } else if (goal == 2){ + } else if (goal == 2) { rawAMR *= 0.9; } else if (goal == 4) { rawAMR *= 1.1; } else if (goal == 5) { - rawAMR*=1.2; + rawAMR *= 1.2; } return (int) rawAMR; } + private static double getAMR(double calories, int exerciseLevel) { if (exerciseLevel == 1) { calories *= 1.2; @@ -57,46 +68,152 @@ private static double getAMR(double calories, int exerciseLevel) { public static void getCaloriesProgressBar(User user) { int caloriesRequired = user.getCaloriesRequired(); - int caloriesConsumed = calorieList.getCaloriesConsumedCurrentDay(); - if (caloriesConsumed < 0){ - caloriesConsumed = 0; + int caloriesConsumedToday = calorieList.getCaloriesConsumed(LocalDate.now()); + if (caloriesConsumedToday < 0) { + caloriesConsumedToday = 0; } - double progress = (double) caloriesConsumed / caloriesRequired; + double progressToday = (double) caloriesConsumedToday / caloriesRequired; + int caloriesConsumedYesterday = calorieList.getCaloriesConsumed + (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_YESTERDAY)); + if (caloriesConsumedYesterday < 0) { + caloriesConsumedYesterday = 0; + } + double progressYesterday = (double) caloriesConsumedYesterday / caloriesRequired; int width = PROGRESS_BAR_WIDTH; - int progressWidth = (int) (width * progress); - StringBuilder progressBar = new StringBuilder("["); - for (int i = 0; i < width; i++) { - if (i < progressWidth) { - progressBar.append("="); - } else { - progressBar.append(" "); - } + int caloriesConsumedDayBefore = calorieList.getCaloriesConsumed + (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_DAY_BEFORE)); + if (caloriesConsumedDayBefore < 0) { + caloriesConsumedDayBefore = 0; } - progressBar.append("] "); + double progressDayBefore = (double) caloriesConsumedDayBefore / caloriesRequired; + + double[] progress = new double[NUMBER_OF_DAYS_TO_TRACK]; + progress[INDEX_OF_TODAY] = progressToday; + progress[INDEX_OF_YESTERDAY] = progressYesterday; + progress[INDEX_OF_DAY_BEFORE] = progressDayBefore; + + int[] caloriesConsumed = new int[NUMBER_OF_DAYS_TO_TRACK]; + caloriesConsumed[INDEX_OF_TODAY] = caloriesConsumedToday; + caloriesConsumed[INDEX_OF_YESTERDAY] = caloriesConsumedYesterday; + caloriesConsumed[INDEX_OF_DAY_BEFORE] = caloriesConsumedDayBefore; + + for (int date = 0; date < NUMBER_OF_DAYS_TO_TRACK; date++) { + int progressWidth = (int) (width * progress[date]); + StringBuilder progressBar = new StringBuilder("["); + for (int i = 0; i < width; i++) { + if (i < progressWidth) { + progressBar.append("="); + } else { + progressBar.append(" "); + } + } + progressBar.append("] "); - int percentage = (int) (progress * 100); - printUserCalorieProgress(caloriesConsumed,caloriesRequired,progressBar.toString(), percentage); + int percentage = (int) (progress[date] * 100); + printUserCalorieProgress(caloriesConsumed[date], caloriesRequired, progressBar.toString(), + percentage, date); + } } public static void getHydrationProgressBar(User user) { int hydrationRequired = user.getHydrationRequired(); - int hydrationConsumed = hydrationList.getHydrationConsumedCurrentDay(); - double progress = (double) hydrationConsumed / hydrationRequired; + int hydrationConsumedToday = hydrationList.getHydrationConsumed(LocalDate.now()); + if (hydrationConsumedToday < 0) { + hydrationConsumedToday = 0; + } + double progressToday = (double) hydrationConsumedToday / hydrationRequired; + int hydrationConsumedYesterday = hydrationList.getHydrationConsumed + (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_YESTERDAY)); + if (hydrationConsumedYesterday < 0) { + hydrationConsumedYesterday = 0; + } + double progressYesterday = (double) hydrationConsumedYesterday / hydrationRequired; int width = PROGRESS_BAR_WIDTH; - int progressWidth = (int) (width * progress); - StringBuilder progressBar = new StringBuilder("["); - for (int i = 0; i < width; i++) { - if (i < progressWidth) { - progressBar.append("="); - } else { - progressBar.append(" "); + int hydrationConsumedDayBefore = hydrationList.getHydrationConsumed + (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_DAY_BEFORE)); + if (hydrationConsumedDayBefore < 0) { + hydrationConsumedDayBefore = 0; + } + double progressDayBefore = (double) hydrationConsumedDayBefore / hydrationRequired; + + double[] progress = new double[NUMBER_OF_DAYS_TO_TRACK]; + progress[INDEX_OF_TODAY] = progressToday; + progress[INDEX_OF_YESTERDAY] = progressYesterday; + progress[INDEX_OF_DAY_BEFORE] = progressDayBefore; + + int[] hydrationConsumed = new int[NUMBER_OF_DAYS_TO_TRACK]; + hydrationConsumed[INDEX_OF_TODAY] = hydrationConsumedToday; + hydrationConsumed[INDEX_OF_YESTERDAY] = hydrationConsumedYesterday; + hydrationConsumed[INDEX_OF_DAY_BEFORE] = hydrationConsumedDayBefore; + + for (int date = 0; date < NUMBER_OF_DAYS_TO_TRACK; date++) { + + int progressWidth = (int) (width * progress[date]); + StringBuilder progressBar = new StringBuilder("["); + for (int i = 0; i < width; i++) { + if (i < progressWidth) { + progressBar.append("="); + } else { + progressBar.append(" "); + } } + progressBar.append("] "); + + int percentage = (int) (progress[date] * 100); + printUserHydrationProgress(hydrationConsumed[date], hydrationRequired, progressBar.toString(), + percentage, date); + } + } + + public static void getSleepProgressBar(User user) { + int sleepRequired = user.getSleepRequired(); + int sleepConsumedToday = sleepList.getSleepConsumed(LocalDate.now()); + if (sleepConsumedToday < 0) { + sleepConsumedToday = 0; + } + double progressToday = (double) sleepConsumedToday / sleepRequired; + int sleepConsumedYesterday = sleepList.getSleepConsumed + (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_YESTERDAY)); + if (sleepConsumedYesterday < 0) { + sleepConsumedYesterday = 0; + } + double progressYesterday = (double) sleepConsumedYesterday / sleepRequired; + int width = PROGRESS_BAR_WIDTH; + + int sleepConsumedDayBefore = sleepList.getSleepConsumed + (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_DAY_BEFORE)); + if (sleepConsumedDayBefore < 0) { + sleepConsumedDayBefore = 0; } - progressBar.append("] "); + double progressDayBefore = (double) sleepConsumedDayBefore / sleepRequired; + + double[] progress = new double[NUMBER_OF_DAYS_TO_TRACK]; + progress[INDEX_OF_TODAY] = progressToday; + progress[INDEX_OF_YESTERDAY] = progressYesterday; + progress[INDEX_OF_DAY_BEFORE] = progressDayBefore; + + int[] sleepConsumed = new int[NUMBER_OF_DAYS_TO_TRACK]; + sleepConsumed[INDEX_OF_TODAY] = sleepConsumedToday; + sleepConsumed[INDEX_OF_YESTERDAY] = sleepConsumedYesterday; + sleepConsumed[INDEX_OF_DAY_BEFORE] = sleepConsumedDayBefore; - int percentage = (int) (progress * 100); - printUserHydrationProgress(hydrationConsumed,hydrationRequired,progressBar.toString(),percentage); + for (int date = 0; date < NUMBER_OF_DAYS_TO_TRACK; date++) { + int progressWidth = (int) (width * progress[date]); + StringBuilder progressBar = new StringBuilder("["); + for (int i = 0; i < width; i++) { + if (i < progressWidth) { + progressBar.append("="); + } else { + progressBar.append(" "); + } + } + progressBar.append("] "); + + int percentage = (int) (progress[date] * 100); + printUserSleepProgress(sleepConsumed[date], sleepRequired, progressBar.toString(), + percentage, date); + } } } From 0535ac50e0927684c9c6d0c0df38ea136f979a26 Mon Sep 17 00:00:00 2001 From: RexYong Date: Sat, 13 Apr 2024 09:54:12 +0800 Subject: [PATCH 304/414] Add caloriesComponent Sequence Diagram --- docs/assets/caloriesComponent.png | Bin 0 -> 45288 bytes docs/diagrams/calories_component.puml | 47 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 docs/assets/caloriesComponent.png create mode 100644 docs/diagrams/calories_component.puml diff --git a/docs/assets/caloriesComponent.png b/docs/assets/caloriesComponent.png new file mode 100644 index 0000000000000000000000000000000000000000..b40e7d7a89c8287d28c751f2bb40d9520f2c54f1 GIT binary patch literal 45288 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 xz}sKPNvkP)cPZ?Q*?#QY{{hpEMWFxy literal 0 HcmV?d00001 diff --git a/docs/diagrams/calories_component.puml b/docs/diagrams/calories_component.puml new file mode 100644 index 0000000000..12528ea9e8 --- /dev/null +++ b/docs/diagrams/calories_component.puml @@ -0,0 +1,47 @@ +@startuml +actor Bob +Bob -> UI: Input "calories in" command +activate UI + +UI -> UI: handleCaloriesInput(String, CalorieList) +activate UI + +UI -> CalorieList: addEntry(String) +activate CalorieList + +CalorieList -> ParserCalories: parseCaloriesInput(String, int) +activate ParserCalories + +ParserCalories -> ParserCalories : makeNewInputEntry(int, String, int, String) +activate ParserCalories + +return InputEntry +return InputEntry + +CalorieList -> CalorieList: calorieArrayList.add(InputEntry) +activate CalorieList +return + +CalorieList -> CalorieList: updateFile() +activate CalorieList + +CalorieList -> FileHandler : writeEntries(ArrayList) +activate FileHandler +FileHandler -> FileHandler : writeToFile(String) +activate FileHandler +return +return + +alt calorieArrayList not sorted by date in ascending order + CalorieList -> CalorieList : sortEntriesByDate() + activate CalorieList + return +end + + +return +return +return +return New InputEntry added + +@enduml \ No newline at end of file From 2964fe791d06a0c005434fb20f0b95219a357970 Mon Sep 17 00:00:00 2001 From: RexYong Date: Sat, 13 Apr 2024 09:54:26 +0800 Subject: [PATCH 305/414] Add calories component description into developer guide --- docs/DeveloperGuide.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6c9683bcd4..0c297fc2da 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -30,15 +30,46 @@ ## Design ### calories component -Here's a (partial) class diagram o the `calories` component. +Here's a (partial) class diagram of the `calories` component. ![calories.png](assets%2Fcalories.png) +The calories component consists of the following Classes +1. `Ui` : Handles user and program interaction. +2. `CalorieList` : Handles the list of calories entries. +3. `FileHandler` : Handles the saving and reading of data from file. +4. `ParserCalories` : Handles the parsing of user input to determine type of command. +5. `Entry` : Handles calories entries data. +6. `OutputEntry` : Handles calories output entries data. +7. `InputEntry` : Handles calories input entries data. +8. `Food` : Handles macronutrients data for calories input. + The sequence diagram bellow illustrates the interactions within the `calories` component, taking `calories in donut c/1000 d/2024-04-10` call as an example. +![caloriesComponent.png](assets%2FcaloriesComponent.png) How the `calories` component works: -1. When the user keys in the `calories in` command, +1. When the user keys in the `calories in donut c/1000 d/2024-04-10` command, +the input is sent to `Ui#handleCaloriesInput(String, CalorieList)`, which calls +`CalorieList#addEntry(String)`. + +2. Inside `CalorieList#addEntry(String)`, the function `ParserCalories#parseCaloriesInput(String, int)` +is then called to extract information such as the description, number of calories, and date of entry. + +3. With the extracted information, the function `ParserCalories#makeNewInputEntry(int, String, int, String)` +is called, which creates a new entry of `InputEntry` that extends `Entry`. The `InputEntry` object is then returned +to the caller, `CalorieList#addEntry(String)` which was called in step 2. + +4. The returned `InputEntry` object is added into the `calorieArrayList` member of type +`ArrayList` in the `CalorieList`, via the `ArrayList.add()` method. + +5. `CalorieList#UpdateFile()` is then called, which calls `FileHandler#writeEntries(ArrayList)`. +Within that function, `FileHandler#writeToFile(String)` function is called, which writes the new data +into the data file. + +6. If the dates of entries are not sorted in ascending order, `CalorieList#sortEntriesByDate()` +function is called, which sorts the entries in ascending order. + ### hydration component ![hydration.png](assets%2Fhydration.png) From 40b5a48b049ef81b809fcde5832bdc04d4d41707 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sat, 13 Apr 2024 12:32:28 +0800 Subject: [PATCH 306/414] remove getters for maxCaloriesID and maxHydrationID, set up new FileHandlerException class --- .../calories/calorielist/CalorieList.java | 2 +- .../hydrationlist/HydrationList.java | 2 +- .../exceptions/FileHandlerException.java | 22 ++++++++ .../FileHandlerExceptionMessage.java | 8 +++ .../exceptions/InvalidInputException.java | 3 -- .../lifetrack/system/storage/FileHandler.java | 50 +++++++++---------- 6 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/system/exceptions/FileHandlerException.java create mode 100644 src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 064c3e763c..678d186a00 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -248,7 +248,7 @@ public int getCaloriesConsumedCurrentDay() { * @return the last entry ID loaded from the file, or a default value if the file doesn't exist or an error occurs */ private int loadLastEntryID() { - return FileHandler.getMaxCaloriesID(); // Default value if file doesn't exist or error occurs + return FileHandler.maxCaloriesID; // Default value if file doesn't exist or error occurs } /** diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index e9cbd48156..4da0d1cb4d 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -195,6 +195,6 @@ public int getSize() { } private int loadLastEntryID() { - return FileHandler.getMaxHydrationID(); + return FileHandler.maxHydrationID; } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerException.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerException.java new file mode 100644 index 0000000000..d3dea1b784 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerException.java @@ -0,0 +1,22 @@ +//@@author owx0130 +package seedu.lifetrack.system.exceptions; + +public class FileHandlerException extends Exception { + + /** + * Constructs a new FileHandlerException with a default error message. + * The default message provides guidance on ensuring correct input format. + */ + public FileHandlerException(){ + super("\t There was an error reading/writing to the file!"); + } + + /** + * Constructs a new FileHandlerException with a custom error message. + * + * @param exception the custom error message describing the specific input error + */ + public FileHandlerException(String exception) { + super(exception); + } +} diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java new file mode 100644 index 0000000000..5f8ceff15c --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -0,0 +1,8 @@ +package seedu.lifetrack.system.exceptions; + +/** + * Utility class for managing error messages related to file handler exceptions. + */ +public class FileHandlerExceptionMessage { + +} diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java index 187f8c4575..39ab2b40bc 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputException.java @@ -1,11 +1,8 @@ //@@author owx0130 package seedu.lifetrack.system.exceptions; - public class InvalidInputException extends Exception { - public final String heythere = ""; - /** * Constructs a new InvalidInputException with a default error message. * The default message provides guidance on ensuring correct input format. diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 1361f39522..8b006e0819 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -15,6 +15,7 @@ import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.sleep.sleeplist.SleepEntry; +import seedu.lifetrack.system.exceptions.FileHandlerException; import seedu.lifetrack.user.User; public class FileHandler { @@ -60,14 +61,6 @@ public FileHandler(String filePath) { this.filePath = filePath; } - public static int getMaxCaloriesID() { - return maxCaloriesID; - } - - public static int getMaxHydrationID() { - return maxHydrationID; - } - private void writeToFile(String textToAdd) throws IOException { FileWriter fw = new FileWriter(filePath); fw.write(textToAdd); @@ -100,24 +93,29 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException ArrayList entries = new ArrayList<>(); String line = ""; while (s.hasNext()) { - line = s.nextLine(); - String[] words = line.split(";"); - int entryID = Integer.parseInt(words[ENTRYID_INDEX]); - calculateMaxCaloriesEntry(entryID); - LocalDate date = LocalDate.parse(words[DATE_INDEX]); - String description = words[DESCRIPTION_INDEX]; - int calories = Integer.parseInt(words[CALORIES_INDEX]); - String entryType = words[ENTRY_TYPE_INDEX]; - if (entryType.equals("C_IN") && words.length == 8) { - int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); - int proteins = Integer.parseInt(words[PROTEINS_INDEX]); - int fats = Integer.parseInt(words[FATS_INDEX]); - Food food = new Food(carbohydrates, proteins, fats); - entries.add(new InputEntry(entryID, description, calories, date, food)); - } else if (entryType.equals("C_IN")) { - entries.add(new InputEntry(entryID, description, calories, date)); - } else { - entries.add(new OutputEntry(entryID, description, calories, date)); + try { + line = s.nextLine(); + String[] words = line.split(";"); + System.out.println(words[0]); + int entryID = Integer.parseInt(words[ENTRYID_INDEX]); + calculateMaxCaloriesEntry(entryID); + LocalDate date = LocalDate.parse(words[DATE_INDEX]); + String description = words[DESCRIPTION_INDEX]; + int calories = Integer.parseInt(words[CALORIES_INDEX]); + String entryType = words[ENTRY_TYPE_INDEX]; + if (entryType.equals("C_IN") && words.length == 8) { + int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); + int proteins = Integer.parseInt(words[PROTEINS_INDEX]); + int fats = Integer.parseInt(words[FATS_INDEX]); + Food food = new Food(carbohydrates, proteins, fats); + entries.add(new InputEntry(entryID, description, calories, date, food)); + } else if (entryType.equals("C_IN")) { + entries.add(new InputEntry(entryID, description, calories, date)); + } else { + entries.add(new OutputEntry(entryID, description, calories, date)); + } + } catch (NumberFormatException e){ + System.out.println("wrong format"); } } s.close(); From a248175299d3a2cf1daacb2ea693ac220ceb482d Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:34:56 +0800 Subject: [PATCH 307/414] complete sleep DP --- docs/DeveloperGuide.md | 35 ++++++++++++++++++++++- docs/assets/SleepAddSeqDiagram.png | Bin 0 -> 41734 bytes docs/diagrams/SleepAddSeqDiagram.puml | 39 ++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 docs/assets/SleepAddSeqDiagram.png create mode 100644 docs/diagrams/SleepAddSeqDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 0c297fc2da..0a5328997d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -74,9 +74,42 @@ function is called, which sorts the entries in ascending order. ### hydration component ![hydration.png](assets%2Fhydration.png) + ### sleep component +Here's a (partial) class diagram of the `calories` component. ![sleep.png](assets%2Fsleep.png) +The sleep component consists of the following classes: +1. `Ui`: Handles user and program interaction. +2. `SleepList`: Handles the list of sleep records. +3. `FileHandler`: Handles the saving and reading of data from the file. +4. `ParserSleep`: Handles the parsing of user input to determine type of command. +5. `Entry`: Handles all entries data. +6. `SleepEntry`: Handles sleep entries data. + +The sequence diagram bellow illustrates the interactions within the `sleep` component, taking `sleep add 7 d/2024-04-13` +call as an example. +![SleepAddSeqDiagram.png](assets%2FSleepAddSeqDiagram.png) + +How the `sleep` component works: +1. When the user keys in the`sleep add 7 d/2024-04-13` command, + the input is sent to `Ui#handleSleepInput(String, SleepList)`, which calls + `SleepList#addSleep(String)`. + +2. Inside `SleepList#addSleep(String)`, the function `ParserSleep#parseSleepInput(String)` + is then called to extract information such as the duration and date by calling `parseDate(String)` and `parseDuration(String)`. + +3. With the extracted information,a new entry of `SleepEntry` that extends `Entry` will be created. The `SleepEntry` object is then returned + to the caller, `SleepList#addSleep(String)` which was called in step 2. + +4. The returned `SleepEntry` object is added into the `sleepList` member of type + `ArrayList` in the `SleepList`, via the `ArrayList.add()` method. + +5. `SleepList#UpdateFile()` is then called, which calls `FileHandler#writeEntries(ArrayList)`. + Within that function, `FileHandler#writeToFile(String)` function is called, which writes the new data + into the data file. + + ### user component ![user.png](assets%2Fuser.png) @@ -294,7 +327,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: `FileHandler#updateFile()` is then called to update the data file with the new entry in the `SleepList`. The sequence diagram for this feature is shown below: -![SleepAddSeqDiagram.jpg](assets%2FSleepAddSeqDiagram.jpg) +![SleepAddSeqDiagram.jpg](assets%2FSleepAddSeqDiagram.png) ### Parsing user input for sleep entries diff --git a/docs/assets/SleepAddSeqDiagram.png b/docs/assets/SleepAddSeqDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..72a4bf015cf55994223039e89cd60e8f3f86916b GIT binary patch literal 41734 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/SleepAddSeqDiagram.puml b/docs/diagrams/SleepAddSeqDiagram.puml new file mode 100644 index 0000000000..882737361c --- /dev/null +++ b/docs/diagrams/SleepAddSeqDiagram.puml @@ -0,0 +1,39 @@ +@startuml +actor Bob +Bob -> UI: Input "sleep add" command +activate UI + +UI -> UI: handleUserInput() +activate UI + +UI -> UI: handleSleepInput() +activate UI + +UI -> SleepList: addSleep() +activate SleepList + +SleepList -> ParserSleep: parseSleepInput() +activate ParserSleep + +ParserSleep -> ParserSleep: parseDuration() +activate ParserSleep +return Double + +ParserSleep -> ParserSleep: parseDate() +activate ParserSleep +return LocalDate + +return SleepEntry + +SleepList -> SleepList: sleepList.add(SleepEntry) + +SleepList->SleepListUi: printNewSleepEntry(SleepEntry) +activate SleepListUi +return + +return +return +return +UI-> Bob:Display successful message + +@enduml From 5117f97ee051446f28623ca53a07f31d0ab1f2b5 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sat, 13 Apr 2024 18:13:41 +0800 Subject: [PATCH 308/414] handle date format/period too late exceptions, handle calories and entryID number format exceptions --- .../FileHandlerExceptionMessage.java | 58 ++++++++++++++++++- .../lifetrack/system/storage/FileHandler.java | 36 ++++++++++-- src/main/java/seedu/lifetrack/ui/Ui.java | 1 + 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java index 5f8ceff15c..1b3ff5d9dc 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -1,8 +1,64 @@ +//@@author owx0130 package seedu.lifetrack.system.exceptions; +import static seedu.lifetrack.ui.Ui.printLine; + /** * Utility class for managing error messages related to file handler exceptions. */ public class FileHandlerExceptionMessage { - + + //prefixes for error messages + private static final String INVALID_ENTRYID = "\t An invalid EntryID value was given in line "; + private static final String INVALID_CALORIES = "\t An invalid calories value was given in line "; + private static final String INVALID_DATE = "\t An invalid date was given in line "; + + //messages to provide user guidance + private static final String INTEGER_GUIDANCE = "\t Please ensure that an integer value is given"; + private static final String DATE_FORMAT_GUIDANCE = "\t Please ensure that a date in format YYYY-MM-DD is given"; + private static final String DATE_PERIOD_GUIDANCE = "\t Please ensure that a date no later than today is given"; + + private static String getLineNotAddedMessage(int lineNumber, String filePath) { + if (filePath.equals("data/caloriesData.txt")) { + return "\t Line " + lineNumber + " was not added into the calories list due to corrupt data!\n"; + } else if (filePath.equals("data/hydrationData.txt")) { + return "\t Line " + lineNumber + " was added into the hydration list due to corrupt data!\n"; + } else if (filePath.equals("data/sleepData.txt")){ + return "\t Line " + lineNumber + " was added into the sleep list due to corrupt data!\n"; + } else { + return "\t The user was not set due to corrupt data!\n"; + } + } + + public static String getFileInvalidEntryIDMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_ENTRYID + lineNumber + " of " + filePath + "!\n" + suffix + INTEGER_GUIDANCE; + } + + public static String getFileInvalidCaloriesMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_CALORIES + lineNumber + " of " + filePath + "!\n" + suffix + INTEGER_GUIDANCE; + } + + public static String getFileInvalidCarbsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + String message = "\t An invalid carbohydrates value was given in line " + + lineNumber + " of " + filePath + "!\n" + "Make sure that this value is an integer!\n" + suffix; + return message; + } + + public static String getFileInvalidDateMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_DATE + lineNumber + " of " + filePath + "!\n" + suffix + DATE_FORMAT_GUIDANCE; + } + + public static String getFileDateLaterThanCurrentMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_DATE + lineNumber + " of " + filePath + "!\n" + suffix + DATE_PERIOD_GUIDANCE; + } } diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 8b006e0819..afd0960ab2 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -6,6 +6,7 @@ import java.io.FileWriter; import java.io.IOException; import java.time.LocalDate; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Scanner; @@ -18,6 +19,11 @@ import seedu.lifetrack.system.exceptions.FileHandlerException; import seedu.lifetrack.user.User; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileDateLaterThanCurrentMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCaloriesMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; + public class FileHandler { //public member for lastEntryID calories @@ -52,6 +58,9 @@ public class FileHandler { private static final int GOAL_INDEX = 6; private static final int REQ_CAL_INDEX = 7; + //exception prefix strings + private static final String NF_EXCEPTION_PREFIX = "For input string: \""; + //error message for IO exception private static final String message = "\t Unable to write to file!"; @@ -61,6 +70,12 @@ public FileHandler(String filePath) { this.filePath = filePath; } + private void checkDateNotLaterThanCurrent(LocalDate date, int lineNumber) throws FileHandlerException { + if (date.isAfter(LocalDate.now())) { + throw new FileHandlerException(getFileDateLaterThanCurrentMessage(lineNumber, filePath)); + } + } + private void writeToFile(String textToAdd) throws IOException { FileWriter fw = new FileWriter(filePath); fw.write(textToAdd); @@ -92,14 +107,15 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException Scanner s = new Scanner(f); ArrayList entries = new ArrayList<>(); String line = ""; + int i = 1; while (s.hasNext()) { + line = s.nextLine(); + String[] words = line.split(";"); try { - line = s.nextLine(); - String[] words = line.split(";"); - System.out.println(words[0]); int entryID = Integer.parseInt(words[ENTRYID_INDEX]); calculateMaxCaloriesEntry(entryID); LocalDate date = LocalDate.parse(words[DATE_INDEX]); + checkDateNotLaterThanCurrent(date, i); String description = words[DESCRIPTION_INDEX]; int calories = Integer.parseInt(words[CALORIES_INDEX]); String entryType = words[ENTRY_TYPE_INDEX]; @@ -114,8 +130,18 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException } else { entries.add(new OutputEntry(entryID, description, calories, date)); } - } catch (NumberFormatException e){ - System.out.println("wrong format"); + } catch (NumberFormatException e) { + if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { + System.out.println(getFileInvalidEntryIDMessage(i, filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[CALORIES_INDEX] + "\"")) { + System.out.println(getFileInvalidCaloriesMessage(i, filePath)); + } + } catch (DateTimeParseException e) { + System.out.println(getFileInvalidDateMessage(i, filePath)); + } catch (FileHandlerException e) { + System.out.println(e.getMessage()); + } finally { + i++; } } s.close(); diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index b343c6f69a..7f4db2621f 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -153,6 +153,7 @@ private static void handleUserProgress(User user) { } public static void sayHello() { + printLine(); System.out.println(WHITESPACE + "Hello from\n\n" + logo); System.out.println(WHITESPACE + "How can I help you today?"); printLine(); From c2258fdb3f65945d0b23ae6ac78415e858ebc062 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sat, 13 Apr 2024 18:35:57 +0800 Subject: [PATCH 309/414] create new class CaloriesFileHandler to handle storage needs for calories list --- .../calories/calorielist/CalorieList.java | 9 +- .../FileHandlerExceptionMessage.java | 5 +- .../system/storage/CaloriesFileHandler.java | 87 +++++++++++++++++ .../lifetrack/system/storage/FileHandler.java | 97 ++++--------------- 4 files changed, 111 insertions(+), 87 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index 678d186a00..c1affc99c9 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -4,7 +4,7 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserCalories; -import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.system.storage.CaloriesFileHandler; import seedu.lifetrack.ui.CalorieListUi; import java.time.LocalDate; @@ -24,7 +24,7 @@ public class CalorieList { //constant for finding entry index from entryID private final int NO_INDEX_FOUND = -1; private ArrayList calorieArrayList; - private FileHandler fileHandler; + private CaloriesFileHandler fileHandler; private int lastEntryID; /** @@ -43,7 +43,7 @@ public CalorieList() { */ public CalorieList(String filePath) { try { - fileHandler = new FileHandler(filePath); + fileHandler = new CaloriesFileHandler(filePath); calorieArrayList = fileHandler.getCalorieEntriesFromFile(); // Initialize lastEntryID from stored data or default to 0 if not available this.lastEntryID = loadLastEntryID(); @@ -239,7 +239,6 @@ public int getCaloriesConsumedCurrentDay() { return totalCalories; } - /** * Loads the last entry ID from a text file. * This method retrieves the last entry ID from the file using the FileHandler.getMaxCaloriesID method. @@ -248,7 +247,7 @@ public int getCaloriesConsumedCurrentDay() { * @return the last entry ID loaded from the file, or a default value if the file doesn't exist or an error occurs */ private int loadLastEntryID() { - return FileHandler.maxCaloriesID; // Default value if file doesn't exist or error occurs + return CaloriesFileHandler.maxCaloriesID; // Default value if file doesn't exist or error occurs } /** diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java index 1b3ff5d9dc..a54bfde076 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -12,6 +12,7 @@ public class FileHandlerExceptionMessage { private static final String INVALID_ENTRYID = "\t An invalid EntryID value was given in line "; private static final String INVALID_CALORIES = "\t An invalid calories value was given in line "; private static final String INVALID_DATE = "\t An invalid date was given in line "; + private static final String INVALID_CARBS = "\t An invalid carbohydrates value was given in line "; //messages to provide user guidance private static final String INTEGER_GUIDANCE = "\t Please ensure that an integer value is given"; @@ -45,9 +46,7 @@ public static String getFileInvalidCaloriesMessage(int lineNumber, String filePa public static String getFileInvalidCarbsMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - String message = "\t An invalid carbohydrates value was given in line " + - lineNumber + " of " + filePath + "!\n" + "Make sure that this value is an integer!\n" + suffix; - return message; + return INVALID_CARBS + lineNumber + " of " + filePath + "!\n" + suffix + INTEGER_GUIDANCE; } public static String getFileInvalidDateMessage(int lineNumber, String filePath) { diff --git a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java new file mode 100644 index 0000000000..403e87b07b --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java @@ -0,0 +1,87 @@ +package seedu.lifetrack.system.storage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Scanner; + +import seedu.lifetrack.Entry; +import seedu.lifetrack.calories.Food; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; +import seedu.lifetrack.system.exceptions.FileHandlerException; + +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCaloriesMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; + +public class CaloriesFileHandler extends FileHandler { + + //class-level member for lastEntryID calories + public static int maxCaloriesID = 0; + + //calorie list constants + private final int ENTRY_TYPE_INDEX = 3; + private final int CALORIES_INDEX = 4; + private final int CARBOHYDRATES_INDEX = 5; + private final int PROTEINS_INDEX = 6; + private final int FATS_INDEX = 7; + + public CaloriesFileHandler(String filePath) { + super(filePath); + } + + private void calculateMaxCaloriesEntry(int entryID) { + if (entryID > maxCaloriesID) { + maxCaloriesID = entryID; + } + } + + public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList entries = new ArrayList<>(); + String line = ""; + int i = 1; + while (s.hasNext()) { + line = s.nextLine(); + String[] words = line.split(";"); + try { + int entryID = Integer.parseInt(words[ENTRYID_INDEX]); + calculateMaxCaloriesEntry(entryID); + LocalDate date = LocalDate.parse(words[DATE_INDEX]); + checkDateNotLaterThanCurrent(date, i); + String description = words[DESCRIPTION_INDEX]; + int calories = Integer.parseInt(words[CALORIES_INDEX]); + String entryType = words[ENTRY_TYPE_INDEX]; + if (entryType.equals("C_IN") && words.length == 8) { + int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); + int proteins = Integer.parseInt(words[PROTEINS_INDEX]); + int fats = Integer.parseInt(words[FATS_INDEX]); + Food food = new Food(carbohydrates, proteins, fats); + entries.add(new InputEntry(entryID, description, calories, date, food)); + } else if (entryType.equals("C_IN")) { + entries.add(new InputEntry(entryID, description, calories, date)); + } else { + entries.add(new OutputEntry(entryID, description, calories, date)); + } + } catch (NumberFormatException e) { + if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { + System.out.println(getFileInvalidEntryIDMessage(i, filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[CALORIES_INDEX] + "\"")) { + System.out.println(getFileInvalidCaloriesMessage(i, filePath)); + } + } catch (DateTimeParseException e) { + System.out.println(getFileInvalidDateMessage(i, filePath)); + } catch (FileHandlerException e) { + System.out.println(e.getMessage()); + } finally { + i++; + } + } + s.close(); + return entries; + } +} diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index afd0960ab2..6817006404 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -27,50 +27,42 @@ public class FileHandler { //public member for lastEntryID calories - public static int maxCaloriesID = 0; public static int maxHydrationID = 0; //general list constants - private static final int ENTRYID_INDEX = 0; - private static final int DATE_INDEX = 1; - private static final int DESCRIPTION_INDEX = 2; + protected final int ENTRYID_INDEX = 0; + protected final int DATE_INDEX = 1; + protected final int DESCRIPTION_INDEX = 2; - //calorie list constants - private static final int ENTRY_TYPE_INDEX = 3; - private static final int CALORIES_INDEX = 4; - private static final int CARBOHYDRATES_INDEX = 5; - private static final int PROTEINS_INDEX = 6; - private static final int FATS_INDEX = 7; - - //liquids list constants - private static final int VOLUME_INDEX = 3; + //hydration list constants + private final int VOLUME_INDEX = 3; //sleep list constants - private static final int DURATION_INDEX = 3; + private final int DURATION_INDEX = 3; //user data constants - private static final int NAME_INDEX = 0; - private static final int HEIGHT_INDEX = 1; - private static final int WEIGHT_INDEX = 2; - private static final int AGE_INDEX = 3; - private static final int SEX_INDEX = 4; - private static final int EXERCISE_INDEX = 5; - private static final int GOAL_INDEX = 6; - private static final int REQ_CAL_INDEX = 7; + private final int NAME_INDEX = 0; + private final int HEIGHT_INDEX = 1; + private final int WEIGHT_INDEX = 2; + private final int AGE_INDEX = 3; + private final int SEX_INDEX = 4; + private final int EXERCISE_INDEX = 5; + private final int GOAL_INDEX = 6; + private final int REQ_CAL_INDEX = 7; //exception prefix strings - private static final String NF_EXCEPTION_PREFIX = "For input string: \""; + protected final String NF_EXCEPTION_PREFIX = "For input string: \""; //error message for IO exception - private static final String message = "\t Unable to write to file!"; + private final String message = "\t Unable to write to file!"; - private String filePath; + protected String filePath; public FileHandler(String filePath) { this.filePath = filePath; } - private void checkDateNotLaterThanCurrent(LocalDate date, int lineNumber) throws FileHandlerException { + protected void checkDateNotLaterThanCurrent(LocalDate date, int lineNumber) throws FileHandlerException { if (date.isAfter(LocalDate.now())) { throw new FileHandlerException(getFileDateLaterThanCurrentMessage(lineNumber, filePath)); } @@ -102,59 +94,6 @@ public void writeEntries(ArrayList entries) { } } - public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException { - File f = new File(filePath); - Scanner s = new Scanner(f); - ArrayList entries = new ArrayList<>(); - String line = ""; - int i = 1; - while (s.hasNext()) { - line = s.nextLine(); - String[] words = line.split(";"); - try { - int entryID = Integer.parseInt(words[ENTRYID_INDEX]); - calculateMaxCaloriesEntry(entryID); - LocalDate date = LocalDate.parse(words[DATE_INDEX]); - checkDateNotLaterThanCurrent(date, i); - String description = words[DESCRIPTION_INDEX]; - int calories = Integer.parseInt(words[CALORIES_INDEX]); - String entryType = words[ENTRY_TYPE_INDEX]; - if (entryType.equals("C_IN") && words.length == 8) { - int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); - int proteins = Integer.parseInt(words[PROTEINS_INDEX]); - int fats = Integer.parseInt(words[FATS_INDEX]); - Food food = new Food(carbohydrates, proteins, fats); - entries.add(new InputEntry(entryID, description, calories, date, food)); - } else if (entryType.equals("C_IN")) { - entries.add(new InputEntry(entryID, description, calories, date)); - } else { - entries.add(new OutputEntry(entryID, description, calories, date)); - } - } catch (NumberFormatException e) { - if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { - System.out.println(getFileInvalidEntryIDMessage(i, filePath)); - } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[CALORIES_INDEX] + "\"")) { - System.out.println(getFileInvalidCaloriesMessage(i, filePath)); - } - } catch (DateTimeParseException e) { - System.out.println(getFileInvalidDateMessage(i, filePath)); - } catch (FileHandlerException e) { - System.out.println(e.getMessage()); - } finally { - i++; - } - } - s.close(); - return entries; - } - - // Method calculates the max calories entry ID - public void calculateMaxCaloriesEntry(int entryID) { - if (entryID > maxCaloriesID) { - maxCaloriesID = entryID; - } - } - public void calculateMaxHydrationEntry(int entryID) { if (entryID > maxHydrationID) { maxHydrationID = entryID; From 2b4e01ae39eb377c7891ca39d216ffd88479ccfa Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sat, 13 Apr 2024 23:25:41 +0800 Subject: [PATCH 310/414] handle all calories related exceptions in the data file --- .../FileHandlerExceptionMessage.java | 93 +++++++++++++++++-- .../system/storage/CaloriesFileHandler.java | 74 +++++++++++++-- .../lifetrack/system/storage/FileHandler.java | 9 +- 3 files changed, 156 insertions(+), 20 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java index a54bfde076..75458fe7f0 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -8,16 +8,39 @@ */ public class FileHandlerExceptionMessage { - //prefixes for error messages + //general error messages + private static final String TOO_FEW_ARGUMENTS = "\t Too few arguments were given in line "; + private static final String TOO_MANY_ARGUMENTS = "\t Too many arguments were given in line "; + private static final String EMPTY_DESC = "\t An empty description was given in line "; + private static final String INVALID_DATE = "\t An invalid date was given in line "; + + //calories list error messages private static final String INVALID_ENTRYID = "\t An invalid EntryID value was given in line "; private static final String INVALID_CALORIES = "\t An invalid calories value was given in line "; - private static final String INVALID_DATE = "\t An invalid date was given in line "; private static final String INVALID_CARBS = "\t An invalid carbohydrates value was given in line "; + private static final String INVALID_PROTEINS = "\t An invalid proteins value was given in line "; + private static final String INVALID_FATS = "\t An invalid fats value was given in line "; + private static final String INVALID_ENTRYTYPE = "\t An invalid entry type was given in line "; + private static final String TOO_FEW_MACROS = "\t Too few macronutrient arguments were given in line "; + private static final String MACROS_IN_OUTPUT = "\t Macronutrient arguments were given in line "; - //messages to provide user guidance - private static final String INTEGER_GUIDANCE = "\t Please ensure that an integer value is given"; + + //messages to provide user guidance (general) private static final String DATE_FORMAT_GUIDANCE = "\t Please ensure that a date in format YYYY-MM-DD is given"; private static final String DATE_PERIOD_GUIDANCE = "\t Please ensure that a date no later than today is given"; + private static final String DESC_GUIDANCE = "\t Please ensure that a non-empty description is given"; + private static final String POS_INT_GUIDANCE = "\t Please ensure that a positive integer value is given"; + private static final String INTEGER_GUIDANCE = "\t Please ensure that an integer value is given"; + + //messages to provide user guidance (calories) + private static final String CALORIES_FORMAT_GUIDANCE = "\t Please ensure that only either five or eight " + + "(including macronutrients) arguments are given!"; + private static final String ENTRYTYPE_GUIDANCE = "\t Please ensure the entry type is only either \"C_IN\" or " + + "\"C_OUT\""; + private static final String MACROS_GUIDANCE = "\t Please ensure that three macronutrient arguments are given!"; + private static final String OUTPUT_GUIDANCE = "\t Do not provide macronutrients for a calorie output " + + "(C_OUT) entry!"; + private static String getLineNotAddedMessage(int lineNumber, String filePath) { if (filePath.equals("data/caloriesData.txt")) { @@ -31,6 +54,38 @@ private static String getLineNotAddedMessage(int lineNumber, String filePath) { } } + //general messages + public static String getFileInvalidDateMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_DATE + lineNumber + " of " + filePath + "!\n" + suffix + DATE_FORMAT_GUIDANCE; + } + + public static String getFileDateLaterThanCurrentMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_DATE + lineNumber + " of " + filePath + "!\n" + suffix + DATE_PERIOD_GUIDANCE; + } + + public static String getFileEmptyDescriptionMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return EMPTY_DESC + lineNumber + " of " + filePath + "!\n" + suffix + DESC_GUIDANCE; + } + + //calories list related messages + public static String getFileCaloriesTooManyArgumentsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_MANY_ARGUMENTS + lineNumber + " of " + filePath + "!\n" + suffix + CALORIES_FORMAT_GUIDANCE; + } + + public static String getFileCaloriesTooFewArgumentsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_FEW_ARGUMENTS + lineNumber + " of " + filePath + "!\n" + suffix + CALORIES_FORMAT_GUIDANCE; + } + public static String getFileInvalidEntryIDMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); @@ -40,24 +95,42 @@ public static String getFileInvalidEntryIDMessage(int lineNumber, String filePat public static String getFileInvalidCaloriesMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - return INVALID_CALORIES + lineNumber + " of " + filePath + "!\n" + suffix + INTEGER_GUIDANCE; + return INVALID_CALORIES + lineNumber + " of " + filePath + "!\n" + suffix + POS_INT_GUIDANCE; } public static String getFileInvalidCarbsMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - return INVALID_CARBS + lineNumber + " of " + filePath + "!\n" + suffix + INTEGER_GUIDANCE; + return INVALID_CARBS + lineNumber + " of " + filePath + "!\n" + suffix + POS_INT_GUIDANCE; } - public static String getFileInvalidDateMessage(int lineNumber, String filePath) { + public static String getFileInvalidProteinsMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - return INVALID_DATE + lineNumber + " of " + filePath + "!\n" + suffix + DATE_FORMAT_GUIDANCE; + return INVALID_PROTEINS + lineNumber + " of " + filePath + "!\n" + suffix + POS_INT_GUIDANCE; } - public static String getFileDateLaterThanCurrentMessage(int lineNumber, String filePath) { + public static String getFileInvalidFatsMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - return INVALID_DATE + lineNumber + " of " + filePath + "!\n" + suffix + DATE_PERIOD_GUIDANCE; + return INVALID_FATS + lineNumber + " of " + filePath + "!\n" + suffix + POS_INT_GUIDANCE; + } + + public static String getFileInvalidEntryTypeMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_ENTRYTYPE + lineNumber + " of " + filePath + "!\n" + suffix + ENTRYTYPE_GUIDANCE; + } + + public static String getFileTooFewMacrosMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_FEW_MACROS + lineNumber + " of " + filePath + "!\n" + suffix + MACROS_GUIDANCE; + } + + public static String getFileMacrosInOutputMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return MACROS_IN_OUTPUT + lineNumber + " of " + filePath + "!\n" + suffix + OUTPUT_GUIDANCE; } } diff --git a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java index 403e87b07b..bae3db2dd1 100644 --- a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java @@ -13,8 +13,16 @@ import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.system.exceptions.FileHandlerException; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooFewArgumentsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileTooFewMacrosMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooManyArgumentsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCaloriesMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCarbsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryTypeMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidFatsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidProteinsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileMacrosInOutputMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; public class CaloriesFileHandler extends FileHandler { @@ -39,6 +47,43 @@ private void calculateMaxCaloriesEntry(int entryID) { } } + private void checkCorrectNumberOfArguments(String[] words, int lineNumber) throws FileHandlerException { + if (words.length < 5) { + throw new FileHandlerException(getFileCaloriesTooFewArgumentsMessage(lineNumber, filePath)); + } else if (words.length > 8) { + throw new FileHandlerException(getFileCaloriesTooManyArgumentsMessage(lineNumber, filePath)); + } + } + + private void checkCaloriesIsPositive(int lineNumber, int calories) throws FileHandlerException { + if (calories <= 0) { + throw new FileHandlerException(getFileInvalidCaloriesMessage(lineNumber, filePath)); + } + } + + private void checkMacrosArePositive(int lineNumber, int carbs, int proteins, int fats) + throws FileHandlerException { + if (carbs <= 0) { + throw new FileHandlerException(getFileInvalidCarbsMessage(lineNumber, filePath)); + } else if (proteins <= 0) { + throw new FileHandlerException(getFileInvalidProteinsMessage(lineNumber, filePath)); + } else if (fats <= 0) { + throw new FileHandlerException(getFileInvalidFatsMessage(lineNumber, filePath)); + } + } + + private void checkValidEntryType(int lineNumber, String entryType, int dataLength) throws FileHandlerException { + if (!(entryType.equals("C_IN") || entryType.equals("C_OUT"))) { + throw new FileHandlerException(getFileInvalidEntryTypeMessage(lineNumber, filePath)); + } + + if (entryType.equals("C_IN") && dataLength != 8) { + throw new FileHandlerException(getFileTooFewMacrosMessage(lineNumber, filePath)); + } else if (entryType.equals("C_OUT") && dataLength != 5) { + throw new FileHandlerException(getFileMacrosInOutputMessage(lineNumber, filePath)); + } + } + public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); @@ -49,18 +94,23 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException line = s.nextLine(); String[] words = line.split(";"); try { - int entryID = Integer.parseInt(words[ENTRYID_INDEX]); + checkCorrectNumberOfArguments(words, i); + int entryID = Integer.parseInt(words[ENTRYID_INDEX].trim()); calculateMaxCaloriesEntry(entryID); - LocalDate date = LocalDate.parse(words[DATE_INDEX]); - checkDateNotLaterThanCurrent(date, i); - String description = words[DESCRIPTION_INDEX]; - int calories = Integer.parseInt(words[CALORIES_INDEX]); - String entryType = words[ENTRY_TYPE_INDEX]; + LocalDate date = LocalDate.parse(words[DATE_INDEX].trim()); + checkDateNotLaterThanCurrent(i, date); + String description = words[DESCRIPTION_INDEX].trim(); + checkNonEmptyDescription(i, description); + int calories = Integer.parseInt(words[CALORIES_INDEX].trim()); + checkCaloriesIsPositive(i, calories); + String entryType = words[ENTRY_TYPE_INDEX].trim(); + checkValidEntryType(i, entryType, words.length); if (entryType.equals("C_IN") && words.length == 8) { - int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX]); - int proteins = Integer.parseInt(words[PROTEINS_INDEX]); - int fats = Integer.parseInt(words[FATS_INDEX]); + int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX].trim()); + int proteins = Integer.parseInt(words[PROTEINS_INDEX].trim()); + int fats = Integer.parseInt(words[FATS_INDEX].trim()); Food food = new Food(carbohydrates, proteins, fats); + checkMacrosArePositive(i, carbohydrates, proteins, fats); entries.add(new InputEntry(entryID, description, calories, date, food)); } else if (entryType.equals("C_IN")) { entries.add(new InputEntry(entryID, description, calories, date)); @@ -72,6 +122,12 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException System.out.println(getFileInvalidEntryIDMessage(i, filePath)); } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[CALORIES_INDEX] + "\"")) { System.out.println(getFileInvalidCaloriesMessage(i, filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[CARBOHYDRATES_INDEX] + "\"")) { + System.out.println(getFileInvalidCarbsMessage(i, filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[PROTEINS_INDEX] + "\"")) { + System.out.println(getFileInvalidProteinsMessage(i, filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[FATS_INDEX] + "\"")) { + System.out.println(getFileInvalidFatsMessage(i, filePath)); } } catch (DateTimeParseException e) { System.out.println(getFileInvalidDateMessage(i, filePath)); diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 6817006404..74171d592c 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -20,6 +20,7 @@ import seedu.lifetrack.user.User; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileDateLaterThanCurrentMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileEmptyDescriptionMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCaloriesMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; @@ -62,12 +63,18 @@ public FileHandler(String filePath) { this.filePath = filePath; } - protected void checkDateNotLaterThanCurrent(LocalDate date, int lineNumber) throws FileHandlerException { + protected void checkDateNotLaterThanCurrent(int lineNumber, LocalDate date) throws FileHandlerException { if (date.isAfter(LocalDate.now())) { throw new FileHandlerException(getFileDateLaterThanCurrentMessage(lineNumber, filePath)); } } + protected void checkNonEmptyDescription(int lineNumber, String description) throws FileHandlerException { + if (description.equals("")) { + throw new FileHandlerException(getFileEmptyDescriptionMessage(lineNumber, filePath)); + } + } + private void writeToFile(String textToAdd) throws IOException { FileWriter fw = new FileWriter(filePath); fw.write(textToAdd); From f0694e10ee94d6549e2ac8b21f8ba86a3245a9f2 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 00:08:29 +0800 Subject: [PATCH 311/414] summary --- src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 1e0dd6e0d0..e90bfb7110 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -2,9 +2,6 @@ package seedu.lifetrack.sleep.sleeplist; import seedu.lifetrack.Entry; -import seedu.lifetrack.calories.calorielist.InputEntry; -import seedu.lifetrack.calories.calorielist.OutputEntry; -import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserSleep; import seedu.lifetrack.system.storage.FileHandler; From 1bf088cc5f3deab56f8995c2c9e3084ceda3bea7 Mon Sep 17 00:00:00 2001 From: Paturi Karthik <64789669+paturikarthik@users.noreply.github.com> Date: Sun, 14 Apr 2024 00:11:42 +0800 Subject: [PATCH 312/414] Edit naming mistake --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 0a5328997d..e91ce54e37 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -30,7 +30,7 @@ ## Design ### calories component -Here's a (partial) class diagram of the `calories` component. +Here's a (partial) class diagram of the `sleep` component. ![calories.png](assets%2Fcalories.png) From 131dc1157fee77b79d13c11e1d1a48fa5867840a Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 00:13:12 +0800 Subject: [PATCH 313/414] handle all hydration related exceptions in the data file --- .../hydrationlist/HydrationList.java | 8 +- .../FileHandlerExceptionMessage.java | 56 +++++++++---- .../system/storage/CaloriesFileHandler.java | 22 +++--- .../lifetrack/system/storage/FileHandler.java | 43 +++------- .../system/storage/HydrationFileHandler.java | 79 +++++++++++++++++++ 5 files changed, 145 insertions(+), 63 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 4da0d1cb4d..3941c0882e 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -5,7 +5,7 @@ import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserHydration; -import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.system.storage.HydrationFileHandler; import seedu.lifetrack.ui.HydrationListUI; import java.io.FileNotFoundException; @@ -28,7 +28,7 @@ public class HydrationList { private final int NO_INDEX_FOUND = -1; private ArrayList hydrationArrayList; - private FileHandler fileHandler; + private HydrationFileHandler fileHandler; private int lastHydrationEntryID; //constructor for JUnit tests @@ -39,7 +39,7 @@ public HydrationList() { //constructor for usage in terminal public HydrationList(String filePath) { try { - fileHandler = new FileHandler(filePath); + fileHandler = new HydrationFileHandler(filePath); hydrationArrayList = fileHandler.getHydrationEntriesFromFile(); this.lastHydrationEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { @@ -195,6 +195,6 @@ public int getSize() { } private int loadLastEntryID() { - return FileHandler.maxHydrationID; + return HydrationFileHandler.maxHydrationID; } } diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java index 75458fe7f0..a549c1fef8 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -9,23 +9,26 @@ public class FileHandlerExceptionMessage { //general error messages - private static final String TOO_FEW_ARGUMENTS = "\t Too few arguments were given in line "; - private static final String TOO_MANY_ARGUMENTS = "\t Too many arguments were given in line "; + private static final String TOO_FEW_FIELDS = "\t Too few fields were given in line "; + private static final String TOO_MANY_FIELDS = "\t Too many fields were given in line "; private static final String EMPTY_DESC = "\t An empty description was given in line "; private static final String INVALID_DATE = "\t An invalid date was given in line "; + private static final String INVALID_ENTRYID = "\t An invalid EntryID value was given in line "; //calories list error messages - private static final String INVALID_ENTRYID = "\t An invalid EntryID value was given in line "; private static final String INVALID_CALORIES = "\t An invalid calories value was given in line "; private static final String INVALID_CARBS = "\t An invalid carbohydrates value was given in line "; private static final String INVALID_PROTEINS = "\t An invalid proteins value was given in line "; private static final String INVALID_FATS = "\t An invalid fats value was given in line "; private static final String INVALID_ENTRYTYPE = "\t An invalid entry type was given in line "; - private static final String TOO_FEW_MACROS = "\t Too few macronutrient arguments were given in line "; - private static final String MACROS_IN_OUTPUT = "\t Macronutrient arguments were given in line "; + private static final String TOO_FEW_MACROS = "\t Too few macronutrient fields were given in line "; + private static final String MACROS_IN_OUTPUT = "\t Macronutrient fields were given in line "; + //hydration list error messages + private static final String INVALID_VOLUME = "\t An invalid volume value was given in line "; //messages to provide user guidance (general) + private static final String FIELDS_GUIDANCE = "\t Please ensure that four fields are given!"; private static final String DATE_FORMAT_GUIDANCE = "\t Please ensure that a date in format YYYY-MM-DD is given"; private static final String DATE_PERIOD_GUIDANCE = "\t Please ensure that a date no later than today is given"; private static final String DESC_GUIDANCE = "\t Please ensure that a non-empty description is given"; @@ -33,11 +36,11 @@ public class FileHandlerExceptionMessage { private static final String INTEGER_GUIDANCE = "\t Please ensure that an integer value is given"; //messages to provide user guidance (calories) - private static final String CALORIES_FORMAT_GUIDANCE = "\t Please ensure that only either five or eight " + - "(including macronutrients) arguments are given!"; + private static final String CALORIES_FIELDS_GUIDANCE = "\t Please ensure that only either five or eight " + + "(including macronutrients) fields are provided!"; private static final String ENTRYTYPE_GUIDANCE = "\t Please ensure the entry type is only either \"C_IN\" or " + "\"C_OUT\""; - private static final String MACROS_GUIDANCE = "\t Please ensure that three macronutrient arguments are given!"; + private static final String MACROS_GUIDANCE = "\t Please ensure that three macronutrient fields are given!"; private static final String OUTPUT_GUIDANCE = "\t Do not provide macronutrients for a calorie output " + "(C_OUT) entry!"; @@ -46,15 +49,27 @@ private static String getLineNotAddedMessage(int lineNumber, String filePath) { if (filePath.equals("data/caloriesData.txt")) { return "\t Line " + lineNumber + " was not added into the calories list due to corrupt data!\n"; } else if (filePath.equals("data/hydrationData.txt")) { - return "\t Line " + lineNumber + " was added into the hydration list due to corrupt data!\n"; + return "\t Line " + lineNumber + " was not added into the hydration list due to corrupt data!\n"; } else if (filePath.equals("data/sleepData.txt")){ - return "\t Line " + lineNumber + " was added into the sleep list due to corrupt data!\n"; + return "\t Line " + lineNumber + " was not added into the sleep list due to corrupt data!\n"; } else { return "\t The user was not set due to corrupt data!\n"; } } //general messages + public static String getFileTooManyFieldsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_MANY_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + FIELDS_GUIDANCE; + } + + public static String getFileTooFewFieldsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_FEW_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + FIELDS_GUIDANCE; + } + public static String getFileInvalidDateMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); @@ -73,23 +88,23 @@ public static String getFileEmptyDescriptionMessage(int lineNumber, String fileP return EMPTY_DESC + lineNumber + " of " + filePath + "!\n" + suffix + DESC_GUIDANCE; } - //calories list related messages - public static String getFileCaloriesTooManyArgumentsMessage(int lineNumber, String filePath) { + public static String getFileInvalidEntryIDMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - return TOO_MANY_ARGUMENTS + lineNumber + " of " + filePath + "!\n" + suffix + CALORIES_FORMAT_GUIDANCE; + return INVALID_ENTRYID + lineNumber + " of " + filePath + "!\n" + suffix + INTEGER_GUIDANCE; } - public static String getFileCaloriesTooFewArgumentsMessage(int lineNumber, String filePath) { + //calories list related messages + public static String getFileCaloriesTooManyFieldsMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - return TOO_FEW_ARGUMENTS + lineNumber + " of " + filePath + "!\n" + suffix + CALORIES_FORMAT_GUIDANCE; + return TOO_MANY_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + CALORIES_FIELDS_GUIDANCE; } - public static String getFileInvalidEntryIDMessage(int lineNumber, String filePath) { + public static String getFileCaloriesTooFewFieldsMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); - return INVALID_ENTRYID + lineNumber + " of " + filePath + "!\n" + suffix + INTEGER_GUIDANCE; + return TOO_FEW_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + CALORIES_FIELDS_GUIDANCE; } public static String getFileInvalidCaloriesMessage(int lineNumber, String filePath) { @@ -133,4 +148,11 @@ public static String getFileMacrosInOutputMessage(int lineNumber, String filePat printLine(); return MACROS_IN_OUTPUT + lineNumber + " of " + filePath + "!\n" + suffix + OUTPUT_GUIDANCE; } + + //hydration list related messages + public static String getFileInvalidVolumeMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_VOLUME + lineNumber + " of " + filePath + "!\n" + suffix + POS_INT_GUIDANCE; + } } diff --git a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java index bae3db2dd1..28b8228be6 100644 --- a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java @@ -13,9 +13,9 @@ import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.system.exceptions.FileHandlerException; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooFewArgumentsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooFewFieldsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileTooFewMacrosMessage; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooManyArgumentsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooManyFieldsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCaloriesMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCarbsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; @@ -41,17 +41,17 @@ public CaloriesFileHandler(String filePath) { super(filePath); } - private void calculateMaxCaloriesEntry(int entryID) { - if (entryID > maxCaloriesID) { - maxCaloriesID = entryID; + protected void checkCorrectNumberOfFields(int lineNumber, int dataLength) throws FileHandlerException { + if (dataLength < 5) { + throw new FileHandlerException(getFileCaloriesTooFewFieldsMessage(lineNumber, filePath)); + } else if (dataLength > 8) { + throw new FileHandlerException(getFileCaloriesTooManyFieldsMessage(lineNumber, filePath)); } } - private void checkCorrectNumberOfArguments(String[] words, int lineNumber) throws FileHandlerException { - if (words.length < 5) { - throw new FileHandlerException(getFileCaloriesTooFewArgumentsMessage(lineNumber, filePath)); - } else if (words.length > 8) { - throw new FileHandlerException(getFileCaloriesTooManyArgumentsMessage(lineNumber, filePath)); + private void calculateMaxCaloriesEntry(int entryID) { + if (entryID > maxCaloriesID) { + maxCaloriesID = entryID; } } @@ -94,7 +94,7 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException line = s.nextLine(); String[] words = line.split(";"); try { - checkCorrectNumberOfArguments(words, i); + checkCorrectNumberOfFields(i, words.length); int entryID = Integer.parseInt(words[ENTRYID_INDEX].trim()); calculateMaxCaloriesEntry(entryID); LocalDate date = LocalDate.parse(words[DATE_INDEX].trim()); diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 74171d592c..7d2fbb4bbf 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -19,25 +19,22 @@ import seedu.lifetrack.system.exceptions.FileHandlerException; import seedu.lifetrack.user.User; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooFewFieldsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileDateLaterThanCurrentMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileEmptyDescriptionMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCaloriesMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileTooFewFieldsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileTooManyFieldsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; public class FileHandler { - //public member for lastEntryID calories - public static int maxHydrationID = 0; - //general list constants protected final int ENTRYID_INDEX = 0; protected final int DATE_INDEX = 1; protected final int DESCRIPTION_INDEX = 2; - //hydration list constants - private final int VOLUME_INDEX = 3; - //sleep list constants private final int DURATION_INDEX = 3; @@ -51,7 +48,7 @@ public class FileHandler { private final int GOAL_INDEX = 6; private final int REQ_CAL_INDEX = 7; - //exception prefix strings + //NumberFormatException exception message prefix protected final String NF_EXCEPTION_PREFIX = "For input string: \""; //error message for IO exception @@ -63,6 +60,14 @@ public FileHandler(String filePath) { this.filePath = filePath; } + protected void checkCorrectNumberOfFields(int lineNumber, int dataLength) throws FileHandlerException { + if (dataLength < 4) { + throw new FileHandlerException(getFileTooFewFieldsMessage(lineNumber, filePath)); + } else if (dataLength > 4) { + throw new FileHandlerException(getFileTooManyFieldsMessage(lineNumber, filePath)); + } + } + protected void checkDateNotLaterThanCurrent(int lineNumber, LocalDate date) throws FileHandlerException { if (date.isAfter(LocalDate.now())) { throw new FileHandlerException(getFileDateLaterThanCurrentMessage(lineNumber, filePath)); @@ -101,30 +106,6 @@ public void writeEntries(ArrayList entries) { } } - public void calculateMaxHydrationEntry(int entryID) { - if (entryID > maxHydrationID) { - maxHydrationID = entryID; - } - } - - public ArrayList getHydrationEntriesFromFile() throws FileNotFoundException { - File f = new File(filePath); - Scanner s = new Scanner(f); - ArrayList entries = new ArrayList<>(); - String line = ""; - while (s.hasNext()) { - line = s.nextLine(); - String[] words = line.split(";"); - int lastHydrationEntryID = Integer.parseInt(words[ENTRYID_INDEX]); - LocalDate date = LocalDate.parse(words[DATE_INDEX]); - String description = words[DESCRIPTION_INDEX]; - int volume = Integer.parseInt(words[VOLUME_INDEX]); - entries.add(new HydrationEntry(lastHydrationEntryID, description, volume, date)); - } - s.close(); - return entries; - } - public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); diff --git a/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java new file mode 100644 index 0000000000..b4bdf03964 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java @@ -0,0 +1,79 @@ +package seedu.lifetrack.system.storage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Scanner; + +import seedu.lifetrack.Entry; +import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; +import seedu.lifetrack.system.exceptions.FileHandlerException; + +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidVolumeMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; + +public class HydrationFileHandler extends FileHandler { + + //class-level member for lastEntryID calories + public static int maxHydrationID = 0; + + //hydration list constants + private final int VOLUME_INDEX = 3; + + public HydrationFileHandler(String filePath) { + super(filePath); + } + + private void calculateMaxHydrationEntry(int entryID) { + if (entryID > maxHydrationID) { + maxHydrationID = entryID; + } + } + + private void checkVolumeIsPositive(int lineNumber, int volume) throws FileHandlerException { + if (volume <= 0) { + throw new FileHandlerException(getFileInvalidVolumeMessage(lineNumber, filePath)); + } + } + + public ArrayList getHydrationEntriesFromFile() throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList entries = new ArrayList<>(); + String line = ""; + int i = 1; + while (s.hasNext()) { + line = s.nextLine(); + String[] words = line.split(";"); + try { + checkCorrectNumberOfFields(i, words.length); + int entryID = Integer.parseInt(words[ENTRYID_INDEX]); + calculateMaxHydrationEntry(entryID); + LocalDate date = LocalDate.parse(words[DATE_INDEX]); + checkDateNotLaterThanCurrent(i, date); + String description = words[DESCRIPTION_INDEX]; + checkNonEmptyDescription(i, description); + int volume = Integer.parseInt(words[VOLUME_INDEX]); + checkVolumeIsPositive(i, volume); + entries.add(new HydrationEntry(entryID, description, volume, date)); + } catch (NumberFormatException e) { + if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { + System.out.println(getFileInvalidEntryIDMessage(i, filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[VOLUME_INDEX] + "\"")) { + System.out.println(getFileInvalidVolumeMessage(i, filePath)); + } + } catch (DateTimeParseException e) { + System.out.println(getFileInvalidDateMessage(i, filePath)); + } catch (FileHandlerException e) { + System.out.println(e.getMessage()); + } finally { + i++; + } + } + s.close(); + return entries; + } +} From 81a2ee492fe1433449dcc2fbe7237ff81cb1fbd5 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 02:10:04 +0800 Subject: [PATCH 314/414] handle all sleep related exceptions for data file --- src/main/java/seedu/lifetrack/Entry.java | 17 ++-- .../calories/calorielist/CalorieList.java | 2 +- .../hydrationlist/HydrationList.java | 2 +- .../lifetrack/sleep/sleeplist/SleepEntry.java | 2 +- .../lifetrack/sleep/sleeplist/SleepList.java | 6 +- .../FileHandlerExceptionMessage.java | 54 +++++++++---- .../system/parser/ParserCalories.java | 5 +- .../lifetrack/system/storage/FileHandler.java | 40 ---------- .../system/storage/HydrationFileHandler.java | 10 +++ .../system/storage/SleepFileHandler.java | 77 +++++++++++++++++++ .../java/seedu/lifetrack/SleepListTest.java | 2 +- 11 files changed, 144 insertions(+), 73 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java diff --git a/src/main/java/seedu/lifetrack/Entry.java b/src/main/java/seedu/lifetrack/Entry.java index 98cac21dab..0d44ded5de 100644 --- a/src/main/java/seedu/lifetrack/Entry.java +++ b/src/main/java/seedu/lifetrack/Entry.java @@ -10,11 +10,10 @@ public abstract class Entry { private String description; private LocalDate date; - - private int lastEntryID; + private int entryID; public Entry(int lastEntryID, String description, LocalDate date){ - this.lastEntryID = lastEntryID; + this.entryID = lastEntryID; this.description = description; this.date = date; } @@ -27,24 +26,24 @@ public LocalDate getDate() { return date; } - public int getLastEntryID() { - return lastEntryID; + public int getEntryID() { + return entryID; } public String toString() { if(this instanceof InputEntry) { - return String.format("\t caloriesID: " + lastEntryID + ", Date: " + date + + return String.format("\t caloriesID: " + entryID + ", Date: " + date + ", Description: " + description); } else if (this instanceof OutputEntry) { - return String.format("\t caloriesID: " + lastEntryID + ", Date: " + date + ", " + + return String.format("\t caloriesID: " + entryID + ", Date: " + date + ", " + "Description: " + description); } else { - return String.format("\t hydrationID: " + lastEntryID + ", Date: " + date + ", " + + return String.format("\t hydrationID: " + entryID + ", Date: " + date + ", " + "Description: " + description); } } public String toFileFriendlyString() { - return String.format(lastEntryID + ";" + date + ";" + description); + return String.format(entryID + ";" + date + ";" + description); } } diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index c1affc99c9..5b61abc058 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -106,7 +106,7 @@ public void deleteEntry(String line) { */ public int getIndexFromEntryID(int entryID) { for (int i = 0; i < calorieArrayList.size(); i++) { - if (calorieArrayList.get(i).getLastEntryID() == entryID) { + if (calorieArrayList.get(i).getEntryID() == entryID) { return i; } } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 3941c0882e..2ba87f09c8 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -95,7 +95,7 @@ public void deleteEntry(String line) { } public int getIndexFromEntryID(int lastEntryID) { for (int i = 0; i < hydrationArrayList.size(); i++) { - if (hydrationArrayList.get(i).getLastEntryID() == lastEntryID) { + if (hydrationArrayList.get(i).getEntryID() == lastEntryID) { return i; } } diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index 749ccd2263..ebc37c572e 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -53,7 +53,7 @@ public String toString() { } public String toFileFriendlyString() { - return String.format(super.toFileFriendlyString() + ";" + duration); + return String.format(sleepEntryID + ";" + date + ";" + duration); } } //@@author diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 29db88e20f..c9d20871d8 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -4,7 +4,7 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.system.exceptions.InvalidInputException; import seedu.lifetrack.system.parser.ParserSleep; -import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.system.storage.SleepFileHandler; import seedu.lifetrack.ui.SleepListUi; import java.io.FileNotFoundException; @@ -14,7 +14,7 @@ public class SleepList { private static int DELETE_IDX = 2; private ArrayList sleepList; - private FileHandler fileHandler; + private SleepFileHandler fileHandler; private int lastSleepEntryID; //constructor for JUnit tests @@ -25,7 +25,7 @@ public SleepList() { //constructor for usage in terminal public SleepList(String filePath) { try { - fileHandler = new FileHandler(filePath); + fileHandler = new SleepFileHandler(filePath); sleepList = fileHandler.getSleepEntriesFromFile(); this.lastSleepEntryID = loadLastEntryID(); } catch (FileNotFoundException e) { diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java index a549c1fef8..617b1fb58a 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -27,12 +27,15 @@ public class FileHandlerExceptionMessage { //hydration list error messages private static final String INVALID_VOLUME = "\t An invalid volume value was given in line "; + //hydration list error messages + private static final String INVALID_DURATION = "\t An invalid duration value was given in line "; + //messages to provide user guidance (general) - private static final String FIELDS_GUIDANCE = "\t Please ensure that four fields are given!"; private static final String DATE_FORMAT_GUIDANCE = "\t Please ensure that a date in format YYYY-MM-DD is given"; private static final String DATE_PERIOD_GUIDANCE = "\t Please ensure that a date no later than today is given"; private static final String DESC_GUIDANCE = "\t Please ensure that a non-empty description is given"; private static final String POS_INT_GUIDANCE = "\t Please ensure that a positive integer value is given"; + private static final String POS_FLOAT_GUIDANCE = "\t Please ensure that a positive float value is given"; private static final String INTEGER_GUIDANCE = "\t Please ensure that an integer value is given"; //messages to provide user guidance (calories) @@ -44,6 +47,12 @@ public class FileHandlerExceptionMessage { private static final String OUTPUT_GUIDANCE = "\t Do not provide macronutrients for a calorie output " + "(C_OUT) entry!"; + //messages to provide user guidance (hydration) + private static final String HYDRATION_FIELDS_GUIDANCE = "\t Please ensure that four fields are provided!"; + + //messages to provide user guidance (sleep) + private static final String SLEEP_FIELDS_GUIDANCE = "\t Please ensure that three fields are provided!"; + private static String getLineNotAddedMessage(int lineNumber, String filePath) { if (filePath.equals("data/caloriesData.txt")) { @@ -58,18 +67,6 @@ private static String getLineNotAddedMessage(int lineNumber, String filePath) { } //general messages - public static String getFileTooManyFieldsMessage(int lineNumber, String filePath) { - String suffix = getLineNotAddedMessage(lineNumber, filePath); - printLine(); - return TOO_MANY_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + FIELDS_GUIDANCE; - } - - public static String getFileTooFewFieldsMessage(int lineNumber, String filePath) { - String suffix = getLineNotAddedMessage(lineNumber, filePath); - printLine(); - return TOO_FEW_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + FIELDS_GUIDANCE; - } - public static String getFileInvalidDateMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); @@ -150,9 +147,40 @@ public static String getFileMacrosInOutputMessage(int lineNumber, String filePat } //hydration list related messages + public static String getFileHydrationTooManyFieldsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_MANY_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + HYDRATION_FIELDS_GUIDANCE; + } + + public static String getFileHydrationTooFewFieldsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_FEW_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + HYDRATION_FIELDS_GUIDANCE; + } + public static String getFileInvalidVolumeMessage(int lineNumber, String filePath) { String suffix = getLineNotAddedMessage(lineNumber, filePath); printLine(); return INVALID_VOLUME + lineNumber + " of " + filePath + "!\n" + suffix + POS_INT_GUIDANCE; } + + //sleep list related messages + public static String getFileSleepTooManyFieldsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_MANY_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + SLEEP_FIELDS_GUIDANCE; + } + + public static String getFileSleepTooFewFieldsMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return TOO_FEW_FIELDS + lineNumber + " of " + filePath + "!\n" + suffix + SLEEP_FIELDS_GUIDANCE; + } + + public static String getFileInvalidDurationMessage(int lineNumber, String filePath) { + String suffix = getLineNotAddedMessage(lineNumber, filePath); + printLine(); + return INVALID_DURATION + lineNumber + " of " + filePath + "!\n" + suffix + POS_FLOAT_GUIDANCE; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 54b1cd1e65..91fc542fb7 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -279,7 +279,6 @@ private static Entry makeNewOutputEntry(int lastEntryID, String description, int * @return a new InputEntry object */ private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date) { - return new InputEntry(lastEntryID, description, calories, date); } @@ -293,10 +292,8 @@ private static Entry makeNewInputEntry(int lastEntryID, String description, int * @return a new InputEntry object with food macros */ private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date, - int[] foodMacros) { - + int[] foodMacros) { Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); - return new InputEntry(lastEntryID, description, calories, date, newFood); } } diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 7d2fbb4bbf..b8dff9631c 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -6,27 +6,15 @@ import java.io.FileWriter; import java.io.IOException; import java.time.LocalDate; -import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Scanner; import seedu.lifetrack.Entry; -import seedu.lifetrack.calories.Food; -import seedu.lifetrack.calories.calorielist.InputEntry; -import seedu.lifetrack.calories.calorielist.OutputEntry; -import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; -import seedu.lifetrack.sleep.sleeplist.SleepEntry; import seedu.lifetrack.system.exceptions.FileHandlerException; import seedu.lifetrack.user.User; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileCaloriesTooFewFieldsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileDateLaterThanCurrentMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileEmptyDescriptionMessage; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidCaloriesMessage; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileTooFewFieldsMessage; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileTooManyFieldsMessage; -import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; public class FileHandler { @@ -35,9 +23,6 @@ public class FileHandler { protected final int DATE_INDEX = 1; protected final int DESCRIPTION_INDEX = 2; - //sleep list constants - private final int DURATION_INDEX = 3; - //user data constants private final int NAME_INDEX = 0; private final int HEIGHT_INDEX = 1; @@ -60,14 +45,6 @@ public FileHandler(String filePath) { this.filePath = filePath; } - protected void checkCorrectNumberOfFields(int lineNumber, int dataLength) throws FileHandlerException { - if (dataLength < 4) { - throw new FileHandlerException(getFileTooFewFieldsMessage(lineNumber, filePath)); - } else if (dataLength > 4) { - throw new FileHandlerException(getFileTooManyFieldsMessage(lineNumber, filePath)); - } - } - protected void checkDateNotLaterThanCurrent(int lineNumber, LocalDate date) throws FileHandlerException { if (date.isAfter(LocalDate.now())) { throw new FileHandlerException(getFileDateLaterThanCurrentMessage(lineNumber, filePath)); @@ -105,23 +82,6 @@ public void writeEntries(ArrayList entries) { System.out.println(message); } } - - public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { - File f = new File(filePath); - Scanner s = new Scanner(f); - ArrayList entries = new ArrayList<>(); - String line = ""; - while (s.hasNext()) { - line = s.nextLine(); - String[] words = line.split(";"); - int lastSleepEntryID = Integer.parseInt(words[ENTRYID_INDEX]); - LocalDate date = LocalDate.parse(words[DATE_INDEX]); - double duration = Double.parseDouble(words[DURATION_INDEX]); - entries.add(new SleepEntry(lastSleepEntryID, duration, date)); - } - s.close(); - return entries; - } public ArrayList getUserDataFromFile() throws FileNotFoundException { File f = new File(filePath); diff --git a/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java index b4bdf03964..6140d8980b 100644 --- a/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java @@ -13,6 +13,8 @@ import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidVolumeMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileHydrationTooFewFieldsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileHydrationTooManyFieldsMessage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; public class HydrationFileHandler extends FileHandler { @@ -33,6 +35,14 @@ private void calculateMaxHydrationEntry(int entryID) { } } + private void checkCorrectNumberOfFields(int lineNumber, int dataLength) throws FileHandlerException { + if (dataLength < 4) { + throw new FileHandlerException(getFileHydrationTooFewFieldsMessage(lineNumber, filePath)); + } else if (dataLength > 4) { + throw new FileHandlerException(getFileHydrationTooManyFieldsMessage(lineNumber, filePath)); + } + } + private void checkVolumeIsPositive(int lineNumber, int volume) throws FileHandlerException { if (volume <= 0) { throw new FileHandlerException(getFileInvalidVolumeMessage(lineNumber, filePath)); diff --git a/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java new file mode 100644 index 0000000000..9964e6bf18 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java @@ -0,0 +1,77 @@ +package seedu.lifetrack.system.storage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Scanner; + +import seedu.lifetrack.Entry; +import seedu.lifetrack.sleep.sleeplist.SleepEntry; +import seedu.lifetrack.system.exceptions.FileHandlerException; + +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidEntryIDMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileSleepTooFewFieldsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileSleepTooManyFieldsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDateMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidDurationMessage; + +public class SleepFileHandler extends FileHandler { + + //sleep list constants + private final int DURATION_INDEX = 2; + + public SleepFileHandler(String filePath) { + super(filePath); + } + + private void checkCorrectNumberOfFields(int lineNumber, int dataLength) throws FileHandlerException { + if (dataLength < 3) { + throw new FileHandlerException(getFileSleepTooFewFieldsMessage(lineNumber, filePath)); + } else if (dataLength > 3) { + throw new FileHandlerException(getFileSleepTooManyFieldsMessage(lineNumber, filePath)); + } + } + + private void checkDurationIsPositive(int lineNumber, double duration) throws FileHandlerException { + if (duration <= 0) { + throw new FileHandlerException(getFileInvalidDurationMessage(lineNumber, filePath)); + } + } + + public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList entries = new ArrayList<>(); + String line = ""; + int i = 1; + while (s.hasNext()) { + line = s.nextLine(); + String[] words = line.split(";"); + try { + checkCorrectNumberOfFields(i, words.length); + int lastSleepEntryID = Integer.parseInt(words[ENTRYID_INDEX]); + LocalDate date = LocalDate.parse(words[DATE_INDEX]); + checkDateNotLaterThanCurrent(i, date); + double duration = Double.parseDouble(words[DURATION_INDEX]); + checkDurationIsPositive(i, duration); + entries.add(new SleepEntry(lastSleepEntryID, duration, date)); + } catch (NumberFormatException e) { + if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { + System.out.println(getFileInvalidEntryIDMessage(i, filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[DURATION_INDEX] + "\"")) { + System.out.println(getFileInvalidDurationMessage(i, filePath)); + } + } catch (DateTimeParseException e) { + System.out.println(getFileInvalidDateMessage(i, filePath)); + } catch (FileHandlerException e) { + System.out.println(e.getMessage()); + } finally { + i++; + } + } + s.close(); + return entries; + } +} diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index deee230317..ec983311d9 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -16,7 +16,7 @@ public void testDeleteSleepValidIndex(){ sleepList.addSleep("sleep add 8 d/2024-12-10"); sleepList.printSleepList(); int initialSize = sleepList.getSize(); - sleepList.deleteSleep("sleep delete "+sleepList.getSleep(0).getLastEntryID()); + sleepList.deleteSleep("sleep delete "+sleepList.getSleep(0).getEntryID()); assertEquals(initialSize - 1, sleepList.getSize()); } @Test From a862a2746313083dece79b607cdc6dc735f1d70e Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 02:34:47 +0800 Subject: [PATCH 315/414] update DG with new filehandler classes --- docs/DeveloperGuide.md | 26 +++++++++++++------------- docs/assets/calories.png | Bin 13293 -> 13855 bytes docs/assets/caloriesComponent.png | Bin 45288 -> 0 bytes docs/assets/calories_component.png | Bin 0 -> 45212 bytes docs/assets/hydration.png | Bin 11783 -> 12669 bytes docs/assets/sleep.png | Bin 11235 -> 11606 bytes docs/diagrams/calories.puml | 4 ++-- docs/diagrams/calories_component.puml | 8 ++++---- docs/diagrams/hydration.puml | 4 ++-- docs/diagrams/sleep.puml | 4 ++-- 10 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 docs/assets/caloriesComponent.png create mode 100644 docs/assets/calories_component.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index e91ce54e37..ca4169bd2c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -30,14 +30,14 @@ ## Design ### calories component -Here's a (partial) class diagram of the `sleep` component. +Here's a (partial) class diagram of the `calories` component. ![calories.png](assets%2Fcalories.png) The calories component consists of the following Classes 1. `Ui` : Handles user and program interaction. 2. `CalorieList` : Handles the list of calories entries. -3. `FileHandler` : Handles the saving and reading of data from file. +3. `CaloriesFileHandler` : Handles the saving and reading of data from file. 4. `ParserCalories` : Handles the parsing of user input to determine type of command. 5. `Entry` : Handles calories entries data. 6. `OutputEntry` : Handles calories output entries data. @@ -46,7 +46,7 @@ The calories component consists of the following Classes The sequence diagram bellow illustrates the interactions within the `calories` component, taking `calories in donut c/1000 d/2024-04-10` call as an example. -![caloriesComponent.png](assets%2FcaloriesComponent.png) +![calories_component.png](assets%2Fcalories_component.png) How the `calories` component works: 1. When the user keys in the `calories in donut c/1000 d/2024-04-10` command, @@ -63,8 +63,8 @@ to the caller, `CalorieList#addEntry(String)` which was called in step 2. 4. The returned `InputEntry` object is added into the `calorieArrayList` member of type `ArrayList` in the `CalorieList`, via the `ArrayList.add()` method. -5. `CalorieList#UpdateFile()` is then called, which calls `FileHandler#writeEntries(ArrayList)`. -Within that function, `FileHandler#writeToFile(String)` function is called, which writes the new data +5. `CalorieList#UpdateFile()` is then called, which calls `CaloriesFileHandler#writeEntries(ArrayList)`. +Within that function, `CaloriesFileHandler#writeToFile(String)` function is called, which writes the new data into the data file. 6. If the dates of entries are not sorted in ascending order, `CalorieList#sortEntriesByDate()` @@ -76,13 +76,13 @@ function is called, which sorts the entries in ascending order. ### sleep component -Here's a (partial) class diagram of the `calories` component. +Here's a (partial) class diagram of the `sleep` component. ![sleep.png](assets%2Fsleep.png) The sleep component consists of the following classes: 1. `Ui`: Handles user and program interaction. 2. `SleepList`: Handles the list of sleep records. -3. `FileHandler`: Handles the saving and reading of data from the file. +3. `SleepFileHandler`: Handles the saving and reading of data from the file. 4. `ParserSleep`: Handles the parsing of user input to determine type of command. 5. `Entry`: Handles all entries data. 6. `SleepEntry`: Handles sleep entries data. @@ -105,8 +105,8 @@ How the `sleep` component works: 4. The returned `SleepEntry` object is added into the `sleepList` member of type `ArrayList` in the `SleepList`, via the `ArrayList.add()` method. -5. `SleepList#UpdateFile()` is then called, which calls `FileHandler#writeEntries(ArrayList)`. - Within that function, `FileHandler#writeToFile(String)` function is called, which writes the new data +5. `SleepList#UpdateFile()` is then called, which calls `SleepFileHandler#writeEntries(ArrayList)`. + Within that function, `SleepFileHandler#writeToFile(String)` function is called, which writes the new data into the data file. @@ -118,11 +118,11 @@ How the `sleep` component works: #### Implementation -This functionality is facilitated by `UI`, `CalorieList`, `FileHandler` and `ParserCalories`. It implements one operation, namely: +This functionality is facilitated by `UI`, `CalorieList`, and `ParserCalories`. It implements one operation, namely: - `UI#handleCaloriesInput(String, CalorieList)` - `CalorieList#addEntry(String)` +- `CalorieList#updateFile()` - `ParserCalories#parseCaloriesInput(String)` -- `FileHandler#updateFile()` This feature is activated when the user inputs a `calories in` or `calories out` command in the terminal. @@ -137,11 +137,11 @@ the string is sent to `UI#handleCaloriesInput(String, CalorieList)`, which calls - Step 4: The returned `Entry` object is added into the `calorieArrayList` member of type `ArrayList` in the `CalorieList`, via the `ArrayList.add()` method. -- Step 5: `FileHandler#updateFile()` is then called to update the data file with the new entry in the `CalorieList`. +- Step 5: `CalorieList#updateFile()` is then called to update the data file with the new entry in the `CalorieList`. The sequence diagram for this feature is shown below: -![CaloriesAddEntrySeqDiagram](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/rexyyong/tp/RexDG/docs/CaloriesAddEntrySeqDiagram.puml) +![CaloriesAddEntrySeqDiagram.png](assets%2FCaloriesAddEntrySeqDiagram.png) ### Calculating calorie requirements based on a user`s goals diff --git a/docs/assets/calories.png b/docs/assets/calories.png index 61616ea0f59069e75cd9dc119e798acfd2f73b4b..dbaa747dc39fe1e9f158a7d88a2c0f36f6e239ae 100644 GIT binary patch literal 13855 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 xz}sKPNvkP)cPZ?Q*?#QY{{hpEMWFxy diff --git a/docs/assets/calories_component.png b/docs/assets/calories_component.png new file mode 100644 index 0000000000000000000000000000000000000000..58607f0a4dd8e5f0d448306520e095d68b8980fe GIT binary patch literal 45212 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 z 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 diff --git a/docs/diagrams/calories.puml b/docs/diagrams/calories.puml index 06c6322b4d..fd9f4fd9cd 100644 --- a/docs/diagrams/calories.puml +++ b/docs/diagrams/calories.puml @@ -6,12 +6,12 @@ rectangle OutputEntry rectangle Food rectangle Entry -rectangle FileHandler +rectangle CaloriesFileHandler rectangle Ui rectangle ParserCalories CalorieList -> "*" Entry -CalorieList --> "1" FileHandler +CalorieList --> "1" CaloriesFileHandler CalorieList --[dotted]> ParserCalories Ui -[dotted]> CalorieList diff --git a/docs/diagrams/calories_component.puml b/docs/diagrams/calories_component.puml index 12528ea9e8..18bc19e48b 100644 --- a/docs/diagrams/calories_component.puml +++ b/docs/diagrams/calories_component.puml @@ -25,10 +25,10 @@ return CalorieList -> CalorieList: updateFile() activate CalorieList -CalorieList -> FileHandler : writeEntries(ArrayList) -activate FileHandler -FileHandler -> FileHandler : writeToFile(String) -activate FileHandler +CalorieList -> CaloriesFileHandler : writeEntries(ArrayList) +activate CaloriesFileHandler +CaloriesFileHandler -> CaloriesFileHandler : writeToFile(String) +activate CaloriesFileHandler return return diff --git a/docs/diagrams/hydration.puml b/docs/diagrams/hydration.puml index 68a74558ab..4832ec0395 100644 --- a/docs/diagrams/hydration.puml +++ b/docs/diagrams/hydration.puml @@ -4,12 +4,12 @@ rectangle HydrationList rectangle HydrationEntry rectangle Entry -rectangle FileHandler +rectangle HydrationFileHandler rectangle Ui rectangle ParserHydration HydrationList --> "*" Entry -HydrationList ---> "1" FileHandler +HydrationList ---> "1" HydrationFileHandler HydrationList --[dotted]> ParserHydration Ui -[dotted]> HydrationList diff --git a/docs/diagrams/sleep.puml b/docs/diagrams/sleep.puml index 0b7eb8b91c..5f84d4331a 100644 --- a/docs/diagrams/sleep.puml +++ b/docs/diagrams/sleep.puml @@ -4,12 +4,12 @@ rectangle SleepList rectangle SleepEntry rectangle Entry -rectangle FileHandler +rectangle SleepFileHandler rectangle Ui rectangle ParserSleep SleepList --> "*" Entry -SleepList ---> "1" FileHandler +SleepList ---> "1" SleepFileHandler SleepList --[dotted]> ParserSleep Ui -[dotted]> SleepList From 9b720410c86994a592d4643d33c430348e8076a8 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 03:09:55 +0800 Subject: [PATCH 316/414] cleared bugs from internal review --- .../lifetrack/sleep/sleeplist/SleepList.java | 4 +-- .../InvalidInputExceptionMessage.java | 12 +++++-- .../lifetrack/system/parser/ParserSleep.java | 8 ++++- .../lifetrack/system/parser/ParserUser.java | 5 ++- src/main/java/seedu/lifetrack/ui/Ui.java | 36 +++++++++---------- src/main/java/seedu/lifetrack/ui/UserUi.java | 13 +++---- .../lifetrack/user/usergoals/UserGoals.java | 14 ++++---- 7 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index e90bfb7110..22029fa71d 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -99,7 +99,7 @@ private int loadLastEntryID() { return 0; // Default value if file doesn't exist or error occurs } - public int getSleepConsumed(LocalDate date) { + public double getSleepConsumed(LocalDate date) { double totalSleep = 0; for (Entry entry : sleepList) { if (entry.getDate().isEqual(date)) { @@ -107,7 +107,7 @@ public int getSleepConsumed(LocalDate date) { totalSleep += tempEntry.getDuration(); } } - return (int) totalSleep; + return totalSleep; } } //@@author diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 0622215807..88fd134d63 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -121,12 +121,16 @@ public static String getTooLongSleepDurationMessage() { //user related messages public static String getOutOfGoalRangeMessage() { - return "\t Please key in a number between 1 and 5! 1 being quick fat loss " + + return "\t Invalid Goal input!\n " + + "Please key in a number between 1 and 5!\n " + + "1 being quick fat loss " + "and 5 being quick bulking"; } public static String getOutOfExerciseLevelsRangeMessage() { - return "\t Please key in a number between 1 and 5! 1 being little exercise done per week and 5 being" + + return "\t Invalid Exercise Level input!\n" + + "\t Please key in a number between 1 and 5!\n" + + " 1 being little exercise done per week and 5 being" + " very heavy levels of exercise done per week."; } @@ -197,4 +201,8 @@ public static String getEmptyNameInputMessage() { public static String getEmptyUserUpdateFieldMessage(){ return "\t Please include the value you would like to update the field to as such:\n" + USER_UPDATE_FIELDS; } + + public static String getReachedMaximumSleepMessage(){ + return "\t Invalid input! The total duration you have slept on this day exceeds 24 hours!"; + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java index dfd118b566..365b1229b6 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserSleep.java @@ -9,7 +9,9 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import static seedu.lifetrack.LifeTrack.sleepList; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectSleepInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getReachedMaximumSleepMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getTooLongSleepDurationMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getSleepMissingKeywordMessage; @@ -20,6 +22,7 @@ public class ParserSleep { private static final int STRING_PARTS_LEN = 2; private static final String SLEEP_HEADER = "sleep add "; private static final String DATE_ICON = "d/"; + private static final int MAX_SLEEP_PER_DAY = 24; public static SleepEntry parseSleepInput(String input) throws InvalidInputException { @@ -39,7 +42,10 @@ public static SleepEntry parseSleepInput(String input) throws InvalidInputExcept duration = parseDuration(strDuration); date = parseDate(strDate); if (date.isAfter(LocalDate.now())) { - throw new InvalidInputException("The date cannot be in the future. Please enter a valid date."); + throw new InvalidInputException("\t The date cannot be in the future. Please enter a valid date."); + } + if (sleepList.getSleepConsumed(date) + duration > MAX_SLEEP_PER_DAY){ + throw new InvalidInputException(getReachedMaximumSleepMessage()); } SleepEntry newSleep = new SleepEntry(duration, date); diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 8b2e4cd677..48205ba210 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -22,7 +22,6 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnknownUpdateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserUpdateInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; @@ -341,8 +340,8 @@ public static void parseUpdate(String input, User user) throws InvalidInputExcep * @throws InvalidInputException if the command is empty */ private static void checkEmptyUpdateInput(String input) throws InvalidInputException { - if (input.substring(LENGTH_OF_UPDATE_COMMAND).trim().isEmpty()) { - throw new InvalidInputException(getEmptyUserUpdateInputMessage()); + if (input.substring(LENGTH_OF_UPDATE_COMMAND).trim().isBlank()) { + throw new InvalidInputException(getEmptyUserUpdateFieldMessage()); } } diff --git a/src/main/java/seedu/lifetrack/ui/Ui.java b/src/main/java/seedu/lifetrack/ui/Ui.java index 6ef599724e..e3f6bdd0c8 100644 --- a/src/main/java/seedu/lifetrack/ui/Ui.java +++ b/src/main/java/seedu/lifetrack/ui/Ui.java @@ -56,11 +56,11 @@ public static void readUserInput(CalorieList calorieList, HydrationList hydratio */ public static void handleCaloriesInput(String line, CalorieList calorieList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("calories in") || line.startsWith("calories out")) { + if (line.startsWith("calories in ") || line.startsWith("calories out ")) { calorieList.addEntry(line); - } else if (line.startsWith("calories list")) { + } else if (line.equals("calories list")) { calorieList.printCalorieList(); - } else if (line.startsWith("calories delete")) { + } else if (line.startsWith("calories delete ")) { calorieList.deleteEntry(line); } else { handleUnknownInput(); @@ -69,11 +69,11 @@ public static void handleCaloriesInput(String line, CalorieList calorieList) { public static void handleHydrationInput(String line, HydrationList hydrationList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("hydration in")) { + if (line.startsWith("hydration in ")) { hydrationList.addEntry(line); - } else if (line.startsWith("hydration list")) { + } else if (line.equals("hydration list")) { hydrationList.printHydrationList(); - } else if (line.startsWith("hydration delete")) { + } else if (line.startsWith("hydration delete ")) { hydrationList.deleteEntry(line); } else { handleUnknownInput(); @@ -84,11 +84,11 @@ public static void handleHydrationInput(String line, HydrationList hydrationList //@@author a-wild-chocolate public static void handleSleepInput(String line, SleepList sleepList) { assert !line.startsWith("bye") : "exit the app"; - if (line.startsWith("sleep add")) { + if (line.startsWith("sleep add ")) { sleepList.addSleep(line); - } else if (line.startsWith("sleep list")) { + } else if (line.equals("sleep list")) { sleepList.printSleepList(); - } else if (line.startsWith("sleep delete")) { + } else if (line.startsWith("sleep delete ")) { sleepList.deleteSleep(line); } else { handleUnknownInput(); @@ -122,9 +122,9 @@ public static void handleUserInput(String line, CalorieList calorieList, Hydrati } public static void handleUserCommands(String line, User user) { - if (line.startsWith("user setup")) { + if (line.startsWith("user setup ")) { user.setUp(line); - } else if (line.startsWith("user progress")) { + } else if (line.equals("user progress")) { handleUserProgress(user); } else if (line.startsWith("user update")) { if (user.getName() == null) { @@ -132,7 +132,7 @@ public static void handleUserCommands(String line, User user) { } else { user.update(line); } - } else if (line.startsWith("user details")) { + } else if (line.equals("user details")) { if (user.getName() == null) { printNoUserYetMessage(); } else { @@ -188,28 +188,28 @@ public static void showHelp() { System.out.println("\t - calories out c/ d/:\n" + "\t Adds a calorie burning entry into the calories tracker."); System.out.println("\t - calories list: Displays all entries currently stored in the calorie list."); - System.out.println("\t - calories delete : Deletes the entry at the specified index" + + System.out.println("\t - calories delete : Deletes the entry at the specified ID" + " from the calorie list."); printLine(); System.out.println("\t - hydration in v/ d/:\n" + "\t Adds a hydration entry into the hydration tracker."); System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list."); - System.out.println("\t - hydration delete : Deletes the hydration entry at the specified index " + + System.out.println("\t - hydration delete : Deletes the hydration entry at the specified ID " + "from the hydration list."); printLine(); System.out.println("\t - sleep add d/: " + "Adds a sleep entry into the sleep tracker."); System.out.println("\t - sleep list: Displays all entries currently stored in the sleep list."); - System.out.println("\t - sleep delete : Deletes the entry at the specified index " + + System.out.println("\t - sleep delete : Deletes the entry at the specified index " + "from the sleep list."); printLine(); System.out.println("\t - user setup h/ w/ a/ s/ e/ " + "g/:\n" + "\t Create a new user, or edit an existing one."); - System.out.println("\t - user progress: Display calories and hydration progress towards the daily " + - "requirement."); + System.out.println("\t - user details: prints the details of the user."); System.out.println("\t - user update name/height/weight/age/sex/exercise levels/goal : " + "updates the corresponding field of the user."); - System.out.println("\t - user details: prints the details of the user."); + System.out.println("\t - user progress: Display calories and hydration progress towards the daily " + + "requirement."); } } //@@author diff --git a/src/main/java/seedu/lifetrack/ui/UserUi.java b/src/main/java/seedu/lifetrack/ui/UserUi.java index adb36b2ec7..1c39092786 100644 --- a/src/main/java/seedu/lifetrack/ui/UserUi.java +++ b/src/main/java/seedu/lifetrack/ui/UserUi.java @@ -47,8 +47,8 @@ public static void printUserHydrationProgress(int hydrationConsumed, int hydrati System.out.printf("\t %s %d%%\n\n", progressBar, percentage); } - public static void printUserSleepProgress(int sleepConsumed, int sleepRequired, String progressBar, - int percentage, int date) { + public static void printUserSleepProgress(double sleepConsumed, int sleepRequired, String progressBar, + int percentage, int date) { String dateInString; if (date == INDEX_OF_TODAY) { System.out.print("\t Sleep:\n"); @@ -59,8 +59,8 @@ public static void printUserSleepProgress(int sleepConsumed, int sleepRequired, } else { dateInString = "on the day before yesterday"; } - System.out.printf("\t You have slept for " + sleepConsumed + "hrs out of your goal of " - + sleepRequired + "hrs " + dateInString + ".\n"); + System.out.print("\t You have slept for " + sleepConsumed + " hrs out of your goal of " + + sleepRequired + " hrs " + dateInString + ".\n"); System.out.printf("\t %s %d%%\n\n", progressBar, percentage); } @@ -106,13 +106,14 @@ public static void printNewUserGoal(User user, int goal) { public static void printUserDetails(User user) { System.out.println("\t User details:\n" + - "\t Name: " + user.getName() + "\n" + "\t Name: " + (user.getName().substring(0, 1).toUpperCase() + user.getName().substring(1)) + + "\n" + "\t Height: " + user.getHeight() + "\n" + "\t Weight: " + user.getWeight() + "\n" + "\t Age: " + user.getAge() + "\n" + "\t Sex: " + user.getSex() + "\n" + "\t Exercise Levels: " + user.getExerciseLevels() + " out of 5 (" + user.getExerciseLevelAsString() + ")" + "\n" - + "\t Goal: " + user.getGoal() + " out of 5 (" + user.getGoalAsString() + ")" + "\n"); + + "\t Goal: " + user.getGoal() + " out of 5 (" + user.getGoalAsString() + ")"); } } diff --git a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java index 2a5a09352d..3c90ffc0b2 100644 --- a/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java +++ b/src/main/java/seedu/lifetrack/user/usergoals/UserGoals.java @@ -169,32 +169,32 @@ public static void getHydrationProgressBar(User user) { public static void getSleepProgressBar(User user) { int sleepRequired = user.getSleepRequired(); - int sleepConsumedToday = sleepList.getSleepConsumed(LocalDate.now()); + double sleepConsumedToday = sleepList.getSleepConsumed(LocalDate.now()); if (sleepConsumedToday < 0) { sleepConsumedToday = 0; } - double progressToday = (double) sleepConsumedToday / sleepRequired; - int sleepConsumedYesterday = sleepList.getSleepConsumed + double progressToday = sleepConsumedToday / sleepRequired; + double sleepConsumedYesterday = sleepList.getSleepConsumed (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_YESTERDAY)); if (sleepConsumedYesterday < 0) { sleepConsumedYesterday = 0; } - double progressYesterday = (double) sleepConsumedYesterday / sleepRequired; + double progressYesterday = sleepConsumedYesterday / sleepRequired; int width = PROGRESS_BAR_WIDTH; - int sleepConsumedDayBefore = sleepList.getSleepConsumed + double sleepConsumedDayBefore = sleepList.getSleepConsumed (LocalDate.now().plusDays(NUMBER_OF_DAYS_TO_DAY_BEFORE)); if (sleepConsumedDayBefore < 0) { sleepConsumedDayBefore = 0; } - double progressDayBefore = (double) sleepConsumedDayBefore / sleepRequired; + double progressDayBefore = sleepConsumedDayBefore / sleepRequired; double[] progress = new double[NUMBER_OF_DAYS_TO_TRACK]; progress[INDEX_OF_TODAY] = progressToday; progress[INDEX_OF_YESTERDAY] = progressYesterday; progress[INDEX_OF_DAY_BEFORE] = progressDayBefore; - int[] sleepConsumed = new int[NUMBER_OF_DAYS_TO_TRACK]; + double[] sleepConsumed = new double[NUMBER_OF_DAYS_TO_TRACK]; sleepConsumed[INDEX_OF_TODAY] = sleepConsumedToday; sleepConsumed[INDEX_OF_YESTERDAY] = sleepConsumedYesterday; sleepConsumed[INDEX_OF_DAY_BEFORE] = sleepConsumedDayBefore; From 2921f8a543c0bb446bc9f0a0d3580a5ae563dad3 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 03:57:54 +0800 Subject: [PATCH 317/414] handle all user related exceptions for data file --- .../FileHandlerExceptionMessage.java | 82 +++++++++- .../lifetrack/system/storage/FileHandler.java | 31 ---- .../system/storage/UserFileHandler.java | 146 ++++++++++++++++++ src/main/java/seedu/lifetrack/user/User.java | 31 ++-- 4 files changed, 242 insertions(+), 48 deletions(-) create mode 100644 src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java index 617b1fb58a..10de8256f5 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -27,9 +27,20 @@ public class FileHandlerExceptionMessage { //hydration list error messages private static final String INVALID_VOLUME = "\t An invalid volume value was given in line "; - //hydration list error messages + //sleep list error messages private static final String INVALID_DURATION = "\t An invalid duration value was given in line "; + //user error messages + private static final String USER_NOT_ADDED = "\t User was not set up due to corrupt data!\n"; + private static final String EMPTY_NAME = "\t An empty name was given in "; + private static final String INVALID_HEIGHT = "\t An invalid height was given in "; + private static final String INVALID_WEIGHT = "\t An invalid weight was given in "; + private static final String INVALID_AGE = "\t An invalid age was given in "; + private static final String INVALID_SEX = "\t An invalid sex was given in "; + private static final String INVALID_EX_LEVELS = "\t An invalid exercise level was given in "; + private static final String INVALID_GOAL = "\t An invalid goal was given in "; + private static final String INVALID_REQ_CAL = "\t An invalid required calories value was given in "; + //messages to provide user guidance (general) private static final String DATE_FORMAT_GUIDANCE = "\t Please ensure that a date in format YYYY-MM-DD is given"; private static final String DATE_PERIOD_GUIDANCE = "\t Please ensure that a date no later than today is given"; @@ -52,17 +63,27 @@ public class FileHandlerExceptionMessage { //messages to provide user guidance (sleep) private static final String SLEEP_FIELDS_GUIDANCE = "\t Please ensure that three fields are provided!"; - + + //messages to provide user guidance (user) + private static final String USER_FIELDS_GUIDANCE = "\t Please ensure that eight fields are provided!"; + private static final String NAME_GUIDANCE = "\t Please ensure that the name field is not empty!"; + private static final String HEIGHT_GUIDANCE = "\t Please ensure that an integer between 90 and 225 (cm) " + + "is provided!"; + private static final String WEIGHT_GUIDANCE = "\t Please ensure that an integer between 30 and 200 (kg) " + + "is provided!"; + private static final String AGE_GUIDANCE = "\t Please ensure that an integer between 13 and 30 (years) " + + "is provided!"; + private static final String SEX_GUIDANCE = "\t Please ensure that the sex field is only either "+ + "\"male\" or \"female\"!"; + private static final String EX_LEVELS_GOALS_GUIDANCE = "\t Please ensure that an integer between 1 to 5 is provided!"; private static String getLineNotAddedMessage(int lineNumber, String filePath) { if (filePath.equals("data/caloriesData.txt")) { return "\t Line " + lineNumber + " was not added into the calories list due to corrupt data!\n"; } else if (filePath.equals("data/hydrationData.txt")) { return "\t Line " + lineNumber + " was not added into the hydration list due to corrupt data!\n"; - } else if (filePath.equals("data/sleepData.txt")){ - return "\t Line " + lineNumber + " was not added into the sleep list due to corrupt data!\n"; } else { - return "\t The user was not set due to corrupt data!\n"; + return "\t Line " + lineNumber + " was not added into the sleep list due to corrupt data!\n"; } } @@ -183,4 +204,55 @@ public static String getFileInvalidDurationMessage(int lineNumber, String filePa printLine(); return INVALID_DURATION + lineNumber + " of " + filePath + "!\n" + suffix + POS_FLOAT_GUIDANCE; } + + //user related messages + public static String getFileUserTooManyFieldsMessage(String filePath) { + printLine(); + return TOO_MANY_FIELDS + " 1 of " + filePath + "!\n" + USER_NOT_ADDED + USER_FIELDS_GUIDANCE; + } + + public static String getFileUserTooFewFieldsMessage(String filePath) { + printLine(); + return TOO_FEW_FIELDS + " 1 of " + filePath + "!\n" + USER_NOT_ADDED + USER_FIELDS_GUIDANCE; + } + + public static String getFileUserEmptyNameMessage(String filePath) { + printLine(); + return EMPTY_NAME + filePath + "!\n" + USER_NOT_ADDED + NAME_GUIDANCE; + } + + public static String getFileInvalidHeightMessage(String filePath) { + printLine(); + return INVALID_HEIGHT + filePath + "!\n" + USER_NOT_ADDED + HEIGHT_GUIDANCE; + } + + public static String getFileInvalidWeightMessage(String filePath) { + printLine(); + return INVALID_WEIGHT + filePath + "!\n" + USER_NOT_ADDED + WEIGHT_GUIDANCE; + } + + public static String getFileInvalidAgeMessage(String filePath) { + printLine(); + return INVALID_AGE + filePath + "!\n" + USER_NOT_ADDED + AGE_GUIDANCE; + } + + public static String getFileInvalidSexMessage(String filePath) { + printLine(); + return INVALID_SEX + filePath + "!\n" + USER_NOT_ADDED + SEX_GUIDANCE; + } + + public static String getFileInvalidExerciseLevelMessage(String filePath) { + printLine(); + return INVALID_EX_LEVELS + filePath + "!\n" + USER_NOT_ADDED + EX_LEVELS_GOALS_GUIDANCE; + } + + public static String getFileInvalidGoalMessage(String filePath) { + printLine(); + return INVALID_GOAL + filePath + "!\n" + USER_NOT_ADDED + EX_LEVELS_GOALS_GUIDANCE; + } + + public static String getFileInvalidReqCalMessage(String filePath) { + printLine(); + return INVALID_REQ_CAL + filePath + "!\n" + USER_NOT_ADDED + POS_INT_GUIDANCE; + } } diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index b8dff9631c..94ba1e9d56 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -1,13 +1,10 @@ //@@author owx0130 package seedu.lifetrack.system.storage; -import java.io.File; -import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.time.LocalDate; import java.util.ArrayList; -import java.util.Scanner; import seedu.lifetrack.Entry; import seedu.lifetrack.system.exceptions.FileHandlerException; @@ -23,16 +20,6 @@ public class FileHandler { protected final int DATE_INDEX = 1; protected final int DESCRIPTION_INDEX = 2; - //user data constants - private final int NAME_INDEX = 0; - private final int HEIGHT_INDEX = 1; - private final int WEIGHT_INDEX = 2; - private final int AGE_INDEX = 3; - private final int SEX_INDEX = 4; - private final int EXERCISE_INDEX = 5; - private final int GOAL_INDEX = 6; - private final int REQ_CAL_INDEX = 7; - //NumberFormatException exception message prefix protected final String NF_EXCEPTION_PREFIX = "For input string: \""; @@ -82,22 +69,4 @@ public void writeEntries(ArrayList entries) { System.out.println(message); } } - - public ArrayList getUserDataFromFile() throws FileNotFoundException { - File f = new File(filePath); - Scanner s = new Scanner(f); - ArrayList data = new ArrayList<>(); - String line = s.nextLine(); - String[] words = line.split(";"); - data.add(words[NAME_INDEX]); - data.add(words[HEIGHT_INDEX]); - data.add(words[WEIGHT_INDEX]); - data.add(words[AGE_INDEX]); - data.add(words[SEX_INDEX]); - data.add(words[EXERCISE_INDEX]); - data.add(words[GOAL_INDEX]); - data.add(words[REQ_CAL_INDEX]); - s.close(); - return data; - } } diff --git a/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java new file mode 100644 index 0000000000..6428c5d0d6 --- /dev/null +++ b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java @@ -0,0 +1,146 @@ +package seedu.lifetrack.system.storage; + +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidAgeMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidExerciseLevelMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidGoalMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidHeightMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidReqCalMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidSexMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidWeightMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileUserEmptyNameMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileUserTooFewFieldsMessage; +import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileUserTooManyFieldsMessage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Scanner; + +import seedu.lifetrack.system.exceptions.FileHandlerException; +import seedu.lifetrack.user.User; + +public class UserFileHandler extends FileHandler { + + //user data constants + private final int NAME_INDEX = 0; + private final int HEIGHT_INDEX = 1; + private final int WEIGHT_INDEX = 2; + private final int AGE_INDEX = 3; + private final int SEX_INDEX = 4; + private final int EXERCISE_LEVEL_INDEX = 5; + private final int GOAL_INDEX = 6; + private final int REQ_CAL_INDEX = 7; + + public UserFileHandler(String filePath) { + super(filePath); + } + + private void checkCorrectNumberOfFields(int dataLength) throws FileHandlerException { + if (dataLength < 8) { + throw new FileHandlerException(getFileUserTooFewFieldsMessage(filePath)); + } else if (dataLength > 8) { + throw new FileHandlerException(getFileUserTooManyFieldsMessage(filePath)); + } + } + + private void checkNonEmptyName(String name) throws FileHandlerException { + if (name.equals("")) { + throw new FileHandlerException(getFileUserEmptyNameMessage(filePath)); + } + } + + private void checkValidHeight(int height) throws FileHandlerException { + if (!(height >= 90 && height <= 225)) { + throw new FileHandlerException(getFileInvalidHeightMessage(filePath)); + } + } + + private void checkValidWeight(int weight) throws FileHandlerException { + if (!(weight >= 30 && weight <= 200)) { + throw new FileHandlerException(getFileInvalidWeightMessage(filePath)); + } + } + + private void checkValidAge(int age) throws FileHandlerException { + if (!(age >= 13 && age <= 30)) { + throw new FileHandlerException(getFileInvalidAgeMessage(filePath)); + } + } + + private void checkValidSex(String sex) throws FileHandlerException { + if (!(sex.equals("male") || sex.equals("female"))) { + throw new FileHandlerException(getFileInvalidSexMessage(filePath)); + } + } + + private void checkValidExerciseLevel(int exerciseLevel) throws FileHandlerException { + if (!(exerciseLevel >= 1 && exerciseLevel <= 5)) { + throw new FileHandlerException(getFileInvalidExerciseLevelMessage(filePath)); + } + } + + private void checkValidGoal(int goal) throws FileHandlerException { + if (!(goal >= 1 && goal <= 5)) { + throw new FileHandlerException(getFileInvalidGoalMessage(filePath)); + } + } + + private void checkReqCalIsPositive(int requiredCals) throws FileHandlerException { + if (requiredCals <= 0) { + throw new FileHandlerException(getFileInvalidReqCalMessage(filePath)); + } + } + + public ArrayList getUserDataFromFile(User user) throws FileNotFoundException { + File f = new File(filePath); + Scanner s = new Scanner(f); + ArrayList data = new ArrayList<>(); + String line = s.nextLine(); + String[] words = line.split(";"); + try { + checkCorrectNumberOfFields(words.length); + String name = words[NAME_INDEX]; + checkNonEmptyName(name); + int height = Integer.parseInt(words[HEIGHT_INDEX]); + checkValidHeight(height); + int weight = Integer.parseInt(words[WEIGHT_INDEX]); + checkValidWeight(weight); + int age = Integer.parseInt(words[AGE_INDEX]); + checkValidAge(age); + String sex = words[SEX_INDEX]; + checkValidSex(sex); + int exerciseLevel = Integer.parseInt(words[EXERCISE_LEVEL_INDEX]); + checkValidExerciseLevel(exerciseLevel); + int goal = Integer.parseInt(words[GOAL_INDEX]); + checkValidGoal(goal); + int requiredCals = Integer.parseInt(words[REQ_CAL_INDEX]); + checkReqCalIsPositive(requiredCals); + data.add(words[NAME_INDEX]); + data.add(words[HEIGHT_INDEX]); + data.add(words[WEIGHT_INDEX]); + data.add(words[AGE_INDEX]); + data.add(words[SEX_INDEX]); + data.add(words[EXERCISE_LEVEL_INDEX]); + data.add(words[GOAL_INDEX]); + data.add(words[REQ_CAL_INDEX]); + } catch (NumberFormatException e) { + if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[HEIGHT_INDEX] + "\"")) { + System.out.println(getFileInvalidHeightMessage(filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[WEIGHT_INDEX] + "\"")) { + System.out.println(getFileInvalidWeightMessage(filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[AGE_INDEX] + "\"")) { + System.out.println(getFileInvalidAgeMessage(filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[EXERCISE_LEVEL_INDEX] + "\"")) { + System.out.println(getFileInvalidExerciseLevelMessage(filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[GOAL_INDEX] + "\"")) { + System.out.println(getFileInvalidGoalMessage(filePath)); + } else if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[REQ_CAL_INDEX] + "\"")) { + System.out.println(getFileInvalidReqCalMessage(filePath)); + } + } catch (FileHandlerException e) { + System.out.println(e.getMessage()); + } + s.close(); + return data; + } +} diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index b871d396c8..9df7bfe8e1 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -2,7 +2,7 @@ package seedu.lifetrack.user; import seedu.lifetrack.system.exceptions.InvalidInputException; -import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.system.storage.UserFileHandler; import seedu.lifetrack.user.usergoals.UserGoals; import java.io.FileNotFoundException; @@ -13,7 +13,7 @@ public class User { - private FileHandler fileHandler; + private UserFileHandler fileHandler; private String name; private int height; private int weight; @@ -44,16 +44,23 @@ public User() { //constructor for usage in terminal public User(String filePath) { try { - fileHandler = new FileHandler(filePath); - ArrayList data = fileHandler.getUserDataFromFile(); - name = data.get(NAME_INDEX); - height = Integer.parseInt(data.get(HEIGHT_INDEX)); - weight = Integer.parseInt(data.get(WEIGHT_INDEX)); - age = Integer.parseInt(data.get(AGE_INDEX)); - sex = data.get(SEX_INDEX); - exerciseLevels = Integer.parseInt(data.get(EXERCISE_INDEX)); - goal = Integer.parseInt(data.get(GOAL_INDEX)); - caloriesRequired = Integer.parseInt(data.get(REQ_CAL_INDEX)); + fileHandler = new UserFileHandler(filePath); + ArrayList data = fileHandler.getUserDataFromFile(this); + if (data.size() == 8) { + name = data.get(NAME_INDEX); + height = Integer.parseInt(data.get(HEIGHT_INDEX)); + weight = Integer.parseInt(data.get(WEIGHT_INDEX)); + age = Integer.parseInt(data.get(AGE_INDEX)); + sex = data.get(SEX_INDEX); + exerciseLevels = Integer.parseInt(data.get(EXERCISE_INDEX)); + goal = Integer.parseInt(data.get(GOAL_INDEX)); + caloriesRequired = Integer.parseInt(data.get(REQ_CAL_INDEX)); + } + System.out.println(name); + System.out.println(height); + System.out.println(weight); + System.out.println(age); + System.out.println(sex); } catch (FileNotFoundException e) { return; } From 77978d41b28d55e311f92cdb564bce797e0e1ff5 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 04:08:37 +0800 Subject: [PATCH 318/414] Added JUnit Test Cases for User Class --- .../lifetrack/system/parser/ParserUser.java | 29 +-- .../java/seedu/lifetrack/ParserUserTest.java | 192 ++++++++++++++++++ 2 files changed, 207 insertions(+), 14 deletions(-) create mode 100644 src/test/java/seedu/lifetrack/ParserUserTest.java diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index 48205ba210..cd5e01e3f6 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -9,6 +9,7 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getAgeOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyGenderInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyNameInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserUpdateFieldMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHeightOutOfRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidAgeNumberMessage; @@ -18,13 +19,11 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidHeightNumberMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidNumberOfSetUpInputs; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidWeightNumberMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnderAgeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getUnknownUpdateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWeightOutOfRangeMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyUserSetupInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfExerciseLevelsRangeMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getOutOfGoalRangeMessage; - import static seedu.lifetrack.ui.UserUi.printNewUserAge; import static seedu.lifetrack.ui.UserUi.printNewUserExerciseLevels; import static seedu.lifetrack.ui.UserUi.printNewUserGoal; @@ -271,12 +270,12 @@ private static void checkEmptyInput(String input) throws InvalidInputException { /** * Parses "user update" command to update the relevant field of the user. Recalculates the user's calorific goal as - * well. + * well. * * @param input input from the user - * @param user current User + * @param user current User * @throws InvalidInputException if the command is empty, the update field is empty, if the field given to update - * is unknown or if the value to update is not correct. + * is unknown or if the value to update is not correct. */ public static void parseUpdate(String input, User user) throws InvalidInputException { checkEmptyUpdateInput(input); @@ -290,7 +289,7 @@ public static void parseUpdate(String input, User user) throws InvalidInputExcep } else if (fieldToUpdate.startsWith("height ")) { int height = parseHeightIndex(fieldToUpdate.substring(LENGTH_OF_HEIGHT).trim()); user.setHeight(height); - assert Objects.equals(user.getHeight(),height); + assert Objects.equals(user.getHeight(), height); printNewUserHeight(height); user.getHealthInfo(); printUserCaloriesRequired(user.getCaloriesRequired()); @@ -304,28 +303,28 @@ public static void parseUpdate(String input, User user) throws InvalidInputExcep } else if (fieldToUpdate.startsWith("age ")) { int age = parseAgeIndex(fieldToUpdate.substring(LENGTH_OF_AGE).trim()); user.setAge(age); - assert Objects.equals(user.getAge(),age); + assert Objects.equals(user.getAge(), age); printNewUserAge(age); user.getHealthInfo(); printUserCaloriesRequired(user.getCaloriesRequired()); } else if (fieldToUpdate.startsWith("exercise levels ")) { int level = parseExerciseLevels(fieldToUpdate.substring(LENGTH_OF_EXERCISE_LEVELS).trim()); user.setExerciseLevels(level); - assert Objects.equals(user.getExerciseLevels(),level); - printNewUserExerciseLevels(user , level); + assert Objects.equals(user.getExerciseLevels(), level); + printNewUserExerciseLevels(user, level); user.getHealthInfo(); printUserCaloriesRequired(user.getCaloriesRequired()); - } else if (fieldToUpdate.startsWith("goal ")){ + } else if (fieldToUpdate.startsWith("goal ")) { int goal = parseGoalIndex(fieldToUpdate.substring(LENGTH_OF_GOAL).trim()); user.setGoal(goal); - assert Objects.equals(user.getGoal(),goal); + assert Objects.equals(user.getGoal(), goal); printNewUserGoal(user, goal); user.getHealthInfo(); printUserCaloriesRequired(user.getCaloriesRequired()); } else if (fieldToUpdate.startsWith("sex ")) { String sex = parseGenderIndex(fieldToUpdate.substring(LENGTH_OF_SEX).trim()); user.setSex(sex); - assert Objects.equals(user.getSex(),sex); + assert Objects.equals(user.getSex(), sex); printNewUserSex(sex); user.getHealthInfo(); printUserCaloriesRequired(user.getCaloriesRequired()); @@ -336,6 +335,7 @@ public static void parseUpdate(String input, User user) throws InvalidInputExcep /** * Checks if the "user update" command is empty + * * @param input input from the user * @throws InvalidInputException if the command is empty */ @@ -347,6 +347,7 @@ private static void checkEmptyUpdateInput(String input) throws InvalidInputExcep /** * Checks if the field to update is empty + * * @param input input from the user * @throws InvalidInputException if the field is empty */ diff --git a/src/test/java/seedu/lifetrack/ParserUserTest.java b/src/test/java/seedu/lifetrack/ParserUserTest.java new file mode 100644 index 0000000000..c8c081acbe --- /dev/null +++ b/src/test/java/seedu/lifetrack/ParserUserTest.java @@ -0,0 +1,192 @@ +package seedu.lifetrack; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.parser.ParserUser; +import seedu.lifetrack.user.User; + +public class ParserUserTest { + + @Test + public void parseUserSetUp_validInput_success() throws InvalidInputException { + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; + User user = new User(); + ParserUser.parseSetUp(input, user); + Assertions.assertEquals("karthik", user.getName()); + Assertions.assertEquals(175, user.getHeight()); + Assertions.assertEquals(70, user.getWeight()); + Assertions.assertEquals(25, user.getAge()); + Assertions.assertEquals("male", user.getSex()); + Assertions.assertEquals(3, user.getExerciseLevels()); + Assertions.assertEquals(2, user.getGoal()); + } + + @Test + public void parseUserSetUp_EmptyInput_throwsException() { + String input = "user setup"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_InCorrectOrder_throwsException() { + String input = "user setup karthik w/175 h/70 a/25 s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_InvalidNumberOfInputs_throwException() { + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2 h/140"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_missingName_throwsException() { + String input = "user setup h/175 w/70 a/25 s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_missingHeight_throwsException() { + String input = "user setup karthik h/ w/70 a/25 s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_missingWeight_throwsException() { + String input = "user setup karthik h/175 w/ a/25 s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_missingAge_throwsException() { + String input = "user setup karthik h/175 w/70 a/ s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_missingSex_throwsException() { + String input = "user setup karthik h/175 w/70 a/25 s/ e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_missingExerciseLevels_throwsException() { + String input = "user setup karthik h/175 w/70 a/25 s/male e/ g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_missingGoal_throwsException() { + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_invalidHeight_throwsInvalidInputException() { + String input = "user setup karthik h/80 w/70 a/25 s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_invalidWeight_throwsInvalidInputException() { + String input = "user setup karthik h/175 w/20 a/25 s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_invalidAge_throwsInvalidInputException() { + String input = "user setup karthik h/175 w/70 a/10 s/male e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_invalidGender_throwsInvalidInputException() { + String input = "user setup karthik h/175 w/70 a/25 s/non-binary e/3 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_invalidExerciseLevels_throwsInvalidInputException() { + String input = "user setup karthik h/175 w/70 a/25 s/male e/6 g/2"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserSetUp_invalidGoal_throwsInvalidInputException() { + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/7"; + User user = new User(); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); + } + + @Test + public void parseUserUpdate_ValidNameSetUp() throws InvalidInputException { + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; + User user = new User(); + ParserUser.parseSetUp(input, user); + String name = "user update name John"; + String height = "user update height 174"; + String weight = "user update weight 90"; + String age = "user update age 18"; + String sex = "user update sex female"; + String exerciseLevel = "user update exercise levels 2"; + String goal = "user update goal 1"; + ParserUser.parseUpdate(name, user); + ParserUser.parseUpdate(height, user); + ParserUser.parseUpdate(weight, user); + ParserUser.parseUpdate(age, user); + ParserUser.parseUpdate(sex, user); + ParserUser.parseUpdate(exerciseLevel, user); + ParserUser.parseUpdate(goal, user); + Assertions.assertEquals("John", user.getName()); + Assertions.assertEquals(174, user.getHeight()); + Assertions.assertEquals(90, user.getWeight()); + Assertions.assertEquals(18, user.getAge()); + Assertions.assertEquals("female", user.getSex()); + Assertions.assertEquals(2, user.getExerciseLevels()); + Assertions.assertEquals(1, user.getGoal()); + } + + @Test + public void parseUserUpdate_EmptyCommand_throwsException() throws InvalidInputException { + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; + User user = new User(); + ParserUser.parseSetUp(input, user); + String test = "user update"; + Assertions.assertThrows(InvalidInputException.class,()->ParserUser.parseUpdate(test, user)); + } + + @Test + public void parseUserUpdate_EmptyField_throwsException() throws InvalidInputException{ + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; + User user = new User(); + ParserUser.parseSetUp(input, user); + String test = "user update name"; + Assertions.assertThrows(InvalidInputException.class,()->ParserUser.parseUpdate(test, user)); + } + + @Test + public void parseUserUpdate_UnknownInput_throwsException() throws InvalidInputException{ + String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; + User user = new User(); + ParserUser.parseSetUp(input, user); + String test = "user update abcd abcd"; + Assertions.assertThrows(InvalidInputException.class,()->ParserUser.parseUpdate(test, user)); + } + +} From 9494fca6cc63c500dcce8c6f99d8fc473c2f0863 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 04:12:12 +0800 Subject: [PATCH 319/414] build error --- .../java/seedu/lifetrack/ParserUserTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/seedu/lifetrack/ParserUserTest.java b/src/test/java/seedu/lifetrack/ParserUserTest.java index c8c081acbe..ed69e180fb 100644 --- a/src/test/java/seedu/lifetrack/ParserUserTest.java +++ b/src/test/java/seedu/lifetrack/ParserUserTest.java @@ -23,21 +23,21 @@ public void parseUserSetUp_validInput_success() throws InvalidInputException { } @Test - public void parseUserSetUp_EmptyInput_throwsException() { + public void parseUserSetUp_emptyInput_throwsException() { String input = "user setup"; User user = new User(); Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); } @Test - public void parseUserSetUp_InCorrectOrder_throwsException() { + public void parseUserSetUp_inCorrectOrder_throwsException() { String input = "user setup karthik w/175 h/70 a/25 s/male e/3 g/2"; User user = new User(); Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); } @Test - public void parseUserSetUp_InvalidNumberOfInputs_throwException() { + public void parseUserSetUp_invalidNumberOfInputs_throwException() { String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2 h/140"; User user = new User(); Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseSetUp(input, user)); @@ -135,7 +135,7 @@ public void parseUserSetUp_invalidGoal_throwsInvalidInputException() { } @Test - public void parseUserUpdate_ValidNameSetUp() throws InvalidInputException { + public void parseUserUpdate_validNameSetUp() throws InvalidInputException { String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; User user = new User(); ParserUser.parseSetUp(input, user); @@ -163,30 +163,30 @@ public void parseUserUpdate_ValidNameSetUp() throws InvalidInputException { } @Test - public void parseUserUpdate_EmptyCommand_throwsException() throws InvalidInputException { + public void parseUserUpdate_emptyCommand_throwsException() throws InvalidInputException { String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; User user = new User(); ParserUser.parseSetUp(input, user); String test = "user update"; - Assertions.assertThrows(InvalidInputException.class,()->ParserUser.parseUpdate(test, user)); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseUpdate(test, user)); } @Test - public void parseUserUpdate_EmptyField_throwsException() throws InvalidInputException{ + public void parseUserUpdate_emptyField_throwsException() throws InvalidInputException { String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; User user = new User(); ParserUser.parseSetUp(input, user); String test = "user update name"; - Assertions.assertThrows(InvalidInputException.class,()->ParserUser.parseUpdate(test, user)); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseUpdate(test, user)); } @Test - public void parseUserUpdate_UnknownInput_throwsException() throws InvalidInputException{ + public void parseUserUpdate_unknownInput_throwsException() throws InvalidInputException { String input = "user setup karthik h/175 w/70 a/25 s/male e/3 g/2"; User user = new User(); ParserUser.parseSetUp(input, user); String test = "user update abcd abcd"; - Assertions.assertThrows(InvalidInputException.class,()->ParserUser.parseUpdate(test, user)); + Assertions.assertThrows(InvalidInputException.class, () -> ParserUser.parseUpdate(test, user)); } } From 87d015748d16aa7657d7a39e28c1facc60779455 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 04:13:04 +0800 Subject: [PATCH 320/414] add authorship --- .../java/seedu/lifetrack/system/storage/CaloriesFileHandler.java | 1 + .../seedu/lifetrack/system/storage/HydrationFileHandler.java | 1 + .../java/seedu/lifetrack/system/storage/SleepFileHandler.java | 1 + .../java/seedu/lifetrack/system/storage/UserFileHandler.java | 1 + src/test/java/seedu/lifetrack/ParserCaloriesTest.java | 1 + 5 files changed, 5 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java index 28b8228be6..1738f0637e 100644 --- a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.storage; import java.io.File; diff --git a/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java index 6140d8980b..3476a48fc5 100644 --- a/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.storage; import java.io.File; diff --git a/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java index 9964e6bf18..d19c4fb76e 100644 --- a/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.storage; import java.io.File; diff --git a/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java index 6428c5d0d6..5c8dce0de7 100644 --- a/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack.system.storage; import static seedu.lifetrack.system.exceptions.FileHandlerExceptionMessage.getFileInvalidAgeMessage; diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index 3e27f0c024..a9e65f09c5 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -1,3 +1,4 @@ +//@@author owx0130 package seedu.lifetrack; import org.junit.jupiter.api.Test; From 87e31be3ae00823164c4eb9e8dabb1c98ec150c3 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 04:50:53 +0800 Subject: [PATCH 321/414] add coming soon and FAQ to UG, add target user and value proposition in DG --- docs/DeveloperGuide.md | 4 ++-- docs/UserGuide.md | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index ca4169bd2c..c95ea8536b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -386,11 +386,11 @@ The Sequence diagram for Sleep delete feature is shown below: ## Product scope ### Target user profile -{Describe the target user profile} +Our target user profile consists of Year 2 NUS Computer Engineering (CEG) students. ### Value proposition -{Describe the value proposition: what problem does it solve?} +It is no secret that Year 2 is the busiest/most difficult period that CEG students will experience in university. As such, it may be easy for students to neglect their health in the midst of the hustle and bustle. We hope that through this application, tracking one's health can be made easy and straightforward, so that students can get their health info quickly on the go, and thus know whether they need to eat/drink/sleep more/less. ## User Stories diff --git a/docs/UserGuide.md b/docs/UserGuide.md index b7a59fbfce..93251d87cf 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -268,31 +268,31 @@ If you have not set your user up beforehand, this command will prompt you to do **A**: In the same directory as where the JAR file is located, the application will automatically create a `/data` directory which stores all the data files required for the application. Simply copy the entire directory and its contents to your new computer and ensure that it is in the same directory as your JAR file, then run the application as per normal. +**Q**: How do I manually edit the data files in the correct format? + +**A**: For the calories data file, the format is as such: `ENTRY_ID;DATE;DESCRIPTION;ENTRY_TYPE;CALORIES;CARBOHYDRATES;PROTEINS;FATS`. Take note that `ENTRY_TYPE` must be `C_IN` for the application to accept additional macronutrient fields after the `CALORIES` field. For the hydration data file, the format is as such: `ENTRY_ID;DATE;DESCRIPTION;VOLUME`. For the sleep data file, the format is as such: `ENTRY_ID;DATE;DURATION`. The delimiter used in the data files must be a semicolon (;). + **Q**: Why must I input integers for my calories when it is a continuous variable? **A**: Although calories is technically a continuous variable, we chose to only take in integer inputs in our application as the difference is just not that significant, i.e. users can just round up values that have decimal values of 0.5 and above, and round down any values below that. An average human will have calorie intake in the thousands daily, thus such a small inaccuracy is insignificant in comparison. An `int` is also much easier to work with than `float`, which is why we chose to only use the former. ## Coming soon -### Calorie lists to show calorie intakes and outflow separately - -When the user inputs `calorie list` into the terminal, they should be able to see the calorie lists for their calorie intakes and outflow separately. - -This upcoming feature will be implemented by allowing the `CalorieList` class to have two `ArrayList` members that store the calorie inflows and outflows separately. +### Undo/Redo feature -### Calorie and hydration progress to be calculated based on date +An undo/redo feature will be a nice quality of life boost for the app, as it provides users with the freedom to navigate their actions with confidence. Mistakes are inevitable, whether it's unintentional deletions, accidental changes, or simply exploring different options. This feature will help to enhance user experience by instilling a sense of control and reducing anxiety about irreversible actions. -The current implementation of the calorie and hydration progress calculates the calories/hydration consumed based on the total number of entries in the list, irregardless of the date. +### Automatic calculation of calories burnt -We want to be able to provide accurate representations of users meeting their daily calorie and hydration needs, so this feature will be updated very soon to accommodate that. +Calories burnt from any exercise can be calculated from online calculators depending on the user's body proportions. If such calculators were implemented within the application itself, the calories burnt can be made even more accurate, and also improve user experience as they do not have to manually calculate and key in the values now. -### Reduce reliance on network features +### Implement daily requirements for macronutrients intake -As of current, calorie requirements for users is calculated from an external API. However, to reduce the reliance on having a strong network connection for our application to work well, we are implementing a failsafe to calculate calorie requirements even if the network connection is not good. +Daily requirements for macronutrients intake can also be implemented to enhance the user's diet further. These requirements also change depending on the user's exercise levels and body goals. -### Edit user details +### Provide recommendations to meet daily requirements -The user should be able to quickly edit their details without having to run the `user setup` command again, as they may only need to change a few details for their account. +The `user progress` command displays the user's current progress towards the daily caloric and hydration requirements. This can be enhanced if the application was able to provide recommendations on how to meet these requirements. For example, healthy meals with carefully calculated macronutrients can be recommended to users to meet their daily caloric and macronutrient requirements. ## Command Summary From 990b69c85afb217300f071547a6bcdc592277c2f Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 04:59:31 +0800 Subject: [PATCH 322/414] fix gradle checkstyle --- .../system/exceptions/FileHandlerExceptionMessage.java | 3 ++- src/main/java/seedu/lifetrack/system/storage/FileHandler.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java index 10de8256f5..49787bfd28 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/FileHandlerExceptionMessage.java @@ -75,7 +75,8 @@ public class FileHandlerExceptionMessage { "is provided!"; private static final String SEX_GUIDANCE = "\t Please ensure that the sex field is only either "+ "\"male\" or \"female\"!"; - private static final String EX_LEVELS_GOALS_GUIDANCE = "\t Please ensure that an integer between 1 to 5 is provided!"; + private static final String EX_LEVELS_GOALS_GUIDANCE = "\t Please ensure that an integer between 1 to 5 " + + "is provided!"; private static String getLineNotAddedMessage(int lineNumber, String filePath) { if (filePath.equals("data/caloriesData.txt")) { diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 94ba1e9d56..22470b2a23 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -23,10 +23,11 @@ public class FileHandler { //NumberFormatException exception message prefix protected final String NF_EXCEPTION_PREFIX = "For input string: \""; + protected String filePath; + //error message for IO exception private final String message = "\t Unable to write to file!"; - protected String filePath; public FileHandler(String filePath) { this.filePath = filePath; From 095f015d320353fd6350894b970b8b8407348558 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 05:02:28 +0800 Subject: [PATCH 323/414] fix text-ui --- src/main/java/seedu/lifetrack/user/User.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/user/User.java b/src/main/java/seedu/lifetrack/user/User.java index 9df7bfe8e1..98c51e7b89 100644 --- a/src/main/java/seedu/lifetrack/user/User.java +++ b/src/main/java/seedu/lifetrack/user/User.java @@ -56,11 +56,6 @@ public User(String filePath) { goal = Integer.parseInt(data.get(GOAL_INDEX)); caloriesRequired = Integer.parseInt(data.get(REQ_CAL_INDEX)); } - System.out.println(name); - System.out.println(height); - System.out.println(weight); - System.out.println(age); - System.out.println(sex); } catch (FileNotFoundException e) { return; } From 3910203eb237fb4178b68853653c26b5fde29358 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 05:12:43 +0800 Subject: [PATCH 324/414] fix expected text --- 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 2f91cf0ff2..7bdc6d7158 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,3 +1,4 @@ + ----------------------------------------------------------------------------- Hello from From e4f279c8f7583146ea142831bafa9d3e8a92aa1f Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 05:31:17 +0800 Subject: [PATCH 325/414] added find functions --- .../calories/calorielist/CalorieList.java | 19 +++++ .../hydrationlist/HydrationList.java | 23 ++++++ .../system/parser/ParserCalories.java | 74 ++++++++++++------- .../system/parser/ParserHydration.java | 15 ++++ .../seedu/lifetrack/ui/CalorieListUi.java | 6 ++ .../seedu/lifetrack/ui/HydrationListUI.java | 6 ++ src/main/java/seedu/lifetrack/ui/Ui.java | 36 +++++++-- 7 files changed, 144 insertions(+), 35 deletions(-) diff --git a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java index e3976273e3..79854ae058 100644 --- a/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java +++ b/src/main/java/seedu/lifetrack/calories/calorielist/CalorieList.java @@ -263,4 +263,23 @@ public int compare(Entry entry1, Entry entry2) { } }); } + + //@@author paturikarthik + public void findEntries(String input){ + ParserCalories.findCalorieListEntries(input,this); + } + + public void addCalorieEntry(Entry entry){ + this.calorieArrayList.add(entry); + } + + public void printFoundCalorieList() { + if (calorieArrayList.isEmpty()) { + CalorieListUi.emptyFoundListMessage(); + } else { + CalorieListUi.calorieListFoundHeader(); + printCalorieInflow(); + printCalorieOutflow(); + } + } } diff --git a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java index 3d5b52de68..be64107204 100644 --- a/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java +++ b/src/main/java/seedu/lifetrack/hydration/hydrationlist/HydrationList.java @@ -3,9 +3,13 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.calories.calorielist.CalorieList; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; +import seedu.lifetrack.system.parser.ParserCalories; import seedu.lifetrack.system.parser.ParserHydration; import seedu.lifetrack.system.storage.FileHandler; +import seedu.lifetrack.ui.CalorieListUi; import seedu.lifetrack.ui.HydrationListUI; import java.io.FileNotFoundException; @@ -200,4 +204,23 @@ public int getSize() { private int loadLastEntryID() { return FileHandler.getMaxHydrationID(); } + + //@@author paturikarthik + public void findEntries(String input){ + ParserHydration.findHydrationListEntries(input,this); + } + + public void addHydrationEntry(Entry entry){ + this.hydrationArrayList.add(entry); + } + + public void printFoundHydrationList() { + if (hydrationArrayList.isEmpty()) { + HydrationListUI.emptyFoundListMessage(); + } else { + HydrationListUI.hydrationListFoundHeader(); + printHydrationInflow(); + } + } + } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 54b1cd1e65..b58989c305 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -1,26 +1,27 @@ //@@author owx0130 package seedu.lifetrack.system.parser; +import seedu.lifetrack.Entry; +import seedu.lifetrack.calories.Food; +import seedu.lifetrack.calories.calorielist.CalorieList; import seedu.lifetrack.calories.calorielist.InputEntry; import seedu.lifetrack.calories.calorielist.OutputEntry; -import seedu.lifetrack.calories.Food; import seedu.lifetrack.system.exceptions.InvalidInputException; + +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesIncorrectOrderMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesMissingKeywordsMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getDateLaterThanPresentDateMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyMacrosMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectCaloriesInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectMacrosInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncompleteMacrosMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesIncorrectOrderMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesMissingKeywordsMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptyMacrosMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getDateLaterThanPresentDateMessage; - -import seedu.lifetrack.Entry; - -import java.time.LocalDate; -import java.time.format.DateTimeParseException; public class ParserCalories { @@ -28,10 +29,11 @@ public class ParserCalories { private static final int PROTEINS_IDX = 1; private static final int FATS_IDX = 2; private static final int CALORIES_OUT_PADDING = 12; + private static final int CALORIES_FIND_LENGTH = "calories find".length(); /** * Parses a string input to create an Entry object representing calorie intake. - * + *

    * This method expects the input string to follow a specific format, where the * description, calorie count, date and macronutrients are separated by the * delimiters 'desc/', 'c/', 'date/', and 'm/'. The method extracts these components @@ -121,6 +123,7 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv } //@@author rexyyong + /** * Parses a string representation of a date and returns a LocalDate object. * @@ -153,8 +156,8 @@ private static int getIntegerCaloriesFromInput(String strCalories) { /** * Extracts the description from the input string based on the command and the index of calories. * - * @param inputString the input string containing the description - * @param command the command string indicating the type of entry ("calories in" or "calories out") + * @param inputString the input string containing the description + * @param command the command string indicating the type of entry ("calories in" or "calories out") * @param caloriesIndex the index of the "calories" keyword in the input string * @return the description extracted from the input string */ @@ -182,7 +185,7 @@ private static int[] getMacrosFromInput(String macroString) throws InvalidInputE try { String[] macroParts = macroString.split(","); int idx = 0; - for (String macro: macroParts) { + for (String macro : macroParts) { //throw exception if user inputs whitespace in the macros field i.e. m/123, ,123 if (macro.trim().isEmpty()) { throw new InvalidInputException(getWhitespaceInMacrosInputMessage()); @@ -217,7 +220,7 @@ private static void checkCaloriesIsPositiveInteger(int calories) throws InvalidI * * @param description the description of the entry * @param strCalories the string representation of calories - * @param date the date of the entry + * @param date the date of the entry * @throws InvalidInputException if any of the inputs are empty strings */ private static void checkInputsAreNonEmpty(String description, String strCalories, String date) @@ -232,7 +235,7 @@ private static void checkInputsAreNonEmpty(String description, String strCalorie * Checks if the keywords for calories and date exist in the input string. * * @param caloriesIndex the index of the "c/" keyword in the input - * @param dateIndex the index of the "date/" keyword in the input + * @param dateIndex the index of the "date/" keyword in the input * @throws InvalidInputException if the keywords are missing */ private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws InvalidInputException { @@ -246,8 +249,8 @@ private static void checkKeywordsExist(int caloriesIndex, int dateIndex) throws * Checks if the keywords for calories and date are correctly ordered in the input string. * * @param caloriesIndex the index of the "c/" keyword in the input - * @param dateIndex the index of the "date/" keyword in the input - * @param macrosIndex the index of the "macros/" keyword in the input + * @param dateIndex the index of the "date/" keyword in the input + * @param macrosIndex the index of the "macros/" keyword in the input * @throws InvalidInputException if the keywords are not correctly ordered */ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateIndex, int macrosIndex) @@ -262,8 +265,8 @@ private static void checkKeywordsCorrectlyOrdered(int caloriesIndex, int dateInd * Creates a new output entry with the given description, calories, and date. * * @param description the description of the entry - * @param calories the number of calories - * @param date the date of the entry + * @param calories the number of calories + * @param date the date of the entry * @return a new OutputEntry object */ private static Entry makeNewOutputEntry(int lastEntryID, String description, int calories, LocalDate date) { @@ -274,8 +277,8 @@ private static Entry makeNewOutputEntry(int lastEntryID, String description, int * Creates a new input entry with the given description, calories, and date. * * @param description the description of the entry - * @param calories the number of calories - * @param date the date of the entry + * @param calories the number of calories + * @param date the date of the entry * @return a new InputEntry object */ private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date) { @@ -287,9 +290,9 @@ private static Entry makeNewInputEntry(int lastEntryID, String description, int * Creates a new input entry with the given description, calories, date, and food macros. * * @param description the description of the entry - * @param calories the number of calories - * @param date the date of the entry - * @param foodMacros an array containing food macros (carbs, proteins, fats) + * @param calories the number of calories + * @param date the date of the entry + * @param foodMacros an array containing food macros (carbs, proteins, fats) * @return a new InputEntry object with food macros */ private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date, @@ -299,4 +302,21 @@ private static Entry makeNewInputEntry(int lastEntryID, String description, int return new InputEntry(lastEntryID, description, calories, date, newFood); } + + //@@author paturikarthik + public static void findCalorieListEntries(String input, CalorieList originalList) { + String keyword = input.substring(CALORIES_FIND_LENGTH).trim(); + CalorieList searchList = new CalorieList(); + for (int i = 0; i < originalList.getSize(); i++) { + Entry entry = originalList.getEntry(i); + if (entry.getDescription().contains(keyword)) { + if (entry instanceof InputEntry) { + Entry entryToAdd = makeNewInputEntry(entry.getLastEntryID(), entry.getDescription() + , ((InputEntry) entry).getCalories(), entry.getDate()); + searchList.addCalorieEntry(entryToAdd); + } + } + } + searchList.printFoundCalorieList(); + } } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index 8d4c8e66cf..2355c55cbd 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -3,6 +3,7 @@ import seedu.lifetrack.Entry; import seedu.lifetrack.hydration.hydrationlist.HydrationEntry; +import seedu.lifetrack.hydration.hydrationlist.HydrationList; import seedu.lifetrack.system.exceptions.InvalidInputException; import java.time.LocalDate; @@ -20,6 +21,7 @@ public class ParserHydration { private static final int VOLUME_IDX = 1; private static final int DATE_IDX = 2; private static final int HYDRATION_ADD_PADDING = 13; + private static final int HYDRATION_FIND_LENGTH = "hydration find".length(); /** * Parses a string input to create a Liquid object representing liquid intake. @@ -184,4 +186,17 @@ private static void checkKeywordsExist(int dateIndex, int volumeIndex) throws In } } + //@@author paturikarthik + public static void findHydrationListEntries(String input, HydrationList originalList){ + String keyword = input.substring(HYDRATION_FIND_LENGTH).trim(); + HydrationList searchList = new HydrationList(); + for (int i = 0 ; i : Deletes the entry at the specified ID" + " from the calorie list."); + System.out.println("\t - calories find : finds and lists all calorie entries " + + "containing the keyword " + "in their description"); printLine(); System.out.println("\t - hydration in v/ d/:\n" + "\t Adds a hydration entry into the hydration tracker."); System.out.println("\t - hydration list: Displays all entries currently stored in the hydration list."); System.out.println("\t - hydration delete : Deletes the hydration entry at the specified ID " + "from the hydration list."); + System.out.println("\t - hydration find : finds and lists all hydration entries containing " + + "the keyword " + "in their description"); printLine(); System.out.println("\t - sleep add d/: " + "Adds a sleep entry into the sleep tracker."); From f00c4f4356856a15adb5df3392fe61fbebce86b6 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 05:41:37 +0800 Subject: [PATCH 326/414] merge master --- .../seedu/lifetrack/system/parser/ParserCalories.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 288976e67e..e8cd5c5aa6 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -295,7 +295,7 @@ private static Entry makeNewInputEntry(int lastEntryID, String description, int * @return a new InputEntry object with food macros */ private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date, - int[] foodMacros) { + int[] foodMacros) { Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); return new InputEntry(lastEntryID, description, calories, date, newFood); } @@ -308,9 +308,13 @@ public static void findCalorieListEntries(String input, CalorieList originalList Entry entry = originalList.getEntry(i); if (entry.getDescription().contains(keyword)) { if (entry instanceof InputEntry) { - Entry entryToAdd = makeNewInputEntry(entry.getLastEntryID(), entry.getDescription() + Entry entryToAdd = makeNewInputEntry(entry.getEntryID(), entry.getDescription() , ((InputEntry) entry).getCalories(), entry.getDate()); searchList.addCalorieEntry(entryToAdd); + } else { + Entry entryToAdd = makeNewOutputEntry(entry.getEntryID(), entry.getDescription(), + ((OutputEntry) entry).getCalories(), entry.getDate()); + searchList.addCalorieEntry(entryToAdd); } } } From a0a760b068b04a9dddde941556f855b8106f3d24 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 05:52:37 +0800 Subject: [PATCH 327/414] small bugfix for calories input --- .../seedu/lifetrack/system/storage/CaloriesFileHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java index 1738f0637e..5ac1c6276d 100644 --- a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java @@ -78,7 +78,7 @@ private void checkValidEntryType(int lineNumber, String entryType, int dataLengt throw new FileHandlerException(getFileInvalidEntryTypeMessage(lineNumber, filePath)); } - if (entryType.equals("C_IN") && dataLength != 8) { + if (entryType.equals("C_IN") && dataLength != 8 && dataLength != 5) { throw new FileHandlerException(getFileTooFewMacrosMessage(lineNumber, filePath)); } else if (entryType.equals("C_OUT") && dataLength != 5) { throw new FileHandlerException(getFileMacrosInOutputMessage(lineNumber, filePath)); From 54a31ad3c08a890eccc45043d1313a147bb7e240 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Sun, 14 Apr 2024 09:26:09 +0800 Subject: [PATCH 328/414] Bug fixes --- docs/UserGuide.md | 2 +- .../system/exceptions/InvalidInputExceptionMessage.java | 5 +++++ .../seedu/lifetrack/system/parser/ParserHydration.java | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 93251d87cf..9fd646c07a 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -139,7 +139,7 @@ Adds a hydration record into the hydration tracker. `hydration in DESCRIPTION v/VOLUME d/DATE` * The `DESCRIPTION` refers to the food that the person consumed. -* The `VOLUME` must be a positive integer 1, 2, 3, …, measured in milliliters. +* The `VOLUME` must be a positive integer 1, 2, 3, …, measured in milliliters, volume cannot not be more than 10000. * The `DATE` provided should be of the form YYYY-MM-DD, such as 2024-03-04. **Examples:** diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 88fd134d63..5c8588ba6f 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -101,6 +101,11 @@ public static String getHydrationNegativeIntegerVolumeMessage() { return HEADER + message + HYDRATION_IN_INPUT; } + public static String getHydrationOverVolumeLimitMessage() { + String message = "\t Please ensure that volume is not more than 10000!\n"; + return HEADER + message + HYDRATION_IN_INPUT; + } + public static String getIncorrectVolumeInputMessage() { return "\t Please input only positive integers into the volume field!"; } diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java index 2355c55cbd..5ac669dcfd 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserHydration.java @@ -15,12 +15,14 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectVolumeInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationNegativeIntegerVolumeMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationEmptyDescriptionMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getHydrationOverVolumeLimitMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getInvalidDateMessage; public class ParserHydration { private static final int VOLUME_IDX = 1; private static final int DATE_IDX = 2; private static final int HYDRATION_ADD_PADDING = 13; + private static final int VOLUME_MAX = 10000; private static final int HYDRATION_FIND_LENGTH = "hydration find".length(); /** @@ -60,6 +62,7 @@ public static Entry parseHydrationInput(String input, int lastHydrationEntryID) int volume = getIntegerVolumeFromInput(strVolume); checkVolumeIsPositiveInteger(volume); + checkVolumeMaxAllowable(volume); assert volume > 0 : "Volume value must be a positive integer!"; //@@author rexyyong @@ -78,6 +81,12 @@ public static Entry parseHydrationInput(String input, int lastHydrationEntryID) return makeNewInputEntry(lastHydrationEntryID, description, volume, date); } + private static void checkVolumeMaxAllowable(int volume) throws InvalidInputException { + if (volume > VOLUME_MAX) { + throw new InvalidInputException(getHydrationOverVolumeLimitMessage()); + } + } + /** * Creates a new HydrationEntry object with the specified description, volume, and date. * From 54e3c3c9743162e5f468efba33a3a8828eb82797 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 15:00:58 +0800 Subject: [PATCH 329/414] special-char-names added --- .../seedu/lifetrack/system/parser/ParserUser.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index cd5e01e3f6..fbd338ffea 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -66,7 +66,13 @@ public class ParserUser { */ public static void parseSetUp(String input, User user) throws InvalidInputException, NumberFormatException { checkEmptyInput(input); + int heightIndex = input.indexOf("h/"); + String name = null; + if (heightIndex != -1) { + name = parseName(input.substring(LENGTH_OF_SETUP_COMMAND, heightIndex).trim()); + input = input.substring(heightIndex); + } int weightIndex = input.indexOf("w/"); int ageIndex = input.indexOf("a/"); int sexIndex = input.indexOf("s/"); @@ -77,13 +83,13 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept || exerciseLevelsIndex == -1 || goalIndex == -1) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); } - checkSetUpInputsCorrectOrder(heightIndex, weightIndex, ageIndex, sexIndex, exerciseLevelsIndex, goalIndex); + checkSetUpInputsCorrectOrder(weightIndex, ageIndex, sexIndex, exerciseLevelsIndex, goalIndex); String[] parts = input.split("h/|w/|a/|s/|e/|g/"); if (parts.length != 7) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); } - String name = parseName(parts[USER_INPUT_NAME_INDEX].substring(LENGTH_OF_SETUP_COMMAND).trim()); +// String name = parseName(parts[USER_INPUT_NAME_INDEX].substring(LENGTH_OF_SETUP_COMMAND).trim()); int height = parseHeightIndex(parts[USER_INPUT_HEIGHT_INDEX].trim()); int weight = parseWeightIndex(parts[USER_INPUT_WEIGHT_INDEX].trim()); int age = parseAgeIndex(parts[USER_INPUT_AGE_INDEX].trim()); @@ -238,7 +244,6 @@ private static int parseExerciseLevels(String input) throws InvalidInputExceptio /** * Ensures that the input given by the user is in the correct order * - * @param heightIndex Index of the input where user's height is specified * @param weightIndex Index of the input where user's weight is specified * @param ageIndex Index of the input where user's age is specified * @param sexIndex Index of the input where user's gender is specified @@ -247,10 +252,10 @@ private static int parseExerciseLevels(String input) throws InvalidInputExceptio * @throws InvalidInputException if the order of the inputs is not correct. The input should be in this order: * height, weight, age, gender, exercise levels and goal. */ - private static void checkSetUpInputsCorrectOrder(int heightIndex, int weightIndex, int ageIndex, int sexIndex, + private static void checkSetUpInputsCorrectOrder(int weightIndex, int ageIndex, int sexIndex, int exerciseLevelsIndex, int goalIndex) throws InvalidInputException { - if (!(heightIndex < weightIndex && weightIndex < ageIndex && sexIndex < exerciseLevelsIndex + if (!(weightIndex < ageIndex && sexIndex < exerciseLevelsIndex && exerciseLevelsIndex < goalIndex)) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); } From 002780eac301d6e79eb2e9657875201c6e8fd5b6 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 15:03:34 +0800 Subject: [PATCH 330/414] build error --- src/main/java/seedu/lifetrack/system/parser/ParserUser.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java index fbd338ffea..2640be4bf4 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserUser.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserUser.java @@ -89,7 +89,6 @@ public static void parseSetUp(String input, User user) throws InvalidInputExcept if (parts.length != 7) { throw new InvalidInputException(getInvalidNumberOfSetUpInputs()); } -// String name = parseName(parts[USER_INPUT_NAME_INDEX].substring(LENGTH_OF_SETUP_COMMAND).trim()); int height = parseHeightIndex(parts[USER_INPUT_HEIGHT_INDEX].trim()); int weight = parseWeightIndex(parts[USER_INPUT_WEIGHT_INDEX].trim()); int age = parseAgeIndex(parts[USER_INPUT_AGE_INDEX].trim()); From 9daf00e581bc87c8579282b2c9daacbe15a016cb Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 15:55:36 +0800 Subject: [PATCH 331/414] disallow negative inputs for macronutrients --- .../java/seedu/lifetrack/system/parser/ParserCalories.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index e8cd5c5aa6..7f9b6cf678 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -190,7 +190,11 @@ private static int[] getMacrosFromInput(String macroString) throws InvalidInputE if (macro.trim().isEmpty()) { throw new InvalidInputException(getWhitespaceInMacrosInputMessage()); } - macros[idx] = Integer.parseInt(macro.trim()); + int macrosInt = Integer.parseInt(macro.trim()); + if (macrosInt <= 0) { + throw new InvalidInputException(getIncorrectMacrosInputMessage()); + } + macros[idx] = macrosInt; idx++; } //throw exception if there are missing values in the macros field From bc13d6092f938009ab9b0d0191a6ad49159acb34 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 16:23:48 +0800 Subject: [PATCH 332/414] add correct input test cases for ParserCalories --- .../system/parser/ParserCalories.java | 2 +- .../seedu/lifetrack/ParserCaloriesTest.java | 84 +++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 7f9b6cf678..21a737ec0a 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -299,7 +299,7 @@ private static Entry makeNewInputEntry(int lastEntryID, String description, int * @return a new InputEntry object with food macros */ private static Entry makeNewInputEntry(int lastEntryID, String description, int calories, LocalDate date, - int[] foodMacros) { + int[] foodMacros) { Food newFood = new Food(foodMacros[CARBS_IDX], foodMacros[PROTEINS_IDX], foodMacros[FATS_IDX]); return new InputEntry(lastEntryID, description, calories, date, newFood); } diff --git a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java index a9e65f09c5..f902a307e1 100644 --- a/src/test/java/seedu/lifetrack/ParserCaloriesTest.java +++ b/src/test/java/seedu/lifetrack/ParserCaloriesTest.java @@ -4,8 +4,14 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import seedu.lifetrack.calories.Food; +import seedu.lifetrack.calories.calorielist.InputEntry; +import seedu.lifetrack.calories.calorielist.OutputEntry; import seedu.lifetrack.system.exceptions.InvalidInputException; import static seedu.lifetrack.system.parser.ParserCalories.parseCaloriesInput; + +import java.time.LocalDate; + import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectCaloriesInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectMacrosInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; @@ -29,7 +35,7 @@ public void parseCaloriesInput_missingKeywords_exceptionThrown() { @Test public void parseCaloriesInput_incompleteInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/ d/220224", 0); + parseCaloriesInput("calories in burger c/ d/2024-02-02", 0); } catch (InvalidInputException e) { assertEquals(getWhitespaceInInputMessage(), e.getMessage()); } @@ -38,7 +44,7 @@ public void parseCaloriesInput_incompleteInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectlyOrderedInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger d/220224 c/123", 0); + parseCaloriesInput("calories in burger d/2024-02-02 c/123", 0); } catch (InvalidInputException e) { assertEquals(getCaloriesIncorrectOrderMessage(), e.getMessage()); } @@ -56,7 +62,7 @@ public void parseCaloriesInput_incorrectMacrosInput_exceptionThrown() { @Test public void parseCaloriesInput_incorrectCaloriesInput_exceptionThrown() { try { - parseCaloriesInput("calories out Running c/abc d/220324", 0); + parseCaloriesInput("calories out Running c/abc d/2024-02-02", 0); } catch (InvalidInputException e) { assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); } @@ -65,7 +71,7 @@ public void parseCaloriesInput_incorrectCaloriesInput_exceptionThrown() { @Test public void parseCaloriesInput_incompleteMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 d/220324 m/123,132", 0); + parseCaloriesInput("calories in burger c/123 d/2024-02-02 m/123,132", 0); } catch (InvalidInputException e) { assertEquals(getIncompleteMacrosMessage(), e.getMessage()); } @@ -74,7 +80,7 @@ public void parseCaloriesInput_incompleteMacrosInput_exceptionThrown() { @Test public void parseCaloriesInput_macrosInCaloriesOut_exceptionThrown() { try { - parseCaloriesInput("calories out running c/123 d/220324 m/123,123,132", 0); + parseCaloriesInput("calories out running c/123 d/2024-02-02 m/123,123,132", 0); } catch (InvalidInputException e) { assertEquals(getMacrosInCaloriesOutMessage(), e.getMessage()); } @@ -83,9 +89,75 @@ public void parseCaloriesInput_macrosInCaloriesOut_exceptionThrown() { @Test public void parseCaloriesInput_whitespaceInMacrosInput_exceptionThrown() { try { - parseCaloriesInput("calories in burger c/123 d/220324 m/123, ,132", 0); + parseCaloriesInput("calories in burger c/123 d/2024-02-02 m/123, ,132", 0); } catch (InvalidInputException e) { assertEquals(getWhitespaceInMacrosInputMessage(), e.getMessage()); } } + + @Test + public void parseCaloriesInput_negativeMacrosInput_exceptionThrown() { + try { + parseCaloriesInput("calories in burger c/123 d/2024-02-02 m/123,-3,132", 0); + } catch (InvalidInputException e) { + assertEquals(getIncorrectMacrosInputMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_negativeCaloriesInput_exceptionThrown() { + try { + parseCaloriesInput("calories in burger c/-123 d/2024-02-02 m/123,123,123", 0); + } catch (InvalidInputException e) { + assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_correctCaloriesInInput_entryReturned() { + try { + InputEntry expectedEntry = new InputEntry(1, "burger", 123, LocalDate.parse("2024-02-02")); + InputEntry entry = (InputEntry)parseCaloriesInput("calories in burger c/123 d/2024-02-02", 0); + assertEquals(expectedEntry.getEntryID(), entry.getEntryID()); + assertEquals(expectedEntry.getDescription(), entry.getDescription()); + assertEquals(expectedEntry.getDate(), entry.getDate()); + assertEquals(expectedEntry.getCalories(), entry.getCalories()); + } catch (InvalidInputException e) { + assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_correctCaloriesInInputWithMacros_entryReturned() { + try { + Food expectedFood = new Food(10, 10, 10); + InputEntry expectedEntry = new InputEntry(1, "burger", 123, LocalDate.parse("2024-02-02"), expectedFood); + InputEntry actualEntry = + (InputEntry)parseCaloriesInput("calories in burger c/123 d/2024-02-02 m/10,10,10", 0); + assertEquals(expectedEntry.getEntryID(), actualEntry.getEntryID()); + assertEquals(expectedEntry.getDescription(), actualEntry.getDescription()); + assertEquals(expectedEntry.getDate(), actualEntry.getDate()); + assertEquals(expectedEntry.getCalories(), actualEntry.getCalories()); + Food actualFood = actualEntry.getFood(); + assertEquals(expectedFood.getCarbohydrates(), actualFood.getCarbohydrates()); + assertEquals(expectedFood.getProteins(), actualFood.getProteins()); + assertEquals(expectedFood.getFats(), actualFood.getFats()); + } catch (InvalidInputException e) { + assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); + } + } + + @Test + public void parseCaloriesInput_correctCaloriesOutInput_entryReturned() { + try { + OutputEntry expectedEntry = new OutputEntry(1, "run", 123, LocalDate.parse("2024-02-02")); + OutputEntry entry = (OutputEntry)parseCaloriesInput("calories out run c/123 d/2024-02-02", 0); + assertEquals(expectedEntry.getEntryID(), entry.getEntryID()); + assertEquals(expectedEntry.getDescription(), entry.getDescription()); + assertEquals(expectedEntry.getDate(), entry.getDate()); + assertEquals(expectedEntry.getCalories(), entry.getCalories()); + } catch (InvalidInputException e) { + assertEquals(getIncorrectCaloriesInputMessage(), e.getMessage()); + } + } } From f525d5f56af3be74a2e97a1e1148dd31fab93055 Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 16:43:23 +0800 Subject: [PATCH 333/414] Update UG --- docs/UserGuide.md | 110 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 8 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 93251d87cf..11ffb344bb 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -14,17 +14,21 @@ LifeTrack is a desktop app for students to track their health data, optimized fo - [Input calorie loss](#input-calorie-loss-calories-out) - [Listing calorie items](#listing-calorie-items-calories-list) - [Deleting a calorie item](#deleting-a-calorie-item-calories-delete) + - [Finding a calorie item from caloric list](#searching-for-a-calorie-item-calories-find) - [Hydration Tracker](#hydration-tracker) - [Input hydration intake](#input-hydration-intake-hydration-in) - [Listing hydration items](#listing-hydration-items-hydration-list) - [Deleting a hydration item](#deleting-a-hydration-item-hydration-delete) + - [Finding a hydration item from hydration list](#searching-for-a-hydration-item-hydration-find) - [Sleep Tracker](#sleep-tracker) - [Input sleeping hours](#input-sleeping-hours-sleep-add) - [Listing sleep records](#listing-sleep-records-sleep-list) - [Deleting a sleep record](#deleting-a-sleep-record-sleep-delete) - [User Profile](#user-profile) - [Set Up User Profile](#set-up-user-profile-user-setup) - - [Check User's daily calories and hydration consumption](#check-your-daily-calories-and-hydration-consumption-user-progress) + - [Check User Details](#check-your-users-details-user-details) + - [Update User Details](#update-your-users-details-user-update) + - [Check User's daily calories and hydration consumption](#check-your-daily-calories-and-hydration-consumption-and-your-sleep-statistics-user-progress) - [FAQ](#faq) - [Command Summary](#command-summary) @@ -130,6 +134,17 @@ Deletes the specified calories ID entry from the calories tracker according to t * `calories list` followed by `calories delete 2` deletes the entry with `CALORIESID` 2 in the calories tracker. +### Searching for a calorie item: `calories find` +Finds and retrieves all calories entries from the caloric list containing the keyword to search for. + +**Format:** +`calories find KEYWORD` + +**Examples:** + +* `calories find cookies` retrieves all the calories entries with `cookies` in their description. + + ## Hydration Tracker ### Input hydration intake: `hydration in` @@ -169,6 +184,16 @@ Deletes the specified hydration entry according to the `HYDRATIONID`. **Examples:** * `hydration list` followed by `hydration delete 2` deletes the entry with `HYDRATIONID` 2 in the hydration tracker. +### Searching for a hydration item: `hydration find` +Finds and retrieves all hydration entries from the hydration list containing the keyword to search for. + +**Format:** +`hydration find KEYWORD` + +**Examples:** + +* `hydration find water` retrieves all the hydration entries with `water` in their description. + ## Sleep Tracker ### Input sleeping hours: `sleep add` @@ -240,11 +265,11 @@ Creates/edits an existing user profile. * `user setup Tom h/180 w/80 a/25 s/male e/3 g/2` * `user setup Jane h/163 w/54 a/23 s/female e/2 g/3` -### Check your daily calories and hydration consumption: `user progress` -Displays a progress bar to show the percentage of calories and hydration you have consumed. +### Check your user's details: `user details` +Displays the details of the user who is using _LifeTrack_. **Format:** -`user progress` +`user details` **Notes about the command:** If you have not set your user up beforehand, this command will prompt you to do so instead. @@ -252,13 +277,80 @@ If you have not set your user up beforehand, this command will prompt you to do #### Expected output ----------------------------------------------------------------------------- + User details: + Name: John + Height: 170 + Weight: 80 + Age: 23 + Sex: male + Exercise Levels: 2 out of 5 (Lightly Active) + Goal: 4 out of 5 (Moderate Weight Gain) + ----------------------------------------------------------------------------- +### Update your user's details: `user update` +Updates the details of the user depending on their input. + +**Format:** +`user update ` + +**List of possible fields to update:** +1. name +2. height +3. weight +4. age +5. sex +6. exercise levels +7. goal + +#### Examples: +- `user update weight 70` +- `user update height 170` +- `user update exercise levels 2` + +### Check your daily calories and hydration consumption and your sleep statistics: `user progress` +Displays progress bars to show the percentage of calories and hydration you have consumed as well as sleep you have gotten over the past 3 days. + +**Format:** +`user progress` + +**Notes about the command:** +If you have not set your user up beforehand, this command will prompt you to do so instead. + +#### Expected output + + ----------------------------------------------------------------------------- Calories: - You have consumed 350 calories out of your goal of 2140 calories so far. - [======== ] 16% + ---------- + You have consumed 200 calories out of your goal of 2650 calories today. + [=== ] 7% + + You have consumed 300 calories out of your goal of 2650 calories yesterday. + [===== ] 11% + + You have consumed 400 calories out of your goal of 2650 calories on the day before yesterday. + [====== ] 15% Hydration: - You have consumed 200ml out of your goal of 2000ml so far. - [===== ] 10% + ---------- + You have consumed 300ml out of your goal of 2000ml today. + [======= ] 15% + + You have consumed 600ml out of your goal of 2000ml yesterday. + [============== ] 30% + + You have consumed 1200ml out of your goal of 2000ml on the day before yesterday. + [============================ ] 60% + + Sleep: + ---------- + You have slept for 4.2 hrs out of your goal of 7 hrs today. + [============================== ] 60% + + You have slept for 4.7 hrs out of your goal of 7 hrs yesterday. + [================================= ] 67% + + You have slept for 8.0 hrs out of your goal of 7 hrs on the day before yesterday. + [==================================================] 114% + ----------------------------------------------------------------------------- @@ -310,5 +402,7 @@ The `user progress` command displays the user's current progress towards the dai | List sleep | `sleep list` | | Delete sleep entry | `sleep delete SLEEPID` | | Set Up User Profile | `user setup NAME h/HEIGHT w/WEIGHT a/AGE s/GENDER e/EXERCISE LEVELS g/BODY GOAL` | +| Check User Profile | `user details` | +| Update User Details | `user update ` | | Check User Progress | `user progress` | From 65d847f106d026cc6f403fee89629f07f1b75020 Mon Sep 17 00:00:00 2001 From: shawnpong <110764881+shawnpong@users.noreply.github.com> Date: Sun, 14 Apr 2024 17:39:37 +0800 Subject: [PATCH 334/414] More fixes --- src/main/java/seedu/lifetrack/ui/HydrationListUI.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java index bf7447cecf..8f2708d934 100644 --- a/src/main/java/seedu/lifetrack/ui/HydrationListUI.java +++ b/src/main/java/seedu/lifetrack/ui/HydrationListUI.java @@ -10,7 +10,7 @@ public static void successfulDeletedMessage(Entry toDelete) { System.out.println("\t " + toDelete.toString()); } public static void unsuccessfulDeletedMessage(int entryID) { - System.out.println("\t The following hydration record corresponding to entry ID " + entryID + " could " + + System.out.println("\t The following hydration record corresponding to hydrationID " + entryID + " could " + "not be found"); } @@ -29,7 +29,7 @@ public static String deleteLogIndexMessage() { } public static String deleteLogNumberMessage() { - return "\t Please enter a valid index!"; + return "\t Please enter a valid hydrationID!"; } public static void hydrationListHeader() { From 31d68a2bb0d77c4e2b86e727c58579ec0870e91d Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 17:51:06 +0800 Subject: [PATCH 335/414] extract methods for more SLAP in filehandler classes, set up sample data for filehandler testing --- sample-data/caloriesTestData.txt | 0 sample-data/hydrationTestData.txt | 0 sample-data/sleepTestData.txt | 0 sample-data/userTestData.txt | 0 .../system/storage/CaloriesFileHandler.java | 51 ++++++++++-------- .../system/storage/HydrationFileHandler.java | 25 +++++---- .../system/storage/SleepFileHandler.java | 19 ++++--- .../system/storage/UserFileHandler.java | 54 ++++++++++--------- .../lifetrack/CaloriesFileHandlerTest.java | 24 +++++++++ 9 files changed, 108 insertions(+), 65 deletions(-) create mode 100644 sample-data/caloriesTestData.txt create mode 100644 sample-data/hydrationTestData.txt create mode 100644 sample-data/sleepTestData.txt create mode 100644 sample-data/userTestData.txt create mode 100644 src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java diff --git a/sample-data/caloriesTestData.txt b/sample-data/caloriesTestData.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sample-data/hydrationTestData.txt b/sample-data/hydrationTestData.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sample-data/sleepTestData.txt b/sample-data/sleepTestData.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sample-data/userTestData.txt b/sample-data/userTestData.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java index 5ac1c6276d..a4845dac2c 100644 --- a/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/CaloriesFileHandler.java @@ -85,6 +85,33 @@ private void checkValidEntryType(int lineNumber, String entryType, int dataLengt } } + private void getSingleCalorieEntry(ArrayList entries, String[] words, int lineNumber) + throws FileHandlerException { + checkCorrectNumberOfFields(lineNumber, words.length); + int entryID = Integer.parseInt(words[ENTRYID_INDEX].trim()); + calculateMaxCaloriesEntry(entryID); + LocalDate date = LocalDate.parse(words[DATE_INDEX].trim()); + checkDateNotLaterThanCurrent(lineNumber, date); + String description = words[DESCRIPTION_INDEX].trim(); + checkNonEmptyDescription(lineNumber, description); + int calories = Integer.parseInt(words[CALORIES_INDEX].trim()); + checkCaloriesIsPositive(lineNumber, calories); + String entryType = words[ENTRY_TYPE_INDEX].trim(); + checkValidEntryType(lineNumber, entryType, words.length); + if (entryType.equals("C_IN") && words.length == 8) { + int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX].trim()); + int proteins = Integer.parseInt(words[PROTEINS_INDEX].trim()); + int fats = Integer.parseInt(words[FATS_INDEX].trim()); + Food food = new Food(carbohydrates, proteins, fats); + checkMacrosArePositive(lineNumber, carbohydrates, proteins, fats); + entries.add(new InputEntry(entryID, description, calories, date, food)); + } else if (entryType.equals("C_IN")) { + entries.add(new InputEntry(entryID, description, calories, date)); + } else { + entries.add(new OutputEntry(entryID, description, calories, date)); + } + } + public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); @@ -95,29 +122,7 @@ public ArrayList getCalorieEntriesFromFile() throws FileNotFoundException line = s.nextLine(); String[] words = line.split(";"); try { - checkCorrectNumberOfFields(i, words.length); - int entryID = Integer.parseInt(words[ENTRYID_INDEX].trim()); - calculateMaxCaloriesEntry(entryID); - LocalDate date = LocalDate.parse(words[DATE_INDEX].trim()); - checkDateNotLaterThanCurrent(i, date); - String description = words[DESCRIPTION_INDEX].trim(); - checkNonEmptyDescription(i, description); - int calories = Integer.parseInt(words[CALORIES_INDEX].trim()); - checkCaloriesIsPositive(i, calories); - String entryType = words[ENTRY_TYPE_INDEX].trim(); - checkValidEntryType(i, entryType, words.length); - if (entryType.equals("C_IN") && words.length == 8) { - int carbohydrates = Integer.parseInt(words[CARBOHYDRATES_INDEX].trim()); - int proteins = Integer.parseInt(words[PROTEINS_INDEX].trim()); - int fats = Integer.parseInt(words[FATS_INDEX].trim()); - Food food = new Food(carbohydrates, proteins, fats); - checkMacrosArePositive(i, carbohydrates, proteins, fats); - entries.add(new InputEntry(entryID, description, calories, date, food)); - } else if (entryType.equals("C_IN")) { - entries.add(new InputEntry(entryID, description, calories, date)); - } else { - entries.add(new OutputEntry(entryID, description, calories, date)); - } + getSingleCalorieEntry(entries, words, i); } catch (NumberFormatException e) { if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { System.out.println(getFileInvalidEntryIDMessage(i, filePath)); diff --git a/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java index 3476a48fc5..365af1d735 100644 --- a/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/HydrationFileHandler.java @@ -50,6 +50,20 @@ private void checkVolumeIsPositive(int lineNumber, int volume) throws FileHandle } } + private void getSingleHydrationEntry(ArrayList entries, String[] words, int lineNumber) + throws FileHandlerException { + checkCorrectNumberOfFields(lineNumber, words.length); + int entryID = Integer.parseInt(words[ENTRYID_INDEX]); + calculateMaxHydrationEntry(entryID); + LocalDate date = LocalDate.parse(words[DATE_INDEX]); + checkDateNotLaterThanCurrent(lineNumber, date); + String description = words[DESCRIPTION_INDEX]; + checkNonEmptyDescription(lineNumber, description); + int volume = Integer.parseInt(words[VOLUME_INDEX]); + checkVolumeIsPositive(lineNumber, volume); + entries.add(new HydrationEntry(entryID, description, volume, date)); + } + public ArrayList getHydrationEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); @@ -60,16 +74,7 @@ public ArrayList getHydrationEntriesFromFile() throws FileNotFoundExcepti line = s.nextLine(); String[] words = line.split(";"); try { - checkCorrectNumberOfFields(i, words.length); - int entryID = Integer.parseInt(words[ENTRYID_INDEX]); - calculateMaxHydrationEntry(entryID); - LocalDate date = LocalDate.parse(words[DATE_INDEX]); - checkDateNotLaterThanCurrent(i, date); - String description = words[DESCRIPTION_INDEX]; - checkNonEmptyDescription(i, description); - int volume = Integer.parseInt(words[VOLUME_INDEX]); - checkVolumeIsPositive(i, volume); - entries.add(new HydrationEntry(entryID, description, volume, date)); + getSingleHydrationEntry(entries, words, i); } catch (NumberFormatException e) { if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { System.out.println(getFileInvalidEntryIDMessage(i, filePath)); diff --git a/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java index d19c4fb76e..0ba0c6e46c 100644 --- a/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/SleepFileHandler.java @@ -41,6 +41,17 @@ private void checkDurationIsPositive(int lineNumber, double duration) throws Fil } } + private void getSingleSleepEntry(ArrayList entries, String[] words, int lineNumber) + throws FileHandlerException { + checkCorrectNumberOfFields(lineNumber, words.length); + int lastSleepEntryID = Integer.parseInt(words[ENTRYID_INDEX]); + LocalDate date = LocalDate.parse(words[DATE_INDEX]); + checkDateNotLaterThanCurrent(lineNumber, date); + double duration = Double.parseDouble(words[DURATION_INDEX]); + checkDurationIsPositive(lineNumber, duration); + entries.add(new SleepEntry(lastSleepEntryID, duration, date)); + } + public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); @@ -51,13 +62,7 @@ public ArrayList getSleepEntriesFromFile() throws FileNotFoundException { line = s.nextLine(); String[] words = line.split(";"); try { - checkCorrectNumberOfFields(i, words.length); - int lastSleepEntryID = Integer.parseInt(words[ENTRYID_INDEX]); - LocalDate date = LocalDate.parse(words[DATE_INDEX]); - checkDateNotLaterThanCurrent(i, date); - double duration = Double.parseDouble(words[DURATION_INDEX]); - checkDurationIsPositive(i, duration); - entries.add(new SleepEntry(lastSleepEntryID, duration, date)); + getSingleSleepEntry(entries, words, i); } catch (NumberFormatException e) { if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[ENTRYID_INDEX] + "\"")) { System.out.println(getFileInvalidEntryIDMessage(i, filePath)); diff --git a/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java index 5c8dce0de7..d906ded62e 100644 --- a/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java @@ -92,6 +92,34 @@ private void checkReqCalIsPositive(int requiredCals) throws FileHandlerException } } + private void extractUserData(ArrayList data, String[] words) throws FileHandlerException { + checkCorrectNumberOfFields(words.length); + String name = words[NAME_INDEX]; + checkNonEmptyName(name); + int height = Integer.parseInt(words[HEIGHT_INDEX]); + checkValidHeight(height); + int weight = Integer.parseInt(words[WEIGHT_INDEX]); + checkValidWeight(weight); + int age = Integer.parseInt(words[AGE_INDEX]); + checkValidAge(age); + String sex = words[SEX_INDEX]; + checkValidSex(sex); + int exerciseLevel = Integer.parseInt(words[EXERCISE_LEVEL_INDEX]); + checkValidExerciseLevel(exerciseLevel); + int goal = Integer.parseInt(words[GOAL_INDEX]); + checkValidGoal(goal); + int requiredCals = Integer.parseInt(words[REQ_CAL_INDEX]); + checkReqCalIsPositive(requiredCals); + data.add(words[NAME_INDEX]); + data.add(words[HEIGHT_INDEX]); + data.add(words[WEIGHT_INDEX]); + data.add(words[AGE_INDEX]); + data.add(words[SEX_INDEX]); + data.add(words[EXERCISE_LEVEL_INDEX]); + data.add(words[GOAL_INDEX]); + data.add(words[REQ_CAL_INDEX]); + } + public ArrayList getUserDataFromFile(User user) throws FileNotFoundException { File f = new File(filePath); Scanner s = new Scanner(f); @@ -99,31 +127,7 @@ public ArrayList getUserDataFromFile(User user) throws FileNotFoundExcep String line = s.nextLine(); String[] words = line.split(";"); try { - checkCorrectNumberOfFields(words.length); - String name = words[NAME_INDEX]; - checkNonEmptyName(name); - int height = Integer.parseInt(words[HEIGHT_INDEX]); - checkValidHeight(height); - int weight = Integer.parseInt(words[WEIGHT_INDEX]); - checkValidWeight(weight); - int age = Integer.parseInt(words[AGE_INDEX]); - checkValidAge(age); - String sex = words[SEX_INDEX]; - checkValidSex(sex); - int exerciseLevel = Integer.parseInt(words[EXERCISE_LEVEL_INDEX]); - checkValidExerciseLevel(exerciseLevel); - int goal = Integer.parseInt(words[GOAL_INDEX]); - checkValidGoal(goal); - int requiredCals = Integer.parseInt(words[REQ_CAL_INDEX]); - checkReqCalIsPositive(requiredCals); - data.add(words[NAME_INDEX]); - data.add(words[HEIGHT_INDEX]); - data.add(words[WEIGHT_INDEX]); - data.add(words[AGE_INDEX]); - data.add(words[SEX_INDEX]); - data.add(words[EXERCISE_LEVEL_INDEX]); - data.add(words[GOAL_INDEX]); - data.add(words[REQ_CAL_INDEX]); + extractUserData(data, words); } catch (NumberFormatException e) { if (e.getMessage().equals(NF_EXCEPTION_PREFIX + words[HEIGHT_INDEX] + "\"")) { System.out.println(getFileInvalidHeightMessage(filePath)); diff --git a/src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java b/src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java new file mode 100644 index 0000000000..f86a8541d2 --- /dev/null +++ b/src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java @@ -0,0 +1,24 @@ +package seedu.lifetrack; + +import java.io.FileNotFoundException; +import java.util.ArrayList; + +import org.junit.jupiter.api.Test; + +import seedu.lifetrack.system.storage.CaloriesFileHandler; + +public class CaloriesFileHandlerTest { + + public String filePath = "/sample-data/caloriesTestData.txt"; + public CaloriesFileHandler fileHandler = new CaloriesFileHandler(filePath); + + @Test + public void getCalorieEntriesFromFile_correctFileInput_entryArrayListReturned() { + try { + fileHandler.writeEntries(null); + ArrayList entries = fileHandler.getCalorieEntriesFromFile(); + } catch (FileNotFoundException e) { + return; + } + } +} From 03c3467f12df52f67d11a792c659a7d1c42453b0 Mon Sep 17 00:00:00 2001 From: owx0130 Date: Sun, 14 Apr 2024 17:58:51 +0800 Subject: [PATCH 336/414] move user methods to UserFileHandler --- .../lifetrack/system/storage/FileHandler.java | 15 +++------------ .../lifetrack/system/storage/UserFileHandler.java | 9 +++++++++ .../seedu/lifetrack/CaloriesFileHandlerTest.java | 5 +++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java index 22470b2a23..236dbe98fb 100644 --- a/src/main/java/seedu/lifetrack/system/storage/FileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/FileHandler.java @@ -23,11 +23,10 @@ public class FileHandler { //NumberFormatException exception message prefix protected final String NF_EXCEPTION_PREFIX = "For input string: \""; - protected String filePath; - //error message for IO exception - private final String message = "\t Unable to write to file!"; + protected final String message = "\t Unable to write to file!"; + protected String filePath; public FileHandler(String filePath) { this.filePath = filePath; @@ -45,20 +44,12 @@ protected void checkNonEmptyDescription(int lineNumber, String description) thro } } - private void writeToFile(String textToAdd) throws IOException { + protected void writeToFile(String textToAdd) throws IOException { FileWriter fw = new FileWriter(filePath); fw.write(textToAdd); fw.close(); } - public void writeUserData(User user) { - try { - writeToFile(user.toFileFriendlyString()); - } catch (IOException e) { - System.out.println(message); - } - } - public void writeEntries(ArrayList entries) { try { String newData = ""; diff --git a/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java index d906ded62e..261963e832 100644 --- a/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java +++ b/src/main/java/seedu/lifetrack/system/storage/UserFileHandler.java @@ -14,6 +14,7 @@ import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.Scanner; @@ -148,4 +149,12 @@ public ArrayList getUserDataFromFile(User user) throws FileNotFoundExcep s.close(); return data; } + + public void writeUserData(User user) { + try { + writeToFile(user.toFileFriendlyString()); + } catch (IOException e) { + System.out.println(message); + } + } } diff --git a/src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java b/src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java index f86a8541d2..296f95b1fb 100644 --- a/src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java +++ b/src/test/java/seedu/lifetrack/CaloriesFileHandlerTest.java @@ -11,14 +11,15 @@ public class CaloriesFileHandlerTest { public String filePath = "/sample-data/caloriesTestData.txt"; public CaloriesFileHandler fileHandler = new CaloriesFileHandler(filePath); - +/* @Test public void getCalorieEntriesFromFile_correctFileInput_entryArrayListReturned() { try { + ArrayList expectedEntries = new ArrayList<>(); fileHandler.writeEntries(null); ArrayList entries = fileHandler.getCalorieEntriesFromFile(); } catch (FileNotFoundException e) { return; } - } + }*/ } From 94ee294ef1393cd94245da5eaa45289c09964c64 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Sun, 14 Apr 2024 20:00:43 +0800 Subject: [PATCH 337/414] fix sleep add bugs, fix sleep delete bugs, update DeveloperGuide.md --- docs/DeveloperGuide.md | 2 +- docs/assets/sleepDeleteSeqDiagram.png | Bin 0 -> 41488 bytes docs/diagrams/sleepDeleteSeqDiagram.puml | 29 ++++++++++++++++++ .../lifetrack/sleep/sleeplist/SleepEntry.java | 2 +- .../lifetrack/sleep/sleeplist/SleepList.java | 21 +++++++++++-- .../InvalidInputExceptionMessage.java | 16 ++++++++++ .../java/seedu/lifetrack/SleepListTest.java | 16 +++++----- 7 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 docs/assets/sleepDeleteSeqDiagram.png create mode 100644 docs/diagrams/sleepDeleteSeqDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index c95ea8536b..220878f23d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -373,7 +373,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 5: The latest sleep list will be updated to saving file by calling `SleepList#updateFile()`. The Sequence diagram for Sleep delete feature is shown below: -![SleepDeleteSeqDiagram.jpg](assets%2FSleepDeleteSeqDiagram.jpg) +![sleepDeleteSeqDiagram.png](assets%2FSsleepDeleteSeqDiagram.png) ### Calculating sleep requirements for each user (Planning) diff --git a/docs/assets/sleepDeleteSeqDiagram.png b/docs/assets/sleepDeleteSeqDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..3252947ca50e37664f078ffebced0b66c28ef1f8 GIT binary patch literal 41488 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/sleepDeleteSeqDiagram.puml b/docs/diagrams/sleepDeleteSeqDiagram.puml new file mode 100644 index 0000000000..c2dac344d6 --- /dev/null +++ b/docs/diagrams/sleepDeleteSeqDiagram.puml @@ -0,0 +1,29 @@ +@startuml +actor User +participant "Ui#handleUserInput()" as UI +participant "Ui#handleSleepInput()" as SleepInput +participant "SleepList#deleteEntry(String line)" as DeleteEntry +participant "SleepList#updateFile()" as UpdateFile +participant "SleepFileHandler#writeEntries(ArrayList entries)" as WriteEntries + +User -> UI : Enters command +activate UI +UI -> SleepInput : Sends command for sleep input handling +activate SleepInput +SleepInput -> DeleteEntry : Calls delete entry method +activate DeleteEntry +DeleteEntry -> UpdateFile : Deletes entry +activate UpdateFile +UpdateFile -> WriteEntries : Updates file with new entries +activate WriteEntries +WriteEntries --> UpdateFile : Updates file +deactivate WriteEntries +UpdateFile --> DeleteEntry : File updated +deactivate UpdateFile +DeleteEntry --> SleepInput : Entry deleted +deactivate DeleteEntry +SleepInput --> UI : Sends success message +deactivate SleepInput +UI -> User : Displays success message +deactivate UI +@enduml \ No newline at end of file diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index ebc37c572e..d77bca0a3b 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -7,7 +7,7 @@ public class SleepEntry extends Entry { - private static int sleepEntryNum=0; + private static int sleepEntryNum=1; private LocalDate date; private double duration; private int sleepEntryID; diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 2936b5a860..e3a039b4ff 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -11,9 +11,12 @@ import java.time.LocalDate; import java.util.ArrayList; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; + public class SleepList { private static int DELETE_IDX = 2; + private static int DELETE_LEN = 3; private ArrayList sleepList; private SleepFileHandler fileHandler; private int lastSleepEntryID; @@ -47,7 +50,16 @@ public Entry getSleep(int index) { public void addSleep(String input) { try { - Entry newSleep = ParserSleep.parseSleepInput(input); + SleepEntry newSleep = ParserSleep.parseSleepInput(input); + double sleepRecord = newSleep.getDuration(); + for(int i=0;i24){ + System.out.println(getSleepDurationSumTooLongMessage()); + return; + } sleepList.add(newSleep); updateFile(); SleepListUi.printNewSleepEntry(newSleep); @@ -58,10 +70,13 @@ public void addSleep(String input) { public void deleteSleep(String line) { try { + if(line.split(" ").length!=DELETE_LEN) { + System.out.println(getIncorrectSleepDeleteMessage()); + return; + } int index = Integer.parseInt(line.split(" ")[DELETE_IDX]) ; //User input format: sleep delete ID if(sleepList.isEmpty()){ - System.out.println("Sorry, there is no sleep record in the sleep list. " + - "You cannot delete sleep entry."); + System.out.println(getEmptySleepListMessage()); return; } for(int i=0; i Date: Sun, 14 Apr 2024 20:07:30 +0800 Subject: [PATCH 338/414] fix gradle issue --- src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index e3a039b4ff..53757d1e3b 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -11,7 +11,10 @@ import java.time.LocalDate; import java.util.ArrayList; -import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.*; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getIncorrectSleepDeleteMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getSleepDurationSumTooLongMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getEmptySleepListMessage; + public class SleepList { From 44e9d177d522c0e871225c299f67f849b246d456 Mon Sep 17 00:00:00 2001 From: a-wild-chocolate <69574098+a-wild-chocolate@users.noreply.github.com> Date: Sun, 14 Apr 2024 20:10:07 +0800 Subject: [PATCH 339/414] fix gradle issue --- src/test/java/seedu/lifetrack/SleepListTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index e86e78514f..aa70f0030f 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -51,9 +51,9 @@ public void testPrintSleepListNonEmpty() { sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 2, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + + "\t \t Sleep ID: 1, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. \t Sleep ID: 2, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator; + "\t 1. \t Sleep ID: 1, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -69,15 +69,15 @@ public void testPrintSleepListMultipleEntries() { sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 3, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + + "\t \t Sleep ID: 2, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 4, Date: 2024-04-09, Duration: 8.0 hours" + lineSeparator + + "\t \t Sleep ID: 3, Date: 2024-04-09, Duration: 8.0 hours" + lineSeparator + "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 5, Date: 2024-02-21, Duration: 4.2 hours" + lineSeparator + + "\t \t Sleep ID: 4, Date: 2024-02-21, Duration: 4.2 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. \t Sleep ID: 3, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + - "\t 2. \t Sleep ID: 4, Date: 2024-04-09, Duration: 8.0 hours" + lineSeparator + - "\t 3. \t Sleep ID: 5, Date: 2024-02-21, Duration: 4.2 hours" + lineSeparator; + "\t 1. \t Sleep ID: 2, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + + "\t 2. \t Sleep ID: 3, Date: 2024-04-09, Duration: 8.0 hours" + lineSeparator + + "\t 3. \t Sleep ID: 4, Date: 2024-02-21, Duration: 4.2 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, sleepList.getSize()); } From 2cb2a8675660d391c83d212bc99cc5470bc3fdbd Mon Sep 17 00:00:00 2001 From: RexYong Date: Sun, 14 Apr 2024 21:23:36 +0800 Subject: [PATCH 340/414] Fix exception message for calories delete --- src/main/java/seedu/lifetrack/ui/CalorieListUi.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java index 1d3ac6c750..2a96c5e9c7 100644 --- a/src/main/java/seedu/lifetrack/ui/CalorieListUi.java +++ b/src/main/java/seedu/lifetrack/ui/CalorieListUi.java @@ -4,7 +4,7 @@ import seedu.lifetrack.Entry; public class CalorieListUi { - private static final String CALORIES_DELETE_SAMPLE_INPUT = "\t Example input: calories delete ENTRY_ID"; + private static final String CALORIES_DELETE_SAMPLE_INPUT = "\t Example input: calories delete CALORIES_ID"; public static void successfulDeletedMessage(Entry toDelete) { @@ -12,8 +12,8 @@ public static void successfulDeletedMessage(Entry toDelete) { System.out.println("\t " + toDelete.toString()); } public static void unsuccessfulDeletedMessage(int entryID) { - System.out.println("\t The following calorie record corresponding to entry ID " + entryID + " could " + - "not be found\n" + "\t Refer to calories list to see valid entry IDs."); + System.out.println("\t The following calorie record corresponding to caloriesID " + entryID + " could " + + "not be found\n" + "\t Refer to calories list to see valid caloriesIDs."); } public static void emptyCalorieList() { @@ -32,7 +32,7 @@ public static String deleteLogIndexMessage() { } public static String deleteLogNumberMessage() { - return "\t Please enter a valid positive integer for the entryID you wish to delete.\n" + + return "\t Please enter a valid positive integer for the caloriesID you wish to delete.\n" + CALORIES_DELETE_SAMPLE_INPUT; } //@@author From ea97182d7a3726734c37806d8c59efca316622d2 Mon Sep 17 00:00:00 2001 From: RexYong Date: Sun, 14 Apr 2024 21:35:00 +0800 Subject: [PATCH 341/414] Fix sorting of sleep list --- .../lifetrack/sleep/sleeplist/SleepList.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java index 2936b5a860..6877442292 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepList.java @@ -10,6 +10,8 @@ import java.io.FileNotFoundException; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; public class SleepList { @@ -51,10 +53,24 @@ public void addSleep(String input) { sleepList.add(newSleep); updateFile(); SleepListUi.printNewSleepEntry(newSleep); + //only sort if newly added date is earlier than date in final entry before adding entry + if (sleepList.size() > 1 && + sleepList.get(sleepList.size() - 2).getDate().compareTo(newSleep.getDate()) > 0 ) { + sortEntriesByDate(); + } } catch (InvalidInputException e) { System.out.println(e.getMessage()); } } + + public void sortEntriesByDate() { + Collections.sort(sleepList, new Comparator() { + @Override + public int compare(Entry entry1, Entry entry2) { + return entry1.getDate().compareTo(entry2.getDate()); + } + }); + } public void deleteSleep(String line) { try { From ff589e09d166a364518957a1a35f209729f9f0a6 Mon Sep 17 00:00:00 2001 From: RexYong Date: Sun, 14 Apr 2024 21:50:27 +0800 Subject: [PATCH 342/414] Fix exception messages printed twice for non integer calories --- .../lifetrack/system/parser/ParserCalories.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index 21a737ec0a..ae338b9018 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -93,9 +93,15 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv } } + int calories; //convert calories from string to integer - int calories = getIntegerCaloriesFromInput(strCalories); - checkCaloriesIsPositiveInteger(calories); + try { + calories = getIntegerCaloriesFromInput(strCalories); + checkCaloriesIsPositiveInteger(calories); + } catch (InvalidInputException e) { + throw new InvalidInputException(getIncorrectCaloriesInputMessage()); + } + assert calories > 0 : "Calories value must be a positive integer!"; //@@author rexyyong @@ -143,12 +149,12 @@ public static LocalDate getLocalDateFromInput(String strDate) throws DateTimePar * @param strCalories the string representation of calories * @return the integer value of calories parsed from the input string */ - private static int getIntegerCaloriesFromInput(String strCalories) { + private static int getIntegerCaloriesFromInput(String strCalories) throws InvalidInputException { int calories = 0; try { calories = Integer.parseInt(strCalories); } catch (NumberFormatException e) { - System.out.println(getIncorrectCaloriesInputMessage()); + throw new InvalidInputException(); } return calories; } @@ -215,7 +221,7 @@ private static int[] getMacrosFromInput(String macroString) throws InvalidInputE */ private static void checkCaloriesIsPositiveInteger(int calories) throws InvalidInputException { if (calories <= 0) { - throw new InvalidInputException(getIncorrectCaloriesInputMessage()); + throw new InvalidInputException(); } } From d148c6c5d3c548bca648e08ee29141620f66fb15 Mon Sep 17 00:00:00 2001 From: RexYong Date: Sun, 14 Apr 2024 22:01:13 +0800 Subject: [PATCH 343/414] Add limit of 5k calories per entry for calories in and calories out --- .../system/exceptions/InvalidInputExceptionMessage.java | 4 ++++ .../java/seedu/lifetrack/system/parser/ParserCalories.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java index 5c8588ba6f..f0464d4670 100644 --- a/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java +++ b/src/main/java/seedu/lifetrack/system/exceptions/InvalidInputExceptionMessage.java @@ -46,6 +46,10 @@ public static String getIncorrectCaloriesInputMessage() { return "\t Please input only positive integers into the calories field!"; } + public static String getCaloriesOverLimitMessage() { + return "\t Please ensure that calories is within the limit of 5000 calories per entry!"; + } + public static String getCaloriesMissingKeywordsMessage() { String message = "\t Please ensure that you have entered all keywords!\n"; return HEADER + message + CALORIES_IN_INPUT; diff --git a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java index ae338b9018..4ac689779f 100644 --- a/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java +++ b/src/main/java/seedu/lifetrack/system/parser/ParserCalories.java @@ -22,6 +22,7 @@ import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getMacrosInCaloriesOutMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInInputMessage; import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getWhitespaceInMacrosInputMessage; +import static seedu.lifetrack.system.exceptions.InvalidInputExceptionMessage.getCaloriesOverLimitMessage; public class ParserCalories { @@ -30,6 +31,7 @@ public class ParserCalories { private static final int FATS_IDX = 2; private static final int CALORIES_OUT_PADDING = 12; private static final int CALORIES_FIND_LENGTH = "calories find".length(); + private static final int CALORIES_LIMIT_5000 = 5000; /** * Parses a string input to create an Entry object representing calorie intake. @@ -102,6 +104,10 @@ public static Entry parseCaloriesInput(String input, int lastEntryID) throws Inv throw new InvalidInputException(getIncorrectCaloriesInputMessage()); } + if (calories > CALORIES_LIMIT_5000) { + throw new InvalidInputException(getCaloriesOverLimitMessage()); + } + assert calories > 0 : "Calories value must be a positive integer!"; //@@author rexyyong From 93b96a52d1353a671a7fa293a78863e1a24aa301 Mon Sep 17 00:00:00 2001 From: RexYong Date: Sun, 14 Apr 2024 22:06:44 +0800 Subject: [PATCH 344/414] Sleep duration from 1 dp to 2 dp --- src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java index ebc37c572e..63b3fc8094 100644 --- a/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java +++ b/src/main/java/seedu/lifetrack/sleep/sleeplist/SleepEntry.java @@ -49,7 +49,7 @@ public int getSleepEntryID() { public String toString() { return "\t Sleep ID: " +this.sleepEntryID+", Date: " + date + - ", Duration: " + String.format("%.1f", duration) + " hours"; + ", Duration: " + String.format("%.2f", duration) + " hours"; } public String toFileFriendlyString() { From 26ec35fc7c0112ac59e49fa2d2b7261b14881463 Mon Sep 17 00:00:00 2001 From: RexYong Date: Sun, 14 Apr 2024 22:13:22 +0800 Subject: [PATCH 345/414] Fix gradle errors --- .../java/seedu/lifetrack/CalorieListTest.java | 4 ++-- src/test/java/seedu/lifetrack/SleepListTest.java | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/seedu/lifetrack/CalorieListTest.java b/src/test/java/seedu/lifetrack/CalorieListTest.java index ea9b45456c..c9a50af627 100644 --- a/src/test/java/seedu/lifetrack/CalorieListTest.java +++ b/src/test/java/seedu/lifetrack/CalorieListTest.java @@ -18,8 +18,8 @@ public class CalorieListTest { private final String DELETE_ENTRY_HEADER = "\t The following calorie record has been successfully deleted!"; private final String DELETE_ENTRY_INVALID_INPUT = "\t Please enter a valid positive integer " + - "for the entryID you wish to delete.\n" + - "\t Example input: calories delete ENTRY_ID"; + "for the caloriesID you wish to delete.\n" + + "\t Example input: calories delete CALORIES_ID"; @Test public void addEntry_validInput_entryAdded() { diff --git a/src/test/java/seedu/lifetrack/SleepListTest.java b/src/test/java/seedu/lifetrack/SleepListTest.java index ec983311d9..00d48e877a 100644 --- a/src/test/java/seedu/lifetrack/SleepListTest.java +++ b/src/test/java/seedu/lifetrack/SleepListTest.java @@ -51,9 +51,9 @@ public void testPrintSleepListNonEmpty() { sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 0, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + + "\t \t Sleep ID: 0, Date: 2024-03-11, Duration: 7.50 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. \t Sleep ID: 0, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator; + "\t 1. \t Sleep ID: 0, Date: 2024-03-11, Duration: 7.50 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); } @@ -69,15 +69,15 @@ public void testPrintSleepListMultipleEntries() { sleepList.printSleepList(); System.setOut(System.out); String expectedOutput = "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 1, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + + "\t \t Sleep ID: 1, Date: 2024-03-11, Duration: 7.50 hours" + lineSeparator + "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 2, Date: 2024-04-09, Duration: 8.0 hours" + lineSeparator + + "\t \t Sleep ID: 2, Date: 2024-04-09, Duration: 8.00 hours" + lineSeparator + "\t The following entry has been added to your sleep list!" + lineSeparator + - "\t \t Sleep ID: 3, Date: 2024-02-21, Duration: 4.2 hours" + lineSeparator + + "\t \t Sleep ID: 3, Date: 2024-02-21, Duration: 4.20 hours" + lineSeparator + "\t Your Sleep List:" + lineSeparator + - "\t 1. \t Sleep ID: 1, Date: 2024-03-11, Duration: 7.5 hours" + lineSeparator + - "\t 2. \t Sleep ID: 2, Date: 2024-04-09, Duration: 8.0 hours" + lineSeparator + - "\t 3. \t Sleep ID: 3, Date: 2024-02-21, Duration: 4.2 hours" + lineSeparator; + "\t 1. \t Sleep ID: 3, Date: 2024-02-21, Duration: 4.20 hours" + lineSeparator + + "\t 2. \t Sleep ID: 1, Date: 2024-03-11, Duration: 7.50 hours" + lineSeparator + + "\t 3. \t Sleep ID: 2, Date: 2024-04-09, Duration: 8.00 hours" + lineSeparator; assertEquals(expectedOutput, outputStream.toString()); assertEquals(3, sleepList.getSize()); } From 923a2187602116b764bf5bea6616b6afc2542d8c Mon Sep 17 00:00:00 2001 From: paturikarthik Date: Sun, 14 Apr 2024 22:21:53 +0800 Subject: [PATCH 346/414] update DG --- docs/DeveloperGuide.md | 32 ++++++++++ docs/assets/UserClassDiagram.png | Bin 0 -> 56866 bytes docs/assets/UserDetailsSequenceDiagram.png | Bin 0 -> 27104 bytes docs/diagrams/UserClassDiagram.puml | 56 ++++++++++++++++++ docs/diagrams/UserDetailsSequenceDiagram.puml | 32 ++++++++++ src/main/java/seedu/lifetrack/ui/Ui.java | 3 +- src/main/java/seedu/lifetrack/user/User.java | 5 ++ 7 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 docs/assets/UserClassDiagram.png create mode 100644 docs/assets/UserDetailsSequenceDiagram.png create mode 100644 docs/diagrams/UserClassDiagram.puml create mode 100644 docs/diagrams/UserDetailsSequenceDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index c95ea8536b..c962b53210 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -143,6 +143,37 @@ The sequence diagram for this feature is shown below: ![CaloriesAddEntrySeqDiagram.png](assets%2FCaloriesAddEntrySeqDiagram.png) +### User Details Feature + +#### Implementation + +This functionality is facilitated by `UI`, `User` and `UserUI` Classes. It implements the following +operation, namely: +- `UI#handleUserInput(String, User)` +- `UI#handleUserCommands(String, User)` +- `User#getUserDetails()` +- `UserUI#printUserDetails(User)` + +This feature is activated when the user inputs `user details` command in the terminal. + +Given below is an example usage scenario and how this mechanism behaves at every step: + +- Step 1: When the user inputs the command `user details` in the terminal, + the string is sent to `UI#handleUserInput(String, User)` and + `UI#handleUserCommands(String, User)`, which calls `User#getUserDetails()`. + +- Step 2: Inside `User#getUserDetails()`, the function `UserUI#printUserDetails(User)` + is then called. The user's details are printed out. + +- Step 3: After printing out the details, the program returns and awaits + the next command typed in by user. + +The class and sequence diagram for this feature is shown below: +Unrelated attributes and Classes were excluded. + +![UserClassDiagram.png](assets/UserClassDiagram.png) +![UserDetailsSequenceDiagram.png](assets/UserDetailsSequenceDiagram.png) + ### Calculating calorie requirements based on a user`s goals #### Implementation @@ -168,6 +199,7 @@ Given below is an example usage scenario and how this mechanism behaves at every - Step 6: This value is set to `User.caloriesRequired` by `User#setCaloriesRequired(int)`. The Sequence Diagram for the above-mentioned process is as follows: + ![Sequence Diagram](assets/UserCalculateCaloriesSeqDiagram.png) diff --git a/docs/assets/UserClassDiagram.png b/docs/assets/UserClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..c94b28df6868c4e62c8d71849d75aade81ebccbd GIT binary patch literal 56866 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