Overview
Modulo is a desktop module management application written for students with fast fingers. A GUI created with JavaFX is also provided. It is written in Java, and has about 16 kLoC.
Summary of contributions
-
Major enhancement:
StatefulLogic
- Enabling Multistep Commands-
This feature allows the user to perform elaborate multistep commands by having their inputs build onto what was previously entered.
-
Justification: This feature greatly reduces the amount of repetitive commands that a user needs to enter to achieve the same result. It also helps to connect related processes.
-
Highlights: This feature was implemented from scratch with minimal changes to existing logic and classes, and instead extended on the Command Pattern. The brainstorming and implementation process required an in-depth analysis of design alternatives.
-
-
Major enhancement:
ModuleLibrary
- Understanding NUS Module JSONs-
This feature allows the user to add modules and events using official NUS module data.
-
Justification: This feature makes the app a lot more useful and convenient to use.
-
Highlights: A lot of difficulties were faced when analysing the patterns and inconsistencies within the NUS JSON files.
ModuleLibrary
now makes sense of the highly nested files to give users the required data meaningfully. -
Credits: Google’s Gson.
-
-
Major enhancement: JavaFX - Pleasing to the Eye
-
I designed and built the current GUI for Modulo.
-
-
Minor enhancement:
Storage
- Saving Your Progress-
I adapted the code for the
Storage
classes to work with the newModel
.
-
Code contributed
My code contributions can be found here: RepoSense Code Dashboard
Other contributions
-
Project Management
-
Managed releases
v1.2.1
-v1.4
(9 releases) on GitHub. -
Managed the issue tracker and milestones
v1.1
-v1.4
(7 milestones) on GitHub.
-
-
Enhancements to Existing Features
-
Community:
-
PRs reviewed (semi-trivial): #36, #52, #53, #59, #137, #152, #155, #160
-
Clarified and shared tips when module first started. (#8, #5)
-
Actively answered questions and responded to help requests on the forums. (#11, #12, #15, #23, #28, #31, #34, #37, #38, #40, #55, #61, #64, #74, #118)
-
Reported bugs and suggestions for other teams in the class (examples: 1, 2)
-
Tools:
-
Integrated and managed the following GitHub plugins:
-
Travis CI - to ensure that our product builds on UNIX platforms: efdb85f
-
AppVeyor - to ensure that our product builds on Windows platforms: 3b5300a
-
Coveralls - to track the coverage of our code: 9846966
-
Codacy - to track the code quality of our project: da9c640
-
Netlify - to deploy a preview of our documentation for PRs
-
-
Integrated a third party library (GSON) to the project (#54).
-
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Tutorial
Isn’t the empty application just ugly? Let us get your modules into the app in just a matter of minutes!
Multistep Commands
Wait… Multistep?
We understand that many may be used to instant, one-step commands when using CLI apps.
Here at Modulo, we strongly believe in smooth and seamless user experiences.
Why type the same thing twice when you can do it once, or even never? Modulo builds onto what you’ve already entered, to reduce the amount of typing required, and make everything easier for you!
And for the testers out there: No, we don’t believe this reduces testability. In fact, it helps you jump right into testing the exciting stuff!
In Modulo, some commands are multistep commands.
These commands require you to go through all the steps before you can move on to other commands.
You will know you’re in the middle of a multistep command when you see the colour of your command box change:
Warning: Quitting the app in the middle of a multistep command will terminate the command!
Any undone operations will be lost.
If you’re interested to help us with improving Modulo, do take a look at our Developer Guide!
Adding Our First Module
Our first command, module
, is a multistep command!
This command helps you to add a module and all of its related events in one go!
Let us start with module m/cs2103
!
This add CS2103 - Software Engineering
into our list of modules.
Now you may see the following prompt:
Simply type in your slot and press Enter! For the purposes of this tutorial, we’ll go with slot 3!
You’ll see two changes. Firstly, the event is added:
Secondly, the command prompt changes:
Key in your lecture slot as well. When you see the below message, you know you’re done!
For users who may be taking CS2103T
instead, go ahead and add that instead.
You’ll only have your lecture to add, so enter your lecture slot when prompted, e.g. G01
.
Viewing the Events we just added
Now that we’ve added our events, it’s time to start planning!
Go ahead to switch to the Event list with the following command: list event
.
You’ll see the events that you just added on the left!
Let us try to view the details for Tutorial 2. We just need to type: view 3
, where 3 is the index of Tutorial 2!
Now we get to see the details of the event on the right!
Tutorial 2 is now the focused event. We call the item that is displayed on the right the focused item. For example, earlier, CS2103 was the focused module.
Adding our first Deadline
With Tutorial 2 in focus, we can now add deadlines easily to it.
Let’s say we want to practice drawing UML diagrams before the tutorial starts.
We can create a suitable deadline by typing: deadline n/Practice UML Diagrams
.
We have now just added the deadline to Tutorial 2, which is the focused event!
Completing our first Deadline
Hopefully, everyone here is hardworking!
We’re now done with creating a deadline.
How about completing it?
Let us complete the deadline we’ve just added by simply typing: done 2
, where 2
is the index of the deadline shown!
And we’re done with the tutorial!
What next?
Of course, Modulo supports CLI usage, i.e. minimal references to the GUI! For that, our commands have variations that do not depend on the focused item, and commands that reduce the need for scrolling!
Want to find out more? Head over to [Features] now!
Adding a module: module
(Multistep)
Adds a module for AY 19/20 Sem 2 to Modulo.
Format: module m/MODULE_CODE
Example:
-
module m/cs2100
This command will kickstart the autopopulation of module events.
Refer to our tutorial for more information!
You will be prompted to enter the slot numbers for all events under this module, e.g. tutorial slot 2, lecture slot 1, lab slot 13, etc.
Example of prompts by Modulo:
-
Enter slot for CS2100 Lab:
-
Enter slot for LAJ1201 Tutorial Type Two:
Adding an event: event
Adds a custom event to a module.
This is perhaps the lengthiest command in Modulo.
Events refer to Tutorials, Lectures, Examinations, etc.
Note: You’re only able to add by slot when going through the multistep module
command!
The event
command requires manual entry.
Format: event m/MODULE_CODE n/EVENT_NAME s/START_DATETIME e/END_DATE v/VENUE [r/REPEAT] [f/FREQUENCY]
[until/REPEAT_STOP_DATE]
Limitations:
-
You cannot add events of the same name under a single module.
-
The datetime format recognised is YYYY-MM-DD HH:MM.
-
The date format is YYYY-MM-DD.
-
The start and end times of the event must be within AY 19/20 Sem 2, and the end time must be after the start time.
-
Your event cannot be longer than a day.
-
By default, the event will not repeat.
-
The default repeat frequency (in weeks) is 1 week.
-
If no repeat end datetime is specified, the event will repeat until the end of the semester (Week 16).
Example:
-
event m/CS2103 n/Make Up Tutorial s/2020-01-30 10:00 e/2020-01-30 11:00 v/COM1-B103 r/YES until/2020-05-08
Pro-tip (GUI-dependent)
If you’re already viewing a module (i.e. focused on a module), you can actually drop the m/MODULE_CODE
!
Adding a deadline: deadline
Adds a deadline to an event. This deadline will be due when the event starts.
Format: deadline m/MODULE_CODE e/EVENT_NAME n/DEADLINE_NAME [r/REPEAT]
Limitations:
-
Deadlines are due when the event starts!
-
Repeat refers to the creation of the same deadline for events of the same type, occuring after the specified event.
-
By default, the deadline will not repeat.
-
The deadline repeat function only works for events of recognised types, e.g.
Tutorial
,Lecture
etc. This is to prevent the creation of irrelevant deadlines for unrecognised types of events.
Pro-tip (GUI-dependent)
If you’re already viewing an event (i.e. focused on an event), you can actually drop the m/MODULE_CODE
AND the e/EVENT_NAME
!
Clearing all data: clear
Clears all data from Modulo and starts the app from a clean slate.
Note: This command cannot be undone! So do use it wisely.
Format: clear
UI
Menu Bar Buttons
These buttons perform the equivalent of list modules
and list events
respectively.
Just click them!
Module and Event List
Click on any of these list items to view them on the right panel.
They perform the equivalent of view INDEX
.
Modules are separated accordingly into their academic years and semesters, and events are separated based on their dates.
Module Details
The right panel displays the details of the module. Only official events added using slots will be listed here.
You can click "Click to view description" to open the Description Window.
Clicking any of the events listed below will also bring you to the first event of that type and slot.
Event Details
The right panel displays the details of the event.
You can check any of the deadlines to mark them as complete or incomplete, performing the equivalent of done INDEX
.
The second tag / label indicates whether all deadlines have been completed.
Status Bar Footer
This status bar displays where your data is being saved at.
You can head over to that folder to find the .json
file for your usage.
Your exported modulo.ics
will also be found in the same directory, unless otherwise specified when exporting.
Appendix
Official Event Types
Recognised official event types are:
-
Tutorial
-
Lecture
-
Sectional
-
Seminar
-
Recitation
-
Laboratory
-
Workshop
-
Packaged Tutorial
-
Packaged Lecture
-
Tutorial Type Two
-
Design Lecture
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Model component
API : Model.java
The Model
,
-
stores a
UserPref
object that represents the user’s preferences. -
stores Modulo data.
-
exposes an unmodifiable
ObservableList<Module>
andObservableList<Event>
that can be 'observed' e.g. the UI can be bound to these lists so that the UI automatically updates when the data in the lists change. -
does not depend on any of the other three components.
As of now, there are two separate ObservableList being maintained, and with each individual module keeping its own list of events as well.
In the future, to better enable the concept of a single source of truth, we can discard the use of a
ObservableList<Event> and expose solely the unmodifiable ObservableList<Module> , and have events accessed through its parent module. |
Alternatively, we can remove the list of events under each module and instead just maintain the two separate
UniqueModuleList and UniqueEventList .
This may, however, make it costly to render the module list, since we now need to search through the entire list of events to find relevant ones.
|
Stateful Logic Feature
Multistep commands are facilitated by StatefulLogic
.
It is an interface with three methods exposed, and currently only has one class implementing it,
AddModuleStatefulLogicManager
.
Let us understand how StatefulLogic
works.
How StatefulLogic
works
As long as a StatefulLogicManager
, which is a class that implements StatefulLogic
, has state, it will take over the LogicManager
in executing the given String
.
Should there be more StatefulLogicManager
instances in play, we will see something like this:
At any point of time, only one StatefulLogicManager
will have state.
The LogicManager
will thus check through them one by one, and will only directly parse the command itself should no StatefulLogicManager
have state.
We can think of these StatefulLogicManager
as "consumers" that consume the input if it has state.
The abstraction of how these StatefulLogicManager
execute the given String
helps to maintain the Single Responsibility Principle.
The interaction between the LogicManager
and StatefulLogicManager
is also facilitated through the interface,
StatefulLogic
, which upholds the Dependency Inversion Principle.
How state is loaded and maintained
State is loaded into StatefulLogicManager
using special CommandResult
subclasses.
In the instance of the
AddModuleStatefulLogicManager
, its state is loaded by passing a AddModuleCommandResult
into its
loadStateFromCommandResult
method.
Let us take a look at the full process, using AddModuleCommand
as an example:
module m/CS2103
commandThere is no difference for the first part from a regular Command
execution.
The differences lie in the state preparation and state loading.
AddModuleCommandResult
The state is prepared during the initialising command, and that command will pack that state into a special subclass of CommandResult
.
This CommandResult
is then passed back to the default LogicManager
.
State loading is then done, if suitable.
AddModuleStatefulLogicManager
The eventList
is passed on to the AddModuleStatefulLogicManager
to be used as state.
As long as the list is not empty, it is considered to have state.
Design Considerations
Aspect: How StatefulLogic
should be positioned with respect to Logic
-
Alternative 1 (current choice): Have
StatefulLogic
exist as a sub-logic-circuit withinLogic
that activates when conditions are met.-
Pros: Reduce repeated code (DRY) and adheres to the Open Closed Principle, since the use of a simple interface makes it easy to create future
StatefulLogic
classes and integrate them. -
Cons:
Logic
now has to take care of checking for state prior to execution of command, as well as follow up on special subclasses ofCommandResult
, thus violating the Single Responsibility Principle.
-
-
Alternative 2: Have
StatefulLogic
extendLogic
and have a separateLogicDispatcher
class that decides whichLogic
to dispatch the command to.-
Pros: Seems to follow the Single Responsibility Principle better.
-
Cons: A lot of repeated code, and to some extent, this may be an adaptation of the current implementation, where the
Logic
class acts as theLogicDispatcher
. Furthermore, it may not be correct to say thatLogic
andStatefulLogic
are similar.StatefulLogic
does operate quite differently fromLogic
.
-
-
Future Extension: It may be worth looking into this and find a better way to handle this logic without breaking the existing Command Pattern. There may be more similarities that can be abstracted out and maximised using OOP.
Aspect: How multistep commands should be implemented and achieved
-
Alternative 1 (current choice): Use
StatefulLogic
to keep relevant states and logic together.-
Pros: Uses OOP to reduce complexity. Adheres to the Open Closed Principle, since each class can just implement
StatefulLogic
, and the exact management of its state is open. -
Cons:
Logic
now needs to handle the pre and post-command logic.
-
-
Alternative 2: Maintain a global state that
Logic
listens to when executing commands.-
Pros: All commands can push state to this global state without having to go through
Logic
, reducing the pre and post-command handling. -
Cons: The implementation of this global state will be highly complex, since the logic required for different states is different as well.
Logic
would be overwhelmed. If a new type of state is required, the changes required will be heavy as well, thus violating the Open Closed Principle.
-
[Proposed] Edit Feature
Currently, Modulo does not allow for the editing of the details of events and deadlines that have been created.
Proposed Implementation
The Edit functionality will be achieved through the introduction of an EditCommand
and its parser EditCommandParser
.
The edit
command will have the following format:
-
Editing events:
edit n/EVENT_NAME m/MODULE_CODE [nn/NEW_NAME] [s/NEW_START_DATETIME] [e/NEW_END_DATETIME] [v/NEW_VENUE]
-
Editing deadlines:
edit d/INDEX m/MODULE_CODE e/EVENT_NAME nn/NEW_NAME
The flow of the command parsing will be as such:
If an EditCommand
is successfully created, it will be executed on the Model
.
It will work mainly through the
setEvent
method already exposed in the Model
interface, as well as a setDeadline
that will be exposed through the parent event.
Here’s an example of how it may look like if the EditCommand
was to modify an event’s venue.
User input: edit n/Tutorial 1 m/CS2103 v/Home
Limitations
Due to the fact that repeated events are not currently being tagged or grouped in any way, it is difficult to allow mass editing of repeated events.
If there’s a need to edit a repeated event, there would be a need to edit the events one by one.
Design Considerations
Aspect: How to easily modify deadlines
-
Alternative 1 (current choice): Manually fetch the deadline from the event and replace.
-
Pros: Easy to implement.
-
Cons: May have slight performance issues in terms of speed, and a lot of coding to be done.
-
-
Alternative 2: Consolidate the deadlines in a UniqueDeadlineList.
-
Pros: Less code to write for this feature specifically, in terms of the
setDeadline
method. -
Cons: Increases complexity of the application, since now three lists are maintained. There is also the same issue with events and modules in terms of the source of truth.
-
Use case: UC01 - Add a deadline
MSS Preconditions: Module data file is loaded properly.
Guarantees: Deadline is added under the correct module and event, with the correct details.
-
User adds a module (UC02).
-
User adds a deadline to the correct module and event, with all the required details.
-
Modulo shows the newly created deadline.
Use case ends.
Extensions
-
2a. User enters incorrect information.
-
2a1. Modulo shows user the correct format for the deadline creation.
-
2a2. User enters new information following the correct format.
Use case resumes from step 3.
-
Use case: UC02 - Add a module
MSS Preconditions: Module data files are loaded properly.
Guarantees: Module is added to the user’s Modulo.
-
User enters a module code, e.g. CS2103.
-
Modulo searches its data files for the module and creates the module using information from the data files.
-
Modulo prompts user to enter the slot for an event type.
-
User enters their slot for the event type.
Steps 3-4 are repeated for as many times as required until the User has entered slots for all event types of the module. -
Modulo shows the newly created module.
Use case ends.
Extensions
-
1a. User enters incorrect data and the wrong module is found.
-
1a1. Steps 2 to 4 are performed.
-
1a2. User deletes the added module (UC03) and its events.
Use case resumes from step 1.
-
-
1b. User enters invalid data and no modules are found.
-
1b1. Modulo informs User that no modules with the given information can be found.
-
1b2. Step 1 repeated until User enters a correct module code.
Use case resumes from step 2.
-
-
3a. User added a module with no events.
-
Use case resumes from step 5.
-
-
3b. User exited Modulo before adding all events.
-
3b1. Modulo saves all events that were already added.
Use case ends.
-
-
4a. User enters an invalid slot.
-
4a1. Modulo cancels the request by the User.
-
4a2. Modulo informs the User of the cancellation.
Use case ends.
-
Appendix A: Instructions for Manual Testing
Given below are instructions to test the app manually.
These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing. |
Launch and Shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with no data. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
-
Shutting down
-
Launch the help window by typing
help
. -
Quit the app by typing
exit
.
Expected: Both the main window and the help window should close.
-
Adding a module
-
Adding CS2103
-
Prerequisites: CS2103 not already added. If added, delete it.
-
Test case:
module m/cs2103
Expected: CS2103 to appear in the list of modules, and app to prompt for further input. -
If prompt is for tutorial, test case:
3
Expected: Tutorial slot 3 should be added. -
If prompt is for lecture, test case:
1
Expected: Lecture slot 1 should be added.
-
Adding an event
-
Adding Exam to CS2103
-
Prerequisites: CS2103 already added. No event named Exam has been added to CS2103 yet.
-
Test case:
event n/Exam m/CS2103 s/2020-01-15 09:00 e/2020-01-15 10:00 v/Home
Expected: Event named Exam should appear in the list of events. To check, enterlist events
. It should be near the top of the list. The details should be as entered.
-
-
Adding recurring events to CS2103
-
Prerequisites: CS2103 already added. No recurring event named Self Study has been added to CS2103 yet.
-
Test case:
event n/Self Study m/CS2103 s/2020-01-20 09:00 e/2020-01-20 10:00 v/Home r/YES until/2020-02-20
Expected: Events named Self Study 1, Self Study 2, until Self Study 5 should appear, with a frequency of 1 week, and stopping before 2020-02-20.
-
Adding a deadline
-
Adding a deadline to CS2103 Exam
-
Prerequisites: CS2103 added. Event named Exam added. No deadline named "Study hard for exam" has been added to the event yet.
-
Test case:
deadline n/Study hard for exam m/CS2103 e/Exam
Expected: Deadline added to Exam. To check, first make sure you’re listing events withlist events
, then enterview INDEX
, whereINDEX
is the index of the event on the list.
-
Completing a deadline
-
Complete Study hard for exam.
-
Prerequisites: CS2103 added. Event named Exam added. Deadline named Study hard for exam added, and is not completed.
-
Test case:
done 2 m/CS2103 e/Exam
Expected: Deadline named Study hard for exam is completed.
-
Switching lists
-
List events
-
Test case:
list events
Expected: If not already listing events, events will now be listed.
-
-
List modules
-
Test case:
list modules
Expected: If not already listing modules, modules will not be listed.
-
View event or module
-
View module
-
Prerequisite: List all modules using the
list modules
command. At least one module. -
Test case:
view 1
Expected: Details of the first module should show on the right panel.
-
-
View event
-
Prerequisite: List all events using the
list events
command. At least one event. -
Test case:
view 1
Expected: Details of the first event should show on the right panel.
-
Find event or module
-
Find event(s) with Self Study in their names
-
Prerequisite: List all events using the
list events
command. Has event(s) named Self Study. -
Test case:
find self study
Expected: All events with Self Study in their names would appear. You can operate on this filtered list.
-
-
Find Software Engineering
-
Prerequisite: List all modules using the
list modules
command. CS2103 added. -
Test case:
find software engineering
Expected: CS2103 should show, along with any other modules that may have Software Engineering in their names.
-
Deleting a module
-
Deleting a module using index while all modules are listed
-
Prerequisites: List all modules using the
list modules
command. Multiple modules in the list. -
Test case:
delete 1
Expected: First module is deleted from the list. All events of that module are deleted. Details of the deleted module shown in the status message. -
Test case:
delete 0
Expected: No module is deleted. No events deleted. Error details shown in the status message. -
Other incorrect delete commands to try:
delete
,delete x
(where x is larger than the list size).
Expected: Similar to previous.
-
-
Deleting a module using search terms while all modules are listed
-
Prerequisites: List all modules using the
list modules
command. CS2103 in the list. Other CS modules in the list. -
Test case:
delete CS2103
Expected: CS2103 is deleted from the list. Details of the deleted module shown in the status message. -
Test case:
delete cs
Expected: All CS modules are deleted. Number of deleted modules shown in the status message. -
Test case:
delete cs2999
Expected: No module is deleted. Information shown in the status message. -
Other incorrect delete commands to try:
delete x
(where x is a term that does not exist in the list).
Expected: Similar to previous.
-
Deleting an event
-
Deleting an event using index while all events are listed
-
Prerequisites: List all events using the
list events
command. Multiple events in the list. -
Test case:
delete 1
Expected: First event is deleted from the list. Details of the deleted event shown in the status message. -
Test case:
delete 0
Expected: No event is deleted. Error details shown in the status message. -
Other incorrect delete commands to try:
delete
,delete x
(where x is larger than the list size).
Expected: Similar to previous.
-
-
Deleting an event using search terms while all events are listed
-
Prerequisites: List all events using the
list events
command. At least one Tutorial 1 in the list. Other Tutorials in the list as well. -
Test case:
delete Tutorial 1
Expected: All events with Tutorial 1 in their names are deleted from the list. Number of deleted events shown in the status message. -
Test case:
delete Tutorial
Expected: All Tutorials are deleted. Number of deleted events shown in the status message. -
Test case:
delete Tvtor!al
Expected: No event is deleted. Information shown in the status message. -
Other incorrect delete commands to try:
delete x
(where x is a term that does not exist in the list).
Expected: Similar to previous.
-
Deleting a deadline
-
Deleting a deadline using index while an event is focused
-
Prerequisites: List all events using the
list events
command. Events in the list. View first event withview 1
. First event has a deadline. -
Test case:
delete d/1
Expected: First deadline is deleted from the list. Details of the deleted deadline shown in the status message. -
Test case:
delete 0
Expected: No deadline is deleted. Error details shown in the status message. -
Other incorrect delete commands to try:
delete
,delete x
(where x is larger than the list size).
Expected: Similar to previous.
-
Exporting data as .ics
file
-
Export data
-
Test case:
export
Expected: Amodulo.ics
file should be created in the same./data/
folder. You can use this.ics
file to add your events and deadlines into your calendar.
-
Viewing help
-
View help
-
Test case:
help
Expected: Help window appears.
-
Clearing Modulo
-
Clear all existing data in Modulo
-
Prerequisites: Some data has been added to Modulo.
-
Test case:
clear
Expected: All data cleared.
-
Saving data
-
Dealing with missing/corrupted data files
-
Corrupt the current save file under
./data/
. The easiest way is to type-
somewhere in one of the saved modules' academic year. Another way may be to add random characters that make the JSON format unreadable. -
Double-click the jar file
Expected: Shows the GUI with no data. -
Delete the current save file under
./data/
. -
Double-click the jar file
Expected: Shows the GUI with no data.
-