001package edu.pdx.cs410J.grader; 002 003import com.google.common.annotations.VisibleForTesting; 004import com.google.common.io.ByteStreams; 005import edu.pdx.cs410J.grader.gradebook.Assignment; 006import edu.pdx.cs410J.grader.gradebook.Grade; 007import edu.pdx.cs410J.grader.gradebook.GradeBook; 008import edu.pdx.cs410J.grader.gradebook.Student; 009import jakarta.mail.Address; 010import jakarta.mail.Message; 011import jakarta.mail.MessagingException; 012import jakarta.mail.internet.InternetAddress; 013 014import java.io.File; 015import java.io.FileOutputStream; 016import java.io.IOException; 017import java.io.InputStream; 018import java.time.LocalDateTime; 019import java.time.ZoneId; 020import java.util.Date; 021import java.util.List; 022import java.util.Optional; 023import java.util.function.Predicate; 024import java.util.stream.Stream; 025 026public abstract class ZipFileSubmissionsProcessor extends StudentEmailAttachmentProcessor { 027 public ZipFileSubmissionsProcessor(File directory, GradeBook gradeBook) { 028 super(directory, gradeBook); 029 } 030 031 @Override 032 public Iterable<? extends String> getSupportedContentTypes() { 033 return List.of("application/zip", "application/x-zip-compressed", "application/x-gzip"); 034 } 035 036 @Override 037 public void processAttachment(Message message, String fileName, InputStream inputStream, String contentType) { 038 String studentId; 039 try { 040 studentId = getIdOfStudentInGradeBookWhoSent(message); 041 042 } catch (SubmissionException ex) { 043 logException("While getting student id", ex); 044 return; 045 } 046 047 try { 048 File file = getLocationToWriteFile(fileName, studentId); 049 info("Writing \"" + fileName + "\" to " + file); 050 051 ByteStreams.copy(inputStream, new FileOutputStream(file)); 052 } catch (IOException ex) { 053 logException("While writing \"" + fileName + "\" to \"" + directory + "\"", ex); 054 } 055 056 try { 057 noteSubmissionInGradeBook(message); 058 059 } catch (SubmissionException ex) { 060 logException("While noting submission from \"" + fileName + "\"", ex); 061 } 062 } 063 064 private String getIdOfStudentInGradeBookWhoSent(Message message) throws SubmissionException { 065 Student student = getStudentFromGradeBook(message); 066 return student.getId(); 067 } 068 069 private void noteSubmissionInGradeBook(Message message) throws SubmissionException { 070 Student student = getStudentFromGradeBook(message); 071 Assignment project = getKoansProjectFromGradeBook(); 072 String note = getSubmissionNote(message); 073 074 Grade grade = student.getGrade(project); 075 if (grade == null) { 076 grade = new Grade(project, Grade.NO_GRADE); 077 student.setGrade(project.getName(), grade); 078 } 079 grade.addNote(note); 080 grade.noteSubmission(getSentDate(message)); 081 } 082 083 private String getSubmissionNote(Message message) throws SubmissionException { 084 String senderName = getSenderName(message); 085 LocalDateTime sentDate = getSentDate(message); 086 087 return getSubmissionNote(senderName, sentDate); 088 } 089 090 private LocalDateTime getSentDate(Message message) throws SubmissionException { 091 Date sentDate; 092 try { 093 sentDate = message.getSentDate(); 094 } catch (MessagingException e) { 095 throw new SubmissionException("While getting the sent date", e); 096 } 097 return LocalDateTime.ofInstant(sentDate.toInstant(), ZoneId.systemDefault()); 098 } 099 100 @VisibleForTesting 101 static String getSubmissionNote(String senderName, LocalDateTime submissionTime) { 102 return "Submitted by " + senderName + " on " + Submit.ManifestAttributes.formatSubmissionTime(submissionTime); 103 } 104 105 private Assignment getKoansProjectFromGradeBook() throws SubmissionException { 106 Assignment assignment = gradeBook.getAssignment(getAssignmentName()); 107 if (assignment == null) { 108 throw new SubmissionException("Could not find \"koans\" assignment in grade book"); 109 } 110 return assignment; 111 } 112 113 protected abstract String getAssignmentName(); 114 115 private Student getStudentFromGradeBook(Message message) throws SubmissionException { 116 String senderName = getSenderName(message); 117 Optional<Student> student = getStudentWithName(senderName); 118 if (student.isPresent()) { 119 return student.get(); 120 } 121 122 String senderAddress = getSenderEmailAddress(message); 123 student = getStudentWithEmailAddress(senderAddress); 124 return student.orElseThrow(() -> { 125 String m = "Couldn't find student named \"" + senderName + "\" with email address \"" + 126 senderAddress + "\" in gradebook"; 127 return new SubmissionException(m); 128 }); 129 } 130 131 private Optional<Student> getStudentWithEmailAddress(String senderAddress) { 132 return getStudentsInGradeBook().filter(doesStudentHaveEmailAddress(senderAddress)).findAny(); 133 } 134 135 private Predicate<Student> doesStudentHaveEmailAddress(String address) { 136 return student -> student.getEmail().equals(address); 137 } 138 139 private String getSenderEmailAddress(Message message) throws SubmissionException { 140 Address from = getSender(message); 141 142 if (from instanceof InternetAddress) { 143 return getEmailAddress((InternetAddress) from); 144 } else { 145 return from.toString(); 146 } 147 } 148 149 private Optional<Student> getStudentWithName(String studentName) { 150 return getStudentsInGradeBook().filter(doesStudentHaveName(studentName)).findAny(); 151 } 152 153 private Stream<Student> getStudentsInGradeBook() { 154 return gradeBook.studentsStream(); 155 } 156 157 private Predicate<Student> doesStudentHaveName(String studentName) { 158 return student -> getFullName(student).equals(studentName) || getNickName(student).equals(studentName); 159 } 160 161 private String getNickName(Student student) { 162 return student.getNickName() + " " + student.getLastName(); 163 } 164 165 private String getFullName(Student student) { 166 return student.getFirstName() + " " + student.getLastName(); 167 } 168 169 private File getLocationToWriteFile(String fileName, String studentId) throws IOException { 170 File dir = new File(directory, getAssignmentName()); 171 if (!dir.exists() && !dir.mkdirs()) { 172 throw new IOException("Could not create directory \"" + dir + "\""); 173 } 174 return new File(dir, appendFileSuffixToStudentId(fileName, studentId)); 175 } 176 177 private String appendFileSuffixToStudentId(String fileName, String studentId) { 178 return studentId + fileName.substring(fileName.lastIndexOf('.')); 179 } 180 181 private String getSenderName(Message message) throws SubmissionException { 182 Address from = getSender(message); 183 184 if (from instanceof InternetAddress) { 185 return getStudentName((InternetAddress) from); 186 } else { 187 return from.toString(); 188 } 189 } 190 191 private Address getSender(Message message) throws SubmissionException { 192 Address from; 193 try { 194 from = message.getFrom()[0]; 195 196 } catch (MessagingException ex) { 197 throw new SubmissionException("Could not get from address from " + message, ex); 198 } 199 return from; 200 } 201 202 private String getStudentName(InternetAddress from) { 203 if (from.getPersonal() != null) { 204 return from.getPersonal(); 205 206 } else { 207 return getEmailAddress(from); 208 } 209 } 210 211 private String getEmailAddress(InternetAddress from) { 212 return from.getAddress(); 213 } 214}