forked from nus-cs2103-AY2425S1/tp
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #59 from Jollybomber/import-export-storage
Add import function
- Loading branch information
Showing
10 changed files
with
379 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/main/java/seedu/address/commons/exceptions/ImproperFormatException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package seedu.address.commons.exceptions; | ||
|
||
/** | ||
* Signals that the format of the import file has errors. | ||
*/ | ||
public class ImproperFormatException extends Exception { | ||
/** | ||
* @param message should contain relevant information on the error(s) | ||
*/ | ||
public ImproperFormatException(String message) { | ||
super(message); | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
src/main/java/seedu/address/logic/commands/ImportCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package seedu.address.logic.commands; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH; | ||
|
||
import java.util.ArrayList; | ||
|
||
import seedu.address.commons.exceptions.ImproperFormatException; | ||
import seedu.address.commons.util.ToStringBuilder; | ||
import seedu.address.logic.CommandHistory; | ||
import seedu.address.logic.commands.exceptions.CommandException; | ||
import seedu.address.model.Model; | ||
import seedu.address.storage.CsvImport; | ||
|
||
/** | ||
* Adds a list of persons to the address book provided by an import file. | ||
*/ | ||
public class ImportCommand extends Command { | ||
public static final String COMMAND_WORD = "import"; | ||
|
||
public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a list of persons provided in" | ||
+ " the provided file." | ||
+ "Example: " + COMMAND_WORD + " " | ||
+ PREFIX_FILEPATH + "~/data/test.txt"; | ||
|
||
public static final String MESSAGE_SUCCESS = "%d persons added"; | ||
public static final String MESSAGE_FAILED = "Rows that could not be added: %s"; | ||
|
||
private final String importFilePath; | ||
public ImportCommand(String importFilePath) { | ||
this.importFilePath = importFilePath; | ||
} | ||
|
||
@Override | ||
public CommandResult execute(Model model, CommandHistory history) throws CommandException { | ||
requireNonNull(model); | ||
|
||
CsvImport importer = new CsvImport(importFilePath); | ||
int personsAdded; | ||
try { | ||
personsAdded = importer.readCsv(model); | ||
} catch (ImproperFormatException e) { | ||
throw new CommandException(e.getMessage()); | ||
} | ||
|
||
ArrayList<Integer> personsFailed = importer.getFailed(); | ||
model.commitAddressBook(); | ||
if (!importer.hasFailures()) { | ||
return new CommandResult(String.format(MESSAGE_SUCCESS, personsAdded)); | ||
} else if (personsAdded == 0) { | ||
return new CommandResult(String.format(MESSAGE_FAILED, personsFailed)); | ||
} else { | ||
return new CommandResult(String.format(MESSAGE_SUCCESS, personsAdded) | ||
+ String.format(MESSAGE_FAILED, personsFailed)); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
if (other == this) { | ||
return true; | ||
} | ||
|
||
// instanceof handles nulls | ||
if (!(other instanceof ImportCommand)) { | ||
return false; | ||
} | ||
|
||
ImportCommand otherImportCommand = (ImportCommand) other; | ||
return importFilePath.equals(otherImportCommand.importFilePath); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return new ToStringBuilder(this) | ||
.add("toImport", importFilePath) | ||
.toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
src/main/java/seedu/address/logic/parser/ImportCommandParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package seedu.address.logic.parser; | ||
|
||
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; | ||
import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH; | ||
|
||
import java.util.stream.Stream; | ||
|
||
import seedu.address.logic.commands.ImportCommand; | ||
import seedu.address.logic.parser.exceptions.ParseException; | ||
|
||
/** | ||
* Parses input arguments and creates a new ImportCommand object | ||
*/ | ||
public class ImportCommandParser implements Parser<ImportCommand> { | ||
|
||
/** | ||
* 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 ImportCommand parse(String args) throws ParseException { | ||
ArgumentMultimap argMultimap = | ||
ArgumentTokenizer.tokenize(args, PREFIX_FILEPATH); | ||
|
||
if (!arePrefixesPresent(argMultimap, PREFIX_FILEPATH) | ||
|| !argMultimap.getPreamble().isEmpty()) { | ||
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE)); | ||
} | ||
|
||
argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_FILEPATH); | ||
String filepath = ParserUtil.parseFilepath(argMultimap.getValue(PREFIX_FILEPATH).get()); | ||
|
||
return new ImportCommand(filepath); | ||
} | ||
|
||
/** | ||
* Returns true if none of the prefixes contains empty {@code Optional} values in the given | ||
* {@code ArgumentMultimap}. | ||
*/ | ||
private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { | ||
return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package seedu.address.storage; | ||
|
||
import java.io.FileNotFoundException; | ||
import java.io.FileReader; | ||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import com.opencsv.CSVReader; | ||
import com.opencsv.bean.CsvToBeanBuilder; | ||
import com.opencsv.exceptions.CsvValidationException; | ||
|
||
import seedu.address.commons.exceptions.IllegalValueException; | ||
import seedu.address.commons.exceptions.ImproperFormatException; | ||
import seedu.address.model.Model; | ||
|
||
/** | ||
* Handles the import of CSV data into the application. | ||
* This class reads a specified CSV file and converts its contents into | ||
* JsonAdaptedPerson objects, which can then be added to a Model. | ||
*/ | ||
public class CsvImport { | ||
private static final String INCORRECT_HEADERS = "Your file is missing or has extra headers. " | ||
+ "Please use the following headers: name,phone,email,address,hours,tags,role "; | ||
private static final String INCORRECT_ROWS = "Some rows have an incorrect number of entries. " | ||
+ "The expected number of entries is %d. The rows that failed are: %s"; | ||
private static final int HEADER_COUNT = 7; | ||
private final String importFilePath; | ||
private final ArrayList<Integer> failed; | ||
|
||
/** | ||
* Constructs a CsvImport instance with the specified file path. | ||
* | ||
* @param importFilePath The path of the CSV file to be imported. | ||
*/ | ||
public CsvImport(String importFilePath) { | ||
this.importFilePath = importFilePath; | ||
this.failed = new ArrayList<>(); | ||
} | ||
|
||
/** | ||
* Reads the CSV file and imports the data into the provided model. | ||
* | ||
* @param model The model to which the imported JsonAdaptedPerson objects will be added. | ||
* @return The number of successful imports. | ||
*/ | ||
public int readCsv(Model model) throws ImproperFormatException { | ||
FileReader reader = null; | ||
FileReader headerReader = null; | ||
FileReader rowReader = null; | ||
try { | ||
reader = new FileReader(importFilePath); | ||
headerReader = new FileReader(importFilePath); | ||
rowReader = new FileReader(importFilePath); | ||
} catch (FileNotFoundException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
assert reader != null; | ||
if (!validateHeaders(headerReader)) { | ||
throw new ImproperFormatException(INCORRECT_HEADERS); | ||
} else if (!validateCsv(rowReader)) { | ||
throw new ImproperFormatException(String.format(INCORRECT_ROWS, HEADER_COUNT, failed)); | ||
} | ||
List<JsonAdaptedPerson> personList = new CsvToBeanBuilder<JsonAdaptedPerson>(reader) | ||
.withType(JsonAdaptedPerson.class).build().parse(); | ||
|
||
int success = 0; | ||
for (JsonAdaptedPerson p : personList) { | ||
try { | ||
if (model.hasPerson(p.toModelType())) { | ||
failed.add(personList.indexOf(p)); | ||
} else { | ||
model.addPerson(p.toModelType()); | ||
success++; | ||
} | ||
} catch (IllegalValueException e) { | ||
failed.add(personList.indexOf(p)); | ||
} | ||
} | ||
return success; | ||
} | ||
|
||
public ArrayList<Integer> getFailed() { | ||
return failed; | ||
} | ||
|
||
public boolean hasFailures() { | ||
return !failed.isEmpty(); | ||
} | ||
|
||
private boolean validateHeaders(FileReader reader) { | ||
CSVReader csvReader = new CSVReader(reader); | ||
|
||
ArrayList<String> expectedHeaders = new ArrayList<>(); | ||
expectedHeaders.add("name"); | ||
expectedHeaders.add("phone"); | ||
expectedHeaders.add("email"); | ||
expectedHeaders.add("address"); | ||
expectedHeaders.add("hours"); | ||
expectedHeaders.add("tags"); | ||
expectedHeaders.add("role"); | ||
|
||
try { | ||
ArrayList<String> actualHeaders = new ArrayList<>(List.of(csvReader.peek())); | ||
if (actualHeaders.isEmpty()) { | ||
return false; | ||
} | ||
|
||
// Check for missing headers | ||
for (String expectedHeader : expectedHeaders) { | ||
if (!actualHeaders.contains(expectedHeader)) { | ||
return false; | ||
} | ||
} | ||
|
||
// Check for extra headers | ||
for (String actualHeader : actualHeaders) { | ||
if (!expectedHeaders.contains(actualHeader)) { | ||
return false; | ||
} | ||
} | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
|
||
} | ||
return true; | ||
} | ||
|
||
private boolean validateCsv(FileReader reader) { | ||
CSVReader csvReader = new CSVReader(reader); | ||
try { | ||
csvReader.skip(1); | ||
int lineCount = 1; | ||
String[] row = csvReader.readNext(); | ||
while (row != null) { | ||
if (row.length != HEADER_COUNT) { | ||
failed.add(lineCount); | ||
} | ||
lineCount++; | ||
row = csvReader.readNext(); | ||
} | ||
} catch (IOException | CsvValidationException e) { | ||
e.printStackTrace(); | ||
} | ||
return failed.isEmpty(); | ||
} | ||
} |
Oops, something went wrong.