diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index e2081a3b4b..c0fee6c0f3 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,11 +1,29 @@
# Developer Guide
+## Table of Contents
+- [Acknowledgements](#acknowledgements)
+- [Design](#design)
+ - [Architecture](#architecture)
+- [Implementation](#implementation)
+ - [Add/Delete Student/Component Feature](#adddelete-studentcomponent-feature)
+ - [Find Student Feature](#find-student-feature)
+ - [Add/Delete Grade Feature](#adddelete-grade-feature)
+ - [Storage Load Feature](#storage-load-feature)
+- [Appendix A: Product Scope](#appendix-a-product-scope)
+- [Appendix B: User Stories](#appendix-b-user-stories)
+- [Appendix C: Non-Functional Requirements](#appendix-c-non-functional-requirements)
+- [Appendix D: Glossary](#appendix-d-glossary)
+- [Appendix E: Instructions for Manual Testing](#appendix-e-instructions-for-manual-testing)
+
+
+---
+
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the
-original source as well}
+This project was inspired by our experiences using the Canvas learning management system. While Canvas serves large educational environments well, we envisioned a simpler, offline tool tailored for small classes that prioritizes essential features like grade tracking, student management, and assessment organization. Thus, TutorLink was born.
-## Design & implementation
+The design and feature set of TutorLink were developed from scratch, drawing inspiration from the need for a lightweight, offline solution for managing class assignments and reducing administrative overhead in small class environments. No code or external sources were directly referenced or reused in the development of TutorLink.
+## Design
### Architecture
@@ -43,14 +61,13 @@ Where ref
frame is a placeholder for each command's specific operat
#### Setup:
During the setup phase of `TutorLink`, the following operations are performed:
-1. `Ui` displays welcome message
-2. `StudentStorage`, `ComponentStorage` and `GradeStorage` objects are instantiated
-3. `ArrayList` of Student, Component and Grade are obtained from the respective Storage classes
-4. `AppState` object is instantiated, passing the `ArrayList`s in step 3
+1. `StudentStorage`, `ComponentStorage` and `GradeStorage` objects are instantiated
+2. `ArrayList` of Student, Component and Grade are obtained from the respective Storage classes
+3. `AppState` object is instantiated, passing the `ArrayList`s in step 3
+4. `Ui` displays welcome message

-The specific implementation of noteworthy operations are presented below:
The specific implementation of noteworthy operations are presented below:
### Add/Delete Student/Component Feature
@@ -77,7 +94,7 @@ The following sequence diagrams depict the exact steps involved in the `AddStude
- `DeleteStudentCommand.execute(AppState appState, HashMap arguments)`: Removes a student via the following
steps:
- 1. Retrieves and validates the matriculation number from arguments, throwing `IllegaValueException` exception
+ 1. Retrieves and validates the matriculation number from arguments, throwing `IllegalValueException` exception
if matriculation number is null.
2. Searches for and deletes the student from `AppState`. Throws `StudentNotFoundException` if no student matching the matriculation number
is found.
@@ -148,24 +165,26 @@ The sequence diagram of the DeleteGradeCommand is shown below.
The `StudentStorage`, `GradeStorage` and `ComponentStorage` classes implement the feature to load data from the
data `.txt` files into their respective List objects at the start of the program.
-The load list methods for the Storage classes have largely similar logic flows.
+The load list methods for the Storage classes have largely similar logic flows. To avoid repetition,
+only the implementation for `GradeStorage` is shown.
-#### Key Operations
-
-The following section and sequence diagram elaborate on the implementation of the `loadGradeList` method in `GradeStorage`:
+The following section and sequence diagram elaborate on the implementation of the `loadGradeList` method in `GradeStorage`,
+as referenced in [Setup](#setup):

1. TutorLink constructs a new `GradeStorage`.
-2. TutorLink calls `loadGradeList`.
-3. `GradeStorage` creates a new `ArrayList` of `Grade`s.
-4. `GradeStorage` creates a new `Scanner` with the path to the file.
+2. `GradeStorage` creates a new `ArrayList` of `String`s for discarded entries.
+3. TutorLink calls `loadGradeList`.
+4. `GradeStorage` creates a new `ArrayList` of `Grade`s.
5. While there are next lines in the data file:
- - `Scanner` returns the current file line as a String and moves to the next file line.
+ - FileScanner returns the current file line as a String and moves to the next file line.
- `GradeStorage` calls its `getGradeFromFileLine` method with the file line.
- If the file line references a valid `Component` and a valid `Student`, a `Grade` is returned and added to the `ArrayList`.
- - If not (e.g. if data file was corrupted), the file line is simply ignored, and the loop continues to the next iteration.
+ - If not (e.g. file line was corrupted), the file line is added to `discardedEntries`,
+ and the loop continues to the next iteration.
6. The `ArrayList` of `Grade`s is returned to TutorLink.
+7. TutorLink calls `getDiscardedEntries`, and the discarded entries are displayed by UI.
## Appendix A: Product Scope
@@ -394,7 +413,7 @@ This appendix provides a guide for manually testing various features of TutorLin
---
-### Handling Missing or Corrupted Data Files
+### Handling Missing Files or Corrupted Data
1. **Simulate Missing Data Files**:
- Delete one or more files from the `data` folder (`studentlist.txt`, `componentlist.txt`, `gradelist.txt`).
@@ -402,10 +421,12 @@ This appendix provides a guide for manually testing various features of TutorLin
**Expected**: TutorLink creates new empty files if missing. The application should not crash, and it should operate normally.
-2. **Simulate Corrupted Data Files**:
- - Open any data file and add random text or invalid data structure, then save.
+2. **Simulate Corrupted Data**:
+ - Open any data file and add random text or invalid data entry, then save.
- Re-launch TutorLink.
- **Expected**: TutorLink should detect the corrupted data, provide an error message or recreate a new empty file if unreadable. The application should not crash.
+ **Expected**: TutorLink should detect the corrupted or invalid data,
+ display them to the user as entries to be discarded, and only load valid data entries.
+ The application should not crash.
----
\ No newline at end of file
+---
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index edf483b411..7ab1a3d18d 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -7,15 +7,30 @@
## Quick Start
1. Ensure you have Java 17 or above installed in your Computer.
-2. Download the latest .jar file of `TutorLink` from [here](http://link.to/duke).
+2. Download the latest .jar file of `TutorLink` from [here](https://github.com/AY2425S1-CS2113-W13-4/tp/releases/tag/v2.1).
3. Copy the file to the folder you want to use as the home folder for your TutorLink.
4. Open a command terminal, cd into the folder you put the jar file in, and use the java -jar TutorLink.jar command to run the application.
Your command terminal should look similar to the one below.

+
+## Important Notes on Commands:
+When inputting commands into `TutorLink`, kindly take note of the following:
+- Commands with duplicate parameters will be rejected. *i.e* `add_student n/John Doe n/John Doe i/A1234567X`
+- Parameters must be separated by at least one space character, otherwise the entire continuous string following a prefix
+will be considered a single parameter. *i.e* `add_student i/A1234567X n/John Doei/A1234567X` will be intepreted as adding
+a student with the name of `John Doei/A1234567X` and matric number `A1234567X`.
+- Parameters can be supplied in any order. *i.e* `add_student n/John i/A1234567X` is the same as `add_student i/A1234567X n/John`
+- **IMPORTANT**: Descriptions should **NOT** contain any separator tokens: `|` as this character is used for storage).
+Including these may yield unpredictable results with the `Storage` component.
+- Matric Number (`i/` argument) is case insensitive. Therefore, `A1234567X` is the same as `a1234567x`. Matric numbers
+will be converted to uppercase for storage.
+- Similarly, all other will be converted to lowercase for storage.
## Features
+
+
### Adding a Student: `add_student`
Adds a student to your class.
@@ -53,6 +68,8 @@ Displays a list of all students in the class.
---
+
+
### Finding a Student: `find_student`
Adds a student to your class.
@@ -94,6 +111,8 @@ Removes an existing grading component from the class.
---
+
+
### Listing Components: `list_component`
Displays all grading components and their respective weights for a class.
@@ -115,7 +134,7 @@ Records a grade for a specific student in a particular assignment or exam compon
- `SCORE`: The score to be recorded. Note that score cannot exceed the max score of the component.
- **Example**:
- - `add_grade i/A1234567X c/Quiz 1 s/85` adds the grade of Quiz 1 for the student with the matric number of A1234567X with a score of 85.
+ - `add_grade i/A1234567X c/Quiz 1 s/45` adds the grade of Quiz 1 for the student with the matric number of A1234567X with a score of 45.
---
@@ -133,6 +152,8 @@ Removes a previously recorded grade for a specific student and component.
---
+
+
### Listing Grades: `list_grade`
Views all recorded grades for a specific student or all students, and final percentage calculation. If the weightage of components does not add up to 100% (i.e., the course is still in progress), "IP" (In Progress) will be shown instead of a final percentage.
@@ -168,30 +189,40 @@ The data from the student, component and grade lists are stored in `studentlist.
respectively, located in the `[JAR file location]/data/` directory.
---
-## Notes:
-- Matric Number (`i/` argument) is case insensitive. Therefore, `A1234567X` is the same as `a1234567x`. Matric numbers
-will be converted to uppercase for storage.
-- (coming soon) All other arguments are case insensitive and will be converted to lowercase for storage.
+
+
+
## FAQ
**Q**: How do I transfer my data to another computer?
**A**: To transfer data, simply copy the `TutorLink` home folder (where the `.jar` file and data files are located) to your new computer. Then, download Java 17 (if not already installed), place the `.jar` file in the copied folder, and run `java -jar TutorLink.jar` from that folder.
+**Q**: Can I update data by directly editing the data files?
+
+**A**: Do so at your own risk. If changes to the data file alter its format, invalid file lines will discarded
+during startup, and displayed in the command line for verification. While TutorLink can detect most invalid file entries,
+certain edits can cause unexpected behaviour. Therefore, it is not recommended to edit the data files
+unless you are confident you can do so correctly.
+
---
+
+
## Command Summary
| **Command** | **Description** | **Example** |
|--------------------|--------------------------------------------------------------|---------------------------------------|
+| `help` | Displays list of commands | `help` |
| `add_student` | Adds a student to the class roster | `add_student i/A1234567X n/John Doe` |
| `delete_student` | Deletes a student from the class roster | `delete_student i/A1234567X` |
| `list_student` | Lists all students in the class | `list_student` |
| `find_student` | Finds a student in the class roster by name or matric number | `find_student i/A1234567X n/John Doe` |
| `add_component` | Adds a new grading component to the class | `add_component c/Quiz 1 w/30 m/50` |
| `delete_component` | Deletes a grading component from the class | `delete_component c/Quiz 1` |
+| `update_component` | Updates a component with a new maxscore or weight | `update_component c/Quiz 1 w/40 m/60` |
| `list_component` | Lists all grading components | `list_component` |
-| `add_grade` | Adds a grade for a student for a specific component | `add_grade i/A1234567X c/Quiz 1 s/85` |
+| `add_grade` | Adds a grade for a student for a specific component | `add_grade i/A1234567X c/Quiz 1 s/45` |
| `delete_grade` | Deletes a student's grade for a specific component | `delete_grade i/A1234567X c/Quiz 1` |
| `list_grade` | Lists all grades for a student | `list_grade i/A1234567X` |
| `bye` | Exits the program | `bye` |
diff --git a/docs/diagrams/AddComponentCommand.png b/docs/diagrams/AddComponentCommand.png
index c8b629e640..79410cee8a 100644
Binary files a/docs/diagrams/AddComponentCommand.png and b/docs/diagrams/AddComponentCommand.png differ
diff --git a/docs/diagrams/AddComponentCommand.puml b/docs/diagrams/AddComponentCommand.puml
index 937efac9b8..22f5d08e01 100644
--- a/docs/diagrams/AddComponentCommand.puml
+++ b/docs/diagrams/AddComponentCommand.puml
@@ -19,6 +19,8 @@ C -> H : get("m/")
activate H
C <-- H : maxScoreNumber
deactivate H
+destroy H
+
opt componentName or weightageNumber or maxScoreNumber is null
[<-- C : throw IllegalValueException
@@ -47,4 +49,6 @@ deactivate R
[<-- C : CommandResult
end
+destroy R
+destroy C
@enduml
diff --git a/docs/diagrams/AddGradeCommand.png b/docs/diagrams/AddGradeCommand.png
index 04fa4b9e7f..57888a95d4 100644
Binary files a/docs/diagrams/AddGradeCommand.png and b/docs/diagrams/AddGradeCommand.png differ
diff --git a/docs/diagrams/AddGradeCommand.puml b/docs/diagrams/AddGradeCommand.puml
index f30026a9f1..f50c218b67 100644
--- a/docs/diagrams/AddGradeCommand.puml
+++ b/docs/diagrams/AddGradeCommand.puml
@@ -55,6 +55,7 @@ deactivate R
deactivate C
end
-
+destroy R
+destroy C
@enduml
\ No newline at end of file
diff --git a/docs/diagrams/AddStudentCommand.png b/docs/diagrams/AddStudentCommand.png
index e8aa060e31..1ea2eb7de9 100644
Binary files a/docs/diagrams/AddStudentCommand.png and b/docs/diagrams/AddStudentCommand.png differ
diff --git a/docs/diagrams/AddStudentCommand.puml b/docs/diagrams/AddStudentCommand.puml
index ca7ea1350b..f30a281df5 100644
--- a/docs/diagrams/AddStudentCommand.puml
+++ b/docs/diagrams/AddStudentCommand.puml
@@ -21,4 +21,6 @@ deactivate R
[<-- C : CommandResult
deactivate C
end
+destroy R
+destroy C
@enduml
\ No newline at end of file
diff --git a/docs/diagrams/ArchitectureSequenceGrouped.png b/docs/diagrams/ArchitectureSequenceGrouped.png
index 3dd7681025..606270e5c4 100644
Binary files a/docs/diagrams/ArchitectureSequenceGrouped.png and b/docs/diagrams/ArchitectureSequenceGrouped.png differ
diff --git a/docs/diagrams/ArchitectureSequenceGrouped.puml b/docs/diagrams/ArchitectureSequenceGrouped.puml
index 04a9bfd713..90c3d69eda 100644
--- a/docs/diagrams/ArchitectureSequenceGrouped.puml
+++ b/docs/diagrams/ArchitectureSequenceGrouped.puml
@@ -1 +1 @@
-@startuml
!include Style.puml
participant ":TutorLink" as TL LOGIC_COLOR_5
participant ":Ui" as UI LOGIC_COLOR_6
participant ":Parser" as P LOGIC_COLOR_7
participant ":XYZCommand" as C LOGIC_COLOR_3
participant ":AppState" as A LOGIC_COLOR_2
participant ":XYZStorage" as S LOGIC_COLOR_4
[->TL
activate TL
ref over TL, UI : setup
loop until exit command issued
TL -> UI: getUserInput()
activate UI
TL <-- UI: line
deactivate UI
TL -> P : getCommand(line)
activate P
create C
P -> C
activate C
P <-- C : XYZCommand
deactivate C
TL <-- P: XYZCommand
deactivate P
TL -> C : getArgumentPrefixes()
activate C
TL <-- C : argumentPrefixes
deactivate C
TL -> P: getArguments(argumentPrefixes, line)
activate P
TL <-- P : HashMap
deactivate P
TL -> C : execute(appState, arguments)
activate C
ref over C, A : specific command execution
alt command execution success
TL <-- C : CommandResult
deactivate C
TL -> UI : displayResult()
activate UI
TL <-- UI
deactivate UI
else TutorLinkException
TL -> UI : displayException(exception)
activate UI
TL <-- UI
deactivate UI
end
TL -> TL : saveAllLists()
activate TL
TL -> S : save Student, Component and Grade Lists
activate S
TL <-- S
deactivate S
TL --> TL
deactivate TL
end
TL->UI: displayGoodbyeMessage()
activate UI
TL <-- UI
deactivate UI
[<--TL
deactivate TL
@enduml
\ No newline at end of file
+@startuml
!include Style.puml
participant ":TutorLink" as TL LOGIC_COLOR_5
participant ":Ui" as UI LOGIC_COLOR_6
participant ":Parser" as P LOGIC_COLOR_7
participant ":XYZCommand" as C LOGIC_COLOR_3
participant ":AppState" as A LOGIC_COLOR_2
participant ":XYZStorage" as S LOGIC_COLOR_4
[->TL
activate TL
ref over TL, UI : setup
loop until exit command issued
TL -> UI: getUserInput()
activate UI
TL <-- UI: line
deactivate UI
TL -> P : getCommand(line)
activate P
create C
P -> C
activate C
P <-- C : XYZCommand
deactivate C
TL <-- P: XYZCommand
deactivate P
TL -> C : getArgumentPrefixes()
activate C
TL <-- C : argumentPrefixes
deactivate C
TL -> P: getArguments(argumentPrefixes, line)
activate P
TL <-- P : HashMap
deactivate P
TL -> C : execute(appState, arguments)
activate C
ref over C, A : specific command execution
alt command execution success
TL <-- C : CommandResult
deactivate C
destroy C
TL -> UI : displayResult()
activate UI
TL <-- UI
deactivate UI
else TutorLinkException
TL -> UI : displayException(exception)
activate UI
TL <-- UI
deactivate UI
end
TL -> TL : saveAllLists()
activate TL
TL -> S : save Student, Component and Grade Lists
activate S
TL <-- S
deactivate S
TL --> TL
deactivate TL
end
TL->UI: displayGoodbyeMessage()
activate UI
TL <-- UI
deactivate UI
[<--TL
deactivate TL
@enduml
\ No newline at end of file
diff --git a/docs/diagrams/DeleteComponentCommand.png b/docs/diagrams/DeleteComponentCommand.png
index 05beaca33d..880eeaf8a2 100644
Binary files a/docs/diagrams/DeleteComponentCommand.png and b/docs/diagrams/DeleteComponentCommand.png differ
diff --git a/docs/diagrams/DeleteComponentCommand.puml b/docs/diagrams/DeleteComponentCommand.puml
index 333b1586d7..fe7e83aa80 100644
--- a/docs/diagrams/DeleteComponentCommand.puml
+++ b/docs/diagrams/DeleteComponentCommand.puml
@@ -13,6 +13,7 @@ C -> H : get("n/")
activate H
C <-- H : componentName
deactivate H
+destroy H
opt componentName is null
[<-- C : throw IllegalValueException
@@ -43,4 +44,7 @@ deactivate R
[<-- C : CommandResult
end
+destroy R
+destroy C
+
@enduml
diff --git a/docs/diagrams/DeleteGradeCommand.png b/docs/diagrams/DeleteGradeCommand.png
index c266847ef4..173a4e3338 100644
Binary files a/docs/diagrams/DeleteGradeCommand.png and b/docs/diagrams/DeleteGradeCommand.png differ
diff --git a/docs/diagrams/DeleteGradeCommand.puml b/docs/diagrams/DeleteGradeCommand.puml
index c0a64fe95d..be71af4c27 100644
--- a/docs/diagrams/DeleteGradeCommand.puml
+++ b/docs/diagrams/DeleteGradeCommand.puml
@@ -39,6 +39,7 @@ deactivate R
deactivate C
end
-
+destroy R
+destroy C
@enduml
diff --git a/docs/diagrams/DeleteStudentCommand.png b/docs/diagrams/DeleteStudentCommand.png
index c45dbd2c11..f8ddb273db 100644
Binary files a/docs/diagrams/DeleteStudentCommand.png and b/docs/diagrams/DeleteStudentCommand.png differ
diff --git a/docs/diagrams/DeleteStudentCommand.puml b/docs/diagrams/DeleteStudentCommand.puml
index 3d62ed8222..35fed323c7 100644
--- a/docs/diagrams/DeleteStudentCommand.puml
+++ b/docs/diagrams/DeleteStudentCommand.puml
@@ -30,4 +30,6 @@ deactivate R
[<-- C : CommandResult
deactivate C
end
+destroy R
+destroy C
@enduml
\ No newline at end of file
diff --git a/docs/diagrams/FindStudentCommand.png b/docs/diagrams/FindStudentCommand.png
index 96796ad16e..4190c9e7af 100644
Binary files a/docs/diagrams/FindStudentCommand.png and b/docs/diagrams/FindStudentCommand.png differ
diff --git a/docs/diagrams/FindStudentCommand.puml b/docs/diagrams/FindStudentCommand.puml
index 70d8b09aae..140c97a80d 100644
--- a/docs/diagrams/FindStudentCommand.puml
+++ b/docs/diagrams/FindStudentCommand.puml
@@ -30,4 +30,6 @@ deactivate R
[<-- C : CommandResult
deactivate C
end
+destroy R
+destroy C
@enduml
\ No newline at end of file
diff --git a/docs/diagrams/GradeStorage.png b/docs/diagrams/GradeStorage.png
index fd9571113f..df06a5455c 100644
Binary files a/docs/diagrams/GradeStorage.png and b/docs/diagrams/GradeStorage.png differ
diff --git a/docs/diagrams/GradeStorage.puml b/docs/diagrams/GradeStorage.puml
index d3772102d7..e24a8d0522 100644
--- a/docs/diagrams/GradeStorage.puml
+++ b/docs/diagrams/GradeStorage.puml
@@ -2,13 +2,22 @@
!include Style.puml
participant ":TutorLink" as TL LOGIC_COLOR_5
+participant ":Ui" as UI LOGIC_COLOR_6
+
participant "gradeStorage: GradeStorage" as GS LOGIC_COLOR_4
-participant "grades: ArrayList" as AL LOGIC_COLOR_6
-[->TL : setupAllLists()
-activate TL
+participant "grades: ArrayList" as AL LOGIC_COLOR_3
+participant "discardedEntries: ArrayList" as DE LOGIC_COLOR_7
+
+group sd load grade data
+
create GS
-TL -> GS: new GradeStorage(GRADE_FILE_PATH, initialComponentList, initialStudentList)
+TL -> GS: new GradeStorage(filepath, components, students)
activate GS
+create DE
+GS -> DE: new ArrayList()
+activate DE
+GS <-- DE: discardedEntries: String
+deactivate DE
TL <-- GS: gradeStorage: GradeStorage
deactivate GS
@@ -21,9 +30,7 @@ activate AL
GS <-- AL: grades: Grade
deactivate AL
-
-
-loop fileScanner.hasNext()
+loop file has next line
GS -> GS: getGradeFromFileLine(currentLine: String)
activate GS
GS --> GS: newGrade: Grade
@@ -35,14 +42,27 @@ loop fileScanner.hasNext()
GS <-- AL
deactivate AL
else InvalidDataFileLineException
+ GS -> DE: add(currentLine: String)
+ activate DE
+ GS <-- DE
+ deactivate DE
end
end
-
TL <-- GS: grades: Grade
deactivate GS
-[<--TL
-deactivate TL
+
+TL -> GS: getDiscardedEntries()
+activate GS
+TL <-- GS: discardedGrades: String
+deactivate GS
+
+TL -> UI: displayDiscardedEntries(discardedGrades)
+activate UI
+TL <-- UI
+deactivate UI
+
+end
@enduml
diff --git a/docs/diagrams/Setup.png b/docs/diagrams/Setup.png
index 553055788a..eb41ba3f8c 100644
Binary files a/docs/diagrams/Setup.png and b/docs/diagrams/Setup.png differ
diff --git a/docs/diagrams/Setup.puml b/docs/diagrams/Setup.puml
index d66b1edbd4..8affaf4b31 100644
--- a/docs/diagrams/Setup.puml
+++ b/docs/diagrams/Setup.puml
@@ -8,49 +8,17 @@ participant "componentStorage:Storage" as CS LOGIC_COLOR_4
participant "gradeStorage:Storage" as GS LOGIC_COLOR_4
group sd setup
-[->TL : main()
-activate TL
-TL->UI: displayWelcomeMessage()
-activate UI
-TL <-- UI
-deactivate UI
alt setup success
- create SS
- TL -> SS : new StudentStorage
- activate SS
- TL <-- SS : StudentStorage
- deactivate SS
-
- create CS
- TL -> CS : new ComponentStorage
- activate CS
- TL <-- CS :ComponentStorage
- deactivate CS
-
- create GS
- TL -> GS : new ComponentStorage
- activate GS
- TL <-- GS :ComponentStorage
- deactivate GS
- TL -> SS : loadStudentList()
- activate SS
- TL <-- SS : ArrayList
- deactivate SS
+ ref over TL, UI, SS: load student data
- TL -> CS : loadComponentList()
- activate CS
- TL <-- CS : ArrayList
- deactivate CS
+ ref over TL, UI, CS: load component data
- TL -> GS : loadGradeList()
- activate GS
- TL <-- GS : ArrayList
- deactivate GS
+ ref over TL, UI, GS: load grade data
create A
- TL -> A : new AppState(ArrayList, ArrayList, ArrayList)
+ TL -> A : new AppState(students, grades, components)
activate A
TL <-- A : appState
deactivate A
@@ -62,7 +30,11 @@ else TutorLinkException
end
deactivate UI
-[<-- TL
-deactivate TL
+
+TL->UI: displayWelcomeMessage()
+activate UI
+TL <-- UI
+deactivate UI
+
end
-@enduml
\ No newline at end of file
+@enduml
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index b74887daa8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bui The Trung - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/trungbui32.md b/docs/team/trungbui32.md
new file mode 100644
index 0000000000..11c742b18f
--- /dev/null
+++ b/docs/team/trungbui32.md
@@ -0,0 +1,73 @@
+# Bui The Trung - Project Portfolio Page
+
+## Project: TutorLink
+
+TutorLink is a desktop CLI application designed to help
+University professors better manage the grades of students
+reading their course.
+
+TutorLink keeps a running tally of grades (GPA, overall score) of each
+`Student` by tagging `Components` to each student via a `Grade` object.
+
+TutorLink provides useful summary statistics of a professor's course that
+can be further filtered down for analysis.
+
+### Summary of Contributions
+
+- **Command**:
+ - `ListGradeCommand` command:
+ - Lists all student grades in the system, showing each student's name, matriculation number, component scores,
+ and
+ final percentage score in a formatted report
+ - Can filter to show grades for a specific student when given their matriculation number (using i/ prefix),
+ displaying their individual component scores with maximum possible points and final percentage score
+ - `ListComponentCommand` command:
+ - Simply lists all assessment components in the system by returning the string representation of the components
+ stored in AppState
+ - Takes no arguments/parameters (returns null for argument prefixes) and displays components directly from the
+ application's state
+- **Component**:
+ - `Component`:
+ - Represents an assessment component in the system with three attributes: name (identifier), maxScore (maximum
+ possible points), and weight (percentage contribution to final grade)
+ - Provides getter/setter methods for its attributes and overrides equals() to compare components by name (
+ case-insensitive) and toString() to display the component's details in format "name (maxScore: X, weight: Y%)"
+ - `Grade`:
+ - Represents a student's grade for a specific component, linking three pieces of information together: the
+ student who received the grade, the component being graded, and the actual score (with automatic capping at
+ component's max score)
+ - Provides methods to access grade information and overrides equals() to compare grades based on both component
+ and student (two grades are equal if they're for the same component and student) and toString() to display all
+ grade details in a formatted string
+- **Diagram**:
+ - `DeleteComponentCommandUML`:
+ - UML diagram for DeleteComponentCommand command
+ - `AddComponentCommandUML`
+ - UML diagram for AddComponentCommandUML
+- **Test**:
+ - `ComponentTest`:
+ - Tests the constructor and getter methods of the Component class by creating test components ("Assignment 1"
+ and "
+ Final
+ Exam") and verifying their name, maxScore, and weight values are correctly stored
+ - Tests the equals() method by verifying that components with the same name (case-insensitive) are considered
+ equal
+ while components with different names, null values, or different object types are considered not equal
+
+- **Code contribution
+ **: [RepoSense](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=TrungBui32&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-09-20&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+- Project Management:
+ - Assisted with release `V2.0`
+ - PRs reviewed: [#51](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/51)
+- Enhancement to existing features:
+ - Wrote tests for Component [#77](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/77)
+- **Documentation**:
+ - Developer Guide:
+ - Create UML for DeleteComponentCommand [#107](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/107) and
+ AddComponentCommand [#105](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/105)
+ - Non-Functional Requirements
+- **Features implemented:**
+ - Implemented ListGradeCommand [#91](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/91) and
+ ListComponentCommand [#83](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/83)
+ - Implemented Component [#49](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/49) and
+ Grade [#55](https://github.com/AY2425S1-CS2113-W13-4/tp/pull/55)
\ No newline at end of file
diff --git a/src/main/java/tutorlink/TutorLink.java b/src/main/java/tutorlink/TutorLink.java
index cec0abce5c..ce30f4da38 100644
--- a/src/main/java/tutorlink/TutorLink.java
+++ b/src/main/java/tutorlink/TutorLink.java
@@ -94,20 +94,21 @@ private static void setupAllLists() throws IOException, StorageOperationExceptio
studentStorage = new StudentStorage(STUDENT_FILE_PATH);
ArrayList initialStudentList = studentStorage.loadStudentList();
ArrayList discardedStudents = studentStorage.getDiscardedEntries();
+ ui.displayDiscardedEntries(discardedStudents, "Discarded student data:");
componentStorage = new ComponentStorage(COMPONENT_FILE_PATH);
ArrayList initialComponentList = componentStorage.loadComponentList();
ArrayList discardedComponents = componentStorage.getDiscardedEntries();
+ ui.displayDiscardedEntries(discardedComponents, "Discarded component data:");
gradeStorage = new GradeStorage(GRADE_FILE_PATH, initialComponentList, initialStudentList);
ArrayList initialGradeList = gradeStorage.loadGradeList();
ArrayList discardedGrades = gradeStorage.getDiscardedEntries();
-
- ui.displayDiscardedEntries(discardedStudents, "Discarded student data:");
- ui.displayDiscardedEntries(discardedComponents, "Discarded component data:");
ui.displayDiscardedEntries(discardedGrades, "Discarded grade data:");
appState = new AppState(initialStudentList, initialGradeList, initialComponentList);
+ appState.updateAllStudentPercentageScores();
+
}
private static void saveAllLists() throws IOException {
diff --git a/src/main/java/tutorlink/appstate/AppState.java b/src/main/java/tutorlink/appstate/AppState.java
index 57bc28ca3e..788eb30e91 100644
--- a/src/main/java/tutorlink/appstate/AppState.java
+++ b/src/main/java/tutorlink/appstate/AppState.java
@@ -27,4 +27,12 @@ public AppState(ArrayList studentArrayList,
grades = new GradeList(gradeArrayList);
components = new ComponentList(componentArrayList);
}
+
+ public void updateAllStudentPercentageScores() {
+ for (Student student : students.getStudentArrayList()) {
+ student.setPercentageScore(
+ grades.calculateStudentPercentageScore(student.getMatricNumber(), components)
+ );
+ }
+ }
}
diff --git a/src/main/java/tutorlink/command/AddComponentCommand.java b/src/main/java/tutorlink/command/AddComponentCommand.java
index 42eaa8a422..94c08fc884 100644
--- a/src/main/java/tutorlink/command/AddComponentCommand.java
+++ b/src/main/java/tutorlink/command/AddComponentCommand.java
@@ -10,65 +10,135 @@
import tutorlink.exceptions.TutorLinkException;
import tutorlink.result.CommandResult;
+/**
+ * Command to add a new component to the list of grade components in the application.
+ * The component includes a name, weightage, and maximum score.
+ */
public class AddComponentCommand extends Command {
public static final String[] ARGUMENT_PREFIXES = {"c/", "w/", "m/"};
public static final String COMMAND_WORD = "add_component";
- private static final int MAX_WEIGHT = 100;
+ /**
+ * Executes the add component command, creating a new component with the specified name,
+ * weightage, and maximum score.
+ *
+ * @param appState The current state of the application.
+ * @param hashmap Contains the arguments passed to the command.
+ * @return The result of executing the add component command.
+ * @throws TutorLinkException If there is an error in processing the command, including invalid values.
+ */
@Override
public CommandResult execute(AppState appState, HashMap hashmap) throws TutorLinkException {
String componentName = hashmap.get(ARGUMENT_PREFIXES[0]);
String weightageNumber = hashmap.get(ARGUMENT_PREFIXES[1]);
String maxScoreNumber = hashmap.get(ARGUMENT_PREFIXES[2]);
+
+ validateArguments(componentName, weightageNumber, maxScoreNumber);
+
+ int weightage = parseAndValidateWeightage(weightageNumber, appState);
+ double maxScore = parseMaxScore(maxScoreNumber);
+
+ addComponentToAppState(appState, componentName, weightage, maxScore);
+
+ return new CommandResult(
+ String.format(Commons.ADD_COMPONENT_SUCCESS, componentName, weightageNumber, maxScoreNumber));
+ }
+
+ /**
+ * Validates that all required arguments are present.
+ *
+ * @param componentName The name of the component.
+ * @param weightageNumber The weightage of the component as a string.
+ * @param maxScoreNumber The maximum score of the component as a string.
+ * @throws IllegalValueException If any argument is null.
+ */
+ private void validateArguments(String componentName, String weightageNumber, String maxScoreNumber)
+ throws IllegalValueException {
if (componentName == null || weightageNumber == null || maxScoreNumber == null) {
throw new IllegalValueException(Commons.ERROR_NULL);
}
+ }
- int weightage = convertWeightageToValidInt(weightageNumber);
-
- if((weightage + Component.totalWeight) > MAX_WEIGHT) {
- throw new InvalidWeightingException(String.format(Commons.ERROR_INVALID_TOTAL_WEIGHTING,
- Component.totalWeight + weightage));
- } else {
- Component.totalWeight += weightage;
+ /**
+ * Parses the weightage from a string to an integer and validates it.
+ * Also checks if adding this weightage would exceed the maximum allowed total weight.
+ *
+ * @param weightageNumber The weightage of the component as a string.
+ * @param appState The current state of the application to get total weight.
+ * @return The validated weightage as an integer.
+ * @throws InvalidWeightingException If the total weight exceeds the maximum allowed.
+ * @throws IllegalValueException If the weightage is invalid.
+ */
+ private int parseAndValidateWeightage(String weightageNumber, AppState appState) throws TutorLinkException {
+ int weightage = parseWeightage(weightageNumber);
+ int totalWeight = weightage + appState.components.getTotalWeighting();
+
+ if (totalWeight > Commons.MAX_WEIGHT) {
+ throw new InvalidWeightingException(String.format(Commons.ERROR_INVALID_TOTAL_WEIGHTING, totalWeight));
}
- double maxScore = convertMaxScoreToValidDouble(maxScoreNumber);
- appState.components.addComponent(new Component(componentName, maxScore, weightage));
- return new CommandResult(String.format(Commons.ADD_COMPONENT_SUCCESS,
- componentName, weightageNumber, maxScoreNumber));
+ return weightage;
}
- private static int convertWeightageToValidInt(String weightageNumber) {
+ /**
+ * Adds the component with the specified attributes to the application's state and updates all
+ * student percentage scores.
+ *
+ * @param appState The current state of the application.
+ * @param componentName The name of the component.
+ * @param weightage The validated weightage of the component.
+ * @param maxScore The validated maximum score of the component.
+ */
+ private void addComponentToAppState(AppState appState, String componentName, int weightage, double maxScore) {
+ Component component = new Component(componentName, maxScore, weightage);
+ appState.components.addComponent(component);
+ appState.updateAllStudentPercentageScores();
+ }
+
+ /**
+ * Parses the weightage from a string to an integer and validates the value.
+ *
+ * @param weightageNumber The weightage of the component as a string.
+ * @return The validated weightage as an integer.
+ * @throws IllegalValueException If the weightage is not a valid integer within the allowed range.
+ */
+ private int parseWeightage(String weightageNumber) throws IllegalValueException {
try {
- int weightage =Integer.parseInt(weightageNumber);
+ int weightage = Integer.parseInt(weightageNumber);
if (weightage < 0 || weightage > 100) {
throw new IllegalValueException(Commons.ERROR_INVALID_WEIGHTAGE);
}
return weightage;
-
} catch (NumberFormatException e) {
throw new IllegalValueException(Commons.ERROR_INVALID_WEIGHTAGE);
}
}
- private static double convertMaxScoreToValidDouble(String maxScoreNumber) {
+ /**
+ * Parses the maximum score from a string to a double and validates the value.
+ *
+ * @param maxScoreNumber The maximum score of the component as a string.
+ * @return The validated maximum score as a double.
+ * @throws IllegalValueException If the maximum score is not a valid double or is negative.
+ */
+ private double parseMaxScore(String maxScoreNumber) throws IllegalValueException {
try {
double maxScore = Double.parseDouble(maxScoreNumber);
-
if (maxScore < 0.0) {
throw new IllegalValueException(Commons.ERROR_INVALID_MAX_SCORE);
}
-
return maxScore;
-
} catch (NumberFormatException e) {
throw new IllegalValueException(Commons.ERROR_INVALID_MAX_SCORE);
}
}
-
+ /**
+ * Returns the argument prefixes for this command.
+ *
+ * @return An array of argument prefixes.
+ */
@Override
public String[] getArgumentPrefixes() {
return ARGUMENT_PREFIXES;
diff --git a/src/main/java/tutorlink/command/AddGradeCommand.java b/src/main/java/tutorlink/command/AddGradeCommand.java
index d23b57555e..40eccdee3d 100644
--- a/src/main/java/tutorlink/command/AddGradeCommand.java
+++ b/src/main/java/tutorlink/command/AddGradeCommand.java
@@ -21,102 +21,166 @@
import static tutorlink.lists.StudentList.STUDENT_NOT_FOUND;
+/**
+ * Command to add a grade to a student's record based on their matriculation number,
+ * the specified component description, and score.
+ */
public class AddGradeCommand extends Command {
public static final String[] ARGUMENT_PREFIXES = {"i/", "c/", "s/"};
public static final String COMMAND_WORD = "add_grade";
private static final String ERROR_DUPLICATE_GRADE_ON_ADD = "Error! Grade (%s, %s) already exists in the list!";
+ /**
+ * Executes the add grade command, creating a new grade record for the specified student
+ * and updating their GPA.
+ *
+ * @param appState The current state of the application.
+ * @param hashmap Contains the arguments passed to the command.
+ * @return The result of executing the add grade command.
+ * @throws TutorLinkException If there is an error in processing the command, including invalid arguments or
+ * component/student not found.
+ */
@Override
- public CommandResult execute(AppState appstate, HashMap hashmap) throws TutorLinkException {
+ public CommandResult execute(AppState appState, HashMap hashmap) throws TutorLinkException {
String matricNumber = hashmap.get(ARGUMENT_PREFIXES[0]);
String componentDescription = hashmap.get(ARGUMENT_PREFIXES[1]);
String scoreNumber = hashmap.get(ARGUMENT_PREFIXES[2]);
- if (matricNumber == null || componentDescription == null || scoreNumber == null) {
- throw new IllegalValueException(Commons.ERROR_NULL);
- }
- matricNumber = matricNumber.toUpperCase();
- Pattern pattern = Pattern.compile(Commons.MATRIC_NUMBER_REGEX);
- Matcher matcher = pattern.matcher(matricNumber);
- if (!matcher.find()) {
- throw new IllegalValueException(Commons.ERROR_ILLEGAL_MATRIC_NUMBER);
- }
- Component component = findComponentFromComponents(appstate, componentDescription);
+ validateArguments(matricNumber, componentDescription, scoreNumber);
- assert component != null : "Component object should not be null after this point";
+ matricNumber = formatMatricNumber(matricNumber);
- Student student = findStudentFromStudents(appstate, matricNumber);
+ Component component = findComponent(appState, componentDescription);
+ Student student = findStudent(appState, matricNumber);
- assert student != null : "Student object should not be null after this point";
-
- //Convert scoreNumber to double
- try {
- double score = convertScoreToValidDouble(scoreNumber, component);
+ double score = parseScore(scoreNumber, component);
- //create a new grade object
- Grade grade = new Grade(component, student, score);
+ addGradeAndCalculateGPA(appState, component, student, score);
- appstate.grades.addGrade(grade);
-
- double newGPA = appstate.grades.calculateStudentGPA(
- student.getMatricNumber(),
- appstate.components
- );
-
- student.setGpa(newGPA);
-
- } catch (NumberFormatException e) {
- throw new IllegalValueException(Commons.ERROR_INVALID_SCORE);
- }
-
- return new CommandResult(String.format(Commons.ADD_GRADE_SUCCESS, scoreNumber, componentDescription,
- matricNumber));
+ return new CommandResult(
+ String.format(Commons.ADD_GRADE_SUCCESS, scoreNumber, componentDescription, matricNumber));
}
- private static double convertScoreToValidDouble(String scoreNumber, Component component)
+ /**
+ * Validates that all required arguments are present.
+ *
+ * @param matricNumber The matriculation number of the student.
+ * @param componentDescription The description of the grade component.
+ * @param scoreNumber The score for the grade component.
+ * @throws IllegalValueException If any argument is null.
+ */
+ private void validateArguments(String matricNumber, String componentDescription, String scoreNumber)
throws IllegalValueException {
- double score = Double.parseDouble(scoreNumber);
+ if (matricNumber == null || componentDescription == null || scoreNumber == null) {
+ throw new IllegalValueException(Commons.ERROR_NULL);
+ }
+ }
- if (score < 0.0 || score > component.getMaxScore()) {
- throw new IllegalValueException(Commons.ERROR_INVALID_SCORE);
+ /**
+ * Formats and validates the matriculation number.
+ *
+ * @param matricNumber The matriculation number to format and validate.
+ * @return The formatted matriculation number.
+ * @throws IllegalValueException If the matriculation number does not match the required format.
+ */
+ private String formatMatricNumber(String matricNumber) throws IllegalValueException {
+ matricNumber = matricNumber.toUpperCase();
+ Pattern pattern = Pattern.compile(Commons.MATRIC_NUMBER_REGEX);
+ Matcher matcher = pattern.matcher(matricNumber);
+ if (!matcher.find()) {
+ throw new IllegalValueException(Commons.ERROR_ILLEGAL_MATRIC_NUMBER);
}
- return score;
+ return matricNumber;
}
- private static Student findStudentFromStudents(AppState appstate, String matricNumber)
- throws StudentNotFoundException {
- //Get Student student object using String matricNumber
- StudentList studentFilteredList = appstate.students.findStudentByMatricNumber(matricNumber);
+ /**
+ * Finds a component based on its description.
+ *
+ * @param appState The current state of the application.
+ * @param componentDescription The description of the component to find.
+ * @return The component with the specified description.
+ * @throws DuplicateComponentException If multiple components are found with the same description.
+ * @throws ComponentNotFoundException If no component is found with the specified description.
+ */
+ private Component findComponent(AppState appState, String componentDescription)
+ throws DuplicateComponentException, ComponentNotFoundException {
+ ComponentList componentFilteredList = appState.components.findComponent(componentDescription);
+ if (componentFilteredList.size() == 1) {
+ return componentFilteredList.getComponentArrayList().get(0);
+ } else if (componentFilteredList.size() == 0) {
+ throw new ComponentNotFoundException(
+ String.format(Commons.ERROR_COMPONENT_NOT_FOUND, componentDescription));
+ } else {
+ throw new DuplicateComponentException(
+ String.format(Commons.ERROR_MULTIPLE_QUERY_RESULT, componentDescription));
+ }
+ }
- Student student;
+ /**
+ * Finds a student based on their matriculation number.
+ *
+ * @param appState The current state of the application.
+ * @param matricNumber The matriculation number of the student to find.
+ * @return The student with the specified matriculation number.
+ * @throws StudentNotFoundException If no student with the matriculation number is found.
+ * @throws DuplicateMatricNumberException If more than one student is found with the same matriculation number.
+ */
+ private Student findStudent(AppState appState, String matricNumber)
+ throws StudentNotFoundException, DuplicateMatricNumberException {
+ StudentList studentFilteredList = appState.students.findStudentByMatricNumber(matricNumber);
if (studentFilteredList.size() == 1) {
- student = studentFilteredList.getStudentArrayList().get(0);
+ return studentFilteredList.getStudentArrayList().get(0);
} else if (studentFilteredList.size() == 0) {
throw new StudentNotFoundException(String.format(STUDENT_NOT_FOUND, matricNumber));
} else {
- String errorMessage = String.format(Commons.ERROR_DUPLICATE_STUDENT, matricNumber);
- throw new DuplicateMatricNumberException(errorMessage);
+ throw new DuplicateMatricNumberException(String.format(Commons.ERROR_DUPLICATE_STUDENT, matricNumber));
}
- return student;
}
- private static Component findComponentFromComponents(AppState appstate, String componentDescription)
- throws DuplicateComponentException {
- //Get component object using String componentDescription
- ComponentList componentFilteredList = appstate.components.findComponent(componentDescription);
- Component component;
- if (componentFilteredList.size() == 1) {
- component = componentFilteredList.getComponentArrayList().get(0);
- } else if (componentFilteredList.size() == 0) {
- throw new ComponentNotFoundException(String.format(Commons.ERROR_COMPONENT_NOT_FOUND,
- componentDescription));
- } else {
- String errorMessage = String.format(Commons.ERROR_DUPLICATE_COMPONENT, componentDescription);
- throw new DuplicateComponentException(errorMessage);
+ /**
+ * Converts the score string to a double and validates it against the component's maximum score.
+ *
+ * @param scoreNumber The score in string format.
+ * @param component The component associated with the score.
+ * @return The score as a valid double.
+ * @throws IllegalValueException If the score is invalid (e.g., non-numeric or out of range).
+ */
+ private double parseScore(String scoreNumber, Component component) throws IllegalValueException {
+ try {
+ double score = Double.parseDouble(scoreNumber);
+ if (score < 0.0 || score > component.getMaxScore()) {
+ throw new IllegalValueException(Commons.ERROR_INVALID_SCORE);
+ }
+ return score;
+ } catch (NumberFormatException e) {
+ throw new IllegalValueException(Commons.ERROR_INVALID_SCORE);
}
- return component;
}
+ /**
+ * Adds the grade to the student's record and updates the student's GPA.
+ *
+ * @param appState The current state of the application.
+ * @param component The component for which the grade is being added.
+ * @param student The student receiving the grade.
+ * @param score The score achieved by the student in the specified component.
+ * @throws TutorLinkException If the grade already exists.
+ */
+ private void addGradeAndCalculateGPA(AppState appState, Component component, Student student, double score)
+ throws TutorLinkException {
+ Grade grade = new Grade(component, student, score);
+ appState.grades.addGrade(grade);
+
+ double newPercentageScore = appState.grades.calculateStudentPercentageScore(student.getMatricNumber(),
+ appState.components);
+ student.setPercentageScore(newPercentageScore);
+ }
+
+ /**
+ * Returns the argument prefixes for this command.
+ *
+ * @return An array of argument prefixes.
+ */
@Override
public String[] getArgumentPrefixes() {
return ARGUMENT_PREFIXES;
diff --git a/src/main/java/tutorlink/command/DeleteComponentCommand.java b/src/main/java/tutorlink/command/DeleteComponentCommand.java
index 23c32b0f3f..254dbce409 100644
--- a/src/main/java/tutorlink/command/DeleteComponentCommand.java
+++ b/src/main/java/tutorlink/command/DeleteComponentCommand.java
@@ -4,7 +4,6 @@
import tutorlink.appstate.AppState;
import tutorlink.commons.Commons;
import tutorlink.exceptions.ComponentNotFoundException;
-import tutorlink.exceptions.DuplicateComponentException;
import tutorlink.exceptions.IllegalValueException;
import tutorlink.exceptions.TutorLinkException;
import tutorlink.lists.ComponentList;
@@ -23,10 +22,10 @@ public CommandResult execute(AppState appState, HashMap hashmap)
ComponentList componentsToDelete = appState.components.findComponent(componentName);
if(componentsToDelete.size() == 0) {
throw new ComponentNotFoundException(String.format(Commons.ERROR_COMPONENT_NOT_FOUND, componentName));
- } else if (componentsToDelete.size() > 1) {
- throw new DuplicateComponentException(Commons.ERROR_DUPLICATE_COMPONENT);
}
appState.components.deleteComponent(componentsToDelete.getComponentArrayList().get(0));
+ appState.grades.deleteGradesByComponent(componentName);
+ appState.updateAllStudentPercentageScores();
return new CommandResult(String.format(Commons.DELETE_COMPONENT_SUCCESS, componentName));
}
diff --git a/src/main/java/tutorlink/command/DeleteGradeCommand.java b/src/main/java/tutorlink/command/DeleteGradeCommand.java
index 926fb7bb66..656552a91e 100644
--- a/src/main/java/tutorlink/command/DeleteGradeCommand.java
+++ b/src/main/java/tutorlink/command/DeleteGradeCommand.java
@@ -16,25 +16,80 @@
import static tutorlink.lists.StudentList.STUDENT_NOT_FOUND;
+/**
+ * Command to delete a grade from a student's record based on the specified matriculation number
+ * and component description.
+ */
public class DeleteGradeCommand extends Command {
public static final String[] ARGUMENT_PREFIXES = {"i/", "c/"};
public static final String COMMAND_WORD = "delete_grade";
+
+ /**
+ * Executes the delete grade command, deleting a specific grade component for a student and updating their GPA.
+ *
+ * @param appState The current state of the application.
+ * @param hashmap Contains the arguments passed to the command.
+ * @return The result of executing the delete grade command.
+ * @throws TutorLinkException If there is an error in processing the command, including invalid arguments or
+ * student not found.
+ */
@Override
public CommandResult execute(AppState appState, HashMap hashmap) throws TutorLinkException {
String matricNumber = hashmap.get(ARGUMENT_PREFIXES[0]);
String componentDescription = hashmap.get(ARGUMENT_PREFIXES[1]);
+ validateArguments(matricNumber, componentDescription);
+ matricNumber = formatMatricNumber(matricNumber);
+
+ Student student = getStudent(appState, matricNumber);
+ deleteGrade(appState, matricNumber, componentDescription);
+
+ updateStudentGPA(appState, student, matricNumber);
+
+ return new CommandResult(String.format(Commons.DELETE_GRADE_SUCCESS, componentDescription, matricNumber));
+ }
+
+ /**
+ * Validates the required arguments for the delete grade command.
+ *
+ * @param matricNumber The matriculation number of the student.
+ * @param componentDescription The description of the grade component to be deleted.
+ * @throws IllegalValueException If any required argument is null.
+ */
+ private void validateArguments(String matricNumber, String componentDescription) throws IllegalValueException {
if (matricNumber == null || componentDescription == null) {
throw new IllegalValueException(Commons.ERROR_NULL);
}
+ }
+
+ /**
+ * Formats and validates the matriculation number.
+ *
+ * @param matricNumber The matriculation number to format.
+ * @return The formatted matriculation number.
+ * @throws IllegalValueException If the matriculation number does not match the required format.
+ */
+ private String formatMatricNumber(String matricNumber) throws IllegalValueException {
matricNumber = matricNumber.toUpperCase();
Pattern pattern = Pattern.compile(Commons.MATRIC_NUMBER_REGEX);
Matcher matcher = pattern.matcher(matricNumber);
if (!matcher.find()) {
throw new IllegalValueException(Commons.ERROR_ILLEGAL_MATRIC_NUMBER);
}
- //Check number of students with matricNumber
+ return matricNumber;
+ }
+
+ /**
+ * Finds a student based on their matriculation number.
+ *
+ * @param appState The current state of the application.
+ * @param matricNumber The matriculation number of the student.
+ * @return The student found with the specified matriculation number.
+ * @throws StudentNotFoundException If no student with the matriculation number is found.
+ * @throws DuplicateMatricNumberException If more than one student is found with the same matriculation number.
+ */
+ private static Student getStudent(AppState appState, String matricNumber) {
StudentList filteredList = appState.students.findStudentByMatricNumber(matricNumber);
if (filteredList.size() == 0) {
@@ -44,17 +99,37 @@ public CommandResult execute(AppState appState, HashMap hashmap)
throw new DuplicateMatricNumberException(errorMessage);
}
- //Attempt to delete grade
- appState.grades.deleteGrade(matricNumber, componentDescription);
+ return filteredList.getStudentArrayList().get(0);
+ }
- // Update student GPA
- Student student = filteredList.getStudentArrayList().get(0);
- double newGPA = appState.grades.calculateStudentGPA(matricNumber, appState.components);
- student.setGpa(newGPA);
+ /**
+ * Deletes the specified grade component for a student.
+ *
+ * @param appState The current state of the application.
+ * @param matricNumber The matriculation number of the student.
+ * @param componentDescription The description of the grade component to be deleted.
+ */
+ private void deleteGrade(AppState appState, String matricNumber, String componentDescription) {
+ appState.grades.deleteGrade(matricNumber, componentDescription);
+ }
- return new CommandResult(String.format(Commons.DELETE_GRADE_SUCCESS, componentDescription, matricNumber));
+ /**
+ * Updates the student's GPA by calculating their new percentage score after a grade component has been deleted.
+ *
+ * @param appState The current state of the application.
+ * @param student The student whose GPA is to be updated.
+ * @param matricNumber The matriculation number of the student.
+ */
+ private void updateStudentGPA(AppState appState, Student student, String matricNumber) {
+ double newGPA = appState.grades.calculateStudentPercentageScore(matricNumber, appState.components);
+ student.setPercentageScore(newGPA);
}
+ /**
+ * Returns the argument prefixes for this command.
+ *
+ * @return An array of argument prefixes.
+ */
@Override
public String[] getArgumentPrefixes() {
return ARGUMENT_PREFIXES;
diff --git a/src/main/java/tutorlink/command/InvalidCommand.java b/src/main/java/tutorlink/command/InvalidCommand.java
index 1d182c5cf9..130dc0ded8 100644
--- a/src/main/java/tutorlink/command/InvalidCommand.java
+++ b/src/main/java/tutorlink/command/InvalidCommand.java
@@ -8,13 +8,32 @@
import java.util.HashMap;
+/**
+ * Represents a command that is triggered when an invalid command word is provided.
+ * This command, when executed, throws an {@code InvalidCommandException} indicating
+ * that the command is not recognized.
+ */
public class InvalidCommand extends Command {
+ /**
+ * Executes the invalid command, resulting in an exception to notify the user
+ * that an invalid command has been entered.
+ *
+ * @param appState The current application state, not used by this command.
+ * @param hashMap A map of argument prefixes to their values, not used by this command.
+ * @return This method does not return normally, as it always throws an {@code InvalidCommandException}.
+ * @throws TutorLinkException Always thrown to indicate an invalid command.
+ */
@Override
- public CommandResult execute(AppState appState, HashMap hashMap) throws TutorLinkException {
+ public CommandResult execute(AppState appState, HashMap hashMap) throws TutorLinkException {
throw new InvalidCommandException(Commons.ERROR_INVALID_COMMAND);
}
+ /**
+ * Returns the argument prefixes required by this command.
+ *
+ * @return {@code null} as this command does not require any argument prefixes.
+ */
@Override
public String[] getArgumentPrefixes() {
return null;
diff --git a/src/main/java/tutorlink/command/ListComponentCommand.java b/src/main/java/tutorlink/command/ListComponentCommand.java
index 2197459680..f9594e8eb9 100644
--- a/src/main/java/tutorlink/command/ListComponentCommand.java
+++ b/src/main/java/tutorlink/command/ListComponentCommand.java
@@ -5,15 +5,36 @@
import tutorlink.appstate.AppState;
import tutorlink.result.CommandResult;
+/**
+ * Represents a command to list all components. The command retrieves the components
+ * from the application state and returns them in a formatted list.
+ */
public class ListComponentCommand extends Command {
+ /** The command word used to trigger this command. */
public static final String COMMAND_WORD = "list_component";
+ private static final String MESSAGE_NO_COMPONENTS = "No components have been recorded yet.";
+ /**
+ * Executes the command to list all components.
+ *
+ * @param appState The current application state containing the component data.
+ * @param parameters A map of argument prefixes to their values, not used for this command.
+ * @return A {@code CommandResult} containing the formatted list of components.
+ */
@Override
public CommandResult execute(AppState appState, HashMap parameters) {
+ if(appState.components.size() <= 0) {
+ return new CommandResult(MESSAGE_NO_COMPONENTS);
+ }
return new CommandResult(appState.components.toString());
}
+ /**
+ * Returns the argument prefixes required by this command.
+ *
+ * @return {@code null} as this command does not require any argument prefixes.
+ */
@Override
public String[] getArgumentPrefixes() {
return null;
diff --git a/src/main/java/tutorlink/command/ListGradeCommand.java b/src/main/java/tutorlink/command/ListGradeCommand.java
index 0b77b78d13..92e1a7d2ff 100644
--- a/src/main/java/tutorlink/command/ListGradeCommand.java
+++ b/src/main/java/tutorlink/command/ListGradeCommand.java
@@ -13,6 +13,10 @@
import java.util.stream.Collectors;
import java.util.Comparator;
+/**
+ * Represents a command to list grades of all students or a specific student.
+ * The grades are displayed with a breakdown of scores by component, and the final percentage score (GPA) is calculated.
+ */
public class ListGradeCommand extends Command {
public static final String COMMAND_WORD = "list_grade";
@@ -20,6 +24,15 @@ public class ListGradeCommand extends Command {
private static final String MESSAGE_NO_GRADES = "No grades have been recorded yet.";
private static final String MESSAGE_STUDENT_NOT_FOUND = "No grades found for student with matriculation number %s.";
+ /**
+ * Executes the command to list grades. If a matriculation number is provided, only that student's grades are shown;
+ * otherwise, grades for all students are displayed.
+ *
+ * @param appState The current application state containing the student and grade data.
+ * @param hashMap A map of argument prefixes to their values.
+ * @return A {@code CommandResult} containing the formatted grades list for the specified student or all students.
+ * @throws StudentNotFoundException if no student is found with the specified matriculation number.
+ */
@Override
public CommandResult execute(AppState appState, HashMap hashMap) throws StudentNotFoundException {
ArrayList grades = appState.grades.getGradeArrayList();
@@ -30,7 +43,7 @@ public CommandResult execute(AppState appState, HashMap hashMap)
String matricNumber = hashMap.get("i/");
- // If a specific student is requested
+ // If a specific student's grades are requested
if (matricNumber != null) {
matricNumber = matricNumber.toUpperCase();
String finalMatricNumber = matricNumber;
@@ -54,27 +67,46 @@ public CommandResult execute(AppState appState, HashMap hashMap)
return generateAllGradesReport(grades, appState);
}
+ /**
+ * Generates a formatted report of grades for a specific student.
+ *
+ * @param student The student for whom the report is generated.
+ * @param studentGrades The list of grades for the specified student.
+ * @param appState The application state containing component data.
+ * @return A {@code CommandResult} containing the formatted grade report for the student.
+ */
private CommandResult generateStudentGradeReport(Student student, ArrayList studentGrades,
AppState appState) {
StringBuilder output = new StringBuilder(
String.format("Grades for %s (%s):\n", student.getName(), student.getMatricNumber()));
- // Sort grades by component name and display each with numbering
int gradeIndex = 1;
for (Grade grade : studentGrades.stream()
.sorted(Comparator.comparing(g -> g.getComponent().getName()))
.collect(Collectors.toList())) {
output.append(
- String.format("%d. %-15s: %.2f\n", gradeIndex++, grade.getComponent().getName(), grade.getScore()));
+ String.format("%d: %-15s: %.2f / %.2f\n",
+ gradeIndex++,
+ grade.getComponent().getName(),
+ grade.getScore(),
+ grade.getComponent().getMaxScore()
+ ));
}
- // Calculate and display the GPA (final grade)
- double gpa = appState.grades.calculateStudentGPA(student.getMatricNumber(), appState.components);
- output.append(String.format("\nFinal GPA: %.2f\n", gpa));
+ double percentageScore = appState.grades.calculateStudentPercentageScore(student.getMatricNumber(),
+ appState.components);
+ output.append(String.format("\nFinal score: %.2f%%\n", percentageScore));
return new CommandResult(output.toString());
}
+ /**
+ * Generates a formatted report of grades for all students.
+ *
+ * @param grades The list of all grades.
+ * @param appState The application state containing component data.
+ * @return A {@code CommandResult} containing the formatted grade report for all students.
+ */
private CommandResult generateAllGradesReport(ArrayList grades, AppState appState) {
StringBuilder output = new StringBuilder("List of All Grades:\n\n");
@@ -89,9 +121,8 @@ private CommandResult generateAllGradesReport(ArrayList grades, AppState
for (Map.Entry> entry : gradesByStudent.entrySet()) {
Student student = entry.getValue().get(0).getStudent();
output.append(
- String.format("%d. %s (%s):\n", studentIndex++, student.getName(), student.getMatricNumber()));
+ String.format("%d: %s (%s):\n", studentIndex++, student.getName(), student.getMatricNumber()));
- // Grade numbering for each student's grades
int gradeIndex = 1;
for (Grade grade : entry.getValue().stream()
.sorted(Comparator.comparing(g -> g.getComponent().getName()))
@@ -101,17 +132,21 @@ private CommandResult generateAllGradesReport(ArrayList grades, AppState
grade.getComponent().getName(), grade.getScore()));
}
- // Calculate and display the GPA for the student
- double gpa = appState.grades.calculateStudentGPA(student.getMatricNumber(), appState.components);
- output.append(String.format(" Final GPA: %.2f\n\n", gpa));
+ double percentageScore = appState.grades.calculateStudentPercentageScore(student.getMatricNumber(),
+ appState.components);
+ output.append(String.format(" Final Percentage Score: %.2f%%\n\n", percentageScore));
}
return new CommandResult(output.toString());
}
+ /**
+ * Returns the argument prefixes required by this command.
+ *
+ * @return An array of argument prefixes: {"i/"}, representing the matriculation number prefix.
+ */
@Override
public String[] getArgumentPrefixes() {
return ARGUMENT_PREFIXES;
}
}
-
diff --git a/src/main/java/tutorlink/command/ListStudentCommand.java b/src/main/java/tutorlink/command/ListStudentCommand.java
index eb0b8bae61..e528dbd00b 100644
--- a/src/main/java/tutorlink/command/ListStudentCommand.java
+++ b/src/main/java/tutorlink/command/ListStudentCommand.java
@@ -1,20 +1,39 @@
package tutorlink.command;
import java.util.HashMap;
-
import tutorlink.appstate.AppState;
import tutorlink.result.CommandResult;
+/**
+ * Represents a command to list all students in the application.
+ * When executed, this command retrieves the list of students from the application state
+ * and returns it as a command result.
+ */
public class ListStudentCommand extends Command {
public static final String COMMAND_WORD = "list_student";
+ private static final String MESSAGE_NO_STUDENTS = "No students have been recorded yet.";
-
+ /**
+ * Executes the command to list all students, retrieving the student list from the application state.
+ *
+ * @param appState The current application state containing the student list.
+ * @param hashMap A map of argument prefixes to their values (unused in this command).
+ * @return A {@code CommandResult} containing the list of students as a string.
+ */
@Override
public CommandResult execute(AppState appState, HashMap hashMap) {
+ if(appState.students.size() <= 0) {
+ return new CommandResult(MESSAGE_NO_STUDENTS);
+ }
return new CommandResult(appState.students.toString());
}
+ /**
+ * Returns the argument prefixes required by this command.
+ *
+ * @return {@code null} as this command does not require any argument prefixes.
+ */
@Override
public String[] getArgumentPrefixes() {
return null;
diff --git a/src/main/java/tutorlink/command/UpdateComponentCommand.java b/src/main/java/tutorlink/command/UpdateComponentCommand.java
new file mode 100644
index 0000000000..841b58157a
--- /dev/null
+++ b/src/main/java/tutorlink/command/UpdateComponentCommand.java
@@ -0,0 +1,114 @@
+package tutorlink.command;
+
+import tutorlink.appstate.AppState;
+import tutorlink.commons.Commons;
+import tutorlink.component.Component;
+import tutorlink.exceptions.ComponentNotFoundException;
+import tutorlink.exceptions.IllegalValueException;
+import tutorlink.exceptions.InvalidWeightingException;
+import tutorlink.exceptions.TutorLinkException;
+import tutorlink.lists.ComponentList;
+import tutorlink.result.CommandResult;
+
+import java.util.HashMap;
+
+/**
+ * Represents a command to update an existing component's weight or maximum score.
+ * The command requires the component's name and either a new weight or maximum score
+ * (or both) to update the component.
+ */
+public class UpdateComponentCommand extends Command {
+ public static final String[] ARGUMENT_PREFIXES = {"c/", "w/", "m/"};
+ public static final String COMMAND_WORD = "update_component";
+
+ /**
+ * Executes the update component command, updating the specified component's
+ * weight and/or maximum score in the application state.
+ *
+ * @param appState The current application state containing the component list.
+ * @param hashmap A map of argument prefixes to their values for this command.
+ * @return A {@code CommandResult} indicating the result of the update.
+ * @throws TutorLinkException If the component is not found, the new values are invalid,
+ * or if the new weight causes the total weight to exceed the limit.
+ */
+ @Override
+ public CommandResult execute(AppState appState, HashMap hashmap) throws TutorLinkException {
+ String name = hashmap.get(ARGUMENT_PREFIXES[0]);
+ String cWeight = hashmap.get(ARGUMENT_PREFIXES[1]);
+ String cMark = hashmap.get(ARGUMENT_PREFIXES[2]);
+
+ // Validate the presence of required arguments
+ if (name == null || (cWeight == null && cMark == null)) {
+ StringBuilder sb = new StringBuilder("Error!");
+ if (name != null) {
+ sb.append(" n/ is required!");
+ }
+ if (cWeight != null && cMark != null) {
+ sb.append(" either w/ or m/ or both are required!");
+ }
+ throw new IllegalValueException(sb.toString());
+ }
+
+ Integer weight = null;
+ Double mark = null;
+
+ // Parse weight and mark values from the arguments
+ try {
+ if (cWeight != null) {
+ weight = Integer.parseInt(cWeight);
+ }
+ if (cMark != null) {
+ mark = Double.parseDouble(cMark);
+ }
+ } catch (NumberFormatException e) {
+ throw new IllegalValueException("Error! weight is not an integer or mark is not a real positive number");
+ }
+
+ // Validate the parsed values for mark and weight
+ if (mark != null && (mark.isNaN() || mark.isInfinite() || mark < 0)) {
+ throw new IllegalValueException("Error! Mark is not a real positive number");
+ }
+ if (weight != null && (weight < 0 || weight > 100)) {
+ throw new IllegalValueException("Error! weight must be between 0 and 100");
+ }
+
+ // Find the component by name
+ ComponentList componentsToEdit = appState.components.findComponent(name);
+ if (componentsToEdit.size() == 0) {
+ throw new ComponentNotFoundException(String.format(Commons.ERROR_COMPONENT_NOT_FOUND, name));
+ }
+
+ Component component = componentsToEdit.getComponentArrayList().get(0);
+
+ // Update weight if provided, ensuring total weighting remains valid
+ int oldWeight = component.getWeight();
+ if (weight != null) {
+ component.setWeight(weight);
+ int totalWeight = appState.components.getTotalWeighting();
+ if (totalWeight > 100) {
+ component.setWeight(oldWeight);
+ throw new InvalidWeightingException(String.format(Commons.ERROR_INVALID_TOTAL_WEIGHTING, totalWeight));
+ }
+ }
+
+ // Update maximum score if provided
+ if (mark != null) {
+ component.setMaxScore(mark);
+ }
+
+ // Update percentage scores for all students based on new component values
+ appState.updateAllStudentPercentageScores();
+
+ return new CommandResult("Here is the updated component\n" + component.toString());
+ }
+
+ /**
+ * Returns the argument prefixes required by this command.
+ *
+ * @return An array of argument prefixes required by this command.
+ */
+ @Override
+ public String[] getArgumentPrefixes() {
+ return ARGUMENT_PREFIXES;
+ }
+}
diff --git a/src/main/java/tutorlink/commons/Commons.java b/src/main/java/tutorlink/commons/Commons.java
index 3bde9e315c..7fe562d0a1 100644
--- a/src/main/java/tutorlink/commons/Commons.java
+++ b/src/main/java/tutorlink/commons/Commons.java
@@ -8,8 +8,8 @@ public class Commons {
//Input Validation
public static final String MATRIC_NUMBER_REGEX = "A\\d{7}[A-Z]";
- public static final String ERROR_ILLEGAL_MATRIC_NUMBER = "Error! Ensure matric " +
- "number is of the form A\\d{7}[A-Z] (case insensitive)";
+ public static final String ERROR_ILLEGAL_MATRIC_NUMBER = "Error! Matric Number should start with \"A\", " +
+ "followed by 7 digits, and end with an uppercase letter (e.g., A1234567X)";
//Student
public static final String ADD_STUDENT_SUCCESS = "Student %s (%s) added successfully!";
public static final String ERROR_DUPLICATE_STUDENT =
@@ -23,20 +23,23 @@ public class Commons {
public static final String DELETE_GRADE_SUCCESS = "Grade: Component %s for student %s successfully deleted";
public static final String ADD_GRADE_SUCCESS = "Score of %s added successfully to %s for %s!";
public static final String ERROR_INVALID_SCORE =
- "Error! Score must be double that is more than or equal to 0, and not exceed the max score!";
+ "Error! Score must be a numerical value and be between 0 and the max score of the component!";
//@@author TrungBui32
//Component
+ public static final int MAX_WEIGHT = 100;
public static final String ADD_COMPONENT_SUCCESS =
"Component %s of weight %s%%, with max score %s added successfully!";
public static final String DELETE_COMPONENT_SUCCESS = "Component %s successfully deleted";
public static final String ERROR_COMPONENT_NOT_FOUND = "Error! Component (Name %s) not found";
public static final String ERROR_DUPLICATE_COMPONENT = "Error! Component (Name %s) already exists in the list!";
+ public static final String ERROR_MULTIPLE_QUERY_RESULT = "Error! Multiple query results for keyword: %s found " +
+ "in list!";
public static final String ERROR_INVALID_WEIGHTAGE = "Error! Weightage must be integer that is between 0 and 100!";
public static final String ERROR_INVALID_MAX_SCORE =
"Error! Max Score must be double that is more than or equal to 0!";
//@@author RCPilot1604
- public static final String ERROR_INVALID_TOTAL_WEIGHTING = "Error! Total weighting must add up to 100%%.\n" +
+ public static final String ERROR_INVALID_TOTAL_WEIGHTING = "Error! Total weighting must not exceed 100%%.\n" +
"Current weighting (after addition): %s%%";
//Invalid
diff --git a/src/main/java/tutorlink/component/Component.java b/src/main/java/tutorlink/component/Component.java
index eb2b5dfe4f..40236a1f12 100644
--- a/src/main/java/tutorlink/component/Component.java
+++ b/src/main/java/tutorlink/component/Component.java
@@ -2,7 +2,6 @@
//@@author TrungBui32
public class Component {
- public static int totalWeight = 0;
private String name;
private double maxScore;
private int weight;
@@ -25,10 +24,18 @@ public int getWeight() {
return weight;
}
+ public void setMaxScore(double maxScore) {
+ this.maxScore = maxScore;
+ }
+
+ public void setWeight(int weight) {
+ this.weight = weight;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof Component comp) {
- return comp.getName().equals(this.getName());
+ return comp.getName().equalsIgnoreCase(this.getName());
}
return false;
}
diff --git a/src/main/java/tutorlink/grade/Grade.java b/src/main/java/tutorlink/grade/Grade.java
index 43a89fce3e..fab1c37f3e 100644
--- a/src/main/java/tutorlink/grade/Grade.java
+++ b/src/main/java/tutorlink/grade/Grade.java
@@ -3,29 +3,66 @@
import tutorlink.component.Component;
import tutorlink.student.Student;
+/**
+ * Represents a grade assigned to a student for a specific component of an assessment.
+ * Each grade has a score, a corresponding component, and a student to whom it belongs.
+ */
public class Grade {
private double score;
private Component component;
private Student student;
+ /**
+ * Constructs a {@code Grade} object with the specified component, student, and score.
+ *
+ * @param component The component associated with the grade.
+ * @param student The student associated with the grade.
+ * @param score The score awarded to the student for the component.
+ */
public Grade(Component component, Student student, double score) {
this.component = component;
this.student = student;
this.score = score;
}
+ /**
+ * Returns the student associated with this grade.
+ *
+ * @return The {@code Student} object linked to this grade.
+ */
public Student getStudent() {
return student;
}
+ /**
+ * Returns the component associated with this grade.
+ *
+ * @return The {@code Component} object linked to this grade.
+ */
public Component getComponent() {
return component;
}
+ /**
+ * Returns the score for this grade. If the score exceeds the component's maximum
+ * score, it is capped to the maximum score of the component.
+ *
+ * @return The score for this grade, capped at the component's maximum score if necessary.
+ */
public double getScore() {
+ if (component != null && component.getMaxScore() < score) {
+ score = component.getMaxScore();
+ }
return score;
}
+ /**
+ * Checks if this grade is equal to another object. Two grades are considered equal if
+ * they have the same component and student.
+ *
+ * @param obj The object to compare with this grade.
+ * @return {@code true} if the object is a grade with the same component and student; {@code false} otherwise.
+ */
@Override
public boolean equals(Object obj) {
if (obj instanceof Grade grade) {
@@ -35,14 +72,30 @@ public boolean equals(Object obj) {
return false;
}
+ /**
+ * Returns the matriculation number of the student associated with this grade.
+ *
+ * @return The matriculation number of the student.
+ */
public String getStudentMatricNumber() {
return student.getMatricNumber();
}
+ /**
+ * Returns the name of the component associated with this grade.
+ *
+ * @return The name of the component.
+ */
public String getComponentName() {
return component.getName();
}
+ /**
+ * Returns a string representation of the grade, including the student, component,
+ * and score.
+ *
+ * @return A string representing the grade.
+ */
@Override
public String toString() {
return "Student: " + this.student + ", Component: " + this.component
diff --git a/src/main/java/tutorlink/lists/ComponentList.java b/src/main/java/tutorlink/lists/ComponentList.java
index aeb6705a23..6df8a690aa 100644
--- a/src/main/java/tutorlink/lists/ComponentList.java
+++ b/src/main/java/tutorlink/lists/ComponentList.java
@@ -9,7 +9,8 @@
import java.util.stream.IntStream;
/**
- * Represents a list of components.
+ * Represents a list of components. Provides methods for adding, deleting, and searching for components,
+ * as well as calculating the total weight of all components in the list.
*/
public class ComponentList {
private static final String ERROR_COMPONENT_NOT_FOUND = "Error! Component %s does not exist in the list!";
@@ -17,14 +18,29 @@ public class ComponentList {
private ArrayList componentArrayList;
+ /**
+ * Creates an empty {@code ComponentList}.
+ */
public ComponentList() {
this.componentArrayList = new ArrayList<>();
}
+ /**
+ * Creates a {@code ComponentList} initialized with the given list of components.
+ *
+ * @param componentArrayList The initial list of components.
+ */
public ComponentList(ArrayList componentArrayList) {
this.componentArrayList = componentArrayList;
}
+ /**
+ * Searches for components in the list by name.
+ *
+ * @param name The name or partial name of the component to search for.
+ * @return A {@code ComponentList} containing all components that match the search criteria.
+ * @throws ComponentNotFoundException If no matching components are found.
+ */
public ComponentList findComponent(String name) throws ComponentNotFoundException {
ComponentList filteredList = new ComponentList();
filteredList.componentArrayList = componentArrayList
@@ -37,6 +53,12 @@ public ComponentList findComponent(String name) throws ComponentNotFoundExceptio
return filteredList;
}
+ /**
+ * Adds a component to the list if it does not already exist.
+ *
+ * @param component The component to add.
+ * @throws DuplicateComponentException If a component with the same properties already exists in the list.
+ */
public void addComponent(Component component) throws DuplicateComponentException {
for (Component comp : componentArrayList) {
if (comp.equals(component)) {
@@ -46,6 +68,12 @@ public void addComponent(Component component) throws DuplicateComponentException
componentArrayList.add(component);
}
+ /**
+ * Deletes a component from the list.
+ *
+ * @param component The component to delete.
+ * @throws ComponentNotFoundException If the component is not found in the list.
+ */
public void deleteComponent(Component component) throws ComponentNotFoundException {
for (Component comp : componentArrayList) {
if (comp.equals(component)) {
@@ -56,22 +84,51 @@ public void deleteComponent(Component component) throws ComponentNotFoundExcepti
throw new ComponentNotFoundException(String.format(ERROR_COMPONENT_NOT_FOUND, component));
}
+ /**
+ * Calculates the total weight of all components in the list.
+ *
+ * @return The total weight as an integer.
+ */
+ public int getTotalWeighting() {
+ return componentArrayList.stream().mapToInt(Component::getWeight).sum();
+ }
+
+ /**
+ * Returns a formatted string representation of the components in the list.
+ *
+ * @return A numbered list of components as a string.
+ */
@Override
public String toString() {
return "\t" +
IntStream.range(0, componentArrayList.size())
- .mapToObj(i -> (i + 1) + ": " + componentArrayList.get(i))
- .collect(Collectors.joining("\n\t"));
+ .mapToObj(i -> (i + 1) + ": " + componentArrayList.get(i))
+ .collect(Collectors.joining("\n\t"));
}
-
+
+ /**
+ * Retrieves all components in the list as a new {@code ArrayList}.
+ *
+ * @return A new list containing all components in this list.
+ */
public ArrayList findAllComponents() {
return new ArrayList<>(componentArrayList);
}
+ /**
+ * Returns the underlying list of components.
+ *
+ * @return The list of components as an {@code ArrayList}.
+ */
public ArrayList getComponentArrayList() {
return componentArrayList;
}
+ /**
+ * Returns the number of components in the list.
+ *
+ * @return The size of the component list.
+ */
public int size() {
return componentArrayList.size();
}
diff --git a/src/main/java/tutorlink/lists/GradeList.java b/src/main/java/tutorlink/lists/GradeList.java
index 77badcd654..334e7a3727 100644
--- a/src/main/java/tutorlink/lists/GradeList.java
+++ b/src/main/java/tutorlink/lists/GradeList.java
@@ -55,10 +55,9 @@ public void deleteGradesByMatric(String matricNumber) {
}
public void deleteGradesByComponent(String componentDescription) {
- componentDescription = componentDescription.toUpperCase();
ArrayList gradesToDelete = new ArrayList<>();
for (Grade grade : gradeArrayList) {
- if (grade.getComponent().getName().toUpperCase().equals(componentDescription.toUpperCase())) {
+ if (grade.getComponent().getName().equalsIgnoreCase(componentDescription)) {
gradesToDelete.add(grade);
}
}
@@ -97,7 +96,7 @@ public GradeList findGrade(String matricNumber, String componentDescription) thr
}
//@@author
- public double calculateStudentGPA(String matricNumber, ComponentList componentList) {
+ public double calculateStudentPercentageScore(String matricNumber, ComponentList componentList) {
ArrayList studentGrades = gradeArrayList
.stream()
.filter(grade -> grade.getStudent().getMatricNumber().equals(matricNumber.toUpperCase()))
@@ -107,11 +106,8 @@ public double calculateStudentGPA(String matricNumber, ComponentList componentLi
return 0;
}
- double totalWeighting = componentList
- .getComponentArrayList()
- .stream()
- .mapToDouble(c -> c.getWeight())
- .sum();
+ int totalWeighting = componentList.getTotalWeighting();
+
if(totalWeighting == 0) {
return 0;
@@ -124,7 +120,7 @@ public double calculateStudentGPA(String matricNumber, ComponentList componentLi
/ grade.getComponent().getMaxScore()
* grade.getComponent().getWeight()
)
- .sum() / totalWeighting;
+ .sum() / totalWeighting * 100;
}
public ArrayList getGradeArrayList() {
diff --git a/src/main/java/tutorlink/parser/Parser.java b/src/main/java/tutorlink/parser/Parser.java
index 44be2f74c8..6d4fa2cccd 100644
--- a/src/main/java/tutorlink/parser/Parser.java
+++ b/src/main/java/tutorlink/parser/Parser.java
@@ -13,37 +13,59 @@
import tutorlink.command.ListStudentCommand;
import tutorlink.command.AddGradeCommand;
import tutorlink.command.AddComponentCommand;
-
+import tutorlink.command.UpdateComponentCommand;
+import tutorlink.exceptions.IllegalValueException;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
import java.util.logging.Logger;
-
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+/**
+ * The {@code Parser} class is responsible for parsing user input into commands and arguments.
+ * It interprets the first word of the input as the command and maps it to a corresponding {@code Command} object.
+ * It also provides a utility for extracting command arguments based on specific prefixes.
+ */
public class Parser {
private static final Logger LOGGER = Logger.getLogger(Parser.class.getName());
+ private static final String ERROR_PARSER_MULTIPLE_PREFIX = "Duplicate prefix detected: ";
+ /**
+ * Extracts the command word from the given input by splitting the input
+ * and taking the first word.
+ *
+ * @param input The user input as a string.
+ * @return The command word extracted from the input.
+ */
private String extractCommandWord(String input) {
- String[] words = input.split("\\s+");
- return words[0]; //return the first word
+ String[] words = input.trim().split("\\s+");
+ return words[0]; // Return the first word
}
-
+ /**
+ * Parses the user input and returns the corresponding {@code Command} object.
+ * It identifies the command by the first word of the input and maps it to a specific command class.
+ * If the command is invalid, it returns an {@code InvalidCommand}.
+ *
+ * @param line The user input line containing the command and arguments.
+ * @return The {@code Command} object representing the parsed command.
+ */
public Command getCommand(String line) {
String commandWord = extractCommandWord(line);
switch (commandWord.toLowerCase()) {
case AddStudentCommand.COMMAND_WORD:
- return new AddStudentCommand(); // Calls delete command handling method
+ return new AddStudentCommand();
case DeleteStudentCommand.COMMAND_WORD:
- return new DeleteStudentCommand(); // Calls add command handling method
+ return new DeleteStudentCommand();
case FindStudentCommand.COMMAND_WORD:
- return new FindStudentCommand(); // Lists all students
+ return new FindStudentCommand();
case ListStudentCommand.COMMAND_WORD:
- return new ListStudentCommand(); // Lists all students
+ return new ListStudentCommand();
case AddGradeCommand.COMMAND_WORD:
return new AddGradeCommand();
@@ -63,24 +85,27 @@ public Command getCommand(String line) {
case ListGradeCommand.COMMAND_WORD:
return new ListGradeCommand();
+ case UpdateComponentCommand.COMMAND_WORD:
+ return new UpdateComponentCommand();
+
case ExitCommand.COMMAND_WORD:
- return new ExitCommand(); // Lists all students
+ return new ExitCommand();
default:
return new InvalidCommand();
}
-
}
-
/**
* Extracts command arguments from the input string based on the given argument prefixes.
+ * Arguments are identified by specific prefixes (e.g., "n/" for name, "i/" for ID)
+ * and are extracted into a map where each prefix is a key and the corresponding argument is the value.
*
- * @param argumentPrefixes an array of valid argument prefixes (e.g., "n/", "i/")
- * @param line the user input containing command arguments
- * @return a HashMap where keys are the prefixes (e.g., "n/", "i/") and the values are the corresponding arguments
+ * @param argumentPrefixes An array of valid argument prefixes (e.g., "n/", "i/").
+ * @param line The user input containing command arguments.
+ * @return A {@code HashMap} where keys are prefixes (e.g., "n/", "i/") and values are the corresponding arguments.
*/
- public HashMap getArguments(String[] argumentPrefixes, String line) {
+ public HashMap getArguments(String[] argumentPrefixes, String line) throws IllegalValueException {
HashMap arguments = new HashMap<>();
if (argumentPrefixes == null) {
@@ -99,11 +124,16 @@ public HashMap getArguments(String[] argumentPrefixes, String li
String regex = regexBuilder.toString();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(line);
-
+ // Set to track prefixes we've already encountered
+ Set seenPrefixes = new HashSet<>();
// Iterate through all found tags and arguments
while (matcher.find()) {
String tag = matcher.group(1); // Group 1 is the tag (e.g., n/, i/, etc.)
String argument = matcher.group(2).trim(); // Group 2 is the argument after the tag
+ if (seenPrefixes.contains(tag)) {
+ throw new IllegalValueException(ERROR_PARSER_MULTIPLE_PREFIX + tag);
+ }
+ seenPrefixes.add(tag);
arguments.put(tag, argument); // Store the tag and argument
}
diff --git a/src/main/java/tutorlink/result/CommandResult.java b/src/main/java/tutorlink/result/CommandResult.java
index 40f5b8c4c9..ff103762c4 100644
--- a/src/main/java/tutorlink/result/CommandResult.java
+++ b/src/main/java/tutorlink/result/CommandResult.java
@@ -1,5 +1,8 @@
package tutorlink.result;
+/**
+ * Represents the result of a command execution.
+ */
public class CommandResult {
private static final String LINE_SEPARATOR = "\n\t";
@@ -10,6 +13,11 @@ public CommandResult(String message) {
this.message = message;
}
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return the message string
+ */
@Override
public String toString() {
return message;
diff --git a/src/main/java/tutorlink/storage/ComponentStorage.java b/src/main/java/tutorlink/storage/ComponentStorage.java
index 03c12782a7..5c80e1825d 100644
--- a/src/main/java/tutorlink/storage/ComponentStorage.java
+++ b/src/main/java/tutorlink/storage/ComponentStorage.java
@@ -8,17 +8,36 @@
import java.util.ArrayList;
import java.util.Scanner;
+/**
+ * Handles the loading and saving of {@code Component} objects from and to a file.
+ * The file contains data on each component with its name, maximum score, and weight.
+ */
public class ComponentStorage extends Storage {
+
+ /**
+ * Constructs a {@code ComponentStorage} with the specified file path.
+ *
+ * @param filePath The file path for storing the component data.
+ */
public ComponentStorage(String filePath) {
super(filePath);
}
+ /**
+ * Loads the list of components from the file specified in the file path.
+ * Each line in the file represents a component's data: name, maximum score, and weight.
+ *
+ * @return A list of {@code Component} objects loaded from the file.
+ * @throws IOException If an I/O error occurs when reading the file.
+ */
public ArrayList loadComponentList() throws IOException {
ArrayList components = new ArrayList<>();
+ int totalWeight = 0;
Scanner fileScanner = new Scanner(path);
while (fileScanner.hasNext()) {
try {
- Component newComponent = getComponentFromFileLine(fileScanner.nextLine(), components);
+ Component newComponent = getComponentFromFileLine(fileScanner.nextLine(), components, totalWeight);
+ totalWeight += newComponent.getWeight();
components.add(newComponent);
} catch (InvalidDataFileLineException e) {
discardedEntries.add(e.getMessage());
@@ -27,6 +46,14 @@ public ArrayList loadComponentList() throws IOException {
return components;
}
+ /**
+ * Saves the list of components to the file specified in the file path.
+ * Each component is written to a new line in the file in the format:
+ * name, maximum score, and weight.
+ *
+ * @param components The list of components to save to the file.
+ * @throws IOException If an I/O error occurs when writing to the file.
+ */
public void saveComponentList(ArrayList components) throws IOException {
FileWriter fileWriter = new FileWriter(path.toFile());
for (Component comp : components) {
@@ -35,23 +62,46 @@ public void saveComponentList(ArrayList components) throws IOExceptio
fileWriter.close();
}
- private Component getComponentFromFileLine(String fileLine, ArrayList components)
+ /**
+ * Parses a line from the file and creates a {@code Component} object.
+ *
+ * @param fileLine The line from the file to parse.
+ * @param components The current list of components for duplicate checks.
+ * @param totalWeight The cumulative weight of all components read so far.
+ * @return A new {@code Component} object parsed from the line.
+ * @throws InvalidDataFileLineException If the line is malformed or represents
+ * invalid component data.
+ */
+ private Component getComponentFromFileLine(
+ String fileLine, ArrayList components, int totalWeight)
throws InvalidDataFileLineException {
String[] stringParts = fileLine.split(READ_DELIMITER);
+ String name;
+ double maxScore;
+ int weight;
try {
- String name = stringParts[0];
- double maxScore = Double.parseDouble(stringParts[1]);
- int weight = Integer.parseInt(stringParts[2]);
- Component newComponent = new Component(name, maxScore, weight);
- if (components.contains(newComponent)) {
- throw new InvalidDataFileLineException(fileLine);
- }
- return newComponent;
+ name = stringParts[0].strip();
+ maxScore = Double.parseDouble(stringParts[1].strip());
+ weight = Integer.parseInt(stringParts[2].strip());
} catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
throw new InvalidDataFileLineException(fileLine);
}
+
+ boolean isValidMaxScore = (maxScore >= 0);
+ boolean isValidWeight = (weight >= 0 && (weight + totalWeight) <= 100);
+ Component newComponent = new Component(name, maxScore, weight);
+ if (!isValidMaxScore || !isValidWeight || components.contains(newComponent)) {
+ throw new InvalidDataFileLineException(fileLine);
+ }
+ return newComponent;
}
+ /**
+ * Formats a {@code Component} object into a string suitable for saving to a file.
+ *
+ * @param component The {@code Component} to format.
+ * @return A string representing the component in the file format.
+ */
private String getFileInputForComponent(Component component) {
String convertedString = component.getName() + WRITE_DELIMITER + component.getMaxScore()
+ WRITE_DELIMITER + component.getWeight();
diff --git a/src/main/java/tutorlink/storage/GradeStorage.java b/src/main/java/tutorlink/storage/GradeStorage.java
index a44540bf05..0365820a65 100644
--- a/src/main/java/tutorlink/storage/GradeStorage.java
+++ b/src/main/java/tutorlink/storage/GradeStorage.java
@@ -10,23 +10,40 @@
import java.util.ArrayList;
import java.util.Scanner;
+/**
+ * Represents a storage manager for grades, handling loading and saving of grade data to and from a file.
+ * Utilizes a list of components and students to validate grade entries.
+ */
public class GradeStorage extends Storage {
private final ArrayList componentList;
private final ArrayList studentList;
+ /**
+ * Constructs a {@code GradeStorage} object with the specified file path, component list, and student list.
+ *
+ * @param filePath The file path to store grades.
+ * @param componentList The list of components for validation.
+ * @param studentList The list of students for validation.
+ */
public GradeStorage(String filePath, ArrayList componentList, ArrayList studentList) {
super(filePath);
this.componentList = componentList;
this.studentList = studentList;
}
- public ArrayList loadGradeList()
- throws IOException {
+ /**
+ * Loads the grade list from the file.
+ *
+ * @return An {@code ArrayList} of {@code Grade} objects loaded from the file.
+ * @throws IOException If an I/O error occurs while reading the file.
+ */
+ public ArrayList loadGradeList() throws IOException {
ArrayList grades = new ArrayList<>();
Scanner fileScanner = new Scanner(path);
while (fileScanner.hasNext()) {
try {
- grades.add(getGradeFromFileLine(fileScanner.nextLine()));
+ Grade newGrade = getGradeFromFileLine(fileScanner.nextLine(), grades);
+ grades.add(newGrade);
} catch (InvalidDataFileLineException e) {
discardedEntries.add(e.getMessage());
}
@@ -34,6 +51,12 @@ public ArrayList loadGradeList()
return grades;
}
+ /**
+ * Saves the provided list of grades to the file.
+ *
+ * @param grades The {@code ArrayList} of grades to be saved.
+ * @throws IOException If an I/O error occurs while writing to the file.
+ */
public void saveGradeList(ArrayList grades) throws IOException {
FileWriter fileWriter = new FileWriter(path.toFile());
for (Grade grade : grades) {
@@ -42,14 +65,28 @@ public void saveGradeList(ArrayList grades) throws IOException {
fileWriter.close();
}
- private Grade getGradeFromFileLine(String fileLine) throws InvalidDataFileLineException {
+ /**
+ * Parses a line from the grade file and returns a {@code Grade} object if the data is valid.
+ *
+ * @param fileLine A line from the file representing a grade entry.
+ * @param grades The current list of grades to check for duplicates.
+ * @return A {@code Grade} object created from the parsed data.
+ * @throws InvalidDataFileLineException If the line data is invalid or contains duplicates.
+ */
+ private Grade getGradeFromFileLine(String fileLine, ArrayList grades)
+ throws InvalidDataFileLineException {
+ String componentName;
+ String matricNumber;
+ double score;
String[] stringParts = fileLine.split(READ_DELIMITER);
- if (stringParts.length != 3) {
+
+ try {
+ componentName = stringParts[0].strip();
+ matricNumber = stringParts[1].strip();
+ score = Double.parseDouble(stringParts[2].strip());
+ } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
throw new InvalidDataFileLineException(fileLine);
}
- String componentName = stringParts[0];
- String matricNumber = stringParts[1];
- double score = Double.parseDouble(stringParts[2]);
Component selectedComp = null;
for (Component comp : componentList) {
@@ -67,13 +104,24 @@ private Grade getGradeFromFileLine(String fileLine) throws InvalidDataFileLineEx
}
}
- if (selectedComp != null && selectedStudent != null) {
- return new Grade(selectedComp, selectedStudent, score);
- } else {
+ if (selectedComp == null || selectedStudent == null) {
+ throw new InvalidDataFileLineException(fileLine);
+ }
+
+ boolean isValidScore = (score >= 0 && score <= selectedComp.getMaxScore());
+ Grade newGrade = new Grade(selectedComp, selectedStudent, score);
+ if (!isValidScore || grades.contains(newGrade)) {
throw new InvalidDataFileLineException(fileLine);
}
+ return newGrade;
}
+ /**
+ * Formats a {@code Grade} object for storage in a file.
+ *
+ * @param grade The grade to format.
+ * @return A string representing the grade in file format.
+ */
private String getFileInputForGrade(Grade grade) {
String componentName = grade.getComponent().getName();
String matricNumber = grade.getStudent().getMatricNumber();
diff --git a/src/main/java/tutorlink/storage/Storage.java b/src/main/java/tutorlink/storage/Storage.java
index 87c9888d5e..7f1882bf4a 100644
--- a/src/main/java/tutorlink/storage/Storage.java
+++ b/src/main/java/tutorlink/storage/Storage.java
@@ -15,6 +15,13 @@ public class Storage {
protected final Path path;
protected ArrayList discardedEntries;
+ /**
+ * Initializes a Storage object with the specified file path.
+ * Creates the necessary directories and file if they do not exist.
+ *
+ * @param filePath The path to the storage file.
+ * @throws StorageOperationException If an error occurs while creating the file or directories.
+ */
public Storage(String filePath) throws StorageOperationException {
path = Paths.get(filePath);
discardedEntries = new ArrayList<>();
diff --git a/src/main/java/tutorlink/storage/StudentStorage.java b/src/main/java/tutorlink/storage/StudentStorage.java
index 8576d51230..08e4cd9631 100644
--- a/src/main/java/tutorlink/storage/StudentStorage.java
+++ b/src/main/java/tutorlink/storage/StudentStorage.java
@@ -1,5 +1,6 @@
package tutorlink.storage;
+import tutorlink.commons.Commons;
import tutorlink.exceptions.InvalidDataFileLineException;
import tutorlink.student.Student;
@@ -7,7 +8,13 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+/**
+ * The StudentStorage class extends the Storage class and provides methods to load and save a list of students
+ * from and to a file.
+ */
public class StudentStorage extends Storage {
public StudentStorage(String filePath) {
super(filePath);
@@ -39,8 +46,14 @@ private Student getStudentFromFileLine(String fileLine, ArrayList stude
throws InvalidDataFileLineException {
String[] stringParts = fileLine.split(READ_DELIMITER);
try {
- String matricNumber = stringParts[0];
- String name = stringParts[1];
+ String matricNumber = stringParts[0].strip().toUpperCase();
+ String name = stringParts[1].strip();
+ Pattern pattern = Pattern.compile(Commons.MATRIC_NUMBER_REGEX);
+ Matcher matcher = pattern.matcher(matricNumber);
+ if (!matcher.find()) {
+ throw new InvalidDataFileLineException(fileLine);
+ }
+
Student newStudent = new Student(matricNumber, name);
if (students.contains(newStudent)) {
throw new InvalidDataFileLineException(fileLine);
diff --git a/src/main/java/tutorlink/student/Student.java b/src/main/java/tutorlink/student/Student.java
index 17b3c89ff8..33bf3e4d06 100644
--- a/src/main/java/tutorlink/student/Student.java
+++ b/src/main/java/tutorlink/student/Student.java
@@ -3,12 +3,25 @@
public class Student {
private String matricNumber;
private String name;
- private double gpa;
+ private double percentageScore;
public Student(String matricNumber, String name) {
this.name = name;
this.matricNumber = matricNumber.toUpperCase();
- this.gpa = 0.0;
+ this.percentageScore = 0.0;
+ }
+
+ /**
+ * Constructs a new Student with the specified matriculation number, name, and GPA.
+ *
+ * @param matricNumber The matriculation number of the student.
+ * @param name The name of the student.
+ * @param gpa The GPA of the student.
+ */
+ public Student(String matricNumber, String name, double gpa) {
+ this.name = name;
+ this.matricNumber = matricNumber.toUpperCase();
+ this.percentageScore = gpa;
}
public String getName() {
@@ -19,17 +32,18 @@ public String getMatricNumber() {
return matricNumber;
}
- public double getGpa() {
- return gpa;
+ public double getPercentageScore() {
+ return percentageScore;
}
- public void setGpa(double gpa) {
- this.gpa = gpa;
+ public void setPercentageScore(double percentageScore) {
+ this.percentageScore = percentageScore;
}
@Override
public String toString() {
- return this.name + " (matric no: " + this.matricNumber + ", GPA: " + this.gpa + ")";
+ return this.name + " (matric no: " + this.matricNumber + ", percentage score: " +
+ String.format("%.2f", this.percentageScore) + ")";
}
@Override
@@ -38,6 +52,6 @@ public boolean equals(Object obj) {
return false;
}
Student s = (Student) obj;
- return this.matricNumber.equals(s.getMatricNumber());
+ return this.matricNumber.equalsIgnoreCase(s.getMatricNumber());
}
}
diff --git a/src/main/java/tutorlink/ui/Ui.java b/src/main/java/tutorlink/ui/Ui.java
index 3afddac24c..fef3191c6f 100644
--- a/src/main/java/tutorlink/ui/Ui.java
+++ b/src/main/java/tutorlink/ui/Ui.java
@@ -9,17 +9,59 @@
public class Ui {
private Scanner in = new Scanner(System.in);
- private final String logo = "___________ __ .____ .__ __\n"
+ private final String LOGO = "___________ __ .____ .__ __\n"
+ "\\__ ___/_ ___/ |_ ___________| | |__| ____ | | __\n"
+ " | | | | \\ __\\/ _ \\_ __ \\ | | |/ \\| |/ /\n"
+ " | | | | /| | ( <_> ) | \\/ |___| | | \\ <\n"
+ " |____| |____/ |__| \\____/|__| |_______ \\__|___| /__|_ \\\n"
+ " \\/ \\/ \\/\n";
- private final String halfBreakLine =
+ private final String HALF_BREAK_LINE =
"-------------------------";
- private final String fullBreakLine =
+ private final String FULL_BREAK_LINE =
"-------------------------------------------------------------";
+ private final String HELP_MESSAGE = """
+ ------------------- List of Commands --------------------
+ help: Displays list of commands
+ Example: help
+
+ add_student: Adds a student to the class roster
+ Example: add_student i/A1234567X n/John Doe
+
+ delete_student: Deletes a student from the class roster
+ Example: delete_student i/A1234567X
+
+ list_student: Lists all students in the class
+ Example: list_student
+
+ find_student: Finds a student in the class roster by name or matric number
+ Example: find_student i/A1234567X n/John Doe
+
+ add_component: Adds a new grading component to the class
+ Example: add_component c/Quiz 1 w/30 m/50
+
+ delete_component: Deletes a grading component from the class
+ Example: delete_component c/Quiz 1
+
+ update_component: Updates a component with a new maxscore or weight
+ Example: update_component c/Quiz 1 w/40 m/60
+
+ list_component: Lists all grading components
+ Example: list_component
+
+ add_grade: Adds a grade for a student for a specific component
+ Example: add_grade i/A1234567X c/Quiz 1 s/45
+
+ delete_grade: Deletes a student's grade for a specific component
+ Example: delete_grade i/A1234567X c/Quiz 1
+
+ list_grade: Lists all grades for a student
+ Example: list_grade i/A1234567X
+
+ bye: Exits the program
+ Example: bye
+ -------------------------------------------------------------""";
+
public Ui() {
}
@@ -28,30 +70,34 @@ public String getUserInput() {
}
public void displayWelcomeMessage() {
- System.out.println(fullBreakLine);
- System.out.println(logo);
- System.out.println(fullBreakLine);
+ System.out.println(FULL_BREAK_LINE);
+ System.out.println(LOGO);
+ System.out.println(FULL_BREAK_LINE);
System.out.println("Hello! I'm TutorLink\nWhat can I do for you?");
- System.out.println(fullBreakLine);
+ System.out.println(FULL_BREAK_LINE);
+ }
+
+ public void displayHelpMessage() {
+ System.out.println(HELP_MESSAGE);
}
public void displayResult(CommandResult result) {
- System.out.println(halfBreakLine + " Result " + halfBreakLine);
+ System.out.println(HALF_BREAK_LINE + " Result " + HALF_BREAK_LINE);
System.out.println(result.toString());
- System.out.println(fullBreakLine);
+ System.out.println(FULL_BREAK_LINE);
}
public void displayException(TutorLinkException error) {
- System.out.println(halfBreakLine + " Error " + halfBreakLine);
+ System.out.println(HALF_BREAK_LINE + " Error " + HALF_BREAK_LINE);
System.out.println(error.getMessage());
- System.out.println(fullBreakLine);
+ System.out.println(FULL_BREAK_LINE);
}
public void displayDiscardedEntries(ArrayList discardedEntries, String header) {
if (discardedEntries.isEmpty()) {
return;
}
- System.out.println(fullBreakLine);
+ System.out.println(FULL_BREAK_LINE);
System.out.println(header);
for (String entry : discardedEntries) {
System.out.println(" " + entry);
diff --git a/src/test/java/tutorlink/command/AddComponentCommandTest.java b/src/test/java/tutorlink/command/AddComponentCommandTest.java
index 5618121420..8bcb677890 100644
--- a/src/test/java/tutorlink/command/AddComponentCommandTest.java
+++ b/src/test/java/tutorlink/command/AddComponentCommandTest.java
@@ -5,8 +5,13 @@
import org.junit.jupiter.api.Test;
import tutorlink.appstate.AppState;
import tutorlink.commons.Commons;
+import tutorlink.component.Component;
+import tutorlink.exceptions.DuplicateComponentException;
import tutorlink.exceptions.IllegalValueException;
+import tutorlink.exceptions.InvalidWeightingException;
+import tutorlink.grade.Grade;
import tutorlink.result.CommandResult;
+import tutorlink.student.Student;
import java.util.HashMap;
@@ -32,29 +37,34 @@ void execute_validArguments_componentAddedSuccessfully() {
arguments.put("c/", "Quiz 1");
arguments.put("w/", "30");
arguments.put("m/", "100");
+ int initialWeighting = appState.components.getTotalWeighting();
CommandResult result = command.execute(appState, arguments);
assertNotNull(result);
assertEquals("Component Quiz 1 of weight 30%, with max score 100 added successfully!", result.toString());
assertEquals(1, appState.components.getComponentArrayList().size());
+ assertEquals(initialWeighting + 30, appState.components.getTotalWeighting());
}
@Test
void execute_nullArguments_throwsIllegalValueException() {
+ int initialWeighting = appState.components.getTotalWeighting();
IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
command.execute(appState, arguments);
});
assertEquals("Error! Null parameter passed!", exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
}
@Test
void execute_missingWeightageArgument_throwsIllegalValueException() {
arguments.put("c/", "Quiz 1");
arguments.put("m/", "100");
-
+ int initialWeighting = appState.components.getTotalWeighting();
IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
command.execute(appState, arguments);
});
assertEquals("Error! Null parameter passed!", exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
}
@Test
@@ -63,11 +73,12 @@ void execute_extraArgument_componentAddedSuccessfully() {
arguments.put("w/", "50");
arguments.put("m/", "100");
arguments.put("extra/", "extra value");
-
+ int initialWeighting = appState.components.getTotalWeighting();
CommandResult result = command.execute(appState, arguments);
assertNotNull(result);
assertEquals("Component Quiz 1 of weight 50%, with max score 100 added successfully!", result.toString());
assertEquals(1, appState.components.getComponentArrayList().size());
+ assertEquals(initialWeighting + 50, appState.components.getTotalWeighting());
}
@Test
@@ -75,11 +86,25 @@ void execute_weightageOutOfRange_throwsIllegalValueException() {
arguments.put("c/", "Quiz 1");
arguments.put("w/", "1.5");
arguments.put("m/", "100");
+ int initialWeighting = appState.components.getTotalWeighting();
+ IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
+ command.execute(appState, arguments);
+ });
+ assertEquals(Commons.ERROR_INVALID_WEIGHTAGE, exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
+ }
+ @Test
+ void execute_negativeWeighting_throwsIllegalValueException() {
+ arguments.put("c/", "Quiz 1");
+ arguments.put("w/", "-1");
+ arguments.put("m/", "100");
+ int initialWeighting = appState.components.getTotalWeighting();
IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
command.execute(appState, arguments);
});
assertEquals(Commons.ERROR_INVALID_WEIGHTAGE, exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
}
@Test
@@ -87,23 +112,25 @@ void execute_negativeMaxScore_throwsIllegalValueException() {
arguments.put("c/", "Quiz 1");
arguments.put("w/", "0.4");
arguments.put("m/", "-10");
-
+ int initialWeighting = appState.components.getTotalWeighting();
IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
command.execute(appState, arguments);
});
assertEquals(Commons.ERROR_INVALID_WEIGHTAGE, exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
}
@Test
- void execute_weightageNotDouble_throwsIllegalValueException() {
+ void execute_weightageNotInt_throwsIllegalValueException() {
arguments.put("c/", "Quiz 1");
arguments.put("w/", "one");
arguments.put("m/", "100");
-
+ int initialWeighting = appState.components.getTotalWeighting();
IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
command.execute(appState, arguments);
});
assertEquals(Commons.ERROR_INVALID_WEIGHTAGE, exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
}
@Test
@@ -111,11 +138,95 @@ void execute_maxScoreNotDouble_throwsIllegalValueException() {
arguments.put("c/", "Quiz 1");
arguments.put("w/", "0.4");
arguments.put("m/", "hundred");
+ int initialWeighting = appState.components.getTotalWeighting();
+ IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
+ command.execute(appState, arguments);
+ });
+ assertEquals(Commons.ERROR_INVALID_WEIGHTAGE, exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
+ }
+
+ @Test
+ void execute_maxWeightageExceeded_throwsInvalidWeightingException() {
+ arguments.put("c/", "Quiz 1");
+ arguments.put("w/", "100");
+ arguments.put("m/", "100");
+
+ CommandResult result = command.execute(appState, arguments);
+ assertNotNull(result);
+
+ int initialWeighting = appState.components.getTotalWeighting();
+
+ arguments.clear();
+ arguments.put("c/", "Quiz 2");
+ arguments.put("w/", "1");
+ arguments.put("m/", "100");
+
+ InvalidWeightingException exception = assertThrows(InvalidWeightingException.class, () -> {
+ command.execute(appState, arguments);
+ });
+ assertEquals(String.format(Commons.ERROR_INVALID_TOTAL_WEIGHTING, appState.components.getTotalWeighting() + 1),
+ exception.getMessage());
+ assertEquals(initialWeighting, appState.components.getTotalWeighting());
+ }
+
+ @Test
+ void execute_duplicateComponents_totalWeightingUnchanged() {
+ arguments.put("c/", "Quiz 1");
+ arguments.put("w/", "20");
+ arguments.put("m/", "100");
+
+ assertEquals(0, appState.components.getTotalWeighting());
+
+ command.execute(appState, arguments);
+
+ assertEquals(20, appState.components.getTotalWeighting());
+
+ DuplicateComponentException exception = assertThrows(DuplicateComponentException.class, () -> {
+ command.execute(appState, arguments);
+ });
+
+ assertEquals(20, appState.components.getTotalWeighting());
+ }
+ @Test
+ void execute_negativeWeighting_totalWeightingUnchanged() {
+ arguments.put("c/", "Quiz 1");
+ arguments.put("w/", "20");
+ arguments.put("m/", "100");
+
+ assertEquals(0, appState.components.getTotalWeighting());
+ command.execute(appState, arguments);
+ assertEquals(20, appState.components.getTotalWeighting());
+
+ arguments.clear();
+ arguments.put("c/", "Quiz 2");
+ arguments.put("w/", "-20");
+ arguments.put("m/", "100");
IllegalValueException exception = assertThrows(IllegalValueException.class, () -> {
command.execute(appState, arguments);
});
assertEquals(Commons.ERROR_INVALID_WEIGHTAGE, exception.getMessage());
+ assertEquals(20, appState.components.getTotalWeighting());
+ }
+
+ @Test
+ void update_gpa_accordingly () {
+ appState.students.addStudent("A1234567X", "John Doe");
+ Student student = appState.students.getStudentArrayList().get(0);
+ Component component = new Component("midterm", 50, 50);
+ appState.components.addComponent(component);
+ appState.grades.addGrade(new Grade(component, student, 50));
+ appState.updateAllStudentPercentageScores();
+
+ assertEquals(student.getPercentageScore(), 100);
+
+ arguments.put("c/", "Final");
+ arguments.put("w/", "50");
+ arguments.put("m/", "50");
+
+ command.execute(appState, arguments);
+ assertEquals(student.getPercentageScore(), 50);
}
}
//@@author
diff --git a/src/test/java/tutorlink/command/DeleteComponentCommandTest.java b/src/test/java/tutorlink/command/DeleteComponentCommandTest.java
index 136d2cee08..4024730e20 100644
--- a/src/test/java/tutorlink/command/DeleteComponentCommandTest.java
+++ b/src/test/java/tutorlink/command/DeleteComponentCommandTest.java
@@ -8,7 +8,9 @@
import tutorlink.component.Component;
import tutorlink.exceptions.ComponentNotFoundException;
import tutorlink.exceptions.IllegalValueException;
+import tutorlink.grade.Grade;
import tutorlink.result.CommandResult;
+import tutorlink.student.Student;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -25,46 +27,66 @@ void setup() {
appState.components.addComponent(new Component("finals", 40.0, 40));
appState.components.addComponent(new Component("iP", 20.0, 10));
appState.components.addComponent(new Component("lectures", 10.0, 10));
+ arguments = new HashMap<>();
}
@Test
void deleteComponent_success() {
- arguments = new HashMap<>();
arguments.put("c/", "finals");
+ int initialWeighting = appState.components.getTotalWeighting();
result = command.execute(appState, arguments);
assertNotNull(result);
assertEquals(appState.components.size(), 2);
- try {
- appState.components.findComponent("finals");
- } catch (ComponentNotFoundException e) {
- assertEquals(e.getMessage(), "Error! Component finals does not exist in the list!");
- } catch (Exception e) {
- fail("Expected: ComponentNotFoundException, actual: " + e.getMessage());
- }
+ assertEquals(appState.components.getTotalWeighting(), initialWeighting - 40);
}
@Test
void deleteComponent_notFound_fail() {
- arguments = new HashMap<>();
arguments.put("c/", "midterms");
+ int initialWeighting = appState.components.getTotalWeighting();
try {
command.execute(appState, arguments);
} catch (ComponentNotFoundException e) {
assertEquals(e.getMessage(), "Error! Component midterms does not exist in the list!");
} catch (Exception e) {
fail("Expected: ComponentNotFoundException, actual: " + e.getMessage());
+ } finally {
+ assertEquals(appState.components.getTotalWeighting(), initialWeighting);
}
}
@Test
void deleteComponent_emptyParam_fail() {
- arguments = new HashMap<>();
+ int initialWeighting = appState.components.getTotalWeighting();
try {
command.execute(appState, arguments);
} catch (IllegalValueException e) {
assertEquals(e.getMessage(), Commons.ERROR_NULL);
} catch (Exception e) {
fail("Expected: ComponentNotFoundException, actual: " + e.getMessage());
+ } finally {
+ assertEquals(appState.components.getTotalWeighting(), initialWeighting);
}
}
+
+ @Test
+ void update_gpa_accordingly () {
+ appState = new AppState();
+ appState.students.addStudent("A1234567X", "John Doe");
+ Student student = appState.students.getStudentArrayList().get(0);
+ Component component = new Component("midterm", 50, 50);
+ appState.components.addComponent(component);
+ appState.grades.addGrade(new Grade(component, student, 0));
+
+ Component component2 = new Component("final", 50, 50);
+ appState.components.addComponent(component2);
+ appState.grades.addGrade(new Grade(component2, student, 50));
+ appState.updateAllStudentPercentageScores();
+ assertEquals(student.getPercentageScore(), 50);
+
+ arguments.put("c/", "Final");
+
+ command.execute(appState, arguments);
+ assertEquals(student.getPercentageScore(), 0);
+ }
}
diff --git a/src/test/java/tutorlink/command/FindStudentCommandTest.java b/src/test/java/tutorlink/command/FindStudentCommandTest.java
index a24509404e..2568f195bc 100644
--- a/src/test/java/tutorlink/command/FindStudentCommandTest.java
+++ b/src/test/java/tutorlink/command/FindStudentCommandTest.java
@@ -40,7 +40,7 @@ void execute_matric_one() {
arguments.clear();
arguments.put("i/","A1234567X");
CommandResult result = findCommand.execute(appState,arguments);
- assertEquals(result.toString(), "\t1: John (matric no: A1234567X, GPA: 0.0)");
+ assertEquals(result.toString(), "\t1: John (matric no: A1234567X, percentage score: 0.00)");
}
@Test
@@ -61,8 +61,8 @@ void execute_name_two() {
HashMap arguments = new HashMap<>();
arguments.put("n/","Jo");
CommandResult result = findCommand.execute(appState,arguments);
- assertEquals(result.toString(), "\t1: John (matric no: A1234567X, GPA: 0.0)" + "\n\t"
- + "2: Jon (matric no: A2234567X, GPA: 0.0)");
+ assertEquals(result.toString(), "\t1: John (matric no: A1234567X, percentage score: 0.00)" + "\n\t"
+ + "2: Jon (matric no: A2234567X, percentage score: 0.00)");
}
@Test
diff --git a/src/test/java/tutorlink/command/UpdateComponentCommandTest.java b/src/test/java/tutorlink/command/UpdateComponentCommandTest.java
new file mode 100644
index 0000000000..385fd24ebf
--- /dev/null
+++ b/src/test/java/tutorlink/command/UpdateComponentCommandTest.java
@@ -0,0 +1,186 @@
+package tutorlink.command;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import tutorlink.appstate.AppState;
+import tutorlink.component.Component;
+import tutorlink.exceptions.TutorLinkException;
+import tutorlink.grade.Grade;
+import tutorlink.student.Student;
+
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class UpdateComponentCommandTest {
+ private UpdateComponentCommand command;
+ private AppState appState;
+ private Component targetDummy;
+ private HashMap args;
+
+ @BeforeEach
+ void setUp() {
+ command = new UpdateComponentCommand();
+ createMockAppState();
+ args = new HashMap<>();
+ }
+
+ @Test
+ void normal_both_arguments() {
+ args.put("c/", "finals");
+ args.put("m/", "30");
+ args.put("w/", "30");
+
+ assertDoesNotThrow(() -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 30);
+ assertEquals(targetDummy.getWeight(), 30);
+ }
+
+ @Test
+ void normal_only_weight() {
+ args.put("c/", "finals");
+ args.put("w/", "30");
+
+ assertDoesNotThrow(() -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 30);
+ }
+
+ @Test
+ void normal_only_mark() {
+ args.put("c/", "finals");
+ args.put("m/", "30");
+
+ assertDoesNotThrow(() -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 30);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+ @Test
+ void normal_updated_score() {
+ args.put("c/", "finals");
+ args.put("m/", "30");
+
+ assertDoesNotThrow(() -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 30);
+ assertEquals(targetDummy.getWeight(), 40);
+ for (Grade grade : appState.grades.getGradeArrayList()) {
+ if (grade.getComponent().equals(targetDummy)) {
+ assertEquals(grade.getScore(), 30);
+ }
+ }
+ }
+
+
+ @Test
+ void error_no_args() {
+ assertThrows(TutorLinkException.class, () -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+ @Test
+ void error_exceed_weight() {
+ args.put("c/", "finals");
+ args.put("w/", "70");
+
+ assertThrows(TutorLinkException.class, () -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+ @Test
+ void error_negative_weight() {
+ args.put("c/", "finals");
+ args.put("w/", "-30");
+
+ assertThrows(TutorLinkException.class, () -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+ @Test
+ void error_weight_over100() {
+ args.put("c/", "finals");
+ args.put("w/", "101");
+
+ assertThrows(TutorLinkException.class, () -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+ @Test
+ void error_nan_weight() {
+ args.put("c/", "finals");
+ args.put("w/", "abc");
+
+ assertThrows(TutorLinkException.class, () -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+ @Test
+ void error_mark_negative() {
+ args.put("c/", "finals");
+ args.put("m/", "-1");
+
+ assertThrows(TutorLinkException.class, () -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+
+ @Test
+ void error_nan_mark() {
+ args.put("c/", "finals");
+ args.put("m/", "abc");
+
+ assertThrows(TutorLinkException.class, () -> command.execute(appState, args));
+ assertEquals(targetDummy.getMaxScore(), 40);
+ assertEquals(targetDummy.getWeight(), 40);
+ }
+
+ @Test
+ void update_gpa_accordingly () {
+ appState = new AppState();
+ appState.students.addStudent("A1234567X", "John Doe");
+ Student student = appState.students.getStudentArrayList().get(0);
+ Component component = new Component("midterm", 50, 10);
+ appState.components.addComponent(component);
+ appState.grades.addGrade(new Grade(component, student, 0));
+
+ Component component2 = new Component("final", 50, 10);
+ appState.components.addComponent(component2);
+ appState.grades.addGrade(new Grade(component2, student, 50));
+ appState.updateAllStudentPercentageScores();
+ assertEquals(student.getPercentageScore(), 50);
+
+ args.put("c/", "midterm");
+ args.put("w/", "30");
+
+ command.execute(appState, args);
+ assertEquals(student.getPercentageScore(), 25);
+ }
+
+ private void createMockAppState() {
+ appState = new AppState();
+ targetDummy = new Component("finals", 40.0, 40);
+ appState.components.addComponent(targetDummy);
+ appState.components.addComponent(new Component("iP", 20.0, 30));
+ appState.components.addComponent(new Component("lectures", 10.0, 10));
+ appState.students.addStudent("A1234567X", "John Smith");
+ appState.students.addStudent("A2345678A", "John Doe");
+ appState.students.addStudent("A3456789E", "Alan Smith");
+ List stuList = appState.students.getStudentArrayList();
+ List comList = appState.components.getComponentArrayList();
+ for (Student student : stuList) {
+ for (Component component : comList) {
+ Grade newGrade = new Grade(component, student, component.getMaxScore());
+ appState.grades.addGrade(newGrade);
+ }
+ }
+ }
+}
diff --git a/src/test/java/tutorlink/lists/ComponentListTest.java b/src/test/java/tutorlink/lists/ComponentListTest.java
index 8b9b9dc441..1e2e67463f 100644
--- a/src/test/java/tutorlink/lists/ComponentListTest.java
+++ b/src/test/java/tutorlink/lists/ComponentListTest.java
@@ -71,7 +71,7 @@ void findComponent_findNotInList_exceptionThrown() {
}
@Test
- void toString_test() {
+ void componentList_toString_success() {
componentList.addComponent(comp1);
componentList.addComponent(comp3);
String expectedResult = "\t1: Homework 1 (maxScore: 30.0, weight: 10%)\n" +
diff --git a/src/test/java/tutorlink/lists/StudentListTest.java b/src/test/java/tutorlink/lists/StudentListTest.java
index 4eef845d2a..36e9f63946 100644
--- a/src/test/java/tutorlink/lists/StudentListTest.java
+++ b/src/test/java/tutorlink/lists/StudentListTest.java
@@ -86,8 +86,8 @@ void find_name_notFound() {
void testToString() throws DuplicateMatricNumberException {
studentList.addStudent("A1234567B", "John Doe");
studentList.addStudent("A7654321B", "Jane Smith");
- String expectedString = "\t1: John Doe (matric no: A1234567B, GPA: 0.0)"
- + "\n\t2: Jane Smith (matric no: A7654321B, GPA: 0.0)";
+ String expectedString = "\t1: John Doe (matric no: A1234567B, percentage score: 0.00)"
+ + "\n\t2: Jane Smith (matric no: A7654321B, percentage score: 0.00)";
assertEquals(expectedString, studentList.toString());
}
}
diff --git a/src/test/java/tutorlink/parser/ParserTest.java b/src/test/java/tutorlink/parser/ParserTest.java
index c49b1aaf22..a1027baecd 100644
--- a/src/test/java/tutorlink/parser/ParserTest.java
+++ b/src/test/java/tutorlink/parser/ParserTest.java
@@ -9,20 +9,22 @@
import tutorlink.command.FindStudentCommand;
import tutorlink.command.InvalidCommand;
import tutorlink.command.ListStudentCommand;
+import tutorlink.exceptions.IllegalValueException;
import java.util.HashMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
public class ParserTest {
Parser parser = new Parser();
@Test
- void parserTestInterchangingArgument() {
+ void parserTestInterchangingArgument() {
String input = "add_student i/A1234567X n/John Doe";
String input2 = "add_student n/John Doe i/A1234567X";
- String[] args = new String[] {"i/, n/"};
+ String[] args = new String[]{"i/, n/"};
var out1 = parser.getArguments(args, input);
var out2 = parser.getArguments(args, input2);
@@ -31,7 +33,7 @@ void parserTestInterchangingArgument() {
}
@Test
- void getCommand_addStudentCommand_addStudentCommandReturned() {
+ void getCommand_addStudentCommand_addStudentCommandReturned() {
Parser parser = new Parser();
String input = "add_student i/A1234567X n/John";
@@ -41,7 +43,7 @@ void getCommand_addStudentCommand_addStudentCommandReturned() {
}
@Test
- void getCommand_deleteStudentCommand_deleteStudentCommandReturned() {
+ void getCommand_deleteStudentCommand_deleteStudentCommandReturned() {
Parser parser = new Parser();
String input = "delete_student i/A1234567X";
Command actualCommand = parser.getCommand(input);
@@ -50,7 +52,7 @@ void getCommand_deleteStudentCommand_deleteStudentCommandReturned() {
}
@Test
- void getCommand_exitCommand_exitCommandReturned() {
+ void getCommand_exitCommand_exitCommandReturned() {
Parser parser = new Parser();
String input = "bye";
Command actualCommand = parser.getCommand(input);
@@ -59,7 +61,7 @@ void getCommand_exitCommand_exitCommandReturned() {
}
@Test
- void getCommand_listStudentCommand_listStudentCommandReturned() {
+ void getCommand_listStudentCommand_listStudentCommandReturned() {
Parser parser = new Parser();
String input = "list_student";
Command actualCommand = parser.getCommand(input);
@@ -68,7 +70,7 @@ void getCommand_listStudentCommand_listStudentCommandReturned() {
}
@Test
- void getCommand_findStudentCommand_findStudentCommandReturned() {
+ void getCommand_findStudentCommand_findStudentCommandReturned() {
Parser parser = new Parser();
String input = "find_student i/A1234567X n/John Doe";
Command actualCommand = parser.getCommand(input);
@@ -77,7 +79,7 @@ void getCommand_findStudentCommand_findStudentCommandReturned() {
}
@Test
- void getCommand_invalidCommand_invalidCommandReturned() {
+ void getCommand_invalidCommand_invalidCommandReturned() {
Parser parser = new Parser();
String input = "test_input";
Command actualCommand = parser.getCommand(input);
@@ -86,14 +88,14 @@ void getCommand_invalidCommand_invalidCommandReturned() {
}
@Test
- void getArguments_addStudentCommand_addStudentCommandHashMapReturned() {
+ void getArguments_addStudentCommand_addStudentCommandHashMapReturned() {
Parser parser = new Parser();
String line = "add_student i/A1234567X n/John Doe";
Command currentCommand = new AddStudentCommand();
String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
- HashMap arguments = parser.getArguments(argumentPrefixes, line);
+ HashMap arguments = parser.getArguments(argumentPrefixes, line);
assertEquals(2, arguments.size());
assertEquals("A1234567X", arguments.get("i/")); // Check matriculation number
@@ -101,27 +103,27 @@ void getArguments_addStudentCommand_addStudentCommandHashMapReturned() {
}
@Test
- void getArguments_exitCommandNoArgumentPrefix_exitCommandHashMapReturned() {
+ void getArguments_exitCommandNoArgumentPrefix_exitCommandHashMapReturned() {
Parser parser = new Parser();
String line = "bye";
Command currentCommand = new ExitCommand();
String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
- HashMap arguments = parser.getArguments(argumentPrefixes, line);
+ HashMap arguments = parser.getArguments(argumentPrefixes, line);
assertEquals(0, arguments.size());
}
@Test
- void getArguments_addStudentCommandExtraArguments_ignoreExtraTag() {
+ void getArguments_addStudentCommandExtraArguments_ignoreExtraTag() {
Parser parser = new Parser();
String line = "add_student i/A1234567X n/John Doe t/extraTag";
Command currentCommand = new AddStudentCommand();
String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
- HashMap arguments = parser.getArguments(argumentPrefixes, line);
+ HashMap arguments = parser.getArguments(argumentPrefixes, line);
assertEquals(2, arguments.size());
assertEquals("A1234567X", arguments.get("i/")); // Check matriculation number
@@ -129,14 +131,14 @@ void getArguments_addStudentCommandExtraArguments_ignoreExtraTag() {
}
@Test
- void getArguments_addStudentCommandSwappedArguments_addStudentCommandHashMapReturned() {
+ void getArguments_addStudentCommandSwappedArguments_addStudentCommandHashMapReturned() {
Parser parser = new Parser();
String line = "add_student n/John Doe i/A1234567X ";
Command currentCommand = new AddStudentCommand();
String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
- HashMap arguments = parser.getArguments(argumentPrefixes, line);
+ HashMap arguments = parser.getArguments(argumentPrefixes, line);
assertEquals(2, arguments.size());
assertEquals("A1234567X", arguments.get("i/")); // Check matriculation number
@@ -144,30 +146,48 @@ void getArguments_addStudentCommandSwappedArguments_addStudentCommandHashMapRet
}
@Test
- void getArguments_addStudentCommandMissingArguments_hashMapWithOnlyGivenArgumentsReturned() {
+ void getArguments_addStudentCommandMissingArguments_hashMapWithOnlyGivenArgumentsReturned() {
Parser parser = new Parser();
String line = "add_student n/John Doe";
Command currentCommand = new AddStudentCommand();
String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
- HashMap arguments = parser.getArguments(argumentPrefixes, line);
+ HashMap arguments = parser.getArguments(argumentPrefixes, line);
assertEquals(1, arguments.size());
assertEquals("John Doe", arguments.get("n/")); // Check
}
@Test
- void getArguments_addStudentCommandInvalidArguments_emptyHashMapReturned() {
+ void getArguments_addStudentCommandInvalidArguments_emptyHashMapReturned() {
Parser parser = new Parser();
String line = "add_student j/test";
Command currentCommand = new AddStudentCommand();
String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
- HashMap arguments = parser.getArguments(argumentPrefixes, line);
+ HashMap arguments = parser.getArguments(argumentPrefixes, line);
assertEquals(0, arguments.size());
}
+
+ @Test
+ void getArguments_duplicatePrefix_exception() {
+ Parser parser = new Parser();
+ String line = "add_student n/Ethan n/Ethan i/A0276007H";
+ AddStudentCommand currentCommand = new AddStudentCommand();
+ String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
+ assertThrows(IllegalValueException.class, () -> parser.getArguments(argumentPrefixes, line));
+ }
+
+ @Test
+ void getArguments_duplicatePrefixAgain_exception() {
+ Parser parser = new Parser();
+ String line = "add_student n/Ethan i/A0276007H i/A0276007H";
+ AddStudentCommand currentCommand = new AddStudentCommand();
+ String[] argumentPrefixes = currentCommand.getArgumentPrefixes();
+ assertThrows(IllegalValueException.class, () -> parser.getArguments(argumentPrefixes, line));
+ }
}
//@@author
diff --git a/src/test/java/tutorlink/student/StudentTest.java b/src/test/java/tutorlink/student/StudentTest.java
index 0a17ce6cf2..26a11f696d 100644
--- a/src/test/java/tutorlink/student/StudentTest.java
+++ b/src/test/java/tutorlink/student/StudentTest.java
@@ -26,8 +26,8 @@ void testConstructor() {
@Test
void testToString() {
- assertEquals("John Doe (matric no: A1234567B, GPA: 0.0)", student1.toString());
- assertEquals("Jane Smith (matric no: A7654321B, GPA: 0.0)", student2.toString());
+ assertEquals("John Doe (matric no: A1234567B, percentage score: 0.00)", student1.toString());
+ assertEquals("Jane Smith (matric no: A7654321B, percentage score: 0.00)", student2.toString());
}
@Test
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 40c4713d7d..37b241b97f 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -16,152 +16,127 @@ Student Ethan Chua (A0276007H) added successfully!
------------------------- Result -------------------------
Student John Doe (A9999999Z) added successfully!
-------------------------------------------------------------
-------------------------- Result -------------------------
-Student John Smith (A1111111B) added successfully!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Ensure matric number is of the form A\d{7}[A-Z] (case insensitive)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
--------------------------------------------------------------
------------------------- Error -------------------------
-Error! Null parameter passed!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Null parameter passed!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+Error! Student with Matric Number, A1111111B, already exists in the list!
-------------------------------------------------------------
------------------------- Result -------------------------
- 1: Ethan Chua (matric no: A0276007H, GPA: 0.0)
- 2: John Doe (matric no: A9999999Z, GPA: 0.0)
- 3: John Smith (matric no: A1111111B, GPA: 0.0)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+ 1: John Smith (matric no: A1111111B, percentage score: 80.00)
+ 2: Ethan Chua (matric no: A0276007H, percentage score: 0.00)
+ 3: John Doe (matric no: A9999999Z, percentage score: 0.00)
-------------------------------------------------------------
------------------------- Result -------------------------
-Student A0276007H successfully deleted
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Student (Matric Number A1234567X) not found
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Ensure matric number is of the form A\d{7}[A-Z] (case insensitive)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+Component Quiz 1 of weight 20%, with max score 10 added successfully!
-------------------------------------------------------------
------------------------- Result -------------------------
- 1: John Doe (matric no: A9999999Z, GPA: 0.0)
- 2: John Smith (matric no: A1111111B, GPA: 0.0)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+Component Quiz 2 of weight 20%, with max score 20 added successfully!
-------------------------------------------------------------
------------------------- Result -------------------------
-Component Finals of weight 50%, with max score 100 added successfully!
+Component Quiz 3 of weight 20%, with max score 20 added successfully!
-------------------------------------------------------------
------------------------- Result -------------------------
-Component Quiz of weight 5%, with max score 20 added successfully!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Component already exists in the list!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Component already exists in the list!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Weightage must be integer that is between 0 and 100!
+Component Quiz 4 of weight 20%, with max score 20 added successfully!
-------------------------------------------------------------
------------------------- Result -------------------------
-Component Fail of weight 0%, with max score 2 added successfully!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Total weighting must add up to 100%.
-Current weighting (after addition): 169%
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Weightage must be integer that is between 0 and 100!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Weightage must be integer that is between 0 and 100!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Weightage must be integer that is between 0 and 100!
+Score of 10 added successfully to Quiz 1 for A0276007H!
-------------------------------------------------------------
------------------------- Result -------------------------
- 1: Finals (maxScore: 100.0, weight: 50%)
- 2: Quiz (maxScore: 20.0, weight: 5%)
- 3: Fail (maxScore: 2.0, weight: 0%)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+Score of 20 added successfully to Quiz 2 for A0276007H!
-------------------------------------------------------------
------------------------- Result -------------------------
-Component Quiz successfully deleted
+Score of 20 added successfully to Quiz 3 for A0276007H!
-------------------------------------------------------------
------------------------- Result -------------------------
- 1: Finals (maxScore: 100.0, weight: 50%)
- 2: Fail (maxScore: 2.0, weight: 0%)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Component Quiz does not exist in the list!
+Score of 20 added successfully to Quiz 4 for A0276007H!
-------------------------------------------------------------
------------------------- Result -------------------------
-Student Ethan Chua (A0276007H) added successfully!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+List of All Grades:
+
+1: Ethan Chua (A0276007H):
+ 1. Quiz 1 : 10.00
+ 2. Quiz 2 : 20.00
+ 3. Quiz 3 : 20.00
+ 4. Quiz 4 : 20.00
+ Final Percentage Score: 94.12%
+
+2: John Smith (A1111111B):
+ 1. Attendance : 4.00
+ Final Percentage Score: 4.71%
+
+
-------------------------------------------------------------
------------------------- Result -------------------------
- 1: John Doe (matric no: A9999999Z, GPA: 0.0)
- 2: John Smith (matric no: A1111111B, GPA: 0.0)
- 3: Ethan Chua (matric no: A0276007H, GPA: 0.0)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Component Quiz does not exist in the list!
+Score of 5 added successfully to Quiz 1 for A9999999Z!
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Component Quiz does not exist in the list!
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Ensure matric number is of the form A\d{7}[A-Z] (case insensitive)
--------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Component Wuiz does not exist in the list!
+------------------------- Result -------------------------
+Score of 7.5 added successfully to Quiz 2 for A9999999Z!
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+------------------------- Result -------------------------
+Score of 10 added successfully to Quiz 3 for A9999999Z!
-------------------------------------------------------------
------------------------- Result -------------------------
-No grades have been recorded yet.
+Score of 10 added successfully to Quiz 4 for A9999999Z!
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+------------------------- Result -------------------------
+List of All Grades:
+
+1: Ethan Chua (A0276007H):
+ 1. Quiz 1 : 10.00
+ 2. Quiz 2 : 20.00
+ 3. Quiz 3 : 20.00
+ 4. Quiz 4 : 20.00
+ Final Percentage Score: 94.12%
+
+2: John Smith (A1111111B):
+ 1. Attendance : 4.00
+ Final Percentage Score: 4.71%
+
+3: John Doe (A9999999Z):
+ 1. Quiz 1 : 5.00
+ 2. Quiz 2 : 7.50
+ 3. Quiz 3 : 10.00
+ 4. Quiz 4 : 10.00
+ Final Percentage Score: 44.12%
+
+
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Ensure matric number is of the form A\d{7}[A-Z] (case insensitive)
+------------------------- Result -------------------------
+Score of 0 added successfully to Quiz 1 for A1111111B!
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Grade for component 123 for student A0276007H does not exist in the list!
+------------------------- Result -------------------------
+Score of 0 added successfully to Quiz 2 for A1111111B!
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Grade for component Quiz for student A0276007H does not exist in the list!
+------------------------- Result -------------------------
+Score of 0 added successfully to Quiz 3 for A1111111B!
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Grade for component Quiz for student A0276007H does not exist in the list!
+------------------------- Result -------------------------
+Score of 0 added successfully to Quiz 4 for A1111111B!
-------------------------------------------------------------
-------------------------- Error -------------------------
-Error! Invalid command given!
+------------------------- Result -------------------------
+List of All Grades:
+
+1: Ethan Chua (A0276007H):
+ 1. Quiz 1 : 10.00
+ 2. Quiz 2 : 20.00
+ 3. Quiz 3 : 20.00
+ 4. Quiz 4 : 20.00
+ Final Percentage Score: 94.12%
+
+2: John Smith (A1111111B):
+ 1. Attendance : 4.00
+ 2. Quiz 1 : 0.00
+ 3. Quiz 2 : 0.00
+ 4. Quiz 3 : 0.00
+ 5. Quiz 4 : 0.00
+ Final Percentage Score: 4.71%
+
+3: John Doe (A9999999Z):
+ 1. Quiz 1 : 5.00
+ 2. Quiz 2 : 7.50
+ 3. Quiz 3 : 10.00
+ 4. Quiz 4 : 10.00
+ Final Percentage Score: 44.12%
+
+
-------------------------------------------------------------
------------------------- Result -------------------------
Goodbye! See you soon!
diff --git a/text-ui-test/data_init/componentlist.txt b/text-ui-test/data_init/componentlist.txt
new file mode 100644
index 0000000000..16efe0f58e
--- /dev/null
+++ b/text-ui-test/data_init/componentlist.txt
@@ -0,0 +1 @@
+Attendance | 5 | 5
diff --git a/text-ui-test/data_init/gradelist.txt b/text-ui-test/data_init/gradelist.txt
new file mode 100644
index 0000000000..779f504ca5
--- /dev/null
+++ b/text-ui-test/data_init/gradelist.txt
@@ -0,0 +1 @@
+Attendance | A1111111B | 4
diff --git a/text-ui-test/data_init/studentlist.txt b/text-ui-test/data_init/studentlist.txt
new file mode 100644
index 0000000000..12c2ec5bbd
--- /dev/null
+++ b/text-ui-test/data_init/studentlist.txt
@@ -0,0 +1 @@
+A1111111B | John Smith
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index 8a9d7ee16e..dba3a72b19 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1,52 +1,24 @@
add_student i/A0276007H n/Ethan Chua
add_student i/A9999999Z n/John Doe
add_student i/a1111111b n/John Smith
-add_student i/A n/failure
-
-add_student i/A0276007H
-add_student n/Ethan Chua
-
list_student
-
-delete_student i/A0276007H
-delete_student i/A1234567X
-
-delete_student i/4567890987654567890
-
-list_student
-
-add_component c/Finals w/50 m/100
-add_component c/Quiz w/5 m/20
-add_component c/Quiz w/5 m/15
-add_component c/Quiz w/10 m/20
-add_component c/Fail w/101 m/1
-add_component c/Fail w/0 m/2
-add_component c/Fail w/99 m/20
-add_component c/123 w/hello m/byebye
-add_component c/FailMax w/2147483647 m/2147483647
-add_component c/FailMax w/-1 m/-1
-list_component
-
-delete_component c/Quiz
-list_component
-
-add_grade i/A0276007H c/Quiz s/19
-add_student i/A0276007H n/Ethan Chua
-
-list_student
-add_grade i/A0276007H c/Quiz s/19
-add_grade i/A0276007H c/Quiz s/100
-add_grade i/A1 c/Quiz s/19
-add_grade i/A0276007H c/Wuiz s/19
-
+add_component c/Quiz 1 w/20 m/10
+add_component c/Quiz 2 w/20 m/20
+add_component c/Quiz 3 w/20 m/20
+add_component c/Quiz 4 w/20 m/20
+add_grade c/Quiz 1 s/10 i/A0276007H
+add_grade c/Quiz 2 s/20 i/A0276007H
+add_grade c/Quiz 3 s/20 i/A0276007H
+add_grade c/Quiz 4 s/20 i/A0276007H
+list_grade
+add_grade c/Quiz 1 s/5 i/A9999999Z
+add_grade c/Quiz 2 s/7.5 i/A9999999Z
+add_grade c/Quiz 3 s/10 i/A9999999Z
+add_grade c/Quiz 4 s/10 i/A9999999Z
+list_grade
+add_grade c/Quiz 1 s/0 i/a1111111b
+add_grade c/Quiz 2 s/0 i/a1111111b
+add_grade c/Quiz 3 s/0 i/a1111111b
+add_grade c/Quiz 4 s/0 i/a1111111b
list_grade
-
-delete_grade i/A2 c/Quiz
-delete_grade i/A0276007H c/123
-delete_grade i/A0276007H c/Quiz
-delete_grade i/A0276007H c/Quiz
-
bye
-
-
-
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
old mode 100644
new mode 100755
index 66283a9095..4e664378b9
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -13,12 +13,15 @@ for /f "tokens=*" %%a in (
)
:: delete data directory from previous run if it exists
-if exist "..\text-ui-test\data" (
- rmdir /s /q "..\text-ui-test\data"
+if exist ".\data" (
+ rmdir /s /q ".\data"
)
+:: copy data_init to data
+xcopy "..\..\text-ui-test\data_init" ".\data" /s /e /i
+
java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TXT
cd ..\..\text-ui-test
-FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed!
+FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed!
\ No newline at end of file
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
index 83b6c187d1..f288746493 100755
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -14,6 +14,9 @@ then
rm -rf data
fi
+# copy data_init to data
+cp -r data_init data
+
java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL.TXT
cp EXPECTED.TXT EXPECTED-UNIX.TXT