|
| 1 | +## Feature Description |
| 2 | + |
| 3 | +This feature provides a way to identify submission grades that have not been recorded in the system. It helps |
| 4 | +instructors ensure that all student submissions are accounted for and graded appropriately. |
| 5 | + |
| 6 | +This is a new feature of the FindUngradedSubmissions tool, which scans through student submissions and compares them |
| 7 | +against recorded grades to identify any discrepancies. |
| 8 | + |
| 9 | +This will feature will require a new command line parameter to the main() method of FindUngradedSubmissions. The first |
| 10 | +parameter will now be the name of a gradebook XML file that contains the recorded grades. Subsequent parameters will |
| 11 | +continue to be the names of one or more submission files/directories to scan for untested, ungraded, and now unrecorded |
| 12 | +grades. |
| 13 | + |
| 14 | +Here's the new command line usage: |
| 15 | + |
| 16 | +``` |
| 17 | +$ java -jar target/grader-1.5.2-SNAPSHOT.jar findUngradedSubmissions |
| 18 | +Usage: java FindUngradedSubmissions -includeReason gradeBookXmlFile submissionZipOrDirectory+ |
| 19 | +``` |
| 20 | + |
| 21 | +The FindUngradedSubmissions class will now output the names of the .out files whose grades do not match what has been |
| 22 | +recorded in the student's gradebook XML file. |
| 23 | + |
| 24 | +``` |
| 25 | +0 submissions need to be tested: |
| 26 | +0 submissions need to be graded: |
| 27 | +3 submissions have unrecorded grades: |
| 28 | + ./fred.out |
| 29 | + ./jane.out |
| 30 | + ./alex.out |
| 31 | +``` |
| 32 | + |
| 33 | +## Implementation Ideas |
| 34 | + |
| 35 | +The `SubmissionAnalysis` record will need a new gradeNeedsToBeRecorded() property. |
| 36 | + |
| 37 | +The gradebook XML file should be parsed into a GradeBook object. Information about student grades can be obtained from |
| 38 | +the GradeBook. |
| 39 | + |
| 40 | +The name of the testOutput file begins with the student's login ID, which can be used to look up the recorded grade in |
| 41 | +the GradeBook XML file. For instance, the student id of `fred` corresponds to the `fred.out` test output file. |
| 42 | + |
| 43 | +In order to determine if a submission's grade is unrecorded, we'll need to know which project assigment is associated |
| 44 | +with the testOutput (`.out`) file. The project can be determined from a line that looks like this in the `.out` file: |
| 45 | + |
| 46 | +``` |
| 47 | + The Joy of Coding Project 1: edu.pdx.cs410J.studentId.Project1 |
| 48 | +``` |
| 49 | + |
| 50 | +In this example, the project assignment is "Project1". This is the name of the assignment in the GradeBook XML file. |
| 51 | + |
| 52 | +The grade should be considered unrecorded if the recorded grade is missing or different from the grade determined in the |
| 53 | +GradeBook XML file. |
| 54 | + |
| 55 | +## Test Cases |
| 56 | + |
| 57 | +A submission is considered graded if there is a line like "4.6 out of 5.0" in the testOutput `.out` file, even if |
| 58 | +that grade is not recorded in the GradeBook XML file. In this case, the submission should be considered unrecorded. |
| 59 | + |
| 60 | +A submission with a line like " out of 5.0" on line 7 is not considered to be graded. |
| 61 | + |
| 62 | +If a line like " out of 5.0" appears on a line after line 7, the submission should be considered graded. We can assume |
| 63 | +that there is a note from the grade in the testOutput .out file that instructs the student to fix a fundamental flaw and |
| 64 | +resubmit. Since there is no grade, the submission does not need to be recorded in the gradebook, and it should not be |
| 65 | +considered unrecorded. |
| 66 | + |
| 67 | +If a submission hasn't been graded yet, it should not be considered unrecorded. |
| 68 | + |
| 69 | +If the submission has been graded (that is, a grade appears in the testOutput `.out` file) and there is no grade for the |
| 70 | +assignment in the GradeBook, the submission should be considered unrecorded. |
| 71 | + |
| 72 | +If the submission has been graded and the grade in the GradeBook is different from the grade in the testOutput `.out`, |
| 73 | +the submission should be considered unrecorded. |
| 74 | + |
| 75 | +If the submission has been graded and the grade in the GradeBook matches the grade in the testOutput `.out`, the |
| 76 | +submission should not be considered unrecorded. |
| 77 | + |
| 78 | +If the submission has not been graded, it should not be considered unrecorded regardless of what is in the GradeBook. |
| 79 | + |
| 80 | +There should be an end-to-end integration test for that includes parsing a gradebook XML file and multiple submission |
| 81 | +files (.out) that verify that some .out files have grades that need to be recorded and other .out files don't. This |
| 82 | +test should be implemented in a new integration test class called `FindUnrecordedSubmissionGradesIT` in the src/it/java |
| 83 | +directory. It should use the invokeMain() from InvokeMainTestCase to run the FindUngradedSubmissions main() method with |
| 84 | +the appropriate command line arguments and validate that the expected output is written to standard output. The files |
| 85 | +that the test uses should be placed in the src/it/resources directory. Or they could be generated programmatically by |
| 86 | +the test and placed in a temporary directory injected with JUnit 5's @TempDir annotation. |
| 87 | + |
| 88 | +## Implementation Steps |
| 89 | + |
| 90 | +### 1. Update the `SubmissionAnalysis` record |
| 91 | + |
| 92 | +Add a new boolean property `gradeNeedsToBeRecorded` to the `SubmissionAnalysis` record (around line 196). |
| 93 | + |
| 94 | +**Changes:** |
| 95 | + |
| 96 | +- Add `boolean gradeNeedsToBeRecorded` as the fifth parameter to the record |
| 97 | +- Update all places that construct `SubmissionAnalysis` objects to pass `false` for this parameter initially |
| 98 | + |
| 99 | +### 2. Create a new interface `GradeBookProvider` |
| 100 | + |
| 101 | +Add a new interface in the `FindUngradedSubmissions` class to provide access to the GradeBook. |
| 102 | + |
| 103 | +**Interface signature:** |
| 104 | + |
| 105 | +```java |
| 106 | +interface GradeBookProvider { |
| 107 | + Optional<GradeBook> getGradeBook(); |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +This allows for testability by mocking the gradebook access in tests. |
| 112 | + |
| 113 | +### 3. Add GradeBookProvider to the constructor |
| 114 | + |
| 115 | +Update the `FindUngradedSubmissions` constructor to accept an optional `GradeBookProvider` parameter. |
| 116 | + |
| 117 | +**Changes:** |
| 118 | + |
| 119 | +- Add `GradeBookProvider` field to the class (around line 24-27) |
| 120 | +- Update the `@VisibleForTesting` constructor (around line 30) to accept a `GradeBookProvider` parameter |
| 121 | +- Update the default constructor (around line 36) to pass `null` for the gradebook provider |
| 122 | + |
| 123 | +### 4. Extend `TestOutputDetails` record |
| 124 | + |
| 125 | +Add fields to capture the project assignment name and the grade from the test output file. |
| 126 | + |
| 127 | +**Changes:** |
| 128 | + |
| 129 | +- Add `String projectName` field to the `TestOutputDetails` record (around line 193) |
| 130 | +- Add `Double grade` field to the `TestOutputDetails` record (around line 193) |
| 131 | +- Update all places that construct `TestOutputDetails` objects |
| 132 | + |
| 133 | +### 5. Update `TestOutputDetailsProviderFromTestOutputFile` |
| 134 | + |
| 135 | +Modify the `TestOutputDetailsProviderFromTestOutputFile` class (around line 233) to extract: |
| 136 | + |
| 137 | +- The project name from lines matching the pattern: `The Joy of Coding Project \\d+: edu.pdx.cs.\\w+.\\w+.(\\w+)` |
| 138 | +- The grade value (already has `parseGrade` method at line 271) |
| 139 | + |
| 140 | +**Changes to `TestOutputDetailsCreator` inner class:** |
| 141 | + |
| 142 | +- Add `String projectName` field |
| 143 | +- Add `Double grade` field |
| 144 | +- In the `accept(String line)` method, add logic to: |
| 145 | + - Extract project name using a regex pattern |
| 146 | + - Capture the grade value (already extracts it, just need to store it) |
| 147 | +- Update `createTestOutputDetails()` to include the new fields |
| 148 | + |
| 149 | +### 6. Update the `analyzeSubmission` method |
| 150 | + |
| 151 | +Modify the `analyzeSubmission` method (around line 38) to determine if a grade needs to be recorded. |
| 152 | + |
| 153 | +**New logic after determining `needsToBeGraded`:** |
| 154 | + |
| 155 | +```java |
| 156 | +boolean gradeNeedsToBeRecorded = false; |
| 157 | +if(!needsToBeGraded &&gradeBookProvider !=null){ |
| 158 | +gradeNeedsToBeRecorded = |
| 159 | + |
| 160 | +checkIfGradeNeedsRecording(submission, testOutput); |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +### 7. Create a new method `checkIfGradeNeedsRecording` |
| 165 | + |
| 166 | +Add a new private method to check if a grade needs to be recorded. |
| 167 | + |
| 168 | +**Method signature:** |
| 169 | + |
| 170 | +```java |
| 171 | +private boolean checkIfGradeNeedsRecording(SubmissionDetails submission, TestOutputDetails testOutput) |
| 172 | +``` |
| 173 | + |
| 174 | +**Logic:** |
| 175 | + |
| 176 | +- Return false if testOutput doesn't have a project name or grade |
| 177 | +- Get the GradeBook from the provider (return false if not available) |
| 178 | +- Get the student from the gradebook using `submission.studentId()` |
| 179 | +- If student not found, return true (unrecorded) |
| 180 | +- Get the grade for the assignment using `student.getGrade(testOutput.projectName())` |
| 181 | +- If grade is null, return true (unrecorded) |
| 182 | +- If grade.getScore() != testOutput.grade(), return true (unrecorded) |
| 183 | +- Otherwise return false |
| 184 | + |
| 185 | +### 8. Update the `main` method |
| 186 | + |
| 187 | +Modify the `main` method to: |
| 188 | + |
| 189 | +- Parse the new command-line parameter for the gradebook XML file |
| 190 | +- Create a `GradeBookProvider` implementation that loads the gradebook |
| 191 | +- Pass the provider to the `FindUngradedSubmissions` constructor |
| 192 | + |
| 193 | +**Changes:** |
| 194 | + |
| 195 | +- Update usage message (around line 89) to show the new syntax |
| 196 | +- Parse the first non-option argument as the gradebook XML file path |
| 197 | +- Remaining arguments are submission files/directories |
| 198 | +- Create a `GradeBookProviderFromXmlFile` implementation |
| 199 | +- Track `gradeNeedsToBeRecorded` submissions in the analysis loop (around line 111) |
| 200 | +- Add a third call to `printOutAnalyses` for unrecorded grades |
| 201 | + |
| 202 | +### 9. Create `GradeBookProviderFromXmlFile` implementation |
| 203 | + |
| 204 | +Add a new static inner class that implements `GradeBookProvider`. |
| 205 | + |
| 206 | +**Implementation:** |
| 207 | + |
| 208 | +```java |
| 209 | +private static class GradeBookProviderFromXmlFile implements GradeBookProvider { |
| 210 | + private final String xmlFilePath; |
| 211 | + private GradeBook gradeBook; |
| 212 | + |
| 213 | + GradeBookProviderFromXmlFile(String xmlFilePath) { |
| 214 | + this.xmlFilePath = xmlFilePath; |
| 215 | + } |
| 216 | + |
| 217 | + @Override |
| 218 | + public Optional<GradeBook> getGradeBook() { |
| 219 | + if (gradeBook == null && xmlFilePath != null) { |
| 220 | + try { |
| 221 | + XmlGradeBookParser parser = new XmlGradeBookParser(xmlFilePath); |
| 222 | + gradeBook = parser.parse(); |
| 223 | + } catch (Exception e) { |
| 224 | + System.err.println("Error loading gradebook: " + e.getMessage()); |
| 225 | + return Optional.empty(); |
| 226 | + } |
| 227 | + } |
| 228 | + return Optional.ofNullable(gradeBook); |
| 229 | + } |
| 230 | +} |
| 231 | +``` |
| 232 | + |
| 233 | +### 10. Update test file imports and add new tests |
| 234 | + |
| 235 | +In `FindUngradedSubmissionsTest.java`, add test cases for the new functionality: |
| 236 | + |
| 237 | +**Test cases to add:** |
| 238 | + |
| 239 | +- `submissionWithUngradedTestOutputDoesNotNeedToBeRecorded()` - verify that if submission isn't graded, it's not |
| 240 | + considered unrecorded |
| 241 | +- `submissionWithGradeNotInGradeBookNeedsToBeRecorded()` - verify missing grade in gradebook is detected |
| 242 | +- `submissionWithDifferentGradeInGradeBookNeedsToBeRecorded()` - verify mismatched grades are detected |
| 243 | +- `submissionWithMatchingGradeInGradeBookDoesNotNeedToBeRecorded()` - verify matching grades are not flagged |
| 244 | +- `parseProjectNameFromTestOutputLine()` - verify project name extraction works |
| 245 | +- `testOutputDetailsIncludesProjectNameAndGrade()` - verify TestOutputDetails captures all needed info |
| 246 | + |
| 247 | +### 11. Integration Testing |
| 248 | + |
| 249 | +Create or update integration tests to verify: |
| 250 | + |
| 251 | +- Command-line parsing with the new gradebook parameter |
| 252 | +- End-to-end flow with actual XML files |
| 253 | +- Output formatting shows the three categories correctly |
| 254 | + |
| 255 | +### Summary of Key Classes to Modify |
| 256 | + |
| 257 | +1. `FindUngradedSubmissions.java` - main implementation |
| 258 | +2. `FindUngradedSubmissionsTest.java` - unit tests |
| 259 | +3. New imports needed: |
| 260 | + - `edu.pdx.cs.joy.grader.gradebook.GradeBook` |
| 261 | + - `edu.pdx.cs.joy.grader.gradebook.Grade` |
| 262 | + - `edu.pdx.cs.joy.grader.gradebook.Student` |
| 263 | + - `edu.pdx.cs.joy.grader.gradebook.XmlGradeBookParser` |
| 264 | + - `java.util.Optional` |
0 commit comments