This commit is contained in:
hohoTT 2015-09-17 19:58:14 +08:00
commit 5fb55221ea
28 changed files with 350 additions and 151 deletions

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 = [
('account', '0002_user_problems_status'),
]
operations = [
migrations.AlterField(
model_name='user',
name='problems_status',
field=models.TextField(default=b'{}'),
),
]

View File

@ -31,8 +31,7 @@ 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(blank=True) problems_status = models.TextField(default="{}")
USERNAME_FIELD = 'username' USERNAME_FIELD = 'username'
REQUIRED_FIELDS = [] REQUIRED_FIELDS = []

View File

@ -7,6 +7,7 @@ from .models import User
class UserLoginSerializer(serializers.Serializer): class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30) username = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30)
captcha = serializers.CharField(required=False,min_length=4,max_length=4)
class UsernameCheckSerializer(serializers.Serializer): class UsernameCheckSerializer(serializers.Serializer):
@ -22,6 +23,7 @@ class UserRegisterSerializer(serializers.Serializer):
real_name = serializers.CharField(max_length=30) real_name = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=30, min_length=6) password = serializers.CharField(max_length=30, min_length=6)
email = serializers.EmailField(max_length=254) email = serializers.EmailField(max_length=254)
captcha = serializers.CharField(max_length=4, min_length=4)
class UserChangePasswordSerializer(serializers.Serializer): class UserChangePasswordSerializer(serializers.Serializer):

View File

@ -5,15 +5,15 @@ from django.shortcuts import render
from django.db.models import Q from django.db.models import Q
from rest_framework.views import APIView from rest_framework.views import APIView
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
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)
class UserLoginAPIView(APIView): class UserLoginAPIView(APIView):
@ -26,6 +26,14 @@ class UserLoginAPIView(APIView):
serializer = UserLoginSerializer(data=request.data) serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
data = serializer.data data = serializer.data
user = User.objects.get(username=data["username"])
# 只有管理员才适用验证码登录
if user.admin_type > 0:
if not "captcha" in data:
return error_response(u"请填写验证码!")
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
user = auth.authenticate(username=data["username"], password=data["password"]) user = auth.authenticate(username=data["username"], password=data["password"])
# 用户名或密码错误的话 返回None # 用户名或密码错误的话 返回None
if user: if user:
@ -64,6 +72,9 @@ class UserRegisterAPIView(APIView):
serializer = UserRegisterSerializer(data=request.data) serializer = UserRegisterSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
data = serializer.data data = serializer.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
try: try:
User.objects.get(username=data["username"]) User.objects.get(username=data["username"])
return error_response(u"用户名已存在") return error_response(u"用户名已存在")
@ -109,39 +120,35 @@ class UserChangePasswordAPIView(APIView):
class UsernameCheckAPIView(APIView): class UsernameCheckAPIView(APIView):
def post(self, request): def get(self, request):
""" """
检测用户名是否存在存在返回True不存在返回False 检测用户名是否存在存在返回状态码400不存在返回200
--- ---
request_serializer: UsernameCheckSerializer
""" """
serializer = UsernameCheckSerializer(data=request.data) username = request.GET.get("username", None)
if serializer.is_valid(): if username:
try: try:
User.objects.get(username=serializer.data["username"]) User.objects.get(username=username)
return success_response(True) return Response(status=400)
except User.DoesNotExist: except User.DoesNotExist:
return success_response(False) return Response(status=200)
else: return Response(status=200)
return serializer_invalid_response(serializer)
class EmailCheckAPIView(APIView): class EmailCheckAPIView(APIView):
def post(self, request): def get(self, request):
""" """
检测邮箱是否存在存在返回True不存在返回False 检测邮箱是否存在存在返回状态码400不存在返回200
--- ---
request_serializer: EmailCheckSerializer
""" """
serializer = EmailCheckSerializer(data=request.data) email = request.GET.get("email", None)
if serializer.is_valid(): if email:
try: try:
User.objects.get(email=serializer.data["email"]) User.objects.get(email=email)
return success_response(True) return Response(status=400)
except User.DoesNotExist: except User.DoesNotExist:
return success_response(False) return Response(status=200)
else: return Response(status=200)
return serializer_invalid_response(serializer)
class UserAdminAPIView(APIView): class UserAdminAPIView(APIView):
@ -206,3 +213,19 @@ class UserInfoAPIView(APIView):
response_serializer: UserSerializer response_serializer: UserSerializer
""" """
return success_response(UserSerializer(request.user).data) return success_response(UserSerializer(request.user).data)
class AccountSecurityAPIView(APIView):
def get(self, request):
"""
判断用户登录是否需要验证码
---
"""
username = request.GET.get("username", None)
if username:
try:
User.objects.get(username=username, admin_type__gt=0)
except User.DoesNotExist:
return success_response({"applied_captcha": False})
return success_response({"applied_captcha": True})
return success_response({"applied_captcha": False})

View File

@ -391,7 +391,7 @@ def _cmp(x, y):
return -1 return -1
def get_the_time_format(seconds): def get_the_formatted_time(seconds):
if not seconds: if not seconds:
return "" return ""
result = str(seconds % 60) result = str(seconds % 60)
@ -427,7 +427,7 @@ def contest_rank_page(request, contest_id):
"first_achieved": status.first_achieved, "first_achieved": status.first_achieved,
"ac": status.ac, "ac": status.ac,
"failed_number": status.total_submission_number, "failed_number": status.total_submission_number,
"ac_time": get_the_time_format(status.ac_time)}) "ac_time": get_the_formatted_time(status.ac_time)})
if status.ac: if status.ac:
result[i]["problems"][-1]["failed_number"] -= 1 result[i]["problems"][-1]["failed_number"] -= 1
except ContestSubmission.DoesNotExist: except ContestSubmission.DoesNotExist:
@ -436,7 +436,8 @@ def contest_rank_page(request, contest_id):
user= User.objects.get(id=result[i]["user_id"]) user= User.objects.get(id=result[i]["user_id"])
result[i]["username"] = user.username result[i]["username"] = user.username
result[i]["real_name"] = user.real_name result[i]["real_name"] = user.real_name
result[i]["total_time"] = get_the_time_format(submissions.filter(ac=True).aggregate(total_time=Sum("total_time"))["total_time"]) 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) result = sorted(result, cmp=_cmp, reverse=True)
r.set("contest_rank_" + contest_id, json.dumps(list(result))) r.set("contest_rank_" + contest_id, json.dumps(list(result)))
else: else:
@ -451,5 +452,5 @@ def contest_rank_page(request, contest_id):
{"contest": contest, "contest_problems": contest_problems, {"contest": contest, "contest_problems": contest_problems,
"result": result, "result": result,
"auto_refresh": request.GET.get("auto_refresh", None) == "true", "auto_refresh": request.GET.get("auto_refresh", None) == "true",
"show_real_name": result.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}) "real_time_rank": contest.real_time_rank})

View File

@ -1,9 +1,11 @@
# coding=utf-8 # coding=utf-8
import json import json
from datetime import datetime
import redis import redis
import pytz
from django.shortcuts import render from django.shortcuts import render
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.utils import timezone
from rest_framework.views import APIView from rest_framework.views import APIView
from judge.judger_controller.tasks import judge from judge.judger_controller.tasks import judge
@ -75,7 +77,7 @@ def contest_problem_my_submissions_list_page(request, contest_id, contest_proble
{"submissions": submissions, "problem": contest_problem}) {"submissions": submissions, "problem": contest_problem})
@login_required @check_user_contest_permission
def contest_problem_submissions_list_page(request, contest_id, page=1): def contest_problem_submissions_list_page(request, contest_id, page=1):
""" """
单个比赛中的所有提交包含自己和别人自己可查提交结果其他人不可查 单个比赛中的所有提交包含自己和别人自己可查提交结果其他人不可查
@ -84,9 +86,17 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
contest = Contest.objects.get(id=contest_id) contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist: except Contest.DoesNotExist:
return error_page(request, u"比赛不存在") return error_page(request, u"比赛不存在")
# 以下是本场比赛中所有的提交
submissions = Submission.objects.filter(contest_id=contest_id). \ submissions = Submission.objects.filter(contest_id=contest_id).\
values("id", "contest_id", "problem_id", "result", "create_time", "accepted_answer_time", "language", "user_id").order_by("-create_time") values("id", "contest_id", "problem_id", "result", "create_time",
"accepted_answer_time", "language", "user_id").order_by("-create_time")
# 封榜的时候只能看到自己的提交
if not contest.real_time_rank:
if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by):
submissions = submissions.filter(user_id=request.user.id)
language = request.GET.get("language", None) language = request.GET.get("language", None)
filter = None filter = None
if language: if language:
@ -131,7 +141,7 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
return render(request, "oj/contest/submissions_list.html", return render(request, "oj/contest/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,
"contest": contest, "filter":filter}) "contest": contest, "filter": filter})
class ContestSubmissionAdminAPIView(APIView): class ContestSubmissionAdminAPIView(APIView):

View File

@ -4,4 +4,5 @@ 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
EXPOSE 8010 EXPOSE 8010
CMD supervisord

View File

@ -0,0 +1,16 @@
[program:gunicorn]
command=gunicorn oj.wsgi:application -b 0.0.0.0:8080 --reload
directory=/code/
user=root
numprocs=1
stdout_logfile=/code/log/gunicorn.log
stderr_logfile=/code/log/gunicorn.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 6
killasgroup=true

View File

@ -0,0 +1,16 @@
[program:mq]
command=python manage.py runscript mq
directory=/code/qduoj/
user=root
numprocs=1
stdout_logfile=/code/log/mq.log
stderr_logfile=/code/log/mq.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 6
killasgroup=true

View File

@ -0,0 +1,26 @@
[unix_http_server]
file=/tmp/supervisor.sock ; path to your socket file
[supervisord]
logfile=/code/log/supervisord.log ; supervisord log file
logfile_maxbytes=50MB ; maximum size of logfile before rotation
logfile_backups=10 ; number of backed up logfiles
loglevel=info ; info, debug, warn, trace
pidfile=/code/log/supervisord.pid ; pidfile location
nodaemon=true ; run supervisord as a daemon
minfds=1024 ; number of startup file descriptors
minprocs=200 ; number of process descriptors
user=root ; default user
childlogdir=/code/log/ ; where child log files will live
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use unix:// schem for a unix sockets.
[include]
files=gunicorn.conf mq.conf

View File

@ -4,9 +4,10 @@
此文件包含 celery 的部分配置但是 celery 并不是运行在docker 中的所以本配置文件中的 redis和 MySQL 的地址就应该是 此文件包含 celery 的部分配置但是 celery 并不是运行在docker 中的所以本配置文件中的 redis和 MySQL 的地址就应该是
运行 redis MySQL docker 容器的地址了怎么获取这个地址见帮助文档测试用例的路径和源代码路径同理 运行 redis MySQL docker 容器的地址了怎么获取这个地址见帮助文档测试用例的路径和源代码路径同理
""" """
import os
# 这个redis 是 celery 使用的,包括存储队列信息还有部分统计信息 # 这个redis 是 celery 使用的,包括存储队列信息还有部分统计信息
redis_config = { redis_config = {
"host": "192.168.42.23", "host": os.environ.get("REDIS_PORT_6379_TCP_ADDR"),
"port": 6379, "port": 6379,
"db": 0 "db": 0
} }
@ -30,7 +31,7 @@ log_dir = "/root/log/"
# 存储提交信息的数据库,是 celery 使用的,与 oj.settings/local_settings 等区分,那是 web 服务器访问的地址 # 存储提交信息的数据库,是 celery 使用的,与 oj.settings/local_settings 等区分,那是 web 服务器访问的地址
submission_db = { submission_db = {
"host": "192.168.42.32", "host": os.environ.get("submission_db_host"),
"port": 3306, "port": 3306,
"db": "oj_submission", "db": "oj_submission",
"user": "root", "user": "root",

View File

@ -11,7 +11,7 @@ from settings import docker_config, source_code_dir, test_case_dir, log_dir, sub
@app.task @app.task
def judge(submission_id, time_limit, memory_limit, test_case_id): def judge(submission_id, time_limit, memory_limit, test_case_id):
try: try:
command = "%s run -t -i --privileged --rm " \ command = "%s run --privileged --rm " \
"--link mysql " \ "--link mysql " \
"-v %s:/var/judger/test_case/ " \ "-v %s:/var/judger/test_case/ " \
"-v %s:/var/judger/code/ " \ "-v %s:/var/judger/code/ " \

View File

@ -46,10 +46,7 @@ class MessageQueue(object):
except User.DoesNotExist: except User.DoesNotExist:
logger.warning("Submission user does not exist, submission_id: " + submission_id) logger.warning("Submission user does not exist, submission_id: " + submission_id)
continue continue
if user.problems_status: problems_status = json.loads(user.problems_status)
problems_status = json.loads(user.problems_status)
else:
problems_status = {}
problems_status[str(problem.id)] = 1 problems_status[str(problem.id)] = 1
user.problems_status = json.dumps(problems_status) user.problems_status = json.dumps(problems_status)
user.save() user.save()

View File

@ -4,7 +4,7 @@ from django.views.generic import TemplateView
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
UserChangePasswordAPIView, EmailCheckAPIView, UserChangePasswordAPIView, EmailCheckAPIView,
UserAdminAPIView, UserInfoAPIView) UserAdminAPIView, UserInfoAPIView, AccountSecurityAPIView)
from announcement.views import AnnouncementAdminAPIView from announcement.views import AnnouncementAdminAPIView
@ -25,7 +25,6 @@ from contest_submission.views import contest_problem_my_submissions_list_page
urlpatterns = [ urlpatterns = [
url(r'^install/$', "install.views.install"),
url("^$", "account.views.page_jump", name="page_jump_api"), url("^$", "account.views.page_jump", name="page_jump_api"),
url(r'^docs/', include('rest_framework_swagger.urls')), url(r'^docs/', include('rest_framework_swagger.urls')),
url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"), url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"),
@ -116,4 +115,5 @@ urlpatterns = [
url(r'^api/submission/share/$', SubmissionShareAPIView.as_view(), name="submission_share_api"), url(r'^api/submission/share/$', SubmissionShareAPIView.as_view(), name="submission_share_api"),
url(r'^captcha/$', "utils.captcha.views.show_captcha", name="show_captcha"), url(r'^captcha/$', "utils.captcha.views.show_captcha", name="show_captcha"),
url(r'^api/account_security_check/$', AccountSecurityAPIView.as_view(), name="account_security_check"),
] ]

View File

@ -24,6 +24,7 @@ import logging
logger = logging.getLogger("app_info") logger = logging.getLogger("app_info")
def problem_page(request, problem_id): def problem_page(request, problem_id):
try: try:
problem = Problem.objects.get(id=problem_id, visible=True) problem = Problem.objects.get(id=problem_id, visible=True)
@ -282,7 +283,7 @@ def problem_list_page(request, page=1):
except Exception: except Exception:
pass pass
if request.user.is_authenticated() and request.user.problems_status: if request.user.is_authenticated():
problems_status = json.loads(request.user.problems_status) problems_status = json.loads(request.user.problems_status)
else: else:
problems_status = {} problems_status = {}

View File

@ -41,4 +41,5 @@ label {
pre { pre {
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
background-color: white;
} }

View File

@ -1,26 +1,31 @@
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) { require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
var applied_captcha = false;
$('form').validator().on('submit', function (e) { $('form').validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) { if (!e.isDefaultPrevented()) {
var username = $("#username").val(); var username = $("#username").val();
var password = $("#password").val(); var password = $("#password").val();
var ajaxData = {username: username, password: password};
if (applied_captcha) {
ajaxData.captcha = $("#captcha").val();
}
$.ajax({ $.ajax({
beforeSend: csrfTokenHeader, beforeSend: csrfTokenHeader,
url: "/api/login/", url: "/api/login/",
data: {username: username, password: password}, data: ajaxData,
dataType: "json", dataType: "json",
method: "post", method: "post",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
//成功登陆 //成功登陆
var ref = document.referrer; var ref = document.referrer;
if(ref){ if (ref) {
// 注册页和本页的来源的跳转回首页,防止死循环 // 注册页和本页的来源的跳转回首页,防止死循环
if(ref.indexOf("register") > -1 || ref.indexOf("login") > -1){ if (ref.indexOf("register") > -1 || ref.indexOf("login") > -1) {
location.href = "/"; location.href = "/";
return; return;
} }
// 判断来源,只有同域下才跳转 // 判断来源,只有同域下才跳转
if(ref.split("/")[2].split(":")[0] == location.hostname){ if (ref.split("/")[2].split(":")[0] == location.hostname) {
location.href = ref; location.href = ref;
return; return;
} }
@ -28,6 +33,7 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
location.href = "/"; location.href = "/";
} }
else { else {
refresh_captcha();
bsAlert(data.data); bsAlert(data.data);
} }
} }
@ -35,5 +41,34 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
}); });
return false; return false;
} }
}) });
$('#username').blur(function () {
if ($("#username").val()) {
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/account_security_check/?username=" + $("#username").val(),
method: "get",
success: function (data) {
if (!data.code) {
if (data.data.applied_captcha) {
$('#captcha-area').html('<label for="captcha">验证码</label>&nbsp;&nbsp;<img src="/captcha/" id="captcha-img"><small><p></p></small><input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required><div class="help-block with-errors"></div>');
applied_captcha = true;
}
else {
$('#captcha-area').html('');
applied_captcha = false;
}
}
}
});
}
});
function refresh_captcha(){
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
$("#captcha")[0].value = "";
}
$("#captcha-img").click(function(){
refresh_captcha();
});
}); });

View File

@ -5,10 +5,11 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
var realName = $("#real_name").val(); var realName = $("#real_name").val();
var password = $("#password").val(); var password = $("#password").val();
var email = $("#email").val(); var email = $("#email").val();
var captcha = $("#captcha").val();
$.ajax({ $.ajax({
beforeSend: csrfTokenHeader, beforeSend: csrfTokenHeader,
url: "/api/register/", url: "/api/register/",
data: {username: username, real_name: realName, password: password, email: email}, data: {username: username, real_name: realName, password: password, email: email, captcha:captcha},
dataType: "json", dataType: "json",
method: "post", method: "post",
success: function (data) { success: function (data) {
@ -16,11 +17,20 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
window.location.href = "/login/"; window.location.href = "/login/";
} }
else { else {
refresh_captcha();
bsAlert(data.data); bsAlert(data.data);
} }
} }
}); });
return false; return false;
} }
}) });
function refresh_captcha() {
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
$("#captcha")[0].value = "";
}
$("#captcha-img").click(function () {
refresh_captcha();
});
}); });

View File

@ -1,5 +1,6 @@
# coding=utf-8 # coding=utf-8
import json import json
import logging
import redis import redis
from django.shortcuts import render from django.shortcuts import render
@ -21,6 +22,9 @@ from .models import Submission
from .serializers import CreateSubmissionSerializer, SubmissionSerializer, SubmissionhareSerializer from .serializers import CreateSubmissionSerializer, SubmissionSerializer, SubmissionhareSerializer
logger = logging.getLogger("app_info")
class SubmissionAPIView(APIView): class SubmissionAPIView(APIView):
@login_required @login_required
def post(self, request): def post(self, request):
@ -44,13 +48,11 @@ class SubmissionAPIView(APIView):
try: try:
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 as e:
logger.error(e)
return error_response(u"提交判题任务失败") return error_response(u"提交判题任务失败")
# 修改用户解题状态 # 修改用户解题状态
if request.user.problems_status: problems_status = json.loads(request.user.problems_status)
problems_status = json.loads(request.user.problems_status)
else:
problems_status = {}
problems_status[str(data["problem_id"])] = 2 problems_status[str(data["problem_id"])] = 2
request.user.problems_status = json.dumps(problems_status) request.user.problems_status = json.dumps(problems_status)
request.user.save() request.user.save()

View File

@ -21,9 +21,10 @@
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="confirm_password">验证码</label> <label for="captcha">验证码</label>
<img src="/captcha/" id="captcha-img"> &nbsp;&nbsp;<img src="/captcha/" id="captcha-img">
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" required> <small><p></p></small>
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -19,6 +19,7 @@
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group" id="captcha-area"></div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary">提交</button> <button type="submit" class="btn btn-primary">提交</button>
</div> </div>

View File

@ -7,7 +7,7 @@
<form id="register-form"> <form id="register-form">
<div class="form-group"> <div class="form-group">
<label for="username">用户名</label> <label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-error="请填写用户名" required autofocus> <input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-remote="/api/username_check/" data-remote-error="该用户名已被注册!" data-error="请填写用户名" required autofocus>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -17,7 +17,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">邮箱地址</label> <label for="email">邮箱地址</label>
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-error="请填写正确的邮箱地址" required> <input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-remote="/api/email_check/" data-remote-error="该邮箱已被注册!" data-error="请填写正确的邮箱地址" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -31,6 +31,12 @@
placeholder="确认密码" data-match="#password" data-match-error="两个密码不一致" data-error="请填写确认密码" required> placeholder="确认密码" data-match="#password" data-match-error="两个密码不一致" data-error="请填写确认密码" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group">
<label for="captcha">验证码</label>
&nbsp;&nbsp;<img src="/captcha/" id="captcha-img"><small><p></p></small>
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary">提交</button> <button type="submit" class="btn btn-primary">提交</button>
</div> </div>

View File

@ -2,107 +2,97 @@
{% block body %} {% block body %}
<div class="container main"> <div class="container main">
<ul class="nav nav-tabs nav-tabs-google"> <ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li> <a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li>
<li role="presentation"><a <li role="presentation"><a
href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/submissions/">我的提交</a> href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/submissions/">我的提交</a>
</li> </li>
</ul> </ul>
<h2 class="text-center">{{ contest_problem.title }}</h2> <h2 class="text-center">{{ contest_problem.title }}</h2>
<p class="text-muted text-center">发布时间 : {{ contest_problem.create_time }}&nbsp;&nbsp; <p class="text-muted text-center">发布时间 : {{ contest_problem.create_time }}&nbsp;&nbsp;
时间限制 : {{ contest_problem.time_limit }}ms&nbsp;&nbsp; 时间限制 : {{ contest_problem.time_limit }}ms&nbsp;&nbsp;
内存限制 : {{ contest_problem.memory_limit }}M 内存限制 : {{ contest_problem.memory_limit }}M
</p> </p>
<div>
<div class="problem-section">
<label class="problem-label">描述</label>
<div class="problem-detail">{{ contest_problem.description|safe }}</div>
</div>
<div class="problem-section">
<label class="problem-label">输入</label>
<p class="problem-detail">{{ contest_problem.input_description }}</p>
<div> <div>
<div class="problem-section"> <div class="problem-section">
<label class="problem-label">输出</label> <label class="problem-label">描述</label>
<p class="problem-detail">{{ contest_problem.output_description }}</p> <div class="problem-detail">{{ contest_problem.description|safe }}</div>
</div> </div>
{% for item in samples %} <div class="problem-section">
<div class="problem-section"> <label class="problem-label">输入</label>
<label class="problem-label">样例输入{{ forloop.counter }}</label>
<a href="javascript:void(0)" class="copy-sample" data-clipboard-text="{{ item.input }}">复制</a>
<pre>
{{ item.input }}</pre>
</div> <p class="problem-detail">{{ contest_problem.input_description }}</p>
<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>
<div class="problem-detail">{{ contest_problem.hint|safe }}</div>
</div>
{% endif %}
{% ifequal contest.status 0 %}
<div> <div>
<label>选择语言</label>
<div> <div class="problem-section">
<label class="radio-inline"> <label class="problem-label">输出</label>
<input type="radio" name="language" value="1" checked> C (GCC 4.8)
</label> <p class="problem-detail">{{ contest_problem.output_description }}</p>
<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 (Oracle JDK 1.7)
</label>
</div> </div>
{% for item in samples %}
<div class="problem-section">
<label class="problem-label">样例输入{{ forloop.counter }}</label>
<a href="javascript:void(0)" class="copy-sample"
data-clipboard-text="{{ item.input }}">复制</a>
<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>
<div class="problem-detail">{{ contest_problem.hint|safe }}</div>
</div>
{% endif %}
{% ifequal contest.status 0 %}
<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 (Oracle JDK 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>
{% endifequal %}
{% if show_warning %}
<div class="alert alert-success" role="alert">{{ warning }}</div>
{% endif %}
<div id="result">
</div>
<hr>
</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>
{% endifequal %}
{% if show_warning %}
<div class="alert alert-success" role="alert">{{ warning }}</div>
{% endif %}
<div id="result">
</div> </div>
<hr>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

15
tools/celeryd.conf Normal file
View File

@ -0,0 +1,15 @@
[program:celery]
command=celery worker -A judge.judger_controller --loglevel=DEBUG
directory=/root/qduoj/
user=root
numprocs=1
stdout_logfile=/root/log/celery_worker.log
stderr_logfile=/root/log/celery_worker.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 6
killasgroup=true

26
tools/supervisord.conf Normal file
View File

@ -0,0 +1,26 @@
[unix_http_server]
file=/tmp/supervisor.sock ; path to your socket file
[supervisord]
logfile=/root/log/supervisord.log ; supervisord log file
logfile_maxbytes=50MB ; maximum size of logfile before rotation
logfile_backups=10 ; number of backed up logfiles
loglevel=info ; info, debug, warn, trace
pidfile=/root/log/supervisord.pid ; pidfile location
nodaemon=false ; run supervisord as a daemon
minfds=1024 ; number of startup file descriptors
minprocs=200 ; number of process descriptors
user=root ; default user
childlogdir=/root/log/ ; where child log files will live
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use unix:// schem for a unix sockets.
[include]
files=*.conf