001package edu.pdx.cs.joy.grader.gradebook;
002
003import edu.pdx.cs.joy.ParserException;
004
005import java.io.File;
006import java.io.FileNotFoundException;
007import java.io.IOException;
008import java.io.PrintWriter;
009import java.util.*;
010
011/**
012 * This class represents a student who is taking the course.
013 */
014public class Student extends NotableImpl {
015
016  private String id;
017  private String firstName;
018  private String lastName;
019  private String nickName;
020  private String email;
021  private String major;
022  private LetterGrade letterGrade;
023
024  /** Maps name of Assignment to Grade.  The grades are sorted so that
025   * they will appear in a canonical order in the student's XML
026   * file. */
027  private SortedMap<String, Grade> grades;
028
029  private List<String> late;   // Names of late Assignments
030  private List<String> resubmitted;  // Names of resubmitted Assignments
031  private String canvasId;
032  private Section enrolledSection;
033  private String gitHubUserName;
034
035  ///////////////////////  Constructors  ///////////////////////
036
037  /**
038   * Creates a new <code>Student</code> with a given id.  An example
039   * of an id is the student's UNIX login name.
040   */
041  public Student(String id) {
042    this.id = id;
043    this.grades = new TreeMap<>();
044    this.late = new ArrayList<>();
045    this.resubmitted = new ArrayList<>();
046  }
047
048  ///////////////////////  Instance Methods  ///////////////////////
049
050  /**
051   * Returns the id of this <code>Student</code>
052   */
053  public String getId() {
054    return this.id;
055  }
056
057  /**
058   * Returns the first name of this <code>Student</code>
059   */
060  public String getFirstName() {
061    return this.firstName;
062  }
063
064  /**
065   * Returns the last name of this <code>Student</code>
066   */
067  public Student setFirstName(String firstName) {
068    this.setDirty(true);
069    this.firstName = firstName;
070    return this;
071  }
072
073  /**
074   * Returns the last name of this <code>Student</code>
075   */
076  public String getLastName() {
077    return this.lastName;
078  }
079
080  /**
081   * Returns the last name of this <code>Student</code>
082   */
083  public Student setLastName(String lastName) {
084    this.setDirty(true);
085    this.lastName = lastName;
086    return this;
087  }
088
089  /**
090   * Returns the nick name of this <code>Student</code>
091   */
092  public String getNickName() {
093    return this.nickName;
094  }
095
096  /**
097   * Returns the nick name of this <code>Student</code>
098   */
099  public void setNickName(String nickName) {
100    this.setDirty(true);
101    this.nickName = nickName;
102  }
103
104  /**
105   * Returns this <code>Student</code>'s full name including first,
106   * last, and nick names.
107   */
108  public String getFullName() {
109    StringBuilder sb = new StringBuilder();
110    if (this.firstName != null) {
111      sb.append(this.firstName);
112    }
113
114    if (this.nickName != null) {
115      sb.append(" \"").append(this.nickName).append("\"");
116    }
117
118    if (this.lastName != null) {
119      sb.append(" ").append(this.lastName);
120    }
121
122    return sb.toString().trim();
123  }
124
125  /**
126   * Returns the email address of this <code>Student</code>
127   */
128  public String getEmail() {
129    return this.email;
130  }
131
132  /**
133   * Sets the email address of this <code>Student</code>
134   */
135  public Student setEmail(String email) {
136    this.setDirty(true);
137    this.email = email;
138    return this;
139  }
140
141  /**
142   * Returns the major of this <code>Student</code>
143   */
144  public String getMajor() {
145    return this.major;
146  }
147
148  /**
149   * Sets the major of this <code>Student</code>
150   */
151  public void setMajor(String major) {
152    this.setDirty(true);
153    this.major = major;
154  }
155
156  /**
157   * Returns the names of the assignments for which this
158   * <code>Student</code> has received a <code>Grade</code>.
159   */
160  public Set<String> getGradeNames() {
161    return this.grades.keySet();
162  }
163
164  /**
165   * Returns the <code>Grade</code> a student received on an
166   * assignment of a given name.  If the student has no grade for that
167   * assignment, <code>null</code> is returned.
168   */
169  public Grade getGrade(String assignmentName) {
170    return this.grades.get(assignmentName);
171  }
172
173  /**
174   * Sets a <code>Grade</code> a student received on an assignment of
175   * a given name.
176   */
177  public void setGrade(String assignmentName, Grade grade) {
178    this.setDirty(true);
179    this.grades.put(assignmentName, grade);
180  }
181
182  /**
183   * Returns the names of all of the assignments that are late.
184   */
185  public List<String> getLate() {
186    return this.late;
187  }
188
189  /**
190   * Makes note of the name of an assignment that is late
191   */
192  public void addLate(String assignmentName) {
193    this.setDirty(true);
194    this.late.add(assignmentName);
195  }
196
197  /**
198   * Returns the names of all of the assignments that are resubmitted.
199   */
200  public List<String> getResubmitted() {
201    return this.resubmitted;
202  }
203
204  /**
205   * Makes note of the name of an assignment that is resubmitted
206   */
207  public void addResubmitted(String assignmentName) {
208    this.setDirty(true);
209    this.resubmitted.add(assignmentName);
210  }
211  
212  /**
213   * Marks this <code>Student</code> as being clean
214   */
215  @Override
216  public void makeClean() {
217          super.makeClean();
218
219    // Make all Grades clean
220    this.grades.values().forEach(Grade::makeClean);
221  }
222
223  /**
224   * If any of its grades is dirty, then the student is dirty
225   */
226  @Override
227  public boolean isDirty() {
228    if (super.isDirty()) {
229      return true;
230      
231    } else {
232      for (Grade grade : this.grades.values()) {
233        if (grade.isDirty()) {
234          return true;
235        }
236      }
237      
238      return false;
239    }
240  }
241
242  /**
243   * Returns a complete textual description of this
244   * <code>Student</code>.
245   */
246  String getDescription() {
247    Student student = this;
248
249    StringBuilder sb = new StringBuilder();
250    sb.append(student.getId()).append(": ");
251    sb.append(student.getFullName());
252
253    String email = student.getEmail();
254    if (email != null && !email.equals("")) {
255      sb.append(", ").append(email);
256    }
257
258    String major = student.getMajor();
259    if (major != null && !major.equals("")) {
260      sb.append(", ").append(major);
261    }
262
263    for (Object note : this.getNotes()) {
264      sb.append(", \"").append(note).append("\"");
265    }
266
267    return sb.toString();
268  }
269
270  ///////////////////////  Utility Methods  ///////////////////////
271
272  /**
273   * Two <code>Student</code>s are equal if they have the same id
274   */
275  public boolean equals(Object o) {
276    return o instanceof Student && this.getId().equals(((Student) o).getId());
277  }
278
279  /**
280   * Two students that are equal must have the same hash code
281   */
282  public int hashCode() {
283    return this.getId().hashCode();
284  }
285
286  /**
287   * Returns a brief textual description of this <code>Student</code>
288   */
289  public String toString() {
290    return this.getId() + " (" + this.getFullName() + ")";
291  }
292
293  ///////////////////////  Main Program  ///////////////////////
294
295  private static PrintWriter err = new PrintWriter(System.err, true);
296
297  /**
298   * Prints usage information about the main program
299   */
300  private static void usage() {
301    err.println("\nusage: java Student -id id -file xmlFile [options]");
302    err.println("  where [options] are:");
303    err.println("  -firstName firstName    Student's first name");
304    err.println("  -lastName lastName      Student's last name");
305    err.println("  -nickName nickName      Student's nick name");
306    err.println("  -email email            Student's email address");
307    err.println("  -ssn SSN                Student's social security"
308                + " number");
309    err.println("  -major major            Student's major");
310    err.println("  -note note              A note about the student");
311    err.println("\n");
312    System.exit(1);
313  }
314
315  /**
316   * Main program that is used to add a <code>Student</code> to a
317   * grade book.
318   */
319  public static void main(String[] args) {
320    String id = null;
321    String xmlFile = null;
322    String firstName = null;
323    String lastName = null;
324    String nickName = null;
325    String email = null;
326    String ssn = null;
327    String major = null;
328    String note = null;
329
330    // Parse the command line
331    for (int i = 0; i < args.length; i++) {
332      if (args[i].equals("-id")) {
333        if (++i >= args.length) {
334          err.println("** Missing id");
335          usage();
336        }
337
338        id = args[i];
339
340      } else if (args[i].equals("-xmlFile") || args[i].equals("-file")) {
341        if (++i >= args.length) {
342          err.println("** Missing xml file name");
343          usage();
344        }
345
346        xmlFile = args[i];
347
348      } else if (args[i].equals("-firstName")) {
349        if (++i >= args.length) {
350          err.println("** Missing first name");
351          usage();
352        }
353
354        firstName = args[i];
355
356      } else if (args[i].equals("-lastName")) {
357        if (++i >= args.length) {
358          err.println("** Missing last name");
359          usage();
360        }
361
362        lastName = args[i];
363
364      } else if (args[i].equals("-nickName")) {
365        if (++i >= args.length) {
366          err.println("** Missing nick name");
367          usage();
368        }
369
370        nickName = args[i];
371
372      } else if (args[i].equals("-email")) {
373        if (++i >= args.length) {
374          err.println("** Missing email address");
375          usage();
376        }
377
378        email = args[i];
379
380      } else if (args[i].equals("-ssn")) {
381        if (++i >= args.length) {
382          err.println("** Missing social security number");
383          usage();
384        }
385
386        ssn = args[i];
387
388      } else if (args[i].equals("-major")) {
389        if (++i >= args.length) {
390          err.println("** Missing major");
391          usage();
392        }
393
394        major = args[i];
395
396      } else if (args[i].equals("-note")) {
397        if (++i >= args.length) {
398          err.println("** Missing text of note");
399          usage();
400        }
401
402        note = args[i];
403
404      } else if (args[i].startsWith("-")) {
405        err.println("** Unknown option: " + args[i]);
406        usage();
407
408      } else {
409        err.println("** Spurious command line: " + args[i]);
410        usage();
411      }
412    }
413
414    // Check to make sure that command line was entered correctly
415    if (id == null) {
416      err.println("** No id specified");
417      usage();
418    }
419
420    if (xmlFile == null) {
421      err.println("** No XML file specified");
422      usage();
423    }
424
425    // Parse the XML file to get a GradeBook
426    File file = new File(xmlFile);
427    if (!file.exists()) {
428      err.println("** Grade book file " + xmlFile + 
429                  " does not exist");
430      System.exit(1);
431    }
432
433    GradeBook book = null;
434    try {
435      XmlGradeBookParser parser = new XmlGradeBookParser(file);
436      book = parser.parse();
437
438    } catch (FileNotFoundException ex) {
439      err.println("** Could not find file: " + ex.getMessage());
440      System.exit(1);
441
442    } catch (IOException ex) {
443      err.println("** IOException during parsing: " + ex.getMessage());
444      System.exit(1);
445
446    } catch (ParserException ex) {
447      err.println("** Exception while parsing " + file + ": " + ex);
448      System.exit(1);
449    }
450
451    // Get the Student
452    Student student = book.getStudent(id).get();
453
454    if (firstName != null) {
455      student.setFirstName(firstName);
456    }
457
458    if (lastName != null) {
459      student.setLastName(lastName);
460    }
461
462    if (nickName != null) {
463      student.setNickName(nickName);
464    }
465
466    if (email != null) {
467      student.setEmail(email);
468    }
469
470    if (major != null) {
471      student.setMajor(major);
472    }
473
474    if (note != null) {
475      student.addNote(note);
476    }
477
478    // Write the changes back out to the XML file
479    try {
480      XmlDumper dumper = new XmlDumper(file);
481      dumper.dump(book);
482
483    } catch (IOException ex) {
484      err.println("** While dumping to " + file + ": " + ex);
485      System.exit(1);
486    }
487  }
488
489  public Grade getGrade(Assignment project) {
490    return this.getGrade(project.getName());
491  }
492
493  public Student setCanvasId(String canvasId) {
494    this.setDirty(true);
495    this.canvasId = canvasId;
496    return this;
497  }
498
499  public String getCanvasId() {
500    return canvasId;
501  }
502
503  public LetterGrade getLetterGrade() {
504    return letterGrade;
505  }
506
507  public void setLetterGrade(LetterGrade letterGrade) {
508    this.setDirty(true);
509    this.letterGrade = letterGrade;
510  }
511
512  public Student setGrade(Assignment assignment, Grade grade) {
513    setGrade(assignment.getName(), grade);
514    return this;
515  }
516
517  public void addLate(Assignment assignment) {
518    this.addLate(assignment.getName());
519  }
520
521  public Student setGrade(Assignment assignment, double score) {
522    this.setGrade(assignment, new Grade(assignment, score));
523    return this;
524  }
525
526  public Student setEnrolledSection(Section enrolledSection) {
527    this.enrolledSection = enrolledSection;
528    return this;
529  }
530
531  public Section getEnrolledSection() {
532    return enrolledSection;
533  }
534
535  public String getGitHubUserName() {
536    return gitHubUserName;
537  }
538
539  public void setGitHubUserName(String gitHubUserName) {
540    this.gitHubUserName = gitHubUserName;
541  }
542
543  public enum Section {
544    UNDERGRADUATE("undergraduate"), GRADUATE("graduate");
545
546    private final String stringValue;
547
548    Section(String stringValue) {
549      this.stringValue = stringValue;
550    }
551
552    public static Section fromString(String string) {
553      for (Section section : values()) {
554        if (section.asString().equals(string)) {
555          return section;
556        }
557      }
558
559      throw new IllegalArgumentException("Could not find LetterGrade for string \"" + string + "\"");
560    }
561
562    public String asString() {
563      return stringValue;
564    }
565
566    public String toString() {
567      return asString();
568    }
569
570  }
571}