From b7f235148edc82a702f8b00bbc6f834845408bc2 Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Sun, 12 Nov 2023 18:07:36 +0800 Subject: [PATCH 01/28] Add tests for JsonUserPrefsStorage --- .../java/seedu/modulight/model/UserPrefs.java | 15 ++ .../ExtraValuesUserPref.json | 5 +- .../TypicalUserPref.json | 9 +- .../storage/JsonUserPrefsStorageTest.java | 129 ++++++++++++++++++ 4 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java diff --git a/src/main/java/seedu/modulight/model/UserPrefs.java b/src/main/java/seedu/modulight/model/UserPrefs.java index 77b3a15905b..2ac680b661e 100644 --- a/src/main/java/seedu/modulight/model/UserPrefs.java +++ b/src/main/java/seedu/modulight/model/UserPrefs.java @@ -71,6 +71,21 @@ public void setAddressBookFilePath(Path addressBookFilePath) { this.addressBookFilePath = addressBookFilePath; } + public void setStudentBookFilePath(Path studentBookFilePath) { + requireNonNull(studentBookFilePath); + this.studentBookFilePath = studentBookFilePath; + } + + public void setScoreBookFilePath(Path scoreBookFilePath) { + requireNonNull(scoreBookFilePath); + this.scoreBookFilePath = scoreBookFilePath; + } + + public void setGcBookFilePath(Path gcBookFilePath) { + requireNonNull(gcBookFilePath); + this.gcBookFilePath = gcBookFilePath; + } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json index 1037548a9cd..3eef8b88c44 100644 --- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json @@ -9,5 +9,8 @@ "z" : 99 } }, - "addressBookFilePath" : "addressbook.json" + "addressBookFilePath" : "addressbook.json", + "studentBookFilePath" : "studentBook.json", + "scoreBookFilePath" : "scoreBook.json", + "gcBookFilePath" : "gradedComponentBook.json" } diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index b819bed900a..5e6fc125838 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -1,11 +1,14 @@ { "guiSettings" : { - "windowWidth" : 1000.0, - "windowHeight" : 500.0, + "windowWidth" : 1000, + "windowHeight" : 500, "windowCoordinates" : { "x" : 300, "y" : 100 } }, - "addressBookFilePath" : "addressbook.json" + "addressBookFilePath" : "addressbook.json", + "studentBookFilePath" : "studentBook.json", + "scoreBookFilePath" : "scoreBook.json", + "gcBookFilePath" : "gradedComponentBook.json" } diff --git a/src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java new file mode 100644 index 00000000000..c6087225765 --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java @@ -0,0 +1,129 @@ +package seedu.modulight.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.modulight.testutil.Assert.assertThrows; + +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 seedu.modulight.commons.core.GuiSettings; +import seedu.modulight.commons.exceptions.DataLoadingException; +import seedu.modulight.model.UserPrefs; + + +public class JsonUserPrefsStorageTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", + "JsonUserPrefsStorageTest"); + + @TempDir + public Path testFolder; + + private Optional readUserPrefs(String userPrefsFileInTestDataFolder) throws DataLoadingException { + Path prefsFilePath = addToTestDataPathIfNotNull(userPrefsFileInTestDataFolder); + return new JsonUserPrefsStorage(prefsFilePath).readUserPrefs(prefsFilePath); + } + + private Path addToTestDataPathIfNotNull(String userPrefsFileInTestDataFolder) { + return userPrefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(userPrefsFileInTestDataFolder) + : null; + } + + private UserPrefs getTypicalUserPrefs() { + UserPrefs userPrefs = new UserPrefs(); + userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); + userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); + userPrefs.setStudentBookFilePath(Paths.get("studentBook.json")); + userPrefs.setScoreBookFilePath(Paths.get("scoreBook.json")); + userPrefs.setGcBookFilePath(Paths.get("gradedComponentBook.json")); + return userPrefs; + } + + /** + * Saves {@code userPrefs} at the specified {@code prefsFileInTestDataFolder} filepath. + */ + private void saveUserPrefs(UserPrefs userPrefs, String prefsFileInTestDataFolder) { + try { + new JsonUserPrefsStorage(addToTestDataPathIfNotNull(prefsFileInTestDataFolder)) + .saveUserPrefs(userPrefs); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file", ioe); + } + } + + @Test + public void readUserPrefs_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readUserPrefs(null)); + } + + @Test + public void readUserPrefs_missingFile_emptyResult() throws DataLoadingException { + assertFalse(readUserPrefs("NonExistentFile.json").isPresent()); + } + + @Test + public void readUserPrefs_notJsonFormat_exceptionThrown() { + assertThrows(DataLoadingException.class, () -> readUserPrefs( + "NotJsonFormatUserPrefs.json")); + } + + @Test + public void readUserPrefs_fileInOrder_successfullyRead() throws DataLoadingException { + UserPrefs expected = getTypicalUserPrefs(); + UserPrefs actual = readUserPrefs("TypicalUserPref.json").get(); + assertEquals(expected, actual); + } + + @Test + public void readUserPrefs_valuesMissingFromFile_defaultValuesUsed() throws DataLoadingException { + UserPrefs actual = readUserPrefs("EmptyUserPrefs.json").get(); + assertEquals(new UserPrefs(), actual); + } + + @Test + public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataLoadingException { + UserPrefs expected = getTypicalUserPrefs(); + UserPrefs actual = readUserPrefs("ExtraValuesUserPref.json").get(); + + assertEquals(expected, actual); + } + + @Test + public void savePrefs_nullPrefs_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveUserPrefs(null, "SomeFile.json")); + } + + @Test + public void saveUserPrefs_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveUserPrefs(new UserPrefs(), null)); + } + + @Test + public void saveUserPrefs_allInOrder_success() throws DataLoadingException, IOException { + + UserPrefs original = new UserPrefs(); + original.setGuiSettings(new GuiSettings(1200, 200, 0, 2)); + + Path pefsFilePath = testFolder.resolve("TempPrefs.json"); + JsonUserPrefsStorage jsonUserPrefsStorage = new JsonUserPrefsStorage(pefsFilePath); + + //Try writing when the file doesn't exist + jsonUserPrefsStorage.saveUserPrefs(original); + UserPrefs readBack = jsonUserPrefsStorage.readUserPrefs().get(); + assertEquals(original, readBack); + + //Try saving when the file exists + original.setGuiSettings(new GuiSettings(5, 5, 5, 5)); + jsonUserPrefsStorage.saveUserPrefs(original); + readBack = jsonUserPrefsStorage.readUserPrefs().get(); + assertEquals(original, readBack); + } + +} From a5d124d0cde28db4a39045641a30da753765f2cf Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Sun, 12 Nov 2023 20:51:18 +0800 Subject: [PATCH 02/28] Add tests for StorageManager --- .../model/gradedcomponent/Weightage.java | 2 +- .../storage/JsonUserPrefsStorageTest.java | 66 +++++----- .../modulight/storage/StorageManagerTest.java | 114 ++++++++++++++++++ .../testutil/GradedComponentBuilder.java | 78 ++++++++++++ .../testutil/TypicalGradedComponents.java | 35 ++++++ .../testutil/TypicalStudentScores.java | 28 +++++ 6 files changed, 289 insertions(+), 34 deletions(-) create mode 100644 src/test/java/seedu/modulight/storage/StorageManagerTest.java create mode 100644 src/test/java/seedu/modulight/testutil/GradedComponentBuilder.java create mode 100644 src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java create mode 100644 src/test/java/seedu/modulight/testutil/TypicalStudentScores.java diff --git a/src/main/java/seedu/modulight/model/gradedcomponent/Weightage.java b/src/main/java/seedu/modulight/model/gradedcomponent/Weightage.java index 059d0a6b1d4..2c8441ce649 100644 --- a/src/main/java/seedu/modulight/model/gradedcomponent/Weightage.java +++ b/src/main/java/seedu/modulight/model/gradedcomponent/Weightage.java @@ -5,7 +5,7 @@ /** * Represents the weightage of a graded component in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidWeightage(double)} + * Guarantees: immutable; is valid as declared in {@link #isValidWeightage(float)} */ public class Weightage { diff --git a/src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java index c6087225765..a71c378612e 100644 --- a/src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/modulight/storage/JsonUserPrefsStorageTest.java @@ -25,44 +25,16 @@ public class JsonUserPrefsStorageTest { @TempDir public Path testFolder; - private Optional readUserPrefs(String userPrefsFileInTestDataFolder) throws DataLoadingException { - Path prefsFilePath = addToTestDataPathIfNotNull(userPrefsFileInTestDataFolder); - return new JsonUserPrefsStorage(prefsFilePath).readUserPrefs(prefsFilePath); - } - - private Path addToTestDataPathIfNotNull(String userPrefsFileInTestDataFolder) { - return userPrefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER.resolve(userPrefsFileInTestDataFolder) - : null; - } - - private UserPrefs getTypicalUserPrefs() { - UserPrefs userPrefs = new UserPrefs(); - userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); - userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); - userPrefs.setStudentBookFilePath(Paths.get("studentBook.json")); - userPrefs.setScoreBookFilePath(Paths.get("scoreBook.json")); - userPrefs.setGcBookFilePath(Paths.get("gradedComponentBook.json")); - return userPrefs; - } - - /** - * Saves {@code userPrefs} at the specified {@code prefsFileInTestDataFolder} filepath. - */ - private void saveUserPrefs(UserPrefs userPrefs, String prefsFileInTestDataFolder) { - try { - new JsonUserPrefsStorage(addToTestDataPathIfNotNull(prefsFileInTestDataFolder)) - .saveUserPrefs(userPrefs); - } catch (IOException ioe) { - throw new AssertionError("There should not be an error writing to the file", ioe); - } - } - @Test public void readUserPrefs_nullFilePath_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> readUserPrefs(null)); } + private Optional readUserPrefs(String userPrefsFileInTestDataFolder) throws DataLoadingException { + Path prefsFilePath = addToTestDataPathIfNotNull(userPrefsFileInTestDataFolder); + return new JsonUserPrefsStorage(prefsFilePath).readUserPrefs(prefsFilePath); + } + @Test public void readUserPrefs_missingFile_emptyResult() throws DataLoadingException { assertFalse(readUserPrefs("NonExistentFile.json").isPresent()); @@ -74,6 +46,12 @@ public void readUserPrefs_notJsonFormat_exceptionThrown() { "NotJsonFormatUserPrefs.json")); } + private Path addToTestDataPathIfNotNull(String userPrefsFileInTestDataFolder) { + return userPrefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(userPrefsFileInTestDataFolder) + : null; + } + @Test public void readUserPrefs_fileInOrder_successfullyRead() throws DataLoadingException { UserPrefs expected = getTypicalUserPrefs(); @@ -95,6 +73,16 @@ public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataLoad assertEquals(expected, actual); } + private UserPrefs getTypicalUserPrefs() { + UserPrefs userPrefs = new UserPrefs(); + userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); + userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); + userPrefs.setStudentBookFilePath(Paths.get("studentBook.json")); + userPrefs.setScoreBookFilePath(Paths.get("scoreBook.json")); + userPrefs.setGcBookFilePath(Paths.get("gradedComponentBook.json")); + return userPrefs; + } + @Test public void savePrefs_nullPrefs_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> saveUserPrefs(null, "SomeFile.json")); @@ -105,6 +93,18 @@ public void saveUserPrefs_nullFilePath_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> saveUserPrefs(new UserPrefs(), null)); } + /** + * Saves {@code userPrefs} at the specified {@code prefsFileInTestDataFolder} filepath. + */ + private void saveUserPrefs(UserPrefs userPrefs, String prefsFileInTestDataFolder) { + try { + new JsonUserPrefsStorage(addToTestDataPathIfNotNull(prefsFileInTestDataFolder)) + .saveUserPrefs(userPrefs); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file", ioe); + } + } + @Test public void saveUserPrefs_allInOrder_success() throws DataLoadingException, IOException { diff --git a/src/test/java/seedu/modulight/storage/StorageManagerTest.java b/src/test/java/seedu/modulight/storage/StorageManagerTest.java new file mode 100644 index 00000000000..230ee72a8d6 --- /dev/null +++ b/src/test/java/seedu/modulight/storage/StorageManagerTest.java @@ -0,0 +1,114 @@ +package seedu.modulight.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static seedu.modulight.testutil.TypicalGradedComponents.getTypicalGradedComponentBook; +import static seedu.modulight.testutil.TypicalStudentScores.getTypicalStudentScoreBook; +import static seedu.modulight.testutil.TypicalStudents.getTypicalAddressBook; + +import java.nio.file.Path; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.modulight.commons.core.GuiSettings; +import seedu.modulight.model.UserPrefs; +import seedu.modulight.model.gradedcomponent.model.GradedComponentBook; +import seedu.modulight.model.gradedcomponent.model.ReadOnlyGradedComponentBook; +import seedu.modulight.model.student.model.ReadOnlyStudentBook; +import seedu.modulight.model.student.model.StudentBook; +import seedu.modulight.model.studentscore.model.ReadOnlyStudentScoreBook; +import seedu.modulight.model.studentscore.model.StudentScoreBook; + +public class StorageManagerTest { + + @TempDir + public Path testFolder; + + private StorageManager storageManager; + + @BeforeEach + public void setUp() { + JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); + JsonStudentBookStorage studentBookStorage = new JsonStudentBookStorage(getTempFilePath("sb")); + JsonGradedComponentBookStorage gradedComponentBookStorage = new JsonGradedComponentBookStorage(getTempFilePath( + "gcb")); + JsonStudentScoreBookStorage studentScoreBookStorage = new JsonStudentScoreBookStorage(getTempFilePath( + "ssb")); + storageManager = new StorageManager(studentBookStorage, studentScoreBookStorage, gradedComponentBookStorage + , userPrefsStorage); + } + + private Path getTempFilePath(String fileName) { + return testFolder.resolve(fileName); + } + + @Test + public void prefsReadSave() throws Exception { + /* + * Note: This is an integration test that verifies the StorageManager is properly wired to the + * {@link JsonUserPrefsStorage} class. + * More extensive testing of UserPref saving/reading is done in {@link JsonUserPrefsStorageTest} class. + */ + UserPrefs original = new UserPrefs(); + original.setGuiSettings(new GuiSettings(300, 600, 4, 6)); + storageManager.saveUserPrefs(original); + UserPrefs retrieved = storageManager.readUserPrefs().get(); + assertEquals(original, retrieved); + } + + @Test + public void studentBookReadSave() throws Exception { + /* + * Note: This is an integration test that verifies the StorageManager is properly wired to the + * {@link JsonStudentBookStorage} class. + * More extensive testing of saving/reading is done in {@link JsonStudentBookStorageTest} class. + */ + StudentBook original = getTypicalAddressBook(); + storageManager.saveStudentBook(original); + ReadOnlyStudentBook retrieved = storageManager.readStudentBook().get(); + assertEquals(original, new StudentBook(retrieved)); + } + + @Test + public void studentScoreBookReadSave() throws Exception { + /* + * Note: This is an integration test that verifies the StorageManager is properly wired to the + * {@link JsonStudentScoreBookStorage} class. + * More extensive testing of saving/reading is done in {@link JsonStudentScoreBookStorageTest} class. + */ + StudentScoreBook original = getTypicalStudentScoreBook(); + storageManager.saveStudentScoreBook(original); + ReadOnlyStudentScoreBook retrieved = storageManager.readStudentScoreBook().get(); + assertEquals(original, new StudentScoreBook(retrieved)); + } + + @Test + public void gradedComponentBookReadSave() throws Exception { + /* + * Note: This is an integration test that verifies the StorageManager is properly wired to the + * {@link JsonGradedComponentBookStorage} class. + * More extensive testing of saving/reading is done in {@link JsonGradedComponentBookStorageTest} class. + */ + GradedComponentBook original = getTypicalGradedComponentBook(); + storageManager.saveGradedComponentBook(original); + ReadOnlyGradedComponentBook retrieved = storageManager.readGradedComponentBook().get(); + assertEquals(original, new GradedComponentBook(retrieved)); + } + + @Test + public void getStudentBookFilePath() { + assertNotNull(storageManager.getStudentBookFilePath()); + } + + @Test + public void getStudentScoreBookFilePath() { + assertNotNull(storageManager.getStudentScoreBookFilePath()); + } + + @Test + public void getGcBookFilePath() { + assertNotNull(storageManager.getGcBookFilePath()); + } +} diff --git a/src/test/java/seedu/modulight/testutil/GradedComponentBuilder.java b/src/test/java/seedu/modulight/testutil/GradedComponentBuilder.java new file mode 100644 index 00000000000..11b9eaca3d2 --- /dev/null +++ b/src/test/java/seedu/modulight/testutil/GradedComponentBuilder.java @@ -0,0 +1,78 @@ +package seedu.modulight.testutil; + +import seedu.modulight.model.gradedcomponent.GcName; +import seedu.modulight.model.gradedcomponent.GradedComponent; +import seedu.modulight.model.gradedcomponent.MaxMarks; +import seedu.modulight.model.gradedcomponent.Weightage; + +/** + * A utility class to help with building Graded Component object + */ +public class GradedComponentBuilder { + + public static final String DEFAULT_GC_NAME = "Midterms"; + public static final float DEFAULT_WEIGHTAGE = (float) 100.0; + public static final float DEFAULT_MAX_MARKS = (float) 100.0; + + private GcName gcName; + private MaxMarks maxMarks; + private Weightage weightage; + + /** + * Creates GradedComponentBuilder with default details. + */ + public GradedComponentBuilder() { + gcName = new GcName(DEFAULT_GC_NAME); + maxMarks = new MaxMarks(DEFAULT_MAX_MARKS); + weightage = new Weightage(DEFAULT_WEIGHTAGE); + } + + /** + * Creates GradedComponentBuilder with an existing graded component. + * + * @param gradedComponentToCopy + */ + public GradedComponentBuilder(GradedComponent gradedComponentToCopy) { + gcName = gradedComponentToCopy.getName(); + maxMarks = gradedComponentToCopy.getMaxMarks(); + weightage = gradedComponentToCopy.getWeightage(); + } + + /** + * Return GradedComponentBuilder with newly set gcName. + * + * @param gcName gcName. + * @return Edited GradedComponentBuilder. + */ + public GradedComponentBuilder withGcName(String gcName) { + this.gcName = new GcName(gcName); + return this; + } + + /** + * Return GradedComponentBuilder with newly set weightage. + * + * @param weightage weightage. + * @return Edited GradedComponentBuilder. + */ + public GradedComponentBuilder withWeightage(float weightage) { + this.weightage = new Weightage(weightage); + return this; + } + + /** + * Return GradedComponentBuilder with newly set maxMarks. + * + * @param maxMarks maxMarks. + * @return Edited GradedComponentBuilder. + */ + public GradedComponentBuilder withMaxMarks(float maxMarks) { + this.maxMarks = new MaxMarks(maxMarks); + return this; + } + + public GradedComponent build() { + return new GradedComponent(gcName, maxMarks, weightage); + } + +} diff --git a/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java b/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java new file mode 100644 index 00000000000..2d556ca551e --- /dev/null +++ b/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java @@ -0,0 +1,35 @@ +package seedu.modulight.testutil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.modulight.model.gradedcomponent.GradedComponent; +import seedu.modulight.model.gradedcomponent.model.GradedComponentBook; + +public class TypicalGradedComponents { + + private TypicalGradedComponents() {}; + + public static final GradedComponent MIDTERMS = new GradedComponentBuilder().withGcName("Midterms") + .withMaxMarks((float) 100.0).withWeightage((float) 25).build(); + public static final GradedComponent FINALS = new GradedComponentBuilder().withGcName("Finals") + .withMaxMarks((float) 100).withWeightage((float) 60).build(); + public static final GradedComponent QUIZ = new GradedComponentBuilder().withGcName("Quiz").withMaxMarks((float) 50) + .withWeightage((float) 15).build(); + + + public static GradedComponentBook getTypicalGradedComponentBook() { + GradedComponentBook gcb = new GradedComponentBook(); + for (GradedComponent gradedComponent : getTypicalGradedComponents()) { + gcb.addGradedComponent(gradedComponent); + } + return gcb; + } + + public static List getTypicalGradedComponents() { + return new ArrayList<>(Arrays.asList(MIDTERMS, FINALS, QUIZ)); + } + + +} diff --git a/src/test/java/seedu/modulight/testutil/TypicalStudentScores.java b/src/test/java/seedu/modulight/testutil/TypicalStudentScores.java new file mode 100644 index 00000000000..f8f7ea6cccd --- /dev/null +++ b/src/test/java/seedu/modulight/testutil/TypicalStudentScores.java @@ -0,0 +1,28 @@ +package seedu.modulight.testutil; + +import static seedu.modulight.logic.commands.CommandTestUtil.VALID_STUDENT_SCORE_AMY; +import static seedu.modulight.logic.commands.CommandTestUtil.VALID_STUDENT_SCORE_JAMES; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.modulight.model.studentscore.StudentScore; +import seedu.modulight.model.studentscore.model.StudentScoreBook; + +public class TypicalStudentScores { + + private TypicalStudentScores() {}; + + public static StudentScoreBook getTypicalStudentScoreBook() { + StudentScoreBook ssb = new StudentScoreBook(); + for (StudentScore studentScore : getTypicalStudentScores()) { + ssb.addStudentScore(studentScore); + } + return ssb; + } + + public static List getTypicalStudentScores() { + return new ArrayList<>(Arrays.asList(VALID_STUDENT_SCORE_AMY, VALID_STUDENT_SCORE_JAMES)); + } +} From 2ec89febb3fbda52055a0f72bcc0743845a876aa Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Sun, 12 Nov 2023 20:56:05 +0800 Subject: [PATCH 03/28] Edit StorageManagerTest --- src/test/java/seedu/modulight/storage/StorageManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/modulight/storage/StorageManagerTest.java b/src/test/java/seedu/modulight/storage/StorageManagerTest.java index 230ee72a8d6..907cf7ad84b 100644 --- a/src/test/java/seedu/modulight/storage/StorageManagerTest.java +++ b/src/test/java/seedu/modulight/storage/StorageManagerTest.java @@ -106,7 +106,7 @@ public void getStudentBookFilePath() { public void getStudentScoreBookFilePath() { assertNotNull(storageManager.getStudentScoreBookFilePath()); } - + @Test public void getGcBookFilePath() { assertNotNull(storageManager.getGcBookFilePath()); From 3dd6ba6663f896a417394caece6ea8e0b2c4374d Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Sun, 12 Nov 2023 22:05:14 +0800 Subject: [PATCH 04/28] Add more test classes --- .../modulight/storage/JsonAdaptedGradedComponentTest.java | 4 ++++ .../seedu/modulight/storage/JsonAdaptedStudentScoreTest.java | 4 ++++ .../java/seedu/modulight/storage/JsonAdaptedStudentTest.java | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java create mode 100644 src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java create mode 100644 src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java new file mode 100644 index 00000000000..1fb0f2a807f --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java @@ -0,0 +1,4 @@ +package seedu.modulight.storage; + +public class JsonAdaptedGradedComponentTest { +} diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java new file mode 100644 index 00000000000..975e4242169 --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java @@ -0,0 +1,4 @@ +package seedu.modulight.storage; + +public class JsonAdaptedStudentScoreTest { +} diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java new file mode 100644 index 00000000000..d55db367261 --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java @@ -0,0 +1,4 @@ +package seedu.modulight.storage; + +public class JsonAdaptedStudentTest { +} From 5ec47b2ca070e11c5388f7c04e296e816f694887 Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Sun, 12 Nov 2023 23:28:28 +0800 Subject: [PATCH 05/28] Add tests for JsonAdaptedStudent --- .../storage/JsonAdaptedStudentTest.java | 131 ++++++++++++++++++ .../modulight/storage/StorageManagerTest.java | 4 +- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java index d55db367261..814fe47c3f9 100644 --- a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java +++ b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java @@ -1,4 +1,135 @@ package seedu.modulight.storage; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.modulight.storage.JsonAdaptedStudent.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.modulight.testutil.Assert.assertThrows; +import static seedu.modulight.testutil.TypicalStudents.BENSON; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.modulight.commons.exceptions.IllegalValueException; +import seedu.modulight.model.student.StudentEmail; +import seedu.modulight.model.student.StudentGrade; +import seedu.modulight.model.student.StudentId; +import seedu.modulight.model.student.StudentName; +import seedu.modulight.model.student.TutorialGroup; + public class JsonAdaptedStudentTest { + + private static final String INVALID_STUDENT_ID = "0000"; + private static final String INVALID_STUDENT_NAME = "R@chel"; + private static final String INVALID_STUDENT_EMAIL = "example.com"; + private static final String INVALID_TUTORIAL_GROUP = "12A"; + private static final String INVALID_STUDENT_GRADE = "AAA"; + private static final String INVALID_TAG = "#friend"; + + private static final String VALID_STUDENT_ID = BENSON.getStudentId().toString(); + private static final String VALID_STUDENT_NAME = BENSON.getName().toString(); + private static final String VALID_STUDENT_EMAIL = BENSON.getEmail().toString(); + private static final String VALID_TUTORIAL_GROUP = BENSON.getTutorial().toString(); + private static final String VALID_STUDENT_GRADE = BENSON.getStudentGrade().toString(); + private static final List VALID_TAGS = BENSON.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList()); + + @Test + public void toModelType_validStudentDetails_returnsStudent() throws Exception { + JsonAdaptedStudent student = new JsonAdaptedStudent(BENSON); + assertEquals(BENSON, student.toModelType()); + } + + @Test + public void toModelType_InvalidStudentID_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(INVALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = StudentId.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_nullStudentID_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(null, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentId.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_InvalidStudentName_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, INVALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = StudentName.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_nullStudentName_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, null, VALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentName.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_InvalidStudentEmail_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, INVALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = StudentEmail.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_nullStudentEmail_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, null + , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentEmail.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_InvalidTutorialGroup_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , INVALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = TutorialGroup.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_nullTutorialGroup_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , null, VALID_TAGS, VALID_STUDENT_GRADE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, TutorialGroup.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_InvalidStudentGrade_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, VALID_TAGS, INVALID_STUDENT_GRADE); + String expectedMessage = StudentGrade.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_nullStudentGrade_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, VALID_TAGS, null); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentGrade.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + List invalidTags = new ArrayList<>(VALID_TAGS); + invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL + , VALID_TUTORIAL_GROUP, invalidTags, VALID_STUDENT_GRADE); + assertThrows(IllegalValueException.class, student::toModelType); + } + + } diff --git a/src/test/java/seedu/modulight/storage/StorageManagerTest.java b/src/test/java/seedu/modulight/storage/StorageManagerTest.java index 907cf7ad84b..251704429e3 100644 --- a/src/test/java/seedu/modulight/storage/StorageManagerTest.java +++ b/src/test/java/seedu/modulight/storage/StorageManagerTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static seedu.modulight.testutil.TypicalGradedComponents.getTypicalGradedComponentBook; import static seedu.modulight.testutil.TypicalStudentScores.getTypicalStudentScoreBook; -import static seedu.modulight.testutil.TypicalStudents.getTypicalAddressBook; +import static seedu.modulight.testutil.TypicalStudents.getTypicalStudentBook; import java.nio.file.Path; @@ -65,7 +65,7 @@ public void studentBookReadSave() throws Exception { * {@link JsonStudentBookStorage} class. * More extensive testing of saving/reading is done in {@link JsonStudentBookStorageTest} class. */ - StudentBook original = getTypicalAddressBook(); + StudentBook original = getTypicalStudentBook(); storageManager.saveStudentBook(original); ReadOnlyStudentBook retrieved = storageManager.readStudentBook().get(); assertEquals(original, new StudentBook(retrieved)); From 34069d363109f5ef805cbce73d18768f0c5135e4 Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Mon, 13 Nov 2023 18:52:41 +0800 Subject: [PATCH 06/28] Add tests for Storage Component --- .../model/studentscore/StudentScore.java | 6 +- .../storage/JsonAdaptedGradedComponent.java | 16 +++ .../storage/JsonAdaptedStudentScore.java | 39 +++++- .../storage/JsonSerializableStudentBook.java | 2 +- .../invalidAndValidGcBook.json | 11 ++ .../invalidGcBook.json | 7 ++ .../notJsonFormatGcBook.json | 1 + .../duplicateGcBook.json | 0 .../invalidGcBook.json | 0 .../duplicateStudentBook.json | 19 +++ .../invalidStudentBook.json | 12 ++ .../typicalStudentBook.json | 52 ++++++++ .../duplicateStudentScoreBook.json | 0 .../invalidStudentScoreBook.json | 13 ++ .../invalidAndValidStudentBook.json | 25 ++++ .../invalidStudentBook.json | 12 ++ .../notJsonFormatStudentBook.json | 1 + .../JsonAdaptedGradedComponentTest.java | 77 ++++++++++++ .../JsonGradedComponentBookStorageTest.java | 115 ++++++++++++++++++ .../JsonSerializableStudentBookTest.java | 47 +++++++ .../JsonSerializableStudentScoreTest.java | 22 ++++ .../storage/JsonStudentBookStorageTest.java | 112 +++++++++++++++++ .../testutil/TypicalGradedComponents.java | 6 + 23 files changed, 585 insertions(+), 10 deletions(-) create mode 100644 src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json create mode 100644 src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json create mode 100644 src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json create mode 100644 src/test/data/JsonSerializableGcBookTest/duplicateGcBook.json create mode 100644 src/test/data/JsonSerializableGcBookTest/invalidGcBook.json create mode 100644 src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json create mode 100644 src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json create mode 100644 src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json create mode 100644 src/test/data/JsonSerializableStudentScoreBookTest/duplicateStudentScoreBook.json create mode 100644 src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json create mode 100644 src/test/data/JsonStudentBookStorageTest/invalidAndValidStudentBook.json create mode 100644 src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json create mode 100644 src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json create mode 100644 src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java create mode 100644 src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java create mode 100644 src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java create mode 100644 src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java diff --git a/src/main/java/seedu/modulight/model/studentscore/StudentScore.java b/src/main/java/seedu/modulight/model/studentscore/StudentScore.java index 42ff9cf03cc..5c1f8110897 100644 --- a/src/main/java/seedu/modulight/model/studentscore/StudentScore.java +++ b/src/main/java/seedu/modulight/model/studentscore/StudentScore.java @@ -58,11 +58,9 @@ public StudentScore(StudentId sid, GcName gcName, float score) { } - private boolean isValidScore(float s) { + public static boolean isValidScore(float s, float mm) { boolean isLessEqualThanMaxMarks = true; - if (this.gc != null) { - isLessEqualThanMaxMarks = s <= this.gc.getMaxMarks().maxMarks; - } + isLessEqualThanMaxMarks = s <= mm; return s >= 0 && isLessEqualThanMaxMarks; } diff --git a/src/main/java/seedu/modulight/storage/JsonAdaptedGradedComponent.java b/src/main/java/seedu/modulight/storage/JsonAdaptedGradedComponent.java index a6a68650f38..1f2fa2a6fee 100644 --- a/src/main/java/seedu/modulight/storage/JsonAdaptedGradedComponent.java +++ b/src/main/java/seedu/modulight/storage/JsonAdaptedGradedComponent.java @@ -61,18 +61,34 @@ public GradedComponent toModelType() throws IllegalValueException { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, MaxMarks.class.getSimpleName())); } + + try { + Float.parseFloat(gcMaxMarks); + } catch (NumberFormatException e) { + throw new IllegalValueException(MaxMarks.MESSAGE_CONSTRAINTS); + } + if (!MaxMarks.isValidMarks(Float.parseFloat(gcMaxMarks))) { throw new IllegalValueException(MaxMarks.MESSAGE_CONSTRAINTS); } + final MaxMarks modelMaxMarks = new MaxMarks(Float.parseFloat(gcMaxMarks)); if (gcWeightage == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Weightage.class.getSimpleName())); } + + try { + Float.parseFloat(gcWeightage); + } catch (NumberFormatException e) { + throw new IllegalValueException(Weightage.MESSAGE_CONSTRAINTS); + } + if (!Weightage.isValidWeightage(Float.parseFloat(gcWeightage))) { throw new IllegalValueException(Weightage.MESSAGE_CONSTRAINTS); } + final Weightage modelWeightage = new Weightage(Float.parseFloat(gcWeightage)); return new GradedComponent(modelGcName, modelMaxMarks, modelWeightage); diff --git a/src/main/java/seedu/modulight/storage/JsonAdaptedStudentScore.java b/src/main/java/seedu/modulight/storage/JsonAdaptedStudentScore.java index 13088c4d688..6bcf99f3ed2 100644 --- a/src/main/java/seedu/modulight/storage/JsonAdaptedStudentScore.java +++ b/src/main/java/seedu/modulight/storage/JsonAdaptedStudentScore.java @@ -11,9 +11,7 @@ import seedu.modulight.commons.exceptions.IllegalValueException; import seedu.modulight.model.gradedcomponent.GcName; -import seedu.modulight.model.gradedcomponent.GradedComponent; import seedu.modulight.model.gradedcomponent.MaxMarks; -import seedu.modulight.model.gradedcomponent.Weightage; import seedu.modulight.model.student.StudentId; import seedu.modulight.model.studentscore.StudentScore; import seedu.modulight.model.tag.Tag; @@ -88,11 +86,42 @@ public StudentScore toModelType() throws IllegalValueException { } final StudentId modelStudentId = new StudentId(studentId); + if (gcName == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + GcName.class.getSimpleName())); + } + if (!GcName.isValidName(gcName)) { + throw new IllegalValueException(GcName.MESSAGE_CONSTRAINTS); + } final GcName newGcName = new GcName(gcName); - final GradedComponent gradedComponent = new GradedComponent(newGcName, - new MaxMarks(Float.parseFloat(gcMaxMarks)), new Weightage(Float.parseFloat(gcWeightage))); - //check validity of scores + try { + Float.parseFloat(gcMaxMarks); + } catch (NumberFormatException e) { + throw new IllegalValueException(MaxMarks.MESSAGE_CONSTRAINTS); + } + + if (!MaxMarks.isValidMarks(Float.parseFloat(gcMaxMarks))) { + throw new IllegalValueException(MaxMarks.MESSAGE_CONSTRAINTS); + } + + if (score == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + StudentScore.class.getSimpleName())); + } + + final float modelMaxMarks = Float.parseFloat(gcMaxMarks); + + try { + Float.parseFloat(score); + } catch (NumberFormatException e) { + throw new IllegalValueException("Score should be a number less than max marks and greater than 0"); + } + + if (!StudentScore.isValidScore(Float.parseFloat(score), modelMaxMarks)) { + throw new IllegalValueException("Score should be a number less than max marks and greater than 0"); + } + final float modelScore = Float.parseFloat(score); final String modelComment = comment; diff --git a/src/main/java/seedu/modulight/storage/JsonSerializableStudentBook.java b/src/main/java/seedu/modulight/storage/JsonSerializableStudentBook.java index 506572de45b..304109937e9 100644 --- a/src/main/java/seedu/modulight/storage/JsonSerializableStudentBook.java +++ b/src/main/java/seedu/modulight/storage/JsonSerializableStudentBook.java @@ -15,7 +15,7 @@ /** - * An Immutable AddressBook that is serializable to JSON format. + * An Immutable StudentBook that is serializable to JSON format. */ @JsonRootName(value = "studentbook") public class JsonSerializableStudentBook { diff --git a/src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json b/src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json new file mode 100644 index 00000000000..cb272b84540 --- /dev/null +++ b/src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json @@ -0,0 +1,11 @@ +{ + "gradedComponents" : [ { + "gcName" : "Midterm Exam", + "gcMaxMarks" : "10001", + "gcWeightage" : "25.0" + }, { + "gcName" : "Final Exam", + "gcMaxMarks" : "75.0", + "gcWeightage" : "25.0" + } ] +} \ No newline at end of file diff --git a/src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json b/src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json new file mode 100644 index 00000000000..ffe89902af5 --- /dev/null +++ b/src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json @@ -0,0 +1,7 @@ +{ + "gradedComponents" : [ { + "gcName" : "Midterm Exam", + "gcMaxMarks" : "10001", + "gcWeightage" : "25.0" + } ] +} \ No newline at end of file diff --git a/src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json b/src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json new file mode 100644 index 00000000000..2c72ff0bfb6 --- /dev/null +++ b/src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json @@ -0,0 +1 @@ +not json format! \ No newline at end of file diff --git a/src/test/data/JsonSerializableGcBookTest/duplicateGcBook.json b/src/test/data/JsonSerializableGcBookTest/duplicateGcBook.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/test/data/JsonSerializableGcBookTest/invalidGcBook.json b/src/test/data/JsonSerializableGcBookTest/invalidGcBook.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json b/src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json new file mode 100644 index 00000000000..b076816e35b --- /dev/null +++ b/src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json @@ -0,0 +1,19 @@ +{ + "students": [ + { + "studentId": "A0000000Y", + "studentName": "Rachel", + "studentEmail": "alexyeoh@example.com", + "tutorialGroup": "T03", + "tags": [], + "studentGrade": "" + }, { + "studentId": "A0000000Y", + "studentName": "Rachel", + "studentEmail": "yeohalex@example.com", + "tutorialGroup": "T03", + "tags": [], + "studentGrade": "" + } + ] +} \ No newline at end of file diff --git a/src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json b/src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json new file mode 100644 index 00000000000..b1015e737a3 --- /dev/null +++ b/src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json @@ -0,0 +1,12 @@ +{ + "students": [ + { + "studentId": "A0000000Y", + "studentName": "R@chel", + "studentEmail": "alexyeoh@example.com", + "tutorialGroup": "T03", + "tags": [], + "studentGrade": "" + } + ] +} \ No newline at end of file diff --git a/src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json b/src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json new file mode 100644 index 00000000000..1f50869c9f1 --- /dev/null +++ b/src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json @@ -0,0 +1,52 @@ +{ + "students" : [ { + "studentId" : "A0000000Y", + "studentName" : "Alice Pauline", + "studentEmail" : "alice@example.com", + "tutorialGroup" : "T01", + "tags" : [ "friends" ], + "studentGrade" : "" + }, { + "studentId" : "A0000001Y", + "studentName" : "Benson Meier", + "studentEmail" : "johnd@example.com", + "tutorialGroup" : "T01", + "tags" : [ "owesMoney", "friends"], + "studentGrade" : "" + }, { + "studentId" : "A0000002Y", + "studentName" : "Carl Kurz", + "studentEmail" : "heinz@example.com", + "tutorialGroup" : "T01", + "tags" : [ ], + "studentGrade" : "" + }, { + "studentId" : "A0000003Y", + "studentName" : "Daniel Meier", + "studentEmail" : "cornelia@example.com", + "tutorialGroup" : "T02", + "tags" : [ "plagiarism" ], + "studentGrade" : "" + }, { + "studentId" : "A0000004Y", + "studentName" : "Elle Meyer", + "studentEmail" : "werner@example.com", + "tutorialGroup" : "T02", + "tags" : [ ], + "studentGrade" : "" + }, { + "studentId" : "A0000005Y", + "studentName" : "Fiona Kunz", + "studentEmail" : "lydia@example.com", + "tutorialGroup" : "T02", + "tags" : [ ], + "studentGrade" : "" + }, { + "studentId" : "A0000006Y", + "studentName" : "George Best", + "studentEmail" : "anna@example.com", + "tutorialGroup" : "T02", + "tags" : [ ], + "studentGrade" : "" + } ] +} \ No newline at end of file diff --git a/src/test/data/JsonSerializableStudentScoreBookTest/duplicateStudentScoreBook.json b/src/test/data/JsonSerializableStudentScoreBookTest/duplicateStudentScoreBook.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json b/src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json new file mode 100644 index 00000000000..86de572af68 --- /dev/null +++ b/src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json @@ -0,0 +1,13 @@ +{ + "studentScores": [ + { + "studentId": "A0000000Y", + "score": "0a", + "comment": "", + "tags": [], + "gcName": "Midterm Exam", + "gcMaxMarks": "100", + "gcWeightage": "100" + } + ] +} \ No newline at end of file diff --git a/src/test/data/JsonStudentBookStorageTest/invalidAndValidStudentBook.json b/src/test/data/JsonStudentBookStorageTest/invalidAndValidStudentBook.json new file mode 100644 index 00000000000..340a8b89bbd --- /dev/null +++ b/src/test/data/JsonStudentBookStorageTest/invalidAndValidStudentBook.json @@ -0,0 +1,25 @@ +{ + "students": [ + { + "studentId": "A0000000Y", + "studentName": "Invalid @Name", + "studentEmail": "alice@example.com", + "tutorialGroup": "T01", + "tags": [ + "friends" + ], + "studentGrade": "" + }, + { + "studentId": "A0000001Y", + "studentName": "Benson Meier", + "studentEmail": "johnd@example.com", + "tutorialGroup": "T01", + "tags": [ + "owesMoney", + "friends" + ], + "studentGrade": "" + } + ] +} diff --git a/src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json b/src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json new file mode 100644 index 00000000000..b1015e737a3 --- /dev/null +++ b/src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json @@ -0,0 +1,12 @@ +{ + "students": [ + { + "studentId": "A0000000Y", + "studentName": "R@chel", + "studentEmail": "alexyeoh@example.com", + "tutorialGroup": "T03", + "tags": [], + "studentGrade": "" + } + ] +} \ No newline at end of file diff --git a/src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json b/src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json new file mode 100644 index 00000000000..2c72ff0bfb6 --- /dev/null +++ b/src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json @@ -0,0 +1 @@ +not json format! \ No newline at end of file diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java index 1fb0f2a807f..104f7590e0f 100644 --- a/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java +++ b/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java @@ -1,4 +1,81 @@ package seedu.modulight.storage; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.modulight.storage.JsonAdaptedGradedComponent.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.modulight.testutil.Assert.assertThrows; +import static seedu.modulight.testutil.TypicalGradedComponents.MIDTERMS; + +import org.junit.jupiter.api.Test; + +import seedu.modulight.commons.exceptions.IllegalValueException; +import seedu.modulight.model.gradedcomponent.GcName; +import seedu.modulight.model.gradedcomponent.MaxMarks; +import seedu.modulight.model.gradedcomponent.Weightage; + public class JsonAdaptedGradedComponentTest { + + private static final String INVALID_GRADED_COMPONENT_NAME = "M@idterm"; + private static final String INVALID_MAX_MARKS = "10001"; + private static final String INVALID_WEIGHTAGE = "1000"; + + private static final String VALID_GRADED_COMPONENT_NAME = MIDTERMS.getName().toString(); + private static final String VALID_MAX_MARKS = MIDTERMS.getMaxMarks().toString(); + private static final String VALID_WEIGHTAGE = MIDTERMS.getWeightage().toString(); + + @Test + public void toModelType_validGcDetails_returnsGc() throws Exception { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(MIDTERMS); + assertEquals(MIDTERMS, gc.toModelType()); + } + + @Test + public void toModelType_InvalidGcName_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(INVALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS + , VALID_WEIGHTAGE); + String expectedMessage = GcName.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); + } + + @Test + public void toModelType_nullGcName_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(null, VALID_MAX_MARKS, VALID_WEIGHTAGE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, GcName.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); + } + + @Test + public void toModelType_InvalidMaxMarks_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, INVALID_MAX_MARKS + , VALID_WEIGHTAGE); + String expectedMessage = MaxMarks.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); + } + + @Test + public void toModelType_nullMaxMarks_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, null + , VALID_WEIGHTAGE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, MaxMarks.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); + } + + @Test + public void toModelType_InvalidWeightage_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS + , INVALID_WEIGHTAGE); + String expectedMessage = Weightage.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); + } + + @Test + public void toModelType_nullWeightage_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS + , null); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Weightage.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); + } + + + + } diff --git a/src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java b/src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java new file mode 100644 index 00000000000..eb683773a5d --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java @@ -0,0 +1,115 @@ +package seedu.modulight.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.modulight.testutil.Assert.assertThrows; +import static seedu.modulight.testutil.TypicalGradedComponents.CA1; +import static seedu.modulight.testutil.TypicalGradedComponents.CA2; +import static seedu.modulight.testutil.TypicalGradedComponents.MIDTERMS; +import static seedu.modulight.testutil.TypicalGradedComponents.getTypicalGradedComponentBook; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.modulight.commons.exceptions.DataLoadingException; +import seedu.modulight.model.gradedcomponent.model.GradedComponentBook; +import seedu.modulight.model.gradedcomponent.model.ReadOnlyGradedComponentBook; + +public class JsonGradedComponentBookStorageTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" + , "JsonGradedComponentBookStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readGradedComponentBook_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readGradedComponentBook(null)); + } + + private java.util.Optional readGradedComponentBook(String filePath) throws Exception { + return new JsonGradedComponentBookStorage(Paths.get(filePath)).readGradedComponentBook( + addToTestDataPathIfNotNull(filePath)); + } + + private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readGradedComponentBook("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataLoadingException.class, () -> readGradedComponentBook("notJsonFormatGcBook.json")); + } + + @Test + public void readGradedComponentBook_invalidGradedComponentBook_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readGradedComponentBook("invalidGcBook.json")); + } + + @Test + public void readGradedComponentBook_invalidAndValidStudentBook_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readGradedComponentBook( + "invalidAndValidGcBook.json")); + } + + @Test + public void readAndSaveGradedComponentBook_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempGradedComponentBook.json"); + GradedComponentBook original = getTypicalGradedComponentBook(); + JsonGradedComponentBookStorage jsonGradedComponentBookStorage = new JsonGradedComponentBookStorage(filePath); + + // Save in new file and read back + jsonGradedComponentBookStorage.saveGradedComponentBook(original, filePath); + ReadOnlyGradedComponentBook readBack = jsonGradedComponentBookStorage.readGradedComponentBook(filePath).get(); + assertEquals(original, new GradedComponentBook(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addGradedComponent(CA1); + original.removeGradedComponent(MIDTERMS); + jsonGradedComponentBookStorage.saveGradedComponentBook(original, filePath); + readBack = jsonGradedComponentBookStorage.readGradedComponentBook(filePath).get(); + assertEquals(original, new GradedComponentBook(readBack)); + + // Save and read without specifying file path + original.addGradedComponent(CA2); + jsonGradedComponentBookStorage.saveGradedComponentBook(original); // file path not specified + readBack = jsonGradedComponentBookStorage.readGradedComponentBook().get(); // file path not specified + assertEquals(original, new GradedComponentBook(readBack)); + + } + + @Test + public void saveGradedComponentBook_nullGradedComponentBook_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveGradedComponentBook(null, "SomeFile.json")); + } + + /** + * Saves {@code gradedComponentBook} at the specified {@code filePath}. + */ + private void saveGradedComponentBook(ReadOnlyGradedComponentBook gradedComponentBook, String filePath) { + try { + new JsonGradedComponentBookStorage(Paths.get(filePath)) + .saveGradedComponentBook(gradedComponentBook, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveGradedComponentBook_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveGradedComponentBook(new GradedComponentBook() + , null)); + } +} diff --git a/src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java b/src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java new file mode 100644 index 00000000000..76a5f0a8df4 --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java @@ -0,0 +1,47 @@ +package seedu.modulight.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.modulight.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.modulight.commons.exceptions.IllegalValueException; +import seedu.modulight.commons.util.JsonUtil; +import seedu.modulight.model.student.model.StudentBook; +import seedu.modulight.testutil.TypicalStudents; + +public class JsonSerializableStudentBookTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" + , "JsonSerializableStudentBookTest"); + private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalStudentBook.json"); + private static final Path INVALID_STUDENTS_FILE = TEST_DATA_FOLDER.resolve("invalidStudentBook.json"); + private static final Path DUPLICATE_STUDENTS_FILE = TEST_DATA_FOLDER.resolve("duplicateStudentBook.json"); + + @Test + public void toModelType_typicalStudentsFile_success() throws Exception { + JsonSerializableStudentBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, + JsonSerializableStudentBook.class).get(); + StudentBook studentBookFromFile = dataFromFile.toModelType(); + StudentBook typicalStudentBook = TypicalStudents.getTypicalStudentBook(); + assertEquals(studentBookFromFile, typicalStudentBook); + } + + @Test + public void toModelType_invalidStudentFile_throwsIllegalValueException() throws Exception { + JsonSerializableStudentBook dataFromFile = JsonUtil.readJsonFile(INVALID_STUDENTS_FILE, + JsonSerializableStudentBook.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicateStudents_throwsIllegalValueException() throws Exception { + JsonSerializableStudentBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_STUDENTS_FILE, + JsonSerializableStudentBook.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableStudentBook.MESSAGE_DUPLICATE_PERSON, + dataFromFile::toModelType); + } +} diff --git a/src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java b/src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java new file mode 100644 index 00000000000..db03696858f --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java @@ -0,0 +1,22 @@ +package seedu.modulight.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.modulight.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.modulight.commons.exceptions.IllegalValueException; +import seedu.modulight.commons.util.JsonUtil; +import seedu.modulight.model.studentscore.model.StudentScoreBook; + +public class JsonSerializableStudentScoreTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" + , "JsonSerializableStudentScoreBookTest"); + private static final Path INVALID_STUDENT_SCORE_FILE = TEST_DATA_FOLDER.resolve( + "invalidStudentScoreBook.json"); + +} diff --git a/src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java b/src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java new file mode 100644 index 00000000000..cce8aeddc47 --- /dev/null +++ b/src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java @@ -0,0 +1,112 @@ +package seedu.modulight.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.modulight.testutil.Assert.assertThrows; +import static seedu.modulight.testutil.TypicalStudents.ALICE; +import static seedu.modulight.testutil.TypicalStudents.HOON; +import static seedu.modulight.testutil.TypicalStudents.IDA; +import static seedu.modulight.testutil.TypicalStudents.getTypicalStudentBook; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.modulight.commons.exceptions.DataLoadingException; +import seedu.modulight.model.student.model.ReadOnlyStudentBook; +import seedu.modulight.model.student.model.StudentBook; + +public class JsonStudentBookStorageTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" + , "JsonStudentBookStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readStudentBook_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readStudentBook(null)); + } + + private java.util.Optional readStudentBook(String filePath) throws Exception { + return new JsonStudentBookStorage(Paths.get(filePath)).readStudentBook(addToTestDataPathIfNotNull(filePath)); + } + + private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readStudentBook("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataLoadingException.class, () -> readStudentBook("notJsonFormatStudentBook.json")); + } + + @Test + public void readStudentBook_invalidStudentBook_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readStudentBook("invalidStudentBook.json")); + } + + @Test + public void readStudentBook_invalidAndValidStudentBook_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readStudentBook("invalidAndValidStudentBook.json")); + } + + @Test + public void readAndSaveStudentBook_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempStudentBook.json"); + StudentBook original = getTypicalStudentBook(); + JsonStudentBookStorage jsonStudentBookStorage = new JsonStudentBookStorage(filePath); + + // Save in new file and read back + jsonStudentBookStorage.saveStudentBook(original, filePath); + ReadOnlyStudentBook readBack = jsonStudentBookStorage.readStudentBook(filePath).get(); + assertEquals(original, new StudentBook(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addStudent(HOON); + original.removeStudent(ALICE); + jsonStudentBookStorage.saveStudentBook(original, filePath); + readBack = jsonStudentBookStorage.readStudentBook(filePath).get(); + assertEquals(original, new StudentBook(readBack)); + + // Save and read without specifying file path + original.addStudent(IDA); + jsonStudentBookStorage.saveStudentBook(original); // file path not specified + readBack = jsonStudentBookStorage.readStudentBook().get(); // file path not specified + assertEquals(original, new StudentBook(readBack)); + + } + + @Test + public void saveStudentBook_nullStudentBook_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveStudentBook(null, "SomeFile.json")); + } + + /** + * Saves {@code studentBook} at the specified {@code filePath}. + */ + private void saveStudentBook(ReadOnlyStudentBook studentBook, String filePath) { + try { + new JsonStudentBookStorage(Paths.get(filePath)) + .saveStudentBook(studentBook, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveStudentBook_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveStudentBook(new StudentBook(), null)); + } +} diff --git a/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java b/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java index 2d556ca551e..e39bf0c61f9 100644 --- a/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java +++ b/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java @@ -18,6 +18,12 @@ public class TypicalGradedComponents { public static final GradedComponent QUIZ = new GradedComponentBuilder().withGcName("Quiz").withMaxMarks((float) 50) .withWeightage((float) 15).build(); + public static final GradedComponent CA1 = new GradedComponentBuilder().withGcName("CA1").withMaxMarks((float) 100) + .withWeightage((float) 15).build(); + + public static final GradedComponent CA2 = new GradedComponentBuilder().withGcName("CA2").withMaxMarks((float) 100) + .withWeightage((float) 20).build(); + public static GradedComponentBook getTypicalGradedComponentBook() { GradedComponentBook gcb = new GradedComponentBook(); From 9c486d24da19688212bd312c2a06a00e214eb683 Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Mon, 13 Nov 2023 19:03:25 +0800 Subject: [PATCH 07/28] Delete some files --- .../duplicateGcBook.json | 0 .../invalidGcBook.json | 0 .../duplicateStudentScoreBook.json | 0 .../invalidStudentScoreBook.json | 13 ----------- .../storage/JsonAdaptedStudentScoreTest.java | 4 ---- .../JsonSerializableStudentScoreTest.java | 22 ------------------- 6 files changed, 39 deletions(-) delete mode 100644 src/test/data/JsonSerializableGcBookTest/duplicateGcBook.json delete mode 100644 src/test/data/JsonSerializableGcBookTest/invalidGcBook.json delete mode 100644 src/test/data/JsonSerializableStudentScoreBookTest/duplicateStudentScoreBook.json delete mode 100644 src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json delete mode 100644 src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java delete mode 100644 src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java diff --git a/src/test/data/JsonSerializableGcBookTest/duplicateGcBook.json b/src/test/data/JsonSerializableGcBookTest/duplicateGcBook.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/test/data/JsonSerializableGcBookTest/invalidGcBook.json b/src/test/data/JsonSerializableGcBookTest/invalidGcBook.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/test/data/JsonSerializableStudentScoreBookTest/duplicateStudentScoreBook.json b/src/test/data/JsonSerializableStudentScoreBookTest/duplicateStudentScoreBook.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json b/src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json deleted file mode 100644 index 86de572af68..00000000000 --- a/src/test/data/JsonSerializableStudentScoreBookTest/invalidStudentScoreBook.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "studentScores": [ - { - "studentId": "A0000000Y", - "score": "0a", - "comment": "", - "tags": [], - "gcName": "Midterm Exam", - "gcMaxMarks": "100", - "gcWeightage": "100" - } - ] -} \ No newline at end of file diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java deleted file mode 100644 index 975e4242169..00000000000 --- a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentScoreTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package seedu.modulight.storage; - -public class JsonAdaptedStudentScoreTest { -} diff --git a/src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java b/src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java deleted file mode 100644 index db03696858f..00000000000 --- a/src/test/java/seedu/modulight/storage/JsonSerializableStudentScoreTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package seedu.modulight.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.modulight.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import seedu.modulight.commons.exceptions.IllegalValueException; -import seedu.modulight.commons.util.JsonUtil; -import seedu.modulight.model.studentscore.model.StudentScoreBook; - -public class JsonSerializableStudentScoreTest { - - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" - , "JsonSerializableStudentScoreBookTest"); - private static final Path INVALID_STUDENT_SCORE_FILE = TEST_DATA_FOLDER.resolve( - "invalidStudentScoreBook.json"); - -} From 1afbb392072b43e673490b4b18a6fb51ce0b67c9 Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Mon, 13 Nov 2023 19:14:06 +0800 Subject: [PATCH 08/28] Fix errors --- .../invalidAndValidGcBook.json | 2 +- .../data/JsonGradedComponentBookStorageTest/invalidGcBook.json | 2 +- .../JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json | 2 +- .../JsonSerializableStudentBookTest/duplicateStudentBook.json | 2 +- .../JsonSerializableStudentBookTest/invalidStudentBook.json | 2 +- .../JsonSerializableStudentBookTest/typicalStudentBook.json | 2 +- .../data/JsonStudentBookStorageTest/invalidStudentBook.json | 2 +- .../JsonStudentBookStorageTest/notJsonFormatStudentBook.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json b/src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json index cb272b84540..3499155ff21 100644 --- a/src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json +++ b/src/test/data/JsonGradedComponentBookStorageTest/invalidAndValidGcBook.json @@ -8,4 +8,4 @@ "gcMaxMarks" : "75.0", "gcWeightage" : "25.0" } ] -} \ No newline at end of file +} diff --git a/src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json b/src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json index ffe89902af5..230eb58052a 100644 --- a/src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json +++ b/src/test/data/JsonGradedComponentBookStorageTest/invalidGcBook.json @@ -4,4 +4,4 @@ "gcMaxMarks" : "10001", "gcWeightage" : "25.0" } ] -} \ No newline at end of file +} diff --git a/src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json b/src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json index 2c72ff0bfb6..a1097343b5d 100644 --- a/src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json +++ b/src/test/data/JsonGradedComponentBookStorageTest/notJsonFormatGcBook.json @@ -1 +1 @@ -not json format! \ No newline at end of file +not json format! diff --git a/src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json b/src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json index b076816e35b..b9a9ad5556f 100644 --- a/src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json +++ b/src/test/data/JsonSerializableStudentBookTest/duplicateStudentBook.json @@ -16,4 +16,4 @@ "studentGrade": "" } ] -} \ No newline at end of file +} diff --git a/src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json b/src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json index b1015e737a3..67d428a28d3 100644 --- a/src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json +++ b/src/test/data/JsonSerializableStudentBookTest/invalidStudentBook.json @@ -9,4 +9,4 @@ "studentGrade": "" } ] -} \ No newline at end of file +} diff --git a/src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json b/src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json index 1f50869c9f1..9770d35e765 100644 --- a/src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json +++ b/src/test/data/JsonSerializableStudentBookTest/typicalStudentBook.json @@ -49,4 +49,4 @@ "tags" : [ ], "studentGrade" : "" } ] -} \ No newline at end of file +} diff --git a/src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json b/src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json index b1015e737a3..67d428a28d3 100644 --- a/src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json +++ b/src/test/data/JsonStudentBookStorageTest/invalidStudentBook.json @@ -9,4 +9,4 @@ "studentGrade": "" } ] -} \ No newline at end of file +} diff --git a/src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json b/src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json index 2c72ff0bfb6..a1097343b5d 100644 --- a/src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json +++ b/src/test/data/JsonStudentBookStorageTest/notJsonFormatStudentBook.json @@ -1 +1 @@ -not json format! \ No newline at end of file +not json format! From dabef04a02163f8c82b36017a51b4459318d548c Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Mon, 13 Nov 2023 19:26:00 +0800 Subject: [PATCH 09/28] Add JavaDoc --- .../seedu/modulight/model/studentscore/StudentScore.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/seedu/modulight/model/studentscore/StudentScore.java b/src/main/java/seedu/modulight/model/studentscore/StudentScore.java index 5c1f8110897..af280f4b280 100644 --- a/src/main/java/seedu/modulight/model/studentscore/StudentScore.java +++ b/src/main/java/seedu/modulight/model/studentscore/StudentScore.java @@ -58,6 +58,13 @@ public StudentScore(StudentId sid, GcName gcName, float score) { } + /** + * Checks whether a score is valid. + * + * @param s score. + * @param mm maximum marks of the graded component. + * @return true if given score is valid. + */ public static boolean isValidScore(float s, float mm) { boolean isLessEqualThanMaxMarks = true; isLessEqualThanMaxMarks = s <= mm; From aa4edf13d2c9fd83cda1dc3ea015653694535146 Mon Sep 17 00:00:00 2001 From: feifeiraindrops Date: Mon, 13 Nov 2023 19:32:28 +0800 Subject: [PATCH 10/28] Fix note display --- docs/UserGuide.md | 47 ++++++++++++++++++------------------ docs/team/feifeiraindrops.md | 6 +++++ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3e903d7c038..d4545eb845e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -23,7 +23,7 @@ Furthermore, we believe that module management should be **efficient**. Therefor via a Command Line Interface** while still having the benefits of a Graphical User Interface (GUI). If you can type fast, ModuLight can get your student grading tasks done faster than traditional GUI apps. -> [!NOTE] +> **Note** > * We assume that the professors are already familiar with module structures, such as the number and weightage of graded components and the number of students and tutorial groups. > * We understand that @@ -114,8 +114,8 @@ The following section gives an overview of the parameters used for the commands -> [!NOTE] -> **Graded Component and Student Score parameters for score calculation** +> **Note**: Graded Component and Student Score parameters for score calculation +> > * The maximum marks of a graded component and marks of a student score are both **absolute values** and are used together to determine the relative performance of a student for a component.
For instance, if the maximum marks for a component Midterms is 50, and the marks for the student is 35, then the student scored 35/50 =70% on this graded component. > * The weightage of a graded component is used to determine its contribution to a student’s overall score, and is calculated @@ -123,9 +123,7 @@ determine the relative performance of a student for a component.
For instan and component A has weightage 30, and component B weightage 20, then component A currently represents 20/(20+30) = 60% of the student’s overall score. This is modified as components are added and removed.
Note that the **total weightage of all graded components should be less than or equal to 100**. > * If a graded component has a maximum mark of 0, the relative score for any associated student scores will be 0. - - -* If a student or graded component has no associated student scores, the average mark will be listed as 0. +> * If a student or graded component has no associated student scores, the average mark will be listed as 0.
@@ -165,21 +163,15 @@ Here is a summary of each GUI component within ModuLight. ## Features -> [!IMPORTANT]
+> **Note**
+> > If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application. -### Viewing help : `help` - -Shows a message explaining how to access the help page. - -![help message](images/helpMessage.png) - -Format: `help` - ## Add Commands Adds a new student or graded component. -> [!NOTE] +> **Note**
+> > Student scores will be automatically added when a new student or component is added. ### Add a student: `addStu` @@ -211,7 +203,8 @@ Examples: `addComp c/Midterm w/30 mm/70` adds a graded component called “Midt ## Edit Commands Edit a student, student score or graded component. -> [!NOTE] +> **Note**
+> > A student score is related to a student and a graded component. Thus, when one entity is edited, its information in all related entities will be edited as well. > For instance, when a student's student ID is edited, the change will be reflected in all scores that belong to this student. @@ -267,7 +260,8 @@ Examples: `editScore 7 m/57` assigns a mark of 57 for the seventh student score ## Delete Commands Delete a student or graded component from the data base. -> [!NOTE] +> **Note**
+> > Student scores will be automatically deleted when a new student or component is deleted. > For instance, when a student is deleted, all scores that belong to this student will be deleted. They will be deleted from the graded components as well. @@ -307,7 +301,8 @@ Examples: `deleteComp 2` deletes the second graded component in the displayed Gr | x/ | findScore | Comment of the student score | If the comment contains the search key words | plagiarism | Potential plagiarism | student has plagiarised | | c/ | findComp, finsScore | Name of the graded components | If component name contains the search keywords | midterm | Midterm | mid semeter test | -> [!NOTE] +> **Note**
+> > * All find commands are case-insensitive > * It is allowed to have 0 searching criteria. In this case, this command will simply list all objects. > * For searching with multiple student parameters of the same type, it will find the objects who satisfy any of the @@ -461,6 +456,15 @@ Examples: * `compStats st/upperQuartile st/lowerQuartile c/Midterm` returns the upper and lower quartile of the student grades in Midterm. +## Other Commands +### Viewing help : `help` + +Shows a message explaining how to access the help page. + +![help message](images/helpMessage.png) + +Format: `help` + ### List all : `listAll` Shows all students, student scores and graded components in their lists respectively. This removes all the filter applied from the find command. @@ -491,11 +495,6 @@ ModuLight data is saved in the hard disk automatically after any command that ch There is no need to manually load data stored on the hard disc. It will be available automatically everytime the program starts. - -### Archiving data files `[coming in v2.0]` - -_Details coming soon ..._ - -------------------------------------------------------------------------------------------------------------------- ## Command summary diff --git a/docs/team/feifeiraindrops.md b/docs/team/feifeiraindrops.md index b029ef04b97..751f71fa45c 100644 --- a/docs/team/feifeiraindrops.md +++ b/docs/team/feifeiraindrops.md @@ -48,6 +48,11 @@ title: "Yufei Sun's Project Portfolio Page" All student scores related to the component will be displayed as well. * **Justification**: Since student score is related to students, they are also displayed so that the user does not need rto do another search for them. * **Highlights**: Implementing this feature requires a good design of the algorithm and understanding or the project models to show the correct graded components. + +* **New Feature**: `listAll` - list all students, student scores and graded components + * **What is does**: Displays all students, student scores and graded components in the respective lists + * **Justification**: This feature is useful in resetting the display after a find command + * **Code contributed**: @@ -68,6 +73,7 @@ title: "Yufei Sun's Project Portfolio Page" * Developer Guide: * Documented implementation of the logic component + * Updated UML diagrams for proposed implementation and better oop * **Testing**: From 8593c2ec19af063507f96d09e4b7aa37501e9fbb Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Mon, 13 Nov 2023 19:44:54 +0800 Subject: [PATCH 11/28] Fix more errors --- .../JsonAdaptedGradedComponentTest.java | 26 ++++----- .../storage/JsonAdaptedStudentTest.java | 54 +++++++++---------- .../JsonGradedComponentBookStorageTest.java | 8 +-- .../JsonSerializableStudentBookTest.java | 4 +- .../storage/JsonStudentBookStorageTest.java | 4 +- .../modulight/storage/StorageManagerTest.java | 4 +- .../testutil/TypicalGradedComponents.java | 7 ++- .../testutil/TypicalStudentScores.java | 3 ++ 8 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java index 104f7590e0f..9bcc1629b38 100644 --- a/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java +++ b/src/test/java/seedu/modulight/storage/JsonAdaptedGradedComponentTest.java @@ -29,9 +29,9 @@ public void toModelType_validGcDetails_returnsGc() throws Exception { } @Test - public void toModelType_InvalidGcName_throwsIllegalValueException() { - JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(INVALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS - , VALID_WEIGHTAGE); + public void toModelType_invalidGcName_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(INVALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS, + VALID_WEIGHTAGE); String expectedMessage = GcName.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); } @@ -44,33 +44,33 @@ public void toModelType_nullGcName_throwsIllegalValueException() { } @Test - public void toModelType_InvalidMaxMarks_throwsIllegalValueException() { - JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, INVALID_MAX_MARKS - , VALID_WEIGHTAGE); + public void toModelType_invalidMaxMarks_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, INVALID_MAX_MARKS, + VALID_WEIGHTAGE); String expectedMessage = MaxMarks.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); } @Test public void toModelType_nullMaxMarks_throwsIllegalValueException() { - JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, null - , VALID_WEIGHTAGE); + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, null, + VALID_WEIGHTAGE); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, MaxMarks.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); } @Test - public void toModelType_InvalidWeightage_throwsIllegalValueException() { - JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS - , INVALID_WEIGHTAGE); + public void toModelType_invalidWeightage_throwsIllegalValueException() { + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS, + INVALID_WEIGHTAGE); String expectedMessage = Weightage.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); } @Test public void toModelType_nullWeightage_throwsIllegalValueException() { - JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS - , null); + JsonAdaptedGradedComponent gc = new JsonAdaptedGradedComponent(VALID_GRADED_COMPONENT_NAME, VALID_MAX_MARKS, + null); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Weightage.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, gc::toModelType); } diff --git a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java index 814fe47c3f9..ac756565e83 100644 --- a/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java +++ b/src/test/java/seedu/modulight/storage/JsonAdaptedStudentTest.java @@ -43,81 +43,81 @@ public void toModelType_validStudentDetails_returnsStudent() throws Exception { } @Test - public void toModelType_InvalidStudentID_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(INVALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + public void toModelType_invalidStudentID_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(INVALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = StudentId.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test public void toModelType_nullStudentID_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(null, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + JsonAdaptedStudent student = new JsonAdaptedStudent(null, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentId.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test - public void toModelType_InvalidStudentName_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, INVALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + public void toModelType_invalidStudentName_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, INVALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = StudentName.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test public void toModelType_nullStudentName_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, null, VALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, null, VALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentName.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test - public void toModelType_InvalidStudentEmail_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, INVALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + public void toModelType_invalidStudentEmail_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, INVALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = StudentEmail.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test public void toModelType_nullStudentEmail_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, null - , VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, null, + VALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentEmail.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test - public void toModelType_InvalidTutorialGroup_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , INVALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); + public void toModelType_invalidTutorialGroup_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + INVALID_TUTORIAL_GROUP, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = TutorialGroup.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test public void toModelType_nullTutorialGroup_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , null, VALID_TAGS, VALID_STUDENT_GRADE); + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + null, VALID_TAGS, VALID_STUDENT_GRADE); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, TutorialGroup.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test - public void toModelType_InvalidStudentGrade_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, VALID_TAGS, INVALID_STUDENT_GRADE); + public void toModelType_invalidStudentGrade_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, VALID_TAGS, INVALID_STUDENT_GRADE); String expectedMessage = StudentGrade.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @Test public void toModelType_nullStudentGrade_throwsIllegalValueException() { - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, VALID_TAGS, null); + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, VALID_TAGS, null); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentGrade.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); } @@ -126,8 +126,8 @@ public void toModelType_nullStudentGrade_throwsIllegalValueException() { public void toModelType_invalidTags_throwsIllegalValueException() { List invalidTags = new ArrayList<>(VALID_TAGS); invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); - JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL - , VALID_TUTORIAL_GROUP, invalidTags, VALID_STUDENT_GRADE); + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_STUDENT_ID, VALID_STUDENT_NAME, VALID_STUDENT_EMAIL, + VALID_TUTORIAL_GROUP, invalidTags, VALID_STUDENT_GRADE); assertThrows(IllegalValueException.class, student::toModelType); } diff --git a/src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java b/src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java index eb683773a5d..ecada137fe6 100644 --- a/src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java +++ b/src/test/java/seedu/modulight/storage/JsonGradedComponentBookStorageTest.java @@ -21,8 +21,8 @@ public class JsonGradedComponentBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" - , "JsonGradedComponentBookStorageTest"); + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", + "JsonGradedComponentBookStorageTest"); @TempDir public Path testFolder; @@ -109,7 +109,7 @@ private void saveGradedComponentBook(ReadOnlyGradedComponentBook gradedComponent @Test public void saveGradedComponentBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveGradedComponentBook(new GradedComponentBook() - , null)); + assertThrows(NullPointerException.class, () -> saveGradedComponentBook(new GradedComponentBook(), + null)); } } diff --git a/src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java b/src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java index 76a5f0a8df4..be3ecc74c14 100644 --- a/src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java +++ b/src/test/java/seedu/modulight/storage/JsonSerializableStudentBookTest.java @@ -15,8 +15,8 @@ public class JsonSerializableStudentBookTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" - , "JsonSerializableStudentBookTest"); + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", + "JsonSerializableStudentBookTest"); private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalStudentBook.json"); private static final Path INVALID_STUDENTS_FILE = TEST_DATA_FOLDER.resolve("invalidStudentBook.json"); private static final Path DUPLICATE_STUDENTS_FILE = TEST_DATA_FOLDER.resolve("duplicateStudentBook.json"); diff --git a/src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java b/src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java index cce8aeddc47..b53ebfcdb4b 100644 --- a/src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java +++ b/src/test/java/seedu/modulight/storage/JsonStudentBookStorageTest.java @@ -21,8 +21,8 @@ public class JsonStudentBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data" - , "JsonStudentBookStorageTest"); + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", + "JsonStudentBookStorageTest"); @TempDir public Path testFolder; diff --git a/src/test/java/seedu/modulight/storage/StorageManagerTest.java b/src/test/java/seedu/modulight/storage/StorageManagerTest.java index 251704429e3..012ce940dd2 100644 --- a/src/test/java/seedu/modulight/storage/StorageManagerTest.java +++ b/src/test/java/seedu/modulight/storage/StorageManagerTest.java @@ -36,8 +36,8 @@ public void setUp() { "gcb")); JsonStudentScoreBookStorage studentScoreBookStorage = new JsonStudentScoreBookStorage(getTempFilePath( "ssb")); - storageManager = new StorageManager(studentBookStorage, studentScoreBookStorage, gradedComponentBookStorage - , userPrefsStorage); + storageManager = new StorageManager(studentBookStorage, studentScoreBookStorage, gradedComponentBookStorage, + userPrefsStorage); } private Path getTempFilePath(String fileName) { diff --git a/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java b/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java index e39bf0c61f9..98fea37ed7e 100644 --- a/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java +++ b/src/test/java/seedu/modulight/testutil/TypicalGradedComponents.java @@ -7,10 +7,11 @@ import seedu.modulight.model.gradedcomponent.GradedComponent; import seedu.modulight.model.gradedcomponent.model.GradedComponentBook; +/** + * A utility class containing a list of {@code GradedComponent} objects to be used in tests. + */ public class TypicalGradedComponents { - private TypicalGradedComponents() {}; - public static final GradedComponent MIDTERMS = new GradedComponentBuilder().withGcName("Midterms") .withMaxMarks((float) 100.0).withWeightage((float) 25).build(); public static final GradedComponent FINALS = new GradedComponentBuilder().withGcName("Finals") @@ -24,6 +25,8 @@ public class TypicalGradedComponents { public static final GradedComponent CA2 = new GradedComponentBuilder().withGcName("CA2").withMaxMarks((float) 100) .withWeightage((float) 20).build(); + private TypicalGradedComponents() {}; + public static GradedComponentBook getTypicalGradedComponentBook() { GradedComponentBook gcb = new GradedComponentBook(); diff --git a/src/test/java/seedu/modulight/testutil/TypicalStudentScores.java b/src/test/java/seedu/modulight/testutil/TypicalStudentScores.java index f8f7ea6cccd..d5f02b5a148 100644 --- a/src/test/java/seedu/modulight/testutil/TypicalStudentScores.java +++ b/src/test/java/seedu/modulight/testutil/TypicalStudentScores.java @@ -10,6 +10,9 @@ import seedu.modulight.model.studentscore.StudentScore; import seedu.modulight.model.studentscore.model.StudentScoreBook; +/** + * A utility class containing a list of {@code StudentScores} objects to be used in tests. + */ public class TypicalStudentScores { private TypicalStudentScores() {}; From fe80e5dafc7c455e90d8f6fdded84e9975f5e438 Mon Sep 17 00:00:00 2001 From: feifeiraindrops Date: Mon, 13 Nov 2023 20:08:39 +0800 Subject: [PATCH 12/28] Add test to listAll --- .../logic/commands/ListAllCommand.java | 13 +++++ .../logic/commands/ListAllCommandTest.java | 55 +++++++++++++++++++ .../parser/ListAllCommandParserTest.java | 22 ++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/test/java/seedu/modulight/logic/commands/ListAllCommandTest.java create mode 100644 src/test/java/seedu/modulight/logic/parser/ListAllCommandParserTest.java diff --git a/src/main/java/seedu/modulight/logic/commands/ListAllCommand.java b/src/main/java/seedu/modulight/logic/commands/ListAllCommand.java index 99e87ac6a19..80b95668e4b 100644 --- a/src/main/java/seedu/modulight/logic/commands/ListAllCommand.java +++ b/src/main/java/seedu/modulight/logic/commands/ListAllCommand.java @@ -27,4 +27,17 @@ public CommandResult execute(Model model) { model.updateFilteredStudentScoreList(PREDICATE_SHOW_ALL_STUDENT_SCORES); return new CommandResult(MESSAGE_SUCCESS); } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ListAllCommand)) { + return false; + } + return true; + } } diff --git a/src/test/java/seedu/modulight/logic/commands/ListAllCommandTest.java b/src/test/java/seedu/modulight/logic/commands/ListAllCommandTest.java new file mode 100644 index 00000000000..8bb95dbe23f --- /dev/null +++ b/src/test/java/seedu/modulight/logic/commands/ListAllCommandTest.java @@ -0,0 +1,55 @@ +package seedu.modulight.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.modulight.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.modulight.testutil.TypicalStudents.getTypicalComponentBook; +import static seedu.modulight.testutil.TypicalStudents.getTypicalScoreBook; +import static seedu.modulight.testutil.TypicalStudents.getTypicalStudentBook; + +import org.junit.jupiter.api.Test; + +import seedu.modulight.model.Model; +import seedu.modulight.model.ModelManager; +import seedu.modulight.model.UserPrefs; + +public class ListAllCommandTest { + private Model model = new ModelManager(getTypicalStudentBook(), getTypicalScoreBook(), getTypicalComponentBook(), + new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalStudentBook(), getTypicalScoreBook(), + getTypicalComponentBook(), new UserPrefs()); + @Test + public void excute_nonFiltered_success() { + ListAllCommand command = new ListAllCommand(); + String expectedMessage = "Listed all students, student scores and graded components"; + assertCommandSuccess(command, model, expectedMessage, expectedModel); + } + + @Test + public void excute_filtered_success() { + ListAllCommand command = new ListAllCommand(); + model.updateFilteredStudentList(Model.PREDICATE_SHOW_NO_STUDENTS); + model.updateFilteredGradedComponentList(Model.PREDICATE_SHOW_NO_COMPONENT); + String expectedMessage = "Listed all students, student scores and graded components"; + assertCommandSuccess(command, model, expectedMessage, expectedModel); + } + + @Test + public void equals() { + + ListAllCommand firstCommand = new ListAllCommand(); + ListAllCommand secondCommand = new ListAllCommand(); + + // same object -> returns true + assertTrue(firstCommand.equals(firstCommand)); + + // same type -> returns true + assertTrue(firstCommand.equals(secondCommand)); + + // different types -> returns false + assertFalse(firstCommand.equals(1)); + + // null -> returns false + assertFalse(firstCommand.equals(null)); + } +} diff --git a/src/test/java/seedu/modulight/logic/parser/ListAllCommandParserTest.java b/src/test/java/seedu/modulight/logic/parser/ListAllCommandParserTest.java new file mode 100644 index 00000000000..c3ae8724c48 --- /dev/null +++ b/src/test/java/seedu/modulight/logic/parser/ListAllCommandParserTest.java @@ -0,0 +1,22 @@ +package seedu.modulight.logic.parser; + +import static seedu.modulight.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.modulight.logic.commands.ListAllCommand; + +public class ListAllCommandParserTest { + private ListAllCommandParser parser = new ListAllCommandParser(); + @Test + public void parse_noInput_success() { + ListAllCommand expectedCommand = new ListAllCommand(); + assertParseSuccess(parser, " ", expectedCommand); + } + + @Test + public void parse_withInput_success() { + ListAllCommand expectedCommand = new ListAllCommand(); + assertParseSuccess(parser, " t/t01 ", expectedCommand); + } +} From 948878c38f7f7f79774d2c9903b95b178d31d5c5 Mon Sep 17 00:00:00 2001 From: seraphimstreets Date: Mon, 13 Nov 2023 20:58:01 +0800 Subject: [PATCH 13/28] Add edit no parameter check --- src/main/java/seedu/modulight/MainApp.java | 4 ++-- .../modulight/logic/commands/DeleteStudentCommand.java | 1 - .../modulight/logic/commands/EditStudentScoreCommand.java | 7 +++++++ .../logic/parser/EditGradedComponentCommandParser.java | 5 +++++ .../logic/parser/EditStudentScoreCommandParser.java | 5 +++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/modulight/MainApp.java b/src/main/java/seedu/modulight/MainApp.java index 9becc1d6d5c..e03c164bc6e 100644 --- a/src/main/java/seedu/modulight/MainApp.java +++ b/src/main/java/seedu/modulight/MainApp.java @@ -101,7 +101,7 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { Optional studentBookOptional; Optional studentScoreBookOptional; Optional gradedComponentBookOptional; - ReadOnlyStudentBook initialStudentData = new StudentBook(); + ReadOnlyStudentBook initialStudentData; ReadOnlyStudentScoreBook initialStudentScoreData; ReadOnlyGradedComponentBook initialGradedComponentData; try { @@ -128,7 +128,7 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { .orElseGet(SampleGcDataUtil::getSampleGcBook); checkBookValidity(initialStudentData, initialGradedComponentData, initialStudentScoreData); - } catch (DataLoadingException e) { + } catch (DataLoadingException | RuntimeException e) { logger.warning("Data file at " + storage.getStudentBookFilePath() + " could not be loaded." + " Will be starting with an empty AddressBook."); initialStudentData = new StudentBook(); diff --git a/src/main/java/seedu/modulight/logic/commands/DeleteStudentCommand.java b/src/main/java/seedu/modulight/logic/commands/DeleteStudentCommand.java index fb191fdb34a..9f24e69c8db 100644 --- a/src/main/java/seedu/modulight/logic/commands/DeleteStudentCommand.java +++ b/src/main/java/seedu/modulight/logic/commands/DeleteStudentCommand.java @@ -54,7 +54,6 @@ public CommandResult execute(Model model) throws CommandException { for (int i = studentScoreList.size() - 1; i >= 0; i--) { StudentScore curScore = studentScoreList.get(i); if (curScore.getStudentId().equals(studentToDelete.getStudentId())) { - // somewhat inefficient, to change GradedComponent gc = gradedComponentBook.getGradedComponentByName(curScore.getGcName()); gc.deleteScore(curScore); gradedComponentBook.setGradedComponent(gc, gc); diff --git a/src/main/java/seedu/modulight/logic/commands/EditStudentScoreCommand.java b/src/main/java/seedu/modulight/logic/commands/EditStudentScoreCommand.java index 5f0d5804ce3..e31d682fbbc 100644 --- a/src/main/java/seedu/modulight/logic/commands/EditStudentScoreCommand.java +++ b/src/main/java/seedu/modulight/logic/commands/EditStudentScoreCommand.java @@ -12,6 +12,7 @@ import java.util.Set; import seedu.modulight.commons.core.index.Index; +import seedu.modulight.commons.util.CollectionUtil; import seedu.modulight.commons.util.ToStringBuilder; import seedu.modulight.logic.Messages; import seedu.modulight.logic.commands.exceptions.CommandException; @@ -200,6 +201,12 @@ public EditStudentScoreDescriptor(EditStudentScoreDescriptor toCopy) { setComment(toCopy.comment); setTags(toCopy.tags); } + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(score, comment, tags); + } public void setStudentId(StudentId sid) { this.sid = sid; diff --git a/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java b/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java index 14fb2a8c3c6..e176c4be8ce 100644 --- a/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java +++ b/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java @@ -13,6 +13,7 @@ import seedu.modulight.commons.core.index.Index; import seedu.modulight.logic.commands.EditGradedComponentCommand; import seedu.modulight.logic.commands.EditGradedComponentCommand.EditGradedComponentDescriptor; +import seedu.modulight.logic.commands.EditStudentCommand; import seedu.modulight.logic.parser.exceptions.ParseException; import seedu.modulight.model.tag.Tag; @@ -61,6 +62,10 @@ public EditGradedComponentCommand parse(String args) throws ParseException { throw new ParseException(e.getMessage()); } + if (!editGradedComponentDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditGradedComponentCommand.MESSAGE_NOT_EDITED); + } + return new EditGradedComponentCommand(index, editGradedComponentDescriptor); } diff --git a/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java b/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java index 9465ccc2492..9dced78a637 100644 --- a/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java +++ b/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java @@ -11,6 +11,7 @@ import java.util.Set; import seedu.modulight.commons.core.index.Index; +import seedu.modulight.logic.commands.EditStudentCommand; import seedu.modulight.logic.commands.EditStudentScoreCommand; import seedu.modulight.logic.parser.exceptions.ParseException; import seedu.modulight.model.tag.Tag; @@ -60,6 +61,10 @@ public EditStudentScoreCommand parse(String args) throws ParseException { throw new ParseException(e.getMessage()); } + if (!editStudentScoreDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditStudentScoreCommand.MESSAGE_NOT_EDITED); + } + return new EditStudentScoreCommand(index, editStudentScoreDescriptor, true); } From cd0319926d4a0101b98ec085156e687642da0889 Mon Sep 17 00:00:00 2001 From: seraphimstreets Date: Mon, 13 Nov 2023 21:05:37 +0800 Subject: [PATCH 14/28] Remove unused imports --- docs/UserGuide.md | 6 +++--- .../logic/parser/EditGradedComponentCommandParser.java | 1 - .../logic/parser/EditStudentScoreCommandParser.java | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 15f1954b4ac..4dca6671153 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -226,7 +226,7 @@ Examples: ### Add a graded component: `addComp` Adds a graded component to the database. If successful, an acknowledgement message will be shown in the output box and data is saved. Otherwise, a failure message is shown instead specifying the cause of failure. -Upon successful creation of a graded component, a corresponding student score will be created for each student in the database. For instance, if a graded component with name “Midterms” is created and there are two students with student numbers “A1234567X” and “A1234567Y” in the database, then two student scores are created with titles “A1234567X - Midterm” and “A1234567Y - Midterm”. +Upon successful creation of a graded component, a corresponding student score will be created for each student in the database. For instance, if a graded component with name “Midterms” is created and there are two students with student numbers `A1234567X` and `A1234567Y` in the database, then two student scores are created with titles `A1234567X - Midterm` and `A1234567Y - Midterm`. * When adding the component, you must ensure that the total weightage of all components does not exceed 100. * Weightage is a relative value calculated relative to the sum of all other weightage values. For more details, view [the notes on score calculations](#notes-on-score-calculation). @@ -301,8 +301,8 @@ criteria. * For searching with student parameters of different types, it will find the students who satisfy all the criteria. * For searching with multiple student parameters of different types, it will find the students who satisfy at least one criterion for each type. -* If a input of the incorrect format is given, there might be no students found. For example, if you search findStu -s/A00000Y, no students will be found since this is not a substring of any valid student number. +* If a input of the incorrect format is given, there might be no students found. For example, if you search `findStu +s/A00000Y`, no students will be found since this is not a substring of any valid student number. Examples: * `findStu n/Alice n/Bob g/T01` returns the data of the students whose name contains 'Alice' or 'Bob' (case-insensitive) diff --git a/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java b/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java index e176c4be8ce..22049fe835f 100644 --- a/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java +++ b/src/main/java/seedu/modulight/logic/parser/EditGradedComponentCommandParser.java @@ -13,7 +13,6 @@ import seedu.modulight.commons.core.index.Index; import seedu.modulight.logic.commands.EditGradedComponentCommand; import seedu.modulight.logic.commands.EditGradedComponentCommand.EditGradedComponentDescriptor; -import seedu.modulight.logic.commands.EditStudentCommand; import seedu.modulight.logic.parser.exceptions.ParseException; import seedu.modulight.model.tag.Tag; diff --git a/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java b/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java index 9dced78a637..e34a5d15770 100644 --- a/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java +++ b/src/main/java/seedu/modulight/logic/parser/EditStudentScoreCommandParser.java @@ -11,7 +11,6 @@ import java.util.Set; import seedu.modulight.commons.core.index.Index; -import seedu.modulight.logic.commands.EditStudentCommand; import seedu.modulight.logic.commands.EditStudentScoreCommand; import seedu.modulight.logic.parser.exceptions.ParseException; import seedu.modulight.model.tag.Tag; From 050ac726deb3d9e7950c0642d9764cecb5c137a4 Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Mon, 13 Nov 2023 22:15:46 +0800 Subject: [PATCH 15/28] Add implementations of sort command --- docs/DeveloperGuide.md | 34 +++++++++ docs/UserGuide.md | 8 +- docs/diagrams/SortScoreAcitivityDiagram.png | Bin 0 -> 16597 bytes docs/diagrams/SortScoreAcitivityDiagram.puml | 18 +++++ .../SortScoreCommandSequenceDiagram.png | Bin 0 -> 41364 bytes .../SortScoreCommandSequenceDiagram.puml | 69 ++++++++++++++++++ 6 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 docs/diagrams/SortScoreAcitivityDiagram.png create mode 100644 docs/diagrams/SortScoreAcitivityDiagram.puml create mode 100644 docs/diagrams/SortScoreCommandSequenceDiagram.png create mode 100644 docs/diagrams/SortScoreCommandSequenceDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index e32f67d14af..8ba5ee576c5 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -185,6 +185,40 @@ Classes used by multiple components are in the `seedu.modulight.commons` package This section describes some noteworthy details on how certain features are implemented. +### Sort Commands + +The Sort related feature allows NUS professors to sort the currently displayed students or student scores. When successfully executed, the sorted students or student scores will be shown on the Graphical User Interface.
+ +We will discuss the implementation of sortScore (Sort Student Scores) command here and omit the discussion of the implementation of sortStu command since it is very similar to sortScore command and simpler. + +#### Implementation + +The `sortScoreCommand` (Sort Student Scores) mechanism is facilitated by GradedComponentBook, StudentBook and StudentScoreBook. It implements the following operations: + +* `GradedComponentBook#hasGc(GcName gcName)` - Returns true if a graded component is already created and in the graded component list. +* `studentScoreBook.sortStudentScore(Boolean isReverse)` - Filters the student scores with the given graded component and sort them according to the given reverse order. +* `studentBook.sortStudentScore(GcName gcName, Boolean isReverse)` - Sorts students by their scores in a given graded component. + +Given below is an example usage scenario and how the `sortScore` mechanism behaves at each step. + +Step 1. The GUI displayed the list of students and their student scores that the user wants to sort after some `find` or `list` commands. + +Step 2. The user executes `sortStuScore c/Midterm` command to sort the current displayed lists of students and student scores. The `sortStuScore` command calls SortStudentScoreCommandParser#parse() which parses the string keyed into the command line of the GUI. + +Step 3. `SortStudentScoreCommandParser#parse()` invokes the creation of a `SortStudentScoreCommand` object. +> **Note**: If a command fails its execution due to incorrect command format, it will not create a `SortStudentScoreCommand` object, an error message will be displayed and user will retype their command. + +Step 4. Upon creation and execution of `SortStudentScoreCommand` object, `GradedComponentBook#hasGc(GcName gcName)`, `studentScoreBook.sortStudentScore(Boolean isReverse)` and `studentBook.sortStudentScore(GcName gcName, Boolean isReverse)` methods are called. +> **Note**: If upon invoking `GradedComponentBook#hasGc(GcName gcName)` method and return value is false, it will throw an error and will not call the remaining two methods, so the students and student scores will not be sorted. + +Step 5. After successfully sorting student scores and their associated students, a `CommandResult` object will be created to tell the user that the student scores has been successfully sorted. + +The following sequence diagram shows how the sort student scores operation works:
+ + + +The following activity diagram summarizes what happens when a user executes a new sortScore command:
+ ### Auto-grading diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d4545eb845e..d9f538b1662 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -358,7 +358,8 @@ keywords have the same effect). * It is allowed to omit sorting order and reverse order. In this case, the default sorting order is by total score while the default reverse order is false (i.e. increasing). * This command will only sort the currently displayed students. If you want to sort all students, please use `listStu` -command in advance. +command in advance. If there is no currently displayed student, the command can still execute successfully, but the list of +students will remain unchanged. Examples: * `sortStu o/name r/true` returns the sorted students whose names are in descending alphabetical order. @@ -376,7 +377,8 @@ Format: `sortScore c/COMP_NAME [r/REVERSE_ORDER]` keywords have the same effect). * It is allowed to omit reverse order. In this case, the default reverse order is false (i.e. increasing). * This command will only sort the currently displayed students. If you want to sort all students, please use `listAll` - command in advance. + command in advance. If there is no currently displayed student, the command can still execute successfully, but the lists of + students and student scores will remain unchanged. Examples: * `sortScore c/Midterm r/true` returns the sorted students whose midterm scores are in descending order. @@ -509,7 +511,7 @@ There is no need to manually load data stored on the hard disc. It will be avail | **Delete a student** | `deleteStu INDEX`
e.g., `deleteStu 2` | | **Delete a graded component** | `deleteComp INDEX`
e.g., `deleteComp 1` | | **Delete everything** | `clearAll` | -| **Find a student** | `findStu [s/STUDENT_NO] [n/NAME] [e/EMAIL] [g/TUTORIAL_GRP] [t/TAG]`
e.g., `findStu n/Alice n/Bob g/T01` | +| **Find a student** | `findStu [s/STUDENT_NO...] [n/NAME...] [e/EMAIL...] [g/TUTORIAL_GRP...] [t/TAG...]`
e.g., `findStu n/Alice n/Bob g/T01` | | **Find a graded component** | `findComp c/COMP_NAME`
e.g., `findComp c/Midterms` | | **Find a student score** | `findScore [s/STUDENT_NO] [n/NAME] [e/EMAIL] [g/TUTORIAL_GRP] [c/COMP_NAME][x/comments][t/tags]...`
e.g., `findScore c/Midterms` | | **List all students, scores and graded components** | `listAll` | diff --git a/docs/diagrams/SortScoreAcitivityDiagram.png b/docs/diagrams/SortScoreAcitivityDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..94fecbb7d4edf5ab0d97ea2b547802f8f49f1c03 GIT binary patch literal 16597 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 O9>}Z7y}N4~^nU>8hDoph literal 0 HcmV?d00001 diff --git a/docs/diagrams/SortScoreAcitivityDiagram.puml b/docs/diagrams/SortScoreAcitivityDiagram.puml new file mode 100644 index 00000000000..c42ef4c9994 --- /dev/null +++ b/docs/diagrams/SortScoreAcitivityDiagram.puml @@ -0,0 +1,18 @@ +@startuml +'https://plantuml.com/activity-diagram-beta + +start +:User executes sortScore command; +if () then ([given graded component exists]) + :student scores filtered and sorted; + :students sorted accordingly; +else ([else]) + :a error message shows up; + :user retypes the command; +endif + +:Updated students and student scores list saved to ModuLight; + +stop + +@enduml \ No newline at end of file diff --git a/docs/diagrams/SortScoreCommandSequenceDiagram.png b/docs/diagrams/SortScoreCommandSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..bedf502c52baa9a085964d27c98110ff80f15d53 GIT binary patch literal 41364 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 nD+zyUoN+*#68;R$ZR|d}{|-IoV!k!u|K+3 LogicManager : execute(sortScore c/Midterm) +activate LogicManager + +LogicManager -> ModuLightParser : parseCommand(sortScore c/Midterm) +activate ModuLightParser + +create SortStudentScoreCommandParser +ModuLightParser -> SortStudentScoreCommandParser :SortStudentScoreCommandParser() +activate SortStudentScoreCommandParser + +SortStudentScoreCommandParser --> ModuLightParser +deactivate SortStudentScoreCommandParser + +ModuLightParser -> SortStudentScoreCommandParser : parse(" c/Midterm") +activate SortStudentScoreCommandParser + +create SortStudentScoreCommand +SortStudentScoreCommandParser -> SortStudentScoreCommand +activate SortStudentScoreCommand + +SortStudentScoreCommand --> SortStudentScoreCommandParser : s +deactivate SortStudentScoreCommand + +SortStudentScoreCommandParser --> ModuLightParser : s +deactivate SortStudentScoreCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +SortStudentScoreCommandParser -[hidden]-> ModuLightParser +destroy SortStudentScoreCommandParser + +ModuLightParser --> LogicManager : s +deactivate ModuLightParser + +LogicManager -> SortStudentScoreCommand : execute() +activate SortStudentScoreCommand + +SortStudentScoreCommand -> Model : hasGc(GcName gcName) +activate Model + +Model --> SortStudentScoreCommand +deactivate Model + +SortStudentScoreCommand -> Model : sortStudentScore(GcName gcName, Boolean isReverse) +activate Model + + +Model --> SortStudentScoreCommand +deactivate Model + +SortStudentScoreCommand --> LogicManager : result +deactivate SortStudentScoreCommand +SortStudentScoreCommand -[hidden]-> LogicManager : result +destroy SortStudentScoreCommand + +[<--LogicManager +deactivate LogicManager +@enduml \ No newline at end of file From 9d5c0bb760d9ce698b6924ceef71ca2b902d8b21 Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Mon, 13 Nov 2023 22:18:19 +0800 Subject: [PATCH 16/28] Fix some checkstyle issues --- docs/diagrams/SortScoreAcitivityDiagram.puml | 2 +- docs/diagrams/SortScoreCommandSequenceDiagram.puml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/diagrams/SortScoreAcitivityDiagram.puml b/docs/diagrams/SortScoreAcitivityDiagram.puml index c42ef4c9994..72c56442ae3 100644 --- a/docs/diagrams/SortScoreAcitivityDiagram.puml +++ b/docs/diagrams/SortScoreAcitivityDiagram.puml @@ -15,4 +15,4 @@ endif stop -@enduml \ No newline at end of file +@enduml diff --git a/docs/diagrams/SortScoreCommandSequenceDiagram.puml b/docs/diagrams/SortScoreCommandSequenceDiagram.puml index d705be010e9..b157714262b 100644 --- a/docs/diagrams/SortScoreCommandSequenceDiagram.puml +++ b/docs/diagrams/SortScoreCommandSequenceDiagram.puml @@ -66,4 +66,4 @@ destroy SortStudentScoreCommand [<--LogicManager deactivate LogicManager -@enduml \ No newline at end of file +@enduml From f7a7744bb934bbc5a609ec9cc864d38e341c28f0 Mon Sep 17 00:00:00 2001 From: feifeiraindrops Date: Mon, 13 Nov 2023 22:24:38 +0800 Subject: [PATCH 17/28] Update implementation in DG --- docs/DeveloperGuide.md | 35 ++++++++++++- docs/diagrams/AddStudent.png | Bin 0 -> 29629 bytes docs/diagrams/AddStudent.puml | 24 +++++++++ docs/diagrams/EditStudent.png | Bin 0 -> 38896 bytes docs/diagrams/EditStudent.puml | 28 ++++++++++ docs/diagrams/FindStudent.png | Bin 0 -> 59954 bytes docs/diagrams/FindStudent.puml | 91 +++++++++++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 docs/diagrams/AddStudent.png create mode 100644 docs/diagrams/AddStudent.puml create mode 100644 docs/diagrams/EditStudent.png create mode 100644 docs/diagrams/EditStudent.puml create mode 100644 docs/diagrams/FindStudent.png create mode 100644 docs/diagrams/FindStudent.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index e32f67d14af..c1ce1e8b877 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -185,13 +185,44 @@ Classes used by multiple components are in the `seedu.modulight.commons` package This section describes some noteworthy details on how certain features are implemented. +### Add Student +The `addStu` function allows the user to add a new student to the database. ModuLight maintains a `UniqueStudentList` +to make sure that there is no duplicates. +The new `Student` object will be added to the `StudentBook`. An empty `StudentScore` related to this `Student` will be added to all existing `GradedComponent`. + +The student can only be added if the user entered valid inputs for its name, student id, email, tutorial group(optional) and tag(optional). A default `TutorialGroup` with value T00 will be assigned to the student if the user did not assign the student to a tutorial group. + +The following activity diagram illustrates the process of execution of an `AddStudentCommand`. +![AddStudentCommand](diagrams/AddStudent.png) + +### Edit Student +The `editStu` function allows the user to edit the information of the student indicated by index. +The previous `Student` object will be removed from the `StudentBook`. A new student object with the edited information will be added to the database. +All student scores related to this `Student` will be updated as well. + +The student can only be edited if the user entered valid inputs for its name, student id, email, tutorial group(optional) and tag(optional). + +The following activity diagram illustrates the process of execution of an `EditStudentCommand`. +![AddStudentCommand](diagrams/EditStudent.png) + +### Find Student +The `findStu` function allows the user to find the student that matches the given search criteria. + +It displays both the matching students and relevant student scores that belongs to the student. +All graded components are displayed since they are considered relevant to the student. + +To find the wanted student, a `StudentMatchPredicate` is created to test whether a student matches the search keywords. A `ScoreMatchPredicate` is created from the `StudentMatchPredicate` to test whether the score belongs to the matched student. + +This only changes the displayed list of students and student scores, stored as `filteredStudentList` and `filteredStudentScoreList` in `Model`, without affecting the data stored in ModuLight. + +The following sequence diagram illustrates the process of execution of an `FindStudentCommand`. + +![AddStudentCommand](diagrams/FindStudent.png) ### Auto-grading #### Proposed Implementation -{Intro} - Given below is an example usage scenario and how the Autograding mechanism for percentile calculation behaves at each step. Step 1. The user launch the application for the first time. diff --git a/docs/diagrams/AddStudent.png b/docs/diagrams/AddStudent.png new file mode 100644 index 0000000000000000000000000000000000000000..d7956466101e346207cde1c3dc96acca8fd22776 GIT binary patch literal 29629 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

-U=8fC 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/EditStudent.puml b/docs/diagrams/EditStudent.puml new file mode 100644 index 00000000000..67f492e99fb --- /dev/null +++ b/docs/diagrams/EditStudent.puml @@ -0,0 +1,28 @@ +@startuml +'https://plantuml.com/activity-diagram + +start +if () then ([else]) +if () then ([else]) + :Retrieve student to edit; + :Create edited student; + if () then ([else]) + :Replace student to edit with edited student in the student book; + if () then ([else]) + else ([there are score related to the student]) + :Edit all scores related to the student; + endif + : Display acknowledge message to user with information of edited student; + else ([edited student already exists in student book]) + :Throw CommandException; + endif +else (given student parameters are invalid) + :Throw ParseException; +endif +else (given index is invalid or out of range) + :Throw ParseException; +endif + +stop + +@enduml \ No newline at end of file diff --git a/docs/diagrams/FindStudent.png b/docs/diagrams/FindStudent.png new file mode 100644 index 0000000000000000000000000000000000000000..f652cdbca6952a9f14a443c4f097bafa2defaf7a GIT binary patch literal 59954 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/FindStudent.puml b/docs/diagrams/FindStudent.puml new file mode 100644 index 00000000000..7ca700736cb --- /dev/null +++ b/docs/diagrams/FindStudent.puml @@ -0,0 +1,91 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLightParser" as ModuLightParser LOGIC_COLOR +participant ":FindStudentCommandParser" as FindStudentCommandParser LOGIC_COLOR +participant "sp:StudentMatchPredicate" as StudentMatchPredicate LOGIC_COLOR +participant "scp:ScoreMatchPredicate" as ScoreMatchPredicate LOGIC_COLOR +participant "d:FindStudentCommand" as FindStudentCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("findStu g/t01") +activate LogicManager + +LogicManager -> ModuLightParser : parseCommand("findStu g/t01") +activate ModuLightParser + +create FindStudentCommandParser +ModuLightParser -> FindStudentCommandParser +activate FindStudentCommandParser + +FindStudentCommandParser --> ModuLightParser +deactivate FindStudentCommandParser + +ModuLightParser -> FindStudentCommandParser : parse(" g/t01") +activate FindStudentCommandParser + +create StudentMatchPredicate +FindStudentCommandParser -> StudentMatchPredicate +activate StudentMatchPredicate + +StudentMatchPredicate --> FindStudentCommandParser : sp +deactivate StudentMatchPredicate + +create ScoreMatchPredicate +FindStudentCommandParser -> ScoreMatchPredicate : ScoreMatchPredicate(sp) +activate ScoreMatchPredicate + +ScoreMatchPredicate --> FindStudentCommandParser : scp +deactivate ScoreMatchPredicate + +create FindStudentCommand +FindStudentCommandParser -> FindStudentCommand : FindStudentCommand(sp, scp) +activate FindStudentCommand + +FindStudentCommand --> FindStudentCommandParser : d +deactivate FindStudentCommand + +FindStudentCommandParser --> ModuLightParser : d +deactivate FindStudentCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindStudentCommandParser -[hidden]-> ModuLightParser +destroy FindStudentCommandParser + +ModuLightParser --> LogicManager : d +deactivate ModuLightParser + +LogicManager -> FindStudentCommand : execute() +activate FindStudentCommand + +FindStudentCommand -> Model : updateFilteredStudentList(sp) +activate Model + +Model --> FindStudentCommand +deactivate Model + +FindStudentCommand -> Model : updateFilteredStudentScoreList(scp) +activate Model + +Model --> FindStudentCommand +deactivate Model + +create CommandResult +FindStudentCommand -> CommandResult +activate CommandResult + +CommandResult --> FindStudentCommand +deactivate CommandResult + +FindStudentCommand --> LogicManager : result +deactivate FindStudentCommand + +[<--LogicManager +deactivate LogicManager +@enduml \ No newline at end of file From 62b8ca60435661126f1fdd1a91bede54b38087ae Mon Sep 17 00:00:00 2001 From: feifeiraindrops Date: Mon, 13 Nov 2023 22:55:08 +0800 Subject: [PATCH 18/28] Edit dg and fix bugs --- docs/DeveloperGuide.md | 36 +++++++++++++++++-- docs/UserGuide.md | 2 +- .../commands/FindGradedComponentCommand.java | 1 + .../logic/commands/FindStudentCommand.java | 1 + .../modulight/model/student/Student.java | 2 +- 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index c1ce1e8b877..53cf49f842c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -393,14 +393,15 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli | `* *` | NUS professor | edit a student | update outdated student information or correct mistakes. | | `* *` | NUS professor | edit a graded component | make changes to a component (eg. modify weightage) or correct mistakes. | | `* *` | NUS professor | edit a student score | regrade student scripts or correct mistakes. | -| `* *` | NUS professor | find student and associated scores by ID | quickly find information about a student and their scores without having to search through the list | -| `* *` | NUS professor | find graded component and associated scores by ID | quickly find information about a graded component and student scores without having to search through the list | +| `* *` | NUS professor | find student and associated scores | quickly find information about a student and their scores without having to search through the list | +| `* *` | NUS professor | find graded component and associated scores | quickly find information about a graded component and student scores without having to search through the list | +| `* *` | NUS professor | list all student, student scores and graded components | remove the filters that has been applied to the lists | | `* *` | NUS professor | quickly calculate the overall statistics of student grades | have a quick insight of how my students are performing | | `* *` | NUS professor | sort students with specific order | find the top students easily | | `* *` | NUS professor | sort student scores with specific order | find the top students with their associated scores easily | | | NUS professor | **autograde** | | | `*` | NUS professor | toggle between dark and light mode | have a pleasant user experience. | -*{More to be added}* + ### Use cases @@ -626,6 +627,22 @@ testers are expected to do more *exploratory* testing. 1. _{ more test cases …​ }_ +### Adding a student +1. Adding a student to Modulight + 1. Test case: `addStu n/John Doe s/A1234567Y e/john@gmail.com g/T07` + Expected: If there is already a person with the same student ID in ModuLight an error message will appear in the feedback box. + Otherwise, a new student with name `John Doe`, student id `A1234567Y`, email `john@gmail.com` and tutorial group `T07` will be created and displayed in the student list. + If there exists graded component in the graded component list, new student scores that belongs to this student will be added. + + 1. Test case: `addStu n/Jane Plain s/A1111111Y e/jane@gmail.com` + Expected: If there is already a person with the same student ID in ModuLight an error message will appear in the feedback box. + Otherwise, a new student with name `Jane Plain`, student id `A1111111Y`, email `jane@gmail.com` and default tutorial group `T00` will be created and displayed in the student list. + If there exists graded component in the graded component list, new student scores that belongs to this student will be added. + + 1. Test case: `addStu n/Amy e/amy@gamil.com` + Expected: An error message of Invalid command format will be displayed in the feedback box, as the student id parameter is missing. + + ### Deleting a student 1. Deleting a student while all students are being shown @@ -643,6 +660,19 @@ testers are expected to do more *exploratory* testing. 1. _{ more test cases …​ }_ +### Finding a student +1. Find a student in ModuLight + 1. Prerequisite: student list is not empty. + 1. Test case: `findStu g/T00` + Expected: All students from tutorial group `T00` will be displayed. All graded components and all scores related to the displayed students should be displayed. + 1. Test case: `findStu` + Expected: Since there is no search words given, all students, student scores and graded components will be displayed. + 2. Test case: `findStu n/John n/Amy` + Expected: All students whose name contains `John` or `Amy` (case-insensitive) will be displayed. All graded components and all scores related to the displayed students should be displayed. + 3. Test case: `findStu n/John g/T00` + Expected: All students whose name contains `John` (case-insensitive) and is from `T00` will be displayed. All graded components and all scores related to the displayed students should be displayed. + + ### Saving data 1. Dealing with missing files diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d4545eb845e..2c70c2364ed 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -303,7 +303,7 @@ Examples: `deleteComp 2` deletes the second graded component in the displayed Gr > **Note**
> -> * All find commands are case-insensitive +> * All find parameters are case-insensitive, except for tag > * It is allowed to have 0 searching criteria. In this case, this command will simply list all objects. > * For searching with multiple student parameters of the same type, it will find the objects who satisfy any of the criteria. diff --git a/src/main/java/seedu/modulight/logic/commands/FindGradedComponentCommand.java b/src/main/java/seedu/modulight/logic/commands/FindGradedComponentCommand.java index be590dfb96c..2a1caab7602 100644 --- a/src/main/java/seedu/modulight/logic/commands/FindGradedComponentCommand.java +++ b/src/main/java/seedu/modulight/logic/commands/FindGradedComponentCommand.java @@ -38,6 +38,7 @@ public FindGradedComponentCommand(GcMatchPredicate gcPredicate, ScoreMatchPredic @Override public CommandResult execute(Model model) { requireNonNull(model); + model.updateFilteredStudentList(Model.PREDICATE_SHOW_ALL_STUDENTS); model.updateFilteredGradedComponentList(gcPredicate); model.updateFilteredStudentScoreList(scorePredicate); String feedback = String.format(Messages.MESSAGE_COMP_LISTED_OVERVIEW, diff --git a/src/main/java/seedu/modulight/logic/commands/FindStudentCommand.java b/src/main/java/seedu/modulight/logic/commands/FindStudentCommand.java index 4d0a614af89..c0ed3999722 100644 --- a/src/main/java/seedu/modulight/logic/commands/FindStudentCommand.java +++ b/src/main/java/seedu/modulight/logic/commands/FindStudentCommand.java @@ -48,6 +48,7 @@ public FindStudentCommand(StudentMatchPredicate studentPredicate, ScoreMatchPred @Override public CommandResult execute(Model model) { requireNonNull(model); + model.updateFilteredGradedComponentList(Model.PREDICATE_SHOW_ALL_GRADED_COMPONENTS); model.updateFilteredStudentList(studentPredicate); model.updateFilteredStudentScoreList(scorePredicate); String feedback = String.format(Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW, diff --git a/src/main/java/seedu/modulight/model/student/Student.java b/src/main/java/seedu/modulight/model/student/Student.java index 93d89b1957c..f07f8d362b5 100644 --- a/src/main/java/seedu/modulight/model/student/Student.java +++ b/src/main/java/seedu/modulight/model/student/Student.java @@ -141,7 +141,7 @@ public boolean isSameStudent(Student otherStudent) { } return otherStudent != null - && otherStudent.getStudentId().equals(getStudentId()) && otherStudent.getName().equals(getName()); + && otherStudent.getStudentId().equals(getStudentId()); } /** * Returns true if both students have the same identity and data fields. From 4939afa2de01ea8736eb1307df7df27c52bb15a6 Mon Sep 17 00:00:00 2001 From: feifeiraindrops Date: Mon, 13 Nov 2023 22:57:44 +0800 Subject: [PATCH 19/28] Update student test --- .../modulight/model/student/StudentTest.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/test/java/seedu/modulight/model/student/StudentTest.java b/src/test/java/seedu/modulight/model/student/StudentTest.java index e83ceaa85b5..9ec03080664 100644 --- a/src/test/java/seedu/modulight/model/student/StudentTest.java +++ b/src/test/java/seedu/modulight/model/student/StudentTest.java @@ -38,25 +38,16 @@ public void isSameStudent() { // null -> returns false assertFalse(ALICE.isSameStudent(null)); - // same name and sid, all other attributes different -> returns true + // same sid, all other attributes different -> returns true StudentScore editedAmyScore = new StudentScoreBuilder(VALID_STUDENT_SCORE_JAMES) .withStudentId(VALID_SID_AMY).build(); - Student editedAlice = new StudentBuilder(ALICE).withEmail(VALID_EMAIL_JAMES) + Student editedAlice = new StudentBuilder(ALICE).withEmail(VALID_EMAIL_JAMES).withName(VALID_NAME_JAMES) .withTg(VALID_TG_JAMES).withTags(VALID_TAG_TA).withScore(editedAmyScore).build(); assertTrue(ALICE.isSameStudent(editedAlice)); - // different name and sid, all other attributes same -> returns false - editedAlice = new StudentBuilder(ALICE).withName(VALID_NAME_JAMES).withId(VALID_SID_JAMES).build(); + // different sid, all other attributes same -> returns false + editedAlice = new StudentBuilder(ALICE).withId(VALID_SID_JAMES).build(); assertFalse(ALICE.isSameStudent(editedAlice)); - - // name differs in case, all other attributes same -> returns false - Student editedJames = new StudentBuilder(JAMES).withName(VALID_NAME_JAMES.toLowerCase()).build(); - assertFalse(JAMES.isSameStudent(editedJames)); - - // name has trailing spaces, all other attributes same -> returns false - String nameWithTrailingSpaces = VALID_NAME_JAMES + " "; - editedJames = new StudentBuilder(JAMES).withName(nameWithTrailingSpaces).build(); - assertFalse(JAMES.isSameStudent(editedJames)); } @Test From bf80ea2d0b23f4434658ecafafc8c3bdc5d91568 Mon Sep 17 00:00:00 2001 From: feifeiraindrops Date: Mon, 13 Nov 2023 23:08:10 +0800 Subject: [PATCH 20/28] Fix checkstyle --- docs/diagrams/AddStudent.puml | 2 +- docs/diagrams/EditStudent.puml | 2 +- docs/diagrams/FindStudent.puml | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/diagrams/AddStudent.puml b/docs/diagrams/AddStudent.puml index 95cd0bd09c7..1d7a199f5b7 100644 --- a/docs/diagrams/AddStudent.puml +++ b/docs/diagrams/AddStudent.puml @@ -21,4 +21,4 @@ endif stop -@enduml \ No newline at end of file +@enduml diff --git a/docs/diagrams/EditStudent.puml b/docs/diagrams/EditStudent.puml index 67f492e99fb..2edf6aee134 100644 --- a/docs/diagrams/EditStudent.puml +++ b/docs/diagrams/EditStudent.puml @@ -25,4 +25,4 @@ endif stop -@enduml \ No newline at end of file +@enduml diff --git a/docs/diagrams/FindStudent.puml b/docs/diagrams/FindStudent.puml index 7ca700736cb..3af7d5511b6 100644 --- a/docs/diagrams/FindStudent.puml +++ b/docs/diagrams/FindStudent.puml @@ -1,4 +1,5 @@ @startuml + !include style.puml skinparam ArrowFontStyle plain box Logic LOGIC_COLOR_T1 @@ -88,4 +89,4 @@ deactivate FindStudentCommand [<--LogicManager deactivate LogicManager -@enduml \ No newline at end of file +@enduml From e43f36ee30925772a78c68ecbcf9e414235972e2 Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Tue, 14 Nov 2023 00:38:30 +0800 Subject: [PATCH 21/28] Add implementation of stats to DG --- docs/DeveloperGuide.md | 44 +++++++++-- docs/diagrams/CompStatsAcitivityDiagram.png | Bin 0 -> 14893 bytes docs/diagrams/CompStatsAcitivityDiagram.puml | 17 ++++ .../CompStatsCommandSequenceDiagram.png | Bin 0 -> 40944 bytes .../CompStatsCommandSequenceDiagram.puml | 73 ++++++++++++++++++ docs/diagrams/SortScoreAcitivityDiagram.png | Bin 16597 -> 19458 bytes docs/diagrams/SortScoreAcitivityDiagram.puml | 3 +- 7 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 docs/diagrams/CompStatsAcitivityDiagram.png create mode 100644 docs/diagrams/CompStatsAcitivityDiagram.puml create mode 100644 docs/diagrams/CompStatsCommandSequenceDiagram.png create mode 100644 docs/diagrams/CompStatsCommandSequenceDiagram.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 8ba5ee576c5..9e02861b0b7 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -187,13 +187,13 @@ This section describes some noteworthy details on how certain features are imple ### Sort Commands -The Sort related feature allows NUS professors to sort the currently displayed students or student scores. When successfully executed, the sorted students or student scores will be shown on the Graphical User Interface.
+The Sort related features allows NUS professors to sort the currently displayed students or student scores. When successfully executed, the sorted students or student scores will be shown on the Graphical User Interface.
-We will discuss the implementation of sortScore (Sort Student Scores) command here and omit the discussion of the implementation of sortStu command since it is very similar to sortScore command and simpler. +We will discuss the implementation of `sortScore` (Sort Student Scores) command here and omit the discussion of the implementation of `sortStu` command since it is very similar to `sortScore` command and simpler. #### Implementation -The `sortScoreCommand` (Sort Student Scores) mechanism is facilitated by GradedComponentBook, StudentBook and StudentScoreBook. It implements the following operations: +The `sortScore` mechanism is facilitated by GradedComponentBook, StudentBook and StudentScoreBook. It implements the following operations: * `GradedComponentBook#hasGc(GcName gcName)` - Returns true if a graded component is already created and in the graded component list. * `studentScoreBook.sortStudentScore(Boolean isReverse)` - Filters the student scores with the given graded component and sort them according to the given reverse order. @@ -217,8 +217,42 @@ The following sequence diagram shows how the sort student scores operation works -The following activity diagram summarizes what happens when a user executes a new sortScore command:
- +The following activity diagram summarizes what happens when a user executes a new `sortScore` command:
+ + +### Stats Commands + +The Stats related features allows NUS professors to calculate the statistics of student scores effectively. When successfully executed, the relevant statistics will be shown in the result display box of Graphical User Interface.
+ +We will discuss the implementation of `compStats` (calculate the statistics for a specific graded component) command here and omit the discussion of the implementation of `stats` command since it is very similar to `compStats` command and simpler. + +#### Implementation + +The `compStats` mechanism is facilitated by GradedComponentBook, StudentBook and StudentScoreBook. It implements the following operations: + +* `GradedComponentBook#hasGc(GcName gcName)` - Returns true if a graded component is already created and in the graded component list. +* `studentBook.getStudentList()` - Returns the stored list of students. +* `compStatsCommand.generateOverallStatsSummary(List students)` - Returns a string represented all the relevant statistics. +* `statsCalculator` - A class that helps calculate different types of statistical measures. + +Given below is an example usage scenario and how the `compStats` mechanism behaves at each step. + +Step 1. The user executes `compStats c/Midterm` command to calculate the statistics of student scores of Midterm. The `compStats` command calls CompStatsCommandParser#parse() which parses the string keyed into the command line of the GUI. + +Step 2. `CompStatsCommandParser#parse()` invokes the creation of a `CompStatsCommand` object. +> **Note**: If a command fails its execution due to incorrect command format, it will not create a `CompStatsCommand` object, an error message will be displayed and user will retype their command. + +Step 4. Upon creation and execution of `CompStatsCommand` object, `GradedComponentBook#hasGc(GcName gcName)`, `studentBook.getStudentList()` and `compStatsCommand.generateOverallStatsSummary(List students)` methods are called. +> **Note**: If upon invoking `GradedComponentBook#hasGc(GcName gcName)` method and return value is false, it will throw an error and will not call the remaining two methods, so statistics will not be calculated and displayed. + +Step 5. After successfully calculating the statistics, a `CommandResult` object will be created to show the calculated statistics. + +The following sequence diagram shows how the `compStats` operation works:
+ + + +The following activity diagram summarizes what happens when a user executes a new `compStats` command:
+ ### Auto-grading diff --git a/docs/diagrams/CompStatsAcitivityDiagram.png b/docs/diagrams/CompStatsAcitivityDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..dc36742a3c76aa3fe33a41732f0cced2cd6dc68b GIT binary patch literal 14893 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/CompStatsAcitivityDiagram.puml b/docs/diagrams/CompStatsAcitivityDiagram.puml new file mode 100644 index 00000000000..0fbe6e9db1b --- /dev/null +++ b/docs/diagrams/CompStatsAcitivityDiagram.puml @@ -0,0 +1,17 @@ +@startuml +'https://plantuml.com/activity-diagram-beta + +start +:User executes compStats command; +if () then ([given graded component exists\nand there is at least a valid score]) + :all relevant statistics calculated; + :a summary of all statistics displayed; +else ([else]) + :a error message shows up; +endif + +:Execution completed; + +stop + +@enduml diff --git a/docs/diagrams/CompStatsCommandSequenceDiagram.png b/docs/diagrams/CompStatsCommandSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1daf7204d5433727bf6822ea34ff1520bdd668 GIT binary patch literal 40944 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/CompStatsCommandSequenceDiagram.puml b/docs/diagrams/CompStatsCommandSequenceDiagram.puml new file mode 100644 index 00000000000..6cec05e91c2 --- /dev/null +++ b/docs/diagrams/CompStatsCommandSequenceDiagram.puml @@ -0,0 +1,73 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLightParser" as ModuLightParser LOGIC_COLOR +participant ":CompStatsCommandParser" as CompStatsCommandParser LOGIC_COLOR +participant "a:CompStatsCommand" as CompStatsCommand LOGIC_COLOR +participant ":StatsCalculator" as StatsCalculator LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute(compStats c/Midterm) +activate LogicManager + +LogicManager -> ModuLightParser : parseCommand(compStats c/Midterm) +activate ModuLightParser + +create CompStatsCommandParser +ModuLightParser -> CompStatsCommandParser :CompStatsCommandParser() +activate CompStatsCommandParser + +CompStatsCommandParser --> ModuLightParser +deactivate CompStatsCommandParser + +ModuLightParser -> CompStatsCommandParser : parse(" c/Midterm") +activate CompStatsCommandParser + +create CompStatsCommand +CompStatsCommandParser -> CompStatsCommand +activate CompStatsCommand + +CompStatsCommand --> CompStatsCommandParser : c +deactivate CompStatsCommand + +CompStatsCommandParser --> ModuLightParser : c +deactivate CompStatsCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +CompStatsCommandParser -[hidden]-> ModuLightParser +destroy CompStatsCommandParser + +ModuLightParser --> LogicManager : c +deactivate ModuLightParser + +LogicManager -> CompStatsCommand : execute() +activate CompStatsCommand + +CompStatsCommand -> Model : getStudentList() +activate Model + +Model --> CompStatsCommand +deactivate Model + +create StatsCalculator +CompStatsCommand ->StatsCalculator : generateOverallStatsSummary(List students) +activate StatsCalculator + + +StatsCalculator --> CompStatsCommand +deactivate StatsCalculator +StatsCalculator -[hidden]-> CompStatsCommand +destroy StatsCalculator + +CompStatsCommand --> LogicManager : result +deactivate CompStatsCommand +CompStatsCommand -[hidden]-> LogicManager : result +destroy CompStatsCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SortScoreAcitivityDiagram.png b/docs/diagrams/SortScoreAcitivityDiagram.png index 94fecbb7d4edf5ab0d97ea2b547802f8f49f1c03..17a81a0e981982f0d4dc8d2cb0288ae314b74d5f 100644 GIT binary patch literal 19458 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 MN>Q>z+{o|$00bYlTL1t6 literal 16597 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 O9>}Z7y}N4~^nU>8hDoph diff --git a/docs/diagrams/SortScoreAcitivityDiagram.puml b/docs/diagrams/SortScoreAcitivityDiagram.puml index 72c56442ae3..dbf70e7441b 100644 --- a/docs/diagrams/SortScoreAcitivityDiagram.puml +++ b/docs/diagrams/SortScoreAcitivityDiagram.puml @@ -6,12 +6,13 @@ start if () then ([given graded component exists]) :student scores filtered and sorted; :students sorted accordingly; + :Updated students and student scores list saved to ModuLight; else ([else]) :a error message shows up; :user retypes the command; endif -:Updated students and student scores list saved to ModuLight; +: execution completed; stop From 0cf88f738fce8fff9086b541d6598261ed097d20 Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Tue, 14 Nov 2023 00:50:27 +0800 Subject: [PATCH 22/28] Update UG --- docs/UserGuide.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 4569e729018..0f1fc90d354 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -344,9 +344,16 @@ Sorts students' data by the given criteria. Format: `sortStu [o/SORTING_ORDER] [r/REVERSE_ORDER]` -* The sorting order keyword must be one of the acceptable description given below:
"n", - "name", "s", "studentId", "studentID", "e", "email", "g", "tutorial", "tut", - "tutGroup", "ts", "totalScore", "totalscore", "score". +* The sorting order keyword must be one of the acceptable description given below:
+ +| Accepted keywords | Field to be sorted | Description | +|------------------------------------|--------------------|-----------------------------------------------------| +| `n`, `name` | `n/` | Name of the student by alphabetical order | +| `s`, `studentId`, `studentID` | `s/` | Student ID of the student by alphabetical order | +| `e`, `email` | `e/` | Email of the student by alphabetical order | +| `g`, `tutorial`, `tut`, `tutGroup` | `g/` | Tutorial group of the student by alphabetical order | +| `ts`, `totalScore`, `score` | NIL | Total score of the student by numerical value | + * The reverse order keyword must be one of the acceptable description given below:
"decreasing", "0", "false", "f" (These 4 keywords have the same effect), "increasing", "1", "true", "t" (These 4 keywords have the same effect). From 0b48a65ac6dc6697cda00a6ddf28238032dc1193 Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Tue, 14 Nov 2023 00:53:23 +0800 Subject: [PATCH 23/28] Update UG --- docs/UserGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 0f1fc90d354..0665c5d5406 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -428,7 +428,7 @@ Format: `stats [st/STATS] [g/TUTORIAL_GRP]` * It is allowed to omit `[st/STATS]`. In this case, it will return a summary of all statistics that are currently supported. * For stats keywords, it must be currently supported. Here is an exhaustive list of currently supported statistical -measures: mean, standardDeviation, upperQuartile, lowerQuartile, max, min, skewness. +measures: `mean`, `standardDeviation`, `upperQuartile`, `lowerQuartile`, `max`, `min`, `skewness`. * For the calculation of upper and lower quartile, we use Method 4 introduced in [Wikipedia](https://en.wikipedia.org/wiki/Quartile). * If there is only valid score matching the criteria, skewness will be displayed as `NaN` because skewness for one data is meaningless. @@ -448,7 +448,7 @@ Format: `compStats c/COMP_NAME [st/STATS] [g/TUTORIAL_GRP]` * It is allowed to omit `[st/STATS]`. In this case, it will return a summary of all statistics that are currently supported. * For stats keywords, it must be currently supported. Here is an exhaustive list of currently supported statistical - measures: mean, standardDeviation, upperQuartile, lowerQuartile, max, min, skewness. + measures: `mean`, `standardDeviation`, `upperQuartile`, `lowerQuartile`, `max`, `min`, `skewness`. * For the calculation of upper and lower quartile, we use Method 4 introduced in [Wikipedia](https://en.wikipedia.org/wiki/Quartile). * If there is only valid score matching the criteria, skewness will be displayed as `NaN` because skewness for one data From be6a7e4c97cef817b66e5b69feb167f8f0ed10fc Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Tue, 14 Nov 2023 01:02:02 +0800 Subject: [PATCH 24/28] Update UG --- docs/UserGuide.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 0665c5d5406..640b545c5df 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -109,16 +109,16 @@ The following section gives an overview of the parameters used for the commands -> **Note**: Graded Component and Student Score parameters for score calculation -> -> * The maximum marks of a graded component and marks of a student score are both **absolute values** and are used together to + **Note**: Graded Component and Student Score parameters for score calculation + + * The maximum marks of a graded component and marks of a student score are both **absolute values** and are used together to determine the relative performance of a student for a component.
For instance, if the maximum marks for a component Midterms is 50, and the marks for the student is 35, then the student scored 35/50 =70% on this graded component. -> * The weightage of a graded component is used to determine its contribution to a student’s overall score, and is calculated + * The weightage of a graded component is used to determine its contribution to a student’s overall score, and is calculated **relative to the sum of all other component weightages**.
For instance, if there are only 2 components in the system currently, and component A has weightage 30, and component B weightage 20, then component A currently represents 20/(20+30) = 60% of the student’s overall score. This is modified as components are added and removed.
Note that the **total weightage of all graded components should be less than or equal to 100**. -> * If a graded component has a maximum mark of 0, the relative score for any associated student scores will be 0. -> * If a student or graded component has no associated student scores, the average mark will be listed as 0. + * If a graded component has a maximum mark of 0, the relative score for any associated student scores will be 0. + * If a student or graded component has no associated student scores, the average mark will be listed as 0.
From e726fd2ac8c7acdf822260d37662965c35cee427 Mon Sep 17 00:00:00 2001 From: Vedant Sinha Date: Tue, 14 Nov 2023 01:09:18 +0800 Subject: [PATCH 25/28] Add How to use guide --- docs/UserGuide.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 899765f8de7..b4c903c6994 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -26,6 +26,7 @@ type fast, ModuLight can get your student grading tasks done faster than traditi -------------------------------------------------------------------------------------------------------------------- * Table of Contents + * **[How to use this guide](#how-to-use-this-guide)** * **[Quick Start](#quick-start)** * **[Parameter Information](#parameter-information)** * **[Navigating the Graphical User Interface (GUI)](#navigating-the-graphical-user-interface-gui)** @@ -37,6 +38,25 @@ type fast, ModuLight can get your student grading tasks done faster than traditi -------------------------------------------------------------------------------------------------------------------- +## How to use this guide +1. For the first time users we recommend to: + + * Start with the [Quick start](#quick-start) to download, setup and run the program. + + * Go through [Navigating the Graphical User Interface (GUI)](#navigating-the-graphical-user-interface-gui) + section to understand the different components of the GUI of the program. + + * Go through the [Command Format](#command-format) to get an idea of the correct way to understand and input the + commands. + +2. For regular users: + + * We have provided a [command summary](#command-summary) for you to check all the available features. + + * More detailed explanation of the features can be found under the [Features](#features) section. + * An overview of all the parameters used in the commands along with their constraints and such can be found + under the [Parameter Information](#parameter-information) section. + ## Quick start 1. Ensure you have Java `11` or above installed in your Computer. From 6d7effdb354244ea15a233bfb5c9fb8cfc449a3e Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Tue, 14 Nov 2023 01:12:52 +0800 Subject: [PATCH 26/28] Update PPP --- docs/team/siqirua.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/team/siqirua.md b/docs/team/siqirua.md index 06e20f17a51..ecaf02af179 100644 --- a/docs/team/siqirua.md +++ b/docs/team/siqirua.md @@ -10,20 +10,20 @@ The user interacts with it using a CLI, and it has a GUI created with JavaFX. It Given below are my contributions to the project. -* **New Feature**: `calculate statistics` - Added command to calculate the statistics. +* **New Feature**: `calculate statistics` - Added command to calculate the statistics [76](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/76), [#86](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/86). * What it does: Generates a statistics summary of the overall grades of all students or a selected tutorial group * Justification: As a professor or teaching assistant, our target users may be interested in the overall performance of all students in terms of various statistical measures, such as max, min, quartile and standard deviation. This command allows the user to easily view a summary of statistics with one single command. In addition, I also implemented the command to calculate the statistics of a specific graded component instead of the overall score. -* **New Feature**: `sort students` - Added command to sort the students. +* **New Feature**: `sort students` - Added command to sort the students [#69](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/69). * What it does: Sorts the students based on a given sorting order. * Justification: When the number of student grows, it will be hard for our users to find the top performing students. Thus, we implement this command to allow our users to quickly identify the top students. In addition, this command is also necessary to make the displayed data more neat. -* **New Feature**: `sort scores` - Added command to sort the student scores. +* **New Feature**: `sort scores` - Added command to sort the student scores [#88](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/88). * What it does: Sorts the students scores of a given graded component and display the associated students in the corresponding order. * Justification: Similar to the previous feature, when the number of student scores grows, it will be hard for our @@ -32,19 +32,19 @@ Given below are my contributions to the project. corresponding order. In addition, this command is also necessary to make the displayed data more neat. * **Enhancements to existing features**: -* Did cosmetic tweaks to the existing `help` features +* Did cosmetic tweaks to the existing `help` features [#142](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/142) * **Code contributed**: * Please refer to this [RepoSense Link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=siqirua&breakdown=true) * **Documentation**: * User Guide: - * Added documentation for the features `findStu`, `stats`, `compStats`, `sortStu`, `sortScore` - * Added documentation for the parameters `o/`, `r/`, `st/` + * Added documentation for the features `findStu`, `stats`, `compStats`, `sortStu`, `sortScore` [#11](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/11), [#90](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/90) + * Added documentation for the parameters `o/`, `r/`, `st/` [#163](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/163) * Developer Guide: - * Added use cases, non-functional requirements and glossary sections. - * Created UML diagrams for most parts - * Did cosmetic tweaks to the proposed `Redo`/`Undo` features + * Added use cases, non-functional requirements and glossary sections [#10](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/10). + * Created UML diagrams for most parts [#162](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/162) + * Did cosmetic tweaks to the proposed `Redo`/`Undo` features [#169](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/169) * **Contributions to team-based tasks**: * Renamed the package, method name and class name from address book related to modulight related From 74c5b970a9e2cb813c4ae3101212f29a1a765096 Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Tue, 14 Nov 2023 01:15:08 +0800 Subject: [PATCH 27/28] Update PPP --- docs/team/siqirua.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/team/siqirua.md b/docs/team/siqirua.md index ecaf02af179..cdc86deb149 100644 --- a/docs/team/siqirua.md +++ b/docs/team/siqirua.md @@ -45,6 +45,7 @@ Given below are my contributions to the project. * Added use cases, non-functional requirements and glossary sections [#10](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/10). * Created UML diagrams for most parts [#162](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/162) * Did cosmetic tweaks to the proposed `Redo`/`Undo` features [#169](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/169) + * Add Implementations of sort related commands and stats related command [#188](https://github.com/AY2324S1-CS2103T-W08-2/tp/pull/188) * **Contributions to team-based tasks**: * Renamed the package, method name and class name from address book related to modulight related From a82e9cbf7574b1788e39972ee4e46d93bcb8a6d0 Mon Sep 17 00:00:00 2001 From: Li Siqi Date: Tue, 14 Nov 2023 01:35:56 +0800 Subject: [PATCH 28/28] Add some manual testing information --- docs/DeveloperGuide.md | 64 +++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index c6f11f11e84..4a6279e253e 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -718,28 +718,60 @@ testers are expected to do more *exploratory* testing. 1. Prerequisites: List all students using the `listAll` command. Multiple students in the list. - 1. Test case: `deleteStu 1`
- Expected: First student is deleted from the student list. All related scores are deleted from the score list. Details of the deleted student shown in the status message. Timestamp in the status bar is updated. + 1. Test case: `deleteStu 1`
+ Expected: First student is deleted from the student list. All related scores are deleted from the score list. Details of the deleted student shown in the status message. Timestamp in the status bar is updated. - 1. Test case: `deleteStu 0`
- Expected: No student is deleted. Error details shown in the status message. Status bar remains the same. + 1. Test case: `deleteStu 0`
+ Expected: No student is deleted. Error details shown in the status message. Status bar remains the same. - 1. Other incorrect delete commands to try: `deleteStu`, `deleteStu x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. - -1. _{ more test cases …​ }_ + 1. Other incorrect delete commands to try: `deleteStu`, `deleteStu x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous. ### Finding a student 1. Find a student in ModuLight 1. Prerequisite: student list is not empty. - 1. Test case: `findStu g/T00` - Expected: All students from tutorial group `T00` will be displayed. All graded components and all scores related to the displayed students should be displayed. - 1. Test case: `findStu` - Expected: Since there is no search words given, all students, student scores and graded components will be displayed. - 2. Test case: `findStu n/John n/Amy` - Expected: All students whose name contains `John` or `Amy` (case-insensitive) will be displayed. All graded components and all scores related to the displayed students should be displayed. - 3. Test case: `findStu n/John g/T00` - Expected: All students whose name contains `John` (case-insensitive) and is from `T00` will be displayed. All graded components and all scores related to the displayed students should be displayed. + 1. Test case: `findStu g/T00` + Expected: All students from tutorial group `T00` will be displayed. All graded components and all scores related to the displayed students should be displayed. + 1. Test case: `findStu` + Expected: Since there is no search words given, all students, student scores and graded components will be displayed. + 2. Test case: `findStu n/John n/Amy` + Expected: All students whose name contains `John` or `Amy` (case-insensitive) will be displayed. All graded components and all scores related to the displayed students should be displayed. + 3. Test case: `findStu n/John g/T00` + Expected: All students whose name contains `John` (case-insensitive) and is from `T00` will be displayed. All graded components and all scores related to the displayed students should be displayed. + +### Sorting Students +1. Sort students in ModuLight + 1. Prerequisite: displayed student list is not empty. + 2. Test case: `sortStu` + Expected: The displayed students are sorted by their total scores. + 3. Test case: `sortStu o/n r/t` + Expected: The displayed students are sorted by their names in the reverse alphabetical order. + 4. Test case: `sortStu o/wrongInput` + Expected: An error message that states "Invalid command format!" and the correct usage is shown. + +### Sorting Student Scores +1. Sort student scores in ModuLight + 1. Prerequisite: displayed student list and student score list are not empty and a graded component with name "Midterm" is created. + 2. Test case: `sortScore c/Midterm` + Expected: Only Midterm student scores are shown and they are sorted in the ascending order. + 3. Test case: `sortScore c/Final` (Assuming there is no such graded component with name "Final") + Expected: An error message that states "This graded component is not created. Please check if the information is correct" is shown. + + +### Calculating Statistics +1. Calculate overall statistics of students' total scores + 1. Prerequisite: student list and student score list are not empty and there is at least a valid score in Tut `T01`. + 1. Test case: `stats` + Expected: A message that states all relevant statistical measures (The exhausitive list can be found in [UG](https://ay2324s1-cs2103t-w08-2.github.io/tp/UserGuide.html#calculating-overall-statistics-stats)) are shown. + 2. Test case: `stats st/max st/min` + Expected: A message that states the max and min is shown. + 3. Test case: `stats g/T01` + Expected: A message that states all relevant statistical measures of Tut `T01` is shown. + 4. Test case: `stats st/wrongInput` + Expected: An error message that states "Some statistic measures are not supported yet." and all supported statistical measures are shown. + 2. Prerequisite: student score list is empty + 1. Test case: `stats` + Expected: An error message that state "Please have at least one score fulfilling the condition." is shown. ### Saving data