Skip to content

Commit

Permalink
[#11571] Cascade user updates/deletes to deadline maps (#11688)
Browse files Browse the repository at this point in the history
* Add methods for updating and deleting deadline maps for a user

* Add tests for updating and deleting deadline maps for a user

* Cascade instructor updates or deletes to deadline maps

* Test cascade of instructor updates or deletes to deadline maps

* Cascade student updates or deletes to deadline maps

* Test cascade of student updates or deletes to deadline maps

* Match deadline extension entities with deadline maps

* Change deadlinesUpdater into a Consumer

* Change unexpected error assertion into a severe log

* Improve readability of feedback session deadline map update code

* Clean code

* Clean code further
  • Loading branch information
jayasting98 authored and moziliar committed Apr 4, 2022
1 parent ba0980f commit 87d60c5
Show file tree
Hide file tree
Showing 8 changed files with 475 additions and 6 deletions.
76 changes: 76 additions & 0 deletions src/main/java/teammates/logic/core/FeedbackSessionsLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import teammates.common.datatransfer.AttributesDeletionQuery;
Expand Down Expand Up @@ -311,6 +313,36 @@ public FeedbackSessionAttributes updateFeedbackSession(FeedbackSessionAttributes
return fsDb.updateFeedbackSession(newUpdateOptions.build());
}

/**
* Updates the instructor email address for all their deadlines in the feedback sessions of the given course.
*/
public void updateFeedbackSessionsInstructorDeadlinesWithNewEmail(String courseId, String oldEmailAddress,
String newEmailAddress) {
updateFeedbackSessionsDeadlinesWithNewEmail(courseId, oldEmailAddress, newEmailAddress, true);
}

/**
* Updates the student email address for all their deadlines in the feedback sessions of the given course.
*/
public void updateFeedbackSessionsStudentDeadlinesWithNewEmail(String courseId, String oldEmailAddress,
String newEmailAddress) {
updateFeedbackSessionsDeadlinesWithNewEmail(courseId, oldEmailAddress, newEmailAddress, false);
}

/**
* Deletes the instructor email address for all their deadlines in the feedback sessions of the given course.
*/
public void deleteFeedbackSessionsDeadlinesForInstructor(String courseId, String emailAddress) {
deleteFeedbackSessionsDeadlinesForUser(courseId, emailAddress, true);
}

/**
* Deletes the student email address for all their deadlines in the feedback sessions of the given course.
*/
public void deleteFeedbackSessionsDeadlinesForStudent(String courseId, String emailAddress) {
deleteFeedbackSessionsDeadlinesForUser(courseId, emailAddress, false);
}

/**
* Updates all feedback sessions of {@code courseId} to have be in {@code courseTimeZone}.
*/
Expand Down Expand Up @@ -575,4 +607,48 @@ public boolean isFeedbackSessionForUserTypeToAnswer(FeedbackSessionAttributes se
: fqLogic.hasFeedbackQuestionsForStudents(session);
}

private void updateFeedbackSessionsDeadlinesWithNewEmail(String courseId, String oldEmailAddress,
String newEmailAddress, boolean isInstructor) {
if (oldEmailAddress.equals(newEmailAddress)) {
return;
}
updateFeedbackSessionsDeadlinesForUser(courseId, oldEmailAddress, isInstructor,
deadlines -> deadlines.put(newEmailAddress, deadlines.remove(oldEmailAddress)));
}

private void deleteFeedbackSessionsDeadlinesForUser(String courseId, String emailAddress, boolean isInstructor) {
updateFeedbackSessionsDeadlinesForUser(courseId, emailAddress, isInstructor,
deadlines -> deadlines.remove(emailAddress));
}

private void updateFeedbackSessionsDeadlinesForUser(String courseId, String emailAddress, boolean isInstructor,
Consumer<Map<String, Instant>> deadlinesUpdater) {
List<FeedbackSessionAttributes> feedbackSessions = fsDb.getFeedbackSessionsForCourse(courseId);
feedbackSessions.forEach(feedbackSession -> {
FeedbackSessionAttributes.UpdateOptions.Builder updateOptionsBuilder = FeedbackSessionAttributes
.updateOptionsBuilder(feedbackSession.getFeedbackSessionName(), courseId);
if (isInstructor) {
Map<String, Instant> instructorDeadlines = feedbackSession.getInstructorDeadlines();
if (!instructorDeadlines.containsKey(emailAddress)) {
return;
}
deadlinesUpdater.accept(instructorDeadlines);
updateOptionsBuilder.withInstructorDeadlines(instructorDeadlines);
} else {
Map<String, Instant> studentDeadlines = feedbackSession.getStudentDeadlines();
if (!studentDeadlines.containsKey(emailAddress)) {
return;
}
deadlinesUpdater.accept(studentDeadlines);
updateOptionsBuilder.withStudentDeadlines(studentDeadlines);
}
try {
fsDb.updateFeedbackSession(updateOptionsBuilder.build());
} catch (InvalidParametersException | EntityDoesNotExistException e) {
// Both Exceptions should not be thrown.
log.severe("Unexpected error", e);
}
});
}

}
5 changes: 5 additions & 0 deletions src/main/java/teammates/logic/core/InstructorsLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public final class InstructorsLogic {
private FeedbackResponsesLogic frLogic;
private FeedbackResponseCommentsLogic frcLogic;
private FeedbackQuestionsLogic fqLogic;
private FeedbackSessionsLogic fsLogic;
private DeadlineExtensionsLogic deLogic;

private InstructorsLogic() {
Expand All @@ -51,6 +52,7 @@ void initLogicDependencies() {
fqLogic = FeedbackQuestionsLogic.inst();
frLogic = FeedbackResponsesLogic.inst();
frcLogic = FeedbackResponseCommentsLogic.inst();
fsLogic = FeedbackSessionsLogic.inst();
deLogic = DeadlineExtensionsLogic.inst();
}

Expand Down Expand Up @@ -264,6 +266,8 @@ public InstructorAttributes updateInstructorByGoogleIdCascade(
frcLogic.updateFeedbackResponseCommentsEmails(
updatedInstructor.getCourseId(), originalInstructor.getEmail(), updatedInstructor.getEmail());
// cascade deadline extensions
fsLogic.updateFeedbackSessionsInstructorDeadlinesWithNewEmail(updatedInstructor.getCourseId(),
originalInstructor.getEmail(), updatedInstructor.getEmail());
deLogic.updateDeadlineExtensionsWithNewEmail(updatedInstructor.getCourseId(),
originalInstructor.getEmail(), updatedInstructor.getEmail(), true);
}
Expand Down Expand Up @@ -319,6 +323,7 @@ public void deleteInstructorCascade(String courseId, String email) {

frLogic.deleteFeedbackResponsesInvolvedEntityOfCourseCascade(courseId, email);
instructorsDb.deleteInstructor(courseId, email);
fsLogic.deleteFeedbackSessionsDeadlinesForInstructor(courseId, email);
deLogic.deleteDeadlineExtensions(courseId, email, true);
}

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/teammates/logic/core/StudentsLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public final class StudentsLogic {
private final StudentsDb studentsDb = StudentsDb.inst();

private FeedbackResponsesLogic frLogic;
private FeedbackSessionsLogic fsLogic;
private DeadlineExtensionsLogic deLogic;

private StudentsLogic() {
Expand All @@ -52,6 +53,7 @@ public static StudentsLogic inst() {

void initLogicDependencies() {
frLogic = FeedbackResponsesLogic.inst();
fsLogic = FeedbackSessionsLogic.inst();
deLogic = DeadlineExtensionsLogic.inst();
}

Expand Down Expand Up @@ -240,6 +242,8 @@ public StudentAttributes updateStudentCascade(StudentAttributes.UpdateOptions up
if (!originalStudent.getEmail().equals(updatedStudent.getEmail())) {
frLogic.updateFeedbackResponsesForChangingEmail(
updatedStudent.getCourse(), originalStudent.getEmail(), updatedStudent.getEmail());
fsLogic.updateFeedbackSessionsStudentDeadlinesWithNewEmail(originalStudent.getCourse(),
originalStudent.getEmail(), updatedStudent.getEmail());
deLogic.updateDeadlineExtensionsWithNewEmail(
originalStudent.getCourse(), originalStudent.getEmail(), updatedStudent.getEmail(), false);
}
Expand Down Expand Up @@ -441,6 +445,7 @@ public void deleteStudentCascade(String courseId, String studentEmail) {
frLogic.deleteFeedbackResponsesInvolvedEntityOfCourseCascade(student.getCourse(), student.getTeam());
}
studentsDb.deleteStudent(courseId, studentEmail);
fsLogic.deleteFeedbackSessionsDeadlinesForStudent(courseId, studentEmail);
deLogic.deleteDeadlineExtensions(courseId, studentEmail, false);
}

Expand Down
160 changes: 160 additions & 0 deletions src/test/java/teammates/logic/core/FeedbackSessionsLogicTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
Expand Down Expand Up @@ -724,6 +728,162 @@ public void testUpdateFeedbackSession_shouldAdjustEmailSendingStatusAccordingly(
typicalSession.getFeedbackSessionName(), typicalSession.getCourseId()).isSentPublishedEmail());
}

@Test
public void testUpdateFeedbackSessionsInstructorDeadlinesWithNewEmail() {
InstructorAttributes instructorToBeUpdated = dataBundle.instructors.get("instructor1OfCourse1");
String courseId = instructorToBeUpdated.getCourseId();
String oldEmailAddress = instructorToBeUpdated.getEmail();
String newEmailAddress = "[email protected]";

______TS("Update email; transfers deadlines to new email.");

Map<Instant, Integer> oldDeadlineCounts = fsLogic.getFeedbackSessionsForCourse(courseId)
.stream()
.map(FeedbackSessionAttributes::getInstructorDeadlines)
.filter(instructorDeadlines -> instructorDeadlines.containsKey(oldEmailAddress))
.map(instructorDeadlines -> instructorDeadlines.get(oldEmailAddress))
.collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(deadline -> 1)));
assertEquals(2, oldDeadlineCounts.values()
.stream()
.reduce(0, Integer::sum)
.intValue());

fsLogic.updateFeedbackSessionsInstructorDeadlinesWithNewEmail(courseId, oldEmailAddress, newEmailAddress);

assertTrue(fsLogic.getFeedbackSessionsForCourse(courseId)
.stream()
.noneMatch(feedbackSessionAttributes -> feedbackSessionAttributes.getInstructorDeadlines()
.containsKey(oldEmailAddress)));
Map<Instant, Integer> newDeadlineCounts = fsLogic.getFeedbackSessionsForCourse(courseId)
.stream()
.map(FeedbackSessionAttributes::getInstructorDeadlines)
.filter(instructorDeadlines -> instructorDeadlines.containsKey(newEmailAddress))
.map(instructorDeadlines -> instructorDeadlines.get(newEmailAddress))
.collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(deadline -> 1)));
assertEquals(oldDeadlineCounts, newDeadlineCounts);
}

@Test
public void testUpdateFeedbackSessionsStudentDeadlinesWithNewEmail() {
StudentAttributes student4InCourse1 = dataBundle.students.get("student4InCourse1");
String courseId = student4InCourse1.getCourse();
String oldEmailAddress = student4InCourse1.getEmail();
String newEmailAddress = "[email protected]";

______TS("Update email; transfers deadlines to new email.");

Map<Instant, Integer> oldDeadlineCounts = fsLogic.getFeedbackSessionsForCourse(courseId)
.stream()
.map(FeedbackSessionAttributes::getStudentDeadlines)
.filter(studentDeadlines -> studentDeadlines.containsKey(oldEmailAddress))
.map(studentDeadlines -> studentDeadlines.get(oldEmailAddress))
.collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(deadline -> 1)));
assertEquals(2, oldDeadlineCounts.values()
.stream()
.reduce(0, Integer::sum)
.intValue());

fsLogic.updateFeedbackSessionsStudentDeadlinesWithNewEmail(courseId, oldEmailAddress, newEmailAddress);

assertTrue(fsLogic.getFeedbackSessionsForCourse(courseId)
.stream()
.noneMatch(feedbackSessionAttributes -> feedbackSessionAttributes.getStudentDeadlines()
.containsKey(oldEmailAddress)));
Map<Instant, Integer> newDeadlineCounts = fsLogic.getFeedbackSessionsForCourse(courseId)
.stream()
.map(FeedbackSessionAttributes::getStudentDeadlines)
.filter(studentDeadlines -> studentDeadlines.containsKey(newEmailAddress))
.map(studentDeadlines -> studentDeadlines.get(newEmailAddress))
.collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(deadline -> 1)));
assertEquals(oldDeadlineCounts, newDeadlineCounts);
}

@Test
public void testDeleteFeedbackSessionsDeadlinesForInstructor() {
InstructorAttributes instructor1OfCourse1 = dataBundle.instructors.get("instructor1OfCourse1");
verifyPresentInDatabase(instructor1OfCourse1);

String courseId = instructor1OfCourse1.getCourseId();
String emailAddress = instructor1OfCourse1.getEmail();

______TS("Delete user; deadlines associated with the email are removed.");

// The instructor should have selective deadlines.
Set<FeedbackSessionAttributes> oldSessionsWithInstructor1Deadlines = fsLogic
.getFeedbackSessionsForCourse(courseId)
.stream()
.filter(feedbackSessionAttributes -> feedbackSessionAttributes.getInstructorDeadlines()
.containsKey(emailAddress))
.collect(Collectors.toSet());
Map<FeedbackSessionAttributes, Integer> oldSessionsDeadlineCounts = oldSessionsWithInstructor1Deadlines
.stream()
.collect(Collectors.toMap(fsa -> fsa, fsa -> fsa.getInstructorDeadlines().size()));
assertEquals(2, oldSessionsWithInstructor1Deadlines.size());

fsLogic.deleteFeedbackSessionsDeadlinesForInstructor(courseId, emailAddress);

// The instructor should have no more selective deadlines.
Set<FeedbackSessionAttributes> newSessionsWithInstructor1Deadlines = fsLogic
.getFeedbackSessionsForCourse(courseId)
.stream()
.filter(feedbackSessionAttributes -> feedbackSessionAttributes.getInstructorDeadlines()
.containsKey(emailAddress))
.collect(Collectors.toSet());
assertTrue(newSessionsWithInstructor1Deadlines.isEmpty());
Map<FeedbackSessionAttributes, Integer> expectedSessionsDeadlineCounts = oldSessionsDeadlineCounts.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() - 1));
Map<FeedbackSessionAttributes, Integer> newSessionsDeadlineCounts = fsLogic
.getFeedbackSessionsForCourse(courseId)
.stream()
.filter(oldSessionsWithInstructor1Deadlines::contains)
.collect(Collectors.toMap(fsa -> fsa, fsa -> fsa.getInstructorDeadlines().size()));
assertEquals(expectedSessionsDeadlineCounts, newSessionsDeadlineCounts);
}

@Test
public void testDeleteFeedbackSessionsDeadlinesForStudent() {
StudentAttributes student4InCourse1 = dataBundle.students.get("student4InCourse1");
verifyPresentInDatabase(student4InCourse1);

String courseId = student4InCourse1.getCourse();
String emailAddress = student4InCourse1.getEmail();

______TS("Delete user; deadlines associated with the email are removed.");

// The student should have selective deadlines.
Set<FeedbackSessionAttributes> oldSessionsWithStudent4Deadlines = fsLogic
.getFeedbackSessionsForCourse(courseId)
.stream()
.filter(feedbackSessionAttributes -> feedbackSessionAttributes.getStudentDeadlines()
.containsKey(emailAddress))
.collect(Collectors.toSet());
Map<FeedbackSessionAttributes, Integer> oldSessionsDeadlineCounts = oldSessionsWithStudent4Deadlines
.stream()
.collect(Collectors.toMap(fsa -> fsa, fsa -> fsa.getStudentDeadlines().size()));
assertEquals(2, oldSessionsWithStudent4Deadlines.size());

fsLogic.deleteFeedbackSessionsDeadlinesForStudent(courseId, emailAddress);

// The student should have no more selective deadlines.
Set<FeedbackSessionAttributes> newSessionsWithStudent4Deadlines = fsLogic
.getFeedbackSessionsForCourse(courseId)
.stream()
.filter(feedbackSessionAttributes -> feedbackSessionAttributes.getStudentDeadlines()
.containsKey(emailAddress))
.collect(Collectors.toSet());
assertTrue(newSessionsWithStudent4Deadlines.isEmpty());
Map<FeedbackSessionAttributes, Integer> expectedSessionsDeadlineCounts = oldSessionsDeadlineCounts.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() - 1));
Map<FeedbackSessionAttributes, Integer> newSessionsDeadlineCounts = fsLogic
.getFeedbackSessionsForCourse(courseId)
.stream()
.filter(oldSessionsWithStudent4Deadlines::contains)
.collect(Collectors.toMap(fsa -> fsa, fsa -> fsa.getStudentDeadlines().size()));
assertEquals(expectedSessionsDeadlineCounts, newSessionsDeadlineCounts);
}

private void testPublishUnpublishFeedbackSession() throws Exception {

______TS("success: publish");
Expand Down
Loading

0 comments on commit 87d60c5

Please sign in to comment.