Merge branch 'new-arch' into otp-auth

* new-arch:
  fix error package name and add pip mirrorwq
  废弃 huey,多数据库连接的时候存在 connection 无法释放的问题,回到 celery
  修复 huey 队列不会释放数据库连接的问题,是用法不对
  修复typo
  完善 sso 登录部分
  修复typo
  规范配置文件写法;数据库用户名也在环境变量中取
  个人博客链接前面也增加图标
  修改判题机器的配置文件
  删除不再使用的配置文件

Conflicts:
	account/views.py
This commit is contained in:
virusdefender 2015-12-12 21:13:19 +08:00
commit 23f804476a
17 changed files with 91 additions and 43 deletions

2
.gitignore vendored
View File

@ -52,7 +52,7 @@ db.db
#redis dump #redis dump
*.rdb *.rdb
#*.out #*.out
db.sqlite3 *.sqlite3
.DS_Store .DS_Store
log/ log/
static/release/css static/release/css

8
account/tasks.py Normal file
View File

@ -0,0 +1,8 @@
# coding=utf-8
from celery import shared_task
from utils.mail import send_email
@shared_task
def _send_email(from_name, to_email, to_name, subject, content):
send_email(from_name, to_email, to_name, subject, content)

View File

@ -16,9 +16,10 @@ from rest_framework.response import Response
from utils.shortcuts import (serializer_invalid_response, error_response, from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, error_page, paginate, rand_str) success_response, error_page, paginate, rand_str)
from utils.captcha import Captcha from utils.captcha import Captcha
from utils.mail import send_email
from utils.otp_auth import OtpAuth from utils.otp_auth import OtpAuth
from .tasks import _send_email
from .decorators import login_required from .decorators import login_required
from .models import User, UserProfile from .models import User, UserProfile
@ -300,18 +301,21 @@ class ApplyResetPasswordAPIView(APIView):
user = User.objects.get(email=data["email"]) user = User.objects.get(email=data["email"])
except User.DoesNotExist: except User.DoesNotExist:
return error_response(u"用户不存在") return error_response(u"用户不存在")
if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60: if user.reset_password_token_create_time and (
now() - user.reset_password_token_create_time).total_seconds() < 20 * 60:
return error_response(u"20分钟内只能找回一次密码") return error_response(u"20分钟内只能找回一次密码")
user.reset_password_token = rand_str() user.reset_password_token = rand_str()
user.reset_password_token_create_time = now() user.reset_password_token_create_time = now()
user.save() user.save()
email_template = codecs.open(settings.TEMPLATES[0]["DIRS"][0] + "utils/reset_password_email.html", "r", "utf-8").read() email_template = codecs.open(settings.TEMPLATES[0]["DIRS"][0] + "utils/reset_password_email.html", "r",
"utf-8").read()
email_template = email_template.replace("{{ username }}", user.username). \ email_template = email_template.replace("{{ username }}", user.username). \
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]). \ replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]). \
replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/t/" + user.reset_password_token) replace("{{ link }}", request.scheme + "://" + request.META[
'HTTP_HOST'] + "/reset_password/t/" + user.reset_password_token)
send_email(settings.WEBSITE_INFO["website_name"], _send_email.delay(settings.WEBSITE_INFO["website_name"],
user.email, user.email,
user.username, user.username,
settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件", settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件",
@ -363,7 +367,10 @@ class SSOAPIView(APIView):
if serializer.is_valid(): if serializer.is_valid():
try: try:
user = User.objects.get(auth_token=serializer.data["token"]) user = User.objects.get(auth_token=serializer.data["token"])
return success_response({"username": user.username}) user.auth_token = None
user.save()
return success_response(
{"username": user.username, "admin_type": user.admin_type, "avatar": user.userprofile.avatar})
except User.DoesNotExist: except User.DoesNotExist:
return error_response(u"用户不存在") return error_response(u"用户不存在")
else: else:
@ -377,7 +384,8 @@ class SSOAPIView(APIView):
token = rand_str() token = rand_str()
request.user.auth_token = token request.user.auth_token = token
request.user.save() request.user.save()
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback}) return render(request, "oj/account/sso.html",
{"redirect_url": callback + "?token=" + token, "callback": callback})
def reset_password_page(request, token): def reset_password_page(request, token):

View File

View File

@ -3,6 +3,6 @@ ENV PYTHONBUFFERED 1
RUN mkdir -p /code/log /code/test_case /code/upload 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 -i http://pypi.douban.com/simple -r requirements.txt --trusted-host pypi.douban.com
EXPOSE 8010 EXPOSE 8010
CMD supervisord CMD supervisord

View File

@ -11,4 +11,5 @@ supervisor
pillow pillow
jsonfield jsonfield
Envelopes Envelopes
huey celery
django-celery

View File

@ -1,6 +1,6 @@
[program:mq] [program:task_queue]
command=python manage.py run_huey command=python manage.py celeryd -B -l DEBUG
directory=/code/ directory=/code/
user=root user=root

View File

@ -22,7 +22,7 @@ class GroupAPIViewBase(object):
def get_group(self, request, group_id): def get_group(self, request, group_id):
""" """
根据group_id查询指定的小组的信息结合判断用户权限 根据group_id查询指定的小组的信息结合判断用户权限
管理员可以查询所有的小组其他用户查询自己创建的自傲 管理员可以查询所有的小组其他用户查询自己创建的
""" """
if request.user.admin_type == SUPER_ADMIN: if request.user.admin_type == SUPER_ADMIN:
group = Group.objects.get(id=group_id) group = Group.objects.get(id=group_id)

View File

@ -32,7 +32,7 @@ class JudgeDispatcher(object):
if servers.exists(): if servers.exists():
return servers.first() return servers.first()
def judge(self, is_waiting_task=False): def judge(self):
self.submission.judge_start_time = int(time.time() * 1000) self.submission.judge_start_time = int(time.time() * 1000)
with transaction.atomic(): with transaction.atomic():
@ -89,7 +89,7 @@ class JudgeDispatcher(object):
submission = Submission.objects.get(id=waiting_submission.submission_id) submission = Submission.objects.get(id=waiting_submission.submission_id)
waiting_submission.delete() waiting_submission.delete()
_judge(submission, time_limit=waiting_submission.time_limit, _judge.delay(submission, time_limit=waiting_submission.time_limit,
memory_limit=waiting_submission.memory_limit, test_case_id=waiting_submission.test_case_id, memory_limit=waiting_submission.memory_limit, test_case_id=waiting_submission.test_case_id,
is_waiting_task=True) is_waiting_task=True)

View File

@ -7,3 +7,8 @@
|___/ |___/ |_| |___/ |___/ |_|
https://github.com/QingdaoU/OnlineJudge https://github.com/QingdaoU/OnlineJudge
""" """
from __future__ import absolute_import
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

19
oj/celery.py Normal file
View File

@ -0,0 +1,19 @@
from __future__ import absolute_import
import os
from celery import Celery
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oj.settings')
from django.conf import settings
app = Celery('oj')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
# load task modules from all registered Django app configs.
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

View File

@ -28,6 +28,12 @@ REDIS_QUEUE = {
"db": 2 "db": 2
} }
# for celery
BROKER_URL = 'redis://%s:%s/%s' % (REDIS_QUEUE["host"], str(REDIS_QUEUE["port"]), str(REDIS_QUEUE["db"]))
ACCEPT_CONTENT = ['json']
DEBUG = True DEBUG = True
ALLOWED_HOSTS = [] ALLOWED_HOSTS = []

View File

@ -37,6 +37,12 @@ REDIS_QUEUE = {
"db": 2 "db": 2
} }
# for celery
BROKER_URL = 'redis://%s:%s/%s' % (REDIS_QUEUE["host"], str(REDIS_QUEUE["port"]), str(REDIS_QUEUE["db"]))
ACCEPT_CONTENT = ['json']
DEBUG = False DEBUG = False
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']

View File

@ -10,7 +10,7 @@ https://docs.djangoproject.com/en/1.8/topics/settings/
For the full list of settings and their values, see For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/ https://docs.djangoproject.com/en/1.8/ref/settings/
""" """
from __future__ import absolute_import
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os import os
@ -22,6 +22,9 @@ if ENV == "local":
elif ENV == "server": elif ENV == "server":
from .server_settings import * from .server_settings import *
import djcelery
djcelery.setup_loader()
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -53,7 +56,7 @@ INSTALLED_APPS = (
'judge_dispatcher', 'judge_dispatcher',
'rest_framework', 'rest_framework',
'huey.djhuey', 'djcelery',
) )
if DEBUG: if DEBUG:
@ -186,14 +189,6 @@ WEBSITE_INFO = {"website_name": "qduoj",
"website_footer": u"青岛大学信息工程学院 创新实验室 <a href=\"http://www.miibeian.gov.cn/\">京ICP备15062075号-1</a>", "website_footer": u"青岛大学信息工程学院 创新实验室 <a href=\"http://www.miibeian.gov.cn/\">京ICP备15062075号-1</a>",
"url": "https://qduoj.com"} "url": "https://qduoj.com"}
HUEY = {
'backend': 'huey.backends.redis_backend',
'name': 'task_queue',
'connection': {'host': REDIS_QUEUE["host"], 'port': REDIS_QUEUE["port"], 'db': REDIS_QUEUE["db"]},
'always_eager': False, # Defaults to False when running via manage.py run_huey
# Options to pass into the consumer when running ``manage.py run_huey``
'consumer_options': {'workers': 50},
}
SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com", SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com",
"email": "noreply@qduoj.com", "email": "noreply@qduoj.com",

View File

@ -2,8 +2,8 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
var applied_captcha = false; var applied_captcha = false;
$('form').validator().on('submit', function (e) { $('form').validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) { if (!e.isDefaultPrevented()) {
var loca = location.href.split("/"); var splited_location = location.href.split("/");
var token = loca[loca.length-2]; var token = splited_location[splited_location.length-2];
var captcha = $("#captcha").val(); var captcha = $("#captcha").val();
var password = $("#new_password").val(); var password = $("#new_password").val();
$.ajax({ $.ajax({

View File

@ -1,9 +1,9 @@
# coding=utf-8 # coding=utf-8
from huey.djhuey import task from __future__ import absolute_import
from celery import shared_task
from judge_dispatcher.tasks import JudgeDispatcher from judge_dispatcher.tasks import JudgeDispatcher
@task() @shared_task
def _judge(submission, time_limit, memory_limit, test_case_id, is_waiting_task=False): def _judge(submission, time_limit, memory_limit, test_case_id):
JudgeDispatcher(submission, time_limit, memory_limit, test_case_id).judge(is_waiting_task) JudgeDispatcher(submission, time_limit, memory_limit, test_case_id).judge()

View File

@ -43,7 +43,7 @@ class SubmissionAPIView(APIView):
problem_id=problem.id) problem_id=problem.id)
try: try:
_judge(submission, problem.time_limit, problem.memory_limit, problem.test_case_id) _judge.delay(submission, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception as e: except Exception as e:
logger.error(e) logger.error(e)
return error_response(u"提交判题任务失败") return error_response(u"提交判题任务失败")
@ -88,7 +88,7 @@ class ContestSubmissionAPIView(APIView):
code=data["code"], code=data["code"],
problem_id=problem.id) problem_id=problem.id)
try: try:
_judge(submission, problem.time_limit, problem.memory_limit, problem.test_case_id) _judge.delay(submission, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception as e: except Exception as e:
logger.error(e) logger.error(e)
return error_response(u"提交判题任务失败") return error_response(u"提交判题任务失败")
@ -273,7 +273,7 @@ class SubmissionRejudgeAdminAPIView(APIView):
except Problem.DoesNotExist: except Problem.DoesNotExist:
return error_response(u"题目不存在") return error_response(u"题目不存在")
try: try:
_judge(submission, problem.time_limit, problem.memory_limit, problem.test_case_id) _judge.delay(submission, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception as e: except Exception as e:
logger.error(e) logger.error(e)
return error_response(u"提交判题任务失败") return error_response(u"提交判题任务失败")