Skip to content

Latest commit

 

History

History
2147 lines (1613 loc) · 95.9 KB

File metadata and controls

2147 lines (1613 loc) · 95.9 KB

CelebManager - Developer Guide

By: CS2103JAN2018-W14-B4      Since: Jun 2016      Licence: MIT

1. Overview

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.

2. Setup

This section will discuss the setting up of project for development.

2.1. Prerequisites

There are two prerequisites before you can work on this software. They are (in the order you should obtain them):

  1. 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 version 1.8.0_60 installed. 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.
  2. 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 to File > Settings > Plugins to re-enable them.

2.2. Local Project Setup

To contribute to this project, you will need to work with a local copy of this project. To do so:

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

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

  3. Set up the correct JDK version for Gradle:

    1. Click Configure > Project Defaults > Project Structure.

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

  4. Click Import Project.

  5. Locate the build.gradle file, select it and click OK.

  6. Click Open as Project.

  7. Click OK to accept the default settings.

  8. Open a console and run the command gradlew processResources. (Mac/Linux: ./gradlew processResources). It should finish with the BUILD SUCCESSFUL message.
    This will generate all resources required by the application and tests.

2.3. Setup Verification

To ensure that you have setup the project correctly:

  1. Run the seedu.address.MainApp and try a few commands.

  2. Run the tests to ensure they all pass.

2.4. Setup Configurations

2.4.1. Coding Style Configurations

This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify:

  1. Go to File > Settings…​ (Windows/Linux), or IntelliJ IDEA > Preferences…​ (macOS).

  2. Select Editor > Code Style > Java.

  3. Click on the Imports tab to set the order. Take note of the following:

    • For Class count to use import with '*' and Names count to use static import with '*': Set to 999 to prevent IntelliJ from contracting the import statements.

    • For Import Layout: The order is import static all other imports, import java.*, import javax.*, import org.*, import com.*, import all other imports. Add a <blank line> between each import.

Alternatively, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.

2.4.2. Documentation Configurations

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.

2.4.3. Continuous Integration (CI) Configurations

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.

3. Design

Before starting to work on the project after successful configurations, you are encouraged to:

  1. Understand the overall design (Section 3.1, “Software Architecture”).

  2. Understand the product scope (Appendix A, Product Scope).

3.1. Software Architecture

The Architecture Diagram given below explains the high-level design of the project.

Architecture
Figure 1. Architecture diagram

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:

  • EventsCenter is used by components to communicate with other components using events.

  • LogsCenter is 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:

  • UI: The user interface of the application.

  • Logic: The command executor of the application.

  • Model: The container for in-memory data of the application.

  • Storage: The driver for reading and writing data of the application.

3.2. Common Classes

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.

SDforDeletePerson
Figure 2. Sequence diagram for delete 1 command (1)

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.

SDforDeletePersonEventHandling
Figure 3. Sequence diagram for delete 1 command (2)

3.3. Architecture Components

3.3.1. UI Component

The following diagram shows the class diagram of the UI component.

UiClassDiagram
Figure 4. 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 Logic component.

  • binds itself to some data in the Model so that the UI can auto-update when data in the Model change.

  • responds to events raised from various parts of the App and updates the UI accordingly.

3.3.2. Logic Component

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.

LogicClassDiagram
Figure 5. Class diagram of the Logic component
LogicCommandClassDiagram
Figure 6. Structure of commands in the Logic component

API: link: Logic.java

When the user types in a new command to be parsed:

  1. The Logic uses the AddressBookParser class to parse the user command.

  2. A Command object is then executed by the LogicManager.

  3. The command execution can affect the Model (e.g. adding a person) and/or raise events.

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

The following diagram shows the sequence diagram for interactions within the Logic component for the execute("delete 1") API call.

DeletePersonSdForLogic
Figure 7. Sequence diagram for the delete 1 command

3.3.3. Model Component

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.

ModelClassDiagram
Figure 8. Class diagram of the Model component

API: link: Model.java

The Model component:

  • stores a UserPref object that represents the user’s preferences.

  • stores the Address Book data.

  • stores a StorageCalendar object that contains all appointments.

  • stores a CalendarSource object that is used to display the calendar.

  • stores a list of appointments which will be displayed for listAppointment command.

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

3.3.4. Storage Component

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.

StorageClassDiagram
Figure 9. Class diagram of the Storage component

API: link: Storage.java

The Storage component:

  • saves UserPref objects 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.

4. Implementation

This section describes some noteworthy features that are implemented in CelebManager.

4.1. RemoveTag Feature

This feature allows the user to remove a specified tag from all the contacts.

4.1.1. Current Implementation

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:

RemoveTagCommand logic seq diagram
Figure 10. Sequence diagram of removeTag command

4.1.2. Design Considerations

Aspect: Command result for removal of celebrity tag
  • Alternative 1 (current choice): Output an error message saying that the celebrity tag cannot be removed

    • Pros: Prevents removeTag from affecting the calendar as celebrities will not get affected by this operation.

    • Cons: Results in no available method to mass remove celebrity tag.

  • Alternative 2: Remove celebrity tag and clear all calendars

    • Pros: Provides an easy way to mass remove celebrity tag and clears all celebrities from the address book.

    • Cons: As changes made to calendars and appointments are not undoable, removing celebrity tag by mistake can result in loss of all celebrities' calendar and appointment information.

4.2. Undo/Redo Command Feature

4.2.1. Current Implementation

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:

LogicCommandClassDiagram
Figure 11. Inheritance diagram for undoable commands

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.

UndoRedoStartingStackDiagram
Figure 12. Execution of delete 5 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.

UndoRedoNewCommand1StackDiagram
Figure 13. Execution of add n/David …​ 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.

UndoRedoExecuteUndoStackDiagram
Figure 14. Execution of 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.

UndoRedoSequenceDiagram
Figure 15. Sequence diagram of undo command

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.

4.2.2. Design Considerations

Aspect: Implementation of UndoableCommand
  • 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 Command do not have to know that executeUndoableCommand() 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 UndoableCommand must remember to call super.execute(), or lose the ability to undo/redo.

Aspect: Execution of undo and redo commands
  • 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.

Aspect: Type of commands that can be undone/redone
  • 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.

Aspect: Type of data structure to support the undo/redo commands
  • 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 HistoryManager and UndoRedoStack.

  • Alternative 2: Use HistoryManager for 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 HistoryManager now needs to do two * different things.

4.3. AddAppointment Feature

4.3.1. Current Implementation

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 CelebCalendar class is used, which extends the default Calendar class from CalendarFX used to describe a calendar.

  • For the appointment, the Appointment class is used, which is extended from Entry, the default class used to represent an entry in a Calendar in CalendarFX.

  • All CelebCalendar instances reside in an instance of CalendarSource, the class used to store a group of calendars in CalendarFX.

  • This instance of CalendarSource is atttached to our CalendarView which 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:

AddAppointmentSequenceDiagram
Figure 16. Sequence diagram of addAppointment command

The figure below (Figure 20) shows the state of the application before input of the AddAppointmentCommand:

BeforeAddAppointment
Figure 17. State of application without any appointments

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):

AfterAddAppointment
Figure 18. State of application with newly added appointment

4.3.2. Design Considerations

Aspect: Ability to undo addAppointment command
  • 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 editAppointment command instead of undo and re-add the new appointment with the correct fields.

    • If user instead just want to cancel the appointment, can use deleteAppointment command

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

4.4. DeleteAppointment Feature

This feature allows the user to delete appointments.

4.4.1. Current Implementation

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.

DeleteAppointmentCommand logic seq diagram
Figure 19. Sequence diagram of deleteAppointment command

4.4.2. Design Considerations

Aspect: Status of CalendarPanel after deletion of the only appointment
  • Alternative 1 (current choice): Switch back to combined calendar view

    • Pros: Keeps consistent with listAppointment as 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 listAppointment command’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 listAppointment while making it easy for users to check if the appointment gets deleted visually on calendar.

    • Cons: Takes long time to run.

Aspect: Ability to undo deleteAppointment command
  • 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.

4.5. ViewAppointment Feature

4.5.1. Current Implementation

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.

ViewAppointmentSequenceDiagram
Figure 20. Sequence Diagram of viewAppointment Command

4.5.2. Design Considerations

Aspect: Implementation of viewAppointment
  • 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.

Aspect: Inclusion of showing location on map
  • Alternative 1 (current choice): Show location of appointment on map

    • Pros: Reduces the hassle of keying an extra command to show Appointment location 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.

4.6. Appointment Storage Feature

4.6.1. Current Implementation

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.

ReadStorageCalendarSequenceDiagram
Figure 21. Sequence Diagram for reading StorageCalendar

In the XmlStorageCalendarStorage class, it allows developers to use methods:

  • readStorageCalendar, to retrieve a StorageCalendar

    • This is done by checking if the file exist, and load the list from XmlSerializableStorageCalendar.

  • saveStorageCalendar, to write information into filePath specified in userPrefs

    • 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:

  • title of appointment

  • startTime of appointment indicating its starting time

  • startDate of appointment indicating its starting date

  • endTime of appointment indicating its ending time

  • endDate of appointment indicating its ending date

  • location of appointment that is going to happen

  • celebrityIds of celebrities that are attending the appointment

  • pointOfContactIds of non-celebrities that are attending the appointment

4.6.2. Design Considerations

Aspect: Implementing of StorageCalendarStorage
  • Alternative 1 (current choice): Adapting existing AddressBookStorage

    • Pros: Allows similar structure that can be maintained easily in Storage component

    • 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

Aspect: Usage of data structures for Appointment
  • 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 Set such as TreeSet

    • Pros: Lowers impact in speed when there are many appointments

    • Cons: Complicates implementation when speed is not an issue

4.7. ShowLocation Feature

4.7.1. Current Implementation

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:

LocationMarker
Figure 22. Location marker

The following diagram shows the inheritance diagram for ShowLocationCommand:

ShowLocationLogicCommandClassDiagram
Figure 23. Inheritance diagram for Command

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 API is used to create the MapPanel class which allows the command to re-center and mark the new location which is then shown to the user.

  • Google Maps Web Services API is used to create the Geocoding class, which is used to convert MapAddress into latitude and longitude form (LatLng). The LatLng form is then used by the command to find the exact location in the MapPanel.

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:

BeforeInputMap
Figure 24. Default State of MapPanel

After the input of "showLocation ma/Punggol" the MapPanel will be updated to the diagram below:

AfterShowLocationInput
Figure 25. State of MapPanel after CommandInput
ℹ️
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:

ShowLocationSequenceDiagram
Figure 26. Sequence Diagram of showLocation Command

4.7.2. Design Considerations

Aspect: Implementation of showLocationCommand
  • 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

Aspect: Use of address model
  • Alternative 1 (current choice): Use MapAddress

    • Pros: Allows the clear distinction of requirements between MapAddress and Address to avoid confusion

    • Cons: Confusing as both MapAddress and Address model 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.

4.8. EstimateRoute Feature

4.8.1. Current Implementation

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:

Start Location Marker
Figure 27. Start Location marker
End Location Marker
Figure 28. End Location marker

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.

  • GMAPSFX is used to create the MapPanel which 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 API is used to create the Geocoding class, which is used to convert MapAddress into latitude and longitude form (LatLng). The LatLng form is then used by the command to find the exact location in the MapPanel.

  • Google Maps Web Services API is also used to create the DistanceEstimate class, which allows the calculation of estimated time and distance of travel between two location by driving. DistanceEstimate class 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:

BeforeInputMap
Figure 29. Default State of MapPanel

After the input of "estimateRoute sma/Punggol ema/NUS" the MapPanel will be updated to the diagram below:

AfterEstimateRouteInput
Figure 30. State of MapPanel after CommandInput

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:

EstimateRouteSequenceDiagram
Figure 31. Sequence Diagram of estimateRoute Command

4.8.2. Design Considerations

Aspect: Implementation of estimateCommand
  • 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.

Aspect: Use of address model
  • Alternative 1 (current choice): Use MapAddress

    • Pros: Allows the clear distinction of requirements between MapAddress and Address to 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.

Aspect: Input using appointment index
  • Alternative 1 (current choice): Use Location name

    • Pros: Allows the function to be used independently

    • Cons: Requires keying in of location instead of just an index.

  • Alternative 2: Use Appointment index

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

5. Documentation

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.

5.1. Editing of Documentation

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.

5.2. Publishing of Documentation

Please refer to UsingTravis.adoc for instructions on how to deploy GitHub pages using Travis.

5.3. Converting of Documentation to PDF format

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:

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

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

  3. Click on the Print option in Chrome’s menu.

  4. Set the destination to Save as PDF, proceed to click Save to 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

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

6. Testing

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.

6.1. Types of Tests

There are two types of tests that we can run during the development of the project:

  1. GUI Tests - These are tests involving the GUI. They include:

    1. System Tests that test the entire App by simulating user actions on the GUI. These are in the systemtests package.

    2. Unit tests that test the individual components of the software. These are in seedu.address.ui package.

  2. Non-GUI Tests - These are tests not involving the GUI. They include:

    1. Unit tests that target the lowest level methods/classes.
      e.g. seedu.address.commons.StringUtilTest

    2. Integration tests that check the integration of multiple code units (those code units are assumed to be working).
      e.g. seedu.address.storage.StorageManagerTest

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

6.2. Running Tests

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/java folder and choose Run '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>''.

6.3. Test Troubleshooting

Problem: HelpWindowTest fails with a NullPointerException.

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

  • Solution: Execute Gradle task processResources.

6.4. Build Automation

Please use Gradle for build automation.
Refer to UsingGradle.adoc for more details.

6.5. Continuous Integration

Please use Travis CI and AppVeyor to perform Continuous Integration on our projects.
Refer to UsingTravis.adoc and UsingAppVeyor.adoc for more details.

6.6. Coverage Reporting

Please use Coveralls to track the code coverage of our projects.
Refer to UsingCoveralls.adoc for more details.

6.7. Documentation Previews

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.

6.8. Application Release

To do the following steps to create a new release, you can:

  1. Update the version number in link: MainApp.java.

  2. Generate a JAR file using Gradle.

  3. Tag the repo with the version number, e.g. v0.1.

  4. Create a new release using GitHub and upload the JAR file you created.

6.9. Dependencies Management

Management of dependencies on third-party libraries is done using Gradle. There is no need to include those libraries in the repo or download them manually.

Appendix A: Product Scope

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

A.1. Feature Contribution

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)

Appendix B: User Stories

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

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

* * *

new user

see usage instructions

refer to instructions 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

Appendix C: Use Cases

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

C.1. Use case: Delete person

MSS

  1. User requests to list persons.

  2. CelebManager shows a list of persons.

  3. User requests to delete a specific person in the list.

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

C.2. Use case: Undo

MSS

  1. User requests to undo.

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

C.3. Use case: Redo

MSS

  1. User requests to redo.

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

C.4. Use case: Remove tag

MSS

  1. User requests to remove a tag.

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

C.5. Use case: Add appointment

MSS

  1. User requests to add an appointment.

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

C.6. Use case: View appointment

MSS

  1. User requests to view appointment.

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

C.7. Use case: List appointments

MSS

  1. User requests to list appointments from a start date to an end date.

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

C.8. Use case: Delete appointment

MSS

  1. User requests to list appointments from a start date to an end date.

  2. CelebManager shows a list of appointments within the date range (inclusive).

  3. User requests to delete a specific appointment in the list.

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

C.9. Use case: Edit appointment

MSS

  1. User requests to list appointments from a start date to an end date.

  2. CelebManager shows a list of appointments within the date range (inclusive).

  3. User requests to edit a specified appointment.

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

C.10. Use case: Show location in map

MSS

  1. User inputs location name or address.

  2. CelebManager converts information into LatLong form.

  3. Celeb Manager uses the LatLong info to update create a new location marker.

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

C.11. Use case: Show estimated route by driving in map

MSS

  1. User inputs start and end location name or address.

  2. CelebManager converts information into LatLong form.

  3. Celeb Manager uses the LatLong info to generate the route.

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

C.12. Use case: Show estimated distance and time of travel by driving

MSS

  1. User inputs start and end location name or address.

  2. CelebManager converts information into LatLong form.

  3. Celeb Manager uses the LatLong info to generate the distance and time required to travel.

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

Appendix D: Non Functional Requirements

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

  2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.

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

  4. Should be usable by people with no knowledge about command line input.

  5. Should respond to any user command within 10 seconds.

  6. Should be backward compatible with data produced by earlier versions of the CelebManager.

  7. Should come with automated unit tests and open source code.

  8. Should favor DOS style commands over Unix-style commands.

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Private contact detail

A contact detail that is not meant to be shared with others

StorageCalendar

A calendar that contains all appointments a celebrity managed by the user has

Calendar view

The way in which appointments are displayed in calendar

💡
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

Appendix F: Instructions for Manual Testing

Given below are instructions to test the App manually.

ℹ️
These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

F.1. Launch

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.

  1. Initial launch

    1. Download the jar file

    2. Copy into an empty folder

    3. Double-click the jar file
      Expected: CelebManager shows the GUI with a set of sample contacts.

      ℹ️
      The window size may not be optimum.
    4. Resize the window to an optimum size

    5. Move the window to a different location

    6. Close the window

  2. Subsequent launch

    1. Re-launch the App by double-clicking the jar file
      Expected: The most recent window size and location are retained.

F.2. Adding a person/celebrity

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.
  1. Type add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoney in 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.

  2. Type add n/Jane Doe p/98765431 e/janed@example.com a/311, Clementi Ave 1, #02-25 t/celebrity in 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 initial J
    will be created in the calendar panel. Details of the added celebrity shown in the status message. Timestamp in the status bar is updated.

  3. Type in add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoney in command box and press enter.
    Expected: No person is added. Error details is shown in the status message. Status bar remains the same.

F.3. Deleting a person/celebrity

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.
  1. Type delete n in 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.

  2. Type delete 1 in 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.

  3. Type delete 0 in command box and press enter.
    Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.

  4. Type other incorrect delete command such as delete, delete x (where x is larger than the list size) in command box and press enter.
    Expected: Similar to previous.

F.4. Removing a tag

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.
  1. Type removeTag friends in command box and press enter.
    Expected: friends tag is removed from any person who has it. Number of persons affected shown in the status message.

  2. Type removeTag !@# in command box and press enter.
    Expected: No tag is removed. Error details shown in the status message.

  3. Type removeTag celebrity in command box and press enter.
    Expected: celebrity tag is not removed. Error details shown in the status message.

  4. Type other incorrect removeTag commands such as removeTag, removeTag x (where x is 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.

F.5. Adding an appointment

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.
  1. Type addAppointment n/Oscar and press enter. Then type la and 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.

  2. Type addAppointment n/Grammy sd/10-04-2018 st/15:00 and press enter. Then type la and 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.

  3. 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 type la and press enter.
    Expected: An appointment with the above details and the selected celebrity and point of contact will appear in the appointment list.

  4. Type the above command again and press enter.
    Expected: No appointment is added. Error details shown in the status message.

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

  6. Type addAppointment n/Dinner l/??? and press enter.
    Expected: No appointment is added. Error details shown in the status message.

F.6. Listing appointments

This section includes instructions to test the listAppointment command of the application.

ℹ️
Prerequisites: At least one appointment stored in CelebManager.
  1. Type listAppointment in command box and press enter.
    Expected: All appointments are listed.

  2. Type listAppointment 01-04 01-05 in 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.

  3. Type listAppointment 0 in command box and press enter.
    Expected: No appointments listed. Error details shown in the status message.

  4. Type listAppointment a in command box and press enter.
    Expected: No appointments listed. Error details shown in the status message.

  5. Type listAppointment 03-05 02-01 in command box and press enter.
    Expected: No appointments listed. Error details shown in the status message.

  6. Type other incorrect listAppointment commands such as listAppointment, listAppointment x (where x is not a date range of format DD-MM-YYYY DD-MM-YYYY, DD-MM-YYYY DD-MM, DD-MM DD-MM-YYYY, or DD-MM DD-MM) in command box and press enter.
    Expected: No appointments listed. Error details shown in the status message.

F.7. Edititng an appointment

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.
  1. while listing appointments:

    1. Type editAppointment 0 in command box and press enter.
      Expected: No appointment is edited. Error details shown in the status message. Appointment list remains the same.

    2. Type editAppointment 1 n/New Appointment in command box and press enter.
      Expected: First appointment is renamed as New Appointment as 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.

    3. Type other incorrect editAppointment commands such as editAppointment, editAppointment x (where x is 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.

  2. While not listing appointments

    1. Type correct deleteAppointment commands such as editAppointment 1 in command box and press enter.
      Expected: No appointment is edited. Error details shown in the status message.

F.8. Deleting an appointment

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.
  1. While listing appointments:

    1. Type deleteAppointment 0 in command box and press enter.
      Expected: No appointment is deleted. Error details shown in the status message. Appointment list remains the same.

    2. Type deleteAppointment 1 in 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.

    3. Type other incorrect deleteAppointment commands such as deleteAppointment, deleteAppointment x (where x is 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.

  2. While not listing appointments

    1. Type correct deleteAppointment commands such as deleteAppointment 1 in command box and press enter.
      Expected: No appointment is deleted. Error details shown in the status message.

F.9. Viewing an appointment

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.
  1. Type viewAppointment 1 in 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.

  2. Type viewAppointment 0 in 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.

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

F.10. Showing location on map

This section includes instructions to test the showLocation command of the application.

  1. Type showLocation ma/NUS in command box and press enter.
    Expected: Location shown in map. Map view will be centered to location.

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

  3. Type other incorrect command such as showLocation, showLocation Punggol in command box and press enter.
    Expected: Similar to previous.

F.11. Estimating route between two location on map

This section includes instructions to test the estimateRoute command of the application.

  1. Type estimateRoute sma/NUS ema/Punggol in 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.

  2. Type estimateRoute sma/!!!!!! ema/Punggol in command box and press enter.
    Expected: Location not found in google server. Error details shown in the result display. Status bar remains the same.

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

  4. Type other incorrect command such as estimateRoute, estimateRoute Punggol NUS, estimateRoute sma/Punggol NUS, estimateRoute NUS ema/Punggol in command box and press enter.
    Expected: Similar to previous.

F.12. Viewing a celebrity’s calendar

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.
  1. Type viewCalendar c (where c is the index of the celebrity) in command box and press enter.
    Expected: CalendarPanel now 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.

  2. Type viewCalendar c (where c is 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.

  3. Type viewCalendar p (where p is 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.

  4. Type viewCalendar 0 in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

  5. Type other incorrect viewCalendar commands such as viewCalendar, viewCalendar x (where x is 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.

F.13. Viewing the combined calendar

This section includes instructions to test the viewCombinedCalendar command of the application.

ℹ️
Prerequisites: Combined calendar is not currently displayed.
  1. Type viewCombinedCalendar in command box and press enter.
    Expected: CalendarPanel now 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.

  2. Type viewCombinedCalendar again after previous execution in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

F.14. Changing to a different calendar view

This section includes instructions to test the viewCalendarBy command of the application.

ℹ️
Prerequisites: Calendar is currently in day view.
  1. Type viewCalendarBy week in command box and press enter.
    Expected: Calendar shows the week which the original day is in.

  2. Type viewCalendarBy month in command box and press enter.
    Expected: Calendar shows the month which the original day is in.

  3. Type viewCalendarBy day in command box and press enter.
    Expected: Calendar shows the day which is the original day.

  4. Type viewCalendarBy day again after executing the previous command in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

  5. Type viewCalendarBy MOnTh in command box and press enter.
    Expected: Calendar shows the week which the original day is in.

  6. Type viewCalendarBy 1 in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

  7. Type other incorrect viewCalendarBy commands such as viewCalendarBy, viewCalendarBy x (where x is not day, week, month or 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.

F.15. Viewing a particular day’s calendar

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.
  1. Type viewDate 01-04-2018 in command box and press enter.
    Expected: CalendarPanel displays the calendar for 1st April of 2018.

  2. Type viewDate d (where d is a date that is different from the current day’s date, in DD-MM format) in command box and press enter.
    Expected: CalendarPanel displays the calendar for date d of current year.

  3. Type viewDate in command box and press enter.
    Expected: CalendarPanel displays the calendar for the current day.

  4. Type viewDate again after executing the previous command in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

  5. Type viewDate 01-04 again after executing the previous command in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

  6. Type viewDate 0 in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

  7. Type viewDate ! in command box and press enter.
    Expected: No change in calendar. Error details shown in the status message.

  8. Type other incorrect viewDate commands such as viewDate, viewDate x (where x is not a date in DD-MM-YYYY or DD-MM format) in command box and press enter
    Expected: No change in calendar. Error details shown in the status message.

F.16. Transferring of storage calendar .xml file

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.
  1. Close the application.

  2. Open the data/ directory in the folder containing the .jar file. Expected: storagecalendar.xml consisting the to-be-replaced data is in the folder.

  3. Replace the old storagecalendar.xml with the the new storagecalendar.xml file.

  4. Restart the application. Expected: The new appointments data from storagecalendar.xml is reflected in the calendar panel.