package org.iatoki.judgels.uriel.services.impls; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.gson.Gson; import org.iatoki.judgels.play.IdentityUtils; import org.iatoki.judgels.play.Page; import org.iatoki.judgels.uriel.Contest; import org.iatoki.judgels.uriel.ContestConfiguration; import org.iatoki.judgels.uriel.ContestContestantStatus; import org.iatoki.judgels.uriel.ContestNotFoundException; import org.iatoki.judgels.uriel.ContestScope; import org.iatoki.judgels.uriel.ContestScopeConfig; import org.iatoki.judgels.uriel.PrivateContestScopeConfig; import org.iatoki.judgels.uriel.PublicContestScopeConfig; import org.iatoki.judgels.uriel.ScoreboardState; import org.iatoki.judgels.uriel.ContestScoreboardType; import org.iatoki.judgels.uriel.ContestStyle; import org.iatoki.judgels.uriel.ContestStyleConfig; import org.iatoki.judgels.uriel.ICPCContestStyleConfig; import org.iatoki.judgels.uriel.IOIContestStyleConfig; import org.iatoki.judgels.uriel.ContestType; import org.iatoki.judgels.uriel.ContestTypeConfig; import org.iatoki.judgels.uriel.StandardContestTypeConfig; import org.iatoki.judgels.uriel.VirtualContestTypeConfig; import org.iatoki.judgels.uriel.adapters.ScoreboardAdapter; import org.iatoki.judgels.uriel.adapters.impls.ScoreboardAdapters; import org.iatoki.judgels.uriel.Scoreboard; import org.iatoki.judgels.uriel.ScoreboardContent; import org.iatoki.judgels.uriel.models.daos.ContestConfigurationDao; import org.iatoki.judgels.uriel.models.daos.ContestContestantDao; import org.iatoki.judgels.uriel.models.daos.ContestDao; import org.iatoki.judgels.uriel.models.daos.ContestManagerDao; import org.iatoki.judgels.uriel.models.daos.ContestProblemDao; import org.iatoki.judgels.uriel.models.daos.ContestScoreboardDao; import org.iatoki.judgels.uriel.models.daos.ContestSupervisorDao; import org.iatoki.judgels.uriel.models.daos.ContestTeamCoachDao; import org.iatoki.judgels.uriel.models.daos.ContestTeamDao; import org.iatoki.judgels.uriel.models.entities.ContestConfigurationModel; import org.iatoki.judgels.uriel.models.entities.ContestContestantModel; import org.iatoki.judgels.uriel.models.entities.ContestContestantModel_; import org.iatoki.judgels.uriel.models.entities.ContestModel; import org.iatoki.judgels.uriel.models.entities.ContestProblemModel; import org.iatoki.judgels.uriel.models.entities.ContestScoreboardModel; import org.iatoki.judgels.uriel.services.ContestService; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import javax.persistence.NoResultException; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @Singleton @Named("contestService") public final class ContestServiceImpl implements ContestService { private final ContestDao contestDao; private final ContestProblemDao contestProblemDao; private final ContestContestantDao contestContestantDao; private final ContestTeamDao contestTeamDao; private final ContestTeamCoachDao contestTeamCoachDao; private final ContestSupervisorDao contestSupervisorDao; private final ContestManagerDao contestManagerDao; private final ContestScoreboardDao contestScoreboardDao; private final ContestConfigurationDao contestConfigurationDao; @Inject public ContestServiceImpl(ContestDao contestDao, ContestProblemDao contestProblemDao, ContestContestantDao contestContestantDao, ContestTeamDao contestTeamDao, ContestTeamCoachDao contestTeamCoachDao, ContestSupervisorDao contestSupervisorDao, ContestManagerDao contestManagerDao, ContestScoreboardDao contestScoreboardDao, ContestConfigurationDao contestConfigurationDao) { this.contestDao = contestDao; this.contestProblemDao = contestProblemDao; this.contestContestantDao = contestContestantDao; this.contestTeamDao = contestTeamDao; this.contestTeamCoachDao = contestTeamCoachDao; this.contestSupervisorDao = contestSupervisorDao; this.contestManagerDao = contestManagerDao; this.contestScoreboardDao = contestScoreboardDao; this.contestConfigurationDao = contestConfigurationDao; } @Override public Contest findContestById(long contestId) throws ContestNotFoundException { ContestModel contestModel = contestDao.findById(contestId); if (contestModel != null) { return createContestFromModel(contestModel); } else { throw new ContestNotFoundException("Contest not found."); } } @Override public Contest findContestByJid(String contestJid) { ContestModel contestModel = contestDao.findByJid(contestJid); return createContestFromModel(contestModel); } @Override public ScoreboardState getContestStateByJid(String contestJid) { List<ContestProblemModel> contestProblemModels = contestProblemDao.findUsedByContestJidOrderedByAlias(contestJid); List<ContestContestantModel> contestContestantModels = contestContestantDao.findSortedByFilters("id", "asc", "", ImmutableMap.of(ContestContestantModel_.contestJid, contestJid, ContestContestantModel_.status, ContestContestantStatus.APPROVED.name()), ImmutableMap.of(), 0, -1); List<String> problemJids = Lists.transform(contestProblemModels, m -> m.problemJid); List<String> problemAliases = Lists.transform(contestProblemModels, m -> m.alias); List<String> contestantJids = Lists.transform(contestContestantModels, m -> m.userJid); return new ScoreboardState(problemJids, problemAliases, contestantJids); } @Override public ContestConfiguration findContestConfigurationByContestJid(String contestJid) { ContestConfigurationModel contestConfigurationModel = contestConfigurationDao.findByContestJid(contestJid); return new ContestConfiguration(contestConfigurationModel.id, contestConfigurationModel.contestJid, contestConfigurationModel.typeConfig, contestConfigurationModel.scopeConfig, contestConfigurationModel.styleConfig); } @Override public Page<Contest> pageAllowedContests(long pageIndex, long pageSize, String orderBy, String orderDir, String filterString, String userJid, boolean isAdmin) { if (isAdmin) { long totalRowsCount = contestDao.countByFilters(filterString, ImmutableMap.of(), ImmutableMap.of()); List<ContestModel> contestModels = contestDao.findSortedByFilters(orderBy, orderDir, filterString, ImmutableMap.of(), ImmutableMap.of(), pageIndex * pageSize, pageSize); List<Contest> contests = Lists.transform(contestModels, m -> createContestFromModel(m)); return new Page<>(contests, totalRowsCount, pageIndex, pageSize); } else { List<String> contestJidsWhereIsContestant = contestContestantDao.findContestJidsByContestantJid(IdentityUtils.getUserJid()); List<ContestModel> contestModels = contestDao.getRunningContestsWithinContestJids(System.currentTimeMillis(), contestJidsWhereIsContestant); long totalRowsCount; boolean anyExclusiveContest = false; for (ContestModel contestModel : contestModels) { anyExclusiveContest = anyExclusiveContest || contestModel.isExclusive; } if (anyExclusiveContest) { // Disable past contest contestModels = contestModels.stream().filter(c -> c.name.contains(filterString)).collect(Collectors.toList()); Collections.sort(contestModels, (c1, c2) -> Long.compare(c1.endTime, c2.endTime)); totalRowsCount = contestModels.size(); contestModels = contestModels.stream().skip(pageIndex * pageSize).limit(pageSize).collect(Collectors.toList()); } else { // Enable any contest List<String> contestJidsWhereIsManager = contestManagerDao.findContestJidsByManagerJid(IdentityUtils.getUserJid()); List<String> contestJidsWhereIsSupervisor = contestSupervisorDao.findContestJidsBySupervisorJid(IdentityUtils.getUserJid()); List<String> contestTeamJids = contestTeamCoachDao.findContestTeamJidsByCoachJid(IdentityUtils.getUserJid()); List<String> contestJidsWhereIsCoach = contestTeamDao.findContestJidsByTeamJids(contestTeamJids); Set<String> contestJids = new HashSet<>(); contestJids.addAll(contestJidsWhereIsManager); contestJids.addAll(contestJidsWhereIsSupervisor); contestJids.addAll(contestJidsWhereIsContestant); contestJids.addAll(contestJidsWhereIsCoach); totalRowsCount = contestDao.countContestsWithinContestJidsOrIsRunningPublic(filterString, contestJids, System.currentTimeMillis()); contestModels = contestDao.findSortedContestsWithinContestJidsOrIsRunningPublicByFilters(orderBy, orderDir, filterString, contestJids, pageIndex * pageSize, pageSize, System.currentTimeMillis()); } List<Contest> contests = Lists.transform(contestModels, m -> createContestFromModel(m)); return new Page<>(contests, totalRowsCount, pageIndex, pageSize); } } @Override public List<Contest> getRunningContests(Date timeNow) { List<ContestModel> contestModels = contestDao.getRunningContests(timeNow.getTime()); return Lists.transform(contestModels, m -> createContestFromModel(m)); } @Override public Contest createContest(String name, String description, ContestType type, ContestScope scope, ContestStyle style, Date startTime, Date endTime, Date clarificationEndTime, boolean isExclusive, boolean isUsingScoreboard, boolean isIncognitoScoreboard, boolean requiresPassword) { ContestModel contestModel = new ContestModel(); contestModel.name = name; contestModel.description = description; contestModel.type = type.name(); contestModel.scope = scope.name(); contestModel.style = style.name(); contestModel.startTime = startTime.getTime(); contestModel.endTime = endTime.getTime(); contestModel.clarificationEndTime = clarificationEndTime.getTime(); contestModel.isExclusive = isExclusive; contestModel.isUsingScoreboard = isUsingScoreboard; contestModel.isIncognitoScoreboard = isIncognitoScoreboard; contestModel.requiresPassword = requiresPassword; contestDao.persist(contestModel, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); Contest contest = createContestFromModel(contestModel); ContestConfigurationModel contestConfigurationModel = new ContestConfigurationModel(); contestConfigurationModel.contestJid = contest.getJid(); if (contestModel.type.equals(ContestType.STANDARD.name())) { contestConfigurationModel.typeConfig = new Gson().toJson(StandardContestTypeConfig.defaultConfig(contest)); } else if (contestModel.type.equals(ContestType.VIRTUAL.name())) { contestConfigurationModel.typeConfig = new Gson().toJson(VirtualContestTypeConfig.defaultConfig(contest)); } if (contestModel.scope.equals(ContestScope.PRIVATE.name())) { contestConfigurationModel.scopeConfig = new Gson().toJson(PrivateContestScopeConfig.defaultConfig(contest)); } else if (contestModel.scope.equals(ContestScope.PUBLIC.name())) { contestConfigurationModel.scopeConfig = new Gson().toJson(PublicContestScopeConfig.defaultConfig(contest)); } if (contestModel.style.equals(ContestStyle.ICPC.name())) { contestConfigurationModel.styleConfig = new Gson().toJson(ICPCContestStyleConfig.defaultConfig(contest)); } else if (contestModel.style.equals(ContestStyle.IOI.name())) { contestConfigurationModel.styleConfig = new Gson().toJson(IOIContestStyleConfig.defaultConfig(contest)); } contestConfigurationDao.persist(contestConfigurationModel, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); ContestScoreboardModel contestScoreboardModel = new ContestScoreboardModel(); contestScoreboardModel.contestJid = contestModel.jid; contestScoreboardModel.type = ContestScoreboardType.OFFICIAL.name(); ScoreboardAdapter adapter = ScoreboardAdapters.fromContestStyle(style); ScoreboardState state = getContestStateByJid(contestModel.jid); ScoreboardContent content = adapter.computeScoreboardContent(contest, contestConfigurationModel.styleConfig, state, ImmutableList.of(), ImmutableMap.of()); Scoreboard scoreboard = adapter.createScoreboard(state, content); contestScoreboardModel.scoreboard = new Gson().toJson(scoreboard); contestScoreboardDao.persist(contestScoreboardModel, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); return contest; } @Override public void updateContest(long contestId, String name, String description, ContestType type, ContestScope scope, ContestStyle style, Date startTime, Date endTime, Date clarificationEndTime, boolean isExclusive, boolean isUsingScoreboard, boolean isIncognitoScoreboard, boolean requiresPassword) { boolean isTypeChanged; boolean isStyleChanged; boolean isScopeChanged; ContestModel contestModel = contestDao.findById(contestId); contestModel.name = name; contestModel.description = description; isTypeChanged = !contestModel.type.equals(type.name()); contestModel.type = type.name(); isScopeChanged = !contestModel.scope.equals(scope.name()); contestModel.scope = scope.name(); isStyleChanged = !contestModel.style.equals(style.name()); contestModel.style = style.name(); contestModel.startTime = startTime.getTime(); contestModel.endTime = endTime.getTime(); contestModel.clarificationEndTime = clarificationEndTime.getTime(); contestModel.isExclusive = isExclusive; contestModel.isUsingScoreboard = isUsingScoreboard; contestModel.isIncognitoScoreboard = isIncognitoScoreboard; contestModel.requiresPassword = requiresPassword; contestDao.edit(contestModel, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); ContestConfigurationModel contestConfigurationModel = contestConfigurationDao.findByContestJid(contestModel.jid); Contest contest = createContestFromModel(contestModel); if (isTypeChanged) { if (contestModel.type.equals(ContestType.STANDARD.name())) { contestConfigurationModel.typeConfig = new Gson().toJson(StandardContestTypeConfig.defaultConfig(contest)); } else if (contestModel.type.equals(ContestType.VIRTUAL.name())) { contestConfigurationModel.typeConfig = new Gson().toJson(VirtualContestTypeConfig.defaultConfig(contest)); } } if (isScopeChanged) { if (contestModel.scope.equals(ContestScope.PRIVATE.name())) { contestConfigurationModel.scopeConfig = new Gson().toJson(PrivateContestScopeConfig.defaultConfig(contest)); } else if (contestModel.scope.equals(ContestScope.PUBLIC.name())) { contestConfigurationModel.scopeConfig = new Gson().toJson(PublicContestScopeConfig.defaultConfig(contest)); } } if (isStyleChanged) { if (contestModel.style.equals(ContestStyle.ICPC.name())) { contestConfigurationModel.styleConfig = new Gson().toJson(ICPCContestStyleConfig.defaultConfig(contest)); } else if (contestModel.style.equals(ContestStyle.IOI.name())) { contestConfigurationModel.styleConfig = new Gson().toJson(IOIContestStyleConfig.defaultConfig(contest)); } try { ContestScoreboardModel contestScoreboardModel = contestScoreboardDao.findContestScoreboardByContestJidAndScoreboardType(contestModel.jid, ContestScoreboardType.OFFICIAL.name()); contestScoreboardDao.remove(contestScoreboardModel); contestScoreboardModel = contestScoreboardDao.findContestScoreboardByContestJidAndScoreboardType(contestModel.jid, ContestScoreboardType.FROZEN.name()); contestScoreboardDao.remove(contestScoreboardModel); } catch (NoResultException e) { throw new RuntimeException(e); } finally { // TODO recompute everything in this scoreboard include fronzen scoreboard ContestScoreboardModel contestScoreboardModel = new ContestScoreboardModel(); contestScoreboardModel.contestJid = contestModel.jid; contestScoreboardModel.type = ContestScoreboardType.OFFICIAL.name(); ScoreboardAdapter adapter = ScoreboardAdapters.fromContestStyle(style); ScoreboardState state = getContestStateByJid(contestModel.jid); ScoreboardContent content = adapter.computeScoreboardContent(contest, contestConfigurationModel.styleConfig, state, ImmutableList.of(), ImmutableMap.of()); Scoreboard scoreboard = adapter.createScoreboard(state, content); contestScoreboardModel.scoreboard = new Gson().toJson(scoreboard); contestScoreboardDao.persist(contestScoreboardModel, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); } } if (isTypeChanged || isScopeChanged || isStyleChanged) { contestConfigurationDao.edit(contestConfigurationModel, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); } } @Override public void updateContestConfigurationByContestJid(String contestJid, ContestTypeConfig typeConfig, ContestScopeConfig scopeConfig, ContestStyleConfig styleConfig) { ContestConfigurationModel contestConfigurationModel = contestConfigurationDao.findByContestJid(contestJid); ContestModel contestModel = contestDao.findByJid(contestJid); if (contestModel.type.equals(ContestType.STANDARD.name())) { contestConfigurationModel.typeConfig = new Gson().toJson(typeConfig); } else if (contestModel.type.equals(ContestType.VIRTUAL.name())) { contestConfigurationModel.typeConfig = new Gson().toJson(typeConfig); } if (contestModel.scope.equals(ContestScope.PRIVATE.name())) { contestConfigurationModel.scopeConfig = new Gson().toJson(scopeConfig); } else if (contestModel.scope.equals(ContestScope.PUBLIC.name())) { contestConfigurationModel.scopeConfig = new Gson().toJson(scopeConfig); } if (contestModel.style.equals(ContestStyle.ICPC.name())) { contestConfigurationModel.styleConfig = new Gson().toJson(styleConfig); } else if (contestModel.style.equals(ContestStyle.IOI.name())) { contestConfigurationModel.styleConfig = new Gson().toJson(styleConfig); } contestConfigurationDao.edit(contestConfigurationModel, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); } private Contest createContestFromModel(ContestModel contestModel) { return new Contest(contestModel.id, contestModel.jid, contestModel.name, contestModel.description, ContestType.valueOf(contestModel.type), ContestScope.valueOf(contestModel.scope), ContestStyle.valueOf(contestModel.style), new Date(contestModel.startTime), new Date(contestModel.endTime), new Date(contestModel.clarificationEndTime), contestModel.isExclusive, contestModel.isUsingScoreboard, contestModel.isIncognitoScoreboard, contestModel.requiresPassword); } }