Overview
FitBiz is a fitness business management application, specially made for fitness coaches to manage their clients. It is primarily a desktop application where the user interacts via the Command Line Interface (CLI), and views data via the Graphical User Interface (GUI). This project is written in Java 11, packaged using Gradle, and uses JavaFX for the GUI.
Summary of Contributions
-
Major enhancement: developed the graph feature, which allows users to conveniently plot and display the progress made by a client for a certain exercise over time. Users can immediately see if the client has improved for that exercise over the months.
-
What it does: allows users to plot and display the graph of the specified exercise and specified y-axis from the stipulated start to end dates. Users have a choice of y-axis so that they can focus on one of the different attributes of an exercise, like the reps done or the weights lifted.
-
Justification: visualisation is always better than looking through a long list of exercises to check if a client has improved for an exercise. It is also much faster and easier to look at a graph to check for progress. The flexibility in axis type and dates also allows users to customise their graph to their needs.
-
Highlights: developed a
Graph
model that has an axis type, exercise name, start and end dates. Checks are also in place to make sure that there are data points to be plotted, else there will be an error message. Sanity checks like the check that the start date cannot be later then the end date are also implemented.
-
-
Major enhancement: developed the personal best feature, which allows users to view the personal best records of their exercises immediately after keying in an exercise session.
-
What it does: allows users to efficiently and conveniently access their clients' personal best while updating the personal best table whenever changes to the exercise table is made.
-
Justification: instead of having to manually look through the entire exercise table to find a client’s personal best, the personal best will be immediately displayed when the user views a certain client. Changes made to the client’s exercise list will also be updated and displayed immediately.
-
Highlights: this feature was made to provide convenience to the user so that they do not have to go through extra commands or look through the entire exercise list to find their client’s personal best. This feature is also related to the graph feature as users will be able to see the progress of how a client achieves a personal best from the graph.
-
-
Minor enhancement: hooked the
view-c
command to FitBiz and added relevant tests to FitBiz. -
Minor enhancement: added the height attribute to the
Client
class. -
Overall contributions: 1.5K+ LOC | 90+ commits | 18 PRs merged | 50+ PRs reviewed
-
Other contributions:
-
Project management:
-
Managed labels and issues on the GitHub issue tracker
-
-
Enhancements to existing/teammates' features:
-
Documentation:
-
Summary:
-
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. |
Delete a client: delete-c
--- Li Zi Ying
You find that your client list contains a client who is no longer working with you and you would like to remove his details from your client list. delete-c
allows you to delete the client at the specified index from your list of clients.
Format: delete-c INDEX
Parameters
This section acts as a summary of the important things to note when using delete-c
including their parameters.
Parameters | Important points to note |
---|---|
|
|
Examples
Let’s say that you want to delete the client named "Alex Yeoh" in client list. You can simply use the delete-c
command as shown.
-
Type
delete-c 1
into the Command Box and pressEnter
to execute it. -
Result Box will display the message of the details of the deleted client. You can now see that the Client List no longer contains the deleted client.
Common errors/problems
You might face some errors or difficulties when you use delete-c
. In this section, you will be able to understand these errors and resolve them.
No index specified
If you type in delete-c
without specifying the INDEX
, no client will be deleted and an error message will be shown.
This happens because there is no INDEX
specified. To correct this, you have to enter a valid INDEX
after delete-c
.
Index out of range
If you key in an INDEX
that is out of range, that is if there are only 4 clients in the list but you try to key in delete-c 5
, no client will be deleted and an error message will be shown.
To correct this, you have to enter a valid INDEX
that is not more than the number of clients in the client list.
View a client profile: view-c
--- Li Zi Ying
You might want to view the complete information of a certain client in your client list. This complete information includes the client’s full details, their exercise table and personal best table. view-c INDEX
shows all available information of the client at the specified index.
Format: view-c INDEX
Parameters
This section acts as a summary of the important things to note when using view-c
including their parameters.
Parameters | Important points to note |
---|---|
|
|
Examples
Let’s say that you want to view the details of the client named "Alex Yeoh" in client list. You can simply use the view-c
command as shown.
-
Type
view-c 1
into the Command Box and pressEnter
to execute it. -
Result Box will display the message telling you the client currently in view. You can now see that the Client View is now populated with the client’s details, the exercise table and the personal best table.
Common errors/problems
You might face some errors or difficulties when you use view-c
. In this section, you will be able to understand these errors and resolve them.
No index specified
If you type in view-c
without specifying the INDEX
, no client will be viewed and an error message will be shown.
This happens because there is no INDEX
specified. To correct this, you have to enter a valid INDEX
after view-c
.
Index out of range
If you key in an INDEX
that is out of range, that is if there are only 5 clients in the list but you try to key in view-c 10
, no client will be viewed and an error message will be shown.
To correct this, you have to enter a valid INDEX
that is not more than the number of clients in the client list.
Display visualisations of training progress: graph
--- Li Zi Ying
graph
allows you to see a graphical visualisation of a client’s exercise progress within a specified timeframe, so that you can easily track your client’s progress and improvement.
Format: n/EXERCISE_NAME a/Y_AXIS sd/START_DATE ed/END_DATE
Parameters
This section acts as a summary of important things to note when using graph
including their parameters.
Parameters | Important points to note |
---|---|
|
Substitute
|
|
Substitute
|
|
Substitute
The range of
|
|
Substitute
The range of
|
Example
Let’s say that you wish to view the exercise graph of Alex Yeoh
for the exercise Sumo Deadlift
from the date 01-01-2020
to 13-04-2020
and you want to focus on the weight
he lifted during the exercise.
You can use the graph
command to view the graph of the exercise following the steps as shown:
-
First view the client that you want to view the exercise graph of. For information on how to view the client, you can refer to the
view-c
section. For this example, we will view the graph ofAlex Yeoh
, who is the first client in the client list. After enteringview-c 1
, you will see the full detailed information and a table of recorded exercises of clientAlex Yeoh
. -
Next, simply type the graph command
graph n/Sumo Deadlift a/weight sd/01-01-2020 ed/13-04-2020
into the Command Box. -
The graph will appear separately in a window.
-
You will see the Result Box informing you of the current graph displayed. Note that any changes made using
add-e, edit-e, delete-e
will not be reflected in the graph.
Common errors/problems
You might face some errors or difficulties when you use graph
. In this section, you will be able to understand these errors and resolve them. You will also get a better understanding of the reply from the Result Box when using graph
.
No exercise within stipulated timeframe
If you have keyed in the graph
command in the correct format but the graph is not appearing, you might want to check the start and end dates in your command. In the example below, the exercise clearly exists in the exercise list, but not within the stipuated timeframe. Therefore the graph will not be displayed.
-
The exercise
Sumo Deadlift
clearly exists in the exercise list. However, there are no records ofSumo Deadlifts
from01-01-2020
to01-02-2020
. -
The graph will not appear and an error message will be shown.
To correct this, change the timeframe to one where there is at least one existing record of the specified exercise in the current exercise list.
No exercise for stipulated axis
If you have keyed in the graph
command in the correct format but the graph is not appearing, you might want to check the y-axis in your command. In the example below, the exercise clearly exists in the exercise list, but there are no inputs for the stipulated y-axis. Therefore the graph will not be displayed.
-
The exercise
Push Up
clearly exists in the given timeframe. However, there are no inputs for weight as seen in the exercise table. -
The graph will not appear and an error message will be shown.
To correct this, change the axis to one where there is at least one non-empty input of the exercise in the current exercise list. You could also choose to view another exercise’s graph instead.
Invalid time frame
If you have keyed in the graph
command in the command box but the graph is not appearing, you might want to check the start and end dates. In the example below, the command format looks correct but the start date is later than the end date. This is not allowed and therefore the graph will not be displayed.
-
The start date
01-04-2020
is chronologically later than the end date01-02-2020
. -
The graph will not appear and an error message will be shown.
To correct this, the start date has to be earlier or the same as the end date.
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. |
Personal Best --- Li Zi Ying
This feature allows the users (ie. gym managers) to view the personal bests of exercises done by a client. This information is displayed in a table form, after the command view-c INDEX
is called.
Implementation
The personal best feature is facilitated by the model PersonalBest
, and the logic behind it is in PersonalBestFinder
. The behaviour of this feature determines the personal best of each exercise done by the client based on these considerations:
-
If the
ExerciseWeight
attribute is recorded in theExercise
, then theExerciseWeight
is used as comparison -
If there is no
ExerciseWeight
recorded in theExercise
, thenExerciseReps
will be used as comparison -
If neither of
ExerciseWeight
andExerciseReps
are recorded into theExercise
, then this particular exercise will not be put into the Exercise Personal Best table-
However, if the another
Exercise
of the same name is added in the future withExerciseWeight
and/orExerciseReps
specified, then the personal best of this exercise will still be calculated and shown in the Exercise Personal Best table
-
-
Note that
ExerciseSets
, although an attribute of theExercise
model, is not considered when checking forPersonalBest
as the number of sets of an exercise does not contribute to a personal best record
A simplified class diagram of the classes involved in this feature is given below:
In the following sequence diagram, we trace the execution for when the user decides to enter the command view-c
into FitBiz:
The explanation for the sequence diagram is as follows: when the user inputs view-c
, add-e
, edit-c
or delete-c
, PersonalBestFinder#generateAndSetPersonalBest
is called, taking the client currently in view as the parameter. PersonalBestFinder#generateAndSetPersonalBest
then retrieves client’s list of exercises using Client#getExerciseList
and creates a new HashMap
, where the key
is ExerciseName
and the value
is Exercise
. Then the personal bests of each exercise of the client in view are generated using the above considerations. Finally the list of personal bests is set using PersonalBest#setPersonalBest
.
Design Considerations
In designing this feature, we had to decide on the placement of the PersonalBest
class in the model to comply with the OOP standards. Currently, the PersonalBest
model has a whole-part relationship with Client
, with Client
being the whole and PersonalBest
being a part of Client
. The alternative is to consider PersonalBest
as a part of Exercise
instead.
Put PersonalBest as a part of Client (Chosen) |
Put PersonalBest as a part of Exercise |
|
---|---|---|
Adhering to OOP standards (Coupling and Cohesion) |
Increases cohesion as it logically makes more sense, currently each client has a list of exercises to themselves, and thus each client should also have a list of |
Increases coupling between the logic and model as every time the commands |
Ease of Implementation |
Might have significant conflicts as the |
Easier to implemention as methods related to |
We decided to use the first approach of placing PersonalBest
as a part of Client
instead of Exercise
. There are multiple reasons for our choice as mentioned below.
We want to maintain the OOP structure of the program. Logically, the personal best should belong to the client as the list of exercises belongs to the client. As the list of exercises is unique to every client, the personal best should also be so. We also do not want to increase coupling of the program as mentioned in the table above.
Moreover, even though personal best is generated using the list of exercises in the client, it can be instantiated even without an exercise list. Therefore it does not require the exercise class to exist and does not have a whole-part relationship with exercise. Coupling will also be increased as the client will be relying on the exercise class to generate the personal best. Therefore, the final choice was to place the personal best under client, with every client having their own personal best attribute.
This personal best feature also leads into the Graph
feature, which will be discussed in the next section, where we plot a graph of the client’s progress of a specified exercise.
Graph --- Li Zi Ying
This feature allows users to see the progress graph of the current client in view. The user has to specify the exercise name, the y-axis (either weights or reps), the start date and the end date. There has to be existing exercises in the client’s exercise list for the specified axis and time period for the graph to be plotted, if no graph can be plotted, an error will be thrown.
Implementation
The graph mechanism is faciliated by the model class Graph
, which contains the details of the graph. These include ExerciseName
, Axis
, StartDate
and EndDate
. The figure below is a UML class diagram to illustrate the Graph
model.
These attributes are bounded by these characteristics:
. ExerciseName
can only be alphanumeric characters
. Axis
can either be reps
or weight
only, case insensitive (sets
are not considered due to the same reasoning in the above section)
. Earliest StartDate
possible can only be one year before the current date and cannot be after EndDate
. StartDate
also cannot be a future date
. Earliest EndDate
possible can only be one year before the current date and cannot be before StartDate
. EndDate
also cannot be a future date
Here is an activity diagram displaying the steps taken when FitBiz receives a user input for the graph
command:
The behaviour of this feature determines the graph plotted of the exercise specified based on these considerations:
-
If there is no such exercise with the matching
ExerciseName
in the client’s exercise list from the specifiedStartDate
toEndDate
, then the graph cannot be plotted -
If the
Axis
input isreps
and the exercise specified does not have any reps input withint theStartDate
toEndDate
, then the graph cannot be plot -
If the
Axis
input isweight
and the exercise specified does not have any weight input within theStartDate
toEndDate
, then the graph cannot be plot -
If all of the above are fulfilled (ie. there is at least one valid exercise with the matching
ExerciseName
and has weight/reps input depending on theAxis
specified), then the graph will be plotted, with each exercise in chronological order
The flow of the program is illustrated using the sequence diagram below:
The explanation is as follows: when the user inputs graph
with all relevant arguments input correctly, a new GraphCommand()
is created, taking the newly created Graph
object as parameter.
GraphCommand#execute()
then retrieves the exercise list from the client currently in view and checks if there is at least one exercise with a matching exercise name. If there is no exercise to plot, then an error GraphCommand.MESSAGE_EXERCISE_NOT_IN_LIST
will be thrown. Next, the list of exercises to be plot will be generated using Graph#generateGraphList()
. Once again, there will be a sanity check to see if the list size is zero, which means that no graph cannot be plotted.
Design Considerations
In designing this feature, we had to decide on the implementation of certain classes like Axis
to comply with the OOP standards of Abstraction.
Create enum class Axis Type (Chosen) |
Check for Axis value using raw types |
|
---|---|---|
Adhering to OOP standards (Abstraction) |
Increases level of abstraction as there are only two different types of axis that can be chosen |
Less abstraction and increases complexity as we will have to check for the equality of the axis type using the equality check for the |
Ease of Implementation |
Requires some refactoring to include |
Easier to implemention as no extra classes or methods are needed, so no refactoring is needed |
We decided to use the approach of abstracting the axis types away into AxisType
enum class. As the graph implementation will require a substantial amount of equality checks, especially for the attributes of Graph
to make sure that we are drawing the correct graph for the user. As such equality checks are made, it makes it difficult to keep checking String
equality as regular data types like String
would allow invalid values to be assigned to a variable.
As our axis values can only be REPS
, WEIGHT
or NA
, we can check for each case using the switch case method instead of checking for equality using raw types. This is also much more efficient than using multiple if-else statements. For example, in the code snippet below, the method fillSeries()
uses switch case statements to add data values depending on the AxisType
.
private void fillSeries() {
switch (axisType) {
case REPS:
fillRepsSeries();
yAxis.setLabel("Reps");
break;
case WEIGHT:
fillWeightSeries();
yAxis.setLabel("Weight");
break;
default:
}
}
Moreover, to keep in line with the OOP standards, we decided that it will be better to abstract away data types like AxisType
into its separate class instead of storing it as a raw type in Axis
. This ensures the code quality of our program and reduces complexity (especially in terms of equality checking as mentioned above) by abstracting away the more complex details into classes of a lower level. The consideration of abstracting details away is also used for creating StartDate
and EndDate
classes as attributes of Graph
, instead of using the Java in-built LocalDate
.
By considering the above two factors, despite having to put in the extra effort to create a new AxisType
class and thus requiring extra methods like getters and setters, we decided to move with the approach of creating the AxisType
enum class and refactor to accomodate for the additional data type.