Accept Merge Request #331 dev -> master : (dev -> master)
Merge Request: dev -> master Created By: @virusdefender Accepted By: @virusdefender URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/331
This commit is contained in:
20
account/migrations/0015_userprofile_student_id.py
Normal file
20
account/migrations/0015_userprofile_student_id.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2015-12-08 06:22
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0014_auto_20151110_1037'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userprofile',
|
||||||
|
name='student_id',
|
||||||
|
field=models.CharField(blank=True, max_length=15, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -70,7 +70,7 @@ class UserProfile(models.Model):
|
|||||||
problems_status = JSONField(default={})
|
problems_status = JSONField(default={})
|
||||||
phone_number = models.CharField(max_length=15, blank=True, null=True)
|
phone_number = models.CharField(max_length=15, blank=True, null=True)
|
||||||
school = models.CharField(max_length=200, blank=True, null=True)
|
school = models.CharField(max_length=200, blank=True, null=True)
|
||||||
|
student_id = models.CharField(max_length=15, blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "user_profile"
|
db_table = "user_profile"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class UserRegisterSerializer(serializers.Serializer):
|
|||||||
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)
|
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||||
|
student_id = serializers.CharField(max_length=15, required=False, default=None)
|
||||||
|
|
||||||
|
|
||||||
class UserChangePasswordSerializer(serializers.Serializer):
|
class UserChangePasswordSerializer(serializers.Serializer):
|
||||||
@@ -74,6 +75,7 @@ class EditUserProfileSerializer(serializers.Serializer):
|
|||||||
codeforces_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
codeforces_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
||||||
school = serializers.CharField(max_length=200, required=False, allow_blank=True, default='')
|
school = serializers.CharField(max_length=200, required=False, allow_blank=True, default='')
|
||||||
phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='')
|
phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='')
|
||||||
|
student_id = serializers.CharField(max_length=15, required=False, default="")
|
||||||
|
|
||||||
|
|
||||||
class UserProfileSerializer(serializers.ModelSerializer):
|
class UserProfileSerializer(serializers.ModelSerializer):
|
||||||
@@ -81,4 +83,4 @@ class UserProfileSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = UserProfile
|
model = UserProfile
|
||||||
fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username",
|
fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username",
|
||||||
"rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school"]
|
"rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school", "student_id"]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ 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 mail.tasks import send_email
|
from utils.mail import send_email
|
||||||
|
|
||||||
from .decorators import login_required
|
from .decorators import login_required
|
||||||
from .models import User, UserProfile
|
from .models import User, UserProfile
|
||||||
@@ -97,7 +97,7 @@ class UserRegisterAPIView(APIView):
|
|||||||
email=data["email"])
|
email=data["email"])
|
||||||
user.set_password(data["password"])
|
user.set_password(data["password"])
|
||||||
user.save()
|
user.save()
|
||||||
UserProfile.objects.create(user=user, school=data["school"])
|
UserProfile.objects.create(user=user, school=data["school"], student_id=data["student_id"])
|
||||||
return success_response(u"注册成功!")
|
return success_response(u"注册成功!")
|
||||||
else:
|
else:
|
||||||
return serializer_invalid_response(serializer)
|
return serializer_invalid_response(serializer)
|
||||||
@@ -262,6 +262,7 @@ class UserProfileAPIView(APIView):
|
|||||||
user_profile.codeforces_username = data["codeforces_username"]
|
user_profile.codeforces_username = data["codeforces_username"]
|
||||||
user_profile.blog = data["blog"]
|
user_profile.blog = data["blog"]
|
||||||
user_profile.school = data["school"]
|
user_profile.school = data["school"]
|
||||||
|
user_profile.student_id = data["student_id"]
|
||||||
user_profile.phone_number = data["phone_number"]
|
user_profile.phone_number = data["phone_number"]
|
||||||
user_profile.save()
|
user_profile.save()
|
||||||
return success_response(u"修改成功")
|
return success_response(u"修改成功")
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ from django.core.urlresolvers import reverse
|
|||||||
|
|
||||||
from utils.shortcuts import error_response, error_page
|
from utils.shortcuts import error_response, error_page
|
||||||
|
|
||||||
from account.models import SUPER_ADMIN
|
from account.models import SUPER_ADMIN, ADMIN
|
||||||
from .models import (Contest, PASSWORD_PROTECTED_CONTEST, PUBLIC_CONTEST, GROUP_CONTEST,
|
from .models import (Contest, PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST, PUBLIC_CONTEST, GROUP_CONTEST,
|
||||||
CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY)
|
CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY)
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +57,10 @@ def check_user_contest_permission(func):
|
|||||||
|
|
||||||
if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by:
|
if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
if request.user.admin_type == ADMIN:
|
||||||
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest in contest_set:
|
||||||
|
return func(*args, **kwargs)
|
||||||
# 管理员可见隐藏的比赛,已经先判断了身份
|
# 管理员可见隐藏的比赛,已经先判断了身份
|
||||||
if not contest.visible:
|
if not contest.visible:
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
@@ -84,6 +87,15 @@ def check_user_contest_permission(func):
|
|||||||
return render(request, "oj/contest/no_contest_permission.html",
|
return render(request, "oj/contest/no_contest_permission.html",
|
||||||
{"reason": "group_limited", "show_tab": False, "contest": contest})
|
{"reason": "group_limited", "show_tab": False, "contest": contest})
|
||||||
|
|
||||||
|
if contest.contest_type == PASSWORD_PROTECTED_GROUP_CONTEST:
|
||||||
|
if not contest.groups.filter(id__in=request.user.group_set.all()).exists():
|
||||||
|
if contest.id not in request.session.get("contests", []):
|
||||||
|
if request.is_ajax():
|
||||||
|
return error_response(u"请先输入密码")
|
||||||
|
else:
|
||||||
|
return render(request, "oj/contest/no_contest_permission.html",
|
||||||
|
{"reason": "password_protect", "show_tab": False, "contest": contest})
|
||||||
|
|
||||||
# 比赛没有开始
|
# 比赛没有开始
|
||||||
if contest.status == CONTEST_NOT_START:
|
if contest.status == CONTEST_NOT_START:
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ 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 jsonfield import JSONField
|
||||||
from judge.judger.result import result
|
from judge.result import result
|
||||||
|
|
||||||
|
|
||||||
GROUP_CONTEST = 0
|
GROUP_CONTEST = 0
|
||||||
PUBLIC_CONTEST = 1
|
PUBLIC_CONTEST = 1
|
||||||
PASSWORD_PROTECTED_CONTEST = 2
|
PASSWORD_PROTECTED_CONTEST = 2
|
||||||
|
PASSWORD_PROTECTED_GROUP_CONTEST = 3
|
||||||
|
|
||||||
CONTEST_NOT_START = 1
|
CONTEST_NOT_START = 1
|
||||||
CONTEST_ENDED = -1
|
CONTEST_ENDED = -1
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ from utils.shortcuts import (serializer_invalid_response, error_response,
|
|||||||
success_response, paginate, error_page, paginate_data)
|
success_response, paginate, error_page, paginate_data)
|
||||||
from account.models import SUPER_ADMIN, User
|
from account.models import SUPER_ADMIN, User
|
||||||
from account.decorators import login_required, super_admin_required
|
from account.decorators import login_required, super_admin_required
|
||||||
from group.models import Group
|
from group.models import Group, AdminGroupRelation, UserGroupRelation
|
||||||
from utils.cache import get_cache_redis
|
from utils.cache import get_cache_redis
|
||||||
from submission.models import Submission
|
from submission.models import Submission
|
||||||
from problem.models import Problem
|
from problem.models import Problem
|
||||||
from .models import (Contest, ContestProblem, CONTEST_ENDED,
|
from .models import (Contest, ContestProblem, CONTEST_ENDED,
|
||||||
CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank)
|
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, PASSWORD_PROTECTED_GROUP_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,
|
||||||
CreateContestProblemSerializer, ContestProblemSerializer,
|
CreateContestProblemSerializer, ContestProblemSerializer,
|
||||||
@@ -50,11 +50,11 @@ class ContestAdminAPIView(APIView):
|
|||||||
if request.user.admin_type != SUPER_ADMIN:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
return error_response(u"只有超级管理员才可创建公开赛")
|
return error_response(u"只有超级管理员才可创建公开赛")
|
||||||
|
|
||||||
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
|
if data["contest_type"] in [PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]:
|
||||||
if not data["password"]:
|
if not data["password"]:
|
||||||
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
|
return error_response(u"此比赛为有密码的比赛,密码不可为空")
|
||||||
# 没有密码的公开赛 没有密码的小组赛
|
# 没有密码的公开赛 没有密码的小组赛
|
||||||
elif data["contest_type"] == GROUP_CONTEST:
|
if data["contest_type"] == GROUP_CONTEST or data["contest_type"] == PASSWORD_PROTECTED_GROUP_CONTEST:
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
groups = Group.objects.filter(id__in=data["groups"])
|
groups = Group.objects.filter(id__in=data["groups"])
|
||||||
else:
|
else:
|
||||||
@@ -91,7 +91,9 @@ class ContestAdminAPIView(APIView):
|
|||||||
try:
|
try:
|
||||||
# 超级管理员可以编辑所有的
|
# 超级管理员可以编辑所有的
|
||||||
contest = Contest.objects.get(id=data["id"])
|
contest = Contest.objects.get(id=data["id"])
|
||||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest not in contest_set:
|
||||||
return error_response(u"无权访问!")
|
return error_response(u"无权访问!")
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"该比赛不存在!")
|
return error_response(u"该比赛不存在!")
|
||||||
@@ -107,7 +109,7 @@ class ContestAdminAPIView(APIView):
|
|||||||
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
|
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
|
||||||
if not data["password"]:
|
if not data["password"]:
|
||||||
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
|
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
|
||||||
elif data["contest_type"] == GROUP_CONTEST:
|
elif data["contest_type"] in [GROUP_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]:
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
groups = Group.objects.filter(id__in=data["groups"])
|
groups = Group.objects.filter(id__in=data["groups"])
|
||||||
else:
|
else:
|
||||||
@@ -151,16 +153,18 @@ class ContestAdminAPIView(APIView):
|
|||||||
# 普通管理员只能获取自己创建的题目
|
# 普通管理员只能获取自己创建的题目
|
||||||
# 超级管理员可以获取全部的题目
|
# 超级管理员可以获取全部的题目
|
||||||
contest = Contest.objects.get(id=contest_id)
|
contest = Contest.objects.get(id=contest_id)
|
||||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
return error_response(u"题目不存在")
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest not in contest_set:
|
||||||
|
return error_response(u"比赛不存在")
|
||||||
return success_response(ContestSerializer(contest).data)
|
return success_response(ContestSerializer(contest).data)
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"题目不存在")
|
return error_response(u"比赛不存在")
|
||||||
|
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
contest = Contest.objects.all().order_by("-create_time")
|
contest = Contest.objects.all().order_by("-create_time")
|
||||||
else:
|
else:
|
||||||
contest = Contest.objects.filter(created_by=request.user).order_by("-create_time")
|
contest = Contest.objects.filter(groups__in=request.user.managed_groups.all()).distinct().order_by("-create_time")
|
||||||
visible = request.GET.get("visible", None)
|
visible = request.GET.get("visible", None)
|
||||||
if visible:
|
if visible:
|
||||||
contest = contest.filter(visible=(visible == "true"))
|
contest = contest.filter(visible=(visible == "true"))
|
||||||
@@ -184,7 +188,9 @@ class ContestProblemAdminAPIView(APIView):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
try:
|
try:
|
||||||
contest = Contest.objects.get(id=data["contest_id"])
|
contest = Contest.objects.get(id=data["contest_id"])
|
||||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest not in contest_set:
|
||||||
return error_response(u"比赛不存在")
|
return error_response(u"比赛不存在")
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"比赛不存在")
|
return error_response(u"比赛不存在")
|
||||||
@@ -362,7 +368,10 @@ def contest_problem_page(request, contest_id, contest_problem_id):
|
|||||||
request.user.admin_type == SUPER_ADMIN or \
|
request.user.admin_type == SUPER_ADMIN or \
|
||||||
request.user == contest.created_by:
|
request.user == contest.created_by:
|
||||||
show_submit_code_area = True
|
show_submit_code_area = True
|
||||||
|
else:
|
||||||
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest in contest_set:
|
||||||
|
show_submit_code_area = True
|
||||||
return render(request, "oj/problem/contest_problem.html", {"problem": problem,
|
return render(request, "oj/problem/contest_problem.html", {"problem": problem,
|
||||||
"contest": contest,
|
"contest": contest,
|
||||||
"samples": json.loads(problem.samples),
|
"samples": json.loads(problem.samples),
|
||||||
|
|||||||
@@ -19,4 +19,5 @@ RUN cd lrun && make install
|
|||||||
RUN mkdir -p /var/judger/run/ && mkdir /var/judger/test_case/ && mkdir /var/judger/code/
|
RUN mkdir -p /var/judger/run/ && mkdir /var/judger/test_case/ && mkdir /var/judger/code/
|
||||||
RUN chmod -R 777 /var/judger/run/
|
RUN chmod -R 777 /var/judger/run/
|
||||||
COPY policy /var/judger/run/
|
COPY policy /var/judger/run/
|
||||||
WORKDIR /var/judger/code/
|
WORKDIR /var/judger/code/judge/
|
||||||
|
CMD python server.py
|
||||||
@@ -4,7 +4,6 @@ redis
|
|||||||
django-redis-sessions
|
django-redis-sessions
|
||||||
djangorestframework
|
djangorestframework
|
||||||
django-rest-swagger
|
django-rest-swagger
|
||||||
celery
|
|
||||||
gunicorn
|
gunicorn
|
||||||
coverage
|
coverage
|
||||||
django-extensions
|
django-extensions
|
||||||
@@ -12,3 +11,4 @@ supervisor
|
|||||||
pillow
|
pillow
|
||||||
jsonfield
|
jsonfield
|
||||||
Envelopes
|
Envelopes
|
||||||
|
huey
|
||||||
@@ -23,4 +23,4 @@ serverurl=unix:///tmp/supervisor.sock ; use unix:// schem for a unix sockets.
|
|||||||
|
|
||||||
[include]
|
[include]
|
||||||
|
|
||||||
files=gunicorn.conf mq.conf
|
files=gunicorn.conf task_queue.conf
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
[program:mq]
|
[program:mq]
|
||||||
|
|
||||||
command=python manage.py runscript mq
|
command=python manage.py run_huey
|
||||||
|
|
||||||
directory=/code/
|
directory=/code/
|
||||||
user=root
|
user=root
|
||||||
numprocs=1
|
numprocs=1
|
||||||
stdout_logfile=/code/log/mq.log
|
stdout_logfile=/code/log/task_queue.log
|
||||||
stderr_logfile=/code/log/mq.log
|
stderr_logfile=/code/log/task_queue.log
|
||||||
autostart=true
|
autostart=true
|
||||||
autorestart=true
|
autorestart=true
|
||||||
startsecs=5
|
startsecs=5
|
||||||
20
group/migrations/0006_auto_20151209_1834.py
Normal file
20
group/migrations/0006_auto_20151209_1834.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2015-12-09 10:34
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('group', '0005_joingrouprequest_accepted'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='group',
|
||||||
|
old_name='admin',
|
||||||
|
new_name='created_by',
|
||||||
|
),
|
||||||
|
]
|
||||||
38
group/migrations/0007_auto_20151209_1836.py
Normal file
38
group/migrations/0007_auto_20151209_1836.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2015-12-09 10:36
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('group', '0006_auto_20151209_1834'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AdminGroupRelation',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='group.Group')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'admin_group_relation',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='group',
|
||||||
|
name='admin',
|
||||||
|
field=models.ManyToManyField(related_name='managed_groups', through='group.AdminGroupRelation', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='admingrouprelation',
|
||||||
|
unique_together=set([('user', 'group')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -8,10 +8,11 @@ class Group(models.Model):
|
|||||||
name = models.CharField(max_length=30, unique=True)
|
name = models.CharField(max_length=30, unique=True)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
create_time = models.DateTimeField(auto_now_add=True)
|
create_time = models.DateTimeField(auto_now_add=True)
|
||||||
admin = models.ForeignKey(User, related_name="my_groups")
|
created_by = models.ForeignKey(User, related_name="my_groups")
|
||||||
# 0是公开 1是需要申请后加入 2是不允许任何人加入
|
# 0是公开 1是需要申请后加入 2是不允许任何人加入
|
||||||
join_group_setting = models.IntegerField(default=1)
|
join_group_setting = models.IntegerField(default=1)
|
||||||
members = models.ManyToManyField(User, through="UserGroupRelation")
|
members = models.ManyToManyField(User, through="UserGroupRelation")
|
||||||
|
admin = models.ManyToManyField(User, through="AdminGroupRelation", related_name="managed_groups")
|
||||||
# 解散小组后,这一项改为False
|
# 解散小组后,这一项改为False
|
||||||
visible = models.BooleanField(default=True)
|
visible = models.BooleanField(default=True)
|
||||||
|
|
||||||
@@ -29,6 +30,16 @@ class UserGroupRelation(models.Model):
|
|||||||
unique_together = ("group", "user")
|
unique_together = ("group", "user")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AdminGroupRelation(models.Model):
|
||||||
|
user = models.ForeignKey(User)
|
||||||
|
group = models.ForeignKey(Group)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "admin_group_relation"
|
||||||
|
unique_together = ("user", "group")
|
||||||
|
|
||||||
|
|
||||||
class JoinGroupRequest(models.Model):
|
class JoinGroupRequest(models.Model):
|
||||||
group = models.ForeignKey(Group)
|
group = models.ForeignKey(Group)
|
||||||
user = models.ForeignKey(User, related_name="my_join_group_requests")
|
user = models.ForeignKey(User, related_name="my_join_group_requests")
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class EditGroupSerializer(serializers.Serializer):
|
|||||||
name = serializers.CharField(max_length=20)
|
name = serializers.CharField(max_length=20)
|
||||||
description = serializers.CharField(max_length=300)
|
description = serializers.CharField(max_length=300)
|
||||||
join_group_setting = serializers.IntegerField()
|
join_group_setting = serializers.IntegerField()
|
||||||
|
visible = serializers.BooleanField()
|
||||||
|
|
||||||
|
|
||||||
class CreateJoinGroupRequestSerializer(serializers.Serializer):
|
class CreateJoinGroupRequestSerializer(serializers.Serializer):
|
||||||
@@ -74,3 +75,7 @@ class EditGroupMemberSerializer(serializers.Serializer):
|
|||||||
class PutJoinGroupRequestSerializer(serializers.Serializer):
|
class PutJoinGroupRequestSerializer(serializers.Serializer):
|
||||||
request_id = serializers.IntegerField()
|
request_id = serializers.IntegerField()
|
||||||
status = serializers.BooleanField()
|
status = serializers.BooleanField()
|
||||||
|
|
||||||
|
class GroupPromoteAdminSerializer(serializers.Serializer):
|
||||||
|
user_id = serializers.IntegerField()
|
||||||
|
group_id = serializers.IntegerField()
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ from django.db import IntegrityError
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate, error_page
|
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate, error_page
|
||||||
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
|
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User
|
||||||
from account.decorators import login_required
|
from account.decorators import login_required
|
||||||
|
|
||||||
from .models import Group, JoinGroupRequest, UserGroupRelation
|
from .models import Group, JoinGroupRequest, UserGroupRelation, AdminGroupRelation
|
||||||
from .serializers import (CreateGroupSerializer, EditGroupSerializer,
|
from .serializers import (CreateGroupSerializer, EditGroupSerializer,
|
||||||
CreateJoinGroupRequestSerializer, GroupSerializer,
|
CreateJoinGroupRequestSerializer, GroupSerializer,
|
||||||
GroupMemberSerializer, EditGroupMemberSerializer,
|
GroupMemberSerializer, EditGroupMemberSerializer,
|
||||||
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer)
|
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer, GroupPromoteAdminSerializer)
|
||||||
from announcement.models import Announcement
|
from announcement.models import Announcement
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
@@ -25,7 +25,7 @@ class GroupAPIViewBase(object):
|
|||||||
管理员可以查询所有的小组,其他用户查询自己创建的自傲组
|
管理员可以查询所有的小组,其他用户查询自己创建的自傲组
|
||||||
"""
|
"""
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
group = Group.objects.get(id=group_id, visible=True)
|
group = Group.objects.get(id=group_id)
|
||||||
else:
|
else:
|
||||||
group = Group.objects.get(id=group_id, visible=True, admin=request.user)
|
group = Group.objects.get(id=group_id, visible=True, admin=request.user)
|
||||||
return group
|
return group
|
||||||
@@ -36,7 +36,7 @@ class GroupAPIViewBase(object):
|
|||||||
如果是管理员,就返回他创建的全部小组
|
如果是管理员,就返回他创建的全部小组
|
||||||
"""
|
"""
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
groups = Group.objects.filter(visible=True)
|
groups = Group.objects.filter()
|
||||||
else:
|
else:
|
||||||
groups = Group.objects.filter(admin=request.user, visible=True)
|
groups = Group.objects.filter(admin=request.user, visible=True)
|
||||||
return groups
|
return groups
|
||||||
@@ -57,9 +57,10 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase):
|
|||||||
group = Group.objects.create(name=data["name"],
|
group = Group.objects.create(name=data["name"],
|
||||||
description=data["description"],
|
description=data["description"],
|
||||||
join_group_setting=data["join_group_setting"],
|
join_group_setting=data["join_group_setting"],
|
||||||
admin=request.user)
|
created_by=request.user)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
return error_response(u"小组名已经存在")
|
return error_response(u"小组名已经存在")
|
||||||
|
AdminGroupRelation.objects.create(group=group, user=request.user)
|
||||||
return success_response(GroupSerializer(group).data)
|
return success_response(GroupSerializer(group).data)
|
||||||
else:
|
else:
|
||||||
return serializer_invalid_response(serializer)
|
return serializer_invalid_response(serializer)
|
||||||
@@ -82,6 +83,7 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase):
|
|||||||
group.name = data["name"]
|
group.name = data["name"]
|
||||||
group.description = data["description"]
|
group.description = data["description"]
|
||||||
group.join_group_setting = data["join_group_setting"]
|
group.join_group_setting = data["join_group_setting"]
|
||||||
|
group.visible = data["visible"]
|
||||||
group.save()
|
group.save()
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
return error_response(u"小组名已经存在")
|
return error_response(u"小组名已经存在")
|
||||||
@@ -132,8 +134,13 @@ class GroupMemberAdminAPIView(APIView, GroupAPIViewBase):
|
|||||||
group = self.get_group(request, group_id)
|
group = self.get_group(request, group_id)
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
return error_response(u"小组不存在")
|
return error_response(u"小组不存在")
|
||||||
|
admin_only = request.GET.get("admin_only", None)
|
||||||
|
if admin_only:
|
||||||
|
members = AdminGroupRelation.objects.filter(group=group)
|
||||||
|
else:
|
||||||
|
members = UserGroupRelation.objects.filter(group=group)
|
||||||
|
|
||||||
return paginate(request, UserGroupRelation.objects.filter(group=group), GroupMemberSerializer)
|
return paginate(request, members, GroupMemberSerializer)
|
||||||
|
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
"""
|
"""
|
||||||
@@ -217,7 +224,7 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
|
|||||||
---
|
---
|
||||||
response_serializer: JoinGroupRequestSerializer
|
response_serializer: JoinGroupRequestSerializer
|
||||||
"""
|
"""
|
||||||
requests = JoinGroupRequest.objects.filter(group=Group.objects.filter(admin=request.user, visible=True),
|
requests = JoinGroupRequest.objects.filter(group__in=Group.objects.filter(admin=request.user, visible=True),
|
||||||
status=False)
|
status=False)
|
||||||
return paginate(request, requests, JoinGroupRequestSerializer)
|
return paginate(request, requests, JoinGroupRequestSerializer)
|
||||||
|
|
||||||
@@ -292,7 +299,12 @@ def group_page(request, group_id):
|
|||||||
group = Group.objects.get(id=group_id, visible=True)
|
group = Group.objects.get(id=group_id, visible=True)
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
return error_page(request, u"小组不存在")
|
return error_page(request, u"小组不存在")
|
||||||
return render(request, "oj/group/group.html", {"group": group})
|
joined = True
|
||||||
|
try:
|
||||||
|
UserGroupRelation.objects.get(user=request.user, group=group)
|
||||||
|
except UserGroupRelation.DoesNotExist:
|
||||||
|
joined = False
|
||||||
|
return render(request, "oj/group/group.html", {"group": group, "joined": joined})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@@ -314,3 +326,30 @@ def application_page(request, request_id):
|
|||||||
return error_page(request, u"申请不存在")
|
return error_page(request, u"申请不存在")
|
||||||
return render(request, "oj/group/my_application.html",
|
return render(request, "oj/group/my_application.html",
|
||||||
{"application": application})
|
{"application": application})
|
||||||
|
|
||||||
|
|
||||||
|
class GroupPrometAdminAPIView(APIView):
|
||||||
|
def post(self, request):
|
||||||
|
"""
|
||||||
|
创建小组管理员的api
|
||||||
|
---
|
||||||
|
request_serializer: GroupPromoteAdminSerializer
|
||||||
|
"""
|
||||||
|
serializer = GroupPromoteAdminSerializer(data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
data = serializer.data
|
||||||
|
try:
|
||||||
|
group = Group.objects.get(id=data["group_id"])
|
||||||
|
except Group.DoesNotExist:
|
||||||
|
return error_response(u"小组不存在")
|
||||||
|
try:
|
||||||
|
user = User.objects.get(id=data["user_id"])
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return error_response(u"用户不存在")
|
||||||
|
try:
|
||||||
|
AdminGroupRelation.objects.create(user=user, group=group)
|
||||||
|
except IntegrityError:
|
||||||
|
return error_response(u"该用户已经是管理员了")
|
||||||
|
return success_response(u"操作成功")
|
||||||
|
else:
|
||||||
|
return serializer_invalid_response(serializer)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import MySQLdb
|
|
||||||
|
|
||||||
from client import JudgeClient
|
|
||||||
from language import languages
|
|
||||||
from compiler import compile_
|
|
||||||
from result import result
|
|
||||||
from settings import judger_workspace, submission_db
|
|
||||||
from logger import logger
|
|
||||||
|
|
||||||
|
|
||||||
# 简单的解析命令行参数
|
|
||||||
# 参数有 -solution_id -time_limit -memory_limit -test_case_id
|
|
||||||
# 获取到的值是['xxx.py', '-solution_id', '1111', '-time_limit', '1000', '-memory_limit', '100', '-test_case_id', 'aaaa']
|
|
||||||
args = sys.argv
|
|
||||||
submission_id = args[2]
|
|
||||||
time_limit = args[4]
|
|
||||||
memory_limit = args[6]
|
|
||||||
test_case_id = args[8]
|
|
||||||
|
|
||||||
|
|
||||||
def db_conn():
|
|
||||||
return MySQLdb.connect(db=submission_db["db"],
|
|
||||||
user=submission_db["user"],
|
|
||||||
passwd=submission_db["password"],
|
|
||||||
host=submission_db["host"],
|
|
||||||
port=submission_db["port"], charset="utf8")
|
|
||||||
|
|
||||||
|
|
||||||
conn = db_conn()
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute("select language, code from submission where id = %s", (submission_id,))
|
|
||||||
data = cur.fetchall()
|
|
||||||
if not data:
|
|
||||||
exit()
|
|
||||||
language_code = data[0][0]
|
|
||||||
code = data[0][1]
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
# 将代码写入文件
|
|
||||||
language = languages[language_code]
|
|
||||||
src_path = judger_workspace + "run/" + language["src_name"]
|
|
||||||
f = open(src_path, "w")
|
|
||||||
f.write(code.encode("utf8"))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
# 编译
|
|
||||||
try:
|
|
||||||
exe_path = compile_(language, src_path, judger_workspace + "run/")
|
|
||||||
except Exception as e:
|
|
||||||
print e
|
|
||||||
conn = db_conn()
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute("update submission set result=%s, info=%s where id=%s",
|
|
||||||
(result["compile_error"], str(e), submission_id))
|
|
||||||
conn.commit()
|
|
||||||
exit()
|
|
||||||
|
|
||||||
# 运行
|
|
||||||
try:
|
|
||||||
client = JudgeClient(language_code=language_code,
|
|
||||||
exe_path=exe_path,
|
|
||||||
max_cpu_time=int(time_limit),
|
|
||||||
max_real_time=int(time_limit) * 2,
|
|
||||||
max_memory=int(memory_limit),
|
|
||||||
test_case_dir=judger_workspace + "test_case/" + test_case_id + "/")
|
|
||||||
judge_result = {"result": result["accepted"], "info": client.run(), "accepted_answer_time": None}
|
|
||||||
|
|
||||||
for item in judge_result["info"]:
|
|
||||||
if item["result"]:
|
|
||||||
judge_result["result"] = item["result"]
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
l = sorted(judge_result["info"], key=lambda k: k["cpu_time"])
|
|
||||||
judge_result["accepted_answer_time"] = l[-1]["cpu_time"]
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(e)
|
|
||||||
conn = db_conn()
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute("update submission set result=%s, info=%s where id=%s", (result["system_error"], str(e), submission_id))
|
|
||||||
conn.commit()
|
|
||||||
exit()
|
|
||||||
|
|
||||||
conn = db_conn()
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute("update submission set result=%s, info=%s, accepted_answer_time=%s where id=%s",
|
|
||||||
(judge_result["result"], json.dumps(judge_result["info"]), judge_result["accepted_answer_time"],
|
|
||||||
submission_id))
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
celery -A judge.controller worker -l DEBUG
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from celery import Celery, platforms
|
|
||||||
from .settings import redis_config
|
|
||||||
|
|
||||||
app = Celery("judge", broker='redis://%s:%s/%s' % (redis_config["host"], redis_config["port"], redis_config["db"]),
|
|
||||||
include=["judge.judger_controller.tasks"])
|
|
||||||
|
|
||||||
platforms.C_FORCE_ROOT =True
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
"""
|
|
||||||
注意:
|
|
||||||
此文件包含 celery 的部分配置,但是 celery 并不是运行在docker 中的,所以本配置文件中的 redis和 MySQL 的地址就应该是
|
|
||||||
运行 redis 和 MySQL 的 docker 容器的地址了。怎么获取这个地址见帮助文档。测试用例的路径和源代码路径同理。
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
# 这个redis 是 celery 使用的,包括存储队列信息还有部分统计信息
|
|
||||||
redis_config = {
|
|
||||||
"host": os.environ.get("REDIS_PORT_6379_TCP_ADDR"),
|
|
||||||
"port": 6379,
|
|
||||||
"db": 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 判题的 docker 容器的配置参数
|
|
||||||
docker_config = {
|
|
||||||
"image_name": "judger",
|
|
||||||
"docker_path": "docker",
|
|
||||||
"shell": True
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 测试用例的路径,是主机上的实际路径
|
|
||||||
test_case_dir = "/root/test_case/"
|
|
||||||
# 源代码路径,也就是 manage.py 所在的实际路径
|
|
||||||
source_code_dir = "/root/qduoj/"
|
|
||||||
# 日志文件夹路径
|
|
||||||
log_dir = "/root/log/"
|
|
||||||
|
|
||||||
|
|
||||||
# 存储提交信息的数据库,是 celery 使用的,与 oj.settings/local_settings 等区分,那是 web 服务器访问的地址
|
|
||||||
submission_db = {
|
|
||||||
"host": os.environ.get("submission_db_host"),
|
|
||||||
"port": 3306,
|
|
||||||
"db": "oj_submission",
|
|
||||||
"user": "root",
|
|
||||||
"password": "root"
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
import json
|
|
||||||
import redis
|
|
||||||
import MySQLdb
|
|
||||||
import subprocess
|
|
||||||
from ..judger.result import result
|
|
||||||
from ..judger_controller.celery import app
|
|
||||||
from settings import docker_config, source_code_dir, test_case_dir, log_dir, submission_db, redis_config
|
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
|
||||||
def judge(submission_id, time_limit, memory_limit, test_case_id):
|
|
||||||
try:
|
|
||||||
command = "%s run --privileged --rm " \
|
|
||||||
"--link mysql " \
|
|
||||||
"-v %s:/var/judger/test_case/:ro " \
|
|
||||||
"-v %s:/var/judger/code/:ro " \
|
|
||||||
"-v %s:/var/judger/code/log/ " \
|
|
||||||
"--device /dev/null:/dev/null " \
|
|
||||||
"%s " \
|
|
||||||
"python judge/judger/run.py " \
|
|
||||||
"--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % \
|
|
||||||
(docker_config["docker_path"],
|
|
||||||
test_case_dir,
|
|
||||||
source_code_dir,
|
|
||||||
log_dir,
|
|
||||||
docker_config["image_name"],
|
|
||||||
submission_id, str(time_limit), str(memory_limit), test_case_id)
|
|
||||||
subprocess.call(command, shell=docker_config["shell"])
|
|
||||||
except Exception as e:
|
|
||||||
conn = MySQLdb.connect(db=submission_db["db"],
|
|
||||||
user=submission_db["user"],
|
|
||||||
passwd=submission_db["password"],
|
|
||||||
host=submission_db["host"],
|
|
||||||
port=submission_db["port"],
|
|
||||||
charset="utf8")
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute("update submission set result=%s, info=%s where id=%s",
|
|
||||||
(result["system_error"], str(e), submission_id))
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
|
|
||||||
r.decr("judge_queue_length")
|
|
||||||
r.lpush("queue", submission_id)
|
|
||||||
@@ -7,16 +7,16 @@ languages = {
|
|||||||
"src_name": "main.c",
|
"src_name": "main.c",
|
||||||
"code": 1,
|
"code": 1,
|
||||||
"syscalls": "!execve:k,flock:k,ptrace:k,sync:k,fdatasync:k,fsync:k,msync,sync_file_range:k,syncfs:k,unshare:k,setns:k,clone:k,query_module:k,sysinfo:k,syslog:k,sysfs:k",
|
"syscalls": "!execve:k,flock:k,ptrace:k,sync:k,fdatasync:k,fsync:k,msync,sync_file_range:k,syncfs:k,unshare:k,setns:k,clone:k,query_module:k,sysinfo:k,syslog:k,sysfs:k",
|
||||||
"compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main",
|
"compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}/main",
|
||||||
"execute_command": "{exe_path}main"
|
"execute_command": "{exe_path}/main"
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
"name": "cpp",
|
"name": "cpp",
|
||||||
"src_name": "main.cpp",
|
"src_name": "main.cpp",
|
||||||
"code": 2,
|
"code": 2,
|
||||||
"syscalls": "!execve:k,flock:k,ptrace:k,sync:k,fdatasync:k,fsync:k,msync,sync_file_range:k,syncfs:k,unshare:k,setns:k,clone:k,query_module:k,sysinfo:k,syslog:k,sysfs:k",
|
"syscalls": "!execve:k,flock:k,ptrace:k,sync:k,fdatasync:k,fsync:k,msync,sync_file_range:k,syncfs:k,unshare:k,setns:k,clone:k,query_module:k,sysinfo:k,syslog:k,sysfs:k",
|
||||||
"compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main",
|
"compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}/main",
|
||||||
"execute_command": "{exe_path}main"
|
"execute_command": "{exe_path}/main"
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
"name": "java",
|
"name": "java",
|
||||||
65
judge/runner.py
Normal file
65
judge/runner.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from client import JudgeClient
|
||||||
|
from language import languages
|
||||||
|
from compiler import compile_
|
||||||
|
from result import result
|
||||||
|
from settings import judger_workspace
|
||||||
|
|
||||||
|
|
||||||
|
class JudgeInstanceRunner(object):
|
||||||
|
|
||||||
|
def run(self, token, submission_id, language_code, code, time_limit, memory_limit, test_case_id):
|
||||||
|
language = languages[language_code]
|
||||||
|
host_name = socket.gethostname()
|
||||||
|
judge_base_path = os.path.join(judger_workspace, "run", submission_id)
|
||||||
|
|
||||||
|
if not token or token != os.environ.get("rpc_token"):
|
||||||
|
return {"code": 2, "data": {"error": "Invalid token", "server": host_name}}
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.mkdir(judge_base_path)
|
||||||
|
os.chmod(judge_base_path, 0777)
|
||||||
|
|
||||||
|
# 将代码写入文件
|
||||||
|
src_path = os.path.join(judge_base_path, language["src_name"])
|
||||||
|
f = open(src_path, "w")
|
||||||
|
f.write(code.encode("utf8"))
|
||||||
|
f.close()
|
||||||
|
except Exception as e:
|
||||||
|
shutil.rmtree(judge_base_path, ignore_errors=True)
|
||||||
|
return {"code": 2, "data": {"error": str(e), "server": host_name}}
|
||||||
|
|
||||||
|
# 编译
|
||||||
|
try:
|
||||||
|
exe_path = compile_(language, src_path, judge_base_path)
|
||||||
|
except Exception as e:
|
||||||
|
shutil.rmtree(judge_base_path, ignore_errors=True)
|
||||||
|
return {"code": 1, "data": {"error": str(e), "server": host_name}}
|
||||||
|
|
||||||
|
# 运行
|
||||||
|
try:
|
||||||
|
client = JudgeClient(language_code=language_code,
|
||||||
|
exe_path=exe_path,
|
||||||
|
max_cpu_time=int(time_limit),
|
||||||
|
max_real_time=int(time_limit) * 2,
|
||||||
|
max_memory=int(memory_limit),
|
||||||
|
test_case_dir=judger_workspace + "test_case/" + test_case_id + "/")
|
||||||
|
judge_result = {"result": result["accepted"], "info": client.run(),
|
||||||
|
"accepted_answer_time": None, "server": host_name}
|
||||||
|
|
||||||
|
for item in judge_result["info"]:
|
||||||
|
if item["result"]:
|
||||||
|
judge_result["result"] = item["result"]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
l = sorted(judge_result["info"], key=lambda k: k["cpu_time"])
|
||||||
|
judge_result["accepted_answer_time"] = l[-1]["cpu_time"]
|
||||||
|
return {"code": 0, "data": judge_result}
|
||||||
|
except Exception as e:
|
||||||
|
return {"code": 2, "data": {"error": str(e), "server": host_name}}
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(judge_base_path, ignore_errors=True)
|
||||||
13
judge/server.py
Normal file
13
judge/server.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import SocketServer
|
||||||
|
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||||
|
from runner import JudgeInstanceRunner
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
server = AsyncXMLRPCServer(('0.0.0.0', 8080), SimpleXMLRPCRequestHandler, allow_none=True)
|
||||||
|
server.register_instance(JudgeInstanceRunner())
|
||||||
|
server.serve_forever()
|
||||||
41
judge_dispatcher/migrations/0001_initial.py
Normal file
41
judge_dispatcher/migrations/0001_initial.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='JudgeServer',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('ip', models.GenericIPAddressField()),
|
||||||
|
('port', models.IntegerField()),
|
||||||
|
('max_instance_number', models.IntegerField()),
|
||||||
|
('left_instance_number', models.IntegerField()),
|
||||||
|
('workload', models.IntegerField(default=0)),
|
||||||
|
('token', models.CharField(max_length=30)),
|
||||||
|
('lock', models.BooleanField(default=False)),
|
||||||
|
('status', models.BooleanField(default=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'judge_server',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='JudgeWaitingQueue',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('submission_id', models.CharField(max_length=40)),
|
||||||
|
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'judge_waiting_queue',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
32
judge_dispatcher/migrations/0002_auto_20151207_2310.py
Normal file
32
judge_dispatcher/migrations/0002_auto_20151207_2310.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('judge_dispatcher', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='judgewaitingqueue',
|
||||||
|
name='memory_limit',
|
||||||
|
field=models.IntegerField(default=1),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='judgewaitingqueue',
|
||||||
|
name='test_case_id',
|
||||||
|
field=models.CharField(default=1, max_length=40),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='judgewaitingqueue',
|
||||||
|
name='time_limit',
|
||||||
|
field=models.IntegerField(default=1),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
0
judge_dispatcher/migrations/__init__.py
Normal file
0
judge_dispatcher/migrations/__init__.py
Normal file
44
judge_dispatcher/models.py
Normal file
44
judge_dispatcher/models.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class JudgeServer(models.Model):
|
||||||
|
ip = models.GenericIPAddressField()
|
||||||
|
port = models.IntegerField()
|
||||||
|
# 这个服务器最大可能运行的判题实例数量
|
||||||
|
max_instance_number = models.IntegerField()
|
||||||
|
left_instance_number = models.IntegerField()
|
||||||
|
workload = models.IntegerField(default=0)
|
||||||
|
token = models.CharField(max_length=30)
|
||||||
|
# 进行测试用例同步的时候加锁
|
||||||
|
lock = models.BooleanField(default=False)
|
||||||
|
# status 为 false 的时候代表不使用这个服务器
|
||||||
|
status = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
def use_judge_instance(self):
|
||||||
|
# 因为use 和 release 中间是判题时间,可能这个 model 的数据已经被修改了,所以不能直接使用self.xxx,否则取到的是旧数据
|
||||||
|
server = JudgeServer.objects.select_for_update().get(id=self.id)
|
||||||
|
server.left_instance_number -= 1
|
||||||
|
server.workload = 100 - int(float(server.left_instance_number) / server.max_instance_number * 100)
|
||||||
|
server.save()
|
||||||
|
|
||||||
|
def release_judge_instance(self):
|
||||||
|
# 使用原子操作
|
||||||
|
server = JudgeServer.objects.select_for_update().get(id=self.id)
|
||||||
|
server.left_instance_number += 1
|
||||||
|
server.workload = 100 - int(float(server.left_instance_number) / server.max_instance_number * 100)
|
||||||
|
server.save()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "judge_server"
|
||||||
|
|
||||||
|
|
||||||
|
class JudgeWaitingQueue(models.Model):
|
||||||
|
submission_id = models.CharField(max_length=40)
|
||||||
|
time_limit = models.IntegerField()
|
||||||
|
memory_limit = models.IntegerField()
|
||||||
|
test_case_id = models.CharField(max_length=40)
|
||||||
|
create_time = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "judge_waiting_queue"
|
||||||
24
judge_dispatcher/rpc_client.py
Normal file
24
judge_dispatcher/rpc_client.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import xmlrpclib
|
||||||
|
import httplib
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutHTTPConnection(httplib.HTTPConnection):
|
||||||
|
def __init__(self, host, timeout=10):
|
||||||
|
httplib.HTTPConnection.__init__(self, host, timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutTransport(xmlrpclib.Transport):
|
||||||
|
def __init__(self, timeout=10, *args, **kwargs):
|
||||||
|
xmlrpclib.Transport.__init__(self, *args, **kwargs)
|
||||||
|
self.timeout = timeout
|
||||||
|
|
||||||
|
def make_connection(self, host):
|
||||||
|
conn = TimeoutHTTPConnection(host, self.timeout)
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutServerProxy(xmlrpclib.ServerProxy):
|
||||||
|
def __init__(self, uri, timeout=10, *args, **kwargs):
|
||||||
|
kwargs['transport'] = TimeoutTransport(timeout=timeout, use_datetime=kwargs.get('use_datetime', 0))
|
||||||
|
xmlrpclib.ServerProxy.__init__(self, uri, *args, **kwargs)
|
||||||
149
judge_dispatcher/tasks.py
Normal file
149
judge_dispatcher/tasks.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
from rpc_client import TimeoutServerProxy
|
||||||
|
|
||||||
|
from judge.result import result
|
||||||
|
from contest.models import ContestProblem, ContestRank, Contest, CONTEST_UNDERWAY
|
||||||
|
from problem.models import Problem
|
||||||
|
from submission.models import Submission
|
||||||
|
from account.models import User
|
||||||
|
from utils.cache import get_cache_redis
|
||||||
|
|
||||||
|
from .models import JudgeServer, JudgeWaitingQueue
|
||||||
|
|
||||||
|
logger = logging.getLogger("app_info")
|
||||||
|
|
||||||
|
|
||||||
|
class JudgeDispatcher(object):
|
||||||
|
def __init__(self, submission, time_limit, memory_limit, test_case_id):
|
||||||
|
self.submission = submission
|
||||||
|
self.time_limit = time_limit
|
||||||
|
self.memory_limit = memory_limit
|
||||||
|
self.test_case_id = test_case_id
|
||||||
|
self.user = User.objects.get(id=submission.user_id)
|
||||||
|
|
||||||
|
def choose_judge_server(self):
|
||||||
|
servers = JudgeServer.objects.filter(workload__lt=100, lock=False, status=True).order_by("-workload")
|
||||||
|
if servers.exists():
|
||||||
|
return servers.first()
|
||||||
|
|
||||||
|
def judge(self, is_waiting_task=False):
|
||||||
|
self.submission.judge_start_time = int(time.time() * 1000)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
judge_server = self.choose_judge_server()
|
||||||
|
|
||||||
|
# 如果没有合适的判题服务器,就放入等待队列中等待判题
|
||||||
|
if not judge_server:
|
||||||
|
JudgeWaitingQueue.objects.create(submission_id=self.submission.id, time_limit=self.time_limit,
|
||||||
|
memory_limit=self.memory_limit, test_case_id=self.test_case_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
judge_server.use_judge_instance()
|
||||||
|
|
||||||
|
try:
|
||||||
|
s = TimeoutServerProxy("http://" + judge_server.ip + ":" + str(judge_server.port), timeout=20)
|
||||||
|
|
||||||
|
data = s.run(judge_server.token, self.submission.id, self.submission.language,
|
||||||
|
self.submission.code, self.time_limit, self.memory_limit, self.test_case_id)
|
||||||
|
# 编译错误
|
||||||
|
if data["code"] == 1:
|
||||||
|
self.submission.result = result["compile_error"]
|
||||||
|
self.submission.info = data["data"]["error"]
|
||||||
|
# system error
|
||||||
|
elif data["code"] == 2:
|
||||||
|
self.submission.result = result["system_error"]
|
||||||
|
self.submission.info = data["data"]["error"]
|
||||||
|
elif data["code"] == 0:
|
||||||
|
self.submission.result = data["data"]["result"]
|
||||||
|
self.submission.info = json.dumps(data["data"]["info"])
|
||||||
|
self.submission.accepted_answer_time = data["data"]["accepted_answer_time"]
|
||||||
|
except Exception as e:
|
||||||
|
self.submission.result = result["system_error"]
|
||||||
|
self.submission.info = str(e)
|
||||||
|
finally:
|
||||||
|
with transaction.atomic():
|
||||||
|
judge_server.release_judge_instance()
|
||||||
|
|
||||||
|
self.submission.judge_end_time = int(time.time() * 1000)
|
||||||
|
self.submission.save()
|
||||||
|
|
||||||
|
if self.submission.contest_id:
|
||||||
|
self.update_contest_problem_status()
|
||||||
|
else:
|
||||||
|
self.update_problem_status()
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
waiting_submissions = JudgeWaitingQueue.objects.select_for_update().all()
|
||||||
|
if waiting_submissions.exists():
|
||||||
|
# 防止循环依赖
|
||||||
|
from submission.tasks import _judge
|
||||||
|
|
||||||
|
waiting_submission = waiting_submissions.first()
|
||||||
|
|
||||||
|
submission = Submission.objects.get(id=waiting_submission.submission_id)
|
||||||
|
waiting_submission.delete()
|
||||||
|
|
||||||
|
_judge(submission, time_limit=waiting_submission.time_limit,
|
||||||
|
memory_limit=waiting_submission.memory_limit, test_case_id=waiting_submission.test_case_id,
|
||||||
|
is_waiting_task=True)
|
||||||
|
|
||||||
|
def update_problem_status(self):
|
||||||
|
problem = Problem.objects.get(id=self.submission.problem_id)
|
||||||
|
|
||||||
|
# 更新普通题目的计数器
|
||||||
|
problem.add_submission_number()
|
||||||
|
|
||||||
|
# 更新用户做题状态
|
||||||
|
problems_status = self.user.problems_status
|
||||||
|
if "problems" not in problems_status:
|
||||||
|
problems_status["problems"] = {}
|
||||||
|
if self.submission.result == result["accepted"]:
|
||||||
|
problem.add_ac_number()
|
||||||
|
problems_status["problems"][str(problem.id)] = 1
|
||||||
|
else:
|
||||||
|
problems_status["problems"][str(problem.id)] = 2
|
||||||
|
self.user.problems_status = problems_status
|
||||||
|
self.user.save()
|
||||||
|
# 普通题目的话,到这里就结束了
|
||||||
|
|
||||||
|
def update_contest_problem_status(self):
|
||||||
|
# 能运行到这里的都是比赛题目
|
||||||
|
contest = Contest.objects.get(id=self.submission.contest_id)
|
||||||
|
if contest.status != CONTEST_UNDERWAY:
|
||||||
|
logger.info("Contest debug mode, id: " + str(contest.id) + ", submission id: " + self.submission.id)
|
||||||
|
return
|
||||||
|
with transaction.atomic():
|
||||||
|
contest_problem = ContestProblem.objects.select_for_update().get(contest=contest, id=self.submission.problem_id)
|
||||||
|
|
||||||
|
contest_problem.add_submission_number()
|
||||||
|
|
||||||
|
# todo 事务
|
||||||
|
problems_status = self.user.problems_status
|
||||||
|
if "contest_problems" not in problems_status:
|
||||||
|
problems_status["contest_problems"] = {}
|
||||||
|
if self.submission.result == result["accepted"]:
|
||||||
|
contest_problem.add_ac_number()
|
||||||
|
problems_status["contest_problems"][str(contest_problem.id)] = 1
|
||||||
|
else:
|
||||||
|
problems_status["contest_problems"][str(contest_problem.id)] = 0
|
||||||
|
self.user.problems_status = problems_status
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
self.update_contest_rank(contest)
|
||||||
|
|
||||||
|
def update_contest_rank(self, contest):
|
||||||
|
if contest.real_time_rank:
|
||||||
|
get_cache_redis().delete(str(contest.id) + "_rank_cache")
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
try:
|
||||||
|
contest_rank = ContestRank.objects.select_for_update().get(contest=contest, user=self.user)
|
||||||
|
contest_rank.update_rank(self.submission)
|
||||||
|
except ContestRank.DoesNotExist:
|
||||||
|
ContestRank.objects.create(contest=contest, user=self.user).update_rank(self.submission)
|
||||||
@@ -1 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
import os
|
|
||||||
from envelopes import Envelope
|
|
||||||
|
|
||||||
SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com",
|
|
||||||
"email": "noreply@qduoj.com",
|
|
||||||
"password": os.environ.get("smtp_password", "111111"),
|
|
||||||
"tls": False}
|
|
||||||
|
|
||||||
|
|
||||||
def send_email(from_name, to_email, to_name, subject, content):
|
|
||||||
envelope = Envelope(from_addr=(SMTP_CONFIG["email"], from_name),
|
|
||||||
to_addr=(to_email, to_name),
|
|
||||||
subject=subject,
|
|
||||||
html_body=content)
|
|
||||||
envelope.send(SMTP_CONFIG["smtp_server"],
|
|
||||||
login=SMTP_CONFIG["email"],
|
|
||||||
password=SMTP_CONFIG["password"],
|
|
||||||
tls=SMTP_CONFIG["tls"])
|
|
||||||
@@ -2,15 +2,15 @@
|
|||||||
import redis
|
import redis
|
||||||
import datetime
|
import datetime
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from judge.judger.result import result
|
from judge.result import result
|
||||||
from judge.judger_controller.settings import redis_config
|
from django.conf import settings
|
||||||
from utils.shortcuts import success_response
|
from utils.shortcuts import success_response
|
||||||
from submission.models import Submission
|
from submission.models import Submission
|
||||||
|
|
||||||
|
|
||||||
class QueueLengthMonitorAPIView(APIView):
|
class QueueLengthMonitorAPIView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
|
r = redis.Redis(host=settings.redis_config["host"], port=settings.redis_config["port"], db=settings.redis_config["db"])
|
||||||
waiting_number = r.get("judge_queue_length")
|
waiting_number = r.get("judge_queue_length")
|
||||||
if waiting_number is None:
|
if waiting_number is None:
|
||||||
waiting_number = 0
|
waiting_number = 0
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
104
mq/scripts/mq.py
104
mq/scripts/mq.py
@@ -1,104 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import redis
|
|
||||||
|
|
||||||
from django.db import transaction
|
|
||||||
|
|
||||||
from judge.judger_controller.settings import redis_config
|
|
||||||
from judge.judger.result import result
|
|
||||||
from submission.models import Submission
|
|
||||||
from problem.models import Problem
|
|
||||||
from utils.cache import get_cache_redis
|
|
||||||
from contest.models import ContestProblem, Contest, CONTEST_UNDERWAY, ContestRank
|
|
||||||
from account.models import User
|
|
||||||
|
|
||||||
logger = logging.getLogger("app_info")
|
|
||||||
|
|
||||||
|
|
||||||
class MessageQueue(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.conn = redis.StrictRedis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
|
|
||||||
self.queue = 'queue'
|
|
||||||
|
|
||||||
def listen_task(self):
|
|
||||||
while True:
|
|
||||||
submission_id = self.conn.blpop(self.queue, 0)[1]
|
|
||||||
logger.debug("receive submission_id: " + submission_id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
submission = Submission.objects.get(id=submission_id)
|
|
||||||
except Submission.DoesNotExist:
|
|
||||||
logger.warning("Submission does not exist, submission_id: " + submission_id)
|
|
||||||
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 not submission.contest_id:
|
|
||||||
try:
|
|
||||||
problem = Problem.objects.get(id=submission.problem_id)
|
|
||||||
except Problem.DoesNotExist:
|
|
||||||
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
|
|
||||||
continue
|
|
||||||
|
|
||||||
problems_status = user.problems_status
|
|
||||||
|
|
||||||
# 更新普通题目的计数器
|
|
||||||
problem.add_submission_number()
|
|
||||||
if "problems" not in problems_status:
|
|
||||||
problems_status["problems"] = {}
|
|
||||||
if submission.result == result["accepted"]:
|
|
||||||
problem.add_ac_number()
|
|
||||||
problems_status["problems"][str(problem.id)] = 1
|
|
||||||
else:
|
|
||||||
problems_status["problems"][str(problem.id)] = 2
|
|
||||||
user.problems_status = problems_status
|
|
||||||
user.save()
|
|
||||||
# 普通题目的话,到这里就结束了
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 能运行到这里的都是比赛题目
|
|
||||||
try:
|
|
||||||
contest = Contest.objects.get(id=submission.contest_id)
|
|
||||||
if contest.status != CONTEST_UNDERWAY:
|
|
||||||
logger.info("Contest debug mode, id: " + str(contest.id) + ", submission id: " + submission_id)
|
|
||||||
continue
|
|
||||||
contest_problem = ContestProblem.objects.get(contest=contest, id=submission.problem_id)
|
|
||||||
except Contest.DoesNotExist:
|
|
||||||
logger.warning("Submission contest does not exist, submission_id: " + submission_id)
|
|
||||||
continue
|
|
||||||
except ContestProblem.DoesNotExist:
|
|
||||||
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 如果比赛现在不是封榜状态,删除比赛的排名缓存
|
|
||||||
if contest.real_time_rank:
|
|
||||||
get_cache_redis().delete(str(contest.id) + "_rank_cache")
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
try:
|
|
||||||
contest_rank = ContestRank.objects.get(contest=contest, user=user)
|
|
||||||
contest_rank.update_rank(submission)
|
|
||||||
except ContestRank.DoesNotExist:
|
|
||||||
ContestRank.objects.create(contest=contest, user=user).update_rank(submission)
|
|
||||||
|
|
||||||
problems_status = user.problems_status
|
|
||||||
|
|
||||||
contest_problem.add_submission_number()
|
|
||||||
if "contest_problems" not in problems_status:
|
|
||||||
problems_status["contest_problems"] = {}
|
|
||||||
if submission.result == result["accepted"]:
|
|
||||||
contest_problem.add_ac_number()
|
|
||||||
problems_status["contest_problems"][str(contest_problem.id)] = 1
|
|
||||||
else:
|
|
||||||
problems_status["contest_problems"][str(contest_problem.id)] = 0
|
|
||||||
user.problems_status = problems_status
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
logger.debug("Start message queue")
|
|
||||||
MessageQueue().listen_task()
|
|
||||||
@@ -22,6 +22,12 @@ REDIS_CACHE = {
|
|||||||
"db": 1
|
"db": 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REDIS_QUEUE = {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 6379,
|
||||||
|
"db": 2
|
||||||
|
}
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = []
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ DATABASES = {
|
|||||||
'CONN_MAX_AGE': 0.1,
|
'CONN_MAX_AGE': 0.1,
|
||||||
'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
|
'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
|
||||||
'PORT': 3306,
|
'PORT': 3306,
|
||||||
'USER': 'root',
|
'USER': os.environ.get("MYSQL_ENV_MYSQL_USER", "root"),
|
||||||
'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
|
'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
|
||||||
},
|
},
|
||||||
'submission': {
|
'submission': {
|
||||||
@@ -20,7 +20,7 @@ DATABASES = {
|
|||||||
'CONN_MAX_AGE': 0.1,
|
'CONN_MAX_AGE': 0.1,
|
||||||
'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
|
'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
|
||||||
'PORT': 3306,
|
'PORT': 3306,
|
||||||
'USER': 'root',
|
'USER': os.environ.get("MYSQL_ENV_MYSQL_USER", "root"),
|
||||||
'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
|
'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,12 @@ REDIS_CACHE = {
|
|||||||
"db": 1
|
"db": 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REDIS_QUEUE = {
|
||||||
|
"host": os.environ.get("REDIS_PORT_6379_TCP_ADDR", "127.0.0.1"),
|
||||||
|
"port": 6379,
|
||||||
|
"db": 2
|
||||||
|
}
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ INSTALLED_APPS = (
|
|||||||
'problem',
|
'problem',
|
||||||
'admin',
|
'admin',
|
||||||
'submission',
|
'submission',
|
||||||
'mq',
|
|
||||||
'contest',
|
'contest',
|
||||||
'mail',
|
'judge',
|
||||||
|
'judge_dispatcher',
|
||||||
|
|
||||||
'django_extensions',
|
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
'huey.djhuey',
|
||||||
)
|
)
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
@@ -98,11 +98,11 @@ 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/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'zh-cn'
|
LANGUAGE_CODE = 'zh-hans'
|
||||||
|
|
||||||
TIME_ZONE = 'Asia/Shanghai'
|
TIME_ZONE = 'Asia/Shanghai'
|
||||||
|
|
||||||
USE_I18N = False
|
USE_I18N = True
|
||||||
|
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
@@ -118,7 +118,6 @@ AUTH_USER_MODEL = 'account.User'
|
|||||||
|
|
||||||
LOG_PATH = "log/"
|
LOG_PATH = "log/"
|
||||||
|
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
'version': 1,
|
||||||
'disable_existing_loggers': True,
|
'disable_existing_loggers': True,
|
||||||
@@ -186,3 +185,17 @@ IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'upload/')
|
|||||||
WEBSITE_INFO = {"website_name": "qduoj",
|
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",
|
||||||
|
"email": "noreply@qduoj.com",
|
||||||
|
"password": os.environ.get("smtp_password", "111111"),
|
||||||
|
"tls": False}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from contest.views import (ContestAdminAPIView, ContestProblemAdminAPIView,
|
|||||||
MakeContestProblemPublicAPIView)
|
MakeContestProblemPublicAPIView)
|
||||||
|
|
||||||
from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
|
from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
|
||||||
JoinGroupAPIView, JoinGroupRequestAdminAPIView)
|
JoinGroupAPIView, JoinGroupRequestAdminAPIView, GroupPrometAdminAPIView)
|
||||||
|
|
||||||
from admin.views import AdminTemplateView
|
from admin.views import AdminTemplateView
|
||||||
|
|
||||||
@@ -56,12 +56,15 @@ urlpatterns = [
|
|||||||
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
|
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
|
||||||
url(r'^api/group_join/$', JoinGroupAPIView.as_view(), name="group_join_api"),
|
url(r'^api/group_join/$', JoinGroupAPIView.as_view(), name="group_join_api"),
|
||||||
|
|
||||||
|
|
||||||
url(r'^api/admin/upload_image/$', SimditorImageUploadAPIView.as_view(), name="simditor_upload_image"),
|
url(r'^api/admin/upload_image/$', SimditorImageUploadAPIView.as_view(), name="simditor_upload_image"),
|
||||||
url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
|
url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
|
||||||
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
|
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
|
||||||
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
|
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
|
||||||
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
|
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
|
||||||
url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"),
|
url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"),
|
||||||
|
url(r'^api/admin/group/promot_as_admin/$', GroupPrometAdminAPIView.as_view(), name="group_promote_admin_api"),
|
||||||
|
|
||||||
|
|
||||||
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
|
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
|
||||||
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
|
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
nohup celery -A judge.judger_controller worker -l DEBUG &
|
|
||||||
@@ -103,14 +103,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
vm.$watch("showGroupDetailPage", function (groupId) {
|
|
||||||
vm.groupId = groupId;
|
|
||||||
vm.template_url = "template/group/group_detail.html";
|
|
||||||
});
|
|
||||||
|
|
||||||
vm.$watch("showGroupListPage", function () {
|
|
||||||
vm.template_url = "template/group/group.html";
|
|
||||||
});
|
|
||||||
|
|
||||||
avalon.scan();
|
avalon.scan();
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
selectedGroups.push(vm.allGroups[i].id);
|
selectedGroups.push(vm.allGroups[i].id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (vm.password) {
|
||||||
|
ajaxData.password = vm.password;
|
||||||
|
ajaxData.contest_type = 3;
|
||||||
|
}
|
||||||
ajaxData.groups = selectedGroups;
|
ajaxData.groups = selectedGroups;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
selectedGroups.push(vm.allGroups[i].id);
|
selectedGroups.push(vm.allGroups[i].id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (vm.password) {
|
||||||
|
ajaxData.password = vm.password;
|
||||||
|
ajaxData.contest_type = 3;
|
||||||
|
}
|
||||||
ajaxData.groups = selectedGroups;
|
ajaxData.groups = selectedGroups;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -131,7 +135,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
vm.startTime = contest.start_time.substring(0, 16).replace("T", " ");
|
vm.startTime = contest.start_time.substring(0, 16).replace("T", " ");
|
||||||
vm.endTime = contest.end_time.substring(0, 16).replace("T", " ");
|
vm.endTime = contest.end_time.substring(0, 16).replace("T", " ");
|
||||||
vm.password = contest.password;
|
vm.password = contest.password;
|
||||||
if (contest.contest_type == 0) { //contest_type == 0, 小组内比赛
|
if (contest.contest_type == 0 || contest.contest_type == 3) { //contest_type == 0, 小组内比赛
|
||||||
vm.isGlobal = false;
|
vm.isGlobal = false;
|
||||||
for (var i = 0; i < vm.allGroups.length; i++) {
|
for (var i = 0; i < vm.allGroups.length; i++) {
|
||||||
vm.allGroups[i].isSelected = false;
|
vm.allGroups[i].isSelected = false;
|
||||||
|
|||||||
@@ -1,7 +1,30 @@
|
|||||||
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
||||||
|
|
||||||
avalon.ready(function () {
|
avalon.ready(function () {
|
||||||
//avalon.vmodels.group = null;
|
$('#add-group-form').validator().on('submit', function (e) {
|
||||||
|
if (!e.isDefaultPrevented()) {
|
||||||
|
var name = vm.name;
|
||||||
|
var description = vm.description;
|
||||||
|
var join_group_setting = vm.group_type;
|
||||||
|
$.ajax({
|
||||||
|
beforeSend: csrfTokenHeader,
|
||||||
|
url: "/api/admin/group/",
|
||||||
|
method: "post",
|
||||||
|
data: {name: name, description: description, join_group_setting: join_group_setting},
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
if (!data.code) {
|
||||||
|
getPageData(1);
|
||||||
|
bsAlert("添加成功");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
if (avalon.vmodels.group) {
|
if (avalon.vmodels.group) {
|
||||||
var vm = avalon.vmodels.group;
|
var vm = avalon.vmodels.group;
|
||||||
}
|
}
|
||||||
@@ -16,7 +39,9 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
|
|||||||
page: 1, // 当前页数
|
page: 1, // 当前页数
|
||||||
totalPage: 1, // 总页数
|
totalPage: 1, // 总页数
|
||||||
keyword: "",
|
keyword: "",
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
group_type: 0,
|
||||||
getNext: function () {
|
getNext: function () {
|
||||||
if (!vm.nextPage)
|
if (!vm.nextPage)
|
||||||
return;
|
return;
|
||||||
@@ -42,8 +67,10 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
|
|||||||
getGroupSettingString: function (setting) {
|
getGroupSettingString: function (setting) {
|
||||||
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
|
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
|
||||||
},
|
},
|
||||||
|
|
||||||
showGroupDetailPage: function (groupId) {
|
showGroupDetailPage: function (groupId) {
|
||||||
vm.$fire("up!showGroupDetailPage", groupId);
|
avalon.vmodels.admin.groupId = groupId;
|
||||||
|
avalon.vmodels.admin.template_url = "template/group/group_detail.html";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
||||||
|
|
||||||
|
|
||||||
// avalon:定义模式 group_list
|
// avalon:定义模式 group_list
|
||||||
avalon.ready(function () {
|
avalon.ready(function () {
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
|
|||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
checkedSetting: "0",
|
checkedSetting: "0",
|
||||||
|
visible: true,
|
||||||
getNext: function () {
|
getNext: function () {
|
||||||
if (!vm.nextPage)
|
if (!vm.nextPage)
|
||||||
return;
|
return;
|
||||||
@@ -53,7 +52,20 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
showGroupListPage: function () {
|
showGroupListPage: function () {
|
||||||
|
avalon.vmodels.admin.template_url = "template/group/group.html";
|
||||||
vm.$fire("up!showGroupListPage");
|
vm.$fire("up!showGroupListPage");
|
||||||
|
},
|
||||||
|
promotAsAdmin: function (relation) {
|
||||||
|
$.ajax({
|
||||||
|
beforeSend: csrfTokenHeader,
|
||||||
|
url: "/api/admin/group/promot_as_admin/",
|
||||||
|
method: "post",
|
||||||
|
data: JSON.stringify({group_id: relation.group, user_id: relation.user.id}),
|
||||||
|
contentType: "application/json;charset=UTF-8",
|
||||||
|
success: function (data) {
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -91,6 +103,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
|
|||||||
vm.name = data.data.name;
|
vm.name = data.data.name;
|
||||||
vm.description = data.data.description;
|
vm.description = data.data.description;
|
||||||
vm.checkedSetting = data.data.join_group_setting.toString();
|
vm.checkedSetting = data.data.join_group_setting.toString();
|
||||||
|
vm.visible = data.data.visible;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bsAlert(data.data);
|
bsAlert(data.data);
|
||||||
@@ -111,7 +124,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
|
|||||||
url: "/api/admin/group/",
|
url: "/api/admin/group/",
|
||||||
method: "put",
|
method: "put",
|
||||||
data: {group_id: group_id, name: name, description: description,
|
data: {group_id: group_id, name: name, description: description,
|
||||||
join_group_setting: join_group_setting},
|
join_group_setting: join_group_setting, visible:vm.visible},
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (!data.code) {
|
if (!data.code) {
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
||||||
|
$("#stu_id").hide();
|
||||||
$('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 realName = $("#real_name").val();
|
var realName = $("#real_name").val();
|
||||||
var school = $('#school').val();
|
var school = $('#school').val();
|
||||||
|
var student_id = $('#student_id').val();
|
||||||
var password = $("#password").val();
|
var password = $("#password").val();
|
||||||
var email = $("#email").val();
|
var email = $("#email").val();
|
||||||
var captcha = $("#captcha").val();
|
var captcha = $("#captcha").val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
beforeSend: csrfTokenHeader,
|
beforeSend: csrfTokenHeader,
|
||||||
url: "/api/register/",
|
url: "/api/register/",
|
||||||
data: {username: username, school: school, real_name: realName, password: password, email: email, captcha:captcha},
|
data: {username: username, school: school, student_id: student_id, real_name: realName, password: password, email: email, captcha: captcha},
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
method: "post",
|
method: "post",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
@@ -29,6 +31,14 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#school").blur(function () {
|
||||||
|
var school = $("#school").val().trim(school).toLowerCase();
|
||||||
|
if (school == "青岛大学" || school == "qdu" || school == "青大") {
|
||||||
|
$("#stu_id").show();
|
||||||
|
$("#school").val("青岛大学");
|
||||||
|
}
|
||||||
|
});
|
||||||
function refresh_captcha() {
|
function refresh_captcha() {
|
||||||
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
||||||
$("#captcha")[0].value = "";
|
$("#captcha")[0].value = "";
|
||||||
|
|||||||
@@ -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 index = location.href.indexOf("/t/");
|
var loca = location.href.split("/");
|
||||||
var token = location.href.substr(36+3, 32);
|
var token = loca[loca.length-2];
|
||||||
var captcha = $("#captcha").val();
|
var captcha = $("#captcha").val();
|
||||||
var password = $("#new_password").val();
|
var password = $("#new_password").val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
|||||||
var blog = $("#blog").val();
|
var blog = $("#blog").val();
|
||||||
var mood = $("#mood").val();
|
var mood = $("#mood").val();
|
||||||
var school = $("#school").val();
|
var school = $("#school").val();
|
||||||
|
var student_id = $("#student_id").val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
beforeSend: csrfTokenHeader,
|
beforeSend: csrfTokenHeader,
|
||||||
url: "/api/account/userprofile/",
|
url: "/api/account/userprofile/",
|
||||||
@@ -19,7 +19,8 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
|||||||
codeforces_username: codeforces_username,
|
codeforces_username: codeforces_username,
|
||||||
blog: blog,
|
blog: blog,
|
||||||
mood: mood,
|
mood: mood,
|
||||||
school: school
|
school: school,
|
||||||
|
student_id: student_id
|
||||||
},
|
},
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
method: "put",
|
method: "put",
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ require(["jquery", "csrfToken", "bsAlert"], function ($, csrfTokenHeader, bsAler
|
|||||||
var message;
|
var message;
|
||||||
if ($("#applyMessage").length) {
|
if ($("#applyMessage").length) {
|
||||||
message = $("#applyMessage").val();
|
message = $("#applyMessage").val();
|
||||||
if (!message)
|
if (!message) {
|
||||||
bsAlert("提交失败,请填写申请信息!");
|
bsAlert("提交失败,请填写申请信息!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var groupId = window.location.pathname.split("/")[2];
|
var groupId = window.location.pathname.split("/")[2];
|
||||||
var data = {group_id: groupId,message:message};
|
var data = {group_id: groupId,message:message};
|
||||||
|
|||||||
@@ -57,25 +57,28 @@
|
|||||||
problem_2_pack: "app/oj/problem/problem",
|
problem_2_pack: "app/oj/problem/problem",
|
||||||
submissionList_3_pack: "app/admin/problem/submissionList",
|
submissionList_3_pack: "app/admin/problem/submissionList",
|
||||||
contestCountdown_4_pack: "app/oj/contest/contestCountdown",
|
contestCountdown_4_pack: "app/oj/contest/contestCountdown",
|
||||||
addProblem_5_pack: "app/admin/problem/addProblem",
|
avatar_5_pack: "app/oj/account/avatar",
|
||||||
problem_6_pack: "app/admin/problem/problem",
|
addProblem_6_pack: "app/admin/problem/addProblem",
|
||||||
contestList_7_pack: "app/admin/contest/contestList",
|
problem_7_pack: "app/admin/problem/problem",
|
||||||
admin_8_pack: "app/admin/admin",
|
contestList_8_pack: "app/admin/contest/contestList",
|
||||||
login_9_pack: "app/oj/account/login",
|
admin_9_pack: "app/admin/admin",
|
||||||
addContest_10_pack: "app/admin/contest/addContest",
|
login_10_pack: "app/oj/account/login",
|
||||||
contestPassword_11_pack: "app/oj/contest/contestPassword",
|
applyResetPassword_11_pack: "app/oj/account/applyResetPassword",
|
||||||
changePassword_12_pack: "app/oj/account/changePassword",
|
addContest_12_pack: "app/admin/contest/addContest",
|
||||||
monitor_13_pack: "app/admin/monitor/monitor",
|
contestPassword_13_pack: "app/oj/contest/contestPassword",
|
||||||
editProblem_14_pack: "app/admin/contest/editProblem",
|
changePassword_14_pack: "app/oj/account/changePassword",
|
||||||
joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList",
|
monitor_15_pack: "app/admin/monitor/monitor",
|
||||||
group_16_pack: "app/oj/group/group",
|
editProblem_16_pack: "app/admin/contest/editProblem",
|
||||||
contestProblemList_17_pack: "app/admin/contest/contestProblemList",
|
joinGroupRequestList_17_pack: "app/admin/group/joinGroupRequestList",
|
||||||
editProblem_18_pack: "app/admin/problem/editProblem",
|
group_18_pack: "app/oj/group/group",
|
||||||
register_19_pack: "app/oj/account/register",
|
contestProblemList_19_pack: "app/admin/contest/contestProblemList",
|
||||||
groupDetail_20_pack: "app/admin/group/groupDetail",
|
editProblem_20_pack: "app/admin/problem/editProblem",
|
||||||
editContest_21_pack: "app/admin/contest/editContest",
|
register_21_pack: "app/oj/account/register",
|
||||||
group_22_pack: "app/admin/group/group",
|
groupDetail_22_pack: "app/admin/group/groupDetail",
|
||||||
settings_23_pack: "app/oj/account/settings"
|
editContest_23_pack: "app/admin/contest/editContest",
|
||||||
|
resetPassword_24_pack: "app/oj/account/resetPassword",
|
||||||
|
group_25_pack: "app/admin/group/group",
|
||||||
|
settings_26_pack: "app/oj/account/settings"
|
||||||
},
|
},
|
||||||
shim: {
|
shim: {
|
||||||
avalon: {
|
avalon: {
|
||||||
@@ -86,12 +89,6 @@
|
|||||||
appDir: "../",
|
appDir: "../",
|
||||||
dir: "../../release/",
|
dir: "../../release/",
|
||||||
modules: [
|
modules: [
|
||||||
{
|
|
||||||
name: "bootstrap",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "codeMirror"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "announcement_0_pack"
|
name: "announcement_0_pack"
|
||||||
},
|
},
|
||||||
@@ -108,62 +105,71 @@
|
|||||||
name: "contestCountdown_4_pack"
|
name: "contestCountdown_4_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "addProblem_5_pack"
|
name: "avatar_5_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "problem_6_pack"
|
name: "addProblem_6_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "contestList_7_pack"
|
name: "problem_7_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "admin_8_pack"
|
name: "contestList_8_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "login_9_pack"
|
name: "admin_9_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "addContest_10_pack"
|
name: "login_10_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "contestPassword_11_pack"
|
name: "applyResetPassword_11_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "changePassword_12_pack"
|
name: "addContest_12_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "monitor_13_pack"
|
name: "contestPassword_13_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "editProblem_14_pack"
|
name: "changePassword_14_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "joinGroupRequestList_15_pack"
|
name: "monitor_15_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "group_16_pack"
|
name: "editProblem_16_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "contestProblemList_17_pack"
|
name: "joinGroupRequestList_17_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "editProblem_18_pack"
|
name: "group_18_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "register_19_pack"
|
name: "contestProblemList_19_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "groupDetail_20_pack"
|
name: "editProblem_20_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "editContest_21_pack"
|
name: "register_21_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "group_22_pack"
|
name: "groupDetail_22_pack"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "settings_23_pack"
|
name: "editContest_23_pack"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "resetPassword_24_pack"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "group_25_pack"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "settings_26_pack"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
optimizeCss: "standard",
|
optimizeCss: "standard",
|
||||||
})
|
})
|
||||||
@@ -59,25 +59,28 @@ var require = {
|
|||||||
problem_2_pack: "app/oj/problem/problem",
|
problem_2_pack: "app/oj/problem/problem",
|
||||||
submissionList_3_pack: "app/admin/problem/submissionList",
|
submissionList_3_pack: "app/admin/problem/submissionList",
|
||||||
contestCountdown_4_pack: "app/oj/contest/contestCountdown",
|
contestCountdown_4_pack: "app/oj/contest/contestCountdown",
|
||||||
addProblem_5_pack: "app/admin/problem/addProblem",
|
avatar_5_pack: "app/oj/account/avatar",
|
||||||
problem_6_pack: "app/admin/problem/problem",
|
addProblem_6_pack: "app/admin/problem/addProblem",
|
||||||
contestList_7_pack: "app/admin/contest/contestList",
|
problem_7_pack: "app/admin/problem/problem",
|
||||||
admin_8_pack: "app/admin/admin",
|
contestList_8_pack: "app/admin/contest/contestList",
|
||||||
login_9_pack: "app/oj/account/login",
|
admin_9_pack: "app/admin/admin",
|
||||||
addContest_10_pack: "app/admin/contest/addContest",
|
login_10_pack: "app/oj/account/login",
|
||||||
contestPassword_11_pack: "app/oj/contest/contestPassword",
|
applyResetPassword_11_pack: "app/oj/account/applyResetPassword",
|
||||||
changePassword_12_pack: "app/oj/account/changePassword",
|
addContest_12_pack: "app/admin/contest/addContest",
|
||||||
monitor_13_pack: "app/admin/monitor/monitor",
|
contestPassword_13_pack: "app/oj/contest/contestPassword",
|
||||||
editProblem_14_pack: "app/admin/contest/editProblem",
|
changePassword_14_pack: "app/oj/account/changePassword",
|
||||||
joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList",
|
monitor_15_pack: "app/admin/monitor/monitor",
|
||||||
group_16_pack: "app/oj/group/group",
|
editProblem_16_pack: "app/admin/contest/editProblem",
|
||||||
contestProblemList_17_pack: "app/admin/contest/contestProblemList",
|
joinGroupRequestList_17_pack: "app/admin/group/joinGroupRequestList",
|
||||||
editProblem_18_pack: "app/admin/problem/editProblem",
|
group_18_pack: "app/oj/group/group",
|
||||||
register_19_pack: "app/oj/account/register",
|
contestProblemList_19_pack: "app/admin/contest/contestProblemList",
|
||||||
groupDetail_20_pack: "app/admin/group/groupDetail",
|
editProblem_20_pack: "app/admin/problem/editProblem",
|
||||||
editContest_21_pack: "app/admin/contest/editContest",
|
register_21_pack: "app/oj/account/register",
|
||||||
group_22_pack: "app/admin/group/group",
|
groupDetail_22_pack: "app/admin/group/groupDetail",
|
||||||
settings_23_pack: "app/oj/account/settings"
|
editContest_23_pack: "app/admin/contest/editContest",
|
||||||
|
resetPassword_24_pack: "app/oj/account/resetPassword",
|
||||||
|
group_25_pack: "app/admin/group/group",
|
||||||
|
settings_26_pack: "app/oj/account/settings",
|
||||||
},
|
},
|
||||||
shim: {
|
shim: {
|
||||||
avalon: {
|
avalon: {
|
||||||
|
|||||||
24
submission/migrations/0007_auto_20151207_1645.py
Normal file
24
submission/migrations/0007_auto_20151207_1645.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('submission', '0006_submission_shared'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='submission',
|
||||||
|
name='judge_end_time',
|
||||||
|
field=models.IntegerField(null=True, blank=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='submission',
|
||||||
|
name='judge_start_time',
|
||||||
|
field=models.IntegerField(null=True, blank=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
25
submission/migrations/0008_auto_20151208_2106.py
Normal file
25
submission/migrations/0008_auto_20151208_2106.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2015-12-08 13:06
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('submission', '0007_auto_20151207_1645'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='submission',
|
||||||
|
name='judge_end_time',
|
||||||
|
field=models.BigIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='submission',
|
||||||
|
name='judge_start_time',
|
||||||
|
field=models.BigIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from utils.shortcuts import rand_str
|
from utils.shortcuts import rand_str
|
||||||
from judge.judger.result import result
|
from judge.result import result
|
||||||
|
|
||||||
|
|
||||||
class Submission(models.Model):
|
class Submission(models.Model):
|
||||||
id = models.CharField(max_length=32, default=rand_str, primary_key=True, db_index=True)
|
id = models.CharField(max_length=32, default=rand_str, primary_key=True, db_index=True)
|
||||||
user_id = models.IntegerField(db_index=True)
|
user_id = models.IntegerField(db_index=True)
|
||||||
create_time = models.DateTimeField(auto_now_add=True)
|
create_time = models.DateTimeField(auto_now_add=True)
|
||||||
|
# 判题开始时间
|
||||||
|
judge_start_time = models.BigIntegerField(blank=True, null=True)
|
||||||
|
# 判题结束时间
|
||||||
|
judge_end_time = models.BigIntegerField(blank=True, null=True)
|
||||||
result = models.IntegerField(default=result["waiting"])
|
result = models.IntegerField(default=result["waiting"])
|
||||||
language = models.IntegerField()
|
language = models.IntegerField()
|
||||||
code = models.TextField()
|
code = models.TextField()
|
||||||
@@ -24,3 +28,6 @@ class Submission(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "submission"
|
db_table = "submission"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.id
|
||||||
|
|||||||
9
submission/tasks.py
Normal file
9
submission/tasks.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
from huey.djhuey import task
|
||||||
|
|
||||||
|
from judge_dispatcher.tasks import JudgeDispatcher
|
||||||
|
|
||||||
|
|
||||||
|
@task()
|
||||||
|
def _judge(submission, time_limit, memory_limit, test_case_id, is_waiting_task=False):
|
||||||
|
JudgeDispatcher(submission, time_limit, memory_limit, test_case_id).judge(is_waiting_task)
|
||||||
@@ -7,14 +7,13 @@ from django.shortcuts import render
|
|||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from judge.judger_controller.tasks import judge
|
|
||||||
from account.decorators import login_required, super_admin_required
|
from account.decorators import login_required, super_admin_required
|
||||||
from account.models import SUPER_ADMIN, User
|
from account.models import SUPER_ADMIN, User
|
||||||
from problem.models import Problem
|
from problem.models import Problem
|
||||||
from contest.models import ContestProblem, Contest
|
from contest.models import ContestProblem, Contest
|
||||||
from contest.decorators import check_user_contest_permission
|
from contest.decorators import check_user_contest_permission
|
||||||
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
|
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
|
||||||
from utils.cache import get_cache_redis
|
from .tasks import _judge
|
||||||
from .models import Submission
|
from .models import Submission
|
||||||
from .serializers import (CreateSubmissionSerializer, SubmissionSerializer,
|
from .serializers import (CreateSubmissionSerializer, SubmissionSerializer,
|
||||||
SubmissionhareSerializer, SubmissionRejudgeSerializer,
|
SubmissionhareSerializer, SubmissionRejudgeSerializer,
|
||||||
@@ -23,11 +22,6 @@ from .serializers import (CreateSubmissionSerializer, SubmissionSerializer,
|
|||||||
logger = logging.getLogger("app_info")
|
logger = logging.getLogger("app_info")
|
||||||
|
|
||||||
|
|
||||||
def _judge(submission_id, time_limit, memory_limit, test_case_id):
|
|
||||||
judge.delay(submission_id, time_limit, memory_limit, test_case_id)
|
|
||||||
get_cache_redis().incr("judge_queue_length")
|
|
||||||
|
|
||||||
|
|
||||||
class SubmissionAPIView(APIView):
|
class SubmissionAPIView(APIView):
|
||||||
@login_required
|
@login_required
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
@@ -49,7 +43,7 @@ class SubmissionAPIView(APIView):
|
|||||||
problem_id=problem.id)
|
problem_id=problem.id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_judge(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
|
_judge(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"提交判题任务失败")
|
||||||
@@ -94,7 +88,7 @@ class ContestSubmissionAPIView(APIView):
|
|||||||
code=data["code"],
|
code=data["code"],
|
||||||
problem_id=problem.id)
|
problem_id=problem.id)
|
||||||
try:
|
try:
|
||||||
_judge(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
|
_judge(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"提交判题任务失败")
|
||||||
@@ -279,7 +273,7 @@ class SubmissionRejudgeAdminAPIView(APIView):
|
|||||||
except Problem.DoesNotExist:
|
except Problem.DoesNotExist:
|
||||||
return error_response(u"题目不存在")
|
return error_response(u"题目不存在")
|
||||||
try:
|
try:
|
||||||
_judge(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
|
_judge(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"提交判题任务失败")
|
||||||
|
|||||||
@@ -46,13 +46,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<span ms-if="showGlobalViewRadio">
|
<span ms-if="showGlobalViewRadio">
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
|
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">公开赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
|
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
@@ -60,12 +60,20 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" ms-visible="isGlobal">
|
<div class="col-md-6" >
|
||||||
|
<div ms-visible="isGlobal">
|
||||||
<label>密码保护</label>
|
<label>密码保护</label>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ms-visible="!isGlobal">
|
||||||
|
<label>邀请密码</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="password" placeholder="留空则只有小组内可以参加" ms-duplex="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
||||||
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
|
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
|
||||||
<div ms-repeat="allGroups" class="col-md-4">
|
<div ms-repeat="allGroups" class="col-md-4">
|
||||||
|
|||||||
@@ -55,13 +55,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<span ms-if="showGlobalViewRadio">
|
<span ms-if="showGlobalViewRadio">
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
|
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">公开赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
|
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
@@ -69,13 +69,21 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" ms-visible="isGlobal">
|
<div class="col-md-6">
|
||||||
|
<div ms-visible="isGlobal">
|
||||||
<label>密码保护</label>
|
<label>密码保护</label>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ms-visible="!isGlobal">
|
||||||
|
<label>邀请密码</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="password" placeholder="留空则只有小组内可以参加" ms-duplex="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
||||||
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
|
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
|
||||||
<div ms-repeat="allGroups" class="col-md-4">
|
<div ms-repeat="allGroups" class="col-md-4">
|
||||||
|
|||||||
@@ -38,6 +38,35 @@
|
|||||||
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
||||||
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
||||||
</div>
|
</div>
|
||||||
|
<h2>创建小组</h2>
|
||||||
|
<form id="add-group-form">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group"><label>小组名</label>
|
||||||
|
<input type="text" name="name" class="form-control" ms-duplex="name"
|
||||||
|
data-error="请填写小组名(名称不能超过20字)" maxlength="20" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group"><label>描述</label>
|
||||||
|
<textarea rows="3" name="description" class="form-control" ms-duplex="description"
|
||||||
|
data-error="请填写描述" maxlength="300" required>
|
||||||
|
</textarea>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>加入小组设置</label>
|
||||||
|
<input type="radio" name="join_group_setting" value="0" ms-duplex-string="group_type">允许任何人加入
|
||||||
|
<input type="radio" name="join_group_setting" value="1" ms-duplex-string="group_type">提交请求后管理员审核
|
||||||
|
<input type="radio" name="join_group_setting" value="2" ms-duplex-string="group_type">不允许任何人加入
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-success btn-lg" value="创建小组" id="submitBtn">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/js/app/admin/group/group.js"></script>
|
<script src="/static/js/app/admin/group/group.js"></script>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
aria-hidden="true">←</span> 返回</a></li>
|
aria-hidden="true">←</span> 返回</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>小组成员管理</h1>
|
<h2>小组成员管理</h2>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
@@ -21,17 +21,17 @@
|
|||||||
<td>{{ el.join_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
|
<td>{{ el.join_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
|
<button class="btn-sm btn-info" ms-click="promotAsAdmin(el)">设为管理员</button>
|
||||||
<button class="btn-sm btn-danger" ms-click="removeMember(el)">移除</button>
|
<button class="btn-sm btn-danger" ms-click="removeMember(el)">移除</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
页数:{{ page }}/{{ totalPage }}
|
页数:{{ page }}/{{ totalPage }}
|
||||||
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
||||||
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
||||||
</div>
|
</div>
|
||||||
<h1>修改小组信息</h1>
|
<h2>修改小组信息</h2>
|
||||||
|
|
||||||
<form id="edit_group_form">
|
<form id="edit_group_form">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@@ -49,18 +49,24 @@
|
|||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-9">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<label>加入小组设置</label><br>
|
||||||
<label>加入小组设置</label>
|
<label><input type="radio" name="join_group_setting" value="0" ms-duplex-string="checkedSetting"><small>允许任何人加入</small></label>
|
||||||
<input type="radio" name="join_group_setting" value="0" ms-duplex-string="checkedSetting">允许任何人加入
|
<label><input type="radio" name="join_group_setting" value="1" ms-duplex-string="checkedSetting"><small>提交请求后管理员审核</small></label>
|
||||||
<input type="radio" name="join_group_setting" value="1" ms-duplex-string="checkedSetting">提交请求后管理员审核
|
<label><input type="radio" name="join_group_setting" value="2" ms-duplex-string="checkedSetting"><small>不允许任何人加入</small></label>
|
||||||
<input type="radio" name="join_group_setting" value="2" ms-duplex-string="checkedSetting">不允许任何人加入
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-3 form-group">
|
||||||
|
<label>前台是否可见</label><br>
|
||||||
|
<label><input type="checkbox" ms-duplex-checked="visible">
|
||||||
|
<small>可见</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
<button class="btn btn-primary" type="submit">提交</button>
|
<button class="btn btn-primary" type="submit">提交</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/js/app/admin/group/groupDetail.js"></script>
|
<script src="/static/js/app/admin/group/groupDetail.js"></script>
|
||||||
|
|||||||
@@ -19,10 +19,14 @@
|
|||||||
<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="real_name">学校</label>
|
<label for="school">学校</label>
|
||||||
<input type="text" class="form-control input-lg" id="school" name="school" placeholder="学校" data-error="请填写学校" required>
|
<input type="text" class="form-control input-lg" id="school" name="school" placeholder="学校" data-error="请填写学校" required>
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" id="stu_id">
|
||||||
|
<label for="student_id">学号</label>
|
||||||
|
<input type="number" class="form-control input-lg" id="student_id" name="student_id" placeholder="非必填,如果你需要使用课程功能,请填写该字段">
|
||||||
|
</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-remote="/api/email_check/" data-remote-error="该邮箱已被注册!" 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>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
value="{{ request.user.email }}" readonly>
|
value="{{ request.user.email }}" readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6"><label>手机</label>
|
<div class="form-group col-md-6"><label>手机</label>
|
||||||
<input name="phone" type="text" maxlength="11" id="phone"
|
<input name="phone_number" type="text" maxlength="11" id="phone"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
value="{% if request.user.userprofile.phone_number %}{{ request.user.userprofile.phone_number }}{% endif %}">
|
value="{% if request.user.userprofile.phone_number %}{{ request.user.userprofile.phone_number }}{% endif %}">
|
||||||
</div>
|
</div>
|
||||||
@@ -50,28 +50,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label>hduoj 用户名</label>
|
<label>hduoj 用户名</label>
|
||||||
<input name="hduoj" type="text" class="form-control" id="hduoj_username"
|
<input name="hduoj_username" type="text" class="form-control" id="hduoj_username"
|
||||||
value="{{ request.user.userprofile.hduoj_username }}">
|
value="{% if request.user.userprofile.hduoj_username %}{{ request.user.userprofile.hduoj_username }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label>BestCoder 用户名</label>
|
<label>BestCoder 用户名</label>
|
||||||
<input name="bestcoder" type="text" class="form-control" id="bestcoder_username"
|
<input name="bestcoder_username" type="text" class="form-control" id="bestcoder_username"
|
||||||
value="{{ request.user.userprofile.bestcoder_username }}">
|
value="{% if request.user.userprofile.bestcoder_username %}{{ request.user.userprofile.bestcoder_username }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label>Codeforces 用户名</label>
|
<label>Codeforces 用户名</label>
|
||||||
<input name="codeforces" type="text" class="form-control" id="codeforce_username"
|
<input name="codeforces_username" type="text" class="form-control" id="codeforces_username"
|
||||||
value="{{ request.user.userprofile.bestcoder_username }}">
|
value="{% if request.user.userprofile.codeforce_username %}{{ request.user.userprofile.codeforces_username }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group col-md-12"><label>学号</label>
|
||||||
|
<input name="student_id" type="number" class="form-control" id="student_id"
|
||||||
|
value="{% if request.user.userprofile.student_id %}{{ request.user.userprofile.student_id }}{% endif %}">
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<div class="form-group col-md-12"><label>blog</label>
|
<div class="form-group col-md-12"><label>blog</label>
|
||||||
<input name="blog" type="url" class="form-control" id="blog"
|
<input name="blog" type="url" class="form-control" id="blog"
|
||||||
value="{{ request.user.userprofile.blog }}">
|
value="{% if request.user.userprofile.blog %}{{ request.user.userprofile.blog }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,14 @@
|
|||||||
{% if user.userprofile.mood %}
|
{% if user.userprofile.mood %}
|
||||||
<p id="user-mood">{{ user.userprofile.mood }}</p>
|
<p id="user-mood">{{ user.userprofile.mood }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group col-lg-9">
|
<div class="list-group col-lg-9">
|
||||||
|
{% if user.userprofile.school %}
|
||||||
|
<p class="list-group-item"> <span class="glyphicon glyphicon-education"></span>
|
||||||
|
{{ user.userprofile.school }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
{% if user.userprofile.blog %}
|
{% if user.userprofile.blog %}
|
||||||
<p class="list-group-item"> <span class="glyphicon glyphicon-link"></span>
|
<p class="list-group-item"> <span class="glyphicon glyphicon-link"></span>
|
||||||
<a href="{{ user.userprofile.blog }}" target="_blank">{{ blog_link }}</a>
|
<a href="{{ user.userprofile.blog }}" target="_blank">{{ blog_link }}</a>
|
||||||
@@ -68,7 +73,7 @@
|
|||||||
<strong id="user-data-number">{{ user.userprofile.rank }}</strong>
|
<strong id="user-data-number">{{ user.userprofile.rank }}</strong>
|
||||||
<span id="user-data-text">Rank</span>
|
<span id="user-data-text">Rank</span>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
<div class="col-lg-6 text-center">
|
<div class="col-lg-6 text-center">
|
||||||
<strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
|
<strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
|
||||||
<span id="user-data-text">AC</span>
|
<span id="user-data-text">AC</span>
|
||||||
@@ -77,6 +82,7 @@
|
|||||||
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
|
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
|
||||||
<span id="user-data-text">Submissions</span>
|
<span id="user-data-text">Submissions</span>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<td>{{ contest.end_time }}</td>
|
<td>{{ contest.end_time }}</td>
|
||||||
<td>{{ contest|contest_status }}</td>
|
<td>{{ contest|contest_status }}</td>
|
||||||
{% ifequal contest.contest_type 0 %}
|
{% ifequal contest.contest_type 0 %}
|
||||||
<td>小组赛</td>
|
<td>私有小组赛</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
{% ifequal contest.contest_type 1 %}
|
{% ifequal contest.contest_type 1 %}
|
||||||
<td>公开赛</td>
|
<td>公开赛</td>
|
||||||
@@ -29,6 +29,9 @@
|
|||||||
{% ifequal contest.contest_type 2 %}
|
{% ifequal contest.contest_type 2 %}
|
||||||
<td>公开赛(密码保护)</td>
|
<td>公开赛(密码保护)</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
|
{% ifequal contest.contest_type 3 %}
|
||||||
|
<td>小组邀请赛</td>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
<td>{{ contest.created_by.username }}</td>
|
<td>{{ contest.created_by.username }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<td>{{ item.start_time }}</td>
|
<td>{{ item.start_time }}</td>
|
||||||
|
|
||||||
{% ifequal item.contest_type 0 %}
|
{% ifequal item.contest_type 0 %}
|
||||||
<td>小组赛</td>
|
<td>私有小组赛</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
{% ifequal item.contest_type 1 %}
|
{% ifequal item.contest_type 1 %}
|
||||||
<td>公开赛</td>
|
<td>公开赛</td>
|
||||||
@@ -45,6 +45,9 @@
|
|||||||
{% ifequal item.contest_type 2 %}
|
{% ifequal item.contest_type 2 %}
|
||||||
<td>公开赛(密码保护)</td>
|
<td>公开赛(密码保护)</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
|
{% ifequal item.contest_type 3 %}
|
||||||
|
<td>小组邀请赛</td>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
<td class="{{ item|contest_status_color }}">{{ item|contest_status }}</td>
|
<td class="{{ item|contest_status_color }}">{{ item|contest_status }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<h2 class="text-center">{{ group.name }}</h2>
|
<h2 class="text-center">{{ group.name }}</h2>
|
||||||
|
|
||||||
<p class="text-muted text-center">发布时间 : {{ group.create_time }}
|
<p class="text-muted text-center">发布时间 : {{ group.create_time }}
|
||||||
创建者 : {{ group.admin }}
|
创建者 : {{ group.created_by }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -25,23 +25,23 @@
|
|||||||
<p class="group-detail">{{ group.description|safe }}</p>
|
<p class="group-detail">{{ group.description|safe }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if not joined %}
|
||||||
<hr>
|
<hr>
|
||||||
<div>
|
<div>
|
||||||
{% if group.join_group_setting %}
|
{% ifequal group.join_group_setting 1 %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input id="groupId" value="{{ group.id }" type="hidden">
|
<input id="groupId" value="{{ group.id }" type="hidden">
|
||||||
<label>申请信息</label>
|
<label>申请信息</label>
|
||||||
<textarea class="form-control" id="applyMessage" rows="10"></textarea>
|
<textarea class="form-control" id="applyMessage" rows="10"></textarea>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endifequal %}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-primary" id="sendApplication">
|
<button class="btn btn-primary" id="sendApplication">{% if group.join_group_setting %}申请{% endif %}加入</button>
|
||||||
{% if group.join_group_setting %}
|
</div>
|
||||||
申请
|
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
加入</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js_block %}
|
{% block js_block %}
|
||||||
|
|||||||
@@ -34,13 +34,17 @@
|
|||||||
<th scope="row"><a href="/group/{{ item.id }}/" target="_blank">{{ item.id }}</a></th>
|
<th scope="row"><a href="/group/{{ item.id }}/" target="_blank">{{ item.id }}</a></th>
|
||||||
<td><a href="/group/{{ item.id }}/" target="_blank">{{ item.name }}</a></td>
|
<td><a href="/group/{{ item.id }}/" target="_blank">{{ item.name }}</a></td>
|
||||||
<td>
|
<td>
|
||||||
{% if item.join_group_setting %}
|
{% ifequal item.join_group_setting 1 %}
|
||||||
需要申请
|
需要申请
|
||||||
{% else %}
|
{% endifequal %}
|
||||||
|
{% ifequal item.join_group_setting 0 %}
|
||||||
无需申请
|
无需申请
|
||||||
{% endif %}
|
{% endifequal %}
|
||||||
|
{% ifequal item.join_group_setting 2 %}
|
||||||
|
不允许加入
|
||||||
|
{% endifequal %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.admin }}</td>
|
<td>{{ item.created_by }}</td>
|
||||||
<td>{{ item.create_time }}</td>
|
<td>{{ item.create_time }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
{% block js_block %}{% endblock %}
|
{% block js_block %}{% endblock %}
|
||||||
<!-- footer begin -->
|
<!-- footer begin -->
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p class="text-muted text-center">Copyright © 2015 {% show_website_info "website_footer" %}</p>
|
<p class="text-muted text-center">Copyright © 2015 {% autoescape off %}{% show_website_info "website_footer" %}{% endautoescape %}</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- footer end -->
|
<!-- footer end -->
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
[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
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
@echo off
|
|
||||||
python manage.py runserver
|
|
||||||
cls
|
|
||||||
cd..
|
|
||||||
python manage.py runserver
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
@echo off
|
|
||||||
coverage run --source='.' manage.py test
|
|
||||||
coverage html
|
|
||||||
cd htmlcov
|
|
||||||
index.html
|
|
||||||
cls
|
|
||||||
cd..
|
|
||||||
coverage run --source='.' manage.py test
|
|
||||||
coverage html
|
|
||||||
cd htmlcov
|
|
||||||
index.html
|
|
||||||
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
[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
|
|
||||||
15
utils/mail.py
Normal file
15
utils/mail.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
from envelopes import Envelope
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def send_email(from_name, to_email, to_name, subject, content):
|
||||||
|
envelope = Envelope(from_addr=(settings.SMTP_CONFIG["email"], from_name),
|
||||||
|
to_addr=(to_email, to_name),
|
||||||
|
subject=subject,
|
||||||
|
html_body=content)
|
||||||
|
envelope.send(settings.SMTP_CONFIG["smtp_server"],
|
||||||
|
login=settings.SMTP_CONFIG["email"],
|
||||||
|
password=settings.SMTP_CONFIG["password"],
|
||||||
|
tls=settings.SMTP_CONFIG["tls"])
|
||||||
Reference in New Issue
Block a user