Code:
Documentation:
Dev-dependencies:
markbind-cli
package with NPM, that runs over the Node.js runtime.captain-githook
, was used for configuring git hooks, for pre-push and pre-commit checks.Ideas:
Refer to the guide Setting up and getting started.
This section describes the design of various components of TAPro.
The Architecture Diagram given above explains the high-level design of TAPro.
Given below is a quick overview of main components and how they interact with each other.
Main
(consisting of classes Main
and MainApp
) is in charge of the app launch and shut down.
The bulk of TAPro's work is done by the following four components:
UI
: The UI of TAPro.Logic
: The command and autocomplete executor.Model
: Holds the data of TAPro in memory.Storage
: Reads data from, and writes data to, the hard disk.Commons
represents a collection of classes used by multiple other components.
The Sequence Diagram below shows how the components interact with each other for the scenario where the user
issues the command delstu nn/E1234567
.
Each of the four main components (also shown in the diagram above),
interface
with the same name as the Component.{Component Name}Manager
class (which follows the corresponding API interface
mentioned in the previous point.Example:
The Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface.
Other components interact with a given component through its interface rather than the concrete class, as illustrated in the (partial) class diagram below.
Reason: To prevent outside component's being coupled to the implementation of a component.
The sections below give more details of each component.
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, PersonListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the 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,
Logic
component.Model
data so that the UI can be updated with the modified data.Logic
component, because the UI
relies on the Logic
to execute commands.Model
component, as it displays Person
object residing in the Model
.API : Logic.java
Here's a (partial) class diagram of the Logic
component:
The sequence diagram below illustrates the interactions within the Logic
component, taking execute("delstu nn/E1234567")
API
call as an example.
Note: The lifeline for DeletePersonCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
How command execution works in Logic
component:
Logic
is called upon to execute a command, it is passed to an AddressBookParser
object which in turn creates a parser that matches the command (e.g., DeletePersonCommandParser
) and uses it to parse the command.Command
object (more precisely, an object of one of its subclasses e.g., DeletePersonCommand
) which is executed by the LogicManager
.Model
when it is executed (e.g. to delete a person).Model
) to achieve.CommandResult
object which is returned back from Logic
.Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
AddressBookParser
class creates an XYZCommandParser
(XYZ
is a placeholder for the specific command name e.g., AddPersonCommandParser
) which uses the other classes shown above to parse the user command and create a XYZCommand
object (e.g., AddPersonCommand
) which the AddressBookParser
returns back as a Command
object.
XYZCommandParser
classes (e.g., AddPersonCommandParser
, DeletePersonCommandParser
, ...) inherit from the Parser
interface so that they can be treated similarly where possible e.g, during testing.AddressBookParser
class checks whether the input contains arguments. If it does not contain arguments, it creates an AutoCompleteCommand
object which autocompletes Commands. Otherwise, it checks for the last argument in the user input and creates the matching AutoComplete
object if it exists. Otherwise, a default AutoComplete
object that always return an empty string is returned.While a command is named as XYZPersonCommandParser
internally (where XYZ
is a placeholder for a specific command), it acts on a student externally in TAPro.
API : Model.java
The Model
component,
Person
objects (which are contained in a UniquePersonList
object).Person
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiable ObservableList<Person>
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.UserPref
object that represents the user’s preferences. This is exposed to the outside as a ReadOnlyUserPref
object.CourseName
object that represents a Course code. This is exposed to the outside as a ReadOnlyCourseName
object.Model
represents data entities of the domain, they should make sense on their own without depending on other components)While Person
objects are used internally, it represents a student externally in TAPro.
An alternative model:
An alternative (arguably, a more OOP) model is given below. It has a Tag
list in the AddressBook
, which Person
references. This allows AddressBook
to only require one Tag
object per unique tag, instead of each Person
needing their own Tag
objects.
API : Storage.java
The Storage
component,
AddressBookStorage
and UserPrefStorage
and CourseStorageName
, which means it can be treated as either one (if only the functionality of only one is needed).Model
component (because the Storage
component's job is to save/retrieve objects that belong to the Model
)While AddressBookXYZ
objects are used internally (where XYZ
is a placeholder), it represents the student contact book externally in TAPro.
Classes used by multiple components are in the seedu.addressbook.commons
package.
The Trie
class is a data structure that allows for efficient prefix matching of strings. It is used in the AutoComplete
feature to suggest completions for user input.
We added the ability to search for the first word that matches a given prefix. This is useful for the autocomplete feature, where we want to suggest the first word that matches the prefix.
This section describes some noteworthy details on how certain features are implemented.
Here is a (partial) class diagram of the autocomplete feature.
There are 3 subcomponents in the Logic component that are involved the autocomplete feature, which are:
Omitted from the (partial) class diagram is the functional interface AttributeValueGenerator
, which produces a List<String>
, representing all possible values for an attribute of a student in TAPro's current data.
AttributeValueGeneratorManager
has static methods that match the method signature of AttributeValueGenerator
, which is pass in as an argument when creating a PrefixResolver
.
PrefixResolverSyntax
stores preset PrefixResolvers
for all the Prefix
s that can be autocompleted.
There are two variants of autocomplete feature. One variant is the autocompletion of static data using AutoCompleteCommand
. Another variant is the autocompletion of dynamic data using AutoCompletePrefixResolver
.
How autocompletion of static data works using AutoCompleteCommand
in the Logic
component:
Logic
is called upon to autocomplete an input string, it is passed to an AddressBookParser
object which in turn matches the input and return the corresponding AutoComplete
object, AutoCompleteCommand
in this case.AutoCompleteCommand
then produces an AutoCompleteResult
which is executed by the LogicManager
.AutoCompleteCommand
classes uses Trie
under the hood to efficiently generate the autocomplete suggestions. The Trie
is preloaded with static data of our command names (e.g. addstu
), which is used to generate the suggestions.AutoCompleteResult
object is solely responsible for generating the autocomplete suggestions based on the input string (e.g. the additional characters that can be appended to the input string).How autocompletion of dynamic data works using AutoCompletePrefixResolver
in the Logic
component:
In the autocompletion of dynamic data there are 5 key steps:
AttributeTrie
Step 1. How the AttributeTrie
is notified of new data:
LogicManager
to determine if the command could potentially modify the data.addressBook
using getAddressBook()
.addressBook
, we would update the AttributeValueGeneratorManager
with updateAddressBook(addressBook)
.PrefixResolver
needs to be updated using notifyOutdatedData
.PrefixResolver
calls its corresponding AttributeTrie
to clearCache()
, which removes the Trie
that is used internally. The absence of a Trie
would cause AttributeTrie
to lazily generate a new Trie
using new data. Meaning that a new Trie
is only generated when necessary.Step 2. How the autocomplete hotkey works:
handleTabKeyPressEvent(...)
is called.lastModifiedText
has changed, it means that the text in the CommandBox
's command input box has changed (e.g. the user types into the command input box), so the autoCompleteResultCache
is set to null
to indicate that the AutoCompleteResult
is outdated.autoCompleteResultCache
is null
, then we know that the lastModifiedText
has changed, so we need to generate a new autocomplete result. We do this by calling MainWindow#getAutoComplete(lastModifiedText)
, which calls the LogicManager
to generate a new autocomplete result.autoCompleteResultCache
still contains the latest AutoCompleteResult
that works for the current lastModifiedText
.autoCompleteResultCache
, we call autoCompleteResultCache.getNextResult()
to generate the next autocompletion.CommandBox
's command input box is updated to the suggested autocompletion using setText(...)
.The reference frame below, create empty autocomplete
, is used in the next few sequence diagrams. It represents that an AutoCompleteResult
with no autocomplete suggestion is returned, meaning that pressing the autocomplete hotkey would cause no change in CommandBox
's command input box when it is used.
Step 3. How LogicManager
parses the input to generate a new AutoCompleteResult
:
LogicManager#autoComplete(commandText)
is called with the text commandText
to autocomplete, it calls the AddressBookParser#parseAutoComplete(commandText)
method.commandText
passed into parseAutoComplete
, there are 3 possible paths:commandText
cannot be parsed as it doesn't meet format specifications.commandText
can be parsed, but is missing prefixes inside the text, so it is treated as an autocompletion for a command name. AutoCompleteCommand
is then constructed and used like how autocompletion of static data works.commandText
can be parsed, and contains at least one prefix in the text, so it is treated as an autocompletion for a prefix. AutoCompletePrefixResolver
is then constructed using ALL_PREFIX_RESOLVERS
and used later on in getAutoComplete
, given an input
, to generate an autocompletion.AutoComplete
is returned, which AutoCompleteCommand
and AutoCompletePrefixResolver
are subclasses of.AutoComplete
produces a AutoCompleteResult
, which is passed back to the MainWindow
.Step 4. How AutoCompletePrefixResolver
is generates an AutoCompleteResult
:
AutoCompletePrefixResolver#getAutoComplete(input)
is called, if the input
is blank, an empty AutoCompleteResult
is returned.findTriePrefixContinuation(input)
which would find the text, that continues from a given input, in the AttributeTrie
. This may update the AttributeTrie
with new attribute data if necessary, due to the lazy evaluation.trieMatchContinuations
returned from findTriePrefixContinuation
, we use it to create the AutoCompleteResult
.trieMatchContinations
is empty, it means there are no autocomplete results for the current input
, so an empty AutoCompleteResult
is returned.AutoCompleteResult
with the associated trieMatchContinuations
is returned.Step 5. How AttributeTrie
is updated with new attribute data and returns the matches:
AutoCompletePrefixResolver#findTriePrefixContinuation(input)
is called, findLastPrefixIndex(input)
is called which returns indexOfLastPrefix
.indexOfLastPrefix
, it calls findLastPrefix
to find the last prefix in the input
.PrefixResolver
s to find one where lastPrefix
matches the PrefixResolver
's Prefix
.PrefixResolver#resolvePrefix(partialValue)
on the matching PrefixResolver
, which calls AttributeTrie#findAllValuesWithPrefix(partialValue)
.trieCache
is absent, it means that the AttributeTrie#clearCache()
has been called before without a new Trie
being generated. So generateTrie()
is called, which generates values in the Trie
using a AttributeValueGenerator
which generates a list of attribute values to populate the Trie
with.AttributeTrie
then computes the list of values with the prefix and returns it back.AutoCompletePrefixResolver
formats the list of values and returns it as trieMatchContinuations
, which are text continuations from the given input
.1. Separation of concerns:
As the autocomplete feature involves many classes all over the codebase, it is important to handle the separation of concerns carefully, to lead to higher cohesion and lower coupling.
This was done through the following:
PrefixResolver#notifyOutdatedData
and AttributeValueGeneratorManager#updateAddressBook
.
AttributeValueGenerator
, that is used by AttributeValueGenerateManager
to generate an attribute values given the addressBook
. The AttributeValueGenerator
is then passed into the AttributeTrie
to generate values to insert into the Trie
.2. Caching of intermediate results for improved performance:
There are two layers in the current implementation used for caching, which are:
lastModifiedText
doesn't change, caching the AutoCompleteResult
in autoCompleteResultCache
.addressBook
doesn't change, caching the Trie
stored in the AttributeTrie
.By having caching of intermediate results, it reduces the need to recompute certain results, thus improving performance of TAPro on users' systems.
3. Lazy evaluation to reduce redundant computations:
Lazy evaluation is carried out in AttributeTrie
, where the new Trie
was lazily evaluated. It means that the Trie
was only generated when the autocompletion of a parameter value doesn't have an AttributeTrie
already present.
Improve detection on whether attributes are actually modified before updating their AttributeTrie
s:
AttributeTrie
s, which would improve the performance of our autocompletion feature.Let's consider the scenario where the user wants to retrieve the last command executed. The user can do this by pressing the UP key on the keyboard.
The UP key press event is captured by the CommandBox
class, which then
retrieves the last command from the CommandHistory
Singleton object.
Note: The CommandHistory
Singleton object is used to store the command history. It is a Singleton object to ensure that there is only one instance of the CommandHistory
object throughout the application.
Below is the activity diagram that shows how the process of a user interacting with the input field to retrieve the last command executed.
The proposed undo/redo mechanism is facilitated by VersionedAddressBook
. It extends AddressBook
with an undo/redo history, stored internally as an addressBookStateList
and currentStatePointer
. Additionally, it implements the following operations:
VersionedAddressBook#commit()
— Saves the current contact book state in its history.VersionedAddressBook#undo()
— Restores the previous contact book state from its history.VersionedAddressBook#redo()
— Restores a previously undone contact book state from its history.These operations are exposed in the Model
interface as Model#commitAddressBook()
, Model#undoAddressBook()
and Model#redoAddressBook()
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 VersionedAddressBook
will be initialized with the initial contact book state, and the currentStatePointer
pointing to that single contact book state.
Step 2. The user executes delstu nn/E1234567
command to delete student with NUSNet ID as E1234567 from the contact book.
The delstu
command
calls
Model#commitAddressBook()
, causing the modified state of the contact book after the delstu nn/E1234567
command
executes to
be saved in the addressBookStateList
, and the currentStatePointer
is shifted to the newly inserted contact book state.
Step 3. The user executes addstu n/David …
to add a new person. The addstu
command also calls Model#commitAddressBook()
, causing another modified contact book state to be saved into the addressBookStateList
.
Note: If a command fails its execution, it will not call Model#commitAddressBook()
, so the contact book state will not be saved into the addressBookStateList
.
Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the undo
command. The undo
command will call Model#undoAddressBook()
, which will shift the currentStatePointer
once to the left, pointing it to the previous contact book state, and restores the contact book to that state.
Step 5. The user wants to set the course name. He enters the command setcrs CS2103T
, causing the course name to appear on the main window's title.
Note: If the currentStatePointer
is at index 0, pointing to the initial AddressBook
state, then there are no previous AddressBook
states to restore. The undo
command uses Model#canUndoAddressBook()
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 an undo operation goes through the Logic
component:
Note: The lifeline for UndoCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Similarly, how an undo operation goes through the Model
component is shown below:
The redo
command does the opposite — it calls Model#redoAddressBook()
, which shifts the currentStatePointer
once to the right, pointing to the previously undone state, and restores the contact book to that state.
Note: If the currentStatePointer
is at index addressBookStateList.size() - 1
, pointing to the latest contact book state, then there are no undone AddressBook
states to restore. The redo
command uses Model#canRedoAddressBook()
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 6. The user then decides to execute the command list
. Commands that do not modify the contact book, such as list
, will usually not call Model#commitAddressBook()
, Model#undoAddressBook()
or Model#redoAddressBook()
. Thus, the addressBookStateList
remains unchanged.
Step 7. The user executes clear
, which calls Model#commitAddressBook()
. Since the currentStatePointer
is not pointing at the end of the addressBookStateList
, all contact book states after the currentStatePointer
will be purged. Reason: It no longer makes sense to redo the addstu 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:
Aspect: How undo & redo executes:
delstu
, just save the person being deleted).Target user profile:
Value proposition: All in one contact book managing student’s progress in the course, by means of attendance of the NUS CS class. Can quickly find information and perform operations with keyboard shortcuts.
Priorities:
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
TA | name/rename the CS course that I am tutoring this semester | keep track of the module I am teaching | |
TA | add a student to the my class that I am tutoring this semester | keep track of him or her | |
TA | view all students from my class | view details about all of them | |
TA | delete a student | remove a student if he or she leaves the class | |
TA | see all students in the contact book | have an overview of all students | |
TA | edit a student's details | have the latest data | |
TA | find a student by name | get a student's data easily | |
attendance tracking TA | mark attendance for a student in my class for a particular week | keep track of who is present | |
attendance tracking TA | unmark attendance for a student in my class for a particular week | keep track of who is absent | |
TA over multiple semesters | delete all students from a previous semester from the contact book | clear my contacts quickly at the start of a semester | |
new user | know all the commands of TAPro via the help window | use it effectively | |
user comfortable with CLI | retrieve command history | avoid retyping a command | |
user comfortable with CLI | autocomplete my input | save time | |
fast typist | use keyboard inputs to interact with TAPro | save time | |
user | exit the program smoothly | save time | |
user | easily read the result message of a command | save time |
For all use cases below, the System is TAPro and the Actor is the user, unless specified otherwise.
Use case: Learn How to Use Available Commands in TAPro
MSS
User requests to view the list of available commands for TAPro.
TAPro displays the list of available commands and a guide on how to use each command.
Use case ends.
Use case: Add a Student
MSS
User requests to add a student, providing the name and NUSNet ID as compulsory information, with the phone number, email, major and tags being optional.
TAPro adds the student to the list of students.
Use case ends.
Extensions
2a. Format of the provided data is incorrect.
2a1. TAPro rejects the student addition and shows an error message.
Use case ends.
Use case: Editing a Student
MSS
User requests to edit an existing student, indicating the student the user wish to edit. The user provides the update details for the student and remaining details are unchanged.
TAPro updates the student with the new details.
Use case ends.
Extensions
2a. No such student exists.
2a1. TAPro shows an error message.
Use case ends.
2b. User provides no details to update.
2b1. TAPro shows an error message.
Use case ends.
2c. Format of the provided data is incorrect.
2c1. TAPro rejects the student update and shows an error message.
Use case ends.
Use case: Delete a Student
MSS
User requests to delete a specific student based on NUSNet ID.
TAPro deletes the student from the list of students.
Use case ends.
Extensions
2a. No such student exists.
2a1. TAPro shows an error message.
Use case ends.
2b. Format of the provided data is incorrect.
2b1. TAPro shows an error message.
Use case ends.
Use case: Name or Rename CS Course
MSS
User requests to name or rename a CS course by specifying the course code.
TAPro names or renames the CS course on the application window bar.
Use case ends.
Extensions
2b. Format of the provided course code is incorrect.
2b1. TAPro shows an error message.
Use case ends.
Use case: View All Students
MSS
User requests to view a list of all students.
TAPro displays a list of all students.
Use case ends.
Use case: Find a Student by Name
MSS
User requests to find all students by one or more keywords in the student's name.
TAPro displays a list of students whose names contain the keyword(s).
Use case ends.
Extensions
2a. No student exists with the given keyword(s).
2a1. TAPro shows no students.
Use case ends.
Use case: Mark Attendance
MSS
User requests to mark attendance for a student by providing the student's NUSNet ID and week number to mark the attendance for.
TAPro marks the attendance for the student for the specified week.
Use case ends.
Extensions
2a. No such student exists.
2a1. TAPro shows an error message.
Use case ends.
2b. Week number is invalid.
2b1. TAPro shows an error message.
Use case ends.
Use case: Unmark Attendance
MSS
User requests to unmark attendance for a student by providing the student's NUSNet ID and week number to unmark the attendance for.
TAPro unmarks the attendance for the student for the specified week.
Use case ends.
Extensions
2a. No such student exists.
2a1. TAPro shows an error message.
Use case ends.
2b. Week number is invalid.
2b1. TAPro shows an error message.
Use case ends.
Use case: Clear All Data
MSS
User requests to purge all data from TAPro.
TAPro successfully clears all data.
Use case ends.
Use case: Autocompletion of Command Inputs
MSS
User focuses on the command box.
User presses the autocompletion hotkey.
Autocompleted command is shown in the command box.
Use case ends.
Extensions
3a. No autocompletion is available for the current input.
3a1. No action is taken.
Use case ends.
Use case: Retrieve a Previous Successful Command Input
MSS
User focuses on the command box.
User presses the retrieve previous command hotkey.
TAPro displays the successful command input in the command box.
Use case ends.
Extensions
3a. No previous successful command input is available.
3a1. No action is taken.
Use case ends.
Use case: Retrieve a More Recent Successful Command Input
MSS
User focuses on the command box.
User presses the retrieve next command hotkey.
TAPro displays a more recent successful command input in the command box.
Use case ends.
Extensions
3a. No successful command input that is more recent is available.
3a1. The command box will be cleared
Use case ends.
ASCIIbetical Order: An ordering where numbers and most punctuations are before letters, and uppercase letters before lowercase letters.
API: Application Programming Interface
CLI: Command Line Interface
CS: Computer Science
GUI: Graphical User Interface
Mainstream OS: Windows, Linux, Unix, MacOS
NUS: National University of Singapore
NUSNet ID: A unique identifier for each student in NUS
TA: Teaching Assistant
TAPro: The name of our product
UI: User Interface
Given below are instructions to test the app manually.
Prerequisites: For all features to test below, TAPro is already downloaded, only single instance of TAPro is already opened, and Java 11 or above is installed, unless specified otherwise.
Note: These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.
1. Prerequisites: TAPro is not downloaded, and an Internet connection is present.
Download the latest TAPro jar file from here and move it into an empty folder.
Ensure that the jar file is still named as TAPro.jar
after moving.
Open a command terminal, and cd
into that folder.
Run java -jar TAPro.jar
.
Expected: TAPro launches and shows the GUI with a set of sample student contacts. The window size may not be optimal.
1. Prerequisites: No prerequisites.
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.
If TAPro does not have any student contacts, the following commands can be used to add some students.
1. Prerequisites: No student with NUSNet ID E0123456 in TAPro.
2. Test case:
addstu n/John Doe p/98765432 e/johndoe@example.com nn/E0123456 m/Computer Science t/friends t/owesMoney
Expected: Student with NUSNet ID E0123456
is added into TAPro. Details of the added student is
shown in the result message panel.
1. Prerequisites: No student with NUSNet ID E0123457 in TAPro.
2. Test case:
addstu n/Mary Jane p/91234911 e/janemary@example.com nn/E0123457 m/Biology t/friends t/owesTutorial2
Expected: Student with NUSNet ID E0123457
is added into TAPro. Details of the added student is
shown in the result message panel.
1. Prerequisites: An existing student in TAPro with NUSNet ID E0123457 shown as index 2 in TAPro's displayed person list.
You may run
the above last addstu
command to add a student with NUSNet ID E0123457 if it does not exist.
2. Test case:
edit 2 p/98765432 m/Computer Science
Expected: Information of student with NUSNet ID E0123457
is updated in TAPro. Details of the added student is
shown in the status message.
This command differs from most other commands that use the NUSNET
to identify a student. This command uses the index number shown in the displayed person list to identify the student to be edited.
1. Prerequisites: TAPro contains at least one student with NUSNet ID E0123456, and no student with NUSNet ID E6543210.
2. Test case:
delstu nn/E0123456
Expected: The student with NUSNet ID E0123456
is deleted from TAPro. Details of the deleted student
shown in the result message panel.
3. Other incorrect delstu
commands to try:
delstu
delstu nn/E6543210
delstu E0123456
1. Prerequisites: TAPro contains one student with the name 'John Doe'.
2. Test case:
find john
Expected: Student with name 'John Doe' is displayed on the student contact cards panel.
3. Other incorrect find
commands to try:
find
1. Prerequisites: TAPro contains one student with NUSNet ID E0123456
, and no student with NUSNet ID E6543210
.
2. Test case:
mark nn/E0123456 wk/1
Expected: Student with NUSNet ID E0123456
is marked as present for week 1 in TAPro, depicted on that student's card in the panel.
Details of the marked student is shown in the result message panel.
3. Examples of incorrect mark
commands to try:
mark
mark nn/E6543210 wk/1
mark wk/1
mark E0123456 1
1. Prerequisites: TAPro contains one student with NUSNet ID E0123456
with his Week 1's attendance marked
and no
student with NUSNet ID E6543210
.
2. Test case:
unmark nn/E0123456 wk/1
Expected: Student with NUSNet ID E0123456
is not marked as for week 1 in TAPro, depicted on that student's
card in the panel.
Details of the marked student is shown in the status message.
3. Examples of incorrect unmark
commands to try:
unmark
unmark nn/E6543210 wk/1
unmark wk/1
unmark E0123456 1
1. Prerequisites: No prerequisites.
2. Test case:
setcrs CS2103
Enter setcrs
followed by a whitespace, followed by a course code in the format XXYYYYZ
, where X
and Z
can be any letter
in upper or lower case, YYYY
can be any 4-digit number and Z
is optional.
Expected: TAPro's main window's title contains the course code CS2103
provided.
addstu
command name1. Prerequisites: No prerequisites.
2. Test case:
a
Then press TAB.
Expected: The command box input text changes to:
addstu
MAJOR
1. Prerequisites: There is at least one student, and all students have the major Computer Science
.
2. Test case:
edit 1 m/
Then press TAB.
Expected: The command box input text changes to:
edit 1 m/Computer Science
1. Prerequisites: Ran the following command as the previous command:
mark nn/E0123456 wk/6
2. Test case: Retrieving a previous command with UP key
mark nn/E0123456 wk/6
1. Prerequisites: No prerequisites.
2. Test case:
help
Expected: The help window automatically pops up, giving further information about TAPro's commands.
1. Prerequisites: No prerequisites.
2. Test case:
clear
Expected: TAPro's Contact Book resets, clearing all existing students (if any).
Why does edit
command use INDEX
as identifier instead of NUSNET
?
edit nn/E0123456 nn/E1234567
is unintuitive to edit the NUSNet ID of a student.INDEX
is visually easier to reference and requires less effort to type.NUSNET
, but this would be a limitation on
the user's freedom, or would necessitate that the user deletes the student and re-enter all the details again.Note: delstu
command uses NUSNET
as identifier because it requires more intentional effort and
therefore ensures that the TA intends to perform this dangerous action.
Why does autocomplete sort in ASCIIbetical order and not normal alphabetical order?
TAG
, it is case-sensitive, so LATE
and late
are two different tags.Example: If a user uses the following tags: LATE
, early
, onTime
, to tag their students based on their latest submission, then LATE
would appear before early
and onTime
in the autocompletion of a blank tag parameter.
Why does autocompletion only give values that are already present instead of predicting the next result?
Why does autocomplete not work when the command input box is empty?
clear
command). We wanted a new user to understand the command, by referring to help or our user guide on what a command did, in order to prevent a situation where data is accidentally lost.Why are placeholder values valid input?
X not provided
, where X is the attribute), without having to perform two commands: delete the student and add back the student if a value was incorrectly filled when it is not available to the user.Example: If a user doesn't have all phone numbers, then the parameter value Phone number not provided
may be a possible value that they will use later on, and so the autocomplete helps make the process easier for the user to reset the value if necessary.
X not provided
, where X is the attribute).Example: Assuming we used the alternative solution, if a user inputs edit 1 p/
and accidentally presses enter, then the phone number of the student at index 1 in the user's contact list would be erased to Phone number not provided
.
Although this was the user's accidental mistake, it resulted in the user needing to do more work to restore back the change. Hence, we decided to use the placeholder value, so that the user will be less likely to accidentally reset an attribute by mistake.
Example: Usually, if a user would store all phone numbers or no phone numbers at all, depending on their needs.
If a user has all phone numbers present, then the parameter value Phone number not provided
would not be available as an autocompletion, to prevent accidental lost of a student's phone number by mistake.
Why does command history retrieval only retrieve successful commands?
As a team, we have conquered many problems and fought uphill battles. We did away a number of potential headaches with improved workflows and use of automation.
Below are some non-exhaustive instances of our challenges, achievements and efforts overcoming them.
Utilised Nunjucks macros and variables in documentation.
Macros were used for creating newPage
breaks as well as when replacing key button presses like TAB, ⌘CMD and
ALT. While macros are powerful tools for reusing code with parameters, variables in
Nunjucks
serve as placeholders that can dynamically insert content, such as implementing page breaks efficiently.
This method abstracts the formatting into a single, manageable location, ensuring uniformity across our documentation. It streamlines the documentation process, making it easier for contributors to apply custom styling and formatting without getting bogged down by repetitive tasks.
For further information and a deeper understanding of how we utilize Nunjucks in our documentation, you can visit the Nunjucks official documentation for Macros and Variables.
Examples:
Refactored the parameter syntax to improve AB3's original OOP.
Implemented autocomplete and command history retrieval. We thought hard about what are the potential shortcuts that would save our target user's time, and we looked to the CLI for inspiration. Given that our product uses NUSNet ID as its unique identifier, it can be a hassle to type out a full command. Especially when it has already been previously used, or only require minor changes to the command. This resulted in the idea of clicking TAB for autocomplete when allowed as well as UP and DOWN arrow keys to retrieve previous commands.
Utilised Node.js and installed MarkBind locally as a
dev-dependency in package.json
. It allows us to serve documentation on our local machines, and ensures that all developers are using the same version of MarkBind for consistency, so that no version related issues of MarkBind result in inconsistencies in our codebase.
Utilised captain-githook
pre-commit
and pre-push
checks
locally to automatically verify if code changes made will pass continuous integration checks. It allows us to focus our time on working on the quality of our code, rather that dealing with formatting related issues.
Utilised Trie data structure for optimizing the performance of TAPro, especially in terms of autocompletion functionality, recognizing the need for rapid and efficient search capabilities. Tries are exceptionally well-suited for autocomplete systems, as they allow for the quick retrieval of full words based on partial inputs, dramatically improving our application's responsiveness during user searches.
Utilised caching to further enhance the performance and user experience of TAPro's autocompletion feature.
Team size: 5
Allowed Enhancements: 10
Add duplicate prefix checks in the mark
command.
mark
command.mark nn/E1234567 wk/1 wk/3
, which would only mark the attendance for a student with NUSNet ID E1234567 in week 3. By adding duplicate prefix checks, it would give the correct error message:
Multiple values specified for the following single-valued field(s): wk/
Add duplicate prefix checks for in the unmark
command.
unmark
command.unmark nn/E1234567 wk/1 wk/3
, which would only unmark the attendance for a student with NUSNet ID E1234567 in week 3. By adding duplicate prefix checks, it would give the correct error message:
Multiple values specified for the following single-valued field(s): wk/
Add duplicate prefix checks in the delstu
command.
delstu
command.delstu nn/E1234567 nn/E2345678
, which would only delete a student with NUSNet ID E2345678. By adding duplicate prefix checks, it would give the correct error message:
Multiple values specified for the following single-valued field(s): nn/
Improve name format validation.
Zubir bin Said
, Balaji s/o Sadasivan
, O'Brien
, McDonald
, van Dyke
, Saint-John
are not valid currently, which is a feature flaw.Saint-John
and O'Brien
are valid.McDonald
, John Smith III
and van Dyke
to be valid.s/o
, d/o
, bin
, and so on. This is so that the names like Balaji s/o Sadasivan
and Zubir bin Said
are valid.da
, de
, di
, del
, dos
, los
, and so on. This allows names like De los Santos
, and Leonardo da Vinci
.The lists of prepositions, abbreviations, and punctuations here are non-exhaustive.
Improve email format validation.
name/surname@example.com
" "@example.org
"john..doe"@example.org
mailhost!username@example.org
"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com
user%example.com@example.org
user-@example.org
postmaster@[123.123.123.123]
postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]
_test@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]
Improve handling of duplicate email addresses.
Improve handling of duplicate phone numbers.
Add multiple majors for a student more intuitively.
MAJOR
is an optional parameter, which means only one or zero values are accepted from this attribute. The workaround currently would be for a user to use comma-separated majors, so that users to indicate multiple majors for a student.MAJOR
into a multiple parameter, which would appear as [m/MAJOR]…
in the command formats.Searching for students by their attendance in a certain week(s).
find
command only works on the name attribute of a student, meaning that we can only find a student by name.find
command in the following way to achieve this change. The find
command format would be changed to find [KEYWORD]… [wk/WEEK]…
, so that the find command works on the WEEK
parameter.KEYWORD
and MORE_KEYWORDS
are modified, is to handle the situation when a user wants to search for students just by attendance, so the constraint that a keyword has to be present has to be removed in order to implement this with the find
command. We minimise the changes the original behaviour of the KEYWORD
and MORE_KEYWORDS
parameters.find
to execute successfully, to preserve the current behaviour that one keyword must be present.find wk/1
will find all students whose attendance is marked in week 1.find wk/1 wk/2
will find all students whose attendance is marked in either week 1 or week 2, following the current behaviour of the find
command with KEYWORDS
and MORE_KEYWORDS
.Alert the user when more than one instance of TAPro is open.
Multiple instances of TAPro open! TAPro may de-synchronize resulting in the loss of data in this session!