增加对GYM题目的Vjudge支持
This commit is contained in:
parent
ff87c1898b
commit
af9811d036
|
@ -28,9 +28,10 @@ public class CorsConfig implements WebMvcConfigurer {
|
|||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// /api/public/img/** /api/public/file/**
|
||||
registry.addResourceHandler(Constants.File.IMG_API.getPath() + "**",Constants.File.FILE_API.getPath() + "**")
|
||||
registry.addResourceHandler(Constants.File.IMG_API.getPath() + "**", Constants.File.FILE_API.getPath() + "**")
|
||||
.addResourceLocations("file:" + Constants.File.USER_AVATAR_FOLDER.getPath() + File.separator,
|
||||
"file:" + Constants.File.MARKDOWN_FILE_FOLDER.getPath() + File.separator,
|
||||
"file:" + Constants.File.HOME_CAROUSEL_FOLDER.getPath() + File.separator);
|
||||
"file:" + Constants.File.HOME_CAROUSEL_FOLDER.getPath() + File.separator,
|
||||
"file:" + Constants.File.PROBLEM_FILE_FOLDER.getPath() + File.separator);
|
||||
}
|
||||
}
|
|
@ -67,14 +67,14 @@ public class CommonController {
|
|||
|
||||
|
||||
@GetMapping("/get-all-problem-tags")
|
||||
public CommonResult getAllProblemTagsList(@RequestParam(value = "oj",defaultValue = "ME")String oj) {
|
||||
public CommonResult getAllProblemTagsList(@RequestParam(value = "oj", defaultValue = "ME") String oj) {
|
||||
List<Tag> tagList;
|
||||
oj = oj.toUpperCase();
|
||||
if (oj.equals("ALL")){
|
||||
if (oj.equals("ALL")) {
|
||||
tagList = tagService.list();
|
||||
}else {
|
||||
} else {
|
||||
QueryWrapper<Tag> tagQueryWrapper = new QueryWrapper<>();
|
||||
tagQueryWrapper.eq( "oj", oj);
|
||||
tagQueryWrapper.eq("oj", oj);
|
||||
tagList = tagService.list(tagQueryWrapper);
|
||||
}
|
||||
if (tagList != null) {
|
||||
|
@ -110,6 +110,10 @@ public class CommonController {
|
|||
}
|
||||
}
|
||||
|
||||
if (OJ.equals("GYM")) { // GYM用与CF一样的编程语言列表
|
||||
OJ = "CF";
|
||||
}
|
||||
|
||||
QueryWrapper<Language> queryWrapper = new QueryWrapper<>();
|
||||
// 获取对应OJ支持的语言列表
|
||||
queryWrapper.eq(all != null && !all, "oj", OJ);
|
||||
|
@ -143,7 +147,7 @@ public class CommonController {
|
|||
if (codeTemplates != null && codeTemplates.size() > 0) {
|
||||
return CommonResult.successResponse(codeTemplates, "获取该题目的代码模板列表成功!");
|
||||
} else {
|
||||
return CommonResult.successResponse(codeTemplates,"获取该题目的代码模板列表失败!");
|
||||
return CommonResult.successResponse(codeTemplates, "获取该题目的代码模板列表失败!");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,22 @@ public class CFProblemStrategy extends ProblemStrategy {
|
|||
public static final String HOST = "https://codeforces.com";
|
||||
public static final String PROBLEM_URL = "/problemset/problem/%s/%s";
|
||||
|
||||
public String getJudgeName() {
|
||||
return JUDGE_NAME;
|
||||
}
|
||||
|
||||
public String getProblemUrl(String contestId, String problemNum) {
|
||||
return HOST + String.format(PROBLEM_URL, contestId, problemNum);
|
||||
}
|
||||
|
||||
public String getProblemSource(String html, String problemId, String contestId, String problemNum) {
|
||||
return String.format("<p>Problem:<a style='color:#1A5CC8' href='https://codeforces.com/problemset/problem/%s/%s'>%s</a></p><p>" +
|
||||
"Contest:" + ReUtil.get("(<a[^<>]+/contest/\\d+\">.+?</a>)", html, 1)
|
||||
.replace("/contest", HOST + "/contest")
|
||||
.replace("color: black", "color: #009688;") + "</p>",
|
||||
contestId, problemNum, getJudgeName() + "-" + problemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteProblemInfo getProblemInfo(String problemId, String author) throws Exception {
|
||||
|
||||
|
@ -40,12 +56,11 @@ public class CFProblemStrategy extends ProblemStrategy {
|
|||
throw new Exception("Codeforces的题号格式错误!");
|
||||
}
|
||||
|
||||
String url = HOST + String.format(PROBLEM_URL, contestId, problemNum);
|
||||
|
||||
String html = HttpUtil.get(url);
|
||||
String html = HttpUtil.get(getProblemUrl(contestId, problemNum));
|
||||
|
||||
Problem info = new Problem();
|
||||
info.setProblemId(JUDGE_NAME + "-" + problemId);
|
||||
info.setProblemId(getJudgeName() + "-" + problemId);
|
||||
|
||||
info.setTitle(ReUtil.get("<div class=\"title\">\\s*" + problemNum + "\\. ([\\s\\S]*?)</div>", html, 1).trim());
|
||||
|
||||
|
@ -70,12 +85,18 @@ public class CFProblemStrategy extends ProblemStrategy {
|
|||
tmpDesc = ReUtil.get("<div class=\"input-file\">([\\s\\S]*?)</div><div class=\"input-specification", html, 1);
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(tmpDesc)){
|
||||
if (StringUtils.isEmpty(tmpDesc)) {
|
||||
// 交互题
|
||||
tmpDesc = ReUtil.get("standard output\\s*</div>\\s*</div>\\s*<div>([\\s\\S]*?)</div>\\s*<div>\\s*<div class=\"section-title", html, 1);
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(tmpDesc)){
|
||||
if (StringUtils.isEmpty(tmpDesc)) {
|
||||
// 单单只有题面描述
|
||||
tmpDesc = ReUtil.get("standard output\\s*</div>\\s*</div>\\s*<div>([\\s\\S]*?)</div>",
|
||||
html, 1);
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(tmpDesc)) {
|
||||
tmpDesc = tmpDesc.replaceAll("\\$\\$\\$", "\\$")
|
||||
.replaceAll("src=\"../../", "src=\"" + HOST + "/")
|
||||
.trim();
|
||||
|
@ -85,20 +106,20 @@ public class CFProblemStrategy extends ProblemStrategy {
|
|||
|
||||
String inputDesc = ReUtil.get("<div class=\"section-title\">\\s*Input\\s*</div>([\\s\\S]*?)</div>\\s*<div class=\"output-specification\">", html, 1);
|
||||
|
||||
if (StringUtils.isEmpty(inputDesc)){
|
||||
if (StringUtils.isEmpty(inputDesc)) {
|
||||
inputDesc = ReUtil.get("<div class=\"section-title\">\\s*Interaction\\s*</div>([\\s\\S]*?)</div>\\s*<div class=\"sample-tests\">", html, 1);
|
||||
}
|
||||
if (StringUtils.isEmpty(inputDesc)){
|
||||
if (StringUtils.isEmpty(inputDesc)) {
|
||||
inputDesc = ReUtil.get("<div class=\"input-specification\">\\s*<div class=\"section-title\">\\s*Input\\s*</div>([\\s\\S]*?)</div>", html, 1);
|
||||
}
|
||||
if (!StringUtils.isEmpty(inputDesc)){
|
||||
if (!StringUtils.isEmpty(inputDesc)) {
|
||||
inputDesc = inputDesc.replaceAll("\\$\\$\\$", "\\$").trim();
|
||||
}
|
||||
|
||||
info.setInput(inputDesc);
|
||||
|
||||
String outputDesc = ReUtil.get("<div class=\"section-title\">\\s*Output\\s*</div>([\\s\\S]*?)</div>\\s*<div class=\"sample-tests\">", html, 1);
|
||||
if (!StringUtils.isEmpty(outputDesc)){
|
||||
if (!StringUtils.isEmpty(outputDesc)) {
|
||||
outputDesc = outputDesc.replaceAll("\\$\\$\\$", "\\$").trim();
|
||||
}
|
||||
info.setOutput(outputDesc);
|
||||
|
@ -134,10 +155,7 @@ public class CFProblemStrategy extends ProblemStrategy {
|
|||
|
||||
info.setIsRemote(true);
|
||||
|
||||
info.setSource(String.format("<p>Problem:<a style='color:#1A5CC8' href='https://codeforces.com/problemset/problem/%s/%s'>%s</a></p><p>" +
|
||||
"Contest:" + ReUtil.get("(<a[^<>]+/contest/\\d+\">.+?</a>)", html, 1).replace("/contest", HOST + "/contest")
|
||||
.replace("color: black", "color: #009688;") + "</p>",
|
||||
contestId, problemNum, JUDGE_NAME + "-" + problemId));
|
||||
info.setSource(getProblemSource(html, problemId, contestId, problemNum));
|
||||
|
||||
info.setType(0)
|
||||
.setAuth(1)
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package top.hcode.hoj.crawler.problem;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import top.hcode.hoj.pojo.entity.Problem;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/11/6 11:35
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
public class GYMProblemStrategy extends CFProblemStrategy {
|
||||
|
||||
public static final String IMAGE_HOST = "https://codeforces.ml";
|
||||
|
||||
@Override
|
||||
public String getJudgeName() {
|
||||
return "GYM";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProblemUrl(String contestId, String problemNum) {
|
||||
String problemUrl = "/gym/%s/problem/%s";
|
||||
return HOST + String.format(problemUrl, contestId, problemNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProblemSource(String html, String problemId, String contestNum, String problemNum) {
|
||||
return String.format("<p>Problem:<a style='color:#1A5CC8' href='https://codeforces.com/gym/%s/problem/%s'>%s</a></p><p>" +
|
||||
"Contest:" + ReUtil.get("(<a[^<>]+/gym/\\d+\">.+?</a>)", html, 1)
|
||||
.replace("/gym", HOST + "/gym")
|
||||
.replace("color: black", "color: #009688;") + "</p>",
|
||||
contestNum, problemNum, getJudgeName() + "-" + problemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteProblemInfo getProblemInfo(String problemId, String author) {
|
||||
try {
|
||||
return super.getProblemInfo(problemId, author);
|
||||
} catch (Exception ignored) {
|
||||
String contestNum = ReUtil.get("([0-9]+)[A-Z]{1}[0-9]{0,1}", problemId, 1);
|
||||
String problemNum = ReUtil.get("[0-9]+([A-Z]{1}[0-9]{0,1})", problemId, 1);
|
||||
return getPDFHtml(problemId, contestNum, problemNum, author);
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteProblemInfo getPDFHtml(String problemId, String contestNum, String problemNum, String author) {
|
||||
|
||||
Problem problem = new Problem();
|
||||
|
||||
String url = HOST + "/gym/" + contestNum;
|
||||
String html = HttpUtil.get(url);
|
||||
String regex = "<a href=\"\\/gym\\/" + contestNum + "\\/problem\\/" + problemNum
|
||||
+ "\"><!--\\s*-->([^<]+)(?:(?:.|\\s)*?<div){2}[^>]*>\\s*([^<]+)<\\/div>\\s*([\\d.]+)\\D*(\\d+)";
|
||||
|
||||
Matcher matcher = Pattern.compile(regex).matcher(html);
|
||||
matcher.find();
|
||||
|
||||
problem.setProblemId(getJudgeName() + "-" + problemId);
|
||||
problem.setTitle(matcher.group(1));
|
||||
problem.setTimeLimit((int) (Double.parseDouble(matcher.group(3)) * 1000));
|
||||
problem.setMemoryLimit(Integer.parseInt(matcher.group(4)));
|
||||
|
||||
problem.setSource(String.format("<p>Problem:<a style='color:#1A5CC8' href='https://codeforces.com/gym/%s/attachments'>%s</a></p><p>" +
|
||||
"Contest:" + ReUtil.get("(<a[^<>]+/gym/\\d+\">.+?</a>)", html, 1)
|
||||
.replace("/gym", HOST + "/gym")
|
||||
.replace("color: black", "color: #009688;") + "</p>",
|
||||
contestNum, getJudgeName() + "-" + problemId));
|
||||
|
||||
|
||||
regex = "\\/gym\\/" + contestNum + "\\/attachments\\/download\\S*?\\.pdf";
|
||||
|
||||
matcher = Pattern.compile(regex).matcher(html);
|
||||
matcher.find();
|
||||
|
||||
String pdfURI;
|
||||
try {
|
||||
String fileName = IdUtil.fastSimpleUUID() + ".pdf";
|
||||
String filePath = Constants.File.PROBLEM_FILE_FOLDER.getPath() + File.separator + fileName;
|
||||
HttpUtil.downloadFile(IMAGE_HOST + matcher.group(0), filePath);
|
||||
pdfURI = Constants.File.FILE_API.getPath() + fileName;
|
||||
} catch (Exception e1) {
|
||||
pdfURI = HOST + matcher.group(0);
|
||||
}
|
||||
String description = "<p><a style='color:#3091f2' href=\"" + pdfURI + "\">Click here to download the PDF file.</a></p>";
|
||||
problem.setDescription(description);
|
||||
problem.setType(0)
|
||||
.setIsRemote(true)
|
||||
.setAuth(1)
|
||||
.setAuthor(author)
|
||||
.setOpenCaseResult(false)
|
||||
.setIsRemoveEndBlank(false)
|
||||
.setDifficulty(1); // 默认为中等
|
||||
return new RemoteProblemInfo().setProblem(problem).setTagList(null);
|
||||
}
|
||||
}
|
|
@ -82,6 +82,11 @@ public class RemoteJudgeReceiver {
|
|||
boolean isNeedAccountRejudge = remoteOJName.equals(Constants.RemoteOJ.POJ.getName())
|
||||
&& isHasSubmitIdRemoteReJudge;
|
||||
|
||||
String remoteOJAccountType = remoteOJName; // GYM与CF共用账号
|
||||
if (remoteOJName.equals(Constants.RemoteOJ.GYM.getName())) {
|
||||
remoteOJAccountType = Constants.RemoteOJ.CODEFORCES.getName();
|
||||
}
|
||||
|
||||
ToJudge toJudge = new ToJudge();
|
||||
toJudge.setJudge(judge)
|
||||
.setTryAgainNum(tryAgainNum)
|
||||
|
@ -101,7 +106,7 @@ public class RemoteJudgeReceiver {
|
|||
QueryWrapper<RemoteJudgeAccount> remoteJudgeAccountQueryWrapper = new QueryWrapper<>();
|
||||
remoteJudgeAccountQueryWrapper
|
||||
.eq("status", true)
|
||||
.eq("oj", remoteOJName)
|
||||
.eq("oj", remoteOJAccountType)
|
||||
.last("for update"); // 开启悲观锁
|
||||
|
||||
List<RemoteJudgeAccount> remoteJudgeAccountList = remoteJudgeAccountService.list(remoteJudgeAccountQueryWrapper);
|
||||
|
@ -165,7 +170,7 @@ public class RemoteJudgeReceiver {
|
|||
} else {
|
||||
|
||||
if (isNeedAccountRejudge) {
|
||||
if (StringUtils.isEmpty(judge.getVjudgeUsername())){
|
||||
if (StringUtils.isEmpty(judge.getVjudgeUsername())) {
|
||||
// poj以往的账号丢失了,那么只能重新从头到尾提交
|
||||
remoteJudgeDispatcher.sendTask(judge, token, remoteJudgeProblem, isContest,
|
||||
tryAgainNum + 1, false);
|
||||
|
@ -173,9 +178,9 @@ public class RemoteJudgeReceiver {
|
|||
}
|
||||
|
||||
QueryWrapper<RemoteJudgeAccount> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("oj", remoteOJName).eq("username", judge.getVjudgeUsername());
|
||||
queryWrapper.eq("oj", remoteOJAccountType).eq("username", judge.getVjudgeUsername());
|
||||
int count = remoteJudgeAccountService.count(queryWrapper);
|
||||
if (count == 0){
|
||||
if (count == 0) {
|
||||
// poj以往的账号丢失了,那么只能重新从头到尾提交
|
||||
remoteJudgeDispatcher.sendTask(judge, token, remoteJudgeProblem, isContest,
|
||||
tryAgainNum + 1, false);
|
||||
|
|
|
@ -564,6 +564,9 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
|||
case "POJ":
|
||||
problemStrategy = new POJProblemStrategy();
|
||||
break;
|
||||
case "GYM":
|
||||
problemStrategy = new GYMProblemStrategy();
|
||||
break;
|
||||
default:
|
||||
throw new Exception("未知的OJ的名字,暂时不支持!");
|
||||
}
|
||||
|
@ -581,7 +584,11 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
|||
boolean addProblemResult = problemMapper.insert(problem) == 1;
|
||||
// 为新的其它oj题目添加对应的language
|
||||
QueryWrapper<Language> languageQueryWrapper = new QueryWrapper<>();
|
||||
languageQueryWrapper.eq("oj", OJName);
|
||||
if (OJName.equals("GYM")) {
|
||||
languageQueryWrapper.eq("oj", "CF");
|
||||
} else {
|
||||
languageQueryWrapper.eq("oj", OJName);
|
||||
}
|
||||
List<Language> OJLanguageList = languageService.list(languageQueryWrapper);
|
||||
List<ProblemLanguage> problemLanguageList = new LinkedList<>();
|
||||
for (Language language : OJLanguageList) {
|
||||
|
|
|
@ -59,6 +59,7 @@ public class Constants {
|
|||
public enum RemoteOJ {
|
||||
HDU("HDU"),
|
||||
CODEFORCES("CF"),
|
||||
GYM("GYM"),
|
||||
POJ("POJ");
|
||||
|
||||
private final String name;
|
||||
|
@ -165,6 +166,8 @@ public class Constants {
|
|||
|
||||
MARKDOWN_FILE_FOLDER("/hoj/file/md"),
|
||||
|
||||
PROBLEM_FILE_FOLDER("/hoj/file/problem"),
|
||||
|
||||
CONTEST_TEXT_PRINT_FOLDER("/hoj/file/contest_print"),
|
||||
|
||||
IMG_API("/api/public/img/"),
|
||||
|
|
|
@ -74,7 +74,7 @@ public class JudgeController {
|
|||
|
||||
HashMap<String, Object> res = new HashMap<>();
|
||||
|
||||
res.put("version", "1.6.0");
|
||||
res.put("version", "1.7.0");
|
||||
res.put("currentTime", new Date());
|
||||
res.put("judgeServerName", name);
|
||||
res.put("cpu", Runtime.getRuntime().availableProcessors());
|
||||
|
|
|
@ -63,8 +63,12 @@ public class RemoteJudgeToSubmit {
|
|||
// 将使用的账号放回对应列表
|
||||
UpdateWrapper<RemoteJudgeAccount> remoteJudgeAccountUpdateWrapper = new UpdateWrapper<>();
|
||||
remoteJudgeAccountUpdateWrapper.set("status", true)
|
||||
.eq("oj", remoteJudge)
|
||||
.eq("username", username);
|
||||
if (remoteJudge.equals("GYM")) {
|
||||
remoteJudgeAccountUpdateWrapper.eq("oj", "CF");
|
||||
} else {
|
||||
remoteJudgeAccountUpdateWrapper.eq("oj", remoteJudge);
|
||||
}
|
||||
boolean isOk = remoteJudgeAccountService.update(remoteJudgeAccountUpdateWrapper);
|
||||
if (!isOk) {
|
||||
log.error("远程判题:修正账号为可用状态失败----------->{}", "username:" + username + ",password:" + password);
|
||||
|
@ -85,13 +89,17 @@ public class RemoteJudgeToSubmit {
|
|||
null);
|
||||
log.error("网络错误---------------->获取不到提交ID");
|
||||
return;
|
||||
}else {
|
||||
} else {
|
||||
// 对POJ特殊 需要一直保持提交和获取结果时账号唯一,所以需要特别过滤
|
||||
if (!remoteJudge.equals(Constants.RemoteJudge.POJ_JUDGE.getName())) {
|
||||
UpdateWrapper<RemoteJudgeAccount> remoteJudgeAccountUpdateWrapper = new UpdateWrapper<>();
|
||||
remoteJudgeAccountUpdateWrapper.set("status", true)
|
||||
.eq("oj", remoteJudge)
|
||||
.eq("username", username);
|
||||
if (remoteJudge.equals("GYM")) {
|
||||
remoteJudgeAccountUpdateWrapper.eq("oj", "CF");
|
||||
} else {
|
||||
remoteJudgeAccountUpdateWrapper.eq("oj", remoteJudge);
|
||||
}
|
||||
boolean isOk = remoteJudgeAccountService.update(remoteJudgeAccountUpdateWrapper);
|
||||
if (!isOk) {
|
||||
log.error("远程判题:修正账号为可用状态失败----------->{}", "username:" + username + ",password:" + password);
|
||||
|
|
|
@ -26,8 +26,7 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
|
|||
public static final String SUBMIT_URL = "problemset/submit";
|
||||
public static final String SUBMISSION_RESULT_URL = "api/user.status?handle=%s&from=1&count=1000";
|
||||
public static final String CE_INFO_URL = "data/submitSource";
|
||||
private List<HttpCookie> cookies = new LinkedList<>();
|
||||
|
||||
protected List<HttpCookie> cookies = new LinkedList<>();
|
||||
|
||||
private static final Map<String, Constants.Judge> statusMap = new HashMap<String, Constants.Judge>() {{
|
||||
put("FAILED", Constants.Judge.STATUS_SUBMITTED_FAILED);
|
||||
|
@ -204,8 +203,12 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
|
|||
return MapUtil.builder(new HashMap<String, Object>()).put("status", response.getStatus()).map();
|
||||
}
|
||||
|
||||
protected String getSubmitUrl(String contestNum) {
|
||||
return IMAGE_HOST + SUBMIT_URL;
|
||||
}
|
||||
|
||||
public void submitCode(String contestId, String problemID, String languageID, String code) {
|
||||
String csrfToken = getCsrfToken(IMAGE_HOST + SUBMIT_URL);
|
||||
String csrfToken = getCsrfToken(getSubmitUrl(contestId));
|
||||
HashMap<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("csrf_token", csrfToken);
|
||||
paramMap.put("_tta", 594);
|
||||
|
@ -219,7 +222,7 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
|
|||
paramMap.put("source", code + getRandomBlankString());
|
||||
paramMap.put("sourceCodeConfirmed", true);
|
||||
paramMap.put("doNotShowWarningAgain", "on");
|
||||
HttpRequest request = HttpUtil.createPost(IMAGE_HOST + SUBMIT_URL + "?csrf_token=" + csrfToken);
|
||||
HttpRequest request = HttpUtil.createPost(getSubmitUrl(contestId) + "?csrf_token=" + csrfToken);
|
||||
request.form(paramMap);
|
||||
request.cookie(this.cookies);
|
||||
request.header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36");
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package top.hcode.hoj.remoteJudge.task.Impl;
|
||||
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/11/6 11:17
|
||||
* @Description:
|
||||
*/
|
||||
public class GYMJudge extends CodeForcesJudge {
|
||||
@Override
|
||||
protected String getSubmitUrl(String contestNum) {
|
||||
return IMAGE_HOST + "gym/" + contestNum + "/submit";
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package top.hcode.hoj.remoteJudge.task;
|
||||
|
||||
import top.hcode.hoj.remoteJudge.task.Impl.CodeForcesJudge;
|
||||
import top.hcode.hoj.remoteJudge.task.Impl.GYMJudge;
|
||||
import top.hcode.hoj.remoteJudge.task.Impl.HduJudge;
|
||||
import top.hcode.hoj.remoteJudge.task.Impl.POJJudge;
|
||||
import top.hcode.hoj.util.Constants;
|
||||
|
@ -17,6 +18,8 @@ public class RemoteJudgeFactory {
|
|||
return new CodeForcesJudge();
|
||||
case POJ_JUDGE:
|
||||
return new POJJudge();
|
||||
case GYM_JUDGE:
|
||||
return new GYMJudge();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ public class Constants {
|
|||
|
||||
CF_JUDGE("CF"),
|
||||
|
||||
GYM_JUDGE("GYM"),
|
||||
|
||||
POJ_JUDGE("POJ"),
|
||||
|
||||
HDU_REMOTE_JUDGE_ACCOUNT("Hdu Remote Judge Account"),
|
||||
|
|
|
@ -153,6 +153,7 @@ export const REMOTE_OJ = [
|
|||
{name:'HDU',key:"HDU"},
|
||||
{name:"Codeforces",key:"CF"},
|
||||
{name:"POJ",key:"POJ"},
|
||||
{name:"GYM",key:"GYM"},
|
||||
]
|
||||
|
||||
export const CONTEST_STATUS = {
|
||||
|
|
|
@ -1108,7 +1108,7 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
if (!this.problem.examples.length) {
|
||||
if (!this.problem.examples.length && !this.problem.isRemote) {
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Problem_Examples') +
|
||||
' ' +
|
||||
|
|
|
@ -153,64 +153,73 @@
|
|||
</div>
|
||||
|
||||
<div id="problem-content">
|
||||
<p class="title">{{ $t('m.Description') }}</p>
|
||||
<p
|
||||
class="content markdown-body"
|
||||
v-html="problemData.problem.description"
|
||||
v-katex
|
||||
v-highlight
|
||||
></p>
|
||||
<p class="title">{{ $t('m.Input') }}</p>
|
||||
<p
|
||||
class="content markdown-body"
|
||||
v-html="problemData.problem.input"
|
||||
v-katex
|
||||
v-highlight
|
||||
></p>
|
||||
<template v-if="problemData.problem.description">
|
||||
<p class="title">{{ $t('m.Description') }}</p>
|
||||
<p
|
||||
class="content markdown-body"
|
||||
v-html="problemData.problem.description"
|
||||
v-katex
|
||||
v-highlight
|
||||
></p>
|
||||
</template>
|
||||
|
||||
<p class="title">{{ $t('m.Output') }}</p>
|
||||
<p
|
||||
class="content markdown-body"
|
||||
v-html="problemData.problem.output"
|
||||
v-katex
|
||||
v-highlight
|
||||
></p>
|
||||
<template v-if="problemData.problem.input">
|
||||
<p class="title">{{ $t('m.Input') }}</p>
|
||||
<p
|
||||
class="content markdown-body"
|
||||
v-html="problemData.problem.input"
|
||||
v-katex
|
||||
v-highlight
|
||||
></p>
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-for="(example, index) of problemData.problem.examples"
|
||||
:key="index"
|
||||
>
|
||||
<div class="flex-container example">
|
||||
<div class="example-input">
|
||||
<p class="title">
|
||||
{{ $t('m.Sample_Input') }} {{ index + 1 }}
|
||||
<a
|
||||
class="copy"
|
||||
v-clipboard:copy="example.input"
|
||||
v-clipboard:success="onCopy"
|
||||
v-clipboard:error="onCopyError"
|
||||
>
|
||||
<i class="el-icon-document-copy"></i>
|
||||
</a>
|
||||
</p>
|
||||
<pre>{{ example.input }}</pre>
|
||||
</div>
|
||||
<div class="example-output">
|
||||
<p class="title">
|
||||
{{ $t('m.Sample_Output') }} {{ index + 1 }}
|
||||
<a
|
||||
class="copy"
|
||||
v-clipboard:copy="example.output"
|
||||
v-clipboard:success="onCopy"
|
||||
v-clipboard:error="onCopyError"
|
||||
>
|
||||
<i class="el-icon-document-copy"></i>
|
||||
</a>
|
||||
</p>
|
||||
<pre>{{ example.output }}</pre>
|
||||
<template v-if="problemData.problem.output">
|
||||
<p class="title">{{ $t('m.Output') }}</p>
|
||||
<p
|
||||
class="content markdown-body"
|
||||
v-html="problemData.problem.output"
|
||||
v-katex
|
||||
v-highlight
|
||||
></p>
|
||||
</template>
|
||||
|
||||
<template v-if="problemData.problem.examples">
|
||||
<div
|
||||
v-for="(example, index) of problemData.problem.examples"
|
||||
:key="index"
|
||||
>
|
||||
<div class="flex-container example">
|
||||
<div class="example-input">
|
||||
<p class="title">
|
||||
{{ $t('m.Sample_Input') }} {{ index + 1 }}
|
||||
<a
|
||||
class="copy"
|
||||
v-clipboard:copy="example.input"
|
||||
v-clipboard:success="onCopy"
|
||||
v-clipboard:error="onCopyError"
|
||||
>
|
||||
<i class="el-icon-document-copy"></i>
|
||||
</a>
|
||||
</p>
|
||||
<pre>{{ example.input }}</pre>
|
||||
</div>
|
||||
<div class="example-output">
|
||||
<p class="title">
|
||||
{{ $t('m.Sample_Output') }} {{ index + 1 }}
|
||||
<a
|
||||
class="copy"
|
||||
v-clipboard:copy="example.output"
|
||||
v-clipboard:success="onCopy"
|
||||
v-clipboard:error="onCopyError"
|
||||
>
|
||||
<i class="el-icon-document-copy"></i>
|
||||
</a>
|
||||
</p>
|
||||
<pre>{{ example.output }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="problemData.problem.hint">
|
||||
<p class="title">{{ $t('m.Hint') }}</p>
|
||||
|
@ -1289,7 +1298,10 @@ export default {
|
|||
}
|
||||
},
|
||||
isCFProblem() {
|
||||
if (this.problemID.indexOf('CF-') == 0) {
|
||||
if (
|
||||
this.problemID.indexOf('CF-') == 0 ||
|
||||
this.problemID.indexOf('GYM-') == 0
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
<vxe-table-column
|
||||
field="problemId"
|
||||
:title="$t('m.Problem_ID')"
|
||||
width="100"
|
||||
width="150"
|
||||
show-overflow
|
||||
></vxe-table-column>
|
||||
|
||||
|
|
BIN
judger/SandBox
BIN
judger/SandBox
Binary file not shown.
Loading…
Reference in New Issue