By: AY1920S2-CS2103-W15-4      Since: Feb 2020      Licence: MIT

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.

1.3. Verifying the setup

  1. Run the clzzz.helper.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

1.4.2. Updating documentation to match your fork

After forking the repo, the documentation will still have the SE-EDU branding and refer to the AY1920S2-CS2103-W15-4/main repo.

If you plan to develop this fork as a separate product (i.e. instead of contributing to AY1920S2-CS2103-W15-4/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.

After setting up Travis, you can optionally set up coverage reporting for your team fork.

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.

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)

2. Design

2.1. Architecture

ArchitectureDiagram
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.

The .puml files used to create diagrams in this document can be found in the diagrams folder at docs/diagrams.

Main has two classes called Main and MainApp. It is responsible for,

  • At app launch: Initializes the components in the correct sequence, and connects them up with each other.

  • At shut down: Shuts down the components and invokes cleanup method where necessary.

Commons represents a collection of classes used by multiple other components. The following class plays an important role at the architecture level:

  • LogsCenter : Used by many classes to write log messages to the App’s log file.

The rest of the App consists of four components.

  • UI: The UI of the App.

  • Logic: The command executor.

  • Model: Holds the data of the App in-memory.

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

Each of the four components

  • Defines its API in an interface with the same name as the Component.

  • Exposes its functionality using a {Component Name}Manager class.

For example, the Logic component (see the class diagram given below) defines it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 2. Class Diagram of the Logic Component

How the architecture components interact with each other

The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command deletepet 1.

ArchitectureSequenceDiagram
Figure 3. Component interactions for deletepet 1 command

The sections below give more details of each component.

2.2. UI component

Written by Dylan Lim

UiClassDiagram
Figure 4. Structure of the UI Component
UiCalendarDiagram
Figure 5. Structure of the Calendar Component in UI
UiDisplayListDiagram
Figure 6. Structure of the DisplayList Component in UI

The UI consists of a MainWindow class that is made up of parts which can be classified broadly into 2 groups:

  • (Group 1) Parts that are always displayed at all times, which include CommandBox, FeedbackDisplay, StatusBarFooter, etc. These parts appear above MainWindow in the class diagram for UI.

  • (Group 2) Parts that are selectively displayed, depending on the command entered by the user. Only some of these parts will be shown at any one instance. These include the Calendar and DisplayList components, and OverallStatistics. These parts appear beneath MainWindow in the class diagram for UI.

All the UI classes, 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.

In addition, each UI part has been styled using CSS. The custom CSS styling rules are defined in multiple CSS stylesheets, which can be found in the src/main/resources/view/stylesheets folder.

The UI component does the following:

  • Executes user commands using the Logic component.

  • Listens for changes to Model data to update the UI with the modified data.

  • Handles a returned CommandResult to perform additional actions, e.g. show the help window, exit the program, display the appropriate part or component from Group 2, etc.

2.3. Logic component

LogicClassDiagram
Figure 7. Structure of the Logic Component
  1. Logic uses the PetTrackerParser 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 a pet).

  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 help to the user.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("deletepet 1") API call.

DeleteSequenceDiagram
Figure 8. Interactions Inside the Logic Component for the deletepet 1 Command
The lifeline for DeletePetCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

Written by Zhu Ruicong

2.4. Model component

ModelClassDiagram
Figure 9. Structure of the Model Component

The Model,

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

  • stores the Pet Store Helper data. Note that the Pet Tracker keeps track of a UniquePetList, which in turn maintains the Pet system, Schedule system and Inventory(FoodCollection) system.

  • exposes an ObservableList<Pet>, an ObservableList<Slot>, and an ObservableList<FoodCollection> that are unmodifiable and 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.

2.5. Storage component

Written by Chen Shenghao

StorageClassDiagram
Figure 10. Structure of the Storage Component

The Storage component,

  • can save UserPref objects in json format and read it back.

  • can save the Pet Tracker data in json format and read it back.

2.6. Common classes

Classes used by multiple components are in the seedu.address.commons package.

3. Implementation

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

3.1. Display switching feature

Written by Dylan Lim

3.1.1. Implementation

The display mechanism is facilitated by an ObservableList of DisplayItem s called filteredDisplayItems, which is an attribute stored in ModelManager.

Any class that is to be displayed as an item in a list implements the DisplayItem interface. This interface has a single method named getDisplaySystemType which returns the type of system to be displayed. In our program, the Pet, Slot, FoodCollection, and FoodAmountAndPet classes implement this interface.

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

Step 1. The user opens the app the the 1st time. A few things happen at start-up:

  • In Model (ModelManager), filteredDisplayItems is created, and is set to bind to filteredPets. This way, filteredDisplayItems contains all the elements of filteredPets, and any changes to filteredPets will also be automatically propogated to filteredDisplayItems.

  • In the MainWindow, a DisplayListPanel and a CalendarPanel are initialized. The DisplayListPanel is created with filteredDisplayItems as its backing list, while the CalendarPanel will be initialized with the List of Slot s obtained from logic.getPetTracker().getSlotList().

  • The resultDisplayPlaceholder component in the MainWindow is set to display the DisplayListPanel. As such, the user sees the list of pets as the initial display.

Step 2. The user executes display c to have the slots displayed in calendar view. Since filteredDisplayItems is not used for displaying the calendar, it is not changed when the display command calls Model#changeDisplaySystem. Upon receiving back the result of the command in MainWindow, resultDisplayPlaceholder is changed to display the CalendarPanel instead of the DisplayListPanel.

The following sequence diagrams shows how the display is switched to calendar:

DisplayCalendarSequenceDiagram0
Figure 11. Sequence diagram for switching display to calendar
DisplayCalendarSequenceDiagram1
Figure 12. Excecution of "display c" in Logic component
The lifeline for CommandResult, DisplayParser, and DisplayCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

Step 3. The user executes display s to have the slots displayed in a list. The display command calls Model#changeDisplaySystem, which causes filteredDisplayItems to re-binded to filteredSlots. Since the program should now display a list of items instead of a calendar, upon receiving back the result of the command in MainWindow, resultDisplayPlaceholder is changed to display the DisplayListPanel from CalendarPanel.

The following sequence diagrams shows how the display is switched to slots list view:

DisplaySlotSequenceDiagram0
Figure 13. Sequence diagram for switching display to slots list view
DisplaySlotSequenceDiagram1
Figure 14. Excecution of "display s" in Logic component
The lifeline for CommandResult, DisplayParser, and DisplayCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

The following activity diagram summarizes what happens in MainWindow#executeCommand upon the return of a command result:

DisplaySwitchingActivityDiagram

3.2. Schedule feature

Written by Zhao Mingjian

3.2.1. Implementation

The schedule feature uses instances of class Slot to organise bathing schedule for the pets. Each Slot contains a starting time of the bath, stored internally as a LocalDateTime, and the duration of the bath stored internally as a Duration. It stores a reference to the Pet in the ModelManager as specified by the user.

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

Step 1. The user launches the application with some pets stored (Garfield, for instance). ObservableList<Slot> in Schedule is currently empty.

Step 2. The user executes addslot n/Garfield t/1/4/2020 1200 d/120 command to create a Slot for Garfield. The AddSlotCommandParser calls parsePet() in SlotParserUtil, which then takes in the Model passed in to find a reference for the specific Pet with Model.getPet(). Prior to this stage, if the name string is invalid, a ParseException(MESSAGE_INVALID_PETNAME) will be thrown. Or the program cannot find the Pet in the model, a ParseException(MESSAGE_PET_DOES_NOT_EXIST) will be thrown. This ensures that every slot created actually points to an existing Pet found in PetTracker.

If parsePet() fails its execution, no new Slot is created.

Step 3. New Slot is created.

Step 4. The user now decides that this exact slot should be occupied by another pet, and decides to edit it, executing the editslot command.

Step 5. SlotParserUtil is used again to create a reference to a pet in the ModelManager.

Step 6. The user now decides that they need to see slots occupied by a particular pet on a particular date, executing the findslots n/Garfield t/1/4/2020 command.

Step 7. The FindSlotCommand reduces the two predicates* and pass it to ModelManager to create a filtered list of slots.

*The FindSlotCommand uses the following classes which both inherit Predicate<Slot> to search for the user specified slots:

  • SlotPetNamePredicate() — Internally stores the Name to search for, and compares it with getPet().getName().fullName.

  • SlotDateTimePredicate() — Internally stores the LocalDateTime to search for (Timing will be ignored), and compares it with getDate().

The activity diagram below is an illustration of the flow of events that happen in the model component when the steps above occurs.

FindSlotParserDiagram

Given below is an example usage scenario that demonstrates how the Schedule system integrates with the Pet system.

Step 1. The user launches the application with a pet stored: Garfield. A single instance of Slot occupies ObservableList<Slot> in Schedule, whose parameters are: petName: Garfield, dateTime: 11/4/2020 1200, duration: 40. Upon launch, a PetListChangeListener will be attached to internalList of UniquePetList.

Step 2. The user executes deletepet 1 command to delete Garfield from the UniquePetList. When user deletes the pet, function onChanged in PetListChangeListener is called. This function calls removeExcessSlot within the PetListChangeListener for each pet removed.

Step 3. removeExcessSlot calls remove in Schedule to remove slots based on the name of the removed pet.

Step 4. The user now have successfully removed Garfield and all the slots the pet used to occupy.

The sequence diagram below is an illustration of the flow of events that happen in the model component when the steps above occurs.

SlotAutoUpdateSequenceDiagram

3.2.2. Design Considerations

Aspect: How schedule stores the pets internally
  • Alternative 1 (current choice): Makes a reference to the the memory address in ModelManager .

    • Pros: When the pet is edited, it is easier to update the corresponding slot. Also reduces save file size and conserve system memory as there will be no duplicate information.

    • Cons: Harder to implement and test. Testing requires a sample list of pets to be instantiated first.

  • Alternative 2: Simply create a new Pet.

    • Pros: Easy to implement and test (A refactor of Pet), low coupling with Model.

    • Cons: Harder to synchronize with any changes in UniquePetList

3.3. Calendar feature

Written by Dylan Lim

3.3.1. Implementation

The class diagram for calendar-related components, as shown in Figure 5, “Structure of the Calendar Component in UI” previously, is reproduced in this section for convenience.

UiCalendarDiagram
Figure 15. Structure of the Calendar Component in UI

The construction of the calendar is accomplished by the construct() method in CalendarPanel. Based on the list of all slots in the system, it determines which type of CalendarRegion is to be placed at which position of the GridPane in CalendarPanel.

CalendarViewAnnotated
Figure 16. Respective CalendarRegion s marked out

In tha above annotated screenshot, rectangles with the following border color correspond to the following CalendarRegion (from left to right, on 7/4/2020):

  • Purple: CalendarDate. It is used to display the day and date of slots in the same row, and it always positioned to the left.

  • Green: CalendarSlot. It constitutes a single slot.

  • Yellow: CalendarBuffer. It acts a buffer between other regions, for padding purposes.

  • Blue: CalendarConflict. It represents a region of more than two or more conflicted slots.

The following activity diagram outlines the rough flow of calendarPanel#construct:

CalendarConstructActivityDiagram
Figure 17. Activity diagram for construction of calendar.
CalendarNewRegionHoldingActivityDiagram
Figure 18. Activity diagram for creating new CalendarRegion for slot(s) in holding.

CalendarRegion s are created sequentially, from left to right (increasing time of day), then top to bottom (increasing date). Each time a CalendarRegion is created, it is placed onto a GridPane based on the row index, and its starting time.

3.3.2. Design Considerations

Aspect: Construction of calendar upon addition / deletion / edition of slot(s)
  • Alternative 1 (current choice): Clear all components in CalendarPanel and redraw everything.

    • Pros: Relatively simpler to implement.

    • Cons: Might take a long time to draw if system has many slots.

  • Alternative 2: Modify only the component that is related the affected slot.

    • Pros: Better performance.

    • Cons: More complex implementation needed to determine and resolve conflicts, if any.

3.4. Inventory feature

Written by Zhu Ruicong

3.4.1. Implementation

The Inventory feature gives a summary of all the food items involved in a pet tracker system. It is supported by FoodCollection which resembles a collection of food of the same type and FoodCollectionList which is a list of these collections. A FoodCollectionList is stored as an attribute of UniquePetList for the following reasons:

  • The list of FoodCollection items associated with a UniquePetList can be directly derived from the UniquePetList itself.

  • Changes in FoodCollection occurs only if there is a change in UniquePetList#internalList.

Data stored in FoodCollectionList is exposed to ModelManager through UniquePetList and PetTracker as an unmodifiable ObservableList<FoodCollection>. ModelManager then passes the list of FoodCollection to UI for display as a list of DisplayItem when display i is called.

The following shows a typical usage scenario that involves the Display Inventory feature.

  • Step 1: The user launches the application. A UniquePetList is initialized in PetTracker, upon which a FoodCollectionList item is created to store the food data of the pets in the list(if it is an empty list, FoodCollectionList is also stores an empty list of FoodCollection)

  • Step 2: The user executes 'display i' command. The display command calls Model#ChangeDisplaySystem() and the i display type determines the displayed list is switched to ObservableList<FoodCollection>. Model#getFilteredDisplayList() then acquires the list and sends it to Ui unit for display.

  • Step 3: The user inputs a command that modifies the UniquePetList, e.g 'editpet 1 f/catfood:100'. UniquePetList#internalList is an instance of ObservableList<Pet>. Thus when it is modified, a ListChangeListener<Pet> is woken up and it calls UniquePetList#updateFoodCollectionList() to update the FoodCollectionList according to the modified Pet list.

The sequence diagram below is an illustration of the flow of events that happen in the logical component when Step 2 above occurs.

DisplayInventorySequenceDiagram
Figure 19. Interactions Inside the Logic Component for the display i Command

Note that there is no explicit initialization or update for FoodCollection in FoodCollectionList. FoodCollectionList is synchronized to the list of pet(UniquePetList#internalList) in the UniquePetList through ListChangeListener<Pet>, which is triggered whenever there is a change in the pet list. The sequence diagram below and the steps illustrates this process:

  • Step 1: UniquePetList passes its internalList(referred to as petList in the sequence diagram below).

  • Step 2: FoodCollectionList creates a new FoodCollectionList object temp and extracts temp#internalList.

  • Step 3: Replaces the content in the current internalList with the content in the extracted temp#internalist.

FoodCollectionListChangeSequenceDiagram
Figure 20. Interactions Inside Model Class when ListChangeListener<Pet> is triggered

3.4.2. Design Considerations

Aspect: Maintaining the collection of food in a pet tracker
  • Alternative 1(current choice): Maintains the list as an attribute of UniquePetList.

    • Pros: Easier to initialize and update the list.

    • Cons: Less extendability. Adding additional food items in inventory(independent of pet list) is difficult.

  • Alternative 2: Maintains a list of food collections separate from UniquePetList.

    • Pros: Higher Extendability that supports more independent operations of FoodCollection List.

    • Cons: More difficult to constantly update and maintain the food collection list should food list changes.

Aspect: Updating the collection of food when pet list is modified.
  • Alternative 1(current choice): Replace the entire list by a new food collection list created from the updated pet list.

    • Pros: Easy to implement and no adaptation is required for different types of modification of pet list.

    • Cons: Computationally intensive when there is a huge pet list.

  • Alternative 2: Modify FoodCollection affected by the command.

    • Pros: Less computationally intensive and more responsive given a large database.

    • Cons: Adaptations for each pet related commands is required since the food list can be affected in different ways.(e.g addition, modification, deletion)

3.5. Conflict feature

Written by Zhao Tianqi

3.5.1. Implementation

The conflict feature shows a list of all slots that has overlapping time period.

  • It makes uses of SlotConflictPredicate which implements SlotPredicate to filter slots that has a conflicting timing with some other slots.

  • If the screen is displaying shortlisted slots, conflics only shows slots with conflicts among the shortlisted list. Otherwise, it shows conflicts in the full slot list

  • The command automatically switch display to slots.

Below is a scenario of using conflicts command:

  • Step 1: The user inputs "conflicts" in the command line. The Logic component processes the input and creates a new instance of ConflictCommand

  • Step 2: The ConflictCommand access filteredSlots in Model, and create a new instance of SlotConflictPredicate with filteredSlots.

  • Step 3: Model filters slots that is conflicting among the filteredSlots using the SlotConflictPredicate.

  • Step 4: ConflictCommand changes currentDisplaySystemType in Model to DisplaySystemType.SCHEDULE such that the window will display shortlisted conflicting slots.

ConflictSequenceDiagram
Figure 21. Interactions Inside the Logic Component for the conflicts Command

3.5.2. Design Considerations

Aspect: Data structure to store slots and method of checking for conflicts:
  • Alternative 1 (current choice): Store slots in a list, with pairwise comparison between elements to find out conflicts

    • Pros: More straightforward in implementation

    • Cons: The performance might not be scalable. Algorithm does not minimize the computation time.

  • Alternative 2: Store slots in an interval tree

    • Pros: Searching for conflicting slots takes less time. The algorithm is more efficient.

    • Cons: Implementation of the data structure needs relatively high level of commitment.

3.6. Statistics

Written by Zhao Tianqi

3.6.1. Implementation

We are generating the overall statistics of Pet Store Helper and translate the data in a user-friendly manner.

  • OverallStats under UI component handles the translation of three sets of data: list of pets, schedule, and list of inventory.

  • Data in OverallStats is obtained from Logic.

  • The statistics displayed will automatically update if there is a change in any related information.

How we implemented overall statistic on UI:

  • The statistics for pets are shown in a form of pie chart, while the pets are grouped according to their species.

  • The schedule statistics is in the form of a timetable of recent 3 days. Each slot is represented as a shaded rectangle in the timetable.

  • The inventory data are generated from the list of pets, and grouped together by their names, such that users have a better understanding of overall food consumption. The list of inventory is represented as a bar chart.

Here is the process of how the overall statistics is displayed to the user:

  • Step 1: The user key in the command 'stats', then the Logic component processes the input and creates an instance of StatsCommand. The StatsCommand first calls Model#updateAll() to make sure that the model will update filteredPets, filteredSlots, and filteredFoodCollections to show the full list of Pet, Slot and FoodCollection in the PetTracker. After that, the StatsCommand calls Model#changeDisplaySystem(DisplaySystemType.STATISTICS) such that the application will later switch the window to show OverallStats. In the Ui component, there is a new instance of OverallStats created, while Model#getFilteredPets, Model#getFilteredSlots, and Model#getFilteredFoodCollections are passed in the instance for processing. OverallStats then generates diagrams according to the data passes in.

  • Step 2: The user inputs a command that modifies the UniquePetList, e.g 'editpet 1 s/cat'. The CommandResult of any EditPetCommand has type with DisplaySystemType.NO_CHANGE. The Ui component identifies the type in the CommandResult and then refresh the window if it is NO_CHANGE. In this case, the window will refresh and create another new instance of OverallStats with the updated list of pets. Therefore the window always shows statistics of the most updated list of pets, slots and food collections.

Following is the sequential diagram of the command stats

StatsLogicSequenceDiagram
Figure 22. Interactions Inside the Logic Component for the stats Command
StatsUiSequenceDiagram
Figure 23. Interactions Inside the Ui Component for the stats Command

3.6.2. Design Considerations

Aspect: How to update diagrams when there are changes
  • Alternative 1 (current choice): Refresh the screen

    • Pros: Easy to implement and only need to refer to model to get data.

    • Cons: Takes time to process the whole lists of data even though there is only one small change in one of the item (pets, slots or foo collection). This approach might be time consuming when the data size is too large.

  • Alternative 2: Make use of Listener to detect change in UniquePetList and make changes accordingly

    • Pros: Avoid unnecessary processing of data. e.g. The diagrams need not be regenerated when there is no change in pet species, pet food, and recent schedule.

    • Cons: More complicated implementation. There might be more coupling between OverallStats under Ui component and Listener class under Model component.

3.7. Back up and load feature

Written by Chen Shenghao

3.7.1. Implementation

The back up/load mechanism is facilitated by Storage.

Storage is extended to implement the following operations:

  • savePetTracker#(ReadOnlyPetTracker, LocalDateTime) — calls savePetTracker#(ReadOnlyPetTracker, Path) after converting LocalDateTime to Path

  • savePetTracker#(ReadOnlyPetTracker, Path) — saves pet tracker data to specified path

The back up/load mechanism follows the exact same steps taken when data is saved after each operation and read at initialization. This ensures maximum compatibility and reduces the need for additional code.

backup should never fail unless the saving of data after every operation also fails. For 'load', there are two additional caveats, the specified file should exist and be in the correct format. This is unlike the reading of data at initialization which simply resorts to an empty file instead if these issues occur.

The following sequence diagram shows how the backup operation works:

BackUpSequenceDiagram

The following sequence diagram shows how the load operation works:

LoadSequenceDiagram

3.7.2. Design Considerations

Aspect: How back up & load executes
  • Alternative 1 (current choice): Executed by Command, following general pattern for Command

    • Pros: Easy to implement, able to rely on established pattern for Command.

    • Cons: Storage is exposed to classes in the parser package, which should not have access to its methods

  • Alternative 2: Executed by LogicManager, which already saves data to hard disk after every operation

    • Pros: Storage does not have to be passed to classes in the parser package

    • Cons: Requires passing instruction back to LogicManager, actual operation performed after end of Command#execute(), unable to obtain reliable success/error message.

3.8. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Section 3.9, “Configuration”)

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level

  • Currently log messages are output through: Console and to a .log file.

Logging Levels

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

  • WARNING : Can continue, but with caution

  • INFO : Information showing the noteworthy actions by the App

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

3.9. Configuration

Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json).

4. Documentation

We use Asciidoc for writing documentation.

We chose Asciidoc over Markdown because Asciidoc, although a bit more complex than Markdown, provides more flexibility in formatting.

4.1. Editing Documentation

We used Gradle to render .adoc files locally to preview the end results of edits. Also, we downloaded the AsciiDoc plugin for Intellij, which allowed use to preview the changes made to the .adoc files in real-time.

4.2. Editing Diagrams

We used the PlantUML plugin for Intellij to create and update the UML diagrams.

4.3. Publishing Documentation

We used Travis for deployment of Github Pages for publishing documentation.

4.4. Converting Documentation to PDF format

We use Google Chrome for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.

4.5. Site-wide Documentation Settings

The build.gradle file specifies some project-specific asciidoc attributes which affects how all documentation files within this project are rendered.

Attributes left unset in the build.gradle file will use their default value, if any.
Table 1. List of site-wide attributes
Attribute name Description Default value

site-name

The name of the website. If set, the name will be displayed near the top of the page.

not set

site-githuburl

URL to the site’s repository on GitHub. Setting this will add a "View on GitHub" link in the navigation bar.

not set

site-seedu

Define this attribute if the project is an official SE-EDU project. This will render the SE-EDU navigation bar at the top of the page, and add some SE-EDU-specific navigation items.

not set

4.6. Per-file Documentation Settings

Each .adoc file may also specify some file-specific asciidoc attributes which affects how the file is rendered.

Asciidoctor’s built-in attributes may be specified and used as well.

Attributes left unset in .adoc files will use their default value, if any.
Table 2. List of per-file attributes, excluding Asciidoctor’s built-in attributes
Attribute name Description Default value

site-section

Site section that the document belongs to. This will cause the associated item in the navigation bar to be highlighted. One of: UserGuide, DeveloperGuide, LearningOutcomes*, AboutUs, ContactUs

* Official SE-EDU projects only

not set

no-site-header

Set this attribute to remove the site navigation bar.

not set

4.7. Site Template

The files in docs/stylesheets are the CSS stylesheets of the site. You can modify them to change some properties of the site’s design.

The files in docs/templates controls the rendering of .adoc files into HTML5. These template files are written in a mixture of Ruby and Slim.

Modifying the template files in docs/templates requires some knowledge and experience with Ruby and Asciidoctor’s API. You should only modify them if you need greater control over the site’s layout than what stylesheets can provide. The SE-EDU team does not provide support for modified template files.

5. Testing

5.1. Running Tests

There are two ways to run tests.

Method 1: 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, you can right-click on a test package, test class, or a test and choose Run 'ABC'

Method 2: Using Gradle

  • Open a console and run the command gradlew clean test (Mac/Linux: ./gradlew clean test)

5.2. Types of tests

We have three types of tests:

  1. Unit tests targeting the lowest level methods/classes.
    e.g. clzzz.helper.commons.util.StringUtilTest

  2. Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
    e.g. clzzz.helper.storage.StorageManagerTest

  3. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
    e.g. clzzz.helper.logic.LogicManagerTest

6. Dev Ops

6.1. Build Automation

We use Gradle for build automation.

6.2. Continuous Integration

We use Travis CI and AppVeyor to perform Continuous Integration.

6.3. Coverage Reporting

We use Coveralls to track the code coverage.

6.4. Documentation Previews

When a pull request has changes to asciidoc files, you can use Netlify to see a preview of how the HTML version of those asciidoc files will look like when the pull request is merged.

6.5. Making a Release

Here are the steps to create a new release.

  1. Update the version number in 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.6. Managing Dependencies

Project dependencies are managed by Gradle, and are not included in this repository.

List of dependencies:

Appendix A: Product Scope

Written collaboratively, collated by Zhao Mingjian

Target user profile:

  • has a need to manage their pet store with a variety of animals and features

  • has a need to make schedules for pet grooming and ensure no-conflict in the scheduling

  • prefer desktop apps over other types

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

Value proposition: manage pets, pet consumables and schedule faster than a typical mouse/GUI driven app

Appendix B: User Stories

Written collaboratively, collated by Zhao Mingjian

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

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

* * *

user

key in a new pet, enter its breed, age, size and food consumption.

keep track of the pets I have in my store and their details

* * *

user

delete a pet

remove pets that I have sold or no longer taking care of

* * *

user

find a pet by name, species or tags

locate details of the pets without having to go through the entire list

* * *

user

keep track of pet food, cleaning products and other consumables

not run low on items needed to keep the pets healthy

* * *

user

schedule when to bathe my own (in-store) pets

avoid clashes in bathing schedule

* * *

user

view the schedule and see which slots are available to bathe the customers' pets

avoid clashes in bathing schedule

* *

user

see which free slots are available for grooming

avoid clashes in grooming

* *

user

view a statistical summary of the pets, stocks and schedule

handle logistics of the store more efficiently

* *

user

add photos for the pets in store to illustrate

easier to make a mental link between the actual pets in the store and the names

* *

user

keep track of the cost and revenue generated by each pet

so to buy the more popular ones in next restock

Appendix C: Use Cases

Written by Zhao Mingjian

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

Use case: Delete pet

MSS

  1. User requests to list pets

  2. PSH shows a list of pets

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

  4. PetTracker deletes the pet

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. PSH shows an error message.

      Use case resumes at step 2.

Use case: Removing a bathing slot

MSS

  1. User requests to show schedule

  2. PSH shows schedule of that day

  3. User requests of delete a slot at a specified timing

  4. PSH deletes the slot

    Use case ends.

Extensions

  • 2a. There is currently conflict in the scheduling (i.e. One slot begins before the previous one ends), the latter slot is shown in red.

    Use case resumes at step 2

  • 3a. The given timing does not exist.

    • 3a1. PSH shows an error message.

      Use case resumes at step 2.

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 500 pets + items in total 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.

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Appendix F: Instructions for Manual Testing

Applied from AB3 by Zhao Mingjian

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 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 pets and slots. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location is retained.

F.2. Switching display

  1. Change display of the program

    1. Prerequisites: Program is pre-populated with the sample data of pets and slots.

      1. Test case: display s
        Expected: Program display is changed to show list of slots.

      2. Test case: display c
        Expected: Program display is changed to show slots in calendar view.

      3. Test case: display i
        Expected: Program display is changed to show inventory.

      4. Test case: display p
        Expected: Program display is changed to back show list of pets.

      5. Test case: display x
        Expected: Program display does not change. Error message shown.

      6. Other incorrect display commands to try: display, display I, Display i
        Expected: Similar to previous.

F.3. Adding a pet

  1. Adding a pet while all pets are displayed

    1. Prerequisites: Program is showing list of pets to (run display p)

      1. Test case: addpet n/Alice g/female b/13/11/2015 s/dog f/Dog Food: 10 t/cute
        Expected: A new pet "Alice" is added to the end of the pet list. Details of the newly added pet shown above the list of pets.

      2. Test case: addpet n/Alice g/female b/24/12/2015 s/rabbit f/Rabbit Food: 15 t/fluffy
        Expected: No pet is added. Error details shown above the list of pets.

      3. Other incorrect addpet commands to try: addpet n/Bubbles g/helicopter b/8/10/2015 s/cat f/Cat Food: 15 t/white, addpet n/Bubbles g/female b/78/10/2015 s/cat f/Cat Food: 15 t/white
        Expected: No pet is added, with the relevant and appropriate error details shown above the list of pets.

F.4. Deleting a pet

  1. Deleting a pet while all pets are displayed

    1. Prerequisites: Display all pets using the display p command. Multiple pets in the display.

    2. Test case: deletepet 1
      Expected: First pet is deleted from the list. Details of the deleted pet shown above the list of pets. Slots that the pet used to occupy is also deleted.

    3. Test case: deletepet 0
      Expected: No pet is deleted. Error details shown above the list of pets. Slots remain the same.

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

F.5. Adding a slot

  1. Adding a slot while all slots are displayed

    1. Prerequisites: Program is showing list of slots to (run display s). Program is pre-populated with the sample data of pets and slots.

      1. Test case: addslot n/Bob t/6/4/2020 1000 d/60
        Expected: A new slot is added to slot list. Details of the newly added slot shown above the list of slots. Because it is the earliest of all slots, it appears at the 1st slot in the list.

      2. Test case: addslot n/Cindy t/67/4/2020 1000 d/60
        Expected: No slot is added. Error details shown above the list of slots.

      3. Other incorrect addslot commands to try: addslot, addslot n/Duke t/6/4/2020 1130 d/60, addslot n/Cindy t/6/4/2020 2300 d/120
        Expected: No slot is added, with the relevant and appropriate error details shown above the list of slots.

F.6. Deleting a slot

  1. Deleting a slot while all slots are displayed

    1. Prerequisites: Program is showing list of slots to (run display s). Program is pre-populated with the sample data of pets and slots.

      1. Test case: deleteslot 1
        Expected: First slot is deleted from the list. Details of the deleted slot shown above the list of slots.

      2. Test case: deleteslot 0
        Expected: No slot is deleted. Error details shown above the list of slots.

      3. Other incorrect deleteslot commands to try: deleteslot, deleteslot x (where x is larger than the list size)
        Expected: Similar to previous.

F.7. View conflicted slots

  1. Viewing slots with conflicts

    1. Prerequisites: Program is pre-populated with the sample data of pets and slots.
      Initial view: On Mon 6/4/2020, there is a single slot occupied by "Gru" from 13:00 - 14:40 (as per the sample data). It is not in conflict.

      1. Test case: conflicts
        Expected: Program successfully shows no slots with message "0 slots listed!", as the sample data has no slots in conflict.

      2. Test case: addslot n/Bob t/6/4/2020 1400 d/60
        Expected: A new slot is added to slot list. Details of the newly added slot shown above the list of slots. This newly added slot and the slot occupied by Gru are now marked as "[CONFLICT]"

      3. Test case: conflicts
        Expected: Program successfully shows only the two slots on 6/4/2020 with message "2 slots listed!"

F.8. Saving data

  1. Dealing with missing/corrupted data files

    1. Prerequisites: List of pets is well populated.

    2. Test case: changing pet name manually to an invalid name in pettracker.json
      Expected: Data is not loaded. Start with an empty Pet Tracker.

    3. Test case: changing pet name of a slot manually to pet that does not exist in pettracker.json
      Expected: Data is not loaded. Start with an empty Pet Tracker.