Skip to content

Commit

Permalink
Merge pull request AY2324S1-CS2103T-F12-4#105 from jason-raiin/mark-c…
Browse files Browse the repository at this point in the history
…ommand

Mark command
  • Loading branch information
howenc authored Nov 2, 2023
2 parents b23b4d1 + 55ecd1a commit 2b19832
Show file tree
Hide file tree
Showing 57 changed files with 815 additions and 231 deletions.
8 changes: 4 additions & 4 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ Once the indexes of the `Person` and `Meeting` objects to view (if any) are stor
- For the case of `editc` and `editm`, this is judged to not be an issue as the view commands still obey their definition of displaying the item at a specified list index.
- For the case of `deletec`, `deletem`, `findc` and `findm`, a simple fix is to simply set the stored `Index` to null only for these commands.


### Find meeting feature

#### Behaviour and Implementation
Expand Down Expand Up @@ -259,7 +258,6 @@ Step 2. The `FindMeetingCommand` will be immediately executed and will call `set
The following diagrams show the entire sequence flow for `LogicManager#execute()` for FindMeetingCommand.
![FindMeetingSequence2](images/FindMeetingSequence2.png)


#### Design Considerations and Rationale

1. Given the amount of predicates `FindMeetingCommand` is supposed to use, every predicate needs to be combined in order to maintain good SLAP.
Expand Down Expand Up @@ -288,16 +286,15 @@ The following sequence diagram shows how the add attendee operation works:
A Person object can be obtained from a Meeting's list of attendees by searching through `UniquePersonList`
for a `Person` with a name matching `attendeeName`.


### Remove attendee feature

User can specify an Attendee to remove from a specified Meeting by specifying its index in the list of Attendees.
This is the main motivation behind using a LinkedHashSet for the implementation of the Attendee Set.

The following sequence diagram shows how the remove attendee operation works:

![RemoveAttendeeSequenceDiagram](images/RemoveAttendeeSequenceDiagram.png)


### \[Proposed\] Undo/redo feature

#### Proposed Implementation
Expand Down Expand Up @@ -459,6 +456,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
| `* * *` | agent | add contacts to meetings | |
| `* * *` | agent | remove contacts from meetings | |
| `* * *` | agent | view contacts in meetings | |
| `* *` | agent | mark meetings as complete | know which meetings are still pending |
| `*` | agent who wants to meet clients regularly | know the last contacted date | when to touch base with a client |

_{More to be added}_
Expand Down Expand Up @@ -516,6 +514,8 @@ _{More to be added}_
4. OutBook shows the details of the meeting.
5. User requests to remove a specific contact from the meeting.
6. OutBook removes the contact from the meeting.
7. User requests to remove a specific contact from the meeting.
8. OutBook removes the contact from the meeting.

Use case ends.

Expand Down
140 changes: 77 additions & 63 deletions docs/UserGuide.md

Large diffs are not rendered by default.

20 changes: 12 additions & 8 deletions docs/team/jason-raiin.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,40 @@ My contributions to the project are listed below.

- **New Feature**: Remove contact from meeting command
- Added command and parser
- Thorough testing

- **New Feature**: Mark meeting command
- Added command and parser
- Updates last contacted time for contacts

- **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=jason-raiin&breakdown=true)

- **Project management**:

- to be added soon
- Contributed issues
- Reviewed PRs

- **Enhancements to existing features**:

- Add Tag to meetings: logic and UI
- Add Tags field to meetings
- Convert Tag to factory class with no duplicates
- Added Status field to meetings

- **Documentation**:

- User Guide
- Remove meeting contact command
- `rmmc` command
- `mark` command
- Minor edits

- Developer Guide
- `rmmc` command
- `mark` command
- User profile
- Value proposition
- User stories
- Use cases

- **Community**:

- to be added soon

- **Tools**:

- Added util method `parseIndexes`
- Improved methods for `typicalMeetings` and `typicalAddressBook`
2 changes: 2 additions & 0 deletions src/main/java/seedu/address/logic/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public static String format(Meeting meeting) {
.append(meeting.getEnd())
.append("; Attendees: ");
meeting.getAttendees().forEach(builder::append);
builder.append("; Completed: ")
.append(meeting.getStatus());
return builder.toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ static Meeting addAttendee(Meeting meeting, Attendee attendeeToAdd) {
Set<Attendee> updatedAttendees = new LinkedHashSet<>(meeting.getAttendees());
updatedAttendees.add(attendeeToAdd);
Meeting updatedMeeting = new Meeting(meeting.getTitle(), meeting.getLocation(), meeting.getStart(),
meeting.getEnd(), updatedAttendees, meeting.getTags());
meeting.getEnd(), updatedAttendees, meeting.getTags(), meeting.getStatus());

return updatedMeeting;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import seedu.address.model.meeting.Attendee;
import seedu.address.model.meeting.Location;
import seedu.address.model.meeting.Meeting;
import seedu.address.model.meeting.MeetingStatus;
import seedu.address.model.meeting.MeetingTime;
import seedu.address.model.meeting.Title;
import seedu.address.model.tag.Tag;
Expand Down Expand Up @@ -102,12 +103,13 @@ static Meeting createEditedMeeting(Meeting meetingToEdit,
LocalDateTime updatedEnd = editMeetingDescriptor.getEnd().orElse(meetingToEdit.getEnd());
Set<Attendee> attendees = meetingToEdit.getAttendees();
Set<Tag> updatedTags = editMeetingDescriptor.getTags().orElse(meetingToEdit.getTags());
MeetingStatus status = meetingToEdit.getStatus();

if (!MeetingTime.isValidMeetingTime(updatedStart, updatedEnd)) {
throw new CommandException(MeetingTime.MESSAGE_CONSTRAINTS);
}

return new Meeting(updatedTitle, updatedLocation, updatedStart, updatedEnd, attendees, updatedTags);
return new Meeting(updatedTitle, updatedLocation, updatedStart, updatedEnd, attendees, updatedTags, status);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class FindMeetingCommand extends Command {
* @param predicate The predicate that will be used by the FindMeetingCommand object.
*/
public FindMeetingCommand(GeneralMeetingPredicate predicate) {
assert predicate != null;
requireNonNull(predicate);
this.predicate = predicate;
}

Expand Down
105 changes: 105 additions & 0 deletions src/main/java/seedu/address/logic/commands/MarkMeetingCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;

import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;

import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.meeting.Attendee;
import seedu.address.model.meeting.Meeting;
import seedu.address.model.meeting.MeetingStatus;
import seedu.address.model.person.Person;

/**
* Marks a meeting as complete.
*/
public class MarkMeetingCommand extends Command {

public static final String COMMAND_WORD = "mark";

public static final String MESSAGE_MARK_MEETING_SUCCESS = "Meeting marked as complete: %1$s";

public static final String MESSAGE_MEETING_ALREADY_COMPLETE = "Meeting has already been marked as complete.";

public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Marks the meeting identified by the index number used in the displayed meetings list as complete.\n"
+ "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + "1";

private final Index targetIndex;

public MarkMeetingCommand(Index targetIndex) {
this.targetIndex = targetIndex;
}

@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
List<Meeting> lastShownList = model.getFilteredMeetingList();

if (targetIndex.getZeroBased() >= lastShownList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_MEETING_DISPLAYED_INDEX);
}

Meeting meetingToMark = lastShownList.get(targetIndex.getZeroBased());
Meeting updatedMeeting = markMeeting(meetingToMark);
model.setMeeting(meetingToMark, updatedMeeting);

Iterator<Attendee> attendeeIterator = meetingToMark.getAttendees().iterator();
while (attendeeIterator.hasNext()) {
Attendee attendee = attendeeIterator.next();
Person person = model.getPerson(attendee.getAttendeeName());
Person updatedPerson = updateLastContactedTime(person, meetingToMark.getEnd());
model.setPerson(person, updatedPerson);
}

return new CommandResult(String.format(MESSAGE_MARK_MEETING_SUCCESS, Messages.format(updatedMeeting)));
}

static Meeting markMeeting(Meeting meeting) throws CommandException {
if (meeting.getStatus().isComplete) {
throw new CommandException(MESSAGE_MEETING_ALREADY_COMPLETE);
}

Meeting markedMeeting = new Meeting(meeting.getTitle(), meeting.getLocation(), meeting.getStart(),
meeting.getEnd(), meeting.getAttendees(), meeting.getTags(), new MeetingStatus(true));

return markedMeeting;
}

static Person updateLastContactedTime(Person person, LocalDateTime lastContactedTime) {
if (lastContactedTime.isBefore(person.getLastContactedTime())) {
return person;
}

Person updatedPerson = new Person(person.getName(), person.getPhone(), person.getEmail(), lastContactedTime,
person.getStatus(), person.getRemark(), person.getTags());

return updatedPerson;
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

// instanceof handles nulls
if (!(other instanceof MarkMeetingCommand)) {
return false;
}

MarkMeetingCommand otherMarkMeetingCommand = (MarkMeetingCommand) other;
return targetIndex.equals(otherMarkMeetingCommand.targetIndex);
}

@Override
public String toString() {
return new ToStringBuilder(this).add("targetIndex", targetIndex).toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ static Meeting removeAttendee(Meeting meeting, Attendee attendeeToRemove) {
Set<Attendee> updatedAttendees = new LinkedHashSet<>(meeting.getAttendees());
updatedAttendees.remove(attendeeToRemove);
Meeting updatedMeeting = new Meeting(meeting.getTitle(), meeting.getLocation(), meeting.getStart(),
meeting.getEnd(), updatedAttendees, meeting.getTags());
meeting.getEnd(), updatedAttendees, meeting.getTags(), meeting.getStatus());

return updatedMeeting;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import seedu.address.model.meeting.Attendee;
import seedu.address.model.meeting.Location;
import seedu.address.model.meeting.Meeting;
import seedu.address.model.meeting.MeetingStatus;
import seedu.address.model.meeting.MeetingTime;
import seedu.address.model.meeting.Title;
import seedu.address.model.tag.Tag;
Expand Down Expand Up @@ -56,8 +57,9 @@ public AddMeetingCommand parse(String args) throws ParseException {
}
Set<Attendee> attendeeList = ParserUtil.parseAttendees(argMultimap.getAllValues(PREFIX_NAME));
Set<Tag> tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
MeetingStatus status = new MeetingStatus(false);

Meeting meeting = new Meeting(title, location, start, end, attendeeList, tagList);
Meeting meeting = new Meeting(title, location, start, end, attendeeList, tagList, status);

return new AddMeetingCommand(meeting);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.commands.ListMeetingCommand;
import seedu.address.logic.commands.MarkMeetingCommand;
import seedu.address.logic.commands.RemoveMeetingContactCommand;
import seedu.address.logic.commands.ViewContactCommand;
import seedu.address.logic.commands.ViewMeetingCommand;
Expand Down Expand Up @@ -113,6 +114,9 @@ public Command parseCommand(String userInput) throws ParseException {
case FindMeetingCommand.COMMAND_WORD:
return new FindMeetingCommandParser().parse(arguments);

case MarkMeetingCommand.COMMAND_WORD:
return new MarkMeetingCommandParser().parse(arguments);

default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;

import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.MarkMeetingCommand;
import seedu.address.logic.parser.exceptions.ParseException;

/**
* Parses input arguments and creates a new MarkMeetingCommand object
*/
public class MarkMeetingCommandParser implements Parser<MarkMeetingCommand> {

/**
* Parses the given {@code String} of arguments in the context of the MarkMeetingCommand
* and returns a MarkMeetingCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public MarkMeetingCommand parse(String args) throws ParseException {
try {
Index index = ParserUtil.parseIndex(args);
return new MarkMeetingCommand(index);
} catch (ParseException pe) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkMeetingCommand.MESSAGE_USAGE), pe);
}
}

}
8 changes: 8 additions & 0 deletions src/main/java/seedu/address/model/AddressBook.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ public void addMeeting(Meeting m) {
meetings.add(m);
}

/**
* Returns the person with the given name
* or throws {@code IndexOutOfBoundsException} if it does not exist.
*/
public Person getPerson(String name) {
return persons.getPerson(name);
}

/**
* Replaces the given person {@code target} in the list with {@code editedPerson}.
* {@code target} must exist in the address book.
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/seedu/address/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ public interface Model {
*/
void addMeeting(Meeting meeting);

/**
* Returns the person with the given name.
* A person with the given name must exist in the address book.
*/
Person getPerson(String name);

/**
* Replaces the given person {@code target} with {@code editedPerson}.
* {@code target} must exist in the address book.
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/seedu/address/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ public void addMeeting(Meeting meeting) {
updateFilteredMeetingList(PREDICATE_SHOW_ALL_MEETINGS);
}

@Override
public Person getPerson(String name) {
return addressBook.getPerson(name);
}

@Override
public void setPerson(Person target, Person editedPerson) {
requireAllNonNull(target, editedPerson);
Expand Down
Loading

0 comments on commit 2b19832

Please sign in to comment.