By: CS2103AUG2017-W13-B4
Since: Oct 2017
Licence: MIT
- 1. Introduction
- 2. Setting up
- 3. Design
- 4. Implementation
- 4.1. Undo/Redo mechanism
- 4.2. ListingUnit mechanism
- 4.3. List/View mechanism
- 4.4. Mark/Unmark mechanism
- 4.5. Find mechanism
- 4.6. Remark mechanism
- 4.7. Sort mechanism
- 4.8. Color Keyword mechanism
- 4.9. Customise mechanism
- 4.10. Switch theme mechanism
- 4.11. Timetable mechanism
- 4.12. StickyNotes mechanism
- 4.13. Logging
- 4.14. Configuration
- 5. Documentation
- 6. Testing
- 7. Dev Ops
- Appendix A: User Stories
- Appendix B: Use Cases
- Appendix C: Non Functional Requirements
- Appendix D: Glossary
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
-
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. -
IntelliJ IDE have to be installed in your computer.
-
Fork this repo, and clone the fork to your computer
-
Launch IntelliJ (if you are not in the welcome screen, click
File
>Close Project
to close the existing project dialog). -
Set up the correct JDK version for Gradle.
-
Click
Configure
>Project Defaults
>Project Structure
. -
Click
New…
and find the directory of the JDK.
-
-
Click
Import Project
. -
Locate the
build.gradle
file and select it. ClickOK
. -
Click
Open as Project
. -
Click
OK
to accept the default settings. -
Open a console and run the command
gradlew processResources
(Mac/Linux:./gradlew processResources
). It should finish with theBUILD SUCCESSFUL
message. This will generate all resources required by the application and tests.
-
Run the
seedu.address.MainApp
and try a few commands. -
Run the tests to ensure they all pass.
This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify,
-
Go to
File
>Settings…
(Windows/Linux), orIntelliJ IDEA
>Preferences…
(macOS). -
Select
Editor
>Code Style
>Java
. -
Click on the
Imports
tab to set the order.-
For
Class count to use import with '*'
andNames count to use static import with '*'
: Set to999
to prevent IntelliJ from contracting the import statements. -
For
Import Layout
: The order isimport static all other imports
,import java.*
,import javax.*
,import org.*
,import com.*
,import all other imports
. Add a<blank line>
between eachimport
.
-
Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.
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).
Before you start coding, understand the overall design by reading the Architecture section.
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 thediagrams
folder. To update a diagram, modify the diagram in the.pptx
file, select the objects of the diagram, and chooseSave 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:
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.
Figure 2 : Class Diagram of the Logic Component
The Sequence Diagram(Figure 3) below shows how the components interact for the scenario where the user executes the command delete 1
.
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.
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.
3.2. UI component
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,
Figure 6: Class Diagram of the Logic Component
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)
-
The Logic component uses the AddressBookParser class to parse the user command.
-
This results in a Command object which is executed by the LogicManager.
-
The command execution can affect the Model component (e.g. adding a lesson) and/or raise events.
-
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.
Figure 8: Sequence Diagram for the delete 1 Command
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.
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.
This section describes how certain features are implemented.
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.
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).
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).
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).
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:
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).
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):
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).
Figure 18: RedoStack Diagram 6
The following Activity Diagram (Figure 19) summarizes what happens inside the UndoRedoStack when a user executes a new command:
Figure 19: Activity Diagram
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.
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.
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.
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:
Figure 20: Sequence Diagram for ListCommand
The following Activity Diagram (Figure 21) summarizes what happens when a user executes list command.
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:
Figure 22 Sequence Diagram for ViewCommand
The following Activity Diagram (Figure 23) summarizes what happens when a user executes ViewCommand.
Figure 23: Activity Diagram for viewCommand
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.
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:
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. |
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.
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:
Figure 25: Sequence Diagram for FindCommand
The following Activity Diagram (Figure 26) summarizes what happens when a user executes find command.
Figure 26: Activity Diagram for FindCommand
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.
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:
Figure 27: Sequence Diagram for RemarkCommand
The following Activity Diagram (Figure 28) summarizes what happens when a user executes remark command.
Figure 28: Activity Diagram for RemarkCommand
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.
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:
Figure 29: Sequence Diagram for SortCommand
The following Activity Diagram (Figure 30) summarizes what happens when a user executes sort command.
Figure 30: Activity Diagram for SortCommand
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.
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:
Figure 31: Sequence Diagram for ColorKeywordCommand
The following Activity Diagram (Figure 32) summarizes what happens when a user executes color keyword command.
Figure 32: Activity Diagram for ColorKeywordCommand
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.
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:
Figure 34: Sequence Diagram for CustomiseCommand
The following Activity Diagram (Figure 35) summarizes what happens when a user executes customise command.
Figure 35: Activity Diagram for CustomiseCommand
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.
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:
Figure 36: Sequence Diagram for ThemeCommand
The following Activity Diagram (Figure 37) summarize what happens when a user executes theme command.
Figure 37: Activity Diagram for ThemeCommand
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.
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 ...//
}
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.
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 ...//
}
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.
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.
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. |
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.
See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.
We use Google Chrome for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.
Here are the steps to convert the project documentation files to PDF format.
-
Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the
docs/
directory to HTML format. -
Go to your generated HTML files in the
build/docs
folder, right click on them and selectOpen with
→Google Chrome
. -
Within Chrome, click on the
Print
option in Chrome’s menu. -
Set the destination to
Save as PDF
, then clickSave
to save a copy of the file in PDF format. For best results, use the settings indicated in the screenshot below (Figure 38).
Figure 38: Saving documentation as PDF files in Chrome
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 chooseRun 'All Tests'
-
To run a subset of tests, you can right-click on a test package, test class, or a test and choose
Run 'ABC'
Method 2: Using Gradle
-
Open a console and run the command
gradlew clean allTests
(Mac/Linux:./gradlew clean allTests
)
ℹ️
|
See UsingGradle.adoc for more info on how to run tests using Gradle. |
Method 3: Using Gradle (headless)
Thanks to the TestFX library we use, our GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. That means the developer can do other things on the Computer while the tests are running.
To run tests in headless mode, open a console and run the command gradlew clean headless allTests
(Mac/Linux: ./gradlew clean headless allTests
)
We have two types of tests:
-
GUI Tests - These are tests involving the GUI. They include,
-
System Tests that test the entire App by simulating user actions on the GUI. These are in the
systemtests
package. -
Unit tests that test the individual components. These are in
seedu.address.ui
package.
-
-
Non-GUI Tests - These are tests not involving the GUI. They include,
-
Unit tests targeting the lowest level methods/classes.
e.g.seedu.address.commons.StringUtilTest
-
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
-
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
-
Problem: HelpWindowTest
fails with a NullPointerException
.
-
Reason: One of its dependencies,
UserGuide.html
insrc/main/resources/docs
is missing. -
Solution: Execute Gradle task
processResources
.
See UsingGradle.adoc to learn how to use Gradle for build automation.
We use Travis CI and AppVeyor to perform Continuous Integration on our projects. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.
Here are the steps to create a new release.
-
Update the version number in
MainApp.java
. -
Generate a JAR file using Gradle.
-
Tag the repo with the version number. e.g.
v0.1
-
Create a new release using GitHub and upload the JAR file you created.
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.
-
Include those libraries in the repo (this bloats the repo size)
-
Require developers to download those libraries manually (this creates extra work for developers)
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. |
(For all use cases below, the System is the ModU
and the Actor is the user
, unless specified otherwise)
MSS
-
User requests to add module time slot.
-
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.
-
MSS
-
User requests to list modules.
-
ModU shows a list of modules.
-
User requests to update a specific module in the list.
-
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.
-
MSS
-
User requests to list module by module/time slot/location.
-
ModU shows a list of module/time slot/location.
-
User requests to delete a specific item (module/time slot/location) on the list.
-
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.
-
MSS
-
User requests redo the previously command.
-
ModU redo previous command.
Use case ends.
Extensions
-
2a. Unable to find previous command.
Use case ends.
MSS
-
User requests to undo previous command.
-
ModU undo previous command.
Use case ends.
Extensions
-
2a. Unable to find previous command.
Use case ends.
MSS
-
User requests to list history of commands.
-
ModU shows a list of history of commands.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
MSS
-
User requests show help guide.
-
ModU shows help guide.
Use case ends.
MSS
-
User requests to list all modules time-slots.
-
ModU shows all modules time-slots.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
MSS
-
User requests to list by attribute.
-
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.
-
MSS
-
User requests to login with a User Account.
-
ModU asks user to key in password.
-
User key in password.
-
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.
-
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).
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.