Skip to content

Latest commit

 

History

History
1281 lines (911 loc) · 66.4 KB

DeveloperGuide.adoc

File metadata and controls

1281 lines (911 loc) · 66.4 KB

SGTravel - Developer Guide

1. Setting up

1.1. Prerequisites

  1. JDK 11 or above

  2. IntelliJ IDE

    ℹ️
    IntelliJ by default has Gradle and JavaFx plugins installed.
    Do not disable them. If you have disabled them, go to File > Settings > Plugins to re-enable them.

1.2. Setting up the project in your computer

  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 and select it. 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.

1.3. Verifying the setup

  1. Run the main and try a few commands

  2. Run the tests to ensure they all pass.

1.4. Configurations to do before writing code

1.4.1. Configuring the coding style

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

    • 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

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

1.4.2. Updating documentation to match your fork

After forking the repo, the documentation will still have the SGTravel branding and refer to the AY1920S1-CS2113T-W13-3/main repo.

If you plan to develop this fork as a separate product (i.e. instead of contributing to AY1920S1-CS2113T-W13-3/main), you should do the following:

  1. Configure the site-wide documentation settings in build.gradle, such as the site-name, to suit your own project.

  2. Replace the URL in the attribute repoURL in DeveloperGuide.adoc and UserGuide.adoc with the URL of your fork.

1.4.3. Setting up CI

Set up Travis to perform Continuous Integration (CI) for your fork. See UsingTravis.adoc to learn how to set it up.

After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).

ℹ️
Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork.

Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).

ℹ️
Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based)

1.4.4. Getting started with coding

When you are ready to start coding, we recommend that you get some sense of the overall design by reading about SGTravel’s architecture.

2. Design

2.1. Architecture

ArchitectureDiagram
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of SGTravel. SGTravel adopts a n-tier style architecture where higher layers make use of services provided by lower layers. Here is a quick overview of each layer/component:

  • UI: The user interface of the application

  • Commons: A collection of classes used by multiple other components

  • Logic: The main controller of the entire application

  • Model: Holds the data of the application in-memory

  • Storage: Reads data from, and writes data to, the hard disk

2.2. UI component

UiClassDiagram
Figure 2. Structure of the UI Component

The UI consists of a MainWindow that is made up of parts e.g.SidePanel etc. All these, including the MainWindow, inherit from the abstract UiPart class.

The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml

The UI component,

  • Executes user commands using the Logic component.

  • Recives commannd results from Logic component so that the UI can be updated with the modified data.

Given below is the simplified activity diagram for the workflow of the UI. Upon the start of the UI, it would request for user input. Then, it would process the user input and execute the input. It would show the respective response after execution of the input. If the response requires calendar or map, it would show it to the user. Subsequently, it would loop back to request for user input again. Else, if the response is exit type, it would exit the app.

UiActivityDiagram
Figure 3. Activity Diagram of the UI Component

2.3. Logic component

LogicClassDiagram
Figure 4. Structure of the Logic Component

API : Logic.java

  1. Logic uses the Parser and ConversationManager class to parse the user command.

  2. This results in a Command object which is executed by the LogicManager.

  3. The command execution can affect the Model (e.g. adding an event).

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

  5. In addition, the CommandResult object can also instruct the Ui to perform certain actions, such as displaying calendar to the user.

  6. Furthermore, the EditorManager is allowed to "snatch" the user inputs from the Parser when it is activated.

Given below is the diagram illustrating the workflow of the Logic component. The user input is passed to Logic component. Then, it would determine the input is of single line type or complex multiple line type. If it is a multiple line type, it would start a Conversation with the user to prompt for more input. Then, using the input, it would build it to become a single line input to be passed to create a Command. In create command, figure at the bottom, the input is parsed to determine which command is to be built. Subsequently, the Command would be executed to create CommandResult which contains the response and result of the execution of the Command.

LogicActivityDiagram
Figure 5. Logic Activity diagram
CreateParserActivityDiagram
Figure 6. Create Parser Activity diagram

2.4. Model component

ModelClassDiagram
Figure 7. Structure of the Model Component

API : Model.java

The Model,

  • stores a ProfileCard object that represents the user’s profile.

  • stores the SGTravel data.

  • the only class that is exposed to Storage component

2.5. Storage component

StorageClassDiagram
Figure 8. Structure of the Storage Component

The Storage component,

  • can save ProfileCard objects in .txt format and read it back.

  • can save the SGTravel data in .txt format and read it back.

The figure below shows some parts of the activity of Storage when it is initialised during the start of launching the SGTravel application. It will first read from event file path and parses it into EventList for Storage. Then it will read bus and train transport information and parses them into TransportationMap for Storage.

StorageActivityDiagram
Figure 9. Activity diagram for the Storage Component

2.6. Common classes

Classes used by multiple components are in the sgtravel.commons package.

3. Implementation

This section describes some noteworthy details on how certain features are implemented.

3.1. Handling user input

3.1.1. Implementation

SGTravel handleUserInput diagram
Figure 10. Sequence diagram of how user input is handled

Given below is an example usage scenario of how user input is handled:

  1. The user types in a command into the terminal, and clicks on the Enter button.

  2. Upon button press, the text in the input is read, if it is non-empty, the application will echo back the user’s input.

  3. The MainWindow will then call dukeResponse(input), which creates a thread using the Platform class.

  4. A CommandResult object is created, and dukeShow(result) is called using this object.

  5. If the result is of CommandResultExit, call tryExitApp(). Else if result is of CommandResultCalender, make a new CalenderWindow object with the result. Else if the result is of CommandResultMap, make a new MapWindow object.

3.2. Parsing of user input into command

3.2.1. Implementation

SGTravel parseUserInput diagram
Figure 11. Activity diagram of how user input is parsed into a command

Given below is an example usage scenario of how user input is handled:

  1. If the input is identified as a single line command (e.g. help, list), the appropriate command is returned.

  2. Else, call getCommandFromConversationManager(userInput), which will cause the ConversationManager to call converse(userInput).

  3. In ConversationManager, converse(userInput) will check for the presence of a Conversation, and process the given user input to see if it is what the Conversation wants. For example, a isDateInput(userInput) will check if the given user input is a date, whereas an isIntInput(userInput) will check to see if it is an Integer). If it matches, an appropriate prompt is returned as a message, and the appropriate fields of the conversation is updated.

  4. By checking if the Conversation in ConversationManager has ended, parseComplexCommand(userInput) will be called to try to parse the entire user input into a Command. Whereas if the Conversation has not ended, a PromptCommand is created by the ConversationManager using the Conversation and shown to the user.

  5. The LogicManager will then execute the Command and return a CommandResult to the Ui.

SGTravel commandCreation
Figure 12. Activity diagram of how Command is created by ConversationManager

3.2.2. Design Considerations

Aspect: How to allow both Single and Multi-step commands
  • Alternative 1 (current choice): Using ConversationManager to "accumulate" user input first before passing to LogicManager as a single step command when the accumulation is ready.

    • Pros: No need to create new command classes.

    • Pros: Can use the same key word to trigger both the single and multi-step commands based o the context.

    • Pros: Does not block out single-step command even when the multi-step command is ongoing.

    • Cons: Challenging to implement, exceptions need to be handled carefully.

  • Alternative 2: Single and Multi-step commands are considered as different commands.

    • Pros: Easy to implement.

    • Cons: We cannot use the same keywords to trigger both the multi-step and single-step command.

    • Cons: There will be many different commands with overlapping code which violates DRY principle.

    • Cons: Blocks out other commands while multi-step command is triggered.

3.3. Edit feature

3.3.1. Implementation

QuickEditCommandDiagram
Figure 13. Class diagram of QuickEditCommand

The naive editing mechanism is facilitated by the QuickEditCommand from the Logic component.

Given below is an example usage scenario and how the editing mechanism behaves at each step:

  1. The user calls the edit command with its relevant parameters.
    e.g. e 1 Bishan 12/02/29 14/02/29

  2. The LogicManager parses the user input using the Parser.

  3. The Parser calls QuickEditParser and returns an QuickEditCommand Object.

  4. The LogicManager will call execute() on the QuickEditCommand object which interacts with and updates the Model component and return a CommandResult Object containing a message. If no Event of the corresponding index is found, it would return a string of message MESSAGE_INVALID_EVENT_INDEX.

  5. The CommandResult Object will then be given back to the Ui component which displays the success message.

Alternatively, the editing mechanism can be facilitated by the EditorManager and Selectors in the Logic Component.
Below, is a diagram describing the work flow of the editing mechanism.

EditActivityDiagram
Figure 14. Activity diagram for how Editing Mechanism works

The EditorManager object oversees the edit operation with the help of Selectors class which selects the Event and the components to be updated. The EditorManager contains an instance of LocationSelector and an EventFieldSelector which selects the index of the Event and the components of the Event respectively.

The EditorManager object implements the following operations:

  • EditorManager+activate(EventList events, VenueList venues) - Activates the EditorManager

  • EditorManager+isActive() - Check if the EditorManager is active

  • EditorManager-deactivate() - Deactivates the EditorManager

  • EditorManager+edit(String userInput) - Updates the state of the editing process based on user input

  • EditorManager+edit(KeyEvent keyInput) - Updates the state of the editing process based on user key press

  • EditorManager-selectEventField() - Uses the EventFieldSelector to select a field within the Event

  • EditorManager-selectEvent() - Uses the LocationSelector to select an Event

EditSequenceDiagram
Figure 15. Sequence diagram for how Editing Mechanism works

Given below is an example usage scenario and how the editing mechanism behaves at each step.

  1. The user invokes the EditorManager by typing edit followed by the enter key.

  2. The LogicManager parses the user input using the Parser.

  3. The Parser breakdowns the user input into and returns an EditorCommand Object.

  4. The LogicManager will call execute() on the EditorCommand object which return a CommandResult Object containing a message and the EventList in the Model component.

  5. The LogicManager will then call activate() on the EditorManager object inside the LogicManager class. Upon activation, the user inputs will be passed to the EditorManager object through edit() instead until it is deactivated.

  6. The EditorManager will deactivate itself once the user input: save or close, which updates or discard the edits that were made respectively

Given below here is an example of how the EditorManager manages which Event and the corresponding field it should select:

Step 1. The user launches edit mode, the EditorManager is in a clean state and will simply select index 0 of the Event in the EventList.

EditState0
Figure 16. Diagram for edit state

Step 2. User presses the up key. The EditorManager calls feedKeyCode() on the LocationSelector which will then find the nearest Event in the up direction and updates the index.

EditState1
Figure 17. Diagram for edit state

Step 3. User presses the enter key which locks in on the Event. The EditorManager will again call LocationSelector+feedKeyCode(), and the LocationSelector will now lock itself, meaning that the arrow keys will no longer change the index.

Step 4. User presses the down key. The EditorManager will now call EventFieldSelector+feedKeyCode(), and the EventFieldSelector will return an index pointing to a field within the Event.

EditState2
Figure 18. Diagram for edit state

Step 5. The user now inputs: 09/11/20. The EditorManager will now call Editor+edit() which edits the event2’s startDate from 30/10/19 to 09/11/20.

3.3.2. Design Considerations

Aspect: How edit executes
  • Alternative 1: The user inputs a specific command instruction and SGTravel executes it

    • Pros: Easy to implement

    • Cons: Difficult for user to learn, hard to make mass edits

  • Alternative 2: The user use arrow, enter keys to navigate around the events and edit directly

    • Pros: Straightforward to use, allow mass edits

    • Cons: Difficult to implement

Aspect: Data structures to support edit operation
  • Alternative 1: An EditCommand to edit

    • Pros: No changes are required on Logic and easy for anyone with knowledge of OOP to understand

    • Cons: Does not allow for flexibility on the user’s end as it takes in only strict inputs that adhered to the format

  • Alternative 2: Using EditorManager to edit

    • Pros: Does not violate separation of concerns and Single responsibility principle as it only deals with edit operation

    • Cons: Require many accessory classes to reduce coupling and increase cohesion within the EditorManager itself

Aspect: Integration of the data structures into the architecture
  • Alternative 1: EditCommand

    • Pros: Require minimal changes to the code

    • Cons: Does not demonstrate student’s understanding of software engineering

  • Alternative 2: EditManager

    • Pros: Requires much more data structures, where SOLID principles can be demonstrated

    • Cons: Changes need to be made to entire architecture

3.4. Route Feature

3.4.1. Implementation

SGTravel allows users to plan and customize routes to assist in travelling around. Multiple transportation options are covered, allowing for flexibility in planning. The Route planning mechanism is facilitated by the RouteList. This section will document the implementation of the Route feature and its various components. Included below is the class diagram for RouteList:

SGTravel RouteList class diagram
Figure 19. RouteList class diagram

Route objects are stored in the RouteList. Each Route object itself represents a list of RouteNode objects that correspond to physical locations in an area. Below is the class diagram of a Route object:

SGTravel Route class diagram
Figure 20. Route class diagram

RouteNode is an abstract class extending the Venue class. RouteNode itself is extended by the BusStop, TrainStation and CustomNode objects. Below is the class diagram of these classes:

SGTravel RouteNode class diagram
Figure 21. RouteNode class diagram

Given below is an example usage scenario and how the Model,RouteList and Route behaves at each step when trying to add a new Route to SGTravel:

  1. The user calls the RouteAddCommand with its relevant parameters.
    e.g. routeAdd Go to Sentosa

  2. The LogicManager parses the user input using the Parser.

  3. The Parser breakdowns the user input into and returns an RouteAddCommand Object.

  4. The LogicManager will call execute() on the RouteAddCommand object which return a CommandResultText Object containing a message.

  5. During the execute() method, the RouteList is obtained by calling the getRoutes() method in the Model.

  6. A new Route is created with the relevant parameters and is added to the RouteList via the add() method in the RouteList.

  7. The method save() is invoked in the Model, and a CommandResultText object is created with an appropriate message to show that the command has been executed properly.

Given below is the sequence diagram of how the various components work in the execute() method of the RouteNodeAdd command:

SGTravel routeAdd diagram
Figure 22. Sequence diagram for the execute() method in RouteAddCommand

Should the user want to edit a Route, the RouteEditCommand can be used. Given below is an example usage scenario and how the Model, RouteList and Route objects behave at each step:

  1. The user calls the RouteEditCommand with its relevant parameters.
    e.g. routeEdit 1 name Go to MBS

  2. The LogicManager parses the user input using the Parser.

  3. The Parser breakdowns the user input into and returns an RouteEditCommand Object.

  4. The LogicManager will call execute() on the RouteEditCommand object which return a CommandResultText Object containing a message.

  5. During the execute() method, the RouteList is obtained by calling the getRoutes() method in the Model.

  6. The Route that is going to be edited is obtained through calling the get() method in the RouteList with the given index parameter.

  7. The editField() method of the RouteEditCommand is invoked, which will call either setName() or setDescription() in the Route object, with the appropriate parameter. If an invalid field is given, an UnknownFieldException is thrown, which will be caught by the MainWindow object later on.

  8. The method save() is invoked in the Model, and a CommandResultText object is created with an appropriate message to show that the command has been executed properly.

Given below is the sequence diagram of how the various components work in the execute() method of the RouteEditCommand command:

SGTravel routeEdit diagram
Figure 23. Sequence diagram for the execute() method in RouteEditCommand

The user can also add different RouteNode objects to a Route. This can be done with the RouteNodeAddCommand. Given below is an example usage scenario and how the Model, RouteList, Route and RouteNode object behave at each step:

  1. The user calls the RouteNodeAddCommand with its relevant parameters.
    e.g. routeNodeAdd 1 1 at 17009 by bus

  2. The LogicManager parses the user input using the Parser.

  3. The Parser breakdowns the user input into and returns an RouteNodeAddCommand Object.

  4. The RouteNodeAddCommand object will contain a new RouteNode object which is created from the createRouteNode() method in the RouteNodeAddParser object. The createRouteNode() method will parse through the given user input, and call the getRouteNode() method if the input is in the correct format.

  5. Depending on the user input, a BusStop, TrainStation or CustomNode object will be created, and the RouteNodeAddCommand will contain this new object.

  6. The LogicManager will call execute() on the RouteNodeAddCommand object which return a CommandResultImage Object containing a message and an image of the map at that location.

  7. During the execute() method, the fetchNodeData() method is invoked to try to get the full information about the new RouteNode object. If this fails, a CommandResultImage object with the appropriate error message will be returned.

  8. The addToRoute() method of the RouteNodeAddCommand is invoked. The Route that the new RouteNode is being add to, is obtained through calling the getRoute() method in the Model with the given index parameter. The RouteNode is added to the Route by invoking the addNode() method in the Route with the given index parameter for the RouteNode.

  9. The method save() is invoked in the Model, and a RouteNodeShowCommand is created with the index parameters of this RouteNode. The execute() method of this RouteNodeShowCommand is invoked, to create a new CommandResultImage object, which contains a message showing that the RouteNode has been added. This CommandResultImage also contains the image of the map of the RouteNode. The RouteNodeShowCommand will be explained later on.

SGTravel routeNodeAdd diagram
Figure 24. Activity diagram for the execute() method in RouteNodeAddCommand

The user can also show the map of a RouteNode by using the RouteNodeShowCommand. Given below is an example usage scenario and how the Model, RouteList, Route and RouteNode object behave at each step:

  1. The user calls the RouteNodeShowCommand with its relevant parameters.
    e.g. routeNodeShow 1 1

  2. The LogicManager parses the user input using the Parser.

  3. The Parser breakdowns the user input into and returns an RouteNodeShowCommand Object.

  4. During the execute() method, the Route that the RouteNode that is being shown is obtained by calling the getRoute() method in the Model. By calling getNode() on the Route, the RouteNode being shown will be obtained.

  5. The generateRouteNodeShow() method is called in the ApiParser class. This method constructs a string from the parameters of the RouteNode object, and obtains an Image object from the getStaticMap method in the ApiParser.

  6. A StaticMapUrlRequest object is created, which will send a request to the OneMap API. The API returns an Image object back to the StaticMapUrlRequest, which will return the object back to the ApiParser. This Image is returned back to the RouteNodeShowCommand object, which will then return a ComandResultImage object with the Image and the parameters of the RouteNode.

SGTravel routeNodeShow diagram
Figure 25. Activity diagram for the execute() method in RouteNodeShowCommand

The user can also show the map of a RouteNode and nearby BusStop and TrainStation objects by using the RouteNodeNearbyCommand. Given below is an example usage scenario and how the Model, RouteList, Route and RouteNode object behave at each step:

  1. The user calls the RouteNodeNearbyCommand with its relevant parameters.
    e.g. routeNodeNearby 1 1

  2. The LogicManager parses the user input using the Parser.

  3. The Parser breakdowns the user input into and returns an RouteNodeShowCommand Object.

  4. During the execute() method, the Route that the RouteNode that is being shown is obtained by calling the getRoute() method in the Model. By calling getNode() on the Route, the RouteNode being shown will be obtained.

  5. The list of nearby RouteNode (BusStop and TrainStation) are obtained by calling the getNeighbour() method in the ApiParser class.

  6. The generateStaticMapNeighbours() method is called in the ApiParser class. This method constructs a string from the parameters of the RouteNode object and the list of nearby RouteNodes, and obtains an Image object from the getStaticMap method in the ApiParser.

  7. A StaticMapUrlRequest object is created, which will send a request to the OneMap API. The API returns an Image object back to the StaticMapUrlRequest, which will return the object back to the ApiParser. This Image is returned back to the RouteNodeNearbyCommand object, which will then return a ComandResultImage object with the Image and list of nearby RouteNodes.

SGTravel routeNodeNearby diagram
Figure 26. Activity diagram for the execute() method in RouteNodeShowCommand

The user can also automatically generate a new Route by using the RouteGenerateCommand. Given below is an exmample usage scenario and how the Model, RouteList and PathFinder object behave at each step.

  1. The user calls the RouteGenerateCommand with its relevant parameters.
    e.g. routeGenerate amk hub to nus by bus

  2. The LogicManager parses the user input using the Parser.

  3. The Parser breakdowns the user input into and returns an RouteGenerateCommand Object.

  4. The generateRoute() method is called in the RouteGenerateCommand. It invokes the getLocationSearch() method in the ApiParser class for the 2 given location parameters. This creates 2 Venue objects corresponding to the starting and ending points of this Route. These 2 objects are passed into the execute() method in the PathFinder object that is constructed in this method. This execute() method will return a list of Venue objects corresponding to the transfer points that the Route will have.

  5. A new Route object is created, and all the Venue objects are iterated through. For each pair of Venue objects in the list, addInbetweenNodes() is invoked from the RouteGenerateCommand, which creates all the RouteNode objects that exist between these 2 transfer points and inserts them into the new Route. addEndingNode() is then invoked to add the Venue objects as RouteNode objects to the Route.

  6. The RouteList is obtained by invoking getRoutes() in the Model, and the new Route is added via the add() method in the RouteList.

  7. The method save() is invoked in the Model, and a CommandResultText object is created and returned.

SGTravel routeGenerate diagram
Figure 27. Activity diagram for the execute() method in RouteGenerateCommand

3.4.2. Design Considerations

Aspect: How RouteNodes are stored
  • Alternative 1: RouteNode is stored as an object.

    • Pros: Easy to edit and interact with.

    • Cons: Requires making a BusStop, TrainStation and CustomNode object, as all 3 items have different properties.

  • Alternative 2: RouteNode would be stored as a String instead, and is broken up into its individual parts when manipulated.

    • Pros: Easy to implement.

    • Cons: Prone to errors in breaking up the String because of its contents, and hard to maintain.

Aspect: How Routes are stored
  • Alternative 1: A RouteList object holds several Routes in an ArrayList.

    • Pros: Easy to interact with and modify a Route. Keeps the Model simple.

    • Cons: Will require more work to implement.

  • Alternative 2: Routes would be located in an ArrayList in the SGTravel’s model.

    • Pros: Quick and easy to implement.

    • Cons: Limited ability to manipulate a Route. Additional manipulation commands will bloat up the model.

3.4.3. [Proposed] Route Manager

The completed Route feature would include a RouteManager class in v2.0. The RouteManager will be similar to the EditorManager class, and will be activated by entering a specific command. With the RouteManager turned on, ther user interacts in multi-step commands instead. In order to simplify the user experience, the Route and RouteNode that the user wants to interact with, are stored in the RouteManager. Commands entered will directly affect the Route and RouteNode stored in the RouteManager, and the user can easily change the Route and RouteNode that are selected by commands too. A saving feature as seen in the EditorManager class will also be implemented, allowing the user to easily save or discard changes.

3.5. Itinerary Feature

3.5.1. Implementation

SGTravel facilitates the user to create itineraries for their Singapore trip either from scratch or based on recommendations given. The user can then store these itineraries and access them at any time leading to effective trip planning and scheduling. This section will document the implementation of the Itinerary feature and its various components. The Itinerary feature is facilitated by the Itinerary class as well as the commands outlined below. Below is the class diagram for Itinerary:

SGTravel Itinerary class diagram
Figure 28. The class diagram for Itinerary and associated classes

As can be seen from the diagram above. The main Itinerary class has different fields such as the startDate and endDates (to calculate the number of days) and a list of Agenda. Here the Agenda class models the activities and locations to be seen in one day. Hence, it contains a list of Todos and extends VenueList.

Given below is an example usage scenario and how the newItinerary mechanism behaves at each step:

  1. The user calls the NewItineraryCommand with its relevant parameters.
    e.g. newItinerary 23/04/20 24/04/20 NewItinerary 1 /venue MBS /do sightseeing /and eating.

  2. The LogicManager parses the user input using the Parser.

  3. The Parser calls CreateNewItineraryParser and returns an NewItineraryCommand object and passes the created itinerary in its constructor.

  4. The LogicManager will call execute() on the NewItineraryCommand object which interacts with and stores the new itinerary in the Model.

  5. The method save() is invoked in the Model, and a CommandResultText object is created with an appropriate message to show that the command has been executed properly and the itinerary has been created.

  6. If no Itinerary is created, it would return a message string MESSAGE_ITINERARY_FAIL_CREATION.

The activity diagram given below shows how the NewItineraryCommand interacts with the Model interface to save the new Itinerary.

SGTravel newItinerary Command diagram
Figure 29. Activity diagram for the execute() method in newItineraryCommand

Alternatively, the creation of itineraries can be facilitated by the RecommendationsCommand and the AddListCommand.

Given below is an example usage scenario and how the Recommendation mechanism behaves and interacts with the Model:

  1. The user calls the RecommendationsCommand with its relevant parameters.
    e.g. recommend itinerary between 23/04/20 and 24/04/20

  2. The LogicManager parses the user input using the RecommendationsCommandParser.

  3. The RecommendationsCommandParser returns a RecommendationsCommand object and passes the relevant array of date details.

  4. The LogicManager will call execute() on the RecommendationsCommand object which returns a CommandResultText object containing the recommended itinerary.

  5. During the execute() method a recommendation object containing all of SGTravel’s possible locations is created.

  6. The recommendation object then calls on the makeItinerary() function. This then returns an itinerary object contains details of a recommended trip of the specified length. If more than 9 days are entered a RecommendationsFailException is passed.

  7. This returned recommended itinerary is then stored into the Model component via the setRecentItinerary() method. This makes the recommendation easily accessible to the addThisList command.

  8. A CommandResultText object is created with an appropriate message to show that the command has been executed properly.

Given below is the sequence diagram of how the various components work in the execute() method of the RecommendationsCommand command:

SGTravel recommend command diagram
Figure 30. Sequence diagram for the execute() method in RecommendationsCommand

Given below is an example usage scenario and how the AddList mechanism behaves and interacts in the Model after a Recommendation has been added :

  1. The user calls the AddThisListCommand with its relevant parameters.
    e.g. addThisList MyNewVacation

  2. The LogicManager parses the user input using the Parser.

  3. The Parser returns a AddThisListCommand object and passes the relevant newName with it.

  4. The LogicManager will call execute() on the AddThisListCommand object which returns a CommandResultText object containing the newly added recommendation in full.

  5. During the execute() method the recommendation given most recently is retrieved from storage. If no recent recommendation is present a NoRecentItineraryException is returned.

  6. The confirmRecentItinerary() method is called. This saves the current recommendation with the new name entered.

  7. The setRecentItinerary() sets the recentItinerary (recommendation) to null so that the same recommendation cannot be added twice.

  8. The method save() is invoked in the Model, and a CommandResultText object is created with an appropriate message to show the stored itinerary has succeeded

Given below is the sequence diagram of how the various components work in the execute() method of the AddThisListCommand command:

SGTravel addList command diagram
Figure 31. Sequence diagram for the execute() method in RecommendationsCommand

Other helper commands such as: listItinerary, showItinerary and doneItinerary have not been shown here as they are simple text displaying commands. These commands offer an important interface to the user so that they can manage and create itineraries with greater ease.

3.5.2. Design Considerations

Aspect: How a new Itinerary is entered
  • Alternative 1: Currently, newItinerary command offers a one-shot command alternative.

    • Pros: Easy to use and learn. Easy to implement.

    • Cons: Parsing function is complicated and error prone.

  • Alternative 2: Through a GUI Interface.

    • Pros: Much easier for the user to use. Less error prone.

    • Cons: Difficult to implement. Does not add to showcasing OOP skill.

Aspect: How SGTravel Saves Itineraries
  • Alternative 1: Currently, SGTravel rewrites the hashmap every time a command is entered.

    • Pros: Easy to implement with little lines of code. Easy to understand.

    • Cons: Decreases efficiency of code and reduces performance.

  • Alternative 2: Option to only update the recently added list.

    • Pros: Increases program efficiency.

    • Cons: Difficult to implement.

3.5.3. [Proposed] Itinerary Builder Command

The completed itinerary feature would allow the user to create an itinerary as if they were engaging in a conversation with SGTravel. This process would also allow the user to edit the itinerary before confirming and storing it. The app would use an algorithm to find the nearest attractions to the user’s hotel stay. The activity diagram for this feature is given below:

SGTravel v.20 newItinerary builder diagram
Figure 32. Activity diagram for the Itinerary Builder command

There are two main additions coming in v2.0 are :

  1. Recommendations based on hotel location

  2. Edit feature during and after itinerary creation.

3.6. Profile feature

3.6.1. Implementation

SGTravel enables the user to create a personal profile and save basic information. The profile mechanism is facilitated by the ProfileCard. It stores information internally as Preferences, Person and favourite as depicted in the figure below.

ProfileCardClassDiagram
Figure 33. ProfileCard Class Diagram
💡

Detailed information of itinerary could be found in Section 3.5 Itinerary

Profile mechanism implements the following feature:

  • profile - set the profile of the person

  • profileSet - set preference of the person

  • profileShow - shows the user the current profile

  • addFav - add favourite itinerary to favourite list

  • deleteFav - delete itinerary from favourite list

  • showFav - shows the details of favourite itinerary

  • listFav - list all the favourite itinerary.

ProfileAddSequenceDiagram
Figure 34. Profile Sequence Diagram

The figure above shows how profile mechanism works. Given below is an example usage scenario and how the profile mechanism behaves at each step:

  1. User input command profile with relevant information.
    E.g profile James 01/01/1999

  2. LogicManager passes the input to Parser.

  3. Parser constructs relevant parser, which is ProfileParser in this case.

  4. ProfileParser to break down the input into String and LocalDateTime, which is the required parameter to construct ProfileAddCommand.

  5. ProfileAddCommand is returned back to LogicManager.

  6. LogicManager calls execute() function of ProfileAddCommand.

  7. ProfileAddCommand calls setPerson() with the parsed input to set Person in ProfileCard to the name and birthday of user.

  8. ProfileAddCommand calls save on model to save the ProfileCard with new information.

ℹ️

If user did not set profile, ProfileCard is initialise with name as “new user” and birthday set as the date SGTravel is first launch.

profileSet have similar mechanism as profile. Given below is an example usage scenario and how the profileSet mechanism behaves at each step:

  1. User input command profileSet with relevant information.
    E.g profile sports true

  2. LogicManager passes the input to Parser.

  3. Parser constructs relevant parser, which is ProfileSetPreferenceParser in this case.

  4. ProfileSetPreferenceParser to break down the input into String and Boolean, which is the required parameter to construct ProfileSetPreferenceCommand.

  5. ProfileSetPreferenceCommand is returned back to LogicManager.

  6. LogicManager calls execute() function of ProfileSetPreferenceCommand.

  7. ProfileSetPreferenceCommand calls getProfileCard() of ModelManager to get the ProfileCard and then calls setPreference() with the parsed input to set preference in ProfileCard to the category to the setting.

  8. ProfileSetPreferenceCommand calls save on ModelManager to save the ProfileCard with new information.

💡

The only valid preference currently in V 1.4 are sports,lifestyle,arts and entertainment. Additional preferences could be added by just adding additional field in preferences.

addFavSequenceDiagram
Figure 35. Profile Sequence Diagram

The figure above shows the mechanism of addFav. Given below is an example usage scenario and how the addFav mechanism behaves at each step:

  1. User input command addFav with relevant information.
    E.g addFav SundayVacay

  2. LogicManager passes the input to Parser.

  3. Parser constructs relevant parser, which is ProfileAddFavParser in this case.

  4. ProfileAddFavParser to break down the input into String, which is the required parameter to construct ProfileAddFavCommand.

  5. ProfileAddFavCommand is returned back to LogicManager

  6. LogicManager calls execute() function of ProfileAddFavCommand.

  7. ProfileAddFavCommand calls getItinerary() of ModelManager to get the Itinerary of given name and then calls addToFavourite() of ModelManager.

  8. ModelManger will call addToFavourite() of ProfileCard to add the itinerary to favourite in ProfileCard.

  9. ProfileAddFavCommand calls save on ModelManager to save the ProfileCard with new information.

deleteFav have similar mechanism as addFav. Given below is an example usage scenario and how the deleteFav mechanism behaves at each step:

  1. User input command deleteFav with relevant information.
    E.g deleteFav SundayVacay

  2. LogicManager passes the input to Parser.

  3. Parser constructs relevant parser, which is ProfileDeleteFavParser in this case.

  4. ProfileDeleteFavParser to break down the input into String, which is the required parameter to construct ProfileDeleteFavCommand.

  5. ProfileDeleteFavCommand is returned back to LogicManager.

  6. LogicManager calls execute() function of ProfileDeleteFavCommand.

  7. ProfileDeleteFavCommand calls deleteFavourite() function of ModelManager.

  8. ModelManger will call deleteFavourite() of ProfileCard to delete the Itinerary from favourite in ProfileCard.

  9. ProfileDeleteFavCommand calls save on ModelManager to save the ProfileCard with new information.

Other helper commands such as: profileShow, listFav and showfav have not been shown here as they are simple text displaying commands.

3.6.2. Design Considerations

Aspect: How SGTravel Saves Favourite Itinerary
  • Alternative 1: Create another itinerary list as a favourite list.

    • Pros: Easy to implement.

    • Cons: Easy to mix up itinerary list with favourite list.

  • Alternative 2: Create itinerary list in profile as favourite list.

    • Pros: Easier and more intuitive for user to view favourite list.

    • Cons: More difficult to implement as some classes can’t be reused.

3.7. [Proposed] Data Encryption

{Explain here how the data encryption feature will be implemented}

3.8. Logging

We are using java.util.logging package for logging.

  • The Logger for a class can be obtained using Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)

  • Log level can be indicated using logger.log(Level.INFO, MESSAGE) which logs messages according to the log level

Logging Levels

  • SEVERE : Critical problem detected which may possibly cause the termination of the application

  • WARNING : Can continue, but with caution

  • INFO : Information showing the noteworthy actions by the App

  • FINE : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size

4. Documentation

Refer to the guide here.

5. Testing

Refer to the guide here.

6. Dev Ops

Refer to the guide here.

Appendix A: Product Scope

Our application, SGTravel, is for tourists and citizens who are looking to travel within Singapore. It allows the user to compare travel timings and provides information on attractions, amenities and costs of travel.

Target user profile:

  • Our application is for those users who are comfortable using CLI apps and prefer desktop apps rather than using phone applications.

  • Our application focuses on users who want to ease their travelling process.

  • Our application takes in the users constraints and plans their trip accordingly.

  • Our application suggests excursion destinations, routes and provides guidance for tourists as well.

Value proposition:

  • By using this application, users gain access to all relevant travel information within Singapore (costs, time taken, attractions) without the need to download other desktop applications.

  • Our application simplifies the process of trip planning by showing the user the shortest path between their starting place and their ending destination.

  • Our application provides supporting information to the user such as the currency exchange rate, the weather forecast and flight information to other countries out of Singapore.

  • Our application will also provide travel itineraries for tourists in Singapore with limited days stay.

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 application

* * *

User

See a dashboard with all of my travels itineraries, destinations and suggestions

Be updated of my schedule

* * *

User

To create new destination request

Be presented with the shortest time to reach my destination

* * *

User

Be able to filter travel paths based on different constraints

Plan an efficient trip based on my needs

* * *

User

Be able to see the cost to get to destination depending on mode of transportation

Plan the mode of transportation

* * *

User

To update destination requests

Update my trips

* * *

User

To delete destination requests

Mark my trips as complete

* * *

Tourist

Be able to see at least 3 different itineraries when I enter number of days I am in Singapore

Choose one according to my liking

* * *

user

Notification alert

Reminded of my flights and travel plans

* * *

user

Be able to put reminders for events

Remember my reservations/plans

* *

User

Be able to search for a destination by using a search bar

Can search for destinations before making a new request

* *

User

Be able to see taxis nearby my location

Quickly locate a ride

* *

Local

Be able to find information on parking spaces and ERP

Travel by car in the most cost effective way

* *

Tourist

Be able to see famous tourist destinations on a map

Plan where to travel

* *

User

Be able to learn about the weather forecast of the day/week

Be prepared while commuting

* *

Tourist

Be able to convert my home currency into Singaporean dollar / other currencies

I can view Singapore’s currency value and other countries I may be connecting to

* *

Local

Be able to see the PSI index

Plan my activities to be indoors or outdoors

* *

Tourist

Be able to see a list of hotels

Choose the best hotel

* *

User

Be able to choose my preferred mode of transportation

Have it as my default option. While displaying destination request put preferred mode first

* *

Tourist

Be able to get a list of attractions along a specific travel path

See the attractions while on the way / during layovers

* *

User

Be able to see a list of closest / recommended restaurant to my location

Choose places to eat with ease

* *

Exchange Student

Be able to have access to information about student prices for various attractions

Be aware of discounts

* *

User

Be able to access more websites / tourist booking sites

Make bookings with the relevant authorities in the country

* *

User

Be able to gain information about events in and around Singapore

Visit time specific events

* *

User

Be able to interact with the app through a graphical user interface

Interact with the application more easily

*

New User (Tourist)

Have recommendations for attraction and travel tips for tourists (getting around, food culture etc)

Read about Singapore before going there

*

User

Inform me of road hazards and delays along the way

Avoid traffic congestion and be careful while driving/travelling

*

Tourist

Rate my favourite attractions and write reviews of my experience

Record a brief summary of my travels

*

Tourist

Be able to get flight information to and out of Singapore

Choose the best flight option

*

New user

Have a natural language-like CLI

So that I can use the app with greater ease

*

Exchange students

Be able to see destinations around my hostel

Plan weekend trips around my station

*

User

Able to access reviews about different destinations by giving relevant links

Pick my destination of choice with second opinions of people who went there

*

User

Have different view mode (night/day)

To customise my App to my liking

*

Elderly User

Be able to increase font size

See the content more easily

{More to be added in v2.0}

Appendix C: Use Cases

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

Use case: See Usage Instructions

MSS

  1. User requests to see instruction manual with the command help

  2. SGTravel shows a list of possible commands

    Use case ends.

Extensions

  • 1a. User input invalid.

    • 1a1. SGTravel shows an error message “Enter help to see all possible commands”.

      Use case resumes at step 1.

Use case: New Destination Request

MSS

  1. User selects new destination option.

  2. SGTravel asks for start and end location.

  3. User inputs start and end location.

  4. SGTravel shows the shortest path between 2 locations on a map.

    Use case ends.

Extensions

  • 3a. The user enters an invalid location

    • 3a1. SGTravel shows an error message.

      Use case resumes at step 2.

Use case: Filter Travel Paths on Constraints

MSS

  1. User enters the choose destination mode

  2. SGTravel asks for start and end location.

  3. User inputs start and end location.

  4. User requests to filter travel paths with filter:<constraint> .

  5. SGTravel shows the filtered list.

  6. User chooses the desired travel path based on the constraints

  7. SGTravel shows the travel path between 2 locations on a map.

    Use case ends.

Extensions

  • 3a. The given constraint is not valid

    • 3a1. SGTravel shows an error message containing the valid constraint options.

      Use case resumes at step 2.

  • *a. User request to exit choose destination mode

    • SGTravel exit choose destination mode and goes back to home page.

  • *b. User request to analysis the cost of current travel path

    • SGTravel shows the cost of current travel path

  • *c. User request to change the mode of transportation

    • *c1. SGTravel shows the list of available transportation

    • *c2. User input the choice of transportation

    • *c3. SGTravel shows the path based on chosen transportation

Use case: Itinerary based on Days of Travel

MSS

  1. User enters the create itinerary mode

  2. SGTravel request start and end date of the itinerary

  3. User Enters start and end date.

  4. SGTravel shows different options of itineraries

  5. SGTravel requests user to enter their choice

  6. User enters their choice of itinerary

  7. SGTravel saves this to the itineraries list to display on the dashboard

    Use case ends.

Extensions

  • 3a. The input is not valid

    • 3a1. SGTravel shows invaild input error message.

      Use case resumes at step 2.

  • 6a. The input is not valid

    • 6a1. SGTravel shows invaild input error message.

      Use case resumes at step 5.

  • *a. User request to exit create itinerary mode

    • SGTravel exit’s create itinerary mode and goes back to home page.

Use case: Convert Currency

MSS

  1. User enters the convert currency command.

  2. SGTravel shows all of the possible currencies to convert.

  3. SGTravel requests user to enter home currency and foreign currency.

  4. User enters home currency and foreign currency.

  5. SGTravel requests to enter home currency amount.

  6. User enters home currency amount.

  7. SGTravel shows the converted currency amount.

    Use case ends.

Extensions

  • 4a. The input currency is not valid

    • 4a1. SGTravel shows an error message.

      Use case resumes at step 2.

  • 6a. The user input is invaild

    • 6a1. SGTravel shows invaild error message.

      Use case resumes at step 5.

  • *a. User request to exit convert currency

    • SGTravel goes back to home page.

Use case: Flight Options

MSS

  1. User enters the flight option mode.

  2. SGTravel requests the user to enter the destination country.

  3. User enters destination country.

  4. SGTravel shows the user all of the flights sorted according to costs.

  5. User enters command to filter flights with “filter:<constraint> ” 6. (constraints based on for example : Airline type, Number of destination etc.)

  6. SGTravel shows the filtered list of flight options.

  7. User enters preferred flight plan.

  8. SGTravel stores flight plan in memory to display on dashboard and notification center.

    Use case ends.

Extensions

  • 3a. The input destination is not valid

    • 3a1. SGTravel shows an error message containing the valid country.

      Use case resumes at step 2.

  • 5a. The given constraint is not valid

    • 5a1. SGTravel shows an error message containing the valid constraint options.

      Use case resumes at step 4.

Use case: Check PSI level

MSS

  1. User request for PSI Level

  2. SGTravel shows PSI Level for past 24hr

    Use case ends.

Use case: Check weather forecast

MSS

  1. User requests for weather forecast

  2. SGTravel shows weather forecast for the week

    Use case ends.

Extensions

  • *a. User requests for weather forecast with specific date

    • SGTravel shows weather forecast for specific date

{More to be added in v2.0}

Appendix D: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 11 or above installed.

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

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

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

  5. Should work on both 32-bit and 64-bit environments.

  6. Should not exceed 200MB in size.

  7. Should not use any words deemed offensive to English speakers.

{More to be added in v2.0}

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

ERP

Electronic Road Pricing

PSI

Pollutant Standards Index

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.
ℹ️
We also recommend testers to have a stable internet connection throughout the tests.

F.1. Launch and Shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file
      Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.

  2. Loading sample data

    1. Put the jar file into an empty directory

    2. Re-launch the app by double-clicking the jar file.
      Expected: Sample data should be displayed in the application on start up.

  3. Shutting down

    1. Prerequisites: Ensure that you are not in editing mode. Type close followed by the enter key to exit edit mode without saving.

    2. Type bye followed by the enter key

    3. Expected: Application should shut down.

F.2. Setting up profile

  1. Set up or change your current profile

    1. Test case: profile Tom 25/12/1997
      Expected: Profile is updated. Name of user is now Tom and date of birth is now 25/12/1997.

    2. Test case: profile Moo 21/12/9999
      Expected: Profile is not updated. Error status is shown by SGTravel as date has yet to happen.

    3. Type profile followed by the enter key
      Expected: Profile is not updated. SGTravel prompts user to input his/her name.

    4. Type Alexander the Great followed by the enter key
      Expected: Profile is not updated SGTravel prompts for user to input date of birth.

    5. Type 09/09/1997 followed by the enter key
      Expected: Profile is updated. Name of the user is now Alexander the Great and date of birth is 09/09/1997.

  2. Setting your hobbies

    1. Test case: profileSet arts true
      Expected: Profile is updated and the user now likes arts.

    2. Test case: profileSet dance true
      Expected: Pofile is not updated. Error invalid format status is shown by SGTravel as there are only 4 options to set currently: arts, lifestyle, sports, entertainment.

F.3. Searching bus routes

  1. Searching for the route of a bus service

    1. Test case: busRoute 96
      Expected: Bus route of the bus service is shown by SGTravel.

    2. Test case: busRoute CS3243
      Expected: No bus route is shown. Error details shown by SGTravel as there is no such bus service.

    3. Other incorrect bus route commands to try: busRoute boomboom, busRoute 1231231231231
      Expected: Similar to previous.

F.4. Adding an event to your plans

  1. Adding an event

    1. Prerequisites: List all events using the list command. There should be no Bishan within the list.

    2. Test case: event Bishan between 24/02/24 and 26/02/24
      Expected: Event is added to the list. Details of added event is shown by SGTravel.

    3. Test case: event Mxwglht between 05/04/28 and 07/04/28
      Expected: Event is not added. Error details shown by SGTravel as location does not exist.

    4. Test case: event Prison between 30/04/18 and 06/05/19
      Expected: Event is not added. Error details shown by SGTravel as dates are already in the past.

    5. Other incorrect add commands to try: event, event moo, event orchard between 20/09/29 and 25/09/28, event between and, event park between and, event park between Mon and, event park between and Mon
      Expected: Similar to the previous.

F.5. Deleting an event from your plans

  1. Deleting an event while all events are listed

    1. Prerequisites: List all events using the list command. Multiple events in the list.

    2. Test case: delete 1
      Expected: First event is deleted from the list. Details of the deleted event is shown by SGTravel.

    3. Test case: delete 0
      Expected: No event is deleted. Error details shown by SGTravel.

    4. Other incorrect delete commands to try: delete, delete x (where x is larger than the list size), delete 999999999999999999999 (where 999999999999999999999 is larger than an Integer)
      Expected: Similar to previous.

F.6. Editing an event in your plans

  1. Editing and changing the information of the events.

    1. Prerequisites: List all events using the list command. Multiple events in the list.

    2. Test case: e 1 Changi 20/09/25 25/09/25
      Expected: First event is edited from the list. Details of the new event is shown by SGTravel.

    3. Test case: e 1 xxxxzwyx 09/08/28 10/08/28
      Expected: No event is edited. Error details shown by SGTravel as there is no such location.

    4. Test case: e 0 Changi 27/08/25 14/10/25
      Expected: No event is edited. Error details shown by SGTravel.

    5. Other incorrect delete commands to try: e, e x Changi 20/09/25 25/09/25 (where x is larger than the list size), e Changi Mon Wed, e 1 09/08/28 10/08/28
      Expected: Similar to previous.

F.7. Recommend and adding travel plans

  1. Recommend new itineraries for users to use as a travel plan

    1. Test case: recommend itinerary between 21/04/20 and 25/04/20
      Expected: A recommended holiday plan is shown by SGTravel.

    2. Test case: recommend itinerary between 19/06/19 and 24/06/19
      Expected: No recommended holiday plan is shown. Error details shown by SGTravel as dates are already in the past.

    3. Test case: recommend itinerary between 21/04/20 and 25/09/20
      Expected: No recommended holiday plan is shown. Error details shown by SGTravel as SGTravel is unable to come up with such a long travel plan.

    4. Other incorrect itinerary commands to try: recommend between 21/04/20 and 25/09/20, recommend, recommend between and, recommend between, recommend and, recommend between and 12/12/21, recommend between 12/12/21 and
      Expected: Similar to previous.

    5. Prerequisites: Have used the recommend command at least once successfully upon application start up. And must not have used the addThisList command previously.

    6. Test case: addThisList myHolidayTrip
      Expected: MyHolidayTrip is added to the list of itineraries. The details of the trip is shown by SGTravel.

    7. Test case: addThisList
      Expected: No itineraries is added. Error details is shown by SGTravel as there is no name provided for the new itinerary.

F.8. Adding routes

  1. Add travelling routes for user to use whilst travelling

    1. Prerequisites: Must not add routes that has the same name as an existing one.

    2. Test case: routeAdd NUS to NTU
      Expected: Adds a route to the list of routes. The details of the route is shown by SGTravel.

    3. Test case: routeAdd
      Exepcted: No routes is added. Error details is show by SGTravel as there is no name provided for the new route.

    4. Prerequisites: List all routes using the routeListAll command. Multiples routes in the list. Must not add route node that is already existing in the route.

    5. Test case: routeNodeAdd 1 1 at 17009 by bus
      Expected: Route node is successfully added to the first route in the list. The location of the node is shown by SGTravel.

    6. Test case: routeNodeAdd 0 0 at 17009 by bus
      Expected: No route node is added. Error details is shown by SGTravel.

    7. Other incorrect add route commands to try: routeNodeAdd x 1 at 17009 by bus (where x is larger than the list size), routeNodeAdd 1 1 at, routeNodeAdd 0, routeNodeAdd 1 1, routeNodeAdd 1 1 17009, routeNodeAdd 1 1 at 17009 by moo, routeNodeAdd 1 1 at by
      Expected: Similar to previous.

F.9. Deleting routes

  1. Delete travelling routes while all routes are listed

    1. Prerequisites: List all routes using the routeListAll command. Multiples routes in the list.

    2. Test case: routeDelete 1
      Expected: First route is deleted from the list. Details of the deleted routes is shown by SGTravel.

    3. Test case: routeDelete 0
      Expected: No route is deleted. Error details shown by SGTravel.

    4. Other incorrect delete commands to try: routeDelete mopi, routeDelete x (where x is larger than the list size), routeDelete 999999999999999999999 (where 999999999999999999999 is larger than an Integer)
      Expected: Similar to previous.

    5. Prerequisites: List all routes using the routeListAll command. Multiples routes in the list. List all route nodes using routeShow x (where x is the index of the route) Listed routes must consist of multiple nodes.

    6. Test case: routeNodeDelete 1 1
      Expected: First route node of the first route node is deleted from the list. Details of the deleted node is shown by SGTravel.

    7. Test case: routeNodeDelete 0 1
      Expected: No route is deleted. Error details shown by SGTravel.

    8. Other incorrect delete commands to try: routeNodeDelete vrift, routeNodeDelete 1 x (where x is larger than the list size), routeNodeDelete 1 999999999999999999999 (where 999999999999999999999 is larger than an Integer)
      Expected: Similar to previous.

F.10. Saving data

  1. Dealing with missing/corrupted data files

    1. Removing all .txt files in the same directory

    2. Re-launch the app by double-clicking the jar file.
      Expected: All sample data should be in the application on start up.

    3. Removing or corrupting only the profile.txt files in the same directory

    4. Re-launch the app by double-clicking the jar file.
      Expected: Sample profile, favourite and itinerary data should be loaded and the other contents still remain intact.

    5. Removing or corrupting only the events.txt files in the same directory

    6. Re-launch the app by double-clicking the jar file.
      Expected: All sample data should be in the application on start up.

    7. Removing or corrupting only the routes.txt files in the same directory

    8. Re-launch the app by double-clicking the jar file.
      Expected: Sample route, profile, favourite and itinerary should be loaded and the other contents still remain intact.

    9. Removing or corrupting only the favourite.txt files in the same directory

    10. Re-launch the app by double-clicking the jar file.
      Expected: Sample favourite data should be loaded and the other contents still remain intact.

    11. Removing or corrupting only the itineraries.txt files in the same directory

    12. Re-launch the app by double-clicking the jar file.
      Expected: Sample itinerary data should be loaded and the other contents still remain intact.