diff --git a/src/main/java/igrad/csvwriter/CsvWriter.java b/src/main/java/igrad/csvwriter/CsvWriter.java new file mode 100644 index 00000000000..c58ed9a51da --- /dev/null +++ b/src/main/java/igrad/csvwriter/CsvWriter.java @@ -0,0 +1,92 @@ +package igrad.csvwriter; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import igrad.model.module.Module; + +/** + * Writes the data stored as a human-readable CSV file. + */ +public class CsvWriter { + + private static final String fileName = "study_plan.csv"; + private FileWriter csvWriter; + private List sortedList; + + public CsvWriter(List sortedList) throws IOException { + csvWriter = new FileWriter(fileName); + + this.sortedList = sortedList; + } + + /** + * Writes to CSV + */ + public void write() throws IOException { + writeHeaders(); + writeBody(); + closeWriter(); + } + + private void closeWriter() throws IOException { + csvWriter.flush(); + csvWriter.close(); + } + + private void appendNewLine() throws IOException { + csvWriter.append("\n"); + } + + private void append(String text) throws IOException { + csvWriter.append(text); + csvWriter.append(","); + } + + /** + * Writes each module as a line. Separates modules taken in different semesters + * by a new line. + */ + private void writeBody() throws IOException { + + for (int i = 0; i < sortedList.size(); i++) { + Module module = sortedList.get(i); + + append(module.getSemester().toString()); + append(module.getModuleCode().toString()); + append(module.getTitle().toString()); + append(module.getCredits().toString()); + appendNewLine(); + + if (i < sortedList.size() - 1) { + Module nextModule = sortedList.get(i + 1); + if (!nextModule.getSemester().equals(module.getSemester())) { + appendNewLine(); + } + } + + } + } + + /** + * Writes the headers of the CSV file. + */ + private void writeHeaders() throws IOException { + + String[] headers = { + "Semester", + "Module Code", + "Module Title", + "MCs" + }; + + for (String header : headers) { + append(header); + } + + appendNewLine(); + + } + +} diff --git a/src/main/java/igrad/csvwriter/exceptions/InvalidDataException.java b/src/main/java/igrad/csvwriter/exceptions/InvalidDataException.java new file mode 100644 index 00000000000..71d5d8cfb7f --- /dev/null +++ b/src/main/java/igrad/csvwriter/exceptions/InvalidDataException.java @@ -0,0 +1,14 @@ +package igrad.csvwriter.exceptions; + +import java.io.IOException; + +/** + * Signals that one or more of the required fields are not available + */ +public class InvalidDataException extends IOException { + + public InvalidDataException(String msg) { + super(msg); + } + +} diff --git a/src/main/java/igrad/logic/commands/ExportCommand.java b/src/main/java/igrad/logic/commands/ExportCommand.java new file mode 100644 index 00000000000..9ef4abae6c7 --- /dev/null +++ b/src/main/java/igrad/logic/commands/ExportCommand.java @@ -0,0 +1,38 @@ +package igrad.logic.commands; + +import java.io.IOException; + +import igrad.csvwriter.CsvWriter; +import igrad.logic.commands.exceptions.CommandException; +import igrad.model.Model; +import igrad.model.module.sorters.SortBySemester; + +/** + * Format full help instructions for every command for display. + */ +public class ExportCommand extends Command { + + public static final String COMMAND_WORD = "export"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Exports data to a CSV file.\n" + + "Example: " + COMMAND_WORD; + + public static final String SHOWING_EXPORT_MESSAGE = "I've exported your data to a CSV file." + + " You can find it in the same folder as this app's executable!"; + public static final String EXPORT_ERROR_MESSAGE = "Unable to export data to CSV file." + + " Please ensure that you do not have the file open and " + + "each module is tagged to a semester."; + + @Override + public CommandResult execute(Model model) throws CommandException { + + try { + CsvWriter csvWriter = new CsvWriter(model.getSortedModuleList(new SortBySemester())); + csvWriter.write(); + } catch (IOException e) { + throw new CommandException(EXPORT_ERROR_MESSAGE); + } + + return new CommandResult(SHOWING_EXPORT_MESSAGE); + } +} diff --git a/src/main/java/igrad/logic/parser/CourseBookParser.java b/src/main/java/igrad/logic/parser/CourseBookParser.java index 65f7758f714..a220d62a6fb 100644 --- a/src/main/java/igrad/logic/parser/CourseBookParser.java +++ b/src/main/java/igrad/logic/parser/CourseBookParser.java @@ -14,6 +14,7 @@ import igrad.logic.commands.CourseDeleteCommand; import igrad.logic.commands.CourseEditCommand; import igrad.logic.commands.ExitCommand; +import igrad.logic.commands.ExportCommand; import igrad.logic.commands.HelpCommand; import igrad.logic.commands.ModuleAddCommand; import igrad.logic.commands.ModuleDeleteCommand; @@ -129,6 +130,9 @@ public Command parseCommand(String userInput) throws ParseException, IOException case HelpCommand.COMMAND_WORD: return new HelpCommand(); + case ExportCommand.COMMAND_WORD: + return new ExportCommand(); + case CourseAddCommand.COMMAND_WORD: return new CourseAddCommandParser().parse(arguments); diff --git a/src/main/java/igrad/model/Model.java b/src/main/java/igrad/model/Model.java index 2fa5203a394..5a180909be0 100644 --- a/src/main/java/igrad/model/Model.java +++ b/src/main/java/igrad/model/Model.java @@ -1,6 +1,8 @@ package igrad.model; import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import igrad.commons.core.GuiSettings; @@ -157,6 +159,11 @@ public interface Model { */ ObservableList getFilteredModuleList(); + /** + * Returns an sorted {@code List} sorted according to {@code Comparator}. + */ + List getSortedModuleList(Comparator comparator); + /** * Returns an unmodifiable view of the requirements list. */ diff --git a/src/main/java/igrad/model/ModelManager.java b/src/main/java/igrad/model/ModelManager.java index afc4e473316..349cfd5e845 100644 --- a/src/main/java/igrad/model/ModelManager.java +++ b/src/main/java/igrad/model/ModelManager.java @@ -4,6 +4,9 @@ import static java.util.Objects.requireNonNull; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import java.util.logging.Logger; @@ -195,6 +198,15 @@ public ObservableList getFilteredModuleList() { return filteredModules; } + @Override + public List getSortedModuleList(Comparator comparator) { + ObservableList tempList = getFilteredModuleList(); + List sortedList = new ArrayList<>(tempList); + sortedList.sort(comparator); + + return sortedList; + } + @Override public void updateFilteredModuleList(Predicate predicate) { requireNonNull(predicate); diff --git a/src/main/java/igrad/model/module/Semester.java b/src/main/java/igrad/model/module/Semester.java index 2aa3fdeef13..afe9f53b27a 100644 --- a/src/main/java/igrad/model/module/Semester.java +++ b/src/main/java/igrad/model/module/Semester.java @@ -21,7 +21,12 @@ public class Semester { */ public Semester(String semester) { checkArgument(isValidSemester(semester), MESSAGE_CONSTRAINTS); - value = semester; + + if (semester != null) { + value = semester.toUpperCase(); + } else { + value = null; + } } /** diff --git a/src/main/java/igrad/model/module/sorters/SortBySemester.java b/src/main/java/igrad/model/module/sorters/SortBySemester.java new file mode 100644 index 00000000000..89a1bcc06f6 --- /dev/null +++ b/src/main/java/igrad/model/module/sorters/SortBySemester.java @@ -0,0 +1,31 @@ +package igrad.model.module.sorters; + +import java.util.Comparator; + +import igrad.model.module.Module; + + +/** + * Sorter for modules by semester + */ +public class SortBySemester implements Comparator { + + /** + * Compares the digits in the semester a module is tagged with. + * Results in an ascending sort. + */ + public int compare(Module m1, Module m2) { + int s1 = extractDigits(m1.getSemester().toString()); + int s2 = extractDigits(m2.getSemester().toString()); + + return s1 - s2; + } + + /** + * Extracts digits from a string + */ + private int extractDigits(String str) { + return Integer.parseInt(str.replaceAll("\\D+", "")); + } + +} diff --git a/src/main/java/igrad/storage/JsonAdaptedModule.java b/src/main/java/igrad/storage/JsonAdaptedModule.java index 26b203a560a..cb38d529311 100644 --- a/src/main/java/igrad/storage/JsonAdaptedModule.java +++ b/src/main/java/igrad/storage/JsonAdaptedModule.java @@ -62,7 +62,7 @@ public JsonAdaptedModule(Module source) { credits = source.getCredits().value; memo = source.getMemo() != null ? source.getMemo().value : null; semester = source.getSemester() != null ? source.getSemester().value : null; - description = source.getDescription().value; + description = source.getDescription() != null ? source.getDescription().value : null; tags.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); diff --git a/src/test/java/igrad/logic/commands/AddCommandTest.java b/src/test/java/igrad/logic/commands/AddCommandTest.java index 1102665a1bb..0c06b23369f 100644 --- a/src/test/java/igrad/logic/commands/AddCommandTest.java +++ b/src/test/java/igrad/logic/commands/AddCommandTest.java @@ -7,6 +7,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import org.junit.jupiter.api.Test; @@ -206,6 +208,11 @@ public ObservableList getFilteredModuleList() { throw new AssertionError("This method should not be called."); } + @Override + public List getSortedModuleList(Comparator comparator) { + throw new AssertionError("This method should not be called."); + } + @Override public ObservableList getRequirementList() { throw new AssertionError("This method should not be called."); diff --git a/study_plan.csv b/study_plan.csv new file mode 100644 index 00000000000..a5017e7b792 --- /dev/null +++ b/study_plan.csv @@ -0,0 +1,4 @@ +Semester,Module Code,Module Title,MCs, +Y1S2,CS2040,Data,32, + +Y2S2,CS2030,Prog,4, diff --git a/study_plan.csv~ b/study_plan.csv~ new file mode 100644 index 00000000000..f8191b01d44 --- /dev/null +++ b/study_plan.csv~ @@ -0,0 +1 @@ +CS2103TCS2106MA2101CS2102 \ No newline at end of file