By: AY1920S2-CS2103T-F09-3
Since: Feb 2020
Licence: MIT
- 1. Setting up
- 2. Design
- 3. Implementation
- 4. Documentation
- 5. Testing
- 6. Dev Ops
- Appendix A: Product Scope
- Appendix B: User Stories
- Appendix C: Use Cases
- 7. Non Functional Requirements
- Appendix D: Glossary
- Appendix E: Instructions for Manual Testing
1. Setting up
Refer to the guide here.
2. Design
2.1. Architecture
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
The .puml files used to create diagrams in this document can be found in the diagrams folder.
Refer to the Using PlantUML guide to learn how to create and edit diagrams.
|
-
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.
Commons
represents a collection of classes used by multiple other components.
The following class plays an important role at the architecture level:
-
LogsCenter
: Used by many classes to write log messages to the App’s log file.
The rest of the App consists of five components.
Each of the first 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 LogicManager.java
class.
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
.
module delete CS1010
commandThe sections below give more details of each component.
2.2. UI component
API : Ui.java
Section by: Daryl
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, ModuleListPanel
, StatusBar
etc.
All these, including the MainWindow
(excluding AvatarImage
), inherit from the abstract UiPart
class.
The UI
component uses JavaFx UI framework.
The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder.
For example, the layout of the MainWindow
is specified in MainWindow.fxml
.
The UI
component,
-
Executes user commands using the
Logic
component. -
Listens for changes to
Model
data so that the UI can be updated with the modified data.
2.2.1. MainWindow
The MainWindow
class serves as the hub for all the UI components, and contains the following UI classes:
-
AvatarSelectionPanel
- Avatar selection screen on first-time startup. -
CommandBox
- Command box for user input. -
CommandReceivedPanel
- Displays the last command entered. -
ResultDisplay
- Displays the resultant message of the command entered. Also contains the avatar image. -
ModuleListPanel
- Panel displaying the modules input into the system. -
RequirementListPanel
- Panel displaying the requirements input into the system. -
ProgressSidePanel
- Panel displaying the user’s academic progress and fundamental details (Eg. CAP).
The MainWindow
coordinates the development between the backend and frontend components to induce a visible change to the interface.
This is done through the executeCommand(String commandText, Model model)
method. Upon user input, the logic
class
executes the command in commandText
, and the model is updated to reflect the changes. Subsequently, after the model
has been updated, the following UI classes ResultDisplay
and ProgressSidePanel
are refreshed as a result.
2.2.2. AvatarSelectionPanel
The AvatarSelectionPanel
class displays the avatar selection screen upon first-time startup. Users will choose an
that will act as a guide throughout their usage of the application.
2.2.3. CommandBox
The CommandBox
class contains an editable TextArea
JavaFX component which allows the user to enter input commands.
2.2.4. CommandReceivedPanel
The CommandReceivedPanel
class contains a panel that shows the last command entered into the system.
Here is an example of how the CommandReceivedPanel
works:
-
Command executed on
MainWindow
. -
MainWindow
calls the methodrefreshCommandReceivedPanel
, which refreshes theCommandReceivedPanel
. -
CommandReceivedPanel
updates its JavaFXLabel
with theString
of the command given. -
CommandReceivedPanel
displays visible change on the interface. -
refreshCommandReceivedPanel
ends execution.
2.2.5. ResultDisplay
The ResultDisplay
class shows the resultant message generated from the user’s input.
The avatar will also showcase a different expression according to the success of the command given.
Here is an example of how the ResultDisplay
works:
-
Command executed on
MainWindow
. -
MainWindow
calls the methodrefreshResultDisplayAvatar
, which refreshes theAvatar
inResultDisplay
. -
ResultDisplay
updates its JavaFXImageView
according to theAvatar
of the command given. In this case, when no exception is thrown, theAvatar
displays that of a positive expression. -
refreshResultDisplayAvatar
ends execution. -
ResultDisplay
displays visible change on the interface. -
MainWindow
calls the methodrefreshResultDisplay
, which refreshes the resultant message displayed inResultDisplay
. -
ResultDisplay
updates its JavaFXTextArea
according to theCommandResult
of the command given. In this case, the TextArea will display the 'success' message generated as a result of the command. -
ResultDisplay
displays visible change on the interface. -
refreshResultDisplay
ends execution.
2.2.6. ModuleListPanel
The ModuleListPanel
class contains the ObservableList<Module>
JavaFX component allowing for a list view of the
components inside it, in this case, a list of ModuleCard
objects.
The contents of the list are dependent on the modules
that the user has input into the system. Each module will be
displayed as a ModuleCard
object.
2.2.7. RequirementListPanel
The RequirementListPanel
class contains the ObservableList<Requirement
JavaFX component allowing for a list view of
the components inside it, in this case, a list of RequirementCard
objects.
The contents of the list are dependent on the requirements
that the user has input into the system. Each requirement
will be displayed as a RequirementCard
object.
2.2.8. ProgressSidePanel
The ProgressSidePanel
class contains the user’s academic progress, as well as essential information. The following
information is displayed on the ProgressSidePanel
:
-
Course
name -
Inspiring quote from
QuoteGenerator
-
Modular Credits Progress Indicator
-
Semesters
left -
Current Cumulative Average Point (C.A.P)
Here is an example of how the ProgressSidePanel
works:
-
Command executed on
MainWindow
. -
Model
is updated. -
MainWindow
calls the methodrefreshProgressPanel
, which refreshes theProgressSidePanel
. -
ProgressSidePanel
usesModel
to obtain the correspondingCourseInfo
information:-
Name
-
Credits
-
Cap
-
Semesters
-
-
ProgressSidePanel
executes corresponding JavaFX methods to update displayed information. -
ProgressSidePanel
shows visible change on the interface. -
refreshProgressPanel
ends execution.
2.2.9. Other Components
In addition to the main UI components grouped in the MainWindow
class, these are the other UI components that are
relevant to the interface:
-
AvatarImage
- Contains the image of the avatar. -
ModuleCard
- Individual card containing the relevant information of the module. List ofModuleCard
contained in theModuleListPanel
. -
RequirementCard
- Individual card containing the relevant information of the requirement. List ofRequirementCards
contained in theRequirementListPanel
. -
HelpWindow
- Pop-up window containing the link the User Guide, as well as a list of all the commands in the application.
2.3. Logic component
API :
Logic.java
-
Logic
uses theCourseBookParser
class to parse the user command. -
This results in a
Command
object which is executed by theLogicManager
. -
The command execution can affect the
Model
(e.g. adding a module). -
The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. -
In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, 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.
delete 1
Command
The lifeline for ModuleDeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
2.4. Model component
API : Model.java
The Model
,
-
stores a
UserPref
object that represents the user’s preferences. -
stores the Course Book data.
-
exposes an unmodifiable
ObservableList<Requirement>
that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. -
exposes an unmodifiable
ObservableList<Module>
that can be 'observed'. -
does not depend on any of the other three components.
As a more OOP model, we can store a Tag list in Course Book , which Module can reference.
This would allow Course Book to only require one Tag object per unique Tag , instead of each Module needing their own Tag object.
An example of how such a model may look like is given below. |
2.5. Storage component
API : Storage.java
The Storage
component,
-
can save
UserPref
objects in json format and read it back. -
can save the Course Book data in json format and read it back.
2.6. Common classes
Classes used by multiple components are in the iGrad.commons
package.
3. Implementation
This section describes some noteworthy details on how certain features are implemented.
3.1. Course Feature
Section by: Nathanael Seen
As per the Model diagram above, there is only one CourseBook
in the system.
A CourseBook
represents all information related to helping a user track her graduation
requirements, including the following:
-
One
UniqueModuleList
, consisting of allModules
in the system, which may or may not be mapped to any (degree)Requirement(s)
-
One
UniqueRequirementList
, consisting of all (degree)Requirements
-
One
CourseInfo
, representing important information related to a degree course, which would be detailed more in this section
3.1.1. Implementation
Section by: Nathanael Seen
In the implementation of the course feature which 'houses' the various Requirements
and the Modules
mapped under those requirements, a CourseInfo
class is
necessary in order to represent overall course information, such as the name of the course, the
current cap of the student (or user), the total credits (MCs) fulfilled/required, and
semesters left before she could graduate.
These important information are encapsulated in the CourseInfo
class which should only
have one Name
, one Cap
, one Credits
and one Semesters
object(s), at any one time:
Also, as per the diagram above, we note that these fields (in a CourseInfo
) are optional,
because a user might not even have a course set in the first place. This occurs when the
application is started out in a 'blank' state, with no initial or sample data.
Now, to describe more of this CourseInfo
class, its fields, the following two sub-sections would
detail the Credits
and Semesters
classes and their design.
Thereafter, the next two sub-sections would attempt to explain the mechanics of the two crucial static
methods computeCredits(…)
and computeCap(…)
, which is used
throughout the application.
Finally, the last two sections would be dedicated to elaborating how the various
course commands; course edit
and course achieve
works.
Credits
Section by: Nathanael Seen
The Credits
class maintains 2 integers; creditsRequired
and creditsFulfilled
, for
storing both the total number of course credits (MCs) required for graduation,
and also the number credits the user has fulfilled thus far, respectively.
Also, it some public
validation methods (isValidCreditsFulfilled()
and isValidCreditsFulfilled()
)
which is used in the constructor for constructing a 'valid' Credits
object.
The following constraints defines a 'valid' Credits
object:
-
creditsRequired
> 0 -
creditsFulfilled
>= 0
Note that creditsFulfilled
can be more than or equals to creditsRequired
, as it is
possible that a student 'over-fulfills' the graduation requirements in her course.
The following are some noteworthy details on the Credits
class/object:
-
Credits
is recomputed through thecomputeCredits(…)
method inCourseInfo
(whenever there is a possible change). This newly recomputedCredits
object would be subsequently updated in theCourseInfo
-
creditsRequired
is recomputed by summing all thecreditsRequired
of the individualRequirements
in theUniqueRequirementList
-
creditsFulfilled
is recomputed in the same way ascreditsFulfilled
-
In the
CourseInfo
class,Credits
is first initialized (to some non-empty value) only when the following conditions have been met:-
The user has already set a course, through the
course set
command. -
There is at least one
Requirement
in theUniqueRequirementList
,` by whichcreditsRequired
andcreditsFulfilled
could be re/computed.
-
Semesters
Section by: Teri
Semesters
stores the total semesters and remaining semesters that a user has in the course.
The following are some noteworthy details on the Semesters
class/object:
-
Semesters
is first initialized when user does commandcourse set
.totalSemesters
will be equal toremainingSemesters
as user has not entered any other data to indicate completion of semesters. -
Semesters
is updated through methodcomputeSemesters
inCourseInfo
. -
totalSemesters
is changed by user through the commandcourse edit
. -
remainingSemesters
is computed by methodcomputeRemainingSemesters
. This method uses themoduleList
to check formodule
that hasSemester
andGrade
. Themodule
which fulfils the mentioned and has the latestSemester
will be taken as the latest completed semester.
Compute Credits
In this section, we describe how computeCredits(requirement: Requirement[])
works to recompute
the latest Credits
.
As previously mentioned, this method is invoked everytime there is a
possible change in the total course Credits
.
This might be caused through the following commands:
-
module done
where aModule
is attributed a grade and marked done. Resultantly, allRequirements
in theUniqueRequirementList
, consisting of thatModule
would have to be updated. Also, but most importantly, thecreditsFulfilled
attribute of thoseRequirements
would have to be updated, causing an eventual change in the total coursecreditsFulfilled
ofCredits
(inCourseInfo
). -
requirement un/assign
whereModule(s)
are assigned to that particular requirement, where someModules
might have already been marked as done and given a grade, hence falling back to the first scenario, wherecreditsFulfilled
of a course would have to be updated. -
requirement edit
where thecreditsRequired
attribute of that requirement might be updated, resulting in the need to update the overall coursecreditsRequired
as well -
And many others such as;
requirement add
,requirement delete
,module edit
,module delete
Now, we have specified the possible scenarios where computeCredits(…)
might have to be
invoked to update Credits
of CourseInfo
, however we have not described how it actually works.
As from our previous discussion, we note that creditsFulfilled
and
creditsRequired
is computed through summing up the creditsFulfilled
and creditsRequired
for the individual Requirements
.
More formally, the interactions between the various classes, for the computation to be performed are as such:
Compute Cap
Similar to Section 3.1.1.3, “Compute Credits”, Cap
has to be updated frequently, each time module information
in coursebook
changes, and the computeCap(…)
method facilitates the recomputation of the
updated Cap
.
The diagram below describes the interactions between the various classes, as the computation is performed:
We note that from above, CourseInfo
does most of the interfacing with other classes,
and the rest of the classes don’t interact with each another.
In summary, the following steps are performed as computeCap(…)
is invoked:
(For each Module
in the moduleList
):
-
CourseInfo
first iterates throughreqList
to determine if aModule
belongs to anyRequirement
-
If it does,
CourseInfo
again interfaces withModule
to extract itsGrade
. -
Finally,
CourseInfo
interacts withGrade
to determine if theGrade
is a non-SU grade. -
If the
Grade
is non-SU, theModule
is factored into Cap computation.
3.1.2. Course Edit
Section by: Teri
Overview
Users can edit their course info, which are Name
and Semesters
by using the course edit
command.
Implementation
Here is how the courseInfo class updates when name and semesters of course is edited.
When a user edits a course, the user has to specify the prefix n/
for Name
or s/
prefix
for Semesters
.
Then the application proceeds to do the following steps:
-
The
CourseEditCommandParser
is called to parse theCourseEditCommand
with then/
ands/
prefix. -
The
CourseEditCommand
is executed and callssetCourseInfo
toModel
. -
Model
calls the same methodsetCourseInfo
toCourseBook
. -
The new course
Name
and courseSemesters
is set in theCourseBook
.
3.1.3. Course Achieve
Section by: Teri
Overview
Users can get an automatic calculation of their desired C.A.P. by using the course achieve
command and
entering their desired Cap
.
Implementation
The computation of C.A.P. is done through computeEstimatedCap
in courseInfo
which uses Semesters
and Cap
of courseInfo
.
When a user wants to calculate achievable C.A.P., the user has to specify the prefix c/
for Cap
.
Then the application proceeds to do the following steps:
-
The
CourseAchieveCommandParser
is called to parse theCourseAchieveCommand
with thec/
prefix. -
The
CourseAchieveCommand
is executed and it calls methodgetCourseInfo
inModel
to getCourseInfo
. -
The
CourseAchieveCommand
then proceeds to call methodcomputeEstimatedCap
inCourseInfo
. -
The
CourseAchieveCommand
then passes the computed result toCommandResult
The below is a detailed description of what happens inside the Model
:
-
After the execute call, the
CourseAchieveCommand
calls methodgetCourseInfo
inModel
. -
Model
then calls the same methodgetCourseInfo
toCourseBook
to retrieveCourseInfo
. -
With the
CourseInfo
andCap
,CourseAchieveCommand
calls methodcomputeEstimatedCap
inCourseInfo
. -
CourseInfo
calls methodgetSemesters
andgetCap
to itself to get the following information:-
Semesters
-
Cap
-
-
computeEstimmatedCap
computes and returns estimateCap
. -
The result is passed back to
CourseAchieveCommand
, which passes toCommandResult
and eventually returns to the user.
Design Considerations
Invalid and Unachievable C.A.P.
It is possible that a calculated Cap
to achieve is not a valid Cap
. In such situations, an exception is
thrown within the computeEstimatedCap
command and it is caught in the CourseAchieveCommand
. User will be
given feedback that the desired C.A.P. is not achievable.
The figure below illustrates this:
Therefore, there are three types of result displayed to User:
-
When User enters invalid Cap to achieve
-
When computed Cap is invalid
-
When computed Cap is valid
3.2. Module Feature
Section by: Wayne
3.2.1. Module Component
The Module
component is the building block of all other components in the system.
In order to track the number of credits
left to fulfill
for each requirement
, each module
is stored in a UniqueModuleList
and the credits
tied to each
module
is then tabulated.
Besides being necessary in tracking the amount of credits left for a requirement
, modules
are also
used to decide which semester
the user is currently in. When a semester
is tagged to a module
, either when
a new module
is added or an existing module
is edited, the latest semester
of all modules
in the filteredList
of modules is taken
to be the current semester.
A module must have the following non-optional values:
Value Type | Class Name | Example |
---|---|---|
String |
Title |
Software Engineering |
String |
ModuleCode |
CS2103T |
String |
Credits |
4 |
List |
ModulePrerequisites |
CS2030, CS2040 |
List |
ModulePreclusions |
CS2103, CS2103T, CS2113T, CS2113 |
A module may also have the following optional values:
Value Type | Class Name | Example |
---|---|---|
String |
Semester |
Y3S1 |
String |
Grade |
A+ |
Figure 21, “Module Class Diagram” illustrates the relation between the various classes:
3.2.2. Module Add Auto
Section by: Wayne
Overview
The "automatic" addition of modules
allows users to add up to 10 modules
at once with all non-optional values filled in.
This is done by making a HTTP GET request to the NUSMods API and fetching the module data
given in a JSON format.
Implementation
The automatic filling in of module
details on addition of a new module
is facilitated by NusModsRequester
.
It creates a new instance of GetRequestManager
which it relies on to make a request to the NUSMods API.
Upon receiving a response, it creates an instance of JsonParsedModule.
JsonParsedModule
parses the JSON object given in the response of the initial request and stores the following values:
Value Type | Name | Example |
---|---|---|
String |
title |
Software Engineering |
String |
moduleCode |
CS2103T |
String |
credits |
4 |
String |
prerequisite |
CS2040C or (CS2030 and (CS2040 or its equivalent)) |
String |
preclusion |
CS2103, CS2103T, (CS2113T for CS2113), (CS2113 for CS2113T) |
Table 1, “JsonParsedModule Table of Values” illustrates the difficulty in parsing prerequisites and preclusions as
the data provided is not in a standard format
|
The created JsonParsedModule
object is then converted into a Module
object, which is subsequently added
to the courseBook
via the method addModule
of the ModelManager
.
Figure 22, “Module Add Auto Sequence Diagram” illustrates this:
Design Considerations
Most of the design considerations arose as a result of having to make a network request.
A secondary module addition feature
As with all network requests, this feature might not work as intended in certain circumstances. Possible cases are:
-
High Network Congestion
-
Poor Network Connection
-
NUSMods Offline
In such situations, it becomes difficult or impossible to carry out the addition of modules
using this command.
Therefore, this feature was built on top of the primary module add
feature, ensuring that the user
could still manage to add modules
even when faced with the issues as listed above.
The use case for this situation is as follows:
System: iGrad
Use case: UCM1 - Add module via NUSMods
Actor: User, NUSMods
MSS:
-
User wants to add a module.
-
iGrad requires user to specify the module codes.
-
User enters the module codes corresponding to the modules he wishes to add.
-
iGrad sends a request to NUSMods.
-
NUSMods responds with the requested data.
-
iGrad adds the module to the module list.
-
User views the module in the module list.
Use case ends.
Extensions:
5a. NUSMods does not respond with the requested data.
5a1. User adds module manually
Messages for individual modules
As this feature allows a user to add modules
by batches, it is possible that one or more modules
in
the batch are invalid or require warning messages. In order to facilitate this, the processing of the list of modules
and the
generation of error and warning messages were done in parallel. This was because if the list of modules
was processed first,
modules
with issues would be filtered out without notice, leading to a confusing user experience.
Improving the user experience [PROPOSED]
Due to network latency - the Round Trip Time taken from when a request is made to when a response is received - the user might experience a situation where it appears that the application has stopped working.
For a large batch of modules
, the application might also display a not responding label in the toolbar.
In order to improve the user experience, it is ideal that a loader be displayed when waiting for the response from the server.
However, due to time constraints, this was not implemented.
Getting the latest data [DEPRECATED]
Past iterations of this feature made a maximum of two requests for one module
. The first request would
attempt to get the module
for the current academic year, whilst the second request attempted to get
the module
for the previous academic year, in the event the module for the current academic year was not available.
This process is illustrated in Figure 25, “Previous implementation of Module Add Auto”. However, it was decided that the benefits of making a maximum of one request outweighed that of getting the latest module information and thus, currently only one request is made.
3.2.3. Module Filter
Overview
The average number of modules
a student has for a 4 year program in NUS is 40. The application window, however, can display
a maximum of 9 modules
for a 17" screen and considerably less for smaller displays. As a result, it is imperative that users have
a way to filter modules
so that only what is required is displayed.
Implementation
The filtering of modules is done by calling the execute
function of ModuleFilterCommand
.ModuleFilterCommand
takes in
optional parameters Semester
, Credits
, Grade
and an operator, which could be AND
or OR
. The ModuleFilterCommand
then
calls updateFilteredModuleList
on the Model
such that the Model
updates the filteredList
based on the predicates provided.
The ModuleFilterCommand
then calls getFilteredModuleList()
to check if the filter was applied successfully.
The matching is done by the functions checkSemesterMatch(Module m)
, checkCreditsMatch(Module m)
and checkGradeMatch(Module m)
. Unlike
the other two functions, checkCreditsMatch(Module m)
does not check if the actual credits
for a module is present since the
credits
field in a module is compulsory.
The AND
operator specifies that the provided parameters be chained with the logical and operator.
The OR
operator specifies that provided parameters be chained with the logical or operator.
When the filter command is issued, the Model
updates the module list based on the predicate given. Figure 26, “Module Filter Sequence Diagram”
illustrates this sequence of events:
Design Considerations
Resetting the state
When this command is issued, the modules that do not match the predicate given will disappear from the module list. It is thus necessary to allow the user to issue a new command in order to view all the modules again.
Whilst creating a new command such as module reset
was proposed, it was decided that a new command would only serve to
make the user experience more complicated than it should be.
Therefore, an allowance was made for module filter
to reset the state when receiving no parameters, a divergence from
the way other functions handled the situation of empty parameters.
Figure 27, “Module Filter Activity Diagram” illustrates this clearly:
The filteredList
of modules
therefore takes three general states (see Figure 28, “Module Filter State Diagram”):
-
Initial State
The
module
list is unfiltered. Allmodules
are displayed. -
Filtered by
AND
StateThe
module
list is filtered by a predicate composed of the provided parameters chained together with the logical and operator -
Filtered by
OR
StateThe
module
list is filtered by a predicate composed of the provided parameters chained together with the logical or operator
Displaying Filter State [PROPOSED]
An issue with the filtering of modules is that when the current state is not obvious, the user might lose track of what the module list is filtering on. To solve this problem, it would be an improvement to display the current state prominently to the user.
3.2.4. Module Done
Section by: Nathanael Seen
Overview
This feature enables students to mark a module which they have completed with a certain grade. Once a module is marked 'done', it would be counted to graduation requirements.
Implementation
Trivial as it might seem, there are actually quite a number of coursebook
data which needs to
be updated when a module is marked 'done'.
This includes the Module
itself, the various Requirements
in the UniqueRequirementList
,
and the CourseInfo
.
The following diagram illustrates this:
As from the above, there are two possible scenarios; either a Module
belongs to at least one
Requirement
or that it does not belong to any Requirement
.
In the latter case, calls to methods which update CourseInfo
would be immediately invoked,
bypassing the updating of the Requirement(s)
.
3.3. Requirement Feature
Section by: Yijie
The Requirement
feature bridges the connection between a Module
and a Course
. A Requirement
represents
a category with a set amount of modular credits needed to be fulfilled in order to graduate. By
adding modules to a Requirement
, these modules are tracked under the modular credits needed to fulfill
the Requirement
, as well as the total modular credits needed to fulfill the Course
.
Multiple requirements can exist in a Course
. When all requirements have been fulfilled,
we indicate that the Course
has been completed.
3.3.1. Implementation
Section by: Yijie
The Requirement
component contains 4 sub-components: RequirementCode
, Title
, Credits
and UniqueModuleList
.
All four sub-components are compulsory and must always be specified in a Requirement
. The properties of these four
sub-components are specified in the table below:
Class Name | Properties | Example |
---|---|---|
|
The unique identifier of a requirement. Implemented to allow users to easily specify the requirement they want to modify or remove. See Requirement Code Design Considerations. |
CSF0 |
|
The title of a requirement. Does not have to be unique. |
Computer Science Foundation |
|
The credits of a requirement, |
40 |
|
The list of modules assigned to a requirement. |
List containing modules CS1101S, CS2101, CS2103T |
When a Module
is added to a Requirement
, the UniqueModuleList
is updated with the new Module
.
At the same time, the creditsAssigned
property of the Credits
class also updates accordingly. If
the Module
is completed, the creditsFulfilled
property of Credits
also updates to add the
credits of the Module
. For example, if a module with 4 modular credits was marked as done and assigned
to a Requirement
, creditsAssigned
and creditsFulfilled
would both increase by 4.
The class diagram below illustrates the relationship between Requirement
components.
Requirement
commands that are available are:
-
requirement add
- adds a new requirement to the course book -
requirement edit
- edits an existing requirement in the course book -
requirement delete
- deletes an existing requirement from the course book -
requirement assign
- assigns module(s) to the requirement
3.3.2. Requirement Add
Section by: Yijie
Overview
The requirement add
command adds a Requirement
to the Course
. The Credits
information held by the Course
is updated to reflect the total modular credits required to complete the course.
Implementation
When a user enters a requirement add
command, the parameters specified by the user, i.e. the requirement title
and the requirement credits required to fulfill the Requirement
, are passed to the LogicManager
,
CourseBookParser
and subsequently into the RequirementAddCommandParser
where the parameters are extracted
and parsed into Title
and Credits
objects.
The RequirementCode
is generated at this stage by using the first letter of every word in the
title parameter passed in, upper-cased. For example, if the title provided is "Computer Science Foundation",
the RequirementCode
generated would be CSF
. Similarly, if the title provided is "3Computer Science 4Foundation",
the RequirementCode
generated would still be CSF
because non-alphabets in the title are ignored.
The parsed Title
, Credits
and the generated RequirementCode
are used to create a RequirementAddCommand
object,
which is later executed by the LogicManager
, as shown in the
Requirement Add Sequence Diagram below.
When the command is executed by the LogicManager
, the new Requirement
is created and
stored in the CourseBook
through the Model
.
Design Considerations
Requirement Code
Originally, we chose to use the requirement Title
as the unique identifier for requirements, which means that
specifying a Requirement
to be modified or deleted would be handled by entering the Title
. However, we realised
that that was a big deterrent in users staying on our application in the long run. The reason is because requirement
titles are generally long, and often require more than one word to be expressed. For example, typing "Computer
Science Foundation" or "Computer Science Breadth and Depth" every time a module was to be assigned to a requirement
would require a relatively long time, and would violate our Non-Functional Requirement
of providing users with an application which can be navigated quickly through typing.
Hence, we chose the design of providing users with a RequirementCode
, the unique identifier which allows
users to quickly access the Requirement
they want to modify or delete.
In order to make the RequirementCode
strictly unique, further parsing is performed on the RequirementCode
generated by the RequirementAddCommandParser
. When the RequirementAddCommand
is executed by the LogicManager
,
a new RequirementCode
is generated, as shown in the
Requirement Add Sequence Diagram, by appending an integer to the back of
the original RequirementCode
. This is necessary as multiple requirements with the same starting alphabets
may exist, so we need to allow users to differentiate amongst such requirements. Furthermore, users are also
allowed to add requirements with the same Title
to enhance the flexibility of adding requirements, so the
RequirementCode
also allows users to differentiate amongst these requirements.
The use case for adding a requirement is as follows:
System: iGrad
Use case: UCR1 - Add requirement
Actor: User
MSS:
-
User requests to add a requirement.
-
iGrad requires user to enter a requirement title and credits.
-
User enters the title and credits.
-
iGrad creates the requirement.
-
Use case ends.
Extensions:
3a. The requirement title is not provided.
3a1. iGrad prompts user for requirement title.
3a2. User enters a requirement title.
Steps 3a1-3a2 are repeated until a valid requirement title is provided.
Use case resumes at step 4.
Below displays the activity diagram for adding a requirement.
3.3.3. Requirement Assign
Section by: Nathanael Seen
Overview
This feature allows Module(s)
to be assigned or mapped under a Requirement
.
Implementation
We note that as like the module done
command, a few pieces of information in the
coursebook
would have to be updated.
The command-parsing-execute mechanism is largely similar to requirement add
the above, however,
we would like to focus more on the activities (such as validations, etc) that happens
at the Model
side.
The following diagram details this:
Hence as per the diagram, we observe that modules are assigned to a Requirement
on the following two conditions:
-
All
Modules
to assign are existent in thecoursebook
(more specifically, theUniqueModuleList
) -
Modules
have not already been previously assigned to thatRequirement
3.4. Export Feature
Section by: Wayne
3.4.1. Overview
The export feature allows the user to export their data into a .csv file.
3.4.2. Implementation
The export feature is facilitated by the CsvWriter
. The ExportCommand
calls exportModuleList()
on ModelManager
,
which then performs a filter on the filteredList
of modules in order to filter out all modules where the Optional<Semester>
object
isEmpty()
.
The ModelManager
allows for write()
to be called on CsvWriter
only if the filteredList
has at least one module.
Figure 34, “Export Sequence Diagram” shows the process of exporting modules with semesters:
Once the CsvWriter
returns from write()
, a file titled study_plan.csv will be created in the top-level directory.
The top-level directory has two states concerning the generated file (see Figure 35, “Export State Diagram”):
-
Empty State
-
does not contain the file study_plan.csv
-
-
Non-empty State
-
contains the file study_plan.csv
-
As seen in the figure, it is only possible to revert the state i.e. from Non-empty State to Empty State, by externally deleting the file.
Additionally, if export
is issued when there is an existing study_plan.csv, the current file will be overwritten.
3.4.3. Design Considerations
Writing and Reading Issues
It is possible that the user has the study_plan.csv file open while attempting to export data. The only known solution is for the user to close the file and issuing the command again.
3.5. Undo Feature
Section by: Wayne
3.5.1. Overview
The undo feature allows for the prior state of a courseBook
to be saved and loaded from when needed.
3.5.2. Implementation
The undo mechanism is facilitated by the ModelManager
and the LogicManager
.
The function saveCourseBook()
, which composes the storage
object, is used to save the previous state of the coursebook
in the file backup_coursebook.json when a new command is executed.
The function undoCourseBook()
, implemented by the ModelManager
, reads from the file backup_coursebook.json and restores
the data by calling setCourseBook()
Given below is an example usage scenario and how the undo mechanism behaves at each step:
Step 1. The user starts up the application.
Step 2. The user issues the command module delete CS2103T
. The previous state of the course book is stored into the file
at backup_coursebook.json
by saveCourseBook()
. This state still contains the module CS2103T.
When the undo command is executed, the state of the courseBook is not saved.
|
Step 3. The user issues the command undo
. The file at backup_coursebook.json
is read from and loaded as the main courseBook
to be read from.
Step 4. The backupCourseBook
becomes the main courseBook
and another instance of courseBook
will be created as its
backup if another command is issued.
The following sequence diagram shows how the undo operation works:
3.5.3. Design Considerations
How undo executes
-
Option 1 (current choice): Saves the entire course book.
-
Pros: Easy to implement.
-
Cons: May have performance issues in terms of memory usage.
-
-
Option 2: Individual command knows how to undo/]] by itself.
-
Pros: Will use less memory (e.g. for
delete
, just save the module being deleted). -
Cons: We must ensure that the implementation of each individual command are correct.
-
While it is recognised that Option 1 may have performance issues in terms of memory, the actual implementation of the feature is unlikely to cause any memory issues. This is due to the fact that modules, requirements and other data users would require are limited to a degree which will not require large memory allocation. |
3.6. [Proposed] Data Encryption
{Explain here how the data encryption feature will be implemented}
3.7. Logging
We are using java.util.logging
package for logging.
The LogsCenter
class is used to manage the logging levels and logging destinations.
-
The logging level can be controlled using the
logLevel
setting in the configuration file (See Section 3.8, “Configuration”) -
The
Logger
for a class can be obtained usingLogsCenter.getLogger(Class)
which will log messages according to the specified logging level -
Currently log messages are output through:
Console
and to a.log
file.
Logging Levels
-
SEVERE
: Critical problem detected which may possibly cause the termination of the application -
WARNING
: Can continue, but with caution -
INFO
: Information showing the noteworthy actions by the App -
FINE
: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
3.8. Configuration
Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json
).
4. Documentation
Refer to the guide here.
5. Testing
Refer to the guide here.
6. Dev Ops
Refer to the guide here.
Appendix A: Product Scope
Target user profile:
-
is a NUS undergraduate
-
prefers desktop apps over other types
-
can type fast
-
prefers typing over mouse input
-
is reasonably comfortable using CLI apps
Value proposition: convenient course requirements tracker for NUS undergraduates
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 … |
---|---|---|---|
|
first-time user |
create a course |
|
|
student |
create a graduation requirement |
|
|
student |
input modules under a graduation requirement |
keep track of when a graduation requirement is fulfilled |
|
careless user |
change the graduation requirements which I assigned to a course |
amend any mistakes made when entering data |
|
fickle user |
change the modules which I assigned to a graduation requirement |
change my study plan |
|
fickle user |
have the option to defer adding modules to a graduation requirement |
delay making up my mind on which modules I wish to take |
|
basic user |
see information regarding the course I created, including graduation requirements, modules and gaps (e.g. modules that are unassigned) that need to be filled |
|
|
user |
see the latest updated information about any module |
make informed decisions |
|
basic user |
mark when a module is completed |
|
|
basic user |
input the grades of a module |
|
|
basic user |
retrieve my CAP of any semester at a command |
stay updated about my results |
|
user |
input my desired CAP and have the program calculate what grades I need to achieve |
find out how well I need to do in following semesters |
|
user |
group modules by graduation requirement |
view by requirement |
|
user |
group modules by semester |
view by semester |
|
user who wants to take notes |
record notes for each module |
record why I took it |
|
picky user |
customize display settings |
customize to my needs |
|
advanced command line user |
use familiar linux commands |
navigate more easily |
{All user stories can be viewed from our wiki page and from our issues tracker.}
Appendix C: Use Cases
Section by: Yijie
(For all use cases below, the System is iGrad
and the Actor is the user
, unless specified otherwise.)
Use case: U01 - Create Course
MSS:
-
iGrad starts up.
-
User requests to create a course.
-
iGrad creates the course.
Use case ends.
Extensions:
-
2a. The course name is not provided.
-
2a1. iGrad prompts user for course name.
-
2a2. User enters a course name.
Steps 2a1-2a2 are repeated until the a non-empty course name is provided.
Use case resumes at step 3.
-
Use case: U02 - Create Requirement
MSS:
-
User requests to create a course.
-
iGrad creates course (UC01).
-
User requests to create a requirement.
-
iGrad creates the requirement.
Use case ends.
Extensions:
-
3a. The requirement title is not provided.
-
3a1. iGrad prompts user for requirement title.
-
3a2. User enters a requirement title.
Steps 3a1-3a2 are repeated until the a non-empty requirement title is provided.
Use case resumes at step 4.
-
Use case: U03 - Create Module
MSS:
-
User requests to create a module by providing a module code.
-
iGrad creates the module with its data pulled from NUSMods.
Use case ends.
Extensions:
-
1a. Module data fails to get pulled due to network error.
-
1a1. iGrad takes from its local module data copy.
Use case ends.
-
-
1b. Module data does not exist on NUSMods.
-
1b1. iGrad creates a empty module with only the module code.
Use case ends.
-
Use case: U04 - Assign Module to Requirement
MSS:
-
User requests to assign a module to a requirement by specifying its module code.
-
iGrad assigns module to requirement.
Use case ends.
Extensions:
-
1a. Module does not exist in system.
-
1a1. iGrad creates the module (UC03).
Use case resumes at step 2.
-
-
1b. Module has already been assigned to the requirement.
-
1b1. iGrad generates a warning and stops the assignment.
Use case ends.
-
7. Non Functional Requirements
-
Should work on any mainstream OS as long as it has Java
11
or above installed. -
Should be able to hold up to 100 modules without a noticeable sluggishness in performance (i.e. should take less than 1 second to load)
-
A user with above 70 wpm 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 interface should be intuitive enough such that a user who has never seen the user guide is able to use the basic features.
{More to be added}
Appendix D: Glossary
- Mainstream OS
-
Windows, Linux, Unix, OS-X
- Private contact detail
-
A contact detail that is not meant to be shared with others
Appendix E: 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. |
E.1. Launch and Shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with a set of sample contacts. 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. { more test cases … }
-
E.2. Deleting a module
-
Deleting a module while all modules are listed
-
Prerequisites: List all modules using the
list
command. Multiple modules in the list. -
Test case:
delete 1
Expected: First module is deleted from the list. Details of the deleted module shown in the status message. Timestamp in the status bar is updated. -
Test case:
delete 0
Expected: No module is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete
,delete x
(where x is larger than the list size) {give more}
Expected: Similar to previous.
-
E.3. Saving data
-
Dealing with missing/corrupted data files
-
{explain how to simulate a missing/corrupted file and the expected behavior}
-