By: Team DukeCooks
Since: Oct 2019
Licence: MIT
- 1. Introduction
- 2. Setting up
- 3. Design
- 4. Implementation
- 5. Documentation
- 6. Testing
- 7. Dev Ops
- Appendix A: Product Scope
- Appendix B: User Stories
- Appendix C: Use Cases
- Appendix D: Non Functional Requirements
- Appendix E: Glossary
- Appendix F: Instructions for Manual Testing
- F.1. Launch and Shutdown
- F.2. Adding a recipe
- F.3. Deleting a recipe
- F.4. Editing a recipe
- F.5. Adding a meal plan
- F.6. Deleting a meal plan
- F.7. Editing a meal plan
- F.8. Adding a task
- F.9. Deleting a task
- F.10. Editing a task
- F.11. Adding a diary
- F.12. Deleting a diary
- F.13. Editing a diary
- F.14. Deleting a page
- F.15. Deleting an exercise
- F.16. Saving data
DukeCooks is a revolutionary lifestyle application, specially designed for health-conscious students. Targeting busy students that are long-time users of the Command-Line Interface (CLI), DukeCooks offers such users a CLI healthy lifestyle application. This means, expert-CLI student users will be able to execute different commands, all within a single line. They will be able to manage various aspects of their lifestyle, from their diet, to exercises and health records.
They can use DukeCooks to manage all their lifestyle needs, all within one single app.
With DukeCooks, being healthy has never been easier!
This developer guide aims to provide you with both the technical and non-technical aspects of DukeCooks. It also provides insight into the future direction of DukeCooks. After reading this document, you should be able to understand the design, architecture and goals of DukeCooks.
💡
|
Pro tips which you may find useful. |
ℹ️
|
Information that you should take note of. |
|
Information that you should be careful with. |
Model
|
Text with orange highlight (also known as mark-up) indicates a class / package name. |
Model.java
|
Italic text with mark-up indicates a file name / github link. |
-
JDK
11
or above -
IntelliJ IDE
ℹ️IntelliJ by default has Gradle and JavaFx plugins installed.
Do not disable them. If you have disabled them, go toFile
>Settings
>Plugins
to re-enable them.
-
Fork this repo, and clone the fork to your computer
-
Open IntelliJ (if you are not in the welcome screen, click
File
>Close Project
to close the existing project dialog first) -
Set up the correct JDK version for Gradle
-
Click
Configure
>Project Defaults
>Project Structure
-
Click
New…
and find the directory of the JDK
-
-
Click
Import Project
-
Locate the
build.gradle
file and select it. ClickOK
-
Click
Open as Project
-
Click
OK
to accept the default settings.
-
Run the
dukecooks.Main
and try a few commands -
Run the tests to ensure they all pass.
This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify,
-
Go to
File
>Settings…
(Windows/Linux), orIntelliJ IDEA
>Preferences…
(macOS) -
Select
Editor
>Code Style
>Java
-
Click on the
Imports
tab to set the order-
For
Class count to use import with '*'
andNames count to use static import with '*'
: Set to999
to prevent IntelliJ from contracting the import statements -
For
Import Layout
: The order isimport static all other imports
,import java.*
,import javax.*
,import org.*
,import com.*
,import all other imports
. Add a<blank line>
between eachimport
-
Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.
After forking the repo, the documentation will still have the DukeCooks branding and refer to the AY1920S1-CS2103T-T10-2/main
repo.
If you plan to develop this fork as a separate product (i.e. instead of contributing to AY1920S1-CS2103T-T10-2/main
), you should do the following:
-
Configure the site-wide documentation settings in
build.gradle
, such as thesite-name
, to suit your own project. -
Replace the URL in the attribute
repoURL
inDeveloperGuide.adoc
andUserGuide.adoc
with the URL of your fork.
Set up Travis to perform Continuous Integration (CI) for your fork. See UsingTravis.adoc to learn how to set it up.
After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).
ℹ️
|
Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork. |
Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).
ℹ️
|
Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based while AppVeyor is Windows-based) |
When you are ready to start coding, we recommend that you get some sense of the overall design by reading about DukeCooks’s architecture.
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
💡
|
The .puml files used to create diagrams in this document can be found in the diagrams folder.
Refer to the Using PlantUML guide to learn how to create and edit diagrams.
|
-
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 method where necessary.
Commons
represents a collection of classes used by multiple other components.
The following class plays an important role at the architecture level:
-
LogsCenter
: Used by many classes to write log messages to the App’s log file.
The rest of the App consists of four components.
Each of the four components
-
Defines its API in an
interface
with the same name as the Component. -
Exposes its functionality using a
{Component Name} Manager
class.
For example, the Logic
component (see the class diagram given below) defines it’s API in the Logic.java
interface and exposes its functionality using the LogicManager.java
class.
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete recipe 1
.
The sections below give more details of each component.
API : Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, XYZListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class.
ℹ️
|
XYZListPanel refers to the 6 different individual components in our application. The 6 different components are:
|
The UI
component uses 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
Model
data so that the UI can be updated with the modified data.
API :
Logic.java
-
Logic
uses theDukeCooksParser
class to parse the user command. -
This results in a
Command
object which is executed by theLogicManager
. -
The command execution can affect the
Model
(e.g. adding a recipe). -
The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. -
In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for simplified interactions within the Logic
component for the execute("delete recipe 1")
API call.
ℹ️
|
The lifeline for DeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
API : Model.java
The Model
,
-
stores a
UserPref
object that represents the user’s preferences. -
stores the Duke Cooks data.
-
exposes an unmodifiable
ObservableList<XYZ>
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. -
does not depend on any of the other three components.
-
BlackBox refers to the model diagram of the individual components of DukeCooks.
ℹ️
|
XYZ refers to the 5 different individual components in our application. The 5 different components are:
|
API : Storage.java
The Storage
component,
-
can save
UserPref
objects in json format and read it back. -
can save the DukeCooks data in json format and read it back.
This section describes some noteworthy details on how certain features are implemented.
All command variants (i.e AddRecipeCommand
, AddExerciseCommand
) extends from AddCommand
,
instead of the abstract Command
class.
This applies to other type of commands as well, such as DeleteCommand
and EditCommand
.
The diagram below shows a simplified class diagram for Commands and Parser.
Aspect | Option 1 (Chosen) | Option 2 |
---|---|---|
Implementing commands and parser |
Command variants (i.e Pros Cons Our Choice |
Command variants extend from Pros Cons |
In DukeCooks, a Dashboard
object represents an event a user needs to attend to, a task. A Dashboard
is made up of
a DashboardName
, TaskDate
and TaskStatus
. Below is a class diagram (Figure 10) that illustrates how Dashboard
interacts with other classes.
The functions add/delete/edit/find/list/done task, are found under the Dashboard.
The Dashboard in DukeCooks
serves as a homepage which displays a list of tasks set out
by the user. The tasks are sorted accordingly by earliest date; using a custom sorting algorithm.
Here, a task is represented by a Dashboard object.
The operations are as followed:
-
add task
— Adds and saves the task intoDukeCooks
. -
delete task
— Removes the specified task fromDukeCooks
. -
edit task
— Edits the specified with a new task name and/or date. -
done task
— Marks a task as complete. -
find task
— Finds a list of task with a given keyword. -
list taskincomlete
— Lists all the incomplete tasks. -
list taskcomlete
— Lists all the completed tasks. -
goto dashboard
— Directs user to the dashboard window.
These operations are exposed in the Model interface as addDashboard()
,
deleteDashboard()
, setDashboard()
, Model#hasDashboard()
, doneDashboard
and
getDashboardRecords()
.
Given below is an example usage scenario and how the add/delete/edit/find/done/dashboard mechanism behaves at each step.
Step 1. The user is currently at a another part of DukeCooks and wants to go to the Dashboard. The
user executes the goto dashboard
command. The goto dashboard
command calls getDashboardRecords()
,
which returns the ReadOnlyDashboard
that returns the UniqueDashboardList
, iterating over all the task and displaying it on the
homepage.
Step 2. The user executes add task tn/bake a cake td/12/12/2019
command to add a new
task into DukeCooks. The add
command calls addDashboard()
, causing the task to
be added into UniqueDashboardList
. In UniqueDashboardList
, it calls`add()` which will call sortDashboard()
.
At this stage, the list of task is sorted by date. After which, the add
command also calls saveDashboard()
to be
saved the goto dashboard
into the UniqueDashboardList
.
Step 3. The user executes delete task 3
command to delete the 3rd task in the
dashboard. The delete
command calls deleteDashboard()
causing the specified task
to be removed from UniqueDashboardList
. In UniqueDashboardList
, it calls
remove()
which will call sortDashboard()
. At this
stage, the list of task is sorted by date. Also, it calls saveDashboard()
,
after the delete task 3
command executes
to be saved in the UniqueDashboardList
.
Step 4. The user executes edit task…
command to edit a field in a task. The edit
command calls setDashboard()
causing the edited task to be updated in
UniqueDashboardList
. In UniqueDashboardList
, it calls setDashboard()
which will call sortDashboard()
. At this stage, the list of task is
sorted by date. Also, it calls saveDashboard()
, after the edit task…
command executes to be saved into the UniqueDashboardList
Step 5. The user executes find task…
command to find a list of task given a keyword.
The find
command calls hasDashboard()
, which searches through the
UniqueDashboardList
for tasks containing the particular keyword.
Step 6. The user executes done task 1
command to mark the first task in the list as complete. The done task
command calls doneDashboard()
, which will cause the task to be marked as complete in the
UniqueDashboardList
. In UniqueDashboardList
, it calls done()
, which adds
the newly created Dashboard
object and adds it into the list. After which, sortDashboard()
is called to sort the list by date and reflect the newly made changes.
The following sequence diagram (Figure 11) shows how the done operation works:
Aspect | Option 1 (Chosen) | Option 2 |
---|---|---|
Storing a task’s status |
A custom class, Pros Cons Our Choice |
Use a boolean to check if a task is complete. Pros Cons |
Sorting tasks by date |
Every time the Pros Cons Our Choice |
Keep an instance of the earliest and latest date. If dates fall out of range from the two dates, sorting does not happen. Pros Cons |
The current implementation of Recipe Book consists of the following:
-
Each
Recipe
consists of a uniqueRecipeName
-
Each
Recipe
contains an Set ofIngredient
-
Each
Recipe
consists of a uniqueCalories
-
Each
Recipe
consists of a uniqueCarbohydrates
-
Each
Recipe
consists of a uniqueFats
-
Each
Recipe
consists of a uniqueProtein
-
Each class has their respective getter methods
The class diagram below gives an overview of the Recipe
class.
Recipe
class supports multiple commands. It includes:
-
AddRecipeCommand
- Adds aRecipe
intoDukeCooks
-
DeleteRecipeCommand
- Deletes aRecipe
fromDukeCooks
-
EditRecipeCommand
- Edits the specifiedRecipe
-
FindRecipeCommand
- Finds allRecipe
whoseRecipeName
contains user-specified keywords
All the above recipe commands behave similarly. The commands will be parsed in DukeCooksParser
and based on their
types (i.e Add, Delete, Edit etc), the corresponding variant parsers will be invoked (i.e AddRecipeCommandParser
,
DeleteRecipeCommandParser
etc). After which, the corresponding command will be executed (i.e AddRecipeCommand
,
DeleteRecipeCommand
etc).
The figure below describes the execution of an DeleteRecipeCommand
.
The input provided is delete recipe 1
.
After a successful execution, the recipe with the specified index will be deleted from the recipe book.
The DeleteRecipeCommand will also delete the recipe from all meal plans that by filtering through all saved meal plans, checking for the existence of the recipe it is about to delete, and replace that meal plan with a new one. This is done using MealPlan#removeRecipe.
Aspect | Option 1 (Chosen) | Option 2 |
---|---|---|
Updating of recipe components in meal plans upon recipe modifications |
Dynamic updates upon recipe modification Pros Cons Our Choice |
Manual updates at any point in time Pros Cons |
The current implementation of Meal Plan consists of the following:
-
Each
Meal Plan
consists of a uniqueMealPlanName
-
Each
Meal Plan
contains 7 Lists ofRecipeName
-
Each class has their respective getter methods
The class diagram below gives an overview of the Meal Plan
class.
MealPlan
class supports multiple commands. It includes:
-
AddMealPlanCommand
- Adds aMealPlan
intoDukeCooks
-
DeleteMealPlanCommand
- Deletes aMealPlan
fromDukeCooks
-
EditMealPlanCommand
- Edits the specifiedMealPlan
-
FindMealPlanCommand
- Finds allMealPlan
whoseMealPlanName
contains user-specified keywords -
FindMealPlanWithCommand
Finds allMealPlan
whose days contain user-specifiedRecipeName
.
All the above meal plan commands behave similarly. The commands will be parsed in DukeCooksParser
and based on their
types (i.e Add, Delete, Edit etc), the corresponding variant parsers will be invoked (i.e AddMealPlanCommandParser
,
DeleteMealPlanCommandParser
etc). After which, the corresponding command will be executed (i.e AddMealPlanCommand
,
DeleteMealPlanCommand
etc).
The figure below describes the execution of an DeleteMealPlanCommand
.
The input provided is delete mealplan 1
.
After a successful execution, the meal plan with the specified index will be deleted from the meal plan book.
Aspect | Option 1 (Chosen) | Option 2 |
---|---|---|
Amount of recipe information to be saved in a meal plan |
Saving only the recipe name Pros Cons Our Choice |
Saving the entire recipe Pros Cons |
The workout feature allows users to create their own custom workouts with add Workout
command and adding their own
custom exercises to it with push exercise
. With these custom workouts, they can then choose to run them through
run workout
and monitor their progress and workout history with view workout
.
Workout Management
Every workout comprises of the following information:
-
WorkoutName
representing the name of the workout -
Average Intensity
representing the average demands of the exercises in the workout -
A set of
MuscleType
which represents all the muscles trained by the workout -
An ArrayList of
ExerciseName
of exercises that would be carried out in the workout -
WorkoutHistory
containing information on all the previous runs of the workout as well as some statistics
The Workout Class also contains the function updateHistory(WorkoutRun run)
which adds the WorkoutRun
into the
WorkoutHistory and updates all the relevant fields accordingly, returning a new Workout instance with updated WorkoutHistory
.
The class also utilises pushExercise(Exercise exercise, Set<ExerciseDetail> details)
function to add new Exercise
and
return a new Workout with update fields. There is also an option to push an exercise without the details with the overloaded
method which instead opts to use the pre-built Set of ExerciseDetails
in the Exercise
itself.
The Workout Class is represented by the class diagram below.
The Workout Class is managed by the following commands:
-
AddWorkoutCommand
- Adds a new emptyWorkout
intoWorkoutPlanner
-
DeleteWorkoutCommand
- Deletes aWorkout
specified by anIndex
fromWorkoutPlanner
-
PushExerciseCommand
- Pushes anExercise
specified by anIndex
into an existingWorkout
Exercise Management
In order to run a Workout
, users will have to add Exercises
into the Workout
as an empty workout cannot be ran.
Users can use existing exercises or create their own custom exercises. Every exercise contains the following information:
-
ExerciseName
representing the name of the exercise -
MusclesTrained
comprising of the primaryMuscleType
as well as an ArrayList of secondaryMuscleType
trained -
Intensity
or how demanding the exercise is -
A set of
ExerciseDetails
which are optional additional information of the exercise such asExerciseWeight
,Distance
,Sets
andRepetitions
-
ExerciseHistory
containing information on all the previousExerciseRun
of the exercise
Like Workout
, Exercise
also has the method updateHistory
which returns an updated Exercise with a new
ExerciseRun
accounted for.
The Exercise class is represented by the following class diagram below.
The Exercise
class is managed by the following commands :
-
AddExerciseCommand
- Adds a newExercise
intoWorkoutPlanner
-
ClearExerciseCommand
- Clears all theExercise
objects inWorkoutPlanner
-
DeleteExerciseCommand
- Deletes anExercise
specified by anIndex
fromWorkoutPlanner
-
EditExerciseCommand
- Edits the specifiedExercise
with newly specified information -
FindExerciseByIntensityCommand
- Lists allExercise
objects with theIntensity
specified -
FindExerciseByMuscleCommand
- Lists allExercise
objects which trains theMuscleType
specified -
'FindExerciseCommand' - Lists all
Exercise
objects withExerciseName
that contains the string specified -
'ListExercise' - Lists all 'Exercise' objects in
WorkoutPlanner
All the exercise and workout commands above are parsed in DukeCooksParser
, invoking the respective Command Parsers
(Add, Delete, Edit etc.). The Exercise
/Workout
variant of the parser will then be instantiated
(i.e AddExerciseCommandParser
,DeleteWorkoutCommandParser
etc) to create the actual command objects
(i.e AddExerciseCommand, DeleteWorkoutCommand etc). These Command Objects will then execute the necessary steps
to fulfill their functionality.
Running of Workouts
The core functionality of the WorkoutPlanner is to run a Workout
and have it automatically tracking your progress
by making records in its history. This is done through the Run Workout Command
. The following sequence diagrams show
what happens when the command is invoked.
As seen in the diagram above, when the command is invoked, the RunWorkoutParser
is initialised to parse the argument String
to initialise RunWorkoutCommand
. The Command object will then run its execute method, which calls upon get method of
UniqueWorkoutList to obtain the target Workout
. The target workout and message will then be passed back to the Ui through
the CommandResult object. The Ui will then boot a new RunWorkoutWindow
with the targeted workout.
The user will then run each set of each exercise until the workout is complete. The full loop is demonstrated in the activity diagram in Figure 17.
Upon completion of the workout, the Ui will immediately generate a new UpdateWorkoutCommand
containing the Workout
that has been ran and a newly instantiated WorkoutRun
with the details of the run. UpdateWorkoutCommand
will then
be executed and the following will occur:
-
New Workout will be generated.
Using Workout’supdateHistory
method, a new updatedWorkout
will be created. -
The outdated Workout will be replaced by the new Workout.
UsingUniqueWorkoutList
'ssetWorkout
method, the old workout will be removed and the updated one will be placed in its stead -
CommandResult is generated and passed back to Ui.
A new CommandResult will be returned containing a congratulatory message to the Ui signalling the successful completion of the workout.
Design Considerations
Aspect | Option 1 (Chosen) | Option 2 |
---|---|---|
Storing an Exercise/Workout’s intensity |
Intensity was stored as an Enumeration instead of a class Pros Cons Our Choice |
Setting Intensity as a Class Pros Cons |
Storing MuscleTypes |
Have MuscleType be a class on its own Pros Cons Our Choice |
Store MuscleType as an enumeration Pros Cons |
Storage of Exercises in Workout |
Workouts only store a list of ExerciseName and not the full exercise Pros Cons Our Choice In the end we decided to choose this option as we foresee that the edit command will be utilised more often than calling an exercise from a workout. Furthermore, to improve timing, we kept a sorted storage for exercise to allow for the quicker binary search. |
Workouts store whole Exercises Pros Cons |
The Health Records feature allows user to track their health progress using the commands
add health
, as well as making changes to their records with edit health
, delete health
and clear health
.
Additionally, users can create and personalize their own profile with the commands add profile
,
edit profile
and delete profile
.
In a nut shell, the Health Records feature supports the following operations:
-
Storing of the health data recorded by the user
-
Present the data with graph representation that shows the health progress of the user
-
Personalizing user profile
A Record
consist of four key components: Type
, Timestamp
, Value
, Remark
.
Refer to the diagram below to understand the structure of Record
.
Record
class supports multiple commands. It includes:
-
AddRecordCommand
- Adds aRecord
into DukeCooks -
DeleteRecordCommand
- Deletes aRecord
from DukeCooks -
EditRecordCommand
- Edits the specifiedRecord
with a new changes made -
ListHealthCommand
- Lists all available records to user -
ListHealthByTypeCommand
- Lists all available records of the corresponding record type to user -
ClearHealthCommand
- Clears all available records
Person
class supports multiple commands. It includes:
-
AddProfileCommand
- Adds a newPerson
into DukeCooks -
DeleteProfileCommand
- Deletes thePerson
into DukeCooks -
EditProfileCommand
- Edits various fields of the specifiedPerson
Important
|
User Profile consist of one |
The sequence diagram below illustrates the execution of an AddRecordCommand
.
The command will first be parsed in 'DukeCooksParser' and be handled to its corresponding parsers (i.e. AddRecordCommandParser
) based on its variant word
(i.e. Add
).
After the parsing has been executed successfully, the string argument will be converted to Type
, Value
, Timestamp
,
Remark
attributes. These attributes will then be passed into the Model
component for its underlying validity checks.
Once all checks have passed successfully, a new Record
will be created according to those attributes
and added to health records with the command Model#addRecord
.
ℹ️
|
The illustration of the above execution only applies to all record types except for Weight and Height. Refer to the next section for more details. |
To illustrate why the simple model of AddRecordCommand
will not suffice for Weight and Height record types,
below depicts the class diagram of the Person
class
It can be observed that Person
share similar attributes to Record
, both Weight and Height.
However, the implementation of the two types are distinctly different among Record
and Person
. Record
have
structured Weight and Height to be attributes of its Type
enum class while Person
has defined
them separately to be classes of their own.
The Sequence diagram below illustrates the execution of AddRecordCommand
with additional component introduced to
help sync data between the Record
and Person
.
Once Model#addRecord
has been executed, AddRecordCommand
will check the type of
the newly added record. If it is a Weight or Height record, LinkProfile#updateProfile
will be called. The LinkProfile
helps AddRecordCommand
to invoke EditProfileCommand
by
calling for changes to be made for profile.
A simplified activity diagram of the problem can be shown below.
Once profile have been successfully updated, person’s weight/ height is expected to be in sync with health records.
Aspect | Option 1 (Chosen) | Option 2 |
---|---|---|
Data Structure used to store Record Type |
Pros Cons This choice was chosen as it is easier to maintain for our model. Additionally, the extension of having user defined health types would mean more validity checks and maintenance needed. |
A Pros Cons |
The current implementation of Diary consists of the following:
-
Each
Diary
consists of a uniqueDiaryName
-
Each
Diary
contains an array list ofPage
-
Each
Page
is identified by a uniqueTitle
,PageType
,Page Description
andPage Image
-
Each class has their respective getter methods
The class diagram below gives an overview of the Diary
class.
Diary
class supports multiple commands. It includes:
-
AddDiaryCommand
- Adds aDiary
into DukeCooks -
DeleteDiaryCommand
- Deletes aDiary
from DukeCooks -
EditDiaryCommand
- Edits the specifiedDiary
with a newDiaryName
-
ViewDiaryCommand
- Views the specifiedDiary
using the provided index -
FindDiaryCommand
- Finds the specifiedDiary
using the provided keyword -
ListDiaryCommand
- Lists all available diaries to user -
AddPageCommand
- Adds a newPage
to the specifiedDiary
-
DeletePageCommand
- Deletes thePage
in the specifiedDiary
-
EditPageCommand
- Edits various fields of the specifiedPage
All the above commands behave similarly. The commands will be parsed in DukeCooksParser
first and based on their
types (i.e Add, Delete, Edit etc), the corresponding variant parsers will be invoked (i.e AddDiaryCommandParser
,
DeleteDiaryCommandParser
etc). After which, the corresponding command will be executed (i.e AddDiaryCommand
,
DeleteDiaryCommand
etc).
The figure below describes the execution of an DeleteDiaryCommand
.
After a successful execution, the specified diary will be removed.
All images used in DukeCooks are copied into an internal data folder and all subsequent loading of images is done from within this internal folder. The following activity diagram explains how an image is created in DukeCooks:
Aspect | Option 1 (Chosen) | Option 2 |
---|---|---|
Data structures used to store |
Pros Cons Our Choice |
Pros Cons |
Loading of images |
Defensively copies images into our internal data folder and all subsequent loading of images is done from this folder. Pros Cons Our Choice |
Load images directly from user’s directory Pros Cons |
We are using java.util.logging
package for logging. The LogsCenter
class is used to manage the logging levels and logging destinations.
-
The logging level can be controlled using the
logLevel
setting in the configuration file (See Section 4.9, “Configuration”) -
The
Logger
for a class can be obtained usingLogsCenter.getLogger(Class)
which will log messages according to the specified logging level -
Currently log messages are output through:
Console
and to a.log
file.
Logging Levels
-
SEVERE
: Critical problem detected which may possibly cause the termination of the application -
WARNING
: Can continue, but with caution -
INFO
: Information showing the noteworthy actions by the App -
FINE
: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
We use asciidoc for writing documentation.
ℹ️
|
We chose asciidoc over Markdown because asciidoc provides greater flexibility in formatting. |
You may refer to UsingGradle.adoc and learn how to render .adoc
files locally to preview the end result of your edits.
Alternatively, you can download the AsciiDoc plugin for IntelliJ, which allows you to preview the changes you have made to your .adoc
files in real-time.
You may refer to UsingPlantUml.adoc and learn how to create and update UML diagrams in the Developer Guide.
You may refer to UsingTravis.adoc and learn how to deploy GitHub Pages using Travis.
We use Google Chrome for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.
Here are the steps to convert the project documentation files to PDF format.
-
Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the
docs/
directory to HTML format. -
Go to your generated HTML files in the
build/docs
folder, right click on them and selectOpen with
→Google Chrome
. -
Within Chrome, click on the
Print
option in Chrome’s menu. -
Set the destination to
Save as PDF
, then clickSave
to save a copy of the file in PDF format. For best results, use the settings indicated in the screenshot below.
The build.gradle
file specifies some project-specific asciidoc attributes which affects how all documentation files within this project are rendered.
💡
|
Attributes left unset in the build.gradle file will use their default value, if any.
|
Attribute name | Description | Default value |
---|---|---|
|
The name of the website. If set, the name will be displayed near the top of the page. |
not set |
|
URL to the site’s repository on GitHub. Setting this will add a "View on GitHub" link in the navigation bar. |
not set |
Each .adoc
file may also specify some file-specific asciidoc attributes which affects how the file is rendered.
Asciidoctor’s built-in attributes may be specified and used as well.
💡
|
Attributes left unset in .adoc files will use their default value, if any.
|
Attribute name | Description | Default value |
---|---|---|
|
Site section that the document belongs to.
This will cause the associated item in the navigation bar to be highlighted.
One of: * Official SE-EDU projects only |
not set |
|
Set this attribute to remove the site navigation bar. |
not set |
The files in docs/stylesheets
are the CSS stylesheets of the site.
You can modify them to change some properties of the site’s design.
The files in docs/templates
controls the rendering of .adoc
files into HTML5.
These template files are written in a mixture of Ruby and Slim.
|
Modifying the template files in |
There are two ways to run tests.
Method 1: Using IntelliJ JUnit test runner
-
To run all tests, right-click on the
src/test/java
folder and chooseRun 'All Tests'
-
To run a subset of tests, you can right-click on a test package, test class, or a test and choose
Run 'ABC'
Method 2: Using Gradle
-
Open a console and run the command
gradlew clean test
(Mac/Linux:./gradlew clean test
)
ℹ️
|
You may refer to UsingGradle.adoc for more information on how to run tests using Gradle. |
We have three types of tests:
-
Unit tests targeting the lowest level methods/classes.
e.g.dukecooks.StringUtilTest
-
Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
e.g.dukecooks.storage.StorageManagerTest
-
Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
e.g.dukecooks.LogicManagerTest
You may refer to UsingGradle.adoc and learn how to use Gradle for build automation.
We use Travis CI and AppVeyor to perform Continuous Integration on our projects. Refer to UsingTravis.adoc and UsingAppVeyor.adoc for more details.
We use Coveralls to track the code coverage of our projects. Refer to UsingCoveralls.adoc for more details.
When a pull request make changes to asciidoc files, you can use Netlify to see a preview of how the HTML version of those asciidoc files will look like when the pull request is merged. Refer to UsingNetlify.adoc for more details.
Here are the steps to create a new release.
-
Update the version number in
MainApp.java
. -
Generate a JAR file using Gradle.
-
Tag the repo with the version number. e.g.
v0.1
-
Create a new release using GitHub and upload the JAR file you created.
A project often depends on third-party libraries. For example, DukeCooks depends on the Jackson library for JSON parsing. Managing these dependencies can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives:
-
Include third-party libraries in the repository (This bloats the repository size)
-
Require developers to download third-party libraries manually (Requires extra work from developers)
Target user profile:
-
health conscious individuals
-
prefers to have a complete health monitoring all in one app
-
prefer desktop apps over other types
-
can type fast
-
prefers typing over mouse input
-
is reasonably comfortable using CLI apps
Value proposition:
DukeCooks monitors all aspects of the user’s lifestyle in one single application. This provides the user with the added
benefit of having to only manage a single application, instead of managing different applications. This is particularly
useful for busy individuals who do not have time to manage different applications.
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
|
Home Cook |
store all my recipes |
easily list out all the ingredients I have to buy for a weekly meal plan |
|
Unorganised student |
sort my task list by date |
see which task to prioritise |
|
Unmotivated student |
receive motivational messages upon completing tasks |
gain motivation |
|
Health conscious person |
track my health progress |
manage my diet |
|
Fitness enthusiast |
track my exercises |
keep a history of my fitness progress |
|
Food Blogger |
consolidate my food and exercise plan |
share them with other interested individuals |
|
Foodie |
find new places to eat |
try out new places and share the experience with others |
|
Fun Seeker |
keep track of my health in a game like way |
have fun and be healthy at the same time |
The following is a Use Case diagram (Figure 29) for the Dashboard.
(For all use cases below, the System is DukeCooks
and the Actor is the user
, unless specified otherwise)
A use case for marking a task as complete.
Software System: DukeCooks UseCase: UC01 - Mark as complete Actors: User MSS: 1. User completes a task and wants to marked it as complete. 2. User selects a task according to it's index number to mark as complete. 3. User enters the command `done task` followed by the index number in the command box. 4. DukeCooks finds the inputted index number and corresponding task and marks it as complete. 5. DukeCooks updates the Dashboard to reflect the changes made. 6. DukeCooks returns a message: Task is marked as complete, for the user. Extensions: 1a. User is not currently at the Dashboard. 1a1. User enters `goto dashboard`. 1a2. DukeCooks displays the Dashboard to the user. Use case resumes from step 2. 3a. DukeCooks detects an error in the entered data. 3a1. DukeCooks displays an error message. 3a2. User enters new data. Steps 3a1. and 3a2. are repeated until the data entered are correct. Use case resumes from step 4.
MSS
-
User requests to list recipes
-
DukeCooks shows a list of recipes
-
User requests to add a recipe to the list
-
DukeCooks adds the recipe to the list
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. DukeCooks shows an error message.
Use case resumes at step 2.
-
MSS
-
User requests to add glucose record
-
A new glucose record is added to Health Records
-
DukeCooks filters the records to only glucose related records
-
DukeCooks directs user to health record’s record type page.
Use case ends.
Extensions
-
1a. DukeCooks detects error in parsing user input
-
1a1. DukeCooks displays error message
-
1a2. User enters new input
Steps 1a1. and 1a2. are repeated until the data entered are correct. Use case resumes from step 2.
-
MSS
Extensions
-
2a. The exercise list is empty
-
2a1. DukeCooks updates the list with pre-made exercises.
Use case resumes at step 2.
-
-
3a. The given index is invalid.
-
3a1. DukeCooks shows an error message.
Use case resumes at step 2.
-
MSS
-
User requests to add a new diary
-
A new diary is added with the specified name
Use case ends.
Extensions
-
1a. Diary name already exists.
-
1a1. DukeCooks displays an error message.
Use case ends.
-
-
DukeCooks should work on any mainstream OS as long as it has Java
11
or above installed. -
DukeCooks should be able to hold up to 1000 recipes without a significant reduction 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.
-
DukeCooks should be able to run with or without internet connection.
-
DukeCooks should not require the user to do any installation.
- Mainstream OS
-
Windows, Linux, Unix, OS-X
- Workout
-
A list of Exercise planned to be done in one session.
- Exercise
-
An activity that works a specific or multiple muscles, usually a repetition of an action.
Given below are instructions to test the app manually.
ℹ️
|
These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing. |
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with a set of sample data. The window size will be automatically set to full-screen.
-
-
Adding a recipe while all recipes are listed
-
Prerequisites: List all recipes using the
list
command. Multiple recipes should be shown in the list. Ensure no recipe in the Recipe Book is named "Chicken Rice". -
Test case:
add recipe n/Chicken Rice i/Chicken i/Rice cal/666 carb/55 fats/44 prot/30
Expected: A new recipe named "Chicken Rice" is added to the Recipe Book. Details of the added recipe are shown in the result display, notably the name and the ingredients of the added recipe. -
Test case:
add recipe n/Chicken Rice i/Chicken i/Rice cal/666 carb/55 fats/44 prot/30
(A repeat of the previous test case)
Expected: No recipe is added. Error message is shown in the result display. Status bar remains the same. -
Other incorrect add commands to try:
add recipe
,add recipe n/<name>
(where <name> is the only specified parameter)
Expected: Similar error messages to previous.
-
-
Deleting a recipe while all recipes are listed
-
Prerequisites: List all recipes using the
list
command. Multiple recipes should be shown in the list. -
Test case:
delete recipe 1
Expected: First recipe is deleted from the list. Details of the deleted recipe are shown in the result display. Any meal plan containing the first recipe should also have the recipe removed from the meal plan. -
Test case:
delete recipe 0
Expected: No recipe is deleted. Error message is shown in the result display. Status bar remains the same. -
Other incorrect delete commands to try:
delete recipe
,delete recipe x
(where x is larger than the list size)
Expected: Similar error messages to previous.
-
-
Editing a recipe while all recipes are listed
-
Prerequisites: List all recipes using the
list
command. Multiple recipes should be shown in the list. Ensure no recipe in the Recipe Book is named "Chicken Rice". -
Test case:
edit recipe 1 n/Chicken Rice
Expected: First recipe from the list is edited. Details of the edited recipe are shown in the result display, notably the name and ingredients of the edited recipe. Any meal plan containing the first recipe should also have the recipe removed from the meal plan. -
Test case:
edit recipe 0
Expected: No recipe is edited. Error message is shown in the result display. Status bar remains the same. -
Other incorrect edit commands to try:
edit recipe
,edit recipe x
(where x is larger than the list size)
Expected: Similar error messages to previous.
-
-
Adding a meal plan while all meal plans are listed
-
Prerequisites: List all meal plans using the
list
command. Multiple meal plans should be shown in the list. Ensure no meal plan in the Meal Plan Book is named "Vegetarian Plan". -
Test case:
add mealplan n/Vegetarian Plan
Expected: A new meal plan named "Vegetarian Plan" is added to the Meal Plan Book. Details of the added meal plan are shown in the result display, notably the name and the recipes of the added meal plan. -
Test case:
add mealplan n/Vegetarian Plan
(A repeat of the previous test case)
Expected: No meal plan is added. Error message is shown in the result display. Status bar remains the same. -
Other incorrect add commands to try:
add mealplan
,add mealplan n/<name>
(where <name> is non-alphanumeric, or longer than 40 characters)
Expected: Similar error messages to previous.
-
-
Deleting a meal plan while all meal plans are listed
-
Prerequisites: List all meal plans using the
list
command. Multiple meal plans should be shown in the list. -
Test case:
delete mealplan 1
Expected: First meal plan is deleted from the list. Details of the deleted meal plan are shown in the result display. -
Test case:
delete mealplan 0
Expected: No meal plan is deleted. Error message is shown in the result display. Status bar remains the same. -
Other incorrect delete commands to try:
delete mealplan
,delete mealplan x
(where x is larger than the list size)
Expected: Similar error messages to previous.
-
-
Editing a meal plan while all meal plans are listed
-
Prerequisites: List all meal plans using the
list
command. Multiple meal plans should be shown in the list. Ensure no meal plan in the Meal Plan Book is named "Vegetarian Plan". -
Test case:
edit mealplan 1 n/Vegetarian Plan
Expected: First meal plan from the list is edited. Details of the edited meal plan are shown in the result display, notably the name and recipes of the edited meal plan. -
Test case:
edit mealplan 0
Expected: No meal plan is edited. Error message is shown in the result display. Status bar remains the same. -
Other incorrect edit commands to try:
edit mealplan
,edit mealplan x
(where x is larger than the list size)
Expected: Similar error messages to previous.
-
-
Adding a task while all tasks are listed
-
Prerequisites: List all tasks using the
goto dashboard
command. Multiple tasks are in the list. -
Test case:
add task tn/ Project meeting at COM1 td/ 12/12/2019
Expected: A new task named "Project meeting at COM1" is added to the list. Details of the added task is shown in the result display. -
Test case:
add task tn/ Project meeting at COM1 td/ 12/12/2019
(A repeat of the previous test case)
Expected: No task is added. Error message is shown in the result display. Status bar remains the same.
-
-
Deleting a task while all tasks are listed
-
Prerequisites: List all tasks using the
goto dashboard
command. Multiple tasks are in the list. -
Test case:
delete task 1
Expected: First task is deleted from the list. Details of the deleted task is shown in the result display. -
Test case:
delete task 0
Expected: No task is deleted. Error message is shown in the result display. Status bar remains the same. -
Other incorrect delete command to try:
delete diary x
(where x is larger than the list size)
Expected: Similar error messages to previous.
-
-
Editing a task while all tasks are listed
-
Prerequisites: List all tasks using the
goto dashboard
command. Multiple tasks are in the list. -
Test case:
edit task 1 tn/Lunch meeting
Expected: First task is edited to the new task name. Details of the edited task is shown in the result display. -
Test case:
edit task 1 tn/Lunch meeting!
Expected: No task is edited. Error message is shown in the result display. Status bar remains the same. -
Other incorrect edit command to try:
edit task 1 tn/ y
(where y contains more than 35 characters)
Expected: Similar error messages to previous. -
Test case: `edit task 1 td/31/11/2019
Expected: No task is edited. Error message is shown in the result display. Status bar remains the same. -
Other incorrect edit command to try:
edit task 1 tn/ 29/2/2019
(2019 is not a leap year; February only has 28 days)
Expected: Similar error messages to previous.
-
-
Adding a diary while all diaries are listed
-
Prerequisites: List all diaries using the
list
command. Multiple diaries are in the list. -
Test case:
add diary n/ Yummy Food
Expected: A new diary named "Yummy Food" is added to the list. Details of the added diary is shown in the result display. -
Test case:
add diary n/ Yummy Food
(A repeat of the previous test case)
Expected: No diary is added. Error message is shown in the result display. Status bar remains the same. -
Other incorrect add commands to try:
add diary n/ <name>
(where <name> already exists in the diary list)
Expected: Similar error messages to previous.
-
-
Deleting a diary while all diaries are listed
-
Prerequisites: List all diaries using the
list
command. Multiple diaries are in the list. -
Test case:
delete diary 1
Expected: First diary is deleted from the list. Details of the deleted diary is shown in the result display. -
Test case:
delete diary 0
Expected: No diary is deleted. Error message is shown in the result display. Status bar remains the same. -
Other incorrect delete commands to try:
delete diary
,delete diary x
(where x is larger than the list size)
Expected: Similar error messages to previous.
-
-
Editing a diary while all diaries are listed
-
Prerequisites: List all diaries using the
list
command. Multiple diaries should be shown in the list. Ensure no diary in the diary list is named "Asian Food". -
Test case:
edit diary 1 n/ Asian Food
Expected: First diary from the list is edited. Details of the edited diary are shown in the result display, notably the name of the edited diary. -
Test case:
edit diary 0
Expected: No diary is edited. Error message is shown in the result display. Status bar remains the same. -
Other incorrect edit commands to try:
edit diary
,edit diary x
(where x is larger than the list size)
Expected: Similar error messages to previous.
-
-
Deleting a page from a specified diary
-
Prerequisites: List all diaries using the
list
command. "Yummy Food" exists in the diary list and has at least one page. -
Test case:
delete page 1 n/ Yummy Food
Expected: Page 1 of the diary named "Yummy Food" will be deleted. Details of the deleted page is shown in the result display. -
Test case:
delete page 0 n/ Yummy Food
Expected: No page is deleted. Error message is shown in the result display. Status bar remains the same. -
Other incorrect delete commands to try:
delete page
,delete page 1 n/ <name>
(where <name> does not exists in the diary list)
Expected: Similar error messages to previous.
-
-
Deleting an exercise from the exercise list
-
Prerequisites: List all exercises using the
list
command. "Pushups" exists in the exercise list. -
Test case:
delete exercise 1
Expected: Exercise "Pushups" is deleted. Details of the deleted exercise is shown in the result display. -
Test case:
delete exercise 0
Expected: No exercise is deleted. Error message is shown in the result display. Status bar remains the same. -
Other incorrect delete commands to try:
delete exercise
,delete exercise <index>
(where <index> does not exists in the list)
Expected: Similar error messages to previous.
-
-
Dealing with missing/corrupted data files
-
Identify the file that is causing the error.
It should be one of the following:-
.\data\dashboard.json
-
.\data\diary.json
-
.\data\exercises.json
-
.\data\healthrecords.json
-
.\data\mealplans.json
-
.\data\recipes.json
-
.\data\userprofile.json
-
.\data\workouts.json
-
-
After identifying the file that causes the error, delete that particular data file.
By deleting the data file, a new data file will be generated from the SampleUtil
corresponding
to the file that was deleted.