001package edu.pdx.cs.joy.grader.canvas;
002
003import edu.pdx.cs.joy.grader.gradebook.Assignment;
004import edu.pdx.cs.joy.grader.gradebook.GradeBook;
005import edu.pdx.cs.joy.grader.gradebook.Student;
006
007import java.util.*;
008import java.util.stream.Collectors;
009
010public class GradesFromCanvas {
011  private final List<CanvasStudent> students = new ArrayList<>();
012
013  public static CanvasStudentBuilder newStudent() {
014    return new CanvasStudentBuilder();
015  }
016
017  public void addStudent(CanvasStudent student) {
018    this.students.add(student);
019  }
020
021  public Optional<Student> findStudentInGradebookForCanvasStudent(CanvasStudent canvasStudent, GradeBook book) {
022    return book.studentsStream().filter((Student student) -> {
023      if (haveSameCanvasId(canvasStudent, student)) {
024        return true;
025
026      } else if (studentIdIsSameAsLoginId(canvasStudent, student)) {
027        noteStudent(canvasStudent, student, book);
028        return true;
029
030      } else if (haveSameFirstAndLastNameIgnoringCase(canvasStudent, student)) {
031        noteStudent(canvasStudent, student, book);
032        return true;
033
034      } else if (haveSameNickAndLastNameIgnoringCase(canvasStudent, student)) {
035        noteStudent(canvasStudent, student, book);
036        return true;
037
038      } else {
039        return false;
040      }
041    }).findAny();
042  }
043
044  private void noteStudent(CanvasStudent canvasStudent, Student student, GradeBook book) {
045    student.setCanvasId(canvasStudent.getCanvasId());
046    book.setSectionName(student.getEnrolledSection(), canvasStudent.getSection());
047  }
048
049  private boolean studentIdIsSameAsLoginId(CanvasStudent d2lStudent, Student student) {
050    return d2lStudent.getLoginId().equals(student.getId());
051  }
052
053  private boolean haveSameFirstAndLastNameIgnoringCase(CanvasStudent d2lStudent, Student student) {
054    return d2lStudent.getFirstName().equalsIgnoreCase(student.getFirstName()) &&
055      d2lStudent.getLastName().equalsIgnoreCase(student.getLastName());
056  }
057
058  private boolean haveSameNickAndLastNameIgnoringCase(CanvasStudent d2lStudent, Student student) {
059    return d2lStudent.getFirstName().equalsIgnoreCase(student.getNickName()) &&
060      d2lStudent.getLastName().equalsIgnoreCase(student.getLastName());
061  }
062
063  private boolean haveSameCanvasId(CanvasStudent canvasStudent, Student student) {
064    return canvasStudent.getCanvasId().equals(student.getCanvasId());
065  }
066
067  public List<CanvasStudent> getStudents() {
068    return this.students;
069  }
070
071  public Optional<Assignment> findAssignmentInGradebookForCanvasQuiz(CanvasAssignment canvasAssignment, GradeBook gradebook) {
072    String canvasQuizName = canvasAssignment.getName();
073    Optional<Assignment> maybeAssignment;
074
075    Assignment assignment = gradebook.getAssignment(canvasQuizName);
076    if (assignment != null) {
077      maybeAssignment = noteMatchedAssignment(assignment, canvasAssignment);
078
079    } else {
080      maybeAssignment = findAssignmentInGradebookLike(canvasAssignment, gradebook);
081    }
082
083    return maybeAssignment;
084  }
085
086  private Optional<Assignment> noteMatchedAssignment(Assignment assignment, CanvasAssignment canvasAssignment) {
087    assignment.setCanvasId(canvasAssignment.getId());
088    return Optional.of(assignment);
089  }
090
091  private Optional<Assignment> findAssignmentInGradebookLike(CanvasAssignment canvasAssignment, GradeBook gradebook) {
092    List<Assignment> assignments = new ArrayList<>();
093    String canvasQuizName = canvasAssignment.getName();
094    for (String assignmentName : gradebook.getAssignmentNames()) {
095      Assignment assignment = gradebook.getAssignment(assignmentName);
096      if (canvasQuizName.startsWith(assignmentName)) {
097        assignments.add(assignment);
098
099      } else if (canvasQuizName.contains(assignment.getDescription())) {
100        assignments.add(assignment);
101      }
102    }
103
104    if (assignments.isEmpty()) {
105      return Optional.empty();
106
107    } else if (assignments.size() == 1) {
108      return noteMatchedAssignment(assignments.get(0), canvasAssignment);
109
110    } else {
111      String message = "Multiple assignments match \"" + canvasQuizName + "\": " +
112        assignments.stream().map(Assignment::getName).collect(Collectors.joining(", "));
113      throw new IllegalStateException(message);
114    }
115  }
116
117  static class CanvasStudent {
118    private final String firstName;
119    private final String lastName;
120    private final String loginId;
121    private final String canvasId;
122    private final String section;
123    private final Map<CanvasAssignment, Double> scores = new HashMap<>();
124
125    public CanvasStudent(String firstName, String lastName, String loginId, String canvasId, String section) {
126      this.firstName = firstName;
127      this.lastName = lastName;
128      this.loginId = loginId;
129      this.canvasId = canvasId;
130      this.section = section;
131    }
132
133    public String getLoginId() {
134      return loginId;
135    }
136
137    public String getFirstName() {
138      return firstName;
139    }
140
141    public String getLastName() {
142      return lastName;
143    }
144
145    public String getCanvasId() {
146      return this.canvasId;
147    }
148
149    public void setScore(CanvasAssignment assignment, Double score) {
150      this.scores.put(assignment, score);
151    }
152
153    public Double getScore(CanvasAssignment assignment) {
154      return this.scores.get(assignment);
155    }
156
157    public Iterable<CanvasAssignment> getAssignments() {
158      return this.scores.keySet();
159    }
160
161    public String getSection() {
162      return section;
163    }
164
165    @Override
166    public String toString() {
167      return "Canvas student \"" + getFirstName() + " " + getLastName() + "\" with id " + getLoginId();
168    }
169  }
170
171  static class CanvasStudentBuilder {
172    private String firstName;
173    private String lastName;
174    private String loginId;
175    private String canvasId;
176    private String section;
177
178    private CanvasStudentBuilder() {
179
180    }
181
182    public CanvasStudentBuilder setFirstName(String firstName) {
183      this.firstName = firstName;
184      return this;
185    }
186
187    public CanvasStudentBuilder setLastName(String lastName) {
188      this.lastName = lastName;
189      return this;
190    }
191
192    public CanvasStudentBuilder setLoginId(String loginId) {
193      this.loginId = loginId;
194      return this;
195    }
196
197    public CanvasStudentBuilder setCanvasId(String canvasId) {
198      this.canvasId = canvasId;
199      return this;
200    }
201
202    public CanvasStudentBuilder setSection(String section) {
203      this.section = section;
204      return this;
205    }
206
207    public CanvasStudent create() {
208      if (firstName == null) {
209        throw new IllegalStateException("Missing first name");
210      }
211
212      if (lastName == null) {
213        throw new IllegalStateException("Missing last name");
214      }
215
216      if (loginId == null) {
217        throw new IllegalStateException("Missing login Id");
218      }
219
220      if (canvasId == null) {
221        throw new IllegalStateException("Missing canvas Id");
222      }
223
224      if (section == null) {
225        throw new IllegalStateException("Missing section");
226      }
227
228      return new CanvasStudent(firstName, lastName, loginId, canvasId, section);
229    }
230  }
231
232  public static class CanvasAssignment {
233    private final String name;
234    private final int id;
235    private double pointsPossible;
236
237    public CanvasAssignment(String name, int id) {
238      this.name = name;
239      this.id = id;
240    }
241
242    public String getName() {
243      return name;
244    }
245
246    public int getId() {
247      return id;
248    }
249
250    public double getPointsPossible() {
251      return pointsPossible;
252    }
253
254    void setPossiblePoints(double possiblePoints) {
255      this.pointsPossible = possiblePoints;
256    }
257  }
258}