mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-12-28 16:12:13 +00:00
初步完成编辑VJ题目的功能
This commit is contained in:
parent
cf2fc9df1a
commit
bbaebc4d70
30
contest/migrations/0014_auto_20160310_1917.py
Normal file
30
contest/migrations/0014_auto_20160310_1917.py
Normal file
@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.1 on 2016-03-10 11:17
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0013_auto_20151017_1511'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='vj_name',
|
||||
field=models.CharField(blank=True, max_length=20, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='vj_problem_url',
|
||||
field=models.URLField(blank=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contestproblem',
|
||||
name='test_case_id',
|
||||
field=models.CharField(blank=True, max_length=40, null=True),
|
||||
),
|
||||
]
|
20
contest/migrations/0015_contestproblem_spj.py
Normal file
20
contest/migrations/0015_contestproblem_spj.py
Normal file
@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.1 on 2016-03-10 12:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0014_auto_20160310_1917'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='spj',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
20
contest/migrations/0016_contestproblem_vj_problem_id.py
Normal file
20
contest/migrations/0016_contestproblem_vj_problem_id.py
Normal file
@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.1 on 2016-03-10 12:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0015_contestproblem_spj'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='vj_problem_id',
|
||||
field=models.CharField(blank=True, max_length=40, null=True),
|
||||
),
|
||||
]
|
@ -67,6 +67,11 @@ class ContestProblem(AbstractProblem):
|
||||
sort_index = models.CharField(max_length=30)
|
||||
# 是否已经公开了题目,防止重复公开
|
||||
is_public = models.BooleanField(default=False)
|
||||
spj = models.BooleanField(default=False)
|
||||
# 如果是vj题目才会使用
|
||||
vj_name = models.CharField(max_length=20, blank=True, null=True)
|
||||
vj_problem_id = models.CharField(max_length=40, blank=True, null=True)
|
||||
vj_problem_url = models.URLField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "contest_problem"
|
||||
|
@ -105,9 +105,26 @@ class EditContestProblemSerializer(serializers.Serializer):
|
||||
hint = serializers.CharField(max_length=3000, allow_blank=True)
|
||||
visible = serializers.BooleanField()
|
||||
sort_index = serializers.CharField(max_length=30)
|
||||
score = serializers.IntegerField(required=False, default=0)
|
||||
|
||||
|
||||
class ContestPasswordVerifySerializer(serializers.Serializer):
|
||||
contest_id = serializers.IntegerField()
|
||||
password = serializers.CharField(max_length=30)
|
||||
password = serializers.CharField(max_length=30)
|
||||
|
||||
|
||||
class CreateContestVJProblemSerializer(serializers.Serializer):
|
||||
contest_id = serializers.IntegerField()
|
||||
oj = serializers.CharField(max_length=30)
|
||||
url = serializers.URLField()
|
||||
|
||||
|
||||
class EditContestVJProblemSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
title = serializers.CharField(max_length=50)
|
||||
description = serializers.CharField(max_length=10000)
|
||||
input_description = serializers.CharField(max_length=10000)
|
||||
output_description = serializers.CharField(max_length=10000)
|
||||
samples = ContestProblemSampleSerializer()
|
||||
hint = serializers.CharField(max_length=3000, allow_blank=True)
|
||||
visible = serializers.BooleanField()
|
||||
sort_index = serializers.CharField(max_length=30)
|
@ -1,7 +1,8 @@
|
||||
# coding=utf-8
|
||||
import json
|
||||
import datetime
|
||||
import redis
|
||||
import logging
|
||||
import requests
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.db import IntegrityError
|
||||
@ -28,7 +29,11 @@ from .decorators import check_user_contest_permission
|
||||
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
|
||||
CreateContestProblemSerializer, ContestProblemSerializer,
|
||||
ContestPasswordVerifySerializer,
|
||||
EditContestProblemSerializer)
|
||||
EditContestProblemSerializer, CreateContestVJProblemSerializer,
|
||||
EditContestVJProblemSerializer)
|
||||
|
||||
|
||||
logger = logging.getLogger("app_info")
|
||||
|
||||
|
||||
class ContestAdminAPIView(APIView):
|
||||
@ -278,12 +283,98 @@ class ContestProblemAdminAPIView(APIView):
|
||||
return paginate(request, contest_problems, ContestProblemSerializer)
|
||||
|
||||
|
||||
class ContestVJProblemAPIView(APIView):
|
||||
def post(self, request):
|
||||
serializer = CreateContestVJProblemSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
contest = Contest.objects.get(id=data["contest_id"])
|
||||
if request.user.admin_type != SUPER_ADMIN:
|
||||
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||
if contest not in contest_set:
|
||||
return error_response(u"比赛不存在")
|
||||
except Contest.DoesNotExist:
|
||||
return error_response(u"比赛不存在")
|
||||
|
||||
url = "http://127.0.0.1:8000/problem/?oj=%s&url=%s" % (data["oj"], data["url"])
|
||||
try:
|
||||
r = requests.get(url).json()
|
||||
except Exception as e:
|
||||
logger.error("Exception %s when fetching url: %s" % (str(e), url))
|
||||
return error_response(u"请求VJ API失败")
|
||||
if r["code"] == 0:
|
||||
# 爬取完成
|
||||
if r["data"]["status"] == 0:
|
||||
vj_problem = r["data"]
|
||||
try:
|
||||
ContestProblem.objects.get(contest=contest, vj_problem_id=vj_problem["id"])
|
||||
return error_response(u"该VJ题目已经存在")
|
||||
except ContestProblem.DoesNotExist:
|
||||
pass
|
||||
ContestProblem.objects.create(contest=contest,
|
||||
title=vj_problem["title"],
|
||||
description=vj_problem["description"],
|
||||
input_description=vj_problem["input_description"],
|
||||
output_description=vj_problem["output_description"],
|
||||
samples=json.dumps(vj_problem["samples"]),
|
||||
time_limit=vj_problem["time_limit"],
|
||||
memory_limit=vj_problem["memory_limit"],
|
||||
created_by=request.user,
|
||||
is_public=True,
|
||||
spj=vj_problem["spj"],
|
||||
hint=vj_problem["hint"],
|
||||
sort_index="_vj", vj_name=data["oj"],
|
||||
vj_problem_id=vj_problem["id"],
|
||||
vj_problem_url=data["url"])
|
||||
return success_response(r)
|
||||
# 正在爬取
|
||||
elif r["data"]["status"] == 1:
|
||||
return success_response({"status": 1})
|
||||
# 失败
|
||||
elif r["data"]["status"] == 2:
|
||||
return error_response(u"VJ 题目爬取失败")
|
||||
else:
|
||||
logger.error("VJ API return %s" % json.dumps(r))
|
||||
return error_response(u"请求VJ API失败, 返回信息: " + r["data"])
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def put(self, request):
|
||||
serializer = EditContestVJProblemSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
|
||||
try:
|
||||
contest_problem = ContestProblem.objects.get(id=data["id"])
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_response(u"该比赛题目不存在!")
|
||||
contest = Contest.objects.get(id=contest_problem.contest_id)
|
||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
||||
return error_response(u"比赛不存在")
|
||||
contest_problem.title = data["title"]
|
||||
contest_problem.description = data["description"]
|
||||
contest_problem.input_description = data["input_description"]
|
||||
contest_problem.output_description = data["output_description"]
|
||||
contest_problem.samples = json.dumps(data["samples"])
|
||||
contest_problem.hint = data["hint"]
|
||||
contest_problem.visible = data["visible"]
|
||||
contest_problem.sort_index = data["sort_index"]
|
||||
contest_problem.last_update_time = now()
|
||||
contest_problem.save()
|
||||
return success_response(ContestProblemSerializer(contest_problem).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
class MakeContestProblemPublicAPIView(APIView):
|
||||
@super_admin_required
|
||||
def post(self, request):
|
||||
problem_id = request.data.get("problem_id", -1)
|
||||
try:
|
||||
problem = ContestProblem.objects.get(id=problem_id)
|
||||
if problem.is_public:
|
||||
return error_response(u"题目已经公开")
|
||||
problem.is_public = True
|
||||
problem.save()
|
||||
except ContestProblem.DoesNotExist:
|
||||
@ -296,8 +387,6 @@ class MakeContestProblemPublicAPIView(APIView):
|
||||
hint=problem.hint, created_by=problem.created_by,
|
||||
time_limit=problem.time_limit, memory_limit=problem.memory_limit,
|
||||
visible=False, difficulty=-1, source=problem.contest.title)
|
||||
problem.is_public = True
|
||||
problem.save()
|
||||
return success_response(u"创建成功")
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@ from announcement.views import AnnouncementAdminAPIView
|
||||
|
||||
from contest.views import (ContestAdminAPIView, ContestProblemAdminAPIView,
|
||||
ContestPasswordVerifyAPIView, ContestTimeAPIView,
|
||||
MakeContestProblemPublicAPIView)
|
||||
MakeContestProblemPublicAPIView, ContestVJProblemAPIView)
|
||||
|
||||
from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
|
||||
JoinGroupAPIView, JoinGroupRequestAdminAPIView, GroupPrometAdminAPIView)
|
||||
@ -65,6 +65,7 @@ urlpatterns = [
|
||||
|
||||
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
|
||||
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
|
||||
url(r'^api/admin/contest_vj_problem/$', ContestVJProblemAPIView.as_view(), name="contest_vj_problem_admin_api"),
|
||||
url(r'^api/admin/contest_problem/public/', MakeContestProblemPublicAPIView.as_view(),
|
||||
name="make_contest_problem_public"),
|
||||
url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"),
|
||||
|
@ -24,7 +24,7 @@ class AbstractProblem(models.Model):
|
||||
# 样例输入 可能会存储 json 格式的数据
|
||||
samples = models.TextField(blank=True)
|
||||
# 测试用例id 这个id 可以用来拼接得到测试用例的文件存储位置
|
||||
test_case_id = models.CharField(max_length=40)
|
||||
test_case_id = models.CharField(max_length=40, blank=True, null=True)
|
||||
# 提示
|
||||
hint = RichTextField(blank=True, null=True)
|
||||
# 创建时间
|
||||
|
@ -25,21 +25,23 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
|
||||
},
|
||||
|
||||
makeProblemPublic: function(problem){
|
||||
$.ajax({
|
||||
url: "/api/admin/contest_problem/public/",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: {"problem_id": problem.id},
|
||||
success: function(response){
|
||||
if(response.code){
|
||||
bsAlert(response.data);
|
||||
if(confirm("您确定要公开题目么? 请勿在比赛未结束的时候公开题目。")) {
|
||||
$.ajax({
|
||||
url: "/api/admin/contest_problem/public/",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: {"problem_id": problem.id},
|
||||
success: function (response) {
|
||||
if (response.code) {
|
||||
bsAlert(response.data);
|
||||
}
|
||||
else {
|
||||
problem.is_public = true;
|
||||
bsAlert("公开题目成功,现在处于隐藏状态,请添加标签难度等信息。");
|
||||
}
|
||||
}
|
||||
else{
|
||||
problem.is_public = true;
|
||||
alert("公开题目成功,现在处于隐藏状态,请添加标签难度等信息。");
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -107,10 +107,9 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
if (!data.data.length) {
|
||||
if (admin_type != 2)
|
||||
if (!data.data.length && admin_type != 2) {
|
||||
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
vm.allGroups = [];
|
||||
for (var i = 0; i < data.data.length; i++) {
|
||||
|
@ -8,18 +8,25 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
.on('submit', function (e) {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
e.preventDefault();
|
||||
if (!avalon.vmodels.testCaseUploader.uploaded) {
|
||||
bsAlert("你还没有上传测试数据!");
|
||||
return false;
|
||||
if(!vm.isVJ) {
|
||||
if (!avalon.vmodels.testCaseUploader.uploaded) {
|
||||
bsAlert("你还没有上传测试数据!");
|
||||
return false;
|
||||
}
|
||||
if (vm.timeLimit < 30 || vm.timeLimit > 5000) {
|
||||
bsAlert("保证时间限制是一个30-5000的合法整数");
|
||||
return false;
|
||||
}
|
||||
if (vm.memoryLimit < 32) {
|
||||
bsAlert("内存不得小于32M");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (avalon.vmodels.contestProblemDescriptionEditor.content == "") {
|
||||
bsAlert("题目描述不能为空!");
|
||||
return false;
|
||||
}
|
||||
if (vm.timeLimit < 30 || vm.timeLimit > 5000) {
|
||||
bsAlert("保证时间限制是一个30-5000的合法整数");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vm.samples.length == 0) {
|
||||
bsAlert("请至少添加一组样例!");
|
||||
return false;
|
||||
@ -55,6 +62,13 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
var alertContent = "题目创建成功";
|
||||
}
|
||||
|
||||
if(vm.isVJ) {
|
||||
var url = "/api/admin/contest_vj_problem/";
|
||||
}
|
||||
else {
|
||||
var url = "/api/admin/contest_problem/";
|
||||
}
|
||||
|
||||
for (var i = 0; i < vm.samples.$model.length; i++) {
|
||||
ajaxData.samples.push({
|
||||
input: vm.samples.$model[i].input,
|
||||
@ -64,7 +78,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/admin/contest_problem/",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: JSON.stringify(ajaxData),
|
||||
method: method,
|
||||
@ -99,6 +113,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
outputDescription: "",
|
||||
testCaseId: "",
|
||||
testCaseList: [],
|
||||
isVJ: false,
|
||||
|
||||
contestProblemDescriptionEditor: {
|
||||
editorId: "contest-problem-description-editor",
|
||||
@ -147,6 +162,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
vm.outputDescription = "";
|
||||
vm.testCaseId = "";
|
||||
vm.testCaseList = [];
|
||||
vm.isVJ = false;
|
||||
}
|
||||
|
||||
if (avalon.vmodels.admin.contestProblemStatus == "edit") {
|
||||
@ -170,8 +186,11 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
vm.visible = problem.visible;
|
||||
vm.inputDescription = problem.input_description;
|
||||
vm.outputDescription = problem.output_description;
|
||||
vm.score = problem.score;
|
||||
avalon.vmodels.testCaseUploader.setTestCase(problem.test_case_id);
|
||||
vm.isVJ = problem.test_case_id == null;
|
||||
// vj题目不需要上传数据
|
||||
if (!vm.isVJ) {
|
||||
avalon.vmodels.testCaseUploader.setTestCase(problem.test_case_id);
|
||||
}
|
||||
vm.samples = [];
|
||||
for (var i = 0; i < problem.samples.length; i++) {
|
||||
vm.samples.push({
|
||||
|
@ -29,14 +29,15 @@
|
||||
<div class="col-md-3">
|
||||
<div class="form-group"><label>时间限制(ms)</label>
|
||||
<input type="number" name="timeLimit" class="form-control" ms-duplex="timeLimit"
|
||||
data-error="请输入时间限制(保证是一个30-5000的合法整数)" required>
|
||||
data-error="请输入时间限制(保证是一个30-5000的合法整数)" required ms-attr-disabled="isVJ">
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group"><label>内存限制(MB)</label>
|
||||
<input type="number" name="memory" class="form-control" ms-duplex="memoryLimit"
|
||||
data-error="请输入内存限制(保证是一个合法整数)" required>
|
||||
<input type="number" name="memory" class="form-control"
|
||||
title="非VJ的题目内存低于512M可能会造成Java代码无法启动" ms-duplex="memoryLimit"
|
||||
data-error="请输入内存限制(保证是一个合法整数)" required ms-attr-disabled="isVJ">
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -96,7 +97,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
|
||||
<div ms-if="!isVJ">
|
||||
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label>提示</label>
|
||||
<ms:editor $id="contestProblemHintEditor" config="contestProblemHintEditor"></ms:editor>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr ms-repeat="problemList">
|
||||
<td>{{ el.sort_index }}</td>
|
||||
<td><span ms-if="el.vj_name" class="glyphicon glyphicon-send" title="Virtual Judge"></span> {{ el.sort_index }}</td>
|
||||
<td>{{ el.title }}</td>
|
||||
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
|
||||
<td ms-text="el.visible?'可见':'不可见'"></td>
|
||||
|
@ -6,12 +6,12 @@
|
||||
<div class="problem-section">
|
||||
<label class="problem-label">输入</label>
|
||||
|
||||
<p class="problem-detail">{{ problem.input_description }}</p>
|
||||
<p class="problem-detail">{{ problem.input_description|linebreaksbr }}</p>
|
||||
</div>
|
||||
<div class="problem-section">
|
||||
<label class="problem-label">输出</label>
|
||||
|
||||
<p class="problem-detail">{{ problem.output_description }}</p>
|
||||
<p class="problem-detail">{{ problem.output_description|linebreaksbr }}</p>
|
||||
</div>
|
||||
{% for item in samples %}
|
||||
<div class="problem-section">
|
||||
|
Loading…
Reference in New Issue
Block a user