Accept Merge Request #266 更新用户提交 ac 标志存储方法;使用新的生成比赛排名的方式 : (virusdefender-dev -> dev)

Merge Request: 更新用户提交 ac 标志存储方法;使用新的生成比赛排名的方式
Created By: @virusdefender
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/266
This commit is contained in:
virusdefender 2015-09-24 21:34:42 +08:00
commit 67adbcb3d1
21 changed files with 301 additions and 197 deletions

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import utils.models
class Migration(migrations.Migration):
dependencies = [
('account', '0005_user_problems_status'),
]
operations = [
migrations.AlterField(
model_name='user',
name='problems_status',
field=utils.models.JsonField(default={}),
),
]

View File

@ -2,6 +2,8 @@
from django.db import models from django.db import models
from django.contrib.auth.models import AbstractBaseUser from django.contrib.auth.models import AbstractBaseUser
from jsonfield import JSONField
class AdminGroup(models.Model): class AdminGroup(models.Model):
pass pass
@ -31,7 +33,9 @@ class User(AbstractBaseUser):
# 0代表不是管理员 1是普通管理员 2是超级管理员 # 0代表不是管理员 1是普通管理员 2是超级管理员
admin_type = models.IntegerField(default=0) admin_type = models.IntegerField(default=0)
# JSON字典用来表示该用户的问题的解决状态 1为ac2为正在进行 # JSON字典用来表示该用户的问题的解决状态 1为ac2为正在进行
problems_status = models.TextField(default="{}") problems_status = JSONField(default={})
# 找回密码用的token
# reset_password_token = models.CharField(max_length=40, blank=True, null=True)
USERNAME_FIELD = 'username' USERNAME_FIELD = 'username'
REQUIRED_FIELDS = [] REQUIRED_FIELDS = []

View File

@ -46,3 +46,9 @@ class EditUserSerializer(serializers.Serializer):
password = serializers.CharField(max_length=30, min_length=6, required=False, default=None) password = serializers.CharField(max_length=30, min_length=6, required=False, default=None)
email = serializers.EmailField(max_length=254) email = serializers.EmailField(max_length=254)
admin_type = serializers.IntegerField(default=0) admin_type = serializers.IntegerField(default=0)
class ApplyResetPasswordSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
email = serializers.EmailField()
captcha = serializers.CharField(max_length=4, min_length=4)

View File

@ -3,17 +3,20 @@ from django import http
from django.contrib import auth from django.contrib import auth
from django.shortcuts import render from django.shortcuts import render
from django.db.models import Q from django.db.models import Q
from django.conf import settings
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate, rand_str
from utils.captcha import Captcha from utils.captcha import Captcha
from .decorators import login_required from .decorators import login_required
from .models import User from .models import User
from .serializers import (UserLoginSerializer, UsernameCheckSerializer, from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
UserRegisterSerializer, UserChangePasswordSerializer, UserRegisterSerializer, UserChangePasswordSerializer,
EmailCheckSerializer, UserSerializer, EditUserSerializer) EmailCheckSerializer, UserSerializer, EditUserSerializer,
ApplyResetPasswordSerializer)
from .decorators import super_admin_required
class UserLoginAPIView(APIView): class UserLoginAPIView(APIView):
@ -150,6 +153,7 @@ class EmailCheckAPIView(APIView):
class UserAdminAPIView(APIView): class UserAdminAPIView(APIView):
@super_admin_required
def put(self, request): def put(self, request):
""" """
用户编辑json api接口 用户编辑json api接口
@ -181,6 +185,7 @@ class UserAdminAPIView(APIView):
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
@super_admin_required
def get(self, request): def get(self, request):
""" """
用户分页json api接口 用户分页json api接口
@ -222,8 +227,39 @@ class AccountSecurityAPIView(APIView):
username = request.GET.get("username", None) username = request.GET.get("username", None)
if username: if username:
try: try:
User.objects.get(username=username, admin_type__gt=0) user = User.objects.get(username=username)
except User.DoesNotExist: except User.DoesNotExist:
return success_response({"applied_captcha": False}) return success_response({"applied_captcha": True})
return success_response({"applied_captcha": True}) if user.admin_type > 0:
return success_response({"applied_captcha": True})
return success_response({"applied_captcha": False}) return success_response({"applied_captcha": False})
class ApplyResetPasswordAPIView(APIView):
def post(self, request):
serializer = ApplyResetPasswordSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
try:
user = User.objects.get(username=data["username"], email=data["email"])
except User.DoesNotExist:
return error_response(u"用户不存在")
user.reset_password_token = rand_str()
user.save()
# todo
email_template = open(settings.TEMPLATES[0]["DIRS"][0] + "utils/reset_password_email.html", "r").read()
email_template.replace("{{ username }}", user.username).replace("{{ link }}", "/reset_password/?token=" + user.reset_password_token)
return success_response(u"邮件发生成功")
else:
return serializer_invalid_response(serializer)
class ResetPasswordAPIView(APIView):
pass
def user_index_page(request, username):
return render(request, "oj/account/user_index.html")

View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
import utils.models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contest', '0010_auto_20150922_1703'),
]
operations = [
migrations.CreateModel(
name='ContestRank',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('total_submission_number', models.IntegerField(default=0)),
('total_ac_number', models.IntegerField(default=0)),
('total_time', models.IntegerField(default=0)),
('submission_info', utils.models.JsonField(default={})),
('contest', models.ForeignKey(to='contest.Contest')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -6,6 +6,8 @@ from account.models import User
from problem.models import AbstractProblem from problem.models import AbstractProblem
from group.models import Group from group.models import Group
from utils.models import RichTextField from utils.models import RichTextField
from jsonfield import JSONField
from judge.judger.result import result
GROUP_CONTEST = 0 GROUP_CONTEST = 0
@ -104,3 +106,66 @@ class ContestSubmission(models.Model):
class Meta: class Meta:
db_table = "contest_submission" db_table = "contest_submission"
class ContestRank(models.Model):
user = models.ForeignKey(User)
contest = models.ForeignKey(Contest)
total_submission_number = models.IntegerField(default=0)
total_ac_number = models.IntegerField(default=0)
# ac 的题目才要加到这个字段里面 = ac 时间 + 错误次数 * 20 * 60
# 没有 ac 的题目不计算罚时 单位是秒
total_time = models.IntegerField(default=0)
# 数据结构{23: {"is_ac": True, "ac_time": 8999, "error_number": 2, "is_first_ac": True}}
# key 是比赛题目的id
submission_info = JSONField(default={})
def update_rank(self, submission):
if not submission.contest_id or submission.contest_id != self.contest_id:
raise ValueError("Error submission type")
# 这道题以前提交过
if str(submission.problem_id) in self.submission_info:
info = self.submission_info[str(submission.problem_id)]
# 如果这道题目已经 ac 了就跳过
if info["is_ac"]:
return
self.total_submission_number += 1
if submission.result == result["accepted"]:
self.total_ac_number += 1
info["is_ac"] = True
info["ac_time"] = (submission.create_time - self.contest.start_time).total_seconds()
# 之前已经提交过,但是是错误的,这次提交是正确的。错误的题目不计入罚时
self.total_time += (info["ac_time"] + info["error_number"] * 20 * 60)
problem = ContestProblem.objects.get(id=submission.problem_id)
if problem.total_accepted_number == 0:
info["is_first_ac"] = True
else:
info["error_number"] += 1
info["is_ac"] = False
else:
# 第一次提交这道题目
self.total_submission_number += 1
info = {"is_ac": False, "ac_time": 0, "error_number": 0, "is_first_ac": False}
if submission.result == result["accepted"]:
self.total_ac_number += 1
info["is_ac"] = True
info["ac_time"] = (submission.create_time - self.contest.start_time).total_seconds()
self.total_time += info["ac_time"]
problem = ContestProblem.objects.get(id=submission.problem_id)
if problem.total_accepted_number == 0:
info["is_first_ac"] = True
else:
info["is_ac"] = False
info["error_number"] = 1
self.submission_info[str(submission.problem_id)] = info
self.save()

View File

@ -16,7 +16,8 @@ from utils.shortcuts import (serializer_invalid_response, error_response,
from account.models import SUPER_ADMIN, User from account.models import SUPER_ADMIN, User
from account.decorators import login_required from account.decorators import login_required
from group.models import Group from group.models import Group
from .models import Contest, ContestProblem, ContestSubmission, CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY from .models import (Contest, ContestProblem, ContestSubmission, CONTEST_ENDED,
CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank)
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST
from .decorators import check_user_contest_permission from .decorators import check_user_contest_permission
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
@ -334,18 +335,6 @@ def contest_problems_list_page(request, contest_id):
""" """
contest = Contest.objects.get(id=contest_id) contest = Contest.objects.get(id=contest_id)
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index") contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
submissions = ContestSubmission.objects.filter(user=request.user, contest=contest)
state = {}
for item in submissions:
state[item.problem_id] = item.ac
for item in contest_problems:
if item.id in state:
if state[item.id]:
item.state = 1
else:
item.state = 2
else:
item.state = 0
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems, return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
"contest": {"id": contest_id}}) "contest": {"id": contest_id}})
@ -391,81 +380,16 @@ def contest_list_page(request, page=1):
"keyword": keyword, "join": join}) "keyword": keyword, "join": join})
def _cmp(x, y):
if x["total_ac"] > y["total_ac"]:
return 1
elif x["total_ac"] < y["total_ac"]:
return -1
else:
if x["total_time"] < y["total_time"]:
return 1
else:
return -1
def get_the_formatted_time(seconds):
if not seconds:
return ""
result = str(seconds % 60)
if seconds % 60 < 10:
result = "0" + result
result = str((seconds % 3600) / 60) + ":" + result
if (seconds % 3600) / 60 < 10:
result = "0" + result
result = str(seconds / 3600) + ":" + result
if seconds / 3600 < 10:
result = "0" + result
return result
@check_user_contest_permission @check_user_contest_permission
def contest_rank_page(request, contest_id): def contest_rank_page(request, contest_id):
contest = Contest.objects.get(id=contest_id) contest = Contest.objects.get(id=contest_id)
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index") contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
r = redis.Redis(host=REDIS_CACHE["host"], port=REDIS_CACHE["port"], db=REDIS_CACHE["db"]) rank = ContestRank.objects.filter(contest_id=contest_id).order_by("-total_ac_number", "total_time")
if contest.real_time_rank:
# 更新rank
result = ContestSubmission.objects.filter(contest=contest).values("user_id"). \
annotate(total_submit=Sum("total_submission_number"))
for i in range(0, len(result)):
# 这个人所有的提交
submissions = ContestSubmission.objects.filter(user_id=result[i]["user_id"], contest_id=contest_id)
result[i]["submissions"] = {}
result[i]["problems"] = []
for problem in contest_problems:
try:
status = submissions.get(problem=problem)
result[i]["problems"].append({
"first_achieved": status.first_achieved,
"ac": status.ac,
"failed_number": status.total_submission_number,
"ac_time": get_the_formatted_time(status.ac_time)})
if status.ac:
result[i]["problems"][-1]["failed_number"] -= 1
except ContestSubmission.DoesNotExist:
result[i]["problems"].append({})
result[i]["total_ac"] = submissions.filter(ac=True).count()
user= User.objects.get(id=result[i]["user_id"])
result[i]["username"] = user.username
result[i]["real_name"] = user.real_name
result[i]["total_time"] = get_the_formatted_time(submissions.filter(ac=True).aggregate(total_time=Sum("total_time"))["total_time"])
result = sorted(result, cmp=_cmp, reverse=True)
r.set("contest_rank_" + contest_id, json.dumps(list(result)))
else:
# 从缓存读取排名信息
result = r.get("contest_rank_" + contest_id)
if result:
result = json.loads(result)
else:
result = []
return render(request, "oj/contest/contest_rank.html", return render(request, "oj/contest/contest_rank.html",
{"contest": contest, "contest_problems": contest_problems, {"rank": rank, "contest": contest,
"result": result, "contest_problems": contest_problems,
"auto_refresh": request.GET.get("auto_refresh", None) == "true", "auto_refresh": request.GET.get("auto_refresh", None) == "true",
"show_real_name": request.GET.get("show_real_name", None) == "true", "show_real_name": request.GET.get("show_real_name", None) == "true",})
"real_time_rank": contest.real_time_rank})
class ContestTimeAPIView(APIView): class ContestTimeAPIView(APIView):
@ -480,5 +404,4 @@ class ContestTimeAPIView(APIView):
return error_response(u"比赛不存在") return error_response(u"比赛不存在")
return success_response({"start": int((contest.start_time - now()).total_seconds() * 1000), return success_response({"start": int((contest.start_time - now()).total_seconds() * 1000),
"end": int((contest.end_time - now()).total_seconds() * 1000), "end": int((contest.end_time - now()).total_seconds() * 1000),
"status": contest.status}) "status": contest.status})

View File

@ -49,6 +49,13 @@ class ContestSubmissionAPIView(APIView):
judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception: except Exception:
return error_response(u"提交判题任务失败") return error_response(u"提交判题任务失败")
# 修改用户解题状态
problems_status = request.user.problems_status
if "contest_problems" not in problems_status:
problems_status["contest_problems"] = {}
problems_status["contest_problems"][str(data["problem_id"])] = 2
request.user.problems_status = problems_status
request.user.save()
# 增加redis 中判题队列长度的计数器 # 增加redis 中判题队列长度的计数器
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
r.incr("judge_queue_length") r.incr("judge_queue_length")

View File

@ -9,4 +9,5 @@ gunicorn
coverage coverage
django-extensions django-extensions
supervisor supervisor
pillow pillow
jsonfield

View File

@ -10,7 +10,7 @@ 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 from contest.models import ContestProblem, Contest, ContestSubmission, CONTEST_UNDERWAY, ContestRank
from account.models import User from account.models import User
logger = logging.getLogger("app_info") logger = logging.getLogger("app_info")
@ -25,12 +25,20 @@ class MessageQueue(object):
while True: while True:
submission_id = self.conn.blpop(self.queue, 0)[1] submission_id = self.conn.blpop(self.queue, 0)[1]
logger.debug("receive submission_id: " + submission_id) logger.debug("receive submission_id: " + submission_id)
try: try:
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)
continue continue
# 更新该用户的解题状态
try:
user = User.objects.get(pk=submission.user_id)
except User.DoesNotExist:
logger.warning("Submission user does not exist, submission_id: " + submission_id)
continue
if submission.result == result["accepted"] and not submission.contest_id: if submission.result == result["accepted"] and not submission.contest_id:
# 更新普通题目的 ac 计数器 # 更新普通题目的 ac 计数器
try: try:
@ -40,15 +48,10 @@ class MessageQueue(object):
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)
continue continue
# 更新该用户的解题状态
try: problems_status = user.problems_status
user = User.objects.get(pk=submission.user_id) problems_status["problems"][str(problem.id)] = 1
except User.DoesNotExist: user.problems_status = problems_status
logger.warning("Submission user does not exist, submission_id: " + submission_id)
continue
problems_status = json.loads(user.problems_status)
problems_status[str(problem.id)] = 1
user.problems_status = json.dumps(problems_status)
user.save() user.save()
# 普通题目的话,到这里就结束了 # 普通题目的话,到这里就结束了
@ -57,7 +60,7 @@ class MessageQueue(object):
# 能运行到这里的都是比赛题目 # 能运行到这里的都是比赛题目
try: try:
contest = Contest.objects.get(id=submission.contest_id) contest = Contest.objects.get(id=submission.contest_id)
if contest.status != 0: if contest.status != CONTEST_UNDERWAY:
logger.info("Contest debug mode, id: " + str(contest.id) + ", submission id: " + submission_id) logger.info("Contest debug mode, id: " + str(contest.id) + ", submission id: " + submission_id)
continue continue
contest_problem = ContestProblem.objects.get(contest=contest, id=submission.problem_id) contest_problem = ContestProblem.objects.get(contest=contest, id=submission.problem_id)
@ -68,50 +71,21 @@ class MessageQueue(object):
logger.warning("Submission problem does not exist, submission_id: " + submission_id) logger.warning("Submission problem does not exist, submission_id: " + submission_id)
continue continue
try: with transaction.atomic():
contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest, try:
problem_id=contest_problem.id) contest_rank = ContestRank.objects.get(contest=contest, user=user)
# 提交次数加1 contest_rank.update_rank(submission)
with transaction.atomic(): except ContestRank.DoesNotExist:
if submission.result == result["accepted"]: ContestRank.objects.create(contest=contest, user=user).update_rank(submission)
# 避免这道题已经 ac 了,但是又重新提交了一遍
if not contest_submission.ac: if submission.result == result["accepted"]:
# 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时 contest_problem.total_accepted_number += 1
contest_submission.ac_time = int((submission.create_time - contest.start_time).total_seconds())
contest_submission.total_time += contest_submission.ac_time
contest_submission.total_submission_number += 1
# 标记为已经通过
if contest_problem.total_accepted_number == 0:
contest_submission.first_achieved = True
contest_submission.ac = True
# contest problem ac 计数器加1
contest_problem.total_accepted_number += 1
else:
# 如果这个提交是错误的就罚时20分钟
contest_submission.total_time += 1200
contest_submission.total_submission_number += 1
contest_submission.save()
contest_problem.save() contest_problem.save()
except ContestSubmission.DoesNotExist:
# 第一次提交 problems_status = user.problems_status
with transaction.atomic(): problems_status["contest_problems"][str(contest_problem.id)] = 1
is_ac = submission.result == result["accepted"] user.problems_status = problems_status
first_achieved = False user.save()
ac_time = 0
if is_ac:
ac_time = int((submission.create_time - contest.start_time).total_seconds())
total_time = int((submission.create_time - contest.start_time).total_seconds())
# 增加题目总的ac数计数器
if contest_problem.total_accepted_number == 0:
first_achieved = True
contest_problem.total_accepted_number += 1
contest_problem.save()
else:
# 没过罚时20分钟
total_time = 1200
ContestSubmission.objects.create(user_id=submission.user_id, contest=contest, problem=contest_problem,
ac=is_ac, total_time=total_time, first_achieved=first_achieved,
ac_time=ac_time)
logger.debug("Start message queue") logger.debug("Start message queue")
MessageQueue().listen_task() MessageQueue().listen_task()

View File

@ -305,15 +305,11 @@ def problem_list_page(request, page=1):
except Exception: except Exception:
pass pass
if request.user.is_authenticated():
problems_status = json.loads(request.user.problems_status)
else:
problems_status = {}
# 右侧标签列表 按照关联的题目的数量排序 排除题目数量为0的 # 右侧标签列表 按照关联的题目的数量排序 排除题目数量为0的
tags = ProblemTag.objects.annotate(problem_number=Count("problem")).filter(problem_number__gt=0).order_by("-problem_number") tags = ProblemTag.objects.annotate(problem_number=Count("problem")).filter(problem_number__gt=0).order_by("-problem_number")
return render(request, "oj/problem/problem_list.html", return render(request, "oj/problem/problem_list.html",
{"problems": current_page, "page": int(page), {"problems": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "tag": tag_text,"problems_status": problems_status, "keyword": keyword, "tag": tag_text,
"tags": tags, "difficulty_order": difficulty_order}) "tags": tags, "difficulty_order": difficulty_order})

View File

@ -53,9 +53,11 @@ class SubmissionAPIView(APIView):
logger.error(e) logger.error(e)
return error_response(u"提交判题任务失败") return error_response(u"提交判题任务失败")
# 修改用户解题状态 # 修改用户解题状态
problems_status = json.loads(request.user.problems_status) problems_status = request.user.problems_status
problems_status[str(data["problem_id"])] = 2 if "problems" not in problems_status:
request.user.problems_status = json.dumps(problems_status) problems_status["problems"] = {}
problems_status["problems"][str(data["problem_id"])] = 2
request.user.problems_status = problems_status
request.user.save() request.user.save()
# 增加redis 中判题队列长度的计数器 # 增加redis 中判题队列长度的计数器
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
@ -124,7 +126,7 @@ def my_submission(request, submission_id):
""" """
try: try:
result = _get_submission(submission_id, request.user) result = _get_submission(submission_id, request.user)
submission = request["submission"] submission = result["submission"]
except Submission.DoesNotExist: except Submission.DoesNotExist:
return error_page(request, u"提交不存在") return error_page(request, u"提交不存在")
@ -231,7 +233,7 @@ class SubmissionShareAPIView(APIView):
result = _get_submission(submission_id, request.user) result = _get_submission(submission_id, request.user)
except Submission.DoesNotExist: except Submission.DoesNotExist:
return error_response(u"提交不存在") return error_response(u"提交不存在")
if not request["can_share"]: if not result["can_share"]:
return error_page(request, u"提交不存在") return error_page(request, u"提交不存在")
submission = result["submission"] submission = result["submission"]
submission.shared = not submission.shared submission.shared = not submission.shared

View File

@ -44,20 +44,7 @@
{% for item in contest_problems %} {% for item in contest_problems %}
<tr> <tr>
<th> <th>
<span class="{% get_problem_status request.user.problems_status.contest_problems item.id %}"></span>
<span class="glyphicon
{% if item.state %}
{% ifequal item.state 1%}
glyphicon-ok ac-flag
{% else %}
glyphicon-minus dealing-flag
{% endifequal %}
{% endif %}
"></span>
</th> </th>
<th scope="row"> <th scope="row">
<a href="/contest/{{ item.contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a> <a href="/contest/{{ item.contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a>

View File

@ -3,6 +3,7 @@
比赛排名 比赛排名
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{% load contest %}
<div class="container main"> <div class="container main">
<ul class="nav nav-tabs nav-tabs-google contest-tab"> <ul class="nav nav-tabs nav-tabs-google contest-tab">
@ -23,13 +24,13 @@
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<h2 class="text-center">排名( <h2 class="text-center">排名(
{% if real_time_rank %} {% if contest.real_time_rank %}
实时 实时
{% else %} {% else %}
已封榜 已封榜
{% endif %}) {% endif %})
</h2> </h2>
{% if result %} {% if rank %}
<table class="table table-bordered text-center"> <table class="table table-bordered text-center">
<thead> <thead>
<tr> <tr>
@ -45,24 +46,20 @@
</tr> </tr>
</thead> </thead>
<tbody class="rank"> <tbody class="rank">
{% for item in result %} {% for item in rank %}
<tr> <tr>
<th scope="row">{{ forloop.counter }}</th> <th scope="row">{{ forloop.counter }}</th>
<td> <td>
{{ item.username }} {{ item.user.username }}
{% if show_real_name %} {% if show_real_name %}
{{ item.real_name }} {{ item.real_name }}
{% endif %} {% endif %}
</td> </td>
<td>{{ item.total_ac }} / {{ item.total_submit }}</td> <td>{{ item.total_ac_number }} / {{ item.total_submission_number }}</td>
<td>{% if item.total_time %}{{ item.total_time }}{% else %}--{% endif %}</td> <td>{% if item.total_time %}{{ item.total_time|format_seconds }}{% else %}--{% endif %}</td>
{% for problem in item.problems %} {% for problem in contest_problems %}
<td class=" <td class="{% get_submission_class item problem %}">
{% if problem %}{% if problem.ac %}{% if problem.first_achieved %}first-achieved{% else %}alert-success{% endif %}{% else %}alert-danger{% endif %}{% endif %}"> {% get_submission_content item problem %}
{% if problem.ac %}{{ problem.ac_time }}{% endif %}
{% if problem.failed_number %}
(-{{ problem.failed_number }})
{% endif %}
</td> </td>
{% endfor %} {% endfor %}
</tr> </tr>

View File

@ -40,6 +40,9 @@
{{ submission.result|translate_result }} {{ submission.result|translate_result }}
</span> </span>
</h4> </h4>
{% ifnotequal request.user.id submission.user_id %}
<p>作者:{{ user.username }}</p>
{% endifnotequal %}
{% ifequal submission.result 0 %} {% ifequal submission.result 0 %}
<p>时间 : {{ submission.accepted_answer_time }}ms 语言 : <p>时间 : {{ submission.accepted_answer_time }}ms 语言 :
{{ submission.language|translate_language }} {{ submission.language|translate_language }}
@ -65,17 +68,18 @@
<div id="code-field"> <div id="code-field">
<textarea id="code-editor">{{ submission.code }}</textarea> <textarea id="code-editor">{{ submission.code }}</textarea>
</div> </div>
{% if can_share %}
<div id="share-code" class="col-lg-6 col-md-6"> <div id="share-code" class="col-lg-6 col-md-6">
{% if submission.shared %} {% if submission.shared %}
<button class="btn btn-warning" id="share-code-btn">取消分享</button> <button class="btn btn-warning" id="share-code-btn">取消分享</button>
{% else %} {% else %}
<button class="btn btn-primary" id="share-code-btn">分享我的代码</button> <button class="btn btn-primary" id="share-code-btn">分享我的代码</button>
{% endif %} {% endif %}
<textarea class="form-control" id="share-code-textarea" <textarea class="form-control" id="share-code-textarea"
{% if not submission.shared %}style="display: none" {% endif %}>{{ problem.title }} {% if not submission.shared %}style="display: none" {% endif %}>{{ problem.title }}
{{ request.build_absolute_uri }}</textarea> {{ request.build_absolute_uri }}</textarea>
</div> </div>
{% endif %}
</div> </div>
@ -102,7 +106,7 @@
url: "/api/submission/share/", url: "/api/submission/share/",
method: "post", method: "post",
data: {submission_id: location.href.split("/")[4]}, data: {submission_id: location.href.split("/")[4]},
success: function(data){ success: function (data) {
location.reload(); location.reload();
} }
}) })

View File

@ -31,7 +31,7 @@
<tbody> <tbody>
{% for item in problems %} {% for item in problems %}
<tr> <tr>
<th><span class="{% get_problem_status problems_status item.id %}"></span></th> <th><span class="{% get_problem_status request.user.problems_status.problems item.id %}"></span></th>
<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> <td>

View File

@ -23,6 +23,7 @@ inspect_redis = json.loads(os.popen("docker inspect redis").read())
if not inspect_redis: if not inspect_redis:
print "Error when inspect redis ip" print "Error when inspect redis ip"
exit() exit()
redis_ip = inspect_redis[0]["NetworkSettings"]["IPAddress"] redis_ip = inspect_redis[0]["NetworkSettings"]["IPAddress"]
print "redis ip ", redis_ip print "redis ip ", redis_ip
@ -31,6 +32,7 @@ inspect_mysql = json.loads(os.popen("docker inspect mysql").read())
if not inspect_mysql: if not inspect_mysql:
print "Error when inspect mysql ip" print "Error when inspect mysql ip"
exit() exit()
mysql_ip = inspect_mysql[0]["NetworkSettings"]["IPAddress"] mysql_ip = inspect_mysql[0]["NetworkSettings"]["IPAddress"]
print "mysql ip ", mysql_ip print "mysql ip ", mysql_ip
@ -44,6 +46,7 @@ for line in f.readlines():
content += ("\nexport submission_db_host=" + mysql_ip + "\n") content += ("\nexport submission_db_host=" + mysql_ip + "\n")
else: else:
content += line content += line
f.close() f.close()

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import json
from django.db import models from django.db import models
from utils.xss_filter import XssHtml from utils.xss_filter import XssHtml
@ -9,8 +10,12 @@ class RichTextField(models.TextField):
def get_prep_value(self, value): def get_prep_value(self, value):
if not value: if not value:
return value value = ""
parser = XssHtml() parser = XssHtml()
parser.feed(value) parser.feed(value)
parser.close() parser.close()
return parser.getHtml() return parser.getHtml()
class JsonField(models.TextField):
pass

View File

@ -100,5 +100,5 @@ def paginate(request, query_set, object_serializer):
def rand_str(length=32): def rand_str(length=32):
string = hashlib.md5(str(time.time()) + str(random.randrange(1, 9999999900))).hexdigest() string = hashlib.md5(str(time.time()) + str(random.randrange(1, 987654321234567))).hexdigest()
return string[0:length] return string[0:length]

View File

@ -23,9 +23,50 @@ def get_contest_status_color(contest):
return "success" return "success"
def get_the_formatted_time(seconds):
if not seconds:
return ""
seconds = int(seconds)
hour = seconds / (60 * 60)
minute = (seconds - hour * 60 * 60) / 60
second = seconds - hour * 60 * 60 - minute * 60
return str(hour) + ":" + str(minute) + ":" + str(second)
def get_submission_class(rank, problem):
if str(problem.id) not in rank.submission_info:
return ""
else:
submission = rank.submission_info[str(problem.id)]
if submission["is_ac"]:
_class = "alert-success"
if submission["is_first_ac"]:
_class += " first-achieved"
else:
_class = "alert-danger"
return _class
def get_submission_content(rank, problem):
if str(problem.id) not in rank.submission_info:
return ""
else:
submission = rank.submission_info[str(problem.id)]
if submission["is_ac"]:
r = get_the_formatted_time(submission["ac_time"])
if submission["error_number"]:
r += "-" + str(submission["error_number"]) + ""
return r
else:
return "-" + str(submission["error_number"]) + ""
from django import template from django import template
register = template.Library() register = template.Library()
register.filter("contest_status", get_contest_status) register.filter("contest_status", get_contest_status)
register.filter("contest_status_color", get_contest_status_color) register.filter("contest_status_color", get_contest_status_color)
register.filter("format_seconds", get_the_formatted_time)
register.simple_tag(get_submission_class, name="get_submission_class")
register.simple_tag(get_submission_content, name="get_submission_content")

View File

@ -9,6 +9,9 @@ def get_problem_accepted_radio(problem):
def get_problem_status(problems_status, problem_id): def get_problem_status(problems_status, problem_id):
# 用户没登陆 或者 user.problem_status 中没有这个字段都会到导致这里的problem_status 为 ""
if not problems_status:
return ""
if str(problem_id) in problems_status: if str(problem_id) in problems_status:
if problems_status[str(problem_id)] == 1: if problems_status[str(problem_id)] == 1:
@ -16,6 +19,7 @@ def get_problem_status(problems_status, problem_id):
return "glyphicon glyphicon-minus dealing-flag" return "glyphicon glyphicon-minus dealing-flag"
return "" return ""
from django import template from django import template
register = template.Library() register = template.Library()