By: NUS School of Computing AY1819S1 F10-1 Team      Since: Aug 2018      Licence: MIT

1. Introduction

1.1. What is OASIS?

OASIS is an employee management application that brings your office together. It is an all-in-one tool for office administration and communication that runs on any computer. With the use of primarily textual commands coupled with a beautiful user interface, you can get your administrative jobs done faster than with traditional point-and-click applications. Some of the main features that you can look forward to using are employee records management, authentication and permissions, assignment management, and leave management.

1.2. Architectural Overview

This document describes the design, implemented features and possible features that could be implemented in OASIS. These could serve as guidelines for future developers. Happy Programming!!

2. Setting up

It is time to start, follow the instructions below to get your developing environment ready.

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

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

2.3. Verifying the setup

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

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

  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.

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 se-edu/addressbook-level4 repo.

If you plan to develop this fork as a separate product (i.e. instead of contributing to se-edu/addressbook-level4), 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.

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,

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

  2. Take a look at Appendix A, User Stories.

3. Design

3.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: Initializes the components in the correct sequence, and connects them up with each other.

  • At shut down: Shuts down the components and invokes cleanup method where necessary.

Commons represents a collection of classes used by multiple other components. 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.

SDforDeletePerson
Figure 3. Component interactions for delete 1 command (part 1)
Note how the Model simply raises a AddressBookChangedEvent when the Address Book 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.

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

3.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, PersonListPanel, 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 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.

3.3. Logic component

LogicClassDiagram
Figure 6. Structure of the Logic Component

API : Logic.java

  1. Logic uses the AddressBookParser 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 a person) 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.

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

3.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 Address Book data.

  • exposes an unmodifiable ObservableList<Person> 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.

3.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 Address Book data in xml format and read it back.

3.6. Common classes

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

4. Implementation

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

4.1. Undo/Redo feature

4.1.1. Current Implementation

The undo/redo mechanism is facilitated by VersionedAddressBook. It extends AddressBook with an undo/redo history, stored internally as an addressBookStateList and currentStatePointer. Additionally, it implements the following operations:

  • VersionedAddressBook#commit() — Saves the current address book state in its history.

  • VersionedAddressBook#undo() — Restores the previous address book state from its history.

  • VersionedAddressBook#redo() — Restores a previously undone address book state from its history.

These operations are exposed in the Model interface as Model#commitAddressBook(), Model#undoAddressBook() and Model#redoAddressBook() 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 VersionedAddressBook will be initialized with the initial address book state, and the currentStatePointer pointing to that single address book state.

UndoRedoStartingStateListDiagram

Step 2. The user executes delete 5 command to delete the 5th person in the address book. The delete command calls Model#commitAddressBook(), causing the modified state of the address book after the delete 5 command executes to be saved in the addressBookStateList, and the currentStatePointer is shifted to the newly inserted address book state.

UndoRedoNewCommand1StateListDiagram

Step 3. The user executes add n/David …​ to add a new person. The add command also calls Model#commitAddressBook(), causing another modified address book state to be saved into the addressBookStateList.

UndoRedoNewCommand2StateListDiagram
If a command fails its execution, it will not call Model#commitAddressBook(), so the address book state will not be saved into the addressBookStateList.

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

UndoRedoExecuteUndoStateListDiagram
If the currentStatePointer is at index 0, pointing to the initial address book state, then there are no previous address book states to restore. The undo command uses Model#canUndoAddressBook() 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#redoAddressBook(), which shifts the currentStatePointer once to the right, pointing to the previously undone state, and restores the address book to that state.

If the currentStatePointer is at index addressBookStateList.size() - 1, pointing to the latest address book state, then there are no undone address book states to restore. The redo command uses Model#canRedoAddressBook() 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 address book, such as list, will usually not call Model#commitAddressBook(), Model#undoAddressBook() or Model#redoAddressBook(). Thus, the addressBookStateList remains unchanged.

UndoRedoNewCommand3StateListDiagram

Step 6. The user executes clear, which calls Model#commitAddressBook(). Since the currentStatePointer is not pointing at the end of the addressBookStateList, all address book 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.

UndoRedoNewCommand4StateListDiagram

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

UndoRedoActivityDiagram

4.1.2. Design Considerations

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

    • 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 person being deleted).

    • Cons: We must ensure that the implementation of each individual command are correct.

Aspect: Data structure to support the undo/redo commands
  • Alternative 1 (current choice): Use a list to store the history of address book 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 VersionedAddressBook.

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

4.2. Command Auto Complete

When user type in the command box, OASIS will predict what commands the user is going to run, and display a drop down list containing all suggestions.

4.2.1. Current Implementation

Auto complete functionality in OASIS is supported by both org.controlsfx.control.textfield.TextFields API and AutoCompleteCommandHelper class.

When text is typed into the Command Box (commandTextField object), it will display a drop down list of possible commands, that is retrieved from AutoCompleteCommandHelper class.

  • org.controlsfx.control.textfield.TextFields API is utilised to display the drop down list.

  • AutoCompleteCommandHelper class is used to generate the Set of possible commands with the input given in CommandBox.

Aspect: Logic & UI

When CommandBox class is being constructed, bindAutoCompletion method provided by TextFields API will be utilised to create an auto-completion binding between the commandTextField object and AutoCompleteCommandHelper#autoCompleteWord method.

With this binding, whenever commandTextField is updated, it will display a drop down list of possible commands retrieved from AutoCompleteCommandHelper#autoCompleteWord if it exist.

Design Considerations
  • Alternative 1 (Current Implementation): Make use of TextFields API

    • Pros: Easy to implement.

    • Pros: Can view ALL possible commands.

    • Cons: There is a slight delay before the drop down list appear. Can potentially slow down users who type fast.

  • Alternative 2: Immediately place predicted command as text into commandTextField.

    • Pros: No delay, predicted command is immediately displayed.

    • Cons: Only able to view 1 possible command.

4.3. Permission System

There are several commands in OASIS that should not be executable by every user. E.g. Add and Delete commands should only be usable by user with the power to hire and dismiss other employees. Permission system is used to ensure that each user are only able to perform commands that they are authorised to when using OASIS.

4.3.1. Current implementation

Aspect: Model

Model of a person have been changed to reflect the permission that each user possesses.

The class highlighted in Red in the following diagram represents the classes that have been created to support the Permission system.

permissionPersonModel
  • Permission class contains the all possible Permissions that are available to a Person.

  • All values in PermissionSet must be from Permission class.

  • PresetPermission is a enumeration class that resides in PermissionSet. PresetPermission is utilised by PermissionSet class to generate a PermissionSet object that represents the set of permission that a certain type of User will possess.

As of v1.4, PresetPermission only contains the following preset: Admin, Manager and Employee.
Aspect: Storage

Permission have to be stored in the addressbook where the information for Person is stored. This is achieved through creation of XmlAdaptedPermission, which was utilised by XmlAdaptedPerson to store the information in an xml file.

The class highlighted in Red in the following diagram represents the classes that have been created to support the Permission system.

permissionStorage
Aspect: Logic

Commands will be required to populate a requiredPermission:PermissionSet object with all Permission the command requires user to have to execute the command.

The following is an example on how to assign permission to a Command.

AddCommand.java
public AddCommand(Person person) {
    requireNonNull(person);
    requiredPermission.addPermissions(Permission.ADD_EMPLOYEE); (1)
    toAdd = person;
}
1 The method to assign permission to a command

The code to ensure that each command is only executed by user with the correct permission is located in Command#execute.

When any command executes, the command will first check if the logged in user possess the correct authorization by comparing requiredPermission with the user’s permissionSet object, before performing the command.

Given below is an example scenario of how commands will be executed.

Step 1. The user enters a command delete 1 into the CLI.

Step 2. The system retrieves current user’s PermissionSet

Step 3. The system compares user’s PermissionSet with DeleteCommand 's requiredPermission.

  • Two different cases

    • User have required permissions, execute command.

    • User don’t have required permissions, show error message.

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

permissionCommandActivityDiagram

4.3.2. Design Considerations

  • Alternative 1 (Current Implementation): Assign permission to each individual user and restrict commands executable by user based on permission assigned.

    • Pros: Easy to control the commands a user can access.

    • Cons: Need to ensure that there is at least 1 user that can assign permissions to other users. Implementation requires knowledge of multiple components of OASIS.

  • Alternative 2: Create subclass of Person to be used to identify the role of the user. E.g. Employee and Manager class. The commands executable by the user will depend on their class.

    • Pros: Easy to implement. Only require small modification in existing classes.

    • Cons: Commands cannot be freely assigned to users as it is now dependent on which subclass the user is. E.g. we cannot create an Employee with a subset of the commands available to Manager.

4.4. Supporting commands for Permission System

The following are commands that have been implemented to support the Permission System.

4.4.1. Modify Permissions of employee

This feature allows the user to change the Permission that have been allocated to an employee.

This feature can only be performed by users that have ASSIGN_PERMISSION permission.
Current Implementation

This feature allows the user to indicate what permission to add and remove based on the prefix.

  • -a PERMISSION_TO_ADD to add permission

  • -r PERMISSION_TO_REMOVE to remove permission

Aspect: Logic

To implement this new command syntax, ModifyPermissionCommandParser utilises ArgumentTokenizer#tokenize to generate a ArgumentMultiMap. The ArgumentMultiMap 's key contains the prefix, and value contains the list of keywords that succeeded the prefix. There will also be a preamble which is used to retrieve the index of the targeted employee.

All the keywords is then added to either permissionToAdd:Set<Permission> or permissionToRemove:Set<Permission> depending on their prefix. The 2 sets, together with the index, will be then be used to create ModifyPermissionCommand.

A ParseException will be thrown if any of the permission names are invalid.

When ModifyPermissionCommand is executed, it will then modify the permission of targeted employee, adding permission from permissionToAdd and removing permissions from permissionToRemove.

The command will be executed successfully if at least one permission is added or removed.

The following is a sequence diagram that visualizes how this operation works.

modifyPermissionSequenceDiagram
Design Considerations
  • Alternative 1 (Current Implementation): Allow both adding and removing of multiple permissions with one command with the use of prefixes.

    • Pros: Only need to learn how to use 1 command. Can perform both adding and removing of permissions with a single command.

    • Cons: Harder to implement then other alternatives.

  • Alternative 2: Create 2 separate commands to handle adding and removing of permission. (E.g. AddPermissionCommand and RemovePermissionCommand)

    • Pros: Easy to implement.

    • Cons: User will have to remember 2 commands. In addition to this, User also have to execute at least 2 commands if they wish to both add and remove permission.

4.4.2. View Permissions of employee

This feature allows the user to view the permissions that have been allocated to an employee.

This feature can only be performed by users that have ASSIGN_PERMISSION permission.
Current Implementation

To use this command, the user only have to give an INDEX parameter.

Aspect: Logic

To process this command, ViewPermissionCommandParser simply utilise ParserUtil#parseIndex to parse the argument into the index of the target employee.

The index will then be used to create a ViewPermissionCommand.

When ViewPermissionCommand is executed, it will retrieve the targeted employee from the Model, and print out the list of permission that the employee possesses.

The following is a sequence diagram that visualizes how this operation works.

viewPermissionSequenceDiagram
Design Considerations
  • Alternative 1 (Current Implementation): Display permissions in the command box result text field.

    • Pros: Easy to implement.

    • Cons: Can only view permissions of a single employee at a time.

  • Alternative 2: Display all permissions in the Person card.

    • Pros: Able to view all permissions, of all employee in one go.

    • Pros: No commands required.

    • Cons: Person card can get very cluttered if employee have many permissions.

4.5. Command Shortcuts

With various keyboard combinations user will be able to access command keywords quickly.

4.5.1. Current Implementation

Keyboard shortcuts in OASIS is implemented using KeyEvent handler within the CommandBox class.

When valid key combination is pressed, the text in the command box will be replaced with the corresponding command word.

4.5.2. Aspect: Logic & UI

Within the CommandBox class, there is KeyEvent handler method which handles keyboard’s input. If the key pressed is a valid combination, then the text field in command box will be replaced with the command corresponding to one of the existing commands.

4.5.3. Design Consideration

Current Implementation: Use the KeyEvent handler in the CommandBox class.

  • Pros: It is easy and simple to implement.

  • Cons: As the number of commands increase, there might not be enough key combination to accommodate all the commands.

4.6. Assignment Management

There are four commands related to assignments. The four commands are addassignment, listassignments, deleteassignment and editassignment.

These commands could only be executed by user with the appropriate permission.

4.6.1. Current Implementation

An assignment is represented by Assignment model object. Assignment object stores the information of AssignmentName, Author and Description.

The following diagram shows the classes to model Assignment:

AssignmentModel
Add Assignment

The following sequence diagram shows how adding an assignment works:

AddAssignmentSequenceDiagram

Below is an example on how addassignment mechanism behaves when a new assignment is added.

  1. When user executes addassignment -au ASSIGNMENTNAME -au AUTHOR -de DESCRIPTION command, it will be parsed, then new Assignment object will be initialized. If the current user does not have the permission to add new assignment, error message will be thrown.

  2. If user has the required permission, then the current Assignment object will be compared to the existing assignments in the system with hasAssignment(toAdd) method. If the new object is unique, then it will be added into the system. However, if there is duplicate, the system will throw DuplicateAssignmentException.

  3. The new Assignment will then be copied and transformed to become XmlAdaptedAssignment object, which is then saved into a file by the Storage component.

Delete Assignment
Deleting an assignment form the system will remove it permanently!

The following sequence diagram shows how deleting an assignment works:

DeleteAssignmentSequenceDiagram

Given is an example of how deleting an assignment mechanism behaves when it is executed.

  1. When user executes the deleteassignment INDEX command, DeleteAssignmentCommand object will be created. It then checks if the current user has the required permission to execute the command. If the user does not have the required permission, then and error message will be displayed.

  2. Meanwhile, if the user has the required permission, system gets the Assignment object to be deleted from list of assignments and finally delete it. It will then update the list in the Storage component.

List Assignment

The parsing sequence of listing all assignments is similar to adding and deleting an assignment. In addition, there is no permission required to run listassignment command.

When user executes listassignment command, system will retrieve all assignment data in the Storage component using updateFilteredAssignmentList() and update the User Interface to list all the assignments.

ListAssignmentCommand.java
public CommandResult runBody(Model model, CommandHistory history) {
        requireNonNull(model);
        model.updateFilteredAssignmentList(PREDICATE_SHOW_ALL_ASSIGNMENTS);(1)
        EventsCenter.getInstance().post(new AssignmentListEvent());
        return new CommandResult(MESSAGE_SUCCESS);
    }
1 The method to retrieve all assignments.
Edit Assignment

Parsing the command when executing editassignment is similar to deleteassignment command.

  1. When user executes editassignment 1 -an ASSIGNMENTNAME command, the command will be parsed. The given prefix and field indicate that the corresponding field of Assignment should be edited with the new field. To do so, EditAssignmentDescriptor object will be produce. This object has the newly edited Assignment object.

  2. After, the 'Permission' of the user will be check. If the user does not have the appropriate permission, error message will be displayed.

  3. While if the user has the appropriate permission, the old Assignment object will be replaced with the new Assignment object.

  4. The list of the assignments will then be updated in the Storage.

4.6.2. Design Consideration

Aspect: How the Assignment object is stored
  • Alternative 1 (Current Implementation): store assignment information in Xml file.

    • Pros: Xml file has extensibility, as it has no fixed set of tags. Allowing future developers to enhance the information of the assignment.

    • Cons: Inefficient retrieval of information of the assignment when the storage size gets too big.

  • Alternative 2: store the assignment information using database system.

    • Pros: Fast and efficient retrieval of information, even when the amount of data is massive.

    • Cons: Separated system needs to be set up to store information. Thus, might result in additional cost.

4.7. Archive employee feature

Employees with the "DELETE_EMPLOYEE" permissions can delete employees in the system - related to firing employees in real life. Deleted employees from the active list will be moved to the archived list. This feature allows an employee to view the archive list of employees.

4.7.1. Proposed implementation

The archive employee feature is facilitated by VersionedArchiveList, VersionedAddressBook and UniquePersonList.

Given below is an example usage scenario and how the VersionedArchiveList, VersionedAddressBook and UniquePersonList behaves at each step.

Step 1. The user launches the application for the first time. The VersionedArchiveList and VerisionedAddressBook will be initialized with the initial archive list and address book state. The UniquePersonList in both classes will be populated with data from read by storage.

Step 2. The user executes delete 3 command on the active list to delete the 3rd person in the active list. The selected Person object from the UniquePersonList in VersionedAddressBook will be transferred to the UniquePersonList in VersionedArchiveList and store the deleted Person’s data.

Step 3. The user executes archive command to change view from active list to the archive list.

Step 4. OASIS shows delete person in step 2 in the archive list on the left panel.

The following activity diagram summarizes what happens when a user executes delete and restore command:

archiveActivityDiagram

4.8. Changes to architecture

This section documents the changes made to the architecture and its components.

Aspect: Model

Added a VersionedArchiveList object. UniquePersonList in VersionedArchiveList will store 0 or more Person objects.

The following diagram shows the class VersionedArchiveList added to reflect the changes in the Model component:

modelChangeJosh
Aspect: Storage

Added ArchiveListStorage interface, XmlArchiveListStorage and XmlSerializableArchiveList classes to Storage component.

The following diagram shows the interface and classes added to the Storage component:

storageModelChangeJosh

4.8.3. Design considerations

This section documents the considerations that were carefully studied for implementing this feature. The selected implementation method is denoted by "(current choice)" .

Aspect: Lifetime of objects in Archive list
  • Alternative 1 (current choice): Deleted permanently after being removed by user again.

    • Pros: Guaranteed no loss of data if an employee is accidentally deleted.

    • Cons: May have performance issues in terms of memory usage as employee records stored a few years back could still be stored.

  • Alternative 2: Deleted after a certain number of time has passed.

    • Pros: More efficient memory usage wont store old employee records which could cause high memory usage.

    • Cons: Loss of data possible if an employee is accidentally deleted and not restored right away.

Aspect: Data structure to support the archive commands
  • Alternative 1 (current choice): Use a list to store the archived employee objects.

    • Pros: Pros: Easy to implement. Only require small modification in existing classes. Faster access to archive list as the user doesn’t need to search every employee in the system to get the employees archived.

    • Cons: We must maintain a separate list for archived objects.

  • Alternative 2: Assign an archive attribute to each employee object in the active list and show only in the active list if archived attribute is false. In contrast show in the archive display list if archive attribute is true.

    • Pros: Only need to change 1 attribute when an employee is deleted.

    • Cons: "Archive" is an unusual attribute for a person and it will be time consuming to view the archive list as you must go through all employees to check the archive attribute.

4.9. Restore employee feature

Employees with the "RESTORE_EMPLOYEE" permissions can restore employees from the archive list into the active list - related to hiring back old employees in real life. The restored employee will be moved back to the active list and removed from the archive list. Employees with “DELETE_EMPLOYEE” permissions can also completely remove employees in the archive list, permanently removing them from OASIS.

4.9.1. Proposed implementation

The Restore or completely remove employee feature is facilitated by VersionedArchiveList, VersionedAddressBook and UniquePersonList.

Given below is an example usage scenario and how the VersionedArchiveList, VersionedAddressBook and UniquePersonList behaves at each step.

Step 1. The user launches the application for the first time. The VersionedArchiveList and VersionedAddressBook will be initialized with the initial archive list and address book state. The UniquePersonList in both classes will be populated with data from read by storage.

Step 2. The user executes archive command to change view from active list to the archive list.

Step 3a1. The user executes restore 1 command to restore the first person in the archive list. The selected Person object from the UniquePersonList in VersionedArchiveList will be transferred to the UniquePersonList in VersionedAddressBook and be back in the active list.

Step 3a2. The user executes list command to change view from archive list to active list.

Step 3a3. The employee restored in Step 3a1 is shown in active list

Step 3b1. The user executes delete 1 command to completely remove the first person in the archive list from OASIS. The selected Person object from the UniquePersonList in VersionedArchiveList will be deleted.

Step3b2. Archive list updated and deleted employee is removed.

The following activity diagram summarizes what happens when a user executes remove and restore command:

archiveActivityDiagram

The following sequence diagram shows the method calls when the restore command is executed:

restoreSequenceDiagram

4.9.2. Design considerations

This section documents the considerations that were carefully studied for implementing this feature. The selected implementation method is denoted by "(current choice)" .

Aspect: Data restored from employee
  • Alternative 1 (current choice): All data from the time when the employee was deleted will be restored when the employee object is restored.

    • Pros: Employee being restored wont lose any important information and skips the task of having to key in all the employee’s information again.

    • Cons: Employee may have changed job scopes and permissions assigned may be different, this could lead to big problems if employees have permissions they are not supposed to have.

  • Alternative 2: Only certain information like name and phone number will be restored when employee is restored.

    • Pros: More efficient memory usage, don’t have to store other information about the employee. Restored employees will not have the same permissions they had before which could save potential problems of employees having permissions they are not supposed to have.

    • Cons: Could potentially add more workload to the user restoring the employee as the user will need to fill in all necessary details of the employee when the restore command is executed.

4.10. Login/Logout feature

4.10.1. Current Implementation

The Login/Logout feature is facilitated through the use of creating a login screen before the application begins, ensuring that the user starts by logging into his account.

These operations are exposed in the MainWindow class through fillLoginParts(), removeLoginWindow(), removeInnerElements(), processLogin(LoginEvent) and processLogout(LogoutEvent)

While the login screen is displayed, other usual UI elements, such as the browserPanel, PersonListPanel, ResultDisplay, StatusBarFooter, CommandBox are not initialized at all, so they cannot be accessed.
To ensure that most tests still work with a login system, the MainWindowHandle, used by all GUI tests, automatically logs the user in right after the UI element loads.

The following sequence diagram shows a high level overview between the components when a User begins logging into OASIS.

LoginSequenceDiagram
Figure 10. A high level overview of how components interact when a login is performed.

This situation branches if the user’s input of username and password is invalid. The following activity diagram shows the branching case:

LoginActivityDiagram
Figure 11. An activity diagram showing where the login code branches if the input does not match a user.

The communication between UI, Logic and Model is mainly from using EventsCenter and firing events to tell the other components to act. The following detailed steps show how the program works as the user login.:

  1. When the program is started, UIManager creates the MainWindow and tells it to fillLoginParts().

  2. The user enters his details, and clicks login.

  3. This causes the LoginForm to fire a LoginEvent onto the central EventBus, with the username and password saved into the LoginEvent.

  4. The Logic Manager catches the LoginEvent. It then checks if the username and password combination matches a person in the system, or the admin user. To do so, it communicates with the model to retrieve everyone in the system.

    1. If there is no successful match, then a FailedLoginEvent is fired. The LoginForm catches this Event and displays the error message provided by the FailedLoginEvent.

  5. If there is a successful match, then a SuccessfulLoginEvent is fired. This event contains the person that is currently logging in, wrapped in a User object.

  6. The mainWindow class catches the SuccessfulLoginEvent and processes it, removing the login UI Elements and replacing it with fillInnerParts()

When the user wishes to logout, he enters logout, which triggers the following:

  1. The LogoutEvent is fired by the LogoutCommand.

  2. The mainWindow class catches the LogoutCommand and processes it, removing the main UI elements and replacing it with the fillLoginParts()

4.10.2. Design Considerations

Aspect: How the login screen is displayed

The login screen needs to be displayed to the user in some fashion.

  • Alternative 1 (current choice): Create a login screen before initializing other UI elements on the fly.

    • Pros: One single window. Clear to the user which window to focus on. Most applications work this way, so it should be familiar to the majority of our users.

    • Cons: Harder to implement. Need to take into account other possible UI elements, preload only those that are required, and ensure that tests stay supported.

  • Alternative 2: Build another UI Window just for login. Before logging in, this window will popup. Once the user has logged in, the login window will close and the main window will pop up.

    • Pros: Far easier to implement. Login system abstracted away from other functionality.

    • Cons: It will be hard to maintain the same window size as the login window, if the user resizes it. More coupling would be required to maintain the same window size. Very odd and unfamiliar to most users. No application today opens a login window, then on successful login, closes that login window and opens a new one, meant for the user to use. This can cause a lot of user confusion. They may think that:

      • The new window is representing error message, it should not have opened.

      • The application had an error and unexpectedly shutdown.

      • The new window is from another application that the user has running on his computer.

      • They did something wrong (perhaps they pressed the button to close the window instead?)

Aspect: UI Elements to build the login system

When OASIS boots up, the login screen needs to be displayed. The UI elements used to build this login screen needs to be decided on where they should go on the screen.

  • Alternative 1 (current choice): Using the same placeholders already available, place the appropriate UI elements on the screen.

    • Pros: Easy to implement. Utilizes the same placeholders currently in the system, so will adapt the same way to window re-sizing.

    • Cons: Looks uglier than if the window was created solely to enter login data

  • Alternative 2: Build the window from scratch to show login UI elements.

    • Pros: Nicer, the UI elements are built for login

    • Cons: Harder to implement. Need a good graphic designer to plan out how said nice login screen would look like, otherwise it’d just look bad and you might as well go with Alternative 1.

Aspect: How the UI and Logic elements should communicate.

Whenever a login is done by the User, the UI, Logic and Model elements need to communicate to handle the event.

  • The UI needs to provide the User Input information.

  • The Logic needs to perform the check of whether this is a valid Username and Password combination.

  • The Model needs to provide the data for the logic to do it’s work.

There needs to be a solution to handle this cleanly and without causing unnecessary coupling, as this will likely be required to be extended upon in the long run.

  • Alternative 1 (current choice): Utilize the EventBus to allow UI, Model and Logic to communicate

    • Pros: Reduces coupling, as UI, Model and Logic doesn’t need to know about each other. If required, other classes can also listen for the Event and process accordingly

    • Cons: Needs some work to implement. Requires building up new classes.

  • Alternative 2: Let UI, Logic and Model know about each other, allowing them to call the relevant methods and do the relevant checks

    • Pros: Easy to implement.

    • Cons: Grealy increases coupling. Not a good design decision, as it will make it harder to maintain the code in the future.

  • Alternative 3: Build a command like system (similar to how Commands are implemented in the system) for UI to talk to Logic. Logic then uses the Command system to reply back to UI.

    • Pros: Reduces the amount of coupling added into the system. Provides a way for UI to get Logic to do things, in case more functionality is added that uses UI input.

    • Cons: Very time consuming to implement. Hard to design as there is no clear functionality that might also need this system in the future.

4.10.3. Username and Password storage

To store username and password, the class Person has been extended to include a Username and Password variable as well. These two variables represent the Username and Password stored in the system for that Person.

4.10.4. Admin account

There is a possibility that the entire system is cleared of all employees (i.e when initializing, or an accidental deleting of all employees). To resolve this issue, an admin account is added that will ensure that there is always a user that can login in. The admin account cannot be removed and always has full access rights. By default, the username of the admin account is Admin and the password is Pa55w0rd.

The password of the admin account can be modified through the passwd command.

4.10.5. Design Considerations

Aspect: Where the admin account password can be stored.

Since the admin account would cause the system to be very insecure if it’s password couldn’t be changed, the admin account password must be changeable and stored somewhere, so that it persists across sessions. But where?

  • Alternative 1 (current choice): Place the storage in User Preferences

    • Pros: Easy to implement. A nice, centralized place to store general application information.

    • Cons: If the file is deleted, then the password will revert back to the default, which leaves the admin account vulnerable.

    • As we plan to store the system in a central server, the admin account’s information will be stored in the server, and not locally. As this is intended to change before the final release, we went for the option that is the simplest to implement.

  • Alternative 2: Add it into the address book.xml file

    • Pros: If the password was attempted to be removed through the deletion of the file, then this will also delete everyone in the system as well, thus rendering the access to the admin account useless.

    • Cons: Since the file is stored in an xml format, it is easy for any dedicated attacker to find and remove the password information, reverting it to the default. Harder to implement, as would require large changes in the address book parser.

  • Alternative 3: Store it within environment variables

    • Pros: Somewhat harder to find. Ensures that admin password remains even when the data files are deleted.

    • Cons: The admin password would not transfer over systems for the average user. It would be difficult and require technical knowledge of the user to get it to transfer.

4.10.6. passwd Command

To change the password, a passwd command is required. However, this command is very different from the other commands. Specifically, it requires a chain of input, and subsequent inputs from the user should not be stored in history (otherwise the user’s password are easily retrievable).

This is, however, not easy to handle. This command history is automatically populated by LogicManager, which Command.execute does not have access to. To complicate things further, CommandParser doesn’t handle exceptions like storing a command for future use, or redirecting user input to a specific Command.

To resolve this issue, CommandResult is extended to hold interceptors - a list of ProcessCommand. ProcessCommand is a functional interface, similar to Function except that it can throw a specific error as well. To implement passwd, it returns an implementor of ProcessCommand to CommandResult, which forwards it to LogicManager.

LogicManager now accepts ProcessCommand from CommandResult, adding them to a list. As long as there exists at least one ProcessCommand, further messages are pushed to ProcessCommand instead of processed normally, and they aren’t added to history.

Other possible design considerations are shown below.

4.10.7. Design Considerations

Aspect: How to implement the passwd Command
  • Alternative 1 (current choice): Build a foundation of ProcessCommand that will intercept user input and process it instead. If it does so, LogicManager doesn’t add the command to history.

    • Pros: Doesn’t increase coupling unnecessarily. Allows other functions to utilize this, allowing other commands to also easily extend to a chain of user inputs.

    • Cons: Harder to implement. Somewhat hard to understand, as it requires knowledge of lambdas and functional interfaces.

  • Alternative 2: Apply a hack for passwd where LogicManager checks that if a passwd command is ongoing, it redirects there instead.

    • Pros: Easy to implement.

    • Cons: Greatly increases coupling. If further commands were to require the same functionality, this hack would need to be done again for that function.

4.10.8. Password Security

There is a need to have passwords be stored securely as opposed to being stored in plain text. The standard today is to have password be salted and hashed, which is also the standard that OASIS is implemented in.

All of this is handled within the Password class. API: Password.java

The Password class stores the password in plaintext (if available), the salt and the hash of the password. The salt and the hash are always available.

If the password object was created this session (i.e. the user changed his password this session), the plaintext will be available. Otherwise, if it was read from the saved XML file, then only the salt and hash are available.

When checking if 2 Passwords are the same, it is better to call isSamePassword to verify if the 2 passwords are the same. isSamePassword checks if both passwords would have had the same plaintext. Utilizing .equals would also require the salt to match as well.

4.10.9. Design Considerations

Aspect: How much security is required for the password
  • Alternative 1: Just store and save the password in plaintext.

    • Pros: Very easy to implement. Makes testing significantly easier. Can verify both appropriate input and output.

    • Cons: No security. Anyone who wants to view someone else’s password can just look into the data files and find it easily.

  • Alternative 2: Apply a hash to all passwords (i.e. SHA512).

    • Pros: Still somewhat easy to implement.

    • Cons: Testing becomes slightly harder as test code can no longer rely on getting the password in plaintext. Still not very secure, rainbowtables and hash crackers exist that can easily get back the plaintext password.

  • Alternative 3: (current choice): Apply a salt + hash to all passwords

    • Pros: Full security suite. Ensures that we aren’t storing our user’s passwords, so if the database is compromised, our user’s passwords aren’t easily broken.

    • Cons: Very hard to implement. Testing becomes significantly harder because calling .equals on Passwords that are the same may return false due to differences in salt.

4.11. Leave Application

In OASIS, an employee can make a leave application by specifying a description, as well as the dates, that he or she wishes to apply for. Users are also able to view a list of all their own leave applications and their details, while those with the required permissions have the added ability to view all leave applications of all employees, as well as approve or reject leave applications.

4.11.1. Current Implementation

A leave application is represented by a LeaveApplication model object, as follows:

LeaveApplicationModelDiagram
Figure 12. Structure of a leave application

API: LeaveApplication.java A leave applications contains a description, status (PENDING, APPROVED, or REJECTED), and one or more dates.

The LeaveApplication constructor removes duplicates, and orders all dates in ascending order.
Applying for leave

This section describes and illustrates how an application for leave by a user works in OASIS.

The user issues a leaveapply command, which includes a description and one or more dates, in the command box UI. The Logic Manager in the Logic Component is then called upon to execute the command. The Model is then updated with the changes, and finally the EventsCenter is notified of this, and it goes on to ask the Storage Component to update the stored file data. This component-level interaction is depicted as follows:

LeaveApplicationSequenceDiagramHigherLevel
Figure 13. Component interactions for leaveapply -de family holiday -da 2018-11-11 command
There are further interactions of the EventCenter reacting to the event raised with the Storage Component, but they are ommitted.

Now, we zoom in on the Logic Component to have a more detailed look as to how the leaveapply command is handled. The command entered is firstly parsed to ensure validity, and then a new LeaveApplication is instantiated with the data parsed from the command. This LeaveApplication is kept inside a newly created LeaveApplyCommand, which is then executed to update the Person in the Model, which corresponds to the user who applied for leave. The sequence diagram is as follows:

LeaveApplicationSequenceDiagram
Figure 14. The Logic Component when leaveapply -de family holiday -da 2018-11-11 is executed
There are further interactions within the Model component that are omitted.

Given below is an example usage scenario and how the leave application mechanism behaves when a new leave application is made by an employee:

  1. The user executes the leaveapply -de family holiday -da 2018-11-11 command. The LeaveApplication will be initialized with the specified Description (family holiday), and one or more Date (2018-11-11), and its LeaveStatus will be the initial value of PENDING.

  2. The new LeaveApplication will then be added to its corresponding Person, which represents the employee that applied for the leave. Internally, a duplicate Person is created with the newly added LeaveApplication, and the original Person in the Model will be replaced with that new Person (see LeaveApplyCommand.java for more details).

  3. In the Storage, the LeaveApplication will be copied and transformed to become an XmlAdaptedLeaveApplication object, which is then added into the XmlAdaptedPerson representing the person who applied for the leave, and finally saved into a file by the Storage component.

Viewing leave applications

Employees can view their leave applications using the leavelist command. Given below is an example usage scenario of how OASIS behaves when a user issues this command:

  1. The user executes the leavelist command.

  2. The system checks if the user is an Admin, or has the required permissions (VIEW_EMPLOYEE_LEAVE or APPROVE_LEAVE). If so, all leave applications of all other users will be displayed. If not, only the current logged-in user’s own leave application records will be shown.

The following activity diagram summarizes how the command is executed:

LeaveViewActivityDiagram
Figure 15. How the leave application list displayed is filtered currently
See "Design Considerations - How leave applications are listed" for further explanation of how the list of leave applications are retrieved and displayed.
Approving or rejecting leave applications

Users with the required permissions can approve or reject leave applications made by other users. Given below is an example usage scenario of when a user issues a leaveapprove 3 command:

  1. The user executes the leaveapprove 3 command (the index specified in the command is based on what is displayed when the user issues a leavelist command).

  2. The corresponding LeaveApplicationWithEmployee is retrieved from the list. For more details about LeaveApplicationWithEmployee, see "Design Considerations - How leave applications are listed".

  3. Internally, a duplicate Person is created, with the original leave application being replaced with a copy of itself with an APPROVED status. The original Person in the Model will then be replaced with that new Person, similar to what happens when applying for leave.

The leaveapprove and leavereject commands behave is nearly identical fashion, with the only difference being what the status that the specified LeaveApplication is changed to (either APPROVED or REJECTED).

4.11.2. Design Considerations

Aspect: How leave applications are stored
  • Alternative 1 (current choice): Saved only as a part of Person.

    • Pros: Easy to implement.

    • Cons: We need to go through every Person to retrieve a list of all LeaveApplication in the system to generate the list of all leaves.

  • Alternative 2: Stored only as a part of AddressBook.

    • Pros: Easy to implement.

    • Cons: We need to go through every LeaveApplication in the system when retrieving the LeaveApplication for a particular Person.

  • Alternative 3: Stored as a part Person as well as AddressBook.

    • Pros: Fast retrieval for a particular Person, as well as for the entire list of LeaveApplication`s from `AddressBook.

    • Cons: Redundant and duplicate storage for each LeaveApplication. We need to ensure that when adding, editing, and deleting a LeaveApplication, it is updated correctly in both parts of the Model as well as Storage.

Aspect: How leave applications are listed
  • Alternative 1 (current choice): Leave applications are tagged with the user that applied for them (see LeaveApplicationWithEmployee.java), and stored in a LeaveApplicationList.java in AddressBook in the Model Component. This additional tagging and storing into a list is done when the application is started where all leave applications are read from each Person, and also when any leave application is created or updated.

    • Pros: Leave applications stored are lightweight, as they are kept within the Person that applied for them, and do not have to contain fields that uniquely identify that Person.

    • Cons: This additional tagging, that only exists while the application is running, is slightly clumsy and not the best way to do it (see the note below Alternative 2 for more details).

  • Alternative 2: Leave applications contain the unique identification fields of a Person.

    • Pros: Leave applications innately store who applied for them, so no additional processing is required when generating the list of leave applications.

    • Cons: Leave applications will have to be stored with 3 additional fields that are used to unique identify a Person. Also, if there are any updates to the Person, it must be ensured that their corresponding leave applications will also have to be updated correctly.

Alternative 2 is actually cleaner to implement and understand. However, Alternative 1 is currently implemented because a Person has 3 identification fields, which means that a lot of unnecessary information would have to be duplicated and stored. In future versions, we suggest that each Person be given a unique ID number that can be used to identify them. This would make it convenient for other entities, like LeaveApplication, to store which Person it is linked to, without too much overhead incurred.

4.12. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Section 4.13, “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

4.13. Configuration

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

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

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.

  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 16. Saving documentation as PDF files in Chrome

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

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

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

6. Testing

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

You may encounter a problem with running Gradle commands on the command line, with the following error message: Cannot find System Java Compiler. Ensure that you have installed a JDK (not just a JRE) and configured your JAVA_HOME system variable to point to the according directory. If you encounter this error, you can apply the fix shown here for Windows.

6.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.address.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.address.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.address.storage.StorageManagerTest

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

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

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.

  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.

7.6. Managing Dependencies

A project often depends on third-party libraries. For example, Address Book 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: User Stories

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

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

* * *

Employee

See usage instructions

Get help when I forget how to use a feature of the application

* * *

Employee

Log in

Access the features of the system

* * *

Employee

Change my password

Ensure that my account will not be compromised

* * *

Employee

View my own profile and personal information

Check if it is up-to-date

* * *

Employee

Edit my contact information

Other employees using the application can see my most recent contact information

* * *

Employee

Log out

Prevent non-authorized users of my computer from accessing the system

* * *

Employee with add employee permissions

Add a new employee into my department

Have the new hire listed in the system

* * *

Employee with delete employee permissions

Delete an employee from my department

Remove employees that have left or have been fired

* * *

Employee

View all other employees in the system

Find out more about employees in the company

* * *

Employee

View the profile of an employee in the system

Find out more a certain employee

* * *

Employee

Apply for leave

Get approval for my yearly leave from my manager

* * *

Employee with view employee leave permissions

View leave application of employees in my department

See who has applied for leave

* * *

Employee with approve leave permissions

Approve or reject employee requests for leave

Plan out future projects

* * *

Employee with add project permissions

Create a new project

Have the new project listed in the application

* * *

Employee with add project permissions

Add an employee from any department into a project I created

Have the employee listed in the project team

* * *

Employee with add project permissions

Remove an employee from a project I created

Have the employee removed from the project team

* * *

Employee with add project permissions

Assign a task to an employee in one of my projects

Delegate the required work in a project to the team

* * *

Employee

View all projects and project teams in the company

Find out more about ongoing projects and employees in the project teams

* * *

Employee

View all projects that I am a part of

Find out more about my projects and employees in the project team

* * *

Employee

View all tasks assigned to me by a project

Check what I have to do for a particular project

* *

Employee with "Department Manager" permissions

See the total manpower strength in each department

Allocate manpower to department in need for more employees

* *

Employee

Search for employees by a certain criteria (e.g. name, department, etc.)

Find a particular employee easily

* *

Employee

Sort employees in the display list by a certain criteria (e.g. name, department, etc.)

View the employees sequentially and increasing clarity

* *

Employee

Filter employees in the display list by a certain criteria

Find details of a specific employee without going through every employee in the system

* *

Employee with view employee leave permissions

View summary of approved leaves taken by employees in my department in the upcoming months

Better plan upcoming projects and manpower distribution

* *

Employee with "Department Manager" permissions

View workload of employees in my department

Delegate the work evenly

* *

Employee

See income summary

Know my monthly income.

* *

Employee with "Administrative" permissions

Modify the access permissions of an employee

Accommodate to the tasks that an employee is allowed to do, possibly in line with promotions or demotions

* *

Employee with "Administrative" permissions

Change the department of an employee

Have the system reflect the change of an employee moving to another department

* *

Employee

Upload a profile picture

Other users can see my face when they access my profile in the system

* *

Employee

View the list of past employees that have been removed from the system

Check the archives for information about previous employees

*

Employee with "Department Manager" permissions

See the performance of every department/sector

Keep track of which departments are not performing

*

Employee with "Department Manager" permissions

Record accomplishments of my employees

Identify the better employees

*

Employee with "Department Manager" permissions

See the list of potential candidates for hiring

Easily rank my top choices on who to hire

*

Employee

Submit claim

Claim money related to company matters

*

Employee

Submit overtime claim

Receive my overtime pay

*

Employee with "Department Manager" permissions

See the performance of individual employees

Identify underperforming employees

*

Employee

View current assigned tasks to me

Track which assignments I have left to finish

*

Employee

Write and send email

Send an email to one or more employees in the company

*

Employee with "Department Manager" permissions

Find out how much I am paying my employees in total

Calculate profit margins for the organization and any bonus for my employees at the end of the year

Appendix B: Use Cases

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

Use case: Add a new employee

MSS

  1. User login to the system

  2. Oasis shows home page

  3. User enters add employee with details

  4. Oasis request for confirmation

  5. User enters confirm

  6. Oasis shows success message

    Use case ends.

Extensions

  • 1a. The credentials are invalid

    Use case ends.

  • 3a. User enters invalid details

    • 3a1. OASIS shows an error message.

      Use case resumes at step 2.

  • 5a. The user chooses to cancel

    Use case resumes at step 2.

Use case: Change user details

MSS

  1. Employee login to the system

  2. Oasis shows home page

  3. Employee enters new user details

  4. Oasis request for confirmation

  5. Employee enters confirm

  6. Oasis save the new user details and show success message

    Use case ends.

Extensions

  • 1a. The credentials are invalid

    Use case ends.

  • 3a. The details are in an invalid format.

    • 3a1. OASIS shows an error message.

      Use case resumes at step 2.

  • 5a. The employee chooses to cancel

    Use case resumes at step 2.

Use case: Delete employee

MSS

  1. Employee login to the system

  2. Oasis shows home page and displays list of employees

  3. Employee executes delete command on a selected employee

  4. Oasis deletes the employee, moves it to archive list and show success message

    Use case ends.

Extensions

  • 1a. The credentials are invalid

    Use case ends.

  • 2a. The list is empty.

    Use case ends.

  • 2b. Employee moves to archive list.

    • 2b1. OASIS shows archive list.

    • 2b2. Employee executes delete command on a selected employee

    • 2b3. Oasis deletes the employee from the system and shows success message.

      Use case ends.

  • 3a. The chosen employee is invalid.

    • 3a1. OASIS shows an error message.

  • 3b. Employee does not have required permissions.

    • 3a1. OASIS shows an error message.

      Use case ends.

Use case: Apply for leave

MSS

  1. User login to the system

  2. OASIS shows home page

  3. User enters description, start date and end date

  4. OASIS displays confirmation message

  5. User enters confirm

  6. OASIS sends the application to the manager

  7. OASIS shows success message

    Use case ends.

Extensions

  • 1a. The credentials are invalid

    Use case ends.

  • 3a. The format is invalid

    • 3a1. OASIS shows an error message.

      Use case resumes at step 2.

  • 5a. The user chooses to cancel

    Use case resumes at step 2.

Use case: List leave applications

Preconditions: user is logged in

MSS

  1. User enters command to view leave applications

  2. OASIS lists all leave applications of the user

    Use case ends.

Extensions

  • 1a. The format is invalid

    • 1a1. OASIS shows an error message

      User case ends.

  • 2a. The user has permission to be able to view all employee leaves.

    • 2a1. OASIS lists all the leaves allplicationsof all users

      Use case ends.

Use case: Approve a leave application

Preconditions: user is logged in

MSS

  1. User lists leave applications (Use case: List leave applications)

  2. User enters command to approve a leave, and chooses the leave application to approve

  3. OASIS approves the leave application

  4. OASIS shows success message

    Use case ends.

Extensions

  • 2a. The format is invalid

    • 2a1. OASIS shows an error message

  • 2b. The index is does not exist

    • 2b1. OASIS shows an error message

  • 2c. The user does not have the permissions to approve leaves

    • 2c1. OASIS shows an error message

      Use case resumes at step 1.

Use case: Add new assignment

MSS

  1. User login to the system

  2. Oasis shows home page

  3. User enters assignment name, author and description

  4. Oasis displays confirmation message

  5. User enters confirm

  6. Oasis sends the application to the manager

  7. Oasis shows success message

    Use case ends.

Extensions

  • 1a. The credentials are invalid

    Use case ends.

  • 3a. The format is invalid

    • 3a1. OASIS shows an error message.

      Use case resumes at step 2.

  • 5a. The user chooses to cancel

    Use case resumes at step 2.

Use case: List assignments

Precondition: user is logged in

MSS

  1. User enterscommand to view list of assignments

  2. OASIS lists all the assignments in the system

    Use case ends.

Extensions

  • 1a. The format is invalid

    • 1a1. OASIS shows an error message

      Use case ends.

Use case: Delete assignment

MSS

  1. User login to the system

  2. User lists assignments (Use case: List assignments)

  3. Employee executes delete command on selected assignment

  4. Oasis deletes the assignment

    Use case ends.

Extensions

  • 1a. The credentials are invalid

    Use case ends.

  • 2a. The list is empty

    Use case ends.

  • 3a. Employee does not have the required permissions

    • 3a1. Oasis shows an error message

      Use case ends.

Use case: Edit assignment

MSS

  1. Employee login to the system

  2. User lists assignments (Use case: List assignments)

  3. Emplyee executes edit command on selected assignment

  4. OASIS edits the assignment

    Use case ends.

Extensions

  • 1a. The credentials are invalid

    Use case ends.

  • 2a. The list is empty

    Use case ends.

  • 3a. Employee does not have the required permissions

    • 3a1. Oasis shows an error message

      Use case ends.

{More to be added}

Appendix C: 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 hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.

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

  4. A user should only be allowed to perform the tasks that he has the credentials to perform based on his login credentials

  5. Passwords should be salted and hashed

  6. The system should crash no more than 1 time a month.

  7. The system should stay responsive even when there are updates to the GUI

Appendix D: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

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

E.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 login screen which prompts for a username and password.

  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 is retained.

E.2. Logging into the system.

  1. Logging in as an admin.

    1. After opening OASIS, enter in "Admin" and "Pa55w0rd" as the password.

    2. Click submit or press enter.
      Expected: You should now be logged in as an admin.

  2. Logging in as a User.

    1. Delete the data folder to have OASIS start with the sample data set

    2. Open OASIS.

    3. Enter in "David Li" for the username and "Pa55w0rd" as the password. (David Li is only of the sample people loaded when no data file is found)

    4. Click submit or press enter.
      Expected: You should now be logged in as David Li.

E.3. Logging out of the system

  1. Prerequisities: The user is currently logged in.

  2. Type in "Logout" into the command box, then press enter.
    Expected: You should now be logged out. OASIS should now show the login screen.

E.4. Editing details of currently logged in user

  1. Editing a valid user.

    1. Prerequisites: The user is currently logged in as a valid user in the system, not as an admin.

    2. Test case: myself -p 99900999
      Expected: The currently logged in user’s phone number is changed to 99900999

  2. Attempting to edit the admin user

    1. Prerequisites: The user is currently logged in as admin.

    2. Test cases: `myself -p 999009991
      Expected: An error message saying that you cannot edit the admin account.

E.5. Deleting a person

  1. Deleting a person while active persons are listed

    1. Prerequisites: The user is currently logged in as someone with the permission to delete people. List all persons using the active command. Multiple persons in the list.

    2. Test case: delete 1
      Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete 0
      Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.

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

  2. Deleting a person while archived persons are listed

    1. Prerequisites: The user is currently logged in as someone with the permission to delete people. List all persons using the archive command. Multiple persons in the list.

    2. Test case: delete 1
      Expected: First contact is deleted from the archive list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete 0
      Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.

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

E.6. Modify permission of an employee

  1. Add or remove employee’s permission using modifypermission command while active persons are listed

    1. Adding one permission

      1. Prerequisites: Logged in as a user with ASSIGN_PERMISSION permission. List all persons using the active command. At least one person in the list. Employee at index 1 without ADD_EMPLOYEE.

      2. Test case: modifypermission 1 -a ADD_EMPLOYEE
        Expected: ADD_EMPLOYEE permission is granted to employee at index 1. List of updated permission shown in status message. Timestamp in status bar is updated.

    2. Adding multiple permission

      1. Prerequisites: Logged in as a user with ASSIGN_PERMISSION permission. List all persons using the active command. At least one person in the list. Employee at index 1 without DELETE_EMPLOYEE and EDIT_EMPLOYEE permission.

      2. Test case: modifypermission 1 -a DELETE_EMPLOYEE -a EDIT_EMPLOYEE
        Expected: DELETE_EMPLOYEE & EDIT_EMPLOYEE permission is granted to employee at index 1. List of updated permission shown in status message. Timestamp in status bar is updated.

    3. Removing multiple permission

      1. Prerequisites: Logged in as a user with ASSIGN_PERMISSION permission. List all persons using the active command. At least one person in the list. Employee at index 1 with DELETE_EMPLOYEE and EDIT_EMPLOYEE permission.

      2. Test case: modifypermission 1 -r DELETE_EMPLOYEE -r EDIT_EMPLOYEE
        Expected: DELETE_EMPLOYEE & EDIT_EMPLOYEE permission is removed from employee at index 1. List of updated permission shown in status message. Timestamp in status bar is updated.

    4. Adding and removing permission

      1. Prerequisites: Logged in as a user with ASSIGN_PERMISSION permission. List all persons using the active command. At least one person in the list. Employee at index 1 with only ADD_EMPLOYEE permission.

      2. Test case: modifypermission 1 -a DELETE_EMPLOYEE -r ADD_EMPLOYEE
        Expected: Employee at index 1 only has DELETE_EMPLOYEE permission. List of updated permission shown in status message. Timestamp in status bar is updated.

    5. Incorrect modifypermission commands to try:

      1. modifypermission 1 -a INVALID_PERMISSION, modifypermission 1 -a DELETE_EMPLOYEE -r INVALID_PERMISSION
        Expected: Status message shows that permission name is invalid.

      2. modifypermission, modifypermission -a DELETE_EMPLOYEE -a EDIT_EMPLOYEE
        Expected: Status message shows that the command format is invalid.

E.7. Leave management

  1. Applying for leave while leave applications are listed

    1. Prerequisites: List all leave applications using the leavelist command. There need not be any leave applications in the list.

    2. Test case: leaveapply -de Family holiday -da 2018-12-05 -da 2018-12-06
      Expected: A new leave application record with the description "Family holiday" and the dates "2018-12-05" and "2018-12-06" appears in the list. Details of the new leave application shown in the status message. Timestamp in the status bar is updated.

    3. Test case: leaveapply -de Second family holiday -da 2018-12-16 -da 2018-12-15 -da 2018-12-15
      Expected: A new leave application record with the description "Second family holiday" and the dates "2018-12-15" and "2018-12-16" in this order appears in the list. Details of the new leave application shown in the status message. Timestamp in the status bar is updated.

    4. Incorrect leaveapply commands to try: leaveapply -de -da 2018-12-15 (empty description), leaveapply -de Family holiday -da 2018-4-5 (date format not YYYY-MM-DD).
      Expected: Status message shows that the command format is invalid.

  2. Approving or rejecting leave applications as a user without either "VIEW_EMPLOYEE_LEAVE" or "APPROVE_LEAVE" permissions

    1. Prerequisites: Login with a user that has neither "VIEW_EMPLOYEE_LEAVE" or "APPROVE_LEAVE" permissions (in a fresh install, the default username "Alex Yeoh" and password "Pa55w0rd" will suffice). List all leave applications using the leavelist command. There should be at least one leave application shown. Perform one leave application with leaveapply if there isn’t.

    2. Test case: leaveapprove 1 or leavereject 1
      Expected: The status of the leave applications do not change. Error details shown in the status message. Status bar remains the same.

    3. Incorrect leaveapply commands to try: leaveapply -de -da 2018-12-15 (empty description), leaveapply -de Family holiday -da 2018-4-5 (date format not YYYY-MM-DD).
      Expected: Status message shows that the command format is invalid.

  3. Approving or rejecting leave applications as a user with either "VIEW_EMPLOYEE_LEAVE" or "APPROVE_LEAVE" permissions

    1. Prerequisites: Login with the Admin account (the default username "Admin" and password "Pa55w0rd"). List all leave applications using the leavelist command. There should be at least one leave application shown. Perform one leave application with a non-admin user with leaveapply if there isn’t.

    2. Test case: leaveapprove 1 or leavereject 1
      Expected: The status of the first leave application changes to "APPROVED" or "REJECTED", depending on the command issued. Status message indicates that the leave has been approved. Timestamp in status bar is updated.

    3. Incorrect leaveapprove commands to try: leaveapprove 0 (non-positive integer index)
      Expected: Status message shows that the command format is invalid.

    4. Incorrect leaveapprove commands to try: leaveapprove 1000 (index larger than displayed list size).
      Expected: Status message shows that the leave list index is invalid.

E.8. Saving data

  1. Dealing with missing/corrupted data files

    1. The data files are located under data folder. For example, data/addressbook.xml contains data of everyone stored in the system.

      Data is only witten to disk if there is any change in the system. If OASIS was simply opened and closed with no changes made, no data file would be written.
    2. To modify the data files to simulate missing or corrupted data files, the element’s tag may be modified. Note that information between the tag may also be modified, but it could still remain valid and would not be detected as an error.
      Expected: If a missing or corrupted data file is used, OASIS will then begin with an empty system instead.