By: T08-3
Since: Jan 2019
Licence: MIT
- 1. Introduction
- 2. About this Developer Guide
- 3. Setting up
- 4. Design
- 5. Implementation
- 5.1. Initialization of the Planner and its Semester
- 5.2. Parser / Command Format and Structure
- 5.3. Slot Management
- 5.4. List feature
- 5.5. Find feature
- 5.6. View Month/Week/Day feature
- 5.7. Undo/Redo feature
- 5.8. Data Encryption / Decryption feature
- 5.9. Data Exporting / Exporting feature
- 5.10. Configuration
- 6. Documentation
- 7. Testing
- 8. Dev Ops
- Appendix A: Product Scope
- Appendix B: User Stories
- Appendix C: Use Cases
- Appendix D: Non Functional Requirements
- Appendix E: Glossary
- Appendix F: Instructions for Manual Testing
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.
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. |
-
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 JDK9
. -
IntelliJ IDE
ℹ️IntelliJ by default has Gradle and JavaFx plugins installed.
Do not disable them. If you have disabled them, go toFile
>Settings
>Plugins
to re-enable them.
-
Fork this repo, and clone the fork to your computer
-
Open IntelliJ (if you are not in the welcome screen, click
File
>Close Project
to close the existing project dialog first) -
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 -
Run the
PlanMySem.Main
class (right-click theMain
class and clickRun Main.main()
) and try executing a few commands -
Run all the tests (right-click the
test
folder, and clickRun 'All Tests'
) and ensure that they pass -
Open the
StorageFile
file and check for any code errors -
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. -
Open
MainWindow.java
and check for any code errors-
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
-
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
-
-
Run the
PlanMySem.Main
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.
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:
-
Configure the site-wide documentation settings in
build.gradle
, such as thesite-name
, to suit your own project. -
Replace the URL in the attribute
repoURL
inDeveloperGuide.adoc
andUserGuide.adoc
with the URL of your fork.
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) |
When you are ready to start coding,
-
Get some sense of the overall design by reading Section 4.1, “Architecture”.
-
Take a look at [GetStartedProgramming].
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:
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.
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1
.
The sections below give more details of each 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 theLogic
component. -
Displays
commandResult
to the user viaoutputConsole
.
API :
Logic.java
-
Logic
uses theparser
class to parse the user command. -
This results in a
Command
object which is executed. -
The command execution can affect the
Model
(e.g. adding a Slot). -
The result of the command execution is encapsulated as a
CommandResult
object which is passed back toUi
. -
In addition, the
CommandResult
object can also instruct theUi
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.
API : Model.java
Model
holds the data involved with the application.
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
.
API : Semester.java
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.
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.
|
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.
This section describes some noteworthy details on how certain features are implemented.
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.
Upon launching PlanMySem, the initialization of the Planner
and its Semester
would be implemented via two steps:
-
Automatically generate the academic calendar from the current date.
-
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.
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
.
-
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.
-
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.
-
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:
-
Prefixed parameters such as
n/NAME
,st/START_TIME
,des/DESCRIPTION
, etc. -
Non-Prefixed parameters, A.K.A. keywords, such as
INDEX
,TYPE_OF_VIEW
. etc.
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 emptystrings
such thatstring.isEmpty()
, then the prefix was keyed in but was left empty on purpose.
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. |
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.
-
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.
-
-
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.
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:
-
Parse more formats of date and time.
-
Parse time as a single parameter instead of two.
-
Enhance function calls to retrieve prepended parameters and keywords to handle trivial cases that should invoke
IncorrectCommand
.
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.
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.
Here are the considerations regarding slot management. The choices made were largely due to computation effectiveness.
-
Alternative 1 (current choice): Use of "primitive" data types instead of creating and utilising wrapped objects.
E.g.name
,location
amddescription
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.
-
Alternative 1 (current implementation): Use of
Map
, such asHashMap
to storeDays
that storeSlots
.-
Pros:
HashMap
allows for easier and faster, O(1) access time, access of particularDay
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.
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).
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.
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.
-
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.
-
This feature presents the planner in different formats. This section will detail how this feature is implemented.
Upon entering the view
command with valid parameters (refer to UserGuide.adoc for view
usage), the
following sequence of events is executed:
-
The
Parser
component parses theview
command. It can be parsed into only 3 general types of views which are the month, week or day view. -
This results in a Command object which is executed.
-
The command execution will retrieve data from the
Model
(e.g. retrieving data from the currentSemester
). -
The result of the command execution is encapsulated as a
CommandResult
object which is passed back toUi
. -
In addition, the
CommandResult
object can also instruct theUi
to display results, such as displaying help to the user.
Given below is the Sequence Diagram upon executing 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:
-
Print month header and calculate required amount of whitespace before the 1st of the month.
-
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:
-
Print academic week header.
-
Retrieve all days of the week and for each day, retrieve its slots into an
ArrayList
. -
For each day, print the slot details (only start time, end time and a shortened title) and remove the slot from the
ArrayList
. -
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:
-
Retrieve all slots for the specified day.
-
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:
-
Print academic week header.
-
Retrieve all days of the week.
-
For each day, print all details of all slots via the
displayDayView()
method.
-
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.
-
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.
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.
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
.
ℹ️
|
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.
ℹ️
|
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:
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.
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.
The following activity diagram summarizes what happens when a user executes a new command:
-
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.
-
-
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
andVersionedPlanner
.
-
-
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.
-
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.
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.
-
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.
-
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”.
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.
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.
|
Attribute name | Description | Default value |
---|---|---|
|
The name of the website. If set, the name will be displayed near the top of the page. |
not set |
|
URL to the site’s repository on GitHub. Setting this will add a "View on GitHub" link in the navigation bar. |
not set |
|
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 |
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.
|
Attribute name | Description | Default value |
---|---|---|
|
Site section that the document belongs to.
This will cause the associated item in the navigation bar to be highlighted.
One of: * Official SE-EDU projects only |
not set |
|
Set this attribute to remove the site navigation bar. |
not set |
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 |
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
)
-
Unit tests targeting the lowest level methods/classes.
e.g.PlanMySem.commons.UtilTest
-
Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
e.g.PlanMySem.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.PlanMySem.logicManager.LogicTest
,PlanMySem.parse,ParserTest
We use Travis CI and AppVeyor to perform Continuous Integration on our projects. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.
We use Coveralls to track the code coverage of our projects. See UsingCoveralls.adoc for more details.
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.
Here are the steps to create a new release.
-
Update the version number in
Main.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.
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:
-
Include those libraries in the repo (this bloats the repo size)
-
Require developers to download those libraries manually (this creates extra work for developers)
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
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 |
|
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 |
(For all use cases below, the System is PlanMySem
and the Actor is the user
, unless specified otherwise)
-
MSS:
-
User inputs the first day of school as well as which semester, 1 or 2, that the planner is meant for.
-
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.
-
-
MSS:
-
User inputs add command followed by all the mandatory parameters.
-
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.
-
MSS:
-
User inputs the command to list slots followed by the tag or name of the slot.
-
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.
-
MSS:
-
User inputs the delete command followed by the index or tag of the intended slot.
-
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.
-
MSS:
-
User inputs command to edit a slot along with the tag or index, followed by the parameters to be changed.
-
System changes the specified parameters for the slot.
-
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.
-
MSS:
-
User inputs command to export the planner.
-
System converts planner to .ics format.
-
System saves .ics file in the main directory as "PlanMySem.ics".
-
System displays confirmation message.
Use case ends.
-
-
Should work on any mainstream OS as long as it has Java 9 or higher installed.
-
Should be able to hold up a fully packed schedule, three times over, without a noticeable sluggishness in performance for typical usage.
-
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.
-
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.
-
The system should take up relatively little space on the local machine so as to cater to all students and OS.
-
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.
-
The system should be flexible to allow all kinds of schedules that target users might have.
-
The data should be encrypted to prevent private data from being accessed.
- 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.
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. |
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows a window with a welcome message. 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.
-
-
Deleting a slot while all slots are listed
-
Prerequisites: List all slot using the
list
command. Multiple slots in the list. -
Test case:
delete 1
Expected: First slot is deleted from the Planner. Number of deleted slots is shown, asi
, and details of the slot is shown. -
Test case:
delete 0
Expected: No Slot 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) {give more}
Expected: Similar to previous.
-