diff --git a/src/main/Resources/view/itemBoxes/FileBox.fxml b/src/main/Resources/view/itemBoxes/FileBox.fxml index 2df9cd69d5..8a1834de0c 100644 --- a/src/main/Resources/view/itemBoxes/FileBox.fxml +++ b/src/main/Resources/view/itemBoxes/FileBox.fxml @@ -6,7 +6,7 @@ - \ No newline at end of file diff --git a/src/main/Resources/view/itemBoxes/GradedComponentBox.fxml b/src/main/Resources/view/itemBoxes/GradedComponentBox.fxml new file mode 100644 index 0000000000..c6c37cce26 --- /dev/null +++ b/src/main/Resources/view/itemBoxes/GradedComponentBox.fxml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/spinbox/Parser.java b/src/main/java/spinbox/Parser.java index b3c0dea626..098fd66249 100644 --- a/src/main/java/spinbox/Parser.java +++ b/src/main/java/spinbox/Parser.java @@ -8,6 +8,7 @@ import spinbox.commands.RemoveCommand; import spinbox.commands.RemoveMultipleCommand; import spinbox.commands.SetDateCommand; +import spinbox.commands.ScoreCommand; import spinbox.commands.SetNameCommand; import spinbox.commands.UpdateCommand; import spinbox.commands.UpdateMultipleCommand; @@ -138,6 +139,9 @@ public static Command parse(String input) throws SpinBoxException { case "update-*": command = new UpdateMultipleCommand(pageDataComponents, content); break; + case "score": + command = new ScoreCommand(pageDataComponents, content); + break; case "find": command = new FindCommand(pageDataComponents, content); break; diff --git a/src/main/java/spinbox/commands/AddCommand.java b/src/main/java/spinbox/commands/AddCommand.java index 01b49c2acb..57ccef8418 100644 --- a/src/main/java/spinbox/commands/AddCommand.java +++ b/src/main/java/spinbox/commands/AddCommand.java @@ -4,8 +4,10 @@ import spinbox.containers.ModuleContainer; import spinbox.containers.lists.FileList; import spinbox.containers.Notepad; +import spinbox.containers.lists.GradeList; import spinbox.entities.items.File; import spinbox.entities.Module; +import spinbox.entities.items.GradedComponent; import spinbox.exceptions.SpinBoxException; import spinbox.exceptions.InputException; import spinbox.Ui; @@ -37,6 +39,8 @@ public class AddCommand extends Command { + "the full command for adding notes:\n"; private static final String TODO_ERROR_MESSAGE = "Please ensure that you enter " + "the full command for adding todo:\n"; + private static final String GRADED_COMPONENT_ERROR_MESSAGE = "Please ensure that you enter " + + "the full command for adding graded components:\n"; private static final String DEADLINE_ERROR_MESSAGE = "Please ensure that you enter " + "the full command for adding deadlines:\n"; private static final String EVENT_ERROR_MESSAGE = "Please ensure that you enter " @@ -45,6 +49,7 @@ public class AddCommand extends Command { + "the full command for adding modules:\n"; private static final String FILE_FORMAT = "add / file "; private static final String NOTE_FORMAT = "add / note "; + private static final String GRADED_COMP_FORMAT = "add / grade weightage: %"; private static final String TODO_FORMAT = "add / todo "; private static final String DEADLINE_FORMAT = "add / deadline by: "; private static final String EVENT_FORMAT = "add / at: " @@ -52,6 +57,7 @@ public class AddCommand extends Command { private static final String MODULE_FORMAT = "add / module "; private static final String EMPTY_TODO_DESCRIPTION = "☹ OOPS!!! The description of a task cannot be empty."; private static final String EMPTY_DEADLINE_DESCRIPTION = "☹ OOPS!!! The description of a deadline cannot be empty."; + private static final String EMPTY_GRADE_DESCRIPTION = "☹ OOPS!!! The description of a component cannot be empty."; private static final String EMPTY_EVENT_DESCRIPTION = "☹ OOPS!!! The description of an event cannot be empty."; private static final String EMPTY_EXAM_DESCRIPTION = "☹ OOPS!!! The description of an exam cannot be empty."; private static final String EMPTY_LAB_DESCRIPTION = "☹ OOPS!!! The description of a lab session cannot be empty."; @@ -80,6 +86,7 @@ public AddCommand(String[] pageDataComponents, String content) throws InputExcep public String execute(ModuleContainer moduleContainer, ArrayDeque pageTrace, Ui ui, boolean guiMode) throws SpinBoxException { File fileAdded; + GradedComponent gradedComponentAdded; Task taskAdded; DateTime start; DateTime end; @@ -121,6 +128,38 @@ public String execute(ModuleContainer moduleContainer, ArrayDeque pageTr throw new InputException(NOTE_ERROR_MESSAGE + NOTE_FORMAT); } + case "grade": + try { + checkIfOnModulePage(moduleCode); + if (moduleContainer.checkModuleExists(moduleCode)) { + HashMap modules = moduleContainer.getModules(); + Module module = modules.get(moduleCode); + GradeList gradeList = module.getGrades(); + String gradedComponentDetails = content.replace(type, "").trim(); + gradedComponentDetails = gradedComponentDetails.replace("%", ""); + + String gradedComponentName = gradedComponentDetails.substring(0, + gradedComponentDetails.lastIndexOf(" weightage:")); + + double weightage = Double.parseDouble(gradedComponentDetails.split("weightage: ")[1]); + + if (gradedComponentDetails.split(" ")[0].equals("weightage:")) { + throw new InputException(EMPTY_GRADE_DESCRIPTION); + } + + gradedComponentAdded = gradeList.add(new GradedComponent(gradedComponentName, weightage)); + + return HORIZONTAL_LINE + "\nAdded into " + module.toString() + " grades: " + + gradedComponentAdded.toString() + "\n You currently have " + gradeList.size() + + ((gradeList.size() == 1) ? " graded component in the list." : " graded components" + + " in the list.") + "\n" + HORIZONTAL_LINE; + } else { + return NON_EXISTENT_MODULE; + } + } catch (IndexOutOfBoundsException e) { + throw new InputException(GRADED_COMPONENT_ERROR_MESSAGE + GRADED_COMP_FORMAT); + } + case "todo": try { checkIfOnModulePage(moduleCode); diff --git a/src/main/java/spinbox/commands/RemoveCommand.java b/src/main/java/spinbox/commands/RemoveCommand.java index 3f2e326d28..e4f920f049 100644 --- a/src/main/java/spinbox/commands/RemoveCommand.java +++ b/src/main/java/spinbox/commands/RemoveCommand.java @@ -2,10 +2,12 @@ import spinbox.containers.ModuleContainer; import spinbox.containers.lists.FileList; +import spinbox.containers.lists.GradeList; import spinbox.containers.lists.TaskList; import spinbox.containers.Notepad; import spinbox.entities.items.File; import spinbox.entities.Module; +import spinbox.entities.items.GradedComponent; import spinbox.entities.items.tasks.Task; import spinbox.exceptions.SpinBoxException; import spinbox.exceptions.InputException; @@ -86,6 +88,31 @@ public String execute(ModuleContainer moduleContainer, ArrayDeque pageTr return NON_EXISTENT_MODULE; } + case "grade": + checkIfOnModulePage(moduleCode); + if (moduleContainer.checkModuleExists(moduleCode)) { + try { + HashMap modules = moduleContainer.getModules(); + Module module = modules.get(moduleCode); + GradeList gradeList = module.getGrades(); + int index = Integer.parseInt(content.split(" ")[1]) - 1; + GradedComponent removedComponent = gradeList.get(index); + gradeList.remove(index); + + return HORIZONTAL_LINE + "\nRemoved task: " + removedComponent.toString() + "\n" + + "You currently have " + gradeList.size() + + ((gradeList.size() == 1) ? " graded component in the list." + : " graded components in the list.") + "\n" + + HORIZONTAL_LINE; + } catch (NumberFormatException e) { + throw new InputException(INVALID_INDEX); + } catch (IndexOutOfBoundsException e) { + throw new InputException(PROVIDE_INDEX); + } + } else { + return NON_EXISTENT_MODULE; + } + case "task": checkIfOnModulePage(moduleCode); if (moduleContainer.checkModuleExists(moduleCode)) { diff --git a/src/main/java/spinbox/commands/ScoreCommand.java b/src/main/java/spinbox/commands/ScoreCommand.java new file mode 100644 index 0000000000..90fe04fdcf --- /dev/null +++ b/src/main/java/spinbox/commands/ScoreCommand.java @@ -0,0 +1,76 @@ +package spinbox.commands; + +import spinbox.Ui; +import spinbox.containers.ModuleContainer; +import spinbox.containers.lists.GradeList; +import spinbox.entities.Module; +import spinbox.entities.items.GradedComponent; +import spinbox.exceptions.InputException; +import spinbox.exceptions.SpinBoxException; + +import java.util.ArrayDeque; +import java.util.HashMap; + +public class ScoreCommand extends Command { + private static final String HORIZONTAL_LINE = "____________________________________________________________"; + private static final String NON_EXISTENT_MODULE = "This module does not exist."; + private static final String INVALID_FORMAT = "Please use the valid score format:\n" + + "Absolute percentage: [score / marks:%]" + "\n" + + "Relative percentage: [score / marks: /"; + private static final String INVALID_VALUE = "PLease enter valid numerical value(s)."; + private static final String PROVIDE_INDEX = "Please provide a valid index of the graded component to be scored."; + private static final String COMPONENT_SCORED = "The following component has been scored: "; + + private String moduleCode; + private String content; + + /** + * Constructor for initialization of variables to support scoring of graded components. + * @param pageDataComponents page data components to provide context based input completion. + * @param content A string containing the content of the processed user input. + */ + public ScoreCommand(String[] pageDataComponents, String content) { + if (pageDataComponents.length > 1) { + this.moduleCode = pageDataComponents[1]; + } + this.content = content; + } + + + @Override + public String execute(ModuleContainer moduleContainer, ArrayDeque pageTrace, Ui ui, boolean guiMode) + throws SpinBoxException { + checkIfOnModulePage(moduleCode); + if (moduleContainer.checkModuleExists(moduleCode)) { + try { + HashMap modules = moduleContainer.getModules(); + Module module = modules.get(moduleCode); + GradeList gradeList = module.getGrades(); + + String[] scoreComponents = this.content.split(" marks:"); + int index = Integer.parseInt(content.split(" ")[0]) - 1; + String[] scores = scoreComponents[1].split("/"); + + if (scoreComponents[1].contains("%")) { + scoreComponents[1] = scoreComponents[1].replace("%", ""); + double attainedPercentage = Double.parseDouble(scoreComponents[1]); + gradeList.updateGradeWeightedScore(index, attainedPercentage); + } else if (scores.length == 2) { + double attainedScore = Double.parseDouble(scores[0]); + double maximumScore = Double.parseDouble(scores[1]); + gradeList.updateGradeWeightedScore(index, attainedScore, maximumScore); + } else { + throw new InputException(INVALID_FORMAT); + } + return HORIZONTAL_LINE + "\n" + COMPONENT_SCORED + "\n" + + gradeList.get(index).toString() + "\n" + HORIZONTAL_LINE; + } catch (NumberFormatException e) { + throw new InputException(INVALID_VALUE); + } catch (IndexOutOfBoundsException e) { + throw new InputException(INVALID_FORMAT); + } + } else { + return NON_EXISTENT_MODULE; + } + } +} diff --git a/src/main/java/spinbox/commands/UpdateCommand.java b/src/main/java/spinbox/commands/UpdateCommand.java index 7044e2719b..12c737f739 100644 --- a/src/main/java/spinbox/commands/UpdateCommand.java +++ b/src/main/java/spinbox/commands/UpdateCommand.java @@ -2,9 +2,11 @@ import spinbox.containers.ModuleContainer; import spinbox.containers.lists.FileList; +import spinbox.containers.lists.GradeList; import spinbox.containers.lists.TaskList; import spinbox.entities.items.File; import spinbox.entities.Module; +import spinbox.entities.items.GradedComponent; import spinbox.entities.items.tasks.Task; import spinbox.exceptions.SpinBoxException; import spinbox.exceptions.InputException; @@ -17,6 +19,7 @@ public class UpdateCommand extends Command { private static final String HORIZONTAL_LINE = "____________________________________________________________"; private static final String NON_EXISTENT_MODULE = "This module does not exist."; private static final String FILE_MARKED = "Updated file: "; + private static final String GRADE_MARKED = "Updated graded component: "; private static final String TASK_MARKED = "Updated task: "; private static final String PROVIDE_INDEX = "Please provide an index of item to be updated."; private static final String INVALID_MARK_FORMAT = "Please use the valid update format:\n" @@ -73,6 +76,34 @@ public String execute(ModuleContainer moduleContainer, ArrayDeque pageTr return NON_EXISTENT_MODULE; } + case "grade": + checkIfOnModulePage(moduleCode); + if (moduleContainer.checkModuleExists(moduleCode)) { + try { + HashMap modules = moduleContainer.getModules(); + Module module = modules.get(moduleCode); + GradeList gradeList = module.getGrades(); + + int index = Integer.parseInt(content.split(" ")[1]) - 1; + GradedComponent gradeMarked = gradeList.get(index); + String[] contentComponents = content.split(" "); + if (contentComponents[2].toLowerCase().equals("true")) { + gradeList.update(index, true); + } else if (contentComponents[2].toLowerCase().equals("false")) { + gradeList.update(index, false); + } else { + throw new InputException(INVALID_VALUE); + } + return HORIZONTAL_LINE + "\n" + GRADE_MARKED + gradeMarked.toString() + "\n" + HORIZONTAL_LINE; + } catch (NumberFormatException e) { + throw new InputException(INVALID_INDEX); + } catch (IndexOutOfBoundsException e) { + throw new InputException(PROVIDE_INDEX); + } + } else { + return NON_EXISTENT_MODULE; + } + case "task": checkIfOnModulePage(moduleCode); if (moduleContainer.checkModuleExists(moduleCode)) { diff --git a/src/main/java/spinbox/containers/lists/GradeList.java b/src/main/java/spinbox/containers/lists/GradeList.java index 13e72dd17a..6a799e6eb4 100644 --- a/src/main/java/spinbox/containers/lists/GradeList.java +++ b/src/main/java/spinbox/containers/lists/GradeList.java @@ -1,5 +1,6 @@ package spinbox.containers.lists; +import spinbox.exceptions.InputException; import spinbox.storage.Storage; import spinbox.exceptions.CorruptedDataException; import spinbox.exceptions.DataReadWriteException; @@ -80,4 +81,15 @@ public List containsKeyword(String keyword) { return output; } + + public void updateGradeWeightedScore(int index, double yourScore, double maximumScore) throws InputException, + DataReadWriteException { + list.get(index).updateWeightedScore(yourScore, maximumScore); + this.saveData(); + } + + public void updateGradeWeightedScore(int index, double weightedScore) throws DataReadWriteException { + list.get(index).updateWeightedScore(weightedScore); + this.saveData(); + } } diff --git a/src/main/java/spinbox/containers/lists/HelpList.java b/src/main/java/spinbox/containers/lists/HelpList.java index 8cc3e3754d..bccd6aa692 100644 --- a/src/main/java/spinbox/containers/lists/HelpList.java +++ b/src/main/java/spinbox/containers/lists/HelpList.java @@ -43,7 +43,7 @@ public class HelpList { + "\t* Adding an item for a specific module (omit module code if current page is the specific module " + "page) *\n" + "\t2. Add a new file under module CG1111: add CG1111 / file quiz 2 2018\n" - + "\t3. Add a new grade component under CG1111: add CG1111 / grade Report : 12.5%\n" + + "\t3. Add a new grade component under CG1111: add CG1111 / grade Report weightage: 12.5%\n" + "\t4. Add a new note under CG1111: add CG1111 / note bring textbook\n" + "\t5. Add a new todo task under module CG1111: add CG1111 / todo finish assignment\n" + "\t -List of task type includes:\n" @@ -109,7 +109,7 @@ public class HelpList { + "Example:\n" + "\t* Note: omit module code if current page is the specific module *\n" + "\t1. Update a file to downloaded under module CG1111: update CG1111 / file 1 true\n" - + "\t2. Update a grade component under CG1111: TBC\n" + + "\t2. Update a grade component under CG1111: update CG1111 / grade 1 false\n" + "\t3. Update a task to done under module CG1111: update CG1111 / task 1 true\n" + horizontalLine); public final String updateMultiple = helpOutput.concat(horizontalLine + "\n" + "Command: update-*\n" diff --git a/src/main/java/spinbox/entities/items/File.java b/src/main/java/spinbox/entities/items/File.java index 96831c0f1f..5267d35467 100644 --- a/src/main/java/spinbox/entities/items/File.java +++ b/src/main/java/spinbox/entities/items/File.java @@ -3,8 +3,12 @@ import spinbox.exceptions.CorruptedDataException; public class File extends Item { + private static final String BRACKET_OPEN = "["; + private static final String BRACKET_CLOSE = "] "; private static final String CORRUPTED_FILES_DATA = "Corrupted files data."; private static final String DELIMITER_FILTER = " \\| "; + private static final String DOWNLOADED = "DOWNLOADED"; + private static final String NOT_DOWNLOADED = "NOT DOWNLOADED"; /** * This constructor is used for recreation of SpinBox.Tasks.FileTask from storage. @@ -32,9 +36,14 @@ public File(String fromStorage) throws CorruptedDataException { } } + @Override + public String getStatusText() { + return (this.getDone() ? DOWNLOADED : NOT_DOWNLOADED); + } + @Override public String toString() { - return super.toString(); + return BRACKET_OPEN + this.getStatusText() + BRACKET_CLOSE + this.getName(); } @Override diff --git a/src/main/java/spinbox/entities/items/GradedComponent.java b/src/main/java/spinbox/entities/items/GradedComponent.java index 5da02f73fe..76560334c8 100644 --- a/src/main/java/spinbox/entities/items/GradedComponent.java +++ b/src/main/java/spinbox/entities/items/GradedComponent.java @@ -12,6 +12,11 @@ public class GradedComponent extends Item { private static final String CORRUPTED_GRADES_DATA = "Corrupted grades data."; private static final String STORE_DELIMITER = " | "; private static final String DELIMITER_FILTER = " \\| "; + private static final String BRACKET_OPEN = "["; + private static final String BRACKET_CLOSE = "] "; + private static final String COMPLETED = "COMPLETED"; + private static final String NOT_COMPLETED = "NOT COMPLETED"; + private double weight; private boolean scoreKnown; @@ -49,6 +54,17 @@ public GradedComponent(String fromStorage) throws CorruptedDataException { } + @Override + public String getStatusText() { + return (this.getDone() ? COMPLETED : NOT_COMPLETED); + } + + @Override + public String toString() { + return BRACKET_OPEN + this.getStatusText() + BRACKET_CLOSE + this.getName() + "\n" + + this.getWeightedScoreAsString() + "/" + Double.toString(this.getWeight()); + } + /** * This is to create a stringified version of a GradedComponent instance for storage purposes. * @return String version of GradedComponent, ready for storage. diff --git a/src/main/java/spinbox/entities/items/Item.java b/src/main/java/spinbox/entities/items/Item.java index 768523c050..9b4fdd0126 100644 --- a/src/main/java/spinbox/entities/items/Item.java +++ b/src/main/java/spinbox/entities/items/Item.java @@ -6,8 +6,8 @@ public abstract class Item implements Exportable { private static final String STORE_DELIMITER = " | "; private static final String BRACKET_OPEN = "["; private static final String BRACKET_CLOSE = "] "; - private static final String DONE = "✓"; - private static final String NOT_DONE = "✗"; + private static final String DONE = "DONE"; + private static final String NOT_DONE = "NOT DONE"; private String name; private Boolean isDone; @@ -33,12 +33,12 @@ public Boolean getDone() { return isDone; } - protected String getStatusIcon() { + protected String getStatusText() { return (this.getDone() ? DONE : NOT_DONE); } public String toString() { - return BRACKET_OPEN + this.getStatusIcon() + BRACKET_CLOSE + this.getName(); + return BRACKET_OPEN + this.getStatusText() + BRACKET_CLOSE + this.getName(); } @Override diff --git a/src/main/java/spinbox/gui/MainWindow.java b/src/main/java/spinbox/gui/MainWindow.java index 5854b5eb12..244da5a523 100644 --- a/src/main/java/spinbox/gui/MainWindow.java +++ b/src/main/java/spinbox/gui/MainWindow.java @@ -37,6 +37,7 @@ import spinbox.exceptions.InvalidIndexException; import spinbox.exceptions.SpinBoxException; import spinbox.gui.boxes.FileBox; +import spinbox.gui.boxes.GradedComponentBox; import spinbox.gui.boxes.ModuleBox; import java.util.ArrayList; @@ -397,8 +398,12 @@ private void updateSpecificModuleGradeList(Module currModule) { modulesTabContainer.add(scrollPane, 1, 1, 1, 1); VBox gradesList = new VBox(); - gradesList.setStyle("-fx-background-color: #25274D"); + for (GradedComponent gradedComponent : gradedComponents) { + GradedComponentBox wrappedGradedComponent = GradedComponentBox.getGradedComponentsBox(gradedComponent); + gradesList.getChildren().add(wrappedGradedComponent); + } + gradesList.setStyle("-fx-background-color: #25274D"); scrollPane.setContent(gradesList); } diff --git a/src/main/java/spinbox/gui/boxes/GradedComponentBox.java b/src/main/java/spinbox/gui/boxes/GradedComponentBox.java new file mode 100644 index 0000000000..7eebcfcada --- /dev/null +++ b/src/main/java/spinbox/gui/boxes/GradedComponentBox.java @@ -0,0 +1,41 @@ +package spinbox.gui.boxes; + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import spinbox.entities.items.GradedComponent; +import spinbox.gui.MainWindow; + +import java.io.IOException; + +public class GradedComponentBox extends VBox { + @FXML + private Label gradedComponentDetails; + + private GradedComponentBox(GradedComponent gradedComponent) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource( + "/view/itemBoxes/GradedComponentBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + this.setStyle("-fx-border-color: #FFF"); + this.setPadding(new Insets(10, 10, 10, 10)); + this.setSpacing(10.0); + setMargin(this, new Insets(10, 10, 10, 10)); + this.gradedComponentDetails.setStyle("-fx-font-weight: bold"); + this.gradedComponentDetails.setTextFill(Color.WHITE); + this.gradedComponentDetails.setText(gradedComponent.toString()); + } + + public static GradedComponentBox getGradedComponentsBox(GradedComponent gradedComponent) { + return new GradedComponentBox(gradedComponent); + } +} diff --git a/src/test/java/unit/FileTest.java b/src/test/java/unit/FileTest.java index c49753de67..597506d8ef 100644 --- a/src/test/java/unit/FileTest.java +++ b/src/test/java/unit/FileTest.java @@ -11,9 +11,9 @@ public class FileTest { @org.junit.jupiter.api.Test void testToString_createAndMarkDoneNewTask() { Item item = new File(0, "file1"); - assertEquals("[✗] file1", item.toString()); + assertEquals("[NOT DOWNLOADED] file1", item.toString()); item.markDone(); - assertEquals("[✓] file1", item.toString()); + assertEquals("[DOWNLOADED] file1", item.toString()); } @org.junit.jupiter.api.Test