By: CS2103JAN2018-W14-B4 Since: Jun 2016 Licence: MIT
- 1. Overview
- 2. Setup
- 3. Design
- 4. Implementation
- 5. Documentation
- 6. Testing
- Appendix A: Product Scope
- Appendix B: User Stories
- Appendix C: Use Cases
- C.1. Use case: Delete person
- C.2. Use case: Undo
- C.3. Use case: Redo
- C.4. Use case: Remove tag
- C.5. Use case: Add appointment
- C.6. Use case: View appointment
- C.7. Use case: List appointments
- C.8. Use case: Delete appointment
- C.9. Use case: Edit appointment
- C.10. Use case: Show location in map
- C.11. Use case: Show estimated route by driving in map
- C.12. Use case: Show estimated distance and time of travel by driving
- Appendix D: Non Functional Requirements
- Appendix E: Glossary
- Appendix F: Instructions for Manual Testing
- F.1. Launch
- F.2. Adding a person/celebrity
- F.3. Deleting a person/celebrity
- F.4. Removing a tag
- F.5. Adding an appointment
- F.6. Listing appointments
- F.7. Edititng an appointment
- F.8. Deleting an appointment
- F.9. Viewing an appointment
- F.10. Showing location on map
- F.11. Estimating route between two location on map
- F.12. Viewing a celebrity’s calendar
- F.13. Viewing the combined calendar
- F.14. Changing to a different calendar view
- F.15. Viewing a particular day’s calendar
- F.16. Transferring of storage calendar .xml file
CelebManager is a software application that aims to allow celebrity managers to maintain schedule of celebrities under them. CelebManager is optimized for users who prefer to work with a Command Line Interface (CLI) while still having a Graphical User Interface (GUI) for visual feedback.
In this software, the users should be able to:
-
Manage contacts
-
Manage appointments
-
Manage calendars
-
Plan efficient routes
This developer guide aims to allow you to develop and maintain the software with information on how the software is designed and implemented. Information on how the software should be developed and maintained is also in this documentation.
This section will discuss the setting up of project for development.
There are two prerequisites before you can work on this software. They are (in the order you should obtain them):
-
Java Development Kit (JDK)
The Java programming language is used in this project. To be able to work with this project, you will need to have JDK version1.8.0_60installed. You can get the JDK from:
http://www.oracle.com/technetwork/java/javase/downloads/index.htmlℹ️Some components of this software will not work with earlier versions of Java 8. -
IntelliJ Integrated Development Environment (IDE)
This software is developed as a Gradle project, which requires you to work on the software using a IDE. While it is possible to work with any IDE that supports Gradle Projects, this guide will use IntelliJ as a basis. You can get IntelliJ from:
https://www.jetbrains.com/idea/ℹ️IntelliJ make use of Gradle and JavaFx plugins, and the project will need these plugins.
If you have disabled them, go toFile>Settings>Pluginsto re-enable them.
To contribute to this project, you will need to work with a local copy of this project. To do so:
-
Fork this repo, and clone the fork to your computer.
-
Open IntelliJ (if you are not in the welcome screen, click
File>Close Projectto 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.gradlefile, select it and clickOK. -
Click
Open as Project. -
Click
OKto accept the default settings. -
Open a console and run the command
gradlew processResources. (Mac/Linux:./gradlew processResources). It should finish with theBUILD SUCCESSFULmessage.
This will generate all resources required by the application and tests.
To ensure that you have setup the project correctly:
-
Run the
seedu.address.MainAppand 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
Importstab to set the order. Take note of the following:-
For
Class count to use import with '*'andNames count to use static import with '*': Set to999to 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.
-
Alternatively, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.
After forking the repo, links in the documentation will still point to the CS2103JAN2018-W14-B4/main repository.
If you plan to develop this as a separate product (i.e. instead of contributing to the CS2103JAN2018-W14-B4/main repository), you should replace the variable repoURL in DeveloperGuide.adoc and UserGuide.adoc with the URL of your fork.
There are two CI configurations that you can set up.
To set up Travis for performing CI for your fork, please refer to UsingTravis.adoc. Optionally, to set up AppVeyor for performing CI, please refer to UsingAppVeyor.adoc.
|
ℹ️
|
Travis is an Unix-based software, while AppVeyor is a Windows-based software. Having both Travis CI and AppVeyor CI ensures your App works on both Unix-based platforms and Windows-based platforms. |
You should also set up coverage reporting for your team fork. Please refer to 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. |
Before starting to work on the project after successful configurations, you are encouraged to:
-
Understand the overall design (Section 3.1, “Software Architecture”).
-
Understand the product scope (Appendix A, Product Scope).
The Architecture Diagram given below explains the high-level design of the project.
Main has only one class called link: MainApp. It is responsible for:
-
Initializing the components in the correct sequence and connects them up with each other at application launch.
-
Shutting down the components and invokes cleanup method where necessary.
Commons represents a collection of classes used by multiple components.
Two of these classes are vital at the architecture level. These are:
-
EventsCenteris used by components to communicate with other components using events. -
LogsCenteris used by many classes to write log messages to the application’s log file.
|
ℹ️
|
EventsCenter is written using the Google’s Event Bus library.
It is a form of event-driven design.
|
The rest of the App consists of four components, each defining its API in an interface, and exposing its functionality using a {Component Name}Manager class.
The components are:
Common class files, such as EventsCenter, are used by multiple components are in the seedu.addressbook.commons package.
The sequence diagram below shows how the components interact for the scenario where the user issues the command delete 1.
Note that the Model simply raises a AddressBookChangedEvent when the CelebManager data is changed, instead of asking the Storage to save the updates to the hard disk.
The diagram below shows how the EventsCenter reacts to that event, which results in the updates being saved to the hard disk.
The status bar of the UI is also updated to reflect the 'Last Updated' time.
Note how the event is propagated through the EventsCenter to the Storage and UI without Model having to be coupled to either of them.
The following diagram shows the class diagram of the UI component.
API: link: Ui.java
The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, PersonListPanel, StatusBarFooter, CalendarPanel etc.
All these classes 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 link: MainWindow is
specified in link: MainWindow.fxml.
The UI component,
-
executes user commands using the
Logiccomponent. -
binds itself to some data in the
Modelso that the UI can auto-update when data in theModelchange. -
responds to events raised from various parts of the App and updates the UI accordingly.
The following diagrams shows the structure of the Logic component, and details on XYZCommand and Command in Figure 5, “Class diagram of the Logic component”.
It describe the overall structure of the Logic component and how a single command such as XYZCommand and other commands are structured respectively.
API: link: Logic.java
When the user types in a new command to be parsed:
-
The
Logicuses theAddressBookParserclass to parse the user command. -
A
Commandobject is then executed by theLogicManager. -
The command execution can affect the
Model(e.g. adding a person) and/or raise events. -
The result of the command execution is then encapsulated as a
CommandResultobject which is passed back to theUi.
The following diagram shows the sequence diagram for interactions within the Logic component for the execute("delete 1") API call.
The following diagram shows the class diagram of the Model component.
It describes the overall structure of the Model component, along with all its sub-components.
API: link: Model.java
The Model component:
-
stores a
UserPrefobject that represents the user’s preferences. -
stores the Address Book data.
-
stores a
StorageCalendarobject that contains all appointments. -
stores a
CalendarSourceobject that is used to display the calendar. -
stores a list of appointments which will be displayed for
listAppointmentcommand. -
exposes 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. -
does not depend on any of the other three components.
The following diagram shows the class diagram of the Storage component.
It describes how the overall structure of the Storage component and its different sub-components.
API: link: Storage.java
The Storage component:
-
saves
UserPrefobjects in json format and read it back. -
saves contacts data in xml format and read it back.
-
saves appointments data in xml format and read it back.
This section describes some noteworthy features that are implemented in CelebManager.
This feature allows the user to remove a specified tag from all the contacts.
The tag removal mechanism is facilitated by both RemoveTagCommand class, which resides inside Logic, and
removeTag method, which resides in AddressBook. This feature removes a specified tag from each person who has it in the
address book. RemoveTagCommand class inherits from the UndoableCommand class and hence supports the undo and redo features.
When a user enters removeTag command, an object of RemoveTagCommand class will be created. To understand this class’
contribution to remove tag feature, please refer to the following code snippet which shows the implementation of
executeUndoableCommand in RemoveTagCommand:
public class RemoveTagCommand extends UndoableCommand {
...
public static final String MESSAGE_DELETE_TAG_SUCCESS = "Removed tag %1$s and %2$s person(s) affected.";
...
@Override
public CommandResult executeUndoableCommand() throws CommandException {
requireNonNull(tagToRemove);
if (tagToRemove.equals(CELEBRITY_TAG)) {
throw new CommandException(MESSAGE_CANNOT_REMOVE_CELEBRITY_TAG);
}
int numberOfAffectedPersons = 0;
try {
numberOfAffectedPersons = model.removeTag(tagToRemove);
} catch (TagNotFoundException tnfe) {
throw new CommandException(String.format(MESSAGE_TAG_NOT_FOUND, tagToRemove.toString()));
} catch (DuplicatePersonException dpe) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
} catch (PersonNotFoundException pnfe) {
throw new AssertionError("The target person cannot be missing");
}
return new CommandResult(String.format(
MESSAGE_DELETE_TAG_SUCCESS,
tagToRemove.toString(),
numberOfAffectedPersons));
}
...
}From the snippet above, RemoveTagCommand class filters out celebrity tag or a non-existent tag to disallow the removal
of both. It is also in-charge of printing the successful message upon a successful execution.
Within RemoveTagCommand class, removeTag method is called to actually remove the tag, which calls removeTag method of
AddressBook. The snippet code below shows the implementation of removeTag in AddressBook:
public class AddressBook {
/**
* Removes {@code tag} from all persons in this {@code AddressBook}.
* @return the number of {@code person}s with this {@code tag} removed.
*/
public int removeTag(Tag tag) throws PersonNotFoundException, DuplicatePersonException, TagNotFoundException {
boolean tagExists = false;
for (Tag existingTag: tags) {
if (existingTag.equals(tag)) {
tagExists = true;
}
}
if (!tagExists) {
throw new TagNotFoundException();
}
int count = 0;
for (Person person: persons) {
if (person.hasTag(tag)) {
//get the new tag set with the specified tag removed
Set<Tag> oldTags = person.getTags();
Set<Tag> newTags = new HashSet<>();
for (Tag tagToKeep: oldTags) {
if (tagToKeep.equals(tag)) {
continue;
}
newTags.add(tagToKeep);
}
//create a new person with the specified tag removed to replace the person
EditCommand.EditPersonDescriptor editPersonDescriptor = new EditCommand.EditPersonDescriptor();
editPersonDescriptor.setTags(newTags);
Person editedPerson = createEditedPerson(person, editPersonDescriptor);
Person syncedEditedPerson = syncWithMasterTagList(editedPerson);
persons.setPerson(person, syncedEditedPerson);
removeUnusedTags();
count++;
}
}
return count;
}
...
}Note that removeTag makes use of EditPersonDescriptor class to create a new person without the tag, to replace
the original person with the tag.
Additionally, removeUnusedTags is called inside removeTag when there is at least one person affected by the removal.
This is because removeTag removes the tag from each person with it and after the operation, no person in the
address book should have the tag. Hence the unused tag should be removed from tags inside the address book.
As an example, the following sequence diagram shows the interaction within classes in Logic package when
the user executes removeTag friends successfully:
-
Alternative 1 (current choice): Output an error message saying that the
celebritytag cannot be removed-
Pros: Prevents
removeTagfrom affecting the calendar as celebrities will not get affected by this operation. -
Cons: Results in no available method to mass remove
celebritytag.
-
-
Alternative 2: Remove
celebritytag and clear all calendars-
Pros: Provides an easy way to mass remove
celebritytag and clears all celebrities from the address book. -
Cons: As changes made to calendars and appointments are not undoable, removing
celebritytag by mistake can result in loss of all celebrities' calendar and appointment information.
-
The undo command allows users to reverse the effect of the previous command, and the redo command allows the users to reverse the effects of undoing commands.
The undo/redo mechanism is facilitated by an UndoRedoStack in LogicManager class. It supports undoing and redoing of commands that modifies the state of the address book, such as add and edit.
In the implementation, these commands will inherit from UndoableCommand class, while the commands that cannot be undone will inherit from the Command class instead.
The following figure shows the inheritance diagram with regards to the feature:
From the figure, the UndoableCommand class provides an interface between the abstract Command class and concrete commands that can be undone, such as the DeleteCommand.
UndoableCommand contains high-level algorithms for additional tasks, such as saving the application state before command execution.
Its child classes implements the details of how to execute the specific command.
|
ℹ️
|
Undoable commands require additional tasks to be completed, such as saving the application state, before command execution. |
|
ℹ️
|
The technique of containing the high-level algorithms in the parent class, while implementing lower-level algorithms in child classes is also known as the template pattern. |
With the additional interface, the commands that are undoable are implemented in this way:
public abstract class UndoableCommand extends Command {
@Override
public CommandResult execute() {
// ... undo logic ...
executeUndoableCommand();
}
}
public class DeleteCommand extends UndoableCommand {
@Override
public CommandResult executeUndoableCommand() {
// ... delete logic ...
}
}Commands that are not undoable are implemented this way:
public class ListCommand extends Command {
@Override
public CommandResult execute() {
// ... list logic ...
}
}The UndoRedoStack will be empty at the beginning when the user first launches the application.
For example, when the user executes a delete 5 command, an UndoableCommand, to delete the 5th person in the address book, the current state of the address book is saved.
The delete 5 command will then be pushed onto the undoStack.
The current state of the application is then saved together with the command.
The following figure shows the illustration after executing the command.
As the user continues to execute commands that are undoable in the application, more commands are added into the undoStack.
For example, the user may execute an add n/David … command to add a new person.
The following figure shows the illustration after executing the second command.
|
ℹ️
|
If a command fails its execution, it will not be pushed to the undoStack at all.
|
If the user decides to undo that action using undo command, the undoStack will pop the most recent command, and push the command into the redoStack.
The application will restore to the state before the add n/David … command executed.
The following figure shows the illustration after executing the undo command.
|
ℹ️
|
If the undoStack is empty, then there are no other commands left to be undone.
An Exception will be thrown when popping the undoStack.
|
The following figure shows the sequence diagram on how the undo command works.
The redo command pops the most recent undone command from redoStack, and push the command to the undoStack.
This will also restore the address book to the state after the command is executed.
|
ℹ️
|
If the redoStack is empty, then there are no other commands left to be redone.
An Exception will be thrown when popping the redoStack.
|
-
Alternative 1 (current choice): Add a new abstract method
executeUndoableCommand()-
Pros: We will not lose any undone/redone functionality as it is now part of the default behaviour. Classes that deal with
Commanddo not have to know thatexecuteUndoableCommand()exist. -
Cons: Hard for new developers to understand the template pattern.
-
-
Alternative 2: Just override
execute()-
Pros: Does not involve the template pattern, easier for new developers to understand.
-
Cons: Classes that inherit from
UndoableCommandmust remember to callsuper.execute(), or lose the ability to undo/redo.
-
-
Alternative 1 (current choice): Save the entire address book.
-
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): Only include commands that modifies the address book (
add,clear,edit).-
Pros: We only revert changes that are hard to change back (the view can easily be re-modified as no data are * lost).
-
Cons: User might think that undo also applies when the list is modified (undoing filtering for example), * only to realize that it does not do that, after executing
undo.
-
-
Alternative 2: Include all commands.
-
Pros: Might be more intuitive for the user.
-
Cons: User have no way of skipping such commands if he or she just want to reset the state of the address * book and not the view. Additional Info: See our discussion here.
-
-
Alternative 1 (current choice): Use separate stack for undo and redo
-
Pros: Easy to understand 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
HistoryManagerandUndoRedoStack.
-
-
Alternative 2: Use
HistoryManagerfor undo/redo-
Pros: We do not need to maintain a separate stack, 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
HistoryManagernow needs to do two * different things.
-
The AddAppointment mechanism is facilitated by the AddAppointmentCommand, which resides inside Logic. It supports the adding of an appointment to an existing calendar.
The appointment, if added successfully, can be viewed in our CalendarPanel UI. This is done by retrieving the list of calendars stored in our CalendarPanel
and then adding the appointment to one or more of these calendars. This command extends Command so it does not support the undo/redo feature.
To be able to create appointments, add them to calendars and view the calendar with the added appointments, the external CalendarFX package is used. The API for all the CalendarFX classes and methods used can be found here.
-
For the calendar, the
CelebCalendarclass is used, which extends the defaultCalendarclass from CalendarFX used to describe a calendar. -
For the appointment, the
Appointmentclass is used, which is extended fromEntry, the default class used to represent an entry in aCalendarin CalendarFX. -
All
CelebCalendarinstances reside in an instance ofCalendarSource, the class used to store a group of calendars in CalendarFX. -
This instance of
CalendarSourceis atttached to ourCalendarViewwhich is the GUI for our calendar.
|
ℹ️
|
Inheritance from the base classes of the external package is done so that we can add in additional methods as necessary. |
Right now, the addAppointment command takes in up to 8 parameters. They are:
-
Appointment name [Compulsory field]
-
Location
-
Start Date
-
Start Time
-
End Date
-
End Time
-
Celebrity Indices
-
Point of Contact Indices
The AddAppointmentCommandParser is able to create sensible appointments even if 1 or more of the non-compulsory fields are not included. The snippet code below shows how the parsing is handled:
public AddAppointmentCommand parse(String args) throws ParseException {
ArgumentMultimap argMultiMap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_START_TIME,
PREFIX_START_DATE, PREFIX_LOCATION, PREFIX_END_TIME, PREFIX_END_DATE, PREFIX_CELEBRITY,
PREFIX_POINT_OF_CONTACT);
if (!arePrefixesPresent(argMultiMap, PREFIX_NAME)
|| !argMultiMap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
AddAppointmentCommand.MESSAGE_USAGE));
}
try {
String appointmentName = ParserUtil.parseGeneralName(argMultiMap.getValue(PREFIX_NAME)).get();
Optional<LocalTime> startTimeInput = ParserUtil.parseTime(argMultiMap.getValue(PREFIX_START_TIME));
Optional<LocalDate> startDateInput = ParserUtil.parseDate(argMultiMap.getValue(PREFIX_START_DATE));
Optional<LocalTime> endTimeInput = ParserUtil.parseTime(argMultiMap.getValue(PREFIX_END_TIME));
Optional<LocalDate> endDateInput = ParserUtil.parseDate(argMultiMap.getValue(PREFIX_END_DATE));
Optional<MapAddress> locationInput = ParserUtil.parseMapAddress(argMultiMap.getValue(PREFIX_LOCATION));
Set<Index> celebrityIndices = ParserUtil.parseIndices(argMultiMap.getAllValues(PREFIX_CELEBRITY));
Set<Index> pointOfContactIndices = ParserUtil.parseIndices(argMultiMap.getAllValues(PREFIX_POINT_OF_CONTACT));
MapAddress location = null;
LocalTime startTime = LocalTime.now();
LocalDate startDate = LocalDate.now();
LocalTime endTime = LocalTime.now();
LocalDate endDate = LocalDate.now();
if (startTimeInput.isPresent()) {
startTime = startTimeInput.get();
endTime = startTimeInput.get();
}
if (endTimeInput.isPresent()) {
endTime = endTimeInput.get();
}
if (startDateInput.isPresent()) {
startDate = startDateInput.get();
endDate = startDateInput.get();
}
if (endDateInput.isPresent()) {
endDate = endDateInput.get();
}
if (locationInput.isPresent()) {
location = locationInput.get();
}
...
}
...
}The format for all the fields are located inside of Appointment and are as follows:
public class Appointment extends Entry {
public static final String MESSAGE_NAME_CONSTRAINTS =
"Appointment names should only contain alphanumeric characters and spaces, and it should not be blank"; // used for name and location
public static final String MESSAGE_TIME_CONSTRAINTS =
"Time should be a 2 digit number between 00 to 23 followed by a :"
+ " followed by a 2 digit number beetween 00 to 59. Some examples include "
+ "08:45, 13:45, 00:30";
public static final String MESSAGE_DATE_CONSTRAINTS =
"Date should be a 2 digit number between 01 to 31 followed by a -"
+ " followed by a 2 digit number between 01 to 12 followed by a -"
+ " followed by a 4 digit number describing a year. Some months might have less than 31 days."
+ " Some examples include: 13-12-2018, 02-05-2019, 28-02-2018";
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-uuuu")
.withResolverStyle(ResolverStyle.STRICT); // prevent incorrect dates
...
}The following sequence diagram (Figure 19) gives an overview of how the command works and interacts with the other components:
The figure below (Figure 20) shows the state of the application before input of the AddAppointmentCommand:
After input of addAppointment n/Oscars 2018 st/18:00 sd/06-04-2018 l/Hollywood et/20:00 ed/06-04-2018 c/1,
the added appointment will be reflected in the calendar as shown in the figure below (Figure 21):
-
Alternative 1 (current choice): Cannot be undone
-
Pros: Needs not remember previous state of the storage calendar.
-
Pros: If user made small mistake in one or more of the fields, can use
editAppointmentcommand instead of undo and re-add the new appointment with the correct fields. -
If user instead just want to cancel the appointment, can use
deleteAppointmentcommand -
Cons: Cannot remove or edit additions made by mistake without looking at the list of appointments.
-
-
Alternative 2: Can be undone
-
Pros: Can remove additions made by mistake.
-
Cons: Requires drastic change in the way calendars are currently saved and loaded, as calendars currently only stay in UI component while appointments in Model component.
-
This feature allows the user to delete appointments.
The mechanism to delete an appointment is facilitated by the DeleteAppointmentCommand class, which resides inside Logic,
and deleteAppointment method in Model. The command requires the user to put in an index to refer to the appointment
to be deleted. This index is taken from the currently displayed appointment list. As such, deletion of an appointment
can only be done when CelebManager is showing an appointment list.
DeleteAppointmentCommand class is responsible for checking whether an appointment list is currently being shown.
If so, DeleteAppointmentCommand class will call deleteAppointment method. Refer to the code snippet below to see
how this method deletes the specified appointment:
public class DeleteAppointmentCommand extends Command {
...
public static final String MESSAGE_SUCCESS = "Deleted Appointment: %1$s";
public static final String MESSAGE_APPOINTMENT_LIST_BECOMES_EMPTY = "\nAppointment list becomes empty, "
+ "Switching back to calendar view by day\n"
+ "Currently showing %1$s calendar";
...
@Override
public CommandResult execute() throws CommandException {
// throw exception if the user is not currently viewing an appointment list
if (!model.getIsListingAppointments()) {
throw new CommandException(Messages.MESSAGE_MUST_SHOW_LIST_OF_APPOINTMENTS);
}
apptToDelete = model.deleteAppointment(targetIndex.getZeroBased());
List<Appointment> currentAppointmentList = model.getAppointmentList();
// if the list becomes empty, switch back to combined calendar day view
if (currentAppointmentList.size() < 1) {
EventsCenter.getInstance().post(new ChangeCalendarViewPageRequestEvent(DAY_VIEW_PAGE));
EventsCenter.getInstance().post(new ShowCalendarEvent());
Celebrity currentCalendarOwner = model.getCurrentCelebCalendarOwner();
if (currentCalendarOwner == null) {
return new CommandResult(
String.format(MESSAGE_SUCCESS, apptToDelete.getTitle())
+ String.format(MESSAGE_APPOINTMENT_LIST_BECOMES_EMPTY,
"combined"));
} else {
return new CommandResult(
String.format(MESSAGE_SUCCESS, apptToDelete.getTitle())
+ String.format(MESSAGE_APPOINTMENT_LIST_BECOMES_EMPTY,
currentCalendarOwner.getName().toString() + "'s"));
}
}
// if the list is not empty yet, update appointment list view
EventsCenter.getInstance().post(new ShowAppointmentListEvent(currentAppointmentList));
return new CommandResult(String.format(MESSAGE_SUCCESS, apptToDelete.getTitle()));
}
...
}From the snippet above, it can be seen that DeleteAppointmentCommand changes CalendarPanel back to combined
calendar view if there is no more appointment in the appointment list after deletion. Otherwise, the appointment list
with the specified appointment deleted will be shown.
The snippet code below shows the implementation of deleteAppointment in Model:
public class ModelManager extends ComponentManager implements Model {
...
@Override
public Appointment deleteAppointment(int index) throws IndexOutOfBoundsException {
Appointment apptToDelete = getChosenAppointment(index);
apptToDelete.removeAppointment();
removeAppointmentFromInternalList(index);
currentlyDisplayedAppointments.remove(apptToDelete);
indicateAppointmentListChanged();
return apptToDelete;
}
...
}The method removeAppointment is in Appointment class, and removes all child entries of an appointment. For example,
an appointment may have two celebrities attending. Then this appointment will have two child entries, one each in each
attending celebrity’s calendar. So when this appointment gets deleted, both entries should get removed as well.
-
Alternative 1 (current choice): Switch back to combined calendar view
-
Pros: Keeps consistent with
listAppointmentas CelebManager does not show an empty list when there is no appointment to list, but instead outputs an error message. -
Cons: Makes it difficult for users to see if the appointment gets deleted correctly.
-
-
Alternative 2: Stay at the appointment list view and shows an empty list
-
Pros: Shows the effect of deletion immediately.
-
Cons: Becomes inconsistent with
listAppointmentcommand’s inability to show an empty list when there is no appointment to list.
-
-
Alternative 3: Switch back to combined calendar view and goes to the day when the deleted appointment should happen
-
Pros: Keeps consistent with
listAppointmentwhile making it easy for users to check if the appointment gets deleted visually on calendar. -
Cons: Takes long time to run.
-
-
Alternative 1 (current choice): Cannot be undone
-
Pros: Needs not remember previous appointments' and calendar’s status.
-
Cons: Cannot restore deletions made by mistake.
-
-
Alternative 2: Can be undone
-
Pros: Can restore deletions made by mistake.
-
Cons: Requires drastic change in the way calendars are currently saved and loaded, as calendars currently only stay in UI component while appointments in Model component.
-
The ViewAppointment mechanism is facilitated by the ViewAppointmentCommand, which resides inside Logic. It supports the viewing of a specific appointment
in the ResultDisplayPanel by displaying the Appointment details. The specific Appointment is selected using an index based on the list generated by ListAppointmentCommand.
This command inherits from Command so it does not support the undo/redo feature.
The input index is one-based which means that the smallest possible index is '1' and the largest possible index is the size of list
generated by ListAppointmentCommand (total number of Appointment).
As this command relies on the list generated by ListAppointmentCommand, the command retrieves the start (earliest) and end (latest) date
from ListAppointmentCommand, which is used to generate the appointment list internally from the StorageCalendar in Model.
This is done by getChosenAppointment() method.
The snippet code below shows the code that retrieves the selected appointment.
public CommandResult execute() throws CommandException {
if (!model.getIsListingAppointments()) {
throw new CommandException(MESSAGE_MUST_SHOW_LIST_OF_APPOINTMENTS);
}
try {
selectedAppointment = model.getChosenAppointment(chosenIndex);
} catch (IndexOutOfBoundsException iobe) {
throw new CommandException(MESSAGE_INVALID_APPOINTMENT_DISPLAYED_INDEX);
}
try {
ShowLocationCommand showLocation = new ShowLocationCommand(
new MapAddress(selectedAppointment.getLocation()));
showLocation.execute();
return new CommandResult(MESSAGE_SUCCESS + getAppointmentDetailsResult());
} catch (NullPointerException npe) {
return new CommandResult(MESSAGE_SUCCESS + getAppointmentDetailsResult());
}
}To show the location in the MapPanel, ShowLocationCommand is used to update the location marker in MapPanel to
point to the Appointment location.
In the case where the Appointment do not have any location data, the result will still be displayed without the
location being shown in the map.
|
ℹ️
|
Whenever an Appointment has no location data, any existing location marker or route will be removed from the map.
|
The diagram below in figure 32 shows the sequence diagram of ViewAppointmentCommand.
-
Alternative 1 (current choice): Extend
Command-
Pros: Easy to understand for new developers who will be developing this project as the command is at the same abstraction level as other commands.
-
Cons: Does not have the undo/redo feature as it is not part of
UndoableCommand.
-
-
Alternative 2: Extend
UndoableCommand-
Pros: Allows for command to have the undo/redo function.
-
Cons: Requires more work that may not fit in with our timeline.
-
-
Alternative 1 (current choice): Show location of appointment on map
-
Pros: Reduces the hassle of keying an extra command to show
Appointmentlocation on map. -
Cons: Reduces independent usage of
ShowLocationCommand.
-
-
Alternative 2: Does not show location on map
-
Pros: Reduces unnecessary showing of location.
-
Cons: Requires an extra command input to show location when required.
-
The storing of appointment is facilitated by the XmlStorageCalendarStorage class, which resides in the Storage component.
It supports the retrieval and storage for appointments made by the user.
During start-up of application, the storage component will be initialized by the MainApp, which retrieves information from the specified file path in UserPrefs.
The following code snippet shows how the storage component will be initialized by the MainApp.
public void init() throws Exception {
// initializes application.
UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath());
userPrefs = initPrefs(userPrefsStorage);
AddressBookStorage addressBookStorage = new XmlAddressBookStorage(userPrefs.getAddressBookFilePath());
StorageCalendarStorage storageCalendarStorage =
new XmlStorageCalendarStorage(userPrefs.getStorageCalendarFilePath());
storage = new StorageManager(addressBookStorage, userPrefsStorage, storageCalendarStorage);
// initializes other component in the application.
}The following figure shows the sequence diagram for reading StorageCalendar.
In the XmlStorageCalendarStorage class, it allows developers to use methods:
-
readStorageCalendar, to retrieve aStorageCalendar-
This is done by checking if the file exist, and load the list from
XmlSerializableStorageCalendar.
-
-
saveStorageCalendar, to write information intofilePathspecified inuserPrefs-
This is done by creating a new file and rewriting to the list in
XmlSerializableStorageCalendar.
-
While the XmlStorageCalendarStorage class allows access to data stored on the hard disk, the XmlSerializableStorageCalendar class represents the data of the appointment list for the calendar.
In XmlSerializableStorageCalendar, it contains a List of XML formats of appointments XmlAdaptedAppointment.
XmlAdaptedAppointment will then contain essential information of different Appointment in StorageCalendar of the Model component, which includes:
-
titleof appointment -
startTimeof appointment indicating its starting time -
startDateof appointment indicating its starting date -
endTimeof appointment indicating its ending time -
endDateof appointment indicating its ending date -
locationof appointment that is going to happen -
celebrityIdsof celebrities that are attending the appointment -
pointOfContactIdsof non-celebrities that are attending the appointment
-
Alternative 1 (current choice): Adapting existing
AddressBookStorage-
Pros: Allows similar structure that can be maintained easily in
Storagecomponent -
Cons: Prevents major overhaul in future if necessary
-
-
Alternative 2: Redefining
StorageCalendarStorage-
Pros: Allows flexibility in implementation
-
Cons: Confuses developer with different requirements for a single component
-
-
Alternative 1 (current choice): Using a single
List-
Pros: Allows simplicity
-
Cons: Slows the application if there are too many appointments
-
-
Alternative 2: Using a single
Setsuch asTreeSet-
Pros: Lowers impact in speed when there are many appointments
-
Cons: Complicates implementation when speed is not an issue
-
The ShowLocation mechanism is facilitated by the ShowLocationCommand, which resides inside Logic. It supports the viewing of location
in the MapPanel by updating the state of the MapPanel. This is done by re-centering the MapPanel to the latitude and longitude of the
location and identifying it with a location marker. This command inherits from Command so it does not support the undo/redo feature.
The following figure shows the marker that is used to identify the location in the MapPanel:
The following diagram shows the inheritance diagram for ShowLocationCommand:
As you can see from the diagram, ShowLocationCommand inherits from the Command class and is not part of the Undoable Command.
Similar to the other commands like FindCommand it will not be identified by the undo/redo feature.
This command does not use the Person Address model to search for a specific location, it uses the MapAddress model. This is due to
the difference in address specification details as the Address model is too specific for the command to work.
An example would be the details of unit number (e.g #11-111) which will result in an invalid command or inaccurate result.
The main difference between both model is shown below in the two code snippets.
The snippet code below shows the Address model:
public class Address {
public static final String MESSAGE_ADDRESS_CONSTRAINTS =
"Person addresses can take any values, and it should not be blank";
/*
* The first character of the address must not be a whitespace,
* otherwise " " (a blank string) becomes a valid input.
*/
public static final String ADDRESS_VALIDATION_REGEX = "[^\\s].*";
/**
* Returns true if a given string is a valid person email.
*/
public static boolean isValidAddress(String test) {
return test.matches(ADDRESS_VALIDATION_REGEX);
}
...
}The snippet code below shows the MapAddress model:
public class MapAddress {
public static final String MESSAGE_ADDRESS_MAP_CONSTRAINTS =
"Address should be in location name, road name, block and road name or postal code format.\n"
+ "Note:(Person address may not be valid as it consist of too many details like unit number)"
/*
* The first character of the address must not be a whitespace,
* otherwise " " (a blank string) becomes a valid input.
*/
public static final String ADDRESS_VALIDATION_REGEX = "[^\\s].*";
...
/**
* Returns true if a given string is a valid map address.
*/
public static boolean isValidAddress(String test) {
boolean isValid;
Geocoding testAddress = new Geocoding();
isValid = testAddress.checkIfAddressCanBeFound(test);
return test.matches(ADDRESS_MAP_VALIDATION_REGEX) && isValid;
}
...
}The difference to note is the isValidAddress method, where Address only checks for blank space whereas MapAddress
checks for blank space and the validity of location in google server. Thus, making the command more restrictive to location,
road, block name and postal code. Any details more than that, would result in a higher possibility of it being invalid or inaccurate.
This command uses the GMAPSFX API and Google Maps Web Services API library which can be found here and here respectively.
-
GMAPSFX APIis used to create theMapPanelclass which allows the command to re-center and mark the new location which is then shown to the user. -
Google Maps Web Services APIis used to create theGeocodingclass, which is used to convertMapAddressinto latitude and longitude form (LatLng). TheLatLngform is then used by the command to find the exact location in theMapPanel.
Every new input of this command will remove the previous route or location marker and add the new marker into the map.
The snippet below shows the state of MapPanel before input of ShowLocation command:
After the input of "showLocation ma/Punggol" the MapPanel will be updated to the diagram below:
|
ℹ️
|
Whenever an invalid showLocation command is done, any existing location marker or route will be removed from the map.
|
The following sequence diagram shows how the command works:
-
Alternative 1 (current choice): Extend
Command-
Pros: Allows new developers to understand easily as the command is at the same abstraction level as other commands.
-
Cons: Does not have the undo/redo feature as it is not part of
UndoableCommand.
-
-
Alternative 2: Extend
UndoableCommand-
Pros: Allows for command to have the undo/redo function.
-
Cons: Requires more work that may not fit in with our timeline
-
-
Alternative 1 (current choice): Use
MapAddress-
Pros: Allows the clear distinction of requirements between
MapAddressandAddressto avoid confusion -
Cons: Confusing as both
MapAddressandAddressmodel are quite similar.
-
-
Alternative 2: Use
Address-
Pros: Reduces the amount of code/class in the project
-
Cons: Confusing as different requirements for a single model. Lacks proper organisation.
-
The EstimateRoute mechanism is facilitated by the EstimateRouteCommand, which resides inside Logic. It supports the viewing of estimated route
in the MapPanel by updating the state of the MapPanel. This is done by re-centering the MapPanel to the new route.
The following figure shows the marker that is used to identify the start and end location in the MapPanel:
EstimateRouteCommand inherits from the Command class and is not part of the Undoable Command.
Similar to the other commands like FindCommand it will not be identified by the undo/redo feature.
This command is similar to the ShowLocation feature which does not use the Person Address model to search for a specific location, it uses the MapAddress model. This is due to the difference in address specification details as the Address model is too specific for the command to work.
Even if it works the results may not be accurate.
An example would be the details of unit number (e.g #11-111) which will result in an invalid command or inaccurate results.
This command uses the GMAPSFX API and Google Maps Web Services API library which can be found here and here respectively.
-
GMAPSFXis used to create theMapPanelwhich allows the command to re-center the map view, create the route, mark the start, end location and route which is then shown to the user. -
Google Maps Web Services APIis used to create theGeocodingclass, which is used to convertMapAddressinto latitude and longitude form (LatLng). TheLatLngform is then used by the command to find the exact location in theMapPanel. -
Google Maps Web Services APIis also used to create theDistanceEstimateclass, which allows the calculation of estimated time and distance of travel between two location by driving.DistanceEstimateclass is also used to check if two locations can be reached by driving.
The snippet below shows the state of MapPanel before input of estimateRoute command:
After the input of "estimateRoute sma/Punggol ema/NUS" the MapPanel will be updated to the diagram below:
Any subsequent estimateRoute command will remove any existing marker or route before updating the MapPanel with the new route.
|
ℹ️
|
Whenever an invalid estimateRoute command is done, any existing location marker or route will be removed from the map.
|
The following sequence diagram shows how the command works:
-
Alternative 1 (current choice): Extend
Command-
Pros: Allows new developers to understand easily as the command is at the same abstraction level as other commands.
-
Cons: Does not have the undo/redo feature as it is not part of
UndoableCommand.
-
-
Alternative 2: Extend
UndoableCommand-
Pros: Allows for command to have the undo/redo function.
-
Cons: Requires more work that may not fit in with our timeline.
-
-
Alternative 1 (current choice): Use
MapAddress-
Pros: Allows the clear distinction of requirements between
MapAddressandAddressto avoid confusion. -
Cons: Confusing as the two models are similar.
-
-
Alternative 2: Use
Address-
Pros: Reduces the amount of code/class in the project.
-
Cons: Confusing as different requirements for a single model. Lacks proper organisation.
-
-
Alternative 1 (current choice): Use
Locationname-
Pros: Allows the function to be used independently
-
Cons: Requires keying in of location instead of just an index.
-
-
Alternative 2: Use
Appointmentindex-
Pros: Reduces the amount of typing.
-
Cons: Restricts the use of function as without an appointment index you will not be able to use it.
-
This section shows you how to document your project effectively.
You can use AsciiDoc, a lightweight markup language, for writing documentation.
|
ℹ️
|
AsciiDoc(markup language) is chosen over markdown language format because it provides more flexibility with regards to formatting. |
Please refer to UsingGradle.adoc for instructions on 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.
Please refer to UsingTravis.adoc for instructions on how to deploy GitHub pages using Travis.
You can use Google Chrome to convert documents to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.
To convert the project documentation files to PDF format:
-
Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in
docs/directory to HTML format. -
Go to your generated HTML files in the
build/docsfolder, right click on them and selectOpen with→Google Chrome. -
Click on the
Printoption in Chrome’s menu. -
Set the destination to
Save as PDF, proceed to clickSaveto save a copy of the file in PDF format. For the best result, use the settings indicated in the screenshot below.
Menu below will appear after step 3
Testing is very important as it allows us to find application defects that were made during development, and it should be done constantly. It can be expensive if software testing is done only in the later stages of development, as a bug may affect different components of the project.
There are two types of tests that we can run during the development of the project:
-
GUI Tests - These are tests involving the GUI. They include:
-
System Tests that test the entire App by simulating user actions on the GUI. These are in the
systemtestspackage. -
Unit tests that test the individual components of the software. These are in
seedu.address.uipackage.
-
-
Non-GUI Tests - These are tests not involving the GUI. They include:
-
Unit tests that target the lowest level methods/classes.
e.g.seedu.address.commons.StringUtilTest -
Integration tests that check the integration of multiple code units (those code units are assumed to be working).
e.g.seedu.address.storage.StorageManagerTest -
Hybrids of unit and integration tests that check multiple code units as well as how they are connected together.
e.g.seedu.address.logic.LogicManagerTest
-
There are three ways to run tests.
Method 1: Using Gradle (headless)
-
Open a console and run the command
gradlew clean headless allTests(Mac/Linux:./gradlew clean headless allTests).
|
ℹ️
|
GUI tests can be run in headless mode due to the TestFX library. GUI tests do not show up on the screen in headless mode, which allows you to work on other matters while tests are running. |
|
ℹ️
|
Using Gradle (headless) is the most reliable way to run tests. Other testing methods may fail some GUI tests due to platform/resolution-specific idiosyncrasies. |
|
ℹ️
|
See UsingGradle.adoc for more info on how to run tests using Gradle. |
Method 2: Using Gradle
-
Open a console and run the command
gradlew clean allTests(Mac/Linux:./gradlew clean allTests).
Method 3: Using IntelliJ JUnit test runner
-
To run all tests, right-click on the
src/test/javafolder and chooseRun 'All Tests'. -
To run a subset of tests, right-click on a test package or a test class, and choose
Run 'Tests in '<test package or test class>''.
Problem: HelpWindowTest fails with a NullPointerException.
-
Reason: One of its dependencies,
UserGuide.htmlinsrc/main/resources/docsis missing. -
Solution: Execute Gradle task
processResources.
Please use Gradle for build automation.
Refer to UsingGradle.adoc for more details.
Please use Travis CI and AppVeyor to perform Continuous Integration on our projects.
Refer to UsingTravis.adoc and UsingAppVeyor.adoc for more details.
Please use Coveralls to track the code coverage of our projects.
Refer to UsingCoveralls.adoc for more details.
Please use Netlify to see a preview of how the HTML version of changed asciidoc files will look
like when a pull request is merged.
Please refer to UsingNetlify.adoc for more details.
To do the following steps to create a new release, you can:
-
Update the version number in link:
MainApp.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.
The target user profile:
-
has a need to manage a significant number of contacts
-
prefers desktop apps over other types
-
can type fast
-
prefers typing over mouse input
-
is reasonably comfortable using CLI Apps
-
needs to manage several people’s (celebrities') schedule
-
has a need to link contacts to appointments
Value proposition: manage contacts faster than a typical mouse/GUI driven app
MAJOR
Adding, deleting, editing appointments: Able to create, delete and edit appointments within the application. (By Muruges)
Listing appointments : Able to list appointments within a date range. (By Muruges)
Showing location on map: Able to show the location of an appointment using address in maps. (By Damien)
Showing route on map: Able to show the rough route used to calculate the distance and time of travel. With the estimated distance and time of travel being shown in result display panel. (By Damien)
Storing appointments: Able to parse appointments information from storage, and save new and edited appointment information into storage. (By Tzer Bin)
Reminding of appointments: Able to draft an email template to remind persons associated with the appointment and fill in the addressees' emails automatically. (By Jinyi)
MINOR
Adding attendees and POCs: Able to add a list of celebrities and a list of Points of Contact to each appointment. (By Muruges)
Switching between different calendar views: Able to switch to view appointments on the calendar by day, week, month and year with CLI. (Jinyi)
Customizing visual themes: Able to customize visual themes of the application. (By Tzer Bin)
Removing a tag: Able to remove a tag from each person with it in the application. (By Jinyi)
View appointment: Able to view a specific appointment in result display from list of appointments based on index. Location will also be shown in map. (By Damien)
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 when I forget how to use the App |
|
user |
add a new person |
access contacting information of the person from the App |
|
user |
delete a person |
remove contacts that I no longer need |
|
user |
find a person by name |
locate details of persons without having to go through the entire list |
|
user |
undo a previous command |
remove the change made by mistake |
|
user |
redo a previous command |
restore the change removed by mistake |
|
user |
have a calendar inside the address book |
know the date and day |
|
user |
display appointments on calendar by day, week, and month |
check appointments in different time frames |
|
user |
add an appointment to a calendar |
schedule different appointments without time clashes |
|
user |
delete an appointment from a calendar |
remove appointments that are cancelled |
|
user |
edit an appointment in a calendar |
change the information about the appointment when there is a change in plan or arrangement |
|
user |
save appointments |
get appointments loaded in the calendar automatically when I re-launch the App |
|
user |
get alerted for upcoming appointments |
set my priorities straight |
|
user |
see the location of an appointment in a map |
plan for travel |
|
user |
see various landmarks around a specific location in a road map |
understand better the roads around the location |
|
user |
see various landmarks around a specific location in a satellite map |
see in real time the actual layout of the surrounding |
|
user |
zoom in and out of the map of a location in a map |
view the location in different levels of details |
|
celebrity manager who chauffeurs celebrities |
see the best route of travel by driving between two locations in a map |
plan for the shortest travel |
|
celebrity manager who chauffeurs celebrities |
know if two locations can be reached by driving |
foresee any problems and plan ahead |
|
celebrity manager who chauffeurs celebrities |
know the estimated distance between two locations by driving |
cater enough time for travelling to avoid being late |
|
celebrity manager who chauffeurs celebrities |
know the estimated time of travel between two locations by driving |
reduce the time of travel to reach an appointment location |
|
celebrity manager |
have multiple calendars to display appointments for different celebrities |
manage multiple celebrities' appointments |
|
user |
hide private contact details by default |
minimize chance of someone else seeing them by accident |
|
user |
change the size of different windows of the App |
customise the window sizes |
|
user who contacts different parties involved in an appointment |
draft the message about appointment details automatically |
save time to draft the email |
|
user who frequently contacts certain people |
sort the contacts by contacting frequency |
find those people I frequently contact easily |
|
user |
change the colour scheme of the App |
choose my preferred colour scheme |
|
celebrity manager |
group celebrities by different talents |
find celebrities by talent easily |
|
celebrity manager managing celebrities of the same group |
add an appointment to the calendars of these celebrities at the same time |
save time |
|
user with many international contacts |
group contacts by country code |
see my contacts from different countries |
|
user of previous versions of the App |
transfer my contacts to the new version |
save the trouble of adding the contacts again |
|
user with poor eyesight |
the address book to read out the contacts to me |
use it more efficiently |
|
user |
output the contacts to a separate list |
have a backup copy of the contacts |
(For all use cases below, the System is the CelebManager and the Actor is the user, unless specified otherwise)
MSS
-
User requests to list persons.
-
CelebManager shows a list of persons.
-
User requests to delete a specific person in the list.
-
CelebManager deletes the person.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. CelebManager shows an error message.
Use case resumes at step 2.
-
-
3b. The person at the given index is a celebrity.
-
3b1. CelebManager deletes the celebrity’s calendar.
Use case continues to step 4.
-
MSS
-
User requests to undo.
-
CelebManager undoes the latest executed command that mutates the data.
Use case ends.
Extensions
-
2a. There is no executed command that mutates the data.
-
2a1. CelebManager shows an error message.
Use case ends.
-
MSS
-
User requests to redo.
-
CelebManager redoes the latest executed undo command.
Use case ends.
Extensions
-
2a. There is no executed undo command.
-
2a1. CelebManager shows an error message.
Use case ends.
-
MSS
-
User requests to remove a tag.
-
CelebManager removes the tag from any person having it.
Use case ends.
Extensions
-
1a. The tag does not exist.
-
1a1. CelebManager shows an error message.
Use case ends.
-
-
1b. The tag is celebrity.
-
1b1. CelebManager shows an error message.
Use case ends.
-
MSS
-
User requests to add an appointment.
-
CelebManager adds the appointment to the currently displayed calendar.
Use case ends.
Extensions
-
1a. Appointment to add has incorrect details or format.
-
1a1. CelebManager shows an error message.
Use case ends.
-
-
2a. Appointment to add clashes with existing appointment.
-
2a1. CelebManager shows an error message.
Use case ends.
-
MSS
-
User requests to view appointment.
-
CelebManager shows the appointment’s details.
Use case ends.
Extensions
-
1a. Appointment to view does not exist.
-
1a1. CelebManager shows an error message.
Use case ends.
-
MSS
-
User requests to list appointments from a start date to an end date.
-
CelebManager shows a list of appointments within the date range (inclusive).
Use case ends.
Extensions
-
1a. The dates are invalid or in wrong format.
-
1a1. CelebManager outputs an error message.
Use case ends.
-
-
1b. The entered start date is after end date.
-
1b1. CelebManager outputs an error message.
Use case ends.
-
-
2a. There is no appointment to show in the date range.
-
2a1. CelebManager outputs a message that says no appointment in the specified date range.
Use case ends.
-
MSS
-
User requests to list appointments from a start date to an end date.
-
CelebManager shows a list of appointments within the date range (inclusive).
-
User requests to delete a specific appointment in the list.
-
CelebManager deletes the appointment.
Use case ends.
Extensions
-
1a. The dates are invalid or in wrong format.
-
1a1. CelebManager outputs an error message.
Use case ends.
-
-
1b. The entered start date is after end date.
-
1b1. CelebManager outputs an error message.
Use case ends.
-
-
2a. There is no appointment to show in the date range.
-
2a1. CelebManager outputs a message that says no appointment in the specified date range.
Use case ends.
-
-
3a. The given index is invalid.
-
3a1. CelebManager shows an error message.
Use case resumes at step 2.
-
MSS
-
User requests to list appointments from a start date to an end date.
-
CelebManager shows a list of appointments within the date range (inclusive).
-
User requests to edit a specified appointment.
-
CelebManager changes appointment details and displays new appointment details to user.
Use case ends.
Extensions
-
1a. The dates are invalid or in wrong format.
-
1a1. CelebManager outputs an error message.
Use case ends.
-
-
2a. There is no appointment to show in the date range.
-
2a1. CelebManager outputs a message that says no appointment in the specified date range.
Use case ends.
-
-
3a. The given index is invalid.
-
3a1. CelebManager shows an error message.
Use case resumes at step 2.
-
-
3b. Information entered for edit is invalid.
-
3b1. CelebManager shows an error message.
Use case resumes at step 2.
-
MSS
-
User inputs location name or address.
-
CelebManager converts information into LatLong form.
-
Celeb Manager uses the LatLong info to update create a new location marker.
-
CelebManager updates the map with the location marker and re-centre its panel view.
Use case ends.
Extensions
-
1a. User provides invalid input.
-
1a1. CelebManager requests User to provide valid input.
-
1a2. User enters new input.
Steps 1a1-1a2 are repeated until input is valid.
Use case resumes from step 2.
-
-
4a. When there is an existing marker in the map.
-
4a1. CelebManager removes it.
Use case ends.
-
MSS
-
User inputs start and end location name or address.
-
CelebManager converts information into LatLong form.
-
Celeb Manager uses the LatLong info to generate the route.
-
CelebManager updates the map with the route.
Use case ends.
Extensions
-
1a. User provides invalid input.
-
1a1. CelebManager requests User to provide valid input.
-
1a2. User enters new input.
Steps 1a1-1a2 are repeated until input is valid.
Use case resumes from step 2.
-
-
3a. When both location cannot be reached by driving
-
3a1. CelebManager shows error message.
Use case ends.
-
-
4a. When there is an existing route in the map
-
4a1. CelebManager removes it.
Use case ends.
-
MSS
-
User inputs start and end location name or address.
-
CelebManager converts information into LatLong form.
-
Celeb Manager uses the LatLong info to generate the distance and time required to travel.
-
CelebManager shows the information.
Use case ends.
Extensions
-
1a. User provides invalid input.
-
1a1. CelebManager requests User to provide valid input.
-
1a2. User enters new input.
Steps 1a1-1a2 are repeated until input is valid.
Use case resumes from step 2.
-
-
3a. When both location cannot be reached by driving
-
3a1. CelebManager shows error message.
-
-
Should work on any mainstream OS as long as it has Java
1.8.0_60or higher installed. -
Should be able to hold up to 1000 persons 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.
-
Should be usable by people with no knowledge about command line input.
-
Should respond to any user command within 10 seconds.
-
Should be backward compatible with data produced by earlier versions of the CelebManager.
-
Should come with automated unit tests and open source code.
-
Should favor DOS style commands over Unix-style commands.
|
💡
|
CelebManager currently supports displaying by day, week and month. |
- Appointment
-
A meeting between contacts at a specific time, date and location
An appointment has a specific name - Attendees
-
A list of contacts who are attending an appointment
- Points of Contacts
-
A list of contacts involved in an appointment but not attending it
- Blacklist
-
A list of contacts which should be marked for being untrustworthy or unacceptable
- User Interface
-
The Design and looks of the software
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. |
This section includes instructions to test the launch and re-launch of the App.
For each subsequent launch, window size and location should be the same as the settings before closing the App for previous launch.
-
Initial launch
-
Download the jar file
-
Copy into an empty folder
-
Double-click the jar file
Expected: CelebManager shows the GUI with a set of sample contacts.ℹ️The window size may not be optimum. -
Resize the window to an optimum size
-
Move the window to a different location
-
Close the window
-
-
Subsequent launch
-
Re-launch the App by double-clicking the jar file
Expected: The most recent window size and location are retained.
-
This section includes instructions to test the add command of the application.
|
ℹ️
|
Prerequisites: No other person in the addressbook with the details of the two persons below. |
-
Type
add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoneyin command box and press enter.
Expected: A contact by the name John Doe and with the above personal particulars will be added.
Details of the added contact shown in the status message. Timestamp in the status bar is updated. -
Type
add n/Jane Doe p/98765431 e/janed@example.com a/311, Clementi Ave 1, #02-25 t/celebrityin command box and press enter.
Expected: A celebrity by the name Jane Doe is added with the above personal particulars. A new calendar with the initialJ
will be created in the calendar panel. Details of the added celebrity shown in the status message. Timestamp in the status bar is updated. -
Type in
add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoneyin command box and press enter.
Expected: No person is added. Error details is shown in the status message. Status bar remains the same.
This section includes instructions to test the delete command of the CelebManager.
|
ℹ️
|
Prerequisites: List all persons using the list command. Multiple persons in the list. At least one celebrity in the list.
|
-
Type
delete nin command box (n refers to the one-based index of the celebrity in the person list panel) and press enter.
Expected: Chosen celebrity is deleted from the list. Calendar corresponding to the deleted celebrity is removed from the calendar panel. Details of the deleted celebrity is shown in the status message. Timestamp in the status bar is updated. -
Type
delete 1in command box and press enter.
Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. -
Type
delete 0in command box and press enter.
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. -
Type other incorrect delete command such as
delete,delete x(wherexis larger than the list size) in command box and press enter.
Expected: Similar to previous.
This section includes instructions to test the removeTag command of the CelebManager.
|
ℹ️
|
Prerequisites: At least one person in the full contact list (not necessarily the last shown list) has friends tag.
|
-
Type
removeTag friendsin command box and press enter.
Expected:friendstag is removed from any person who has it. Number of persons affected shown in the status message. -
Type
removeTag !@#in command box and press enter.
Expected: No tag is removed. Error details shown in the status message. -
Type
removeTag celebrityin command box and press enter.
Expected:celebritytag is not removed. Error details shown in the status message. -
Type other incorrect
removeTagcommands such asremoveTag,removeTag x(wherexis a tag that nobody has in the full contact list or an invalid tag name) in command box and press enter.
Expected: No tag is removed. Error details shown in the status message.
This section includes instructions to test the addAppointment command of the CelebManager.
|
ℹ️
|
Prerequisites: List all persons using the list command. At least one celebrity and one normal person in the person list panel. No appointments currently present.
|
-
Type
addAppointment n/Oscarand press enter. Then typelaand press enter.
Expected: An appointment with the name Oscar is added. The start date/time corresponds to the current time. The end date/time is 15 minutes from now. -
Type
addAppointment n/Grammy sd/10-04-2018 st/15:00and press enter. Then typelaand press enter.
Expected: An appointment with the name Grammy is added. The start date/time correspond to the input values. End date/time is 15 minutes from that. -
Type
addAppointment n/Dentist appointment sd/11-04-2018 st/16:00 ed/12-04-2018 et/09:00 c/n p/k(where n corresponds to the index of a celebrity and k corresponds to the index of a normal person) and press enter. Then typelaand press enter.
Expected: An appointment with the above details and the selected celebrity and point of contact will appear in the appointment list. -
Type the above command again and press enter.
Expected: No appointment is added. Error details shown in the status message. -
Type
addAppointment n/Lunch meeting c/x(where x refers to an invalid celebrity index) and press enter.
Expected: No appointment is added. Error details shown in the status message. -
Type
addAppointment n/Dinner l/???and press enter.
Expected: No appointment is added. Error details shown in the status message.
This section includes instructions to test the listAppointment command of the application.
|
ℹ️
|
Prerequisites: At least one appointment stored in CelebManager. |
-
Type
listAppointmentin command box and press enter.
Expected: All appointments are listed. -
Type
listAppointment 01-04 01-05in command box and press enter.
Expected: Appointments with scheduled timings which overlap with the period of 1st April to 1st May of the current year are listed. If there is no such appointment, error details shown in the status message. -
Type
listAppointment 0in command box and press enter.
Expected: No appointments listed. Error details shown in the status message. -
Type
listAppointment ain command box and press enter.
Expected: No appointments listed. Error details shown in the status message. -
Type
listAppointment 03-05 02-01in command box and press enter.
Expected: No appointments listed. Error details shown in the status message. -
Type other incorrect
listAppointmentcommands such aslistAppointment,listAppointment x(wherexis not a date range of formatDD-MM-YYYY DD-MM-YYYY,DD-MM-YYYY DD-MM,DD-MM DD-MM-YYYY, orDD-MM DD-MM) in command box and press enter.
Expected: No appointments listed. Error details shown in the status message.
This section includes instructions to test the editAppointment command of the CelebManager.
|
ℹ️
|
Prerequisites: List all appointments using the listAppointment command. At least one appointment stored in the list.
|
-
while listing appointments:
-
Type
editAppointment 0in command box and press enter.
Expected: No appointment is edited. Error details shown in the status message. Appointment list remains the same. -
Type
editAppointment 1 n/New Appointmentin command box and press enter.
Expected: First appointment is renamed asNew Appointmentas long as it did not already have that name previously, and will go back to the calendar page. If there is an error because the name is the same, no appointment is edited, error details shown in the status message and appointment list remains the same. -
Type other incorrect
editAppointmentcommands such aseditAppointment,editAppointment x(wherexis larger than the appointment list size) in command box and press enter.
Expected: No appointment is edited. Error details shown in the status message. Appointment list remains the same.
-
-
While not listing appointments
-
Type correct deleteAppointment commands such as
editAppointment 1in command box and press enter.
Expected: No appointment is edited. Error details shown in the status message.
-
This section includes instructions to test the deleteAppointment command of the application.
|
ℹ️
|
Prerequisites: List appointments using the listAppointment command. At least one appointment in the list.
|
-
While listing appointments:
-
Type
deleteAppointment 0in command box and press enter.
Expected: No appointment is deleted. Error details shown in the status message. Appointment list remains the same. -
Type
deleteAppointment 1in command box and press enter.
Expected: First appointment is deleted from the list. If the deleted appointment is the only appointment in the list, CelebManager will switch back to the calendar of the current day. -
Type other incorrect
deleteAppointmentcommands such asdeleteAppointment,deleteAppointment x(wherexis larger than the appointment list size) in command box and press enter.
Expected: No appointment is deleted. Error details shown in the status message. Appointment list remains the same.
-
-
While not listing appointments
-
Type correct deleteAppointment commands such as
deleteAppointment 1in command box and press enter.
Expected: No appointment is deleted. Error details shown in the status message.
-
This section includes instructions to test the viewAppointment command of the application.
|
ℹ️
|
Prerequisites: List all appointments using the listAppointment command. Multiple appointments in the list.
|
-
Type
viewAppointment 1in command box and press enter.
Expected: First appointment is selected from the list. Details of the selected appointment shown in the result display. Location shown in map. Success message shown in result display. -
Type
viewAppointment 0in command box and press enter.
Expected: No appointment is selected. Error details shown in the result display. Status bar remains the same. Map removes any existing location marker or route. -
Type other incorrect command such as
viewAppointment,viewAppointment x(where x is larger than the list size or appointment chosen does not have location data) in command box and press enter.
Expected: Similar to previous.
This section includes instructions to test the showLocation command of the application.
-
Type
showLocation ma/NUSin command box and press enter.
Expected: Location shown in map. Map view will be centered to location. -
Type
showLocation ma/!!!!!!in command box and press enter.
Expected: Error details shown in the result display. Status bar remains the same. Map removes any existing location marker or route. -
Type other incorrect command such as
showLocation,showLocation Punggolin command box and press enter.
Expected: Similar to previous.
This section includes instructions to test the estimateRoute command of the application.
-
Type
estimateRoute sma/NUS ema/Punggolin command box and press enter.
Expected: Best route by driving will be added to map. Map view will be centered to location. Details of distance and time of travel shown in result display. Success message shown in result display. -
Type
estimateRoute sma/!!!!!! ema/Punggolin command box and press enter.
Expected: Location not found in google server. Error details shown in the result display. Status bar remains the same. -
Type
estimateRoute sma/NUS ema/!!!!!!in command box and press enter.
Expected: Error details shown in the result display. Status bar remains the same. Map removes any existing marker or route. -
Type other incorrect command such as
estimateRoute,estimateRoute Punggol NUS,estimateRoute sma/Punggol NUS,estimateRoute NUS ema/Punggolin command box and press enter.
Expected: Similar to previous.
This section includes instructions to test the viewCalendar command of the application.
|
ℹ️
|
Prerequisites: At least one celebrity in the last shown list and the celebrity’s calendar is not currently displayed. |
-
Type
viewCalendar c(wherecis the index of the celebrity) in command box and press enter.
Expected:CalendarPanelnow displays the celebrity’s calendar. If the celebrity is the only celebrity in the full contact list, there won’t be any change in terms of the outlook of calendar. -
Type
viewCalendar c(wherecis the index of the same celebrity in the previous command) again after previous execution in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type
viewCalendar p(wherepis the index of a non-celebrity person) in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type
viewCalendar 0in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type other incorrect
viewCalendarcommands such asviewCalendar,viewCalendar x(wherexis larger than the last shown contact list size or not a valid index number or the index of the celebrity whose calendar is currently displayed) in command box and press enter.
Expected: No change in calendar. Error details shown in the status message.
This section includes instructions to test the viewCombinedCalendar command of the application.
|
ℹ️
|
Prerequisites: Combined calendar is not currently displayed. |
-
Type
viewCombinedCalendarin command box and press enter.
Expected:CalendarPanelnow displays the combined calendar. If there is only one celebrity in the full contact list, there won’t be any change in terms of the outlook of calendar. -
Type
viewCombinedCalendaragain after previous execution in command box and press enter.
Expected: No change in calendar. Error details shown in the status message.
This section includes instructions to test the viewCalendarBy command of the application.
|
ℹ️
|
Prerequisites: Calendar is currently in day view.
|
-
Type
viewCalendarBy weekin command box and press enter.
Expected: Calendar shows the week which the original day is in. -
Type
viewCalendarBy monthin command box and press enter.
Expected: Calendar shows the month which the original day is in. -
Type
viewCalendarBy dayin command box and press enter.
Expected: Calendar shows the day which is the original day. -
Type
viewCalendarBy dayagain after executing the previous command in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type
viewCalendarBy MOnThin command box and press enter.
Expected: Calendar shows the week which the original day is in. -
Type
viewCalendarBy 1in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type other incorrect
viewCalendarBycommands such asviewCalendarBy,viewCalendarBy x(where x is notday,week,monthor mixed uppercase and lowercase of one of them) in command box and press enter.
Expected: No change in calendar. Error details shown in the status message.
This section includes instructions to test the viewDate command of the application.
|
ℹ️
|
Prerequisites: Calendar is currently in day view and displays the current day’s calendar. Current day is not 1st April of 2018.
|
-
Type
viewDate 01-04-2018in command box and press enter.
Expected:CalendarPaneldisplays the calendar for 1st April of 2018. -
Type
viewDate d(wheredis a date that is different from the current day’s date, in DD-MM format) in command box and press enter.
Expected:CalendarPaneldisplays the calendar for datedof current year. -
Type
viewDatein command box and press enter.
Expected:CalendarPaneldisplays the calendar for the current day. -
Type
viewDateagain after executing the previous command in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type
viewDate 01-04again after executing the previous command in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type
viewDate 0in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type
viewDate !in command box and press enter.
Expected: No change in calendar. Error details shown in the status message. -
Type other incorrect
viewDatecommands such asviewDate,viewDate x(wherexis not a date inDD-MM-YYYYorDD-MMformat) in command box and press enter
Expected: No change in calendar. Error details shown in the status message.
This section includes instructions to test the transferring of file of the application
|
ℹ️
|
Preprequisites: The file is a valid .xml file for storage calendar, and is named storagecalendar.xml.
|
-
Close the application.
-
Open the
data/directory in the folder containing the .jar file. Expected:storagecalendar.xmlconsisting the to-be-replaced data is in the folder. -
Replace the old
storagecalendar.xmlwith the the newstoragecalendar.xmlfile. -
Restart the application. Expected: The new appointments data from
storagecalendar.xmlis reflected in the calendar panel.





























