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.
Navigation Tabs
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
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
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
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
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.
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:
-
WorkoutList
implements the interfaceReadOnlyWorkoutList
-
The
WorkoutList
is managed by theModelManager
-
FilteredWorkoutList
is an aggregation of oneWorkoutList
, to filter the view of the workout list shown to the user -
WorkoutList
composes of aUniqueWorkoutList
-
In turn, a
UniqueWorkoutList
contains any number ofWorkout
objects -
Each
Workout
is comprised of two things: aWorkoutName
and any number ofExercise
objects-
WorkoutName
contains the name of theWorkout
-
Exercise
is an exercise that belongs in aWorkout
-
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 thePath
of theWorkoutList
-
Model#setWorkoutListFilePath(Path workoutListFilePath)
- Sets thePath
of theWorkoutList
-
Model#setWorkoutList(ReadOnlyWorkoutList workoutList)
- Sets theWorkoutList
to be aReadOnlyWorkoutList
-
Model#getWorkoutList()
- Returns an unmodifiableReadOnlyWorkoutList
-
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 ofObservableList<Workout>
-
Model#updateFilteredWorkoutList(Predicate<Workout> predicate)
- Updates the filter ofFilteredWorkoutList
to show filtered views of the list to the user
Storage of Workouts
-
The
WorkoutListStorageManager
implements the interfaceWorkoutListStorage
. It also creates aJacksonWorkoutList
. -
The
JacksonWorkoutList
composes of any number ofJacksonWorkout
objects. -
In turn, each
JacksonWorkout
composes of aString
which is the workout name, and any number ofJacksonExercise
objects which are the exercises in each workout. -
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.
-
The
WorkoutCommandParser
creates allWorkout
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 theWorkoutExerciseCommandParser
. -
In turn,
WorkoutExerciseCommandParser
creates all workoutExercise
related Parsers. These parsers allow the user to add, edit and delete saidExercise
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.
-
When the user runs the command
workout find w/Arms Workout
, theLogicManager
will first take in the command, by calling theexecute()
function on it. -
The
ParserManager
then has toparse("workout find w/Arms Workout")
. -
Next, the
WorkoutCommandParser
has toparse("find w/Arms Workout")
for the command. -
Once the command has been parsed as a
FindCommand
, it will be passed on to theFindCommandParser
. -
The
FindCommandParser
can then create aFindCommand
. This is constructed with theWorkoutName
for "Arms Workout" (a low-level detail that has been abstracted from the diagram). -
The
FindCommand
can be returned to theLogicManager
, where it willexecute()
theFindCommand
. -
Model#updateFilteredWorkoutList(Predicate<Workout> predicate)
is used to update the view of the workout list to show the requested Workout(s), using thePredicateFilterWorkoutName
that is returned by theFindCommand
(a low-level detail that has been abstracted from the diagram). -
Finally, the resulting output message will be returned as the
CommandResult
.
Design Considerations
Aspect: Exercises in workout
-
Option 1: Use existing
Exercise
class inWorkout
-
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 theWorkout
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.