package org.kalipo.service;
import org.kalipo.aop.KalipoExceptionHandler;
import org.kalipo.aop.RateLimit;
import org.kalipo.domain.*;
import org.kalipo.repository.AchievementRepository;
import org.kalipo.repository.CommentRepository;
import org.kalipo.repository.ReputationModifierRepository;
import org.kalipo.repository.UserRepository;
import org.kalipo.security.Privileges;
import org.kalipo.service.util.Asserts;
import org.kalipo.web.rest.KalipoException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.concurrent.Future;
@Service
@KalipoExceptionHandler
public class ReputationModifierService {
private final Logger log = LoggerFactory.getLogger(ReputationModifierService.class);
@Inject
private CommentRepository commentRepository;
@Inject
private UserRepository userRepository;
@Inject
private AchievementRepository achievementRepository;
@Inject
private ReputationModifierRepository reputationModifierRepository;
/**
* Create a reputation revision for a vote
* on dislikes: reputation -1 of voter and -2 of author
* on like: reputation +10 of author, probably reputation -1 of voter to hinder meat-puppet issue
*
* @param vote the vote
* @param currentLogin the current login, since async
* @throws KalipoException
*/
@Async
public void onCommentVoting(Vote vote, String currentLogin) throws KalipoException {
Asserts.isNotNull(vote, "vote");
Asserts.isNotNull(vote.getCommentId(), "commentId");
Asserts.isNotNull(currentLogin, "login");
vote.setAuthorId(currentLogin);
final Comment comment = commentRepository.findOne(vote.getCommentId());
Asserts.isNotNull(comment, "commentId");
final String resourceRef = comment.getId();
final String authorId = comment.getAuthorId();
final String voterId = vote.getAuthorId();
Achievement achievementForAuthor, achievementForVoter;
if (vote.isLike()) {
achievementForAuthor = createAchievement(authorId, resourceRef, ReputationModifier.Type.LIKE);
achievementForVoter = createAchievement(voterId, resourceRef, ReputationModifier.Type.LIKED);
} else {
achievementForAuthor = createAchievement(authorId, resourceRef, ReputationModifier.Type.DISLIKE);
achievementForVoter = createAchievement(voterId, resourceRef, ReputationModifier.Type.DISLIKED);
}
achievementRepository.save(achievementForAuthor);
achievementRepository.save(achievementForVoter);
updateUserReputation(achievementForAuthor);
updateUserReputation(achievementForVoter);
}
@Async
public void onReportApprovalOrRejection(Report report) throws KalipoException {
Asserts.isNotNull(report, "report");
final Comment comment = commentRepository.findOne(report.getCommentId());
Asserts.isNotNull(comment, "commentId");
final String resourceRef = report.getId();
Asserts.isNotNull(resourceRef, "id");
final String reporterId = report.getAuthorId();
final String authorId = comment.getAuthorId();
if (report.getStatus() == Report.Status.APPROVED) {
/**
* report is ok, comment is rightly flagged
*/
Achievement rvForAuthor = createAchievement(authorId, resourceRef, ReputationModifier.Type.REPORT);
achievementRepository.save(rvForAuthor);
Achievement rvForReporter = createAchievement(reporterId, resourceRef, ReputationModifier.Type.REPORTED);
achievementRepository.save(rvForReporter);
updateUserReputation(rvForAuthor);
updateUserReputation(rvForReporter);
} else {
/**
* report is rejected
*/
if (report.isAbused()) {
Achievement achievementForReporter = createAchievement(reporterId, resourceRef, ReputationModifier.Type.ABUSED_REPORT);
achievementRepository.save(achievementForReporter);
updateUserReputation(achievementForReporter);
}
}
}
/**
* A newly created user receives a welcome reputation. The background of this is to establish user.reputation=0 as virtual death (the inability of actively joining the discussion)
*
* @param user the user
*/
@Async
public void onUserCreation(@Valid @NotNull User user) {
Achievement achievementForNewUser = createAchievement(user.getLogin(), user.getLogin(), ReputationModifier.Type.WELCOME);
achievementRepository.save(achievementForNewUser);
updateUserReputation(achievementForNewUser);
}
/**
* Punish comment deleting by Reputation.Type.RM_COMMENT
*
* @param comment the comment
* @throws KalipoException
*/
@Async
public void onCommentDeletion(@Valid @NotNull Comment comment) throws KalipoException {
Asserts.isNotNull(comment, "comment");
Achievement achievementForUser = createAchievement(comment.getAuthorId(), comment.getId(), ReputationModifier.Type.RM_COMMENT);
achievementRepository.save(achievementForUser);
updateUserReputation(achievementForUser);
}
@RolesAllowed(Privileges.UPDATE_PRIVILEGE)
@RateLimit
public void update(ReputationModifier reputationModifier) throws KalipoException {
reputationModifierRepository.save(reputationModifier);
}
@Async
public Future<ReputationModifier> get(String id) throws KalipoException {
return new AsyncResult<>(reputationModifierRepository.findOne(id));
}
@Async
public Future<List<ReputationModifier>> getAll() {
return new AsyncResult<>(reputationModifierRepository.findAll());
}
// --
private void updateUserReputation(Achievement achievement) {
User u = userRepository.findOne(achievement.getUserId());
ReputationModifier modifier = reputationModifierRepository.findByType(achievement.getType());
u.setReputation(u.getReputation() + modifier.getReputation());
if (modifier.getReputation() < 0) {
log.info(String.format("User '%s' reputation decreases to %s on %s", achievement.getUserId(), u.getReputation(), achievement.getType()));
} else {
log.info(String.format("User '%s' reputation increases to %s on %s", achievement.getUserId(), u.getReputation(), achievement.getType()));
}
userRepository.save(u);
}
private Achievement createAchievement(String authorId, String resourceRef, ReputationModifier.Type type) {
Achievement achievement = new Achievement();
achievement.setUserId(authorId);
achievement.setResourceRef(resourceRef);
achievement.setType(type);
return achievement;
}
}