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 the Exercise Feature
-
What it does: This feature allows users to create, read, update and delete exercises and exercise sets.
-
Justification: The feature formed the base for all the other features to build upon. For example, the Workout feature required exercises to have been created first.
-
Highlights: Since this feature forms the base for all other features, the design of this feature will inevitably affect them, as well as features that will be implemented in the future. As such, I ensured that the feature is compliant with the SOLID priciples. All relevant classes are abstracted and well documented, such that each class only has one single responsibility. As such, this allows the Exercise feature to be extensible by any other developers.
-
-
Major enhancement: added the Splash Screen
-
What it does: The splash screen will load up first while the application runs through its initialisation process.
-
Justification: This enables users to feel that the application is more responsive, enhancing the user experience of the application.
-
Highlights: While the application runs locally and the loading process is relatively fast right now, the aim of this enhancement is for future-proofing purposes. It allows developers to further extend the application using other external sources such as an external DBMS, while retaining the "responsiveness" of the application.
-
Credits: Some ideas to implement this feature were taken from this tutorial created by Oracle.
-
-
Code contributed:
-
Other contributions:
-
Project management:
-
Lead release
v1.2
on GitHub -
Explained the original codebase to developers
-
-
Enhancements to existing features:
-
Headed the initial code refactorisation
-
Replaced all references to AddressBook to ZeroToOne
-
Modularised the inital AddressBook-Level3 codebase that allowed developers to easily extend the project
-
Pull Request #66
-
-
-
Documentation:
-
Helped Chi Shan with the editing of the User Guide and Developer Guide to ensure that the English used is friendly and simpler to understand.
-
Edited high-level UML Diagrams of the application to fit the latest update. Pull Request #228
-
-
Community:
-
Tools:
-
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. |
Managing Your Exercises [Aloysius Chan]
The commands in this section allows you to manage your customised exercises in ZeroToOne. These exercises will eventually be the building blocks of a workout.
Creating a new exercise
To create a new exercise in ZeroToOne, simply enter this command into the command box:
exercise create e/<exercise_name>
Example use:
exercise create e/squat
The newly created exercise will be automatically added to the bottom of the exercise list. This exercise will not contain any sets at this point.
NOTE: <exercise_name>
has to be a string, consisting of only alphanumeric characters
Adding a set to an exercise
After you have created a new exercise in ZeroToOne, the next step is to add a set to the exercise! To add a set, simply enter this command:
exercise set add EXERCISE_ID r/<num_of_reps> m/<weight>
Example use:
exercise set add 2 r/2 m/30
The exercise set will be automatically appended to the current list of sets in the exercise. The user interface will be updated to show the edited exercise.
NOTE:
-
This command assumes that you have already created an exercise under
EXERCISE_ID
. If you have not created the exercise, refer to the section on “Creating a new exercise” first. -
EXERCISE_ID
refers to the index of the exercise inexercise list
-
<num_of_reps>
should be a positive integer -
<weight>
should be a positive integer between 1 and 1000
Editing a set in an exercise
Changed your mind on the details of an exercise set? No worries, you can edit the information in an exercise set by simply entering this command:
exercise set edit EXERCISE_ID SET_ID r/<num_of_reps> m/<weight>
Example use:
exercise set edit 1 1 r/20 m/30
The exercise set will be automatically updated in the exercise list. If so, the following message will be displayed in the feedback display:
Edited exercise set: Deadlift
NOTE:
-
EXERCISE_ID
refers to the index of the exercise inexercise list
-
SET_ID
refers to the index of the set in the exercise -
<num_of_reps>
has to be a positive integer -
<weight>
has to be a positive integer between 1 and 1000
Deleting a set in an exercise
Want to delete an exercise set from the exercise? You can do so by simply entering this command:
exercise set delete EXERCISE_ID SET_ID
Example use:
exercise set delete 1 2
The exercise set will be removed from the exercise, and the view will automatically update to show that the exercise no longer contains that set. If this is successful, the following message will be displayed in the feedback display:
Deleted Exercise Set: Deadlift
NOTE:
-
EXERCISE_ID
refers to the index of the exercise inexercise list
-
SET_ID
refers to the index of the set in the exercise
Listing all exercises
To show a list of exercises that you have created in ZeroToOne, simply enter this command into the command box:
exercise list
The User Interface will automatically switch to the “Exercise” tab, and the result display will automatically update with the list of exercises.
Finding an exercise by name
To find and view the information of a particular exercise that you have previously created, you can simply enter this command:
exercise find e/<exercise_name>
Example use:
exercise find e/Bench Press
The Result Display will automatically update to only show exercises that match the search keyword.
NOTE:
-
<exercise_name>
has to be a String, consisting of only Alphanumeric characters -
<exercise_name>
can be a partial substring of the full exercise name -
<exercise_name>
is not case-sensitive
Changing an exercise’s name
Made a mistake while creating the exercise’s name? You can change the exercise name by simply running this command in the command box:
exercise edit EXERCISE_ID e/<exercise_name>
Example use:
exercise edit 1 e/Squat
The exercise in ZeroToOne will be automatically updated to show its new name. If this is successful, the following message will be displayed in the feedback display:
Edited exercise: Squat
NOTE:
-
EXERCISE_ID
refers to the index of the exercise inexercise list
-
<exercise_name>
has to be a String, consisting of only Alphanumeric characters
Deleting an exercise
Want to remove an exercise from ZeroToOne? You can do so by entering this command into the command box:
exercise delete EXERCISE_ID
Example use:
exercise delete 1
The exercise will be removed from ZeroToOne. At the same time, all current workouts that contain this exercise will also have this exercise removed. If this is successful, the following message will be displayed in the feedback display:
Deleted Exercise: Deadlift
NOTE:
-
EXERCISE_ID
refers to the index of the exercise inexercise list
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. |
Exercise [Aloysius Chan]
Overview
The Exercise feature forms the basic building block for the application. It allows users to CRUD exercises, which will then be used for the creation of workouts. The command is prefixed by the keyword exercise
. Available commands are create
, edit
, list
, delete
and many more.
Implementation
The following portion will explain in-detail each component of the Exercise feature.
Model
The figure above depicts the Model of the Exercise feature. Starting from the most primitive type:
-
NumReps
stores the number of repetitions a person has to complete for a particular exercise set -
Weight
stores the weight in kg that a person has to complete for a particular exercise set -
ExerciseSet
represents an exercise set that a person has to complete. EachExerciseSet
comprises of exactly 1NumReps
object and 1Weight
object -
ExerciseName
stores the name of a particular exercise -
Exercise
represents a collection of exercise sets that a person has to complete. EachExercise
will consists of exactly 1ExerciseName
object and any number ofExerciseSet
(including zero) -
UniqueExerciseList
represents a collection ofExercise
objects. There can be any number ofExercise
objects inUniqueExerciseList
, but they must be unique. -
ExerciseList
implements the interfaceReadOnlyExerciseList
that ensures that the outward-facing exercise list is unmodifiable by other modules. TheExerciseList
object has exactly 1UniqueExerciseList
for storage purposes. -
ExerciseList
is controlled by theModelManager
.
Do note that some of the Exercise
objects in the ExerciseList
are also referenced in the WorkoutList
. For more information, refer to the Workout
implementation.
Storage
The Storage component provides the functionalities that enable the persistent storage of the model. Starting from the most primitive type:
-
JacksonExerciseSet
contains exactly twoString
objects, one for the number of repetitions and one for the weight. This class has a dependency to theNumReps
andWeight
model due to thetoModelType()
function. This function convertsJacksonExerciseSet
into the model’sExerciseSet
so that it can be used in other parts of the application. -
JacksonExercise
contains exactly oneString
object that represents the exercise name, and any number ofJacksonExerciseSet
objects. Similarly, it has a dependency toExerciseName
,ExerciseSet
andExercise
object due to thetoModelType()
function. -
JacksonExerciseList
is the persistent storage for theExerciseList
model, and it contains any number ofJacksonExercise
objects. -
ExerciseListStorageManager
implements theExerciseListStorage
which provides certain functionalities required for the storage to work properly. TheExerciseListStorageManager
is controlled by theStorageManager
.
Logic - Commands
The Exercise Commands package stores the business logic of the exercise feature. The commands are organised in a hierarchical fashion, in the order of precedence in a valid input. For example, SetCommand
inherits from ExerciseCommand
as the set
comes after exercise
in the input exercise set
.
Each command contains a COMMAND_WORD
which is a single word that is unique to the command. Each command also implements an execute
method that represents the logic of the command. Instructions to control the model, storage and view are stored inside this method.
Logic - Parsers
The parsers are responsible for parsing a user input into a Command
object. For the Exercise component, there are parsers for every command that accepts user arguments. For example, since exercise list
does not take in any argument, there is no parser for the ListCommand
. After parsing the user input, the parser will return Command
object to the caller, which will execute the command via the execute
method.
Sample Command Execution
This section will illustrate an example of an exercise command execution using the input exercise create e/Bench Press
.
In this portion, we will trace the sequence diagram of the exercise create
command to better understand the internals of the Exercise feature.
-
The user enters the command
exercise create e/Bench Press
-
LogicManager
will pass the command to theParserManager
for parsing -
ParserManager
upon seeing that the command is prefixed byexercise
creates aExerciseCommandParser
-
ParserManager
then passcreate e/Bench Press
toExerciseCommandParser
-
ExerciseCommandParser
upon seeing that the command is prefixed bycreate
creates aCreateCommandParser
-
ExerciseCommandParser
then pass the argumente/Bench Press
toCreateCommandParser
-
CreateCommandParser
then attempts to create anExerciseName
object using theString
in the argument -
Using the
ExerciseName
,CreateCommandParser
then create aCreateCommand
object with the exercise name -
The
CreateCommand
is then passed back to theLogicManager
-
LogicManager
callsc.execute()
-
CreateCommand
will attempt to create anExercise
using the exercise name -
After creating the
Exercise
object, theCreateCommand
will attempt to store the new exercise by calling theaddExercise
method ofModel
-
After the exercise is successfully added, a
CommandResult
object is created -
This result is then passed back to the
LogicManager
which will display the output on the GUI
Summary
At this point, you should have gather enough information to start developing the Exercise feature. As a summary, this is a sample Activity Diagram that depicts a user flow when they want to edit an exercise set.
Design Considerations
Parser Component
One of the consideration while designing was that the commands in exercise are extremely nested. We have commands such as exercise set create r/1 m/10
. While we could have chucked all the parsing in ExerciseSetCreateParser
class, we realised that it will be better if we were to abstract the parser into separate classes. This allows us to group the functionalities of the parser in a single file. For example, ExerciseCommandParser
will parse any string that has the word exercise
as the prefix. SetCommandParser
will do so for a prefix of set
. This means that for the above command, while we have to go through multiple parsers which can make the performance of the application suffer, each of the parsers have a single responsibility which makes it a better design choice.
Model Component
For the Model component, note that Exercise
objects are supposed to be unique whereas ExerciseSet
objects are not. This is created due to our observations of the workout regimes in the real world.
For ExerciseSet
, while set weights and number of repetitions tend to vary during an exercise, users may want to have the freedom to do multiple sets with the same configuration during the course of the exercise. Hence, it is unwise to make it unique.
However, for exercises, we noted that users tend to reuse the same exercise throughout different workout plans. At the same time, there is a high chance of users creating duplicate exercises when the number of exercises in the application increases significantly. Therefore, we chose to make Exercise
a unique object instead.