Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f65b15a
Add E2E Sql test class
WeeJean Jan 26, 2026
e6e97a3
Add Sql json
WeeJean Jan 26, 2026
4f113c4
Merge branch 'master' into migrate-InstructorStudentActivityLogsPage
WeeJean Jan 28, 2026
e470492
Fix InstructorStudentActivityLogsPage E2ETest timing and logic issues
WeeJean Feb 1, 2026
76f8ab7
Fix typo in activity logs Page Object
WeeJean Feb 1, 2026
456ba1b
Fix Lint
WeeJean Feb 1, 2026
f9d0467
Refactor e2etest access to selenium API into page object
WeeJean Feb 1, 2026
02979ff
Refactor e2etest access to selenium API into page object V2
WeeJean Feb 1, 2026
7711a92
Merge branch 'master' into migrate-InstructorStudentActivityLogsPage
DhiraPT Feb 2, 2026
03c9073
Merge branch 'master' into migrate-InstructorStudentActivityLogsPage
mingyuancode Feb 2, 2026
31e7764
Merge branch 'master' into migrate-InstructorStudentActivityLogsPage
mingyuancode Feb 2, 2026
5e69c3b
Remove thread usage from InstructorStudentActivityLogsPageE2ETest
WeeJean Feb 4, 2026
bbae65e
Merge branch 'migrate-InstructorStudentActivityLogsPage' of github.co…
WeeJean Feb 4, 2026
fc8d9ce
Merge branch 'master' into migrate-InstructorStudentActivityLogsPage
DhiraPT Feb 5, 2026
ed3f597
Update CreateFeedbackSessionLogAction to create sql feedbacksessionlog
WeeJean Feb 6, 2026
d9659a1
Fix date/timezone issue for InstructorStudentActivityLogsPageE2ETest
WeeJean Feb 6, 2026
ded23f0
Merge branch 'master' into migrate-InstructorStudentActivityLogsPage
DhiraPT Feb 6, 2026
dc9960e
Remove redundant comment
WeeJean Feb 6, 2026
0749359
Fix naming issue
WeeJean Feb 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package teammates.e2e.cases.sql;

import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;

import org.testng.annotations.Test;

import teammates.common.datatransfer.questions.FeedbackTextResponseDetails;
import teammates.common.util.AppUrl;
import teammates.common.util.Const;
import teammates.e2e.pageobjects.FeedbackSubmitPageSql;
import teammates.e2e.pageobjects.InstructorStudentActivityLogsPage;
import teammates.storage.sqlentity.Course;
import teammates.storage.sqlentity.FeedbackQuestion;
import teammates.storage.sqlentity.FeedbackResponse;
import teammates.storage.sqlentity.FeedbackSession;
import teammates.storage.sqlentity.Instructor;
import teammates.storage.sqlentity.Student;

/**
* SUT: {@link Const.WebPageURIs#INSTRUCTOR_STUDENT_ACTIVITY_LOGS_PAGE}.
*/
public class InstructorStudentActivityLogsPageE2ETest extends BaseE2ETestCase {
private Instructor instructor;
private Course course;
private FeedbackSession feedbackSession;
private FeedbackQuestion feedbackQuestion;
private Student student;

@Override
protected void prepareTestData() {
testData = removeAndRestoreDataBundle(
loadSqlDataBundle("/InstructorStudentActivityLogsPageE2ETestSql.json"));

instructor = testData.instructors.get("instructor");
course = testData.courses.get("course");
student = testData.students.get("alice.tmms@ISActLogs.CS2104");
feedbackQuestion = testData.feedbackQuestions.get("qn1");
feedbackSession = testData.feedbackSessions.get("openSession");
}

@Test
@Override
public void testAll() {
AppUrl url = createFrontendUrl(Const.WebPageURIs.INSTRUCTOR_STUDENT_ACTIVITY_LOGS_PAGE)
.withCourseId("tm.e2e.ISActLogs.CS2104");
InstructorStudentActivityLogsPage studentActivityLogsPage =
loginToPage(url, InstructorStudentActivityLogsPage.class, instructor.getGoogleId());

______TS("verify default datetime");
String currentLogsFromDate = studentActivityLogsPage.getLogsFromDate();
String currentLogsToDate = studentActivityLogsPage.getLogsToDate();
String currentLogsFromTime = studentActivityLogsPage.getLogsFromTime();
String currentLogsToTime = studentActivityLogsPage.getLogsToTime();

studentActivityLogsPage.setLogsFromDateTime(
Instant.now().minus(1, ChronoUnit.DAYS),
ZoneId.systemDefault().getId());
studentActivityLogsPage.setLogsToDateTime(Instant.now(), ZoneId.systemDefault().getId());

assertEquals(currentLogsFromDate, studentActivityLogsPage.getLogsFromDate());
assertEquals(currentLogsToDate, studentActivityLogsPage.getLogsToDate());
assertEquals(currentLogsFromTime, "23:59H");
assertEquals(currentLogsToTime, "23:59H");

______TS("verify logs output");
logout();
AppUrl studentSubmissionPageUrl = createFrontendUrl(Const.WebPageURIs.STUDENT_SESSION_SUBMISSION_PAGE)
.withCourseId(course.getId())
.withSessionName(feedbackSession.getName());
FeedbackSubmitPageSql studentSubmissionPage = loginToPage(studentSubmissionPageUrl,
FeedbackSubmitPageSql.class, student.getGoogleId());

Student receiver = testData.students.get("benny.tmms@ISActLogs.CS2104");

FeedbackTextResponseDetails details = new FeedbackTextResponseDetails("Response");
FeedbackResponse response = FeedbackResponse.makeResponse(
feedbackQuestion, student.getEmail(), student.getSection(),
receiver.getEmail(), receiver.getSection(), details);

studentSubmissionPage.fillTextResponse(1, receiver.getName(), response);
studentSubmissionPage.clickSubmitQuestionButton(1);

logout();
studentActivityLogsPage = loginToPage(url, InstructorStudentActivityLogsPage.class,
instructor.getGoogleId());
studentActivityLogsPage.setActivityType("session access and submission");
studentActivityLogsPage.setSessionDropdown(feedbackSession.getName());
studentActivityLogsPage.waitForPageToLoad();
studentActivityLogsPage.startSearching();

assertTrue(studentActivityLogsPage.isLogPresentForSession(feedbackQuestion.getFeedbackSessionName()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void setStudentName(String studentName) {
}

public void setLogsFromDateTime(Instant instant, String timeZone) {
setDateTime(logsFromDatepicker, logsToTimepicker, instant, timeZone);
setDateTime(logsFromDatepicker, logsFromTimepicker, instant, timeZone);
}

public void setLogsToDateTime(Instant instant, String timeZone) {
Expand All @@ -131,4 +131,12 @@ private void setDateTime(WebElement dateBox, WebElement timeBox, Instant startIn

selectDropdownOptionByText(timeBox.findElement(By.tagName("select")), getTimeString(startInstant, timeZone));
}

public String getLogsOutputText() {
return logsOutput.getText();
}

public void waitForLogsToLoad() {
waitForElementPresence(By.id("logs-output"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
{
"accounts": {
"instructorWithSessions": {
"id": "00000000-0000-4000-8000-000000000001",
"googleId": "tm.e2e.ISActLogs.instructor",
"name": "Teammates Test",
"email": "tmms.test@gmail.tmt"
},
"alice.tmms@ISActLogs.CS2104": {
"id": "00000000-0000-4000-8000-000000000002",
"googleId": "tm.e2e.ISActLogs.alice.tmms",
"name": "Alice Betsy",
"email": "alice.b.tmms@gmail.tmt"
},
"benny.tmms@ISActLogs.CS2104": {
"id": "00000000-0000-4000-8000-000000000003",
"googleId": "tm.e2e.ISActLogs.benny.tmms",
"name": "Benny Charles",
"email": "benny.tmms@gmail.tmt"
}
},
"courses": {
"course": {
"id": "tm.e2e.ISActLogs.CS2104",
"name": "Programming Language Concepts",
"institute": "TEAMMATES Test Institute 1",
"timeZone": "Africa/Johannesburg"
}
},
"sections": {
"section1": {
"id": "00000000-0000-4000-8000-000000000101",
"course": {
"id": "tm.e2e.ISActLogs.CS2104"
},
"name": "Section 1"
}
},
"teams": {
"team1": {
"id": "00000000-0000-4000-8000-000000000201",
"section": {
"id": "00000000-0000-4000-8000-000000000101"
},
"name": "Team 1"
}
},
"instructors": {
"instructor": {
"id": "00000000-0000-4000-8000-000000000301",
"account": {
"id": "00000000-0000-4000-8000-000000000001"
},
"course": {
"id": "tm.e2e.ISActLogs.CS2104"
},
"name": "Teammates Test",
"email": "tmms.test@gmail.tmt",
"role": "INSTRUCTOR_PERMISSION_ROLE_COOWNER",
"isDisplayedToStudents": true,
"displayName": "Co-owner",
"privileges": {
"courseLevel": {
"canViewStudentInSections": true,
"canSubmitSessionInSections": true,
"canModifySessionCommentsInSections": true,
"canModifyCourse": true,
"canViewSessionInSections": true,
"canModifySession": true,
"canModifyStudent": true,
"canModifyInstructor": true
},
"sectionLevel": {},
"sessionLevel": {}
}
}
},
"students": {
"alice.tmms@ISActLogs.CS2104": {
"id": "00000000-0000-4000-8000-000000000401",
"account": {
"id": "00000000-0000-4000-8000-000000000002"
},
"course": {
"id": "tm.e2e.ISActLogs.CS2104"
},
"section": {
"id": "00000000-0000-4000-8000-000000000101"
},
"team": {
"id": "00000000-0000-4000-8000-000000000201"
},
"name": "Alice Betsy",
"email": "alice.b.tmms@gmail.tmt",
"comments": "This student's name is Alice Betsy"
},
"benny.tmms@ISActLogs.CS2104": {
"id": "00000000-0000-4000-8000-000000000402",
"account": {
"id": "00000000-0000-4000-8000-000000000003"
},
"course": {
"id": "tm.e2e.ISActLogs.CS2104"
},
"section": {
"id": "00000000-0000-4000-8000-000000000101"
},
"team": {
"id": "00000000-0000-4000-8000-000000000201"
},
"name": "Benny Charles",
"email": "benny.tmms@gmail.tmt",
"comments": "This student's name is Benny Charles"
}
},
"feedbackSessions": {
"openSession": {
"id": "00000000-0000-4000-8000-000000000501",
"name": "First Session",
"course": {
"id": "tm.e2e.ISActLogs.CS2104"
},
"creatorEmail": "tmms.test@gmail.tmt",
"instructions": "<p>Instructions for first session</p>",
"startTime": "2012-04-01T22:00:00Z",
"endTime": "2026-04-30T22:00:00Z",
"sessionVisibleFromTime": "2012-04-01T22:00:00Z",
"resultsVisibleFromTime": "2026-05-01T22:00:00Z",
"gracePeriod": 10,
"isOpenedEmailEnabled": true,
"isClosingSoonEmailEnabled": true,
"isPublishedEmailEnabled": true,
"isOpeningSoonEmailSent": false,
"isOpenedEmailSent": false,
"isClosingSoonEmailSent": false,
"isClosedEmailSent": false,
"isPublishedEmailSent": false
}
},
"feedbackQuestions": {
"qn1": {
"id": "00000000-0000-4000-8000-000000000601",
"feedbackSession": {
"id": "00000000-0000-4000-8000-000000000501"
},
"questionDetails": {
"questionType": "TEXT",
"questionText": "Testing question text"
},
"description": "<p>Testing description</p>",
"questionNumber": 1,
"giverType": "STUDENTS",
"recipientType": "STUDENTS",
"numOfEntitiesToGiveFeedbackTo": 2,
"showResponsesTo": [
"STUDENTS",
"INSTRUCTORS",
"OWN_TEAM_MEMBERS",
"RECEIVER"
],
"showGiverNameTo": [
"STUDENTS",
"INSTRUCTORS",
"OWN_TEAM_MEMBERS",
"RECEIVER"
],
"showRecipientNameTo": [
"STUDENTS",
"INSTRUCTORS",
"OWN_TEAM_MEMBERS",
"RECEIVER"
]
}
}
}
1 change: 1 addition & 0 deletions src/e2e/resources/testng-unstable-e2e-sql.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<package name="teammates.e2e.util" />
</packages>
<classes>
<class name="teammates.e2e.cases.sql.InstructorStudentActivityLogsPageE2ETest" />
<class name="teammates.e2e.cases.sql.InstructorSearchPageE2ETest" />
</classes>
</test>
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/teammates/sqllogic/api/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ public void adjustFeedbackSessionEmailStatusAfterUpdate(FeedbackSession session)
/**
* Gets the expected number of submissions for a feedback session.
*
* <br>Preconditions: <br>
* <br>Preconditions: <br/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it required to add the / ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While not strictly required by the Java compiler, adding the / is highly recommended to ensure the Javadoc is well-formed XML and to prevent potential build warnings from the documentation tool. This is also to ensure consistency with the
usage in multiple instances in this file before.

* * All parameters are non-null.
*/
public int getExpectedTotalSubmission(FeedbackSession fs) {
Expand All @@ -763,7 +763,7 @@ public int getExpectedTotalSubmission(FeedbackSession fs) {
/**
* Gets the actual number of submissions for a feedback session.
*
* <br>Preconditions: <br>
* <br>Preconditions: <br/>
* * All parameters are non-null.
*/
public int getActualTotalSubmission(FeedbackSession fs) {
Expand Down Expand Up @@ -1184,7 +1184,7 @@ public void deleteStudentCascade(String courseId, String studentEmail) {
/**
* Deletes all the students in the course cascade their associated responses, deadline extensions and comments.
*
* <br/>Preconditions: <br>
* <br/>Preconditions: <br/>
* Parameter is non-null.
*/
public void deleteStudentsInCourseCascade(String courseId) {
Expand Down Expand Up @@ -1755,6 +1755,13 @@ public void createFeedbackSessionLogs(List<FeedbackSessionLog> feedbackSessionLo
feedbackSessionLogsLogic.createFeedbackSessionLogs(feedbackSessionLogs);
}

/**
* Create feedback session log.
*/
public void createFeedbackSessionLog(FeedbackSessionLog feedbackSessionLog) {
feedbackSessionLogsLogic.createFeedbackSessionLog(feedbackSessionLog);
}

/**
* Gets the feedback session logs as filtered by the given parameters ordered by
* ascending timestamp. Logs with the same timestamp will be ordered by the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ public void createFeedbackSessionLogs(List<FeedbackSessionLog> fsLogs) {
}
}

/**
* Creates feedback session log.
*/
public void createFeedbackSessionLog(FeedbackSessionLog fsLog) {
try {
fslDb.createFeedbackSessionLog(fsLog);
} catch (ObjectNotFoundException e) {
log.severe(String.format(ERROR_FAILED_TO_CREATE_LOG), e);
}
}

/**
* Gets the feedback session logs as filtered by the given parameters ordered by
* ascending timestamp. Logs with the same timestamp will be ordered by the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package teammates.ui.webapi;

import java.time.Instant;
import java.util.UUID;

import teammates.common.datatransfer.logs.FeedbackSessionAuditLogDetails;
import teammates.common.datatransfer.logs.FeedbackSessionLogType;
import teammates.common.util.Const;
import teammates.common.util.Logger;
import teammates.storage.sqlentity.FeedbackSession;
import teammates.storage.sqlentity.FeedbackSessionLog;
import teammates.storage.sqlentity.Student;

/**
* Action: creates a feedback session log for the purposes of tracking and auditing.
Expand Down Expand Up @@ -52,8 +56,13 @@ public JsonResult execute() {
details.setStudentId(studentId.toString());
details.setFeedbackSessionId(fsId.toString());

// Necessary to assist local testing. For production usage, this will be a no-op.
logsProcessor.createFeedbackSessionLog(courseId, studentId, fsId, fslType);
Student student = sqlLogic.getStudent(studentId);
FeedbackSession feedbackSession = sqlLogic.getFeedbackSession(fsId);

FeedbackSessionLog feedbackSessionLog = new FeedbackSessionLog(student, feedbackSession,
convertedFslType, Instant.now());

sqlLogic.createFeedbackSessionLog(feedbackSessionLog);
} else {
// Necessary to assist local testing. For production usage, this will be a no-op.
logsProcessor.createFeedbackSessionLog(courseId, studentEmail, fsName, fslType);
Expand Down
Loading