diff --git a/docs/team/kiatkat.md b/docs/team/kiatkat.md index 5ba30a19c18..5c2232466ab 100644 --- a/docs/team/kiatkat.md +++ b/docs/team/kiatkat.md @@ -3,46 +3,46 @@ layout: page title: Kiat Win's Project Portfolio Page --- -## Project: ProfPlan +### Project: ProfPlan ProfPlan is a desktop productivity application written in Java, used for task management and organization. The user interacts with it using a CLI, and it has a GUI created with JavaFX. -Listed below are my contributions to the project, with the relevant pull requests linked where possible. - -**Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=kiatkat&breakdown=true) +Listed below are my contributions to the project, with some relevant pull requests linked where possible. ([RepoSense +link to code contributed](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=kiatkat&breakdown=true)) ### New Features * Added the ability to create recurring tasks. [#130](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/130) - * What it does: Allows the user to denote a task as a recurring task, a new type of task that is never entirely - completed. Rather, marking this type of task as done will push its due date into the future by an amount - depending on its type. - * Justification: This feature better reflects the needs of most users, as a large number of tasks are cyclical in - nature and repeat on a periodic basis. The application can then better model these tasks and provide a more - natural means for the user to manage them. - * Highlights: This enhancement directly affects the `add` command,adding directly onto its implementation. A - thorough consideration of design options was necessary to determine a solution which could seamlessly integrate - with the `add` command and which had minimal overhead, while still providing extensibility and flexibility to - its own usage. - * Credits: Code written for this feature is original. + * What it does: Allows the user to create a new type of task that postpones its due date when marked as completed. + * Justification: A large number of tasks are cyclical in nature. The application can now better model these tasks + and provide a more natural means for the user to manage them. + * Highlights: This enhancement directly affects the `add` command, adding directly onto its implementation. A + thorough consideration of design options was necessary to determine a solution which could do so with minimal + overhead, while still providing extensibility and flexibility. + * Credits: Code written is original. -* Added the ability for the user to change some defined settings to customize their experience. [#138](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/138) +* Added the ability for the user to modify settings. [#138](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/138) * What it does: Allows the user to change certain global parameters that affect the behaviour of other commands via the `set` command. - * Justification: Different users may desire different parameters for some commands, and wish to customize the + * Justification: Different users may desire different parameters for some commands and wish to customize the application to suit their unique needs. * Highlights: This feature required changes across the codebase in multiple regions of the architecture. A - `settings.json` file must be created to maintain these user configurations through sessions, and so various - infrastructure must be set up to properly store and read from this file. Furthermore, the user has to be able to - modify the values in this file, and any command must be able to read these settings. Careful thought had to be - given to the structure and design of this system to reduce dependencies across the codebase. - * Credits: The design of the `UserConfigs` system is similar to AB3's `UserPrefs` in the interest of consistency. - However, `UserConfigs` and its related classes are independent of the existing systems, and all such code - written is otherwise original. - * Note: The `set` feature initially implemented (pre-v1.3) is a different feature from the current `set` feature, - and it has since been removed. As I was responsible for both of these features, past pull requests may mention - this dated `set` feature. All further mentions of `set` in this document are meant to refer to the current - implementation (settings) as described above. + file is created to maintain user configurations between sessions, and the user must be able to modify the + values in this file, with any command also being able to read these settings. Careful thought had to be given to + the structure and design of this system to minimize dependencies. + * Credits: Code written is original. + * Note: There was a `set` feature of the same name implemented in versions pre-v1.3, which is completely different + in specification, and has since been removed. All mentions of `set` in this document refer to the implementation + described above. + +* Added the ability for the user to add descriptions. [#105](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/105) + * What it does: Allows the user to add an associated description field to a task and edit it with the + `description` command. + * Justification: Users may want to store information related to the task that is not expressible by the limited + values of the other fields. + * Highlights: This feature required good planning to integrate well into the existing structure of a task, while + maintaining flexibility and keeping dependence on other classes low. + * Credits: Code written is original. ### Project management and team-based tasks * Responsible for creating and setting deadlines for all milestones @@ -56,22 +56,19 @@ Listed below are my contributions to the project, with the relevant pull request ### Enhancements to existing features * Updated the `find` feature to new specifications: [#77](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/77) -### Documentation - -#### User Guide +### User Guide -* Added documentation for the features `find` and `set` [#22](https://github.com/AY2324S1-CS2103T-W15-1/tp/issues/22) -* Added documentation for the `recur/` flag and all corresponding mentions of recurring tasks -* Added documentation for supported setting parameters -* Added information on the acceptable values for the parameters that the user can input -* Wrote the introductory section, "What can ProfPlan do for you?" -* Contributed to the preambles of some sections under "Advanced Features" +* Added documentation for the `find` and `set` features ([#22](https://github.com/AY2324S1-CS2103T-W15-1/tp/issues/22)), + the `recur/` flag and corresponding mentions of recurring tasks, as well as supported setting parameters +* Clarified the acceptable values for the parameters that the user can input +* Wrote the section "What can ProfPlan do for you?" and some sections under "Advanced Features" * Made some tweaks to fix language errors -#### Developer Guide +### Developer Guide * Modified UML diagrams to reflect the addition of the `UserConfigs` system -* Added use cases specific to `find` and `set` +* Added use cases specific to the features implemented above +* Made tweaks to the design and layout and corrected some language errors ### Community diff --git a/src/test/data/JsonUserConfigsStorageTest/EmptyUserConfigs.json b/src/test/data/JsonUserConfigsStorageTest/EmptyUserConfigs.json new file mode 100644 index 00000000000..0db3279e44b --- /dev/null +++ b/src/test/data/JsonUserConfigsStorageTest/EmptyUserConfigs.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/src/test/data/JsonUserConfigsStorageTest/ExtraValuesUserConfig.json b/src/test/data/JsonUserConfigsStorageTest/ExtraValuesUserConfig.json new file mode 100644 index 00000000000..3fe713d2d72 --- /dev/null +++ b/src/test/data/JsonUserConfigsStorageTest/ExtraValuesUserConfig.json @@ -0,0 +1,7 @@ +{ + "settings" : { + "semesterDays" : 180, + "extra" : "some value" + }, + "profPlanFilePath" : "data\\profplan.json" +} diff --git a/src/test/data/JsonUserConfigsStorageTest/NotJsonFormatUserConfigs.json b/src/test/data/JsonUserConfigsStorageTest/NotJsonFormatUserConfigs.json new file mode 100644 index 00000000000..b738f344942 --- /dev/null +++ b/src/test/data/JsonUserConfigsStorageTest/NotJsonFormatUserConfigs.json @@ -0,0 +1 @@ +Not a json file! diff --git a/src/test/data/JsonUserConfigsStorageTest/TypicalUserConfig.json b/src/test/data/JsonUserConfigsStorageTest/TypicalUserConfig.json new file mode 100644 index 00000000000..4eb7b9b20a3 --- /dev/null +++ b/src/test/data/JsonUserConfigsStorageTest/TypicalUserConfig.json @@ -0,0 +1,6 @@ +{ + "settings" : { + "semesterDays" : 180 + }, + "profPlanFilePath" : "data\\profplan.json" +} diff --git a/src/test/java/profplan/logic/commands/CommandTestUtil.java b/src/test/java/profplan/logic/commands/CommandTestUtil.java index 4874bc2be0a..dc012c5d6e9 100644 --- a/src/test/java/profplan/logic/commands/CommandTestUtil.java +++ b/src/test/java/profplan/logic/commands/CommandTestUtil.java @@ -34,7 +34,9 @@ public class CommandTestUtil { public static final String VALID_TAG_FRIEND = "friend"; public static final String VALID_LINK_EXAMPLE = "www.exmaple.com"; public static final String VALID_LINK_GOOGLE = "www.google.com"; - public static final String VALID_DESCRIPTION = "gg"; + public static final String VALID_DESCRIPTION = "this is a valid description"; + public static final String EMPTY_DESCRIPTION = ""; + public static final String UNUSUAL_VALID_DESCRIPTION = "!@#$%^&*()_ ;'d;'~~~"; public static final String DUEDATE_DESC = " " + PREFIX_DUEDATE + "10-12-2023"; diff --git a/src/test/java/profplan/logic/parser/AddCommandParserTest.java b/src/test/java/profplan/logic/parser/AddCommandParserTest.java index c1bfccee498..4f84ce61344 100644 --- a/src/test/java/profplan/logic/parser/AddCommandParserTest.java +++ b/src/test/java/profplan/logic/parser/AddCommandParserTest.java @@ -96,27 +96,26 @@ public void parse_optionalFieldsMissing_success() { new AddCommand(expectedTask)); } - /* - @Test - <> + @Test public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_FULL_HELP); // missing name prefix - CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.VALID_NAME_BOB - + CommandTestUtil.PRIORITY_DESC_BOB, + CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.PRIORITY_DESC_BOB + + CommandTestUtil.DUEDATE_DESC, expectedMessage); // missing priority prefix - CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.NAME_DESC_BOB - + CommandTestUtil.VALID_PRIORITY_BOB, + CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.VALID_NAME_BOB + + CommandTestUtil.DUEDATE_DESC, expectedMessage); - // all prefixes missing + // missing due date prefix CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.VALID_NAME_BOB - + CommandTestUtil.VALID_PRIORITY_BOB, + + CommandTestUtil.PRIORITY_DESC_BOB, expectedMessage); } - */ + @Test public void parse_invalidValue_failure() { diff --git a/src/test/java/profplan/logic/parser/DescriptionCommandParserTest.java b/src/test/java/profplan/logic/parser/DescriptionCommandParserTest.java new file mode 100644 index 00000000000..edd819ef064 --- /dev/null +++ b/src/test/java/profplan/logic/parser/DescriptionCommandParserTest.java @@ -0,0 +1,53 @@ +package profplan.logic.parser; + +import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import org.junit.jupiter.api.Test; + +import profplan.logic.commands.DescriptionCommand; +import profplan.model.task.Description; +import profplan.testutil.TypicalIndexes; + +public class DescriptionCommandParserTest { + + private DescriptionCommandParser parser = new DescriptionCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + CommandParserTestUtil.assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DescriptionCommand.MESSAGE_FULL_HELP)); + } + + @Test + public void parse_validArgs_returnsDescriptionCommand() { + DescriptionCommand expectedDescriptionCommand = + new DescriptionCommand(TypicalIndexes.INDEX_FIRST_TASK, new Description("test description")); + + CommandParserTestUtil.assertParseSuccess(parser, " 1 des/ test description ", + expectedDescriptionCommand); + } + + @Test + public void parse_invalidIndex_throwsParseException() { + CommandParserTestUtil.assertParseFailure(parser, " abc des/def ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DescriptionCommand.MESSAGE_FULL_HELP)); + } + + @Test + public void parse_emptyDescription_returnsDescriptionCommand() { + DescriptionCommand expectedDescriptionCommand = + new DescriptionCommand(TypicalIndexes.INDEX_FIRST_TASK, new Description("")); + + CommandParserTestUtil.assertParseSuccess(parser, " 1 des/ ", + expectedDescriptionCommand); + } + + @Test + public void parse_specialCharactersDescription_returnsDescriptionCommand() { + DescriptionCommand expectedDescriptionCommand = + new DescriptionCommand(TypicalIndexes.INDEX_FIRST_TASK, new Description("!@#$%^&*()_")); + + CommandParserTestUtil.assertParseSuccess(parser, " 1 des/ !@#$%^&*()_ ", + expectedDescriptionCommand); + } +} diff --git a/src/test/java/profplan/logic/parser/EditSettingsCommandParserTest.java b/src/test/java/profplan/logic/parser/EditSettingsCommandParserTest.java new file mode 100644 index 00000000000..986d17955d4 --- /dev/null +++ b/src/test/java/profplan/logic/parser/EditSettingsCommandParserTest.java @@ -0,0 +1,24 @@ +package profplan.logic.parser; + +import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import org.junit.jupiter.api.Test; + +import profplan.logic.commands.EditSettingsCommand; + +public class EditSettingsCommandParserTest { + + private EditSettingsCommandParser parser = new EditSettingsCommandParser(); + + @Test + public void parse_emptyArgs_throwsParseException() { + CommandParserTestUtil.assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditSettingsCommand.MESSAGE_FULL_HELP)); + } + + @Test + public void parse_invalidParameter_throwsParseException() { + CommandParserTestUtil.assertParseFailure(parser, " set aaaaaa 123 ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditSettingsCommand.MESSAGE_FULL_HELP)); + } +} diff --git a/src/test/java/profplan/storage/JsonUserConfigsStorageTest.java b/src/test/java/profplan/storage/JsonUserConfigsStorageTest.java new file mode 100644 index 00000000000..9333d2c31ee --- /dev/null +++ b/src/test/java/profplan/storage/JsonUserConfigsStorageTest.java @@ -0,0 +1,123 @@ +package profplan.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import profplan.commons.core.Settings; +import profplan.commons.exceptions.DataLoadingException; +import profplan.model.UserConfigs; +import profplan.testutil.Assert; + +public class JsonUserConfigsStorageTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonUserConfigsStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readUserConfigs_nullFilePath_throwsNullPointerException() { + Assert.assertThrows(NullPointerException.class, () -> readUserConfigs(null)); + } + + private Optional readUserConfigs(String userConfigsFileInTestDataFolder) throws DataLoadingException { + Path configsFilePath = addToTestDataPathIfNotNull(userConfigsFileInTestDataFolder); + return new JsonUserConfigsStorage(configsFilePath).readUserConfigs(configsFilePath); + } + + @Test + public void readUserConfigs_missingFile_emptyResult() throws DataLoadingException { + assertFalse(readUserConfigs("NonExistentFile.json").isPresent()); + } + + @Test + public void readUserConfigs_notJsonFormat_exceptionThrown() { + Assert.assertThrows(DataLoadingException.class, () -> readUserConfigs("NotJsonFormatUserConfigs.json")); + } + + private Path addToTestDataPathIfNotNull(String userConfigsFileInTestDataFolder) { + return userConfigsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(userConfigsFileInTestDataFolder) + : null; + } + + @Test + public void readUserConfigs_fileInOrder_successfullyRead() throws DataLoadingException { + UserConfigs expected = getTypicalUserConfigs(); + UserConfigs actual = readUserConfigs("TypicalUserConfig.json").get(); + assertEquals(expected, actual); + } + + @Test + public void readUserConfigs_valuesMissingFromFile_defaultValuesUsed() throws DataLoadingException { + UserConfigs actual = readUserConfigs("EmptyUserConfigs.json").get(); + assertEquals(new UserConfigs(), actual); + } + + @Test + public void readUserConfigs_extraValuesInFile_extraValuesIgnored() throws DataLoadingException { + UserConfigs expected = getTypicalUserConfigs(); + UserConfigs actual = readUserConfigs("ExtraValuesUserConfig.json").get(); + + assertEquals(expected, actual); + } + + private UserConfigs getTypicalUserConfigs() { + UserConfigs userConfigs = new UserConfigs(); + userConfigs.setSettings(new Settings(180)); + userConfigs.setProfPlanFilePath(Paths.get("data\\profplan.json")); + return userConfigs; + } + + @Test + public void saveConfigs_nullConfigs_throwsNullPointerException() { + Assert.assertThrows(NullPointerException.class, () -> saveUserConfigs(null, "SomeFile.json")); + } + + @Test + public void saveUserConfigs_nullFilePath_throwsNullPointerException() { + Assert.assertThrows(NullPointerException.class, () -> saveUserConfigs(new UserConfigs(), null)); + } + + /** + * Saves {@code userConfigs} at the specified {@code ConfigsFileInTestDataFolder} filepath. + */ + private void saveUserConfigs(UserConfigs userConfigs, String configsFileInTestDataFolder) { + try { + new JsonUserConfigsStorage(addToTestDataPathIfNotNull(configsFileInTestDataFolder)) + .saveUserConfigs(userConfigs); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file", ioe); + } + } + + @Test + public void saveUserConfigs_allInOrder_success() throws DataLoadingException, IOException { + + UserConfigs original = new UserConfigs(); + original.setSettings(new Settings(180)); + + Path pefsFilePath = testFolder.resolve("TempConfigs.json"); + JsonUserConfigsStorage jsonUserConfigsStorage = new JsonUserConfigsStorage(pefsFilePath); + + //Try writing when the file doesn't exist + jsonUserConfigsStorage.saveUserConfigs(original); + UserConfigs readBack = jsonUserConfigsStorage.readUserConfigs().get(); + assertEquals(original, readBack); + + //Try saving when the file exists + original.setSettings(new Settings(180)); + jsonUserConfigsStorage.saveUserConfigs(original); + readBack = jsonUserConfigsStorage.readUserConfigs().get(); + assertEquals(original, readBack); + } + +}