Accept Merge Request #121 增加比赛提交相关的代码和样式 : (virusdefender-dev -> dev)

Merge Request: 增加比赛提交相关的代码和样式
Created By: @virusdefender
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/121
This commit is contained in:
virusdefender
2015-08-23 19:40:56 +08:00
18 changed files with 535 additions and 33 deletions

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contest', '0004_remove_contestproblem_difficulty'),
]
operations = [
migrations.CreateModel(
name='ContestSubmission',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('total_submission_number', models.IntegerField(default=1)),
('ac', models.BooleanField()),
('total_time', models.IntegerField(default=0)),
('contest', models.ForeignKey(to='contest.Contest')),
('problem', models.ForeignKey(to='contest.ContestProblem')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'contest_submission',
},
),
]

View File

@@ -58,3 +58,20 @@ class ContestProblemTestCase(models.Model):
class Meta: class Meta:
db_table = "contest_problem_test_case" db_table = "contest_problem_test_case"
class ContestSubmission(models.Model):
"""
用于保存比赛提交和排名的一些数据,加快检索速度
"""
user = models.ForeignKey(User)
problem = models.ForeignKey(ContestProblem)
contest = models.ForeignKey(Contest)
total_submission_number = models.IntegerField(default=1)
# 这道题是 AC 还是没过
ac = models.BooleanField()
# 总的时间用于acm 类型的,也需要保存罚时
total_time = models.IntegerField(default=0)
class Meta:
db_table = "contest_submission"

View File

@@ -263,6 +263,32 @@ def contest_page(request, contest_id):
return render(request, "oj/contest/contest_index.html", {"contest": contest}) return render(request, "oj/contest/contest_index.html", {"contest": contest})
def contest_problem_page(request, contest_id, contest_problem_id):
try:
Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except ContestProblem.DoesNotExist:
return error_page(request, u"比赛题目不存在")
return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem,
"samples": json.loads(contest_problem.samples)})
@check_user_contest_permission
def contest_problems_list_page(request, contest_id):
try:
contest_problems = ContestProblem.objects.filter(contest=Contest.objects.get(id=contest_id)).order_by("sort_index")
except Contest.DoesNotExist:
return error_page(request, u"比赛题目不存在")
# 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
"announcements": announcements,
"contest": {"id": contest_id}})
def contest_list_page(request, page=1): def contest_list_page(request, page=1):
# 正常情况 # 正常情况
contests = Contest.objects.filter(visible=True) contests = Contest.objects.filter(visible=True)
@@ -298,10 +324,13 @@ def contest_list_page(request, page=1):
# 右侧的公告列表 # 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
# 系统当前时间
now = datetime.datetime.now()
return render(request, "oj/contest/contest_list.html", return render(request, "oj/contest/contest_list.html",
{"contests": current_page, "page": int(page), {"contests": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "announcements": announcements, "keyword": keyword, "announcements": announcements,
"join": join, "now": now}) "join": join})

View File

@@ -1,10 +1,13 @@
# coding=utf-8 # coding=utf-8
import logging import logging
import redis import redis
from judge.judger_controller.settings import redis_config from judge.judger_controller.settings import redis_config
from judge.judger.result import result from judge.judger.result import result
from submission.models import Submission from submission.models import Submission
from problem.models import Problem from problem.models import Problem
from contest.models import ContestProblem, Contest, ContestSubmission
logger = logging.getLogger("app_info") logger = logging.getLogger("app_info")
@@ -22,17 +25,68 @@ class MessageQueue(object):
submission = Submission.objects.get(id=submission_id) submission = Submission.objects.get(id=submission_id)
except Submission.DoesNotExist: except Submission.DoesNotExist:
logger.warning("Submission does not exist, submission_id: " + submission_id) logger.warning("Submission does not exist, submission_id: " + submission_id)
pass continue
if submission.result == result["accepted"]: if submission.result == result["accepted"] and not submission.contest_id:
# 更新题目的 ac 计数器 # 更新普通题目的 ac 计数器
try: try:
problem = Problem.objects.get(id=submission.problem_id) problem = Problem.objects.get(id=submission.problem_id)
problem.total_accepted_number += 1 problem.total_accepted_number += 1
problem.save() problem.save()
except Problem.DoesNotExist: except Problem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id) logger.warning("Submission problem does not exist, submission_id: " + submission_id)
pass # 普通题目的话,到这里就结束了
continue
# 能运行到这里的都是比赛题目
try:
contest = Contest.objects.get(id=submission.contest_id)
contest_problem = ContestProblem.objects.get(contest=contest, id=submission.problem_id)
except Contest.DoesNotExist:
logger.warning("Submission contest does not exist, submission_id: " + submission_id)
continue
except ContestProblem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
continue
try:
contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest,
problem_id=contest_problem.id)
if submission.result == result["accepted"]:
# 避免这道题已经 ac 了,但是又重新提交了一遍
if not contest_submission.ac:
# 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时
logger.debug(contest.start_time)
logger.debug(submission.create_time)
logger.debug((submission.create_time - contest.start_time).total_seconds())
contest_submission.total_time += int((submission.create_time - contest.start_time).total_seconds() / 60)
# 标记为已经通过
contest_submission.ac = True
# contest problem ac 计数器加1
contest_problem.total_accepted_number += 1
else:
# 如果这个提交是错误的就罚时20分钟
contest_submission.ac = False
contest_submission.total_time += 20
contest_submission.save()
contest_problem.save()
except ContestSubmission.DoesNotExist:
# 第一次提交
is_ac = submission.result == result["accepted"]
# 增加题目总提交数计数器
contest_problem.total_submit_number += 1
if is_ac:
total_time = 0
# 增加题目总的ac数计数器
contest_problem.total_accepted_number += 1
contest_problem.save()
else:
# 没过罚时20分钟
total_time = 20
ContestSubmission.objects.create(user_id=submission.user_id, contest=contest, problem=contest_problem,
ac=is_ac, total_time=total_time)
logger.debug("Start message queue") logger.debug("Start message queue")

View File

@@ -16,7 +16,7 @@ from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
from admin.views import AdminTemplateView from admin.views import AdminTemplateView
from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView
from submission.views import SubmissionAPIView, SubmissionAdminAPIView from submission.views import SubmissionAPIView, SubmissionAdminAPIView, ContestSubmissionAPIView
from monitor.views import QueueLengthMonitorAPIView from monitor.views import QueueLengthMonitorAPIView
@@ -40,7 +40,14 @@ urlpatterns = [
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"), url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"), url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"), url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^contest/(?P<contest_id>\d+)/problem/(?P<contest_problem_id>\d+)/$', "contest.views.contest_problem_page",
name="contest_problem_page"),
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"), url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^contest/(?P<contest_id>\d+)/problems/$', "contest.views.contest_problems_list_page",
name="contest_problems_list_page"),
url(r'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page", url(r'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page",
name="announcement_page"), name="announcement_page"),
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"),
@@ -60,6 +67,10 @@ urlpatterns = [
url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"),
url(r'^problem/(?P<problem_id>\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page", url(r'^problem/(?P<problem_id>\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page",
name="problem_my_submissions_page"), name="problem_my_submissions_page"),
url(r'^contest/(?P<contest_id>\d+)/problem/(?P<contest_problem_id>\d+)/my_submissions/$',
"submission.views.contest_problem_my_submissions_list_page", name="contest_problem_my_submissions_list_page"),
url(r'^my_submission/(?P<submission_id>\w+)/$', "submission.views.my_submission", name="my_submission_page"), url(r'^my_submission/(?P<submission_id>\w+)/$', "submission.views.my_submission", name="my_submission_page"),
url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(),
@@ -71,5 +82,6 @@ urlpatterns = [
url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"), url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"),
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"), url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"), url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_api"),
] ]

View File

@@ -88,3 +88,7 @@ li.list-group-item {
outline: none; outline: none;
} }
.contest-tab{
margin-bottom: 5px;
}

View File

@@ -56,7 +56,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
var counter = 0; var counter = 0;
function getResult() { function getResult() {
if(counter++ > 10){ if (counter++ > 10) {
hideLoading(); hideLoading();
bsAlert("抱歉,服务器可能出现了故障,请稍后到我的提交列表中查看"); bsAlert("抱歉,服务器可能出现了故障,请稍后到我的提交列表中查看");
counter = 0; counter = 0;
@@ -88,12 +88,32 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
} }
$("#submit-code-button").click(function () { $("#submit-code-button").click(function () {
var problemId = window.location.pathname.split("/")[2];
var code = codeEditor.getValue(); var code = codeEditor.getValue();
if (location.href.indexOf("contest") > -1) {
var problemId = location.pathname.split("/")[4];
var contestId = location.pathname.split("/")[2];
var url = "/api/contest/submission/";
var data = {
problem_id: problemId,
language: language,
code: code,
contest_id: contestId
};
}
else {
var problemId = window.location.pathname.split("/")[2];
var url = "/api/submission/";
var data = {
problem_id: problemId,
language: language,
code: code
};
}
showLoading(); showLoading();
if(!code.trim()){ if (!code.trim()) {
bsAlert("请填写代码!"); bsAlert("请填写代码!");
hideLoading(); hideLoading();
return false; return false;
@@ -101,15 +121,12 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
$("#result").html(""); $("#result").html("");
$.ajax({ $.ajax({
beforeSend: csrfTokenHeader, beforeSend: csrfTokenHeader,
url: "/api/submission/", url: url,
method: "post", method: "post",
data: JSON.stringify({ data: JSON.stringify(data),
problem_id: problemId,
language: language,
code: codeEditor.getValue()
}),
contentType: "application/json", contentType: "application/json",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
@@ -118,7 +135,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
setTimeout(getResult, 2000); setTimeout(getResult, 2000);
} }
else { else {
bs_alert(data.data); bsAlert(data.data);
hideLoading(); hideLoading();
} }
} }
@@ -127,11 +144,11 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
}); });
$.ajax({ $.ajax({
url : "/api/user/", url: "/api/user/",
method: "get", method: "get",
dataType: "json", dataType: "json",
success: function(data){ success: function (data) {
if(data.code){ if (data.code) {
$("#submit-code-button").attr("disabled", "disabled"); $("#submit-code-button").attr("disabled", "disabled");
$("#result").html('<div class="alert alert-danger" role="alert"><div class="alert-link">请先<a href="/login/" target="_blank">登录</a>!</div> </div>'); $("#result").html('<div class="alert alert-danger" role="alert"><div class="alert-link">请先<a href="/login/" target="_blank">登录</a>!</div> </div>');
} }

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('submission', '0003_auto_20150821_1654'),
]
operations = [
migrations.RemoveField(
model_name='submission',
name='is_counted',
),
]

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('submission', '0004_remove_submission_is_counted'),
]
operations = [
migrations.AddField(
model_name='submission',
name='contest_id',
field=models.IntegerField(null=True, blank=True),
),
]

View File

@@ -11,6 +11,7 @@ class Submission(models.Model):
result = models.IntegerField(default=result["waiting"]) result = models.IntegerField(default=result["waiting"])
language = models.IntegerField() language = models.IntegerField()
code = models.TextField() code = models.TextField()
contest_id = models.IntegerField(blank=True, null=True)
problem_id = models.IntegerField(db_index=True) problem_id = models.IntegerField(db_index=True)
# 这个字段可能存储很多数据 比如编译错误、系统错误的时候,存储错误原因字符串 # 这个字段可能存储很多数据 比如编译错误、系统错误的时候,存储错误原因字符串
# 正常运行的时候存储 lrun 的判题结果比如cpu时间内存之类的 # 正常运行的时候存储 lrun 的判题结果比如cpu时间内存之类的
@@ -18,7 +19,6 @@ class Submission(models.Model):
accepted_answer_time = models.IntegerField(blank=True, null=True) accepted_answer_time = models.IntegerField(blank=True, null=True)
# 这个字段只有在题目是accepted 的时候才会用到,比赛题目的提交可能还会有得分等信息,存储在这里面 # 这个字段只有在题目是accepted 的时候才会用到,比赛题目的提交可能还会有得分等信息,存储在这里面
accepted_answer_info = models.TextField(blank=True, null=True) accepted_answer_info = models.TextField(blank=True, null=True)
is_counted = models.BooleanField(default=False)
class Meta: class Meta:
db_table = "submission" db_table = "submission"

View File

@@ -20,3 +20,10 @@ class SubmissionSerializer(serializers.ModelSerializer):
def _get_submission_user(self, obj): def _get_submission_user(self, obj):
return User.objects.get(id=obj.user_id).username return User.objects.get(id=obj.user_id).username
class CreateContestSubmissionSerializer(serializers.Serializer):
contest_id = serializers.IntegerField()
problem_id = serializers.IntegerField()
language = serializers.IntegerField()
code = serializers.CharField(max_length=3000)

View File

@@ -1,21 +1,25 @@
# coding=utf-8 # coding=utf-8
import json import json
import redis
import redis
from django.shortcuts import render from django.shortcuts import render
from django.core.paginator import Paginator
from rest_framework.views import APIView from rest_framework.views import APIView
from judge.judger.result import result
from judge.judger_controller.tasks import judge from judge.judger_controller.tasks import judge
from judge.judger_controller.settings import redis_config from judge.judger_controller.settings import redis_config
from account.decorators import login_required from account.decorators import login_required
from account.models import SUPER_ADMIN from account.models import SUPER_ADMIN
from contest.decorators import check_user_contest_permission
from problem.models import Problem from problem.models import Problem
from contest.models import Contest, ContestProblem
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
from .models import Submission from .models import Submission
from .serializers import CreateSubmissionSerializer, SubmissionSerializer from .serializers import CreateSubmissionSerializer, SubmissionSerializer, CreateContestSubmissionSerializer
from django.core.paginator import Paginator
class SubmissionAPIView(APIView): class SubmissionAPIView(APIView):
@@ -79,6 +83,23 @@ def problem_my_submissions_list_page(request, problem_id):
{"submissions": submissions, "problem": problem}) {"submissions": submissions, "problem": problem})
@login_required
def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id):
try:
Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except Problem.DoesNotExist:
return error_page(request, u"比赛问题不存在")
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by(
"-create_time"). \
values("id", "result", "create_time", "accepted_answer_time", "language")
return render(request, "oj/contest/my_submissions_list.html",
{"submissions": submissions, "contest_problem": contest_problem})
@login_required @login_required
def my_submission(request, submission_id): def my_submission(request, submission_id):
try: try:
@@ -136,3 +157,43 @@ def my_submission_list_page(request, page=1):
return render(request, "oj/submission/my_submissions_list.html", return render(request, "oj/submission/my_submissions_list.html",
{"submissions": current_page, "page": int(page), {"submissions": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20}) "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20})
class ContestSubmissionAPIView(APIView):
# @check_user_contest_permission
def post(self, request):
"""
创建比赛的提交
---
request_serializer: ConestSubmissionSerializer
"""
serializer = CreateContestSubmissionSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
contest = Contest.objects.get(id=data["contest_id"])
except Contest.DoesNotExist:
return error_response(u"比赛不存在")
try:
problem = ContestProblem.objects.get(contest=contest, id=data["problem_id"])
# 更新题目提交计数器
problem.total_submit_number += 1
problem.save()
except Problem.DoesNotExist:
return error_response(u"题目不存在")
submission = Submission.objects.create(user_id=request.user.id, language=int(data["language"]),
contest_id=contest.id, code=data["code"], problem_id=problem.id)
try:
judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception:
return error_response(u"提交判题任务失败")
# 增加redis 中判题队列长度的计数器
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
r.incr("judge_queue_length")
return success_response({"submission_id": submission.id})
else:
return serializer_invalid_response(serializer)

View File

@@ -0,0 +1,100 @@
{% extends 'oj_base.html' %}
{% block body %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li>
<li role="presentation"><a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/my_submissions/">我的提交</a></li>
</ul>
<h2 class="text-center">{{ contest_problem.title }}</h2>
<p class="text-muted text-center">发布时间 : {{ contest_problem.create_time }}&nbsp;&nbsp;
时间限制 : {{ contest_problem.time_limit }}ms&nbsp;&nbsp;
内存限制 : {{ contest_problem.memory_limit }}M
</p>
<div>
<div class="problem-section">
<label class="problem-label">描述</label>
<p class="problem-detail">{{ contest_problem.description|safe }}</p>
</div>
<div class="problem-section">
<label class="problem-label">输入</label>
<p class="problem-detail">{{ contest_problem.input_description }}</p>
</div>
<div class="problem-section">
<label class="problem-label">输出</label>
<p class="problem-detail">{{ contest_problem.output_description }}k</p>
</div>
{% for item in samples %}
<div class="problem-section">
<label class="problem-label">样例输入{{ forloop.counter }}</label>
<pre>
{{ item.input }}</pre>
</div>
<div class="problem-section">
<label class="problem-label">样例输出{{ forloop.counter }}</label>
<pre>
{{ item.output }}</pre>
</div>
{% endfor %}
{% if problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<p class="problem-detail">{{ contest_problem.hint|safe }}</p>
</div>
{% endif %}
{% if contest_problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<p class="problem-detail">{{ contest_problem.hint|safe }}</p>
</div>
{% endif %}
<div>
<label>选择语言</label>
<div>
<label class="radio-inline">
<input type="radio" name="language" value="1" checked> C (gcc 4.8)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="2"> C++ (g++ 4.3)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="3"> Java (jre 1.7)
</label>
</div>
</div>
<div id="code-field">
<label class="problem-label">提交代码</label>
<textarea id="code-editor"></textarea>
</div>
<hr>
<div id="submit-code">
<button type="button" class="btn btn-primary" id="submit-code-button">
提交代码
</button>
<img src="/static/img/loading.gif" id="loading-gif">
</div>
<div id="result">
</div>
<hr>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/problem/problem.js"></script>
{% endblock %}

View File

@@ -0,0 +1,81 @@
{% extends "oj_base.html" %}
{% block body %}
{% load problem %}
<div class="container main">
<div class="row">
<div class="col-lg-12 contest-tab">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation">
<a href="/contest/{{ contest.id }}/">比赛详情</a>
</li>
<li role="presentation" class="active">
<a href="/contest/{{ contest.id }}/problems/">题目</a>
</li>
<li role="presentation">
<a href="/contest/{{ contest.id }}/submissions/">提交</a>
</li>
<li role="presentation">
<a href="/contest/{{ contest.id }}/rank/">排名</a>
</li>
</ul>
</div>
<div class="col-lg-9">
<div>
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th>#</th>
<th>题目</th>
<th>通过率</th>
</tr>
</thead>
<tbody>
{% for item in contest_problems %}
<tr>
<th><span class="glyphicon glyphicon-ok ac-flag"></span></th>
<th scope="row"><a
href="/contest/{{ item.contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a>
</th>
<td><a href="/contest/{{ item.contest.id }}/problem/{{ item.id }}/">{{ item.title }}</a>
</td>
<td>{{ item|accepted_radio }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<nav>
<ul class="pager">
{% if previous_page %}
<li class="previous"><a
href="/problems/{{ previous_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页</a></li>
{% endif %}
{% if next_page %}
<li class="next"><a
href="/problems/{{ next_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">下一页 <span
aria-hidden="true">&rarr;</span></a></li>
{% endif %}
</ul>
</nav>
</div>
</div>
<div class="col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/problem/problem_list.js"></script>
{% endblock %}

View File

@@ -0,0 +1,52 @@
{% extends 'oj_base.html' %}
{% block body %}
{% load submission %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li>
<li role="presentation" class="active">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/my_submissions/">
我的提交</a>
</li>
</ul>
<h2 class="text-center">{{ contest_problem.title }}</h2>
<p class="text-muted text-center">发布时间: {{ contest_problem.create_time }}&nbsp;&nbsp;
时间限制: {{ contest_problem.time_limit }}ms&nbsp;&nbsp;
内存限制: {{ contest_problem.memory_limit }}M</p>
<table class="table table-bordered">
<thead>
<tr class="" success>
<th>#</th>
<th>提交时间</th>
<th>结果</th>
<th>运行时间</th>
<th>语言</th>
</tr>
</thead>
<tbody>
{% for item in submissions %}
<tr class="{{ item.result|translate_result_class }}">
<th scope="row"><a href="/my_submission/{{ item.id }}/">{{ forloop.counter }}</a></th>
<td>{{ item.create_time }}</td>
<td>{{ item.result|translate_result }}</td>
<td>
{% if item.accepted_answer_time %}
{{ item.accepted_answer_time }}ms
{% else %}
--
{% endif %}
</td>
<td>
{{ item.language|translate_language }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@@ -90,7 +90,7 @@
<body> <body>
<div id="header"> <div id="header">
<span id="name">qduoj</span> <span id="name">qduoj</span>
<a href="/problems/">题目</a>&nbsp;&nbsp;<a href="#">比赛</a>&nbsp;&nbsp;<a href="#">小组</a> <a href="/problems/">题目</a>&nbsp;&nbsp;<a href="/contests/">比赛</a>&nbsp;&nbsp;<a href="#">小组</a>
</div> </div>
<div id="fullpage"> <div id="fullpage">

View File

@@ -32,7 +32,7 @@
<th scope="row"><a href="/problem/{{ item.id }}/">{{ item.id }}</a></th> <th scope="row"><a href="/problem/{{ item.id }}/">{{ item.id }}</a></th>
<td><a href="/problem/{{ item.id }}/">{{ item.title }}</a></td> <td><a href="/problem/{{ item.id }}/">{{ item.title }}</a></td>
<td>{{ item.difficulty }}</td> <td>{{ item.difficulty }}</td>
<td>{{ item|accepted_radio }}%</td> <td>{{ item|accepted_radio }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -2,13 +2,13 @@
def get_problem_accepted_radio(problem): def get_problem_accepted_radio(problem):
if problem.total_accepted_number: if problem.total_submit_number:
return int((problem.total_accepted_number * 100) / problem.total_submit_number) return str(int((problem.total_accepted_number * 100) / problem.total_submit_number)) \
return 0 + "% (" + str(problem.total_accepted_number) + "/" + str(problem.total_submit_number) + ")"
return "0%"
from django import template from django import template
register = template.Library() register = template.Library()
register.filter("accepted_radio", get_problem_accepted_radio) register.filter("accepted_radio", get_problem_accepted_radio)