Skip to content

Commit

Permalink
feat: export ranking to excel
Browse files Browse the repository at this point in the history
  • Loading branch information
kastnerorz committed Nov 13, 2019
1 parent 248f6b6 commit 4537f38
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 22 deletions.
21 changes: 21 additions & 0 deletions src/main/java/cn/kastner/oj/controller/ContestRestController.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
import cn.kastner.oj.query.ContestQuery;
import cn.kastner.oj.query.RankingQuery;
import cn.kastner.oj.service.ContestService;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

@RestController
Expand Down Expand Up @@ -167,6 +171,23 @@ public RankingDTO getRanking(@PathVariable String id, RankingQuery query) throws
return contestService.getRanking(id, query);
}

@GetMapping("/{id}/ranking/export")
public void exportRanking(
@PathVariable String id, RankingQuery query, HttpServletResponse response)
throws ContestException {
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
String fileName = contestService.findById(id).getName() + "排名";
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

Workbook workbook = contestService.exportRanking(id, query);
try (OutputStream os = response.getOutputStream()) {
workbook.write(os);
} catch (IOException e) {
throw new ContestException(ContestException.EXPORT_ERROR);
}
}

@PatchMapping("/{id}/status")
@PreAuthorize("hasAnyRole('ADMIN', 'STUFF')")
public ContestDTO setContestStatus(@PathVariable String id, @RequestParam ContestOption option)
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/cn/kastner/oj/domain/ContestProblem.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class ContestProblem {
@Transient
private List<TimeCost> timeList = new ArrayList<>();

@OneToOne
@OneToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
private Submission firstSubmission;

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/cn/kastner/oj/domain/RankingUser.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class RankingUser {

private Long time = 0L;

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "rankingUser")
@OneToMany(fetch = FetchType.EAGER, mappedBy = "rankingUser")
@Fetch(FetchMode.SUBSELECT)
@OrderBy("id DESC ")
@JsonIgnore
Expand Down
6 changes: 2 additions & 4 deletions src/main/java/cn/kastner/oj/domain/TimeCost.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class TimeCost {
@Column(length = 40)
private String id = UUID.randomUUID().toString();

@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
private ContestProblem contestProblem;

Expand All @@ -30,11 +30,9 @@ public class TimeCost {

private Boolean firstPassed = false;

private Boolean frozen = false;

private Double score = 0.0;

@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
private RankingUser rankingUser;

Expand Down
6 changes: 6 additions & 0 deletions src/main/java/cn/kastner/oj/exception/ContestException.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class ContestException extends AppException {

public static final String BAD_CONTEST_STATUS = "不合法的比赛状态";

public static final String EXPORT_ERROR = "导出错误";

public ContestException(String message) {
super(message);
switch (message) {
Expand Down Expand Up @@ -97,6 +99,10 @@ public ContestException(String message) {
this.code = -16;
this.status = HttpStatus.FORBIDDEN;
break;
case EXPORT_ERROR:
this.code = -17;
this.status = HttpStatus.INTERNAL_SERVER_ERROR;
break;
default:
this.code = -1;
this.status = HttpStatus.INTERNAL_SERVER_ERROR;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public static RankingUser create(User user, Contest contest, Group group) {
TimeCost timeCost = new TimeCost();
timeCost.setContestProblem(contestProblem);
timeCost.setRankingUser(rankingUser);
timeCost.setFrozen(false);
timeCostList.add(timeCost);
}
rankingUser.setTimeList(timeCostList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ public interface ContestProblemRepository
void deleteAllByProblemAndContest(Iterable<Problem> problems, Contest contest);

void deleteAllByContest(Contest contest);

Integer countByContest(Contest contest);
}
3 changes: 3 additions & 0 deletions src/main/java/cn/kastner/oj/service/ContestService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import cn.kastner.oj.exception.ProblemException;
import cn.kastner.oj.query.ContestQuery;
import cn.kastner.oj.query.RankingQuery;
import org.apache.poi.ss.usermodel.Workbook;

import java.util.List;

Expand Down Expand Up @@ -49,4 +50,6 @@ void deleteProblems(List<String> problemIdList, String contestId)
void joinContest(String id, String password) throws ContestException;

RankingDTO getRanking(String id, RankingQuery query) throws ContestException;

Workbook exportRanking(String id, RankingQuery query) throws ContestException;
}
104 changes: 90 additions & 14 deletions src/main/java/cn/kastner/oj/service/impl/ContestServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
import cn.kastner.oj.service.ContestService;
import cn.kastner.oj.util.CommonUtil;
import cn.kastner.oj.util.DTOMapper;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -243,7 +247,9 @@ public List<RankingUserDTO> addUsersByGroups(List<String> groupIdList, String co
for (Group group : groupList) {
for (User user : group.getUserSet()) {
if (!userSet.contains(user)) {
addRankingUserList.add(RankingUserFactory.create(user, contest, group));
RankingUser rankingUser = RankingUserFactory.create(user, contest, group);
timeCostRepository.saveAll(rankingUser.getTimeList());
addRankingUserList.add(rankingUser);
}
}
}
Expand Down Expand Up @@ -387,7 +393,6 @@ public List<ProblemDTO> addProblems(List<String> problemIdList, String contestId
TimeCost timeCost = new TimeCost();
timeCost.setContestProblem(contestProblem);
timeCost.setRankingUser(rankingUser);
timeCost.setFrozen(true);
addTimeCostList.add(timeCost);
}

Expand Down Expand Up @@ -436,7 +441,6 @@ public void addProblem(String problemId, String contestId, Integer score)
TimeCost timeCost = new TimeCost();
timeCost.setContestProblem(contestProblem);
timeCost.setRankingUser(rankingUser);
timeCost.setFrozen(true);

addTimeCostList.add(timeCost);

Expand Down Expand Up @@ -606,20 +610,13 @@ public RankingDTO getRanking(String id, RankingQuery query) throws ContestExcept
return rankingDTO;
} else if (contest.getStatus() == ContestStatus.ENDED) {
Set<RankingUser> rankingUserList = rankingUserRepository.findByContest(contest);

if (!rankingUserList.isEmpty()) {
rankingUserList = filterWithQuery(rankingUserList, query);
}
for (RankingUser ru : rankingUserList) {
ru.setTimeList(timeCostRepository.findByRankingUser(ru));
}
if (!rankingUserList.isEmpty()) {
if (null != query.getGroupId()) {
rankingUserList = rankingUserList.stream().filter(
rankingUserDTO -> query.getGroupId().equals(rankingUserDTO.getGroupId())
).collect(Collectors.toSet());
} else if (null != query.getTeacherId()) {
rankingUserList = rankingUserList.stream().filter(
rankingUserDTO -> query.getTeacherId().equals(rankingUserDTO.getTeacherId())
).collect(Collectors.toSet());
}
}
contest.setRankingUserList(rankingUserList);
return mapper.contestToRankingDTO(contest);
} else {
Expand All @@ -628,6 +625,85 @@ public RankingDTO getRanking(String id, RankingQuery query) throws ContestExcept

}

private Set<RankingUser> filterWithQuery(
Set<RankingUser> rankingUserList, RankingQuery query) {
if (null != query.getGroupId()) {
return rankingUserList.stream().filter(
rankingUserDTO -> query.getGroupId().equals(rankingUserDTO.getGroupId())
).collect(Collectors.toSet());
} else if (null != query.getTeacherId()) {
return rankingUserList.stream().filter(
rankingUserDTO -> query.getTeacherId().equals(rankingUserDTO.getTeacherId())
).collect(Collectors.toSet());
}
return rankingUserList;
}

@Override
public Workbook exportRanking(String id, RankingQuery query) throws ContestException {
Contest contest =
contestRepository
.findById(id)
.orElseThrow(() -> new ContestException(ContestException.NO_SUCH_CONTEST));
Set<RankingUser> rankingUserSet = rankingUserRepository.findByContest(contest);
rankingUserSet = filterWithQuery(rankingUserSet, query);
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("排名信息");

int rowNum = 0;
Row header = sheet.createRow(rowNum++);
int headerColumnNum = 0;
header.createCell(headerColumnNum++).setCellValue("排名");
header.createCell(headerColumnNum++).setCellValue("学号");
header.createCell(headerColumnNum++).setCellValue("姓名");
if (contest.getJudgeType() == JudgeType.IMMEDIATELY) {
header.createCell(headerColumnNum++).setCellValue("提交数");
header.createCell(headerColumnNum++).setCellValue("通过数");
} else {
header.createCell(headerColumnNum++).setCellValue("总分");
}
header.createCell(headerColumnNum++).setCellValue("指导教师");
header.createCell(headerColumnNum++).setCellValue("小组/班级");
for (int i = 'A', j = 0; j < contestProblemRepository.countByContest(contest); i++, j++) {
header.createCell(headerColumnNum++).setCellValue(String.format("%c", i));
}

for (RankingUser rankingUser : rankingUserSet) {
Row row = sheet.createRow(rowNum++);
int columnNum = 0;
row.createCell(columnNum++).setCellValue(rankingUser.getRankingNumber());
row.createCell(columnNum++).setCellValue(rankingUser.getUser().getStudentNumber());
row.createCell(columnNum++).setCellValue(rankingUser.getUser().getName());
if (contest.getJudgeType() == JudgeType.IMMEDIATELY) {
row.createCell(columnNum++).setCellValue(rankingUser.getAcceptCount());
row.createCell(columnNum++).setCellValue(rankingUser.getSubmitCount());
} else {
row.createCell(columnNum++).setCellValue(rankingUser.getScore());
}
Optional<User> teacherOptional = userRepository.findById(rankingUser.getTeacherId());
if (teacherOptional.isPresent()) {
row.createCell(columnNum++).setCellValue(teacherOptional.get().getName());
} else {
columnNum++;
}
Optional<Group> groupOptional = groupRepository.findById(rankingUser.getGroupId());
if (groupOptional.isPresent()) {
row.createCell(columnNum++).setCellValue(groupOptional.get().getName());
} else {
columnNum++;
}
List<TimeCost> timeCostList = timeCostRepository.findByRankingUser(rankingUser);
for (TimeCost timeCost : timeCostList) {
if (contest.getJudgeType() == JudgeType.IMMEDIATELY) {
row.createCell(columnNum++).setCellValue(timeCost.getPassed() ? 1 : 0);
} else {
row.createCell(columnNum++).setCellValue(timeCost.getScore());
}
}
}
return workbook;
}

private void requirePassword(Contest contest) throws ContestException {
if (ContestType.SECRET_WITH_PASSWORD.equals(contest.getContestType())
&& contest.getPassword() == null) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/cn/kastner/oj/task/RankingComputingTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void computeRank() {
i--;
}
}
redisTemplate.opsForValue().set("rankingUserList:" + contest.getId(), mapper.toRankingUserDTOs(rankingUserList));
redisTemplate.opsForValue().set("rankingUserList:" + contest.getId(), mapper.toRankingUserDTOs(rankingUserRepository.saveAll(rankingUserList)));
}
}
}

0 comments on commit 4537f38

Please sign in to comment.