By: Team F11-1
Since: March 2020
Licence: MIT
Table of Contents:
- 1. About this guide
- 2. Setting up
- 3. Design
- 4. Implementation
- 4.1. Generating consumption statistics and insights report
- 4.2. Setting daily calorie goals for motivation
- 4.3. Searching for specific
Food
via categories and substrings - 4.4. Lexicographical
Food
order - 4.5. Exporting the current
FoodRecord
into a portable file - 4.6.
Food
consumption management - 4.7. Modifying the
FoodRecord
- 4.8. Real-time Suggestions for existing
Food
inFoodRecord
- 4.9. Command guide
help
command - 4.10. Past seven days calorie data graph
- 4.11. Logging
- 4.12. Configuration
- 4.1. Generating consumption statistics and insights report
- 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. Getting help
- F.3. Creating a
Food
- F.4. Editing a
Food
- F.5. Deleting a
Food
- F.6. Listing all
Food
entries - F.7. Navigating
Food record
- F.8. Adding
Food
to consumption record - F.9. Removing a portion of
Food
from consumption record - F.10. Navigating
Consumption Record
- F.11. Setting a goal
- F.12. Generating report for the day
- F.13. Generating a copy of your
Food Record
- F.14. Clearing
Food Record
data - F.15. Saving data
- Appendix G: Effort
1. About this guide
This Developer Guide is a document to guide future software developers of the Calgo App by providing a sufficient and comprehensible overview of the project.
While we aim to provide a reasonable amount of depth, do keep in mind that the goal of this document is not to serve as a replacement for reading the actual code.
Welcome on-board the Software Development Team for Calgo! Together, we will inspire a healthier lifestyle!
2. Setting up
Refer to the guide here.
3. Design
3.1. Architecture
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.
|
The Architecture Diagram given above describes the high-level design of the Calgo Application. From now on, all instances of Calgo Application will be referred to as App. Given below is a quick overview of each component.
The Main
component comprises of two classes called Main
and
MainApp
.
This component is responsible for:
-
Launching App: Initializes the other components in the correct sequence, and connects them up with each other.
-
Exiting App: Shuts down the components and invokes cleanup method where necessary.
Commons
represents a collection of classes used by multiple other components.
In particular, the LogsCenter
class plays an important role at the architecture level:
-
LogsCenter
: Writes log messages to the App’s log file, for various classes.
The rest of the App comprises of four components.
Each of the four components:
-
Defines its Application Programming Interface (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 its API in the Logic.java
interface and exposes its functionality using the LogicManager.java
class.
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 n/Apple
.
delete n/Apple
commandThe sections below give more details of each component.
3.2. UI component
API : Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, FoodListPanel
, DailyListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class.
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.
3.3. Logic component
API :
Logic.java
-
Logic
uses theCalgoParser
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 food). -
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 interactions within the Logic
component for the execute("delete n/Apple")
API call.
delete n/Apple
Command
The lifeline for DeleteCommandParser and DeleteCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
3.4. Model component
API : Model.java
-
Model
stores user’s preferences in aUserPref
object. -
Model
also stores Food Record and Consumption Record data. -
This component exposes both
ObservableList<Food>
andObservableList<DisplayFood>
. The data stored in these two list objects is reflected in UI. Therefore, any changes made to the data in these lists are shown in the UI in real-time. -
To update the
Model
(and hence reflect the changes in the UI),Food
attributes need to satisfy certainPredicates
, which represent these changes. -
This component does not depend on any of the other three components.
To make Model follow the Object-Oriented Paradigm (OOP) more closely,
we can store a Tag list in Food Record , which Food objects can reference.
This would allow Food Record to only require one Tag object per unique Tag ,
instead of each Food needing their own Tag object.
An example of how such a model may look like is given in the below diagram. |
3.5. Storage component
API : Storage.java
The Storage
component allows us to save FoodRecord
, UserPref
, Goal
, and ConsumptionRecord
data in json format onto the disk, and read them back later on during the next session.
This would facilitate the following functions:
-
Load past user App data and preferences.
-
Generate and save insights reports based on previously and currently recorded user consumption.
-
Generate and save a user-friendly version of the accumulated
FoodRecord
.
3.6. Common classes
Classes used by multiple components are in the life.calgo.commons
package.
4. Implementation
This section describes some noteworthy details on how certain features are implemented.
4.1. Generating consumption statistics and insights report
(by Vineeth)
This feature allows a user to automatically generate a report that contains statistics and personalised insights based on his or her food consumption pattern on a given date. Do note that the generated report is a .txt file.
The user can invoke this functionality by entering the report
command, which follows the following format: report d/DATE
.
This section explains:
-
how the
report
command works and the crucial method it invokes during execution (In the Implementation subsection). -
the various aspects that were deliberated over when coming up with the design of the statistics and insights report feature (In the Design Considerations subsection).
-
a summary that shows a simplified Activity Diagram that captures the essential logic in the execution of the
report
command (In the Summary subsection).
Before moving on to learn how the feature works, if you want to see what the report includes, you can refer to our User Guide.
4.1.1. Implementation
The specified feature is facilitated by a ReportGenerator
object. If you are interested in how ReportGenerator
fits into the architecture of Calgo,
refer to this section.
To learn how the report
command works, the most important method that you need to know is the generateReport()
method from the ReportGenerator
class.
Refer to the sequence diagram below to understand the top-level execution of the generateReport()
operation after the user enters a valid report
command.
report
command: generating 2020-03-27_report.txt
The lifelines for the ReportCommandParser object, ReportCommand object, ReportGenerator object should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.
|
From the above diagram, creating a report for the consumption patterns on 27th of March 2020 involves the following steps:
Step 1: User inputs report d/2020-03-27
to generate the insights report based on food consumption of the abovementioned date.
Step 2: This input is saved as a String
and passed into the LogicManager
.
Step 3: The String
input is parsed by CalgoParser
, which removes the "d/" Prefix
and sends
the date input to ReportCommandParser
.
Step 4: Once the ReportCommandParser
checks that the given date is valid, it creates a ReportCommand
object and
returns it to LogicManager
.
Step 5: LogicManager
then executes the ReportCommand
.
Step 6: From Model
, ReportCommand
retrieves the required objects to construct an instance of ReportGenerator
.
Step 7: With the relevant objects retrieved from Steps 6, ReportCommand
constructs a
ReportGenerator
object.
Step 8: Using the ReportGenerator
object, ReportCommand
invokes the crucial method generateInsights()
, which prints
neatly organised sections of analysed data based on the DailyFoodLog
of the input date. For the section that gives insights related to
the user’s favourite Food
, the past seven days of DailyFoodLog
objects are analysed.
Step 9: This newly generated report is saved in the data/reports
folder. If the report is successfully generated,
the CommandResult
is true. Otherwise, it is false. This CommandResult
object is finally returned to LogicManager
,
to signify the end of the command and GUI shows a result message to the user.
4.1.2. Design considerations
Many of the design considerations made for report
command are similar to that of the export
command. You can check
out the similar design considerations over here.
In addition to those design considerations, the following consideration is specific to the functionality of the report
command.
Aspect: How many days of past data should be used to analyse user’s favourite Food
for the suggestions section of the report.
-
Alternative 1 (current choice): Analyse past seven days of consumption data
-
Pros: This implementation is more efficient as it does not have to churn out all existing consumption data produced by the user so far.
-
Cons: The reliability of the suggestions may be impacted due to certain special events such as buffet celebrations, in which the user may consume
Food
that he or she does not usually eat.
-
-
Alternative 2: Analyse all data
-
Pros: The insights will be much more reliable and more tolerant of outlier data points.
-
Cons: The efficiency of the
report
command gets worse over time and may cause dissatisfaction to the user.
-
4.1.3. Summary
The following activity diagram summarizes what happens when user executes a report d/DATE
command with a correctly formatted date:
You can check out this code snippet to see how the ReportCommand
object determines if there are no Food
entries present
in the given date:
// if there is no food consumed on the given date, do not execute command if (!model.hasLogWithSameDate(queryDate) || model.getLogByDate(queryDate).getFoods().size() == 0) { throw new CommandException(MESSAGE_REPORT_FAILURE + "\n" + String.format(NO_SUCH_DATE, queryDate)) }
4.2. Setting daily calorie goals for motivation
(by Vineeth)
This feature helps a user chunk his or her long term goal of developing a healthy lifestyle into smaller daily goals. Psychologically, this helps to motivate them as the perceived difficulty of achieving the long term goal reduces.
The user can set a daily calorie goal with the goal
command, which follows the following format: goal GOAL
.
This section addresses how the goal
command works.
4.2.1. Implementation
In addition to the GoalCommandParser
, the goal
command relies heavily on the DailyGoal
class, which is part of the
Model
component. Refer to the Model
component diagram here.
To address the issue where a user does not want to set up a daily calorie goal, Calgo places a DUMMY_VALUE
of 0 calories,
as shown in the code snippet below, from DailyGoal
class. To cater to a wide range of users, it also has a broad range of acceptable
values, ranging from 1 to 99999. However, to guide users towards a healthy lifestyle, the App does display a warning
message whenever a user sets a goal below the MINIMUM_HEALTHY_CALORIES
.
// Values used for GoalCommandParser when parsing user inputted goals. public static final int MINIMUM_HEALTHY_CALORIES = 1200; public static final int MINIMUM_ACCEPTABLE_CALORIES = 1; public static final int MAXIMUM_ACCEPTABLE_CALORIES = 99999; // Default value, when user does not input a goal. public static final int DUMMY_VALUE = 0;
Refer to the sequence diagram below to understand how a goal
command is executed.
.Sequence Diagram for goal
command: updating daily calorie goal to 2000 calories.
The lifelines of the GoalCommandParser object, GoalCommand object and DailyGoal object should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.
|
The following steps explain the sequence diagram:
Step 1: User inputs goal 2000
to update his or her goal to 2000 calories.
Step 2: This input is saved as a String
and passed into the LogicManager
.
Step 3: The String
input is parsed by CalgoParser
, which sends the goal
value input to GoalCommandParser
.
Step 4: Once the GoalCommandParser
checks that the given value is valid, it converts the input to an Integer
and creates a GoalCommand
object and
returns it to LogicManager
.
Step 5: LogicManager
then executes the GoalCommand
, which in turn invokes updateDailyGoal
method of Model
.
Step 6: In Model
, the updateDailyGoal
method is a static method that generates a new DailyGoal
object with the corresponding input. This DailyGoal
object is returned to Model
, which replaces the existing DailyGoal
attribute of the ModelManager
with the newly generated DailyGoal
object.
4.2.2. Design Considerations
Aspect: Type of user input data that is required for goal command
-
Alternative 1 (current choice): Use a simple goal feature that accepts the user’s inputted value.
-
Pros:
-
The user is not daunted by the large amount of information he or she needs to provide to set a goal.
-
The user will not feel paranoid as Calgo does not ask for personal data such as height, weight, gender and age.
-
-
Cons:
-
The goal may not be effective unless the user diligently checks online for a appropriate goal and then enters it into Calgo.
-
-
-
Alternative 2: Use a scientific method to calculate the basal metabolic rate of the user.
-
Pros:
-
The goal is very effective because it matches their body type.
-
-
Cons:
-
A lot of data is required to be inputted by the user.
-
May cause users to avoid setting goals because of the large amount of personal data they need to store in Calgo.
-
User feeling uncomfortable about setting a goal will also affect effectiveness of
report
command.
-
-
4.2.3. Summary
In essence, the goal
command is a fun feature that is used to motivate the user and generate specific insights if the
user were to invoke the report
command after setting a daily calorie goal.
Refer to the Activity Diagram below for a visual summary of the logic behind the execution of the goal
command.
goal
command.4.3. Searching for specific Food
via categories and substrings
(by Eugene)
This section addresses how the find
and list
commands work. As they are complementary in their functions during the search process, both find
and list
commands will be explained together here for better coherence.
The find
command allows us to search through the FoodRecord
(via categorical or substring search) based on what the user enters for the Prefix
. Users may enter one and only one Prefix
. The search results can then be displayed in the GUI’s Food Record
.
Meanwhile, the list
command allows us to reset the GUI’s Food Record
to once again show all entries in lexicographical order. This can be thought of as the reverse of a find
command. However, unlike the find
command, the list
command does not use any Prefix
, and ignores any input after its command word.
Prefix here indicates which Food attribute we are interested in. Categorical search finds Food objects with values that match the user-specified value representing one of the nutritional categories (Calorie , Protein , Carbohydrate , or Fat ). Meanwhile, substring search finds matches for the user-entered substring in any part of the the Name or in any of the Tag objects belonging to the Food objects.
|
For more information on lexicographical ordering, please refer to its relevant section here. |
The above commands rely on the FindCommand
and ListCommand
objects respectively. Objects of both classes use a Predicate<Food>
object to filter through the current Food
objects, where Food
objects will be displayed in the GUI’s Food Record
should they evaluate these predicates to be true.
4.3.1. Implementation
To search via a particular Food
attribute, we use a FindCommandParser
to create the corresponding Predicate<Food>
based on which Food
attribute the Prefix
entered represents. This predicate is then used to construct a new FindCommand
object, which changes the GUI display when executed.
The class diagram below shows the relevant Predicate<Food>
classes used in the construction of FindCommand
objects.
FindCommand
objectsAs seen in the above class diagram, each Predicate<Food>
is indeed representative of either Name
, Calorie
, Protein
, Carbohydrate
, Fat
, or Tag
. Moreover, it should be noted that each of these predicates test against a Food
object, and therefore have a dependency on Food
.
The sequence diagram below demonstrates how the find
command works, for both categorical and substring search:
find
command: Categorical Search and Substring Search
The lifeline for the both of the FindCommandParser objects, and both of the FindCommand objects should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.
|
From the above, it is clear that both categorical search and substring search of the find
command have similar steps:
Step 1: LogicManager
executes the user input, using CalgoParser
to realise this is a find
command, and a new FindCommandParser
object is then created.
Step 2: The FindCommandParser
object parses the user-entered arguments that come with the Prefix
, creating a Predicate<Food>
object based on which Food
attribute the Prefix
represents.
-
In the above diagram examples, a
ProteinContainsKeywordsPredicate
object is created for the categorical search viaProtein
while aNameContainsKeywordsPredicate
object is created for the substring search viaName
.
Step 3: This Predicate<Food>
object is then used to construct a new FindCommand
object, returned to LogicManager
.
Step 4: LogicManager
calls the execute
method of the FindCommand
created, which filters for Food
objects that evaluate the predicate previously created to be true. It then returns a new CommandResult
object reflecting the status of the execution. These changes are eventually reflected in the GUI.
The find
command therefore searches through the existing FoodRecord
and then displays the relevant search results in the GUI’s Food Record
. To once again show all Food
entries in the display, we use the list
command.
In constrast to FindCommand
, the ListCommand
constructor takes in no arguments and simply uses the predicate Model.PREDICATE_SHOW_ALL_FOODS
to always show all Food
entries in its execute
method. This is described by the sequence diagram below:
list
command
The lifeline for the ListCommand object should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
How the list
command works:
Step 1: LogicManager
executes the user input, using CalgoParser
to realise this is a list
command, and a new ListCommand
object is created.
Step 2: LogicManager
then calls the execute
method of this ListCommand
, which uses Model.PREDICATE_SHOW_ALL_FOODS
to evaluate to true for all Food
objects in the FoodRecord
.
Step 3: LogicManager
then returns a new CommandResult
object to reflect the status of the execution, in the GUI. The GUI’s Food Record
reflects the above changes to show all Food
entries once again.
4.3.2. Design considerations
Aspect: Predicate construction source.
-
Alternative 1 (current choice): Each
Predicate<Food>
is constructed using a new object of type eitherName
,Calorie
,Protein
,Carbohydrate
,Fat
,Tag
.-
Pros:
-
Defensive programming by building new objects rather than relying on mutable sources.
-
Can reuse existing code and classes like ArgumentMultimap and their methods.
-
Models objects well to reflect the real-world.
-
-
Cons:
-
May be more resource-intensive than other alternatives.
-
New developers may not find this intuitive.
-
-
-
Alternative 2: Each
Predicate<Food>
is created using aString
which represents the keywords.-
Pros:
-
Easier to implement with fewer existing dependencies.
-
Less resource-intensive.
-
-
Cons:
-
More prone to bugs.
-
Difficult to ascertain which
Food
attribute it actually represents. -
More difficult to debug as
String
type is easily modified. -
Does not reflect good OOP practices
-
-
Aspect: Enabling substring search.
-
Alternative 1 (current choice): Allow substring search for both
Name
andTag
-
Pros:
-
Improves user experience.
-
Can reuse common code as the approach for both
Name
andTag
are similar. -
Generally easy to implement substring finding.
-
Can use regular expressions if needed, which are powerful and suitable for our purpose.
-
-
Cons:
-
Requires good understanding of the original project.
-
Need to know the
String
type, regular expressions, and their implications. -
Need to implement searching via multiple types of
Food
attributes and hence introduces more dependencies. -
Need to implement a new
Parser
class to detect each relevantPrefix
.
-
-
-
Alternative 2: Only allow exact word matches for
Name
andTag
-
Pros:
-
Can simply reuse large parts of the original project’s existing code.
-
Less prone to bugs.
-
Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.
-
-
Cons:
-
Diminishes user experience.
-
May not fully satisfy the user requirements.
-
Need to implement searching via multiple types of
Food
attributes and hence introduces more dependencies. -
Need to implement a new
Parser
class to detect each relevantPrefix
.
-
-
4.3.3. Summary
In essence, this section focuses on searching which is implemented via find
and list
commands.
The find
command performs a categorical search if a value from a nutritional category (Calorie
, Protein
, Carbohydrate
, Fat
) is specified. Otherwise, a substring search is performed to find Food
objects that contain the entered substring in their Name
or in one of their Tag
s. These rely on the Predicate<Food>
object used in constructing the FindCommand
, which depend on the Prefix
entered by the user.
Meanwhile, the list
command simply uses the predicate already defined in Model
to display all Food
objects.
The above can be summarised in the activity diagram below:
4.4. Lexicographical Food
order
(by Eugene)
This section addresses how the GUI Food Record
entries appear in lexicographical order, which is an effect of sorting Food
objects in the FoodRecord
.
Over time, users will eventually have many Food
entries — these should be sorted for a better experience. Intuitively, the lexicographical order is the most suitable here.
In essence, Food
objects are sorted by the UniqueFoodList
(which is inside FoodRecord
).
Sorting is performed each time Food
object(s) are newly added to the UniqueFoodList
, or during the initialisation of the UniqueFoodList
upon App start-up.
There is no need to re-sort when a Food
object is deleted or edited as the order is maintained.
For a better understanding of adding and editing Food objects using the update command, please refer to its relevant section here.
|
Although the the list command changes the GUI Food Record display, it does not actually perform sorting. It simply resets the GUI Food Record to show all Food entries, and is usually used after a find command. You can read more about them here.
|
4.4.1. Implementation
The UniqueFoodList
is able to sort Food
objects because the Food
class implements the Comparable<Food>
interface.
This allows us to specify the lexicographical order for sorting Food
objects via their Name
, using the following compareTo method in the Food
class:
public int compareTo(Food other) {
String currentName = this.getName().toString();
String otherName = other.getName().toString();
return currentName.compareTo(otherName);
}
How the sorting process works:
-
When the App starts up, a new
UniqueFoodList
is created from the source json file (if available) or otherwise the default entries, and the createdFood
objects are sorted as they are added to it. -
Existing
Food
objects are therefore arranged in lexicographic order byName
. -
Thereafter,
UniqueFoodList
sorts theFood
objects whenever newFood
objects are added.
It should be noted that sorting is only performed by the addFood
and setFoods
method of the UniqueFoodList
, which calls the sortInternalList
method. Not to be confused, the setFood
method, which is used when a Food
object is edited, does not perform any sorting.
The sequence diagram below shows how the lexicographical ordering is performed when Calgo starts up:
Based on the above diagram, when Calgo starts:
Step 1: We initialise the ModelManager
object. For this, we use previously stored user data if available (by reading in from the source json files). Otherwise, we use the default Calgo Food
entries.
Step 2: Before we can finish constructing a new ModelManager
object, we require the creation of a new FoodRecord
object which in turn requires the creation of a new UniqueFoodList
object.
Step 3: Once UniqueFoodList
is constructed, we introduce the initialising data into it using the setFoods
method. This calls the sortInternalList
method, which sorts the newly added Food
objects in the ObservableList<Food>
contained in UniqueFoodList
, according to the specified lexicographical order (defined in the Food
class).
Moving on, the sequence diagram below (which is a reference frame omitting irrelevant update
command details) describes the lexicographical sorting process when Food
objects are added (not edited) using the update
command:
This is in a reference frame as it is reused in the update section here)
|
Here, the diagram describes what happens after parsing the user input and creating an UpdateCommand
object. Since the Food
entered by the user is an entirely new Food
object without a Name
-equivalent Food
existing in the UniqueFoodList
:
Step 1: We call the respective addFood
and add
methods as seen in the diagram, eventually adding the Food
object into the UniqueFoodList
and arriving at its sortInternalList
method call.
Step 2: The sortInternalList
method then sorts the Food
objects in the ObservableList<Food>
contained in UniqueFoodList
, according to the specified lexicographical order defined in the Food
class.
During an update command, we do not perform sorting if the user enters a Food object that already has an existing counterpart with an equivalent Name in the UniqueFoodList .
|
Any re-ordering will eventually be reflected in the GUI, facilitated by the following (in the case of an update
command) or otherwise something similar:
model.updateFilteredFoodRecord(Model.PREDICATE_SHOW_ALL_FOODS);
4.4.2. Design considerations
Aspect: Frequency of sorting operation.
-
Alternative 1 (current choice): Sort whenever a new
Food
is added or during App start-up.-
Pros:
-
Guarantees correctness of sorting.
-
Saves on computational cost by not sorting during deletion or edits as the order is preserved.
-
Computational cost is not too expensive since the introduced
Food
objects usually come individually rather than as a collection (except during App start-up).
-
-
Cons:
-
Need to ensure implementations of various commands changing the
Model
are correct and do not interfere with the sorting process. -
May be computationally expensive if there are many unsorted
Food
objects at once, which is possible when Calgo starts up.
-
-
-
Alternative 2: Sort only when calling the
list
command.-
Pros:
-
Easier to implement with fewer existing dependencies.
-
Uses less computational resources since sorting is only done when
list
command is called.
-
-
Cons:
-
Diminishes user experience.
-
May be incompatible with certain
Storage
functions. -
May lead to bugs in overall product due to incompatible features.
-
-
Aspect: Data structure to store Food
objects.
-
Alternative 1 (current choice): Use
UniqueFoodList
to store allFood
objects.-
Pros:
-
Can reuse existing code, removing the need to maintain a separate list-like data structure.
-
Based on existing code, any changes to the
Model
from the sorting process are automatically reflected in the GUI. This is very useful for testing and debugging manually.
-
-
Cons:
-
Many of the underlying
ObservableList
methods are built-in and cannot be edited. They are also difficult to understand for those unfamiliar. This can make development slightly trickier, especially in following certain software engineering principles.
-
-
-
Alternative 2: Use a simpler data structure like an
ArrayList
.-
Pros:
-
Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.
-
-
Cons:
-
More troublesome as we require self-defined methods, abstracted over the existing ones. If not careful, these self-defined methods can possibly contain violations of certain software engineering principles, which may introduce regression in the future.
-
May be inefficient in using resources.
-
-
4.4.3. Summary
The UniqueFoodList
facilitates the lexicographical ordering of Food
objects and hence how their respective entries appear in the GUI Food Record
. This can be summarised in the activity diagram below:
4.5. Exporting the current FoodRecord
into a portable file
(by Eugene)
This section addresses how the export
command works, creating a FoodRecord.txt file showing details of all the Food
objects currently stored in the FoodRecord
. The information is presently neatly in table form and the file is created in the data/exports
folder.
The export
command mainly uses an ExportGenerator
object to generate the file. All formatting options and methods to write the contents of the file are included in the ExportGenerator
class, which extends the DocumentGenerator
class.
You may find the report command similar as they both create a new file for the user. You can read more about it here.
|
4.5.1. Implementation
Most of the work in generating the file is done by the generateExport
method of ExportGenerator
. You can access the class to view its methods for writing the header and footer components, which are relatively easily to understand.
However, the methods for writing the file body is likely where some explaining is required. Here, the formatting of the table body is determined by the following:
private static final int NAME_COLUMN_SIZE = 45;
private static final int VALUE_COLUMN_SIZE = 20;
NAME_COLUMN_SIZE
represents the allowed space for the Name
. If a Food
object has a Name
which is too long, the Name
will be truncated and continued on the following lines.
Meanwhile, VALUE_COLUMN_SIZE
represents the allowed space for each nutritional value of Calorie
, Protein
, Carbohydrate
, and Fat
in the table. These are guaranteed to be within a length of 5 characters when parsing, and should not exceed the given space.
The nutritional values will always be shown in the first line of their respective Food
object after its (possibly truncated) Name
. This is facilitated by the printBody
method of ExportGenerator
, which calls its printBodyComponent
method and subsequently its generateFinalisedEntryString
method, which performs the truncation and amendment of the Name
as necessary.
Moving on, the sequence diagram below demonstrates how the export
command works to create the user copy of the current FoodRecord
:
export
command: Generating FoodRecord.txt
The lifeline for the ExportCommand object and that of the ExportGenerator object should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.
|
From the above, creating FoodRecord.txt involves the following steps:
Step 1: LogicManager
executes the user input, using CalgoParser
to realise this is a export
command, and a new ExportCommand
object is created.
Step 2: LogicManager then calls the execute
method of this ExportCommand
object. This results in a call to the Model
to get the current FoodRecord
, which is used to construct a new ExportGenerator
object. The ExportGenerator
is responsible for creating the FoodRecord.txt file and writing to it.
Step 3: ExportCommand
then calls the generateExport
method of ExportGenerator
, writing the required parts to the file. This returns a boolean indicating whether the file creation and writing are successful.
Step 4: A new CommandResult
object indicating the result of the execution is then constructed and reflected in the GUI.
4.5.2. Design considerations
Aspect: Type of file to create.
-
Alternative 1 (current choice): Create a .txt file to represent the
FoodRecord
.-
Pros:
-
Satisfies user requirements by allowing editing of the file to include custom entries.
-
-
Cons:
-
Need to define new classes and methods for file writing, which may introduce more dependencies.
-
May be more resource-intensive than other alternatives.
-
New developers may be unfamiliar with
String
manipulation and regular expressions.
-
-
-
Alternative 2: Create a .pdf file to represent the
FoodRecord
-
Pros:
-
The contents appear to be more legitimate.
-
Can use external libraries for convenience.
-
May be less resource-intensive.
-
-
Cons:
-
May not satisfy user requirements as the file cannot be edited easily.
-
May introduce more bugs, additional dependencies, and become prone to external factors.
-
More difficult to debug due to lack of familiarity with external libraries.
-
May require more space.
-
-
Aspect: Abstraction for ExportGenerator
and ReportGenerator
.
-
Alternative 1 (current choice): Create
DocumentGenerator
abstract class which bothExportGenerator
andReportGenerator
extends.-
Pros:
-
Good OOP practice, following its principles.
-
Allows for code reuse and neater code.
-
Able to apply concepts of polymorphism, if required.
-
May be now easier to debug.
-
-
Cons:
-
Need to define new class, possibly introducing more dependencies.
-
Need to identify what is common to both
ExportGenerator
andReportGenerator
.
-
-
-
Alternative 2: Use an interface which both classes will implement.
-
Pros:
-
Similar to Alternative 1.
-
-
Cons:
-
Does not allow methods to be defined in the interface. (Some exceptions: default methods, etc)
-
May need to repeat definitions which may be the same for both classes.
-
-
-
Alternative 3: Do not use an interface or abstract class.
-
Pros:
-
Requires less effort.
-
Does not introduce additional dependencies.
-
-
Cons:
-
Unable to reap benefits of the above alternatives.
-
-
4.5.3. Summary
In short, this section addresses how users are able to obtain an editable copy of the current FoodRecord
using the export
command.
The export
command largely relies on the ExportGenerator
class, which facilitates creating the file and writing to it.
The above can be summarised in the activity diagram below:
4.6. Food
consumption management
(by Ruicong)
This section addresses how nom
, vomit
, and stomach
commands work. They are the 3 commands that you will use
to interact with ConsumptionRecord
. nom
allows you to add Food
, vomit
allows you to remove Food
, and stomach
gives you a way to browse a list of Food
within the ConsumptionRecord
at a different date. ConsumptionRecord
is an important
component because it serves as a backend for features such as goal
, report
and graph
.
The high level idea of how Food
consumption is managed is that ConsumptionRecord
stores all the Food
consumed.
Whenever nom
, vomit
, or stomach
is used, a list will be retrieve and sent to a FilteredList. Such a list consists of DisplayFood
objects,
for the purpose of displaying information compiled from each Food
.
The FilteredList is an observable, so whenever it is updated, the GUI will be informed and display the contents accordingly.
4.6.1. Implementation
In this section, I will be walking you through the implementation of the ConsumptionRecord
, what happens on App startup,
and what happens when a consumption related command is called. I will be talking about the nom
command more specifically.
This is because vomit
and stomach
work very similarly, and you will see that it’s easy to understand once you have read through this
section.
In Calgo, you will find that the GUI ConsumptionRecord
use a uniqueDateToLogMap
to map each LocalDate
to a DailyFoodLog
.
As you can guess, LocalDate
keys are unique.
Each DailyFoodLog
is related to a LocalDate
object and contains 2 LinkedHashMap
, one to map Food
consumed to their portion,
another to map Food
to an ArrayList
of Integer
, which represents the ratings given to that Food
item consumed on that day.
On App startup, initModelManager
of MainApp
class is invoked. This will cause storage
to read consumption record data from a .json
file
which stores App data. The .json
file stores JsonAdaptedDailyFoodLog
, which is similar to DailyFoodLog
in every way,
but deals with JsonAdaptedFood
class instead of Food
. Notice that there are a chain of toModelType
commands
called as we dive deeper into the method call stack. toModelType
is actually the method to return a working counterpart of JsonSerializable
and JsonAdapted
classes
that will be delivered to the model of Calgo.
So you might ask, what does JsonAdapted mean? Well, JsonAdapted classes are specially formatted versions
of their counterparts that makes it easy for the Jackson API to read and write to.
|
Here is what happens when different classes call toModelType
:
-
JsonSerializableConsumptionRecord
returns its equivalent copy ofConsumptionRecord.
-
JsonAdaptedDailyFoodLog
returns its equivalent copy ofDailyFoodLog
.
Below shows the high level view of the initialization process:
For the subsequent sequence diagrams in the section, the lifelines for objects should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. |
Now that the ConsumptionRecord
has been initialized, the App can start interacting with the user.
Whenever the user enters a nom
command into the GUI, a sequence of events occur.
Here is a a step-by-step guide to what happens in such a scenario:
Step 1: UI
component MainWindow
receives the input as a String
. That String
is then passed into LogicManager,
which calls the parseCommand
of CalgoParser
. Suppose the String
is "nom n/Apple d/2020-04-12 portion/2 r/7"
CalgoParser
detects that this is a nom
command. CalgoParser
then delegates this job by creating
a new NomCommandParser
object which will parse this String
.
Step 2: NomCommandParser
gets relevant values from Prefixes
of input String
, and then checks with the ModelManager
.
It specifically checks if there exists a DailyFoodLog
with the same LocalDate
as what was parsed so that it can use existing information if
they are already present. From all these information, a DailyFoodLog
object representing the result of consuming a Food
is created, and supplied to create
NomCommand
. The diagram below shows how this happens:
Step 3: NomCommand
updates the ModelManager
with the DailyFoodLog
obtained during its execution by LogicManager
.
Such information cascades down the layers of abstraction until it reaches ConsumptionRecord
,
which updates its underlying 'uniqueDateToLogMap' aforementioned here.
Step 4: NomCommand
then informs the ModelManager
to update its FilteredList
, which gets information from the updated ConsumptionRecord
.
Since the FilteredList
is a wrapper of ObservableList
, its update will inform the UI components that utilise JavaFx of changes.
This results in the GUI automatically updating to reflect the changes.
Step 5: A new commandResult
object is created an passed back to MainWindow
, and displayed in Result Display
.
Step 6: Finally, the changes in ConsumptionRecord
are saved to StorageManager
.
4.6.2. Design considerations
Aspect: How nom
executes
-
Alternative 1 (current choice): Create a new
DailyFoodLog
to pass intoModelManager
and thenConsumptionRecord
.-
Pros:
-
Maintain comprehensive layers of abstraction
-
Allows code to be easily testable.
-
-
Cons: Difficult for newcomers or even existing users to trace because of long execution path.
-
-
Alternative 2: Bypass
ModelManager
or even not useConsumptionRecord
for storage of data during runtime by allowing everything to be done from parser.-
Pros: Reduce dependencies on
ModelManager
andConsumptionRecord
, and make code contained in a single class file easier to navigate. -
Cons: Violates layers of abstraction set in place by previous structure of AddressBook3. Violates Single Responsibility Principle and reduce cohesiveness of code.
-
Aspect: Data structure to support the consumption commands
-
Alternative 1 (current choice): Use a single
FilteredList
to store food for any day by repopulating it each time a consumption related command is used.-
Pros: Only uses a single
FilteredList
, so it is clear which list you are using for display. -
Cons: May have performance issue in terms of speed when there are too many entries.
-
-
Alternative 2: Use a
FilteredList
for each date, to store food consumed on that date.-
Pros: Faster retrieval for display of
ConsumedFood
items. However, under practical circumstances, the difference is negligible. -
Cons: May have performance issue in terms of storage because it requires many lists to be stored in addition to
LinkedHashMap
inDailyFoodLog
for eachLocalDate
.
-
Aspect: Selecting items to delete from ConsumptionRecord
using Vomit
command
-
Alternative 1 (current choice): Use index to select item to delete.
-
Pros: When the list is short, user can quickly identify the entry to delete.
-
Pros: Convenient for user as he is required to type less.
-
Pros: User need not spend effort remembering names.
-
Cons: When there are too many records, user is required to scroll through records.
-
-
Alternative 2: Use name to select item to delete
-
Pros: Can utilise the
Result Display
suggestion to improve user experience. -
Cons: User face the hassle of memorising names and typing more.
-
4.6.3. Summary
This section is a summary on all the above discussed. I would do so with the aid of a few activity diagrams so that you are clear about the flow of the processes covered.
The 2 diagrams below serves as (rakes), which shows more details.
4.7. Modifying the FoodRecord
(By Zhang Xian)
This section addresses how the FoodRecord
can be modified by the update
and delete
commands.
The update
command allows the user to modify the FoodRecord
by either adding a new Food
into the FoodRecord
or editing the
nutritional values of an existing Food
in the FoodRecord
.
From the user’s perspective, the update
command does either of the adding and editing functions. This implementation of update
decides whether to
override an existing Food
in the FoodRecord
with new values, or create a new Food
in the FoodRecord
for them.
For better user experience, for all new Food
being updated into the FoodRecord
with the update
command, the Name
attribute
will be formatted to proper case. This means that if the user updates a new Food
into the FoodRecord
with the Name
as "char kuay teow", the Food
that
is stored in the FoodRecord
will be of Name
"Char Kuay Teow".
When a new Food is updated into the FoodRecord, the FoodRecord is sorted in lexicographical order. For more information on how this is implemented, please refer to its relevant section here. |
The delete
command allows the user to modify the FoodRecord by deleting a specified Food
entry from the FoodRecord.
This command takes in the Name
of the Food
entry to be deleted.
For both delete
and update
commands, the Name
parameter is implemented to be case-insensitive. This means that n/APPLE
and n/apple
refers to
the same Food
entry with Name
stored as Apple
.
4.7.1. Implementation
The modification of the FoodRecord
is facilitated by UniqueFoodList
, which is responsible for storing all the Food
entries in the FoodRecord
.
Additional abstractions were used by Model
and Logic
for any operations that results in a modification of the UniqueFoodList
.
Both commands require an additional operation, hasFood
, in FoodRecord
to be implemented. hasFood
checks if there is an existing Food
in FoodRecord
by checking if there is any Food
in the FoodRecord
with the same Name
. Two Food
entries is deemed to be of the same Name
if their lowercase variant
is the same.
This operation was exposed in the Model
interface as hasFood
, allowing UpdateCommand
and DeleteCommand
this functionality.
Implementation of update
command:
For the update
command, the hasFood
operation decides whether UpdateCommand
adds a new Food
into UniqueFoodList
or
edits the nutritional values of an existing Food
in the UniqueFoodList
.
The following sequence diagram shows how the update
operation works in both cases:
update
command
The lifeline for UpdateCommandParser and UpdateCommand should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.
|
How the update
command works:
Step 1: LogicManager
executes the user input of update n/apple cal/52 p/2 c/14 f/1", using `CalgoParser
to realise this is
an update
command and creates a new UpdateCommandParser
object.
Step 2: UpdateCommandParser
then parses the arguments provided by CalgoParser
with the parse
method.
During this parsing process, UpdateCommandParser
calls the covertToTitleCase
method on the Name
argument, converting it
to proper case.
Step 3: UpdateCommandParser
then creates a new UpdateCommand
object, which LogicManager
calls the execute
method with this object
as an argument.
Step 4: UpdateCommand
now checks if there exists an existing Food
in the FoodRecord
by calling Model
's
hasFood
method.
Step 5:
-
Scenario 1: If Food already exists in the
FoodRecord
:-
Model calls the
getExistingFood
method with the user inputtedFood
as a parameter to get the existingFood
,existingFood
in theUniqueFoodList
. It thens call thesetFood
method to replace the existingFood
in theUniqueFoodList
with the newFood
which contains new nutritional values.
-
-
Scenario 2: If
Food
does not exist inFoodRecord
:-
This scenario is handled by the Lexicographical Ordering feature. Please refer to its relevant section here.
-
Model calls the
addFood
method with the user inputtedFood
as a parameter to add the newFood
into theUniqueFoodList
inFoodRecord
-
After the
Food
is added into theUniqueFoodList
, theUniqueFoodList
is also sorted in lexicographical order.
-
Step 6: A new CommandResult
object is then created and returned back to LogicManager
.
Implementation of delete
command:
For the delete
command, the hasFood
operation allows UpdateCommand
to check whether the Food that the user requests to be
deleted exists in the UniqueFoodList
.
The following sequence diagram shows how the delete
command works:
delete
command
The lifeline for DeleteCommandParser and DeleteCommand should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.
|
How the delete
command works:
Step 1: LogicManager
executes the user input of "delete n/Apple", using CalgoParser
to realise this is
an delete
command and creates a new DeleteCommandParser
object.
Step 2: DeleteCommandParser
then parses the arguments provided by CalgoParser
with the parse
method, before creating a new
DeleteCommand
object that is returned back to the LogicManager
which calls the execute
method with this as an argument.
Step 3: DeleteCommand
now checks if there exists an existing Food
in the FoodRecord
by calling Model’s `hasFood
method, which
checks if there is such Food
in the UniqueFoodList
.
Step 4: Model
then calls the getExistingFood
method to return the Food
object to be removed from the UniqueFoodList
. Thereafter,
Model
calls the deleteFood
method with this Food
object as an argument to remove this Food
from the UniqueFoodList
.
Step 5: A new CommandResult
object is then created and returned back to the LogicManager
.
4.7.2. Design considerations
Aspect: Updating the FoodRecord
when there is an existing Food
item in FoodRecord
-
Alternative 1 (current choice): Overrides the existing
Food
item with the newFood
item-
Pros:
-
No need for an additional command of
edit
just for the user to edit an existingFood
item in theFoodRecord
.
-
-
Cons:
-
Might not be intuitive for the user since the word "update" is generally assumed to be for editing something only and not necessarily adding something.
-
May result in additional performance overhead.
-
-
-
Alternative 2: Informs the user that there is already an existing
Food
item, and direct him to use another commandedit
to edit the existingFood
instead.-
Pros:
-
More intuitive for user, since he might not know that he is overriding an existing
Food
item
-
-
Cons:
-
Additional command has to be created just to handle editing
-
More tedious for user since more steps are required to achieve the same result.
-
-
4.7.3. Summary
In summary, this section explains how commands related to modifying the FoodRecord
is implemented.
The update
command is a smart command that either updates an existing Food
entry in the FoodRecord
with new nutritional information,
or updates a new Food
item into the FoodRecord
The following activity diagram summarises what happens when a user enters a valid update
command:
update
commandThe delete
command allows the user to remove a Food
entry from the FoodRecord
by specifying it’s Name
as an parameter.
The following activity diagram summarises what happens when a user enters a valid 'delete' command:
delete
command4.8. Real-time Suggestions for existing Food
in FoodRecord
(By Zhang Xian)
This section addresses how the GUI Result Display
suggests Food
with similar Name
to the user for the commands update
, delete
and nom
.
When the user have many Food
entries in the FoodRecord
, they may have difficulties finding out if a particular Food
exists in the FoodRecord
.
For better user experience, this feature listens to the input of the user for these three commands and suggests similar existing Food
entries in real time in the GUI’s
Result Display
.
This feature listens to the input of the user after the Prefix
n/
and checks if there is a Food
entry in the FoodRecord
with a similar Name
.
The Name parameter is case-insensitive and searches the Food entries in the FoodRecord by whether they start with the user input so far after the
Prefix n/ .
|
4.8.1. Implementation
To be able to process user’s input in real-time, we set a listener
in the CommandBox
to listen for the input of any of the three commands: update
, delete
or nom
This feature is then facilitated by different objects, mainly MainWindow
and UniqueFoodList
. MainWindow
interacts with LogicManager
's method of getSimilarFood
which exposes
the FoodRecord
, allowing a filtered list of similar Food
entries in the UniqueFoodList
to be returned back to the user.
A predicate, FoodRecordContainsFoodNamePredicate
is also essential in this implementation in ensuring that the correct similar Food
items can be filtered from the UniqueFoodList
back to the LogicManager
to be displayed by the GUI. The test
method of this predicate which is responsible for the above is shown:
public boolean test(Food food) {
boolean foodStartsWithInputFoodName = food.getName().fullName.toLowerCase()
.startsWith(foodName.toLowerCase().trim());
boolean inputFoodNameStartsWithFood = foodName.toLowerCase().trim()
.startsWith(food.getName().fullName.toLowerCase());
return foodStartsWithInputFoodName || inputFoodNameStartsWithFood;
}
Both of the boolean
used for this predicate is essential. For instance, if "Laksa is already present" in the FoodRecord
:
-
If the user keys in "Lak", the first
boolean
foodStartsWithInputFoodName
ensures that "Laksa" will be suggested to the user. -
If the user keys in "Laksa Spicy", the second
boolean
inputFoodNameStartsWithFood
ensures that "Laksa" will be suggested to the user.
The following sequence diagram will explain how the different objects interact to achieve the Real-time Suggestion Feature.
Based on the above diagram, when a user has already entered any of the CommandWord
: update
, delete
or nom
, and also the Prefix n/
:
Step 1: CommandBox
calls the MainWindow
method of getSuggestions
with the parameter as the entire String
of user input in the CommandBox
.
Step 2: MainWindow
then parses the user inputted String
and calls LogicManager
method of getSimilarFood
with the parameter foodName
which is the entire String
after the Prefix
n/
Step 3: The Model
then does the necessary work by calling methods getFoodRecord
and getFoodList
. This results in the current UniqueFoodList
being returned
Step 4: The UniqueFoodList
is then filtered with the Predicate<Food>
, FoodRecordContainsFoodNamePredicate
which returns a List<Food>
of Food
objects that have similar Name
fields to the user input.
Step 5: Finally, the filtered List<Food>
is then parsed into a String
for the user by the MainWindow
and then displayed in the GUI’s Result Display
.
4.8.2. Design Considerations
Aspect: How the suggestions is shown to the user.
-
Alternative 1: (current choice):
ResultDisplay
displays the names of similarFood
entries inFood Record
.-
Pros:
-
Improved user experience, allowing user to still view the unfiltered
FoodRecord
in the GUI. -
User can have access to the raw
String
of theName
similarFood
entries for copying and pasting.
-
-
Cons:
-
Additional interacting with
UI
components required, instead of just filteringUniqueFoodList
-
Cannot reusing existing lexicographical sorting feature of
FoodRecord
.
-
-
-
Alternative 2: Filter the GUI’s
Food Record
to show similar Food entries.-
Pros:
-
Feature is limited to minimal interactions with
UI
, making use of existingUI
-Model
abstractions. -
Compatible with existing code relating to the
FoodRecord
, allowing code to be reused.
-
-
Cons:
-
Takes away most of the need for
find
andlist
features since they achieve mostly the same purpose.
-
-
Aspect: Commands that utilise Real-time Suggestions
-
Alternative 1: (current choice): Only three commands:
update
,delete
,nom
-
Pros:
-
Improves computational performance, since real-time features for every command will be computationally expensive.
-
Keeps the desired outcomes of other features such as
find
andlist
intact
-
-
Cons:
-
Decrease in user experience, as they might expect this feature to be universal for all commands
-
-
-
Alternative 2 All the commands
-
Pros:
-
Better standardisation of feature across all commands.
-
-
Cons:
-
Additional computational overhead.
-
Not all commands have a
Name
field. -
Additional implementation or significant change in how this feature works is necessary to make it universal.
-
-
4.8.3. Summary
CommandBox
listens for any of the three commands as mentioned, allowing LogicManager
and FoodRecord
to facilitate
the suggestions of similar Food
entries from the UniqueFoodList
to display in the GUI’s Result Display
. This can be summarised
in the activity diagram below:
4.9. Command guide help
command
(by Janice)
This section addresses how the help
command works.
The help
command allows users to reference a summarised version of the User Guide (called the command guide)
containing the usages of the commands and their formats, arranged in alphabetical order. Users may enter an
optional command word
that filters the displayed command guide.
command word filters out only commands which contain the command word as a substring. If no commands contain it
as a substring, an error message will be displayed at the top of the GUI component Help Window and the full command guide will be shown.
|
4.9.1. Implementation
To generate a command guide using the help
command, a HelpCommand
object generates the relevant command guides
based on the provided command word
in the input.
The sequence diagram below demonstrates how the help
command works, should a command word
of "nom" be provided.
Step 1: LogicManager
executes the user input, using CalgoParser
to realise it is a help
command, and thus creates
HelpCommand
Step 2: HelpCommand
constructor generates the necessary mapping of command name to the corresponding command guide.
Step 3: LogicManager
calls the execute
method on the HelpCommand
object, which produces the String containing the
relevant command guides. A CommandResult
object is produced reflecting the response to the help
command.
-
In the above sequence diagram, one possibility shown, where the user provides a
command_word
.setFilteredGuide
will attempt to retrieve only relevant command guides, defaulting to a list of all guides if no relevant guides exist. Otherwise, by default a list of all guides will be provided.
Step 4: The CommandResult
is eventually passed to the MainWindow
class, which then displays the command guide in a separate
window, using the HelpWindow
class.
4.9.2. Design considerations
Aspect: How Help is displayed
-
Alternative 1 (current choice): GUI component
Help Window
is displayed as a separate popup.-
Pros:
-
User can refer to the command guide in a window separately from Calgo, keeping it present as they use the App.
-
Command guide can give a more detailed description of command usage and format as it has more space to display in.
-
No internet access is required as all information on commands is stored offline.
-
-
Cons:
-
help
does not redirect to a url containing the most up-to-date User Guide. Changes made to the User Guide must be updated inHelpCommand
separately. -
GUI component
Help Window
might obstruct view of the App upon initially loading it, causing annoyance.
-
-
-
Alternative 2: GUI component
Help Window
is not used, and instead content is displayed as part of GUI componentResult Display
.-
Pros:
-
No possibility of a popup blocking the main app.
-
All information is contained within a single window.
-
-
Cons:
-
User must use the
help
command every time they require a guide, as GUI componentResult Display
will be overwritten after other commands.
-
-
Aspect: Command guides can be selectively displayed
-
Alternative 1 (current choice):
help
displays all command guides by default. User can selectively filter to display only desired commands by entering an optional keyword afterhelp
.-
Pros:
-
Desired command can be more rapidly found.
-
Removes all unwanted commands from GUI component
Help Window
, reducing clutter.
-
-
Cons:
-
Filtered
help
does not benefit users who don’t know the command they’re looking for.
-
-
-
Alternative 2: Always display all command guides to ensure user will find the guide they require.
-
Pros:
-
No possibility of user being unable to find their desired command after sufficient searching.
-
-
Cons:
-
Relatively large array of commands can be overwhelming to a new user, deterring them from using the App.
-
Can be very frustrating to search through for experienced users.
-
-
4.9.3. Summary
help
will produce a popup, displaying a guide on the App’s available commands' purposes and usage format.
4.10. Past seven days calorie data graph
(by Janice)
This section addresses how the graph displaying the user’s past seven day’s daily total calorie consumption works.
Note that the graph counts starting from the date on the current Food Record
, and the six days prior to it.
The graph will always display the past seven days' data at the bottom of the app, and will update whenever app data is changed.
If a command changes the date of the Food Record (such as nom or stomach ), the graph will update to show data
for the past seven days from that date, inclusive.
|
4.10.1. Implementation
GraphPanel
in the Ui
component. It contains a LineChart
of String
date against Number
calories, and is populated with
data from an XYChart.series
. The data is in turn obtained from the Logic
component, which provides only the past seven days'
of DailyFoodLog
. The implementation of the GraphPanel
class will be further explained.
GraphPanel
class implements the following operations:
-
initialiseTreeMap
- Sets up the TreeMap that mapsLocalDate
date ofDailyFoodLog
to theDouble
total calorie consumption n that day. -
initialseGraph
- Sets up theLineChart
with xAxis aString
representing date, and yAxis aDouble
representing total calories consumed on that date. -
updateSeries
- Ensures theXYChart.series
that populates the graph with data is always updated with the most recent app data. -
makeGraph
- Wrapper function that calls the above three methods. -
getGraph
- Public accessor function to generate and retrieve theLineChart
.
Calgo will display the past seven days' graph automatically, and likewise update automatically. It does so by having the
MainWindow
class call getGraph
on startup and after execution of commands.
The sequence diagram below demonstrates how the Graph
feature works.
Sequence Diagram for Graph
feature.
Step 1: MainWindow
requests for an instance of GraphPanel
.
If no instance exists, a new GraphPanel
is created. Otherwise one is retrieved. This ensures that GraphPanel
is a singleton.
Step 2: MainWindow
calls GraphPanel
again to generate the graph and add it to the GraphPanelPlaceholder
inside MainWindow
.
Step 3: Inside GraphPanel
, a wrapper method makeGraph
calls three methods in a row:
First, initialiseTreeMap
, which has Logic
call the getPastWeekLogs
method onto GraphPanel
, generating
a TreeMap
of String
date mapped to Double
calories using the past seven days' DailyFoodLog
.
Second, initialiseGraph
method is called to generate the graph itself.
Third, updateSeries
method is called to ensure the data populating the graph is up to date.
After which, the GraphPanel
adds the graph to MainWindow
.
4.10.2. Design considerations
Aspect: Choice of visuals for past seven days summary
-
Alternative 1 (current choice): Summary is represented using a line graph.
-
Alternative 2: Summary is represented in a table.
Aspect: When graph’s dates are based on
-
Alternative 1 (current choice): Dates are based on past seven days starting from date of
Consumption Record
, inclusive. -
Alternative 2: Dates are based on past seven days starting from today’s date, inclusive.
4.10.3. Summary
In summary, this section addresses how the graph obtains information on the past seven DailyFoodLog
, and correspondingly
produces a visual graph output onto Calgo’s Main Window
GUI component viewable by the user.
The graph requires the LogicManager
class to obtain the information, and the MainWindow
class to facilitate display
to the user.
4.11. Logging
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.12, “Configuration” below) -
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 App -
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
4.12. Configuration
Certain properties of the App can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json
).
5. Documentation
Refer to the guide here.
6. Testing
Refer to the guide here.
7. Dev Ops
Refer to the guide here.
Appendix A: Product Scope
Target user profile:
-
Can type fast
-
Is reasonably comfortable using
CLI
Apps -
Wants to have, or already has, a lifestyle of eating healthy
-
Manages a significant number of
Food
items -
Prefers desktop Apps over other types of Apps (such as mobile or tablet)
-
Prefers typing over mouse input
Value proposition:
-
Insights: set goals, generate consumption reports and view progress and statistics
-
Hassle-Free Convenience: conveniently handles entry conflicts, tolerates incomplete search inputs and produces fast responses
-
Flexibility: generate Food records as a portable file, tracking wherever, whenever, without a device
-
Efficiency: manage caloric tracking faster than a typical mouse/GUI driven App
Appendix B: User Stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (possible future development) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
|
user who does not know what my food is made of |
find out the nutritional composition of a particular food by name |
locate details of the entry without having to go through the entire record. |
|
new user |
see usage instructions |
refer to instructions when I forget how to use the App. |
|
user |
have a portable, editable, and readable file to store the relevant values for each entry made |
edit, share, or print my personal entries. |
|
user who may not be able to access his laptop at some time |
have a copy of my past entries |
use it for physical reference. |
|
user who dislikes sieving through information and prefers to have only the relevant information presented |
have a way to easily find the entries I want in the record |
save time and effort and not get annoyed. |
|
lazy user who does not like typing too many tedious characters |
find entries using incomplete words or phrases |
obtain the same intended results for a search through the entries as in the case of typing fully and correctly. |
|
user who dislikes memorising things |
have an option to see the entire record |
know what entries currently exist in the records. |
|
user who has many entries |
view entire record in lexicographical order |
easily navigate to the entry I want in the record. |
|
user who is forgetful |
be able to edit the nutritional value of a previously saved entry in the record |
edit the entry if I remembered a nutrition value wrongly previously. |
|
user who is busy |
be able to create a list of goto Food with nutritional values |
quickly choose a Food Item with preset values and add it to my calorie tracker. |
|
user who doesn’t like redundant things |
override a Food entry in the Food Record which already exists |
save time and effort and not create a duplicate item in the Food Record. |
|
user who gets bored of food easily |
deletpe a Food item that I no longer want to eat in future from my Food Record |
avoid having so many Food items in the Food Record that I no longer eat. |
|
user who is a foodie |
find out the statistics of the food that I have been consuming each day |
systematically cut down on overeaten food and monitor progress. |
|
user who cannot decide on what to eat |
obtain a list of personalised food recommendations that still align with my dietary goals |
avoid wasting time deciding what to eat nor will I give in to impulse and eat junk food. |
|
user who is interested to lose weight |
find out the number of calories I have consumed each day |
check which days I have exceed my desired number of daily calorie and exercise more to compensate. |
|
user who is busy |
obtain an easy-to-understand consumption report |
quickly understand my food consumption patterns and make plans to rectify them accordingly. |
|
user who remembers the big picture but not the specifics |
search for a particular part of a guide |
not be bothered by unnecessary information. |
|
user who values visuals |
curated information expressed in a well organised graph |
intuitively understand information. |
|
user who values opinions |
have some suggestions based on my goals and consumption patterns |
know my options when I am indecisive on what to eat. |
|
user who cannot fully remember the name for a particular entry |
view all entries which have the nutritional value I happen to remember |
obtain a list of possible Food entries that are relevant. |
|
forgetful user |
be able to lookup exact command formats |
so that I won’t need to go through the trouble of memorising commands. |
As you continue developing the Calgo, feel free to add more user stories here.
Appendix C: Use Cases
For all use cases below, the System is the Calgo application and the Actor is the user, unless specified otherwise.
Also note that the term MSS refers to the Main Success Scenario for each Use Case.
Use case: obtain reference for app’s commands
MSS
-
User wants to find the command guide for the commands in Calgo.
-
User enters
help
command with no additionalcommand_word
. -
Calgo
generates a popup, displaying a list of all command guides in the popup.Use case ends.
Extensions
2a. User enters a command_word
after help
, such as foe example help nom
.
2a1. Calgo
filters out only command guides containing the command_word
"nom".
2a2. Calgo
generates a popup, displaying this filtered list of command guides.
2b. User enters a command_word
after help
that has no corresponding command guides.
2b1. Calgo
tries to filter out only command guides containing the command_word
but fails to find any guides. Thus
it defaults to display all command guides.
Use case resumes from Step 2.
Use case: find
Food
item by Name
or Tag
keyword (which can be an incomplete word)
MSS
-
User wants to find a
Food
entry by a specific keyword inName
orTag
. -
User enters
find
command with theName
Prefix
, or theTag
Prefix
, accordingly. -
Calgo shows a list of
Food
entries which contains the substring indicated in any part of theName
orTag
of theFood
entries respectively.Use case ends.
Extensions
2a. User enters invalid input for particular Prefix
2a1. A message prompting the user to enter a valid input is shown.
Use case resumes from Step 2.
3a. The FoodRecord
is empty
3a1. A message is shown indicating that there are zero matching Food
items and prompts users to make new entries.
Use case ends.
Use case: find
Food
item by nutritional value
MSS
-
User wants to
find
aFood
item by a single nutritional value of eitherCalorie
,Protein
,Carbohydrate
, orFat
. -
User enters
find
command with appropriatePrefix
. -
Calgo
shows a list ofFood
entries which has the same nutritional value.Use case ends.
Extensions
2a. User enters invalid input for particular Prefix
2a1. A message prompting the user to enter a valid input is shown.
Use case resumes from Step 2.
3a. The FoodRecord
is empty
3a1. A message is shown indicating that there are zero matching Food
items and prompts users to make new entries.
Use case ends.
Use case: export
current FoodRecord
MSS
-
User wants to
export
the currentFoodRecord
. -
User enters the
export
command intoCalgo
. -
Calgo creates a user-friendly text file
FoodRecord.txt
containing allFood
item details in thedata/exports
folder.Use case ends.
Extensions
3a. User’s system prevents the file from being created
3a1. A message is shown indicating that the file is unable to be created.
Use case ends.
Use case: list
all current Food
entries
MSS
-
User wants to
list
all currentFoodRecord
entries. -
User enters the
list
command intoCalgo
. -
Calgo
shows a list of allFood
entries in the GUI’sFood Record
.Use case ends.
Extensions
3a. The FoodRecord
is empty.
3a1. Calgo
shows a message indicating that all entries are shown, with the GUI showing an empty Food Record
. User is also prompted by this message to make new entries.
Use case ends.
Use case: update
current FoodRecord
with a new Food
item
MSS
-
User wants to add a new
Food
entry in theFoodRecord
. -
User begins to type in an
update
command withName
Prefix
. -
Calgo
shows that there are no similarFood
entries in GUIResult Display
. -
User completes typing in remaining
Prefixes
ofCalorie
,Protein
,Carbohydrate
,Fat
Prefixes
accordingly and enters it inCalgo
. -
Calgo
adds a newFood
entry intoFoodRecord
with paramaters as specified by User.Use case ends.
Extensions
3a. There are similar Food
entries in the FoodRecord
3a1. Calgo
the similar Food entries in the GUI’s Result Display
Use case resumes from Step 3.
5a. Calgo’s FoodRecord already contains the same `Food
entry
5a1. Calgo
overrides this existing Food
entry with the new Food
entry
Use case ends.
Use case: delete
an existing Food
item in current FoodRecord
MSS
-
User wants to delete a Food entry from the
FoodRecord
-
User types in an delete command with the
Name
Prefix. -
Calgo
shows that theFood
entry that the User wishes to delete exists in one of the similar Food items message in the GUI Result Display. -
User enters the command into
Calgo
-
Calgo
deletes theFood
entry from theFoodRecord
.Use case ends.
Extensions
3a. The Food entry that the User wishes to delete does not exists in the FoodRecord
.
3a1. GUI Result Display
shows that there are no similar Food
items in the FoodRecord
.
Use cases resumes from Step 3.
Use case: set a daily goal
MSS
-
User enters the
goal
command with the intended value. -
Calgo updates the user’s
goal
to the new value provided by the user.
Use case ends.
Extensions
1a. User enters an invalid input for the goal
.
1a1. Calgo shows a message indicating the acceptable range of values for the goal
command.
1a2. User enters goal
command with a new value.
Steps 1a1 and 1a2 are repeated until user enters a valid input.
Use case resumes from step 2.
Use case: generate a report
on a specific date
.
MSS
-
User enters the
report
command with a particulardate
. -
Calgo analyses the
Food
consumed on thatdate
and generates areport
text file in thedata/reports
folder for the user.
Use case ends.
Extensions
1a. There is no Food
consumed on the inputted date
.
1a1. Calgo shows a message indicating that there was no Food
consumed on the given date
.
Use case ends.
1b. Inputted date
in wrong format.
1b1. Calgo shows a message indicating the correct format for the date
.
1b2. User enters report
command with the date
in the correct format.
Use case resumes from step 2.
1c. User enters report
command without setting a daily calorie goal
.
1c1. Calgo generates a report
without the sections related to the goal
.
Use case ends.
Use case: consuming food on a on a specific day with nom
.
MSS
-
User wants to record their
Food
consumption on a particular day. -
User enters
nom
command with the appropriatePrefixes
and values. -
Calgo processes the command and update display.
Extensions
2a. User misspells the command or Prefix
2a1. A message prompting the user to enter a valid input is shown.
Use case resumes from Step 2.
2b. The Food
does not exist in FoodRecord
2b1. A message prompting the user to enter a valid input is shown.
Use case resumes from Step 2.
2c. User enters invalid value for particular Prefix
2c1. A message prompting the user to enter a valid input is shown.
Use case resumes from Step 2.
Appendix D: 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
Food
items 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.
-
Calgo should work on both 32-bit and 64-bit environments.
-
The minimum screen size for the App window to fully display its GUI is 1250 x 600.
-
Calgo should be designed for a single-user (i.e. Calgo should not be a multi-user App).
-
The product should be developed incrementally over the project duration.
-
The software’s codebase should adhere to OOP.
-
The product should have minimal network usage. Therefore, it is expected that users will find out about the respective nutritional values of a
Food
entry whenever they want toupdate
it into theFoodRecord
for the first time.
Appendix E: Glossary
- Application User Interface (API)
-
A set of tools for building software application.
- Command Line Interface (CLI)
-
Text-based user interface used to view and manage computer files.
- Food
-
Food
items entered by the user to represent a real life Food. This contains nutritional values of each of theirCalorie
s, number of grams ofProtein
s,Carbohydrate
s andFat
s. They can also contains a series ofTag
s. - Food Entry
-
An entry in the GUI’s
Food Record
box, which shows all details for oneFood
object. - FoodRecord
-
The accumulated list of all
Food
objects entered by the user. - Food Record
-
The GUI’s
Food Record
box, which shows all details for everyFood
entry. - GUI
-
The Graphical User Interface of Calgo.
- Mainstream OS
-
Windows, Linux, Unix, OS-X.
- Nutritional Information
-
Refers to
Calorie
s,Protein
s,Carbohydrate
s andFat
s. - OOP
-
Objected-Oriented Paradigm.
- Prefix
-
A set of characters placed before a parameter when entering a command.
Appendix F: Instructions for Manual Testing
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. |
F.1. Launch and Shutdown
-
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 contacts. The window size may not be optimum. -
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the App by double-clicking the jar file.
Expected: The most recent window size and location is retained.
F.2. Getting help
-
Displays a guide for all commands. Can also display only commands containing the given command word.
-
Prerequisites: Launch
Calgo
succesfully. -
Test case:
help
Expected: A help window pops up and shows you how to use each command. -
Test case:
help abcd
Expected: A help window pops up and shows you how to use each command anyway, but tells you abcd does not exist.
F.3. Creating a Food
-
Adding a
Food
item to theFoodRecord
-
Prerequisites: Launch
Calgo
successfully -
Test case
update n/Apple cal/50 p/3 c/2 f/45
-
Expected: Updated all foods into Food Record: Apple Calories: 50 Proteins (g): 3 Carbohydrates (g): 2 Fats (g): 45
-
Test case:
update x
-
Expected:
Invalid command format. update: Updates the food entered into the Food Record. If the food entered already exists, it will be overwritten by input values. Parameters: n/NAME cal/CALORIES p/PROTEIN c/CARBOHYDRATE f/FAT [t/TAG]… Example: update n/Kiwi cal/150 p/2 c/25 f/3 t/Green t/Sweet -
Other incorrect commands to try:
update n/Apple c/2 f/45
(where parameters are missing)
Expected: Similar to previous.
F.4. Editing a Food
-
Editing a
Food
item in theFoodRecord
-
Prerequisites: Launch
Calgo
successfully andFood
already exists inFoodRecord
. -
Test case
update n/Apple cal/53 p/3 c/3 f/45
-
Expected:
Updated all foods into Food Record: Updated existing food item in Food Record: Apple Calories: 50 Proteins (g): 3 Carbohydrates (g): 3 Fats (g): 45 -
Test case:
update x
-
Expected: Invalid command format. update: Updates the food entered into the Food Record. If the food entered already exists, it will be overwritten by input values. Parameters: n/NAME cal/CALORIES p/PROTEIN c/CARBOHYDRATE f/FAT [t/TAG]… Example: update n/Kiwi cal/150 p/2 c/25 f/3 t/Green t/Sweet
-
Other incorrect commands to try:
update n/Apple c/2 f/45
(where parameters are missing)
Expected: Similar to previous.
F.5. Deleting a Food
-
Deleting a
Food
item from theFoodRecord
-
Prerequisites: Launch Calgo successfully and a
Food
item Apple already exists inFoodRecord
-
Test case:
delete n\Apple
-
-
Test case:
delete 0
Expected: No food is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete
,delete n/Banana
(whereFood
banana does not exists inFoodRecord
)
Expected: Similar to previous.
F.6. Listing all Food
entries
-
Listing down all entries, regardless of previous commands
-
Prerequisites: Launch
Calgo
successfully. -
Test case:
list
Expected: The GUI will show allFood
entries existing in theFoodRecord
.
F.7. Navigating Food record
-
Searches through the Calgo’s Food entries and displays relevant ones based on the specifications entered.
-
Prerequisite: Launch
Calgo
succesfully, andFood
being searched exists inFood Record
-
Test case:
find n/Apple
Expected:Food
having name that partially match "Apple" will be displayed -
Test case:
find t/sWeet
having tag that partially match "sweet" will be displayed
Expected: `Food -
Test case:
find t/swEeT n/Apple
Expected: Please specify 1 and only 1 correct parameter for filtering using the find command. -
Other incorrect commands to try:
find n/
Expected: Names should only contain alphanumeric characters and spaces, and it should not be blank.
F.8. Adding Food
to consumption record
-
Adds a Food to a specific day’s Consumption Record.
-
Prerequisite: Launch
Calgo
succesfully, andFood
being consumed exists inFood Record
-
Test case:
nom n/chicken d/2020-03-04 portion/1.5 r/8
Expected: Successfully consumed Chicken Calories: 32 Proteins (g): 20 Carbohydrates (g): 1 Fats (g): 11 -
Test case:
nom n/chickn d/2020-03-04 portion/1.5 r/8
Expected: You can’t eat that because it does not exist in food record. -
Other incorrect commands to try:
nom n/chicken d/2020-03-04 portion/-1 r/8
Expected: Portion should be a positive number.
F.9. Removing a portion of Food
from consumption record
-
Deletes a portion of a specific Food from the Consumption Record.
-
Prerequisite: Launch
Calgo
succesfully, andFood
being consumed exists inConsumption Record
-
Test case:
vomit num/1 d/2020-03-04
Expected: Successfully throw up Chicken Calories: 32 Proteins (g): 20 Carbohydrates (g): 1 Fats (g): 11 -
Test case:
vomit num/
Expected: Position should be a positive integer! -
Other incorrect commands to try:
vomit num/-1
Expected: Position required an integer within range of list!
F.10. Navigating Consumption Record
-
Deletes a portion of a specific Food from the Consumption Record.
-
Prerequisite: Launch
Calgo
succesfully, and have eaten something on the day you want to browse. -
Test case:
stomach d/
Expected: Display all food consumed. (As long as you ate on that day before, applies even if your record is empty due to using vomit) -
Test case:
stomach d/1930-04-01
Expected: Your consumption record is empty because you have not consumed food on 1930-04-01 before
F.11. Setting a goal
-
Sets your daily calorie goal.
-
Prerequisite: Launch
Calgo
succesfully. -
Test case:
goal 69
Expected: That is a really low goal to set. Warning: You may suffer from malnutrition. We’ll accept this now because Calgo will eventually help you to reach a daily calorie count of 1200, which is the minimum calories you should eat to stay moderately healthy. -
Test case:
goal 0
Expected: Please key in a whole number that is at least 1 calorie and at most 99999 calories.
F.12. Generating report for the day
-
Generates consumption report for a given date.
-
Prerequisite: Launch
Calgo
succesfully, and have eaten something on the day you want to generate report on. -
Test case:
report d/
Expected: Successfully generated a report in the data/reports folder for the following date: 2020-04-13. -
Test case:
report d/2070-04-12
Expected: Did not manage to generate report. There was no food consumed on 2070-04-12.
F.13. Generating a copy of your Food Record
-
Generates a neat and editable file containing the current Food entries.
-
Prerequisite: Launch
Calgo
succesfully -
Test case:
export
Expected: Successfully generated FoodRecord.txt in the data/exports folder.
F.14. Clearing Food Record
data
-
Clears all food entries from Calgo. Note that data in Consumption Record is not deleted.
-
Test case:
clear
Expected: Food Record has been cleared! Use the update command to add new food into your Food Record.
F.15. Saving data
-
Dealing with missing/corrupted data files.
-
Calgo will start from a fresh state if your files are corrupted.
-
Warning: You can edit the
.json
files in the/data
folder. Be careful, if the files you edit ends up with invalid format, you risk losing all existing data.
Appendix G: Effort
G.1. Challenges and Difficulties
At the start, all of us were very new to software engineering projects. Hence, the learning curve was very steep. Because of this, most of the time, we were very confused. However, we demonstrated good teamwork because we always met often and helped each other out by explaining frameworks and teaching each other on software development tools like Git, Intellij and JavaFX.
Due to the COVID-19 situation, there was a lot of uncertainty and our style of meetings were significantly affected. However, everyone demonstrated good attitude and the team was full of good sports, so we covered each other’s weaknesses and supported one another, therefore being able to realise a strong team potential.
G.2. Effort put in by the team
-
3-4 meetings weekly on average
-
Many unrecorded hours were put in for self-learning and managing the project.
-
We placed a lot of emphasis on brainstorming our features and implementing them to make it user-centric. For e.g. a real-time suggestion feature
G.3. Achievements
-
Product Design
-
Our team successfully morphed AB3 and its relevant tests into the Calgo you see today.
-
Our team’s project idea was validated and appreciated by peers and tutors, most notably from our CS2101 presentation, CS2103T demo and PE-Dry Run testing. It also has potential to be collaborated with other peer projects such as FitBiz (Group F11-2).
-
-
Implementation
-
Ambitious in experimenting with new interesting features. For e.g. intelligent insights and graphs.
-
Implemented features consistently and incrementally, allowing us to make changes to past features and
-
Experimented with new workflow before deciding on one, Agile(Scrum), which we liked the most.
-
Put in additional effort in making the GUI different and novel, even though it is not part of the grading rubric. This is another example of how we go the extra mile to make our product more user-centric. For e.g. we tried out new JavaFX APIs like LineChart and TableView.
-
-
Project Management
-
Predominantly followed the forking workflow.
-
Diligently created issues and assigned them on GitHub, while also consistently communicating with each other on Telegram.
-
Planned and incrementally implemented our user stories throughout different milestones.
-
Regularly reviewed each other’s code on and off Github.
-