Skip to content

Commit

Permalink
Merge pull request #105 from yichong96/import_command
Browse files Browse the repository at this point in the history
Import and Export command
  • Loading branch information
KerrynEer authored Mar 29, 2019
2 parents b715eff + e453195 commit 72130ed
Show file tree
Hide file tree
Showing 22 changed files with 657 additions and 239 deletions.
10 changes: 5 additions & 5 deletions config/travis/check-eof-newline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

ret=0

# Preserve filename with spaces by only splitting on newlines.
# Preserve csvFile with spaces by only splitting on newlines.
IFS='
'

for filename in $(git grep --cached -I -l -e '' -- ':/'); do
if [ "$(tail -c 1 "./$filename")" != '' ]; then
line="$(wc -l "./$filename" | cut -d' ' -f1)"
echo "ERROR:$filename:$line: no newline at EOF."
for csvFile in $(git grep --cached -I -l -e '' -- ':/'); do
if [ "$(tail -c 1 "./$csvFile")" != '' ]; then
line="$(wc -l "./$csvFile" | cut -d' ' -f1)"
echo "ERROR:$csvFile:$line: no newline at EOF."
ret=1
fi
done
Expand Down
2 changes: 1 addition & 1 deletion docs/DeveloperGuide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ CsvManager's API’s are exposed in the model interface as `Model#ExportCardFol
[discrete]
==== Example Usage

1. User wants to export folders `Human Anatomy` and `Nervous System`. User inputs the following folders and the filename specified that he wants to export the folders to.
1. User wants to export folders `Human Anatomy` and `Nervous System`. User inputs the following folders and the csvFile specified that he wants to export the folders to.

picture of command box with user inputs

Expand Down
2 changes: 1 addition & 1 deletion docs/UserGuide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ Format: `search KEYWORDS [MORE_KEYWORDS]`

==== Import flashcards : `import`

Searches for a json file with the specified filename in the program directory and parses the file to generate a flashcard folder.
Searches for a json file with the specified csvFile in the program directory and parses the file to generate a flashcard folder.

Format: `import FILENAME`

Expand Down
17 changes: 15 additions & 2 deletions src/main/java/seedu/address/commons/core/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,27 @@ public class Messages {
+ " session";
public static final String MESSAGE_INVALID_COMMAND_INSIDE_TEST_SESSION = "This command is valid only outside a test"
+ " session";
public static final String MESSAGE_INVALID_COMMAND_INSIDE_REPORT = "This command is not valid while displaying a "
+ "report";

public static final String MESSAGE_INVALID_ANSWER_COMMAND = "Answer command is valid only when a question is "
+ "displayed";
public static final String MESSAGE_NO_NEGATIVE_INDEX = "Negative index not allowed !";
public static final String MESSAGE_ILLEGAL_COMMAND_NOT_IN_FOLDER = "Command can only be executed in folder";
public static final String MESSAGE_ILLEGAL_COMMAND_NOT_IN_HOME = "Command can only be executed in home directory";


public static final String MESSAGE_INVALID_COMMAND_INSIDE_REPORT = "This command is not valid while displaying a "
+ "report";


public static final String MESSAGE_INVALID_COMMAND_ON_EMPTY_FOLDER = "This command is not valid on an empty"
+ " folder";
public static final String MESSAGE_INVALID_NEXT_COMMAND = "Next command is valid only when this question has been"
+ " answered";

public static final String MESSAGE_INVALID_COMMAND_OUTSIDE_FOLDER = "Command can only be executed in folder";
public static final String MESSAGE_INVALID_COMMAND_INSIDE_FOLDER = "Command can only be executed in home directory";
public static final String MESSAGE_CSV_MANAGER_NOT_INITIALIZED = "Unable to carry out import and export commands";
public static final String MESSAGE_INCORRECT_CSV_FILE_HEADER = "Incorrect Csv file headers. Check that the\n"
+ "csv file contains question,answer,options,hints header";

}
5 changes: 5 additions & 0 deletions src/main/java/seedu/address/commons/core/index/Index.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,9 @@ public boolean equals(Object other) {
|| (other instanceof Index // instanceof handles nulls
&& zeroBasedIndex == ((Index) other).zeroBasedIndex); // state check
}


public String displayIndex() {
return Integer.toString(getOneBased());
}
}
48 changes: 24 additions & 24 deletions src/main/java/seedu/address/logic/commands/ExportCommand.java
Original file line number Diff line number Diff line change
@@ -1,65 +1,65 @@
package seedu.address.logic.commands;

import static seedu.address.logic.parser.CliSyntax.PREFIX_FILENAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_FOLDERNAME;

import java.io.IOException;
import java.util.Set;
import java.util.List;

import seedu.address.commons.core.Messages;
import seedu.address.logic.CommandHistory;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.CardFolderNotFoundException;
import seedu.address.model.Model;
import seedu.address.storage.csvmanager.CardFolderExport;
import seedu.address.storage.csvmanager.CsvFile;
import seedu.address.storage.csvmanager.exceptions.CsvManagerNotInitialized;


/**
* Exports single or multiple card folders into a .json file. Users must specify file name to export card folders to.
* Exports single or multiple card folders into a .csv file.
*/
public class ExportCommand extends Command {

public static final String COMMAND_WORD = "export";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": exports single or multiple card folders into"
+ "a .csv file. "
+ "Users must include the .csv extension.\n"
+ "Parameters: "
+ PREFIX_FOLDERNAME + "CARD_FOLDER_NAME [MORE CARD_FOLDER_NAMES]..."
+ PREFIX_FILENAME + "Filename.csv\n"
+ "Example: " + COMMAND_WORD + "f/Human_anatomy f/Bone_structure n/myfilename.csv";
+ "their respective .csv files."
+ "Parameters: INDEX (Index specifies the card folder index to export) \n"
+ "Can specify more than one card folder index to export"
+ "Example: " + COMMAND_WORD + "1 3 5 7";

public static final String MESSAGE_SUCCESS = "Successfully exported card folders to: $1%s";
public static final String MESSAGE_SUCCESS = "Successfully exported card folders";

public static final String MESSAGE_MISSING_CARD_FOLDERS = "Could not find the specified folder: ";
public static final String MESSAGE_MISSING_CARD_FOLDERS = "Could not find the specified folder index: ";

public static final String MESSAGE_FILE_OPS_FAILURE = "Could not export to specified file";

private Set<CardFolderExport> cardFolders;
private CsvFile filename;
private List<Integer> cardFolderIndexes;

public ExportCommand(Set<CardFolderExport> cardFolders, CsvFile filename) {
this.cardFolders = cardFolders;
this.filename = filename;
public ExportCommand(List<Integer> cardFolderIndexes) {
this.cardFolderIndexes = cardFolderIndexes;
}

@Override
public CommandResult execute(Model model, CommandHistory history) throws CommandException {
// check whether model contains the card folders desired. Catch exception thrown

if (model.isInFolder()) {
throw new CommandException(Messages.MESSAGE_INVALID_COMMAND_INSIDE_FOLDER);
}
try {
model.exportCardFolders(cardFolders, filename);
model.exportCardFolders(cardFolderIndexes);
} catch (CardFolderNotFoundException e) {
throw new CommandException(MESSAGE_MISSING_CARD_FOLDERS + e.getMessage());
} catch (IOException e) {
throw new CommandException(MESSAGE_FILE_OPS_FAILURE);
} catch (CsvManagerNotInitialized e) {
throw new CommandException(Messages.MESSAGE_CSV_MANAGER_NOT_INITIALIZED);
}
return new CommandResult(String.format(MESSAGE_SUCCESS, filename));
return new CommandResult(MESSAGE_SUCCESS);
}


@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
|| other instanceof ExportCommand // instanceof handles nulls
&& (cardFolders.containsAll(((ExportCommand) other).cardFolders)
&& filename.equals(((ExportCommand) other).filename));
&& cardFolderIndexes.containsAll(((ExportCommand) other).cardFolderIndexes);
}
}
28 changes: 20 additions & 8 deletions src/main/java/seedu/address/logic/commands/ImportCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;

import seedu.address.commons.core.Messages;
import seedu.address.logic.CommandHistory;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
Expand All @@ -19,34 +20,45 @@ public class ImportCommand extends Command {
+ "card folders information.\n"
+ "File imported must have a .csv extension.\n"
+ "Default file path if not specified will be in the root folder of this application\n"
+ "Parameters: JSON_FILE_NAME\n"
+ "Parameters: CSV_FILE_NAME\n"
+ "Example: " + COMMAND_WORD + "alice.csv";
public static final String MESSAGE_FILE_OPS_FAILURE = "Could not import from specified file. Check that it exists "
+ "in root directory";
public static final String MESSAGE_SUCCESS = "Successfully imported: %1$s";

private CsvFile filename;
private CsvFile csvFile;

public ImportCommand(CsvFile filename) {
this.filename = filename;
public ImportCommand(CsvFile csvFile) {
this.csvFile = csvFile;
}


@Override
public CommandResult execute(Model model, CommandHistory history) throws CommandException {

if (model.isInFolder()) {
throw new CommandException(Messages.MESSAGE_INVALID_COMMAND_INSIDE_FOLDER);
}

try {
model.importCardFolders(filename);
model.importCardFolders(csvFile);
} catch (IOException e) {
throw new CommandException(MESSAGE_FILE_OPS_FAILURE);
}
return null;
return new CommandResult(String.format(MESSAGE_SUCCESS, csvFile.filename));
}



/**
* Returns true if file extension is of .json format.
*/
private boolean isCorrectFileExtension(String filename) {
return filename.split("\\.(?=[^\\.]+$)")[1].equals("csv");
}

@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
|| other instanceof ImportCommand // instanceof handles nulls
&& csvFile.filename.equals(((ImportCommand) other).csvFile.filename);
}
}
24 changes: 11 additions & 13 deletions src/main/java/seedu/address/logic/parser/ExportCommandParser.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package seedu.address.logic.parser;

import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_FILENAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_FOLDERNAME;
import static seedu.address.commons.core.Messages.MESSAGE_NO_NEGATIVE_INDEX;

import java.util.Set;
import java.util.List;

import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.logic.commands.ExportCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.storage.csvmanager.CardFolderExport;
import seedu.address.storage.csvmanager.CsvFile;


/**
* Parses input for export command arguments and creates a new export command object
Expand All @@ -18,14 +17,13 @@ public class ExportCommandParser implements Parser<ExportCommand> {

@Override
public ExportCommand parse(String userInput) throws ParseException {
ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(
userInput, PREFIX_FILENAME, PREFIX_FOLDERNAME);
if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_FOLDERNAME, PREFIX_FILENAME)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ExportCommand.MESSAGE_USAGE));
try {
List<Integer> folderIndexes = ParserUtil.parseFolderIndex(userInput);
return new ExportCommand(folderIndexes);
} catch (NumberFormatException e) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ExportCommand.MESSAGE_USAGE), e);
} catch (IllegalValueException e) {
throw new ParseException(MESSAGE_NO_NEGATIVE_INDEX);
}
Set<CardFolderExport> folderNames = ParserUtil.parseFolders(argMultimap.getAllValues(PREFIX_FOLDERNAME));
CsvFile filename = ParserUtil.parseFileName(argMultimap.getValue(PREFIX_FILENAME).get());
return new ExportCommand(folderNames, filename);
}
}
44 changes: 23 additions & 21 deletions src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@
import static java.util.Objects.requireNonNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import seedu.address.commons.core.Messages;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.commons.util.StringUtil;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.card.Answer;
import seedu.address.model.card.Option;
import seedu.address.model.card.Question;
import seedu.address.model.hint.Hint;
import seedu.address.storage.csvmanager.CardFolderExport;
import seedu.address.storage.csvmanager.CsvFile;

/**
Expand All @@ -25,8 +28,10 @@
public class ParserUtil {

public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
public static final String MESSAGE_INDEX_LESS_THAN_ZERO = "Index is less than zero";
public static final String HOME_SYMBOL = "..";


/**
* Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
* trimmed.
Expand Down Expand Up @@ -134,30 +139,26 @@ public static Set<Option> parseOptions(Collection<String> options) throws ParseE
return optionSet;
}

/**
* Parsers {@code Collection<String> folderNames} into a {@Code Set<CardFolderExport>}.
* Similar folder names will not be included inside the set.
*/
public static Set<CardFolderExport> parseFolders(Collection<String> folderNames) throws ParseException {
requireNonNull(folderNames);
final Set<CardFolderExport> cardFolderExports = new HashSet<>();
for (String folderName : folderNames) {
cardFolderExports.add(parseFolder(folderName));
}
return cardFolderExports;

public static Integer stringToInt(String element) throws NumberFormatException {
return Integer.parseInt(element);
}

/**
* Parses a {@Code String folderName} into a {@code CardFolderExport}.
* Trims any leading and trailing white space for folder name.
* Parses a user input of a string of integers into a {@code List<Integer>}
*/
private static CardFolderExport parseFolder(String folderName) throws ParseException {
requireNonNull(folderName);
folderName = folderName.trim();
if (CardFolderExport.isFolderNameEmpty(folderName)) {
throw new ParseException(CardFolderExport.MESSAGE_CONSTRAINTS);
public static List<Integer> parseFolderIndex(String folderIndexes) throws NumberFormatException,
IllegalValueException {
folderIndexes = folderIndexes.trim();
List<Integer> indexList = Arrays.stream(folderIndexes.split(" "))
.map(ParserUtil::stringToInt)
.collect(Collectors.toList());

List<Integer> invalidIndexList = indexList.stream().filter(i -> i < 0).collect(Collectors.toList());
if (invalidIndexList.size() > 0) {
throw new IllegalValueException(MESSAGE_INDEX_LESS_THAN_ZERO);
}
return new CardFolderExport(folderName);
return indexList;
}

/**
Expand All @@ -166,7 +167,8 @@ private static CardFolderExport parseFolder(String folderName) throws ParseExcep
public static CsvFile parseFileName(String filename) throws ParseException {
requireNonNull(filename);
if (!CsvFile.isValidFileName(filename)) {
throw new ParseException(CsvFile.MESSAGE_CONSTRAINTS);
throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
CsvFile.MESSAGE_CONSTRAINTS));
}
return new CsvFile(filename);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ public class CardFolderNotFoundException extends RuntimeException {
public CardFolderNotFoundException(String message) {
super(message);
}

public CardFolderNotFoundException() {
super();
}

}
Loading

0 comments on commit 72130ed

Please sign in to comment.