PROJECT: ZeroToOne

Overview

ZeroToOne is a desktop application that serves as an all-in-one exercise tracker and personal aide. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 26 kLoC.

Summary of contributions

  • Major enhancement: Added Workout feature.

    • What it does: Allows users to manage their workouts and the corresponding exercises in each of their workouts. Users can perform CRUD operations on workouts, and on exercises belonging to workouts. This whole set of functionality can be viewed from the Workout tab in the app.

    • Justification: This feature improves the product significantly because a user will want to group and organise different exercises into a workout. This allows the user to have different workouts for different fitness purposes.

    • Highlights: This enhancement affects the existing Exercise, Schedule and Log features. Future expansions will likely utilise Workouts because it has significant practical usage in order to work out. Hence, this feature required an in-depth analysis of design alternatives.

    • Credits & Enhancements: Some ideas for this feature was taken from the AB3 code. However, it ended up significantly different, as the purpose of the code differed. Hence, an additional layer of code was added to support Workout objects that in turn managed Exercise objects.

  • Major enhancement: Added the About display.

    • What it does: It is a separate tab in the app, which displays some basic information about the app, and also a full list of all commands that can be executed.

    • Justification: Allows users to easily access information about ZeroToOne in-app, without needing to refer to a User Guide.

  • Code contribution: [RepoSense]

Feature Component Functional Code Test Code

Workout Feature

Model

[Link]

[Link]

Commands

[Link]

[Link]

Parser

[Link]

[Link]

Storage

[Link]

[Link]

Ui

[Link]

NA

About Display

Command

[Link]

[Link]

Util

[Link]

NA

Ui

[Link]

NA

  • Other contributions:

    • Project management:

      • Managed releases v1.3 (1 release) on GitHub. Generated the jar file and updated the User Guide to reflect the details of this particular release.

    • Enhancements to existing features:

      • Did a major enhancement to the GUI overall style and colour scheme. (Pull request #173)

      • Implemented switching of UI views via CLI. (Pull requests #175, #176)

      • My organisation of the Model code (Pull request #94) was adopted by one of my team members (Pull request #172) and subsequently the whole team. (Pull request #228)

      • Wrote additional tests for my existing Workout feature to increase coverage of files related to my Workout feature. (Pull requests #182, #198)

      • Wrote additional tests for the Exercise feature to increase coverage. (Pull request #62)

    • Documentation:

      • Moved User Guide from our shared Google Docs over into the adoc file, for both our initial version of the User Guide and for the v1.3 release. (Pull requests #11, #115)

      • Created the initial set of user stories and use cases in the Developer Guide, so that the team could help to build upon it. (Pull request #8)

      • Helped the team define sections in the Developer Guide to fill in for better organisation. (Pull request #108)

    • Community:

      • I reviewed a total of 31+ PRs.

      • Regularly collated and updated a list of deadlines and deliverables in our group chat, to help everyone keep track of deadlines.

      • Created a full set of app mockups while the project was still in its early planning stages, including the key user flows and UI design. This acted as a concrete goal for us to work towards as we developed the app.

Contributions to the User Guide

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

User Interface Basics [Wong Chi Shan]

Before we delve into the commands that you can use, let us first explain the components of the User Interface, and how we can navigate through them.

UgUiComponents
Figure 1. Components of the User Interface

The navigation tabs allow you to easily access different parts of our application. Simply click on each tab to bring you to your desired feature! The coloured underline indicates which tab is currently activated and hence which feature you are currently accessing.

Result Display

This is where the results of your executed command will be displayed. The display will change according to the feature that you are currently using.

Feedback Display

Whenever you enter a command into the Command Box, the application will output a feedback message to you through this display!

Command Box

You type all of your commands in here. To execute the command, simply press the "Enter" button on your keyboard.

Viewing App Information [Wong Chi Shan]

Can’t remember the commands off the top of your head? Fret not. ZeroToOne provides you with a convenient way to view a list of all available commands that you can try. Simply enter the following command into the command box:

about
UgHelp
Figure 2. About Tab

The app view will switch tabs to show the About tab.

Managing Your Workouts [Wong Chi Shan]

After creating and modifying your exercises however you desire, it’s time to use those exercises to create workouts! In this section, we will walk you through how to manage all your workouts.

Creating a new workout

To create a new workout, simply type the following command:

workout create w/<workout_name>
Example use:
workout create w/Abs Workout
UgCreateWorkout
Figure 3. Creating A Workout

The feedback display will let you know if the creation of your workout was successful. The application view will also update to display your new workout!

Adding an exercise to a workout

After creating your workout, the next step is to add an exercise to it! To do so, simply enter the following command:

workout exercise add WORKOUT_ID EXERCISE_ID
Example use:
workout exercise add 1 3

If this is successful, the following message will be displayed in the feedback display:

Added exercise Bench Press to Arms Workout!
NOTE:
* You cannot add an empty exercise (i.e. exercise that does not contain any sets) to a workout
* WORKOUT_ID refers to the index of the workout in `workout list`
* EXERCISE_ID refers to the index of the exercise in `exercise list`

Editing an exercise in a workout

If you add the wrong exercise to a workout by mistake, or want to change a particular exercise to a different one, no worries! You can run this command:

workout exercise edit WORKOUT_ID EXERCISE_ID NEW_EXERCISE_ID
Example use:
workout exercise edit 1 2 3

This command allows you to edit an exercise in a workout, by replacing the exercise corresponding to EXERCISE_ID with the exercise corresponding to NEW_EXERCISE_ID. Hence, the order of exercises within the workout is retained. ZeroToOne will automatically update the exercise in the workout on your result display. If this is successful, the following message will be displayed in the feedback display:

Successfully edited Bench Press in Arms Workout to become Bicep Curl
NOTE:
* WORKOUT_ID refers to the index of the workout in `workout list`
* EXERCISE_ID refers to the index of the exercise in the workout, when `workout find w/<workout_name>` is executed
* NEW_EXERCISE_ID refers to the index of the exercise in `exercise list`

Deleting an exercise from a workout

If editing an exercise does not work for your purposes, you can also choose to simply delete any exercise from a workout. You may type the following command:

workout exercise delete WORKOUT_ID EXERCISE_ID
Example use:
workout exercise delete 1 3

ZeroToOne will delete the exercise with the specified exercise ID, from the workout with the specified workout ID. If this is successful, the following message will be displayed in the feedback display:

Successfully deleted Bench Press from Arms Workout
NOTE:
* WORKOUT_ID refers to the index of the workout in `workout list`
* EXERCISE_ID refers to the index of the exercise in the workout, when `workout find w/<workout_name>` is executed

Listing all workouts

Now that we have covered how to manage individual workouts, how about viewing all your workouts in one place? Simply type the following command:

workout list
UgWorkoutList
Figure 4. Workout List

ZeroToOne will show you a list of all the workouts you have created! From this list, you can see the names of all your workouts and their corresponding IDs. Additionally, it shows all the information about the exercises in each workout.

Finding a workout by name

You may find that you need to know a workout’s ID for some commands, or that you need to retrieve the details of a specific workout. Fret not! Simply type the following command:

workout find w/<workout_name>
Example use:
workout find w/Push Day
UgFindWorkout

ZeroToOne will return a list of all the workouts whose name matches the workout name you have typed into the command. From this command, you can find out the workout ID number of the workout you are looking for, as well as see the details of each exercise in the workout.

NOTE:
* <workout_name> is not case sensitive
* <workout_name> can be a partial substring of the actual workout name

Editing the name of a workout

If you ever want to edit the name of a workout, simply type this command:

workout edit WORKOUT_ID w/<new_workout_name>
Example use:
workout edit 1 w/Arms Training

ZeroToOne will update its display to show you the new workout name. If this is successful, the following message will be displayed in the feedback display:

Successfully edited name from Arms Workout to Arms Training
NOTE:
* WORKOUT_ID refers to the index of the workout in `workout list`

Deleting a workout

To delete a workout from ZeroToOne, simply type this command:

workout delete WORKOUT_ID
Example use:
workout delete 1

ZeroToOne will update its display to show you the updated list of workouts. If this is successful, the following message will be displayed in the feedback display:

Successfully deleted workout: Arms Training
NOTE:
* WORKOUT_ID refers to the index of the workout in `workout list`

Exporting a workout plan to a file (Coming in v2.0)

Do you enjoy sharing your fitness journey with your friends? Well, this feature allows you to share your workouts with your friends, so you can help each other out in the journey to become fit!

workout export WORKOUT_ID p/<file_path>
Example use:
workout export 1 p/data/myWorkout.txt

If this is successful, the following message will be displayed in the feedback display:

Successfully exported workout 1 to /data/myWorkout.txt!

Importing a workout plan from a file (Coming in v2.0)

ZeroToOne will import a workout from a plain text file stored in the specified file_path in your computer. This feature allows you to get workouts from your friends, so you can help each other out in the journey to become fit.

workout import w/<workout_name> p/<file_path>
Example use:
workout import w/My Friend's Workout p/data/myFriendsWorkout.txt

If this is successful, the following message will be displayed in the feedback display:

Successfully imported /data/myFriendsWorkout.txt as a new workout: My Friend's Workout!

Contributions to the Developer Guide

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

Workout [Wong Chi Shan]

Overview

The workout feature in ZeroToOne allows users to manage their workouts! Users will be able to create a workout, add exercises to it, as well as edit the workout and its exercises, and more.

WorkoutActivityDiagram
Figure 5. Activity Diagram for Creating a Workout

This is an activity diagram for the user to create a new workout. Essentially, the user will first create a workout with a name that does not contain any exercises yet. The user will then try to find exercises to add, and if their desired exercises cannot be found, the user will create them and add them into the workout.

Components

The Workout Manager consists of a WorkoutList, which contains a number of workouts that the user has created. Each workout consists of a WorkoutId, a WorkoutName and finally, an WorkoutExerciseList.

The following class diagram shows the overview of the Workout feature:

WorkoutClassDiagram
Figure 6. Class Diagram for Workout
  • WorkoutList implements the interface ReadOnlyWorkoutList

  • The WorkoutList is managed by the ModelManager

  • FilteredWorkoutList is an aggregation of one WorkoutList, to filter the view of the workout list shown to the user

  • WorkoutList composes of a UniqueWorkoutList

  • In turn, a UniqueWorkoutList contains any number of Workout objects

  • Each Workout is comprised of two things: a WorkoutName and any number of Exercise objects

    • WorkoutName contains the name of the Workout

    • Exercise is an exercise that belongs in a Workout

Each Workout can contain any number of Exercise objects, including zero. However, a Workout must not be empty i.e. have at least one Exercise, in order to be scheduled. This is implemented in the Schedule feature as referenced in [Schedule].

Workout Model

ZeroToOne’s Model extends the WorkoutModel. Here are all the functions to carry out workout-related activities:

  • Model#getWorkoutListFilePath() - Retrieves the Path of the WorkoutList

  • Model#setWorkoutListFilePath(Path workoutListFilePath) - Sets the Path of the WorkoutList

  • Model#setWorkoutList(ReadOnlyWorkoutList workoutList) - Sets the WorkoutList to be a ReadOnlyWorkoutList

  • Model#getWorkoutList() - Returns an unmodifiable ReadOnlyWorkoutList

  • Model#hasWorkout(Workout workout) - Returns true if a workout exists in the workout list

  • Model#deleteWorkout(Workout target) - Deletes a specified workout from the workout list

  • Model#addWorkout(Workout target) - Adds a new workout to the workout list

  • Model#setWorkout(Workout target, Workout editedWorkout) - Replaces a particular workout with an edited workout

  • Model#setExerciseInWorkout(Exercise target, Exercise editedExericse - Replaces a target exercise in any workout with the edited exercise

  • Model#deleteExerciseFromWorkout(Exercise exercise) - Deletes any instance of an exercise from all workouts

  • Model#getFilteredWorkoutList() - Returns an unmodifiable view of ObservableList<Workout>

  • Model#updateFilteredWorkoutList(Predicate<Workout> predicate) - Updates the filter of FilteredWorkoutList to show filtered views of the list to the user

Storage of Workouts

WorkoutStorageClassDiagram
Figure 7. Class Diagram for Workout Storage
  1. The WorkoutListStorageManager implements the interface WorkoutListStorage. It also creates a JacksonWorkoutList.

  2. The JacksonWorkoutList composes of any number of JacksonWorkout objects.

  3. In turn, each JacksonWorkout composes of a String which is the workout name, and any number of JacksonExercise objects which are the exercises in each workout.

  4. The relationship of the Storage component to the Model component is also shown.

Parser for Workouts

I will go on to explain the Parser structure for the workout commands.

WorkoutParserClassDiagram
Figure 8. Class Diagram of the Parser for Workouts
  • The WorkoutCommandParser creates all Workout related Parsers. These parsers allow the user to create, edit, delete and find workouts, as can be seen in the left hand side of the above diagram.

  • In addition, the WorkoutCommandParser creates the WorkoutExerciseCommandParser.

  • In turn, WorkoutExerciseCommandParser creates all workout Exercise related Parsers. These parsers allow the user to add, edit and delete said Exercise objects.

Sample Command Execution

To illustrate an example of a command from the Workout Manager, the following sequence diagram depicts flow of the program when the command workout find w/Arms Workout is run.

WorkoutSequenceDiagram
Figure 9. Sequence Diagram for Finding a Workout
  1. When the user runs the command workout find w/Arms Workout, the LogicManager will first take in the command, by calling the execute() function on it.

  2. The ParserManager then has to parse("workout find w/Arms Workout").

  3. Next, the WorkoutCommandParser has to parse("find w/Arms Workout") for the command.

  4. Once the command has been parsed as a FindCommand, it will be passed on to the FindCommandParser.

  5. The FindCommandParser can then create a FindCommand. This is constructed with the WorkoutName for "Arms Workout" (a low-level detail that has been abstracted from the diagram).

  6. The FindCommand can be returned to the LogicManager, where it will execute() the FindCommand.

  7. Model#updateFilteredWorkoutList(Predicate<Workout> predicate) is used to update the view of the workout list to show the requested Workout(s), using the PredicateFilterWorkoutName that is returned by the FindCommand (a low-level detail that has been abstracted from the diagram).

  8. Finally, the resulting output message will be returned as the CommandResult.

Design Considerations

Aspect: Exercises in workout
  • Option 1: Use existing Exercise class in Workout

    • Advantage: Building on an existing class is simpler and more intuitive.

    • Disadvantage: Introduces a dependency on the Exercise class

  • Option 2: Create a new WorkoutExercise class for the Workout class

    • Advantage: Creates an extra layer of abstraction and removes the dependency on Exercise.

    • Disadvantage: More code needed which may be redundant.

In the end, I decided to stick with Option 1. This is because creating a new WorkoutExercise class is redundant and unnecessary, when there is no functional difference between an Exercise and a WorkoutExercise, other than the context that they are referenced in. In addition, this would make the deletion of any instance of a particular Exercise from a Workout easier, when an Exercise is deleted from the ExerciseList. Hence, to simplify matters, using the existing Exercise class to construct workouts was better.