001package edu.pdx.cs410J.grader.gradebook; 002 003import edu.pdx.cs410J.ParserException; 004 005import java.io.File; 006import java.io.FileNotFoundException; 007import java.io.IOException; 008import java.io.PrintWriter; 009import java.time.LocalDateTime; 010import java.util.ArrayList; 011import java.util.List; 012import java.util.stream.Collectors; 013 014/** 015 * This class represent the grade a student got on an assignment. 016 */ 017public class Grade extends NotableImpl { 018 /** 019 * The assignment is not complete 020 */ 021 public static final double INCOMPLETE = -1.0; 022 023 /** 024 * Some work has been submitted, but no grade has been assigned 025 */ 026 public static final double NO_GRADE = -2.0; 027 028 private final String assignmentName; 029 private double score; // Score student received 030 private final List<SubmissionInfo> submissionInfo = new ArrayList<>(); 031 032 /** 033 * Creates a <code>Grade</code> for a given assignment 034 */ 035 public Grade(String assignmentName, double score) { 036 this.assignmentName = assignmentName; 037 this.score = score; 038 this.setDirty(true); // Initially dirty 039 } 040 041 public Grade(Assignment project, double score) { 042 this(project.getName(), score); 043 } 044 045 /** 046 * Returns the name of the assignment that this <code>Grade</code> 047 * is for. 048 */ 049 public String getAssignmentName() { 050 return this.assignmentName; 051 } 052 053 /** 054 * Returns the score the student received on the assignment. 055 */ 056 public double getScore() { 057 return this.score; 058 } 059 060 public boolean isIncomplete() { 061 return this.getScore() == INCOMPLETE; 062 } 063 064 /** 065 * Sets the score the student received on the assignment. 066 */ 067 public void setScore(double score) { 068 this.score = score; 069 this.setDirty(true); 070 } 071 072 private static PrintWriter err = new PrintWriter(System.err, true); 073 074 /** 075 * Returns a brief textual description of this <code>Grade</code> 076 */ 077 public String toString() { 078 return this.assignmentName + ": " + this.score; 079 } 080 081 /** 082 * Prints usage information about the main program. 083 */ 084 private static void usage() { 085 err.println("\njava Grade [options] -xmlFile xmlFile " + 086 "-id id -assignment name"); 087 err.println(" where [options] ares:"); 088 err.println(" -score points How many points student got"); 089 err.println(" -incomplete Notes that the assignment " 090 + "was INCOMPLETE"); 091 err.println(" -no-grade No grade was given for the " 092 + "assignment"); 093 err.println(" -note note A note about the grade"); 094 err.println("\n"); 095 System.exit(1); 096 } 097 098 /** 099 * A main program that creates/edits a student's grade. 100 */ 101 public static void main(String[] args) { 102 String xmlFile = null; 103 String id = null; 104 String assignment = null; 105 String score = null; 106 boolean incomplete = false; 107 boolean noGrade = false; 108 String note = null; 109 110 // Parse the command line 111 for (int i = 0; i < args.length; i++) { 112 if (args[i].equals("-xmlFile")) { 113 if (++i >= args.length) { 114 err.println("** Missing XML file name"); 115 usage(); 116 } 117 118 xmlFile = args[i]; 119 120 } else if (args[i].equals("-id")) { 121 if (++i >= args.length) { 122 err.println("** Missing student id"); 123 usage(); 124 } 125 126 id = args[i]; 127 128 } else if (args[i].equals("-assignment")) { 129 if (++i >= args.length) { 130 err.println("** Missing assignment name"); 131 usage(); 132 } 133 134 assignment = args[i]; 135 136 } else if (args[i].equals("-score")) { 137 if (++i >= args.length) { 138 err.println("** Missing score"); 139 usage(); 140 } 141 142 score = args[i]; 143 144 } else if (args[i].equals("-incomplete")) { 145 incomplete = true; 146 147 } else if (args[i].equals("-no-grade")) { 148 noGrade = true; 149 150 } else if (args[i].equals("-note")) { 151 if (++i >= args.length) { 152 err.println("** Missing note text"); 153 usage(); 154 } 155 156 note = args[i]; 157 158 } else if (args[i].startsWith("-")) { 159 err.println("** Unknown command line option: " + args[i]); 160 usage(); 161 162 } else { 163 err.println("** Spurious command line: " + args[i]); 164 usage(); 165 } 166 } 167 168 // Verify command line 169 if (xmlFile == null) { 170 err.println("** No XML file specified"); 171 usage(); 172 } 173 174 if (id == null) { 175 err.println("** No student id specified"); 176 usage(); 177 return; 178 } 179 180 if (assignment == null) { 181 err.println("** No assignment name specified"); 182 usage(); 183 } 184 185 File file = new File(xmlFile); 186 if (!file.exists()) { 187 err.println("** Grade book file " + xmlFile + 188 " does not exist"); 189 System.exit(1); 190 } 191 192 // Parse XML file 193 GradeBook book = null; 194 try { 195 XmlGradeBookParser parser = new XmlGradeBookParser(file); 196 book = parser.parse(); 197 198 } catch (FileNotFoundException ex) { 199 err.println("** Could not find file: " + ex.getMessage()); 200 System.exit(1); 201 202 } catch (IOException ex) { 203 err.println("** IOException during parsing: " + ex.getMessage()); 204 System.exit(1); 205 206 } catch (ParserException ex) { 207 err.println("** Exception while parsing " + file + ": " + ex); 208 System.exit(1); 209 } 210 211 if (!book.containsStudent(id)) { 212 err.println("** No student with id: " + id); 213 System.exit(1); 214 } 215 216 // Get the student 217 Student student = book.getStudent(id).get(); 218 219 // Get the grade 220 Grade grade = student.getGrade(assignment); 221 if (grade == null) { 222 // Create a new Grade 223 double s; 224 225 if (incomplete) { 226 s = Grade.INCOMPLETE; 227 228 } else if (noGrade) { 229 s = Grade.NO_GRADE; 230 231 } else if (score == null) { 232 err.println("** No score for " + assignment); 233 System.exit(1); 234 s = -4.2; 235 236 } else { 237 try { 238 s = Double.parseDouble(score); 239 240 } catch (NumberFormatException ex) { 241 err.println("** Score is not a double: " + score); 242 System.exit(1); 243 s = -4.2; 244 } 245 } 246 247 grade = new Grade(assignment, s); 248 student.setGrade(assignment, grade); 249 250 } else { 251 // Set the grade 252 if (incomplete) { 253 grade.setScore(Grade.INCOMPLETE); 254 255 } else if (noGrade) { 256 grade.setScore(Grade.NO_GRADE); 257 258 } else if (score != null) { 259 try { 260 grade.setScore(Double.parseDouble(score)); 261 262 } catch (NumberFormatException ex) { 263 err.println("** Score is not a double: " + score); 264 } 265 } 266 } 267 268 if (note != null) { 269 grade.addNote(note); 270 } 271 272 // Write the changes back out to the XML file 273 try { 274 XmlDumper dumper = new XmlDumper(file); 275 dumper.dump(book); 276 277 } catch (IOException ex) { 278 err.println("** While dumping to " + file + ": " + ex); 279 System.exit(1); 280 } 281 } 282 283 public boolean isNotGraded() { 284 return this.getScore() == Grade.NO_GRADE; 285 } 286 287 public List<LocalDateTime> getSubmissionTimes() { 288 return submissionInfo.stream().map(SubmissionInfo::getSubmissionTime).collect(Collectors.toList()); 289 } 290 291 public SubmissionInfo noteSubmission(LocalDateTime submissionTime) { 292 SubmissionInfo info = new SubmissionInfo(); 293 info.setSubmissionTime(submissionTime); 294 return noteSubmission(info); 295 } 296 297 public SubmissionInfo noteSubmission(SubmissionInfo info) { 298 this.setDirty(true); 299 this.submissionInfo.add(info); 300 301 return info; 302 } 303 304 public List<SubmissionInfo> getSubmissionInfos() { 305 return this.submissionInfo; 306 } 307 308 public static class SubmissionInfo { 309 private LocalDateTime submissionTime; 310 private Double estimatedHours; 311 private boolean isLate; 312 313 public LocalDateTime getSubmissionTime() { 314 return submissionTime; 315 } 316 317 public SubmissionInfo setSubmissionTime(LocalDateTime submissionTime) { 318 this.submissionTime = submissionTime; 319 return this; 320 } 321 322 public Double getEstimatedHours() { 323 return estimatedHours; 324 } 325 326 public SubmissionInfo setEstimatedHours(Double estimatedHours) { 327 this.estimatedHours = estimatedHours; 328 return this; 329 } 330 331 public SubmissionInfo setIsLate(boolean isLate) { 332 this.isLate = isLate; 333 return this; 334 } 335 336 public boolean isLate() { 337 return isLate; 338 } 339 } 340}