001package edu.pdx.cs.joy.grader;
002
003import edu.pdx.cs.joy.ParserException;
004import edu.pdx.cs.joy.grader.gradebook.*;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008import java.io.*;
009import java.util.ArrayList;
010import java.util.List;
011import java.util.Optional;
012
013public class ProjectGradesImporter {
014
015  private final GradeBook gradeBook;
016  private final Assignment assignment;
017  private final Logger logger;
018
019  public ProjectGradesImporter(GradeBook gradeBook, Assignment assignment, Logger logger) {
020    this.gradeBook = gradeBook;
021    this.assignment = assignment;
022    this.logger = logger;
023  }
024
025  public static TestedProjectSubmissionOutputParser.ProjectScore getScoreFrom(Reader reader) throws TestedProjectSubmissionOutputParser.TestedProjectSubmissionOutputParsingException, IOException {
026    return TestedProjectSubmissionOutputParser.parseTestedSubmissionOutput(reader);
027  }
028
029  public void recordScoreFromProjectReport(String studentId, Reader report) throws TestedProjectSubmissionOutputParser.TestedProjectSubmissionOutputParsingException, IOException {
030    TestedProjectSubmissionOutputParser.ProjectScore score = getScoreFrom(report);
031
032    if (score.getTotalPoints() != this.assignment.getPoints()) {
033      String message = "Assignment " + this.assignment.getName() + " should be worth " + this.assignment.getPoints() +
034        " points, but the report for " + studentId + " was out of " + score.getTotalPoints();
035      throw new IllegalStateException(message);
036    }
037
038    Optional<Student> maybeStudent = gradeBook.getStudent(studentId);
039    if (maybeStudent.isEmpty()) {
040      warn("Student \"" + studentId + "\" not found in gradebook");
041      return;
042    }
043
044    Student student = maybeStudent.get();
045    Grade grade = student.getGrade(this.assignment);
046    if (grade == null) {
047      grade = new Grade(assignment, score.getScore());
048      student.setGrade(assignment.getName(), grade);
049
050    } else {
051      grade.setScore(score.getScore());
052    }
053
054    info("Recorded grade of " + score.getScore() + " for " + studentId);
055  }
056
057  private void info(String message) {
058    this.logger.info(message);
059  }
060
061  private void warn(String message) {
062    logger.warn(message);
063  }
064
065  public static void main(String[] args) {
066    String gradeBookFileName = null;
067    String assignmentName = null;
068    List<String> projectFileNames = new ArrayList<>();
069
070    for (String arg : args) {
071      if (gradeBookFileName == null) {
072        gradeBookFileName = arg;
073
074      } else if (assignmentName == null) {
075        assignmentName = arg;
076
077      } else {
078        projectFileNames.add(arg);
079      }
080    }
081
082    usageIfNull(gradeBookFileName, "Missing grade book file");
083    usageIfNull(assignmentName, "Missing assignment name");
084    usageIfEmpty(projectFileNames, "No project file names provided");
085
086    GradeBook gradeBook = getGradeBook(gradeBookFileName);
087    Assignment assignment = getAssignment(assignmentName, gradeBook);
088    Logger logger = LoggerFactory.getLogger(ProjectGradesImporter.class.getPackage().getName());
089
090    ProjectGradesImporter importer = new ProjectGradesImporter(gradeBook, assignment, logger);
091
092    for (String projectFileName : projectFileNames) {
093      File projectFile = getProjectFile(projectFileName);
094      String studentId = getStudentIdFromFileName(projectFile);
095      try {
096        importer.recordScoreFromProjectReport(studentId, new FileReader(projectFile));
097
098      } catch (FileNotFoundException ex) {
099        throw new IllegalStateException("Could not find file \"" + projectFile + "\"");
100
101      } catch (IllegalStateException | IOException ex) {
102        throw new IllegalStateException("While recording score from " + projectFileName, ex);
103
104      } catch (TestedProjectSubmissionOutputParser.TestedProjectSubmissionOutputParsingException e) {
105        logger.warn("Could not find score in " + projectFileName);
106      }
107    }
108
109    saveGradeBookIfModified(gradeBook, gradeBookFileName);
110  }
111
112  private static void saveGradeBookIfModified(GradeBook gradeBook, String gradeBookFileName) {
113    if (gradeBook.isDirty()) {
114      File file = new File(gradeBookFileName);
115      try {
116        XmlDumper dumper = new XmlDumper(file);
117        dumper.dump(gradeBook);
118
119      } catch (IOException e) {
120        usage("Can't write grade book in \"" + gradeBookFileName + "\"");
121      }
122    }
123  }
124
125  private static String getStudentIdFromFileName(File projectFile) {
126    String fileName = projectFile.getName();
127    int index = fileName.lastIndexOf('.');
128    if (index < 0) {
129      return usage("Project file \"" + fileName + "\" does not have a file extension");
130    }
131
132    return fileName.substring(0, index);
133  }
134
135  private static File getProjectFile(String projectFileName) {
136    File projectFile = new File(projectFileName);
137    if (!projectFile.exists()) {
138      return usage("Project file \"" + projectFileName + "\" does not exist");
139
140    } else if (!projectFile.isFile()) {
141      return usage("Project file \"" + projectFileName + "\" is not a file");
142    }
143
144    return projectFile;
145  }
146
147  private static Assignment getAssignment(String assignmentName, GradeBook gradeBook) {
148    Assignment assignment = gradeBook.getAssignment(assignmentName);
149    if (assignment == null) {
150      return usage("Could not find assignment \"" + assignmentName + "\" in grade book");
151    }
152    return assignment;
153  }
154
155  private static GradeBook getGradeBook(String gradeBookFileName) {
156    try {
157      XmlGradeBookParser parser = new XmlGradeBookParser(gradeBookFileName);
158      return parser.parse();
159
160    } catch (IOException ex) {
161      return usage("Couldn't read grade book file \"" + gradeBookFileName + "\"");
162
163    } catch (ParserException ex) {
164      return usage("Couldn't parse grade book file \"" + gradeBookFileName + "\"");
165    }
166  }
167
168  private static void usageIfEmpty(List<String> list, String message) {
169    if (list.isEmpty()) {
170      usage(message);
171    }
172  }
173
174  private static void usageIfNull(String argument, String message) {
175    if (argument == null) {
176      usage(message);
177    }
178  }
179
180  private static <T> T usage(String message) {
181    PrintStream err = System.err;
182
183    err.println("** " + message);
184
185    err.println("java ProjectGradesImporter gradeBookFileName assignmentName projectFileName+");
186    err.println();
187    err.println("Imports grades (of the form \"5.8 out of 6.0\") from project reports into a grade book.");
188    err.println("The name of the project report file begins with the student's id.");
189    err.println();
190    err.println("  gradeBookFileName     Name of file containing grade book");
191    err.println("  assignmentName        Assignment/project whose score is to be recorded");
192    err.println("  projectFileName       Name of file containing graded project");
193    err.println();
194
195    System.exit(1);
196
197    return null;
198  }
199
200}