package org.iatoki.judgels.uriel.controllers.apis;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import org.apache.commons.io.FilenameUtils;
import org.iatoki.judgels.play.IdentityUtils;
import org.iatoki.judgels.uriel.Contest;
import org.iatoki.judgels.uriel.ContestConfiguration;
import org.iatoki.judgels.uriel.ContestNotFoundException;
import org.iatoki.judgels.uriel.ContestPermissions;
import org.iatoki.judgels.uriel.ContestScoreboard;
import org.iatoki.judgels.uriel.ContestScoreboardType;
import org.iatoki.judgels.uriel.ContestTeamMember;
import org.iatoki.judgels.uriel.VirtualContestTypeConfig;
import org.iatoki.judgels.uriel.VirtualContestTypeConfigStartTrigger;
import org.iatoki.judgels.uriel.UrielProperties;
import org.iatoki.judgels.uriel.UrielUtils;
import org.iatoki.judgels.uriel.controllers.securities.Authenticated;
import org.iatoki.judgels.uriel.controllers.securities.HasRole;
import org.iatoki.judgels.uriel.controllers.securities.LoggedIn;
import org.iatoki.judgels.uriel.services.ContestAnnouncementService;
import org.iatoki.judgels.uriel.services.ContestClarificationService;
import org.iatoki.judgels.uriel.services.ContestContestantService;
import org.iatoki.judgels.uriel.services.ContestManagerService;
import org.iatoki.judgels.uriel.services.ContestScoreboardService;
import org.iatoki.judgels.uriel.services.ContestService;
import org.iatoki.judgels.uriel.services.ContestSupervisorService;
import org.iatoki.judgels.uriel.services.ContestTeamService;
import play.data.DynamicForm;
import play.db.jpa.Transactional;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Singleton
@Named
public final class ContestAPIController extends Controller {
private final ContestService contestService;
private final ContestTeamService contestTeamService;
private final ContestAnnouncementService contestAnnouncementService;
private final ContestClarificationService contestClarificationService;
private final ContestScoreboardService contestScoreboardService;
private final ContestContestantService contestContestantService;
private final ContestManagerService contestManagerService;
private final ContestSupervisorService contestSupervisorService;
@Inject
public ContestAPIController(ContestService contestService, ContestTeamService contestTeamService, ContestAnnouncementService contestAnnouncementService, ContestClarificationService contestClarificationService, ContestScoreboardService contestScoreboardService, ContestContestantService contestContestantService, ContestManagerService contestManagerService, ContestSupervisorService contestSupervisorService) {
this.contestService = contestService;
this.contestTeamService = contestTeamService;
this.contestAnnouncementService = contestAnnouncementService;
this.contestClarificationService = contestClarificationService;
this.contestScoreboardService = contestScoreboardService;
this.contestContestantService = contestContestantService;
this.contestManagerService = contestManagerService;
this.contestSupervisorService = contestSupervisorService;
}
@Authenticated(value = {LoggedIn.class, HasRole.class})
@Transactional(readOnly = true)
public Result unreadAnnouncement(long contestId) throws ContestNotFoundException {
Contest contest = contestService.findContestById(contestId);
if (isAllowedToEnterContest(contest)) {
long unreadCount = contestAnnouncementService.getUnreadContestAnnouncementsCount(IdentityUtils.getUserJid(), contest.getJid());
ObjectNode objectNode = Json.newObject();
objectNode.put("success", true);
objectNode.put("count", unreadCount);
return ok(objectNode);
} else {
ObjectNode objectNode = Json.newObject();
objectNode.put("success", false);
return ok(objectNode);
}
}
@Authenticated(value = {LoggedIn.class, HasRole.class})
@Transactional(readOnly = true)
public Result unreadClarification(long contestId) throws ContestNotFoundException {
Contest contest = contestService.findContestById(contestId);
if (isAllowedToEnterContest(contest)) {
long unreadCount;
if (isCoach(contest)) {
List<ContestTeamMember> contestTeamMemberList = contestTeamService.findContestTeamMembersByContestJidAndCoachJid(contest.getJid(), IdentityUtils.getUserJid());
unreadCount = contestClarificationService.getUnreadContestClarificationsCount(contestTeamMemberList.stream().map(ContestTeamMember::getMemberJid).collect(Collectors.toList()), IdentityUtils.getUserJid(), contest.getJid(), false);
} else {
unreadCount = contestClarificationService.getUnreadContestClarificationsCount(ImmutableList.of(IdentityUtils.getUserJid()), IdentityUtils.getUserJid(), contest.getJid(), true);
}
ObjectNode objectNode = Json.newObject();
objectNode.put("success", true);
objectNode.put("count", unreadCount);
return ok(objectNode);
} else {
ObjectNode objectNode = Json.newObject();
objectNode.put("success", false);
return ok(objectNode);
}
}
@Authenticated(value = {LoggedIn.class, HasRole.class})
@Transactional(readOnly = true)
public Result unansweredClarification(long contestId) throws ContestNotFoundException {
Contest contest = contestService.findContestById(contestId);
if (isAllowedToSuperviseClarifications(contest)) {
long unreadCount = contestClarificationService.getUnansweredContestClarificationsCount(contest.getJid());
ObjectNode objectNode = Json.newObject();
objectNode.put("success", true);
objectNode.put("count", unreadCount);
return ok(objectNode);
} else {
ObjectNode objectNode = Json.newObject();
objectNode.put("success", false);
return ok(objectNode);
}
}
@Authenticated(value = {LoggedIn.class, HasRole.class})
@Transactional(readOnly = true)
public Result renderTeamAvatarImage(String imageName) {
response().setHeader("Cache-Control", "no-transform,public,max-age=300,s-maxage=900");
String imageURL = contestTeamService.getTeamAvatarImageURL(imageName);
try {
new URL(imageURL);
return temporaryRedirect(imageURL);
} catch (MalformedURLException e) {
File image = new File(imageURL);
if (image.exists()) {
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
response().setHeader("Last-Modified", sdf.format(new Date(image.lastModified())));
if (request().hasHeader("If-Modified-Since")) {
try {
Date lastUpdate = sdf.parse(request().getHeader("If-Modified-Since"));
if (image.lastModified() > lastUpdate.getTime()) {
BufferedImage in = ImageIO.read(image);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String type = FilenameUtils.getExtension(image.getAbsolutePath());
ImageIO.write(in, type, baos);
return ok(baos.toByteArray()).as("image/" + type);
} else {
return status(304);
}
} catch (ParseException | IOException e2) {
throw new RuntimeException(e2);
}
} else {
try {
BufferedImage in = ImageIO.read(image);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String type = FilenameUtils.getExtension(image.getAbsolutePath());
ImageIO.write(in, type, baos);
return ok(baos.toByteArray()).as("image/" + type);
} catch (IOException e2) {
return internalServerError();
}
}
} else {
return notFound();
}
}
}
@Transactional(readOnly = true)
public Result getScoreboard() {
DynamicForm form = DynamicForm.form().bindFromRequest();
String secret = form.get("secret");
String contestJid = form.get("contestJid");
String type = form.get("type");
if (secret == null || contestJid == null || type == null) {
return notFound();
}
if (!UrielProperties.getInstance().getUrielScoreboardSecret().equals(secret)) {
return notFound();
}
ContestScoreboardType contestScoreboardType;
try {
contestScoreboardType = ContestScoreboardType.valueOf(type);
} catch (IllegalArgumentException e) {
return notFound();
}
ContestScoreboard contestScoreboard;
if (contestScoreboardService.isContestScoreboardExistByContestJidAndScoreboardType(contestJid, contestScoreboardType)) {
contestScoreboard = contestScoreboardService.findContestScoreboardByContestJidAndScoreboardType(contestJid, contestScoreboardType);
} else {
// Resort to the official one.
contestScoreboard = contestScoreboardService.findContestScoreboardByContestJidAndScoreboardType(contestJid, ContestScoreboardType.OFFICIAL);
}
return ok(new Gson().toJson(contestScoreboard));
}
private boolean isAdmin() {
return UrielUtils.hasRole("admin");
}
private boolean isManager(Contest contest) {
return contestManagerService.isContestManagerInContestByUserJid(contest.getJid(), IdentityUtils.getUserJid());
}
private boolean isSupervisor(Contest contest) {
return contestSupervisorService.isContestSupervisorInContestByUserJid(contest.getJid(), IdentityUtils.getUserJid());
}
private boolean isCoach(Contest contest) {
return contestTeamService.isUserCoachInAnyTeamByContestJid(contest.getJid(), IdentityUtils.getUserJid());
}
private boolean isContestant(Contest contest) {
return contestContestantService.isContestContestantInContestByUserJid(contest.getJid(), IdentityUtils.getUserJid());
}
private boolean isContestStarted(Contest contest) {
return (!new Date().before(contest.getStartTime()));
}
private boolean isAllowedToEnterContest(Contest contest) {
if (isAdmin() || isManager(contest) || isSupervisor(contest)) {
return true;
}
if (contest.isStandard()) {
return ((isContestant(contest) && isContestStarted(contest)) || (isCoach(contest)));
} else {
ContestConfiguration contestConfiguration = contestService.findContestConfigurationByContestJid(contest.getJid());
VirtualContestTypeConfig virtualContestTypeConfig = new Gson().fromJson(contestConfiguration.getTypeConfig(), VirtualContestTypeConfig.class);
if (virtualContestTypeConfig.getStartTrigger().equals(VirtualContestTypeConfigStartTrigger.CONTESTANT)) {
return (isContestant(contest) && (isContestStarted(contest)));
} else {
return ((isContestStarted(contest)) && (isCoach(contest) || (isContestant(contest) && (contestContestantService.isContestStarted(contest.getJid(), IdentityUtils.getUserJid())))));
}
}
}
private boolean isAllowedToSuperviseClarifications(Contest contest) {
return isAdmin() || isManager(contest) || (isSupervisor(contest) && contestSupervisorService.findContestSupervisorByContestJidAndUserJid(contest.getJid(), IdentityUtils.getUserJid()).getContestPermission().isAllowed(ContestPermissions.CLARIFICATION));
}
}