PROJECT: Incident Management System


Overview

Incident Management System is a desktop incident Manager application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.

Summary of contributions

  • Major enhancement: Implemented Incident Search and Listing

    • What it does: allows the user to list a filtered or unfiltered list of incidents dependent on commands and parameters used.

    • Justification: This feature improves the product significantly because a user can handle large sets of data efficiently through searching for relevant incident reports based on the parameters, to prepare these incidents for filling and submission, or for reviewing.

    • Highlights: This enhancement is easily extendable to various data within the Incident model, and has been designed to easily accommodate additional parameters. It required an in-depth analysis of design alternatives, as the implementation was challenging, requiring a good understanding of how to parse multiple parameters with multiple words for each parameter, and consideration of the abstraction of said parameters.

    • Credits: Extended from the FindPerson command in the initial AB3

  • Major enhancement: Implemented initial Incident GUI layout [#59]

    • Summary: Modified the GUI for a new panel for displaying Incidents and the relevant information in the initial implementation.

    • Highlights: This enhancement required an in-depth understanding of the GUI design and the JavaFX library, as well as great familiarity with the initial Incident class. Further abstraction of the Incident class was also executed to ensure SLAP principlese were adhered to.

  • Code contributed: [View on RepoSense]

  • Other contributions:

    • Add Netlify bot to repo and add badge to README [#18]

    • Remove irrelevant Developer Guide sections [#128]

    • Make minor aesthetic and usability edits to the GUI [#289]

Contributions to the User Guide

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

Listing all incidents: list-i

Shows a list of all incidents in the Incident Manager
Format: list-i

Example usage of the list-i command returning all incidents within the system: image::listIncidentsCommand.png[width="500"]

  • The listing returns all incidents, inclusive of all incomplete drafts and complete drafts and submitted incident reports

  • Adding any keywords or parameters will result in an error. Only list-i is allowed.

Locating an incident report using incident ID, operator name or description keyword: find-i

Finds incidents containing the relevant specified parameters. Possible Parameters: id/, op/, desc/, self

Different Parameters

By Operator Name
Format: find-i op/<OPERATOR KEYWORD [MORE_KEYWORDS]>
Lists all incidents whereby the operator name contains any of the given keywords

  • Accepts multiple search terms for the parameter, searching for any match with any search term

  • Example of the find-i command with one word under parameter op/, returning all incidents whereby the operator name matches alex (case-insensitive):

findIncidentsCommandWithOperatorParameter
  • Example of the find-i command with multiple words under parameter op/, returning all incidents whereby the operator name matches irfan or bernice (case-insensitive):

findIncidentsCommandWithOperatorsParameter

By Description
Format: find-i desc/<DESCRIPTION KEYWORD [MORE_KEYWORDS]…​>
Lists all incidents whereby the incident description contains any the given keywords

  • Example of the find-i command with parameter desc/, returning all incidents whereby the description contains either keyword fire or arson:

findIncidentsCommandWithDescriptionParameter

By ID
Format: find-i id/KEYWORD
Lists all incidents whereby the incident ID is an exact match with the given keyword

  • Requires an exact ID match, only accepts one ID

  • Example of find-i command with parameter id/ returning all incidents whereby the ID matches 0620150001 exactly:

findIncidentsCommandWithIdParameter

Self-Search
Format: find-i self
Lists all incidents whereby the operator name matches the logged-in user’s name.

  • Example of executing find-i self to list all incidents:

findIncidentsCommandWithSelfParameter
  • Requires an exact name match with the logged-in user’s name

  • The search is case insensitive and punctuation insensitive. e.g dave and Dav-e will match Dave

  • The search returns all incidents found regardless of state (incomplete and complete drafts, submitted incidents)

  • Multiple words can be taken for the operator and description parameter. The command returns all incidents which contains at least one of the words within the parameter

  • The ID parameter requires an exact match

  • Multiple parameters can be searched, returning incidents whereby all parameters match

  • Only full words/IDs will be matched e.g. Fir will not match Fire

Examples:

  • ID Match: find-i id/0920160001
    Returns Incident #0920160001

  • Multiple Parameters: find-i op/Dave desc/fire
    Returns any incidents whereby the operator’s name contains 'Dave' and the description contains 'fire'

  • Multi-word Parameter Search: find-i op/Alex Bernice
    Returns any incidents whereby the operator’s name contains either 'Alex' or 'Bernice'

  • Self-Search: find-i self
    Returns any incidents whereby the operator’s name matches the logged-in operator’s name

If multiple keywords of the same prefix are provided by user, the command will take the last valid parameter of each prefix.
For example find-i desc/arson desc/fire op/alex op/charlotte is equivalent to find-i desc/fire op/charlotte.
Rationale: user need not waste time backspacing if an input was keyed in wrongly.

Future Extensions

  • Find Command filters search results by the following:

    • Incident status

    • DateTime

    • District number of Incident

    • Vehicle Type of Vehicle involved

    • Vehicle Number of Vehicle involved

Contributions to the Developer Guide

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

Incident Finding and Listing feature

Implementation

The incident search mechanism features a set of different types of searches that a user could utilise to list out all related incidents, regardless of status of report. Further documentation on the commands available in this set can be found in the User Guide. It is facilitated by the List Incidents Command and the Find Incidents Command. To differentiate the reports by statuses, the Incident Filling and Submission feature cover these capabilities already, as mentioned earlier.

Overview of Running Find Incidents and List Incidents commands
  1. Listing all - Displays all incidents in Model, taking in no parameters
    eg. list-i

  2. Finding based on parameters- Displays all incidents in Model based on matching parameters indicated

    • ID - Displays all incidents with exact matches in IncidentId incidentId in Incident incident, within Model
      eg. find-i id/0620150001

    • Description - Displays all incidents with keyword(s) contained within the Description description in Incident incident, within Model+ eg. find-i desc/traffic

    • Operator - Displays all incidents with keyword(s) contained within the name of the Person operator in Incident incident, within Model
      eg. find-i op/bill

    • Operator - Displays all incidents with the name of the Person operator in Incident incident matching the logged-in user’s name exactly, within Model
      eg. find-i self

Activity Diagram for illustration:

FindIncidentsCommandActivityDiagram
  • list-i does not take any parameters, and returns errors if parameters are added to the command

  • Search by keywords is case-insensitive for convenience and ease of use

  • Each parameter in find-i search commands can be combined in any order and quantity, returning only results that return a match in all predicates indicated

  • Each parameter in find-i accepts multiple keywords, and searches for matches containing any or all of these keywords

  • find-i can take in multiple parameters of the same prefix, taking only the last valid parameter of each prefix

Overview of Implementation of Find Incidents and List Incidents commands
  • The incident search mechanism is facilitated by ModelManager, which implements abstract class Model.

  • ModelManager contains a FilteredList<Incidents> filteredIncidents, which internally stores the list of displayed incidents in the GUI.

  • filteredIncidents implements the following key methods utilised in the List Incidents and Find Incidents command:

    • updateFilteredIncidentsList(Predicate<Incident> predicate) - Updates the stored filtered incidents list with the new predicate

    • getFilteredIncidentsList() - Returns full list of all incidents

Implementation of List Incidents Command

The following sequence diagram shows how the list-i command works when list-i is called:

ListIncidentsSequenceDiagram
Figure 1. Sequence Diagram of the List Incidents Command running list-i
  • The LogicManager passes the command to IncidentManagerParser when running command execute("list-i"), which instantiates a ListIncidentsCommand with Predicate<Incident> PREDICATE_SHOW_ALL_INCIDENTS that returns all incidents regardless of state.

  • The LogicManager then calls ListIncidentsCommand#execute(), which runs ListIncidentsCommand#handleAllIncidents(), a private method within ListIncidentsCommand.

  • handleAllIncidents() runs Model#updateFilteredIncidentList() with Predicate<Incident> PREDICATE_SHOW_ALL_INCIDENTS, which always evaluates to true.

  • This Predicate<Incident> is passed to FilteredList<Incident> filteredList, as a parameter to run the method filteredList#setPredicate().

  • The list of visible incidents is updated.

  • CommandResult commandResult is returned to the LogicManager to log the success/failure of the method.

Implementation of Find Incidents Command

Next, we will look at an example in which the user calls find-i to look for incidents written by an operator whose name contains Alex.

The execution of this method is a little more complex. The following sequence diagram shows how the find-i command identifies the keyword and flag, and returns related incidents:

FindIncidentsSequenceDiagram
Figure 2. Sequence Diagram of the List Incidents Command running find-i op/alex desc/pmd

The key steps are as follows:

  • IncidentManagerParser passes the arguments to FindIncidentsCommandParser to parse the keywords after the prefixes desc/ and op/ in the command.

  • FindIncidentsCommandParser utilises ParserUtil to parse out the keywords, in this case "pmd" for the description prefix and "alex" for the operator name prefix

  • FindIncidentsCommandParser creates a new instance of DescriptionKeywordsPredicate to be added to List<Predicate<Incident>> predicateArr

  • It does the same for NameKeywordsPredicate and any other valid search parameters identified

  • A new instance of FindIncidentsCommand is passed the new List<Predicate<Incident>> predicateArr, combining the predicates using combinePredicates() to account for all predicates in the List

  • As before, the LogicManager calls FindIncidentsCommand#execute(), which causes the Model to run Model#updateFilteredIncidentList(predicate) using the combined predicate stored in FindIncidentsCommand.

  • This filters and updates the list in FilteredList<Incident> filteredList, by running filteredList#setPredicate(predicate) with the passed combined predicate.

  • Upon updating the list similar to the List Command above, FindIncidentsCommand also calls Model#getFilteredIncidentList() to return ObservableList<Incident>. It obtains the size of this list, and returns it in CommandResult commandResult.

Design Considerations

Aspect: How multiple keywords with the same prefixes are processed
  • Current choice: Keywords are read as an array into Predicate<Incident> to be fed into a stream to search for any match with any of the keywords

    • Pros: Requires lesser code. Abstraction of checking matches in keywords remains within the Predicate<Incident> class
      Able to separate the handling of multiple keywords with the same prefixes, in Predicate<Incident>, with the handling of different prefixes, done in FindIncidentsCommand

    • Cons: More difficult to implement

  • Alternative: Every new predicate takes in a keyword and returns all results with exact matches, to be combined in FindIncidentsCommand as separate predicates

    • Pros: Easier to implement

    • Cons: May lead to confusing implementation of the combinePredicates() function for returning results which have all prefixes fulfilled, but only require at least one match in keywords fulfilled for each prefix

Aspect: How multiple different prefixes are processed
  • Current choice: FindIncidentsCommandParser checks for the presence of every possible prefix and creates a new Predicate<Incident> to be added to predicateArr to be combined in FindCommandParser

    • Pros: Intuitive to use, majorly improves the Find Incidents Command
      Utilises Inheritance and Polymorphism principles from OOP to combine the DescriptionKeywordsPredicate and NameKeywordsPredicate and IdKeywordsPredicate, inherited from Predicate<Incident> class, all stored in predicateArr

    • Cons: Challenging to implement.

  • Alternative: FindIncidentsCommandParser returns a new FindCommand for every prefix identified

    • Pros: Less complicated

    • Cons: More code needed
      Does not follow the Command interface abstracted from, in that multiple FindCommands will run execute() at each time, returning multiple CommandResults

  • Alternative: Do not allow multiple different prefixes to be processed

    • Pros: Easy to implement, less challenging.

    • Cons: Very limited capabilities of Find Incidents Command if multiple parameters cannot be accepted at once.

Aspect: How predicate is added to FindIncidentsCommand
  • Current choice: FindIncidentsCommand class calls Model to create a new Predicate based on search string.

    • Pros: Abstracts the creation and management of predicates to the Model. Maintains Separation of Concerns principle.

    • Cons: Requires greater level of coupling between classes.

  • Alternative: FindIncidentsCommand or FindIncidentsCommand directly create Predicate based on search string.

    • Pros: Less dependencies within the parser class.

    • Cons: Breaks Model-View-Controller pattern.

Aspect: How user keys in find-i keywords
  • Current choice: Parse user input after flag (eg. op\ or desc\)

    • Pros: Easy to implement.

    • Cons: Have to parse keyword from command and flag, user has to follow style of flag for successful search.

  • Alternative: Prompt user for search input

    • Pros: Separates command from keyword for ease of reading and parsing.

    • Cons: Difficult to implement multi-command execution.

Aspect: How listing all incidents is called
  • Current choice: Utilise separate command list-i

    • Pros: Intuitive to use.

    • Cons: Similar code under different command, leading to code reuse.

  • Alternative: Utilise find-i command (eg. find-i unfiltered)

    • Pros: Less overlap in code.

    • Cons: Unintuitive to the user as no search is being made, even more keywords to remember.

Aspect: Whether the search only returns full word matches
  • Current choice: Search only returns full word matches

    • Pros: Intuitive to use.

    • Cons: Unable to return similar words eg. search for "desc/fire" does not return description containing "fires"

  • Alternative: Search returns all keyword-containing matches

    • Pros: Able to account for similar words to be returned.

    • Cons: Unintuitive to return certain longer words from certain searches eg. "desc/the" returns descriptions containing "weather"
      Faster search as code is less inefficient