Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
J-Y-Yan committed Nov 3, 2023
2 parents c9d8ac5 + 6056f8b commit 90e81d7
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 10 deletions.
63 changes: 62 additions & 1 deletion docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,73 @@
# Developer Guide

## **Content**
- [Handing User Inputs & Displaying Output](#item-one)
- [Updates Exercise from Log feature](#item-two)
- [Adding a New Exercise Goal](#item-three)

## Acknowledgements

{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}

## Design & implementation

{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
<a id="item-one"></a>
### Handling User Inputs & Displaying Output

A lot of the initial project setup that deals with things like parsing, displaying outputs back to the user, exceptions, etc. are strongly inspired by the address book example project demoed in class. Specifically, we have classes that are responsible for various aspects of our project in order to follow the single-responsibility principle and limit the amount of coupling.

One specific design implementation we chose was having a Command superclass that all commands in our application will inherit from. This allows every command we write in the future to be guaranteed to possess all methods specified in the superclass. Therefore we can call these methods in our main function without needing to worry about the differences between various commands because they all inherit from the same parent. Another alternative we considered was simply making each separate command its own function and calling them from our parser class. However, we want to keep the commands as high level as possible and avoid calling more lower-level functions if possible so we can provide more encapsulation to each command. Therefore, we decided to go with this class-based command hierarchy that was also found in the example address book repo.

Below is a class diagram that describes how our various components interact with one another:

![image](https://github.com/AY2324S1-CS2113-F11-1/tp/assets/54857388/21bd2aa9-8e50-49d2-b888-50446115f922)

The parser class is responsible for reading the user input and creating specific command objects. All of these subcommands inherit from a general Command parent class which has an `execute` method that can be called regardless of child class and will return a CommandResult object. The CommandResult object dictates what info (string or list of items) needs to be returned to the user for viewing. Both CommandResult and TextUi (responsible for actually displaying data from CommandResult to user) will only print objects from classes that inherit from the Printable interface. This ensures that objects in the `relevantItems` list are all serializable as a human-readable string to be displayed to the end user.

<a id="item-two"></a>
### Update Exercises from Log feature

The proposed feature is to allow users to update exercises from the ExerciseLog class to allow for full CRUD functionality of the class. Currently, users are able to create exercises and log them as well as view all the exercises that they have created, but in the case of a typo, they are unable to update the log to accomodate for the user error. To improve the user experience, this update feature has been proposed to allow users to have a better experience using the app.

The update feature will be implemented by allowing users to select the task that they want to edit by specifying the month and day that they logged this exercise at, as well as the corresponding exercise name and the number of calories that the exercise has burned. This is because each exercise is stored within Day objects, and in order to check if two exercises are equivalent, we check both the exerciseName and caloriesBurned fields. Thus, these parameters will need to be specified by the user in order to update the exercise log.

Below is an example of how this feature will work:

Step 1: Upon starting the app, the ExerciseLog class is initialized and a log is created for the user to log their exercises.

<img width="661" alt="Screenshot 2023-10-22 at 22 21 04" src="https://github.com/Remy9926/tp/assets/95456114/933b3636-eba7-442b-bcf2-aec22ef49dba">

Step 2: The user calls the log command with the specified parameters to add a new exercise to the list. However, the user notices that they made a typo and want to change the details of the exercise that they just logged.

<img width="682" alt="Screenshot 2023-10-22 at 22 23 59" src="https://github.com/Remy9926/tp/assets/95456114/318cd321-6516-4163-a4ce-4a9e5d5edf7b">

Step 3: The user calls the update command with the information of the old exercise as well as the new information that the user wants to update with. With this, the update is done.

<img width="652" alt="Screenshot 2023-10-22 at 22 25 09" src="https://github.com/Remy9926/tp/assets/95456114/d07d5570-11fc-426c-877d-feb31a338f0a">

<a id="item-three"></a>
### Adding a New Exercise Goal
Setting up exercise goals is one of the major components in our FitNus app. It could guide users to do exercise in a more systematic way, while ensuring the possibility of keeping track of the record.

Inheritance Level:

As one of the commands input by user, the goal class should be inherited from the command class, with the general abstract method such as constructor, execution of command, etc.

Implementation:
The goalist class has some helper functions, including checkGoalInput to ensure the validity of user input, addGoal to add a new goal record into the goal list. The execution of goal command simply create a new goal record by first validating the user input, and create a goal record and finally return the result of creating the goal.

Future Enhancement:

1. List function and delete function

The list function is vital in visualization of the goal list while delete function helps to remove some redundant, incorrect, or achieved goals. Note that since other command classes may also need a list function or delete function, these two functions can be created for a general use and applicable for all commands. Alternative method is to create an interface class including both methods. Those commands which inherited the interface class should override all methods.

2. Achievement List function

This aims to keep the record of achieved goals. Once the user marks a goal as finished, the record will be automatically backed
up into the history list. All records in the history list should be sorted according to their finish time and in alphabetical order. A simple copy function with proper file input output maintenance should be good enough to achieve this function.

3. Achieve goal function: allow users to mark a goal as achieved and saved the record into backup history.


## Product scope
Expand Down
44 changes: 44 additions & 0 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@

{Give detailed description of each feature}

### Viewing help: `help`
Shows a message explaning how to access the help page.

Format: `help`

### Adding a todo: `todo`
Adds a new item to the list of todo items.

Expand All @@ -29,6 +34,45 @@ Example of usage:

`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`

### Adding an exercise: `log`
Adds a new exercise to the Exercise Log.

Format: `log MONTH DAY EXERCISE_NAME CALORIES_BURNED`

* The `MONTH` is an integer specifying the month in which the exercise was performed (1-12 inclusive)
* The `DAY` is an integer specifying the day of the month in which the exercise was performed
* The `EXERCISE_NAME` is a single word string specifying the type of exercise performed
* The `CALORIES_BURNED` is an integer specifying the number of calories burned by the exercise

Example of usage:

`log 1 26 Basketball 179`

`log 12 24 Volleyball 5`

### Updating an exercise: `update`
Updates the specified exercise within the Exercise Log.

Format: `update MONTH DAY OLD_EXERCISE_NAME OLD_CALORIES_BURNED NEW_EXERCISE_NAME NEW_CALORIES_BURNED`

* The `MONTH` is an integer specifying the month in which the exercise was performed (1-12 inclusive)
* The `DAY` is an integer specifying the day of the month in which the exercise was performed
* The `OLD_EXERCISE_NAME` is a single word string specifying the name of the exercise to be updated
* The `OLD_CALORIES_BURNED` is an integer specifying the number of calories burned by the exercise to be updated
* The `NEW_EXERCISE_NAME` is a single word string specifying the new exercise name
* The `NEW_CALORIES_BURNED` is an integer specifying the new number of calories burned

Example of usage:

`update 1 26 Basketball 179 Rugby 55`

`update 12 24 Volleyball 5 Hockey 98`

### Exiting the program: `exit`
Exits the program.

Format: `exit`

## FAQ

**Q**: How do I transfer my data to another computer?
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/seedu/duke/Duke.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import seedu.duke.commands.Command;
import seedu.duke.commands.CommandResult;
import seedu.duke.commands.ExitCommand;
import seedu.duke.commands.meal.MealCommand;
import seedu.duke.data.GoalList;
import seedu.duke.data.meal.Meal;
import seedu.duke.parser.Parser;
import seedu.duke.exerciselog.Log;
import seedu.duke.ui.TextUi;
import java.util.ArrayList;

/**
* Entry point of the Address Book application.
Expand All @@ -20,6 +23,7 @@ public class Duke {
public static final String VERSION = "AddressBook Level 2 - Version 1.0";
public static GoalList goals = new GoalList();
public static Log exerciseLog = new Log();
static ArrayList<Meal> meals = new ArrayList<Meal>();
private TextUi ui;

// private StorageFile storage;
Expand Down Expand Up @@ -48,7 +52,7 @@ private void start(String[] launchArgs) {
this.ui = new TextUi();
// this.storage = initializeStorage(launchArgs);
ui.showWelcomeMessage(VERSION, "storage.getPath()");

MealCommand.setMeals(meals);
} catch (Exception e) { // TODO: change to specific storage exceptions later
ui.showInitFailedMessage();
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package seedu.duke.commands.logcommands;

import java.util.List;

import seedu.duke.commands.Command;
import seedu.duke.commands.CommandResult;
import seedu.duke.Duke;

public class UpdateLogCommand extends Command {
public static final String COMMAND_WORD = "update";
public String feedbackToUser;
private List<String> exerciseDetails;

public UpdateLogCommand() {
super();
}

public UpdateLogCommand(List<String> exerciseDetails) {
super();
this.exerciseDetails = exerciseDetails;
}

/**
* Extracts the details of the update command and updates the specified exercise.
*
* @return CommandResult that tells the user whether an exercise was successfully updated.
*/
public CommandResult execute() {
int month = Integer.parseInt(exerciseDetails.get(0));
int day = Integer.parseInt(exerciseDetails.get(1));
String oldExerciseName = exerciseDetails.get(2);
int oldCaloriesBurned = Integer.parseInt(exerciseDetails.get(3));
String newExerciseName = exerciseDetails.get(4);
int newCaloriesBurned = Integer.parseInt(exerciseDetails.get(5));

feedbackToUser = Duke.exerciseLog.updateExercise(month, day, oldExerciseName, oldCaloriesBurned,
newExerciseName, newCaloriesBurned) ? "Exercise successfully updated!" :
"Could not find exercise to update.";

return new CommandResult(feedbackToUser);
}
}
14 changes: 12 additions & 2 deletions src/main/java/seedu/duke/commands/meal/MealCommand.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
package seedu.duke.commands.meal;

import java.util.List;
import java.util.ArrayList;

import seedu.duke.commands.Command;
import seedu.duke.data.meal.Meal;

public class MealCommand extends Command {
public static final String COMMAND_WORD = "test";
protected static ArrayList<Meal> meals;

public static void setMeals(ArrayList<Meal> meals) {
MealCommand.meals = meals;
public MealCommand() {
super();
}

public MealCommand(List<String> meals) {

}

public static void setMeals(List<Meal> meals) {
MealCommand.meals = new ArrayList<>(meals);
}

protected void checkArgument(String[] arguments, int validArgumentAmount) throws Exception {
Expand Down
8 changes: 2 additions & 6 deletions src/main/java/seedu/duke/data/meal/Meal.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package seedu.duke.data.meal;

import java.time.LocalDateTime;

import seedu.duke.data.DateTime;

public class Meal {
public String name;
public int calories;
public DateTime time;
// public DateTime time;

public Meal(String name, int calories) throws Exception {
this.name = name;
this.calories = calories;
this.time = new DateTime(LocalDateTime.now().toString());
// this.time = new DateTime(LocalDateTime.now().toString());
}
}
23 changes: 23 additions & 0 deletions src/main/java/seedu/duke/exerciselog/Day.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,29 @@ public boolean containsExercises() {
return !exercises.isEmpty();
}

/**
* Given the specific details of an Exercise, finds the exercise in the log and updates it to the new specified
* details.
*
* @param oldExerciseName original name of the exercise to be updated
* @param oldCaloriesBurned original number of calories burned by the exercise to be updated
* @param newExerciseName the new name to update the exercise to
* @param newCaloriesBurned the new number of calories burned to update the exercise to
* @return true when an exercise is found and updated, false otherwise
*/
public boolean updateExercise(String oldExerciseName, int oldCaloriesBurned, String newExerciseName,
int newCaloriesBurned) {
int index = exercises.indexOf(new Exercise(oldExerciseName, oldCaloriesBurned));
if (index == -1) {
return false;
}

Exercise targetExercise = exercises.get(index);
targetExercise.setExerciseName(newExerciseName);
targetExercise.setCaloriesBurned(newCaloriesBurned);
return true;
}

/**
* Returns a string representing all the exercises contained for the Day.
*
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/seedu/duke/exerciselog/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ public int getNumberOfExercisesForDay(int month, int day) {
return exerciseLog.get(month - 1).getNumberOfExercisesForDay(day);
}

/**
* Given the specific details of an Exercise, finds the exercise in the log on the given month and day, and updates
* it to the new specified details.
*
* @param month the month of the exercise to be updated
* @param day the day of the month of the exercise to be updated
* @param oldExerciseName original name of the exercise to be updated
* @param oldCaloriesBurned original number of calories burned by the exercise to be updated
* @param newExerciseName the new name to update the exercise to
* @param newCaloriesBurned the new number of calories burned to update the exercise to
* @return true if an exercise is successfully updated, false otherwise
*/
public boolean updateExercise(int month, int day, String oldExerciseName, int oldCaloriesBurned,
String newExerciseName, int newCaloriesBurned) {
return exerciseLog.get(month - 1).updateExercise(day, oldExerciseName, oldCaloriesBurned, newExerciseName,
newCaloriesBurned);
}

/**
* Returns the Month object of the specified month.
*
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/seedu/duke/exerciselog/Month.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@ public int getNumberOfExercisesForDay(int day) {
return dates.get(day - 1).getNumberOfExercises();
}

/**
* Given the specific details of an Exercise, finds the exercise in the log on the given day, and updates it to the
* new specified details.
*
* @param day the day of the month of the exercise to be updated
* @param oldExerciseName original name of the exercise to be updated
* @param oldCaloriesBurned original number of calories burned by the exercise to be updated
* @param newExerciseName the new name to update the exercise to
* @param newCaloriesBurned the new number of calories burned to update the exercise to
* @return true if an exercise is successfully updated, false otherwise
*/
public boolean updateExercise(int day, String oldExerciseName, int oldCaloriesBurned, String newExerciseName,
int newCaloriesBurned) {
return dates.get(day - 1).updateExercise(oldExerciseName, oldCaloriesBurned, newExerciseName,
newCaloriesBurned);
}

/**
* Returns the name of the month
*
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/seedu/duke/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import seedu.duke.commands.logcommands.LogCommand;
import seedu.duke.commands.logcommands.DeleteLogCommand;
import seedu.duke.commands.logcommands.ViewLogCommand;
import seedu.duke.commands.logcommands.UpdateLogCommand;
import seedu.duke.commands.meal.MealCommand;
import seedu.duke.data.exception.IllegalValueException;

/**
Expand Down Expand Up @@ -83,6 +85,12 @@ public Command parseCommand(String userInput) {
case ViewLogCommand.COMMAND_WORD:
return new ViewLogCommand(Arrays.asList(arguments.trim().split(" ")));

case UpdateLogCommand.COMMAND_WORD:
return new UpdateLogCommand(Arrays.asList(arguments.trim().split(" ")));

case MealCommand.COMMAND_WORD:
return new MealCommand(Arrays.asList(arguments.trim().split(" ")));

case GoalCommand.COMMAND_WORD:
return new GoalCommand(userInput);

Expand Down

0 comments on commit 90e81d7

Please sign in to comment.