Skip to content

Latest commit

 

History

History
1758 lines (1280 loc) · 80.1 KB

DeveloperGuide.adoc

File metadata and controls

1758 lines (1280 loc) · 80.1 KB

iScheduler Xs Max - Developer Guide

By: CS2103-AY1819S1-F11-1      Since: Sep 2018      Licence: MIT

1. Setting up

1.1. Prerequisites

  1. JDK 9 or later

    ⚠️
    JDK 10 on Windows will fail to run tests in headless mode due to a JavaFX bug. Windows developers are highly recommended to use JDK 9.
  2. IntelliJ IDE

    ℹ️
    IntelliJ by default has Gradle and JavaFX plugins installed.
    Do not disable them. If you have disabled them, go to File > Settings > Plugins to re-enable them.

1.2. Setting up the project in your computer

  1. Fork this repo, and clone the fork to your computer

  2. Open IntelliJ (if you are not in the welcome screen, click File > Close Project to close the existing project dialog first)

  3. Set up the correct JDK version for Gradle

    1. Click Configure > Project Defaults > Project Structure

    2. Click New…​ and find the directory of the JDK

  4. Click Import Project

  5. Locate the build.gradle file and select it. Click OK

  6. Click Open as Project

  7. Click OK to accept the default settings

  8. Open a console and run the command gradlew processResources (Mac/Linux: ./gradlew processResources). It should finish with the BUILD SUCCESSFUL message.
    This will generate all resources required by the application and tests.

  9. Open XmlAdaptedEvent.java and MainWindow.java and check for any code errors

    1. 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

    2. 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

  10. Repeat this for the test folder as well (e.g. check XmlUtilTest.java and HelpWindowTest.java for code errors, and if so, resolve it the same way)

1.3. Verifying the setup

  1. Run the seedu.scheduler.MainApp and try a few commands

  2. Run the tests to ensure they all pass.

1.4. Configurations to do before writing code

1.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,

  1. Go to File > Settings…​ (Windows/Linux), or IntelliJ IDEA > Preferences…​ (macOS)

  2. Select Editor > Code Style > Java

  3. Click on the Imports tab to set the order

    • For Class count to use import with '*' and Names count to use static import with '*': Set to 999 to prevent IntelliJ from contracting the import statements

    • For Import Layout: The order is import static all other imports, import java.*, import javax.*, import org.*, import com.*, import all other imports. Add a <blank line> between each import

Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.

1.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 CS2103-AY1819S1-F11-1/main repo.

If you plan to develop this fork as a separate product (i.e. instead of contributing to CS2103-AY1819S1-F11-1/main), you should do the following:

  1. Configure the site-wide documentation settings in build.gradle, such as the site-name, to suit your own project.

  2. Replace the URL in the attribute repoURL in DeveloperGuide.adoc and UserGuide.adoc with the URL of your fork.

1.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)

1.4.4. Getting started with coding

When you are ready to start coding,

  1. Get some sense of the overall design by reading Section 2.1, “Architecture”.

  2. Take a look at Appendix A, Suggested Programming Tasks to Get Started.

2. Design

2.1. Architecture

Architecture
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of the App. Given below 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 invokes 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.

  • 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: Used by many classes to write log messages to the App’s log file.

The rest of the App consists of four components.

  • UI: The UI of the App.

  • Logic: The command executor.

  • Model: Holds the data of the App in-memory.

  • Storage: Reads data from, and writes data to, the hard disk.

Each of the four components

  • Defines its API in an interface with the same name as the Component.

  • Exposes its functionality using a {Component Name}Manager class.

For example, the Logic component (see the class diagram given below) defines it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 2. Class Diagram of the Logic Component

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.

SDforDeleteEvent
Figure 3. Component interactions for delete 1 command (part 1)
ℹ️
Note how the Model simply raises a SchedulerChangedEvent when the Scheduler data are changed, instead of asking the Storage to save the updates to the hard disk.

The diagram below shows how the EventsCenter reacts to that event, which eventually results in the updates being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.

SDforDeleteEventEventHandling
Figure 4. Component interactions for delete 1 command (part 2)
ℹ️
Note how the event is propagated through the EventsCenter to the Storage and UI without Model having to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct coupling between components.

The sections below give more details of each component.

2.2. UI component

UiClassDiagram
Figure 5. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, EventListPanel, StatusBarFooter, BrowserPanel 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 link:https://github.com/CS2103-AY1819S1-F11-1/main/tree/master/src/main/java/seedu/scheduler/ui/ MainWindow.java[MainWindow] is specified in MainWindow.fxml

The UI component,

  • Executes user commands using the Logic component.

  • Binds itself to some data in the Model so that the UI can auto-update when data in the Model change.

  • Responds to events raised from various parts of the App and updates the UI accordingly.

2.3. Logic component

LogicClassDiagram
Figure 6. Structure of the Logic Component

API : Logic.java

  1. Logic uses the SchedulerParser class to parse the user command.

  2. This results in a Command object which is executed by the LogicManager.

  3. The command execution can affect the Model (e.g. adding an event) and/or raise events.

  4. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("delete 1") API call.

DeleteEventSdForLogic
Figure 7. Interactions Inside the Logic Component for the delete 1 Command

2.4. Model component

ModelClassDiagram
Figure 8. Structure of the Model Component

API : Model.java

The Model,

  • stores a UserPref object that represents the user’s preferences.

  • stores the Scheduler data.

  • exposes an unmodifiable ObservableList<Event> that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.

  • does not depend on any of the other three components.

ℹ️
As a more OOP model, we can store a Tag list in Scheduler, which Event can reference. This would allow Scheduler to only require one Tag object per unique Tag, instead of each Event needing their own Tag object. An example of how such a model may look like is given below.

ModelClassBetterOopDiagram

2.5. Storage component

StorageClassDiagram
Figure 9. Structure of the Storage Component

API : Storage.java

The Storage component,

  • can save UserPref objects in JSON format and read it back.

  • can save the Scheduler data in XML format and read it back.

2.6. Common classes

Classes used by multiple components are in the seedu.scheduler.commons package.

3. Implementation

This section describes some noteworthy details on how certain features are implemented.

3.1. Undo/Redo feature

3.1.1. Current Implementation

The undo/redo mechanism is facilitated by VersionedScheduler. It extends Scheduler with an undo/redo history, stored internally as a schedulerStateList and currentStatePointer. Additionally, it implements the following operations:

  • VersionedScheduler#commit() — Saves the current scheduler state in its history.

  • VersionedScheduler#undo() — Restores the previous scheduler state from its history.

  • VersionedScheduler#redo() — Restores a previously undone scheduler state from its history.

These operations are exposed in the Model interface as Model#commitScheduler(), Model#undoScheduler() and Model#redoScheduler() respectively.

Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.

Step 1. The user launches the application for the first time. The VersionedScheduler will be initialized with the initial scheduler state, and the currentStatePointer pointing to that single scheduler state.

UndoRedoStartingStateListDiagram

Step 2. The user executes delete 5 command to delete the 5th event in the scheduler. The delete command calls Model#commitScheduler(), causing the modified state of the scheduler after the delete 5 command executes to be saved in the schedulerStateList, and the currentStatePointer is shifted to the newly inserted scheduler state.

UndoRedoNewCommand1StateListDiagram

Step 3. The user executes add n/Holiday …​ to add a new event. The add command also calls Model#commitscheduler(), causing another modified scheduler state to be saved into the schedulerStateList.

UndoRedoNewCommand2StateListDiagram
ℹ️
If a command fails its execution, it will not call Model#commitScheduler(), so the scheduler state will not be saved into the schedulerStateList.

Step 4. The user now decides that adding the event was a mistake, and decides to undo that action by executing the undo command. The undo command will call Model#undoScheduler(), which will shift the currentStatePointer once to the left, pointing it to the previous scheduler state, and restores the scheduler to that state.

UndoRedoExecuteUndoStateListDiagram
ℹ️
If the currentStatePointer is at index 0, pointing to the initial scheduler state, then there are no previous scheduler states to restore. The undo command uses Model#canUndoScheduler() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.

The following sequence diagram shows how the undo operation works:

UndoRedoSequenceDiagram

The redo command does the opposite — it calls Model#redoScheduler(), which shifts the currentStatePointer once to the right, pointing to the previously undone state, and restores the scheduler to that state.

ℹ️
If the currentStatePointer is at index schedulerStateList.size() - 1, pointing to the latest scheduler state, then there are no undone scheduler states to restore. The redo command uses Model#canRedoScheduler() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.

Step 5. The user then decides to execute the command list. Commands that do not modify the scheduler, such as list, will usually not call Model#commitScheduler(), Model#undoScheduler() or Model#redoScheduler(). Thus, the schedulerStateList remains unchanged.

UndoRedoNewCommand3StateListDiagram

Step 6. The user executes clear, which calls Model#commitScheduler(). Since the currentStatePointer is not pointing at the end of the schedulerStateList, all scheduler states after the currentStatePointer will be purged. We designed it this way because it no longer makes sense to redo the add n/Party …​ command. This is the behavior that most modern desktop applications follow.

UndoRedoNewCommand4StateListDiagram

The following activity diagram summarizes what happens when a user executes a new command:

UndoRedoActivityDiagram

3.1.2. Design Considerations

Aspect: How undo & redo executes
  • Alternative 1 (current choice): Saves the entire scheduler.

    • Pros: Easy to implement.

    • Cons: May have performance issues in terms of memory usage.

  • Alternative 2: Individual command knows how to undo/redo by itself.

    • Pros: Will use less memory (e.g. for delete, just save the event 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): Use a list to store the history of scheduler states.

    • Pros: 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 and VersionedScheduler.

  • Alternative 2: Use HistoryManager for undo/redo

    • Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase.

    • Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as HistoryManager now needs to do two different things.

3.2. EnterGoogleCalendarMode

3.2.1. Current Implementation

The EnterGoogleCalendarMode mechanism is facilitated by utility method to get Google authentication and enables the Google Calendar feature. It implements the following operations:

  • EnterGoogleCalendarMode — Enable the Google Calendar feasture and , get (pull) the online Google Calendar events and merge with local events.

  • postGoogleCalendarEvents[coming in v2.0] — Post (push) the local events and merge with online Google Calendar.

It also supports the following existing features, to enhance the with real-time effects on Google Calendar (those created with the app) * Add — When the Google Calendar feature is enabled, the app creates the same event(s) in the google calendar. * Edit — When the Google Calendar feature is enabled, the app edits the same event(s) in the google calendar. * Delete — When the Google Calendar feature is enabled, the app deletes the same event(s) in the google calendar. * Clear — When the Google Calendar feature is enabled, the app clears the primary google calendar. The Event(s) mentioned above refer to those created by the app. The existing ones are not supproted yet. Even they are pulled to local database, commonds have no effect on them, as mentioned in the UserGuide

Below is an example usage scenario and explanation on the EnterGoogleCalendarMode mechanism behavior behind each step.

Step 1
→ The user has always been using the application locally and never enabled this feasure.
→ All Scheduler Events have been created locally. That is, the Scheduler has only local events (no Google Calendar Events).

→ An empty database is illustrated below as an imaginary array:

EmptyLocalDataBaseDiagram

Step 2
→ The user executes EnterGoogleCalendarMode command to enable the feature. download the Google Calendar Events to the local database.

→ A Calendar object is to be built with method getCalendar() in ConnectToGoogleCalendar.java
→ A method getNetHttpTransport() is called to let application establish a connection with Google to initiate an authorization process (for those who use this command for the first time).

→ A pop-up window will appear. The User will be required to log in their Google Account as per other Google Service.
After successful login, the login credential will be saved locally such that no future log-in is required.

Step 3
→ After successful authentication, a success message is shown for the user and instruct the user to close the browser.

+ → At the same time, the application proceeds automatically for the downloading and merging of Google Calendar’s events to local events.
→ The flowchart below shows the process of deciding whether to prompt a login page or to proceed with a local credential file:

flowChatForDecisionOnLogIn
ℹ️

For developers, if you test the application locally, please do not include this log-in credential file to the out/production package.
Errors could occur when the user downloads and uses it.

Step 4
→ The application checks for whether the feature has already been enabled. If it is enabled, the command is rejected. This is because the method is an initialization method. Second initialization is rejected.

Step 5
→ The application will then call method getEvents to extract a (Google)Events objects from the user’s online Google Calendar.

ℹ️

For current implementation, only events in the primary (default) calendar are extracted.

Step 6
→ For each event extracted, various ConvertEvent utility methods will be called to convert the Google Extracted Event to local Event.

Step 7
→ The converted events are saved in the local database, together with other locally created Events.

3.2.2. Design Considerations for EnterGoogleCalendarMode

  • Save local login credential (current choice)

    • Pro: Improves the user experience — user won’t have to log in every time they use this command, it would be too troublesome and not user-friendly.

    • Con: Improper protection could cause a leak of personal info.

  • Require login everytime (alternative)

    • Pro: Very secured.

    • Con: Not user-friendly.

3.2.3. EnterGoogleCalendarMode - Add

Below is an example usage scenario and explanation on the Add mechanism behavior behind each step.

After the user enabled the Google Calendar feature, when the command add n/Online Shopping! s/Dec 12, 2018 5pm e/Dec 12, 2018 7pm re/2H is entered.

Step 1
→ The local event is generated.

Step 2
→ The toAdd Event is captured and send to pushToGoogleCal method.

Step 3
→Regardless of whether the Event is a single event or a repeated event. pushToGoogleCal method sets the common attributes such as EventName, Venue(Location), Description, StartDateTime, EndDateTime and Reminders.

Step 4
→ If it is a RepeatEvent, set special attributes to it such as googleRecurringEventId, googleiCalId, eventRepeatType,eventUntilDateTime. A Google specific commandMessage is constructed and sent to Google API.

Various convertion methods are called to convert the Event format from local Event to Google Event.

3.2.4. Design Considerations - Add

A common identifier is needed to uniquelly identify the same Event in local database and in Google Calendar.

  • Use Event Unique Id and EventSet Unique Id (current choice)

    • Pro: It is unique and randomly generated.

    • Con: Very hard to implement and have to find the equivalent unique string in Google’s implementation. Eventually, after checking out Google’s implementation, we set the following equivalent properties:
      EventUid == EventId
      EventSetUid == iCalId

  • By Name (alternative)

    • Pro: Very easy to implement

    • Con: User unable to create events with the same name.

3.2.5. EnterGoogleCalendarMode - Edit

Step 1
→Command clear
Cleans up the enviroment

Step 2
→Command add n/Study For next sem s/2018-12-1 9pm e/2018-12-1 10pm rt/WEEKLY ru/2018-12-25 9pm to create a repeat event, this event will trigger Google to create 4 Event Instances

Step 3
→Command edit 1 n/change1
Only the first event will be edited, its name will become change1.
A command requst will be sent to edit the first event instance.

Step 4
edit 2 n/change2 -u+ The first event is untouched.
The event instances from the second one onwards in the same EventSet will be edited, their name swill become change2.
A command requst will be sent to edit the various event instances.

Step 5
edit 3 n/change3 -a+ All event instances in the same EventSet will be edited, their name swill become change3.
A command requst will be sent to edit the underlying Event, instead of editing individual event instances.

3.2.6. EnterGoogleCalendarMode - Delete

The ideas follow the Edit section.

3.2.7. EnterGoogleCalendarMode - Test

We need to carry out integration test on Github.
However various limits are posted on the usage of APIs, but tests are necessary to run.

  • Introduce offline mode (current choice)

    • Pro: Offline mode introduced at the code level to disable Google Calendar methods.

    • Con: Have to write seperate set of tests and helper functions, and control the switch accurately. enabl() and disable() helper methods are used for tests related to Google Calendar methods.

  • No offline mode (alternative)

    • Pro: Easy to implement, no additional tests needed.

    • Con: Running the current set of test cases will certainly sending too many requests to Google, and triggers the IP address/ account to be blocked.

Note on Google Calendar Tests Please ensure only one running PR is running the tests.
All tests will be using the StoredCredential file to access a test account’s google calendar, running the same set of tests.

3.3. [Proposed] Data Encryption

{Explain here how the data encryption feature will be implemented}

3.4. 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 3.5, “Configuration”)

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level

  • Currently log messages are output through: Console and to a .log file.

Logging Levels

  • SEVERE: Critical problem detected which may possibly cause the termination of the application

  • WARNING: Can continue, but with caution

  • INFO: Information showing the noteworthy actions by the App

  • FINE: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size

3.5. Configuration

Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file (default: config.json).

3.6. Add Command

3.6.1. Current Implementation

The add command allows the user to add new events to the scheduler. Add Command is facilitated by the AddCommand class. The format of this command is add event n/EVENT_NAME [s/START_DATETIME] [e/END_DATETIME] [d/DESCRIPTION] [v/VENUE] [rt/RECURRING_TYPE] [ru/RECURRING_UNTIL_DATETIME] [t/TAG]…​.

The command uses Natty, a natural language date parser written in Java to parse DateTime given by the user. When given a user inputted string, Natty will parse the string into a LocalDateTime class.

AddCommand requires the interaction between both Logic and Model Component. The Logic component comprises of AddCommandParser and RepeatEventGenerator. The AddCommandParser parses the user input and RepeatEventGenerator generates repeating events (if any). The Model component is responsible for updating the internal event list.

Given below is the execution flow of Add Command.

  1. When the user inputs a command, LogicManager will call the parseCommand method in SchedulerParser with the user input as arguments.

  2. If the user input is valid and contains the keyword add, AddCommandParser will be instantiated.

  3. SchedulerParser will then call the parse method of AddCommandParser.

  4. AddCommandParser parses the user input with the respective parsing methods.

  5. AddCommand will be instantiated with the parsed event.

  6. generateAllRepeatedEvents method in RepeatEventGenerator is invoked to generate a list of repeating events according to the repeat type of the parsed event.

  7. LogicManager will then proceed to call the execute() method of AddCommand.

  8. Model Component will then add the list of events to an internal list of events using the addEvents method.

The following sequence diagram demonstrates how the add operation works:

AddCommandSequenceDiagram

The following activity diagram summarizes what happens when a user executes a new Add Command:

AddCommandActivityDiagram

3.6.2. Design Considerations

Aspect: Generation of repeating events
  • Alternative 1 (current choice): Use RepeatEventGenerator to generate repeating events.

    • Pros: Singleton pattern. Easy to generate repeating events anywhere in the code base. Reduce RepeatEventGenerator object creations in testing. Uses the Single Responsibility Principle.

    • Cons: May increase coupling across the code base. May increase the difficulty of testing due to the singleton object.

  • Alternative 2: Event model knows how to generate its repeating events by itself.

    • Pros: Will use less memory (e.g. generating events do not need to depend on another class to generate the repeating events).

    • Cons: Increase coupling.

Aspect: Datetime Parser
  • Alternative 1 (current choice): Use Natty, a natural language date parser.

    • Pros: Users do not need to conform to a standard format and able to enter DateTime in a more natural way. The user can also specify relative DateTime, which provides flexibility in DateTime input.

    • Cons: Parser may not be able to parse all natural language format due to ambiguity in language formats. There may also be a chance of parsing an invalid DateTime.

  • Alternative 2: Use standard DDMMYY HHMMSS format for all DateTime inputs.

    • Pros: Users will always be assured of correct DateTime being parsed if they provide the correct input.

    • Cons: Reduce the flexibility as users have to conform to a standard format.

3.7. Edit Command

3.7.1. Current Implementation

The edit command allows users to add edit existing events in the scheduler. Edit Command is facilitated by the EditCommand class. The format of this command is edit INDEX [n/EVENT_NAME] [s/START_DATETIME] [e/END_DATETIME] [d/DESCRIPTION] [v/VENUE] [rt/REPEAT_TYPE] [ru/REPEAT_UNTIL_DAETIME] [t/TAG]…​ [re/REMINDER_TIME]…​ [-a | -u]

EditCommand requires the interaction between both Logic and Model Component. The Logic component comprises of EditCommandParser and RepeatEventGenerator. The EditCommandParser parses the user input and RepeatEventGenerator generates repeating events (if any). The Model component is responsible for updating the internal event list with the generated event(s).

Given below is the execution flow of Edit Command.

  1. When the user inputs a command, LogicManager will call the parseCommand method in SchedulerParser with the user input as arguments.

  2. If the user input is valid and contains the keyword edit, EditCommandParser will be instantiated.

  3. SchedulerParser will then call the parse method of EditCommandParser.

  4. EditCommandParser parses the user input with the respective parsing methods.

  5. EditCommand will be instantiated with details of the edited event.

  6. generateAllRepeatedEvents method in RepeatEventGenerator is invoked to generate edited event(s) if user is trying to edit a set of repeating events with options -a or -u.

  7. LogicManager will then proceed to call the execute() method of EditCommand.

  8. Model Component will then find the targeted event(s) in internal list according to the parsed option type (no option or -a or -u).

  9. Model Component will replace the targeted event(s) with the edited event(s).

The following sequence diagram demonstrates how the add operation works:

EditCommandSequenceDiagram
EditCommandSequenceDiagram2

To find the targeted event(s) in Model(determined by the parsed option), we filter the internal event list using certain predicates.

The following code blocks shows the predicate used for -a and -u respectively.

versionedScheduler.updateEvents(target, editedEvents,
    event -> event.getEventSetUid().equals(target.getEventSetUid()));
versionedScheduler.updateEvents(target, editedEvents, event ->
    event.getEventSetUid().equals(target.getEventSetUid())
    && event.getStartDateTime().compareTo(target.getStartDateTime()) >= 0);

3.7.2. Design Considerations

Aspect: Generation of edited events
  • Alternative 1 (current choice): Generating edited events also targets first instance of event.

    • Pros: For -a option, we can generate all the repeating events starting from the first event in repeating event set even when the user targets the second event in the repeating event set.

      E.g. In a set of events: event A1, event A2 and eventA3, when we input the command edit 2 rt/weekly -a, we can check whether event A1 has the same day of week as eventA2. If yes, we generate new events starting from eventA1 else, start from eventA2.

    • Cons: New Events generated will always be based on the targeted event. So, when edit the eventA1 only first, then edit the whole set (all) of events starting from the eventA2, the subsequent events generated will be based on eventA2 which overrides any changes that were made individually to eventA1.

  • Alternative 2: Generating edited events based on selected event and delete previous events.

    • Pros: Do not need to be know what is in the model before generating events.

    • Cons: Events generated may not be as intuitive to users because the if we start editing a repeated event from the second event in a set, the first event will always be deleted.

3.8. Delete Command

3.8.1. Current Implementation

The delete command allows users to delete existing events in the scheduler. Delete Command is facilitated by the DeleteCommand class. The format of this command is delete INDEX [-a | -u].

DeleteCommand requires the interaction between both Logic and Model Component. The Logic component comprises of DeleteCommandParser. The DeleteCommandParser parses the user input. The Model component is responsible for deleting the internal event list(s).

Given below is the execution flow of Delete Command.

  1. When the user inputs a command, LogicManager will call the parseCommand method in SchedulerParser with the user input as arguments.

  2. If the user input is valid and contains the keyword edit, DeleteCommandParser will be instantiated.

  3. SchedulerParser will then call the parse method of EditCommandParser.

  4. DeleteCommandParser parses the user input with the respective parsing methods.

  5. EditCommand will be instantiated with index of the event to be deleted.

  6. LogicManager will then proceed to call the execute() method of DeleteCommand.

  7. Model Component will then find the targeted event(s) in internal list according to the parsed option type (no option or -a or -u).

  8. Model Component will delete the targeted event(s).

3.9. Reminder Operations

3.9.1. Current Implementation

The input is duration in the following format: [re/xxHxxMxxS] , meaning a reminder will pop up xxHxxMxxS before the event start time. Multiple unique reminders are allowed for one event.

The reminder operations are first implemented with the add and edit command as input fields. Commands addReminder, deleteReminder, postponeReminder are then implemented by inheriting editCommand class for more customised reminder operations. The differences in logic between three reminder commands and edit are explained below:

  • addReminder INDEX [/re xxHxxMxxS]…​[-a|-u]
    Add reminders INCREMENTALLY to the event, whereas edit will replace the reminders of the event by input durations.

  • deleteReminder INDEX [/re xxHxxMxxS]…​[-a|-u]
    Delete reminders if present. If edit is used for delete reminders, user will have to input all the reminders he/she wants to keep.

  • postponeReminder INDEX [/re xxHxxMxxS] [-a|-u]
    Postpone all reminders of the event by the duration specified.

Pop-up Alert feature requires the interaction between both 'Logic' and 'Model' Component as shown by the class diagram and explanation below. The class diagram only shows relevant classes for clarity.

ReminderClassDiagram

The main utility responsible for this feature is PopUpManager, which is implemented in a singleton pattern since it should have no more than just one instance.

Event has a field ReminderDurationList which is a set of Duration objects. A Duration such as 30m represents the period before the event start time that reminder will pop up. Therefore each Event object can have multiple unique Duration objects in ReminderDurationList, and thus multiple reminders.

Each Duration, together with the Event object itself, will be used by PopUpManager to generate 1 EventPopUpInfo object, which correspond to 1 reminder.

Except for Duration, EventPopUpInfo object also has a DateTime field (PopUpTime) calculated by Start Time - Duration. It is the time the corresponding reminder should popped up. The rest of the fields of EventPopUpInfo is the same as the Event object. Descriptive fields such as Description, Venue, EndTime are needed for display reminders. While the two IDs are used for PopUpManager 's various updating methods.

PopUpManager takes in Event object to generate EventPopUpInfo objects and then stores them in a PriorityQueue (PopUpQueue) order by PopUpTime. The startRunning() method in PopUpManager will run in parallel to constantly check when to pop an EventPopUpInfo and call PopUp in UI to display the reminder.

Every time a command is executed and handled by ModelManager, ModelManager will call relevant method in PopUpManager to update PopUpQueue. The sequence diagram below illustrate a generic case when user key in edit command. Some parts are omitted for clarity.

ReminderSequenceDiagram

Below is an example usage scenario with sequence diagram to illustrate how PopUpManager is updated

  1. The user has a set of recurring events: CS2103 lecture that happen every Friday 4pm - 6pm. He has set reminders to be 1H before the start time.

  2. Now is week 7 and the time for the lectures has just changed onwards. So he "edit 7 s/xxx 2pm e/xxx 4pm -u".

  3. When updateUpcomingEvents(eventToEdit, editedEvents) in ModelManager is called, this method will call editUpcoming(eventToEdit, editedEvents) in PopUpManager, which runs deleteUpcoming(..) and then add(..)

  4. deleteUpcoming will delete all EventPopUpInfo objects that share the same SetId with eventToEdit but have a later start time. Then add method will generate all editedEvents' corresponding 'EventPopUpInfo' and add them to PopUpQueue.

  • At any time, ONLY EventPopUpInfo with PopUpTime in the future will be added to the queue.

  • Any updates to the Scheduler will trigger corresponding updates in PopUpManager.

The updates in PopUpManager takes place on the main thread. However, the checking of PopUpQueue takes place on a parallel thread as illustrated by the activity diagram.

ReminderActivityDiagram

3.9.2. Design Considerations

  • Use Set to store ReminderDurationList (current choice)

    • Store the Durations in a Set. Do nothing to the Set when EventPopUpInfo is popped out. Every time adding EventPopUpInfo to PopUpQueue, only those with future PopUpDateTime will be added.

    • Pro: Easier implementation

    • Con: Will not be able to keep track of those reminders that have passed when the app is not open. Past reminders cannot pop up when the app opens.

  • Use HashMap to store ReminderDurationList (alternative)

    • Store as Duration: Boolean (if the reminder has popped up). Every time a reminder pops up, set to True.

    • Pro: Will be able to keep track of those reminders that have passed when the app is not open. When the user open the app, Duration with False will generate corresponding EventPopUpInfo to add to the queue. They will be popped up as past reminders to remind the users they might have miss some important events.

    • Con: This design will be complicated and affect undo and redo function.

3.10. Add/Delete/List Tags

3.10.1. Current Implementation

Current tag is implemented as a feature of an event instead of an independent object stored in local storage.

Similar to add event command, all tags are stored in local storage.

The addTag command allows the user to add new tags to the scheduler. Add Tag Command is facilitated by the AddTagCommand class. The format of this command is add [TAG_NAME].

AddTagCommand requires the interaction between both Logic and Model Component. The Logic component comprises of AddTagCommandParser. The AddTagCommandParser parses the user input and generates a tag. The Model component is responsible for updating the internal tag list.

Given below is the execution flow of Add Tag Command.

  1. When the user inputs a command, LogicManager will call the parseCommand method in SchedulerParser with the user input as arguments.

  2. If the user input is valid and contains the keyword addTag, AddTagCommandParser will be instantiated.

  3. SchedulerParser will then call the parse method of AddTagCommandParser.

  4. AddTagCommandParser parses the user input with the respective parsing methods.

  5. AddTagCommand will be instantiated with the list of tags.

  6. LogicManager will then proceed to call the execute command of AddTagCommand.

  7. Model Component will then add the list of tags to an internal list of tags using the addTags method.

3.10.2. Delete Tag Command

To be updated.

3.10.3. List Tag Command

To be updated.

{more to be updated}

3.11. Command Aliasing

3.11.1. Current Implementation

  • For the add command, typing add, ad or even simply a with the appropriate parameters can add an event.

  • Similarly, for the 'delete' command, typing one of the following — delete, delet, dele`, del, de or d with the appropriate parameters can delete an event.

  • Because of technical restrictions in programming, if there are multiple (2 or more) commands that start with the same alphabetical letter, for e.g. history and help, in that case:
    → We have chosen to prioritize help over the history command. It is as follows: history can be invoked with history, histor …​ all the way to hi. However, help can be called with help, he …​ h.

  • The same logic applies to the edit and exit command. → We have chosen to prioritize exit over the edit command. exit can be invoked with exit down to e while edit can be invoked down till ed with the appropriate parameters specified.

  • The alias(es) of each command go through the same logic as the actual command itself. They are parsed through the parseCommand() method in the SchedulerParser.java class.

3.11.2. Final notes:

  • The current implementation may not be the most efficient. We create constants of public static final String type and name them as COMMAND_ALIAS_ONE, COMMAND_ALIAS_TWO, etc…​ in the affected commands' respective classes.
    ⇒ A suggestion would be to create a suitable CommandAlias class and/or use data structures like enums, HashMap, TreeMap etc…​ for the command aliases.

  • An activity diagram (a swimlane diagram) showing how an alias of the history command would work is shown below:

HistoryCommandAliasActivityDiagram

Note: The above swimlane diagram is a simplified activity diagram which omits details of interactions with interfaces such as Model.java and/or other relevant classes.

3.12. Auto-Completion of Commands

3.12.1. Implementation

This feature is implemented would using the auto-complete feature available in the ControlsFX library. To be precise, the method TextFields.bindAutoCompletion() is invoked in the constructor of the CommandBox#Command class.

4. Documentation

We use AsciiDoc for writing documentation.

ℹ️
We chose AsciiDoc over Markdown because AsciiDoc, although a bit more complex than Markdown, provides more flexibility in formatting.

4.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.

4.2. Publishing Documentation

See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.

4.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 web pages.

Here are the steps to convert the project documentation files to PDF format.

  1. Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the docs/ directory to HTML format.

  2. Go to your generated HTML files in the build/docs folder, right click on them and select Open withGoogle Chrome.

  3. Within Chrome, click on the Print option in Chrome’s menu.

  4. Set the destination to Save as PDF, then click Save to save a copy of the file in PDF format. For best results, use the settings indicated in the screenshot below.

chrome save as pdf
Figure 10. Saving documentation as PDF files in Chrome

4.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.
Table 1. List of site-wide attributes
Attribute name Description Default value

site-name

The name of the website. If set, the name will be displayed near the top of the page.

not set

site-githuburl

URL to the site’s repository on GitHub. Setting this will add a "View on GitHub" link in the navigation bar.

not set

site-seedu

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

4.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.
Table 2. List of per-file attributes, excluding Asciidoctor’s built-in attributes
Attribute name Description Default value

site-section

Site section that the document belongs to. This will cause the associated item in the navigation bar to be highlighted. One of: UserGuide, DeveloperGuide, LearningOutcomes*, AboutUs, ContactUs

* Official SE-EDU projects only

not set

no-site-header

Set this attribute to remove the site navigation bar.

not set

4.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 docs/templates requires some knowledge and experience with Ruby and Asciidoctor’s API. You should only modify them if you need greater control over the site’s layout than what stylesheets can provide. The SE-EDU team does not provide support for modified template files.

5. Testing

5.1. Running Tests

There are three ways to run tests.

💡
The most reliable way to run tests is the 3rd one. 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 choose Run '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)

5.2. Types of tests

We have two types of tests:

  1. GUI Tests - These are tests involving the GUI. They include,

    1. System Tests that test the entire App by simulating user actions on the GUI. These are in the systemtests package.

    2. Unit tests that test the individual components. These are in seedu.scheduler.ui package.

  2. Non-GUI Tests - These are tests not involving the GUI. They include,

    1. Unit tests targeting the lowest level methods/classes.
      e.g. seedu.scheduler.commons.StringUtilTest

    2. Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
      e.g. seedu.scheduler.storage.StorageManagerTest

    3. Hybrids of unit and integration tests. These tests are checking multiple code units as well as how they are connected together.
      e.g. seedu.scheduler.logic.LogicManagerTest

5.3. Troubleshooting Testing

Problem: HelpWindowTest fails with a NullPointerException.

  • Reason: One of its dependencies, HelpWindow.html in src/main/resources/docs is missing.

  • Solution: Execute Gradle task processResources.

6. Dev Ops

6.1. Build Automation

See UsingGradle.adoc to learn how to use Gradle for build automation.

6.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.

6.3. Coverage Reporting

We use Coveralls to track the code coverage of our projects. See UsingCoveralls.adoc for more details.

6.4. Documentation Previews

When a pull request makes 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.

6.5. Making a Release

Here are the steps to create a new release.

  1. Update the version number in MainApp.java.

  2. Generate a JAR file using Gradle.

  3. Tag the repo with the version number. e.g. v0.1

  4. Create a new release using GitHub and upload the JAR file you created.

6.6. Managing Dependencies

A project often depends on third-party libraries. For example, Scheduler 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 these alternatives.

+ a. Include those libraries in the repo (this bloats the repo size)
b. Require developers to download those libraries manually (this creates extra work for developers)

Appendix A: Suggested Programming Tasks to Get Started

A suggested path for new programmers:

  1. First, add small local-impact (i.e. the impact of the change does not go beyond the component) enhancements to one component at a time. Some suggestions are given in Section A.1, “Improving each component”.

  2. Next, add a feature that touches multiple components to learn how to implement an end-to-end feature across all components. Section A.2, “Creating a new command: remark explains how to go about adding such a feature.

A.1. Improving each component

Each individual exercise in this section is component-based (i.e. you would not need to modify the other components to get it to work).

Logic component

Scenario: You are in charge of logic. During dogfooding, your team realizes that it is troublesome for the user to type the whole command in order to execute a command. Your team devises some strategies to help cut down the amount of typing necessary, and one of the suggestions was to implement aliases for the command words. Your job is to implement such aliases.

💡
Do take a look at Section 2.3, “Logic component” before attempting to modify the Logic component.
  1. Add a shorthand equivalent alias for each of the individual commands. For example, besides typing clear, the user can also type c to remove all events in the list.

    • Hints

    • Solution

      • Modify the switch statement in schedulerParser#parseCommand(String) such that both the proper command word and alias can be used to execute the same intended command.

      • Add new tests for each of the aliases that you have added.

      • Update the user guide to document the new aliases.

      • See this PR for the full solution.

Model component

Scenario: You are in charge of model. One day, the logic-in-charge approaches you for help. He wants to implement a command such that the user is able to remove a particular tag from everyone in the scheduler, but the model API does not support such a functionality at the moment. Your job is to implement an API method so that your teammate can use your API to implement his command.

💡
Do take a look at Section 2.4, “Model component” before attempting to modify the Model component.
  1. Add a removeTag(Tag) method. The specified tag will be removed from everyone in the scheduler.

    • Hints

      • The Model and the Scheduler API need to be updated.

      • Think about how you can use SLAP to design the method. Where should we place the main logic of deleting tags?

      • Find out which of the existing API methods in Scheduler and Event classes can be used to implement the tag removal logic. Scheduler allows you to update an event, and Event allows you to update the tags.

    • Solution

      • Implement a removeTag(Tag) method in Scheduler. Loop through each event, and remove the tag from each event.

      • Add a new API method deleteTag(Tag) in ModelManager. Your ModelManager should call Scheduler#removeTag(Tag).

      • Add new tests for each of the new public methods that you have added.

      • See this PR for the full solution.

Ui component

Scenario: You are in charge of ui. During a beta testing session, your team is observing how the users use your scheduler application. You realize that one of the users occasionally tries to delete non-existent tags from an event, because the tags all look the same visually, and the user got confused. Another user made a typing mistake in their command but did not realize they had done so because the error message wasn’t prominent enough. A third user keeps scrolling down the list because they keep forgetting the index of the last event in the list. Your job is to implementimprovements to the UI to solve all these problems.

💡
Do take a look at Section 2.2, “UI component” before attempting to modify the UI component.
  1. Use different colors for different tags inside event cards. For example, holidays tags can be all in brown, and appointments tags can be all in yellow.

    Before

    getting started ui tag before

    After

    getting started ui tag after
    • Hints

      • The tag labels are created inside the EventCard constructor (new Label(tag.tagName)). JavaFX’s Label class allows you to modify the style of each Label, such as changing its color.

      • Use the .css attribute -fx-background-color to add a color.

      • You may wish to modify DarkTheme.css to include some pre-defined colors using CSS, especially if you have experience with web-based CSS.

    • Solution

      • You can modify the existing test methods for EventCard to include testing the tag’s color as well.

      • See this PR for the full solution.

        • The PR uses the hash code of the tag names to generate a color. This is deliberately designed to ensure consistent colors each time the application runs. You may wish to expand on this design to include additional features, such as allowing users to set their own tag colors, and directly saving the colors to storage, so that tags retain their colors even if the hash code algorithm changes.

  2. Modify NewResultAvailableEvent such that ResultDisplay can show a different style on error (currently it shows the same regardless of errors).

    Before

    getting started ui result before

    After

    getting started ui result after
  3. Modify the StatusBarFooter to show the total number of events in the scheduler.

    Before

    getting started ui status before

    After

    getting started ui status after
    • Hints

      • StatusBarFooter.fxml will need a new StatusBar. Be sure to set the GridPane.columnIndex properly for each StatusBar to avoid misalignment!

      • StatusBarFooter needs to initialize the status bar on application start, and to update it accordingly whenever the scheduler is updated.

    • Solution

Storage component

Scenario: You are in charge of storage. For your next project milestone, your team plans to implement a new feature of saving the scheduler to the cloud. However, the current implementation of the application constantly saves the scheduler after the execution of each command, which is not ideal if the user is working on a limited internet connection. Your team decided that the application should instead save the changes to a temporary local backup file first, and only upload to the cloud after the user closes the application. Your job is to implement a backup API for the scheduler storage.

💡
Do take a look at Section 2.5, “Storage component” before attempting to modify the Storage component.
  1. Add a new method backupScheduler(ReadOnlyScheduler), so that the scheduler can be saved in a fixed temporary location.

A.2. Creating a new command: remark

By creating this command, you will get a chance to learn how to implement a feature end-to-end, touching all major components of the app.

Scenario: You are a software maintainer for scheduler, as the former developer team has moved on to new projects. The current users of your application have a list of new feature requests that they hope the software will eventually have. The most popular request is to allow adding additional comments/notes about a particular event, by providing a flexible remark field for each event, rather than relying on tags alone. After designing the specification for the remark command, you are convinced that this feature is worth implementing. Your job is to implement the remark command.

A.2.1. Description

Edits the remark for an event specified in the INDEX.
Format: remark INDEX r/[REMARK]

Examples:

  • remark 1 r/Anniversary.
    Edits the remark for the first event as Anniversary.

  • remark 1 r/
    Removes the remark for the first event

A.2.2. Step-by-step Instructions

[Step 1] Logic: Teach the app to accept 'remark' which does nothing

Let’s start by teaching the application how to parse a remark command. We will add the logic of remark later.

Main:

  1. Add a RemarkCommand that extends Command. Upon execution, it should just throw an Exception.

  2. Modify SchedulerParser to accept a RemarkCommand.

Tests:

  1. Add RemarkCommandTest that tests that execute() throws an Exception.

  2. Add new test method to SchedulerParserTest, which tests that typing "remark" returns an instance of RemarkCommand.

[Step 2] Logic: Teach the app to accept 'remark' arguments

Let’s teach the application to parse arguments that our remark command will accept. E.g. 1 r/Likes to drink coffee.

Main:

  1. Modify RemarkCommand to take in an Index and String and print those two parameters as the error message.

  2. Add RemarkCommandParser that knows how to parse two arguments, one index and one with prefix 'r/'.

  3. Modify SchedulerParser to use the newly implemented RemarkCommandParser.

Tests:

  1. Modify RemarkCommandTest to test the RemarkCommand#equals() method.

  2. Add RemarkCommandParserTest that tests different boundary values for RemarkCommandParser.

  3. Modify SchedulerParserTest to test that the correct command is generated according to the user input.

[Step 3] Ui: Add a placeholder for remark in EventCard

Let’s add a placeholder on all our EventCard s to display a remark for each event later.

Main:

  1. Add a Label with any random text inside EventListCard.fxml.

  2. Add FXML annotation in EventCard to tie the variable to the actual label.

Tests:

  1. Modify EventCardHandle so that future tests can read the contents of the remark label.

[Step 4] Model: Add Remark class

We have to properly encapsulate the remark in our Event class. Instead of just using a String, let’s follow the conventional class structure that the codebase already uses by adding a Remark class.

Main:

  1. Add Remark to model component (you can copy from Venue, remove the regex and change the names accordingly).

  2. Modify RemarkCommand to now take in a Remark instead of a String.

Tests:

  1. Add test for Remark, to test the Remark#equals() method.

[Step 5] Model: Modify Event to support a Remark field

Now we have the Remark class, we need to actually use it inside Event.

Main:

  1. Add getRemark() in Event.

  2. You may assume that the user will not be able to use the add and edit commands to modify the remarks field (i.e. the event will be created without a remark).

  3. Modify SampleSchedulerDataUtil to add remarks for the sample data (delete your scheduler.xml so that the application will load the sample data when you launch it.)

[Step 6] Storage: Add Remark field to XmlAdaptedEvent class

We now have Remark s for Event s, but they will be gone when we exit the application. Let’s modify XmlAdaptedEvent to include a Remark field so that it will be saved.

Main:

  1. Add a new XML field for Remark.

Tests:

  1. Fix invalidAndValidEventSheduler.xml, typicalEventsScheduler.xml, validScheduler.xml etc., such that the XML tests will not fail due to a missing <remark> element.

[Step 6b] Test: Add withRemark() for EventBuilder

Since Event can now have a Remark, we should add a helper method to EventBuilder, so that users are able to create remarks when building a Event.

Tests:

  1. Add a new method withRemark() for EventBuilder. This method will create a new Remark for the event that it is currently building.

  2. Try and use the method on any sample Event in TypicalEvents.

[Step 7] Ui: Connect Remark field to EventCard

Our remark label in EventCard is still a placeholder. Let’s bring it to life by binding it with the actual remark field.

Main:

  1. Modify EventCard's constructor to bind the Remark field to the `Event’s remark.

Tests:

  1. Modify GuiTestAssert#assertCardDisplaysEvent(…​) so that it will compare the now-functioning remark label.

[Step 8] Logic: Implement RemarkCommand#execute() logic

We now have everything set up…​ but we still can’t modify the remarks. Let’s finish it up by adding in actual logic for our remark command.

Main:

  1. Replace the logic in RemarkCommand#execute() (that currently just throws an Exception), with the actual logic to modify the remarks of an event.

Tests:

  1. Update RemarkCommandTest to test that the execute() logic works.

A.2.3. Full Solution

See this PR for the step-by-step solution.

Appendix B: Product Scope

Target user profile:

  • has a need to manage a significant number of events

  • prefer desktop apps over other types

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

Value proposition: manage events faster than a typical mouse/GUI driven app

Appendix C: User Stories

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

Priority As a …​ I want to …​ So that I can…​

* * *

university student

add time and event for an venue

locate my classroom at specific timings(s)

* * *

university student

search for an event

see the details for that specific event

* * *

university student

create repeated events

schedule repeated events easily

* * *

university student

see my academic timetable

know what modules I am taking and plan accordingly

* * *

organized university student

classify events into different categories

identify which event belongs to which category

* * *

busy university student

know what task I need to complete by a certain date

plan my schedule in an efficient manner

* * *

university student

create a new event

keep track of all my events

* * *

university student

delete an event

remove a canceled event

* * *

university student

update the event details

update the event if there is a change in event details

* * *

busy university student

type fewer words when I enter a command

save some time

* *

forgetful university student

receive email reminders for my events

be reminded of events even if I do not check the scheduler

* *

disorganized university student

notified if I create events that have a time conflict

know if my events have time clashes

* *

university student

view a filtered list of my events

prepare for my upcoming events according to the filter criteria

* *

disorganized university student

mark events which I attended or did not attend

keep track of my attended and unattended events

* *

university student

see attended events

keep track of events I attended

* *

university student

monitor and track deadlines

keep track of my upcoming deadlines

* *

popular university student

know my friends' contact

can contact them whenever I want

* *

organized university student

send event details to my friends

plan events together with my friends

*

goal oriented university student

view statistics of my attended events

know how many events I attended

*

university student

see all the public holidays

avoid planning certain events on a public holiday

*

university student

customize my profile

keep track of my profile and view it whenever I want

*

university student

obtain details of the teaching staff

clarify my questions with the teaching staff

*

university student

obtain details of a speaker easily

view their credentials

*

university student with private events

encrypt certain events

prevent others from obtaining details of certain events

*

busy university student

get the shortest path from current location to next

reduce my traveling time

*

university student who love to control things remotely

add events using email

schedule my events even when I am not using the application

*

university student who has a bad sense of direction

display the location of an event on a map

locate the venue of my event and not be lost

*

sleep deprived university student

record my estimated sleeping hours

monitor the number of hours I sleep at night

*

unmotivated university student

shown a random cat video

stay motivated to study

*

university student who likes to exercise

sync my fitness trackers to the application

monitor the number of steps I take

*

lazy university student

interact with scheduler using syntax closer to natural language

feel more interactive when using the application

*

university student

sync events with social media

share with my acquaintance about my events

*

university student who has poor eyesight

add events by voice

reduce reading and typing

*

university student who has poor eyesight

be able to hear events being read out

reduce my reliance on what is being displayed on the screen

*

university student who is used to the Google ecosystem

sync my scheduler with my own Google Calendar

be in sync with that calendar

Appendix D: Use Cases

(For all use cases below, the System is the Scheduler and the Actor is the user, unless specified otherwise)

Use case: Add event

MSS

  1. User requests to add a new event

  2. iScheduler Xs Max adds the new event(s) into the scheduler

  3. iScheduler Xs Max show a list of latest events

    Use case ends.

Extensions

  • 1a. The event to be added is repeated event

    • 1a1. Scheduler generates repeating events

Use case: Edit Event

MSS

  1. User requests to list events

  2. iScheduler Xs Max shows a list of events

  3. User requests to edit an event in the list

  4. iScheduler Xs Max edit(s) the targeted event(s)

    Use case ends.

Extensions

  • 1a. The user does not input any specific options

    • 1a1.iScheduler Xs Max targets only the targeted event

  • 1b. The user inputs a select all option

    • 1b1. iScheduler Xs Max target all associated events of the targeted event

  • 1c. The user inputs a select upcoming option

    • 1c1. iScheduler Xs Max target all upcoming and associated event of the targeted event

Use case: Delete Event

MSS

  1. User requests to list events

  2. iScheduler Xs Max shows a list of events

  3. User requests to delete an event in the list

  4. iScheduler Xs Max delete(s) the targeted event(s)

    Use case ends.

Extensions

  • 1a. The user does not input any specific options

    • 1a1.iScheduler Xs Max targets only the targeted event

  • 1b. The user inputs a select all option

    • 1b1. iScheduler Xs Max target all associated events of the targeted event

  • 1c. The user inputs a select upcoming option

    • 1c1. iScheduler Xs Max target all upcoming and associated event of the targeted event

Appendix E: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 9 or higher installed.

  2. Should be able to add events that repeat up to 100 times without any problem.

  3. Should be able to hold up to 1000 events without a noticeable sluggishness in performance for typical usage.

  4. 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.

  5. Should work on both 32-bit and 64-bit environments.

  6. Should work without any dependence of other software.

Appendix F: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Appendix G: Product Survey

Product Name

Author: …​

Pros:

  • …​

  • …​

Cons:

  • …​

  • …​

Appendix H: 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.

H.1. Launch and Shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file
      Expected: Shows the GUI with a set of sample events. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location are retained.

H.2. Adding an event

  1. Adds new event(s)

    1. Test case: add n/Study With Yoko s/tomorrow at 3pm d/Study again and again rt/monthly t/ad-hoc
      Expected: Adds an event with event name:'Study with Yoko', start date time:'tomorrow 3pm', description:'study again and again', repeat type:monthly, tag:ad-hoc

H.3. Editing an event

  1. Edits existing events

    1. Prerequisites: List all events using the list command. Multiple events in the list.

    2. Test case: edit 1 n/Dinner with Joy
      Expected: Edits the 1st event’s event name to Dinner with Joy.

H.4. Deleting an event

  1. Deleting an event while all events are listed

    1. Prerequisites: List all events using the list command. Multiple events in the list.

    2. Test case: delete 1
      Expected: The first event is deleted from the list. Event name of the deleted event shown in the status message.

    3. Test case: delete 0
      Expected: No event is deleted. Error details are shown in the status message.

    4. Other incorrect delete commands to try: delete, delete x (where x is larger than the list size)
      Expected: Similar to previous.

H.5. Saving data

  1. Dealing with missing/corrupted data files

    1. {explain how to simulate a missing/corrupted file and the expected behavior}

{ more test cases …​ }