Developer Guide
- About FitBook
- Acknowledgements
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
- Appendix: Effort
- Appendix: Planned Enhancements
About FitBook
FitBook app is a collective project by a group of NUS undergrads (AY2223/CS2103T-T15-2).
This app aims to promote efficiency recording of clients and exercise routines for fitness instructors. It is written in java 11 and uses javafx for its UI. The storage are made up of Json files to store the user’s input data.
Acknowledgements
- Libraries
- LocalDateTime
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
.puml
files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Architecture
The Architecture Diagram given above explains the high-level design of the FitBook App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
has two classes called Main
and MainApp
. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons
represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI
: The UI of the App. -
Logic
: The command executor. -
FitBookModel
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1
.
Each of the four main components (also shown in the diagram above),
- defines its API in an
interface
with the same name as the Component. - implements its functionality using a concrete
{Component Name}Manager
class (which follows the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, ClientListPanel
, ExercisePanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the JavaFx UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
- executes user commands using the
Logic
component. - listens for changes to
FitBookModel
data so that the UI can be updated with the modified data. - keeps a reference to the
Logic
component, because theUI
relies on theLogic
to execute commands. - depends on some classes in the
FitBookModel
component, as it displaysClient
object residing in theFitBookModel
.
Logic component
API : Logic.java
Here’s a (partial) class diagram of the Logic
component:
How the Logic
component works:
- When
Logic
is called upon to execute a command, it uses theFitBookParser
class to parse the user command. - This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,AddCommand
) which is executed by theLogicManager
. - The command can communicate with the
FitBookModel
when it is executed (e.g. to add a client). - The result of the command execution is encapsulated as a
CommandResult
object which is return fromLogic
.
The Sequence Diagram below illustrates the interactions within the Logic
component for the execute("delete 1")
API call.
DeleteCommandParser
should end at destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
FitBookParser
class creates anXYZCommandParser
(XYZ
is a placeholder for the specific command name e.g.,AddCommandParser
) which uses the other classes shown above to parse the user command and create aXYZCommand
object (e.g.,AddCommand
) which theFitBookParser
returns back as aCommand
object. - All
XYZCommandParser
classes (e.g.,AddCommandParser
,DeleteCommandParser
, …) inherit from theParser
interface so that they can be treated similarly where possible e.g, during testing.
FitBookModel component
API : FitBookModel.java
The FitBookModel
component,
- stores the FitBook data i.e., all
Client
objects (which are contained in aUniqueClientList
object). - stores the FitBookExerciseRoutine data i.e., all
Routine
objects (which are contained in aUniqueRoutineList
object). - stores the currently ‘selected’
Client
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Client>
that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. - stores the currently ‘selected’
Routine
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Routine>
that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. - stores a
UserPref
object that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPref
objects. - does not depend on any of the other three components (as the
FitBookModel
represents data entities of the domain, they should make sense on their own without depending on other components)
Tag
list in the FitBook
, which Client
references. This allows FitBook
to only require one Tag
object per unique tag, instead of each Client
needing their own Tag
objects. Consists of other type of objects like Appointment
Exercise
list in the FitBookExerciseRoutine
, which Routine
references. This allows FitBookExerciseRoutine
to only require one Exercise
object per unique exercise, instead of each Routine
needing their own Exercise
objects.Storage component
API : Storage.java
The Storage
component,
- can save all FitBook, FitBookExerciseRoutine data and user preference data in json format, and read them back into corresponding objects.
- inherits from all
FitBookExerciseRoutineStorage
,FitBookStorage
andUserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed). - depends on some classes in the
FitBookModel
component (because theStorage
component’s job is to save/retrieve objects that belong to theFitBookModel
)
Common classes
Classes used by multiple components are in the seedu.fitbook.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Add/Edit Client feature
Implementation
The proposed add client mechanism is facilitated by FitBook
. It extends FitBook
with a FitBook storage, stored in a fitbook.json
file. Additionally, it implements the following operations:
-
AddCommand#execute()
— Adds the new client into the FitBook storage of the FitBook. -
EditCommand#execute()
— Edits the target routine in the FitBook storage of the FitBook.
These operations are exposed in the Command
interface as Command:execute()
.
Diagram below shows an example of a client called c0
after using the add
command to add c0
.
The Client
consists of these objects after adding. Objects that are a Set
can contain 0 or more objects. Goal object can contain a String client has not added a goal
to represent that there is no goal. Calorie object can contain 0000
value to represent there is no recommended calorie input.
All objects must contain non-null attributes. All objects must be present after adding/editing the client other than objects in the Set
objects.
Given below is an example usage scenario and how the add/edit mechanism behaves at each step.
Step 1. The user launches the application for the first time. The FitBook
will be initialized with the FitBook on start up, and the information from the Storage will be converted into JsonAdaptedClient
and all the other JsonAdapted classes accordingly.
Step 2. The user executes add n/John Doe p/32692562 …
command to add the Client in the FitBook. The add
command calls AddCommandParser
, causing the command to be parsed and checked for any errors before executing the command and calling AddCommand:execute()
to execute the command to add the Client in the FitBook.
- The add command has a similar UML diagram as
Step 1
. The only changes are the addCommand to editCommand, edits the client and add it into storage and edits the client in the model.
Step 3. The user executes edit 1 w/50.0 …
to edit the 1st client (target Client
) in the FitBook. The edit
command also calls EditCommandParser
, causing it to check if the command is appropriate and calling EditCommand:execute()
to execute the command to be edited in the client for FitBook Model.
Step 2.5/3.5. These commands (from Steps 2 and 3) will therefore go through updates for the FitBookModel and also update the FitBook Storages for each command functionality.
AddCommand:execute()
or EditCommand:execute()
, so the updated client will not be saved in the FitBook.
edit
command is at index 4, pointing at an invalid Client
, it will return an error to the user, prompting the user that he/she has used an invalid client index.
The following sequence diagram shows how the add operation works:
AddCommand
should end at to destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Design considerations
Aspect: How add & edit client executes:
-
Alternative 1 (current choice): Add or edit the Client that allows multiple attributes and save the entire model into the FitBook.
- Pros: Easy to implement.
- Cons: May cause some performance issues in terms of memory usage and speed.
-
Alternative 2: Individual command knows how to add/edit by
itself.
- Pros: Will use less memory (e.g. for
edit
, just allocate an array for any edits of the same client before adding the latest edit of that client only). - Cons: Each command implementation must be correct which is hard to maintain.
- Pros: Will use less memory (e.g. for
Past 30 days weight data graph
Implementation
The Past 30 days weight data graph feature displays a LineChart of the weight data against the date for the past 30 days in a pop-up window. This feature retrieves the weight data from the Logic component and updates the graph with the most recent data.
The following details explain how it works:
- The
WeightHistory
class stores the list of weights and dateTime. - The GraphPopup.Scene class sets up the LineChart with x-axis as a
Date
representing date, and y-axis as aDouble
representing the weight measurement on that date. - Ensure the XYChart.Series that populates the graph with data is always updated with the most recent data.
- The
handleStatistics
is called by the MainWindow class to generate the weight data graph in a pop-up window. This method retrieves the weight data from the WeightHistory class and updates the graph with the most recent data using the updateSeries method. - The
updateSeries
method in the Logic component updates theXYChart.Series
that populates the graph with data.
Sequence Diagram for Graph feature. The diagram omits some method sequence in the pane, scene and stage as those details are just setting the parts for those classes which will not fit in the sequence diagram
Step 1: MainWindow requests for a graph pop-up window by executing the graph command.
Step 2: Logic call the WeightHistory class to generate a list of Date
date mapped to Double
weight over 30 days. handleStatistics
method is called to generate
graph. updateSeries
method is called to ensure the data populating the graph is up-to-date.
-
Example Usage Scenario
Below is an example usage scenario of how the weight data graph behaves at each step:
- The user launches the application for the first time.
- The user executes the
addWeight
command to add weights and dates. The execution of theaddWeight
command also checks whether the date is valid in the date list. If it is, the weight and date are added to the weight and date list. Otherwise, an error is displayed. - The user executes the
graph
command to generate weight data graph in the pop-up windows. - The Past 30 days weight data graph feature retrieves the weight data from the WeightHistory class and updates the graph with the most recent data using the updateSeries method.
- The weight data graph is displayed in a pop-up window.
-
Design Considerations
One important design consideration is how to display the weight data graph. Our approach is to generate each graph in a separate pop-up windows.
- pros: Users can easily view multiple clients’ weight data graphs side-by-side without having to switch between different views.
- cons: If the user opens multiple pop-up windows to view different weight data graphs, this may clutter the user’s desktop and make it difficult to manage.
Edit appointments feature
Implementation
The edit appointments feature allows users to view appointments in the upcoming dates.
This feature is implemented using a panel on the main window with a list of clients names that is updated with every command that may affect appointment set.
One situation that trigger the edit appointments feature is when a user edits an appointment.
The following details explain how it works:
- What it does:
- When an appointment is edited in the clients list, it is automatically added to the list of appointments.
- The list is automatically sorted in increasing order of the appointment data time.
- Details:
- When the user enters the edit appointment command, it triggers the creation of an object the Appointment class.
- In the EditCommand class, the data entered by user is parsed.
- If there is no error, the Appointment object is created which triggers the getAppointment() function in FitBookModel.
- This function, in turn, calls editCommand() function in FitBook.
- These functions call isValidDate() and isValidAppointment() functions in Appointment to confirm whether the appointment date time are valid.
- If the appointment date and time are valid, they are added to the appointment list, which is then sorted. Otherwise, an error message is returned.
The following sequence diagram shows how the edit
command works with appointment input: edit 1 app/05-05-2023 19:00
-
Example Usage Scenario
Below is an example usage scenario of how the appointment list mechanism behaves at each step:
- The user launches the application for the first time.
- The user executes the
Edit
command to edit an appointment. The execution of theEdit
command also checks whether this appointment is valid in the appointment list. If it is, the appointment is added to the appointment list. Otherwise, an error is displayed.
-
Design Considerations
One important design consideration is how to handle expired appointment dates and times. The current choice is to automatically remove them after switching tabs.
- pros: This allows users to focus only on upcoming appointment dates and times, which can help them stay organized and avoid confusion.
- cons: Expired date and time information cannot be removed immediately unless the user switches tabs within the application, which could potentially lead to confusion appointments.
Find feature
Implementation
The proposed find mechanism is facilitated by FitBook
. It implements the following operations:
-
FitBook#updateFilteredClientList(Predicate<Client> predicate)
— Filters the client list with the given predicate.
This operation is exposed in the FitBookModel
interface as FitBookModel#updateFilteredClientList(Predicate<Client> predicate)
.
Given below is an example usage scenario and how the find mechanism behaves at each step.
Step 1. The user launches the application for the first time. The FitBook
will be initialized with the initial FitBook state.
Step 2. The user executes find n/alex n/john
command to find all clients with “alex” or “john” in their name in the
FitBook. The find
command calls FindCommandParser
, causing the command to be parsed and checked for any errors before executing the command.
Step 3. After the command is parsed and there are no errors in the format of the command, FindCommand#execute()
is called, which
calls FitBook#updateFilteredClientList(Predicate<Client> predicate)
.
Step 4. The user now wishes to find clients with phone number ‘91234567’. The user executes find p/91234567
and the process restarts at step 2.
The following sequence diagram shows how the find operation works:
FindCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Design considerations
Aspect: How find executes more than once:
-
Alternative 1 (current choice): Finds from the entire FitBook.
- Pros: Easy to implement.
- Cons: Lower performance as every command will have to filter the entire FitBook.
-
Alternative 2: Finds from an already filtered list.
- Pros: Better performance.
- Cons: May result in high memory usage as each new state has to be saved.
Aspect: How find filters by keyword:
-
Alternative 1 (current choice): Finds using predicates i.e.
n/
,p/
,add/
…- Pros: User-friendly.
- Cons: Harder implementation due to the need to create specific predicates e.g.
NameContainsKeywordsPredicate
,EmailContainsKeywordsPredicate
.
-
Alternative 2: Finds using only keywords.
- Pros: Easy to implement.
- Cons: Poor design which may give rise to higher chances of errors in the future. This implementation may also be confusing for users.
Add Exercise feature
Implementation
The proposed Add Exercise mechanism is facilitated by FitBook
. It implements the following operations:
-
FitBook#addExercise()
— Adds an exercise to the specified routine in ‘FitBookExerciseRoutine’.
This operation is exposed in the FitBookModel
interface as FitBookModel#addExercise()
Given below is an example usage scenario and how the addExercise mechanism behaves at each step.
Step 1. The user launches the application for the first time. The FitBookExerciseRoutine()
will be initialized with the FitBook on start up, and the information from the Storage will be converted into JsonAdaptedRoutine
accordingly
Step 2. The user executes addExercise 2 ex/push ups...
command to add the exercise push-up
to the exercise list of the routine specified at index 2
The addExercise
command calls AddExerciseCommandParser
, causing the command to be parsed and checked for any errors before executing the command
which thereafter calls AddExerciseCommand#execute()
which calls FitBookModel#addExercise()
to add the exercise to the routine in FitBookExerciseRoutine
.
AddExerciseCommand:execute()
so the updated Routine will not be saved in the FitBookExerciseRoutine .
The following sequence diagram shows how the add exercise operation works:
AddExerciseCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Design considerations
-
Alternative 1 (current choice): Adds the exercise into the specific Routine from the Routine List and save the entire model into FitBookExerciseRoutine.
- Pros: Easy to implement.
- Cons: Might cause performance issues in terms of memory usage and speed.
-
Alternative 2: Keep track of the update list without saving the entire model into FitBookExerciseRoutine after each addExercise command.
- Pros: Might be faster.
- Cons: Will be risky as it does not maintain accuracy of data in the model.
Delete Routine feature
Implementation
The proposed Delete Routine mechanism is facilitated by FitBook
. It implements the following operations:
-
FitBook#deleteRoutine()
— Deletes the routine in the routline list in ‘FitBookExerciseRoutine’.
This operation is exposed in the FitBookModel
interface as FitBookModel#deleteRoutine()
Given below is an example usage scenario and how the deleteRoutine mechanism behaves at each step.
Step 1. The user launches the application for the first time. The FitBookExerciseRoutine()
will be initialized with the FitBook on start up, and the information from the Storage will be converted into JsonAdaptedRoutine
accordingly
Step 2. The user executes deleteRoutine 1
command to delete the corresponding index specified in the Routine list of the FitBookExerciseRoutine
. The deleteRoutine
command calls DeleteRoutineCommandParser
, causing the command to be parsed and checked for any errors before executing the command
which thereafter calls DeleteRoutineCommand#execute()
which calls FitBookModel#deleteRoutine()
to delete the routine in FitBookExerciseRoutine
.
Step 2.5. These commands will therefore go through updates for the FitBookModel and also update the FitBookExerciseRoutineStorages.
DeleteRoutineCommand:execute()
so the updated Routine will not be saved in the FitBookExerciseRoutine .
The following sequence diagram shows how the deleteRoutine operation works:
DeleteRoutineCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Design considerations
-
Alternative 1 (current choice): Delete the Routine from the Routine List and save the entire model into FitBookExerciseRoutine.
- Pros: Easy to implement.
- Cons: Might cause performance issues in terms of memory usage and speed.
-
Alternative 2: Keep track of the update list without saving the entire model into FitBookExerciseRoutine after each deleteRoutine command.
- Pros: Might be faster.
- Cons: Will be risky as it does not maintain accuracy of data in the model.
Export client/routine list
Implementation
This feature allows the user to extract data efficiently from FitBook to be used for other purposes such as statistical analysis.
Fitbook
creates a new CSV file and write data to it.
The proposed export mechanism is facilitated by FitBook
. It implements the following operations:
-
FitBook#getFilteredClientList
— Retrieves the client list.
This operation is exposed in the FitBookModel
interface as FitBookModel#getFilteredClientList()
Given below is an example usage scenario and how the deleteRoutine mechanism behaves at each step.
Step 1. The user launches the application for the first time. The FitBook
will be initialized with the FitBook on start up, and the information from the Storage will be converted into JsonAdaptedClients
accordingly
Step 2. The user executes export
command to export the client list of FitBook
. The FitBookParser
calls the ExportCommand()
command which calls ExportCommand#writeToCsvFile(model)
, to write the client details into a csv file with the client list obtained from model
.
The ExportCommand#writeToCsvFile(model)
calls ExportCommand#writeHeaderRow(pw)
and ExportCommand#writeClientRows(PrintWriter pw, List<Client> clients)
which uses
the input parameter printwriter to write the header row and client rows respectively into the csv file
DeleteRoutineCommand:execute()
so the updated Routine will not be saved in the FitBookExerciseRoutine .
The following sequence diagram shows how the find operation works:
ExportCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The following activity diagram summarizes what happens when a user executes a new command:
Design considerations
Aspect: How to export the client details :
-
Alternative 1 (current choice): Obtains the client list from the model and writes to the csv file which is facilitated by
PrintWriter
- Pros: Code is modular, and easy to implement.
- Cons: this implementation is specific to exporting client details to a CSV file, cannot be adapted to other types of exports and the use of static methods makes it difficult to extend or modify the behavior of the command.
-
Alternative 2: Allows the user to specify which client to export by providing the index of the client. Use a
CsvUtil
class to handle the file I/O operations and list generation.- Pros: The file I/O operations are abstracted away in a separate utility class, which improves modularity and readability.
- Cons: This implementation may be more complex and harder to understand for someone unfamiliar with the code.
Add/Edit Routine feature
Implementation
The proposed add routine mechanism is facilitated by FitBook
. It extends FitBook
with a Routine storage, stored in a exerciseroutine.json
file. Additionally, it implements the following operations:
-
AddRoutineCommand#execute()
— Adds the new routine into the Exercise Routines of the FitBook. -
EditRoutineCommand#execute()
— Edits the target routine in the Exercise Routines of the FitBook.
These operations are exposed in the Command
interface as Command:execute()
.
Given below is an example usage scenario and how the addRoutine/editRoutine mechanism behaves at each step.
Step 1. The user launches the application for the first time. The FitBookExerciseRoutine
will be initialized with the FitBook on start up, and the information from the Storage will be converted into JsonAdaptedRoutine
and JsonAdaptedExercise
accordingly.
Step 2. The user executes addRoutine r/Cardio …
command to add the Routine in the FitBookExerciseRoutine. The addRoutine
command calls AddRoutineCommandParser
, causing the command to be parsed and checked for any errors before executing the command and calling AddRoutineCommand:execute()
to execute the command to add the New Routine in the Exercise Routine.
- The addRoutine command has a similar UML diagram as
Step 1
. The only changes are the AddRoutineCommand to EditRoutineCommand, edits the routine and add it into storage and edits the routine in the model instead.
Step 3. The user executes editRoutine 1 r/HIIT
to edit the 1st Routine (target Routine) in the Exercise Routine of FitBook. The editRoutine
command also calls EditRoutineCommandParser
, causing it to check if the command is appropriate and calling EditRoutineCommand:execute()
to execute the command to be edited in the Exercise Routine.
Step 4. The user executes editRoutine 1 exno/3 ex/Push Ups
to edit the 1st Routine (target Routine) in the Exercise Routine of the FitBook. This command works in the same way as step 3 but instead of changing the RoutineName
, it changes the Exercise
in the routine.
RoutineName
or the Exercise
in the Routine
. However, it cannot be used together. (eg, editRoutine 1 r/HIIT exno/2 ex/Pushups
is not allowed as there is a mix for the command.)
Step 5. These commands (Steps 2, 3 and 4) will therefore go through updates for the FitBookModel and also update the FitBookExerciseRoutineStorages for each command functionality.
AddRoutineCommand:execute()
or EditRoutineCommand:execute()
, so the updated Routine will not be saved in the FitBookExerciseRoutine .
editRoutine
command is at index 4, pointing at an invalid Routine
, it will return an error to the user, prompting the user that he/she has used an invalid routine index.
The following sequence diagram shows how the addRoutine operation works:
AddRoutineCommand
should end at to destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Design considerations
Aspect: How add & edit routine executes:
-
Alternative 1 (current choice): Add or edit the Routine and save the entire model into the FitBookExerciseRoutine.
- Pros: Easy to implement.
- Cons: May cause some performance issues in terms of memory usage and speed.
-
Alternative 2: Individual command knows how to add/edit by
itself.
- Pros: Will use less memory (e.g. for
edit
, just allocate an array for any edits of the same routine before adding the latest edit of that routine only). - Cons: Each command implementation must be correct which is hard to maintain.
- Pros: Will use less memory (e.g. for
[Proposed] Undo/redo feature
Proposed Implementation
The proposed undo/redo mechanism is facilitated by VersionedFitBook
. It extends FitBook
with an undo/redo history, stored internally as an fitBookStateList
and currentStatePointer
. Additionally, it implements the following operations:
-
VersionedFitBook#commit()
— Saves the current FitBook state in its history. -
VersionedFitBook#undo()
— Restores the previous FitBook state from its history. -
VersionedFitBook#redo()
— Restores a previously undone FitBook state from its history.
These operations are exposed in the FitBookModel
interface as FitBookModel#commitFitBook()
, FitBookModel#undoFitBook()
and FitBookModel#redoFitBook()
respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
Step 1. The user launches the application for the first time. The VersionedFitBook
will be initialized with the initial FitBook state, and the currentStatePointer
pointing to that single FitBook state.
Step 2. The user executes delete 5
command to delete the 5th client in the FitBook. The delete
command calls FitBookModel#commitFitBook()
, causing the modified state of the FitBook after the delete 5
command executes to be saved in the fitBookStateList
, and the currentStatePointer
is shifted to the newly inserted FitBook state.
Step 3. The user executes add n/David …
to add a new client. The add
command also calls FitBookModel#commitFitBook()
, causing another modified FitBook state to be saved into the fitBookStateList
.
FitBookModel#commitFitBook()
, so the FitBook state will not be saved into the fitBookStateList
.
Step 4. The user now decides that adding the client was a mistake, and decides to undo that action by executing the undo
command. The undo
command will call FitBookModel#undoFitBook()
, which will shift the currentStatePointer
once to the left, pointing it to the previous FitBook state, and restores the FitBook to that state.
currentStatePointer
is at index 0, pointing to the initial FitBook state, then there are no previous FitBook states to restore. The undo
command uses FitBookModel#canUndoFitBook()
to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
The following sequence diagram shows how the undo operation works:
UndoCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The redo
command does the opposite — it calls FitBookModel#redoFitBook()
, which shifts the currentStatePointer
once to the right, pointing to the previously undone state, and restores the FitBook to that state.
currentStatePointer
is at index fitBookStateList.size() - 1
, pointing to the latest FitBook state, then there are no undone FitBook states to restore. The redo
command uses FitBookModel#canRedoFitBook()
to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
Step 5. The user then decides to execute the command list
. Commands that do not modify the FitBook, such as list
, will usually not call FitBookModel#commitFitBook()
, FitBookModel#undoFitBook()
or FitBookModel#redoFitBook()
. Thus, the fitBookStateList
remains unchanged.
Step 6. The user executes clear
, which calls FitBookModel#commitFitBook()
. Since the currentStatePointer
is not pointing at the end of the fitBookStateList
, all FitBook states after the currentStatePointer
will be purged. Reason: It no longer makes sense to redo the add n/David …
command. This is the behavior that most modern desktop applications follow.
The following activity diagram summarizes what happens when a user executes a new command:
Design considerations
Aspect: How undo & redo executes:
-
Alternative 1 (current choice): Saves the entire FitBook.
- Pros: Easy to implement.
- Cons: May have performance issues in terms of memory usage.
-
Alternative 2: Individual command knows how to undo/redo by
itself.
- Pros: Will use less memory (e.g. for
delete
, just save the client being deleted). - Cons: We must ensure that the implementation of each individual command are correct.
- Pros: Will use less memory (e.g. for
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- has a need to manage a significant number of clients
- has a need to manage details of each client
- has a need to store exercises routines for each client
- has a need to mark done or not done for each exercises
- prefers desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: Fitness trainers tend to write all their clients’ fitness checklist, weight and progress with targets in a notebook. Keeping track of so many clients’ routines and progress on a notebook can be tough and inefficient. Our product aims to help solve this by combining the addressbook with additional features such as adding fitness routines to each client with checkboxes for easy access and to check if they have completed their routines during their individual sessions. The trainer can also find the clients’ progress such as weight and their appointment dates and times they have with their clients.
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
=======
Priority | As a/an … | I can … | So that … |
---|---|---|---|
* * * |
new user | look at the list of clients | I know who are my clients and their details |
* * * |
new user | look at the list of exercises | I know what exercise routines are there saved in my application |
* * * |
new user | edit my client’s routine | the client’s routine data is accurate |
* * * |
new user | edit my client’s current routine | the client’s calorie intake is accurate |
* * * |
new user | edit my client’s information | I can update my clients with the updated details |
* * * |
new user | add new clients details | I can have my clients saved and view the next time easily |
* * * |
new user | add new routines and its exercises | I can view my exercise routines whenever I want |
* * * |
new user | put my client’s current weight | I can see if they get closer to their targeted weight |
* * * |
new user | set goal for each of my clients | I can help my clients to make the most suitable plan |
* * * |
new user | set recommended calorie intakes | I can keep track of how much calorie I have recommended to my client |
* * * |
new user | filter persons by name | I can locate a person easily |
* * * |
new user | search for clients through their names | I can find my client’s data |
* * * |
intermediate user | edit previous data | I can change my client’s data |
* * * |
intermediate user | add time to a client’s appointment | I can easily view my appointments for the week |
* |
intermediate user | search client’s using their information | I can find my contacts easily |
* |
intermediate user | search clients by gender | I can find my client easily |
* * |
intermediate user | show a graph of my client’s weight history | I can see the trend of my client’s weight to determine what to improve |
* * |
intermediate user | edit after using a find feature | I can easily edit the client in the filtered list without changing back to the unfiltered list |
* * |
intermediate user | export my clients’ details | I can view my clients data on a file like excel |
* * |
intermediate user | export my exercise routines’ details | I can view my exercise routines data on a file like excel |
* * |
expert user | add clients into specific groups (tagging) | it will be easier to track if they are in the same groups |
* * |
expert user | download data collected in FitBook | I can show results of my services to new potential clients |
* * |
expert user | view appointments that are coming | I know which appointment session is coming first |
* * * |
expert user | search clients by tags | I can view clients by the searched tags |
* * * |
expert user | shift the data to another computer | I can use the same data for the same app on another computer |
=======
Use cases
(For all use cases below, the System is the FitBook
and the Actor is the user
, unless specified otherwise)
Use case: UC01 - Delete a client
MSS
- User requests to list clients.
- FitBook shows a list of clients.
- User requests to delete a specific client in the list.
-
FitBook deletes the client.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. FitBook shows an error message.
Use case resumes at step 2.
-
Use case: UC02 - Add a client
MSS
- User requests to add a client.
- FitBook adds the client into the list.
-
FitBook displays that the client has been added.
Use case ends.
Extensions
-
1a. The client added has missing compulsory fields.
-
1a1. FitBook shows an error message for missing fields.
Use case ends.
-
-
1b. The client added has duplicate names.
-
1b1. FitBook shows an error message for duplicate names.
Use case ends.
-
-
1c. The client added has a routine that is not in FitBook.
-
1c1. FitBook shows an error message to use a valid routine.
Use case ends.
-
Use case: UC03 - List clients
MSS
- User requests to list clients.
-
FitBook displays a list of clients.
Use case ends.
Extensions
-
1a. Client list is empty use case.
Use case ends.
Use case: UC04 - Edit a client
MSS
- User requests to edit a client’s details.
-
FitBook edits the client’s detail into the database.
Use case ends.
Extensions
-
1a. The user enters the command incorrectly.
-
1a1. FitBook shows an error message.
Use case ends.
-
-
1b. The user enters a client that does not exist in the database.
-
1b1. FitBook shows an error message that the client does not exist.
Use case ends.
-
Use case: UC05 - Clear all clients
MSS
- User requests to clear the list of clients.
- FitBook clears the list and database of clients.
-
FitBook displays that the list is cleared.
Use case ends.
Extensions
-
1a. The list is empty in the database.
-
1a1. FitBook displays that the list is cleared.
Use case ends.
-
Use case: UC06 - Exit
MSS
- User requests to exit the application.
-
FitBook exits the program.
Use case ends.
Use case: UC07 - Find
MSS
- User requests to find a client.
-
FitBook displays the list of matching clients.
Use case ends.
Extensions
- 1a. The input does not match any client in the database.
-
1a1. FitBook displays that there are no matches.
Use case ends.
-
- 2a. The list is empty in the database.
-
2a1. FitBook displays that there are no matches.
Use case ends.
-
- 3a. The find command has incorrect format.
-
3a1. FitBook displays an error that the find format is wrong.
Use case ends.
-
Use case: UC08 - List Routines
MSS
- User requests to list routines.
-
FitBook displays a list of routines.
Use case ends.
Extensions
-
1a. Routine list is empty use case.
Use case ends.
Use case: UC09 - Clear Routines
MSS
- User requests to clear the list of routines.
- FitBook clears the list and database of routines.
-
FitBook displays that the routine list is cleared.
Use case ends.
Extensions
-
1a. The routine list is empty in the database.
-
1a1. FitBook displays that the routine list is cleared.
Use case ends.
-
Use case: UC10 - Delete Routine
MSS
- User requests to list routines.
- FitBook shows a list of routines.
- User requests to delete a specific routine in the list.
-
FitBook deletes the routine.
Use case ends.
Extensions
-
2a. The routine list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. FitBook shows an error message.
Use case resumes at step 2.
-
Use case: UC11 - Delete Exercise
MSS
- User requests to list routines.
- FitBook shows a list of routines.
- User requests to delete an exercise from a specific routine in the list.
-
FitBook deletes the exercise from the specific routine in the list.
Use case ends.
Extensions
-
2a. The routine list is empty.
Use case ends.
-
3a. The given routine index is invalid.
-
3a1. FitBook shows an error message.
Use case resumes at step 2.
3b. The given exercise index is invalid.
-
3b1. FitBook shows an error message.
Use case resumes at step 2.
-
Use case: UC12 - Find Routine
MSS
- User requests to find a routine by name.
-
FitBook displays the list of matching clients.
Use case ends.
Extensions
- 1a. The list is empty in the database.
-
1a1. FitBook displays that there are no matches.
Use case ends.
-
Use case: UC13 - Export Client List
MSS
- User request to export Client List.
-
FitBook exports the Client List to csv format.
Use case ends.
Extensions
- 1a. The Client csv file is opened in the background.
-
1a1. FitBook shows an error message.
Use case ends.
-
Use case: UC14 - Export Routine List
MSS
- User request to export Routine List.
-
FitBook exports the Routine List to csv format.
Use case ends.
Extensions
- 1a. The Routine csv file is opened in the background.
-
1a1. FitBook shows an error message.
Use case ends.
-
Use case: UC15 - Add routine
MSS
- User request to add a routine.
-
FitBook adds the routine to its routine list.
Use case ends.
Extensions
- 1a. User request have missing routine name field.
-
1a1. FitBook shows an error for missing routine name.
Use case ends.
-
- 1b. User request have missing exercise field.
-
1b1. FitBook adds a routine with no exercise. (Only routine itself)
Use case ends.
-
- 1c. User request have all missing fields.
-
1c1. FitBook shows an error message.
Use case ends.
-
Use case: UC16 - Add Exercise
MSS
- User request to add an Exercise to a specific Routine.
-
FitBook adds the Exercise to the Routine in the routine list.
Use case ends.
Extensions
- 1a. User request have missing exercise name field.
-
1a1. FitBook shows an error for missing exercise name.
Use case ends.
-
- 1b. User request have missing Routine Index.
-
1b1. FitBook shows an error for missing Routine Index.
Use case ends.
-
- 1c. The given routine index is invalid.
-
1c1. FitBook shows an error for missing fields.
Use case ends.
Use case: UC17 - Edit routine or exercise in routine
-
MSS
- User request to edit a routine.
- User either edit by target routine’s name or one of the target routine’s exercise.
-
FitBook edits that routine in its routine list.
Use case ends.
Extensions
- 2a. User request have mix fields which is not allowed.
-
2a1. FitBook shows an error for incorrect command format.
Use case ends.
-
- 2b. User request have all missing field.
-
2b1. FitBook shows an error for missing fields.
Use case ends.
-
- 2c. User request have only target routine field.
-
2c1. FitBook shows an error for missing field.
Use case ends.
-
- 2d. User request for changing exercise only has one field. (Changing exercise requires two fields)
-
2d1. FitBook shows an error for incorrect format.
Use case ends.
-
Use case: UC18 - View selected client’s summary information
MSS
- User requests to view selected client’s summary information.
-
FitBook shows selected client’s summary information.
Use case ends.
Extensions
- 1a. User request have missing client index number field.
-
1a1. FitBook shows an error for missing client index number.
Use case ends.
-
Use case: UC19 - Add weight
MSS
- User request to add weight to a client.
-
FitBook adds weight to the specified client.
Use case ends.
Extensions
- 1a. The client does not exist in the FitBook.
-
1a1. FitBook shows an error for invalid client.
Use case ends.
-
- 1b. The date specified is not in the correct format.
-
1b1. FitBook shows an error for invalid date format.
Use case ends.
-
- 1c. The user adds a different weight to the same date and time of an existing weight in the database.
-
1c1. FitBook shows an error for duplicate weight.
Use case ends.
-
Use case: UC20 - Plot weight history graph
MSS
- User request to plot weight history graph of a client.
-
FitBook shows weight history graph of the specified client.
Use case ends.
Extensions
- 1a. The client does not exist in the FitBook.
-
1a1. FitBook shows an error for invalid client.
Use case ends.
-
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
11
or above installed. - Should be able to hold up to 1000 clients without a noticeable sluggishness in performance for typical usage.
- A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
- Should be usable by users even if they are not familiar with command lines.
- Should start up fast when starting the application.
- Should work on most screen resolutions.
- Should not consume too much battery or data usage on the user’s device.
- Source code should be open source.
- JAR file should be less than 2GB.
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- Private contact detail: A contact detail that is not meant to be shared with others
- Open source: Programs that people can modify and share because its design is publicly accessible
- Ui: User interface of the program where the user interacts with
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.
Launch and shutdown
-
Initial launch
A. Download the jar file and copy into an empty folder.
B. Double-click the jar file.
Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. -
Saving window preferences
A. Resize the window to an optimum size. Move the window to a different location. Close the window.
B. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
Finding a client
-
Finds a Client by predicates
A. Prerequisites: List all clients using the
listClients
command. Multiple Clients with their respective details displayed in the list.B. Test case:
find n/alex
Expected: Displays all clients with ‘alex’ in their name.C. Test case:
find n/
Expected: Displays an error, with error details shown in the status message.D. Other incorrect find commands to try:
find alex
,find n/alex p/
Expected: Similar to previous.
Deleting a client
-
Deleting a client while all clients are being shown
A. Prerequisites: List all clients using the
list
command. Multiple clients in the list.B. Test case:
delete 1
Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.C. Test case:
delete 0
Expected: No client is deleted. Error details shown in the status message. Status bar remains the same.D. Other incorrect delete commands to try:
delete
,delete x
,...
(where x is larger than the list size)
Expected: Similar to previous.
Deleting an exercise
-
Deleting an exercise while all routines are being shown
A. Prerequisites: List all routines using the
listRoutines
command. Multiple routines with their respective set of exercises in the list.B. Test case:
deleteExercise 1 2
Expected: Second exercise from the first routine is deleted from the list. Details of the deleted exercise shown in the status message.C. Test case:
deleteExercise 0 0
Expected: No exercise is deleted. Error details shown in the status message.D. Other incorrect delete commands to try:
deleteExercise
,delete x y
, (where x or y is larger than the list size and exercise list size respectively )
Expected: Similar to previous.
Export/ExportRoutine
-
Exports the client details/Routine details into a csv file.
A. Prerequisites: Ensure that previously exported csv file (if any) is not opened in the background.
B. Test case:
export
orexportRoutine
Expected: FitBook displays successful export message.
Adding a Routine
-
Adding a Routine while all routines are being shown
A. Prerequisites: List all routines using the
listRoutines
command. Multiple routines with their respective set of exercises in the list.B. Test case:
addRoutine r/Cardio2 ex/push-ups
Expected: Adds a routine with exercise push-ups to the current list of routines.C. Test case:
addRoutine r/Cardio3
Expected: Adds a routine with no exercises to the current list of routines.
Adding an Exercise
-
Deleting an exercise while all routines are being shown
A. Prerequisites: List all routines using the
listRoutines
command. Multiple routines with their respective set of exercises in the list.B. Test case:
addExercise 1 ex/Swimming
Expected: Adds aSwimming
exercise to the first routine in the list. Details of the added exercise shown in the status message.C. Test case:
addExercise 0
Expected: No exercise is added. Error details shown in the status message.D. Other incorrect addExercise commands to try:
addExercise
,addExercise x
, (where x is larger than the list size)
Expected: Similar to previous.
Finding a Routine
-
Finds a Routine by keywords
A. Prerequisites: List all routines using the
listRoutines
command. Multiple routines with their respective set of exercises in the list.B. Test case:
findRoutine Cardio
Expected: Displays all Routines matchingCardio
, Routines names that consist ofCardio
will be matched. (i.eCardio1
,Cardio2
etc…)C. Test case:
findRoutine
Expected: Error details shown in the status message.
View Summary
-
View a Clients Summary Details
A. Prerequisites: List all clients using the
listClients
command. Multiple Clients with their respective details displayed in the list.B. Test case:
view 2
Expected: Displays the summary of Client at index2
in the Summary Panel of the UI.C. Test case:
view
Expected: Error details shown in the status message.D. Other incorrect view commands to try:
view x
, (where x is larger than the list size)
Expected: Similar to previous.
Graph
-
Plot a graph of clients Weight History.
A. Prerequisites: List all clients using the
listClients
command. Multiple Clients with their respective details displayed in the list.B. Test case:
graph 2
Expected: Displays the graph pop up of the weight history of Client at index2
.C. Test case:
graph
Expected: Error details shown in the status messageD. Other incorrect view commands to try:
graph x
, (where x is larger than the list size)
Expected: Similar to previous.
Exit
-
Exits from the application
A. Prerequisites: Launch the application. B. Test case: Enter ‘exit’ command Expected: It exits the application.
Saving data
-
Dealing with missing/corrupted data files
A. Prerequisites: Delete the “fitbook.json” file from data folder. B. Test case: Launch the application Expected: Shows the GUI with a quick start page with default data. C. Prerequisites: Modify the “fitbook.json” file from data folder which contains invalid data. This could be done by removing the HH:mm in appointment field. D. Test case: Launch the application Expected: Application launches successfully but has no data.
Appendix: Effort
Difficulty level
- Programming level: High
- Documentation level: High
- Testing level: Middle
- Design level: High
Challenges faced
- Setting up of the graph for the client’s weight.
- Require changes to the storage and UI part.
- AB3 only has fixed attributes for each Person. Storing the Weight History requires modification to the storage to achieve sequential weight and date.
- AB3 only has one stage which is the
primaryStage
. Implementing graph has some difficulty as it requires newStage
to show the updated graph. Found a workaround by creating a newStage
as a popup.
- Creating a new Exercise Routine storage to separate Exercise and Client.
- AB3 only has one storage. Adding a storage requires knowledge of AB3 design through its developer guide.
- Arranging Exercises in order.
- Since most of the stored collections are used as sets. Saving it to the storage was a problem at the start as Set is unordered. Found a workaround by tweaking the program to accept a list where it is ordered.
- Comparing of Appointment to put the earliest appointment at the top in the schedule.
- Appointment has to sort accordingly from earliest to latest. Required to use a comparator class to compare the different dates and delete the overdue dates.
- View command for Summary panel.
- Putting all the different client’s attributes into a single card and automatically update. Learn from AB3, use ObservableList to make the changes to automatically update.
Achievements
- Displaying out all the different tabs and panels for UI
- Creating new storage and improved storage for the FitBook from previous AB3 template.
- Exporting and importing of client and routine details to csv file.
- Excellent design of the UI.
- Refactored all the code and updated User Guide and Developer Guide.
- User Friendly application with excellent User Guide.
- Link between Clients and Exercises are prominent.
Appendix: Planned Enhancements
- FitBook lowering the computer’s resources.
- FitBook causes some minor lag after some usage. Future enhancement: Hide the other panels as the other panels are still active (stacking on top of each other) which is probably the main cause of using a lot of ram and gpu.
- FitBook’s validating of correct details.
- FitBook is unable to check the validity of addresses and phone numbers currently. Future enhancement: Check against with the online database (for phone, addresses and email) to validate if it is a valid attribute before adding/changing it for the client.
- FitBook’s indexing for negative value and large numbers.
- FitBook will throw a different error after using a large number (more than Integer.MAX_VALUE) and throw a generic error for negative value. Future enhancement: Deal with this error by using a bigger size class like double and provide a better message for negative value input (even though the current one is correct).
- FitBook’s UI commands.
- Future enhancement: Add commands that allow users to switch tabs instead of using clicking.
- FitBook showing of corrupted storage.
- Future enhancement: Better to show a popup to tell users that the data is corrupted, and it will be using an empty model instead.