Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#11843] Create FeedbackSessionLog entity and cron job action #12895

Merged
merged 12 commits into from
Mar 18, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* Represents a log entry of a feedback session.
*/
public class FeedbackSessionLogEntry {
public class FeedbackSessionLogEntry implements Comparable<FeedbackSessionLogEntry> {
private final String studentEmail;
private final String feedbackSessionName;
private final String feedbackSessionLogType;
Expand Down Expand Up @@ -32,4 +32,9 @@ public String getFeedbackSessionLogType() {
public long getTimestamp() {
return this.timestamp;
}

@Override
public int compareTo(FeedbackSessionLogEntry o) {
return Long.compare(this.getTimestamp(), o.getTimestamp());
}
}
2 changes: 2 additions & 0 deletions src/main/java/teammates/common/util/Const.java
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ public static class CronJobURIs {
URI_PREFIX + "/feedbackSessionPublishedReminders";
public static final String AUTOMATED_USAGE_STATISTICS_COLLECTION =
URI_PREFIX + "/calculateUsageStatistics";
public static final String AUTOMATED_FEEDBACK_SESSION_LOGS_PROCESSING =
URI_PREFIX + "/updateFeedbackSessionLogs";
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/teammates/logic/api/LogsProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ public void createFeedbackSessionLog(String courseId, String email, String fsNam
}

/**
* Gets the feedback session logs as filtered by the given parameters.
* Gets the feedback session logs as filtered by the given parameters ordered by ascending timestamp.
* @param email Can be null
*/
public List<FeedbackSessionLogEntry> getFeedbackSessionLogs(String courseId, String email,
public List<FeedbackSessionLogEntry> getOrderedFeedbackSessionLogs(String courseId, String email,
long startTime, long endTime, String fsName) {
return service.getFeedbackSessionLogs(courseId, email, startTime, endTime, fsName);
return service.getOrderedFeedbackSessionLogs(courseId, email, startTime, endTime, fsName);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void createFeedbackSessionLog(String courseId, String email, String fsNam
}

@Override
public List<FeedbackSessionLogEntry> getFeedbackSessionLogs(String courseId, String email,
public List<FeedbackSessionLogEntry> getOrderedFeedbackSessionLogs(String courseId, String email,
long startTime, long endTime, String fsName) {
List<String> filters = new ArrayList<>();
if (courseId != null) {
Expand All @@ -131,6 +131,7 @@ public List<FeedbackSessionLogEntry> getFeedbackSessionLogs(String courseId, Str
.withLogEvent(LogEvent.FEEDBACK_SESSION_AUDIT.name())
.withSeverityLevel(LogSeverity.INFO)
.withExtraFilters(String.join("\n", filters))
.withOrder(ASCENDING_ORDER)
dishenggg marked this conversation as resolved.
Show resolved Hide resolved
.build();
LogSearchParams logSearchParams = LogSearchParams.from(queryLogsParams)
.addLogName(STDOUT_LOG_NAME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ public void createFeedbackSessionLog(String courseId, String email, String fsNam
}

@Override
public List<FeedbackSessionLogEntry> getFeedbackSessionLogs(String courseId, String email,
public List<FeedbackSessionLogEntry> getOrderedFeedbackSessionLogs(String courseId, String email,
long startTime, long endTime, String fsName) {
return FEEDBACK_SESSION_LOG_ENTRIES
.getOrDefault(courseId, new ArrayList<>())
Expand All @@ -218,6 +218,7 @@ public List<FeedbackSessionLogEntry> getFeedbackSessionLogs(String courseId, Str
.filter(log -> fsName == null || log.getFeedbackSessionName().equals(fsName))
.filter(log -> log.getTimestamp() >= startTime)
.filter(log -> log.getTimestamp() <= endTime)
.sorted()
.collect(Collectors.toList());
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/teammates/logic/external/LogService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public interface LogService {
void createFeedbackSessionLog(String courseId, String email, String fsName, String fslType);

/**
* Gets the feedback session logs as filtered by the given parameters.
* Gets the feedback session logs as filtered by the given parameters ordered by ascending timestamp.
*/
List<FeedbackSessionLogEntry> getFeedbackSessionLogs(String courseId, String email,
List<FeedbackSessionLogEntry> getOrderedFeedbackSessionLogs(String courseId, String email,
long startTime, long endTime, String fsName);
}
9 changes: 9 additions & 0 deletions src/main/java/teammates/sqllogic/api/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import teammates.storage.sqlentity.FeedbackResponse;
import teammates.storage.sqlentity.FeedbackResponseComment;
import teammates.storage.sqlentity.FeedbackSession;
import teammates.storage.sqlentity.FeedbackSessionLog;
import teammates.storage.sqlentity.Instructor;
import teammates.storage.sqlentity.Notification;
import teammates.storage.sqlentity.Section;
Expand Down Expand Up @@ -1595,4 +1596,12 @@ public List<FeedbackSession> getFeedbackSessionsClosingWithinTimeLimit() {
public List<FeedbackSession> getFeedbackSessionsOpeningWithinTimeLimit() {
return feedbackSessionsLogic.getFeedbackSessionsOpeningWithinTimeLimit();
}

/**
* Create feedback session logs.
*/
public void createFeedbackSessionLogs(List<FeedbackSessionLog> feedbackSessionLogs)
throws EntityAlreadyExistsException, InvalidParametersException {
// TODO: implement logic layer
}
}
123 changes: 123 additions & 0 deletions src/main/java/teammates/storage/sqlentity/FeedbackSessionLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package teammates.storage.sqlentity;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import teammates.common.datatransfer.logs.FeedbackSessionLogType;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

/**
* Represents a feedback session log.
*/
@Entity
@Table(name = "FeedbackSessionLogs")
public class FeedbackSessionLog extends BaseEntity {
@Id
private UUID id;

@Column(nullable = false)
private String studentEmail;

@Column(nullable = false)
private String feedbackSessionName;

@Column(nullable = false)
dishenggg marked this conversation as resolved.
Show resolved Hide resolved
@Enumerated(EnumType.STRING)
private FeedbackSessionLogType feedbackSessionLogType;

@Column(nullable = false)
private Instant timestamp;

protected FeedbackSessionLog() {
// required by Hibernate
}

public FeedbackSessionLog(String email, String feedbackSessionName, FeedbackSessionLogType feedbackSessionLogType,
Instant timestamp) {
this.setId(UUID.randomUUID());
this.studentEmail = email;
this.feedbackSessionName = feedbackSessionName;
this.feedbackSessionLogType = feedbackSessionLogType;
this.timestamp = timestamp;
}

public UUID getId() {
return id;
}

public void setId(UUID id) {
this.id = id;
}

public String getStudentEmail() {
return studentEmail;
}

public void setStudentEmail(String email) {
this.studentEmail = email;
}

public String getFeedbackSessionName() {
return feedbackSessionName;
}

public void setFeedbackSessionName(String feedbackSessionName) {
this.feedbackSessionName = feedbackSessionName;
}

public FeedbackSessionLogType getFeedbackSessionLogType() {
return feedbackSessionLogType;
}

public void setFeedbackSessionLogType(FeedbackSessionLogType feedbackSessionLogType) {
this.feedbackSessionLogType = feedbackSessionLogType;
}

public Instant getTimestamp() {
return timestamp;
}

public void setTimestamp(Instant timestamp) {
this.timestamp = timestamp;
}

@Override
public String toString() {
return "FeedbackSessionLog [id=" + id + ", email=" + studentEmail + ", feedbackSessionName="
+ feedbackSessionName
+ ", feedbackSessionLogType=" + feedbackSessionLogType.getLabel() + ", timestamp=" + timestamp + "]";
}

@Override
public int hashCode() {
return this.getId().hashCode();
}

@Override
public boolean equals(Object other) {
if (other == null) {
return false;
} else if (this == other) {
return true;
} else if (this.getClass() == other.getClass()) {
FeedbackSessionLog otherFeedbackSessionLog = (FeedbackSessionLog) other;
return Objects.equals(this.getId(), otherFeedbackSessionLog.getId());
} else {
return false;
}
}

@Override
public List<String> getInvalidityInfo() {
return new ArrayList<>();
}
}
1 change: 1 addition & 0 deletions src/main/java/teammates/ui/webapi/ActionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public final class ActionFactory {
map(CronJobURIs.AUTOMATED_FEEDBACK_OPENING_SOON_REMINDERS, GET,
FeedbackSessionOpeningSoonRemindersAction.class);
map(CronJobURIs.AUTOMATED_USAGE_STATISTICS_COLLECTION, GET, CalculateUsageStatisticsAction.class);
map(CronJobURIs.AUTOMATED_FEEDBACK_SESSION_LOGS_PROCESSING, GET, UpdateFeedbackSessionLogsAction.class);

// Task queue workers; use POST request
// Reference: https://cloud.google.com/tasks/docs/creating-appengine-tasks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public JsonResult execute() {
}

List<FeedbackSessionLogEntry> fsLogEntries =
logsProcessor.getFeedbackSessionLogs(courseId, email, startTime, endTime, feedbackSessionName);
logsProcessor.getOrderedFeedbackSessionLogs(courseId, email, startTime, endTime, feedbackSessionName);

if (isCourseMigrated(courseId)) {
Map<String, Student> studentsMap = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package teammates.ui.webapi;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import teammates.common.datatransfer.FeedbackSessionLogEntry;
import teammates.common.datatransfer.logs.FeedbackSessionLogType;
import teammates.common.exception.EntityAlreadyExistsException;
import teammates.common.exception.InvalidParametersException;
import teammates.common.util.Logger;
import teammates.common.util.TimeHelper;
import teammates.storage.sqlentity.FeedbackSessionLog;

/**
* Process feedback session logs in the past defined time period and store in the database.
*/
public class UpdateFeedbackSessionLogsAction extends AdminOnlyAction {

static final int COLLECTION_TIME_PERIOD = 60; // represents one hour
static final long SPAM_FILTER = 2000L; // in ms
private static final Logger log = Logger.getLogger();

@Override
public JsonResult execute() {
List<FeedbackSessionLog> filteredLogs = new ArrayList<>();

Instant endTime = TimeHelper.getInstantNearestHourBefore(Instant.now());
Instant startTime = endTime.minus(COLLECTION_TIME_PERIOD, ChronoUnit.MINUTES);

List<FeedbackSessionLogEntry> logEntries = logsProcessor.getOrderedFeedbackSessionLogs(null, null,
startTime.toEpochMilli(), endTime.toEpochMilli(), null);

Map<String, Map<String, Map<String, Long>>> lastSavedTimestamps = new HashMap<>();
for (FeedbackSessionLogEntry logEntry : logEntries) {
String email = logEntry.getStudentEmail();
String fbSessionName = logEntry.getFeedbackSessionName();
String type = logEntry.getFeedbackSessionLogType();
Long timestamp = logEntry.getTimestamp();

lastSavedTimestamps.putIfAbsent(email, new HashMap<>());
lastSavedTimestamps.get(email).putIfAbsent(fbSessionName, new HashMap<>());
Long lastSaved = lastSavedTimestamps.get(email).get(fbSessionName).getOrDefault(type, 0L);

if (Math.abs(timestamp - lastSaved) > SPAM_FILTER) {
lastSavedTimestamps.get(email).get(fbSessionName).put(type, timestamp);
FeedbackSessionLog fslEntity = new FeedbackSessionLog(email, fbSessionName,
FeedbackSessionLogType.valueOfLabel(type), Instant.ofEpochMilli(timestamp));
filteredLogs.add(fslEntity);
}
}

try {
sqlLogic.createFeedbackSessionLogs(filteredLogs);
} catch (InvalidParametersException | EntityAlreadyExistsException e) {
log.severe("Unexpected error", e);
}

return new JsonResult("Successful");
}
}
3 changes: 2 additions & 1 deletion src/test/java/teammates/logic/api/MockLogsProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ public void createFeedbackSessionLog(String courseId, String email, String fsNam
}

@Override
public List<FeedbackSessionLogEntry> getFeedbackSessionLogs(String courseId, String email,
public List<FeedbackSessionLogEntry> getOrderedFeedbackSessionLogs(String courseId, String email,
long startTime, long endTime, String fsName) {
feedbackSessionLogs.sort((x, y) -> x.compareTo(y));
return feedbackSessionLogs;
}

Expand Down
Loading