By: Team T12-3
Since: Aug 2018
Licence: MIT
- 1. Introduction
- 2. Setting up
- 3. Design
- 4. Implementation
- 4.1. Add Recurring Tasks
- 4.2. Undo/Redo Commands
- 4.3. Log Messages
- 4.4. Configuration
- 4.5. View Tasks Due Today/This Week
- 4.6. Archive Task
- 4.7. Automatic Partial Deletion of Archived Tasks
- 4.8. List Tasks by Month
- 4.9. Generate Academic Calendar Weeks
- 4.10. Filter by Tags (
filter
andfilterstrict
) - 4.11. List Overdue Tasks
- 4.12. List Archived Tasks
- 4.13. Sort tasks
- 4.14. Managing Tags Using Category
- 4.15. Display Tags in Sidebar
- 4.16. Display Progress Bars
- 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
1. Introduction
This Developer Guide for the Student Schedule Planner is written by the SSP team for the benefits of future developers and maintainers of the application.
Student Schedule Planner is a desktop application aims to help students save their tasks manage them all in one place .
This introduction briefly covers the main flow of the Developer Guide:
1. Setting up : Walkthrough on the prequisites steps to setup the Developer Environment
2. Design : Overview on the structure of the project code, and how each component interacts with each other.
3. Implementation : In-depth explanation on the implementation of the various features in the Schedule Planner, as well as the considerations behind the implementations
4. Documentation : General guide on the documentation process of the project
5. Testing : Documentation for the coverage tests of the application and how to set up your testing environment.
6. DevOps : General processes used during development phase, and documentation on how to set it up.
2. Setting up
Before you start working on the project, please make sure that you have completed the necessary prerequisites.
2.1. Prerequisites
-
JDK
9
or laterJDK 10
on Windows will fail to run tests in headless mode due to a https://github .com/javafxports/openjdk-jfx/issues/66[JavaFX bug]. Windows developers are highly recommended to use JDK9
. -
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.
2.2. Setting up the project in your computer
-
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 -
Open a console and run the command
gradlew processResources
(Mac/Linux:./gradlew processResources
). It should finish with theBUILD SUCCESSFUL
message.
This will generate all resources required by the application and tests. -
Open
XmlAdaptedTask.java
andMainWindow.java
and check for any code errors-
Due to an ongoing issue with some of the newer versions of IntelliJ, code errors may be detected even if the project can be built and run successfully
-
To resolve this, place your cursor over any of the code section highlighted in red. Press ALT+ENTER, and select
Add '--add-modules=…' to module compiler options
for each error
-
-
Repeat this for the test folder as well (e.g. check
XmlUtilTest.java
andHelpWindowTest.java
for code errors, and if so, resolve it the same way)
2.3. Verifying the setup
-
Run
MainApp
and try a few commands -
Run the tests to ensure they all pass.
2.4. Configurations to do before writing code
2.4.1. Configuring the coding style
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.
2.4.2. Updating documentation to match your fork
After forking the repo, the documentation will still have the SE-EDU branding and refer to the ssp/scheduleplanner
repo.
If you plan to develop this fork as a separate product (i.e. instead of contributing to ssp/scheduleplanner
), 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.
2.4.3. Setting up CI
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 and AppVeyor is Windows-based) |
2.4.4. Getting started with coding
When you are ready to start coding,
-
Get some sense of the overall design by reading Section 3.1, “Architecture”.
-
Take a look at Appendix A, Product Scope.
3. Design
This section covers the structural design of the App, as well as how its various components interact with each other.
3.1. Architecture
The Architecture Diagram given below explains the structural design of the App. It is followed by is a quick overview of each component.
The .pptx files used to create diagrams in this document can be found in the diagrams folder. To update a diagram, modify the diagram in the pptx file, select the objects of the diagram, and choose Save as picture .
|
Main
has only one class called MainApp
. It is responsible for:
-
At app launch: Initializing the components in the correct sequence, and connecting them up with each other.
-
At shut down: Shutting down the components and invoking cleanup method where necessary.
Commons
represents a collection of classes used by multiple other components. Two of those classes play important roles at the architecture level. They are:
-
EventsCenter
: This class (written using Google’s Event Bus library) is used by components to communicate with other components using events (i.e. a form of Event Driven design) -
LogsCenter
: This class is 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, Logic
(see the class diagram given below) defines its API in Logic.java
interface
and exposes its functionality using LogicManager.java
class.
Events-Driven nature of the design
The Sequence Diagram below shows how the components interact for the scenario where the user issues the command
delete 1
.
delete 1
command (part 1)
Model simply raises a SchedulePlannerChangedEvent when the schedule planner data is changed, instead of
asking Storage to save updates to the hard disk.
|
The diagram below shows how EventsCenter
reacts to the event, after which the updates are saved
to the hard disk, and the status bar of the UI
is updated to reflect 'Last Updated' time.
delete 1
command (part 2)
The event is propagated through EventsCenter to Storage and UI , without Model having to be coupled to either of
them. This is an example of how Event Driven approach helps us reduce direct coupling between components.
|
The sections below give more details of each component.
3.2. UI component
API : Ui.java
The UI consists of MainWindow
, which is made up of CommandBox
, ResultDisplay
, TaskListPanel
,
StatusBarFooter
, SidebarPanel
etc.
All these, including MainWindow
, inherit from the abstract UiPart
class.
UI
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
UI
component does the following:
-
Executes user commands using
Logic
component. -
Binds itself to some data in
Model
so that the UI can auto-update when data inModel
changes. -
Responds to events raised from various parts of the App and updates the UI accordingly.
3.3. Logic component
API :
Logic.java
Below are the steps of how Logic
executes an user command:
-
Logic
usesSchedulePlannerParser
to parse the user command. -
This creates a
Command
object which is executed byLogicManager
. -
The command execution can affect
Model
(e.g. adding a task) and/or raise events. -
The result is encapsulated as a
CommandResult
object which is passed back toUi
.
Given below is the Sequence Diagram for interactions within Logic
for the execute("delete 1")
API call.
delete 1
Command within Logic Component3.4. Model component
API : Model.java
The Model
component does the following:
-
Stores a
UserPref
object that represents the user’s preferences. -
Stores the Schedule Planner data.
-
Exposes an unmodifiable, observable
ObservableList<Task>
e.g. UI can be bound to this list, such that it automatically updates when data in the list changes. -
Does not depend on any of the other three components.
As a more OOP model, we can store a Tag list in Schedule Planner , which Task can reference. This would allow Schedule Planner to only require one Tag object per unique Tag , instead of each Task needing their own Tag object. An example of how such a model may look like is given below. |
3.5. Storage component
API : Storage.java
The Storage
component does the following:
-
Saves
UserPref
objects in json format and reads it back. -
Saves the Schedule Planner data in xml format and reads it back.
3.6. Common classes
Classes used by multiple components are in the ssp.scheduleplanner.commons
package.
4. Implementation
This section elaborates on how certain features are implemented.
4.1. Add Recurring Tasks
The Add Repeat feature allows the user to add recurring tasks of a specified number of repeats and a specified interval between each repeat.
4.1.1. Current Implementation
The following code snippet shows how the command is implemented:
... // Loop through to add the rest of the tasks. for (int i = 1; i < Integer.parseInt(repeat.value); i++) { baseDate.add(Calendar.DAY_OF_YEAR, interval); newDate = schedulerFormat.format(baseDate.getTime()); date = new Date(newDate); newTask = new Task(toAdd.getName(), date, toAdd.getPriority(), toAdd.getVenue(), toAdd.getTags()); // Add the task only if there is no duplicate task within the model. if (!model.hasTask(newTask)) { model.addTask(newTask); } } ...
This loop occurs after the first task is added. The tasks are created within the loop with duplicate names, priorities, venues and tags. Before adding a task, the function checks with the model if such a task already exists. If not, the task is added. If the task is a duplicate task, it is skipped and the loop proceeds until it terminates.
The following outlines an example of the command’s usage.
Step 1. The user enters the command repeat n/CS2103T Tutorial i/7 r/3 p/3 t/CS2103T t/Tutorial v/COM1
Step 2. The AddRepeatParser parses the command and isolates each of the arguments.
Step 3. The Model checks if the first task exists. If the first task does not exist in Model, it is added. If not, it is skipped.
Step 4. The execute command loops through to build each repeated task. In each loop, the date is incremented by the specified interval. Same as Step 3, a duplicate task is skipped and a task that does not exist in Model is added.
Step 5. The changes are committed and the new list is shown to the user.
The following sequence diagram shows how the add repeat operation works:
4.2. Undo/Redo Commands
The Undo/Redo feature allows the user to revert the schedule planner to an earlier state when 'undo' is called, or a later state when 'redo' is called.
4.2.1. Current Implementation
The undo/redo mechanism is facilitated by VersionedSchedulePlanner
.
It extends SchedulePlanner
with an undo/redo history, stored internally as an SchedulePlannerStateList
and currentStatePointer
.
Additionally, it implements the following operations:
-
VersionedSchedulePlanner.commit()
— Saves the current schedule planner state in 'history'. -
VersionedSchedulePlanner.undo()
— Restores the previous schedule planner state from 'history'. -
VersionedSchedulePlanner.redo()
— Restores a previously undone schedule planner state from 'history'.
These operations are exposed in Model
interface as Model#commitSchedulePlanner()
, Model#undoSchedulePlanner()
and Model#redoSchedulePlanner()
respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step:
Step 1. The user launches the application for the first time. VersionedSchedulePlanner
will be initialized with the
initial schedule planner state, with currentStatePointer
pointing to that single schedule planner state.
Step 2. The user executes delete 5
command. The delete
command calls Model#commitSchedulePlanner()
, as a result
the modified state of the schedule planner is saved in schedulePlannerStateList
after the delete 5
command
executes, the currentStatePointer
shifts to the newly inserted schedule planner state.
Step 3. The user executes add n/CS2100 Lect …
to add a new task. The add
command calls
Model#commitSchedulePlanner()
`, and the modified schedule planner state is saved into `schedulePlannerStateList
.
If a command execution fails, it will not call Model#commitSchedulePlanner() , so the schedule planner state will not
be saved into schedulePlannerStateList .
|
Step 4. The user now decides to undo that action by executing the undo
command. The undo
command will call
Model#undoSchedulePlanner()
, which shifts the currentStatePointer
to the previous index, pointing to the
previous schedule planner state, and restoring the schedule planner to its previous state.
If the currentStatePointer is at index 0(i.e the initial schedule planner state), then there are no previous states to restore to. The undo command uses Model#canUndoSchedulePlanner() to check this case.
If so, it will return an error to the user rather than attempting to perform the undo.
|
The following sequence diagram shows how the undo operation works:
The redo
command does the opposite — it calls Model#redoSchedulePlanner()
, which shifts the
currentStatePointer
to the next index, pointing to the next state, and restores the schedule planner to
that state.
If currentStatePointer is at index schedulePlannerStateList.size() - 1 (i.e pointing to the latest state), then there are no latest states to restore. The redo command uses Model#canRedoSchedulePlanner() to check this case. If so, it will return an error to the user rather than attempting to perform the redo.
|
Step 5. The user then executes the command list
. Commands that do not modify the schedule planner, such as list
,
will usually not call Model#commitSchedulePlanner()
, Model#undoSchedulePlanner()
or Model#redoSchedulePlanner()
. Thus, the schedulePlannerStateList
remains unchanged.
Step 6. The user executes clear
, which calls Model#commitSchedulePlanner()
. If the currentStatePointer
is not
pointing to the latest state in the schedulePlannerStateList
, all states after the
currentStatePointer
will be purged. We designed it this way because it no longer makes sense to redo the add n/David …
command. This is the behavior that most modern desktop applications follow.
The following activity diagram summarizes what happens when a user executes a new command:
4.2.2. Design Considerations
Aspect: How undo & redo executes
-
Alternative 1 (current choice): The entire schedule planner is saved.
-
Pros: It is easy to implement.
-
Cons: It may have performance issues in terms of memory usage.
-
-
Alternative 2: Each individual command knows how to undo/redo by itself.
-
Pros: It will use less memory (e.g. for
delete
, just save the task being deleted). -
Cons: We must ensure that the implementation of each individual command is correct.
-
Aspect: Data structure to support the undo/redo commands
-
Alternative 1 (current choice): A list is used to store the history of schedule planner states.
-
Pros: It is easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.
-
Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both
HistoryManager
andVersionedSchedulePlanner
.
-
-
Alternative 2:
HistoryManager
is used for undo/redo.-
Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase.
-
Cons: It requires dealing with commands that have already been undone: We must remember to skip these commands. It violates Single Responsibility Principle and Separation of Concerns as
HistoryManager
now needs to do two different things.
-
4.3. Log Messages
We are using java.util.logging
package for logging. LogsCenter
class is used to manage logging levels and
destinations.
How to log:
-
The logging level can be controlled using
logLevel
setting in the configuration file (See Section 4.4, “Configuration”) -
The
Logger
for a class can be obtained usingLogsCenter.getLogger(Class)
which will log messages according to the specified logging level -
The log messages are output through:
Console
and to a.log
file.
Logging Levels
The following are logging levels used:
-
SEVERE
: This is for critical problems detected which may possibly cause the termination of the application -
WARNING
: This is to warn that application can continue running, but with caution -
INFO
: This is for information showing the noteworthy actions by the App -
FINE
: This is for details that are not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
4.4. Configuration
Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file (default: config.json
).
4.5. View Tasks Due Today/This Week
In order to enable users to better monitor their tasks in the schedule planner, Two variations of
list
command were added.
listday
command supports viewing tasks due on the current date, whereas listweek
command supports viewing tasks
from the current date till the closest Sunday.
4.5.1. Current Implementation
listday
/listweek
utilises the same implementation used by list
command:
-
Model#updateFilteredTaskList()
— Takes in a predicate parameter and updates the model according to the predicate.
listday
further implements the following operation:
-
DateSamePredicate
— takes in asystemDate
parameter.systemDate
is aString
value after converting current system date intoDDMMYY
format.
listweek
further implements the following operations:
-
numDaysTillSunday(dateName)
— It compute the number of days from current date until closest Sunday (exclusive of Sunday) usingdateName
.dateName
is the name of the current day. -
appendDateList(dateList, numDaysTillSunday(dateName))
— It generates sequentialDDMMYY
values based on the result ofnumDaysTillSunday(dateName)
and inserts them intodateList
.dateList
is aList<String>
object. -
DateWeekSamePredicate
— It takes in adateList
parameter.dateList
is aList<String>
object that contains the list of dates from current date to closest Sunday date inDDMMYY
format.
As both listday
/listweek
commands are similar, we will only illustrate how listweek
works.
Given below is an example usage scenario and how listweek
mechanism behaves at each steps:
Step 1. The user entered the command listweek
.
Step 2. The command word listweek
invoke LogicManger
to invoke SchedulePlannerParser
to return a
ListWeekCommand
object. LogicManager
then invoke ListWeekCommand#execute()
.
Step 3. ListWeekCommand#appendDateList(datelist, numDaysTillSunday(dateName))
will be activated. It helps to generates
values for dateList
and the values are sequential dates in DDMMYY
format after using the result from
numDaysTillSunday()
method. numDaysTillSunday()
will compute the number of days from current date till Sunday based on dateName
,
the name of the current day.
Step 4. model.updateFilteredTaskList()
will update the task list with DateWeekSamePredicate
as the parameter. DateWeekSamePredicate
itself would take dateList
in Step 3 as the parameter.
Step 5. The updated task list would be reflected on UI
to be displayed to the user.
The following sequence diagram illustrates how the mechanism works:
4.5.2. Design Considerations
Aspect: How does listday and listweek function
-
Alternative 1 (current choice): Filter accordingly based on the command itself
-
Pros: It is easy to implement and use.
-
Cons: It restrict to view all tasks from current date or from current date till Sunday only.
-
-
Alternative 2: Allow the commands to receive argument for which date(s) to filter. E.g listday 130818 to view all tasks for 13 August 2018.
-
Pros: It allows viewing for different date(s).
-
Cons: It would cause inconvenience for entering the date(s) each time the command was to be used.
-
4.6. Archive Task
The Archive Task feature allows user to archive completed tasks. The archived tasks are hidden from task list,
can only be viewed through command listarchived
. For implementation details
regarding listing archived tasks, please refer to Section 4.12, “List Archived Tasks”.
4.6.1. Current Implementation
The archiveTask mechanism is facilitated by SchedulePlanner.
Schedule Planner has two lists of tasks,
one is taskList for normal tasks, another is archivedTaskList for archived tasks.
Normal task list is implemented using UniqueTaskList that does not allow duplicates,
while archived task list is implemented using TaskList that allows duplicates.
Task list that is used to store normal tasks is implemented this way:
public class UniqueTaskList implements Iterable<Task> { private boolean tasksAreUnique(List<Task> tasks) { //... Check for duplicates logic... } //... }
While task list that is used to store archived tasks does not check for duplicates:
public class TaskList implements Iterable<Task> { //... }
Given below is an example usage scenario and how the archiveTask mechanism behaves. We first
assume that user executes command archive 1
:
-
The archiveCommand is created, 1 is recognised as
targetIndex
. -
ArchiveCommand.execute()
callsmodel.archiveTask()
and parse intargetIndex
as parameter, which is1
. -
archiveTask()
method in schedulePlanner class is called, which deletes the first task shown in current task list from task list and stores it into archived task list.
The following sequence diagram illustrates how the mechanism works:
4.6.2. Design Considerations
Aspect: Storage of archived tasks
-
Alternative 1 (current choice): Save archived tasks in a separate list. Implement two task lists in Schedule Planner, one for normal tasks, another for archived tasks. For archived tasks, instead of using UniqueTaskList, implement a new class that allow duplicates.
-
Pros: When listing normal tasks, the program does not have to filter out archived tasks every time; which makes the algorithm more efficient. Besides, users is able to add in new task with same attributes as an archived task, which makes the design logic more reasonable for user and more user-friendly as well.
-
Cons: Listing archived tasks will need a new approach, different from other list commands; new class has to be implemented to store archived tasks.
-
-
Alternative 2: Save archived tasks together with normal tasks. Schedule Planner only has one task list. Add boolean filed isArchived to Task class to differentiate archived and not archived tasks.
-
Pros: It is easy to implement.
-
Cons: Each time list command is called, archived tasks have to be filtered out, which may affect performance. Besides, user cannot add in new task with same attributes as archived tasks, which is much less user-friendly.
-
4.7. Automatic Partial Deletion of Archived Tasks
The automatic deletion feature removes archived tasks with deadlines date earlier than 2 weeks ago from current time
point from archived task list. There are two main reasons behind this feature.
Firstly, we want to optimize the user experience of viewing archived tasks. Users should be able to view archived tasks,
but displaying all archived tasks would make the archived task list too bulky and hard to view. In other words, we want to stop the archived task
list from growing to infinity.
Another reason is we want to save storage space used by our application as much as possible.
4.7.1. Current Implementation
Every time when the application is launched, archived list is checked through. Tasks with deadline date earlier than 2 weeks ago are then permanently deleted from archived task list.
Given below is detailed explanation of how auto-deletion mechanism behaves:
-
Application is launched
-
MainApp#init()
is called, which instantiatesModel
class and callsModel#autoDeleteArchived()
. -
Model#autoDeleteArchived()
then callsTaskList#autoDelete()
. Note thatTaskList
is an object type implemented to store archived tasks.TaskList#autoDelete()
then scan through archived tasks and deletes those archived tasks with deadline date earlier than 2 weeks before current time point.
4.7.2. Design Considerations
Aspect: What tasks should we delete?
We had a rough idea of deleting away "old" tasks. However, how should we define "old"?
-
Alternative 1 (current choice): Delete tasks with deadline date earlier than 2 weeks before current date.
-
Pros: This implementation is easier for testing. Testers do not have to alternate their system time to test the feature.
-
Cons: The logic may be less user-friendly comparing to Alternative 2. A user’s expectation is more likely to be "I can check tasks that were archived recently". For example, if user has just archived a task that has been overdue for 1 month (the deadline date of this task is 1 month earlier than current date), he/she may find it not so intuitive if next time he/she runs the app, the task cannot be found in archived task list.
-
-
Alternative 2 : Delete tasks that were archived 2 weeks before current date.
-
Pros: This implementation may be more user-friendly comparing with Alternative 1.
-
Cons: The feature would be hard to test for testers.
-
4.8. List Tasks by Month
The listmonth
command is another variation of the list
command, which supports viewing tasks due from the current
date till the end of the month.
4.8.1. Current Implementation
listmonth
utilises the same implementation used by the list
command:
-
Model#updateFilteredTaskList()
— Takes in a predicate parameter and updates the model according to the predicate.
listmonth
also utilises the same implementation used by the listweek
command:
-
DateWeekSamePredicate
— It takes in adateList
parameter.dateList
is aList<String>
object that contains a list of dates from current date to the end of the month inDDMMYY
format.
listmonth
further implements the following operations:
-
numDaysTillEndOfMonth(currentDay) — It computes the number of days from current date until the end of the month using
currentDay
.currentDay
is the current date inLocalDate
format. -
appendDateList(dateList, numDaysTillEndOfMonth(currentDay)) — It generates sequential
DDMMYY
values based on the result ofnumDaysTillEndOfMonth(currentDay)
and adds them into 'dateList` .dateList
is aList<String>
object.
Given below is an example usage scenario and how listmonth
mechanism behaves at each steps:
Step 1. The user enters the command listmonth
.
Step 2. The command word listmonth
invokes LogicManger
, which invokes SchedulePlannerParser
to return a
ListMonthCommand
object. LogicManager
then invokes ListMonthCommand#execute()
.
Step 3. ListMonthCommand#appendDateList(datelist, numDaysTillEndOfMonth(currentDay))
will be invoked. It
generates sequential dates in DDMMYY
format which are added into dateList
. numDaysTillEndOfMonth(currentDay)
computes the number of days from the current date till the end of the month based on currentDay
. currentDay
is the
current date in LocalDate
format.
Step 4. model.updateFilteredTaskList()
updates the task list with DateWeekSamePredicate
as the predicate.
DateWeekSamePredicate
takes dateList
in Step 3 as its parameter.
Step 5. The updated task list would be reflected on UI
to be displayed to the user.
The following sequence diagram illustrates how the mechanism works:
4.8.2. Design Considerations
Aspect: How does listmonth function
-
Alternative 1 (current choice): Fixed date interval based on commands
-
Pros: It is easier to implement, and faster for the user.
-
Cons: It may not be as flexible, as it restricts view to tasks from current date till a fixed date in the future (last day of the month)
-
-
Alternative 2: Allow
list
commands to receive arguments for start/end dates E.g list 130818 200918 to view all tasks between the two dates.-
Pros: It allows flexible viewing for different ranges.
-
Cons: It would be inconvenient to enter two date(s) each time the command is used.
-
4.9. Generate Academic Calendar Weeks
In order to enable users to better monitor their tasks in the schedule planner by knowing what is the current
academic week, firstday
command was added.
firstday
command will generate the weeks referencing to NUS academic calendar, and will append the week
description to the application title if the user launches the application within the academic calendar dates.
This allows the user to know the current academic week.
Only 17 weeks are referenced from NUS academic calendar. More details at glossary. |
4.9.1. Current Implementation
firstday
mechanism is facilitated by FirstDayCommand
and implements the following operations:
-
FirstDayCommand#computeRangeOfWeek(firstDay)
— It generate the academic calendar weeks data based onfirstDay
parameter. -
FirstDayCommand#addDescriptionForWeeks
— It appends description for each academic calendar weeks. -
FirstDayCommand#saveRangeOfWeeks(rangeOfWeek)
— It save the academic calendar weeks data intorangeofweek.xml
-
FirstDayCommand#createDefaultFileIfNotExist()
— It creates the defaultrangeofweek.xml
if it does not exist. -
FirstDayCommand#createDefaultFileIfUnableConvert()
— It creates the defaultrangeofweek.xml
if data is unable to convert to be used. -
FirstDayCommand#createDefaultFileIfSizeDiff()
— It creates the defaultrangeofweek.xml
if the number of entries differ from the expected academic number of weeks. -
FirstDayCommand#createDefaultFileIfNull()
— It creates the defaultrangeofweek.xml
if any data is null. -
FirstDayCommand#createDefaultFileIfInvalidDateOrRange()
— It creates the defaultrangeofweek.xml
if date data in modified to be an invalid date or date range format. -
FirstDayCommand#computeAppTitle()
— It computes the corresponding application title after checking if current system date is within academic calendar dates. -
FirstDayCommand#retrieveRangeOfWeeks(storeRangeOfWeeks)
— It retrieves the saved academic calendar weeks data fromrangeofweek.xml
-
FirstDayCommand#isWithinDateRange(firstDayOfSem, lastDayOfSem)
— Check if current system date is withinfirstDayOfSem
andlastDayOfSem
and return true or false. -
FirstDayCommand#retrieveWeekDescription(rangeOfWeek)
— It return the description of a particular week -
FirstDayCommand#isMonday(inputDate)
— It checks ifinputDate
is Monday and return true or false.
Given below is an example usage scenario and how firstday
mechanism behaves at each steps:
Step 1. The user enter the command firstday 130818
Step 2. The command word firstday
invoke LogicManager
to invoke SchedulePlannerParser
. SchedulePlannerParser
then invoke FirstDayCommandParser#parse(130818)
which will then trim the argument 130818
into trimmedArgs
.
Step 3. Methods onlyOneSetArgument(trimmedArgs)
, Date#isValidDate(trimmedArgs)
and isMonday(trimmedArgs)
are used
in sequential order to check if trimmedArgs
is valid.
If either method in Step 3 failed, ParseException with respective message will be thrown to inform user what had
gone wrong. FirstDayCommand would then not be called.
|
Step 4. FirstDayCommandParser
return a FirstDayCommand
with the validated trimmedArgs
as its
parameter.
Step 5. LogicManager
then invoke FirstDayCommand#execute()
.
Step 6. FirstDayCommand#computeRangeOfWeeks(trimmedArgs)
will be activated and generate the academic calendar weeks
. This method will further call FirstDayCommand#addDescriptionForWeeks
to add description for each of the academic
calendar weeks. The academic calendar weeks will be stored in a 2D String array
named rangeOfWeek
.
Step 7. FirstDayCommand#saveRangeOfWeeks(rangeOfWeek)
will be activated. It will create a
XmlSerializableRangeOfWeek
object with rangeOfWeek
as its parameter to allow rangeOfWeek
data to be
converted into Xml
format to be easily saved. Next, this method would call XmlFileStorage#saveWeekDataToFile
to
save XmlSerializableRangeOfWeek
object data into Xml
format in rangeofweek.xml
CommandException will be thrown if rangeofweek.xml does not exist.
|
Step 8. After the data had been saved properly, should the current system date lies within the academic calendar
weeks, UI
would display the corresponding week description to the user.
Step 9. When user launch the application,MainApp
will create a FirstDayCommand
object named fdc
to utilise the
methods fdc#createDefaultFileIfNotExist
, fdc#createDefaultFileIfUnableConvert
, fdc#createDefaultFileIfDiffSize
,
fdc#createDefaultFileIfNull
and fdc#createDefaultFileIfInvalidDateOrRange
.
If it is the first time the user launches the application or if user deleted rangeofweek.xml
or invalidated data in
rangeofweek.xml
, the application will record the log message and create a default rangeofweek.xml
.
The following code snippet shows that with the extra layer of data verification and rectification, the user do not need to worry when they accidentally invalidated the storage file.
Step 10. MainApp
will create a Config
object named as updateConfig
and then calls the method
updateConfig.setAppTitle(fdc.computeAppTitle())
. fdc.computeAppTitle()
would create a 2D String Array
called
retrieveData
for storing academic semester dates to operate FirstDayCommand#retrieveRangeOfWeeks(retrieveData)
to
retrieve saved data.
It then check if system date is within the retrieveData
by using FirstDayCommand#isWithinDateRange(x,y)
where x
and y
stands for the first academic and last academic day respectively. If it is within, it then generate the
corresponding application title by using FirstDayCommand#retrieveWeekDescription(retrieveData)
. Else it uses the
default application title. It would then return the result into updateConfig.setAppTitle()
to update the
application title.
Step 11. MainApp
then calls ConfigUtil#saveConfig(updateConfig, configFilePathUsed)
to save the updated
configuration into the path configFilePathUsed
where config.json
is.
Step 12. MainApp
would then retrieve the application title from config.json
and display on UI
.
CommandException will be thrown if data from rangeofweek.xml could not be converted or if rangeofweek.xml does
not exist.
|
The following sequence diagrams illustrates how the mechanism works:
4.9.2. Design Considerations
Aspect: How firstday command functions
-
Alternative 1 (current choice): Generate entire academic calendar weeks by input the first academic Monday date.
-
Pros: It would only need one set of date.
-
Cons: It would require many methods to validate, generate the data.
-
-
Alternative 2: Allow user to create their own academic calendar such as having customised number of weeks and description for each weeks.
-
Pros: It would allow customisation.
-
Cons: It would be time and effort consuming for the user and also to validate the data.
-
Aspect: Data structure to support storing of academic calendar weeks data
-
Alternative 1 (current choice):
2D String array
is used.-
Pros: It would allow easy data retrieval for specific
index
. -
Cons: It could cause confusion especially if magic numbers were used instead of constant.
-
-
Alternative 2:
ArrayList
is used.-
Pros: It would not require sequential memory for storage.
-
Cons: It would not allow data retrieval at any specific
index
which requires traversing.
-
4.10. Filter by Tags (filter
and filterstrict
)
The filter
& filterstrict
commands allow the user to filter tasks in the schedule planner according to their tags.
The user may search for multiple tags at once, and the schedule planner returns a list of tasks containing the tags
specified by the user.
The filter
command searches for tasks inclusively, which means that when multiple tags are input, SSP filters tasks
containing ANY of the user-input tags (e.g 'A', or 'B', or both 'A' & 'B').
In contrast, filterstrict
,filters tasks containing ALL of the user-input tags. (e.g ONLY 'A' & 'B')
4.10.1. Current Implementation
Since filter
and filterstrict
are implemented in similar fashion, we will simply refer to filter
.
The filter mechanism utilises FilterCommandParser
to parse the user command into separate tags by invoking the method
FilterCommandParser.parse(args)
, where args
are the tags to be filtered. e.g tag1 tag2
will be parsed into
tag1
and tag2
.
Given below is an example usage scenario and how the filter mechanism behaves at each step:
Step 1. The user executes the command filter tutorial CS2100
Step 2. The 'filter' command invokes FilterCommandParser
, which parses the argument tutorial CS2100
into separate
words tutorial
and CS2100
, and are stored in an array in the predicate TagsContainsKeywordsPredicate
.
Step 3. FilterCommandParser
then returns a FilterCommand
, which contains TagsContainsKeywordsPredicate
, a
predicate which tests for the tags 'tutorial' and 'CS2100'. FilterCommand.execute()
calls model
.updateFilteredTaskList(TagsContainsKeywordsPredicate)
, which returns an updated list of tasks containing any of the
tags input by the user.
filter filters tasks inclusively, while filterstrict filters tasks exclusively.
|
The following sequence diagram summarizes what happens when a user executes a new filter command:
4.10.2. Design Considerations
Aspect: How filter executes
-
Alternative 1 (current choice): The filter command is inclusive i.e filtering for A and B returns tasks with A, B, or both.
-
Pros: It is easy to implement, it is also consistent with how the 'find' command works.
-
Cons: It is not as specific.
-
-
Alternative 2: The filter command is exclusive, i.e filtering for A and B returns tasks with A & B
-
Pros: It is more specific (e.g. filters the tasks more strictly).
-
Cons: It is inconsistent with the original implementation of the 'find' command. It is more difficult to implement.
-
4.11. List Overdue Tasks
The listoverdue
command allows the user to view the list of all overdue tasks. A task is considered overdue if its
deadline has passed. In other words, the system’s current date is after the deadline.
4.11.1. Current Implementation
Given below is a code snippet of the ListOverdueCommand.
... public CommandResult execute(Model model, CommandHistory history) { // Filter the task list using the OverduePredicate with the current date as the parameter. model.updateFilteredTaskList(new OverduePredicate(SYSTEM_DATE)); EventsCenter.getInstance().post(new ChangeViewEvent(ChangeViewEvent.View.NORMAL)); return new CommandResult(MESSAGE_SUCCESS); } ...
The ListOverdueCommand calls the method updateFilteredTaskList with a new OverduePredicate which has the current date as the parameter.
public boolean test(Task task) { return date - task.getDate().yymmdd > 0 ? true : false; }
The test function within OverduePredicate class compares the current system date with the date of the task. If the task’s date is after the current time, the function returns false, and vice versa.
Model contains a UniqueTaskList called tasks.
Given below is an example usage scenario and how the list overdue mechanism behaves at each step:
Step 1. The user executes the command listoverdue
.
Step 2. model.updateFilteredTaskList()
will update the task list with OverduePredicate
as the parameter
OverduePredicate
itself takes the current system date in the yyMMdd format.
Step 3. The updated task list would be reflected on the UI to be displayed to the user.
4.11.2. Design Considerations
Aspect: How the overdue tasks are stored
-
Alternative 1 (current choice): The overdue tasks are not stored in a separate UniqueTaskList. Instead, the UniqueTaskList tasks is filtered with OverduePredicate each time the
listoverdue
command is called.-
Pros: Does not require a new UniqueTaskList.
-
Cons: If the number of tasks is huge, filtering will take a long time. However, this is not a problem since the number of tasks is small at any given point of time.
-
-
Alternative 2: The overdue tasks are stored in a new UniqueTaskList.
-
Pros: Retrieving is overdue tasks is faster.
-
Cons: A new UniqueTaskList has to be implemented.
-
4.12. List Archived Tasks
The listarchived
command allows the user to view the list of all archived tasks. Whenever the user completes a task,
he/she will archive it. When archived, the task will be moved from the task list into a separate archive list that stores
all completed tasks. If the user wishes to view all of his/her completed tasks, the listarchived
command will display
all of the archived tasks.
4.12.1. Current Implementation
Model contains two UniqueTaskLists - tasks and archivedTasks - each containing the tasks and archived tasks respectively.
Given below is an example usage scenario and how the list archived mechanism behaves at each step:
Step 1. The user executes the command listarchived
.
Step 2. The listarchived
command raises a new ChangeViewEvent that signals a change to archived view.
Step 3. MainWindow responds to the ChangeViewEvent with MainWindow#handleChangeViewEvent().
Step 4. MainWindow calls Logic#getFilteredArchivedTaskList(). It then creates a new TaskListPanel instance with Tasks in that list.
Step 5. MainWindow places the new TaskListPanel in the TaskListPanelPlaceHolder. The archived tasks are now displayed.
The following sequence diagram shows how the list archive operation works:
4.12.2. Design Considerations
Aspect: How to change what is displayed
-
Alternative 1 (current choice): The task list panel’s contents are replaced with a new task list containing archived tasks.
-
Pros: Only one section of the UI has to be changed.
-
-
Alternative 2: Two JavaFX scenes are created. To handle display changes, switch to the corresponding scene.
-
Pros: It is the proper way of handling change in display with JavaFX.
-
Cons: Since only one section of the UI needs to be changed, changing the whole scene seems redundant.
-
4.13. Sort tasks
The sorting feature enables tasks to be displayed in the order of deadline date and priority. This feature applies to all tasks except archived tasks.
4.13.1. Current Implementation
Sorting feature does not require any user input command.
Whenever the tasks are listed, they are always listed according to deadline date, then priority. Our application offers
3 priority levels from 1 to 3, 3 is the highest priority and 1 is the lowest.
Assume there are two tasks in a task list, task A and task B. Given below is the scenario when
task list is retrieved from schedule planner using any kind of list
command and how the sorting mechanism
behanves.
-
UniqueTaskList#sorted()
is called andTask#compare()
is parsed in as comparator. -
Task#compare()
calls Date.compare(), which compares the deadline date of task A and task B.-
If task A has earlier deadline date than task B, the order will be task A followed by task B, and vice versa.
-
If task A and task B have the same deadline date,
Priority#compare()
is then called, which compares priority of task A and task B.-
If task A has higher priority than task B, the order will be task A followed by task B and vice versa.
-
If task A and task B have the same priority, then
Name#compare()
is called, which compares the task name of A and B according to JAVAcompareTo()
method ofString
class.
-
-
Given below is a sequence diagram of the sorting feature.
4.13.2. Design Considerations
Aspect: How should sorting be executed
-
Alternative 1 (current choice): Automatically sort tasks whenever tasks are listed. In other words, whenever tasks are shown on UI, they are sorted according to deadline and priority.
-
Pros: The user experience is better using this approach, because tasks are always listed in the order of deadline and priority. The code required is less than Alternative 2 as well.
-
Cons: The performance of the application may be affected if the task list size is significantly large, because the task list has to be traversed and sorted before displaying on UI. Of course considering actual use case, such performance issue should be negligible.
-
-
Alternative 2: Implement sort command exclusively for sorting tasks. For example, use command
sort tasks
to obtain sorted list of all tasks, andsort archived
for sorted list of archived tasks.-
Pros: We do not have to worry about performance issue.
-
Cons: Users have to type command
sort
every time, which is apparently not so user-friendly.
-
4.14. Managing Tags Using Category
The category feature enables users to organize their tags. Users can create category and
save relevant tags inside category. There are two default categories in schedule planner,
category Modules
and category Others
. These two default categories cannot be renamed or
deleted, but user have full freedom to rename or delete other categories added by themselves.
4.14.1. Current Implementation
Each category has a unique name, and contains a list of tags. The list of tags is implemented using UniqueTagList, which does not allow duplicates, in other words, tags with same name. Given below is a snippet of implementation of UniqueTagList:
public class UniqueTagList implements Iterable<Tag> { private boolean tagsAreUnique(List<Tag> tags) { //Checks if tags are unique... } }
Given below is a snippet of implementation of category:
public class Category { //Two attributes, name and tag list private String name; private UniqueTagList tags; ///Other implementation... }
Schedule Planner contains a list of cateogry list, implemented using UniqueCategoryList
.
UniqueCategoryList
is a list used to save categories which does not allow duplicates.
Any two categories with same name are considered identical. Given below is a snippet
its implementation:
public class UniqueCategoryList implements Iterable<Category> { public boolean contains(String name) { //Checks whether this category name has been used... } private boolean categoriesAreUnique(List<Category> categories) { //Checks whether there are duplicate categories... } //Other implementation... }
Given below is an example usage scenario and how the category mechanism behaves at each step:
Step 1. User enters command addcat c/Game list
to add a category named Game list
.
-
The
addcat
command is executed.AddCategoryCommand#execute()
callsModel#addCategory()
. -
Model#addCategory()
callsVersionedSchedulePlanner#addCategory()
. -
SchedulePlanner#addCategory()
then callsUniqueCategoryList#add()
. -
UniqueCategoryList#add()
checks if the existing category list contains any category with identical name as new category, if exists,DuplicateCategoryException()
is thrown; if not, new category is created and added to category list.
Given below is the sequence diagram of add category command which explains how the mechanism works:
When user is adding tasks using unspecified tags (tags that have never been added to any category),
the tags will be automatically added to default category Others
.
Step 2. User enters command addtag c/Game list t/Zelda
to add tag Zelda
into category Game list
.
-
The command string is first passed to
AddTagCommandParser
, which parses stringZelda
to tag (let us call ittagZelda
) and createsAddTag
command. -
The command is then executed.
AddTagCommand#execute()
callsModel#addTag()
and passestagZelda
and stringGame list
as parameter. -
Model#addTag()
callsVersionedSchedulePlanner#addTag()
, passes intagZelda
and stringGame list
as parameter. -
VersionedSchedulePlanner#addCategory()
then callsUniqueCategoryList#getCategory()
, passing in stringGame list
as parameter and obtains corresponding category (let us call itcategoryGameList
). -
Category#addTag()
is then called with parametercategoryGameList
to add tagtagZelda
to categorycategoryGameList
. If there already exists a tag namedZelda
in UniqueTagList of categorycategoryGameList
, exceptionDuplicateTagException
is thrown.
Given below is the sequence diagram of add tag command.
Step 3. User enters command editcat c/Game list c/Reading list
to rename category from Game list
to Reading list
.
-
EditCategoryCommandParser
createsEditCategoryCommand
. WhenEditCategoryCommand
executes, it callsModel#editCategory()
and passes in stringGame list
and stringReading list
as parameter. -
Model#editCategory()
callsVersionedSchedulePlanner#editCategory()
with parameter stringGame list
and stringReading list
. -
VersionedSchedulePlanner
then callsUniqueCategoryList#setCategory()
to obtain the category namedGame list
(let us call itcategoryGameList
) and create a new category with nameReading list
and same tag list as categorycategoryGameList
, replace categorycategoryGameList
with this new category. Below is a snippet of implementation ofUniqueCategoryList#setCategory()
used here:
public void setCategory(String originalName, String newName) { //... //Obtain the category with name "Game list" Category oldCategory = getCategory(originalName); //Creat a new category which is identical to original category except name Category newCategory = new Category(newName, oldCategory.getUniqueTagList()); internalList.set(internalList.indexOf(oldCategory), newCategory); }
Given below is the sequence diagram of edit category command:
-
Sequence Diagram of Edit Category Command
Step 4. User enters command removecat c/Reading list
to remove the category named Reading list
. Let us call this
category categoryReadingList
.
-
RemoveCategoryCommandParser
createsRemoveCategoryCommand
. -
When
RemoveCategoryCommand
executes, it callsModel#removeCategory()
and passes in stringReading list
as parameter. -
Model#removeCategory()
callsVersionedSchedulePlanner#removeCategory()
with parameter stringReading list
. -
VersionedSchedulePlanner
then callsUniqueCategoryList#removeCategory()
to obtain category namedReading list
(let us call itcategoryReadingList
) and remove it. -
After
categoryReadingList
is removed, its tag list is removed as well. For example, if user has added tagZelda
only in category namedReading list
, then after removing this category, tagZelda
will be lost from schedule planner.
Given below is the sequence diagram of remove category command:
Besides adding tag, renaming (editing) and removing, we support clearing category as well.
Clear category command clears the tags list of selected category.
Mechanism of clearing category is very similar to renaming (editing) category.
Model#clearCategory()
calls VersionedSchedulePlanner#clearCategory()
, which then initialize a new empty
category with name "Modules"
.
Below is a code snippet of VersionedSchedulePlanner#clearCategory()
:
public void clearCategory(String name) { //... this.categories.setCategory(name, new Category(name)); }
Below is a code snippet of #UniqueCategoryList#setCategory()` used here, which is slightly different from what is used in editing category:
public void setCategory(String originalName, Category newCategory) { //... Category oldCategory = getCategory(originalName); internalList.set(internalList.indexOf(oldCategory), newCategory); }
We omit the sequence diagram here because of its similarity to edit category command.
4.14.2. Design Consideration
Aspect: How to store and manage tags
-
Alternative 1 (current choice): Store Categories as
UniqueCategoryList
and allow users to add tags to categories. There will be noUniqueTagList
directly managed by schedule planner. All tags are saved under UniqueTagList under individual Category. Because our application is catered for university students, we set two default categories,Modules
andOthers
.-
Pros: We allow users more freedom to manipulate with their tags and categorize their tags. User can add one tag to several categories, and the tags saved under different categories do not interfere with each other. This is the most user-friendly design we can think of so far.
-
Cons: The implementation is more complicated than Alternative 2. Besides, this mechanism requires more commands to operate, which could be harder for user to remember.
-
-
Alternative 2: Store tags in
UniqueTagList
directly managed by schedule planner. The schedule planner has two tag lists, one isModules
and another one isOthers
.-
Pros: It is easy to implement, and the operation requires less commands.
-
Cons: User has less freedom in managing tags. This design is less user-friendly than Alternative 1.
-
4.15. Display Tags in Sidebar
Whenever the user tags a task with a new tag, the tag will be added to the sidebar panel. The user will be able to view all of the tags he/she has used to categorize tasks. Entering the command tags c/CATEGORY will expand the tab for the specified category to show all tags listed under that category.
4.15.1. Current Implementation
Every time any category or tag is changed in the Student Schedule Planner, it raises a SchedulePlannerChangedEvent. The UI will then handle that event and update the side bar panel with the new version of categories and tags.
Given below is an example usage scenario and how the schedule planner behaves at each step:
Step 1. The user executes command addtag c/Modules t/CS2100
.
Step 2. The addtag
command updates the model with the new tag and raises a new SchedulePlannerChangedEvent.
Step 3. SidebarPanel responds to the SchedulePlannerChangedEvent with SidebarPanel#handleSchedulePlannerChangedEvent().
Step 4. The Accordion containing all the categories and tags is cleared.
Step 5. The Accordion is filled with the new list of updated categories and tags.
Step 6. The user executes command tags c/Modules
.
Step 7. The ShowTagsCommand raises a new ShowTagsRequestEvent("Modules").
Step 8. SidebarPanel responds to the ShowTagsRequestEvent with SidebarPanel#handleShowTagsRequestEvent().
Step 9. The TitledPane with the matching name "Modules" is expanded to display all the tags listed under the Modules category.
Below is a code snippet showing how the SidebarPanel handles a ShowTagsRequestEvent:
@Subscribe public void handleShowTagsRequestEvent(ShowTagsRequestEvent e) { String catName = e.getCategory(); ObservableList<TitledPane> titledPanes = accordion.getPanes(); for (TitledPane titledPane : titledPanes) { if (titledPane.getText().equals(catName)) { accordion.setExpandedPane(titledPane); } } }
4.15.2. Design Considerations
Aspect: How to update the tags on UI
-
Alternative 1 (current choice): Clear all the TitledPanes and add new TitledPanes accordingly.
-
Pros: No matter which category or tag is changed, or what type of change (ie. delete, add, or edit), this change can be handled by the same method each time.
-
Cons: It is redundant to clear everything and replace them with new TitledPanes.
-
-
Alternative 2: Handle different kinds of changes to the category or tag lists.
-
Pros: It is a lot faster to only change the TitledPane that is affected.
-
Cons: There are too many cases for how the lists can be changed. (ie. a different change is needed for each of these cases: category is deleted/edited/created/cleared, or a tag is deleted/added)
-
4.16. Display Progress Bars
The progress bars for today’s progress and this week’s progress at the bottom reflect the percentages automatically when the schedule planner is changed in any way.
4.16.1. Current Implementation
Whenever the user makes a change in the schedule planner, for example add, delete, or archive a task, it raises a new SchedulePlannerChangedEvent. UI part ProgressBarPanel will handle this event and update the values for both today and this week’s progress bars.
Given below is an example usage scenario and how the progress bar mechanism behaves at each step:
Step 1. User archives a Task due today.
Step 2. A new SchedulePlannerChangedEvent is raised.
Step 3. ProgressBarPanel handles the event with ProgressBarPanel#handleSchedulePlannerChangedEvent().
Step 4. The taskList is retrieved by SchedulePlannerChangedEvent#data.getTaskList(). The archivedTaskList is retrieved by SchedulePlannerChangedEvent#data.getArchivedTaskList().
Step 5. ProgressBarPanel#updateProgressBars(taskList, archivedTaskList) is called.
Step 6. After filtering through each lists with the DateSamePredicate, their sizes are calculated.
Step 7. The number of completed tasks for today is the size of the filtered archived list, while the total number of tasks for today is the size of both the filtered archived list and the filtered task list.
Step 8. The fraction is calculated from completed / total. Then the progress bar for today is set to that fraction.
Step 9. The same is done for this week’s progress bar but the lists are filtered with DateWeekSamePredicate.
The following sequence diagram shows how the progress bar mechanism works:
4.16.2. Design Considerations
Aspect: How the percentages are calculated
-
Alternative 1 (current choice): Calculate the percentage of progress by finding the size of filtered task list and filtered archived list.
-
Pros: It is easy to implement.
-
Cons: It may be slow if the task list and archived task list are very long.
-
-
Alternative 2: Keep track of number of uncompleted and completed tasks in new Objects called Day and Week.
-
Pros: It is much faster to calculate the percentage compared to filtering each task list by a predicate then calculating its size.
-
Cons: The Day and Week objects must be updated for a majority of the commands the app supports. If I had more time, I would have been able to implement this fully.
-
5. Documentation
We used asciidoc for writing documentation.
We chose asciidoc over Markdown because asciidoc, although a bit more complex than Markdown, provides more flexibility in formatting. |
5.1. Editing Documentation
See UsingGradle.adoc to 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.
5.2. Publishing Documentation
See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.
5.3. Converting Documentation to PDF format
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.
5.4. Site-wide Documentation Settings
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 |
|
Define this attribute if the project is an official SE-EDU project. This will render the SE-EDU navigation bar at the top of the page, and add some SE-EDU-specific navigation items. |
not set |
5.5. Per-file Documentation Settings
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 |
5.6. Site Template
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 |
6. Testing
6.1. Running Tests
There are three ways to run tests.
The most reliable method is Method 3. The first two methods might fail some GUI tests due to platform/resolution-specific idiosyncrasies. |
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 allTests
(Mac/Linux:./gradlew clean allTests
)
See UsingGradle.adoc for more info on how to run tests using Gradle. |
Method 3: Using Gradle (headless)
Thanks to the TestFX library we use, our GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. That means the developer can do other things on the Computer while the tests are running.
To run tests in headless mode, open a console and run the command gradlew clean headless allTests
(Mac/Linux: ./gradlew clean headless allTests
)
6.2. Types of tests
We have two types of tests:
-
GUI Tests - These are tests involving the GUI. They include:
-
System Tests that test the entire App by simulating user actions on the GUI. These are in the
systemtests
package. -
Unit tests that test the individual components. These are in
ssp.scheduleplanner.ui
package.
-
-
Non-GUI Tests - These are tests not involving the GUI. They include:
-
Unit tests that target the lowest level methods/classes.
e.g.ssp.scheduleplanner.commons.StringUtilTest
-
Integration tests that check the integration of multiple code units (those code units are assumed to be working).
e.g.StorageManagerTest
-
Hybrids of unit and integration tests. These tests check multiple code units as well as how the are connected together.
e.g.LogicManagerTest
-
6.3. Troubleshooting Testing
Problem: HelpWindowTest
fails with a NullPointerException
.
-
Reason: One of its dependencies,
HelpWindow.html
insrc/main/resources/docs
is missing. -
Solution: Execute Gradle task
processResources
.
7. Dev Ops
7.1. Build Automation
See UsingGradle.adoc to learn how to use Gradle for build automation.
7.2. Continuous Integration
We use Travis CI and AppVeyor to perform Continuous Integration on our projects. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.
7.3. Coverage Reporting
We use Coveralls to track the code coverage of our projects. See UsingCoveralls.adoc for more details.
7.4. Documentation Previews
When a pull request has 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. See UsingNetlify.adoc for more details.
7.5. Making a Release
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.
7.6. Managing Dependencies
A project often depends on third-party libraries. For example, Schedule Planner depends on the Jackson library for XML parsing. Managing these dependencies can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than the following alternatives:
a. Including those libraries in the repo (this bloats the repo size)
b. Requiring developers to download those libraries manually (this creates extra work for developers)
Appendix A: Product Scope
Target user profile:
-
have a need to manage their schedules for daily tasks
-
have a want to view and be reminded of important things
-
be able to set priority level on their schedule
-
prefer typing over mouse input
-
want a customizable schedule planner
Value proposition: Student Scheduler Planner manages schedules faster than a typical mouse/GUI driven app yet retains some GUI interface to allow users to have an easier view.
Appendix B: User Stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
|
student |
add task to to-do list |
keep track of tasks to do |
|
student |
view tasks in to-do list |
know when the deadlines of my tasks are |
|
student |
modify tasks in to-do list |
adjust or change my tasks according to my schedule |
|
student |
delete completed tasks in to-do list |
keep my to-do list clean and neat |
|
student |
view overdue tasks at the main page |
take note of the deadlines that I missed and try to make up |
|
student |
sort tasks by priority |
know what tasks I should do first |
|
student |
view tasks for today |
have a study/life plan for this day |
|
student |
view tasks for next 7 days |
have a relatively clear plan of this week |
|
student |
view tasks for the rest of the month |
view my schedule for the month |
|
student |
search my tasks by name |
find specific tasks easily |
|
student |
search my tasks by tags |
find specific tasks easily |
|
student |
view all commands |
to guide me so that I could use the application |
|
student |
view a calendar of all to-do tasks |
have an overview of all the tasks to do |
|
student |
view a history of all my commands in user-friendliness format |
view what changes I had made before |
|
student |
clear the screen |
have my screen interface cleaned of commands and result |
|
student |
sort tasks by tags |
tag my tasks |
|
student |
sort tasks by category |
categorise my tasks |
|
student |
create a "workshop" list |
record all the workshops that I have signed up |
|
student |
customise the theme of the student schedule planner |
personalise how my student schedule planner looks like |
|
student |
undo my command |
correct my mistake |
|
student |
redo my command |
correct my accidental "undo" |
|
student |
add and view subtasks under each task |
know specifically what tasks requires E.g CS2103T Project: Write User guide, Review Pull Requests, Peer Evaluation) |
|
student |
add my tasks that are non-school related |
have a schedule mixed with both school and non-school related tasks |
|
student |
list my tasks that are non-school related |
view my upcoming schedule of non-school related tasks |
|
student |
update my tasks that are non-school related |
update my upcoming schedule of non-school related tasks |
|
student |
delete my non-school related tasks if they are done |
update my upcoming schedule of non-school related tasks |
|
student |
search for my non-school related tasks |
to find a particular non-school related tasks |
{More to be added}
Appendix C: Use Cases
(For all use cases below, the System is the Student Schedule Planner
and the Actor is the user
, unless specified otherwise)
Use case: Add Task
MSS
-
User requests to add task into Student Schedule Planner.
-
Student Schedule Planner adds the exam into task list.
Use case ends.
Extensions
-
1a. The add Task command did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
Use case: List Task
MSS
-
User requests to view task list from Student Schedule Planner.
-
Student Schedule Planner display the task list.
Use case ends.
Extensions
-
1a. The list task command did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
2a1. The list is empty.
Use case ends.
Use case: Modify Task
MSS
-
User requests to list task list from Student Schedule Planner.
-
Student Schedule Planner display the task list.
-
User request to edit a specific task in the list.
-
Student Schedule Planner updates the task.
Use case ends.
Extensions
-
1a. The list task command did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
2a1. The list is empty.
Use case ends.
-
3a. The Modify task command did not follow the correct format.
-
3a1. Student Schedule Planner shows an error message.
Use case resume at step 3.
-
Use case: Delete Task
MSS
-
User requests to list task list from Student Schedule Planner.
-
Student Schedule Planner display the task list.
-
User request to delete a specific task in the list.
-
Student Schedule Planner delete the task.
Use case ends.
Extensions
-
1a. The list task command did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
2a1. The list is empty.
Use case ends.
-
3a. The Delete task command did not follow the correct format.
-
3a1. Student Schedule Planner shows an error message.
-
-
3b. The task to be deleted does not exist.
-
3b1. Student Schedule Planner shows an error message.
Use case ends.
-
Use case: Search Task
MSS
-
User requests to search a specific task by its name from Student Schedule Planner.
-
Student Schedule Planner filter the task by the given parameter.
-
Student Schedule Planner display the filtered task.
Use case ends.
Extensions
-
1a. The search task command did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
3a. The filtered task list is empty and thus display empty list.
Use case ends.
Use case: Filter Tasks according to Tags (Inclusive)
MSS
-
User requests to filter tasks by its tags from Student Schedule Planner.
-
Student Schedule Planner filters the tasks with tags entered by user.
-
Student Schedule Planner displays the filtered tasks.
Use case ends.
Extensions
-
1a. The filter task command did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
3a. The filtered task list is empty and thus displays an empty list.
Use case ends.
Use case: Filter Tasks according to Tags (Exclusive)
MSS
-
User requests to strictly filter tasks by its tags from Student Schedule Planner.
-
Student Schedule Planner strictly filters the tasks with tags entered by user.
-
Student Schedule Planner displays the strictly filtered tasks.
Use case ends.
Extensions
-
1a. The filterstrict task command did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
3a. The strictly filtered task list is empty and thus displays an empty list.
Use case ends.
Use case: List tasks due today
MSS
-
User requests to view tasks due on current date from the Student Schedule Planner.
-
Student Schedule Planner filters tasks by current date.
-
Student Schedule Planner displays list of tasks due on current date.
Use case ends.
Extensions
-
1a. The
listday
command did not follow the correct format.-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
3a. The filtered task list is empty and thus displays an empty list.
Use case ends.
Use case: List tasks due until this Sunday
MSS
-
User requests to view tasks due from the current date to Sunday from the Student Schedule Planner.
-
Student Schedule Planner filters tasks from current date to Sunday.
-
Student Schedule Planner displays list of tasks due from current date to Sunday.
Use case ends.
Extensions
-
1a. The
listweek
command did not follow the correct format.-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
3a. The filtered task list is empty and thus displays an empty list.
Use case ends.
Use case: List tasks due until the end of the month
MSS
-
User requests to view tasks due from the current date until the end of month from the Student Schedule Planner.
-
Student Schedule Planner filters tasks from the range of current date until end of month
-
Student Schedule Planner display list of tasks due from current date until end of month.
Use case ends.
Extensions
-
1a. The
listmonth
command did not follow the correct format.-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
3a. The filtered task list is empty and thus displays an empty list.
Use case ends.
Use case: Generate academic calendar weeks
MSS
-
User keys in a specific date to generate the academic calendar weeks.
-
Student Schedule Planner generates the academic calendar weeks.
-
Student Schedule Planner saves the academic calendar weeks.
-
Student Schedule Planner displays to user the command result.
-
User launches the Student Schedule Planner at any time after Steps 1 - 4.
-
Student Schedule Planner retrieves data saved.
-
Student Schedule Planner displays the updated application title based on the retrieved data.
Use case ends.
Extensions
-
1a. The specific date did not follow the correct format.
-
1a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
3a. The data could not be saved.
-
3a1. Student Schedule Planner shows an error message.
Use case ends.
-
-
5a. The data could not be retrieved.
-
5a1. Student Schedule Planner shows an error message. Use case ends.
-
{More to be added}
Appendix D: Non Functional Requirements
-
The app should work on most Mainstream OS as long as it has Java
9
or higher installed. -
Each command execution time should not take more than 3 seconds.
{More to be added}
Appendix E: Glossary
- MSS
-
Main Success Scenario (MSS) is the steps of a scenario to be followed to accomplish the objective goal.
- Mainstream OS
-
Windows, Linux, Unix, OS-X
- Private contact detail
-
A contact detail that is not meant to be shared with others
- NUS academic calendar
-
Refers to the academic calendar used by National University of Singapore. This product references only 17 weeks of the calendar (Week 1 to Examination Week).
Referenced from: http://www.nus.edu.sg/registrar/info/calendar/AY2018-2019.pdf
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. Adding a task
-
Adding a task to the task list
-
Test case:
add n/cs2100 d/161118 p/3 v/home
Expected: A new task is added and reflected in the panel on the bottom right ofUI
. Status bar is updated. -
Test case:
add n/cs2100 d/161118 p/4 v/home
Expected: No task is added. Error details shown in status message. Status bar remains the same. -
Test case:
add n/cs2100 d/999999 p/3 v/home
Expected: No task is added. Error details shown for date field.
-
F.3. Adding a recurring task
-
Adding a recurring task to the task list
-
Test case:
repeat r/5 i/1 n/go to school d/030118 v/NUS p/3
Expected: Five tasks with the same name, priority, venue, but with differing dates of 3, 4, 5, 6, 7 Nov 2018 are added to the list. The tasks are reflected in the task list panel. Status bar is updated. -
Test case:
repeat r/17 i/1 n/sleep d/010101 v/home p/1
Expected: No tasks are added. Error details shown in status message. Status bar remains the same. -
Test case:
repeat r/2 i/0 n/sleep d/010101 v/home p/1
Expected: No tasks are added. Error details shown in status message. Status bar remains the same.
-
F.4. Viewing overdue tasks
-
Viewing tasks due on dates before the current date
-
Test case: listoverdue
Expected: All tasks with dates before the current date are displayed in the task list panel. Status bar remains the same.
-
F.5. Editing a task
-
Editing a task from the task list
-
Test case:
edit 1 v/library
Expected: First task is edited. The new venue is library. -
Test case:
edit 1 t/
Expected: First task is edited. Any existing tags would be removed. -
Test case:
edit 1 d/999999
Expected: First task is not edited. Error details shown in status message. Status bar remains the same.
-
F.6. Archiving a task
-
Archiving a task from the task list
-
Test case:
archive 1
Expected: First task is archived if there exists at least 1 task in your schedule planner. -
Test case:
archive 0
Expected: No task is archived. Error details shown in the status message. Status bar may grow to the right depending on the deadline date and current system time.
-
F.7. Managing tags and categories
-
Adding category
-
Test case:
addcat c/Modules
Expected: No category is added, becauseModules
is a default category and it already exists in schedule planner. Error details shown in status message. -
Test case:
addcat c/School
Expected: If there is no category namedSchool
in your schedule planner at this time point, categorySchool
is now added. If there already exists a category namedSchool
in your schedule planner, then no category is added. Error details shown in status message.
-
-
Renaming a category
-
Test case:
editcat c/Modules c/Work
Expected: No category is renamed, becauseModules
is a default category that cannot be renamed. Error details shown in status message. -
Test case:
editcat c/School c/Work
Expected: If there is no category namedSchool
in your schedule planner at this time point, no category is renamed. Error details shown in status message. If there exists a category namedSchool
in your schedule planner and no category namedWork
in your schedule planner, categorySchool
is now renamed toWork
. If there exists both a category namedSchool
and a category namedWork
, no category is renamed. Error details shown in status message.
-
-
Removing a category
-
Test case:
removecat c/Modules
Expected: No category is added, becauseModules
is a default category that cannot be removed. Error details shown in status message. -
Test case:
removecat c/School
Expcected: If there exists a category namedSchool
, it is now removed from schedule planner. If this category does not exist, no category is removed. Error details shown in status message.
-
-
Clearing a category
-
Test case:
clearcat c/Modules
Expected: Tag list under categoryModules
is successfully cleared. Now categoryModules
contains no tags. -
Test case:
clearcat c/School
Expected: If categorySchool
exists in your schedule planner, tag list under categorySchool
is successfully cleared. Now categorySchool
contains no tags. If this category does not exist, no category is cleared. Error details shown in status message.
-
-
Adding a tag to a category
-
Test case:
addtag c/Modules t/CS2101
Expected: IfModules
category currently does not contain tagCS2101
, tagCS2101
is successfully added to categoryModules
. IfModules
category already contains tagCS2101
, no tag is added to this category. Error details shown in status message. -
Test case:
addtag c/School t/Lecture
Expected: If categorySchool
exists in your schedule planner, the result is similar to above. If categorySchool
does not exist, no tag is added to any category. Error details shown in status message.
-
F.8. Deleting a task
-
Deleting a task while all tasks are listed
-
Prerequisites: List all tasks using the
list
command. Multiple tasks in the list. -
Test case:
delete 1
Expected: First task is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. -
Test case:
delete 0
Expected: No task is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete
,delete x
(where x is larger than the list size)
Expected: Similar to previous.
-
F.9. Task Monitoring System
-
Generating academic calendar weeks data
-
Prerequisites: Only dates from 21st century in
DDMMYY
format is considered valid date. -
Test case:
firstday 161118 12
Expected: No academic calendar weeks data is generated. Error details shown in status message. Status bar remains the same. -
Test case:
firstday 999999
Expected: No academic calendar weeks data is generated. Error details shown in status message. Status bar remains the same. -
Test case:
firstday 161118
Expected: No academic calendar weeks data is generated. Error details shown in status message. Status bar remains the same. -
Test case:
firstday 130818
Expected: Academic calendar weeks data is generated and saved. Details shown in status message to inform user. After relaunching the application, the application title will be appended with current week’s description.
-
-
Viewing tasks due today/this week
-
Test case:
listday
Expected: All task(s) that match the current system date will be reflected in the panel on the bottom right ofUI
. -
Test case:
listweek
Expected: All task(s) that match the date from the current system date until the closest Sunday will be reflected in the panel on the bottom right ofUI
.
-
F.10. Filtering Tasks
-
Viewing tasks due this month
-
Test case:
listmonth
Expected: All task(s) that match the current system date until the end of the month will be reflected in the task list panel in theUI
. -
Test case:
filter tutorial
Expected: All tasks that contain the tagtutorial
will be reflected in the task list panel in theUI
. -
Test case:
filter tutorial CS2100
Expected: All tasks that contain EITHERtutorial
,CS2100
, OR BOTH tags, will be reflected in the task list panel in theUI
. -
Test case:
filterstrict tutorial
Expected: All tasks that contain the tagtutorial
will be reflected in the task list panel in theUI
. Similar tofilter tutorial
. -
Test case:
filterstrict tutorial GEH1034
Expected: All tasks that contain BOTHtutorial
andGEH1034
tags will be reflected in the task list panel in theUI
-
F.11. Testing GUI
-
Displaying categories and tags in the sidebar
-
Test case:
clear
Expected: The default categories "Modules" and "Others" are be displayed in the sidebar. -
Test case:
addcat c/School
Expected: The new category "School" is displayed in the sidebar below "Modules" and "Others". -
Test case:
tags c/Modules
Expected: The Modules pane will be expanded to reveal nothing is listed underneath. -
Test case:
addtag c/Modules t/CS2100
followed bytags c/Modules
Expected: The Modules pane will be expanded to reveal "CS2100" underneath.
-
-
Showing progress
-
Test case: add 5 tasks due on the current date and archive one of them. Expected: Both progress bars for today and this week are 20% filled.
-
Test case:
progresstoday
Expected: Status message displays that user has completed 20.00% of tasks for today. -
Test case:
progressweek
Expected: Status message displays that user has completed 20.00% of tasks for this week.
-