Skip to content

Commit

Permalink
[TEAMMATES#12048] Migrate enroll students action (TEAMMATES#12715)
Browse files Browse the repository at this point in the history
* Modify student entity

* Add update comment logic

* Modify logic files for cascading update and creation for student

* Add database queries for updating student

* Update EnrollStudentsAction

* Fix checkstyle

* Remove extra query for editor update

* Remove email update logic

* Update javadocs

* Copy over logic for Team and Section validation

* Edit javadocs

* Change StudentAttributes to Student instead

* Fix lint issues

* Fix lint issues

* Fix component tests and lint

* Remove ununsed method

* Fix lint

* Update validation logic to use Student

* Update test case

* Add tests for duplicate team across sections

* Remove unused methods and add getSection to UsersLogic

* Fix sorting logic

* Change getName method calls for section and team

* Remove unused methods

* Add more detail to JavaDocs

* Remove unusued methods

* Use getCourseId instead of toString

* Modify test case

* Revert changes

* Change toString to  getCourseId

* Update tests to include unregistered student

* Fix trailing whitespaces
  • Loading branch information
domoberzin authored Feb 4, 2024
1 parent c8723d5 commit 333f582
Show file tree
Hide file tree
Showing 13 changed files with 914 additions and 68 deletions.
152 changes: 152 additions & 0 deletions src/it/java/teammates/it/ui/webapi/EnrollStudentsActionIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package teammates.it.ui.webapi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import teammates.common.util.Const;
import teammates.common.util.HibernateUtil;
import teammates.storage.sqlentity.Course;
import teammates.storage.sqlentity.FeedbackResponse;
import teammates.storage.sqlentity.FeedbackResponseComment;
import teammates.storage.sqlentity.Instructor;
import teammates.storage.sqlentity.Section;
import teammates.storage.sqlentity.Student;
import teammates.storage.sqlentity.Team;
import teammates.ui.output.EnrollStudentsData;
import teammates.ui.request.StudentsEnrollRequest;
import teammates.ui.webapi.EnrollStudentsAction;
import teammates.ui.webapi.InvalidOperationException;
import teammates.ui.webapi.JsonResult;

/**
* SUT: {@link EnrollStudentsAction}.
*/

public class EnrollStudentsActionIT extends BaseActionIT<EnrollStudentsAction> {

@Override
@BeforeMethod
protected void setUp() throws Exception {
super.setUp();
persistDataBundle(typicalBundle);
HibernateUtil.flushSession();
}

@Override
protected String getActionUri() {
return Const.ResourceURIs.STUDENTS;
}

@Override
protected String getRequestMethod() {
return PUT;
}

private StudentsEnrollRequest prepareRequest(List<Student> students) {
List<StudentsEnrollRequest.StudentEnrollRequest> studentEnrollRequests = new ArrayList<>();
students.forEach(student -> {
studentEnrollRequests.add(new StudentsEnrollRequest.StudentEnrollRequest(student.getName(),
student.getEmail(), student.getTeam().getName(), student.getSection().getName(), student.getComments()));
});

return new StudentsEnrollRequest(studentEnrollRequests);
}

@Override
@Test
public void testExecute() throws Exception {
Instructor instructor = typicalBundle.instructors.get("instructor1OfCourse1");
String courseId = typicalBundle.students.get("student1InCourse1").getCourseId();
Course course = logic.getCourse(courseId);
Section section = logic.getSection(courseId, "Section 1");
Team team = logic.getTeamOrCreate(section, "Team 1");
Student newStudent = new Student(course, "Test Student", "[email protected]", "Test Comment", team);

loginAsInstructor(instructor.getGoogleId());

String[] params = new String[] {
Const.ParamsNames.COURSE_ID, courseId,
};

List<Student> students = new ArrayList<>(logic.getStudentsForCourse(courseId));
assertEquals(4, students.size());

______TS("Typical Success Case For Enrolling a Student");

StudentsEnrollRequest request = prepareRequest(Arrays.asList(newStudent));
EnrollStudentsAction enrollStudentsAction = getAction(request, params);
JsonResult res = getJsonResult(enrollStudentsAction);
EnrollStudentsData data = (EnrollStudentsData) res.getOutput();
assertEquals(1, data.getStudentsData().getStudents().size());
List<Student> studentsInCourse = logic.getStudentsForCourse(courseId);
assertEquals(5, studentsInCourse.size());

______TS("Fail to enroll due to duplicate team name across sections");

String expectedMessage = "Team \"%s\" is detected in both Section \"%s\" and Section \"%s\"."
+ " Please use different team names in different sections.";
Section newSection = logic.getSection(courseId, "Section 3");
Team newTeam = new Team(newSection, "Team 1");
newStudent = new Student(course, "Test Student", "[email protected]", "Test Comment", newTeam);
Student secondStudent = new Student(course, "Test Student 2", "[email protected]", "Test Comment",
team);
StudentsEnrollRequest req = prepareRequest(Arrays.asList(secondStudent, newStudent));
InvalidOperationException exception = verifyInvalidOperation(req, params);
assertEquals(String.format(expectedMessage, "Team 1", "Section 3", "Section 1"), exception.getMessage());

______TS("Typical Success Case For Changing Details (except email) of a Student");

Section section3 = logic.getSection(courseId, "Section 3");
Team team3 = logic.getTeamOrCreate(section3, "Team 3");

Student changedTeam = new Student(course, "Student 1", "[email protected]", "Test Comment", team3);

request = prepareRequest(Arrays.asList(changedTeam));
enrollStudentsAction = getAction(request, params);
res = getJsonResult(enrollStudentsAction);
data = (EnrollStudentsData) res.getOutput();
assertEquals(1, data.getStudentsData().getStudents().size());
studentsInCourse = logic.getStudentsForCourse(courseId);
assertEquals(5, studentsInCourse.size());

// Verify that changes have cascaded to feedback responses
String giverEmail = "[email protected]";

List<FeedbackResponse> responsesFromUser =
logic.getFeedbackResponsesFromGiverForCourse(courseId, giverEmail);

for (FeedbackResponse response : responsesFromUser) {
assertEquals(logic.getSection(courseId, "Section 3"), response.getGiverSection());
}

List<FeedbackResponse> responsesToUser =
logic.getFeedbackResponsesForRecipientForCourse(courseId, giverEmail);

for (FeedbackResponse response : responsesToUser) {
assertEquals(logic.getSection(courseId, "Section 3"), response.getRecipientSection());
List<FeedbackResponseComment> commentsFromUser = logic.getFeedbackResponseCommentsForResponse(response.getId());
for (FeedbackResponseComment comment : commentsFromUser) {
if (comment.getGiver().equals(giverEmail)) {
assertEquals(logic.getSection(courseId, "Section 3"), comment.getGiverSection());
}
}
}
}

@Test
@Override
protected void testAccessControl() throws Exception {
Course course = typicalBundle.courses.get("course1");

String[] params = new String[] {
Const.ParamsNames.COURSE_ID, course.getId(),
};

verifyOnlyInstructorsOfTheSameCourseWithCorrectCoursePrivilegeCanAccess(
course, Const.InstructorPermissions.CAN_MODIFY_STUDENT, params);
}
}
41 changes: 39 additions & 2 deletions src/it/resources/data/typicalDataBundle.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@
"id": "course-2"
},
"name": "Section 2"
},
"section2InCourse1": {
"id": "00000000-0000-4000-8000-000000000203",
"course": {
"id": "course-1"
},
"name": "Section 3"
}
},
"teams": {
Expand All @@ -133,9 +140,16 @@
"team1InCourse2": {
"id": "00000000-0000-4000-8000-000000000302",
"section": {
"id": "00000000-0000-4000-8000-000000000202"
"id": "00000000-0000-4000-8000-000000000201"
},
"name": "Team 1"
"name": "Team 2"
},
"team2InCourse2": {
"id": "00000000-0000-4000-8000-000000000303",
"section": {
"id": "00000000-0000-4000-8000-000000000203"
},
"name": "Team 3"
}
},
"deadlineExtensions": {
Expand Down Expand Up @@ -968,6 +982,29 @@
"showGiverNameTo": [],
"lastEditorEmail": "[email protected]"
},
"comment2ToResponse1ForQ1": {
"feedbackResponse": {
"id": "00000000-0000-4000-8000-000000000901",
"answer": {
"questionType": "TEXT",
"answer": "Student 1 self feedback."
}
},
"giver": "[email protected]",
"giverType": "STUDENTS",
"giverSection": {
"id": "00000000-0000-4000-8000-000000000201"
},
"recipientSection": {
"id": "00000000-0000-4000-8000-000000000201"
},
"commentText": "Student 1 comment to student 1 self feedback",
"isVisibilityFollowingFeedbackQuestion": false,
"isCommentFromFeedbackParticipant": false,
"showCommentTo": [],
"showGiverNameTo": [],
"lastEditorEmail": "[email protected]"
},
"comment2ToResponse2ForQ1": {
"feedbackResponse": {
"id": "00000000-0000-4000-8000-000000000902",
Expand Down
90 changes: 89 additions & 1 deletion src/main/java/teammates/sqllogic/api/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import teammates.common.datatransfer.NotificationStyle;
import teammates.common.datatransfer.NotificationTargetUser;
import teammates.common.datatransfer.SqlDataBundle;
import teammates.common.exception.EnrollException;
import teammates.common.exception.EntityAlreadyExistsException;
import teammates.common.exception.EntityDoesNotExistException;
import teammates.common.exception.InstructorUpdateException;
Expand Down Expand Up @@ -42,6 +43,7 @@
import teammates.storage.sqlentity.Notification;
import teammates.storage.sqlentity.Section;
import teammates.storage.sqlentity.Student;
import teammates.storage.sqlentity.Team;
import teammates.storage.sqlentity.UsageStatistics;
import teammates.storage.sqlentity.User;
import teammates.ui.request.FeedbackQuestionUpdateRequest;
Expand Down Expand Up @@ -211,6 +213,13 @@ public Course getCourse(String courseId) {
return coursesLogic.getCourse(courseId);
}

/**
* Gets a section from a course by section name.
*/
public Section getSection(String courseId, String section) {
return usersLogic.getSection(courseId, section);
}

/**
* Gets courses associated with student.
* Preconditions: <br>
Expand Down Expand Up @@ -267,6 +276,33 @@ public void deleteCourseCascade(String courseId) {
coursesLogic.deleteCourseCascade(courseId);
}

/**
* Updates a student by {@link Student}.
*
* <p>If email changed, update by recreating the student and cascade update all responses
* the student gives/receives as well as any deadline extensions given to the student.
*
* <p>If team changed, cascade delete all responses the student gives/receives within that team.
*
* <p>If section changed, cascade update all responses the student gives/receives.
*
* <br/>Preconditions: <br/>
* * All parameters are non-null.
*
* @return updated student
* @throws InvalidParametersException if attributes to update are not valid
* @throws EntityDoesNotExistException if the student cannot be found
* @throws EntityAlreadyExistsException if the student cannot be updated
* by recreation because of an existent student
*/
public Student updateStudentCascade(Student student)
throws InvalidParametersException, EntityDoesNotExistException, EntityAlreadyExistsException {

assert student != null;

return usersLogic.updateStudentCascade(student);
}

/**
* Moves a course to Recycle Bin by its given corresponding ID.
* @return the deletion timestamp assigned to the course.
Expand Down Expand Up @@ -821,6 +857,20 @@ public List<Student> getStudentsByTeamName(String teamName, String courseId) {
return usersLogic.getStudentsForTeam(teamName, courseId);
}

/**
* Gets a team by associated {@code courseId} and {@code sectionName}.
*/
public Section getSectionOrCreate(String courseId, String sectionName) {
return usersLogic.getSectionOrCreate(courseId, sectionName);
}

/**
* Gets a team by associated {@code section} and {@code teamName}.
*/
public Team getTeamOrCreate(Section section, String teamName) {
return usersLogic.getTeamOrCreate(section, teamName);
}

/**
* Creates a student.
*
Expand Down Expand Up @@ -1175,7 +1225,45 @@ public void deleteFeedbackResponseComment(Long frcId) {
}

/**
* Updates a feedback question by {@code FeedbackQuestionAttributes.UpdateOptions}.
* Gets all feedback responses from a giver for a question.
*/
public List<FeedbackResponse> getFeedbackResponsesFromGiverForCourse(String courseId, String giverEmail) {
return feedbackResponsesLogic.getFeedbackResponsesFromGiverForCourse(courseId, giverEmail);
}

/**
* Gets all feedback responses for a recipient for a course.
*/
public List<FeedbackResponse> getFeedbackResponsesForRecipientForCourse(String courseId, String recipientEmail) {
return feedbackResponsesLogic.getFeedbackResponsesForRecipientForCourse(courseId, recipientEmail);
}

/**
* Gets all feedback response comments for a feedback response.
*/
public List<FeedbackResponseComment> getFeedbackResponseCommentsForResponse(UUID feedbackResponse) {
return feedbackResponseCommentsLogic.getFeedbackResponseCommentsForResponse(feedbackResponse);
}

/**
* Validates sections for any limit violations and teams for any team name violations.
*
* <p>Preconditions: <br>
* * All parameters are non-null.
*
* @see StudentsLogic#validateSectionsAndTeams(List, String)
*/
public void validateSectionsAndTeams(
List<Student> studentList, String courseId) throws EnrollException {

assert studentList != null;
assert courseId != null;

usersLogic.validateSectionsAndTeams(studentList, courseId);
}

/**
* Updates a feedback question by {@code FeedbackQuestionUpdateRequest}.
*
* <p>Cascade adjust the question number of questions in the same session.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,5 +117,4 @@ public void deleteDeadlineExtensionsForUser(User user) {
}
});
}

}
Loading

0 comments on commit 333f582

Please sign in to comment.