Project: Mobilize

Mobilize is a desktop application built to help people organize their contacts and tasks. It is primarily written in Java and uses the Object Oriented Paradigm. The user interacts with it using a CLI, and it has a GUI created with JavaFX.

Code contributed: [Functional code] [Test code]

Enhancement Added: Add Task

External behavior


Start of Extract [from: User Guide]

Adding tasks: add

Whether it is a host of pending submissions, a get-together or a multiday program, adding it to Mobilize can easily help you keep track of it all.

Use prefixes:

  • on or by or from to denote the date in which the task is due to be completed or when a task is set to begin.

  • at to denote the starting time of the task and to to denote the ending time of the task.

Format: add DESCRIPTION [by/on/from DEADLINE] [at START TIME to END TIME] [t/TAG]…

  • The task added is automatically sorted according to the deadline. With expired tasks coming first, follow by tasks with deadline that are coming up next and lastly tasks without deadline.

  • If you want to enter a date, it must follow the MONTH-DAY-YEAR format.

  • Words or phrases like 'tomorrow', 'yesterday', 'Tue', 'Tuesday', 'day after tomorrow', '5 Dec', '5/9/2017' and 'the 8th of Jan' are all valid dates.

  • 2 pm, 2:00, 02:00, 1400 are all valid times.

  • Deadlines and times are optional items.

Examples:

  • add important meeting on 17 Nov at 9 am to 11 am Sets the description to important meetin, deadline to the 15th of November in the current or upcoming year and times to 09:00 and 11:00 respectively.

  • add recess week from tomorrow by 15 Sep Sets the description to recess week start date to the next day and the deadline to the 15th of September in the current or upcoming year.

  • add get groceries Sets the description to get groceries leaving the other parameters blank.

Constraints

Note the following constraints when adding tasks:

  • Dates must be preceded by one of the prefixes.

  • Invalid dates (such as 32 Jan) will not be flagged by Mobilize and will be replaced in a task card by an incorrect date (such as Feb 1 '48).

  • Invalid times (such as 200 and 3 (without am or pm)) will not be flagged by Mobilize and will be replaced in a task card by an incorrect time (such as 00:00).

  • If one time (e.g. start time) is invalid while the other (e.g. end time) is valid, the incorrect time will be ignored by Mobilize and only the correct time will be added as a single time for the task.

  • If a prefix is used and no subsequent parameter is specified, the task will be added without the associated item, e.g. add meeting on will set an empty deadline for the task.

  • Different deadline prefixes are not allowed in the same command, e.g. add submit assignment by friday on friday will result in an error.

  • Multiple values for a single deadline prefix for a single task will only result in the first value being selected, e.g. the command add finish quiz by friday saturday will set the deadline to the upcoming Friday only and not Saturday.

  • Multiple copies of the same deadline prefix with multiple values each will result in the last value being selected, e.g. the command add finish quiz by friday by saturday will set the deadline to the upcoming Saturday only and not Friday.

  • Multiple values for each time prefix is not allowed and will result in incorrect time values being registered for a task card without warning.

End of Extract


Justification

The Add Task command was the first step in expanding Mobilize’s capabilities. In allowing users to add pending tasks to their application, Mobilize has adopted the responsibility of organizing its user’s life.

The Add Task uses a Natural Language Parsing Library, PrettyTime, in order to make it more user-friendly, by allowing task dates to be set using natural language dates and times. This makes the command format of the application less rigid and increases its appeal to potential users.

Implementation


Start of Extract [from: Developer Guide]

AddTask mechanism

The Add Task mechanism is facilitated by the AddTaskCommand class which inherits from the UndoableCommand class. It allows for the addition of new tasks to the application with a starting date and a deadline. Both dates are optional and may be omitted.

Suppose the user has just executed the add command using:

`add CS assignment by tomorrow at 23:59`.

At first, the command is called is called, which causes the LogicManager to pass the command string into the parseCommand() function of the AddressBookParser class. This, in turn, determines that the AddTask feature is being evoked and calls the AddTaskCommandParser to parse the String and separate it into three components: Description, StartDate and Deadline. These are validated and formatted by the Task class and returned as a Task object to be used to call the AddTaskCommand class. The LogicManager then calls the executeUndoableFunction() in the AddTaskCommand class which creates and returns a CommandResult object.

The following sequence diagram summarizes how this operation works:

AddTaskCommandSequenceDiagram

All parameters except the description are set to be optional wherein an empty string is added when no value is stated by a user.

Natural Language Input is processed by the PrettyTime NLP, in the ParserUtil class in the following way:


public static Optional<EventTime[]> parseEventTimes(Optional<String> dateTime) throws IllegalValueException {
    requireNonNull(dateTime);
    if (dateTime.isPresent()) {
        List<DateGroup> dateGroup = new PrettyTimeParser().parseSyntax(dateTime.get().trim());
        if (dateGroup.isEmpty()) {
            throw new IllegalValueException(DateTimeValidator.MESSAGE_TIME_CONSTRAINTS);
        }
        List<Date> dates = dateGroup.get(dateGroup.size() - 1).getDates();
String endTime = DateTimeFormatter.formatTime(dates.get(dates.size() - 1));
String startTime = dates.size() > 1 ? DateTimeFormatter.formatTime(dates.get(dates.size() - 2)) : "";
        return Optional.of(new EventTime[]{new EventTime(startTime), new EventTime(endTime)});
    } else {
        return Optional.empty();
    }
}
/**
 * Parses a {@code String naturalLanguageInput} using PrettyTime NLP, into a {@code Date}.
 * @throws IllegalValueException if the date cannot be parsed from the phrase or if the given date is invalid.
 */
public static Date parseDate(String naturalLanguageInput) throws IllegalValueException {
    List<DateGroup> dateGroup = new PrettyTimeParser().parseSyntax(naturalLanguageInput.trim());
    if (dateGroup.isEmpty() | !DateTimeValidator.isDateValid(naturalLanguageInput)) {
        throw new IllegalValueException(DateTimeValidator.MESSAGE_DATE_CONSTRAINTS);
    }
    List<Date> dates = dateGroup.get(dateGroup.size() - 1).getDates();
    return dates.get(dates.size() - 1);
}

Design Considerations

Aspect: Creating classes for StartDate and Deadline
Alternative 1 (current choice): Use Strings to contains the StartDates and Deadlines.
Pros: * Easy to assign an empty string to the instance variable so that the dates are optional and do not show up in the UI. In addition, dates can be stored after being formatted to a more user-friendly style (i.e. Tue, Oct 24, '17). This prevents dates from becoming confusing (i.e. distinguishing between dd-MM-yyyy and mm-DD-yyyy)
Cons: Dates are not stored the correct format. This makes it more difficult to compare and validate later on as they have to be reformatted into Date objects every time. For example, in validating whether a start date is indeed before a deadline.
Alternative 2: Use java.time.LocalDate class.
Pros: Dates are in correct format. Validation becomes easier and reformatting is not necessary.
Cons: LocalDate cannot be initialized when no input is given. Using a phony value of 00:00 or the current date might cause errors in validation and sorting of dates.
Alternative 3: Use java.util.Optional class for instance variables along with the LocalDate class.
Pros: Easy to understand reasoning, for new developers looking to contribute. Easy to format specifically for display (once) and allows empty values when no dates are supplied.
Cons The general agreement in the software engineering community seems to be that it is better not to use extra wrapping for an instance variable, that might pose problems in unwrapping later on.


Aspect: Natural Language Processing
Alternative 1 (current choice): Use an external library (PrettyTime) to process dates in natural language after they have been tokenized by their respective prefixes and categorized according to type.
Pros Dates are sure to be instantiated according to the correct class.
Cons Command format becomes less flexible.
Alternative 2: Parse all dates first then scan the argument string for keywords.
Pros More flexible command format and consequently, more user-friendly.
Cons Open to more mistakes in setting starting dates and deadlines.


Aspect: Setting prefixes for adding tasks
Alternative 1 (current choice): Use natural language prefixes i.e. from, to, by
Pros Easier to type and remember.
Cons Difficult to implement as descriptions containing these words must be separated from dates.
Alternative 2 Use slashes such as in task commands i.e. f/, o/, b/ etc.
Pros Faster to type.
Cons Description might contain slashes. t/ (to) would clash with t/ prefix for tags so to/ would have to be used. Limits possibilities of adding new prefixes and making the format more flexible in the future.

End of Extract


Enhancement Added: Edit Task

External behavior


Start of Extract [from: User Guide]

Editing tasks: edit

Change of plans? The edit feature offers a hassle-free solution to change any parameter of an existing task.

Format: edit INDEX DESCRIPTION by/from/at DEADLINE at START TIME to END TIME

One or more parameters can be specified.
The time parameter can only be edited using the prefix at.

For example:

  • edit 1 exam on 4th Dec at 8 pm to 9 pm
    Changes all parameters of the task at INDEX 1 to set task description to exams, deadline to Mon, Dec 4, '17 and times to 20:00 and 21:00 respectively.

  • edit 2 from tomorrow
    Changes the deadline of the task at INDEX 2 to the date of the following day.

  • edit 3 at 9 am Changes the time of the task at INDEX 3 to 09:00.

Constraints

Note the following constraints when editing tasks:

  • INDEX refers to the index number shown in the most recent task listing.

  • If a task is first searched by the “find” feature, then the new index of the task according to the filtered list of Task Cards, is what must be used in the INDEX parameter.

  • The index must be a positive integer 1, 2, 3, …​

  • The time parameter can only be changed using the at prefix.

  • When changing the times of a task, both start time and end time must be specified or repeated, even if only one time must be changed, e.g. to change the end time of 09:00 - 11:00 at INDEX 1, the command must be edit 1 at 10 am to 11 am. If edit 1 at 10 am, both times will be replaced by 10:00 only. If edit 1 to 10 am is used, then Mobilize will give an error.

  • If a prefix is used with a blank parameter, the respective date associated with that prefix will be deleted.

  • Different deadline prefixes are not allowed in the same command, e.g. edit 1 by friday on friday will result in an error.

  • Multiple values for a single deadline prefix for a single task will only result in the first value being selected, e.g. the command edit 1 by friday saturday will set the deadline to the upcoming Friday only and not Saturday.

  • Multiple copies of the same deadline prefix with multiple values each will result in the last value being selected, e.g. the command edit 1 by friday by saturday will set the deadline to the upcoming Saturday only and not Friday.

  • Multiple values for each time prefix is not allowed and will result in incorrect time values being registered for a task card without warning.

End of Extract


Justification

Editing tasks is a natural extension of the add task feature

Implementation


Start of Extract [from: Developer Guide]

EditTask Mechanism

The EditTask Mechanism is facilitated by the EditTaskCommand class and allows th euser to edit any parameter of a given task.

Suppose the user has just executed the command:


edit 1 finish homework by sunday at 9 am to 12 pm ---

The command is passed by the LogicManager class to the AddressBookParser which goes on to pass it to the EditTaskCommandParser class. Here a new EditTaskDescriptor is created using the newly specified parameters while old parameters are copied. Finally, a newly edited person is created and the Model is called to update the task using the UpdateTask() method.

The following sequence diagram summarizes how this operation works:

EditTaskCommandSequenceDiagram

The optional parameters are assigned in the following manner in the EditTaskCommandParser:


            parseDescriptionForEdit(argMultimap.getPreamble()).ifPresent(editTaskDescriptor::setDescription);
            parseDeadlineForEdit(argMultimap.getAllValues(PREFIX_DEADLINE_BY, PREFIX_DEADLINE_FROM, PREFIX_DEADLINE_ON))
                    .ifPresent(editTaskDescriptor::setDeadline);
            parseEventTimesForEdit(argMultimap.getAllValues(PREFIX_TIME_AT))
                    .ifPresent(editTaskDescriptor::setEventTimes);
            ParserUtil.parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editTaskDescriptor::setTags);
---

End of Extract


Enhancement Added: Tag Multiple Contacts and Tasks

External behavior


Start of Extract [from: User Guide]

Tagging multiple tasks : tag

Tags are a useful way of grouping and labeling tasks. But editing them manually, one by one, can be a tedious process. Instead, you can use the tag command to tag multiple tasks simultaneously.

Format: tag INDEX… t/[TAG]…

You can add multiple tags to multiple tasks by repeating the tag prefix.

Example:

tag 1, 2, 3 t/urgent t/family
Results in all contacts in indices 1, 2 and 3 being tagged with both [urgent] and [family].

Constraints

Note the following constraints when trying to add tags:

  • The index must be a positive integer 1, 2, 3, …​

  • Each index must be valid according to the current list of tasks.

  • If a task is first searched by the “find” feature, then the new index of the task according to the filtered list of Task Cards, is what must be used in the INDEX parameter.

  • Every INDEX must be separated by a comma.

  • Every TAG must be preceded by a tag prefix, t/.

  • If a mix of valid and invalid indices are given, then tags will only be added to the valid INDEX without warning. If all indices are invalid, Mobilize will show an error message.

End of Extract



Start of Extract [from: User Guide]

Tagging multiple contacts : tag

Tags are a useful way of grouping and labeling contacts. But editing them manually, one by one, can be a tedious process. Instead, you can use the tag command to tag multiple contacts simultaneously.

Format: tag INDEX… t/[TAG]…

You can add multiple tags to multiple contacts by repeating the tag prefix.

Example:

tag 1, 2, 3 t/friend t/family
Results in all contacts in indices 1, 2 and 3 being tagged with both [friend] and [family].

Constraints

Note the following constraints when trying to add tags:

  • The index must be a positive integer 1, 2, 3, …​

  • Each index must be valid according to the current list of contacts.

  • If a contact is first searched by the “find” feature, then the new index of the contact according to the filtered list of Contact Cards, is what must be used in the INDEX parameter.

  • Every INDEX must be separated by a comma.

  • Every TAG must be preceded by a tag prefix, t/.

  • If a mix of valid and invalid indices are given, then tags will only be added to the valid INDEX without warning. If all indices are invalid, Mobilize will show an error message.

End of Extract


Justification

The rationale behind adding a special feature to add tags cumulatively, was primarily to open up a world of possibilities of the tags themselves, to a user. Not only can they be used to effectively, yet easily organize contacts and tasks - that too, by multiple parameters - but they can also be used to link the two with the same tag to keep track of tasks with which people are associated and vice versa.

That being said, the caveat to consider was whether to create a new command or modify the edit command itself to allow for tagging cumulatively. In the end, it was decided that separating the two would help achieve the goal of helping users be better organized, as the edit function focuses solely on replacing existing tags or removing them altogether while the tag command serves to increment them. In addition, the flexible indexing for the tag command helps users tag their desired contact or task even if they make some mistakes in typing the index unlike the edit command.

Overall the tag command achieves the application’s purposes of organizing its user’s life much more efficiently.

Implementation


Start of Extract [from: Developer Guide]

Tag mechanism

The Tag mechanism is a specialised form of the edit feature that allows users to tag multiple contacts with multiple tags. The mechanism is facilitated by the TagCommand class which inherits from the UndoableCommand class, indicating that it can be undone.

Suppose the user has just executed the TagCommand using:

`tag 1,2 t/friend t/classmate`.

At first the command is called, which causes LogicManager to pass the command string to the parseCommand() function of the AddressBookParser class. This determines that the TagCommand feature is being evoked and calls the TagCommandParser class.

Here, the arguments are validated and tokenized and new TagCommand object is returned with parsed indices and tags.

The ModelManager class in the Model component is then called to update all the tags of the respective people using the updatePersonTags() method.

The Storage and UI are subsequently updated with the new information.

The following sequence diagram summarizes how this operation works:

TagSequenceDiagram

The TagCommand skips over the invalid indices and attempts to tag the contacts who are in the valid indices. If no valid indices are found, then an error is thrown. This is done by the following:


/**
 * Filters the valid indices in a given array of indices.
 */
public static Index[] filterValidIndices(int lastShownListSize, Index[] indices) {
    assert indices != null;
    assert lastShownListSize != 0;
    return Arrays.stream(indices)
            .filter(currentIndex -> currentIndex.getZeroBased() < lastShownListSize)
            .toArray(Index[]::new);
}


Index[] validIndices = CommandUtil.filterValidIndices(lastShownList.size(), indices);
if (validIndices.length == 0) {
    throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}

TagTask Mechanism

This works in a similar way to the above.

End of Extract


Other contributions

  • Added autocomplete feature using ControlsFX Library (Pull request #96).

  • Discovered bug during trial acceptance testing (Issue #103).

  • Rewrote large parts of the User Guide and Developer to enhance it (Pull Request #144) (Pull Request #112).

  • Reviewed and edited the language and structure of all documentation.

  • Generated SampleData.xml file (Pull Request #138).

  • Set up organization, Milestones and Issues.

  • Assisted other students in Slack.