Skip to content

Commit

Permalink
Merge pull request #322 from ReflectiveObsidian/dg-implementation-person
Browse files Browse the repository at this point in the history
Add implementation text for person commands
  • Loading branch information
Bandov authored Apr 14, 2024
2 parents b66cb21 + 0aff3f1 commit bc1141e
Show file tree
Hide file tree
Showing 15 changed files with 270 additions and 28 deletions.
104 changes: 102 additions & 2 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ The bulk of the app's work is done by the following four components:

**How the architecture components interact with each other**

The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 0000`, assuming that `0000` corresponds to a valid Person UUID.
The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete /0001`, assuming that `0001` corresponds to a valid Person UUID.

<img src="images/ArchitectureSequenceDiagram.png" width="574" />
<img src="images/DeletePersonSequenceDiagram.png" width="574" />

Each of the four main components (also shown in the diagram above),

Expand Down Expand Up @@ -271,6 +271,106 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa

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

### Add Person feature

This feature is the `Add` command that allows users to add a person to the address book.
This feature creates a person with 0 or more attributes, and then adds that person to the address book.

#### Implementation

Adding a person is carried out using `AddCommand` and `AddCommandParser`.
The `AddCommand` class extends the `Command` class.
The `AddCommandParser` class extends the `Parser` class.

Given below is an example usage scenario and how adding a person works.

Step 1: The user enters a command to add a person with `Name` attribute to their family tree in Gene-nie.
For example: `add /Name Bob`.

Step 2: The `LogicManager` component receives this command as a string and passes it to the `AddressBookParser`.

Step 3: The `AddressBookParser` recognizes the `add` keyword and creates a new `AddCommandParser`.

Step 4: The `AddCommandParser` parses the rest of the command `/Name Bob`.
It creates a `HashMap` containing the attribute `Name` with value `Bob`.
It then creates a new `AddCommand` with this `HashMap`.
In this step, duplicate attribute names are also checked for, as part of the HashMap creation process in ParserUtil.

Step 5: The `AddCommand` is executed.

Step 6: `AddCommand#execute` continues by calling the following method from `Model`:
* `Model#addPerson(Person)` It adds a given person to the address book.

Step 7: The `AddCommand#execute` method returns the `CommandResult` object to the `LogicManager` component.

The following sequence diagram shows how adding a person works:

![AddPersonSequenceDiagram](images/AddPersonSequenceDiagram.png)

<div markdown="span" class="alert alert-info">:information_source: **Note:** The lifelines should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

The following activity diagram sheds more light on exactly what happens when a user executes the 'add' command:

![AddPersonActivityDiagram](images/AddPersonActivityDiagram.png)

#### Design Considerations

##### Aspect: When to check for duplicate attributes
* Alternative 1 (current choice): `AddCommand#execute` checks for duplicates before adding the person to the addressbook.
* Pros: Makes the command more atomic - if there is an error, no changes are made.
* Cons: Less efficient as the program iterates through all attributes twice.
* Alternative 2: `AddCommand#execute` adds an empty person to the address book and then checks for duplicates while appending attributes to that person.
* Pros: Shorter code, less repetition of actions.
* Cons: Duplicate attributes would halt command and leave the address book in a partially edited state. Fixing this would result in less readable code.

[Back to Table of Contents](#table-of-contents)

--------------------------------------------------------------------------------------------------------------------
### Delete Person feature

This feature is the `Delete` command that allows users to remove a person from the address book.

#### Implementation

Deleting a person is carried out using `DeleteCommand` and `DeleteCommandParser`.
The `DeleteCommand` class extends the `Command` class.
The `DeleteCommandParser` class extends the `Parser` class.

Given below is an example usage scenario and how deleting a person works.

Step 1: The user enters a command to delete a person with the UUID `0001`.
For example: `delete /0001`.

Step 2: The `LogicManager` component receives this command as a string and passes it to the `AddressBookParser`.

Step 3: The `AddressBookParser` recognizes the `delete` keyword and creates a new `DeleteCommandParser`.

Step 4: The `DeleteCommandParser` parses the rest of the command `/0001`.
It then creates a new `DeleteCommand` with the UUID `0001`.

Step 5: The `DeleteCommand` is executed.

Step 6: `DeleteCommand#execute` continues by calling the following method from `Model`:
* `Model#getFullUuid(String)` It takes in the partial 4-character UUID provided by the user and converts it to the `UUID` object representative of the person to delete.
* `Model#getPersonByUuid(UUID)` It takes the full UUID obtained earlier and returns the corresponding `Person` object.
* `Model#deletePerson(Person)` It deletes the provided person from the address book.
* `Model#deleteRelationshipsOfPerson(UUID)` It deletes all relationships associated with the person being deleted.

Step 7: The `DeleteCommand#execute` method returns the `CommandResult` object to the `LogicManager` component.

The following sequence diagram shows how deleting a person works:

![DeletePersonSequenceDiagram](images/DeletePersonSequenceDiagram.png)

<div markdown="span" class="alert alert-info">:information_source: **Note:** The lifelines should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

The following activity diagram sheds more light on exactly what happens when a user executes the 'delete' command:

![DeletePersonActivityDiagram](images/DeletePersonActivityDiagram.png)

[Back to Table of Contents](#table-of-contents)

--------------------------------------------------------------------------------------------------------------------
### Add Attribute feature

An `AddAttribute` feature that allows users to add attributes to a person in the address book. This feature ensures that only unique attributes are added to a person, maintaining data integrity.
Expand Down
24 changes: 24 additions & 0 deletions docs/diagrams/AddPersonActivityDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@startuml
skin rose
skinparam ActivityFontSize 15
skinparam ArrowFontSize 12
skinparam ArrowFontStyle plain

start

:User enters command;

:Parse command for attributes of person to add;

if (Are there duplicate attribute names?) then (no)
:Create a new person;
:Add the attributes to the person;
:Add the person to the model;
:Save the address book containing the new person to storage;
:Inform user: New person added. Details: ...;
else (yes)
:Inform user: Duplicate attributes found ...;
endif

stop
@enduml
57 changes: 57 additions & 0 deletions docs/diagrams/AddPersonSequenceDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@startuml
!include style.puml
skinparam ArrowFontStyle plain

Actor User as user USER_COLOR
Participant ":UI" as ui UI_COLOR
Participant ":Logic" as logic LOGIC_COLOR
Participant ":Model" as model MODEL_COLOR
Participant ":Person" as person PERSON_COLOR
Participant ":Attribute" as attribute ATTRIBUTE_COLOR
Participant ":Storage" as storage STORAGE_COLOR

user -[USER_COLOR]> ui : "add /Name Bob"
activate ui UI_COLOR

ui -[UI_COLOR]> logic : execute("add /Name Bob")
activate logic LOGIC_COLOR

loop all attributes in command
logic -[LOGIC_COLOR]> attribute : AttributeUtil.createAttribute(attributeName, attributeValue)
activate attribute ATTRIBUTE_COLOR

attribute --[ATTRIBUTE_COLOR]> logic : return new Attribute
deactivate attribute
end

logic -[LOGIC_COLOR]> person : Person(attributesToAdd)
activate person PERSON_COLOR

person --[PERSON_COLOR]> logic : return new Person
deactivate person
destroy person

logic -[LOGIC_COLOR]> model : addPerson(personToAdd)
activate model MODEL_COLOR

model --[MODEL_COLOR]> logic : return added Person
deactivate model

logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
activate storage STORAGE_COLOR

storage -[STORAGE_COLOR]> storage : Save to file
activate storage STORAGE_COLOR_T1
storage --[STORAGE_COLOR]> storage : file saved
deactivate storage STORAGE_COLOR_T1

storage --[STORAGE_COLOR]> logic : save complete
deactivate storage
destroy storage

logic --[LOGIC_COLOR]> ui : CommandResult("New person added. Details: Name: Bob")
deactivate logic

ui--[UI_COLOR]> user : display "New person added. Details: Name: Bob"
deactivate ui
@enduml
25 changes: 25 additions & 0 deletions docs/diagrams/DeletePersonActivityDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@startuml
skin rose
skinparam ActivityFontSize 15
skinparam ArrowFontSize 12
skinparam ArrowFontStyle plain

start

:User enters command;

:Parse command for 4-character UUID;
:Get full UUID;

if (Does full UUID exist?) then (yes)
:Get the corresponding person from address book;
:Delete person from address book;
:Delete person's relationships from address book;
:Save updated address book with deleted person to storage;
:Inform user: Deleted Person: Details: ...;
else (no)
:Inform user: The UUID provided is invalid...;
endif

stop
@enduml
49 changes: 49 additions & 0 deletions docs/diagrams/DeletePersonSequenceDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@startuml
!include style.puml
skinparam ArrowFontStyle plain

Actor User as user USER_COLOR
Participant ":UI" as ui UI_COLOR
Participant ":Logic" as logic LOGIC_COLOR
Participant ":Model" as model MODEL_COLOR
Participant ":Storage" as storage STORAGE_COLOR

user -[USER_COLOR]> ui : "delete /0001"
activate ui UI_COLOR

ui -[UI_COLOR]> logic : execute("delete /0001")
activate logic LOGIC_COLOR

logic -[LOGIC_COLOR]> model : getFullUuid("0001")
activate model MODEL_COLOR

model --[MODEL_COLOR]> logic : return UUID object
deactivate model

logic -[LOGIC_COLOR]> model : person.getPersonbyUuid(targetUuid)
activate model MODEL_COLOR

model --[MODEL_COLOR]> logic : return matching Person
deactivate model

logic -[LOGIC_COLOR]> model : deletePerson(personToDelete)
logic -[LOGIC_COLOR]> model : deleteRelationshipsOfPerson(targetUuid)

logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
activate storage STORAGE_COLOR

storage -[STORAGE_COLOR]> storage : Save to file
activate storage STORAGE_COLOR_T1
storage --[STORAGE_COLOR]> storage : file saved
deactivate storage STORAGE_COLOR_T1

storage --[STORAGE_COLOR]> logic : save complete
deactivate storage
destroy storage

logic --[LOGIC_COLOR]> ui : CommandResult("Deleted Person: Details: ...")
deactivate logic

ui--[UI_COLOR]> user : display "Deleted Person: Details: ..."
deactivate ui
@enduml
Binary file added docs/images/AddPersonActivityDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/AddPersonSequenceDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/DeletePersonActivityDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/DeletePersonSequenceDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 0 additions & 7 deletions src/main/java/seedu/address/logic/commands/AddCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);

Attribute[] attributesToAdd = generateAttributeList();
for (int i = 0; i < attributesToAdd.length; i++) {
for (int j = i; j < attributesToAdd.length; j++) {
if (i != j && attributesToAdd[i].getName().equalsIgnoreCase(attributesToAdd[j].getName())) {
throw new CommandException(Messages.MESSAGE_DUPLICATE_ATTRIBUTES);
}
}
}
Person addedPerson = addPersonToModel(model, attributesToAdd);

return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(addedPerson)));
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ public static HashMap<String, String> getAttributeHashMapFromAttributeStrings(St
HashMap<String, String> attributeMap = new HashMap<>();
for (int i = 0; i < parts.length; i++) {
String[] attribute = separateAttributeNamesAndValues(parts[i]);
String attributeName = attribute[0];
String attributeName = attribute[0].toLowerCase();
String attributeValue = attribute[1];
if (attributeMap.containsKey(attributeName.toLowerCase())) {
throw new ParseException(Messages.MESSAGE_DUPLICATE_ATTRIBUTES);
}
attributeMap.put(attributeName, attributeValue);
}
return attributeMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,6 @@ public void toStringMethod() {
assertEquals(expected, addCommand.toString());
}

@Test
public void multipleSameAttributes() {
HashMap<String, String> aliceHashMap = new HashMap<>();
aliceHashMap.put("Name", "Alice");
aliceHashMap.put("name", "Alice");
AddCommand addCommand = new AddCommand(aliceHashMap);
assertThrows(CommandException.class, () -> addCommand.execute(model));
}

/**
* A default model stub that have all of the methods failing.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class AddCommandParserTest {
@Test
public void parse_singleAttribute_success() {
HashMap<String, String> expectedAttributeMap = new HashMap<>();
expectedAttributeMap.put("Name", VALID_NAME_BOB);
expectedAttributeMap.put("name", VALID_NAME_BOB);

// whitespace only preamble
assertParseSuccess(parser, "add /Name " + VALID_NAME_BOB, new AddCommand(expectedAttributeMap));
Expand All @@ -33,10 +33,10 @@ public void parse_singleAttribute_success() {
@Test
public void parse_multipleAttributes_success() {
HashMap<String, String> expectedAttributeMap = new HashMap<>();
expectedAttributeMap.put("Name", VALID_NAME_BOB);
expectedAttributeMap.put("Phone", VALID_PHONE_BOB);
expectedAttributeMap.put("Email", VALID_EMAIL_BOB);
expectedAttributeMap.put("Address", VALID_ADDRESS_BOB);
expectedAttributeMap.put("name", VALID_NAME_BOB);
expectedAttributeMap.put("phone", VALID_PHONE_BOB);
expectedAttributeMap.put("email", VALID_EMAIL_BOB);
expectedAttributeMap.put("address", VALID_ADDRESS_BOB);


assertParseSuccess(parser, "add /Name " + VALID_NAME_BOB + " /Phone " + VALID_PHONE_BOB + " /Email "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ public class AddressBookParserTest {
@Test
public void parseCommand_add() throws Exception {
Person person = new Person(new Attribute[0]);
person.updateAttribute(new NameAttribute("Name", "Amy Bee"));
person.updateAttribute(new NameAttribute("name", "Amy Bee"));
AddCommand command = (AddCommand) parser.parseCommand(
AddCommand.COMMAND_WORD
+ " /Name Amy Bee");
HashMap<String, String> attributes = new HashMap<>();
attributes.put("Name", "Amy Bee");
attributes.put("name", "Amy Bee");
assertEquals(new AddCommand(attributes), command);
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/java/seedu/address/logic/parser/ParserUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public void getAttributeHashMapFromAttributeStrings_emptyArray_emptyHashMap() {
public void getAttributeHashMapFromAttributeStrings_validArray_validHashMap() {
String[] parts = {"Name Rachel", " Phone 123456 "};
try {
assertEquals("Rachel", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("Name"));
assertEquals("123456", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("Phone"));
assertEquals("Rachel", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("name"));
assertEquals("123456", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("phone"));
} catch (ParseException e) {
e.printStackTrace();
}
Expand Down

0 comments on commit bc1141e

Please sign in to comment.