Skip to content

Latest commit

 

History

History
1753 lines (1317 loc) · 83 KB

DeveloperGuide.adoc

File metadata and controls

1753 lines (1317 loc) · 83 KB

PlanMySem - Developer Guide

By: T08-3 Since: Jan 2019 Licence: MIT

1. Introduction

Welcome to PlanMySem!

PlanMySem is a text-based (Command Line Interface) scheduling/calendar application that caters to NUS students and staff who prefer to use a desktop application for managing their schedule/calendar. PlanMySem automatically creates a planner that is synchronised according to the NUS academic calendar for the current semester and enables easy creation, editing and deleting of items. Special weeks such as recess week and reading week are taken into account within our unique recursion system. Items can then be efficiently managed via the intuitive tagging system.

PlanMySem is optimized for those who prefer to work with a Command Line Interface (CLI) and/or are learning to work more efficiently with CLI tools. Additionally, unlike traditional calendar/scheduling applications, PlanMySem utilizes minimal resources on the user’s machine while still allowing the user to view their schedules swiftly and efficiently. ​

2. About this Developer Guide

This developer guide provides a detailed documentation on the implementation of all the various features PlanMySem offers. To navigate between the different sections, you could use the table of contents above.

For ease of communication, this document will refer to lessons/activities/events/appointments that you might add into the planner as slots.

Additionally, throughout this developer guide, there will be various icons used as described below.

💡
This is a tip. Follow these suggested tips to make your life much simpler when using PlanMySem!
ℹ️
This is a note. These are things for you to take note of when using PlanMySem.
This is a sign-post dictating important information. These are information that you will surely need to know to use PlanMySem efficiently.
🔥
This is a sign-post informing caution. Please take note of these items and exercise some care.
⚠️
This is a rule. Ensure that you follow these rules to ensure proper usage of PlanMySem. ​

3. Setting up

3.1. Prerequisites

  1. JDK 9 or later

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

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

3.2. Setting up the project in your computer

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

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

  3. Set up the correct JDK version for Gradle

    1. Click Configure > Project Defaults > Project Structure

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

  4. Click Import Project

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

  6. Click Open as Project

  7. Click OK to accept the default settings

  8. Run the PlanMySem.Main class (right-click the Main class and click Run Main.main()) and try executing a few commands

  9. Run all the tests (right-click the test folder, and click Run 'All Tests') and ensure that they pass

  10. Open the StorageFile file and check for any code errors

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

  12. Open MainWindow.java and check for any code errors

    1. Due to an ongoing issue with some of the newer versions of IntelliJ, code errors may be detected even if the project can be built and run successfully

    2. To resolve this, place your cursor over any of the code section highlighted in red. Press ALT+ENTER, and select Add '--add-modules=…​' to module compiler options for each error ​

3.3. Verifying the setup

  1. Run the PlanMySem.Main and try a few commands

  2. Run the tests to ensure they all pass. ​

3.4. Configurations to do before writing code

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

3.4.2. Updating documentation to match your fork

After forking the repo, the documentation will still have the PlanMySem branding and refer to the https://github.com/CS2113-AY1819S2-T08-3/main repo.

If you plan to develop this fork as a separate product (i.e. instead of contributing to https://github.com/CS2113-AY1819S2-T08-3/main), you should do the following:

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

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

3.4.3. Setting up CI

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

After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).

ℹ️
Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork.

Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).

ℹ️
Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based) ​

3.4.4. Getting started with coding

When you are ready to start coding,

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

  2. Take a look at [GetStartedProgramming]. ​

4. Design

4.1. Architecture

Architecture
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.

💡
The .pptx files used to create diagrams in this document can be found in the diagrams folder. To update a diagram, modify the diagram in the pptx file, select the objects of the diagram, and choose Save as picture.

Main has only one class called Main. It is responsible for,

  • At app launch: Initializes the components in the correct sequence, and connects them up with each other.

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

Common represents a collection of classes used by multiple other components.

The following class plays an important role at the architecture level, the App consists of four components:

  • UI: The UI of the App.

  • Logic: The command executor.

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

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

Each of the four components

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

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

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

OverallClassDiagram
Figure 2. Class Diagram of overall application.

How the architecture components interact with each other

The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1.

SDforDeleteSlot
Figure 3. Component interactions for delete 1 command

The sections below give more details of each component. ​

4.2. UI component

UiClassDiagram
Figure 4. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of just commandInput and outputConsole. This application is mainly a text-based application, hence here are not much componenets here.

The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml

The UI component,

  • Executes user commands read from commandInput, using the Logic component.

  • Displays commandResult to the user via outputConsole. ​

4.3. Logic component

LogicClassDiagram
Figure 5. Structure of the Logic Component

API : Logic.java

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

  2. This results in a Command object which is executed.

  3. The command execution can affect the Model (e.g. adding a Slot).

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

  5. In addition, the CommandResult object can also instruct the Ui to display results, such as displaying help to the user.

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

SDforDeleteSlot
Figure 6. Interactions Inside the Logic Component for the delete 1 Command

4.4. Model component

ModelClassDiagram
Figure 7. Overall structure of the Model Component

API : Model.java

Model holds the data involved with the application. ​

4.4.1. Planner component

API : Planner.java

The Planner component,

  • stores a Planner object that represents the entire Planner.

  • stores the data of the entire application.

  • stores the data of the current semester in an unmodifiable Semester. ​

4.4.2. Semester component

The Semester component,

  • stores a Semester object that represents the an academic semester.

  • stores the data of the entire semester in an unmodifiable HashMap<LocalDate, Day>.

  • Semesters essentially hold "days" in which holds slots. ​

4.4.3. Slot component

API : Slot.java

The Slot component,

  • stores a Slot object that represents a time-slot similar to traditional/conventional calendar/scheduling applications.

    • such as outlook or google calendar.

  • stores the data of the slot details as well as start time and duration.

ℹ️
Notice how Slot does not hold its end time but rather it holds the duration. This is simply our design choice as it is meaningless to save both variables. ​

4.5. Storage component

StorageClassDiagram
Figure 8. Structure of the Storage Component

API : Storage.java

The Storage component,

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

  • can save the Planner data in json format and read it back. ​

4.6. Common classes

Classes used by multiple components are in the PlanMySem.common package. ​

5. Implementation

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

5.1. Initialization of the Planner and its Semester

The Planner and its Semester has to be initialized for PlanMySem to work as all other features of PlanMySem would interact with this Semester object. The initialization is automated and dynamic to ensure sustainability. ​

5.1.1. Current Implementation

Upon launching PlanMySem, the initialization of the Planner and its Semester would be implemented via two steps:

  1. Automatically generate the academic calendar from the current date.

  2. Setup current Semester from the academic calendar.

The academic calendar is dynamically generated by invoking the function generateSemester in the Semester class. The function will first retrieve the current date from the system clock to determine which academic year it is. As a new academic year starts from August, it can be determined from the month of the current date.

  • If the current date is before August, the current academic year is "the previous year / current year".
    e.g. If the date is 25/3/2019, the academic year is "2018 / 2019".

  • If the current date is after August, the current academic year is "the current year / next year".
    e.g. If the date is 25/8/2019, the academic year is "2019 / 2020".

After determining the academic year, the details of the semesters will be generated. All the weeks of the academic year can be calculated from the first day of semester 1 since each semester has a fixed amount of weeks.

ℹ️
Semester 1 of the academic year starts with an orientation week and will always begin from the first Monday of August.
  • Semester 1 has 18 weeks (inclusive of orientation week) and semester 2 has 17 weeks.

  • The vacation between semester 1 and 2 has 5 weeks.

  • The vacation between academic years will have 12 or 13 weeks depending on the starting week of the next academic year.

Each week of the year will correspond to an academic week and this information will be stored in a HashMap. This HashMap can be used to determine the academic week given a date (by finding out the week of the year for that date). The figures below shows an example of the relation between academic week and the week of the year for academic year 2018/2019.

Sem1
Figure 9. Weeks in academic year 2018/2019, Semester 1.
Sem2
Figure 10. Weeks in academic year 2018/2019, Semester 2.

Hence, the information listed below can be determined from the current date.

  • Current academic week

  • Current academic semester

  • Current academic year

  • Number of weeks in current academic semester

  • Start date of current academic semester

  • End date of current academic semester

These information would be assigned to the Semester object upon initialization of the Planner. ​

5.1.2. Design Considerations

Aspect: Generation of academic calendar
  • Alternative 1 (current choice): Generate academic calendar by performing calculations from the current date.

    • Pros: Generation of academic calendar is dynamic and will work for future dates.

    • Cons: Computationally expensive as many operations have to be performed.

  • Alternative 2: Retrieve academic calendar from a pre-generated file.

    • Pros: Generation of academic calendar is efficient and not prone to calculation errors.

    • Cons: Requires the pre-generated file which may be accidentally edited or deleted by the user. ​

5.2. Parser / Command Format and Structure

Due to the flexibility and huge variation of the envisioned command format and structures, it was decided that it was more appropriate to create a new Parser instead of relying on the existing regex implementation in AB3 for heavy parsing.

The AB3 parser was heavily modified to serve unordered command parameters as well as to allow more flexibility such that mistakes in commands will still be interpreted as valid as long as the "minimal" set of parameters are present. Regex is currently only used to retrieve the command keywords and arguments. Arguments are then parsed via 2 different methods/techniques according to the format and structure of the command keyword. ​

5.2.1. Current Implementation

  • Ordering of parameters are ignored when possible.

  • Repeated parameters are ignored. The first parameter of the same "type" are taken as valid, the rest are discarded.

  • Alternate formats of commands are implemented to give freedom of choice and cater to different types of users such as different personalities and comfort levels.

  • Shortened versions of command keywords are implemented to give ways for users to shortened commands and be more efficient.

Hence, parameters in PlanMySem can be categorised into 2 categories:

  1. Prefixed parameters such as n/NAME, st/START_TIME, des/DESCRIPTION, etc.

  2. Non-Prefixed parameters, A.K.A. keywords, such as INDEX, TYPE_OF_VIEW. etc. ​

Parsing Prefixed Parameters

To retrieve the set of parameters, the function private static HashMap<String, Set<String>> getParametersWithArguments(String args) can be called. The function returns a HashMap data structure, specifically HashMap<String, Set<String>>, to hold parameters, allowing for easy, quick and efficient access to specific prefixes and its keyed in parameters; O(1) access, insertion and removal.

However, this means that exceptions have to be manually taken care of, at the stage of parsing, this exception would particularly be ParseException.

The following are cases in which ParseException is invoked:

  • When the returned set is null, then the prefix and parameters was not keyed in at all.

  • When the returned set is not null but contains empty strings such that string.isEmpty(), then the prefix was keyed in but was left empty on purpose. ​

Parsing Keywords

Here, keywords are thought of as parameters that are not prefixed. In PlanMySem, keywords are utilized in command structures when they are to be used alone or when order of parameters are important. In such cases, there is no logical need for prefixing as the meaning of these parameters can be identified.

The function private String getStartingArgument(String args) provides this functionality. Here, IncorrectCommand is invoked due to different circumstances:

  • When the keyword is null, then the parameter was not keyed in.

  • When the keyword data type does not match the intended, then the parameter was keyed in wrongly or is mis-ordered.

ℹ️
Additional keywords are purposefully not handled to provide ease of use and cater to user mistakes. ​

5.2.2. Design Considerations

Here are the considerations that led to the new parsing system. The choices made were largely due to the fact that they provide a better user experience and ease of use. ​

Aspect: Handling repeated parameters
  • Alternative 1 (current choice): Accept and ignore repeated parameters when possible.

    • Pros: Less Computationally expensive and allows users to make minor mistakes.

    • Cons: User errors may be misinterpreted and hence wrong actions may be executed.

  • Alternative 2: Accept repeated parameters only when as necessary.

    • Pros: Errors are shown to the user so that the invalid command may be fixed.

    • Cons: May hinder user experience, ease of use. ​

Aspect: Handling order of parameters
  • Alternative 1 (current choice): Parse parameters without regards to order.

    • Pros: Greater user experience due to greater ease of use.

    • Cons: More computationally expensive and tougher development process due to more cases to care for, requires manual parsing.

  • Alternative 2: Accept only a specific ordering of parameters.

    • Pros: Less computationally expensive and short development process, able to use existing regex solutions in AB3.

    • Cons: Greatly hinder user experience as order of parameters have no relation to meaning of commands.

Alternative 1 was chosen due to the team’s priority in providing a better user experience and allow ease of use.

5.2.3. Future Implementation

Though the current implementation has much flexibility, there is more that can be done to elevate user experience to the next level. These are some possible enhancements:

  1. Parse more formats of date and time.

  2. Parse time as a single parameter instead of two.

  3. Enhance function calls to retrieve prepended parameters and keywords to handle trivial cases that should invoke IncorrectCommand. ​

5.3. Slot Management

Slot Management involves mainly the interaction between the users and their slots.

The section below will describe in detail the Current Implementation, Design Considerations and Future Implementation of the Slot Management. ​

5.3.1. Current Implementation

Users are able to perform three actions (or commands), though a small variety of methods, involving slots:

  • Add

    • Add multiple slots via the recursion system.

    • Add a single slot via omitting the recursion system.

  • Edit

    • Edit multiple slots via tags.

    • Edit a single slot via index.

  • Delete

    • Delete slots via tags.

    • Delete a single slot via index.

The Add command heavily relies on the recursion system to select multiple dates in which to add the same slot to multiple days. Additionally, the Add command also allows users to input tags to tag slots.

The Edit and Delete command then makes use of the tagging system to then select multiple slots for editing/deleting. ​

5.3.2. Design Considerations

Here are the considerations regarding slot management. The choices made were largely due to computation effectiveness. ​

Aspect: Wrapping of primitive data types in Slot
  • Alternative 1 (current choice): Use of "primitive" data types instead of creating and utilising wrapped objects.
    E.g. name, location amd description are not wrapped but "primitive".

    • Pros: Allows for more flexible code to account for flexible parsing (as needed in this application).

    • Cons: Bigger code base and duplicated code.

  • Alternative 2: Wrap "primitive" data types.

    • Pros: Less errors in handling invalid values.

    • Cons: May cause inflexibility in writing code to account for flexible parsing.

Alternative 1 was chosen as, in this case of PlanMySem, there is a need to achieve varied and flexible commands and as such, it is not necessary to handle invalid values with the innate Model objects as these are taken care of when parsing.

Aspect: Storing and accessing Slots
  • Alternative 1 (current implementation): Use of Map, such as HashMap to store Days that store Slots.

    • Pros: HashMap allows for easier and faster, O(1) access time, access of particular Day according to date.

    • Cons: This requires splitting of the calendar into days, as such there is no easy way to account for Slots that occur across days.

  • Alternative 2: Store Slots in a huge list.

    • Pros: Allows for easier access by "index" and offers flexibility, for example, in the time of slots.

    • Cons: Expensive to access, add and remove items. Furthermore, it is extremely expensive to collect slots that occur in a day, a very important and most likely to be a commonly used feature.

Alternative 1 was chosen as the benefits of quick and easy access to days outweigh the disadvantages involve with forbidding slots than span over a day. After all, there are few cases of slots crossing the boundaries of a day, over midnight.

5.3.3. Future Implementation

Create a class to hold Days, instead of utilising a HashMap

Currently, Days are held in a HashMap of key LocalDate and value Day. While this works without any loss in performance, this causes duplication of code and removes some key concepts of abstraction. For example, there are code blocks dedicated to retrieving days or slots that could have been placed into this new class. This is an issue as these code have nothing to do with for instance, Semester but they are placed there.

Therefore, this needs to be implemented in the future to achieve less coupling, more cohesion and respect the Single Responsibility Principle (SRP), Open-Closed Principle (OCP) and Separation of Concerns Principle (SoC). ​

Planner to hold multiple Semesters

While PlanMySem now allow users to work on the current semester, it is unable to cater to future semesters. For instance when a semester is about to end, users are not able to plan ahead for the coming semester.

This is an issue that plagues user experience and is a significant problem. To solve this issue, Planner needs to hold multiple semesters in a List and more features need to be included to allow saving, loading and switching of semesters and etc. ​

5.4. List feature

5.4.1. Current Implementation

The list function supports searching using a single keyword.

The keyword is compared to all names/tags of all Slots existing in Planner.

If an exact match is found, the Slot will be added to the output list. ​

5.5. Find feature

5.5.1. Current Implementation

The find function supports searching using a single keyword.
All existing Slots are weighted based on their name/tag’s Levenshtein Distance from the keyword.

A low Levenshtein Distance is attributed to a high level of similarity between the name/tag and the keyword. (A value of 0 constitutes an exact match.)

The weighted Slots are inserted into a PriorityQueue and the closest matching Slots will be polled into the output list.

5.5.2. Design Considerations

Aspect: What constitutes a positive search result in find command
  • Alternative 1: Positive search result by strictly matching the entered keyword

    • Pros: Easy to implement.

    • Cons: Search must be exact, typos or an incomplete keyword will yield incorrect results. Nothing different from List feature.

  • Alternative 2: Positive search result as long as name/tag contains the keyword.

    • Pros: Searches will detect names/tags similar to the keyword.

    • Cons: Output list will be longer. May become excessively long if short keyword is provided.

  • Alternative 3 (current implementation): Store the search results in a PriorityQueue ordered by their Levenshtein distances from the search keyword.

    • Pros: Will also consider searches that are similar to the desired name/tag and will account for a typo or an incomplete keyword

    • Cons: Added complexities in finding and searching. ​

5.6. View Month/Week/Day feature

This feature presents the planner in different formats. This section will detail how this feature is implemented. ​

5.6.1. Current Implementation

Upon entering the view command with valid parameters (refer to UserGuide.adoc for view usage), the following sequence of events is executed:

  1. The Parser component parses the view command. It can be parsed into only 3 general types of views which are the month, week or day view.

  2. This results in a Command object which is executed.

  3. The command execution will retrieve data from the Model (e.g. retrieving data from the current Semester).

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

  5. In addition, the CommandResult object can also instruct the Ui to display results, such as displaying help to the user.

Given below is the Sequence Diagram upon executing the view month command.

ViewMonthSequenceDiagram
Figure 11. Interactions between components for the view month Command

The 3 general types of view (month, week, day) are generated by the methods displayMonthView(), displayWeekView(), displayDetailedWeekView() and displayDayView() from the ViewCommand class.

displayMonthView() displays all the months of the current semester in a monthly calendar format. Each academic week of the semester is also indicated in the display. The implementation of this function can be broken down into 2 parts:

  1. Print month header and calculate required amount of whitespace before the 1st of the month.

  2. Print all days of the month using a loop.

    • Append academic week after each Saturday or last day of month.

displayWeekView() displays the weekly calendar format of a specified week. The implementation of this function can be broken down into the following steps:

  1. Print academic week header.

  2. Retrieve all days of the week and for each day, retrieve its slots into an ArrayList.

  3. For each day, print the slot details (only start time, end time and a shortened title) and remove the slot from the ArrayList.

  4. Repeat step 3 until the ArrayList of slots for each day is empty.

displayDayView() displays the details of all slots of a specified day. The implementation of this function can be broken down into 2 parts:

  1. Retrieve all slots for the specified day.

  2. Print all details of each slot found.

displayDetailedWeekView() displays the details of all slots of a specified week since displayWeekView() only shows a formatted and summarised week view. The implementation of this function can be broken down into the following steps:

  1. Print academic week header.

  2. Retrieve all days of the week.

  3. For each day, print all details of all slots via the displayDayView() method. ​

5.6.2. Design Considerations

Aspect: Functionality of view week command
  • Alternative 1 (current choice): Option for user to display a formatted summarised week view or a detailed week view.

    • Pros: The formatted summarised week view is uncluttered. User given the choice and flexibility for the week view.

    • Cons: User is required to spend a little more time to specify an additional parameter in the view week command.

  • Alternative 2: Only a single formatted week view which displays details of all slots in the specified week.

    • Pros: Efficient for the user as user is only required to enter a single command to view all details of all slots.

    • Cons: The formatted week view will be too cluttered as there are too many slots and lots of details. Formatting is an issue as well as details of each slot can be of varying lengths. ​

5.7. Undo/Redo feature

5.7.1. Current Implementation

The undo/redo mechanism is facilitated by VersionedPlanner.

ℹ️
Only Add, Edit and Delete commands can be undone/redone.

It extends Planner with an undo/redo history, stored internally as an plannerStateList and currentStatePointer. Additionally, it implements the following operations:

  • VersionedPlanner#commit() — Saves the current planner state in its history.

  • VersionedPlanner#undo() — Restores the previous planner state from its history.

  • VersionedPlanner#redo() — Restores a previously undone planner state from its history.

These operations are exposed in the Model interface as Model#commitPlanner(), Model#undoPlanner() and Model#redoPlanner() respectively.

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

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

UndoRedoStartingStateListDiagram

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

UndoRedoNewCommand1StateListDiagram

Step 3. The user executes add n/CS2113T …​ to add a new slot. The add command also calls Model#commitPlanner(), causing another modified planner state to be saved into the plannerStateList.

UndoRedoNewCommand2StateListDiagram

ℹ️
If a command fails its execution, it will not call Model#commitPlanner(), so the planner state will not be saved into the plannerStateList.

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

UndoRedoExecuteUndoStateListDiagram

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

The following sequence diagram shows how the undo operation works:

UndoRedoSequenceDiagram

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

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

Step 5. The user then decides to execute the command list. Commands that do not modify the planner, such as list, will usually not call Model#commitPlanner(), Model#undoPlanner() or Model#redoPlanner(). Thus, the plannerStateList remains unchanged.

UndoRedoNewCommand3StateListDiagram

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

UndoRedoNewCommand4StateListDiagram

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

UndoRedoActivityDiagram

5.7.2. Design Considerations

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

    • Pros: Easy to implement.

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

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

    • Pros: Will use less memory (e.g. for delete, just save the person being deleted).

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

Aspect: Data structure to support the undo/redo commands
  • Alternative 1 (current choice): Use a list to store the history of planner states.

    • Pros: Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.

    • Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both HistoryManager and VersionedPlanner.

  • Alternative 2: Use HistoryManager for undo/redo

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

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

5.8. Data Encryption / Decryption feature

The storage file "PlanMySem.txt" is encrypted to prevent easy access of the user’s calendar. We are encrypting and decrypting the data using the Java Cypher class. This feature is implemented through creating a Encryptor that contains encrypt and decrypt methods. The encrypt method takes a String object as an argument and returns a encrypted String object. The decrypt method takes in a String object as an argument and returns the decrypted message as a String object.

The encryption is done using AES/CBC/PKCS5Padding. The key used for encryption/decryption is generated randomly and stored in a file named "KeyStorage.jceks". No password is required from the user to retrieve this key, but a password input can be added in the KeyStorage.java class to improve security.

A initialization vector (IV) is required for the Cipher Block Chain (CBC) mode of encryption. A random IV is generated and appended at the beginning of the data before being stored. The IV is then retrieved from the same file to decrypt the data.

Encryption of the data is done automatically before the file is saved. In the implementation, the AdaptedPlanner object is first marshaled into a StringWriter before being encrypted and written into the file. This is to ensure that the data is JAXB formatted and the save algorithm is unaffected. Similarly, decryption of the data is done automatically before it is loaded. In the implementation, the file is read and decrypted and parsed into a StringReader object. The StringReader object is then un-marshaled and loaded. This is to ensure that the file is converted back into a JAXB object before being loaded and the load algorithm is unaffected. ​

5.9. Data Exporting / Exporting feature

The user can export the current planner into a .ics file to use in external calendar applications. The .ics file will contain the names of the slots in the SUMMARY field and the descriptions in the DESCRIPTION field. This command automatically exports into the main directory and names the file “PlanMySem.ics”. Future updates can include user input to allow saving the file in another directory and naming the file. We have chosen to use the iCalendar format due to its popularity and it’s use in applications such as Google Calendar, Microsoft Outlook and NUSmods.

In our implementation, we have chosen not to export the tags into the .ics file. This is because iCalendar does not have in-built tag fields. This means that other other applications that import .ics will not be able to use the tags. ​

Aspect: Exporting tags into .ics file.
  • Alternative 1 (current choice): Ignore tags when exporting.

    • Pros: Easier to implement as iCalendar does not have in-built tag fields.

    • Cons: Not all the information about the slots will be retained.

    • Reason for choice: We do not have much control over other applications, and importing and exporting .ics within PlanMySem can be done using the storage .txt file.

  • Alternative 2: Use the notes field and a tag identifier to save the tags.

    • Pros: All the information from the semester will be exported.

    • Cons: Requires other applications to be coded to read these tag identifiers and also to store and use the tags in their functions. ​

5.10. Configuration

There is no need for manual configuration of the Semester as it is initialized dynamically as mentioned in Section 5.1, “Initialization of the Planner and its Semester”. ​

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

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

6.2. Publishing Documentation

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

6.3. Converting Documentation to PDF format

We use Google Chrome for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.

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

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

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

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

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

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

6.4. Site-wide Documentation Settings

The build.gradle file specifies some project-specific asciidoc attributes which affects how all documentation files within this project are rendered.

💡
Attributes left unset in the build.gradle file will use their default value, if any.
Table 1. List of site-wide attributes
Attribute name Description Default value

site-name

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

not set

site-githuburl

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

not set

site-seedu

Define this attribute if the project is an official SE-EDU project. This will render the SE-EDU navigation bar at the top of the page, and add some SE-EDU-specific navigation items.

not set

6.5. Per-file Documentation Settings

Each .adoc file may also specify some file-specific asciidoc attributes which affects how the file is rendered.

Asciidoctor’s built-in attributes may be specified and used as well.

💡
Attributes left unset in .adoc files will use their default value, if any.
Table 2. List of per-file attributes, excluding Asciidoctor’s built-in attributes
Attribute name Description Default value

site-section

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

* Official SE-EDU projects only

not set

no-site-header

Set this attribute to remove the site navigation bar.

not set

6.6. Site Template

The files in docs/stylesheets are the CSS stylesheets of the site. You can modify them to change some properties of the site’s design.

The files in docs/templates controls the rendering of .adoc files into HTML5. These template files are written in a mixture of Ruby and Slim.

⚠️

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

7. Testing

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

7.2. Types of tests

  1. Unit tests targeting the lowest level methods/classes.
    e.g. PlanMySem.commons.UtilTest

  2. Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
    e.g. PlanMySem.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. PlanMySem.logicManager.LogicTest, PlanMySem.parse,ParserTest

7.3. Troubleshooting Testing

Problem: Logic fails with a NullPointerException.

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

  • Solution: Execute Gradle task processResources. ​

8. Dev Ops

8.1. Build Automation

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

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

8.3. Coverage Reporting

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

8.4. Documentation Previews

When a pull request has changes to asciidoc files, you can use Netlify to see a preview of how the HTML version of those asciidoc files will look like when the pull request is merged. See UsingNetlify.adoc for more details. ​

8.5. Making a Release

Here are the steps to create a new release.

  1. Update the version number in Main.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. ​

8.6. Managing Dependencies

Projects often depends on third-party libraries. For example, PlanMySem depends on the Jackson library for JSON 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: Product Scope

Target user profile:

  • NUS students and staff

  • has a need to manage a significant number of categories, activites, timeslots, tags in a calendar

  • prefer desktop apps over other types

  • prefers having a completely offline calendar

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

Value proposition: manage personal planner faster than a typical mouse/GUI driven app and caters to users who prefer an offline solution due to the current technology climate where information privacy/data privacy/data protection has become an uncertainty ​

Appendix B: User Stories

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

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

* * *

new user

see usage instructions

refer to instructions if I do not know how to use the app

* * *

new user

initialize the calendar by year and semester

align the planner with the school’s academic calendar

* * *

user

add a slot

store all my slots in the calendar

* * *

user

delete a slot

remove all slots from my calendar that have been cancelled

* * *

user

edit a slot

edit slots from my calendar that have been postponed/ brought forward/ changed

* * *

user

list all slots

view all slots on the planner which I have activities on

* * *

user

recurse a slot

easily create all the relevant time slots for a module to recur every week

* * *

user

view all slots on a certain day

conveniently view my planner for the day

* * *

user

view all slots on a certain week

conveniently view my planner for the week

* * *

user

view all slots on a certain month

conveniently view my planner for the month

* * *

user

add details to a slot

record information related to the slot

* * *

user

undo a command

easily revert my changes and restore a previous state

* * *

user

redo a command

easily revert my undo command in case I mistakenly undo too far

* *

user

view the planner in a graphic calendar format

easily view my schedule for the day/week/month/semester

* *

user

view a slot

view the details of a specific activity I am looking for

* *

user

remove tags on a time slot

remove unused/ unnecessary tags from an activity

* *

user

edit tags

rename tags

* *

user

list all tags

view all existing tags

* *

user

view color coded categories

easily view the different types of categories

* *

user

encrypt my planner data

ensure the privacy of my planner

* *

user

decrypt an encrypted planner data

securely transfer the planner data to be operated on another device

*

user

import semester timetable (.ics files)

transfer my existing activities into the new timetable

*

user

export semester timetable (.ics files)

view my timetable on another platform

*

user

receive notifications of upcoming activities

be reminded of important upcoming activities

*

user

view recess week and exam week

view specifically the weeks to rest

*

user

view vacations

plan my schedule on vacation days or special semesters

*

user

favourite an activity

prioritise important activities

*

user

view public holidays

be aware of upcoming public holidays

*

user

compare my timetable with someone else’s

find a common time slot for a meeting

*

user

generate summary reports

view how much time I spent attending training / tutorials

Appendix C: Use Cases

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

C.1. Use case: Initialize Calendar

  • MSS:

    1. User inputs the first day of school as well as which semester, 1 or 2, that the planner is meant for.

    2. System generates a new timetable which is aligned with the academic semester. Weeks 1 to 13 as well as the Recess and Reading Weeks are labelled.

    Use case ends. ​

C.2. Use Case: Add a slot

  • MSS:

    1. User inputs add command followed by all the mandatory parameters.

    2. System reflects the additions to the planner.

      User case ends.

  • Extensions: :: 1a. System detects an error in the entered data.

    :

    1a1. System outputs error message.

    User case ends.

:: 1a. System detects insufficient parameters in the entered data. :::: 1a1. System outputs error message.

+ User case ends. ​

C.3. Use Case: List slots

  • MSS:

    1. User inputs the command to list slots followed by the tag or name of the slot.

    2. System displays all slots with the specified name or tag with their indexes.

      Use case ends.

  • Extensions: :: 1b. Planner is empty.

    :

    1b1. System outputs error message.

    Use case ends. :: 1b. Tag or name does not exist in the planner. :::: 1b1. System outputs error message.

    + Use case ends. ​

C.4. Use Case: Delete a slot

  • MSS:

    1. User inputs the delete command followed by the index or tag of the intended slot.

    2. System deletes the intended slot from the planner and outputs confirmation message.

      Use case ends.

  • Extensions: :: 1a. Tag or index does not exist in the planner.

    :

    1a1. System outputs error message.

    Use case ends. ​

C.5. Use Case: Edit a slot

  • MSS:

    1. User inputs command to edit a slot along with the tag or index, followed by the parameters to be changed.

    2. System changes the specified parameters for the slot.

    3. System reflects the slots as well as the perimeters changed.

      Use case ends.

  • Extensions: :: 1a. Tag or index does not exist in the planner.

    :

    1a1. System outputs error message.

    Use case ends.

:: 1b. System detects an error in the entered data. :::: 1b1. System outputs error message.

+ Use case ends.

C.6. Use Case: Export planner

  • MSS:

    1. User inputs command to export the planner.

    2. System converts planner to .ics format.

    3. System saves .ics file in the main directory as "PlanMySem.ics".

    4. System displays confirmation message.

      Use case ends. ​

C.7. Use Case: View all Activities in a Day

  • MSS:

    1. User inputs the command to view all slots along with the time frame.

    2. System displays all the slots for that specified time frame.

      Use case ends.

{More to be added}

Appendix D: Non Functional Requirements

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

  2. Should be able to hold up a fully packed schedule, three times over, without a noticeable sluggishness in performance for typical usage.

  3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.

  4. The system should respond relatively quickly to user commands so as to not make the user wait around; this is an advantage of using PlanMySem.

  5. The system should take up relatively little space on the local machine so as to cater to all students and OS.

  6. The system should be easy to use, intuitive and simple, such that any student regardless of past experience with calendar/scheduling software is able to use it.

  7. The system should be flexible to allow all kinds of schedules that target users might have.

  8. The data should be encrypted to prevent private data from being accessed. ​

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Levenshtein Distance

The Levenshtein distance is a string metric for measuring difference between two sequences.
Informally, the Levenshtein distance between two words is the minimum number of single-character edits (i.e. insertions, deletions or substitutions) required to change one word into the other.

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

F.1. Launch and Shutdown

  1. Initial launch

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

    2. Double-click the jar file
      Expected: Shows a window with a welcome message. The window size may not be optimum.

  2. Saving window preferences

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

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

F.2. Deleting a slot

  1. Deleting a slot while all slots are listed

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

    2. Test case: delete 1
      Expected: First slot is deleted from the Planner. Number of deleted slots is shown, as i, and details of the slot is shown.

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

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

F.3. Saving data

  1. Dealing with missing/corrupted data files

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