diff --git a/.gitignore b/.gitignore index dc535ead9c1f..9ac4a5b12eee 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ classes/ src/main/resources/docs/ out/ Collate-TUI.jar +Collate-GUI.jar diff --git a/Wu.md b/Wu.md new file mode 100644 index 000000000000..039566c67b78 --- /dev/null +++ b/Wu.md @@ -0,0 +1,2386 @@ +# Wu +###### \main\java\seedu\address\commons\events\model\TaskBookChangedEvent.java +``` java +/** Indicates the AddressBook in the model has changed*/ +public class TaskBookChangedEvent extends BaseEvent { + + public final ReadOnlyAddressBook data; + + public TaskBookChangedEvent(ReadOnlyAddressBook data) { + this.data = data; + } + + @Override + public String toString() { + return "number of tasks " + data.getTaskList().size(); + } +} +``` +###### \main\java\seedu\address\commons\events\ui\TodoPanelSelectionChangedEvent.java +``` java +/** + * Represents a selection change in the TodoList Panel + */ +public class TodoPanelSelectionChangedEvent extends BaseEvent { + + + private final TodoCard newSelection; + + public TodoPanelSelectionChangedEvent(TodoCard newSelection) { + this.newSelection = newSelection; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + + public TodoCard getNewSelection() { + return this.newSelection; + } +} +``` +###### \main\java\seedu\address\logic\commands\DeleteTaskCommand.java +``` java +/** + * Deletes a task identified using it's last displayed index from the address book. + */ +public class DeleteTaskCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "deleteTask"; + public static final String COMMAND_ALIAS = "dt"; + public static final String COMMAND_SIGN = "-t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted task: %1$s"; + + private final Index targetIndex; + + private Task taskToDelete; + + public DeleteTaskCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + + @Override + public CommandResult executeUndoableCommand() { + requireNonNull(taskToDelete); + try { + model.deleteTask(taskToDelete); + } catch (TaskNotFoundException tnfe) { + throw new AssertionError("The target task cannot be missing"); + } + + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete)); + } + + @Override + protected void preprocessUndoableCommand() throws CommandException { + List lastShownList = model.getFilteredTaskList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + taskToDelete = lastShownList.get(targetIndex.getZeroBased()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteTaskCommand // instanceof handles nulls + && this.targetIndex.equals(((DeleteTaskCommand) other).targetIndex) // state check + && Objects.equals(this.taskToDelete, ((DeleteTaskCommand) other).taskToDelete)); + } +} + +``` +###### \main\java\seedu\address\logic\commands\EditTaskCommand.java +``` java +/** + * Edits the details of an existing person in the address book. + */ +public class EditTaskCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "editTask"; + public static final String COMMAND_ALIAS = "et"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the task identified " + + "by the index number used in the last person listing. " + + "Existing values will be overwritten by the input values." + + "Note: Deadline cannot be modified by editTask command.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_TITLE + "TITLE] " + + "[" + PREFIX_TASK_DESC + "TASK DESCRIPTION] " + + "[" + PREFIX_DEADLINE + "TASK DEADLINE] " + + "[" + PREFIX_PRIORITY + "PRIORITY] " + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_TITLE + "notes " + + PREFIX_TASK_DESC + "Send lab notes to students." + + PREFIX_DEADLINE + "05-05-2018" + + PREFIX_PRIORITY + "50. "; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited Task: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided except deadline."; + + private final Index index; + private final EditTaskDescriptor editTaskDescriptor; + + private Task taskToEdit; + private Task editedTask; + + /** + * @param index of the task in the filtered task list to edit + * @param editTaskDescriptor details to edit the task with + */ + public EditTaskCommand(Index index, EditTaskDescriptor editTaskDescriptor) { + requireNonNull(index); + requireNonNull(editTaskDescriptor); + + this.index = index; + this.editTaskDescriptor = new EditTaskDescriptor(editTaskDescriptor); + } + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + try { + model.updateTask(taskToEdit, editedTask); + } catch (TaskNotFoundException tnfe) { + throw new AssertionError("The target task cannot be missing"); + } + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask)); + } + + @Override + protected void preprocessUndoableCommand() throws CommandException { + List lastShownList = model.getFilteredTaskList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + taskToEdit = lastShownList.get(index.getZeroBased()); + editedTask = createEditedTask(taskToEdit, editTaskDescriptor); + } + + /** + * Creates and returns a {@code Task} with the details of {@code taskToEdit} + * edited with {@code editTaskDescriptor}. + */ + private static Task createEditedTask(Task taskToEdit, EditTaskDescriptor editTaskDescriptor) { + assert taskToEdit != null; + + Title updatedTitle = editTaskDescriptor.getTitle().orElse(taskToEdit.getTitle()); + TaskDescription updatedDesc = editTaskDescriptor.getDescription().orElse(taskToEdit.getTaskDesc()); + Deadline updatedDeadline = editTaskDescriptor.getDeadline().orElse(taskToEdit.getDeadline()); + Priority updatedPriority = editTaskDescriptor.getPriority().orElse(taskToEdit.getPriority()); + + return new Task(updatedTitle, updatedDesc, updatedDeadline, updatedPriority); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditTaskCommand)) { + return false; + } + + // state check + EditTaskCommand e = (EditTaskCommand) other; + return index.equals(e.index) + && editTaskDescriptor.equals(e.editTaskDescriptor) + && Objects.equals(taskToEdit, e.taskToEdit); + } + + /** + * Stores the details to edit the task with. Each non-empty field value will replace the + * corresponding field value of the task. + */ + public static class EditTaskDescriptor { + private Title title; + private TaskDescription desc; + private Deadline deadline; + private Priority priority; + + public EditTaskDescriptor() {} + + /** + * Copy constructor. + */ + public EditTaskDescriptor(EditTaskDescriptor toCopy) { + setTitle(toCopy.title); + setDescription(toCopy.desc); + setDeadline(toCopy.deadline); + setPriority(toCopy.priority); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(this.title, this.desc, this.deadline, this.priority); + } + + public void setTitle(Title title) { + this.title = title; + } + + public Optional getTitle() { + return Optional.ofNullable(title); + } + + public void setDescription(TaskDescription desc) { + this.desc = desc; + } + + public Optional<TaskDescription> getDescription() { + return Optional.ofNullable(desc); + } + + public void setDeadline(Deadline deadline) { + this.deadline = deadline; + } + + public Optional<Deadline> getDeadline() { + return Optional.ofNullable(deadline); + } + + public void setPriority(Priority priority) { + this.priority = priority; + } + + public Optional<Priority> getPriority() { + return Optional.ofNullable(priority); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditTaskDescriptor)) { + return false; + } + + // state check + EditTaskDescriptor e = (EditTaskDescriptor) other; + + return getTitle().equals(e.getTitle()) + && getDescription().equals(e.getDescription()) + && getDeadline().equals(e.getDeadline()) + && getPriority().equals(e.getPriority()); + } + } +} + +``` +###### \main\java\seedu\address\logic\commands\ImportCommand.java +``` java +/** + * Imports data from a xml file and overwrites the current data stored + */ +public class ImportCommand extends Command { + + public static final String COMMAND_WORD = "import"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Imports data from an external xml data file with " + + "the provided path and overwrites the current data stored.\n" + + "Parameters: FILE_PATH\n" + + "Example: " + COMMAND_WORD + " ~/DOWNLOADS/NewDataSet.xml"; + + public static final String MESSAGE_SUCCESS = "Data imported successfully"; + public static final String MESSAGE_INVALID_PATH = "File not found"; + public static final String MESSAGE_INVALID_FILE = "Data configuration failed"; + + private final String filePath; + + public ImportCommand(String filePath) { + this.filePath = filePath.trim(); + } + + @Override + public CommandResult execute() throws CommandException { + requireNonNull(model); + try { + ReadOnlyAddressBook newDataSet = XmlFileStorage.loadDataFromSaveFile(new File(filePath)); + model.resetData(newDataSet); + return new CommandResult(MESSAGE_SUCCESS); + } catch (IOException e) { + throw new CommandException(MESSAGE_INVALID_PATH); + } catch (DataConversionException e) { + throw new CommandException(MESSAGE_INVALID_FILE); + } + } +} +``` +###### \main\java\seedu\address\logic\commands\ListCurrentTaskCommand.java +``` java +/** + * Lists all tasks due by the current month stored in the address book. + */ +public class ListCurrentTaskCommand extends Command { + + public static final String COMMAND_WORD = "listCurrentTask"; + public static final String COMMAND_ALIAS = "lct"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks due this month"; + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_CURRENT_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \main\java\seedu\address\logic\commands\ListTaskCommand.java +``` java +/** + * Lists all tasks stored in the address book. + */ +public class ListTaskCommand extends Command { + + public static final String COMMAND_WORD = "listTask"; + public static final String COMMAND_ALIAS = "lt"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \main\java\seedu\address\logic\commands\SortTaskCommand.java +``` java +/** + * Lists all tasks stored in the address book in date order. + */ +public class SortTaskCommand extends Command { + + public static final String COMMAND_WORD = "sortTask"; + public static final String COMMAND_ALIAS = "stt"; + + public static final String MESSAGE_SUCCESS = "Todo List is sorted"; + + + @Override + public CommandResult execute() { + model.sortTasks(); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \main\java\seedu\address\logic\commands\SwitchTabCommand.java +``` java +public class SwitchTabCommand extends Command { + + public static final String COMMAND_WORD = "switchTab"; + public static final String COMMAND_ALIAS = "swt"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Switches between tabs of Person List and Todo List. "; + public static final String MESSAGE_SUCCESS = "Tab switched."; + + private static final int PERSON_TAB = 0; + private static final int TASK_TAB = 1; + + private TabPane tabPane; + + public SwitchTabCommand(TabPane tabPane) { + this.tabPane = tabPane; + } + + @Override + public CommandResult execute() { + SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel(); + int selectedTab = selectionModel.getSelectedIndex(); + selectionModel.select(selectAnotherTab(selectedTab)); + return new CommandResult(MESSAGE_SUCCESS); + } + + /** + * Alternates the tab index + * @param currentTab index + * @return alternated tab index + */ + private int selectAnotherTab(int currentTab) { + if (currentTab == PERSON_TAB) { + return TASK_TAB; + } + return PERSON_TAB; + } +} +``` +###### \main\java\seedu\address\logic\LogicManager.java +``` java + @Override + public void setTabPane(TabPane tabPane) { + addressBookParser.setTabPane(tabPane); + } +} +``` +###### \main\java\seedu\address\logic\parser\AddressBookParser.java +``` java + public void setTabPane(TabPane tabPane) { + this.tabPane = tabPane; + } + + /** + * Selects Person List tab before executing list command + * @param tabPane + */ + public void selectPersonTab(TabPane tabPane) { + if (tabPane != null) { + SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel(); + selectionModel.select(PERSON_TAB); + } + } + +} +``` +###### \main\java\seedu\address\logic\parser\DeleteTaskCommandParser.java +``` java +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeleteTaskCommandParser implements Parser<DeleteTaskCommand> { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteCommand + * and returns an DeleteCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteTaskCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteTaskCommand(index); + } catch (IllegalValueException ive) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE)); + } + } + +} +``` +###### \main\java\seedu\address\logic\parser\EditTaskCommandParser.java +``` java +/** + * Parses input arguments and creates a new EditTaskCommand object + */ +public class EditTaskCommandParser implements Parser<EditTaskCommand> { + + public static final String PLACE_HOLDER_HASH = "EDITED_DISPLAY"; + + /** + * Parses the given {@code String} of arguments in the context of the EditTaskCommand + * and returns an EditTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditTaskCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_TASK_DESC, PREFIX_DEADLINE, PREFIX_PRIORITY); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE)); + } + + EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor(); + try { + ParserUtil.parseTaskTitle(argMultimap.getValue(PREFIX_TITLE)).ifPresent(editTaskDescriptor::setTitle); + ParserUtil.parseTaskDescription(argMultimap.getValue(PREFIX_TASK_DESC)) + .ifPresent(editTaskDescriptor::setDescription); + ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_DEADLINE)).ifPresent(editTaskDescriptor::setDeadline); + ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY)).ifPresent(editTaskDescriptor::setPriority); + } catch (IllegalValueException ive) { + throw new ParseException(ive.getMessage(), ive); + } + + if (!editTaskDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditTaskCommand.MESSAGE_NOT_EDITED); + } + + return new EditTaskCommand(index, editTaskDescriptor); + } + +} +``` +###### \main\java\seedu\address\logic\parser\ImportCommandParser.java +``` java +/** + * Parses input arguments and creates a new ImportCommandParser object + */ +public class ImportCommandParser implements Parser<ImportCommand> { + + /** + * Parses the given {@code String} of arguments in the context of the ImportCommand + * and returns an Import Command object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ImportCommand parse(String userInput) throws ParseException { + String trimmedInput = userInput.trim(); + + String exceptionMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE); + if (trimmedInput.isEmpty()) { + throw new ParseException(exceptionMessage); + } + + return new ImportCommand(userInput); + } +} +``` +###### \main\java\seedu\address\logic\parser\ParserUtil.java +``` java + /** + * Parses a {@code String taskTitle} into a {@code TaskTitle}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws IllegalValueException if the given {@code taskDescription} is invalid. + */ + public static Title parseTaskTitle(String taskTitle) throws IllegalValueException { + requireNonNull(taskTitle); + String trimmedTaskTitle = taskTitle.trim(); + if (!TaskDescription.isValidDescription(trimmedTaskTitle)) { + throw new IllegalValueException(Title.MESSAGE_TITLE_CONSTRAINTS); + } + return new Title(trimmedTaskTitle); + } + + /** + * Parses a {@code Optional<String> taskDescription} into an {@code Optional<TaskDescription>} + * if {@code TaskDscription} is present. + * See header comment of this class regarding the use of {@code Optional} parameters. + */ + public static Optional<Title> parseTaskTitle(Optional<String> title) throws IllegalValueException { + requireNonNull(title); + return title.isPresent() ? Optional.of(parseTaskTitle(title.get())) : Optional.empty(); + } + +``` +###### \main\java\seedu\address\model\AddressBook.java +``` java + /** + * Removes {@code key} from this {@code AddressBook}. + * @throws TaskNotFoundException if the {@code key} is not in this {@code AddressBook}. + */ + public boolean removeTask(Task key) throws TaskNotFoundException { + if (tasks.remove(key)) { + return true; + } else { + throw new TaskNotFoundException(); + } + } + + //// tag-level operations +``` +###### \main\java\seedu\address\model\ModelManager.java +``` java + @Override + public void deleteTask(Task target) throws TaskNotFoundException { + addressBook.removeTask(target); + indicateAddressBookChanged(); + } + +``` +###### \main\java\seedu\address\model\ModelManager.java +``` java + @Override + public void updateTask(Task target, Task editedTask) + throws TaskNotFoundException { + requireAllNonNull(target, editedTask); + + addressBook.updateTask(target, editedTask); + indicateAddressBookChanged(); + } + + @Override + public void sortTasks() { + addressBook.sortTaskList(); + } + +``` +###### \main\java\seedu\address\model\task\Task.java +``` java + @Override + public int compareTo(Task task) { + int yearDiff = this.getDeadlineYear() - task.getDeadlineYear(); + int monthDiff = this.getDeadlineMonth() - task.getDeadlineMonth(); + int dayDiff = this.getDeadlineDay() - task.getDeadlineDay(); + + return compareDate(yearDiff, monthDiff, dayDiff); + } + + /** + * Compares the dates + * @param yearDiff difference in year + * @param monthDiff difference in month + * @param dayDiff difference in day + */ + private int compareDate (int yearDiff, int monthDiff, int dayDiff) { + if (yearDiff != 0) { + return yearDiff; + } + if (monthDiff != 0) { + return monthDiff; + } + return dayDiff; + } +} +``` +###### \main\java\seedu\address\model\task\Title.java +``` java +/** + * Represents a task's title in the TodoList. + * Guarantees: immutable; is valid as declared in {@link #isValidTitle(String)} + */ +public class Title { + + public static final String MESSAGE_TITLE_CONSTRAINTS = + "Task title can contain any characters but it should not be blank"; + + /* + * The first character of the title must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String TITLE_VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String value; + + /** + * Constructs a {@code Title}. + * + * @param title A valid name. + */ + public Title(String title) { + requireNonNull(title); + checkArgument(isValidTitle(title), MESSAGE_TITLE_CONSTRAINTS); + this.value = title; + } + + /** + * Returns true if a given string is a valid person name. + */ + public static boolean isValidTitle(String test) { + return test.matches(TITLE_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Title // instanceof handles nulls + && this.value.equals(((Title) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + +``` +###### \main\java\seedu\address\model\task\UniqueTaskList.java +``` java + /** + * Replaces the task {@code target} in the list with {@code editedTask}. + * + * @throws TaskNotFoundException if {@code target} could not be found in the list. + */ + public void setTask(Task target, Task editedTask) + throws TaskNotFoundException { + requireNonNull(editedTask); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new TaskNotFoundException(); + } + remove(target); + add(editedTask); + } + + /** + * Removes the equivalent task from the list. + * + * @throws TaskNotFoundException if no such task could be found in the list. + */ + public boolean remove(Task toRemove) throws TaskNotFoundException { + requireNonNull(toRemove); + final boolean taskFoundAndDeleted = internalList.remove(toRemove) + && calendarList[toRemove.getDeadline().diff][toRemove.getDeadlineDay()].remove(toRemove); + if (!taskFoundAndDeleted) { + throw new TaskNotFoundException(); + } + return taskFoundAndDeleted; + } + + /** + * Sorts the tasks in the list in date order + */ + public void sortList() { + requireNonNull(internalList); + + FXCollections.sort(internalList); + } + +``` +###### \main\java\seedu\address\ui\TodoCard.java +``` java +/** + * A UI component that displays information of a {@code task} in TodoList. + */ +public class TodoCard extends UiPart<Region> { + + private static final String FXML = "TodoListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see <a href="https://github.com/se-edu/addressbook-level4/issues/336">The issue on AddressBook level 4</a> + */ + + public final Task task; + + @FXML + private HBox cardPane; + @FXML + private Label title; + @FXML + private Label id; + @FXML + private Label priority; + @FXML + private Label deadline; + @FXML + private Label description; + @FXML + private FlowPane tags; + + public TodoCard(Task task, int displayedIndex) { + super(FXML); + this.task = task; + id.setText(displayedIndex + ". "); + title.setText("Title: " + task.getTitle().value); + priority.setText("Priority: " + task.getPriority().toString()); + deadline.setText("Deadline: " + task.getDeadline().dateString); + description.setText("Description: " + task.getTaskDesc().value); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TodoCard)) { + return false; + } + + // state check + TodoCard card = (TodoCard) other; + return id.getText().equals(card.id.getText()) + && task.equals(card.task); + } +} +``` +###### \main\java\seedu\address\ui\TodoListPanel.java +``` java +/** + * Panel containing the list of tasks shown in TodoList. + */ +public class TodoListPanel extends UiPart<Region> { + private static final String FXML = "todoListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(TodoListPanel.class); + + @FXML + private ListView<TodoCard> todoListView; + + public TodoListPanel(ObservableList<Task> taskList) { + super(FXML); + setConnections(taskList); + registerAsAnEventHandler(this); + } + + private void setConnections(ObservableList<Task> taskList) { + ObservableList<TodoCard> mappedList = EasyBind.map( + taskList, (task) -> new TodoCard(task, taskList.indexOf(task) + 1)); + todoListView.setItems(mappedList); + todoListView.setCellFactory(listView -> new todoListViewCell()); + setEventHandlerForSelectionChangeEvent(); + } + + private void setEventHandlerForSelectionChangeEvent() { + todoListView.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in task list panel changed to : '" + newValue + "'"); + raise(new TodoPanelSelectionChangedEvent(newValue)); + } + }); + } + + /** + * Scrolls to the {@code TodoCard} at the {@code index} and selects it. + */ + private void scrollTo(int index) { + Platform.runLater(() -> { + todoListView.scrollTo(index); + todoListView.getSelectionModel().clearAndSelect(index); + }); + } + + @Subscribe + private void handleJumpToListRequestEvent(JumpToListRequestEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + scrollTo(event.targetIndex); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code PersonCard}. + */ + class todoListViewCell extends ListCell<TodoCard> { + + @Override + protected void updateItem(TodoCard task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(task.getRoot()); + } + } + } +} +``` +###### \main\resources\view\DarkMainWindow.fxml +``` fxml + <SplitPane id="splitPane" fx:id="splitPane" dividerPositions="0.4" VBox.vgrow="ALWAYS"> + <VBox fx:id="personList" minWidth="360" prefWidth="360" SplitPane.resizableWithParent="false"> + <TabPane fx:id="tabPane" VBox.vgrow="ALWAYS" tabClosingPolicy="UNAVAILABLE"> + <tabs> + <Tab fx:id="personListTab" text="Person List"> + <StackPane fx:id="personListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> + </Tab> + <Tab fx:id="todoListTab" text="Todo List"> + <StackPane fx:id="todoListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> + </Tab> + </tabs> + </TabPane> + </VBox> + +``` +###### \main\resources\view\DarkTheme.css +``` css +.tab-pane { + -fx-padding: 0 0 0 1; + -fx-background-color: #232A34; +} + +.tab-pane .tab-header-area { + -fx-background-color: #232A34; + -fx-padding: 0 0 0 0; + -fx-min-height: 0; + -fx-max-height: 0; +} + +.tab-pane .tab-header-area .tab-header-background { + -fx-opacity: 0; +} + +.tab-pane { + -fx-tab-min-width:150px; +} + +.tab { + -fx-background-insets: 0 1 0 1,0,0; +} + +.tab-pane .tab { + -fx-background-color: #404040; + +} + +.tab-pane .tab:selected { + -fx-border-color: transparent !important; + -fx-background-color: #5F6A6A; +} + +.tab .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #f3f3f3; + -fx-font-size: 12px; + -fx-font-weight: bold; +} + +.tab:selected .tab-label { + -fx-border-color: transparent !important; + -fx-text-fill: white; +} +``` +###### \main\resources\view\TodoListCard.fxml +``` fxml +<HBox id="cardPane" fx:id="cardPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> + <GridPane HBox.hgrow="ALWAYS"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10" prefWidth="150" /> + </columnConstraints> + <VBox alignment="CENTER_LEFT" minHeight="105" GridPane.columnIndex="0"> + <padding> + <Insets top="5" right="5" bottom="5" left="15"/> + </padding> + <HBox spacing="5" alignment="CENTER_LEFT"> + <Label fx:id="id" styleClass="cell_big_label"> + <minWidth> + <Region fx:constant="USE_PREF_SIZE"/> + </minWidth> + </Label> + <Label fx:id="title" text="\$title" styleClass="cell_big_label"/> + </HBox> + <FlowPane fx:id="tags" /> + <Label fx:id="description" styleClass="cell_small_label" text="\$description"/> + <Label fx:id="deadline" styleClass="cell_small_label" text="\$deadline"/> + <Label fx:id="priority" styleClass="cell_small_label" text="\$priority"/> + </VBox> + </GridPane> +</HBox> +``` +###### \main\resources\view\todoListPanel.fxml +``` fxml +<VBox xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1"> + <ListView fx:id="todoListView" VBox.vgrow="ALWAYS" /> +</VBox> +``` +###### \test\java\guitests\guihandles\TodoCardHandle.java +``` java +public class TodoCardHandle extends NodeHandle<Node> { + private static final String ID_FIELD_ID = "#id"; + private static final String TITLE_FIELD_ID = "#title"; + private static final String DESC_FIELD_ID = "#description"; + private static final String DEADLINE_FIELD_ID = "#deadline"; + private static final String PRIORITY_FIELD_ID = "#priority"; + + private final Label idLabel; + private final Label titleLabel; + private final Label descriptionLabel; + private final Label deadlineLabel; + private final Label priorityLabel; + + public TodoCardHandle(Node cardNode) { + super(cardNode); + + this.idLabel = getChildNode(ID_FIELD_ID); + this.titleLabel = getChildNode(TITLE_FIELD_ID); + this.descriptionLabel = getChildNode(DESC_FIELD_ID); + this.deadlineLabel = getChildNode(DEADLINE_FIELD_ID); + this.priorityLabel = getChildNode(PRIORITY_FIELD_ID); + + } + + public String getId() { + return idLabel.getText(); + } + + public String getTitle() { + return titleLabel.getText(); + } + + public String getDescription() { + return descriptionLabel.getText(); + } + + public String getDeadline() { + return deadlineLabel.getText(); + } + + public String getPriority() { + return priorityLabel.getText(); + } + +} +``` +###### \test\java\guitests\guihandles\TodoListPanelHandle.java +``` java +public class TodoListPanelHandle extends NodeHandle<ListView<TodoCard>> { + public static final String TODO_LIST_VIEW_ID = "#todoListView"; + + private Optional<TodoCard> lastRememberedSelectedTodoCard; + + public TodoListPanelHandle(ListView<TodoCard> todoListPanelNode) { + super(todoListPanelNode); + } + + /** + * Returns a handle to the selected {@code TodoCardHandle}. + * A maximum of 1 item can be selected at any time. + * @throws AssertionError if no card is selected, or more than 1 card is selected. + */ + public TodoCardHandle getHandleToSelectedCard() { + List<TodoCard> taskList = getRootNode().getSelectionModel().getSelectedItems(); + + if (taskList.size() != 1) { + throw new AssertionError("Person list size expected 1."); + } + + return new TodoCardHandle(taskList.get(0).getRoot()); + } + + /** + * Returns the index of the selected card. + */ + public int getSelectedCardIndex() { + return getRootNode().getSelectionModel().getSelectedIndex(); + } + + /** + * Returns true if a card is currently selected. + */ + public boolean isAnyCardSelected() { + List<TodoCard> selectedCardsList = getRootNode().getSelectionModel().getSelectedItems(); + + if (selectedCardsList.size() > 1) { + throw new AssertionError("Card list size expected 0 or 1."); + } + + return !selectedCardsList.isEmpty(); + } + + /** + * Navigates the listview to display and select the task. + */ + public void navigateTodoCard(Task task) { + List<TodoCard> cards = getRootNode().getItems(); + Optional<TodoCard> matchingCard = cards.stream().filter(card -> card.task.equals(task)).findFirst(); + + if (!matchingCard.isPresent()) { + throw new IllegalArgumentException("Person does not exist."); + } + + guiRobot.interact(() -> { + getRootNode().scrollTo(matchingCard.get()); + getRootNode().getSelectionModel().select(matchingCard.get()); + }); + guiRobot.pauseForHuman(); + } + + /** + * Returns the Todocard handle of a task associated with the {@code index} in the list. + */ + public TodoCardHandle getTodoCardHandle(int index) { + return getTodoCardHandle(getRootNode().getItems().get(index).task); + } + + /** + * Returns the {@code TodoCardHandle} of the specified {@code task} in the list. + */ + public TodoCardHandle getTodoCardHandle(Task task) { + Optional<TodoCardHandle> handle = getRootNode().getItems().stream() + .filter(card -> card.task.equals(task)) + .map(card -> new TodoCardHandle(card.getRoot())) + .findFirst(); + return handle.orElseThrow(() -> new IllegalArgumentException("Task does not exist.")); + } + + /** + * Selects the {@code TodoCard} at {@code index} in the list. + */ + public void select(int index) { + getRootNode().getSelectionModel().select(index); + } + + /** + * Remembers the selected {@code TodoCard} in the list. + */ + public void rememberSelectedTodoCard() { + List<TodoCard> selectedItems = getRootNode().getSelectionModel().getSelectedItems(); + + if (selectedItems.size() == 0) { + lastRememberedSelectedTodoCard = Optional.empty(); + } else { + lastRememberedSelectedTodoCard = Optional.of(selectedItems.get(0)); + } + } + + /** + * Returns true if the selected {@code TodoCard} is different from the value remembered by the most recent + * {@code rememberSelectedTodoCard()} call. + */ + public boolean isSelectedTodoCardChanged() { + List<TodoCard> selectedItems = getRootNode().getSelectionModel().getSelectedItems(); + + if (selectedItems.size() == 0) { + return lastRememberedSelectedTodoCard.isPresent(); + } else { + return !lastRememberedSelectedTodoCard.isPresent() + || !lastRememberedSelectedTodoCard.get().equals(selectedItems.get(0)); + } + } + + /** + * Returns the size of the list. + */ + public int getListSize() { + return getRootNode().getItems().size(); + } +} +``` +###### \test\java\seedu\address\logic\commands\AddTaskCommandTest.java +``` java +public class AddTaskCommandTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void constructor_nullTask_throwNullPointerException() { + thrown.expect(NullPointerException.class); + new AddTaskCommand(null); + } + + @Test + public void execute_taskAcceptedByModel_addTaskSuccessful() throws Exception { + ModelStubAcceptingTaskAdded modelStub = new ModelStubAcceptingTaskAdded(); + Task validTask = new TaskBuilder().build(); + + CommandResult commandResult = getAddTaskCommandForTask(validTask, modelStub).execute(); + + assertEquals(String.format(AddTaskCommand.MESSAGE_SUCCESS, validTask), commandResult.feedbackToUser); + assertEquals(Arrays.asList(validTask), modelStub.tasksAdded); + } + + @Test + public void equals() { + Task meeting = new TaskBuilder().withTitle("Meeting").build(); + Task assignment = new TaskBuilder().withTitle("Assignment").build(); + AddTaskCommand addMeetingCommand = new AddTaskCommand(meeting); + AddTaskCommand addAssignmentCommand = new AddTaskCommand(assignment); + + // same object -> returns true + assertTrue(addMeetingCommand.equals(addMeetingCommand)); + + // same values -> returns true + AddTaskCommand addMeetingCommandCopy = new AddTaskCommand(meeting); + assertTrue(addMeetingCommand.equals(addMeetingCommandCopy)); + + // different types -> returns false + assertFalse(addMeetingCommand.equals(1)); + + // null -> returns false + assertFalse(addMeetingCommand.equals(null)); + + // different person -> returns false + assertFalse(addMeetingCommand.equals(addAssignmentCommand)); + } + + private AddTaskCommand getAddTaskCommandForTask(Task task, Model model) { + AddTaskCommand command = new AddTaskCommand(task); + command.setData(model, new CommandHistory(), new UndoRedoStack()); + return command; + } + + /** + * A default model stub that have all of the methods failing. + */ + private class ModelStub implements Model { + @Override + public void addPerson(Person person) throws DuplicatePersonException { + fail("This method should not be called."); + } + + @Override + public void addTask(Task task) { + } + + @Override + public void deleteTask(Task target) throws TaskNotFoundException { + } + + @Override + public void updateTask(Task target, Task editedTask) throws TaskNotFoundException { + } + + @Override + public void resetData(ReadOnlyAddressBook newData) { + fail("This method should not be called."); + } + + @Override + public ReadOnlyAddressBook getAddressBook() { + fail("This method should not be called."); + return null; + } + + @Override + public void deletePerson(Person target) throws PersonNotFoundException { + fail("This method should not be called."); + } + + @Override + public void updatePerson(Person target, Person editedPerson) + throws DuplicatePersonException { + fail("This method should not be called."); + } + + @Override + public void sortPersons() { + fail("This method should not be called"); + } + + @Override + public void sortTasks() { + fail("This method should not be called"); + } + + @Override + public ObservableList<Person> getFilteredPersonList() { + fail("This method should not be called."); + return null; + } + + @Override + public ObservableList<Task> getFilteredTaskList() { + return null; + } + + @Override + public ObservableList<Task>[][] getCalendarTaskLists() { + return new ObservableList[0][]; + } + + @Override + public void updateFilteredPersonList(Predicate<Person> predicate) { + fail("This method should not be called."); + } + + @Override + public void updateFilteredTaskList(Predicate<Task> predicate) { + + } + + @Override + public List<String> getItemList() { + return null; + } + + @Override + public void clearDeleteItems() { + } + } + + /** + * A Model stub that always accept the task being added. + */ + private class ModelStubAcceptingTaskAdded extends ModelStub { + final ArrayList<Task> tasksAdded = new ArrayList<>(); + + @Override + public void addTask(Task task) { + requireNonNull(task); + tasksAdded.add(task); + } + + @Override + public ReadOnlyAddressBook getAddressBook() { + return new AddressBook(); + } + } +} +``` +###### \test\java\seedu\address\logic\commands\CommandTaskTestUtil.java +``` java +public class CommandTaskTestUtil { + + public static final String VALID_TITLE_EXAM = "Test Preparation"; + public static final String VALID_TITLE_MARK = "Mark Test"; + public static final String VALID_TASK_DESC_EXAM = "Giving Practical Exam tips and reviewing past year test"; + public static final String VALID_TASK_DESC_MARK = "Grade test papers for CS1020 tutorial classes"; + public static final String VALID_DEADLINE_EXAM = "01-06-2018"; + public static final String VALID_DEADLINE_MARK = "04-06-2018"; + public static final String VALID_PRIORITY_EXAM = "1"; + public static final String VALID_PRIORITY_MARK = "2"; + + public static final String TITLE_DESC_EXAM = " " + PREFIX_TITLE + VALID_TITLE_EXAM; + public static final String TITLE_DESC_MARK = " " + PREFIX_TITLE + VALID_TITLE_MARK; + public static final String TASK_DESC_DESC_EXAM = " " + PREFIX_TASK_DESC + VALID_TASK_DESC_EXAM; + public static final String TASK_DESC_DESC_MARK = " " + PREFIX_TASK_DESC + VALID_TASK_DESC_MARK; + public static final String DEADLINE_DESC_EXAM = " " + PREFIX_DEADLINE + VALID_DEADLINE_EXAM; + public static final String DEADLINE_DESC_MARK = " " + PREFIX_DEADLINE + VALID_DEADLINE_MARK; + public static final String PRIORITY_DESC_EXAM = " " + PREFIX_PRIORITY + VALID_PRIORITY_EXAM; + public static final String PRIORITY_DESC_MARK = " " + PREFIX_PRIORITY + VALID_PRIORITY_MARK; + + public static final String INVALID_TITLE_DESC = " " + PREFIX_TITLE + " "; // '&' not allowed in title + public static final String INVALID_TASK_DESC_DESC = " " + PREFIX_TASK_DESC + + " "; // empty string not allowed in task description + public static final String INVALID_DEADLINE_DESC = " " + PREFIX_DEADLINE + "30-13-2018"; // invalid date + public static final String INVALID_PRIORITY_DESC = " " + PREFIX_PRIORITY + "0"; // priority from 1 to 3 + + public static final String PREAMBLE_WHITESPACE = "\t \r \n"; + public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; + + public static final EditTaskCommand.EditTaskDescriptor DESC_EXAM; + public static final EditTaskCommand.EditTaskDescriptor DESC_MARK; + static { + DESC_EXAM = new EditTaskDescriptorBuilder().withTitle(VALID_TITLE_EXAM).withDesc(VALID_TASK_DESC_EXAM) + .withDeadline(VALID_DEADLINE_EXAM).withPriority(VALID_PRIORITY_EXAM).build(); + DESC_MARK = new EditTaskDescriptorBuilder().withTitle(VALID_TITLE_MARK).withDesc(VALID_TASK_DESC_MARK) + .withDeadline(VALID_DEADLINE_MARK).withPriority(VALID_PRIORITY_MARK).build(); + } + + /** + * Executes the given {@code command}, confirms that <br> + * - the result message matches {@code expectedMessage} <br> + * - the {@code actualModel} matches {@code expectedModel} + */ + public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + Model expectedModel) { + try { + CommandResult result = command.execute(); + assertEquals(expectedMessage, result.feedbackToUser); + } catch (CommandException ce) { + throw new AssertionError("Execution of command should not fail.", ce); + } + } + + /** + * Executes the given {@code command}, confirms that <br> + * - a {@code CommandException} is thrown <br> + * - the CommandException message matches {@code expectedMessage} <br> + * - the address book and the filtered task list in the {@code actualModel} remain unchanged + */ + public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { + // we are unable to defensively copy the model for comparison later, so we can + // only do so by copying its components. + AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); + List<Task> expectedFilteredList = new ArrayList<>(actualModel.getFilteredTaskList()); + + try { + command.execute(); + fail("The expected CommandException was not thrown."); + } catch (CommandException e) { + assertEquals(expectedMessage, e.getMessage()); + } + } + + /** + * Deletes the first task in {@code model}'s filtered list from {@code model}'s address book. + */ + public static void deleteFirstTask(Model model) { + Task firstTask = model.getFilteredTaskList().get(0); + try { + model.deleteTask(firstTask); + } catch (TaskNotFoundException tnfe) { + throw new AssertionError("Task in filtered list must exist in model.", tnfe); + } + } + + /** + * Returns an {@code UndoCommand} with the given {@code model} and {@code undoRedoStack} set. + */ + public static UndoCommand prepareUndoCommand(Model model, UndoRedoStack undoRedoStack) { + UndoCommand undoCommand = new UndoCommand(); + undoCommand.setData(model, new CommandHistory(), undoRedoStack); + return undoCommand; + } + + /** + * Returns a {@code RedoCommand} with the given {@code model} and {@code undoRedoStack} set. + */ + public static RedoCommand prepareRedoCommand(Model model, UndoRedoStack undoRedoStack) { + RedoCommand redoCommand = new RedoCommand(); + redoCommand.setData(model, new CommandHistory(), undoRedoStack); + return redoCommand; + } +} +``` +###### \test\java\seedu\address\logic\commands\DeleteCommandTest.java +``` java +public class DeleteCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() throws Exception { + Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + DeleteCommand deleteCommand = prepareCommand(INDEX_FIRST_PERSON); + + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + expectedModel.deletePerson(personToDelete); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() throws Exception { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + DeleteCommand deleteCommand = prepareCommand(outOfBoundIndex); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() throws Exception { + showPersonAtIndex(model, INDEX_FIRST_PERSON); + + Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + DeleteCommand deleteCommand = prepareCommand(INDEX_FIRST_PERSON); + + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + expectedModel.deletePerson(personToDelete); + showNoPerson(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showPersonAtIndex(model, INDEX_FIRST_PERSON); + + Index outOfBoundIndex = INDEX_SECOND_PERSON; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); + + DeleteCommand deleteCommand = prepareCommand(outOfBoundIndex); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void executeUndoRedo_validIndexUnfilteredList_success() throws Exception { + UndoRedoStack undoRedoStack = new UndoRedoStack(); + UndoCommand undoCommand = prepareUndoCommand(model, undoRedoStack); + RedoCommand redoCommand = prepareRedoCommand(model, undoRedoStack); + Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + DeleteCommand deleteCommand = prepareCommand(INDEX_FIRST_PERSON); + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + // delete -> first person deleted + deleteCommand.execute(); + undoRedoStack.push(deleteCommand); + + // undo -> reverts addressbook back to previous state and filtered person list to show all persons + assertCommandSuccess(undoCommand, model, UndoCommand.MESSAGE_SUCCESS, expectedModel); + + // redo -> same first person deleted again + expectedModel.deletePerson(personToDelete); + assertCommandSuccess(redoCommand, model, RedoCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void executeUndoRedo_invalidIndexUnfilteredList_failure() { + UndoRedoStack undoRedoStack = new UndoRedoStack(); + UndoCommand undoCommand = prepareUndoCommand(model, undoRedoStack); + RedoCommand redoCommand = prepareRedoCommand(model, undoRedoStack); + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + DeleteCommand deleteCommand = prepareCommand(outOfBoundIndex); + + // execution failed -> deleteCommand not pushed into undoRedoStack + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + + // no commands in undoRedoStack -> undoCommand and redoCommand fail + assertCommandFailure(undoCommand, model, UndoCommand.MESSAGE_FAILURE); + assertCommandFailure(redoCommand, model, RedoCommand.MESSAGE_FAILURE); + } + + /** + * 1. Deletes a {@code Person} from a filtered list. + * 2. Undo the deletion. + * 3. The unfiltered list should be shown now. Verify that the index of the previously deleted person in the + * unfiltered list is different from the index at the filtered list. + * 4. Redo the deletion. This ensures {@code RedoCommand} deletes the person object regardless of indexing. + */ + @Test + public void executeUndoRedo_validIndexFilteredList_samePersonDeleted() throws Exception { + UndoRedoStack undoRedoStack = new UndoRedoStack(); + UndoCommand undoCommand = prepareUndoCommand(model, undoRedoStack); + RedoCommand redoCommand = prepareRedoCommand(model, undoRedoStack); + DeleteCommand deleteCommand = prepareCommand(INDEX_FIRST_PERSON); + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + showPersonAtIndex(model, INDEX_SECOND_PERSON); + Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + // delete -> deletes second person in unfiltered person list / first person in filtered person list + deleteCommand.execute(); + undoRedoStack.push(deleteCommand); + + // undo -> reverts addressbook back to previous state and filtered person list to show all persons + assertCommandSuccess(undoCommand, model, UndoCommand.MESSAGE_SUCCESS, expectedModel); + + expectedModel.deletePerson(personToDelete); + assertNotEquals(personToDelete, model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased())); + // redo -> deletes same second person in unfiltered person list + assertCommandSuccess(redoCommand, model, RedoCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void equals() throws Exception { + DeleteCommand deleteFirstCommand = prepareCommand(INDEX_FIRST_PERSON); + DeleteCommand deleteSecondCommand = prepareCommand(INDEX_SECOND_PERSON); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteCommand deleteFirstCommandCopy = prepareCommand(INDEX_FIRST_PERSON); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // one command preprocessed when previously equal -> returns false + deleteFirstCommandCopy.preprocessUndoableCommand(); + assertFalse(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } + + /** + * Returns a {@code DeleteCommand} with the parameter {@code index}. + */ + private DeleteCommand prepareCommand(Index index) { + DeleteCommand deleteCommand = new DeleteCommand(index); + deleteCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + return deleteCommand; + } + + /** + * Updates {@code model}'s filtered list to show no one. + */ + private void showNoPerson(Model model) { + model.updateFilteredPersonList(p -> false); + + assertTrue(model.getFilteredPersonList().isEmpty()); + } +} +``` +###### \test\java\seedu\address\logic\commands\DeleteTaskCommandTest.java +``` java +public class DeleteTaskCommandTest { + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() throws Exception { + Task taskToDelete = model.getFilteredTaskList().get(INDEX_FIRST_TASK.getZeroBased()); + DeleteTaskCommand deleteTaskCommand = prepareCommand(INDEX_FIRST_TASK); + + String expectedMessage = String.format(DeleteTaskCommand.MESSAGE_DELETE_TASK_SUCCESS, taskToDelete); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + expectedModel.deleteTask(taskToDelete); + + assertCommandSuccess(deleteTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() throws Exception { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1); + DeleteTaskCommand deleteTaskCommand = prepareCommand(outOfBoundIndex); + + assertCommandFailure(deleteTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() throws Exception { + Task taskToDelete = model.getFilteredTaskList().get(INDEX_FIRST_TASK.getZeroBased()); + DeleteTaskCommand deleteTaskCommand = prepareCommand(INDEX_FIRST_TASK); + + String expectedMessage = String.format(DeleteTaskCommand.MESSAGE_DELETE_TASK_SUCCESS, taskToDelete); + + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + expectedModel.deleteTask(taskToDelete); + showNoTask(expectedModel); + + assertCommandSuccess(deleteTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void executeUndoRedo_validIndexUnfilteredList_success() throws Exception { + UndoRedoStack undoRedoStack = new UndoRedoStack(); + UndoCommand undoCommand = prepareUndoCommand(model, undoRedoStack); + RedoCommand redoCommand = prepareRedoCommand(model, undoRedoStack); + Task taskToDelete = model.getFilteredTaskList().get(INDEX_FIRST_TASK.getZeroBased()); + DeleteTaskCommand deleteTaskCommand = prepareCommand(INDEX_FIRST_TASK); + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + // delete -> first task deleted + deleteTaskCommand.execute(); + undoRedoStack.push(deleteTaskCommand); + + // undo -> reverts addressbook back to previous state and to show all tasks + assertCommandSuccess(undoCommand, model, UndoCommand.MESSAGE_SUCCESS, expectedModel); + + // redo -> same first task deleted again + expectedModel.deleteTask(taskToDelete); + assertCommandSuccess(redoCommand, model, RedoCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void executeUndoRedo_invalidIndexUnfilteredList_failure() { + UndoRedoStack undoRedoStack = new UndoRedoStack(); + UndoCommand undoCommand = prepareUndoCommand(model, undoRedoStack); + RedoCommand redoCommand = prepareRedoCommand(model, undoRedoStack); + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1); + DeleteTaskCommand deleteTaskCommand = prepareCommand(outOfBoundIndex); + + // execution failed -> deleteCommand not pushed into undoRedoStack + assertCommandFailure(deleteTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + + // no commands in undoRedoStack -> undoCommand and redoCommand fail + assertCommandFailure(undoCommand, model, UndoCommand.MESSAGE_FAILURE); + assertCommandFailure(redoCommand, model, RedoCommand.MESSAGE_FAILURE); + } + + @Test + public void equals() throws Exception { + DeleteTaskCommand deleteFirstCommand = prepareCommand(INDEX_FIRST_TASK); + DeleteTaskCommand deleteSecondCommand = prepareCommand(INDEX_SECOND_TASK); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteTaskCommand deleteFirstCommandCopy = prepareCommand(INDEX_FIRST_TASK); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // one command preprocessed when previously equal -> returns false + deleteFirstCommandCopy.preprocessUndoableCommand(); + assertFalse(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } + + /** + * Returns a {@code DeleteTaskCommand} with the parameter {@code index}. + */ + private DeleteTaskCommand prepareCommand(Index index) { + DeleteTaskCommand deleteTaskCommand = new DeleteTaskCommand(index); + deleteTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + return deleteTaskCommand; + } + + /** + * Updates {@code model}'s filtered list to show no task. + */ + private void showNoTask(Model model) { + model.updateFilteredTaskList(p -> false); + + assertTrue(model.getFilteredTaskList().isEmpty()); + } +} +``` +###### \test\java\seedu\address\logic\commands\EditTaskCommandTest.java +``` java +public class EditTaskCommandTest { + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() throws Exception { + Task editedTask = new TaskBuilder().build(); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(editedTask).build(); + EditTaskCommand editTaskCommand = prepareCommand(INDEX_FIRST_TASK, descriptor); + + String expectedMessage = String.format(EditTaskCommand.MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + expectedModel.updateTask(model.getFilteredTaskList().get(0), editedTask); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() throws Exception { + Index indexLastTask = Index.fromOneBased(model.getFilteredTaskList().size()); + Task lastTask = model.getFilteredTaskList().get(indexLastTask.getZeroBased()); + + TaskBuilder taskInList = new TaskBuilder(lastTask); + Task editedTask = taskInList.withTitle(VALID_TITLE_EXAM).withDesc(VALID_TASK_DESC_EXAM) + .withDeadline(VALID_DEADLINE_EXAM).build(); + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withTitle(VALID_TITLE_EXAM) + .withDesc(VALID_TASK_DESC_EXAM).withDeadline(VALID_DEADLINE_EXAM).build(); + EditTaskCommand editTaskCommand = prepareCommand(indexLastTask, descriptor); + + String expectedMessage = String.format(EditTaskCommand.MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + expectedModel.updateTask(lastTask, editedTask); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditTaskCommand editTaskCommand = prepareCommand(INDEX_FIRST_TASK, new EditTaskDescriptor()); + Task editedTask = model.getFilteredTaskList().get(INDEX_FIRST_TASK.getZeroBased()); + + String expectedMessage = String.format(EditTaskCommand.MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidTaskIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withTitle(VALID_TITLE_MARK).build(); + EditTaskCommand editTaskCommand = prepareCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void executeUndoRedo_validIndexUnfilteredList_success() throws Exception { + UndoRedoStack undoRedoStack = new UndoRedoStack(); + UndoCommand undoCommand = prepareUndoCommand(model, undoRedoStack); + RedoCommand redoCommand = prepareRedoCommand(model, undoRedoStack); + Task editedTask = new TaskBuilder().build(); + Task taskToEdit = model.getFilteredTaskList().get(INDEX_FIRST_TASK.getZeroBased()); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(editedTask).build(); + EditTaskCommand editTaskCommand = prepareCommand(INDEX_FIRST_TASK, descriptor); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + + // edit -> first task edited + editTaskCommand.execute(); + undoRedoStack.push(editTaskCommand); + + // undo -> reverts addressbook back to previous state and filtered task list to show all persons + assertCommandSuccess(undoCommand, model, UndoCommand.MESSAGE_SUCCESS, expectedModel); + + // redo -> same first task edited again + expectedModel.updateTask(taskToEdit, editedTask); + assertCommandSuccess(redoCommand, model, RedoCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void executeUndoRedo_invalidIndexUnfilteredList_failure() { + UndoRedoStack undoRedoStack = new UndoRedoStack(); + UndoCommand undoCommand = prepareUndoCommand(model, undoRedoStack); + RedoCommand redoCommand = prepareRedoCommand(model, undoRedoStack); + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withTitle(VALID_TITLE_MARK).build(); + EditTaskCommand editTaskCommand = prepareCommand(outOfBoundIndex, descriptor); + + // execution failed -> editCommand not pushed into undoRedoStack + assertCommandFailure(editTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + + // no commands in undoRedoStack -> undoCommand and redoCommand fail + assertCommandFailure(undoCommand, model, UndoCommand.MESSAGE_FAILURE); + assertCommandFailure(redoCommand, model, RedoCommand.MESSAGE_FAILURE); + } + + @Test + public void equals() throws Exception { + final EditTaskCommand standardCommand = prepareCommand(INDEX_FIRST_TASK, DESC_MARK); + + // same values -> returns true + EditTaskDescriptor copyDescriptor = new EditTaskDescriptor(DESC_MARK); + EditTaskCommand commandWithSameValues = prepareCommand(INDEX_FIRST_TASK, copyDescriptor); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // one command preprocessed when previously equal -> returns false + commandWithSameValues.preprocessUndoableCommand(); + assertFalse(standardCommand.equals(commandWithSameValues)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new EditTaskCommand(INDEX_SECOND_TASK, DESC_EXAM))); + + // different descriptor -> returns false + assertFalse(standardCommand.equals(new EditTaskCommand(INDEX_FIRST_TASK, DESC_EXAM))); + } + + /** + * Returns an {@code EditCommand} with parameters {@code index} and {@code descriptor} + */ + private EditTaskCommand prepareCommand(Index index, EditTaskDescriptor descriptor) { + EditTaskCommand editTaskCommand = new EditTaskCommand(index, descriptor); + editTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + return editTaskCommand; + } +} +``` +###### \test\java\seedu\address\logic\commands\ImportCommandTest.java +``` java +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code ImportCommand}. + */ +public class ImportCommandTest { + + public static final String VALID_IMPORT_FILE_PATH = "src/data/ValidImport.xml"; + public static final String INVALID_IMPORT_FILE_PATH = "src/data/InValidImport.txt"; + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + @Rule + public final EventsCollectorRule eventsCollectorRule = new EventsCollectorRule(); + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private StorageManager storageManager; + + @Before + public void setUp() { + XmlAddressBookStorage addressBookStorage = new XmlAddressBookStorage(getTempFilePath("ab")); + JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); + storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + } + + private String getTempFilePath(String fileName) { + return testFolder.getRoot().getPath() + fileName; + } + + @Test + public void fileNotFound() { + ImportCommand importCommand = pathInCommand(INVALID_FILE); + assertCommandFailure(importCommand, model, ImportCommand.MESSAGE_INVALID_PATH); + } + + /** + * Returns an {@code ImportCommand} with parameters {@code filePath} + */ + private ImportCommand pathInCommand(String filePath) { + ImportCommand testCommand = new ImportCommand(filePath); + testCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + return testCommand; + } +} +``` +###### \test\java\seedu\address\logic\commands\ListCurrentTaskCommandTest.java +``` java +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCurrentTaskCommand. + */ +public class ListCurrentTaskCommandTest { + + private Model model; + private Model expectedModel; + private ListCurrentTaskCommand listCurrentTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + listCurrentTaskCommand = new ListCurrentTaskCommand(); + listCurrentTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(listCurrentTaskCommand, model, ListCurrentTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + new ListTaskCommand(); + assertCommandSuccess(listCurrentTaskCommand, model, ListCurrentTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} +``` +###### \test\java\seedu\address\logic\commands\ListTaskCommandTest.java +``` java +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTaskCommand. + */ +public class ListTaskCommandTest { + + private Model model; + private Model expectedModel; + private ListTaskCommand listTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + listTaskCommand = new ListTaskCommand(); + listTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(listTaskCommand, model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + new ListCurrentTaskCommand(); + assertCommandSuccess(listTaskCommand, model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} +``` +###### \test\java\seedu\address\logic\commands\SortTaskCommandTest.java +``` java +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTaskCommand. + */ +public class SortTaskCommandTest { + + private Model model; + private Model expectedModel; + private SortTaskCommand sortTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + sortTaskCommand = new SortTaskCommand(); + sortTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_sortsEverything() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nullList_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} +``` +###### \test\java\seedu\address\logic\parser\AddressBookParserTest.java +``` java + @Test + public void parseCommand_add_alias() throws Exception { + Person person = new PersonBuilder().build(); + AddCommand command = (AddCommand) parser.parseCommand(AddCommand.COMMAND_ALIAS + " " + + PersonUtil.getPersonDetails(person)); + assertEquals(new AddCommand(person), command); + } + + @Test + public void parseCommand_add_sign() throws Exception { + Person person = new PersonBuilder().build(); + AddCommand command = (AddCommand) parser.parseCommand(AddCommand.COMMAND_SIGN + " " + + PersonUtil.getPersonDetails(person)); + assertEquals(new AddCommand(person), command); + } + +``` +###### \test\java\seedu\address\logic\parser\AddressBookParserTest.java +``` java + @Test + public void parseCommand_clear_alias() throws Exception { + assertTrue(parser.parseCommand(ClearCommand.COMMAND_ALIAS) instanceof ClearCommand); + assertTrue(parser.parseCommand(ClearCommand.COMMAND_ALIAS + " 3") instanceof ClearCommand); + } + + @Test + public void parseCommand_delete() throws Exception { + DeleteCommand command = (DeleteCommand) parser.parseCommand( + DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); + assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); + } + +``` +###### \test\java\seedu\address\logic\parser\AddressBookParserTest.java +``` java + @Test + public void parseCommand_delete_alias() throws Exception { + DeleteCommand command = (DeleteCommand) parser.parseCommand( + DeleteCommand.COMMAND_ALIAS + " " + INDEX_FIRST_PERSON.getOneBased()); + assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); + } + + @Test + public void parseCommand_delete_sign() throws Exception { + DeleteCommand command = (DeleteCommand) parser.parseCommand( + DeleteCommand.COMMAND_SIGN + " " + INDEX_FIRST_PERSON.getOneBased()); + assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); + } + +``` +###### \test\java\seedu\address\logic\parser\AddressBookParserTest.java +``` java + @Test + public void parseCommand_redoCommandAlias_returnsRedoCommand() throws Exception { + assertTrue(parser.parseCommand(RedoCommand.COMMAND_ALIAS) instanceof RedoCommand); + assertTrue(parser.parseCommand(RedoCommand.COMMAND_ALIAS + " 1") instanceof RedoCommand); + } + +``` +###### \test\java\seedu\address\logic\parser\AddressBookParserTest.java +``` java + @Test + public void parseCommand_undoCommandAlias_returnsUndoCommand() throws Exception { + assertTrue(parser.parseCommand(UndoCommand.COMMAND_ALIAS) instanceof UndoCommand); + assertTrue(parser.parseCommand(UndoCommand.COMMAND_ALIAS + " 3") instanceof UndoCommand); + } + + @Test + public void parseCommand_unrecognisedInput_throwsParseException() throws Exception { + thrown.expect(ParseException.class); + thrown.expectMessage(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + parser.parseCommand(""); + } + + @Test + public void parseCommand_unknownCommand_throwsParseException() throws Exception { + thrown.expect(ParseException.class); + thrown.expectMessage(MESSAGE_UNKNOWN_COMMAND); + parser.parseCommand("unknownCommand"); + } +} +``` +###### \test\java\seedu\address\logic\parser\AddTaskCommandParserTest.java +``` java +public class AddTaskCommandParserTest { + private AddTaskCommandParser parser = new AddTaskCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Task expectedTask = new TaskBuilder().withTitle(VALID_TITLE_EXAM) + .withDesc(VALID_TASK_DESC_EXAM).withDeadline(VALID_DEADLINE_EXAM) + .withPriority(VALID_PRIORITY_EXAM).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + + DEADLINE_DESC_EXAM + PRIORITY_DESC_EXAM, new AddTaskCommand(expectedTask)); + + // multiple titles - last title accepted + assertParseSuccess(parser, TITLE_DESC_MARK + TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + DEADLINE_DESC_EXAM + + PRIORITY_DESC_EXAM, new AddTaskCommand(expectedTask)); + + // multiple taskDescs - last taskDesc accepted + assertParseSuccess(parser, TITLE_DESC_EXAM + TASK_DESC_DESC_MARK + TASK_DESC_DESC_EXAM + + DEADLINE_DESC_EXAM + PRIORITY_DESC_EXAM, new AddTaskCommand(expectedTask)); + + // multiple deadlines - last deadline accepted + assertParseSuccess(parser, TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + DEADLINE_DESC_MARK + + DEADLINE_DESC_EXAM + PRIORITY_DESC_EXAM, new AddTaskCommand(expectedTask)); + + // multiple priorities - last priority accepted + assertParseSuccess(parser, TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + DEADLINE_DESC_EXAM + + PRIORITY_DESC_MARK + PRIORITY_DESC_EXAM, new AddTaskCommand(expectedTask)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE); + + // missing title prefix + assertParseFailure(parser, VALID_TITLE_EXAM + TASK_DESC_DESC_EXAM + DEADLINE_DESC_EXAM + + PRIORITY_DESC_EXAM, expectedMessage); + + // missing taskDesc prefix + assertParseFailure(parser, TITLE_DESC_EXAM + VALID_TASK_DESC_EXAM + DEADLINE_DESC_EXAM + + PRIORITY_DESC_EXAM, expectedMessage); + + // missing deadline prefix + assertParseFailure(parser, TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + VALID_DEADLINE_EXAM + + PRIORITY_DESC_EXAM, expectedMessage); + + // missing priority prefix + assertParseFailure(parser, TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + DEADLINE_DESC_EXAM + + VALID_PRIORITY_EXAM, expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_TITLE_EXAM + VALID_TASK_DESC_EXAM + VALID_DEADLINE_EXAM + + VALID_PRIORITY_EXAM, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid title + assertParseFailure(parser, INVALID_TITLE_DESC + TASK_DESC_DESC_EXAM + DEADLINE_DESC_EXAM + + PRIORITY_DESC_EXAM, Title.MESSAGE_TITLE_CONSTRAINTS); + + // invalid taskDesc + assertParseFailure(parser, TITLE_DESC_EXAM + INVALID_TASK_DESC_DESC + DEADLINE_DESC_EXAM + + PRIORITY_DESC_EXAM, TaskDescription.MESSAGE_DESCRIPTION_CONSTRAINTS); + + // invalid deadline + assertParseFailure(parser, TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + INVALID_DEADLINE_DESC + + PRIORITY_DESC_EXAM, Deadline.MESSAGE_DEADLINE_CONSTRAINTS); + + // invalid priority + assertParseFailure(parser, TITLE_DESC_EXAM + TASK_DESC_DESC_EXAM + DEADLINE_DESC_EXAM + + INVALID_PRIORITY_DESC, Priority.MESSAGE_PRIORITY_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_TITLE_DESC + TASK_DESC_DESC_EXAM + DEADLINE_DESC_EXAM + + INVALID_PRIORITY_DESC, Title.MESSAGE_TITLE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + TASK_DESC_DESC_EXAM + + DEADLINE_DESC_EXAM + INVALID_PRIORITY_DESC, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE)); + } +} +``` +###### \test\java\seedu\address\logic\parser\DeleteTaskCommandParserTest.java +``` java +public class DeleteTaskCommandParserTest { + private DeleteTaskCommandParser parser = new DeleteTaskCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteCommand() { + assertParseSuccess(parser, "1", new DeleteTaskCommand(INDEX_FIRST_TASK)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE)); + } +} +``` +###### \test\java\seedu\address\logic\parser\ImportCommandParserTest.java +``` java +public class ImportCommandParserTest { + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + private ImportCommandParser parser = new ImportCommandParser(); + + @Test + public void parse_noFile_throwsParseException() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE); + assertParseFailure(parser, " ", expectedMessage); + } +} +``` +###### \test\java\seedu\address\testutil\EditTaskDescriptorBuilder.java +``` java +public class EditTaskDescriptorBuilder { + private EditTaskDescriptor descriptor; + + public EditTaskDescriptorBuilder() { + descriptor = new EditTaskDescriptor(); + } + + public EditTaskDescriptorBuilder(EditTaskDescriptor descriptor) { + this.descriptor = new EditTaskDescriptor(descriptor); + } + + /** + * Returns an {@code EditTaskDescriptor} with fields containing {@code task}'s details + */ + public EditTaskDescriptorBuilder(Task task) { + descriptor = new EditTaskDescriptor(); + descriptor.setTitle(task.getTitle()); + descriptor.setDescription(task.getTaskDesc()); + descriptor.setDeadline(task.getDeadline()); + descriptor.setPriority(task.getPriority()); + } + + /** + * Sets the {@code Title} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withTitle(String title) { + descriptor.setTitle(new Title(title)); + return this; + } + + /** + * Sets the {@code TaskDescription} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withDesc(String taskDesc) { + descriptor.setDescription(new TaskDescription(taskDesc)); + return this; + } + + /** + * Sets the {@code Deadline} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withDeadline(String deadline) { + descriptor.setDeadline(new Deadline(deadline)); + return this; + } + + /** + * Sets the {@code Priority} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withPriority(String priority) { + descriptor.setPriority(new Priority(priority)); + return this; + } + + public EditTaskDescriptor build() { + return descriptor; + } +} +``` +###### \test\java\seedu\address\testutil\TaskBuilder.java +``` java +/** + * A utility class to help with building Task objects. + */ +public class TaskBuilder { + + public static final String DEFAULT_TITLE = "Dance"; + public static final String DEFAULT_DESC = "Dance till I drop"; + public static final String DEFAULT_PRIORITY = "3"; + + private static LocalDate now = LocalDate.now(); + private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + public static final String DEFAULT_DEADLINE = now.format(formatter); + + private Title title; + private TaskDescription desc; + private Deadline deadline; + private Priority priority; + + public TaskBuilder() { + title = new Title(DEFAULT_TITLE); + desc = new TaskDescription(DEFAULT_DESC); + deadline = new Deadline(DEFAULT_DEADLINE); + priority = new Priority(DEFAULT_PRIORITY); + } + + /** + * Initializes the TaskBuilder with the data of {@code taskToCopy} + */ + public TaskBuilder(Task taskToCopy) { + title = taskToCopy.getTitle(); + desc = taskToCopy.getTaskDesc(); + deadline = taskToCopy.getDeadline(); + priority = taskToCopy.getPriority(); + } + + /** + * Sets the {@code Title} of the {@code Task} that we are building + */ + public TaskBuilder withTitle (String title) { + this.title = new Title(title); + return this; + } + + /** + * Sets the {@code TaskDescription} of the {@code Task} that we are building. + */ + public TaskBuilder withDesc(String desc) { + this.desc = new TaskDescription(desc); + return this; + } + + /** + * Sets the {@code Deadline} of the {@code Task} that we are building. + */ + public TaskBuilder withDeadline(String deadline) { + this.deadline = new Deadline(deadline); + return this; + } + + /** + * Sets the {@code Priority} of the {@code Task} that we are building. + */ + public TaskBuilder withPriority(String priority) { + this.priority = new Priority(priority); + return this; + } + + public Task build() { + return new Task(title, desc, deadline, priority); + } +} +``` +###### \test\java\seedu\address\testutil\TypicalTasks.java +``` java +public class TypicalTasks { + + private static LocalDate now = LocalDate.now(); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + private static String tutorialDeadline = now.plusDays(5).format(formatter); + private static String sleepDeadline = now.plusDays(6).format(formatter); + private static String lunchDeadline = now.plusDays(10).format(formatter); + private static String reportDeadline = now.plusDays(8).format(formatter); + private static String biddingDeadline = now.plusMonths(1).format(formatter); + + private static final Task TUTORIAL = new TaskBuilder().withTitle("Prepare Tut") + .withDesc("Prepare tutorial contents for friday Tutorial") + .withDeadline(tutorialDeadline).withPriority("1").build(); + private static final Task SLEEP = new TaskBuilder().withTitle("Sleep Early") + .withDesc("I need to sleep early before midnight today") + .withDeadline(sleepDeadline).withPriority("2").build(); + private static final Task LUNCH = new TaskBuilder().withTitle("Group Lunch") + .withDesc("Have lunch with the TA group") + .withDeadline(lunchDeadline).withPriority("3").build(); + private static final Task REPORT = new TaskBuilder().withTitle("Sem Report") + .withDesc("Prepare for end of semester report") + .withDeadline(reportDeadline).withPriority("2").build(); + private static final Task BIDDING = new TaskBuilder().withTitle("Bid Modules") + .withDesc("Prepare for bidding modules for the coming semester") + .withDeadline(biddingDeadline).withPriority("2").build(); + + private TypicalTasks() {} // prevents instantiation + + /** + * Returns an {@code AddressBook} with all the typical persons. + */ + public static AddressBook getTypicalAddressBook() { + AddressBook ab = new AddressBook(); + for (Task task : getTypicalTasks()) { + ab.addTask(task); + } + return ab; + } + + public static List<Task> getTypicalTasks() { + return new ArrayList<>(Arrays.asList(TUTORIAL, SLEEP, LUNCH, REPORT, BIDDING)); + } +} +``` diff --git a/collated/functional/Alaru.md b/collated/functional/Alaru.md index 227f95c7660c..3a6ad2ff8508 100644 --- a/collated/functional/Alaru.md +++ b/collated/functional/Alaru.md @@ -77,7 +77,7 @@ public class DeleteUtil { /** * Copies a file over. The new file will be binary equivalent to the original. */ - public static void copyFile(String origFile, File outputFile) throws IOException { + public static void copyFile(String origFile, File outputFile) throws IOException { byte[] buffer = new byte[4096]; createIfMissing(outputFile); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(origFile)); @@ -250,7 +250,7 @@ public class UpdateDisplayCommand extends UndoableCommand { public UpdateDisplayCommand(Index index, DisplayPic dp) { requireNonNull(index); requireNonNull(dp); - this.targetIndex = index; + targetIndex = index; this.dp = dp; } @@ -362,7 +362,7 @@ public class MarkCommandParser implements Parser<MarkCommand> { */ public static DisplayPic parseDisplayPic(String displayPic) throws IllegalValueException { - if (displayPic == null) { + if (displayPic.equals("")) { return new DisplayPic(); } else { String trimmedDisplayPath = displayPic.trim(); @@ -575,11 +575,9 @@ public class DisplayPic { public static final String MESSAGE_DISPLAY_PIC_NO_EXTENSION = "The filepath should point to a file with an extension."; - public final String originalPath; private String value; public DisplayPic() { - this.originalPath = DEFAULT_DISPLAY_PIC; this.value = DEFAULT_DISPLAY_PIC; } @@ -593,7 +591,6 @@ public class DisplayPic { checkArgument(DisplayPicStorage.isValidPath(filePath), MESSAGE_DISPLAY_PIC_NONEXISTENT_CONSTRAINTS); checkArgument(DisplayPicStorage.hasValidExtension(filePath), MESSAGE_DISPLAY_PIC_NO_EXTENSION); checkArgument(DisplayPicStorage.isValidImage(filePath), MESSAGE_DISPLAY_PIC_NOT_IMAGE); - this.originalPath = filePath; this.value = filePath; } @@ -601,11 +598,11 @@ public class DisplayPic { * Saves the display picture to the specified storage location. */ public void saveDisplay(String personDetails) throws IllegalValueException { - if (originalPath.equals(DEFAULT_DISPLAY_PIC)) { + if (value.equals(DEFAULT_DISPLAY_PIC)) { return; } - String fileType = FileUtil.getFileType(originalPath); - String uniqueFileName = DisplayPicStorage.saveDisplayPic(personDetails, originalPath, fileType); + String fileType = FileUtil.getFileType(value); + String uniqueFileName = DisplayPicStorage.saveDisplayPic(personDetails, value, fileType); this.value = DEFAULT_IMAGE_LOCATION + uniqueFileName + '.' + fileType; } @@ -648,7 +645,8 @@ public class IllegalMarksException extends IllegalArgumentException { ``` java public class Participation { - public static final String MESSAGE_PARTICIPATION_CONSTRAINTS = "Participation marks cannot be negative or over 100!"; + public static final String MESSAGE_PARTICIPATION_CONSTRAINTS = + "Participation marks cannot be negative or over 100!"; public static final String UI_DISPLAY_HEADER = "Participation marks: "; public final Integer threshold; @@ -684,6 +682,11 @@ public class Participation { return (value >= threshold); } + /** + * Validates the participation mark + * @param value + * @return true if it is valid + */ public static boolean isValidParticipation(String value) { requireNonNull(value); try { diff --git a/collated/functional/JoonKai1995.md b/collated/functional/JoonKai1995.md index 31b50b66b6bd..50fc0641be36 100644 --- a/collated/functional/JoonKai1995.md +++ b/collated/functional/JoonKai1995.md @@ -1,471 +1,505 @@ # JoonKai1995 -###### /resources/view/CalendarTaskCard.fxml -``` fxml -<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="17.0" prefWidth="126.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> - <children> - <Label fx:id="desc" alignment="CENTER_LEFT" styleClass="label-small" prefHeight="10.0" prefWidth="147.0" scaleShape="false" text="Label"> - <font> - <Font size="11.0" /> - </font></Label> - </children> -</HBox> -``` -###### /resources/view/CalendarNode.fxml -``` fxml -<Pane prefHeight="95.0" prefWidth="147.0" styleClass="pane-with-border" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> - <children> - <VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="90.0" prefWidth="146.0"> - <children> - <Label fx:id="date" text="Label" styleClass="label-date" > - <VBox.margin> - <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> - </VBox.margin> - <font> - <Font size="10.0" /> - </font> - </Label> - <ListView fx:id="tasks" prefHeight="20.0" prefWidth="70.0" styleClass="list-view-calendar" VBox.vgrow="ALWAYS"> - <VBox.margin> - <Insets bottom="1.0" left="2.0" right="1.0" top="1.0" /> - </VBox.margin> - </ListView> - </children> - </VBox> - </children> -</Pane> -``` -###### /resources/view/CalendarView.fxml -``` fxml -<VBox id="calendarVBox" fx:id="calendarVBox" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> - <children> - <HBox alignment="CENTER" spacing="10"> - <children> - <Button fx:id="previousMonth" styleClass="button" onAction="#handlePreviousButtonAction" text="<<" HBox.hgrow="SOMETIMES"> - </Button> - <Label fx:id="calendarTitle" styleClass="calendar_title" text="\$calendarTitle" HBox.hgrow="SOMETIMES" /> - <Button fx:id="nextMonth" styleClass="button" onAction="#handleNextButtonAction" text=">>" HBox.hgrow="SOMETIMES"> - </Button> - </children> - <padding> - <Insets bottom="5" left="10" right="10" top="5" /> - </padding> - <padding> - <Insets bottom="5" left="10" right="10" top="5" /> - </padding> - </HBox> - <GridPane alignment="CENTER" minHeight="-Infinity" minWidth="-Infinity"> - <columnConstraints> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> - <children> - <Label alignment="CENTER" contentDisplay="CENTER" styleClass="calendar_text" text="Sunday" textAlignment="CENTER"> - <GridPane.margin> - <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> - </GridPane.margin> - </Label> - <Label text="Monday" styleClass="calendar_text" GridPane.columnIndex="1"> - <GridPane.margin> - <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> - </GridPane.margin> - </Label> - <Label text="Tuesday" styleClass="calendar_text" GridPane.columnIndex="2"> - <GridPane.margin> - <Insets bottom="18.0" left="18.0" right="18.0" top="18.0" /> - </GridPane.margin> - </Label> - <Label alignment="CENTER" text="Wednesday" styleClass="calendar_text" GridPane.columnIndex="3"> - <GridPane.margin> - <Insets bottom="10.0" left="8.0" right="8.0" top="10.0" /> - </GridPane.margin> - </Label> - <Label text="Thursday" styleClass="calendar_text" GridPane.columnIndex="4"> - <GridPane.margin> - <Insets bottom="15.0" left="15.0" right="15.0" top="15.0" /> - </GridPane.margin> - </Label> - <Label text="Friday" styleClass="calendar_text" GridPane.columnIndex="5"> - <GridPane.margin> - <Insets bottom="20.0" left="23.0" right="23.0" top="20.0" /> - </GridPane.margin> - </Label> - <Label text="Saturday" styleClass="calendar_text" GridPane.columnIndex="6"> - <GridPane.margin> - <Insets bottom="15.0" left="15.0" right="15.0" top="15.0" /> - </GridPane.margin> - </Label> - </children> - <VBox.margin> - <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> - </VBox.margin> - </GridPane> - <GridPane fx:id="calendar" alignment="CENTER" styleClass="calendar-grid-pane" gridLinesVisible="true" minHeight="-Infinity" minWidth="-Infinity" VBox.vgrow="SOMETIMES"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> - </rowConstraints> - <VBox.margin> - <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> - </VBox.margin> - </GridPane> - </children> -</VBox> -``` -###### /java/seedu/address/ui/CalendarView.java +###### \java\seedu\address\logic\commands\AddTaskCommand.java ``` java /** - * The Calendar of the app. + * Adds a task to the To-do list and calendar. */ -public class CalendarView extends UiPart<Region> { +public class AddTaskCommand extends UndoableCommand { - private static final String FXML = "CalendarView.fxml"; - private final Logger logger = LogsCenter.getLogger(this.getClass()); - private ArrayList<AnchorPane> allCalendarDays = new ArrayList<>(35); - private YearMonth currentYearMonth; - private ObservableList<Task>[][] tasks; - private int currentMonth = 0; + public static final String COMMAND_WORD = "addTask"; + public static final String COMMAND_ALIAS = "at"; - @FXML - private VBox calendarVBox; - @FXML - private GridPane calendar; - @FXML - private Button previousMonth; - @FXML - private Button nextMonth; - @FXML - private Label calendarTitle; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the todo list and calendar. " + + "Parameters: " + + PREFIX_TITLE + "TITLE " + + PREFIX_TASK_DESC + "TASK " + + PREFIX_DEADLINE + "DEADLINE " + + PREFIX_PRIORITY + "PRIORITY LEVEL\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TITLE + "Dance " + + PREFIX_TASK_DESC + "Dance till I drop " + + PREFIX_DEADLINE + "20-03-2018 " + + PREFIX_PRIORITY + "1"; + + public static final String MESSAGE_SUCCESS = "New Task added: %1$s"; + + private final Task toAdd; /** - * Creates the calendar of the app + * Creates an AddTaskCommand to add the specified {@code Task} */ - public CalendarView(ObservableList<Task>[][] tasksArray) { - super(FXML); - this.tasks = tasksArray; - YearMonth yearMonth = YearMonth.now(); - currentYearMonth = yearMonth; - initCalendar(yearMonth); + public AddTaskCommand(Task task) { + requireNonNull(task); + toAdd = task; } + @Override + public CommandResult executeUndoableCommand() throws CommandException { + requireNonNull(model); + model.addTask(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + + + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddTaskCommand // instanceof handles nulls + && toAdd.equals(((AddTaskCommand) other).toAdd)); + } +} + +``` +###### \java\seedu\address\logic\parser\AddTaskCommandParser.java +``` java +/** + * Parses input arguments and creates a new AddTaskCommand object + */ +public class AddTaskCommandParser implements Parser<AddTaskCommand> { + /** - * Create rows and columns with anchor panes for the calendar + * Parses the given {@code String} of arguments in the context of the AddCommand + * and returns an AddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format */ - private void initCalendar(YearMonth yearMonth) { + public AddTaskCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_TASK_DESC, + PREFIX_DEADLINE, PREFIX_PRIORITY); - for (int i = 0; i < 5; i++) { - for (int j = 0; j < 7; j++) { - AnchorPane ap = new AnchorPane(); - ap.setPrefSize(300, 300); - calendar.add(ap, j, i); - allCalendarDays.add(ap); - } + if (!arePrefixesPresent(argMultimap, PREFIX_TITLE, PREFIX_TASK_DESC, + PREFIX_DEADLINE, PREFIX_PRIORITY) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE)); + } + + try { + Title title = ParserUtil.parseTaskTitle(argMultimap.getValue(PREFIX_TITLE)).get(); + TaskDescription taskDescription = ParserUtil + .parseTaskDescription(argMultimap.getValue(PREFIX_TASK_DESC)).get(); + Deadline deadline = + ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_DEADLINE)).get(); + Priority priority = ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY)).get(); + + Task task = new Task(title, taskDescription, deadline, priority); + + return new AddTaskCommand(task); + } catch (IllegalValueException ive) { + throw new ParseException(ive.getMessage(), ive); } - setCalendarDays(yearMonth); } /** - * Set the days of the calendar to display the correct date - * @param yearMonth year and month of the current month + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. */ - public void setCalendarDays(YearMonth yearMonth) { - LocalDate calendarDate = LocalDate.of(yearMonth.getYear(), yearMonth.getMonthValue(), 1); - while (!calendarDate.getDayOfWeek().toString().equals("SUNDAY")) { - calendarDate = calendarDate.minusDays(1); - } - for (AnchorPane ap : allCalendarDays) { - if (ap.getChildren().size() != 0) { - ap.getChildren().clear(); - } - String txt = String.valueOf(calendarDate.getDayOfMonth()); - try { - if (calendarDate.getMonthValue() == currentYearMonth.getMonthValue()) { - CalendarNode node = new CalendarNode(txt, tasks[currentMonth][calendarDate.getDayOfMonth()]); - ap.getChildren().add(node.getRoot()); - } else if (calendarDate.getMonthValue() > currentYearMonth.getMonthValue()) { - CalendarNode node = new CalendarNode(txt, tasks[currentMonth + 1][calendarDate.getDayOfMonth()]); - ap.getChildren().add(node.getRoot()); - } else { - CalendarNode node = new CalendarNode(txt, tasks[currentMonth - 1][calendarDate.getDayOfMonth()]); - ap.getChildren().add(node.getRoot()); - } - calendarDate = calendarDate.plusDays(1); - } catch (ArrayIndexOutOfBoundsException oob) { - CalendarNode node = new CalendarNode(txt, FXCollections.observableArrayList()); - ap.getChildren().add(node.getRoot()); - calendarDate = calendarDate.plusDays(1); - } + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} + +``` +###### \java\seedu\address\model\ModelManager.java +``` java + @Override + public synchronized void addTask(Task task) { + addressBook.addTask(task); + updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + indicateAddressBookChanged(); + } + +``` +###### \java\seedu\address\model\person\MatriculationNumber.java +``` java +/** + * Represents a Person's matriculation number in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidMatricNumber(String)} + */ +public class MatriculationNumber { + + + public static final String MESSAGE_MATRIC_NUMBER_CONSTRAINTS = + "The first character of the matriculation number should be either an 'A' or 'U'," + + " followed by 7 digits and end with a capital letter."; + public static final String MATRIC_NUMBER_VALIDATION_REGEX_FIRST = "[AU]{1}"; + public static final String MATRIC_NUMBER_VALIDATION_REGEX_SECOND = "\\d{7}"; + public static final String MATRIC_NUMBER_VALIDATION_REGEX_LAST = "[A-Z]{1}"; + public final String value; + + /** + * Constructs a {@code MatriculationNumber}. + * + * @param matricNumber A valid matriculation number. + */ + public MatriculationNumber(String matricNumber) { + requireNonNull(matricNumber); + checkArgument(isValidMatricNumber(matricNumber), MESSAGE_MATRIC_NUMBER_CONSTRAINTS); + this.value = matricNumber; + } + + /** + * Returns true if a given string is a valid person matriculation number. + */ + public static boolean isValidMatricNumber(String test) { + if (test.length() != 9) { + return false; } - calendarTitle.setText(yearMonth.getMonth().toString() + " " + String.valueOf(yearMonth.getYear())); + String firstCharacter = test.substring(0, 1); + String nextCharacters = test.substring(1, test.length() - 1); + String lastCharacter = test.substring(test.length() - 1, test.length()); + return firstCharacter.matches(MATRIC_NUMBER_VALIDATION_REGEX_FIRST) + && nextCharacters.matches(MATRIC_NUMBER_VALIDATION_REGEX_SECOND) + && lastCharacter.matches(MATRIC_NUMBER_VALIDATION_REGEX_LAST); } + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MatriculationNumber // instanceof handles nulls + && this.value.equals(((MatriculationNumber) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} +``` +###### \java\seedu\address\model\task\Priority.java +``` java +/** + * Represents a Task's priority in the todo list and calendar. + * Guarantees: immutable; is valid as declared in {@link #isValidPriority(String)} + */ +public class Priority { + + + public static final String MESSAGE_PRIORITY_CONSTRAINTS = + "Priority value input can only be a value from 1 to 3. 1 being lowest priority and 3 being highest."; + public static final String PRIORITY_VALIDATION_REGEX = "[1-3]{1}"; + public final String priority; + public final int value; + /** - * Refreshes the calendar with new information. + * Constructs a {@code Phone}. + * + * @param priorityValue A valid priority value. */ - public void refreshCalendar() { - initCalendar(currentYearMonth); + public Priority(String priorityValue) { + requireNonNull(priorityValue); + checkArgument(isValidPriority(priorityValue), MESSAGE_PRIORITY_CONSTRAINTS); + this.priority = priorityValue; + this.value = Integer.parseInt(priorityValue); } - @FXML /** - * Move the month back by one. Repopulate the calendar with the correct dates. + * Returns true if a given string is a valid task priority. */ - private void handlePreviousButtonAction() { - currentYearMonth = currentYearMonth.minusMonths(1); - currentMonth--; - setCalendarDays(currentYearMonth); + public static boolean isValidPriority(String test) { + return test.matches(PRIORITY_VALIDATION_REGEX); + } + + @Override + public String toString() { + return priority; } - @FXML + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Priority // instanceof handles nulls + && this.priority.equals(((Priority) other).priority)); // state check + } + + @Override + public int hashCode() { + return priority.hashCode(); + } + +} +``` +###### \java\seedu\address\model\task\Task.java +``` java +/** + * Represents a Person in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Task implements Comparable<Task> { + + private final Title title; + private final TaskDescription taskDesc; + private final Deadline deadline; + private final Priority priority; + /** - * Move the month forward by one. Repopulate the calendar with the correct dates. + * Every field must be present and not null. */ - private void handleNextButtonAction() { - currentYearMonth = currentYearMonth.plusMonths(1); - currentMonth++; - setCalendarDays(currentYearMonth); + public Task(Title title, TaskDescription taskDesc, Deadline deadline, Priority priority) { + requireAllNonNull(title, taskDesc, deadline, priority); + this.title = title; + this.taskDesc = taskDesc; + this.deadline = deadline; + this.priority = priority; } - public ArrayList<AnchorPane> getAllCalendarDays() { - return allCalendarDays; + public Title getTitle() { + return title; + } + public TaskDescription getTaskDesc() { + return taskDesc; } - public void setAllCalendarDays(ArrayList<AnchorPane> allCalendarDays) { - this.allCalendarDays = allCalendarDays; + public Deadline getDeadline() { + return deadline; } -} -``` -###### /java/seedu/address/ui/CalendarTaskCard.java -``` java -/** - * A UI component that displays compressed information of a {@code Task} on the calendar. - */ -public class CalendarTaskCard extends UiPart<Region> { - private static final String FXML = "CalendarTaskCard.fxml"; + public Priority getPriority() { + return priority; + } - public final Task task; + public int getDeadlineDay() { + return deadline.day; + } - @FXML - private Label desc; + public int getDeadlineYear() { + return deadline.year; + } - public CalendarTaskCard(Task task) { - super(FXML); - this.task = task; - if (task.getTitle().toString().length() <= 20) { - desc.setText(task.getTitle().toString()); - } else { - String text = task.getTitle().toString().substring(0, 20) + "..."; - desc.setText(text); - } - if (task.getPriority().value == 1) { - desc.getStyleClass().clear(); - desc.getStyleClass().add("label-small-green"); - } else if (task.getPriority().value == 2) { - desc.getStyleClass().clear(); - desc.getStyleClass().add("label-small-yellow"); - } else { - desc.getStyleClass().clear(); - desc.getStyleClass().add("label-small-red"); - } + public int getDeadlineMonth() { + return deadline.month; } @Override public boolean equals(Object other) { - // short circuit if same object if (other == this) { return true; } - // instanceof handles nulls - if (!(other instanceof CalendarTaskCard)) { + if (!(other instanceof seedu.address.model.task.Task)) { return false; } - // state check - CalendarTaskCard card = (CalendarTaskCard) other; - return task.equals(card.task); + seedu.address.model.task.Task otherTask = (seedu.address.model.task.Task) other; + return otherTask.getTitle().equals(this.getTitle()) + && otherTask.getTaskDesc().equals(this.getTaskDesc()) + && otherTask.getDeadline().equals(this.getDeadline()) + && otherTask.getPriority().equals(this.getPriority()); } -} + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(taskDesc, deadline, priority); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(" Title: ") + .append(getTitle()) + .append(" Task TaskDescription: ") + .append(getTaskDesc()) + .append(" Deadline: ") + .append(getDeadline()) + .append(" Calculated Priority: ") + .append(getPriority()); + return builder.toString(); + } + ``` -###### /java/seedu/address/ui/CalendarNode.java +###### \java\seedu\address\model\task\TaskDescription.java ``` java /** - * Create an anchor pane that can store additional data. + * Represents a short description of a todo task */ -public class CalendarNode extends UiPart<Region> { +public class TaskDescription { - private static final String FXML = "CalendarNode.fxml"; + public static final String MESSAGE_DESCRIPTION_CONSTRAINTS = + "Task description can take any values, and it should not be blank"; - @FXML - private Label date; + /* + * The first character of the description must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String DESCRIPTION_VALIDATION_REGEX = "[^\\s].*"; - @FXML - private ListView<CalendarTaskCard> tasks; + public final String value; + + public final String shortDesc; /** - * Create a calendar node. - * @param txt the date of the node - * @param taskList the task list linked to it + * Constructs an {@code TaskDescription}. + * + * @param description A valid address. */ - public CalendarNode(String txt, ObservableList<Task> taskList) { - super(FXML); - date.setText(txt); - setConnections(taskList); - } - - private void setConnections(ObservableList<Task> taskList) { - ObservableList<CalendarTaskCard> mappedList = EasyBind.map( - taskList, (task) -> new CalendarTaskCard(task)); - tasks.setItems(mappedList); - tasks.setCellFactory(listView -> new TasksCell()); + public TaskDescription(String description) { + assert description != null : MESSAGE_DESCRIPTION_CONSTRAINTS; + checkArgument(isValidDescription(description)); + this.value = description; + if (value.length() <= 20) { + shortDesc = value; + } else { + shortDesc = value.substring(0, 20) + "..."; + } } /** - * Custom {@code ListCell} that displays the graphics of a {@code CalendarTaskCard}. + * Returns true if a given string is a valid person email. */ - class TasksCell extends ListCell<CalendarTaskCard> { + public static boolean isValidDescription(String test) { + return test.matches(DESCRIPTION_VALIDATION_REGEX); + } - @Override - protected void updateItem(CalendarTaskCard task, boolean empty) { - super.updateItem(task, empty); + @Override + public String toString() { + return value; + } - if (empty || task == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(task.getRoot()); - } - } + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskDescription // instanceof handles nulls + && this.value.equals(((TaskDescription) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); } } ``` -###### /java/seedu/address/logic/parser/AddTaskCommandParser.java +###### \java\seedu\address\model\task\UniqueTaskList.java ``` java +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.task.exceptions.TaskNotFoundException; + /** - * Parses input arguments and creates a new AddTaskCommand object + * A list of tasks that enforces uniqueness between its elements and does not allow nulls. + * + * Supports a minimal set of list operations. + * + * @see Task#equals(Object) */ -public class AddTaskCommandParser implements Parser<AddTaskCommand> { +public class UniqueTaskList implements Iterable<Task> { - /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public AddTaskCommand parse(String args) throws ParseException { - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_TASK_DESC, - PREFIX_DEADLINE, PREFIX_PRIORITY); + private final ObservableList<Task> internalList = FXCollections.observableArrayList(); - if (!arePrefixesPresent(argMultimap, PREFIX_TITLE, PREFIX_TASK_DESC, - PREFIX_DEADLINE, PREFIX_PRIORITY) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE)); + private final ObservableList<Task>[][] calendarList = new ObservableList[7][32]; + + private Date dateNow = new Date(); + + private LocalDate now = dateNow.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + + private int monthNow = now.getMonthValue(); + + public UniqueTaskList() { + super(); + for (int i = 0; i < 7; i++) { + for (int j = 0; j < 32; j++) { + calendarList[i][j] = FXCollections.observableArrayList(); + } } + } - try { - Title title = ParserUtil.parseTaskTitle(argMultimap.getValue(PREFIX_TITLE)).get(); - TaskDescription taskDescription = ParserUtil - .parseTaskDescription(argMultimap.getValue(PREFIX_TASK_DESC)).get(); - Deadline deadline = - ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_DEADLINE)).get(); - Priority priority = ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY)).get(); + /** + * Returns true if the list contains an equivalent task as the given argument. + */ + public boolean contains(Task toCheck) { + requireNonNull(toCheck); + return internalList.contains(toCheck); + } - Task task = new Task(title, taskDescription, deadline, priority); + /** + * Adds a task to the list. + * + */ + public void add(Task toAdd) { + requireNonNull(toAdd); + internalList.add(toAdd); + int diff = toAdd.getDeadline().diff; + calendarList[diff][toAdd.getDeadlineDay()].add(toAdd); + Collections.sort(calendarList[diff][toAdd.getDeadlineDay()]); + } + +``` +###### \java\seedu\address\model\task\UniqueTaskList.java +``` java + public void setTasks(UniqueTaskList replacement) { + this.internalList.setAll(replacement.internalList); + for (int i = 0; i < 7; i++) { + for (int j = 0; j < 32; j++) { + calendarList[i][j].setAll(replacement.calendarList[i][j]); + } + } + } - return new AddTaskCommand(task); - } catch (IllegalValueException ive) { - throw new ParseException(ive.getMessage(), ive); + public void setTasks(List<Task> tasks) { + requireAllNonNull(tasks); + final UniqueTaskList replacement = new UniqueTaskList(); + for (final Task task : tasks) { + replacement.add(task); } + setTasks(replacement); } /** - * Returns true if none of the prefixes contains empty {@code Optional} values in the given - * {@code ArgumentMultimap}. + * Returns the backing list as an unmodifiable {@code ObservableList}. */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + public ObservableList<Task> asObservableList() { + return FXCollections.unmodifiableObservableList(internalList); } -} - -``` -###### /java/seedu/address/logic/commands/AddTaskCommand.java -``` java -/** - * Adds a task to the To-do list and calendar. - */ -public class AddTaskCommand extends UndoableCommand { - - public static final String COMMAND_WORD = "addTask"; - public static final String COMMAND_ALIAS = "at"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the todo list and calendar. " - + "Parameters: " - + PREFIX_TITLE + "TITLE " - + PREFIX_TASK_DESC + "TASK " - + PREFIX_DEADLINE + "DEADLINE " - + PREFIX_PRIORITY + "PRIORITY LEVEL\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_TITLE + "Dance " - + PREFIX_TASK_DESC + "Dance till I drop " - + PREFIX_DEADLINE + "20-03-2018 " - + PREFIX_PRIORITY + "1"; - - public static final String MESSAGE_SUCCESS = "New Task added: %1$s"; - - private final Task toAdd; - /** - * Creates an AddTaskCommand to add the specified {@code Task} + * Returns the calendarList array for tasks + * @return */ - public AddTaskCommand(Task task) { - requireNonNull(task); - toAdd = task; + public ObservableList<Task>[][] asCalendarList() { + return calendarList; } @Override - public CommandResult executeUndoableCommand() throws CommandException { - requireNonNull(model); - model.addTask(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - - + public Iterator<Task> iterator() { + return internalList.iterator(); } @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof AddTaskCommand // instanceof handles nulls - && toAdd.equals(((AddTaskCommand) other).toAdd)); + || (other instanceof UniqueTaskList // instanceof handles nulls + && this.internalList.equals(((UniqueTaskList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); } } ``` -###### /java/seedu/address/storage/XmlAdaptedTask.java +###### \java\seedu\address\storage\XmlAdaptedTask.java ``` java /** - * JAXB-friendly version of the Person. + * JAXB-friendly version of the Task. */ public class XmlAdaptedTask { @@ -501,7 +535,7 @@ public class XmlAdaptedTask { * @param source future changes to this will not affect the created XmlAdaptedPerson */ public XmlAdaptedTask (Task source) { - title = source.getTitle().toString(); + title = source.getTitle().value; taskDescription = source.getTaskDesc().value; deadline = source.getDeadline().dateString; priority = source.getPriority().priority; @@ -516,9 +550,8 @@ public class XmlAdaptedTask { if (this.title == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, - Title.class.getSimpleName())); + Title.class.getSimpleName())); } - if (!Title.isValidTitle(this.title)) { throw new IllegalValueException(Title.MESSAGE_TITLE_CONSTRAINTS); } @@ -549,222 +582,12 @@ public class XmlAdaptedTask { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Priority.class.getSimpleName())); } - if (!Priority.isValidPriority(this.priority)) { - throw new IllegalValueException(Priority.MESSAGE_PRIORITY_CONSTRAINTS); - } - final Priority priority = new Priority(this.priority); - - return new Task(title, taskDesc, deadline, priority); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof XmlAdaptedTask)) { - return false; - } - - XmlAdaptedTask otherTask = (XmlAdaptedTask) other; - return Objects.equals(title, otherTask.title) - && Objects.equals(taskDescription, otherTask.taskDescription) - && Objects.equals(deadline, otherTask.deadline) - && Objects.equals(priority, otherTask.priority); - } -} -``` -###### /java/seedu/address/storage/XmlSerializableAddressBook.java -``` java - @Override - public ObservableList<Task> getTaskList() { - final ObservableList<Task> tasks = this.tasks.stream().map(p -> { - try { - return p.toModelType(); - } catch (IllegalValueException e) { - return null; - } - }).collect(Collectors.toCollection(FXCollections::observableArrayList)); - return FXCollections.unmodifiableObservableList(tasks); - } -``` -###### /java/seedu/address/storage/XmlSerializableAddressBook.java -``` java - @Override - public ObservableList<Task>[][] getCalendarList() { - return null; - } - - @Override - public ObservableList<Tag> getTagList() { - final ObservableList<Tag> tags = this.tags.stream().map(t -> { - try { - return t.toModelType(); - } catch (IllegalValueException e) { - return null; - } - }).collect(Collectors.toCollection(FXCollections::observableArrayList)); - return FXCollections.unmodifiableObservableList(tags); - } - - @Override - public ObservableList<String> getItemList() { - final ObservableList<String> items = this.items.stream().map(it -> { - try { - return it.toModelType(); - } catch (IllegalValueException e) { - return null; - } - }).collect(Collectors.toCollection(FXCollections::observableArrayList)); - return FXCollections.unmodifiableObservableList(items); - } -} -``` -###### /java/seedu/address/model/person/MatriculationNumber.java -``` java -/** - * Represents a Person's matriculation number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidMatricNumber(String)} - */ -public class MatriculationNumber { - - - public static final String MESSAGE_MATRIC_NUMBER_CONSTRAINTS = - "The first character of the matriculation number should be either an 'A' or 'U'," - + " followed by 7 digits and end with a capital letter."; - public static final String MATRIC_NUMBER_VALIDATION_REGEX_FIRST = "[AU]{1}"; - public static final String MATRIC_NUMBER_VALIDATION_REGEX_SECOND = "\\d{7}"; - public static final String MATRIC_NUMBER_VALIDATION_REGEX_LAST = "[A-Z]{1}"; - public final String value; - - /** - * Constructs a {@code MatriculationNumber}. - * - * @param matricNumber A valid matriculation number. - */ - public MatriculationNumber(String matricNumber) { - requireNonNull(matricNumber); - checkArgument(isValidMatricNumber(matricNumber), MESSAGE_MATRIC_NUMBER_CONSTRAINTS); - this.value = matricNumber; - } - - /** - * Returns true if a given string is a valid person matriculation number. - */ - public static boolean isValidMatricNumber(String test) { - if (test.length() != 9) { - return false; - } - String firstCharacter = test.substring(0, 1); - String nextCharacters = test.substring(1, test.length() - 1); - String lastCharacter = test.substring(test.length() - 1, test.length()); - return firstCharacter.matches(MATRIC_NUMBER_VALIDATION_REGEX_FIRST) - && nextCharacters.matches(MATRIC_NUMBER_VALIDATION_REGEX_SECOND) - && lastCharacter.matches(MATRIC_NUMBER_VALIDATION_REGEX_LAST); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof MatriculationNumber // instanceof handles nulls - && this.value.equals(((MatriculationNumber) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} -``` -###### /java/seedu/address/model/ModelManager.java -``` java - @Override - public synchronized void addTask(Task task) { - addressBook.addTask(task); - updateFilteredTaskList(PREDICATE_SHOW_ALL_CURRENT_TASKS); - indicateAddressBookChanged(); - } -``` -###### /java/seedu/address/model/ModelManager.java -``` java - @Override - public synchronized void addDeleteItem(String filepath) { - addressBook.addDeleteItem(filepath); - indicateAddressBookChanged(); - } - - @Override - public synchronized void clearDeleteItems() { - addressBook.clearItems(); - indicateAddressBookChanged(); - } - - @Override - public void updatePerson(Person target, Person editedPerson) - throws DuplicatePersonException, PersonNotFoundException { - requireAllNonNull(target, editedPerson); - - addressBook.updatePerson(target, editedPerson); - indicateAddressBookChanged(); - } - -``` -###### /java/seedu/address/model/task/Task.java -``` java -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Task implements Comparable<Task> { - - private final Title title; - private final TaskDescription taskDesc; - private final Deadline deadline; - private final Priority priority; - - /** - * Every field must be present and not null. - */ - public Task(Title title, TaskDescription taskDesc, Deadline deadline, Priority priority) { - requireAllNonNull(title, taskDesc, deadline, priority); - this.title = title; - this.taskDesc = taskDesc; - this.deadline = deadline; - this.priority = priority; - } - - public Title getTitle() { - return title; - } - public TaskDescription getTaskDesc() { - return taskDesc; - } - - public Deadline getDeadline() { - return deadline; - } - - public Priority getPriority() { - return priority; - } - - public int getDeadlineDay() { - return deadline.day; - } - - public int getDeadlineYear() { - return deadline.year; - } + if (!Priority.isValidPriority(this.priority)) { + throw new IllegalValueException(Priority.MESSAGE_PRIORITY_CONSTRAINTS); + } + final Priority priority = new Priority(this.priority); - public int getDeadlineMonth() { - return deadline.month; + return new Task(title, taskDesc, deadline, priority); } @Override @@ -773,333 +596,422 @@ public class Task implements Comparable<Task> { return true; } - if (!(other instanceof seedu.address.model.person.Person)) { + if (!(other instanceof XmlAdaptedTask)) { return false; } - seedu.address.model.task.Task otherTask = (seedu.address.model.task.Task) other; - return otherTask.getTitle().equals(this.getTitle()) - && otherTask.getTaskDesc().equals(this.getTaskDesc()) - && otherTask.getDeadline().equals(this.getDeadline()) - && otherTask.getPriority().equals(this.getPriority()); + XmlAdaptedTask otherTask = (XmlAdaptedTask) other; + return Objects.equals(title, otherTask.title) + && Objects.equals(taskDescription, otherTask.taskDescription) + && Objects.equals(deadline, otherTask.deadline) + && Objects.equals(priority, otherTask.priority); } - +} +``` +###### \java\seedu\address\storage\XmlSerializableAddressBook.java +``` java @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(taskDesc, deadline, priority); + public ObservableList<Task> getTaskList() { + final ObservableList<Task> tasks = this.tasks.stream().map(p -> { + try { + return p.toModelType(); + } catch (IllegalValueException e) { + return null; + } + }).collect(Collectors.toCollection(FXCollections::observableArrayList)); + return FXCollections.unmodifiableObservableList(tasks); + } +``` +###### \java\seedu\address\storage\XmlSerializableAddressBook.java +``` java + @Override + public ObservableList<Task>[][] getCalendarList() { + return null; } @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(" Title: ") - .append(getTitle()) - .append(" Task TaskDescription: ") - .append(getTaskDesc()) - .append(" Deadline: ") - .append(getDeadline()) - .append(" Calculated Priority: ") - .append(getPriority()); - return builder.toString(); + public ObservableList<Tag> getTagList() { + final ObservableList<Tag> tags = this.tags.stream().map(t -> { + try { + return t.toModelType(); + } catch (IllegalValueException e) { + return null; + } + }).collect(Collectors.toCollection(FXCollections::observableArrayList)); + return FXCollections.unmodifiableObservableList(tags); } + @Override - public int compareTo(Task task) { - return task.getPriority().value - this.getPriority().value; + public ObservableList<String> getItemList() { + final ObservableList<String> items = this.items.stream().map(it -> { + try { + return it.toModelType(); + } catch (IllegalValueException e) { + return null; + } + }).collect(Collectors.toCollection(FXCollections::observableArrayList)); + return FXCollections.unmodifiableObservableList(items); } } ``` -###### /java/seedu/address/model/task/UniqueTaskList.java +###### \java\seedu\address\ui\CalendarNode.java ``` java -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.task.exceptions.TaskNotFoundException; - /** - * A list of tasks that enforces uniqueness between its elements and does not allow nulls. - * - * Supports a minimal set of list operations. - * - * @see Task#equals(Object) + * Create an anchor pane that can store additional data. */ -public class UniqueTaskList implements Iterable<Task> { - - private final ObservableList<Task> internalList = FXCollections.observableArrayList(); - - private final ObservableList<Task>[][] calendarList = new ObservableList[7][32]; - - private Date dateNow = new Date(); +public class CalendarNode extends UiPart<Region> { - private LocalDate now = dateNow.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + private static final String FXML = "CalendarNode.fxml"; - private int monthNow = now.getMonthValue(); + @FXML + private Label date; - public UniqueTaskList() { - super(); - for (int i = 0; i < 7; i++) { - for (int j = 0; j < 32; j++) { - calendarList[i][j] = FXCollections.observableArrayList(); - } - } - } + @FXML + private ListView<CalendarTaskCard> tasks; /** - * Returns true if the list contains an equivalent task as the given argument. + * Create a calendar node. + * @param txt the date of the node + * @param taskList the task list linked to it */ - public boolean contains(Task toCheck) { - requireNonNull(toCheck); - return internalList.contains(toCheck); + public CalendarNode(String txt, ObservableList<Task> taskList) { + super(FXML); + date.setText(txt); + setConnections(taskList); + } + + private void setConnections(ObservableList<Task> taskList) { + ObservableList<CalendarTaskCard> mappedList = EasyBind.map( + taskList, (task) -> new CalendarTaskCard(task)); + tasks.setItems(mappedList); + tasks.setCellFactory(listView -> new TasksCell()); } /** - * Adds a task to the list. - * + * Custom {@code ListCell} that displays the graphics of a {@code CalendarTaskCard}. */ - public void add(Task toAdd) { - requireNonNull(toAdd); - internalList.add(toAdd); - int diff = toAdd.getDeadline().diff; - calendarList[diff][toAdd.getDeadlineDay()].add(toAdd); - Collections.sort(calendarList[diff][toAdd.getDeadlineDay()]); + class TasksCell extends ListCell<CalendarTaskCard> { + + @Override + protected void updateItem(CalendarTaskCard task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(task.getRoot()); + } + } } +} ``` -###### /java/seedu/address/model/task/TaskDescription.java +###### \java\seedu\address\ui\CalendarTaskCard.java ``` java /** - * Represents a short description of a todo task + * A UI component that displays compressed information of a {@code Task} on the calendar. */ -public class TaskDescription { - - public static final String MESSAGE_DESCRIPTION_CONSTRAINTS = - "Task description can take any values, and it should not be blank"; +public class CalendarTaskCard extends UiPart<Region> { - /* - * The first character of the description must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String DESCRIPTION_VALIDATION_REGEX = "[^\\s].*"; + private static final String FXML = "CalendarTaskCard.fxml"; - public final String value; + public final Task task; - public final String shortDesc; + @FXML + private Label desc; - /** - * Constructs an {@code TaskDescription}. - * - * @param description A valid address. - */ - public TaskDescription(String description) { - assert description != null : MESSAGE_DESCRIPTION_CONSTRAINTS; - checkArgument(isValidDescription(description)); - this.value = description; - if (value.length() <= 20) { - shortDesc = value; + public CalendarTaskCard(Task task) { + super(FXML); + this.task = task; + if (task.getTitle().toString().length() <= 20) { + desc.setText(task.getTitle().toString()); } else { - shortDesc = value.substring(0, 20) + "..."; + String text = task.getTitle().toString().substring(0, 20) + "..."; + desc.setText(text); + } + if (task.getPriority().value == 1) { + desc.getStyleClass().clear(); + desc.getStyleClass().add("label-small-green"); + } else if (task.getPriority().value == 2) { + desc.getStyleClass().clear(); + desc.getStyleClass().add("label-small-yellow"); + } else { + desc.getStyleClass().clear(); + desc.getStyleClass().add("label-small-red"); } - } - - /** - * Returns true if a given string is a valid person email. - */ - public static boolean isValidDescription(String test) { - return test.matches(DESCRIPTION_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; } @Override public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof TaskDescription // instanceof handles nulls - && this.value.equals(((TaskDescription) other).value)); // state check - } + // short circuit if same object + if (other == this) { + return true; + } - @Override - public int hashCode() { - return value.hashCode(); - } + // instanceof handles nulls + if (!(other instanceof CalendarTaskCard)) { + return false; + } + // state check + CalendarTaskCard card = (CalendarTaskCard) other; + return task.equals(card.task); + } } ``` -###### /java/seedu/address/model/task/Deadline.java +###### \java\seedu\address\ui\CalendarView.java ``` java /** - * Represents a Task's deadline in the address book. - * Guarantees: immutable; + * The Calendar of the app. */ -public class Deadline { +public class CalendarView extends UiPart<Region> { + private static final String FXML = "CalendarView.fxml"; + private final Logger logger = LogsCenter.getLogger(this.getClass()); + private ArrayList<AnchorPane> allCalendarDays = new ArrayList<>(35); + private YearMonth currentYearMonth; + private ObservableList<Task>[][] tasks; + private int currentMonth = 0; - public static final String MESSAGE_DEADLINE_CONSTRAINTS = - "Deadline should be a valid date that exists and in the format dd-mm-yyyy. Tasks cannot be scheduled in the past." - + "And can only be scheduled at most 6 months in advance. (Based on months: tasks cannot be" - + " scheduled on 1st August 2018 if the current date is 31st January 2018)."; - public final String dateString; - public final LocalDate value; - public final int diff; - public final int day; - public final int month; - public final int year; + @FXML + private VBox calendarVBox; + @FXML + private GridPane calendar; + @FXML + private Button previousMonth; + @FXML + private Button nextMonth; + @FXML + private Label calendarTitle; /** - * Constructs a {@code Deadline}. - * - * @param deadline A valid deadline. + * Creates the calendar of the app */ - public Deadline(String deadline) { - requireNonNull(deadline); - dateString = deadline; - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); - LocalDate deadlineDate = LocalDate.parse(deadline, formatter); - this.value = deadlineDate; - LocalDate now = LocalDate.now(); - this.diff = calculateDifference(deadlineDate, now); - this.day = deadlineDate.getDayOfMonth(); - this.month = deadlineDate.getMonthValue(); - this.year = deadlineDate.getYear(); + public CalendarView(ObservableList<Task>[][] tasksArray) { + super(FXML); + this.tasks = tasksArray; + YearMonth yearMonth = YearMonth.now(); + currentYearMonth = yearMonth; + initCalendar(yearMonth); } /** - * Returns true if a given string is a valid deadline. + * Create rows and columns with anchor panes for the calendar */ - public static boolean isValidDeadline(String test) { - try { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); - LocalDate deadlineDate = LocalDate.parse(test, formatter); - LocalDate now = LocalDate.now(); - if (deadlineDate.getYear() < now.getYear()) { - throw new IllegalArgumentException(MESSAGE_DEADLINE_CONSTRAINTS); - } - if (deadlineDate.getMonthValue() < now.getMonthValue() && deadlineDate.getYear() == now.getYear()) { - throw new IllegalArgumentException(MESSAGE_DEADLINE_CONSTRAINTS); - } - if (deadlineDate.getMonthValue() == now.getMonthValue() - && deadlineDate.getYear() == now.getYear() && deadlineDate.getDayOfMonth() < now.getDayOfMonth()) { - throw new IllegalArgumentException(MESSAGE_DEADLINE_CONSTRAINTS); - } - if (!isWithinSixMonths(deadlineDate, now)) { - throw new IllegalArgumentException(MESSAGE_DEADLINE_CONSTRAINTS); + private void initCalendar(YearMonth yearMonth) { + + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 7; j++) { + AnchorPane ap = new AnchorPane(); + ap.setPrefSize(300, 300); + calendar.add(ap, j, i); + allCalendarDays.add(ap); } - } catch (Exception e) { - return false; } - return true; + setCalendarDays(yearMonth); } /** - * checks and see if the deadline is within 6 months of the current date. - * @return + * Set the days of the calendar to display the correct date + * @param yearMonth year and month of the current month */ - public static boolean isWithinSixMonths(LocalDate deadlineDate, LocalDate now) { - int difference; - if (deadlineDate.getYear() == now.getYear()) { - difference = deadlineDate.getMonthValue() - now.getMonthValue(); - } else if (deadlineDate.getYear() - now.getYear() == 1) { - difference = 12 - now.getMonthValue() + deadlineDate.getMonthValue(); - } else { - difference = 100; + public void setCalendarDays(YearMonth yearMonth) { + LocalDate calendarDate = LocalDate.of(yearMonth.getYear(), yearMonth.getMonthValue(), 1); + while (!calendarDate.getDayOfWeek().toString().equals("SUNDAY")) { + calendarDate = calendarDate.minusDays(1); + } + for (AnchorPane ap : allCalendarDays) { + if (ap.getChildren().size() != 0) { + ap.getChildren().clear(); + } + String txt = String.valueOf(calendarDate.getDayOfMonth()); + try { + if (calendarDate.getMonthValue() == currentYearMonth.getMonthValue()) { + CalendarNode node = new CalendarNode(txt, tasks[currentMonth][calendarDate.getDayOfMonth()]); + ap.getChildren().add(node.getRoot()); + } else if (calendarDate.getMonthValue() > currentYearMonth.getMonthValue()) { + CalendarNode node = new CalendarNode(txt, tasks[currentMonth + 1][calendarDate.getDayOfMonth()]); + ap.getChildren().add(node.getRoot()); + } else { + CalendarNode node = new CalendarNode(txt, tasks[currentMonth - 1][calendarDate.getDayOfMonth()]); + ap.getChildren().add(node.getRoot()); + } + calendarDate = calendarDate.plusDays(1); + } catch (ArrayIndexOutOfBoundsException oob) { + CalendarNode node = new CalendarNode(txt, FXCollections.observableArrayList()); + ap.getChildren().add(node.getRoot()); + calendarDate = calendarDate.plusDays(1); + } } - return difference <= 6; + calendarTitle.setText(yearMonth.getMonth().toString() + " " + String.valueOf(yearMonth.getYear())); } /** - * Calculates the value of the difference in months between the deadline and the current date. - * @return + * Refreshes the calendar with new information. */ - private int calculateDifference(LocalDate deadlineDate, LocalDate now) { - int diff; - if (deadlineDate.getYear() == now.getYear()) { - diff = deadlineDate.getMonthValue() - now.getMonthValue(); - } else { - diff = 12 - now.getMonthValue() + deadlineDate.getMonthValue(); - } - return diff; - } - - @Override - public String toString() { - return value.toString(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Deadline // instanceof handles nulls - && this.value.equals(((Deadline) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); + public void refreshCalendar() { + initCalendar(currentYearMonth); } -} -``` -###### /java/seedu/address/model/task/Priority.java -``` java -/** - * Represents a Task's priority in the todo list and calendar. - * Guarantees: immutable; is valid as declared in {@link #isValidPriority(String)} - */ -public class Priority { - - - public static final String MESSAGE_PRIORITY_CONSTRAINTS = - "Priority value input can only be a value from 1 to 3. 1 being lowest priority and 3 being highest."; - public static final String PRIORITY_VALIDATION_REGEX = "[1-3]{1}"; - public final String priority; - public final int value; - + @FXML /** - * Constructs a {@code Phone}. - * - * @param priorityValue A valid priority value. + * Move the month back by one. Repopulate the calendar with the correct dates. */ - public Priority(String priorityValue) { - requireNonNull(priorityValue); - checkArgument(isValidPriority(priorityValue), MESSAGE_PRIORITY_CONSTRAINTS); - this.priority = priorityValue; - this.value = Integer.parseInt(priorityValue); + private void handlePreviousButtonAction() { + currentYearMonth = currentYearMonth.minusMonths(1); + currentMonth--; + setCalendarDays(currentYearMonth); } + @FXML /** - * Returns true if a given string is a valid task priority. + * Move the month forward by one. Repopulate the calendar with the correct dates. */ - public static boolean isValidPriority(String test) { - return test.matches(PRIORITY_VALIDATION_REGEX); - } - - @Override - public String toString() { - return priority; + private void handleNextButtonAction() { + currentYearMonth = currentYearMonth.plusMonths(1); + currentMonth++; + setCalendarDays(currentYearMonth); } - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Priority // instanceof handles nulls - && this.priority.equals(((Priority) other).priority)); // state check + public ArrayList<AnchorPane> getAllCalendarDays() { + return allCalendarDays; } - @Override - public int hashCode() { - return priority.hashCode(); + public void setAllCalendarDays(ArrayList<AnchorPane> allCalendarDays) { + this.allCalendarDays = allCalendarDays; } - } ``` +###### \resources\view\CalendarNode.fxml +``` fxml +<Pane prefHeight="95.0" prefWidth="147.0" styleClass="pane-with-border" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="90.0" prefWidth="146.0"> + <children> + <Label fx:id="date" text="Label" styleClass="label-date" > + <VBox.margin> + <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> + </VBox.margin> + <font> + <Font size="10.0" /> + </font> + </Label> + <ListView fx:id="tasks" prefHeight="20.0" prefWidth="70.0" styleClass="list-view-calendar" VBox.vgrow="ALWAYS"> + <VBox.margin> + <Insets bottom="1.0" left="2.0" right="1.0" top="1.0" /> + </VBox.margin> + </ListView> + </children> + </VBox> + </children> +</Pane> +``` +###### \resources\view\CalendarTaskCard.fxml +``` fxml +<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="17.0" prefWidth="126.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <Label fx:id="desc" alignment="CENTER_LEFT" styleClass="label-small" prefHeight="10.0" prefWidth="147.0" scaleShape="false" text="Label"> + <font> + <Font size="11.0" /> + </font></Label> + </children> +</HBox> +``` +###### \resources\view\CalendarView.fxml +``` fxml +<VBox id="calendarVBox" fx:id="calendarVBox" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <HBox alignment="CENTER" spacing="10"> + <children> + <Button fx:id="previousMonth" styleClass="button" onAction="#handlePreviousButtonAction" text="<<" HBox.hgrow="SOMETIMES"> + </Button> + <Label fx:id="calendarTitle" styleClass="calendar_title" text="\$calendarTitle" HBox.hgrow="SOMETIMES" /> + <Button fx:id="nextMonth" styleClass="button" onAction="#handleNextButtonAction" text=">>" HBox.hgrow="SOMETIMES"> + </Button> + </children> + <padding> + <Insets bottom="5" left="10" right="10" top="5" /> + </padding> + <padding> + <Insets bottom="5" left="10" right="10" top="5" /> + </padding> + </HBox> + <GridPane alignment="CENTER" minHeight="-Infinity" minWidth="-Infinity"> + <columnConstraints> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <Label alignment="CENTER" contentDisplay="CENTER" styleClass="calendar_text" text="Sunday" textAlignment="CENTER"> + <GridPane.margin> + <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> + </GridPane.margin> + </Label> + <Label text="Monday" styleClass="calendar_text" GridPane.columnIndex="1"> + <GridPane.margin> + <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> + </GridPane.margin> + </Label> + <Label text="Tuesday" styleClass="calendar_text" GridPane.columnIndex="2"> + <GridPane.margin> + <Insets bottom="18.0" left="18.0" right="18.0" top="18.0" /> + </GridPane.margin> + </Label> + <Label alignment="CENTER" text="Wednesday" styleClass="calendar_text" GridPane.columnIndex="3"> + <GridPane.margin> + <Insets bottom="10.0" left="8.0" right="8.0" top="10.0" /> + </GridPane.margin> + </Label> + <Label text="Thursday" styleClass="calendar_text" GridPane.columnIndex="4"> + <GridPane.margin> + <Insets bottom="15.0" left="15.0" right="15.0" top="15.0" /> + </GridPane.margin> + </Label> + <Label text="Friday" styleClass="calendar_text" GridPane.columnIndex="5"> + <GridPane.margin> + <Insets bottom="20.0" left="23.0" right="23.0" top="20.0" /> + </GridPane.margin> + </Label> + <Label text="Saturday" styleClass="calendar_text" GridPane.columnIndex="6"> + <GridPane.margin> + <Insets bottom="15.0" left="15.0" right="15.0" top="15.0" /> + </GridPane.margin> + </Label> + </children> + <VBox.margin> + <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> + </VBox.margin> + </GridPane> + <GridPane fx:id="calendar" alignment="CENTER" styleClass="calendar-grid-pane" gridLinesVisible="true" minHeight="-Infinity" minWidth="-Infinity" VBox.vgrow="SOMETIMES"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="90.0" prefWidth="90.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" /> + </rowConstraints> + <VBox.margin> + <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> + </VBox.margin> + </GridPane> + </children> +</VBox> +``` diff --git a/collated/functional/Wu.md b/collated/functional/Wu.md index a6ed24919bd3..7799330ee195 100644 --- a/collated/functional/Wu.md +++ b/collated/functional/Wu.md @@ -1,237 +1,22 @@ # Wu -###### /resources/view/DarkMainWindow.fxml -``` fxml - <SplitPane id="splitPane" fx:id="splitPane" dividerPositions="0.4" VBox.vgrow="ALWAYS"> - <VBox fx:id="personList" minWidth="360" prefWidth="360" SplitPane.resizableWithParent="false"> - <TabPane fx:id="tabPane" VBox.vgrow="ALWAYS" tabClosingPolicy="UNAVAILABLE"> - <tabs> - <Tab fx:id="personListTab" text="Person List"> - <StackPane fx:id="personListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> - </Tab> - <Tab fx:id="todoListTab" text="Todo List"> - <StackPane fx:id="todoListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> - </Tab> - </tabs> - </TabPane> - </VBox> - -``` -###### /resources/view/DarkTheme.css -``` css -.tab-pane { - -fx-padding: 0 0 0 1; - -fx-background-color: #232A34; -} - -.tab-pane .tab-header-area { - -fx-background-color: #232A34; - -fx-padding: 0 0 0 0; - -fx-min-height: 0; - -fx-max-height: 0; -} - -.tab-pane .tab-header-area .tab-header-background { - -fx-opacity: 0; -} - -.tab-pane { - -fx-tab-min-width:150px; -} - -.tab { - -fx-background-insets: 0 1 0 1,0,0; -} - -.tab-pane .tab { - -fx-background-color: #404040; - -} - -.tab-pane .tab:selected { - -fx-border-color: transparent !important; - -fx-background-color: #5F6A6A; -} - -.tab .tab-label { - -fx-alignment: CENTER; - -fx-text-fill: #f3f3f3; - -fx-font-size: 12px; - -fx-font-weight: bold; -} - -.tab:selected .tab-label { - -fx-border-color: transparent !important; - -fx-text-fill: white; -} -``` -###### /resources/view/todoListCard.fxml -``` fxml -<HBox id="cardPane" fx:id="cardPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> - <GridPane HBox.hgrow="ALWAYS"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10" prefWidth="150" /> - </columnConstraints> - <VBox alignment="CENTER_LEFT" minHeight="105" GridPane.columnIndex="0"> - <padding> - <Insets top="5" right="5" bottom="5" left="15"/> - </padding> - <HBox spacing="5" alignment="CENTER_LEFT"> - <Label fx:id="id" styleClass="cell_big_label"> - <minWidth> - <Region fx:constant="USE_PREF_SIZE"/> - </minWidth> - </Label> - <Label fx:id="title" text="\$title" styleClass="cell_big_label"/> - </HBox> - <FlowPane fx:id="tags" /> - <Label fx:id="priority" styleClass="cell_small_label" text="\$priority"/> - <Label fx:id="deadline" styleClass="cell_small_label" text="\$deadline"/> - <Label fx:id="description" styleClass="cell_small_label" text="\$description"/> - </VBox> - </GridPane> -</HBox> -``` -###### /resources/view/todoListPanel.fxml -``` fxml -<VBox xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1"> - <ListView fx:id="todoListView" VBox.vgrow="ALWAYS" /> -</VBox> -``` -###### /java/seedu/address/ui/TodoListPanel.java -``` java -/** - * Panel containing the list of tasks shown in TodoList. - */ -public class TodoListPanel extends UiPart<Region> { - private static final String FXML = "TodoListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(TodoListPanel.class); - - @FXML - private ListView<TodoCard> todoListView; - - public TodoListPanel(ObservableList<Task> taskList) { - super(FXML); - setConnections(taskList); - registerAsAnEventHandler(this); - } - - private void setConnections(ObservableList<Task> taskList) { - ObservableList<TodoCard> mappedList = EasyBind.map( - taskList, (task) -> new TodoCard(task, taskList.indexOf(task) + 1)); - todoListView.setItems(mappedList); - todoListView.setCellFactory(listView -> new todoListViewCell()); - setEventHandlerForSelectionChangeEvent(); - } - - private void setEventHandlerForSelectionChangeEvent() { - todoListView.getSelectionModel().selectedItemProperty() - .addListener((observable, oldValue, newValue) -> { - if (newValue != null) { - logger.fine("Selection in task list panel changed to : '" + newValue + "'"); - raise(new TodoPanelSelectionChangedEvent(newValue)); - } - }); - } - - /** - * Scrolls to the {@code TodoCard} at the {@code index} and selects it. - */ - private void scrollTo(int index) { - Platform.runLater(() -> { - todoListView.scrollTo(index); - todoListView.getSelectionModel().clearAndSelect(index); - }); - } - - @Subscribe - private void handleJumpToListRequestEvent(JumpToListRequestEvent event) { - logger.info(LogsCenter.getEventHandlingLogMessage(event)); - scrollTo(event.targetIndex); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code PersonCard}. - */ - class todoListViewCell extends ListCell<TodoCard> { - - @Override - protected void updateItem(TodoCard task, boolean empty) { - super.updateItem(task, empty); - - if (empty || task == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(task.getRoot()); - } - } - } -} -``` -###### /java/seedu/address/ui/TodoCard.java +###### \java\seedu\address\commons\events\model\TaskBookChangedEvent.java ``` java -/** - * A UI component that displays information of a {@code task} in TodoList. - */ -public class TodoCard extends UiPart<Region> { - - private static final String FXML = "TodoListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see <a href="https://github.com/se-edu/addressbook-level4/issues/336">The issue on AddressBook level 4</a> - */ - - public final Task task; +/** Indicates the AddressBook in the model has changed*/ +public class TaskBookChangedEvent extends BaseEvent { - @FXML - private HBox cardPane; - @FXML - private Label title; - @FXML - private Label id; - @FXML - private Label priority; - @FXML - private Label deadline; - @FXML - private Label description; - @FXML - private FlowPane tags; + public final ReadOnlyAddressBook data; - public TodoCard(Task task, int displayedIndex) { - super(FXML); - this.task = task; - id.setText(displayedIndex + ". "); - title.setText(task.getTitle().value); - priority.setText("Priority: " + task.getPriority().toString()); - deadline.setText("Deadline: " + task.getDeadline().dateString); - description.setText(task.getTaskDesc().value); + public TaskBookChangedEvent(ReadOnlyAddressBook data) { + this.data = data; } @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof TodoCard)) { - return false; - } - - // state check - TodoCard card = (TodoCard) other; - return id.getText().equals(card.id.getText()) - && task.equals(card.task); + public String toString() { + return "number of tasks " + data.getTaskList().size(); } } ``` -###### /java/seedu/address/commons/events/ui/TodoPanelSelectionChangedEvent.java +###### \java\seedu\address\commons\events\ui\TodoPanelSelectionChangedEvent.java ``` java /** * Represents a selection change in the TodoList Panel @@ -255,147 +40,7 @@ public class TodoPanelSelectionChangedEvent extends BaseEvent { } } ``` -###### /java/seedu/address/commons/events/model/TaskBookChangedEvent.java -``` java -/** Indicates the AddressBook in the model has changed*/ -public class TaskBookChangedEvent extends BaseEvent { - - public final ReadOnlyAddressBook data; - - public TaskBookChangedEvent(ReadOnlyAddressBook data) { - this.data = data; - } - - @Override - public String toString() { - return "number of tasks " + data.getTaskList().size(); - } -} -``` -###### /java/seedu/address/logic/parser/DeleteTaskCommandParser.java -``` java -/** - * Parses input arguments and creates a new DeleteCommand object - */ -public class DeleteTaskCommandParser implements Parser<DeleteTaskCommand> { - - /** - * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns an DeleteCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public DeleteTaskCommand parse(String args) throws ParseException { - try { - Index index = ParserUtil.parseIndex(args); - return new DeleteTaskCommand(index); - } catch (IllegalValueException ive) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE)); - } - } - -} -``` -###### /java/seedu/address/logic/parser/ImportCommandParser.java -``` java -/** - * Parses input arguments and creates a new ImportCommandParser object - */ -public class ImportCommandParser implements Parser<ImportCommand> { - - /** - * Parses the given {@code String} of arguments in the context of the ImportCommand - * and returns an Import Command object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public ImportCommand parse(String userInput) throws ParseException { - String trimmedInput = userInput.trim(); - - String exceptionMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE); - if (trimmedInput.isEmpty()) { - throw new ParseException(exceptionMessage); - } - - return new ImportCommand(userInput); - } -} -``` -###### /java/seedu/address/logic/parser/ParserUtil.java -``` java - /** - * Parses a {@code String taskTitle} into a {@code TaskTitle}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws IllegalValueException if the given {@code taskDescription} is invalid. - */ - public static Title parseTaskTitle(String taskTitle) throws IllegalValueException { - requireNonNull(taskTitle); - String trimmedTaskTitle = taskTitle.trim(); - if (!TaskDescription.isValidDescription(trimmedTaskTitle)) { - throw new IllegalValueException(Title.MESSAGE_TITLE_CONSTRAINTS); - } - return new Title(trimmedTaskTitle); - } - - /** - * Parses a {@code Optional<String> taskDescription} into an {@code Optional<TaskDescription>} - * if {@code TaskDscription} is present. - * See header comment of this class regarding the use of {@code Optional} parameters. - */ - public static Optional<Title> parseTaskTitle(Optional<String> title) throws IllegalValueException { - requireNonNull(title); - return title.isPresent() ? Optional.of(parseTaskTitle(title.get())) : Optional.empty(); - } - -``` -###### /java/seedu/address/logic/parser/EditTaskCommandParser.java -``` java -/** - * Parses input arguments and creates a new EditTaskCommand object - */ -public class EditTaskCommandParser implements Parser<EditTaskCommand> { - - public static final String PLACE_HOLDER_HASH = "EDITED_DISPLAY"; - - /** - * Parses the given {@code String} of arguments in the context of the EditTaskCommand - * and returns an EditTaskCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public EditTaskCommand parse(String args) throws ParseException { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_TASK_DESC, PREFIX_DEADLINE, PREFIX_PRIORITY); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (IllegalValueException ive) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE)); - } - - EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor(); - try { - ParserUtil.parseTaskTitle(argMultimap.getValue(PREFIX_TITLE)).ifPresent(editTaskDescriptor::setTitle); - ParserUtil.parseTaskDescription(argMultimap.getValue(PREFIX_TASK_DESC)) - .ifPresent(editTaskDescriptor::setDescription); - ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_DEADLINE)).ifPresent(editTaskDescriptor::setDeadline); - ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY)).ifPresent(editTaskDescriptor::setPriority); - } catch (IllegalValueException ive) { - throw new ParseException(ive.getMessage(), ive); - } - - if (!editTaskDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditTaskCommand.MESSAGE_NOT_EDITED); - } - - return new EditTaskCommand(index, editTaskDescriptor); - } - -} -``` -###### /java/seedu/address/logic/commands/DeleteTaskCommand.java +###### \java\seedu\address\logic\commands\DeleteTaskCommand.java ``` java /** * Deletes a task identified using it's last displayed index from the address book. @@ -455,7 +100,7 @@ public class DeleteTaskCommand extends UndoableCommand { } ``` -###### /java/seedu/address/logic/commands/EditTaskCommand.java +###### \java\seedu\address\logic\commands\EditTaskCommand.java ``` java /** * Edits the details of an existing person in the address book. @@ -643,7 +288,7 @@ public class EditTaskCommand extends UndoableCommand { } ``` -###### /java/seedu/address/logic/commands/ImportCommand.java +###### \java\seedu\address\logic\commands\ImportCommand.java ``` java /** * Imports data from a xml file and overwrites the current data stored @@ -665,7 +310,6 @@ public class ImportCommand extends Command { public ImportCommand(String filePath) { this.filePath = filePath.trim(); -<<<<<<< HEAD } @Override @@ -683,6 +327,64 @@ public class ImportCommand extends Command { } } ``` +###### \java\seedu\address\logic\commands\ListCurrentTaskCommand.java +``` java +/** + * Lists all tasks due by the current month stored in the address book. + */ +public class ListCurrentTaskCommand extends Command { + + public static final String COMMAND_WORD = "listCurrentTask"; + public static final String COMMAND_ALIAS = "lct"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks due this month"; + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_CURRENT_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \java\seedu\address\logic\commands\ListTaskCommand.java +``` java +/** + * Lists all tasks stored in the address book. + */ +public class ListTaskCommand extends Command { + + public static final String COMMAND_WORD = "listTask"; + public static final String COMMAND_ALIAS = "lt"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \java\seedu\address\logic\commands\SortTaskCommand.java +``` java +/** + * Lists all tasks stored in the address book in date order. + */ +public class SortTaskCommand extends Command { + + public static final String COMMAND_WORD = "sortTask"; + public static final String COMMAND_ALIAS = "stt"; + + public static final String MESSAGE_SUCCESS = "Todo List is sorted"; + + + @Override + public CommandResult execute() { + model.sortTasks(); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` ###### \java\seedu\address\logic\commands\SwitchTabCommand.java ``` java public class SwitchTabCommand extends Command { @@ -722,6 +424,33 @@ public class SwitchTabCommand extends Command { } } ``` +###### \java\seedu\address\logic\LogicManager.java +``` java + @Override + public void setTabPane(TabPane tabPane) { + addressBookParser.setTabPane(tabPane); + } +} +``` +###### \java\seedu\address\logic\parser\AddressBookParser.java +``` java + public void setTabPane(TabPane tabPane) { + this.tabPane = tabPane; + } + + /** + * Selects Person List tab before executing list command + * @param tabPane + */ + public void selectPersonTab(TabPane tabPane) { + if (tabPane != null) { + SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel(); + selectionModel.select(PERSON_TAB); + } + } + +} +``` ###### \java\seedu\address\logic\parser\DeleteTaskCommandParser.java ``` java /** @@ -842,26 +571,10 @@ public class ImportCommandParser implements Parser<ImportCommand> { public static Optional<Title> parseTaskTitle(Optional<String> title) throws IllegalValueException { requireNonNull(title); return title.isPresent() ? Optional.of(parseTaskTitle(title.get())) : Optional.empty(); -======= ->>>>>>> 8120314b1b5ecce7cb60b6c03090396164741055 } - @Override - public CommandResult execute() throws CommandException { - requireNonNull(model); - try { - ReadOnlyAddressBook newDataSet = XmlFileStorage.loadDataFromSaveFile(new File(filePath)); - model.resetData(newDataSet); - return new CommandResult(MESSAGE_SUCCESS); - } catch (IOException e) { - throw new CommandException(MESSAGE_INVALID_PATH); - } catch (DataConversionException e) { - throw new CommandException(MESSAGE_INVALID_FILE); - } - } -} ``` -###### /java/seedu/address/model/AddressBook.java +###### \java\seedu\address\model\AddressBook.java ``` java /** * Removes {@code key} from this {@code AddressBook}. @@ -877,7 +590,7 @@ public class ImportCommandParser implements Parser<ImportCommand> { //// tag-level operations ``` -###### /java/seedu/address/model/ModelManager.java +###### \java\seedu\address\model\ModelManager.java ``` java @Override public void deleteTask(Task target) throws TaskNotFoundException { @@ -886,7 +599,7 @@ public class ImportCommandParser implements Parser<ImportCommand> { } ``` -###### /java/seedu/address/model/ModelManager.java +###### \java\seedu\address\model\ModelManager.java ``` java @Override public void updateTask(Task target, Task editedTask) @@ -897,8 +610,41 @@ public class ImportCommandParser implements Parser<ImportCommand> { indicateAddressBookChanged(); } + @Override + public void sortTasks() { + addressBook.sortTaskList(); + } + +``` +###### \java\seedu\address\model\task\Task.java +``` java + @Override + public int compareTo(Task task) { + int yearDiff = this.getDeadlineYear() - task.getDeadlineYear(); + int monthDiff = this.getDeadlineMonth() - task.getDeadlineMonth(); + int dayDiff = this.getDeadlineDay() - task.getDeadlineDay(); + + return compareDate(yearDiff, monthDiff, dayDiff); + } + + /** + * Compares the dates + * @param yearDiff difference in year + * @param monthDiff difference in month + * @param dayDiff difference in day + */ + private int compareDate (int yearDiff, int monthDiff, int dayDiff) { + if (yearDiff != 0) { + return yearDiff; + } + if (monthDiff != 0) { + return monthDiff; + } + return dayDiff; + } +} ``` -###### /java/seedu/address/model/task/Title.java +###### \java\seedu\address\model\task\Title.java ``` java /** * Represents a task's title in the TodoList. @@ -956,7 +702,7 @@ public class Title { } ``` -###### /java/seedu/address/model/task/UniqueTaskList.java +###### \java\seedu\address\model\task\UniqueTaskList.java ``` java /** * Replaces the task {@code target} in the list with {@code editedTask}. @@ -971,12 +717,8 @@ public class Title { if (index == -1) { throw new TaskNotFoundException(); } - internalList.set(index, editedTask); - int indexCalendar = calendarList[target.getDeadline().diff][target.getDeadlineDay()].indexOf(target); - if (indexCalendar == -1) { - throw new TaskNotFoundException(); - } - calendarList[target.getDeadline().diff][target.getDeadlineDay()].set(indexCalendar, editedTask); + remove(target); + add(editedTask); } /** @@ -994,8 +736,16 @@ public class Title { return taskFoundAndDeleted; } + /** + * Sorts the tasks in the list in date order + */ + public void sortList() { + requireNonNull(internalList); + + FXCollections.sort(internalList); + } + ``` -<<<<<<< HEAD ###### \java\seedu\address\ui\TodoCard.java ``` java /** @@ -1034,10 +784,10 @@ public class TodoCard extends UiPart<Region> { super(FXML); this.task = task; id.setText(displayedIndex + ". "); - title.setText(task.getTitle().value); - priority.setText(task.getPriority().toString()); - deadline.setText(task.getDeadline().dateString); - description.setText(task.getTaskDesc().value); + title.setText("Title: " + task.getTitle().value); + priority.setText("Priority: " + task.getPriority().toString()); + deadline.setText("Deadline: " + task.getDeadline().dateString); + description.setText("Description: " + task.getTaskDesc().value); } @Override @@ -1065,7 +815,7 @@ public class TodoCard extends UiPart<Region> { * Panel containing the list of tasks shown in TodoList. */ public class TodoListPanel extends UiPart<Region> { - private static final String FXML = "TodoListPanel.fxml"; + private static final String FXML = "todoListPanel.fxml"; private final Logger logger = LogsCenter.getLogger(TodoListPanel.class); @FXML @@ -1129,6 +879,23 @@ public class TodoListPanel extends UiPart<Region> { } } } +``` +###### \resources\view\DarkMainWindow.fxml +``` fxml + <SplitPane id="splitPane" fx:id="splitPane" dividerPositions="0.4" VBox.vgrow="ALWAYS"> + <VBox fx:id="personList" minWidth="360" prefWidth="360" SplitPane.resizableWithParent="false"> + <TabPane fx:id="tabPane" VBox.vgrow="ALWAYS" tabClosingPolicy="UNAVAILABLE"> + <tabs> + <Tab fx:id="personListTab" text="Person List"> + <StackPane fx:id="personListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> + </Tab> + <Tab fx:id="todoListTab" text="Todo List"> + <StackPane fx:id="todoListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> + </Tab> + </tabs> + </TabPane> + </VBox> + ``` ###### \resources\view\DarkTheme.css ``` css @@ -1178,24 +945,7 @@ public class TodoListPanel extends UiPart<Region> { -fx-text-fill: white; } ``` -###### \resources\view\MainWindow.fxml -``` fxml - <SplitPane id="splitPane" fx:id="splitPane" dividerPositions="0.4" VBox.vgrow="ALWAYS"> - <VBox fx:id="personList" minWidth="360" prefWidth="360" SplitPane.resizableWithParent="false"> - <TabPane fx:id="tabPane" VBox.vgrow="ALWAYS" tabClosingPolicy="UNAVAILABLE"> - <tabs> - <Tab fx:id="personListTab" text="Person List"> - <StackPane fx:id="personListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> - </Tab> - <Tab fx:id="todoListTab" text="Todo List"> - <StackPane fx:id="todoListPanelPlaceholder" minWidth="357" maxWidth="357" VBox.vgrow="ALWAYS"/> - </Tab> - </tabs> - </TabPane> - </VBox> - -``` -###### \resources\view\todoListCard.fxml +###### \resources\view\TodoListCard.fxml ``` fxml <HBox id="cardPane" fx:id="cardPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> <GridPane HBox.hgrow="ALWAYS"> @@ -1228,5 +978,3 @@ public class TodoListPanel extends UiPart<Region> { <ListView fx:id="todoListView" VBox.vgrow="ALWAYS" /> </VBox> ``` -======= ->>>>>>> 8120314b1b5ecce7cb60b6c03090396164741055 diff --git a/collated/test/Alaru.md b/collated/test/Alaru.md index 45d71941aeac..2d0d490bb327 100644 --- a/collated/test/Alaru.md +++ b/collated/test/Alaru.md @@ -366,11 +366,16 @@ public class ParticipationTest { Assert.assertThrows(NullPointerException.class, () -> Participation.isValidParticipation(null)); // invalid participation - Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("")); // empty string - Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation(" ")); // spaces only - Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("^")); // only non-alphanumeric characters - Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("abcd")); // contains alpha characters - Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("peter*")); // contains non-alphanumeric characters + // empty string + Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("")); + // spaces only + Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation(" ")); + // only non-alphanumeric characters + Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("^")); + // contains alpha characters + Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("abcd")); + // contains non-alphanumeric characters + Assert.assertThrows(IllegalMarksException.class, () -> Participation.isValidParticipation("peter*")); assertFalse(Participation.isValidParticipation("101")); // over limit assertFalse(Participation.isValidParticipation("-500")); // below limit @@ -405,14 +410,3 @@ public class UniqueItemListTest { } } ``` -###### \java\systemtests\EditCommandSystemTest.java -``` java - /* Case: invalid display pic (missing) -> rejected */ - assertCommandFailure(EditCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased() + INVALID_DISPLAY_DESC, - DisplayPic.MESSAGE_DISPLAY_PIC_NONEXISTENT_CONSTRAINTS); - - /* Case: invalid display pic (not image) -> rejected */ - assertCommandFailure(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + INVALID_DISPLAY_TYPE_DESC, - DisplayPic.MESSAGE_DISPLAY_PIC_NOT_IMAGE); -``` diff --git a/collated/test/JoonKai1995.md b/collated/test/JoonKai1995.md index 86dacd2c93d6..757f6779d44b 100644 --- a/collated/test/JoonKai1995.md +++ b/collated/test/JoonKai1995.md @@ -1,36 +1,45 @@ # JoonKai1995 -###### /java/seedu/address/ui/CalendarViewTest.java +###### \java\seedu\address\model\task\DeadlineTest.java ``` java -public class CalendarViewTest extends GuiUnitTest { +public class DeadlineTest { - @Test - public void display() { - // no tags - ObservableList<Task>[][] lists = new ObservableList[1][1]; - lists[0][0] = FXCollections.observableArrayList(); - CalendarView calendar = new CalendarView(lists); - uiPartRule.setUiPart(calendar); - YearMonth yearMonth = YearMonth.now(); - String correctTitle = yearMonth.getMonth().toString() + " " + String.valueOf(yearMonth.getYear()); - assertCalendarTitle(calendar, correctTitle); + private LocalDate now = LocalDate.now(); + private LocalDate yesterday = now.minusDays(1); + private LocalDate tomorrow = now.plusDays(1); + private String dateYesterday = yesterday.toString(); + private String correctDateYesterday = dateYesterday.substring(8, 10) + "-" + dateYesterday.substring(5, 7) + + "-" + dateYesterday.substring(0, 4); + private String dateTomorrow = tomorrow.toString(); + private String correctDateTomorrow = dateTomorrow.substring(8, 10) + "-" + dateTomorrow.substring(5, 7) + + "-" + dateTomorrow.substring(0, 4); + @Test + public void constructor_null_throwsNullPointerException() { + Assert.assertThrows(NullPointerException.class, () -> new Deadline(null)); } - /** - * Asserts that {@code personCard} displays the details of {@code expectedPerson} correctly and matches - * {@code expectedId}. - */ - private void assertCalendarTitle(CalendarView calendar, String expectedTitle) { - guiRobot.pauseForHuman(); + @Test + public void constructor_invalidDeadline_throwsDateTimeParseException() { + String invalidDeadline = ""; + Assert.assertThrows(DateTimeParseException.class, () -> new Deadline(invalidDeadline)); + } - CalendarViewHandle calendarViewHandle = new CalendarViewHandle(calendar.getRoot()); + @Test + public void isValidDeadline() { + // invalid dates + assertFalse(Deadline.isValidDeadline("")); // empty string + assertFalse(Deadline.isValidDeadline(" ")); // spaces only + assertFalse(Deadline.isValidDeadline("91")); // numbers + assertFalse(Deadline.isValidDeadline("02/04/2017")); // / instead of - + assertFalse(Deadline.isValidDeadline(correctDateYesterday)); // scheduled yesterday - // verify id is displayed correctly - assertEquals(expectedTitle, calendarViewHandle.getCalendarTitle()); + // valid dates + assertTrue(Deadline.isValidDeadline(correctDateTomorrow)); } } + ``` -###### /java/seedu/address/model/task/PriorityTest.java +###### \java\seedu\address\model\task\PriorityTest.java ``` java public class PriorityTest { @@ -62,7 +71,7 @@ public class PriorityTest { } ``` -###### /java/seedu/address/model/task/TaskDescriptionTest.java +###### \java\seedu\address\model\task\TaskDescriptionTest.java ``` java public class TaskDescriptionTest { @@ -98,43 +107,34 @@ public class TaskDescriptionTest { } ``` -###### /java/seedu/address/model/task/DeadlineTest.java +###### \java\seedu\address\ui\CalendarViewTest.java ``` java -public class DeadlineTest { - - private LocalDate now = LocalDate.now(); - private LocalDate yesterday = now.minusDays(1); - private LocalDate tomorrow = now.plusDays(1); - private String dateYesterday = yesterday.toString(); - private String correctDateYesterday = dateYesterday.substring(8, 10) + "-" + dateYesterday.substring(5, 7) - + "-" + dateYesterday.substring(0, 4); - private String dateTomorrow = tomorrow.toString(); - private String correctDateTomorrow = dateTomorrow.substring(8, 10) + "-" + dateTomorrow.substring(5, 7) - + "-" + dateTomorrow.substring(0, 4); +public class CalendarViewTest extends GuiUnitTest { @Test - public void constructor_null_throwsNullPointerException() { - Assert.assertThrows(NullPointerException.class, () -> new Deadline(null)); - } + public void display() { + // no tags + ObservableList<Task>[][] lists = new ObservableList[1][1]; + lists[0][0] = FXCollections.observableArrayList(); + CalendarView calendar = new CalendarView(lists); + uiPartRule.setUiPart(calendar); + YearMonth yearMonth = YearMonth.now(); + String correctTitle = yearMonth.getMonth().toString() + " " + String.valueOf(yearMonth.getYear()); + assertCalendarTitle(calendar, correctTitle); - @Test - public void constructor_invalidDeadline_throwsDateTimeParseException() { - String invalidDeadline = ""; - Assert.assertThrows(DateTimeParseException.class, () -> new Deadline(invalidDeadline)); } - @Test - public void isValidDeadline() { - // invalid dates - assertFalse(Deadline.isValidDeadline("")); // empty string - assertFalse(Deadline.isValidDeadline(" ")); // spaces only - assertFalse(Deadline.isValidDeadline("91")); // numbers - assertFalse(Deadline.isValidDeadline("02/04/2017")); // / instead of - - assertFalse(Deadline.isValidDeadline(correctDateYesterday)); // scheduled yesterday + /** + * Asserts that {@code personCard} displays the details of {@code expectedPerson} correctly and matches + * {@code expectedId}. + */ + private void assertCalendarTitle(CalendarView calendar, String expectedTitle) { + guiRobot.pauseForHuman(); - // valid dates - assertTrue(Deadline.isValidDeadline(correctDateTomorrow)); + CalendarViewHandle calendarViewHandle = new CalendarViewHandle(calendar.getRoot()); + + // verify id is displayed correctly + assertEquals(expectedTitle, calendarViewHandle.getCalendarTitle()); } } - ``` diff --git a/collated/test/Wu.md b/collated/test/Wu.md index c7d09c3c3f11..646ba7e786eb 100644 --- a/collated/test/Wu.md +++ b/collated/test/Wu.md @@ -1,5 +1,4 @@ # Wu -<<<<<<< HEAD ###### \java\guitests\guihandles\TodoCardHandle.java ``` java public class TodoCardHandle extends NodeHandle<Node> { @@ -275,6 +274,11 @@ public class AddTaskCommandTest { fail("This method should not be called"); } + @Override + public void sortTasks() { + fail("This method should not be called"); + } + @Override public ObservableList<Person> getFilteredPersonList() { fail("This method should not be called."); @@ -301,10 +305,6 @@ public class AddTaskCommandTest { } - @Override - public void addDeleteItem(String filepath) { - } - @Override public List<String> getItemList() { return null; @@ -312,7 +312,6 @@ public class AddTaskCommandTest { @Override public void clearDeleteItems() { - } } @@ -376,9 +375,44 @@ public class CommandTaskTestUtil { } /** - * Deletes the first person in {@code model}'s filtered list from {@code model}'s address book. + * Executes the given {@code command}, confirms that <br> + * - the result message matches {@code expectedMessage} <br> + * - the {@code actualModel} matches {@code expectedModel} + */ + public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + Model expectedModel) { + try { + CommandResult result = command.execute(); + assertEquals(expectedMessage, result.feedbackToUser); + } catch (CommandException ce) { + throw new AssertionError("Execution of command should not fail.", ce); + } + } + + /** + * Executes the given {@code command}, confirms that <br> + * - a {@code CommandException} is thrown <br> + * - the CommandException message matches {@code expectedMessage} <br> + * - the address book and the filtered task list in the {@code actualModel} remain unchanged + */ + public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { + // we are unable to defensively copy the model for comparison later, so we can + // only do so by copying its components. + AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); + List<Task> expectedFilteredList = new ArrayList<>(actualModel.getFilteredTaskList()); + + try { + command.execute(); + fail("The expected CommandException was not thrown."); + } catch (CommandException e) { + assertEquals(expectedMessage, e.getMessage()); + } + } + + /** + * Deletes the first task in {@code model}'s filtered list from {@code model}'s address book. */ - public static void deleteTaskPerson(Model model) { + public static void deleteFirstTask(Model model) { Task firstTask = model.getFilteredTaskList().get(0); try { model.deleteTask(firstTask); @@ -847,24 +881,149 @@ public class ImportCommandTest { public static final String VALID_IMPORT_FILE_PATH = "src/data/ValidImport.xml"; public static final String INVALID_IMPORT_FILE_PATH = "src/data/InValidImport.txt"; -======= -###### /java/seedu/address/logic/parser/ImportCommandParserTest.java -``` java -public class ImportCommandParserTest { ->>>>>>> 8120314b1b5ecce7cb60b6c03090396164741055 @Rule public TemporaryFolder testFolder = new TemporaryFolder(); + @Rule + public final EventsCollectorRule eventsCollectorRule = new EventsCollectorRule(); + @Rule + public ExpectedException thrown = ExpectedException.none(); - private ImportCommandParser parser = new ImportCommandParser(); + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private StorageManager storageManager; + + @Before + public void setUp() { + XmlAddressBookStorage addressBookStorage = new XmlAddressBookStorage(getTempFilePath("ab")); + JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); + storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + } + + private String getTempFilePath(String fileName) { + return testFolder.getRoot().getPath() + fileName; + } @Test - public void parse_noFile_throwsParseException() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE); - assertParseFailure(parser, " ", expectedMessage); + public void fileNotFound() { + ImportCommand importCommand = pathInCommand(INVALID_FILE); + assertCommandFailure(importCommand, model, ImportCommand.MESSAGE_INVALID_PATH); + } + + /** + * Returns an {@code ImportCommand} with parameters {@code filePath} + */ + private ImportCommand pathInCommand(String filePath) { + ImportCommand testCommand = new ImportCommand(filePath); + testCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + return testCommand; + } +} +``` +###### \java\seedu\address\logic\commands\ListCurrentTaskCommandTest.java +``` java +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCurrentTaskCommand. + */ +public class ListCurrentTaskCommandTest { + + private Model model; + private Model expectedModel; + private ListCurrentTaskCommand listCurrentTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + listCurrentTaskCommand = new ListCurrentTaskCommand(); + listCurrentTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(listCurrentTaskCommand, model, ListCurrentTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + new ListTaskCommand(); + assertCommandSuccess(listCurrentTaskCommand, model, ListCurrentTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} +``` +###### \java\seedu\address\logic\commands\ListTaskCommandTest.java +``` java +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTaskCommand. + */ +public class ListTaskCommandTest { + + private Model model; + private Model expectedModel; + private ListTaskCommand listTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + listTaskCommand = new ListTaskCommand(); + listTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(listTaskCommand, model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + new ListCurrentTaskCommand(); + assertCommandSuccess(listTaskCommand, model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} +``` +###### \java\seedu\address\logic\commands\SortTaskCommandTest.java +``` java +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTaskCommand. + */ +public class SortTaskCommandTest { + + private Model model; + private Model expectedModel; + private SortTaskCommand sortTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + sortTaskCommand = new SortTaskCommand(); + sortTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_sortsEverything() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nullList_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); } } ``` -###### /java/seedu/address/logic/parser/AddressBookParserTest.java +###### \java\seedu\address\logic\parser\AddressBookParserTest.java ``` java @Test public void parseCommand_add_alias() throws Exception { @@ -883,7 +1042,7 @@ public class ImportCommandParserTest { } ``` -###### /java/seedu/address/logic/parser/AddressBookParserTest.java +###### \java\seedu\address\logic\parser\AddressBookParserTest.java ``` java @Test public void parseCommand_clear_alias() throws Exception { @@ -899,7 +1058,7 @@ public class ImportCommandParserTest { } ``` -###### /java/seedu/address/logic/parser/AddressBookParserTest.java +###### \java\seedu\address\logic\parser\AddressBookParserTest.java ``` java @Test public void parseCommand_delete_alias() throws Exception { @@ -916,7 +1075,7 @@ public class ImportCommandParserTest { } ``` -###### /java/seedu/address/logic/parser/AddressBookParserTest.java +###### \java\seedu\address\logic\parser\AddressBookParserTest.java ``` java @Test public void parseCommand_redoCommandAlias_returnsRedoCommand() throws Exception { @@ -925,7 +1084,7 @@ public class ImportCommandParserTest { } ``` -###### /java/seedu/address/logic/parser/AddressBookParserTest.java +###### \java\seedu\address\logic\parser\AddressBookParserTest.java ``` java @Test public void parseCommand_undoCommandAlias_returnsUndoCommand() throws Exception { @@ -948,7 +1107,6 @@ public class ImportCommandParserTest { } } ``` -<<<<<<< HEAD ###### \java\seedu\address\logic\parser\AddTaskCommandParserTest.java ``` java public class AddTaskCommandParserTest { @@ -1052,286 +1210,17 @@ public class DeleteTaskCommandParserTest { } ``` ###### \java\seedu\address\logic\parser\ImportCommandParserTest.java -======= -###### /java/seedu/address/logic/commands/ImportCommandTest.java ->>>>>>> 8120314b1b5ecce7cb60b6c03090396164741055 ``` java -/** - * Contains integration tests (interaction with the Model) and unit tests for - * {@code ImportCommand}. - */ -public class ImportCommandTest { - - public static final String VALID_IMPORT_FILE_PATH = "src/data/ValidImport.xml"; - public static final String INVALID_IMPORT_FILE_PATH = "src/data/InValidImport.txt"; - +public class ImportCommandParserTest { @Rule public TemporaryFolder testFolder = new TemporaryFolder(); - @Rule - public final EventsCollectorRule eventsCollectorRule = new EventsCollectorRule(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - private StorageManager storageManager; - - @Before - public void setUp() { - XmlAddressBookStorage addressBookStorage = new XmlAddressBookStorage(getTempFilePath("ab")); - JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); - } - - private String getTempFilePath(String fileName) { - return testFolder.getRoot().getPath() + fileName; - } - - @Test - public void fileNotFound() { - ImportCommand importCommand = pathInCommand(INVALID_FILE); - assertCommandFailure(importCommand, model, ImportCommand.MESSAGE_INVALID_PATH); - } - - /** - * Returns an {@code ImportCommand} with parameters {@code filePath} - */ - private ImportCommand pathInCommand(String filePath) { - ImportCommand testCommand = new ImportCommand(filePath); - testCommand.setData(model, new CommandHistory(), new UndoRedoStack()); - return testCommand; - } -} -``` -###### /java/seedu/address/logic/commands/AddTaskCommandTest.java -``` java -public class AddTaskCommandTest { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void constructor_nullTask_throwNullPointerException() { - thrown.expect(NullPointerException.class); - new AddTaskCommand(null); - } - - @Test - public void execute_taskAcceptedByModel_addTaskSuccessful() throws Exception { - ModelStubAcceptingTaskAdded modelStub = new ModelStubAcceptingTaskAdded(); - Task validTask = new TaskBuilder().build(); - CommandResult commandResult = getAddTaskCommandForTask(validTask, modelStub).execute(); - - assertEquals(String.format(AddTaskCommand.MESSAGE_SUCCESS, validTask), commandResult.feedbackToUser); - assertEquals(Arrays.asList(validTask), modelStub.tasksAdded); - } + private ImportCommandParser parser = new ImportCommandParser(); @Test - public void equals() { - Task meeting = new TaskBuilder().withTitle("Meeting").build(); - Task assignment = new TaskBuilder().withTitle("Assignment").build(); - AddTaskCommand addMeetingCommand = new AddTaskCommand(meeting); - AddTaskCommand addAssignmentCommand = new AddTaskCommand(assignment); - - // same object -> returns true - assertTrue(addMeetingCommand.equals(addMeetingCommand)); - - // same values -> returns true - AddTaskCommand addMeetingCommandCopy = new AddTaskCommand(meeting); - assertTrue(addMeetingCommand.equals(addMeetingCommandCopy)); - - // different types -> returns false - assertFalse(addMeetingCommand.equals(1)); - - // null -> returns false - assertFalse(addMeetingCommand.equals(null)); - - // different person -> returns false - assertFalse(addMeetingCommand.equals(addAssignmentCommand)); - } - - private AddTaskCommand getAddTaskCommandForTask(Task task, Model model) { - AddTaskCommand command = new AddTaskCommand(task); - command.setData(model, new CommandHistory(), new UndoRedoStack()); - return command; - } - - /** - * A default model stub that have all of the methods failing. - */ - private class ModelStub implements Model { - @Override - public void addPerson(Person person) throws DuplicatePersonException { - fail("This method should not be called."); - } - - @Override - public void addTask(Task task) { - } - - @Override - public void deleteTask(Task target) throws TaskNotFoundException { - } - - @Override - public void updateTask(Task target, Task editedTask) throws TaskNotFoundException { - } - - @Override - public void resetData(ReadOnlyAddressBook newData) { - fail("This method should not be called."); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - fail("This method should not be called."); - return null; - } - - @Override - public void deletePerson(Person target) throws PersonNotFoundException { - fail("This method should not be called."); - } - - @Override - public void updatePerson(Person target, Person editedPerson) - throws DuplicatePersonException { - fail("This method should not be called."); - } - - @Override - public void sortPersons() { - fail("This method should not be called"); - } - - @Override - public ObservableList<Person> getFilteredPersonList() { - fail("This method should not be called."); - return null; - } - - @Override - public ObservableList<Task> getFilteredTaskList() { - return null; - } - - @Override - public ObservableList<Task>[][] getCalendarTaskLists() { - return new ObservableList[0][]; - } - - @Override - public void updateFilteredPersonList(Predicate<Person> predicate) { - fail("This method should not be called."); - } - - @Override - public void updateFilteredTaskList(Predicate<Task> predicate) { - - } - - @Override - public void addDeleteItem(String filepath) { - } - - @Override - public List<String> getItemList() { - return null; - } - - @Override - public void clearDeleteItems() { - - } - } - - /** - * A Model stub that always accept the task being added. - */ - private class ModelStubAcceptingTaskAdded extends ModelStub { - final ArrayList<Task> tasksAdded = new ArrayList<>(); - - @Override - public void addTask(Task task) { - requireNonNull(task); - tasksAdded.add(task); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return new AddressBook(); - } - } -} -``` -###### /java/seedu/address/testutil/TaskBuilder.java -``` java -/** - * A utility class to help with building Task objects. - */ -public class TaskBuilder { - - public static final String DEFAULT_TITLE = "Dance"; - public static final String DEFAULT_DESC = "Dance till I drop"; - public static final String DEFAULT_DEALINE = "03-08-2018"; - public static final String DEFAULT_PRIORITY = "3"; - - private Title title; - private TaskDescription desc; - private Deadline deadline; - private Priority priority; - - public TaskBuilder() { - title = new Title(DEFAULT_TITLE); - desc = new TaskDescription(DEFAULT_DESC); - deadline = new Deadline(DEFAULT_DEALINE); - priority = new Priority(DEFAULT_PRIORITY); - } - - /** - * Initializes the TaskBuilder with the data of {@code taskToCopy} - */ - public TaskBuilder(Task taskToCopy) { - title = taskToCopy.getTitle(); - desc = taskToCopy.getTaskDesc(); - deadline = taskToCopy.getDeadline(); - priority = taskToCopy.getPriority(); - } - - /** - * Sets the {@code Title} of the {@code Task} that we are building - */ - public TaskBuilder withTitle (String title) { - this.title = new Title(title); - return this; - } - - /** - * Sets the {@code TaskDescription} of the {@code Task} that we are building. - */ - public TaskBuilder withDesc(String desc) { - this.desc = new TaskDescription(desc); - return this; - } - - /** - * Sets the {@code Deadline} of the {@code Task} that we are building. - */ - public TaskBuilder withDeadline(String deadline) { - this.deadline = new Deadline(deadline); - return this; - } - - /** - * Sets the {@code Priority} of the {@code Task} that we are building. - */ - public TaskBuilder withPriority(String priority) { - this.priority = new Priority(priority); - return this; - } - - public Task build() { - return new Task(title, desc, deadline, priority); + public void parse_noFile_throwsParseException() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE); + assertParseFailure(parser, " ", expectedMessage); } } ``` diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 3813f164070a..343ca4f90bbf 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -1,6 +1,6 @@ = Your TA - User Guide :toc: -:toc-title: +:toc-title: Table of Content :toc-placement: preamble :sectnums: :imagesDir: images @@ -32,11 +32,19 @@ This app will not work with earlier versions of Java 8. + . Download the latest `YourTA.jar` link:{repoURL}/releases[here]. . Copy the file to the folder you want to use as the home folder for your application. -. Double-click the file to start the application. The GUI should appear in a few seconds, as shown in Figure 1 below. +. Double-click the file to start the application. The Login GUI should appear in a few seconds, as shown in Figure 1 below. + -.The GUI upon Starting Up. +.The Login GUI upon double-clicking. +//dummy image, plz update this image +image::Login.png[width="800"] + +. Enter your `Username` and `Password` to log in. _Refer to <<Features>> for details of the feature._ +. After logging in, now you should see the main Application GUI in turn appear, as shown in Figure 2 below. + +.The main Application GUI upon login. +//update this image also image::UiInitial.png[width="800"] -+ + . The command box is where you type in commands followed by pressing kbd:[Enter] to execute it. + e.g. typing *`help`* and pressing kbd:[Enter] will open the help window for the application. . Some example commands you can try: @@ -72,7 +80,7 @@ Then, click on the `Login` button. ==== *Command Format* -* There are some equivalent shortcut commands known as aliases with fewer alphabetic letters or a sign. + +* There are some equivalent shortcut commands known as _aliases_ with fewer alphabetic letters or a sign. + e.g. to add a person to the addressbook, you can type 'add n/John Doe', 'a n/John Doe' or '+ n/John Doe'. * Words in `UPPER_CASE` are the parameters to be supplied by the user. + e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. @@ -360,16 +368,23 @@ The 1st person in the results of the `find` command is selected, in this case, J .1st Person from `find` Command Selected. + image::SecondSelect.png[width="800"] +[[TaskList_Feature]] == Task, To-do list and Calendar Features -To see how the task scheduling feature works on the `Todo List` and `Calendar`, select the `Todo Lit` tab located right beside the `Person List` tab. +To see how the task scheduling feature works on the `Todo List` and `Calendar`, select the `Todo List` tab located right beside the `Person List` tab. === Adding a task: `addTask` Adds a task to the address book. + +Alias: `at` + Format: `addTask title/TITLE desc/TASK DESCRIPTION by/DEADLINE priority/PRIORITY` -New tasks will be added into the *To-do list* and *Calendar*. +New tasks will be added into the *To-do list* and *Calendar*. + + +[NOTE] +==== +`DEADLINE` must be in the format of dd-MM-YYYY. +==== Examples: @@ -387,9 +402,15 @@ This will add a task, 'Submit tutorial attendance' into the calendar and to-do l === Editing a task: `editTask` Edits a task to the address book. + +Alias: `et` + Format: `editTask INDEX [title/TASK TITLE] [desc/TASK DESCRIPTION] [by/DEADLINE] [priority/PRIORITY]` -The specified tasks will be edited in the *To-do list* and *Calendar*. +[NOTE] +==== +Not all fields are required, you may only pick the fields that you want to edit. +==== + +The specified tasks will be edited in the *Todo List* and *Calendar*. **** * The index refers to the index number shown in the last task listing. The index *must be a positive integer* 1, 2, 3, ... @@ -400,10 +421,37 @@ The specified tasks will be edited in the *To-do list* and *Calendar*. === Removing a task: `deleteTask` Deletes a task in the address book. + +Alias: `dt` + Format: `deleteTask INDEX` -The specified tasks will be deleted from the *To-do list* and *Calendar*. +The specified indexed task will be deleted from the *Todo List* and *Calendar*. +// tag::dispalyTask[] +=== Listing all tasks: `listTask` + +Shows a list of all tasks in application. + +Alias: `lt` + +Format: `listTask` + +All the tasks stored in application will be listed in the *Todo List* + +=== Listing current month tasks: `listCurrentTask` + +Shows a list of tasks that have deadline in the current month. + +Alias: `lct` + +Format: `listCurrentTask` + +Only the tasks due within current month will be listed in the *Todo List* + +=== Sorting tasks: `sortTask` + +Sorts all entries displayed in *Todo List* in date order. + +Alias: `stt` +Format: `sortTask` + +// end::displayTask[] + +[[otherCommands]] == Miscellaneous Commands === Listing entered commands : `history` @@ -494,7 +542,7 @@ This command clears ALL the data from the application (both people and tasks). + === Importing data from another file : `import` -Extracting data from an xml formatted file and +Extracts data from an xml formatted file and replaces the current stored data. + Format: `import FILEPATH` @@ -503,6 +551,15 @@ Examples: Command entered: + `import ~/download/NewData.xml` +// tag::switchTab[] +=== Switching between tabs: `switchTab` + +Switches between the *Person List* tab and *Todo List* tab. +Alias: `swt` +Format: `switchTab` + +// end::switchTab[] + === Sorting all entries : `sort` Sorts all entries from the address book in alphabetical order based on name. + @@ -603,12 +660,15 @@ e.g `updateDP 1 dp/C:\Users\Betsy\Desktop\betsy.jpg` === Tasks, To-do list and Calendar commands -*Add Task* `addTask desc/TASK DESCRIPTION by/DEADLINE priority/PRIORITY` + +*Add Task*: `addTask desc/TASK DESCRIPTION by/DEADLINE priority/PRIORITY` + e.g. `addTask title/Grade Exams desc/Grade mid-terms by/04-04-2018 priority/2` -*Delete Task* `deleteTask INDEX` + +*Delete Task*: `deleteTask INDEX` + e.g. `deleteTask 1` -*Edit Task* `editTask INDEX [title/TASK TITLE] [desc/TASK DESCRIPTION] [by/DEADLINE] [priority/PRIORITY]` -e.g. `editTask 1 title/Eat Dinner` +*Edit Task*: `editTask INDEX [title/TASK TITLE] [desc/TASK DESCRIPTION] [by/DEADLINE] [priority/PRIORITY]` +e.g. `editTask 1 title/Eat Dinner` + +*List Task*: `listTask` + +*List Current month tasks*: `listCurrentTask` + +*Sort tasks by date*: `sortTask` === Miscellaneous Commands @@ -616,3 +676,4 @@ e.g. `editTask 1 title/Eat Dinner` * *History* : `history` * *Undo* : `undo` * *Redo* : `redo` +* *Switch tabs*: `switchTab` diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 25efec22f5ec..630b443bf107 100755 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -78,6 +78,7 @@ public ListElementPointer getHistorySnapshot() { return new ListElementPointer(history.getHistory()); } + //@@author Wu Di @Override public void setTabPane(TabPane tabPane) { addressBookParser.setTabPane(tabPane); diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 6eeba1d34de1..bb9131b3161f 100755 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -30,7 +30,7 @@ public class AddCommand extends UndoableCommand { + PREFIX_PHONE + "PHONE " + PREFIX_EMAIL + "EMAIL " + PREFIX_ADDRESS + "ADDRESS " - + PREFIX_DISPLAY_PIC + "[IMAGE TO DISPLAY PICTURE] " + + PREFIX_DISPLAY_PIC + "[IMAGE TO DISPLAY PICTURE] \n" + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "John Doe " + PREFIX_MATRIC_NUMBER + "A1234567J " diff --git a/src/main/java/seedu/address/logic/commands/ListCurrentTaskCommand.java b/src/main/java/seedu/address/logic/commands/ListCurrentTaskCommand.java new file mode 100644 index 000000000000..ca48086324a0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListCurrentTaskCommand.java @@ -0,0 +1,21 @@ +package seedu.address.logic.commands; + +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CURRENT_TASKS; + +//@@author Wu Di +/** + * Lists all tasks due by the current month stored in the address book. + */ +public class ListCurrentTaskCommand extends Command { + + public static final String COMMAND_WORD = "listCurrentTask"; + public static final String COMMAND_ALIAS = "lct"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks due this month"; + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_CURRENT_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ListTaskCommand.java b/src/main/java/seedu/address/logic/commands/ListTaskCommand.java new file mode 100644 index 000000000000..6d31a2ae4b4b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListTaskCommand.java @@ -0,0 +1,21 @@ +package seedu.address.logic.commands; + +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TASKS; + +//@@author Wu Di +/** + * Lists all tasks stored in the address book. + */ +public class ListTaskCommand extends Command { + + public static final String COMMAND_WORD = "listTask"; + public static final String COMMAND_ALIAS = "lt"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SortTaskCommand.java b/src/main/java/seedu/address/logic/commands/SortTaskCommand.java new file mode 100644 index 000000000000..89e3854a4b0e --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SortTaskCommand.java @@ -0,0 +1,20 @@ +package seedu.address.logic.commands; + +//@@author Wu Di +/** + * Lists all tasks stored in the address book in date order. + */ +public class SortTaskCommand extends Command { + + public static final String COMMAND_WORD = "sortTask"; + public static final String COMMAND_ALIAS = "stt"; + + public static final String MESSAGE_SUCCESS = "Todo List is sorted"; + + + @Override + public CommandResult execute() { + model.sortTasks(); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index dd48e45d9f5e..5f6d551ac0b1 100755 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -23,10 +23,13 @@ import seedu.address.logic.commands.HistoryCommand; import seedu.address.logic.commands.ImportCommand; import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.ListCurrentTaskCommand; +import seedu.address.logic.commands.ListTaskCommand; import seedu.address.logic.commands.MarkCommand; import seedu.address.logic.commands.RedoCommand; import seedu.address.logic.commands.SelectCommand; import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.commands.SortTaskCommand; import seedu.address.logic.commands.SwitchTabCommand; import seedu.address.logic.commands.UndoCommand; import seedu.address.logic.commands.UpdateDisplayCommand; @@ -140,11 +143,24 @@ public Command parseCommand(String userInput) throws ParseException { case SwitchTabCommand.COMMAND_ALIAS: return new SwitchTabCommand(tabPane); + case ListTaskCommand.COMMAND_WORD: + case ListTaskCommand.COMMAND_ALIAS: + return new ListTaskCommand(); + + case ListCurrentTaskCommand.COMMAND_WORD: + case ListCurrentTaskCommand.COMMAND_ALIAS: + return new ListCurrentTaskCommand(); + + case SortTaskCommand.COMMAND_WORD: + case SortTaskCommand.COMMAND_ALIAS: + return new SortTaskCommand(); + default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } } + //@@author Wu Di public void setTabPane(TabPane tabPane) { this.tabPane = tabPane; } diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index b1ae6aeee8ac..6a7b24c3583d 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -250,6 +250,10 @@ public void sortList() { persons.sortList(); } + public void sortTaskList() { + tasks.sortList(); + } + @Override public ObservableList<Task> getTaskList() { return tasks.asObservableList(); diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 81ce9f5406d3..847e6bf020bc 100755 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -61,6 +61,8 @@ void updatePerson(Person target, Person editedPerson) void sortPersons(); + void sortTasks(); + /** Returns an unmodifiable view of the filtered person list */ ObservableList<Person> getFilteredPersonList(); diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 649d69e54cc4..4034d0f7fcbf 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -97,7 +97,7 @@ public void deleteTask(Task target) throws TaskNotFoundException { @Override public synchronized void addTask(Task task) { addressBook.addTask(task); - updateFilteredTaskList(PREDICATE_SHOW_ALL_CURRENT_TASKS); + updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); indicateAddressBookChanged(); } @@ -130,6 +130,11 @@ public void updateTask(Task target, Task editedTask) indicateAddressBookChanged(); } + @Override + public void sortTasks() { + addressBook.sortTaskList(); + } + //@@author @Override public void sortPersons() { diff --git a/src/main/java/seedu/address/model/task/Task.java b/src/main/java/seedu/address/model/task/Task.java index 39396e9a47e0..dd08b5f2315f 100755 --- a/src/main/java/seedu/address/model/task/Task.java +++ b/src/main/java/seedu/address/model/task/Task.java @@ -90,8 +90,30 @@ public String toString() { .append(getPriority()); return builder.toString(); } + + //@@author Wu Di @Override public int compareTo(Task task) { - return task.getPriority().value - this.getPriority().value; + int yearDiff = this.getDeadlineYear() - task.getDeadlineYear(); + int monthDiff = this.getDeadlineMonth() - task.getDeadlineMonth(); + int dayDiff = this.getDeadlineDay() - task.getDeadlineDay(); + + return compareDate(yearDiff, monthDiff, dayDiff); + } + + /** + * Compares the dates + * @param yearDiff difference in year + * @param monthDiff difference in month + * @param dayDiff difference in day + */ + private int compareDate (int yearDiff, int monthDiff, int dayDiff) { + if (yearDiff != 0) { + return yearDiff; + } + if (monthDiff != 0) { + return monthDiff; + } + return dayDiff; } } diff --git a/src/main/java/seedu/address/model/task/UniqueTaskList.java b/src/main/java/seedu/address/model/task/UniqueTaskList.java index c6effe262104..e072b76be1e3 100644 --- a/src/main/java/seedu/address/model/task/UniqueTaskList.java +++ b/src/main/java/seedu/address/model/task/UniqueTaskList.java @@ -77,12 +77,8 @@ public void setTask(Task target, Task editedTask) if (index == -1) { throw new TaskNotFoundException(); } - internalList.set(index, editedTask); - int indexCalendar = calendarList[target.getDeadline().diff][target.getDeadlineDay()].indexOf(target); - if (indexCalendar == -1) { - throw new TaskNotFoundException(); - } - calendarList[target.getDeadline().diff][target.getDeadlineDay()].set(indexCalendar, editedTask); + remove(target); + add(editedTask); } /** @@ -100,7 +96,16 @@ public boolean remove(Task toRemove) throws TaskNotFoundException { return taskFoundAndDeleted; } - //@@author + /** + * Sorts the tasks in the list in date order + */ + public void sortList() { + requireNonNull(internalList); + + FXCollections.sort(internalList); + } + + //@@author JoonKai1995 public void setTasks(UniqueTaskList replacement) { this.internalList.setAll(replacement.internalList); for (int i = 0; i < 7; i++) { diff --git a/src/main/java/seedu/address/ui/TodoCard.java b/src/main/java/seedu/address/ui/TodoCard.java index 1fa16c5b742a..6093b8f87b75 100644 --- a/src/main/java/seedu/address/ui/TodoCard.java +++ b/src/main/java/seedu/address/ui/TodoCard.java @@ -44,10 +44,10 @@ public TodoCard(Task task, int displayedIndex) { super(FXML); this.task = task; id.setText(displayedIndex + ". "); - title.setText(task.getTitle().value); + title.setText("Title: " + task.getTitle().value); priority.setText("Priority: " + task.getPriority().toString()); deadline.setText("Deadline: " + task.getDeadline().dateString); - description.setText(task.getTaskDesc().value); + description.setText("Description: " + task.getTaskDesc().value); } @Override diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index 6a36ad4f283f..0e10b4657bdc 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -143,6 +143,11 @@ public void sortPersons() { fail("This method should not be called"); } + @Override + public void sortTasks() { + fail("This method should not be called"); + } + @Override public ObservableList<Person> getFilteredPersonList() { fail("This method should not be called."); diff --git a/src/test/java/seedu/address/logic/commands/AddTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/AddTaskCommandTest.java index 956ef6a91f23..4fba06787457 100644 --- a/src/test/java/seedu/address/logic/commands/AddTaskCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddTaskCommandTest.java @@ -129,6 +129,11 @@ public void sortPersons() { fail("This method should not be called"); } + @Override + public void sortTasks() { + fail("This method should not be called"); + } + @Override public ObservableList<Person> getFilteredPersonList() { fail("This method should not be called."); diff --git a/src/test/java/seedu/address/logic/commands/CommandTaskTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTaskTestUtil.java index 6597277a3007..709104fce8a9 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTaskTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTaskTestUtil.java @@ -1,12 +1,19 @@ package seedu.address.logic.commands; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.fail; import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; import static seedu.address.logic.parser.CliSyntax.PREFIX_PRIORITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TASK_DESC; import static seedu.address.logic.parser.CliSyntax.PREFIX_TITLE; +import java.util.ArrayList; +import java.util.List; + import seedu.address.logic.CommandHistory; import seedu.address.logic.UndoRedoStack; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.task.Task; import seedu.address.model.task.exceptions.TaskNotFoundException; @@ -55,9 +62,44 @@ public class CommandTaskTestUtil { } /** - * Deletes the first person in {@code model}'s filtered list from {@code model}'s address book. + * Executes the given {@code command}, confirms that <br> + * - the result message matches {@code expectedMessage} <br> + * - the {@code actualModel} matches {@code expectedModel} + */ + public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + Model expectedModel) { + try { + CommandResult result = command.execute(); + assertEquals(expectedMessage, result.feedbackToUser); + } catch (CommandException ce) { + throw new AssertionError("Execution of command should not fail.", ce); + } + } + + /** + * Executes the given {@code command}, confirms that <br> + * - a {@code CommandException} is thrown <br> + * - the CommandException message matches {@code expectedMessage} <br> + * - the address book and the filtered task list in the {@code actualModel} remain unchanged + */ + public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { + // we are unable to defensively copy the model for comparison later, so we can + // only do so by copying its components. + AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); + List<Task> expectedFilteredList = new ArrayList<>(actualModel.getFilteredTaskList()); + + try { + command.execute(); + fail("The expected CommandException was not thrown."); + } catch (CommandException e) { + assertEquals(expectedMessage, e.getMessage()); + } + } + + /** + * Deletes the first task in {@code model}'s filtered list from {@code model}'s address book. */ - public static void deleteTaskPerson(Model model) { + public static void deleteFirstTask(Model model) { Task firstTask = model.getFilteredTaskList().get(0); try { model.deleteTask(firstTask); diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index 04b896e96f71..764b536c9de6 100755 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -25,6 +25,7 @@ import seedu.address.model.person.InfoContainsKeywordsPredicate; import seedu.address.model.person.Person; import seedu.address.model.person.exceptions.PersonNotFoundException; +import seedu.address.model.task.Task; import seedu.address.testutil.EditPersonDescriptorBuilder; /** @@ -155,6 +156,7 @@ public static void showPersonAtIndex(Model model, Index targetIndex) { assertEquals(1, model.getFilteredPersonList().size()); } + /** * Deletes the first person in {@code model}'s filtered list from {@code model}'s address book. */ diff --git a/src/test/java/seedu/address/logic/commands/EditTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/EditTaskCommandTest.java index b8bcfb156bfd..c34e1a800440 100644 --- a/src/test/java/seedu/address/logic/commands/EditTaskCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/EditTaskCommandTest.java @@ -8,10 +8,10 @@ import static seedu.address.logic.commands.CommandTaskTestUtil.VALID_TASK_DESC_EXAM; import static seedu.address.logic.commands.CommandTaskTestUtil.VALID_TITLE_EXAM; import static seedu.address.logic.commands.CommandTaskTestUtil.VALID_TITLE_MARK; +import static seedu.address.logic.commands.CommandTaskTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTaskTestUtil.assertCommandSuccess; import static seedu.address.logic.commands.CommandTaskTestUtil.prepareRedoCommand; import static seedu.address.logic.commands.CommandTaskTestUtil.prepareUndoCommand; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_TASK; import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_TASK; import static seedu.address.testutil.TypicalTasks.getTypicalAddressBook; diff --git a/src/test/java/seedu/address/logic/commands/ListCurrentTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCurrentTaskCommandTest.java new file mode 100644 index 000000000000..50cf07be43e9 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ListCurrentTaskCommandTest.java @@ -0,0 +1,44 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalTasks.getTypicalAddressBook; + +import org.junit.Before; +import org.junit.Test; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.UndoRedoStack; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; + +//@@author Wu Di +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCurrentTaskCommand. + */ +public class ListCurrentTaskCommandTest { + + private Model model; + private Model expectedModel; + private ListCurrentTaskCommand listCurrentTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + listCurrentTaskCommand = new ListCurrentTaskCommand(); + listCurrentTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(listCurrentTaskCommand, model, ListCurrentTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + new ListTaskCommand(); + assertCommandSuccess(listCurrentTaskCommand, model, ListCurrentTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ListTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/ListTaskCommandTest.java new file mode 100644 index 000000000000..eac008b03324 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ListTaskCommandTest.java @@ -0,0 +1,44 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalTasks.getTypicalAddressBook; + +import org.junit.Before; +import org.junit.Test; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.UndoRedoStack; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; + +//@@author Wu Di +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTaskCommand. + */ +public class ListTaskCommandTest { + + private Model model; + private Model expectedModel; + private ListTaskCommand listTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + listTaskCommand = new ListTaskCommand(); + listTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(listTaskCommand, model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + new ListCurrentTaskCommand(); + assertCommandSuccess(listTaskCommand, model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/SortTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/SortTaskCommandTest.java new file mode 100644 index 000000000000..d04560e26079 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/SortTaskCommandTest.java @@ -0,0 +1,53 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTaskTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalTasks.getTypicalAddressBook; + +import org.junit.Before; +import org.junit.Test; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.UndoRedoStack; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; + +//@@author Wu Di +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTaskCommand. + */ +public class SortTaskCommandTest { + + private Model model; + private Model expectedModel; + private SortTaskCommand sortTaskCommand; + + @Before + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + + sortTaskCommand = new SortTaskCommand(); + sortTaskCommand.setData(model, new CommandHistory(), new UndoRedoStack()); + } + + @Test + public void execute_listIsNotFiltered_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_sortsEverything() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nullList_sortsList() { + assertCommandSuccess(sortTaskCommand, model, SortTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +}