Skip to content

Latest commit

 

History

History
1311 lines (892 loc) · 59.5 KB

DeveloperGuide.adoc

File metadata and controls

1311 lines (892 loc) · 59.5 KB

ModU - Developer Guide

1. Introduction

ModU is a software designed to assist teachers and lecturers to plan their weekly teaching schedule.

There are many ways to improve and extend the functionality of this software and this guide provides essential information to help you get started. This developer guide contains information of the architecture design of this software, implementation of the existing commands that are accessible for the users and other useful information

2. Setting up

2.1. Prerequisites

  1. JDK 1.8.0_60 or later have to be installed in your computer.

    ℹ️
    This application will not work with earlier versions of Java 8.
  2. IntelliJ IDE have to be installed in your computer.

    ℹ️
    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. Launch IntelliJ (if you are not in the welcome screen, click File > Close Project to close the existing project dialog).

  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.

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. Setting up CI

Set up Travis to perform Continuous Integration (CI) for your fork. See UsingTravis.adoc to learn how to set it up.

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.3. Getting started with coding

Before you start coding, understand the overall design by reading the Architecture section.

3. Design

3.1. Architecture

Architecture

Figure 1 : Architecture Diagram

The Architecture Diagram (Figure 1) above explains the high-level design of the application. 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 application 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 represent 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 : This class is used by other classes to write log messages to the application’s log file.

The rest of the application consists of four components:

  • UI : The user interface of the application.

  • Logic : The command executor.

  • Model : The data handler in the application.

  • Storage : The storage manager.

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 (Figure 2) given below) defines its 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(Figure 3) below shows how the components interact for the scenario where the user executes the command delete 1.

SDforDeletePerson

Figure 3 : Sequence Diagram for delete 1 command (part 1)

ℹ️
Note how the Model simply raises a AddressBookChangedEvent when ModU data are changed, instead of asking the Storage to save the updates to the hard disk.

The Sequence diagram below (Figure 4) 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 : Sequence Diagram 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.

UiClassDiagram

Figure 5 : Class Diagram of the UI Component

As shown in the Class diagram above (Figure 5), the UI consists of a MainWindow that is made up of subcomponents e.g. CommandBox, ResultDisplay, PersonListPanel, and etc. All of them 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.java.

The UI component,

  • executes user commands using the Logic component.

  • binds itself to some data in the Model so that the UI component can auto-update when the data in the Model component changes.

  • responds to events raised from various parts of the application and updates the UI accordingly.

3.3. Logic component

LogicClassDiagram

Figure 6: Class Diagram of the Logic Component

LogicCommandClassDiagram

Figure 7: Inheritance Diagram of Commands in the Logic Component. This diagram shows finer details concerning XYZCommand and Command in Figure 5

API: Logic.java (Figure 6 and 7)

  1. The Logic component 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 component (e.g. adding a lesson) and/or raise events.

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

Given below is the Sequence diagram (Figure 8) for interactions within the Logic component for the execution of "delete 1" API call.

DeletePersonSdForLogic

Figure 8: Sequence Diagram for the delete 1 Command

3.4. Model component

ModelClassDiagram

Figure 9: Class Diagram of the Model Component

The Model component (Figure 9),

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

  • stores all the data of ModU.

  • exposes an unmodifiable ObservableList<ReadOnlyLesson> that can be 'observed' e.g. the UI component can be bound to this list so that it automatically updates when the data in the list has changed.

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

3.5. Storage component

StorageClassDiagram

Figure 10: Class Diagram of the Storage Component

The Storage component (Figure 10),

  • can save UserPref objects in json format.

  • can read UserPref objects in json format.

  • can save all other data in xml format.

  • can read all other data in xml format.

3.6. Common classes

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

4. Implementation

This section describes how certain features are implemented.

4.1. Undo/Redo mechanism

The undo/redo mechanism is facilitated by an UndoRedoStack, which resides in LogicManager. It supports undoing and redoing of commands that modify the state of ModU (e.g. add, edit). Such commands will inherit from UndoableCommand.

UndoRedoStack only deals with UndoableCommands. Commands that cannot be undone will inherit from Command instead. The following diagram shows the Inheritance Diagram (Figure 11) for commands.

LogicCommandClassDiagram

Figure 11: Inheritance Diagram of Commands in the Logic Component

As you can see from the diagram, UndoableCommand adds an extra layer between the abstract Command class and concrete Commands that can be undone, such as the AddCommand. Note that extra tasks need to be done when executing a command in an undoable way, such as saving the state of ModU before execution.

UndoableCommand contains the high-level algorithm for those extra tasks while the child classes implement the details of how to execute the specific command. Note that this technique of putting the high-level algorithm in the parent class and lower-level steps of the algorithm in child classes is also known as the template pattern.

Commands that are not undoable are implemented this way:

public class ListCommand extends Command {
    @Override
    public CommandResult execute() {
        // ... list logic ...
    }
}

With the extra layer, the commands that are undoable are implemented this way:

public abstract class UndoableCommand extends Command {
    @Override
    public CommandResult execute() {
        // ... undo logic ...

        executeUndoableCommand();
    }
}

public class DeleteCommand extends UndoableCommand {
    @Override
    public CommandResult executeUndoableCommand() {
        // ... delete logic ...
    }
}

Suppose that the user has just launched the application. The UndoRedoStack will be empty at the beginning.

The user executes a new UndoableCommand, delete 5, to delete all lessons associated with the module in index 5. The current state of ModU is saved before executing the command. The command will then be pushed onto the undoStack (the current state is saved together with the command) (Figure 12).

UndoRedoStartingStackDiagram

Figure 12: undoStack Diagram 1

As the user continues to use the program, more commands are pushed onto the undoStack. For example, the user may execute add m/CS2103T … to add a new lesson (Figure 13).

UndoRedoNewCommand1StackDiagram

Figure 13: undoStack Diagram 2

ℹ️
If a command fails its execution, it will not be pushed onto the UndoRedoStack.

Suppose that the user now decides that adding the lesson was a mistake, and wants to undo that action using undo.

We will pop the most recent command out of the undoStack, and check if the command is redoable (a command is redoable if the listing panel has not been changed). For example, delete 5 has different meaning when listing by module and location, and thus redoing the command is no longer allowed. However, if the listing element has not been changed, we will push it back to the redoStack. We will restore ModU to the state before the add command was executed (Figure 14).

UndoRedoExecuteUndoStackDiagram

Figure 14: undoStack Diagram 3

ℹ️
If the undoStack is empty, then there are no other commands left to be undone, and an exception will be thrown when trying to pop from the undoStack.

The following Sequence diagram (Figure 15) shows how the undo operation works:

UndoRedoSequenceDiagram

Figure 15: Sequence Diagram for undo operation

The redo command does the exact opposite (pops from redoStack, push to undoStack, and restores ModU to the state after the command is executed).

ℹ️
If the redoStack is empty, then there are no other commands left to redo. In this case, an exception will be thrown when trying to pop from the redoStack.

The user now decides to execute a new command, clear. As before, clear will be pushed onto the undoStack. This time, the undoStack is no longer empty. It will be purged as it no longer make sense to redo the add m/MA1101R command (this is the behaviour that most modern desktop applications follow) (Figure 16).

UndoRedoNewCommand2StackDiagram

Figure 16: RedoStack Diagram 4

Commands that are not undoable are not pushed onto the undoStack. For example, list, which inherits from Command rather than UndoableCommand, will not be pushed onto the undoStack after execution (Figure 17):

UndoRedoNewCommand3StackDiagram

Figure 17: RedoStack Diagram 5

Suppose now user wants to view all lessons of module code CS2103, thus user types command view 1 (Suppose the index of module CS2103 is 1), command view will change the listing element from MODULE to LESSON. The undoStack will then be cleared because the user can only undo if the listing element type has not been switched (Figure 18).

UndoRedoNewCommand4StackDiagram

Figure 18: RedoStack Diagram 6

The following Activity Diagram (Figure 19) summarizes what happens inside the UndoRedoStack when a user executes a new command:

UndoRedoActivityDiagram

Figure 19: Activity Diagram

4.1.1. Design Considerations

Aspect: Implementation of UndoableCommand
Alternative 1 (current choice): Add a new abstract method executeUndoableCommand().
Pros: This implementation allows us to not lose any undo/redo functionality as it is now part of the default behaviour. Classes that deal with Command do not have to know that executeUndoableCommand() exists.
Cons: It would be difficult for new developers to understand the template pattern.
Alternative 2: Override execute() method.
Pros: This implementation does not involve the template pattern which makes it easier for new developers to understand.
Cons: Classes which inherit from UndoableCommand must remember to call super.execute(), or risk losing the ability to undo/redo.


Aspect: How undo & redo executes
Alternative 1 (current choice): Save the current state of ModU.
Pros: This implementation requires less effort.
Cons: It may have performance issues in terms of memory usage.
Alternative 2: Undo/redo are executed by individual commands.
Pros: This implementation will use less memory.
Cons: We must ensure that the implementation of each individual command is correct.


Aspect: Type of commands that can be undone/redone+ Alternative 1 (current choice): Include only commands that modifies the data in ModU (add, clear, edit).
Pros: We only revert changes that are difficult to change back (the view can easily be re-modified as no data are lost).
Cons: User might think that undo also applies when the list is modified (undoing filtering for example), only to realize that it does not do that, after executing undo.
Alternative 2: Include all commands.
Pros: This implementation might be more intuitive for the user.
Cons: User could not skip such commands if he or she just wants to reset the state of ModU and not the view mode.


Aspect: Data structure to support the undo/redo commands
Alternative 1 (current choice): Use separate stack for undo/redo.
Pros: This implementation makes it easy for developers who are taking over this application to understand.
Cons: The Logic component is duplicated twice. For example, when a new command is executed, we must remember to update both HistoryManager and UndoRedoStack.
Alternative 2: Use HistoryManager for undo/redo.
Pros: This implementation does not require maintaining a separate stack, thus allowing us to reuse existing codes in the codebase.
Cons: It would require dealing with commands that have already been undone and we must remember to skip these commands. It also violates Single Responsibility Principle and Separation of Concerns as HistoryManager now needs to do two things.

4.2. ListingUnit mechanism

ListingUnit is an enumeration class which has three enumeration types: LESSON, MODULE and LOCATION. It records the current listing attribute of the lesson list panel. It has static method getCurrentListingUnit and setCurrentListingUnit that allows us to record the current listing type and get the current listing type at any time. It is implemented in this way:

public enum ListingUnit {
    MODULE, LOCATION, LESSON;

    private static ListingUnit currentListingUnit = MODULE;
    private static Predicate currentPredicate;

    //...
}

When list command is used, the variable currentListingUnit will be set accordingly, either by LOCATION or MODULE. Similarly, when view command is used, the variable currentListingUnit will be set to LESSON.

ListingUnit facilitates the implementation of the various parts of the Logic component, such as EditCommand, ViewCommand, DeleteCommand and others. For example, EditCommand will need to know the current listing attribute in order to parse the arguments.

ListingUnit also records the current predicate used in filteredLessonList and has static methods setCurrentPredicate and getCurrentPredicate. Whenever the predicate is changed, the variable currentPredicate will be updated accordingly.

The recorded predicate is mainly used to facilitate the implementation of RedoCommand. We do not allow redoing of the previous command in a different context. Since many of the commands are index-based, executing the command with a different listing type will cause different results. As a result, when implementing the redo mechanism, we can obtain the currentPredicate and compare it with the predicate memorised by the command and only push it onto the redoStack if the predicate is the same.

4.2.1. Design Considerations

Aspect: Implementation of ListingUnit
Alternative 1 (current choice): Construct an enumeration class ListingUnit and record current listing type and predicates.
Pros: It makes the implementation of many other features easier.
Cons: In testing, ListingUnit class carries data from one test to another even when we want each test to be independent of the others.
Alternative 2 (previous choice): Use a variable in model manager to keep record of current ListingUnit.
Pros: There is no need for an extra enumeration class.
Cons: In order to access the variable in other components, the variable must be declared static. However, we cannot define static abstract methods in an interface.

4.3. List/View mechanism

The list and view mechanisms are similar and both are facilitated by ListingUnit and predicate. When the user lists by different attributes, the static variable current ListingUnit will reset to either MODULE or LOCATION, depending on the user’s input. When the user view by index, the static variable current ListingUnit will be set to LESSON.

ℹ️
The default listing unit is MODULE. When listing by attribute, for example, list location, the currentListingUnit will be set to LOCATION and shows a list of all locations.

For different listing type, we always use the same lessonList but set different predicate to it. UniqueModuleCodePredicate and UniqueLocationPredicate are used to facilitate the implementation of list. For example, when the user types in list module, a UniqueModulePredicate will be used to filter the lessonList. UI will hide all other irrelevant information of the lesson such as location and time slot and shows a list of all modules.

The following Sequence diagram (Figure 20) shows how the list command works:

ListCommandSequenceDiagram

Figure 20: Sequence Diagram for ListCommand

The following Activity Diagram (Figure 21) summarizes what happens when a user executes list command.

ListCommandActivityDiagram

Figure 21: Activity Diagram for ListCommand

FixedCodePredicate and FixedLocationPredicate are used to facilitate the implementation of ViewCommand. According to the current listing type, which we can obtain from ListingUnit, we can create either a FixedCodePredicate or a FixedLocationPredicate to filter the lesson list and set currentListingUnit to be LESSON.

The following Sequence diagram (Figure 22) shows how the view command works:

ViewCommandSequenceDiagram

Figure 22 Sequence Diagram for ViewCommand

The following Activity Diagram (Figure 23) summarizes what happens when a user executes ViewCommand.

ViewCommandActivityDiagram

Figure 23: Activity Diagram for viewCommand

4.3.1. Design Considerations

Aspect: Implementation of List and View Command
Alternative 1 (current choice): Set different predicates to the filteredList and hide irrelevant information in the lesson list panel.
Pros: It would be easy to understand and do not use extra spaces to store different kind of lists (For example, location list).
Cons: This will increase overhead due to implementation of new predicates.
Alternative 2 (previous choice): Store lesson list, unique module list and unique location list separately and switches to different panel list before calling view command.
Pros: This implementation will make the data structure more organized.
Cons: Waste of space due to muptiple list is created and the switching of panel will cause heavier load to the system compared to a single panel.

4.4. Mark/Unmark mechanism

The mark mechanism is facilitated by an extra attribute isMarked of Lesson class. isMarked is a boolean attribute which indicates if the given lesson is in the marked list.

When the user mark a lesson, the specified lesson’s isMarked attribute will be set to true. When the user unmark a lesson, the specified lesson’s isMarked attribute will be set to false.

By default, when a new lesson is added, it is not in the marked list and thus the isMarked attribute will be set to false. The following Sequence diagram (Figure 24) shows how the mark command works:

MarkCommandSequenceDiagram

Figure 24: Sequence Diagram for MarkCommand

You can see a list of all marked lessons by the command list marked.

ℹ️
Each lesson is only allowed to be added into the marked list once, thus any further mark attempt will cause an exception to be thrown.

4.4.1. Design Considerations

Aspect: Implementation of mark/unmark
Alternative 1 (current choice): Create an extra attribute isMarked for each lesson to record if the lesson is in the marked list.
Pros: It would be easy to implement list marked since we are using predicates to update the lesson list.
Cons: It requires to filter out marked lessons every time.
Alternative 2 (previous choice): Stores additional marked lesson list.
Pros: No do not need to filter out the marked lessons each time.
Cons: Lessons might be stored two times which could potentially waste space.

4.5. Find mechanism

The find mechanism is facilitated by predicates. Whenever find command is executed, FindCommandParser will turn the parameter into a string list and passed to FindCommand. In FindCommand, different find predicates will be called depending on what the current ListingUnit is and updates the predicate of filteredList with one of the following four predicates:

  • LocationContainsKeywordPredicate

  • ModuleContainsKeywordPredicate

  • MarkedLessonContainsKeywordPredicate

  • LessonContainsKeywordPredicate

ℹ️
The LocationContainsKeywordPredicate will be used when the current ListingUnit is LOCATION.
The ModuleContainsKeywordPredicate will be used when the current ListingUnit is MODULE.
The MarkedLessonContainsKeywordPredicate will be used when the current ListingUnit is LESSON and currentViewingPredicate is marked.
The LessonContainsKeywordPredicate will be used when the current ListingUnit is LESSON.

The following Sequence diagram (Figure 25) shows how the find operation works:

FindCommandSeqDiagram

Figure 25: Sequence Diagram for FindCommand

The following Activity Diagram (Figure 26) summarizes what happens when a user executes find command.

FindCommandActivityDiagram

Figure 26: Activity Diagram for FindCommand

4.5.1. Design Considerations

Aspect: Implementation of FindCommand
Alternative 1 (current choice): Find module/lesson/location based on the current ListingUnit.
Pros: User does not always view all the lessons under LessonListPanel, it makes more sense for this implementation.
Cons: It would requires more work to be done as implementing FindCommand as this implementation would need to create multiple predicates for different type of ListCommand attributes.
Alternative 2: Find lessons based on the full list regardless of which listing attribute was executed previously.
Pros: It would require less work to implement the FindCommand as compared to alternative 1.
Cons: It does not make sense for the user as FindCommand will return a result that is irrelevant for the user. For example, the LessonListPanel shows a list of modules and when the user wants to search for module "CS1010", FindCommand will return a list of "CS1010" lessons instead of the module the user is trying to look for.

4.6. Remark mechanism

The remark mechanism is facilitated by UniqueRemarkList. Each time we add or delete a remark, the UniqueRemarkList is updated and changes are saved into storage. A filtered list is used to show remarks related to a specified module when a module is selected.

ℹ️
Only module can be remarked, therefore user can use remark if and only if the current listing element is MODULE.

The following Sequence diagram (Figure 27) shows how the remark operation works:

RemarkCommandSequenceDiagram

Figure 27: Sequence Diagram for RemarkCommand

The following Activity Diagram (Figure 28) summarizes what happens when a user executes remark command.

RemarkCommandActivityDiagram

Figure 28: Activity Diagram for RemarkCommand

4.6.1. Design Considerations

Aspect: Implementation of RemarkCommand
Alternative 1 (current choice): use a uniqueRemarkList similar to uniqueLessonList.
Pros: Store all remarks as a single list thus it is easy to manage.
Cons: Need to filter out a selected set of remarks when user select a specific module.

4.7. Sort mechanism

The sort mechanism is executed by sorting the internalList in UniqueLessonList depending on the current ListingUnit.

ℹ️
If the previous attribute type of list command is module, the list will be sorted by the lesson’s module code alphabetically for alphabets and in ascending order for number. Such sorting will also be applied for the location and lesson attributes. If there is no attribute in the execution of previous list command, the list will be sorted by the lesson’s module code by default.

The following Sequence diagram (Figure 29) shows how the sort operation works:

SortCommandSeqDiagram

Figure 29: Sequence Diagram for SortCommand

The following Activity Diagram (Figure 30) summarizes what happens when a user executes sort command.

sortCommandactivitydiagram

Figure 30: Activity Diagram for SortCommand

4.7.1. Design Considerations

Aspect: Implementation of SortCommand
Alternative 1 (current choice): Sort the internalList in UniqueLessonList.
Pros: It would be easier for new developer to understand the operation of SortCommand execution as it is more intuitive. Prevent conflicting with other command execution which uses internalList.
Cons: This implementation would require creation of different levels of abstraction of the same sort method in Model for a single method which results to more work.
Alternative 2: Sort list by raising an event to sort the current Observable<ReadOnlyLesson> lessonList in LessonListPanel.
Pros: This implementation of SortCommand would requires less work.
Cons: This implementation could be counter intuitive for new developer who is looking at the code for the first time as normally developers would implement it in the Model component.


Aspect: Execution of SortCommand
Alternative 1 (current choice): Sort list based on the attribute type of previous ListCommand.
Pros: This implementation could allow user to sort by different attributes and work hand in hand with ListCommand.
Cons: This implementation requires ModU to find out what attribute type was given as a attribute for previous ListCommand and sort accordingly which amount to more work.
Alternative 2: Ignore previous list attribute type and return a list with all the details from all attributes sorted by name.
Pros: This implementation of SortCommand would require less work.
Cons: SortCommand will not work well with ListCommand.

4.8. Color Keyword mechanism

The color keyword mechanism is facilitated by EventBus. Whenever color keyword command is executed, it will raise a ColorKeywordEvent via EventCenter. A listener in CommandBox will then enable or disable the highlighting of command keyword feature according to the parameter which was passed in by the user.

ℹ️
The default setting for this feature is set as disable.

The following Sequence diagram (Figure 31) shows how the color keyword command operation works:

ColorKeywordCommandSeqDiagram

Figure 31: Sequence Diagram for ColorKeywordCommand

The following Activity Diagram (Figure 32) summarizes what happens when a user executes color keyword command.

ColorKeywordActivityDiagram

Figure 32: Activity Diagram for ColorKeywordCommand

4.8.1. Design Considerations

Aspect: Implementation of ColorKeywordCommand
Alternative 1 (current choice): Update boolean variable "isEnable" in the command box by calling event to set the status of this variable.
Pros: Since only command box will be updated, using EventCenter will maintain good data encapsulation.
Cons: It would be difficult for new developers to understand the EventCenter mechanism.
Alternative 2: Declare a global boolean variable.
Pros: It would be easier for new developer to design.
Cons: This method breaks encapsulation and fails to follow the standard java coding style.

4.9. Customise mechanism

The customise mechanism is facilitated by both the EventBus and an enumeration class FontSizeUnit. Currently, CustomiseCommand only supports changing the font size of the application. Each time CustomiseCommand is executed, it will raise a ChangedFontSizeEvent via EventCenter according to the user specified parameter as well as update the currentFontSizeUnit.

ℹ️
The FontSizeUnit enumeration is to set a global static variable currentFontSizeUnit so that PersonCard is able to obtain the current font size whenever it is called.

The following Sequence diagram (Figure 34) shows how the customise command operation works:

CustomiseCommandSeqDiagram

Figure 34: Sequence Diagram for CustomiseCommand

The following Activity Diagram (Figure 35) summarizes what happens when a user executes customise command.

CustomiseCommandActivityDiagram

Figure 35: Activity Diagram for CustomiseCommand

4.9.1. Design Considerations

Aspect: Implementation of CustomiseCommand
Alternative 1 (current choice): Update static variable currentFontSizeUnit to the corresponding fontSizeUnit and use EventBus to inform UI of the change to fontSizeUnit.
Pros: It only requires one global variable to records current fontSizeUnit.
Cons: It would be difficult for new developers to understand the EventCenter mechanism.
Alternative 2: Use a variable in model manager to keep a record of currentFontSizeUnit.
Pros: It do not require additional enumeration class and easier for new developers to design.
Cons: In order to obtain the variable from other components, the variable must be declared static. However, we cannot define static abstract method in an interface. It also does not follow Java’s standard coding style.

4.10. Switch theme mechanism

The switch theme mechanism is facilitated by both the EventBus and an enumeration class ThemeUnit. Currently, ThemeCommand only supports changing between light (default) and dark theme. Each time ThemeCommand is executed, it will raise a SwitchThemeRequestEvent via EventCenter and update the currentThemeUnit.

ℹ️
The ThemeUnit enumeration is to set a global static variable currentThemeUnit so that MainWindow is able to obtain the current theme whenever it is called.

The following Sequence diagram (Figure 36) shows how the switch theme command operation works:

SwitchThemeSequenceDiagram

Figure 36: Sequence Diagram for ThemeCommand

The following Activity Diagram (Figure 37) summarize what happens when a user executes theme command.

SwitchThemeActivityDiagram

Figure 37: Activity Diagram for ThemeCommand

4.10.1. Design Considerations

Aspect: Implementation of ThemeCommand
Alternative 1 (current choice): Update static variable currentThemeUnit to the corresponding ThemeUnit and use EventBus to inform UI the change of ThemeUnit.
Pros: One global variable that records current ThemeUnit.
Cons: Hard for new developers to understand the event center mechanism.
Alternative 2:Use a variable in model manager to keep record of currentThemeUnit
Pros: There is no need for an extra enumeration class and it is also easier for new developers to design.
Cons: In order to access the variable in other components, it must be declared static. However, we cannot define static abstract method in interface. It also does not follow Java’s standard coding style.

4.11. Timetable mechanism

Timetable is an enhancement of UI. The role of the timetable view is to display a timetable of all the lessons listed in the display panel.

Timetable mechanism is facilitated by the GridPane properties in JavaFx.

The first row of timetableView is the header for time slot. In our software, the range for available time slots are from 0800 to 2000.

The generation of timetable column header is implemented this way:

 public void generateTimeslotHeader() {
        // ... generate column header for time slots ...//
    }

The first column of timetableView is the header for weekday. In our software, the range is from Monday to Friday.

The generation of timetable row header is implemented this way:

    public void generateWeekDay() {
       // ... generate row header for weekday ...//
    }

When the view command is executed, a number of lessons will be displayed on the display panel. In the CombinePanel, the handleViewedLessonEvent() will call generateTimeTableGrid() to set up the timetableView. The generateTimeTableGrid() will then obtain the grid data from generateTimeTableData().After that, it will display the data onto the timetableView.

The generation of timetable data is implemented this way:

public void generateTimeTableData() {
        // ... initGridData() ...//
        // ... generate grid data for grid view ...//
    }

The generation of timetable view is implemented this way:

public void generateTimeTableGrid() {

        // ... generateTimeTableData(); ...//
        // ... generateTimeslotHeader(); ...//
       // ... generateWeekDay();  ...//
       // ... generate timetable view  ...//
    }

4.11.1. Design considerations

Aspect: Implementation of Timetable.
Alternative 1 (current choice): Use java code to create the timetable view instead of SceneBuilder.
Pros: It is more flexible to change the style or the text of the label.
Cons: Since everything is written in code, you cannot visualise the layout until you run the code.
Alternative 2: Use SceneBuilder to create the timetable view.
Pros: It is easier to visualise the layout.
Cons: There are many limitations such as unable to add time slot to span 2 columns.

4.12. StickyNotes mechanism

StickyNotes is an enhancement of UI. The role of the stickynotes view is to display information about a module.

ℹ️
The maximum number of notes for a module is 9.

StickyNotes mechanism is facilitated by the GridPane properties in JavaFx.

When the remark command is executed, a new note will be created and linked to a module. The handleRemarkChangedEvent() will call stickyNotesInit() to set up the stickyNotesView. The stickyNotesInit() will then obtain the grid data from noteDataInit(). After that, it will display the data onto the stickyNotesView.

The generation of stickynotes data is implemented this way:

public void noteDataInit() {
        // ... generate note data for stickynotes view ...//
    }

The generation of stickynotes view is implemented this way:

public void stickyNotesInit() {

        // ... noteDataInit(); ...//
       // ... generate stickynotes view  ...//
    }

4.12.1. Design considerations

Aspect: Implementation of StickyNotes.
Alternative 1 (current choice): Use java code to create the stickynotes view instead of SceneBuilder.
Pros: The color of stickynotes can be changed easily.
Cons: It might be difficult to implement for new developers.
Alternative 2: Use Scene Builder to create the stickynotes view.
Pros: It is easier to visualise the layout and set the properties needed.
Cons: There are many limitations such as unable to show only a selected few TextArea objects.

4.13. 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 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: Potential problem detected which will not terminate the application but cause application oddities.

  • INFO: Information showing the noteworthy actions by the application

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

Certain properties of the application can be controlled (e.g application 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 (Figure 38).

chrome save as pdf

Figure 38: Saving documentation as PDF files in Chrome

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)

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, UserGuide.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. 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.4. Managing Dependencies

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

  1. Include those libraries in the repo (this bloats the repo size)

  2. 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 …​ I want to …​ So that I can…​

* * *

admin

add new module into the system.

allow the teachers and students to access this new module.

* * *

admin

add new teachers into the system.

allow new teachers to access the software.

* * *

admin

list the existing modules.

view how many and what module do i have in the system.

* * *

admin

update the detail of the existing module.

make changes to the module without having to delete the module and recreate it.

* * *

admin

delete teachers from the system.

take away the access right of the teachers who are no longer working for the school.

* * *

admin

update the detail of the teachers.

make changes of the detail of the teachers without having to delete and recreate them.

* * *

admin

view the commands/function i can use in this software.

review the commands in ModU conveniently whenever i want.

* * *

admin

add new location.

update the list of location in the system whenever a new location is constructed.

* *

admin

find a particular lecturer.

search for lecturer immediately.

* *

admin

generate comprehensive reports of the current semester detail.

save time from making this report myself.

* *

admin

view the history of commands.

know what command I have used so far.

* * *

student

check location and time slot of different modules.

make my study plan.

* * *

student

view all modules that are available next semester.

plan what module I would like to take next semester.

* * *

teacher

add new timeslot in timetable.

plan my teaching schedule.

* * *

teacher

delete an existing module in a particular time slot.

make changes to the time slot.

* * *

teacher

delete an existing module in a particular location.

make changes to the time slot.

* * *

teacher

update an existing module time slot.

change the current time slot to an updated one.

* * *

teacher

update an existing module location.

change the current location to an updated one.

* * *

teacher

list all the locations.

know all the locations I can choose from.

* * *

teacher

view the help file.

know all the available functions.

* * *

teacher

view available slots at a specified location.

determine if i can schedule my lecture there.

* * *

teacher

login with my username/password.

secure the information in my account.

* * *

teacher

register with username/password.

gain access to the personalised timetable.

* *

teacher

undo the previous command.

revert to the previous state.

* *

teacher

redo the previously undo-ed command.

revert to the previous state.

* *

teacher

show history of commands.

know what my previous commands were.

* *

teacher

back up my file.

retrieve back data.

* *

teacher/student

export the timetable to various files (pdf, excel, png, jpeg).

print the timetable.

* *

teacher/student

report bugs to developers.

help developer to solve the bug and improve the product.

* *

teacher/student

bookmark a particular module.

have easy access to the particular module.

* * *

new user

see usage instructions.

refer to instructions when I forget how to use the App.

* * *

user

delete a lesson.

remove entries that I no longer need.

* * *

user

find a lecturer by name.

locate details of lecturers without having to go through the entire list.

* *

user

hide private contact details by default.

minimize chance of someone else seeing them by accident.

*

user with many lessons in the ModU.

sort lessons by code

locate a lesson easily.

Appendix B: Use Cases

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

Use case: Add module to time slot

MSS

  1. User requests to add module time slot.

  2. ModU adds module shows “successfully added” message.

    Use case ends.

Extensions

  • 1a. User enter wrong command format.

    • 1a1. ModU shows an error message.

      Use case ends.

  • 2a. The module code, class type, time-slot or location is not available.

    • 2a1. ModU shows an error message.

      Use case ends.

Use case: Update module in a particular time slot

MSS

  1. User requests to list modules.

  2. ModU shows a list of modules.

  3. User requests to update a specific module in the list.

  4. ModU updates the module.

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The module code, class type, time-slot or location is not available.

    • 3a1. ModU shows an error message.

      Use case ends.

Use case: Delete module by module/time slot/location

MSS

  1. User requests to list module by module/time slot/location.

  2. ModU shows a list of module/time slot/location.

  3. User requests to delete a specific item (module/time slot/location) on the list.

  4. ModU deletes the item.

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. ModU shows an error message.

      Use case resumes at step 3.

Use case: Redo command

MSS

  1. User requests redo the previously command.

  2. ModU redo previous command.

    Use case ends.

Extensions

  • 2a. Unable to find previous command.

    Use case ends.

Use case: Undo command

MSS

  1. User requests to undo previous command.

  2. ModU undo previous command.

    Use case ends.

Extensions

  • 2a. Unable to find previous command.

    Use case ends.

Use case: History

MSS

  1. User requests to list history of commands.

  2. ModU shows a list of history of commands.

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

Use case: Help

MSS

  1. User requests show help guide.

  2. ModU shows help guide.

    Use case ends.

Use case: list

MSS

  1. User requests to list all modules time-slots.

  2. ModU shows all modules time-slots.

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

Use case: list [attribute (module / location/ marked)]

MSS

  1. User requests to list by attribute.

  2. ModU shows a list of information by attribute.

    Use case ends.

Extensions

  • 2a. The attribute given by user is invalid.

    • 2a1. ModU shows an error message.

      Use case ends.

Use case: login [userAccount]

MSS

  1. User requests to login with a User Account.

  2. ModU asks user to key in password.

  3. User key in password.

  4. ModU checks the password and show successful message.

    Use case ends.

Extensions

  • 4a. The account is not registered.

    • 4a1. ModU shows an error message.

      Use case ends.

Appendix C: Non Functional Requirements

Environment requirement(s):
    . The application should work on any mainstream OS as long as it has Java `1.8.0_60` or higher installed.
    . The application requires a minimum 512 MB of RAM memory.
    . The server should have minimum 5 GB of storage size.
    . The application should compatible with both 32-bits and 64-bits systems.
Capacity:
    .  The application should be able to hold up to 10000 lessons without a noticeable sluggishness in performance for typical usage.
Constraint(s)
    .  The application should be backward compatible with data produced by earlier version of the system.
    .  The total project cost should not exceed $10,000.
    .  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.
Performance requirement(s):
    .  The application should be respond to 1000 people within 2 seconds.
    .  Time to restart after failure should not be more than 5 seconds.
    .  The screen refresh time should be less than 1 second.
Quality requirement(s):
    .  The application should be usable by a novice who has never used an online timetable planner.
Process requirement(s)
    .  The project is expected to adhere a schedule that delivers an enhancement every week before tutorial.
Privacy:
    .  Admin have higher access than lecturer as they have to manage the system.
Notes about project scope:
    .  The product is not required to allow users to chat and message with each other.
Portability:
    .  Can be compiled and run in different operating systems and processors.
Security:
    .  The application must preserve the availability, integrity and confidentiality of data.
Reliability:
    .  Data created in the system will be retained for 2 years.
    .  The application must have less than 1 hour downtime per 3 months (e.g. to update/maintain the system).

Appendix D: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X.

Private contact detail

A contact detail that is not meant to be shared with others.

API

An application program interface (API) is code that allows two software programs to communicate with each other. The API defines the correct way for a developer to write a program that requests services from an operating system (OS) or other application. APIs are implemented by function calls composed of verbs and nouns. The required syntax is described in the documentation of the application being called.

JavaFX

JavaFX is a software platform for creating and delivering desktop applications, as well as rich internet applications (RIAs) that can run across a wide variety of devices. JavaFX is intended to replace Swing as the standard GUI library for Java SE, but both will be included for the foreseeable future.

Gradle

Gradle is a flexible general purpose build tool.

Travis

Travis CI is a hosted, distributed continuous integration service used to build and test projects hosted at GitHub. Travis CI automatically detects when a commit has been made and pushed to a GitHub repository that is using Travis CI, and each time this happens, it will try to build the project and run tests. This includes commits to all branches, not just to the master branch.

GUI

A graphical user interface (GUI) is a human-computer interface (i.e., a way for humans to interact with computers) that uses windows, icons and menus and which can be manipulated by a mouse (and often to a limited extent by a keyboard as well).

UI

The user interface (UI), in the industrial design field of human–computer interaction, is the space where interactions between humans and machines occur.

Activity diagram

Activity diagram is basically a flowchart to represent the flow from one activity to another activity.

Class diagram

In software engineering, a class diagram in the Unified Modeling Language (UML) is a type of static structure diagram that describes the structure of a system by showing the system's classes, their attributes, operations (or methods), and the relationships among objects.

Sequence diagram

A sequence diagram is an interaction diagram that shows how objects operate with one another and in what order.