package edu.pdx.cs410J.grader;
import com.google.common.annotations.VisibleForTesting;
import org.w3c.dom.Document;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.util.ByteArrayDataSource;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.function.Consumer;
/**
* This program presents a survey that all students in CS410J should
* answer. It emails the results of the survey to the TA and emails a
* receipt back to the student.
*/
public class Survey extends EmailSender {
private static final PrintWriter out = new PrintWriter(System.out, true);
private static final PrintWriter err = new PrintWriter(System.err, true);
private static final BufferedReader in =
new BufferedReader(new InputStreamReader(System.in));
private static final String TA_EMAIL = "sjavata@gmail.com";
private static boolean saveStudentXmlFile = false;
/**
* Returns a textual summary of a <code>Student</code>
*/
private static String getSummary(Student student) {
StringBuilder sb = new StringBuilder();
sb.append("Name: ").append(student.getFullName()).append("\n");
sb.append("UNIX login: ").append(student.getId()).append("\n");
if (student.getEmail() != null) {
sb.append("Email: ").append(student.getEmail()).append("\n");
}
if (student.getSsn() != null) {
sb.append("Student id: ").append(student.getSsn()).append("\n");
}
if (student.getMajor() != null) {
sb.append("Major: ").append(student.getMajor()).append("\n");
}
sb.append("Enrolled in: ").append(student.getEnrolledSection().asString()).append("\n");
return sb.toString();
}
/**
* Ask the student a question and return his response
*/
private static String ask(String question) {
out.print(breakUpInto80CharacterLines(question));
out.print(" ");
out.flush();
String response = null;
try {
response = in.readLine();
} catch (IOException ex) {
printErrorMessageAndExit("** IOException while reading response", ex);
}
return response;
}
/**
* Prints out usage information for this program
*/
private static void usage() {
err.println("\nusage: java Survey [options]");
err.println(" where [options] are:");
err.println(" -mailServer serverName Mail server to send mail");
err.println(" -saveStudentXmlFile Save a copy of the generated student.xml file");
err.println("\n");
System.exit(1);
}
public static void main(String[] args) {
parseCommandLine(args);
printIntroduction();
Student student = gatherStudentInformation();
String learn = ask("What do you hope to learn in CS410J?");
String comments = ask("What else would you like to tell me?");
addNotesToStudent(student, learn, comments);
emailSurveyResults(student, learn, comments);
}
private static void addNotesToStudent(Student student, String learn, String comments) {
if (isNotEmpty(learn)) {
student.addNote(student.getFullName() + " would like to learn " + learn);
}
if (isNotEmpty(comments)) {
student.addNote(student.getFullName() + " has these comments: " + comments);
}
}
private static Student gatherStudentInformation() {
String firstName = ask("What is your first name?");
String lastName = ask("What is your last name?");
String nickName = ask("What is your nickname? (Leave blank if " +
"you don't have one)");
String id = ask("MANDATORY: What is your UNIX login id?");
if (isEmpty(id)) {
printErrorMessageAndExit("** You must enter a valid UNIX login id");
}
Student student = new Student(id);
setValueIfNotEmpty(firstName, student::setFirstName);
setValueIfNotEmpty(lastName, student::setLastName);
setValueIfNotEmpty(nickName, student::setNickName);
askQuestionAndSetValue("What is your email address (doesn't have to be PSU)?", student::setEmail);
askQuestionAndSetValue("What is your student id (XXXXXXXXX)?", student::setSsn);
askQuestionAndSetValue("What is your major?", student::setMajor);
askEnrolledSectionQuestion(student);
return student;
}
private static void askEnrolledSectionQuestion(Student student) {
String answer = ask("MANDATORY: Are you enrolled in the undergraduate or graduate section of this course? [u/g]");
if (isEmpty(answer)) {
printErrorMessageAndExit("Missing enrolled section. Please enter a \"u\" or \"g\"");
}
if (answer.toLowerCase().startsWith("u")) {
student.setEnrolledSection(Student.Section.UNDERGRADUATE);
} else if (answer.toLowerCase().startsWith("g")) {
student.setEnrolledSection(Student.Section.GRADUATE);
} else {
printErrorMessageAndExit("Unknown section \"" + answer + "\". Please enter a \"u\" or \"g\"");
}
}
private static void askQuestionAndSetValue(String question, Consumer<String> setter) {
String answer = ask(question);
setValueIfNotEmpty(answer, setter);
}
static void setValueIfNotEmpty(String string, Consumer<String> setter) {
if (isNotEmpty(string)) {
setter.accept(string);
}
}
private static void emailSurveyResults(Student student, String learn, String comments) {
String summary = verifyInformation(student);
// Email the results of the survey to the TA and CC the student
MimeMessage message = createEmailMessage(student);
MimeBodyPart textPart = createEmailText(learn, comments, summary);
MimeBodyPart xmlFilePart = createXmlAttachment(student);
addAttachmentsAndSendEmail(message, textPart, xmlFilePart);
}
private static void addAttachmentsAndSendEmail(MimeMessage message, MimeBodyPart textPart, MimeBodyPart filePart) {
// Finally, add the attachments to the message and send it
try {
Multipart mp = new MimeMultipart();
mp.addBodyPart(textPart);
mp.addBodyPart(filePart);
message.setContent(mp);
Transport.send(message);
logSentEmail(message);
} catch (MessagingException ex) {
printErrorMessageAndExit("** Exception while adding parts and sending", ex);
}
}
private static void logSentEmail(MimeMessage message) throws MessagingException {
StringBuilder sb = new StringBuilder();
sb.append("\nAn email with with subject \"");
sb.append(message.getSubject());
sb.append("\" was sent to ");
Address[] recipients = message.getAllRecipients();
for (int i = 0; i < recipients.length; i++) {
Address recipient = recipients[i];
sb.append(recipient);
if (i < recipients.length - 2) {
sb.append(", ");
} else if (i < recipients.length - 1) {
sb.append(" and ");
}
}
out.println(breakUpInto80CharacterLines(sb.toString()));
}
private static MimeBodyPart createXmlAttachment(Student student) {
byte[] xmlBytes = getXmlBytes(student);
if (saveStudentXmlFile) {
writeStudentXmlToFile(xmlBytes, student);
}
DataSource ds = new ByteArrayDataSource(xmlBytes, "text/xml");
DataHandler dh = new DataHandler(ds);
MimeBodyPart filePart = new MimeBodyPart();
try {
String xmlFileTitle = student.getId() + ".xml";
filePart.setDataHandler(dh);
filePart.setFileName(xmlFileTitle);
filePart.setDescription("XML file for " + student.getFullName());
} catch (MessagingException ex) {
printErrorMessageAndExit("** Exception with file part", ex);
}
return filePart;
}
private static void writeStudentXmlToFile(byte[] xmlBytes, Student student) {
File directory = new File(System.getProperty("user.dir"));
File file = new File(directory, student.getId() + ".xml");
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(xmlBytes);
fos.flush();
} catch (IOException e) {
printErrorMessageAndExit("Could not write student XML file: " + file, e);
}
out.println("\nSaved student XML file to " + file + "\n");
}
private static MimeBodyPart createEmailText(String learn, String comments, String summary) {
// Create the text portion of the message
StringBuilder text = new StringBuilder();
text.append("Results of CS410J Survey:\n\n");
text.append(summary);
text.append("\n\nWhat do you hope to learn in CS410J?\n\n");
text.append(learn);
text.append("\n\nIs there anything else you'd like to tell me?\n\n");
text.append(comments);
text.append("\n\nThanks for filling out this survey!\n\nDave");
MimeBodyPart textPart = new MimeBodyPart();
try {
textPart.setContent(text.toString(), "text/plain");
// Try not to display text as separate attachment
textPart.setDisposition("inline");
} catch (MessagingException ex) {
printErrorMessageAndExit("** Exception with text part", ex);
}
return textPart;
}
private static MimeMessage createEmailMessage(Student student) {
MimeMessage message = null;
try {
message = newEmailTo(newEmailSession(false), TA_EMAIL, "CS410J Survey for " + student.getFullName());
String studentEmail = student.getEmail();
if (studentEmail != null) {
InternetAddress[] cc = {new InternetAddress(studentEmail)};
message.setRecipients(Message.RecipientType.CC, cc);
}
} catch (AddressException ex) {
printErrorMessageAndExit("** Exception with email address", ex);
} catch (MessagingException ex) {
printErrorMessageAndExit("** Exception while setting recipients email", ex);
}
return message;
}
private static byte[] getXmlBytes(Student student) {
// Create a temporary "file" to hold the Student's XML file. We
// use a byte array so that potentially sensitive data (SSN, etc.)
// is not written to disk
byte[] bytes = null;
Document xmlDoc = XmlDumper.toXml(student);
try {
bytes = XmlHelper.getBytesForXmlDocument(xmlDoc);
} catch (TransformerException ex) {
ex.printStackTrace(System.err);
System.exit(1);
}
return bytes;
}
private static String verifyInformation(Student student) {
String summary = getSummary(student);
out.println("\nYou entered the following information about " +
"yourself:\n");
out.println(summary);
String verify = ask("\nIs this information correct (y/n)?");
if (!verify.equals("y")) {
printErrorMessageAndExit("** Not sending information. Exiting.");
}
return summary;
}
private static void printErrorMessageAndExit(String message) {
printErrorMessageAndExit(message, null);
}
private static void printErrorMessageAndExit(String message, Throwable ex) {
err.println(message);
if (ex != null) {
ex.printStackTrace(err);
}
System.exit(1);
}
private static boolean isNotEmpty(String string) {
return string != null && !string.equals("");
}
private static boolean isEmpty(String string) {
return string == null || string.equals("");
}
private static void printIntroduction() {
// Ask the student a bunch of questions
String welcome =
"Welcome to the CS410J Survey Program. I'd like to ask you a couple of " +
"questions about yourself. Except for your UNIX login id and the section " +
"that you are enrolled in, no question" +
"is mandatory. Your answers will be emailed to the TA and a receipt " +
"will be emailed to you.";
out.println("");
out.println(breakUpInto80CharacterLines(welcome));
out.println("");
}
@VisibleForTesting
static String breakUpInto80CharacterLines(String message) {
StringBuilder sb = new StringBuilder();
int currentLineLength = 0;
String[] words = message.split(" ");
for (String word : words) {
if (currentLineLength + word.length() > 80) {
sb.append('\n');
sb.append(word);
currentLineLength = word.length();
} else {
if (currentLineLength > 0) {
sb.append(' ');
}
sb.append(word);
currentLineLength += word.length() + 1;
}
}
return sb.toString();
}
private static void parseCommandLine(String[] args) {
// Parse the command line
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.equals("-mailServer")) {
if (++i >= args.length) {
err.println("** Missing mail server name");
usage();
}
serverName = arg;
} else if (arg.equals("-saveStudentXmlFile")) {
saveStudentXmlFile = true;
} else if (arg.startsWith("-")) {
err.println("** Unknown command line option: " + arg);
usage();
} else {
err.println("** Spurious command line: " + arg);
usage();
}
}
}
}