Merge branch 'dev' into hohoTT-dev

This commit is contained in:
hohoTT 2015-09-15 16:09:01 +08:00
commit 66b9b1a8f2
28 changed files with 427 additions and 171 deletions

1
.gitignore vendored
View File

@ -63,3 +63,4 @@ build.txt
tmp/ tmp/
test_case/ test_case/
release/ release/
upload/

View File

@ -1,6 +1,6 @@
FROM python:2.7 FROM python:2.7
ENV PYTHONBUFFERED 1 ENV PYTHONBUFFERED 1
RUN mkdir -p /code/log /code/test_case RUN mkdir -p /code/log /code/test_case /code/upload
WORKDIR /code WORKDIR /code
ADD requirements.txt /code/ ADD requirements.txt /code/
RUN pip install -r requirements.txt RUN pip install -r requirements.txt

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 = [
('contest', '0008_auto_20150912_1912'),
]
operations = [
migrations.AddField(
model_name='contestsubmission',
name='first_achieved',
field=models.BooleanField(default=False),
),
]

View File

@ -89,10 +89,12 @@ class ContestSubmission(models.Model):
total_submission_number = models.IntegerField(default=1) total_submission_number = models.IntegerField(default=1)
# 这道题是 AC 还是没过 # 这道题是 AC 还是没过
ac = models.BooleanField() ac = models.BooleanField()
# ac 用时 # ac 用时以秒计
ac_time = models.IntegerField(default=0) ac_time = models.IntegerField(default=0)
# 总的时间用于acm 类型的,也需要保存罚时 # 总的时间用于acm 类型的,也需要保存罚时
total_time = models.IntegerField(default=0) total_time = models.IntegerField(default=0)
# 第一个解出此题目
first_achieved = models.BooleanField(default=False)
class Meta: class Meta:
db_table = "contest_submission" db_table = "contest_submission"

View File

@ -13,7 +13,7 @@ class CreateContestSerializer(serializers.Serializer):
description = serializers.CharField(max_length=5000) description = serializers.CharField(max_length=5000)
mode = serializers.IntegerField() mode = serializers.IntegerField()
contest_type = serializers.IntegerField() contest_type = serializers.IntegerField()
show_rank = serializers.BooleanField() real_time_rank = serializers.BooleanField()
show_user_submission = serializers.BooleanField() show_user_submission = serializers.BooleanField()
password = serializers.CharField(max_length=30, required=False, default=None) password = serializers.CharField(max_length=30, required=False, default=None)
start_time = serializers.DateTimeField() start_time = serializers.DateTimeField()
@ -47,7 +47,7 @@ class EditContestSerializer(serializers.Serializer):
description = serializers.CharField(max_length=10000) description = serializers.CharField(max_length=10000)
mode = serializers.IntegerField() mode = serializers.IntegerField()
contest_type = serializers.IntegerField() contest_type = serializers.IntegerField()
show_rank = serializers.BooleanField() real_time_rank = serializers.BooleanField()
show_user_submission = serializers.BooleanField() show_user_submission = serializers.BooleanField()
password = serializers.CharField(max_length=30, required=False, default=None) password = serializers.CharField(max_length=30, required=False, default=None)
start_time = serializers.DateTimeField() start_time = serializers.DateTimeField()

View File

@ -21,6 +21,8 @@ from .serializers import (CreateContestSerializer, ContestSerializer, EditContes
CreateContestProblemSerializer, ContestProblemSerializer, CreateContestProblemSerializer, ContestProblemSerializer,
ContestPasswordVerifySerializer, ContestPasswordVerifySerializer,
EditContestProblemSerializer) EditContestProblemSerializer)
from oj.settings import REDIS_CACHE
import redis
class ContestAdminAPIView(APIView): class ContestAdminAPIView(APIView):
@ -58,7 +60,7 @@ class ContestAdminAPIView(APIView):
try: try:
contest = Contest.objects.create(title=data["title"], description=data["description"], contest = Contest.objects.create(title=data["title"], description=data["description"],
mode=data["mode"], contest_type=data["contest_type"], mode=data["mode"], contest_type=data["contest_type"],
show_rank=data["show_rank"], password=data["password"], real_time_rank=data["real_time_rank"], password=data["password"],
show_user_submission=data["show_user_submission"], show_user_submission=data["show_user_submission"],
start_time=dateparse.parse_datetime(data["start_time"]), start_time=dateparse.parse_datetime(data["start_time"]),
end_time=dateparse.parse_datetime(data["end_time"]), end_time=dateparse.parse_datetime(data["end_time"]),
@ -112,7 +114,7 @@ class ContestAdminAPIView(APIView):
contest.description = data["description"] contest.description = data["description"]
contest.mode = data["mode"] contest.mode = data["mode"]
contest.contest_type = data["contest_type"] contest.contest_type = data["contest_type"]
contest.show_rank = data["show_rank"] contest.real_time_rank = data["real_time_rank"]
contest.show_user_submission = data["show_user_submission"] contest.show_user_submission = data["show_user_submission"]
contest.start_time = dateparse.parse_datetime(data["start_time"]) contest.start_time = dateparse.parse_datetime(data["start_time"])
contest.end_time = dateparse.parse_datetime(data["end_time"]) contest.end_time = dateparse.parse_datetime(data["end_time"])
@ -353,7 +355,6 @@ def contest_list_page(request, page=1):
if request.user.is_authenticated and join: if request.user.is_authenticated and join:
contests = contests.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())). \ contests = contests.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())). \
filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now()) filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now())
paginator = Paginator(contests, 20) paginator = Paginator(contests, 20)
try: try:
current_page = paginator.page(int(page)) current_page = paginator.page(int(page))
@ -390,23 +391,62 @@ def _cmp(x, y):
return -1 return -1
def get_the_time_format(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"])
if contest.real_time_rank:
# 更新rank
result = ContestSubmission.objects.filter(contest=contest).values("user_id"). \ result = ContestSubmission.objects.filter(contest=contest).values("user_id"). \
annotate(total_submit=Sum("total_submission_number")) annotate(total_submit=Sum("total_submission_number"))
for i in range(0, len(result)): for i in range(0, len(result)):
# 这个人所有的提交 # 这个人所有的提交
submissions = ContestSubmission.objects.filter(user_id=result[i]["user_id"], contest_id=contest_id) submissions = ContestSubmission.objects.filter(user_id=result[i]["user_id"], contest_id=contest_id)
result[i]["submissions"] = {} result[i]["submissions"] = {}
for item in submissions: result[i]["problems"] = []
result[i]["submissions"][item.problem_id] = item 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_time_format(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() result[i]["total_ac"] = submissions.filter(ac=True).count()
result[i]["user"] = User.objects.get(id=result[i]["user_id"]) result[i]["username"] = User.objects.get(id=result[i]["user_id"]).username
result[i]["total_time"] = submissions.filter(ac=True).aggregate(total_time=Sum("total_time"))["total_time"] result[i]["total_time"] = get_the_time_format(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, {"contest": contest, "contest_problems": contest_problems,
"result": sorted(result, cmp=_cmp, reverse=True), "result": result,
"auto_refresh": request.GET.get("auto_refresh", None) == "true"}) "auto_refresh": request.GET.get("auto_refresh", None) == "true",
"real_time_rank": contest.real_time_rank})

View File

@ -3,6 +3,8 @@ MAINTAINER virusdefender<qduliyang@outlook.com>
RUN mkdir /var/install/ RUN mkdir /var/install/
WORKDIR /var/install/ WORKDIR /var/install/
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
RUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/
RUN apt-get update RUN apt-get update
RUN apt-get -y install software-properties-common python-software-properties RUN apt-get -y install software-properties-common python-software-properties
RUN add-apt-repository -y ppa:webupd8team/java RUN add-apt-repository -y ppa:webupd8team/java

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import os
# 单个判题端最多同时运行的程序个数因为判题端会同时运行多组测试数据比如一共有5组测试数据 # 单个判题端最多同时运行的程序个数因为判题端会同时运行多组测试数据比如一共有5组测试数据
# 如果MAX_RUNNING_NUMBER大于等于5那么这5组数据就会同时进行评测然后返回结果。 # 如果MAX_RUNNING_NUMBER大于等于5那么这5组数据就会同时进行评测然后返回结果。
# 如果MAX_RUNNING_NUMBER小于5为3那么就会同时运行前三组测试数据然后再运行后两组数据 # 如果MAX_RUNNING_NUMBER小于5为3那么就会同时运行前三组测试数据然后再运行后两组数据
@ -14,12 +15,10 @@ lrun_gid = 1002
# judger工作目录 # judger工作目录
judger_workspace = "/var/judger/" judger_workspace = "/var/judger/"
# 这个是在docker 中访问数据库 ip 不一定和web服务器还有celery的一样
submission_db = { submission_db = {
"host": "192.168.42.1", "host": os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
"port": 3306, "port": 3306,
"db": "oj_submission", "db": "oj_submission",
"user": "root", "user": "root",
"password": "mypwd" "password": os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
} }

10
judge/sources.list Normal file
View File

@ -0,0 +1,10 @@
deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse

View File

@ -8,7 +8,7 @@ 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
from account.models import User
logger = logging.getLogger("app_info") logger = logging.getLogger("app_info")
@ -53,10 +53,8 @@ class MessageQueue(object):
contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest, contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest,
problem_id=contest_problem.id) problem_id=contest_problem.id)
# 提交次数加1 # 提交次数加1
contest_submission.total_submission_number += 1
if submission.result == result["accepted"]: if submission.result == result["accepted"]:
# 避免这道题已经 ac 了,但是又重新提交了一遍 # 避免这道题已经 ac 了,但是又重新提交了一遍
if not contest_submission.ac: if not contest_submission.ac:
# 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时 # 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时
@ -64,30 +62,40 @@ class MessageQueue(object):
# logger.debug(submission.create_time) # logger.debug(submission.create_time)
# logger.debug((submission.create_time - contest.start_time).total_seconds()) # logger.debug((submission.create_time - contest.start_time).total_seconds())
# logger.debug(int((submission.create_time - contest.start_time).total_seconds() / 60)) # logger.debug(int((submission.create_time - contest.start_time).total_seconds() / 60))
contest_submission.ac_time = int((submission.create_time - contest.start_time).total_seconds() / 60) contest_submission.ac_time = int((submission.create_time - contest.start_time).total_seconds())
contest_submission.total_time += contest_submission.ac_time 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_submission.ac = True
# contest problem ac 计数器加1 # contest problem ac 计数器加1
contest_problem.total_accepted_number += 1 contest_problem.total_accepted_number += 1
else: else:
# 如果这个提交是错误的就罚时20分钟 # 如果这个提交是错误的就罚时20分钟
contest_submission.total_time += 20 contest_submission.total_time += 1200
contest_submission.total_submission_number += 1
contest_submission.save() contest_submission.save()
contest_problem.save() contest_problem.save()
except ContestSubmission.DoesNotExist: except ContestSubmission.DoesNotExist:
# 第一次提交 # 第一次提交
is_ac = submission.result == result["accepted"] is_ac = submission.result == result["accepted"]
first_achieved = False
ac_time = 0
if is_ac: if is_ac:
total_time = int((submission.create_time - contest.start_time).total_seconds() / 60) ac_time = int((submission.create_time - contest.start_time).total_seconds())
total_time = int((submission.create_time - contest.start_time).total_seconds())
# 增加题目总的ac数计数器 # 增加题目总的ac数计数器
if contest_problem.total_accepted_number == 0:
first_achieved = True
contest_problem.total_accepted_number += 1 contest_problem.total_accepted_number += 1
contest_problem.save() contest_problem.save()
else: else:
# 没过罚时20分钟 # 没过罚时20分钟
total_time = 20 total_time = 1200
ContestSubmission.objects.create(user_id=submission.user_id, contest=contest, problem=contest_problem, ContestSubmission.objects.create(user_id=submission.user_id, contest=contest, problem=contest_problem,
ac=is_ac, total_time=total_time) 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")

View File

@ -3,10 +3,6 @@ import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 下面是需要自己修改的
LOG_PATH = "log/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上 # 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = { DATABASES = {
'default': { 'default': {
@ -17,11 +13,11 @@ DATABASES = {
'submission': { 'submission': {
'NAME': 'oj_submission', 'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'HOST': "121.42.32.129", 'CONN_MAX_AGE': 0.1,
'HOST': "127.0.0.1",
'PORT': 3306, 'PORT': 3306,
'USER': 'root', 'USER': 'root',
'PASSWORD': 'mypwd', 'PASSWORD': 'root',
'CONN_MAX_AGE': 0.1,
} }
} }
@ -33,9 +29,11 @@ REDIS_CACHE = {
DEBUG = True DEBUG = True
# 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/')
ALLOWED_HOSTS = [] ALLOWED_HOSTS = []
IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'static/src/upload_image/') # 在 debug 关闭的情况下,静态文件不是有 django runserver 来处理的,应该由 nginx 返回
# 在 debug 开启的情况下django 会在下面两个文件夹中寻找对应的静态文件。
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/src/"), BASE_DIR]
# 模板文件夹
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')]

View File

@ -3,43 +3,41 @@ import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 下面是需要自己修改的
LOG_PATH = "/var/log/oj/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上 # 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': "oj", 'NAME': "oj",
'CONN_MAX_AGE': 0.1, 'CONN_MAX_AGE': 0.1,
'HOST': '127.0.0.1', 'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
'PORT': 3306, 'PORT': 3306,
'USER': 'root', 'USER': 'root',
'PASSWORD': 'mypwd' 'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
}, },
'submission': { 'submission': {
'NAME': 'oj_submission', 'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 0.1, 'CONN_MAX_AGE': 0.1,
'HOST': "127.0.0.1", 'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
'PORT': 3306, 'PORT': 3306,
'USER': 'root', 'USER': 'root',
'PASSWORD': 'mypwd' 'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
} }
} }
REDIS_CACHE = { REDIS_CACHE = {
"host": "127.0.0.1", "host": os.environ.get("REDIS_PORT_6379_TCP_ADDR", "127.0.0.1"),
"port": 6379, "port": 6379,
"db": 1 "db": 1
} }
DEBUG = True DEBUG = False
# 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = '/root/test_case/'
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
IMAGE_UPLOAD_DIR = '/var/mnt/source/OnlineJudge/static/src/upload_image/' # 在 debug 关闭的情况下,静态文件不是有 django runserver 来处理的,应该由 nginx 返回
# 在 debug 开启的情况下django 会在下面两个文件夹中寻找对应的静态文件。
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/release/"), os.path.join(BASE_DIR, "static/release/")]
# 模板文件夹
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/release/')]

View File

@ -32,8 +32,6 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'hzfp^8mbgapc&x%$#xv)0=t8s7_ilingw(q3!@h&2fty6v6fxz' SECRET_KEY = 'hzfp^8mbgapc&x%$#xv)0=t8s7_ilingw(q3!@h&2fty6v6fxz'
# Application definition # Application definition
INSTALLED_APPS = ( INSTALLED_APPS = (
@ -76,7 +74,7 @@ ROOT_URLCONF = 'oj.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'template/src')], 'DIRS': TEMPLATE_DIRS,
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@ -91,7 +89,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'oj.wsgi.application' WSGI_APPLICATION = 'oj.wsgi.application'
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/ # https://docs.djangoproject.com/en/1.8/topics/i18n/
@ -111,10 +108,11 @@ USE_TZ = True
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static/src/"),)
AUTH_USER_MODEL = 'account.User' AUTH_USER_MODEL = 'account.User'
LOG_PATH = "log/"
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': True, 'disable_existing_loggers': True,
@ -167,3 +165,7 @@ REST_FRAMEWORK = {
} }
DATABASE_ROUTERS = ['oj.db_router.DBRouter'] DATABASE_ROUTERS = ['oj.db_router.DBRouter']
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/')
IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'upload/')

View File

@ -31,6 +31,7 @@ class CreateProblemSerializer(serializers.Serializer):
difficulty = serializers.IntegerField() difficulty = serializers.IntegerField()
tags = serializers.ListField(child=serializers.CharField(max_length=10)) tags = serializers.ListField(child=serializers.CharField(max_length=10))
hint = serializers.CharField(max_length=3000, allow_blank=True) hint = serializers.CharField(max_length=3000, allow_blank=True)
visible = visible = serializers.BooleanField()
class ProblemTagSerializer(serializers.ModelSerializer): class ProblemTagSerializer(serializers.ModelSerializer):

View File

@ -13,13 +13,16 @@ from rest_framework.views import APIView
from django.conf import settings from django.conf import settings
from announcement.models import Announcement from announcement.models import Announcement
from utils.shortcuts import (serializer_invalid_response, error_response, from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, rand_str, error_page) success_response, paginate, rand_str, error_page)
from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer, from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer,
ProblemTagSerializer, CreateProblemTagSerializer) ProblemTagSerializer, CreateProblemTagSerializer)
from .models import Problem, ProblemTag from .models import Problem, ProblemTag
import logging
logger = logging.getLogger("app_info")
def problem_page(request, problem_id): def problem_page(request, problem_id):
try: try:
@ -56,7 +59,8 @@ class ProblemAdminAPIView(APIView):
memory_limit=data["memory_limit"], memory_limit=data["memory_limit"],
difficulty=data["difficulty"], difficulty=data["difficulty"],
created_by=request.user, created_by=request.user,
hint=data["hint"]) hint=data["hint"],
visible=data["visible"])
for tag in data["tags"]: for tag in data["tags"]:
try: try:
tag = ProblemTag.objects.get(name=tag) tag = ProblemTag.objects.get(name=tag)
@ -151,8 +155,9 @@ class TestCaseUploadAPIView(APIView):
with open(tmp_zip, "wb") as test_case_zip: with open(tmp_zip, "wb") as test_case_zip:
for chunk in f: for chunk in f:
test_case_zip.write(chunk) test_case_zip.write(chunk)
except IOError: except IOError as e:
return error_response(u"上传错误,写入临时目录失败") logger.error(e)
return error_response(u"上传失败")
test_case_file = zipfile.ZipFile(tmp_zip, 'r') test_case_file = zipfile.ZipFile(tmp_zip, 'r')
name_list = test_case_file.namelist() name_list = test_case_file.namelist()

View File

@ -108,3 +108,8 @@ li.list-group-item {
#about-acm-logo{ #about-acm-logo{
width: 40%; width: 40%;
} }
.rank .first-achieved{
background: #33CC99;
}

View File

@ -28,7 +28,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
ajaxData.groups = selectedGroups; ajaxData.groups = selectedGroups;
} }
else { else {
if (vm.password) { if (vm.editPassword) {
ajaxData.password = vm.editPassword; ajaxData.password = vm.editPassword;
ajaxData.contest_type = 2; ajaxData.contest_type = 2;
} }
@ -114,7 +114,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
isGlobal: true, isGlobal: true,
allGroups: [], allGroups: [],
showGlobalViewRadio: true, showGlobalViewRadio: true,
admin_type: 1,
getNext: function () { getNext: function () {
if (!vm.nextPage) if (!vm.nextPage)
return; return;
@ -211,6 +211,39 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
if (el) if (el)
problemId = el.id; problemId = el.id;
vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode); vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
addToProblemList: function (problem) {
var ajaxData = {
title: problem.title,
description: problem.description,
time_limit: problem.time_limit,
memory_limit: problem.memory_limit,
samples: problem.samples,
test_case_id: problem.test_case_id,
hint: problem.hint,
source: problem.contest.title,
visible: false,
tags: [],
input_description: problem.input_description,
output_description: problem.output_description,
difficulty: 0
};
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/problem/",
dataType: "json",
data: JSON.stringify(ajaxData),
method: "post",
contentType: "application/json",
success: function (data) {
if (!data.code) {
bsAlert("题目添加成功!题目现在处于隐藏状态,请到题目列表手动修改,并添加分类和难度信息!");
}
else {
bsAlert(data.data);
}
}
});
} }
}); });
vm.$watch("showVisibleOnly", function () { vm.$watch("showVisibleOnly", function () {
@ -266,6 +299,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
var admin_type = data.data.admin_type; var admin_type = data.data.admin_type;
vm.admin_type = admin_type;
if (data.data.admin_type == 1) { if (data.data.admin_type == 1) {
vm.isGlobal = false; vm.isGlobal = false;
vm.showGlobalViewRadio = false; vm.showGlobalViewRadio = false;
@ -278,6 +312,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
if (!data.data.length) { if (!data.data.length) {
if (admin_type != 2) if (admin_type != 2)
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组"); bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
return; return;

View File

@ -117,11 +117,11 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert", "ZeroClipboard"],
function guessLanguage(code) { function guessLanguage(code) {
//cpp //cpp
if (code.indexOf("using namespace std") > -1) { if (code.indexOf("using namespace std") > -1||code.indexOf("<cstdio>") > -1) {
return "2"; return "2";
} }
//c if (code.indexOf("printf"))
if (code.indexOf("printf") > -1) { {
return "1"; return "1";
} }
//java //java
@ -146,6 +146,19 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert", "ZeroClipboard"],
} }
} }
if (language < 3) {
if (code.indexOf("__int64") > -1) {
if (!confirm("您是否在尝试使用'__int64'类型? 这不是 c/c++ 标准并将引发编译错误可以使用 'long long' 代替(详见 关于->帮助),是否仍然提交?")) {
return;
}
}
if (code.indexOf("%I64d") > -1) {
if (!confirm("您是否在尝试将'%I64d'用于long long类型的I/O? 这不被支持,并可能会导致程序输出异常,可以使用 '%lld' 代替(详见 关于->帮助),是否仍然提交?")) {
return;
}
}
}
if (location.href.indexOf("contest") > -1) { if (location.href.indexOf("contest") > -1) {
var problemId = location.pathname.split("/")[4]; var problemId = location.pathname.split("/")[4];
var contestId = location.pathname.split("/")[2]; var contestId = location.pathname.split("/")[2];

View File

@ -4,7 +4,7 @@
// 第三方脚本模块的别名,jquery比libs/jquery-1.11.1.min.js简洁明了 // 第三方脚本模块的别名,jquery比libs/jquery-1.11.1.min.js简洁明了
paths: { paths: {
jquery: "empty:", jquery: "empty:",
avalon: "lib/avalon/avalon", avalon: "empty:",
editor: "utils/editor", editor: "utils/editor",
uploader: "utils/uploader", uploader: "utils/uploader",
formValidation: "utils/formValidation", formValidation: "utils/formValidation",
@ -70,9 +70,71 @@
appDir: "../", appDir: "../",
dir: "../../release/", dir: "../../release/",
modules: [ modules: [
{
name: "bootstrap",
},
{
name: "addProblem_0_pack"
},
{ {
name: "addContest_1_pack" name: "addContest_1_pack"
},
{
name: "problem_2_pack"
},
{
name: "register_3_pack"
},
{
name: "contestList_4_pack"
},
{
name: "group_5_pack"
},
{
name: "editProblem_6_pack"
},
{
name: "announcement_7_pack"
},
{
name: "monitor_8_pack"
},
{
name: "groupDetail_9_pack"
},
{
name: "admin_10_pack"
},
{
name: "problem_11_pack"
},
{
name: "submissionList_12_pack"
},
{
name: "editProblem_13_pack"
},
{
name: "joinGroupRequestList_14_pack"
},
{
name: "changePassword_15_pack"
},
{
name: "group_16_pack"
},
{
name: "submissionList_17_pack"
},
{
name: "login_18_pack"
},
{
name: "contestPassword_19_pack"
},
{
name: "userList_20_pack"
} }
], ],
optimizeCss: "standard", optimizeCss: "standard",

View File

@ -21,7 +21,6 @@ from .models import Submission
from .serializers import CreateSubmissionSerializer, SubmissionSerializer, SubmissionhareSerializer from .serializers import CreateSubmissionSerializer, SubmissionSerializer, SubmissionhareSerializer
class SubmissionAPIView(APIView): class SubmissionAPIView(APIView):
@login_required @login_required
def post(self, request): def post(self, request):
@ -81,7 +80,8 @@ def problem_my_submissions_list_page(request, problem_id):
except Problem.DoesNotExist: except Problem.DoesNotExist:
return error_page(request, u"问题不存在") return error_page(request, u"问题不存在")
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id, contest_id__isnull=True).order_by("-create_time"). \ submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id,
contest_id__isnull=True).order_by("-create_time"). \
values("id", "result", "create_time", "accepted_answer_time", "language") values("id", "result", "create_time", "accepted_answer_time", "language")
return render(request, "oj/problem/my_submissions_list.html", return render(request, "oj/problem/my_submissions_list.html",

View File

@ -31,9 +31,9 @@
</div> </div>
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li class="active"><a href="#">主页</a></li> <li class="active"><a href="/" target="_blank">主页</a></li>
<li><a href="#about">题目</a></li> <li><a href="#problem">题目</a></li>
<li><a href="#contact">提交</a></li> <li><a href="#">提交</a></li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class="dropdown"> <li class="dropdown">

View File

@ -178,6 +178,8 @@
ms-click="showProblemEditPage(el)">编辑</a> ms-click="showProblemEditPage(el)">编辑</a>
<a href="javascript:void(0)" class="btn-sm btn-info" <a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="showSubmissionPage(el)">提交</a> ms-click="showSubmissionPage(el)">提交</a>
<a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="addToProblemList(el)" ms-visible="admin_type=='2'">添加到前台</a>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -1,6 +1,5 @@
{% extends "oj_base.html" %} {% extends "oj_base.html" %}
{% block body %} {% block body %}
{% load submission %}
<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">
@ -19,31 +18,43 @@
</ul> </ul>
<div class="row"> <div class="row">
<div class="col-lg-12">
<h2 class="text-center">排名(
{% if real_time_rank %}
实时
{% else %}
已封榜
{% endif %})
</h2>
{% if result %} {% if result %}
<table class="table table-bordered"> <table class="table table-bordered text-center">
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
<th>用户名</th> <th class="text-center">用户名</th>
<th>AC / 总提交</th> <th class="text-center">AC / 总提交</th>
<th>用时 + 罚时</th> <th class="text-center">用时 + 罚时</th>
{% for item in contest_problems %} {% for item in contest_problems %}
<th><a href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a> <th class="text-center"><a
href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a>
</th> </th>
{% endfor %} {% endfor %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody class="rank">
{% for item in result %} {% for item in result %}
<tr> <tr>
<th scope="row">{{ forloop.counter }}</th> <th scope="row">{{ forloop.counter }}</th>
<td>{{ item.user.username }}</td> <td>{{ item.username }}</td>
<td>{{ item.total_ac }} / {{ item.total_submit }}</td> <td>{{ item.total_ac }} / {{ item.total_submit }}</td>
<td>{% if item.total_time %}{{ item.total_time }} min{% else %}--{% endif %}</td> <td>{% if item.total_time %}{{ item.total_time }}{% else %}--{% endif %}</td>
{% for problem in contest_problems %} {% for problem in item.problems %}
<td class="{% submission_problem_result_class problem item.submissions %}"> <td class="
{% submission_problem problem item.submissions %} {% if problem %}{% if problem.ac %}{% if problem.first_achieved %}first-achieved{% else %}alert-success{% endif %}{% else %}alert-danger{% endif %}{% endif %}">
{% if problem.ac %}{{ problem.ac_time }}{% endif %}
{% if problem.failed_number %}
(-{{ problem.failed_number }})
{% endif %}
</td> </td>
{% endfor %} {% endfor %}
</tr> </tr>
@ -59,6 +70,7 @@
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -57,6 +57,9 @@
{{ request.user.username }} {{ request.user.username }}
<span class="caret"></span></a> <span class="caret"></span></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% if request.user.admin_type == 2 %}
<li><a href="/admin/">后台管理</a></li>
{% endif %}
<li><a href="/submissions/">我的提交</a></li> <li><a href="/submissions/">我的提交</a></li>
<li><a href="#">我的资料</a></li> <li><a href="#">我的资料</a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>

View File

@ -0,0 +1,60 @@
{% 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="/help/">帮助</a>
</li>
<li role="presentation">
<a href="/about/">关于</a>
</li>
</ul>
<div>
<h2 class="text-center">判题系统</h2>
<h4>判题结果</h4>
<ul>
<li>Accepted: 你的答案符合判题标准</li>
<li>Runtime Error: 你的程序运行时出现错误(指针越界,栈溢出,有未处理的异常,主函数返回值非零等)</li>
<li>Time Limit Exceeded: 你的程序执行时间超出题目要求</li>
<li>Memory Limit Exceeded: 你的程序内存使用超出题目要求</li>
<li>Compile Error: 你的程序在编译(包括链接)时出现错误</li>
<li>Wrong Answer: 你的程序输出的答案不符合判题标准</li>
<li>System Error: 判题系统发生故障,请等待重判</li>
<li>Waiting: 你的提交正在等待处理</li>
</ul>
<h4>支持的语言</h4>
<ul>
<li>C (GCC 4.8)</li>
<li>C++ (G++ 4.3)</li>
<li>Java (Oracle JDK 1.7)</li>
</ul>
<h4>编译参数</h4>
<ul>
<li>C</li>
<p>gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main</p>
<li>C++</li>
<p>g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main</p>
<li>Java</li>
<p>javac {src_path} -d {exe_path}</p>
<p>java -cp {exe_path} Main</p>
</ul>
<h2 class="text-center">常见问题</h2>
<ul>
<li>输入输出</li>
<p>无特殊说明,请使用标准输入输出</p>
<li>C/C++的64位整数类型</li>
<p>请使用long long声明使用cin/cout或 %lld输入输出</p>
<li>判题结果与本地执行结果不一致</li>
<p>是否使用了不同版本的编译器VC和TC并不完全符合C/C++标准)</p>
<p>判题时可能使用了与您测试时不同的测试数据(不仅限于样例中展示的数据)</p>
<li>程序执行时间和占用的内存如何计算</li>
<p>执行时间指CPU时间占用内存按执行过程中内存消耗的峰值计有多组测试数据时以最大的时间和内存消耗为准</p>
</ul>
</div>
</div>
{% endblock %}

View File

@ -9,9 +9,11 @@ template_release_path = "template/release/"
static_src_path = "static/src/" static_src_path = "static/src/"
static_release_path = "static/release/" static_release_path = "static/release/"
try:
# 删除模板的 release 文件夹 # 删除模板的 release 文件夹
shutil.rmtree(template_release_path) shutil.rmtree(template_release_path)
except Exception:
pass
# 复制一份模板文件夹到 release # 复制一份模板文件夹到 release
shutil.copytree(template_src_path, template_release_path) shutil.copytree(template_src_path, template_release_path)

View File

@ -29,33 +29,7 @@ def translate_result_class(value):
return "danger" return "danger"
def get_contest_submission_problem_detail(contest_problem, my_submission):
if contest_problem.id in my_submission:
submission = my_submission[contest_problem.id]
if submission.ac:
# 只提交了一次就AC
if submission.total_submission_number == 1:
return str(submission.ac_time) + " min"
else:
return "20 min × " + str(submission.total_submission_number - 1) + " WA + " + str(submission.ac_time) + " min"
return str(submission.total_submission_number) + " WA"
else:
return ""
def get_submission_problem_result_class(contest_problem, my_submission):
if contest_problem.id in my_submission:
submission = my_submission[contest_problem.id]
if submission.ac:
return "success"
else:
return "danger"
else:
return ""
register = template.Library() register = template.Library()
register.filter("translate_result", translate_result) register.filter("translate_result", translate_result)
register.filter("translate_language", translate_language) register.filter("translate_language", translate_language)
register.filter("translate_result_class", translate_result_class) register.filter("translate_result_class", translate_result_class)
register.simple_tag(get_contest_submission_problem_detail, name="submission_problem")
register.simple_tag(get_submission_problem_result_class, name="submission_problem_result_class")

View File

@ -5,7 +5,9 @@ from rest_framework.response import Response
from django.conf import settings from django.conf import settings
from utils.shortcuts import rand_str from utils.shortcuts import rand_str
import logging
logger = logging.getLogger("app_info")
class SimditorImageUploadAPIView(APIView): class SimditorImageUploadAPIView(APIView):
def post(self, request): def post(self, request):
@ -22,12 +24,13 @@ class SimditorImageUploadAPIView(APIView):
with open(image_dir, "wb") as imageFile: with open(image_dir, "wb") as imageFile:
for chunk in img: for chunk in img:
imageFile.write(chunk) imageFile.write(chunk)
except IOError: except IOError as e:
logger.error(e)
return Response(data={ return Response(data={
"success": True, "success": True,
"msg": "上传错误", "msg": "上传错误",
"file_path": "/static/upload_image/" + image_name}) "file_path": "/static/upload/" + image_name})
return Response(data={ return Response(data={
"success": True, "success": True,
"msg": "", "msg": "",
"file_path": "/static/upload_image/" + image_name}) "file_path": "/static/upload/" + image_name})