mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-12-27 23:51:47 +00:00
重构
This commit is contained in:
parent
38d6bf3427
commit
61ab910d53
@ -1,56 +0,0 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-06-13 21:24+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: account/decorators.py:29 account/decorators.py:31
|
||||
msgid "Your account is disabled"
|
||||
msgstr "您的账号已被禁用"
|
||||
|
||||
#: account/decorators.py:35 account/middleware.py:21
|
||||
msgid "Please login in first"
|
||||
msgstr "请先登录"
|
||||
|
||||
#: account/tests.py:36 account/tests.py:59 account/tests.py:114
|
||||
#: account/views.py:51 account/views.py:59 account/views.py:96
|
||||
#: account/views.py:118
|
||||
msgid "Succeeded"
|
||||
msgstr ""
|
||||
|
||||
#: account/tests.py:45 account/views.py:63
|
||||
msgid "Invalid username or password"
|
||||
msgstr ""
|
||||
|
||||
#: account/tests.py:70 account/views.py:61
|
||||
msgid "Invalid two factor verification code"
|
||||
msgstr ""
|
||||
|
||||
#: account/tests.py:106 account/views.py:78 account/views.py:112
|
||||
msgid "Invalid captcha"
|
||||
msgstr ""
|
||||
|
||||
#: account/tests.py:122 account/views.py:81
|
||||
msgid "Username already exists"
|
||||
msgstr ""
|
||||
|
||||
#: account/tests.py:130 account/views.py:86 account/views.py:89
|
||||
msgid "Email already exists"
|
||||
msgstr ""
|
||||
|
||||
#: account/views.py:120
|
||||
msgid "Invalid old password"
|
||||
msgstr ""
|
@ -1,7 +1,6 @@
|
||||
# coding=utf-8
|
||||
import time
|
||||
import json
|
||||
import urllib
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
@ -23,5 +22,18 @@ class SessionSecurityMiddleware(object):
|
||||
content_type="application/json")
|
||||
else:
|
||||
return redirect_to_login(request)
|
||||
# 更新最后活动日期
|
||||
# update last active time
|
||||
request.session["last_activity"] = time.time()
|
||||
|
||||
|
||||
class AdminRequiredMiddleware(object):
|
||||
def process_request(self, request):
|
||||
path = request.path_info
|
||||
if path.startswith("/admin/") or path.startswith("/api/admin/"):
|
||||
if not(request.user.is_authenticated() and request.user.is_admin()):
|
||||
if request.is_ajax():
|
||||
return HttpResponse(json.dumps({"code": 1, "data": _("Please login in first")}),
|
||||
content_type="application/json")
|
||||
else:
|
||||
return HttpResponse(json.dumps({"code": 1, "data": _("Admin required")}),
|
||||
content_type="application/json")
|
@ -1,12 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-09-25 05:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import account.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
@ -14,14 +20,24 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)),
|
||||
('username', models.CharField(unique=True, max_length=30)),
|
||||
('real_name', models.CharField(max_length=30, null=True, blank=True)),
|
||||
('email', models.EmailField(max_length=254, null=True, blank=True)),
|
||||
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('username', models.CharField(max_length=30, unique=True)),
|
||||
('real_name', models.CharField(blank=True, max_length=30, null=True)),
|
||||
('email', models.EmailField(blank=True, max_length=254, null=True)),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, null=True)),
|
||||
('admin_type', models.IntegerField(default=0)),
|
||||
('admin_extra_permission', jsonfield.fields.JSONField(default=[])),
|
||||
('problems_status', jsonfield.fields.JSONField(default={})),
|
||||
('reset_password_token', models.CharField(blank=True, max_length=40, null=True)),
|
||||
('reset_password_token_create_time', models.DateTimeField(blank=True, null=True)),
|
||||
('auth_token', models.CharField(blank=True, max_length=40, null=True)),
|
||||
('two_factor_auth', models.BooleanField(default=False)),
|
||||
('tfa_token', models.CharField(blank=True, max_length=40, null=True)),
|
||||
('open_api', models.BooleanField(default=False)),
|
||||
('open_api_appkey', models.CharField(blank=True, max_length=35, null=True)),
|
||||
('is_disabled', models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'user',
|
||||
@ -31,9 +47,24 @@ class Migration(migrations.Migration):
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AdminGroup',
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('avatar', models.CharField(default=account.models._random_avatar, max_length=50)),
|
||||
('blog', models.URLField(blank=True, null=True)),
|
||||
('mood', models.CharField(blank=True, max_length=200, null=True)),
|
||||
('hduoj_username', models.CharField(blank=True, max_length=30, null=True)),
|
||||
('bestcoder_username', models.CharField(blank=True, max_length=30, null=True)),
|
||||
('codeforces_username', models.CharField(blank=True, max_length=30, null=True)),
|
||||
('accepted_problem_number', models.IntegerField(default=0)),
|
||||
('submission_number', models.IntegerField(default=0)),
|
||||
('phone_number', models.CharField(blank=True, max_length=15, null=True)),
|
||||
('school', models.CharField(blank=True, max_length=200, null=True)),
|
||||
('student_id', models.CharField(blank=True, max_length=15, null=True)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'user_profile',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
36
account/migrations/0002_auto_20160925_1337.py
Normal file
36
account/migrations/0002_auto_20160925_1337.py
Normal file
@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-09-25 05:37
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='user',
|
||||
name='real_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userprofile',
|
||||
name='bestcoder_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userprofile',
|
||||
name='codeforces_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userprofile',
|
||||
name='hduoj_username',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='real_name',
|
||||
field=models.CharField(blank=True, max_length=30, null=True),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='problems_status',
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0002_user_problems_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='problems_status',
|
||||
field=models.TextField(default=b'{}'),
|
||||
),
|
||||
]
|
@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.1 on 2016-02-17 01:20
|
||||
# Generated by Django 1.9.5 on 2016-09-25 06:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
@ -8,16 +8,17 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0017_auto_20151212_2139'),
|
||||
('account', '0002_auto_20160925_1337'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='AdminGroup',
|
||||
migrations.RemoveField(
|
||||
model_name='userprofile',
|
||||
name='real_name',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='openapi_appkey',
|
||||
field=models.CharField(blank=True, max_length=35, null=True),
|
||||
name='real_name',
|
||||
field=models.CharField(blank=True, max_length=30, null=True),
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0003_auto_20150915_2025'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='user',
|
||||
name='problems_status',
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0004_remove_user_problems_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='problems_status',
|
||||
field=models.TextField(default=b'{}'),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0005_user_problems_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='problems_status',
|
||||
field=utils.models.JsonField(default={}),
|
||||
),
|
||||
]
|
@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0006_auto_20150924_1530'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='reset_password_token',
|
||||
field=models.CharField(max_length=40, null=True, blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='problems_status',
|
||||
field=jsonfield.fields.JSONField(default={}),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0007_auto_20150929_2320'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='login_failed_counter',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0008_user_login_failed_counter'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='reset_password_token_create_time',
|
||||
field=models.DateTimeField(null=True, blank=True),
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0009_user_reset_password_token_create_time'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='user',
|
||||
name='login_failed_counter',
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0010_remove_user_login_failed_counter'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='auth_token',
|
||||
field=models.CharField(max_length=40, null=True, blank=True),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0011_user_auth_token'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='create_time',
|
||||
field=models.DateTimeField(auto_now_add=True, null=True),
|
||||
),
|
||||
]
|
@ -1,37 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import jsonfield.fields
|
||||
import account.models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0012_auto_20151012_1546'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('avatar', models.CharField(default=account.models._random_avatar, max_length=50)),
|
||||
('blog', models.URLField(null=True, blank=True)),
|
||||
('mood', models.CharField(max_length=200, null=True, blank=True)),
|
||||
('hduoj_username', models.CharField(max_length=30, null=True, blank=True)),
|
||||
('bestcoder_username', models.CharField(max_length=30, null=True, blank=True)),
|
||||
('codeforces_username', models.CharField(max_length=30, null=True, blank=True)),
|
||||
('rank', models.IntegerField(default=65535)),
|
||||
('accepted_number', models.IntegerField(default=0)),
|
||||
('submissions_number', models.IntegerField(default=0)),
|
||||
('problems_status', jsonfield.fields.JSONField(default={})),
|
||||
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'user_profile',
|
||||
},
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0013_userprofile'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='phone_number',
|
||||
field=models.CharField(max_length=15, null=True, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='school',
|
||||
field=models.CharField(max_length=200, null=True, blank=True),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- 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),
|
||||
),
|
||||
]
|
@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9 on 2015-12-11 14:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0015_userprofile_student_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='tfa_token',
|
||||
field=models.CharField(blank=True, max_length=10, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='two_factor_auth',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9 on 2015-12-12 13:39
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0016_auto_20151211_2230'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='tfa_token',
|
||||
field=models.CharField(blank=True, max_length=40, null=True),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2016-03-26 16:39
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0018_auto_20160217_0920'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='is_forbidden',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-04-24 04:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0019_user_is_forbidden'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='userprofile',
|
||||
old_name='accepted_number',
|
||||
new_name='accepted_problem_number',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userprofile',
|
||||
name='rank',
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-04-24 04:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0020_auto_20160424_1241'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='userprofile',
|
||||
old_name='submissions_number',
|
||||
new_name='submission_number',
|
||||
),
|
||||
]
|
@ -1,40 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-06-13 08:06
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0021_auto_20160424_1243'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='user',
|
||||
old_name='is_forbidden',
|
||||
new_name='is_disabled',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='user',
|
||||
old_name='openapi_appkey',
|
||||
new_name='open_api_appkey',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userprofile',
|
||||
name='problems_status',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='admin_extra_permission',
|
||||
field=jsonfield.fields.JSONField(default=[]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='open_api',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -1,14 +1,8 @@
|
||||
# coding=utf-8
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractBaseUser
|
||||
|
||||
from django.db import models
|
||||
from jsonfield import JSONField
|
||||
|
||||
# TODO remove these
|
||||
REGULAR_USER = 0
|
||||
ADMIN = 1
|
||||
SUPER_ADMIN = 2
|
||||
|
||||
|
||||
class AdminType(object):
|
||||
REGULAR_USER = 0
|
||||
@ -65,6 +59,9 @@ class User(AbstractBaseUser):
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
def is_admin(self):
|
||||
return self.admin_type > AdminType.REGULAR_USER
|
||||
|
||||
class Meta:
|
||||
db_table = "user"
|
||||
|
||||
@ -79,13 +76,11 @@ class UserProfile(models.Model):
|
||||
avatar = models.CharField(max_length=50, default=_random_avatar)
|
||||
blog = models.URLField(blank=True, null=True)
|
||||
mood = models.CharField(max_length=200, blank=True, null=True)
|
||||
hduoj_username = models.CharField(max_length=30, blank=True, null=True)
|
||||
bestcoder_username = models.CharField(max_length=30, blank=True, null=True)
|
||||
codeforces_username = models.CharField(max_length=30, blank=True, null=True)
|
||||
accepted_problem_number = models.IntegerField(default=0)
|
||||
submission_number = models.IntegerField(default=0)
|
||||
phone_number = models.CharField(max_length=15, 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)
|
||||
|
||||
def add_accepted_problem_number(self):
|
||||
|
@ -1,8 +1,8 @@
|
||||
# coding=utf-8
|
||||
from rest_framework import serializers
|
||||
|
||||
from utils.serializers import DateTimeTZField, JSONField
|
||||
from .models import User, UserProfile
|
||||
from utils.serializers import DateTimeTZField
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserLoginSerializer(serializers.Serializer):
|
||||
@ -11,22 +11,11 @@ class UserLoginSerializer(serializers.Serializer):
|
||||
tfa_code = serializers.CharField(min_length=6, max_length=6, required=False)
|
||||
|
||||
|
||||
class UsernameCheckSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(max_length=30)
|
||||
|
||||
|
||||
class EmailCheckSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField(max_length=254)
|
||||
|
||||
|
||||
class UserRegisterSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(max_length=30)
|
||||
real_name = serializers.CharField(max_length=30)
|
||||
password = serializers.CharField(max_length=30, min_length=6)
|
||||
email = serializers.EmailField(max_length=254)
|
||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||
school = serializers.CharField(max_length=200, required=False, default=None)
|
||||
student_id = serializers.CharField(max_length=15, required=False, default=None)
|
||||
|
||||
|
||||
class UserChangePasswordSerializer(serializers.Serializer):
|
||||
@ -58,43 +47,3 @@ class EditUserSerializer(serializers.Serializer):
|
||||
is_disabled = serializers.BooleanField()
|
||||
admin_extra_permission = serializers.ListField(required=False, default=[],
|
||||
child=serializers.IntegerField())
|
||||
|
||||
|
||||
class ApplyResetPasswordSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField()
|
||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||
|
||||
|
||||
class ResetPasswordSerializer(serializers.Serializer):
|
||||
token = serializers.CharField(min_length=1, max_length=40)
|
||||
password = serializers.CharField(min_length=6, max_length=30)
|
||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||
|
||||
|
||||
class SSOSerializer(serializers.Serializer):
|
||||
appkey = serializers.CharField(max_length=35)
|
||||
token = serializers.CharField(max_length=40)
|
||||
|
||||
|
||||
class EditUserProfileSerializer(serializers.Serializer):
|
||||
avatar = serializers.CharField(max_length=50, required=False, default=None)
|
||||
blog = serializers.URLField(required=False, allow_blank=True, default='')
|
||||
mood = serializers.CharField(max_length=60, required=False, allow_blank=True, default='')
|
||||
hduoj_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
||||
bestcoder_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='')
|
||||
phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='')
|
||||
student_id = serializers.CharField(max_length=15, required=False, allow_blank=True, default="")
|
||||
|
||||
|
||||
class UserProfileSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username",
|
||||
"rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school", "student_id"]
|
||||
|
||||
|
||||
class TwoFactorAuthCodeSerializer(serializers.Serializer):
|
||||
code = serializers.IntegerField()
|
||||
|
@ -1,8 +0,0 @@
|
||||
# coding=utf-8
|
||||
from celery import shared_task
|
||||
from utils.mail import send_email
|
||||
|
||||
|
||||
@shared_task
|
||||
def _send_email(from_name, to_email, to_name, subject, content):
|
||||
send_email(from_name, to_email, to_name, subject, content)
|
8
account/urls/admin.py
Normal file
8
account/urls/admin.py
Normal file
@ -0,0 +1,8 @@
|
||||
# coding=utf-8
|
||||
from django.conf.urls import url
|
||||
|
||||
from ..views.admin import UserAdminAPIView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
|
||||
]
|
10
account/urls/oj.py
Normal file
10
account/urls/oj.py
Normal file
@ -0,0 +1,10 @@
|
||||
# coding=utf-8
|
||||
from django.conf.urls import url
|
||||
|
||||
from ..views.oj import UserLoginAPIView, UserRegisterAPIView, UserChangePasswordAPIView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^login/$', UserLoginAPIView.as_view(), name="user_login_api"),
|
||||
url(r'^register/$', UserRegisterAPIView.as_view(), name="user_register_api"),
|
||||
url(r'^change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api")
|
||||
]
|
531
account/views.py
531
account/views.py
@ -1,531 +0,0 @@
|
||||
# coding=utf-8
|
||||
from __future__ import unicode_literals
|
||||
import StringIO
|
||||
import codecs
|
||||
import os
|
||||
|
||||
import qrcode
|
||||
from django.conf import settings
|
||||
from django.contrib import auth
|
||||
from django.core.exceptions import MultipleObjectsReturned
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext as _
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from utils.captcha import Captcha
|
||||
from utils.otp_auth import OtpAuth
|
||||
from utils.shortcuts import (serializer_invalid_response, error_response,
|
||||
success_response, error_page, paginate, rand_str)
|
||||
from .decorators import login_required
|
||||
from .decorators import super_admin_required
|
||||
from .models import User, UserProfile, AdminType
|
||||
from .serializers import (UserLoginSerializer, UserRegisterSerializer,
|
||||
UserChangePasswordSerializer,
|
||||
UserSerializer, EditUserSerializer,
|
||||
ApplyResetPasswordSerializer, ResetPasswordSerializer,
|
||||
SSOSerializer, EditUserProfileSerializer,
|
||||
TwoFactorAuthCodeSerializer)
|
||||
from .tasks import _send_email
|
||||
|
||||
|
||||
class UserLoginAPIView(APIView):
|
||||
def post(self, request):
|
||||
"""
|
||||
User login api
|
||||
"""
|
||||
serializer = UserLoginSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
user = auth.authenticate(username=data["username"], password=data["password"])
|
||||
# None is returned if username or password is wrong
|
||||
if user:
|
||||
if not user.two_factor_auth:
|
||||
auth.login(request, user)
|
||||
return success_response(_("Succeeded"))
|
||||
|
||||
# `tfa_code` not in post data
|
||||
if user.two_factor_auth and "tfa_code" not in data:
|
||||
return success_response("tfa_required")
|
||||
|
||||
if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]):
|
||||
auth.login(request, user)
|
||||
return success_response(_("Succeeded"))
|
||||
else:
|
||||
return error_response(_("Invalid two factor verification code"))
|
||||
else:
|
||||
return error_response(_("Invalid username or password"))
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
# todo remove this
|
||||
def get(self, request):
|
||||
auth.login(request, auth.authenticate(username="root", password="123456"))
|
||||
return success_response({})
|
||||
|
||||
|
||||
class UserRegisterAPIView(APIView):
|
||||
def post(self, request):
|
||||
"""
|
||||
User register api
|
||||
"""
|
||||
serializer = UserRegisterSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
return error_response(_("Invalid captcha"))
|
||||
try:
|
||||
User.objects.get(username=data["username"])
|
||||
return error_response(_("Username already exists"))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
try:
|
||||
User.objects.get(email=data["email"])
|
||||
return error_response(_("Email already exists"))
|
||||
# Some old data has duplicate email
|
||||
except MultipleObjectsReturned:
|
||||
return error_response(_("Email already exists"))
|
||||
except User.DoesNotExist:
|
||||
user = User.objects.create(username=data["username"], real_name=data["real_name"],
|
||||
email=data["email"])
|
||||
user.set_password(data["password"])
|
||||
user.save()
|
||||
UserProfile.objects.create(user=user, school=data["school"], student_id=data["student_id"])
|
||||
return success_response(_("Succeeded"))
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
class UserChangePasswordAPIView(APIView):
|
||||
@login_required
|
||||
def post(self, request):
|
||||
"""
|
||||
User change password api
|
||||
"""
|
||||
serializer = UserChangePasswordSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
return error_response(_("Invalid captcha"))
|
||||
username = request.user.username
|
||||
user = auth.authenticate(username=username, password=data["old_password"])
|
||||
if user:
|
||||
user.set_password(data["new_password"])
|
||||
user.save()
|
||||
return success_response(_("Succeeded"))
|
||||
else:
|
||||
return error_response(_("Invalid old password"))
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
class UserAdminAPIView(APIView):
|
||||
@super_admin_required
|
||||
def put(self, request):
|
||||
"""
|
||||
Edit user api
|
||||
"""
|
||||
serializer = EditUserSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
user = User.objects.get(id=data["id"])
|
||||
except User.DoesNotExist:
|
||||
return error_response(_("User does not exist"))
|
||||
try:
|
||||
user = User.objects.get(username=data["username"])
|
||||
if user.id != data["id"]:
|
||||
return error_response(_("Username already exists"))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
try:
|
||||
user = User.objects.get(email=data["email"])
|
||||
if user.id != data["id"]:
|
||||
return error_response(_("Email already exists"))
|
||||
# Some old data has duplicate email
|
||||
except MultipleObjectsReturned:
|
||||
return error_response(_("Email already exists"))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
user.username = data["username"]
|
||||
user.real_name = data["real_name"]
|
||||
user.email = data["email"]
|
||||
user.admin_type = data["admin_type"]
|
||||
user.is_disabled = data["is_disabled"]
|
||||
|
||||
if data["password"]:
|
||||
user.set_password(data["password"])
|
||||
|
||||
if data["open_api"]:
|
||||
# Avoid reset user appkey after saving changes
|
||||
if not user.open_api:
|
||||
user.open_api_appkey = rand_str()
|
||||
else:
|
||||
user.open_api_appkey = None
|
||||
user.open_api = data["open_api"]
|
||||
|
||||
if data["two_factor_auth"]:
|
||||
# Avoid reset user tfa_token after saving changes
|
||||
if not user.two_factor_auth:
|
||||
user.tfa_token = rand_str()
|
||||
else:
|
||||
user.tfa_token = None
|
||||
user.two_factor_auth = data["two_factor_auth"]
|
||||
|
||||
if data["admin_type"] == AdminType.ADMIN:
|
||||
user.admin_extra_permission = list(set(data["admin_extra_permission"]))
|
||||
else:
|
||||
user.admin_extra_permission = []
|
||||
|
||||
user.save()
|
||||
return success_response(UserSerializer(user).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
@super_admin_required
|
||||
def get(self, request):
|
||||
"""
|
||||
User list api / Get user by id
|
||||
"""
|
||||
user_id = request.GET.get("user_id")
|
||||
if user_id:
|
||||
try:
|
||||
user = User.objects.get(id=user_id)
|
||||
except User.DoesNotExist:
|
||||
return error_response(_("User does not exist"))
|
||||
return success_response(UserSerializer(user).data)
|
||||
|
||||
user = User.objects.all().order_by("-create_time")
|
||||
|
||||
admin_type = request.GET.get("admin_type", None)
|
||||
if admin_type:
|
||||
try:
|
||||
user = user.filter(admin_type__gte=int(admin_type))
|
||||
except ValueError:
|
||||
return error_response(_("Invalid parameter"))
|
||||
keyword = request.GET.get("keyword", None)
|
||||
if keyword:
|
||||
user = user.filter(Q(username__contains=keyword) |
|
||||
Q(real_name__contains=keyword) |
|
||||
Q(email__contains=keyword))
|
||||
return paginate(request, user, UserSerializer)
|
||||
|
||||
|
||||
def logout(request):
|
||||
auth.logout(request)
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
|
||||
def index_page(request):
|
||||
if not request.user.is_authenticated():
|
||||
return render(request, "oj/index.html")
|
||||
|
||||
if request.META.get('HTTP_REFERER') or request.GET.get("index"):
|
||||
return render(request, "oj/index.html")
|
||||
else:
|
||||
return HttpResponseRedirect('/problems/')
|
||||
|
||||
|
||||
class UsernameCheckAPIView(APIView):
|
||||
def get(self, request):
|
||||
"""
|
||||
检测用户名是否存在,存在返回状态码400,不存在返回200
|
||||
---
|
||||
"""
|
||||
username = request.GET.get("username", None)
|
||||
if username:
|
||||
try:
|
||||
User.objects.get(username=username)
|
||||
return Response(status=400)
|
||||
except User.DoesNotExist:
|
||||
return Response(status=200)
|
||||
return Response(status=200)
|
||||
|
||||
|
||||
class EmailCheckAPIView(APIView):
|
||||
def get(self, request):
|
||||
"""
|
||||
检测邮箱是否存在,用状态码标识结果
|
||||
---
|
||||
"""
|
||||
# 这里是为了适应前端表单验证空间的要求
|
||||
reset = request.GET.get("reset", None)
|
||||
# 如果reset为true说明该请求是重置密码页面发出的,要返回的状态码应正好相反
|
||||
if reset:
|
||||
existed = 200
|
||||
does_not_existed = 400
|
||||
else:
|
||||
existed = 400
|
||||
does_not_existed = 200
|
||||
|
||||
email = request.GET.get("email", None)
|
||||
if email:
|
||||
try:
|
||||
User.objects.get(email=email)
|
||||
return Response(status=existed)
|
||||
except Exception:
|
||||
return Response(status=does_not_existed)
|
||||
return Response(status=does_not_existed)
|
||||
|
||||
|
||||
class UserInfoAPIView(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
"""
|
||||
返回这个用户的个人信息
|
||||
---
|
||||
response_serializer: UserSerializer
|
||||
"""
|
||||
return success_response(UserSerializer(request.user).data)
|
||||
|
||||
|
||||
class UserProfileAPIView(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
"""
|
||||
返回这个用户的个人信息
|
||||
---
|
||||
response_serializer: UserSerializer
|
||||
"""
|
||||
return success_response(UserSerializer(request.user).data)
|
||||
|
||||
@login_required
|
||||
def put(self, request):
|
||||
serializer = EditUserProfileSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
user_profile = request.user.userprofile
|
||||
if data["avatar"]:
|
||||
user_profile.avatar = data["avatar"]
|
||||
else:
|
||||
user_profile.mood = data["mood"]
|
||||
user_profile.hduoj_username = data["hduoj_username"]
|
||||
user_profile.bestcoder_username = data["bestcoder_username"]
|
||||
user_profile.codeforces_username = data["codeforces_username"]
|
||||
user_profile.blog = data["blog"]
|
||||
user_profile.school = data["school"]
|
||||
user_profile.student_id = data["student_id"]
|
||||
user_profile.phone_number = data["phone_number"]
|
||||
user_profile.save()
|
||||
return success_response(u"修改成功")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
class ApplyResetPasswordAPIView(APIView):
|
||||
def post(self, request):
|
||||
"""
|
||||
提交请求重置密码
|
||||
---
|
||||
request_serializer: ApplyResetPasswordSerializer
|
||||
"""
|
||||
serializer = ApplyResetPasswordSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
return error_response(u"验证码错误")
|
||||
try:
|
||||
user = User.objects.get(email=data["email"])
|
||||
except User.DoesNotExist:
|
||||
return error_response(u"用户不存在")
|
||||
if user.reset_password_token_create_time and (
|
||||
now() - user.reset_password_token_create_time).total_seconds() < 20 * 60:
|
||||
return error_response(u"20分钟内只能找回一次密码")
|
||||
user.reset_password_token = rand_str()
|
||||
user.reset_password_token_create_time = now()
|
||||
user.save()
|
||||
email_template = codecs.open(settings.TEMPLATES[0]["DIRS"][0] + "utils/reset_password_email.html", "r",
|
||||
"utf-8").read()
|
||||
|
||||
email_template = email_template.replace("{{ username }}", user.username). \
|
||||
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]). \
|
||||
replace("{{ link }}", settings.WEBSITE_INFO["url"] + "/reset_password/t/" +
|
||||
user.reset_password_token)
|
||||
|
||||
_send_email.delay(settings.WEBSITE_INFO["website_name"],
|
||||
user.email,
|
||||
user.username,
|
||||
settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件",
|
||||
email_template)
|
||||
return success_response(u"邮件发送成功,请前往您的邮箱查收")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
class ResetPasswordAPIView(APIView):
|
||||
def post(self, request):
|
||||
serializer = ResetPasswordSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
return error_response(u"验证码错误")
|
||||
try:
|
||||
user = User.objects.get(reset_password_token=data["token"])
|
||||
except User.DoesNotExist:
|
||||
return error_response(u"token 不存在")
|
||||
if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60:
|
||||
return error_response(u"token 已经过期,请在30分钟内重置密码")
|
||||
user.reset_password_token = None
|
||||
user.set_password(data["password"])
|
||||
user.save()
|
||||
return success_response(u"密码重置成功")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
def user_index_page(request, username):
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
return error_page(request, u"用户不存在")
|
||||
|
||||
blog_link = ""
|
||||
|
||||
if user.userprofile.blog:
|
||||
blog_link = user.userprofile.blog.replace("http://", "").replace("https://", "")
|
||||
|
||||
return render(request, "oj/account/user_index.html", {"user": user, "blog_link": blog_link})
|
||||
|
||||
|
||||
class SSOAPIView(APIView):
|
||||
def post(self, request):
|
||||
serializer = SSOSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
try:
|
||||
User.objects.get(openapi_appkey=serializer.data["appkey"])
|
||||
except User.DoesNotExist:
|
||||
return error_response(u"appkey无效")
|
||||
try:
|
||||
user = User.objects.get(auth_token=serializer.data["token"])
|
||||
user.auth_token = None
|
||||
user.save()
|
||||
return success_response({"username": user.username,
|
||||
"id": user.id,
|
||||
"admin_type": user.admin_type,
|
||||
"avatar": user.userprofile.avatar})
|
||||
except User.DoesNotExist:
|
||||
return error_response(u"用户不存在")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
@login_required
|
||||
def get(self, request):
|
||||
callback = request.GET.get("callback", None)
|
||||
if not callback:
|
||||
return error_page(request, u"参数错误")
|
||||
token = rand_str()
|
||||
request.user.auth_token = token
|
||||
request.user.save()
|
||||
return render(request, "oj/account/sso.html",
|
||||
{"redirect_url": callback + "?token=" + token, "callback": callback})
|
||||
|
||||
|
||||
def reset_password_page(request, token):
|
||||
try:
|
||||
user = User.objects.get(reset_password_token=token)
|
||||
except User.DoesNotExist:
|
||||
return error_page(request, u"链接已失效")
|
||||
if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60:
|
||||
return error_page(request, u"链接已过期")
|
||||
return render(request, "oj/account/reset_password.html", {"user": user})
|
||||
|
||||
|
||||
class TwoFactorAuthAPIView(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
"""
|
||||
获取绑定二维码
|
||||
"""
|
||||
user = request.user
|
||||
if user.two_factor_auth:
|
||||
return error_response(u"已经开启两步验证了")
|
||||
token = rand_str()
|
||||
user.tfa_token = token
|
||||
user.save()
|
||||
|
||||
image = qrcode.make(OtpAuth(token).to_uri("totp", settings.WEBSITE_INFO["url"], "OnlineJudgeAdmin"))
|
||||
buf = StringIO.StringIO()
|
||||
image.save(buf, 'gif')
|
||||
|
||||
return HttpResponse(buf.getvalue(), 'image/gif')
|
||||
|
||||
@login_required
|
||||
def post(self, request):
|
||||
"""
|
||||
开启两步验证
|
||||
"""
|
||||
serializer = TwoFactorAuthCodeSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
code = serializer.data["code"]
|
||||
user = request.user
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
user.two_factor_auth = True
|
||||
user.save()
|
||||
return success_response(u"开启两步验证成功")
|
||||
else:
|
||||
return error_response(u"验证码错误")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
@login_required
|
||||
def put(self, request):
|
||||
serializer = TwoFactorAuthCodeSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
user = request.user
|
||||
code = serializer.data["code"]
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
user.two_factor_auth = False
|
||||
user.save()
|
||||
else:
|
||||
return error_response(u"验证码错误")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
def user_rank_page(request, page=1):
|
||||
ranks = UserProfile.objects.filter(submission_number__gt=0).order_by("-accepted_problem_number", "-submission_number")
|
||||
paginator = Paginator(ranks, 20)
|
||||
try:
|
||||
ranks = paginator.page(int(page))
|
||||
except Exception:
|
||||
return error_page(request, u"不存在的页码")
|
||||
previous_page = next_page = None
|
||||
try:
|
||||
previous_page = ranks.previous_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
next_page = ranks.next_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
return render(request, "utils/rank.html", {"ranks": ranks, "page": page,
|
||||
"previous_page": previous_page,
|
||||
"next_page": next_page,
|
||||
"start_id": int(page) * 20 - 20,})
|
||||
|
||||
|
||||
class AvatarUploadAPIView(APIView):
|
||||
def post(self, request):
|
||||
if "file" not in request.FILES:
|
||||
return error_response(u"文件上传失败")
|
||||
|
||||
f = request.FILES["file"]
|
||||
if f.size > 1024 * 1024:
|
||||
return error_response(u"图片过大")
|
||||
if os.path.splitext(f.name)[-1].lower() not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]:
|
||||
return error_response(u"需要上传图片格式")
|
||||
name = "avatar_" + rand_str(5) + os.path.splitext(f.name)[-1]
|
||||
with open(os.path.join(settings.IMAGE_UPLOAD_DIR, name), "wb") as img:
|
||||
for chunk in request.FILES["file"]:
|
||||
img.write(chunk)
|
||||
return success_response({"path": "/static/upload/" + name})
|
107
account/views/admin.py
Normal file
107
account/views/admin.py
Normal file
@ -0,0 +1,107 @@
|
||||
# coding=utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.core.exceptions import MultipleObjectsReturned
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext as _
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from utils.shortcuts import (serializer_invalid_response, error_response,
|
||||
success_response, paginate, rand_str)
|
||||
from ..decorators import super_admin_required
|
||||
from ..models import User, AdminType
|
||||
from ..serializers import (UserSerializer, EditUserSerializer)
|
||||
|
||||
|
||||
class UserAdminAPIView(APIView):
|
||||
@super_admin_required
|
||||
def put(self, request):
|
||||
"""
|
||||
Edit user api
|
||||
"""
|
||||
serializer = EditUserSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
user = User.objects.get(id=data["id"])
|
||||
except User.DoesNotExist:
|
||||
return error_response(_("User does not exist"))
|
||||
try:
|
||||
user = User.objects.get(username=data["username"])
|
||||
if user.id != data["id"]:
|
||||
return error_response(_("Username already exists"))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
try:
|
||||
user = User.objects.get(email=data["email"])
|
||||
if user.id != data["id"]:
|
||||
return error_response(_("Email already exists"))
|
||||
# Some old data has duplicate email
|
||||
except MultipleObjectsReturned:
|
||||
return error_response(_("Email already exists"))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
user.username = data["username"]
|
||||
user.real_name = data["real_name"]
|
||||
user.email = data["email"]
|
||||
user.admin_type = data["admin_type"]
|
||||
user.is_disabled = data["is_disabled"]
|
||||
|
||||
if data["password"]:
|
||||
user.set_password(data["password"])
|
||||
|
||||
if data["open_api"]:
|
||||
# Avoid reset user appkey after saving changes
|
||||
if not user.open_api:
|
||||
user.open_api_appkey = rand_str()
|
||||
else:
|
||||
user.open_api_appkey = None
|
||||
user.open_api = data["open_api"]
|
||||
|
||||
if data["two_factor_auth"]:
|
||||
# Avoid reset user tfa_token after saving changes
|
||||
if not user.two_factor_auth:
|
||||
user.tfa_token = rand_str()
|
||||
else:
|
||||
user.tfa_token = None
|
||||
user.two_factor_auth = data["two_factor_auth"]
|
||||
|
||||
if data["admin_type"] == AdminType.ADMIN:
|
||||
user.admin_extra_permission = list(set(data["admin_extra_permission"]))
|
||||
else:
|
||||
user.admin_extra_permission = []
|
||||
|
||||
user.save()
|
||||
return success_response(UserSerializer(user).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
@super_admin_required
|
||||
def get(self, request):
|
||||
"""
|
||||
User list api / Get user by id
|
||||
"""
|
||||
user_id = request.GET.get("user_id")
|
||||
if user_id:
|
||||
try:
|
||||
user = User.objects.get(id=user_id)
|
||||
except User.DoesNotExist:
|
||||
return error_response(_("User does not exist"))
|
||||
return success_response(UserSerializer(user).data)
|
||||
|
||||
user = User.objects.all().order_by("-create_time")
|
||||
|
||||
admin_type = request.GET.get("admin_type", None)
|
||||
if admin_type:
|
||||
try:
|
||||
user = user.filter(admin_type__gte=int(admin_type))
|
||||
except ValueError:
|
||||
return error_response(_("Invalid parameter"))
|
||||
keyword = request.GET.get("keyword", None)
|
||||
if keyword:
|
||||
user = user.filter(Q(username__contains=keyword) |
|
||||
Q(real_name__contains=keyword) |
|
||||
Q(email__contains=keyword))
|
||||
return paginate(request, user, UserSerializer)
|
107
account/views/oj.py
Normal file
107
account/views/oj.py
Normal file
@ -0,0 +1,107 @@
|
||||
# coding=utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import auth
|
||||
from django.core.exceptions import MultipleObjectsReturned
|
||||
from django.utils.translation import ugettext as _
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from utils.captcha import Captcha
|
||||
from utils.otp_auth import OtpAuth
|
||||
from utils.shortcuts import (serializer_invalid_response, error_response,
|
||||
success_response)
|
||||
from ..decorators import login_required
|
||||
from ..models import User, UserProfile
|
||||
from ..serializers import (UserLoginSerializer, UserRegisterSerializer,
|
||||
UserChangePasswordSerializer)
|
||||
|
||||
|
||||
class UserLoginAPIView(APIView):
|
||||
def post(self, request):
|
||||
"""
|
||||
User login api
|
||||
"""
|
||||
serializer = UserLoginSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
user = auth.authenticate(username=data["username"], password=data["password"])
|
||||
# None is returned if username or password is wrong
|
||||
if user:
|
||||
if not user.two_factor_auth:
|
||||
auth.login(request, user)
|
||||
return success_response(_("Succeeded"))
|
||||
|
||||
# `tfa_code` not in post data
|
||||
if user.two_factor_auth and "tfa_code" not in data:
|
||||
return success_response("tfa_required")
|
||||
|
||||
if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]):
|
||||
auth.login(request, user)
|
||||
return success_response(_("Succeeded"))
|
||||
else:
|
||||
return error_response(_("Invalid two factor verification code"))
|
||||
else:
|
||||
return error_response(_("Invalid username or password"))
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
# todo remove this, only for debug use
|
||||
def get(self, request):
|
||||
auth.login(request, auth.authenticate(username=request.GET["username"], password=request.GET["password"]))
|
||||
return success_response({})
|
||||
|
||||
|
||||
class UserRegisterAPIView(APIView):
|
||||
def post(self, request):
|
||||
"""
|
||||
User register api
|
||||
"""
|
||||
serializer = UserRegisterSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
return error_response(_("Invalid captcha"))
|
||||
try:
|
||||
User.objects.get(username=data["username"])
|
||||
return error_response(_("Username already exists"))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
try:
|
||||
User.objects.get(email=data["email"])
|
||||
return error_response(_("Email already exists"))
|
||||
# Some old data has duplicate email
|
||||
except MultipleObjectsReturned:
|
||||
return error_response(_("Email already exists"))
|
||||
except User.DoesNotExist:
|
||||
user = User.objects.create(username=data["username"], email=data["email"])
|
||||
user.set_password(data["password"])
|
||||
user.save()
|
||||
UserProfile.objects.create(user=user)
|
||||
return success_response(_("Succeeded"))
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
class UserChangePasswordAPIView(APIView):
|
||||
@login_required
|
||||
def post(self, request):
|
||||
"""
|
||||
User change password api
|
||||
"""
|
||||
serializer = UserChangePasswordSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
return error_response(_("Invalid captcha"))
|
||||
username = request.user.username
|
||||
user = auth.authenticate(username=username, password=data["old_password"])
|
||||
if user:
|
||||
user.set_password(data["new_password"])
|
||||
user.save()
|
||||
return success_response(_("Succeeded"))
|
||||
else:
|
||||
return error_response(_("Invalid old password"))
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
@ -1,16 +0,0 @@
|
||||
# coding=utf-8
|
||||
import json
|
||||
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
|
||||
|
||||
class AdminRequiredMiddleware(object):
|
||||
def process_request(self, request):
|
||||
path = request.path_info
|
||||
if path.startswith("/admin/") or path.startswith("/api/admin/"):
|
||||
if not(request.user.is_authenticated() and request.user.admin_type):
|
||||
if request.is_ajax():
|
||||
return HttpResponse(json.dumps({"code": 1, "data": u"请先登录"}),
|
||||
content_type="application/json")
|
||||
else:
|
||||
return HttpResponseRedirect("/login/")
|
@ -1,15 +0,0 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse, Http404
|
||||
|
||||
from rest_framework.views import APIView
|
||||
|
||||
|
||||
class AdminTemplateView(APIView):
|
||||
def get(self, request, template_dir, template_name):
|
||||
path = os.path.join(settings.TEMPLATES[0]["DIRS"][0], "admin", template_dir, template_name + ".html")
|
||||
try:
|
||||
return HttpResponse(open(path).read(), content_type="text/html")
|
||||
except IOError:
|
||||
return HttpResponse(u"模板不存在", content_type="text/html")
|
@ -1,12 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-09-25 05:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
@ -15,13 +20,13 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='Announcement',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=50)),
|
||||
('content', models.TextField()),
|
||||
('content', utils.models.RichTextField()),
|
||||
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||
('last_update_time', models.DateTimeField(auto_now=True)),
|
||||
('visible', models.BooleanField(default=True)),
|
||||
('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'announcement',
|
||||
|
@ -1,26 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0004_merge'),
|
||||
('announcement', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='announcement',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(to='group.Group'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='announcement',
|
||||
name='is_global',
|
||||
field=models.BooleanField(default=True),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('announcement', '0002_auto_20150818_1445'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='announcement',
|
||||
name='content',
|
||||
field=utils.models.RichTextField(),
|
||||
),
|
||||
]
|
@ -1,22 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('announcement', '0003_auto_20150922_1703'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='announcement',
|
||||
name='groups',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='announcement',
|
||||
name='is_global',
|
||||
),
|
||||
]
|
@ -1,8 +1,8 @@
|
||||
# coding=utf-8
|
||||
from __future__ import unicode_literals
|
||||
from django.db import models
|
||||
|
||||
from account.models import User
|
||||
from group.models import Group
|
||||
from utils.models import RichTextField
|
||||
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
# coding=utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from account.models import User
|
||||
from utils.serializers import DateTimeTZField
|
||||
|
||||
from .models import Announcement
|
||||
|
||||
|
||||
@ -31,4 +32,4 @@ class EditAnnouncementSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
title = serializers.CharField(max_length=50)
|
||||
content = serializers.CharField(max_length=10000)
|
||||
visible = serializers.BooleanField()
|
||||
visible = serializers.BooleanField()
|
||||
|
8
announcement/urls/admin.py
Normal file
8
announcement/urls/admin.py
Normal file
@ -0,0 +1,8 @@
|
||||
# coding=utf-8
|
||||
from django.conf.urls import url
|
||||
|
||||
from ..views import AnnouncementAdminAPIView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
|
||||
]
|
@ -1,29 +1,17 @@
|
||||
# coding=utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.utils.translation import ugettext as _
|
||||
from utils.shortcuts import serializer_invalid_response, error_response, success_response
|
||||
|
||||
from utils.shortcuts import paginate, error_page
|
||||
from account.decorators import super_admin_required
|
||||
from utils.shortcuts import paginate
|
||||
from utils.shortcuts import serializer_invalid_response, error_response, success_response
|
||||
from .models import Announcement
|
||||
from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer,
|
||||
EditAnnouncementSerializer)
|
||||
|
||||
|
||||
def announcement_page(request, announcement_id):
|
||||
"""
|
||||
announcement detail page
|
||||
"""
|
||||
try:
|
||||
announcement = Announcement.objects.get(id=announcement_id, visible=True)
|
||||
except Announcement.DoesNotExist:
|
||||
return error_page(request, _("Announcement does not exist"))
|
||||
return render(request, "oj/announcement/announcement.html", {"announcement": announcement})
|
||||
|
||||
|
||||
class AnnouncementAdminAPIView(APIView):
|
||||
@super_admin_required
|
||||
def post(self, request):
|
||||
|
@ -1,3 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
3
conf/tests.py
Normal file
3
conf/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
conf/views.py
Normal file
3
conf/views.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
@ -1,113 +0,0 @@
|
||||
# coding=utf-8
|
||||
import urllib
|
||||
from functools import wraps
|
||||
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from utils.shortcuts import error_response, error_page
|
||||
|
||||
from account.models import SUPER_ADMIN, ADMIN
|
||||
from .models import (Contest, PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST, PUBLIC_CONTEST, GROUP_CONTEST,
|
||||
CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY)
|
||||
|
||||
|
||||
def check_user_contest_permission(func):
|
||||
@wraps(func)
|
||||
def _check_user_contest_permission(*args, **kwargs):
|
||||
"""
|
||||
这个函数检查当前的这个比赛对于 request 的用户来说能不能参加
|
||||
需要比较:比赛的开始和结束时间、比赛是否有密码、比赛是不是限定指定小组参加
|
||||
如果是有密码或者限定指定小组参加的话,即使比赛已经结束,那么也是可以看到所有的题目和结果的
|
||||
否则不能看到这个比赛的题目结果排名等等
|
||||
"""
|
||||
# CBV 的情况,第一个参数是self,第二个参数是request
|
||||
if len(args) == 2:
|
||||
request = args[-1]
|
||||
else:
|
||||
request = args[0]
|
||||
|
||||
if not request.user.is_authenticated():
|
||||
if request.is_ajax():
|
||||
return error_response(u"请先登录")
|
||||
else:
|
||||
return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.path))
|
||||
|
||||
# kwargs 就包含了 url 里面的参数
|
||||
if "contest_id" in kwargs:
|
||||
contest_id = kwargs["contest_id"]
|
||||
elif "contest_id" in request.data:
|
||||
contest_id = request.data["contest_id"]
|
||||
elif "contest_id" in request.GET:
|
||||
contest_id = request.GET["contest_id"]
|
||||
else:
|
||||
if request.is_ajax():
|
||||
return error_response(u"参数错误")
|
||||
else:
|
||||
return error_page(request, u"参数错误")
|
||||
|
||||
try:
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
except Contest.DoesNotExist:
|
||||
if request.is_ajax():
|
||||
return error_response(u"比赛不存在")
|
||||
else:
|
||||
return error_page(request, u"比赛不存在")
|
||||
|
||||
if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by:
|
||||
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 request.is_ajax():
|
||||
return error_response(u"比赛不存在")
|
||||
else:
|
||||
return error_page(request, u"比赛不存在")
|
||||
|
||||
# 有密码的公开赛
|
||||
if contest.contest_type == PASSWORD_PROTECTED_CONTEST:
|
||||
# 没有输入过密码
|
||||
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.contest_type == GROUP_CONTEST:
|
||||
if not contest.groups.filter(id__in=request.user.group_set.all()).exists():
|
||||
if request.is_ajax():
|
||||
return error_response(u"只有指定小组的可以参加这场比赛")
|
||||
else:
|
||||
return render(request, "oj/contest/no_contest_permission.html",
|
||||
{"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 request.is_ajax():
|
||||
return error_response(u"比赛还没有开始")
|
||||
else:
|
||||
return render(request, "oj/contest/no_contest_permission.html",
|
||||
{"reason": "contest_not_start", "show_tab": False, "contest": contest})
|
||||
|
||||
# 比赛已经结束了,只拦截 ajax 的答案提交
|
||||
if contest.status == CONTEST_ENDED and request.path == reverse("contest_submission_api") and request.is_ajax():
|
||||
return error_response(u"比赛已经结束")
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return _check_user_contest_permission
|
@ -1,74 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('group', '0004_merge'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Contest',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(unique=True, max_length=40)),
|
||||
('description', models.TextField()),
|
||||
('mode', models.IntegerField()),
|
||||
('show_rank', models.BooleanField()),
|
||||
('show_user_submission', models.BooleanField()),
|
||||
('password', models.CharField(max_length=30, null=True, blank=True)),
|
||||
('contest_type', models.IntegerField()),
|
||||
('start_time', models.DateTimeField()),
|
||||
('end_time', models.DateTimeField()),
|
||||
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||
('last_updated_time', models.DateTimeField(auto_now=True)),
|
||||
('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('groups', models.ManyToManyField(to='group.Group')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'contest',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ContestProblem',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=50)),
|
||||
('description', models.TextField()),
|
||||
('input_description', models.CharField(max_length=10000)),
|
||||
('output_description', models.CharField(max_length=10000)),
|
||||
('samples', models.TextField(blank=True)),
|
||||
('test_case_id', models.CharField(max_length=40)),
|
||||
('hint', models.TextField(null=True, blank=True)),
|
||||
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||
('time_limit', models.IntegerField()),
|
||||
('memory_limit', models.IntegerField()),
|
||||
('visible', models.BooleanField(default=True)),
|
||||
('total_submit_number', models.IntegerField(default=0)),
|
||||
('total_accepted_number', models.IntegerField(default=0)),
|
||||
('sort_index', models.CharField(max_length=30)),
|
||||
('contest', models.ForeignKey(to='contest.Contest')),
|
||||
('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'contest_problem',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ContestProblemTestCase',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=40, serialize=False, primary_key=True, db_index=True)),
|
||||
('score', models.IntegerField()),
|
||||
('problem', models.ForeignKey(to='contest.ContestProblem')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'contest_problem_test_case',
|
||||
},
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contest',
|
||||
name='visible',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0002_contest_visible'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='difficulty',
|
||||
field=models.IntegerField(default=1),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0003_contestproblem_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='contestproblem',
|
||||
name='difficulty',
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0004_remove_contestproblem_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='score',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
@ -1,31 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('contest', '0004_remove_contestproblem_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ContestSubmission',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('total_submission_number', models.IntegerField(default=1)),
|
||||
('ac', models.BooleanField()),
|
||||
('total_time', models.IntegerField(default=0)),
|
||||
('contest', models.ForeignKey(to='contest.Contest')),
|
||||
('problem', models.ForeignKey(to='contest.ContestProblem')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'contest_submission',
|
||||
},
|
||||
),
|
||||
]
|
@ -1,15 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0005_contestsubmission'),
|
||||
('contest', '0005_contestproblem_score'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0006_merge'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestsubmission',
|
||||
name='ac_time',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0007_contestsubmission_ac_time'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='contest',
|
||||
old_name='show_rank',
|
||||
new_name='real_time_rank',
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0008_auto_20150912_1912'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestsubmission',
|
||||
name='first_achieved',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0009_contestsubmission_first_achieved'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contest',
|
||||
name='description',
|
||||
field=utils.models.RichTextField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contestproblem',
|
||||
name='description',
|
||||
field=utils.models.RichTextField(),
|
||||
),
|
||||
]
|
@ -1,29 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('contest', '0010_auto_20150922_1703'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ContestRank',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('total_submission_number', models.IntegerField(default=0)),
|
||||
('total_ac_number', models.IntegerField(default=0)),
|
||||
('total_time', models.IntegerField(default=0)),
|
||||
('submission_info', utils.models.JsonField(default={})),
|
||||
('contest', models.ForeignKey(to='contest.Contest')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0011_contestrank'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contestrank',
|
||||
name='submission_info',
|
||||
field=jsonfield.fields.JSONField(default={}),
|
||||
),
|
||||
migrations.AlterModelTable(
|
||||
name='contestrank',
|
||||
table='contest_rank',
|
||||
),
|
||||
]
|
@ -1,64 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0012_auto_20151008_1124'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='contestproblemtestcase',
|
||||
name='problem',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contestsubmission',
|
||||
name='contest',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contestsubmission',
|
||||
name='problem',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contestsubmission',
|
||||
name='user',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contest',
|
||||
name='mode',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contest',
|
||||
name='show_user_submission',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contestproblem',
|
||||
name='score',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='is_public',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='last_update_time',
|
||||
field=models.DateTimeField(null=True, blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contestproblem',
|
||||
name='hint',
|
||||
field=utils.models.RichTextField(null=True, blank=True),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ContestProblemTestCase',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ContestSubmission',
|
||||
),
|
||||
]
|
@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.1 on 2016-04-04 07:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0013_auto_20151017_1511'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='spj',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='spj_code',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='spj_code_language',
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.1 on 2016-04-04 08:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0014_auto_20160404_1509'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='contestproblem',
|
||||
old_name='spj_code_language',
|
||||
new_name='spj_language',
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.1 on 2016-04-06 04:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0015_auto_20160404_1641'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='spj_version',
|
||||
field=models.CharField(blank=True, max_length=32, null=True),
|
||||
),
|
||||
]
|
@ -1,148 +0,0 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
|
||||
from account.models import User
|
||||
from problem.models import AbstractProblem
|
||||
from group.models import Group
|
||||
from utils.models import RichTextField
|
||||
from jsonfield import JSONField
|
||||
from judge.result import result
|
||||
|
||||
|
||||
GROUP_CONTEST = 0
|
||||
PUBLIC_CONTEST = 1
|
||||
PASSWORD_PROTECTED_CONTEST = 2
|
||||
PASSWORD_PROTECTED_GROUP_CONTEST = 3
|
||||
|
||||
CONTEST_NOT_START = 1
|
||||
CONTEST_ENDED = -1
|
||||
CONTEST_UNDERWAY = 0
|
||||
|
||||
|
||||
logger = logging.getLogger("app_info")
|
||||
|
||||
|
||||
class Contest(models.Model):
|
||||
title = models.CharField(max_length=40, unique=True)
|
||||
description = RichTextField()
|
||||
# 是否显示实时排名结果
|
||||
real_time_rank = models.BooleanField()
|
||||
# 只能超级管理员创建公开赛,管理员只能创建小组内部的比赛
|
||||
# 如果这一项不为空,即为有密码的公开赛,没有密码的可以为小组赛或者是公开赛(此时用比赛的类型来表示)
|
||||
password = models.CharField(max_length=30, blank=True, null=True)
|
||||
# 比赛的类型: 0 即为是小组赛(GROUP_CONTEST),1 即为是无密码的公开赛(PUBLIC_CONTEST),
|
||||
# 2 即为是有密码的公开赛(PASSWORD_PUBLIC_CONTEST)
|
||||
contest_type = models.IntegerField()
|
||||
# 开始时间
|
||||
start_time = models.DateTimeField()
|
||||
# 结束时间
|
||||
end_time = models.DateTimeField()
|
||||
# 创建时间
|
||||
create_time = models.DateTimeField(auto_now_add=True)
|
||||
# 最后修改时间
|
||||
last_updated_time = models.DateTimeField(auto_now=True)
|
||||
# 这个比赛是谁创建的
|
||||
created_by = models.ForeignKey(User)
|
||||
groups = models.ManyToManyField(Group)
|
||||
# 是否可见 false的话相当于删除
|
||||
visible = models.BooleanField(default=True)
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.start_time > now():
|
||||
# 没有开始 返回1
|
||||
return CONTEST_NOT_START
|
||||
elif self.end_time < now():
|
||||
# 已经结束 返回-1
|
||||
return CONTEST_ENDED
|
||||
else:
|
||||
# 正在进行 返回0
|
||||
return CONTEST_UNDERWAY
|
||||
|
||||
class Meta:
|
||||
db_table = "contest"
|
||||
|
||||
|
||||
class ContestProblem(AbstractProblem):
|
||||
contest = models.ForeignKey(Contest)
|
||||
# 比如A B 或者1 2 或者 a b 将按照这个排序
|
||||
sort_index = models.CharField(max_length=30)
|
||||
# 是否已经公开了题目,防止重复公开
|
||||
is_public = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
db_table = "contest_problem"
|
||||
|
||||
|
||||
class ContestRank(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
contest = models.ForeignKey(Contest)
|
||||
total_submission_number = models.IntegerField(default=0)
|
||||
total_ac_number = models.IntegerField(default=0)
|
||||
# ac 的题目才要加到这个字段里面 = ac 时间 + 错误次数 * 20 * 60
|
||||
# 没有 ac 的题目不计算罚时 单位是秒
|
||||
total_time = models.IntegerField(default=0)
|
||||
# 数据结构{23: {"is_ac": True, "ac_time": 8999, "error_number": 2, "is_first_ac": True}}
|
||||
# key 是比赛题目的id
|
||||
submission_info = JSONField(default={})
|
||||
|
||||
class Meta:
|
||||
db_table = "contest_rank"
|
||||
|
||||
def update_rank(self, submission):
|
||||
if not submission.contest_id or submission.contest_id != self.contest_id:
|
||||
raise ValueError("Error submission type")
|
||||
|
||||
if submission.result == result["system_error"]:
|
||||
logger.warning("submission " + submission.id + " result is system error, update rank operation is ignored")
|
||||
return
|
||||
|
||||
# 这道题以前提交过
|
||||
if str(submission.problem_id) in self.submission_info:
|
||||
info = self.submission_info[str(submission.problem_id)]
|
||||
# 如果这道题目已经 ac 了就跳过
|
||||
if info["is_ac"]:
|
||||
return
|
||||
|
||||
self.total_submission_number += 1
|
||||
|
||||
if submission.result == result["accepted"]:
|
||||
|
||||
self.total_ac_number += 1
|
||||
|
||||
info["is_ac"] = True
|
||||
info["ac_time"] = (submission.create_time - self.contest.start_time).total_seconds()
|
||||
|
||||
# 之前已经提交过,但是是错误的,这次提交是正确的。错误的题目不计入罚时
|
||||
self.total_time += (info["ac_time"] + info["error_number"] * 20 * 60)
|
||||
problem = ContestProblem.objects.get(id=submission.problem_id)
|
||||
# 更新题目计数器在前 所以是1
|
||||
if problem.total_accepted_number == 1:
|
||||
info["is_first_ac"] = True
|
||||
|
||||
else:
|
||||
info["error_number"] += 1
|
||||
info["is_ac"] = False
|
||||
|
||||
else:
|
||||
# 第一次提交这道题目
|
||||
self.total_submission_number += 1
|
||||
info = {"is_ac": False, "ac_time": 0, "error_number": 0, "is_first_ac": False}
|
||||
if submission.result == result["accepted"]:
|
||||
self.total_ac_number += 1
|
||||
info["is_ac"] = True
|
||||
info["ac_time"] = (submission.create_time - self.contest.start_time).total_seconds()
|
||||
self.total_time += info["ac_time"]
|
||||
problem = ContestProblem.objects.get(id=submission.problem_id)
|
||||
|
||||
if problem.total_accepted_number == 1:
|
||||
info["is_first_ac"] = True
|
||||
|
||||
else:
|
||||
info["is_ac"] = False
|
||||
info["error_number"] = 1
|
||||
self.submission_info[str(submission.problem_id)] = info
|
||||
self.save()
|
@ -1,119 +0,0 @@
|
||||
# coding=utf-8
|
||||
import json
|
||||
from rest_framework import serializers
|
||||
from django.utils import timezone
|
||||
import datetime
|
||||
from account.models import User
|
||||
from account.serializers import UserSerializer
|
||||
from .models import Contest, ContestProblem
|
||||
|
||||
|
||||
class CreateContestSerializer(serializers.Serializer):
|
||||
title = serializers.CharField(max_length=40)
|
||||
description = serializers.CharField(max_length=5000)
|
||||
contest_type = serializers.IntegerField()
|
||||
real_time_rank = serializers.BooleanField()
|
||||
password = serializers.CharField(max_length=30, required=False, default=None)
|
||||
start_time = serializers.DateTimeField()
|
||||
end_time = serializers.DateTimeField()
|
||||
groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[])
|
||||
visible = serializers.BooleanField()
|
||||
|
||||
|
||||
class DateTimeLocal(serializers.DateTimeField):
|
||||
def to_representation(self, value):
|
||||
return timezone.localtime(value)
|
||||
|
||||
|
||||
class ContestSerializer(serializers.ModelSerializer):
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["username"]
|
||||
|
||||
created_by = UserSerializer()
|
||||
start_time = DateTimeLocal()
|
||||
end_time = DateTimeLocal()
|
||||
|
||||
class Meta:
|
||||
model = Contest
|
||||
|
||||
|
||||
class EditContestSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
title = serializers.CharField(max_length=40)
|
||||
description = serializers.CharField(max_length=10000)
|
||||
contest_type = serializers.IntegerField()
|
||||
real_time_rank = serializers.BooleanField()
|
||||
password = serializers.CharField(max_length=30, required=False, default=None)
|
||||
start_time = serializers.DateTimeField()
|
||||
end_time = serializers.DateTimeField()
|
||||
groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[])
|
||||
visible = serializers.BooleanField()
|
||||
|
||||
|
||||
class ContestProblemSampleSerializer(serializers.ListField):
|
||||
input = serializers.CharField(max_length=3000)
|
||||
output = serializers.CharField(max_length=3000)
|
||||
|
||||
|
||||
class JSONField(serializers.Field):
|
||||
def to_representation(self, value):
|
||||
return json.loads(value)
|
||||
|
||||
|
||||
class CreateContestProblemSerializer(serializers.Serializer):
|
||||
contest_id = serializers.IntegerField()
|
||||
title = serializers.CharField(max_length=50)
|
||||
description = serializers.CharField(max_length=10000)
|
||||
input_description = serializers.CharField(max_length=10000)
|
||||
output_description = serializers.CharField(max_length=10000)
|
||||
# [{"input": "1 1", "output": "2"}]
|
||||
samples = ContestProblemSampleSerializer()
|
||||
test_case_id = serializers.CharField(max_length=40)
|
||||
time_limit = serializers.IntegerField()
|
||||
memory_limit = serializers.IntegerField()
|
||||
spj = serializers.BooleanField()
|
||||
spj_language = serializers.IntegerField(required=False, default=None)
|
||||
spj_code = serializers.CharField(max_length=10000, required=False, default=None)
|
||||
hint = serializers.CharField(max_length=3000, allow_blank=True)
|
||||
score = serializers.IntegerField(required=False, default=0)
|
||||
sort_index = serializers.CharField(max_length=30)
|
||||
|
||||
|
||||
class ContestProblemSerializer(serializers.ModelSerializer):
|
||||
class ContestSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Contest
|
||||
fields = ["title", "id"]
|
||||
|
||||
samples = JSONField()
|
||||
contest = ContestSerializer()
|
||||
created_by = UserSerializer()
|
||||
|
||||
class Meta:
|
||||
model = ContestProblem
|
||||
|
||||
|
||||
class EditContestProblemSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
title = serializers.CharField(max_length=50)
|
||||
description = serializers.CharField(max_length=10000)
|
||||
input_description = serializers.CharField(max_length=10000)
|
||||
output_description = serializers.CharField(max_length=10000)
|
||||
test_case_id = serializers.CharField(max_length=40)
|
||||
time_limit = serializers.IntegerField()
|
||||
memory_limit = serializers.IntegerField()
|
||||
spj = serializers.BooleanField()
|
||||
spj_language = serializers.IntegerField(required=False, default=None)
|
||||
spj_code = serializers.CharField(max_length=10000, required=False, default=None)
|
||||
samples = ContestProblemSampleSerializer()
|
||||
hint = serializers.CharField(max_length=3000, allow_blank=True)
|
||||
visible = serializers.BooleanField()
|
||||
sort_index = serializers.CharField(max_length=30)
|
||||
score = serializers.IntegerField(required=False, default=0)
|
||||
|
||||
|
||||
class ContestPasswordVerifySerializer(serializers.Serializer):
|
||||
contest_id = serializers.IntegerField()
|
||||
password = serializers.CharField(max_length=30)
|
612
contest/views.py
612
contest/views.py
@ -1,612 +0,0 @@
|
||||
# coding=utf-8
|
||||
import json
|
||||
import os
|
||||
import datetime
|
||||
import hashlib
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.db import IntegrityError
|
||||
from django.utils import dateparse
|
||||
from django.db.models import Q, Sum
|
||||
from django.core.paginator import Paginator
|
||||
from django.utils.timezone import now
|
||||
from django.conf import settings
|
||||
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from utils.shortcuts import (serializer_invalid_response, error_response,
|
||||
success_response, paginate, error_page, paginate_data)
|
||||
from account.models import SUPER_ADMIN, User
|
||||
from account.decorators import login_required, super_admin_required
|
||||
from group.models import Group, AdminGroupRelation, UserGroupRelation
|
||||
from utils.cache import get_cache_redis
|
||||
from submission.models import Submission
|
||||
from problem.models import Problem
|
||||
from .models import (Contest, ContestProblem, CONTEST_ENDED,
|
||||
CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank)
|
||||
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST
|
||||
from .decorators import check_user_contest_permission
|
||||
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
|
||||
CreateContestProblemSerializer, ContestProblemSerializer,
|
||||
ContestPasswordVerifySerializer,
|
||||
EditContestProblemSerializer)
|
||||
|
||||
|
||||
class ContestAdminAPIView(APIView):
|
||||
def post(self, request):
|
||||
"""
|
||||
比赛发布json api接口
|
||||
---
|
||||
request_serializer: CreateContestSerializer
|
||||
response_serializer: ContestSerializer
|
||||
"""
|
||||
serializer = CreateContestSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
groups = []
|
||||
# 首先判断比赛的类型: 0 即为是小组赛(GROUP_CONTEST),1 即为是无密码的公开赛(PUBLIC_CONTEST),
|
||||
# 2 即为是有密码的公开赛(PASSWORD_PUBLIC_CONTEST)
|
||||
# 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛
|
||||
if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]:
|
||||
if request.user.admin_type != SUPER_ADMIN:
|
||||
return error_response(u"只有超级管理员才可创建公开赛")
|
||||
|
||||
if data["contest_type"] in [PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]:
|
||||
if not data["password"]:
|
||||
return error_response(u"此比赛为有密码的比赛,密码不可为空")
|
||||
# 没有密码的公开赛 没有密码的小组赛
|
||||
if data["contest_type"] == GROUP_CONTEST or data["contest_type"] == PASSWORD_PROTECTED_GROUP_CONTEST:
|
||||
if request.user.admin_type == SUPER_ADMIN:
|
||||
groups = Group.objects.filter(id__in=data["groups"])
|
||||
else:
|
||||
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
|
||||
if not groups.count():
|
||||
return error_response(u"请至少选择一个小组")
|
||||
if data["start_time"] >= data["end_time"]:
|
||||
return error_response(u"比赛的开始时间必须早于比赛结束的时间")
|
||||
try:
|
||||
contest = Contest.objects.create(title=data["title"], description=data["description"],
|
||||
contest_type=data["contest_type"],
|
||||
real_time_rank=data["real_time_rank"], password=data["password"],
|
||||
start_time=dateparse.parse_datetime(data["start_time"]),
|
||||
end_time=dateparse.parse_datetime(data["end_time"]),
|
||||
created_by=request.user, visible=data["visible"])
|
||||
except IntegrityError:
|
||||
return error_response(u"比赛名已经存在")
|
||||
contest.groups.add(*groups)
|
||||
return success_response(ContestSerializer(contest).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def put(self, request):
|
||||
"""
|
||||
比赛编辑json api接口
|
||||
---
|
||||
request_serializer: EditContestSerializer
|
||||
response_serializer: ContestSerializer
|
||||
"""
|
||||
serializer = EditContestSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
groups = []
|
||||
try:
|
||||
# 超级管理员可以编辑所有的
|
||||
contest = Contest.objects.get(id=data["id"])
|
||||
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"无权访问!")
|
||||
except Contest.DoesNotExist:
|
||||
return error_response(u"该比赛不存在!")
|
||||
try:
|
||||
contest = Contest.objects.get(title=data["title"])
|
||||
if contest.id != data["id"]:
|
||||
return error_response(u"该比赛名称已经存在")
|
||||
except Contest.DoesNotExist:
|
||||
pass
|
||||
if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]:
|
||||
if request.user.admin_type != SUPER_ADMIN:
|
||||
return error_response(u"只有超级管理员才可创建公开赛")
|
||||
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
|
||||
if not data["password"]:
|
||||
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
|
||||
elif data["contest_type"] in [GROUP_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]:
|
||||
if request.user.admin_type == SUPER_ADMIN:
|
||||
groups = Group.objects.filter(id__in=data["groups"])
|
||||
else:
|
||||
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
|
||||
if not groups.count():
|
||||
return error_response(u"请至少选择一个小组")
|
||||
if data["start_time"] >= data["end_time"]:
|
||||
return error_response(u"比赛的开始时间必须早于比赛结束的时间")
|
||||
|
||||
# 之前是封榜,现在要开放,需要清除缓存
|
||||
if contest.real_time_rank == False and data["real_time_rank"] == True:
|
||||
r = get_cache_redis()
|
||||
cache_key = str(contest.id) + "_rank_cache"
|
||||
r.delete(cache_key)
|
||||
|
||||
contest.title = data["title"]
|
||||
contest.description = data["description"]
|
||||
contest.contest_type = data["contest_type"]
|
||||
contest.real_time_rank = data["real_time_rank"]
|
||||
contest.start_time = dateparse.parse_datetime(data["start_time"])
|
||||
contest.end_time = dateparse.parse_datetime(data["end_time"])
|
||||
contest.visible = data["visible"]
|
||||
contest.password = data["password"]
|
||||
contest.save()
|
||||
|
||||
contest.groups.clear()
|
||||
contest.groups.add(*groups)
|
||||
return success_response(ContestSerializer(contest).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
比赛分页json api接口
|
||||
---
|
||||
response_serializer: ContestSerializer
|
||||
"""
|
||||
contest_id = request.GET.get("contest_id", None)
|
||||
if contest_id:
|
||||
try:
|
||||
# 普通管理员只能获取自己创建的题目
|
||||
# 超级管理员可以获取全部的题目
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
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 success_response(ContestSerializer(contest).data)
|
||||
except Contest.DoesNotExist:
|
||||
return error_response(u"比赛不存在")
|
||||
|
||||
if request.user.admin_type == SUPER_ADMIN:
|
||||
contest = Contest.objects.all().order_by("-create_time")
|
||||
else:
|
||||
contest = Contest.objects.filter(groups__in=request.user.managed_groups.all()).distinct().order_by("-create_time")
|
||||
visible = request.GET.get("visible", None)
|
||||
if visible:
|
||||
contest = contest.filter(visible=(visible == "true"))
|
||||
keyword = request.GET.get("keyword", None)
|
||||
if keyword:
|
||||
contest = contest.filter(Q(title__contains=keyword) |
|
||||
Q(description__contains=keyword))
|
||||
return paginate(request, contest, ContestSerializer)
|
||||
|
||||
|
||||
class ContestProblemAdminAPIView(APIView):
|
||||
def _spj_version(self, code):
|
||||
if code is None:
|
||||
return None
|
||||
return hashlib.md5(code.encode("utf-8")).hexdigest()
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
比赛题目发布json api接口
|
||||
---
|
||||
request_serializer: CreateContestProblemSerializer
|
||||
response_serializer: ContestProblemSerializer
|
||||
"""
|
||||
serializer = CreateContestProblemSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
contest = Contest.objects.get(id=data["contest_id"])
|
||||
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"比赛不存在")
|
||||
except Contest.DoesNotExist:
|
||||
return error_response(u"比赛不存在")
|
||||
contest_problem = ContestProblem.objects.create(title=data["title"],
|
||||
description=data["description"],
|
||||
input_description=data["input_description"],
|
||||
output_description=data["output_description"],
|
||||
test_case_id=data["test_case_id"],
|
||||
samples=json.dumps(data["samples"]),
|
||||
time_limit=data["time_limit"],
|
||||
memory_limit=data["memory_limit"],
|
||||
spj=data["spj"],
|
||||
spj_language=data["spj_language"],
|
||||
spj_code=data["spj_code"],
|
||||
spj_version=self._spj_version(data["spj_code"]),
|
||||
created_by=request.user,
|
||||
hint=data["hint"],
|
||||
contest=contest,
|
||||
sort_index=data["sort_index"])
|
||||
return success_response(ContestProblemSerializer(contest_problem).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def put(self, request):
|
||||
"""
|
||||
比赛题目编辑json api接口
|
||||
---
|
||||
request_serializer: EditContestProblemSerializer
|
||||
response_serializer: ContestProblemSerializer
|
||||
"""
|
||||
serializer = EditContestProblemSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
|
||||
try:
|
||||
contest_problem = ContestProblem.objects.get(id=data["id"])
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_response(u"该比赛题目不存在!")
|
||||
|
||||
contest = Contest.objects.get(id=contest_problem.contest_id)
|
||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
||||
return error_response(u"比赛不存在")
|
||||
contest_problem.title = data["title"]
|
||||
contest_problem.description = data["description"]
|
||||
contest_problem.input_description = data["input_description"]
|
||||
contest_problem.output_description = data["output_description"]
|
||||
contest_problem.test_case_id = data["test_case_id"]
|
||||
contest_problem.time_limit = data["time_limit"]
|
||||
contest_problem.memory_limit = data["memory_limit"]
|
||||
contest_problem.spj = data["spj"]
|
||||
contest_problem.spj_language = data["spj_language"]
|
||||
contest_problem.spj_code = data["spj_code"]
|
||||
contest_problem.spj_version = self._spj_version(data["spj_code"])
|
||||
contest_problem.samples = json.dumps(data["samples"])
|
||||
contest_problem.hint = data["hint"]
|
||||
contest_problem.visible = data["visible"]
|
||||
contest_problem.sort_index = data["sort_index"]
|
||||
contest_problem.last_update_time = now()
|
||||
contest_problem.save()
|
||||
return success_response(ContestProblemSerializer(contest_problem).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
比赛题目分页json api接口
|
||||
---
|
||||
response_serializer: ContestProblemSerializer
|
||||
"""
|
||||
contest_problem_id = request.GET.get("contest_problem_id", None)
|
||||
if contest_problem_id:
|
||||
try:
|
||||
contest_problem = ContestProblem.objects.get(id=contest_problem_id)
|
||||
if request.user.admin_type != SUPER_ADMIN and contest_problem.created_by != request.user:
|
||||
return error_response(u"比赛题目不存在")
|
||||
return success_response(ContestProblemSerializer(contest_problem).data)
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_response(u"比赛题目不存在")
|
||||
|
||||
contest_problems = ContestProblem.objects.all().order_by("sort_index")
|
||||
if request.user.admin_type != SUPER_ADMIN:
|
||||
contest_problems = contest_problems.filter(created_by=request.user).order_by("sort_index")
|
||||
visible = request.GET.get("visible", None)
|
||||
if visible:
|
||||
contest_problems = contest_problems.filter(visible=(visible == "true"))
|
||||
keyword = request.GET.get("keyword", None)
|
||||
if keyword:
|
||||
contest_problems = contest_problems.filter(Q(title__contains=keyword) |
|
||||
Q(description__contains=keyword))
|
||||
contest_id = request.GET.get("contest_id", None)
|
||||
if contest_id:
|
||||
contest_problems = contest_problems.filter(contest__id=contest_id).order_by("sort_index")
|
||||
|
||||
return paginate(request, contest_problems, ContestProblemSerializer)
|
||||
|
||||
|
||||
class MakeContestProblemPublicAPIView(APIView):
|
||||
@super_admin_required
|
||||
def post(self, request):
|
||||
problem_id = request.data.get("problem_id", -1)
|
||||
try:
|
||||
problem = ContestProblem.objects.get(id=problem_id, is_public=False)
|
||||
if problem.contest.status != CONTEST_ENDED:
|
||||
return error_response(u"比赛还没有结束,不能公开题目")
|
||||
problem.is_public = True
|
||||
problem.save()
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_response(u"比赛不存在")
|
||||
Problem.objects.create(title=problem.title, description=problem.description,
|
||||
input_description=problem.input_description,
|
||||
output_description=problem.output_description,
|
||||
samples=problem.samples,
|
||||
test_case_id=problem.test_case_id,
|
||||
hint=problem.hint, created_by=problem.created_by,
|
||||
time_limit=problem.time_limit, memory_limit=problem.memory_limit,
|
||||
visible=False, difficulty=-1, source=problem.contest.title)
|
||||
problem.is_public = True
|
||||
problem.save()
|
||||
return success_response(u"创建成功")
|
||||
|
||||
|
||||
class ContestPasswordVerifyAPIView(APIView):
|
||||
@login_required
|
||||
def post(self, request):
|
||||
serializer = ContestPasswordVerifySerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = request.data
|
||||
try:
|
||||
contest = Contest.objects.get(id=data["contest_id"], contest_type__in=[PASSWORD_PROTECTED_CONTEST,PASSWORD_PROTECTED_GROUP_CONTEST])
|
||||
except Contest.DoesNotExist:
|
||||
return error_response(u"比赛不存在")
|
||||
|
||||
if data["password"] != contest.password:
|
||||
return error_response(u"密码错误")
|
||||
else:
|
||||
if "contests" not in request.session:
|
||||
request.session["contests"] = []
|
||||
request.session["contests"].append(int(data["contest_id"]))
|
||||
# https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved
|
||||
request.session.modified = True
|
||||
|
||||
return success_response(True)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
@check_user_contest_permission
|
||||
def contest_page(request, contest_id):
|
||||
"""
|
||||
单个比赛的详情页
|
||||
"""
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
return render(request, "oj/contest/contest_index.html", {"contest": contest})
|
||||
|
||||
|
||||
@check_user_contest_permission
|
||||
def contest_problem_page(request, contest_id, contest_problem_id):
|
||||
"""
|
||||
单个比赛题目的详情页
|
||||
"""
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
try:
|
||||
problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_page(request, u"比赛题目不存在")
|
||||
warning = u"您已经提交过本题的正确答案,重复提交可能造成时间累计。"
|
||||
show_warning = False
|
||||
|
||||
try:
|
||||
rank = ContestRank.objects.get(user=request.user, contest=contest)
|
||||
# 提示已经 ac 过这道题了
|
||||
show_warning = rank.submission_info.get(str(problem.id), {"is_ac": False})["is_ac"]
|
||||
except ContestRank.DoesNotExist:
|
||||
pass
|
||||
|
||||
# 已经结束
|
||||
if contest.status == CONTEST_ENDED:
|
||||
show_warning = True
|
||||
warning = u"比赛已经结束"
|
||||
elif contest.status == CONTEST_NOT_START:
|
||||
show_warning = True
|
||||
warning = u"比赛没有开始,您是管理员,可以提交和测试题目,但是目前的提交不会计入排名。"
|
||||
|
||||
show_submit_code_area = False
|
||||
if contest.status == CONTEST_UNDERWAY or \
|
||||
request.user.admin_type == SUPER_ADMIN or \
|
||||
request.user == contest.created_by:
|
||||
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,
|
||||
"contest": contest,
|
||||
"samples": json.loads(problem.samples),
|
||||
"show_warning": show_warning,
|
||||
"warning": warning,
|
||||
"show_submit_code_area": show_submit_code_area})
|
||||
|
||||
|
||||
@check_user_contest_permission
|
||||
def contest_problems_list_page(request, contest_id):
|
||||
"""
|
||||
比赛所有题目的列表页
|
||||
"""
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).select_related("contest").order_by("sort_index")
|
||||
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
|
||||
"contest": {"id": contest_id}})
|
||||
|
||||
|
||||
def contest_list_page(request, page=1):
|
||||
"""
|
||||
所有比赛的列表页
|
||||
"""
|
||||
# 正常情况
|
||||
contests = Contest.objects.filter(visible=True).order_by("-create_time")
|
||||
|
||||
# 搜索的情况
|
||||
keyword = request.GET.get("keyword", "").strip()
|
||||
if keyword:
|
||||
contests = contests.filter(Q(title__contains=keyword) | Q(description__contains=keyword))
|
||||
|
||||
# 筛选我能参加的比赛
|
||||
join = request.GET.get("join", None)
|
||||
if request.user.is_authenticated() and join:
|
||||
contests = contests.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())). \
|
||||
filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now())
|
||||
paginator = Paginator(contests, 20)
|
||||
try:
|
||||
current_page = paginator.page(int(page))
|
||||
except Exception:
|
||||
return error_page(request, u"不存在的页码")
|
||||
|
||||
previous_page = next_page = None
|
||||
|
||||
try:
|
||||
previous_page = current_page.previous_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
next_page = current_page.next_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return render(request, "oj/contest/contest_list.html",
|
||||
{"contests": current_page, "page": int(page),
|
||||
"previous_page": previous_page, "next_page": next_page,
|
||||
"keyword": keyword, "join": join})
|
||||
|
||||
|
||||
def _get_rank(contest_id):
|
||||
rank = ContestRank.objects.filter(contest_id=contest_id). \
|
||||
select_related("user"). \
|
||||
order_by("-total_ac_number", "total_time"). \
|
||||
values("id", "user__id", "user__username", "user__real_name", "user__userprofile__student_id",
|
||||
"contest_id", "submission_info", "total_submission_number", "total_ac_number", "total_time")
|
||||
rank_number = 1
|
||||
for item in rank:
|
||||
# 只有有ac的题目而且不是打星的队伍才参与排名
|
||||
if item["total_ac_number"] > 0 and item["user__username"][0] != "*":
|
||||
item["rank_number"] = rank_number
|
||||
rank_number += 1
|
||||
return rank
|
||||
|
||||
|
||||
@check_user_contest_permission
|
||||
def contest_rank_page(request, contest_id):
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).order_by("sort_index")
|
||||
|
||||
force_real_time_rank = False
|
||||
if request.GET.get("force_real_time_rank") == "true" and (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by):
|
||||
rank = _get_rank(contest_id)
|
||||
force_real_time_rank = True
|
||||
else:
|
||||
r = get_cache_redis()
|
||||
cache_key = str(contest_id) + "_rank_cache"
|
||||
rank = r.get(cache_key)
|
||||
|
||||
if not rank:
|
||||
rank = _get_rank(contest_id)
|
||||
r.set(cache_key, json.dumps([dict(item) for item in rank]))
|
||||
else:
|
||||
rank = json.loads(rank)
|
||||
|
||||
# 2016-05-19 增加了缓存项目,以前的缓存主动失效
|
||||
if rank and "rank_number" not in rank[0]:
|
||||
rank = _get_rank(contest_id)
|
||||
r.set(cache_key, json.dumps([dict(item) for item in rank]))
|
||||
|
||||
return render(request, "oj/contest/contest_rank.html",
|
||||
{"rank": rank, "contest": contest,
|
||||
"contest_problems": contest_problems,
|
||||
"auto_refresh": request.GET.get("auto_refresh", None) == "true",
|
||||
"show_real_name": request.GET.get("show_real_name", None) == "true",
|
||||
"force_real_time_rank": force_real_time_rank})
|
||||
|
||||
|
||||
class ContestTimeAPIView(APIView):
|
||||
"""
|
||||
获取比赛开始或者结束的倒计时,返回毫秒数字
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
contest_id = request.GET.get("contest_id", -1)
|
||||
try:
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
except Contest.DoesNotExist:
|
||||
return error_response(u"比赛不存在")
|
||||
return success_response({"start": int((contest.start_time - now()).total_seconds() * 1000),
|
||||
"end": int((contest.end_time - now()).total_seconds() * 1000),
|
||||
"status": contest.status})
|
||||
|
||||
|
||||
@login_required
|
||||
def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id):
|
||||
"""
|
||||
我比赛单个题目的所有提交列表
|
||||
"""
|
||||
try:
|
||||
Contest.objects.get(id=contest_id)
|
||||
except Contest.DoesNotExist:
|
||||
return error_page(request, u"比赛不存在")
|
||||
try:
|
||||
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_page(request, u"比赛问题不存在")
|
||||
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id, contest_id=contest_id). \
|
||||
order_by("-create_time"). \
|
||||
values("id", "result", "create_time", "accepted_answer_time", "language")
|
||||
return render(request, "oj/submission/problem_my_submissions_list.html",
|
||||
{"submissions": submissions, "problem": contest_problem})
|
||||
|
||||
|
||||
@check_user_contest_permission
|
||||
def contest_problem_submissions_list_page(request, contest_id, page=1):
|
||||
"""
|
||||
单个比赛中的所有提交(包含自己和别人,自己可查提交结果,其他人不可查)
|
||||
"""
|
||||
try:
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
except Contest.DoesNotExist:
|
||||
return error_page(request, u"比赛不存在")
|
||||
|
||||
submissions = Submission.objects.filter(contest_id=contest_id). \
|
||||
values("id", "contest_id", "problem_id", "result", "create_time",
|
||||
"accepted_answer_time", "language", "user_id").order_by("-create_time")
|
||||
|
||||
# 如果比赛已经开始,就不再显示之前测试题目的提交
|
||||
if contest.status != CONTEST_NOT_START:
|
||||
submissions = submissions.filter(create_time__gte=contest.start_time)
|
||||
|
||||
user_id = request.GET.get("user_id", None)
|
||||
if user_id:
|
||||
submissions = submissions.filter(user_id=request.GET.get("user_id"))
|
||||
|
||||
problem_id = request.GET.get("problem_id", None)
|
||||
if problem_id:
|
||||
submissions = submissions.filter(problem_id=problem_id)
|
||||
|
||||
# 封榜的时候只能看到自己的提交
|
||||
if not contest.real_time_rank:
|
||||
if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by):
|
||||
submissions = submissions.filter(user_id=request.user.id)
|
||||
|
||||
language = request.GET.get("language", None)
|
||||
filter = None
|
||||
if language:
|
||||
submissions = submissions.filter(language=int(language))
|
||||
filter = {"name": "language", "content": language}
|
||||
result = request.GET.get("result", None)
|
||||
if result:
|
||||
submissions = submissions.filter(result=int(result))
|
||||
filter = {"name": "result", "content": result}
|
||||
|
||||
paginator = Paginator(submissions, 20)
|
||||
try:
|
||||
submissions = paginator.page(int(page))
|
||||
except Exception:
|
||||
return error_page(request, u"不存在的页码")
|
||||
|
||||
# 为查询题目标题创建新字典
|
||||
title = {}
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest)
|
||||
for item in contest_problems:
|
||||
title[item.id] = item.title
|
||||
for item in submissions:
|
||||
item['title'] = title[item['problem_id']]
|
||||
|
||||
previous_page = next_page = None
|
||||
try:
|
||||
previous_page = submissions.previous_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
next_page = submissions.next_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for item in submissions:
|
||||
# 自己提交的 管理员和创建比赛的可以看到所有的提交链接
|
||||
if item["user_id"] == request.user.id or request.user.admin_type == SUPER_ADMIN or \
|
||||
request.user == contest.created_by:
|
||||
item["show_link"] = True
|
||||
else:
|
||||
item["show_link"] = False
|
||||
|
||||
return render(request, "oj/contest/submissions_list.html",
|
||||
{"submissions": submissions, "page": int(page),
|
||||
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
|
||||
"contest": contest, "filter": filter, "user_id": user_id, "problem_id": problem_id})
|
@ -1,4 +1,4 @@
|
||||
django
|
||||
django<1.10
|
||||
MySQL-python
|
||||
redis
|
||||
django-redis-sessions
|
||||
|
@ -125,7 +125,7 @@
|
||||
}
|
||||
}
|
||||
this.request({
|
||||
url: "/api/admin/user/",
|
||||
url: "/api/admin/account/user/",
|
||||
method: "PUT",
|
||||
data: data
|
||||
})
|
||||
@ -134,7 +134,7 @@
|
||||
route: {
|
||||
data() {
|
||||
this.request({
|
||||
url: "/api/admin/user/?user_id=" + this.$route.params["userId"],
|
||||
url: "/api/admin/account/user/?user_id=" + this.$route.params["userId"],
|
||||
method: "GET",
|
||||
success: (data)=> {
|
||||
this.user = data.data;
|
||||
|
@ -82,7 +82,7 @@
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
var url = "/api/admin/user/?paging=true&page_size=2&page=" + this.pagination.currentPage;
|
||||
var url = "/api/admin/account/user/?paging=true&page_size=2&page=" + this.pagination.currentPage;
|
||||
if (this.keyword) {
|
||||
url += ("&keyword=" + this.keyword)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<td>{{ announcement.create_time }}</td>
|
||||
<td>{{ announcement.last_update_time }}</td>
|
||||
<td>{{ announcement.created_by.username }}</td>
|
||||
<td>{{ $t(announcementStatus[announcement.visible?1:0]) }}</td>
|
||||
<td>{{ $t(announcementStatus[announcement.visible?0:1]) }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn-sm btn-info" v-on:click="edit(announcement.id)">{{ $t("adminUtils.edit") }}</button>
|
||||
</td>
|
||||
|
@ -1,61 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Group',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=30)),
|
||||
('description', models.TextField()),
|
||||
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||
('join_group_setting', models.IntegerField()),
|
||||
('visible', models.BooleanField(default=True)),
|
||||
('admin', models.ForeignKey(related_name='my_groups', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'group',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='JoinGroupRequest',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('message', models.TextField()),
|
||||
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||
('status', models.BooleanField(default=False)),
|
||||
('group', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('user', models.ForeignKey(related_name='my_join_group_requests', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'join_group_request',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserGroupRelation',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('join_time', models.DateTimeField(auto_now_add=True)),
|
||||
('group', models.ForeignKey(to='group.Group')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'user_group_relation',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='group',
|
||||
name='members',
|
||||
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='group.UserGroupRelation'),
|
||||
),
|
||||
]
|
@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='group',
|
||||
name='name',
|
||||
field=models.CharField(unique=True, max_length=30),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='joingrouprequest',
|
||||
name='group',
|
||||
field=models.ForeignKey(to='group.Group'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='usergrouprelation',
|
||||
unique_together=set([('group', 'user')]),
|
||||
),
|
||||
]
|
@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='group',
|
||||
name='name',
|
||||
field=models.CharField(unique=True, max_length=30),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='joingrouprequest',
|
||||
name='group',
|
||||
field=models.ForeignKey(to='group.Group'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='usergrouprelation',
|
||||
unique_together=set([('group', 'user')]),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0002_auto_20150811_1456'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='group',
|
||||
name='join_group_setting',
|
||||
field=models.IntegerField(default=1),
|
||||
),
|
||||
]
|
@ -1,15 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0003_auto_20150811_1906'),
|
||||
('group', '0002_auto_20150811_1649'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0004_merge'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='joingrouprequest',
|
||||
name='accepted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- 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',
|
||||
),
|
||||
]
|
@ -1,38 +0,0 @@
|
||||
# -*- 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')]),
|
||||
),
|
||||
]
|
@ -1,53 +0,0 @@
|
||||
# coding=utf-8
|
||||
from django.db import models
|
||||
|
||||
from account.models import User
|
||||
|
||||
|
||||
class Group(models.Model):
|
||||
name = models.CharField(max_length=30, unique=True)
|
||||
description = models.TextField()
|
||||
create_time = models.DateTimeField(auto_now_add=True)
|
||||
created_by = models.ForeignKey(User, related_name="my_groups")
|
||||
# 0是公开 1是需要申请后加入 2是不允许任何人加入
|
||||
join_group_setting = models.IntegerField(default=1)
|
||||
members = models.ManyToManyField(User, through="UserGroupRelation")
|
||||
admin = models.ManyToManyField(User, through="AdminGroupRelation", related_name="managed_groups")
|
||||
# 解散小组后,这一项改为False
|
||||
visible = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "group"
|
||||
|
||||
|
||||
class UserGroupRelation(models.Model):
|
||||
group = models.ForeignKey(Group)
|
||||
user = models.ForeignKey(User)
|
||||
join_time = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "user_group_relation"
|
||||
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):
|
||||
group = models.ForeignKey(Group)
|
||||
user = models.ForeignKey(User, related_name="my_join_group_requests")
|
||||
message = models.TextField()
|
||||
create_time = models.DateTimeField(auto_now_add=True)
|
||||
# 是否处理
|
||||
status = models.BooleanField(default=False)
|
||||
accepted = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
db_table = "join_group_request"
|
@ -1,81 +0,0 @@
|
||||
# coding=utf-8
|
||||
from rest_framework import serializers
|
||||
|
||||
from account.models import User
|
||||
from account.serializers import UserSerializer
|
||||
from .models import Group, UserGroupRelation, JoinGroupRequest
|
||||
|
||||
|
||||
class CreateGroupSerializer(serializers.Serializer):
|
||||
name = serializers.CharField(max_length=20)
|
||||
description = serializers.CharField(max_length=300)
|
||||
join_group_setting = serializers.IntegerField(min_value=0, max_value=2)
|
||||
|
||||
|
||||
class EditGroupSerializer(serializers.Serializer):
|
||||
group_id = serializers.IntegerField()
|
||||
name = serializers.CharField(max_length=20)
|
||||
description = serializers.CharField(max_length=300)
|
||||
join_group_setting = serializers.IntegerField()
|
||||
visible = serializers.BooleanField()
|
||||
|
||||
|
||||
class CreateJoinGroupRequestSerializer(serializers.Serializer):
|
||||
group_id = serializers.IntegerField()
|
||||
message = serializers.CharField(max_length=30, required=False)
|
||||
|
||||
|
||||
class JoinGroupRequestSerializer(serializers.ModelSerializer):
|
||||
class GroupSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Group
|
||||
fields = ["id", "name"]
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["username"]
|
||||
|
||||
group = GroupSerializer()
|
||||
user = UserSerializer()
|
||||
|
||||
class Meta:
|
||||
model = JoinGroupRequest
|
||||
|
||||
|
||||
class GroupSerializer(serializers.ModelSerializer):
|
||||
members_number = serializers.SerializerMethodField("_get_group_members_number")
|
||||
|
||||
def _get_group_members_number(self, group):
|
||||
return group.members.all().count()
|
||||
|
||||
class Meta:
|
||||
model = Group
|
||||
exclude = ["members"]
|
||||
|
||||
|
||||
class GroupMemberSerializer(serializers.ModelSerializer):
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["id", "username", "real_name"]
|
||||
|
||||
user = UserSerializer()
|
||||
|
||||
class Meta:
|
||||
model = UserGroupRelation
|
||||
exclude = ["id"]
|
||||
|
||||
|
||||
class EditGroupMemberSerializer(serializers.Serializer):
|
||||
group_id = serializers.IntegerField()
|
||||
members = serializers.ListField(child=serializers.IntegerField())
|
||||
|
||||
|
||||
class PutJoinGroupRequestSerializer(serializers.Serializer):
|
||||
request_id = serializers.IntegerField()
|
||||
status = serializers.BooleanField()
|
||||
|
||||
class GroupPromoteAdminSerializer(serializers.Serializer):
|
||||
user_id = serializers.IntegerField()
|
||||
group_id = serializers.IntegerField()
|
355
group/views.py
355
group/views.py
@ -1,355 +0,0 @@
|
||||
# coding=utf-8
|
||||
from django.shortcuts import render
|
||||
from django.db import IntegrityError
|
||||
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate, error_page
|
||||
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User
|
||||
from account.decorators import login_required
|
||||
|
||||
from .models import Group, JoinGroupRequest, UserGroupRelation, AdminGroupRelation
|
||||
from .serializers import (CreateGroupSerializer, EditGroupSerializer,
|
||||
CreateJoinGroupRequestSerializer, GroupSerializer,
|
||||
GroupMemberSerializer, EditGroupMemberSerializer,
|
||||
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer, GroupPromoteAdminSerializer)
|
||||
from announcement.models import Announcement
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
class GroupAPIViewBase(object):
|
||||
def get_group(self, request, group_id):
|
||||
"""
|
||||
根据group_id查询指定的小组的信息,结合判断用户权限
|
||||
管理员可以查询所有的小组,其他用户查询自己创建的小组
|
||||
"""
|
||||
if request.user.admin_type == SUPER_ADMIN:
|
||||
group = Group.objects.get(id=group_id)
|
||||
else:
|
||||
group = Group.objects.get(id=group_id, visible=True, admin=request.user)
|
||||
return group
|
||||
|
||||
def get_groups(self, request):
|
||||
"""
|
||||
如果是超级管理员,就返回全部的小组
|
||||
如果是管理员,就返回他创建的全部小组
|
||||
"""
|
||||
if request.user.admin_type == SUPER_ADMIN:
|
||||
groups = Group.objects.filter()
|
||||
else:
|
||||
groups = Group.objects.filter(admin=request.user, visible=True)
|
||||
return groups
|
||||
|
||||
|
||||
class GroupAdminAPIView(APIView, GroupAPIViewBase):
|
||||
def post(self, request):
|
||||
"""
|
||||
创建小组的api
|
||||
---
|
||||
request_serializer: CreateGroupSerializer
|
||||
response_serializer: GroupSerializer
|
||||
"""
|
||||
serializer = CreateGroupSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
group = Group.objects.create(name=data["name"],
|
||||
description=data["description"],
|
||||
join_group_setting=data["join_group_setting"],
|
||||
created_by=request.user)
|
||||
except IntegrityError:
|
||||
return error_response(u"小组名已经存在")
|
||||
AdminGroupRelation.objects.create(group=group, user=request.user)
|
||||
return success_response(GroupSerializer(group).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def put(self, request):
|
||||
"""
|
||||
修改小组信息的api
|
||||
---
|
||||
request_serializer: EditGroupSerializer
|
||||
response_serializer: GroupSerializer
|
||||
"""
|
||||
serializer = EditGroupSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
group = self.get_group(request, data["group_id"])
|
||||
except Group.DoesNotExist:
|
||||
return error_response(u"小组不存在")
|
||||
try:
|
||||
group.name = data["name"]
|
||||
group.description = data["description"]
|
||||
group.join_group_setting = data["join_group_setting"]
|
||||
group.visible = data["visible"]
|
||||
group.save()
|
||||
except IntegrityError:
|
||||
return error_response(u"小组名已经存在")
|
||||
|
||||
return success_response(GroupSerializer(group).data)
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
查询小组列表或者单个小组的信息,查询单个小组需要传递group_id参数,否则返回全部
|
||||
---
|
||||
response_serializer: GroupSerializer
|
||||
"""
|
||||
group_id = request.GET.get("group_id", None)
|
||||
# 根据 id 查询小组信息
|
||||
if group_id:
|
||||
try:
|
||||
group = self.get_group(request, group_id)
|
||||
return success_response(GroupSerializer(group).data)
|
||||
except Group.DoesNotExist:
|
||||
return error_response(u"小组不存在")
|
||||
else:
|
||||
groups = self.get_groups(request)
|
||||
# 搜索小组
|
||||
if request.GET.get("keyword", None):
|
||||
groups = groups.filter(name__contains=request.GET["keyword"])
|
||||
# 只返回我创建的小组 适用于超级管理员
|
||||
if request.GET.get("my_group", None):
|
||||
groups = groups.filter(admin=request.user)
|
||||
# 只返回指定用户的小组 适用于管理员
|
||||
elif request.GET.get("admin_id", None):
|
||||
groups = groups.filter(admin__id=request.GET["admin_id"])
|
||||
return paginate(request, groups, GroupSerializer)
|
||||
|
||||
|
||||
class GroupMemberAdminAPIView(APIView, GroupAPIViewBase):
|
||||
def get(self, request):
|
||||
"""
|
||||
查询小组成员的api,需要传递group_id参数
|
||||
---
|
||||
response_serializer: GroupMemberSerializer
|
||||
"""
|
||||
group_id = request.GET.get("group_id", None)
|
||||
if not group_id:
|
||||
return error_response(u"参数错误")
|
||||
try:
|
||||
group = self.get_group(request, group_id)
|
||||
except Group.DoesNotExist:
|
||||
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, members, GroupMemberSerializer)
|
||||
|
||||
def put(self, request):
|
||||
"""
|
||||
删除小组成员的api接口
|
||||
---
|
||||
request_serializer: EditGroupMemberSerializer
|
||||
"""
|
||||
serializer = EditGroupMemberSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
try:
|
||||
group = self.get_group(request, serializer.data["group_id"])
|
||||
except Group.DoesNotExist:
|
||||
return error_response(u"小组不存在")
|
||||
user_id_list = serializer.data["members"]
|
||||
UserGroupRelation.objects.filter(group=group, user__id__in=user_id_list).delete()
|
||||
return success_response(u"删除成功")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
def join_group(user, group):
|
||||
try:
|
||||
UserGroupRelation.objects.create(user=user, group=group)
|
||||
return True
|
||||
except IntegrityError:
|
||||
return False
|
||||
|
||||
|
||||
class JoinGroupAPIView(APIView):
|
||||
# @login_required
|
||||
def post(self, request):
|
||||
"""
|
||||
加入某个小组的api
|
||||
---
|
||||
request_serializer: CreateJoinGroupRequestSerializer
|
||||
"""
|
||||
serializer = CreateJoinGroupRequestSerializer(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"小组不存在")
|
||||
if group.join_group_setting == 0:
|
||||
if join_group(request.user, group):
|
||||
return success_response(u"你已经成功的加入该小组")
|
||||
else:
|
||||
return error_response(u"你已经是小组成员了")
|
||||
elif group.join_group_setting == 1:
|
||||
if not data["message"]:
|
||||
return error_response(u"message : 该字段是必填项。")
|
||||
try:
|
||||
JoinGroupRequest.objects.get(user=request.user, group=group, status=False)
|
||||
return error_response(u"你已经提交过申请了,请等待审核")
|
||||
except JoinGroupRequest.DoesNotExist:
|
||||
JoinGroupRequest.objects.create(user=request.user, group=group, message=data["message"])
|
||||
return success_response(u"申请提交成功,请等待审核")
|
||||
elif group.join_group_setting == 2:
|
||||
return error_response(u"该小组不允许任何人加入")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
搜索小组的api,需要传递keyword参数
|
||||
---
|
||||
response_serializer: GroupSerializer
|
||||
"""
|
||||
keyword = request.GET.get("keyword", None)
|
||||
if not keyword:
|
||||
return error_response(u"参数错误")
|
||||
# 搜索包含这个关键词的 没有解散的 而且允许加入的小组
|
||||
groups = Group.objects.filter(name__contains=keyword, visible=True, join_group_setting__lte=2)
|
||||
return paginate(request, groups, GroupSerializer)
|
||||
|
||||
|
||||
class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
|
||||
def get(self, request):
|
||||
"""
|
||||
返回管理的群的加群请求
|
||||
---
|
||||
response_serializer: JoinGroupRequestSerializer
|
||||
"""
|
||||
requests = JoinGroupRequest.objects.filter(group__in=Group.objects.filter(admin=request.user, visible=True),
|
||||
status=False)
|
||||
return paginate(request, requests, JoinGroupRequestSerializer)
|
||||
|
||||
def put(self, request):
|
||||
"""
|
||||
同意或者拒绝加入小组请求
|
||||
---
|
||||
request_serializer: PutJoinGroupRequestSerializer
|
||||
"""
|
||||
serializer = PutJoinGroupRequestSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
join_request = JoinGroupRequest.objects.get(id=data["request_id"], group__admin=request.user,
|
||||
status=False)
|
||||
except JoinGroupRequest.DoesNotExist:
|
||||
return error_response(u"请求不存在")
|
||||
|
||||
join_request.status = True
|
||||
join_request.save()
|
||||
if data["status"]:
|
||||
if join_group(join_request.user, join_request.group):
|
||||
join_request.accepted = True
|
||||
join_request.save()
|
||||
return success_response(u"加入成功")
|
||||
else:
|
||||
return error_response(u"加入失败,已经在本小组内")
|
||||
else:
|
||||
return success_response(u"已拒绝")
|
||||
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
@login_required
|
||||
def group_list_page(request, page=1):
|
||||
groups = Group.objects.filter(visible=True, join_group_setting__lte=2)
|
||||
# 搜索的情况
|
||||
keyword = request.GET.get("keyword", None)
|
||||
if keyword:
|
||||
groups = groups.filter(Q(name__contains=keyword) | Q(description__contains=keyword))
|
||||
|
||||
paginator = Paginator(groups, 20)
|
||||
try:
|
||||
current_page = paginator.page(int(page))
|
||||
except Exception:
|
||||
return error_page(request, u"不存在的页码")
|
||||
|
||||
previous_page = next_page = None
|
||||
|
||||
try:
|
||||
previous_page = current_page.previous_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
next_page = None
|
||||
try:
|
||||
next_page = current_page.next_page_number()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return render(request, "oj/group/group_list.html", {
|
||||
"groups": groups,
|
||||
"contests": current_page, "page": int(page),
|
||||
"previous_page": previous_page, "next_page": next_page,
|
||||
"keyword": keyword
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def group_page(request, group_id):
|
||||
try:
|
||||
group = Group.objects.get(id=group_id, visible=True)
|
||||
except Group.DoesNotExist:
|
||||
return error_page(request, u"小组不存在")
|
||||
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
|
||||
def application_list_page(request, group_id):
|
||||
try:
|
||||
group = Group.objects.get(id=group_id, visible=True)
|
||||
except Group.DoesNotExist:
|
||||
return error_page(request, u"小组不存在")
|
||||
applications = JoinGroupRequest.objects.filter(user=request.user, group=group)
|
||||
return render(request, "oj/group/my_application_list.html",
|
||||
{"group": group, "applications": applications})
|
||||
|
||||
|
||||
@login_required
|
||||
def application_page(request, request_id):
|
||||
try:
|
||||
application = JoinGroupRequest.objects.get(user=request.user, pk=request_id)
|
||||
except JoinGroupRequest.DoesNotExist:
|
||||
return error_page(request, u"申请不存在")
|
||||
return render(request, "oj/group/my_application.html",
|
||||
{"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)
|
176
judge/client.py
176
judge/client.py
@ -1,176 +0,0 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
import json
|
||||
import hashlib
|
||||
import judger
|
||||
import spj_client
|
||||
|
||||
from multiprocessing import Pool
|
||||
|
||||
from settings import max_running_number
|
||||
from language import languages
|
||||
from result import result
|
||||
from judge_exceptions import JudgeClientError
|
||||
from logger import logger
|
||||
|
||||
|
||||
# 下面这个函数作为代理访问实例变量,否则Python2会报错,是Python2的已知问题
|
||||
# http://stackoverflow.com/questions/1816958/cant-pickle-type-instancemethod-when-using-pythons-multiprocessing-pool-ma/7309686
|
||||
def _run(instance, test_case_id):
|
||||
return instance._judge_one(test_case_id)
|
||||
|
||||
|
||||
class JudgeClient(object):
|
||||
def __init__(self, language_code, exe_path, max_cpu_time, max_memory, test_case_dir, judge_base_path, spj_path):
|
||||
"""
|
||||
:param language_code: 语言编号
|
||||
:param exe_path: 可执行文件路径
|
||||
:param max_cpu_time: 最大cpu时间,单位ms
|
||||
:param max_memory: 最大内存,单位字节,直接传给judger.run方法
|
||||
:param test_case_dir: 测试用例文件夹路径
|
||||
:return:返回结果list
|
||||
"""
|
||||
self._language = languages[language_code]
|
||||
self._exe_path = exe_path
|
||||
self._max_cpu_time = max_cpu_time
|
||||
|
||||
# 如果是Java, 就不在judger中限制内存分配了, 而是转移到Java运行参数中,
|
||||
# 参见 https://github.com/QingdaoU/OnlineJudge/issues/23
|
||||
# 这里给出3倍的限制, 是为了防止出现OutOfMemory异常导致误判为Runtime Error,
|
||||
# 如果实际使用超过了3倍, 就只能得到Runtime Error的结果了
|
||||
# 而最后会比较Java实际使用的内存和1.5倍的设定内存的大小
|
||||
self._real_max_memory = max_memory
|
||||
if self._language["name"] == "java":
|
||||
self._max_memory = judger.MEMORY_UNLIMITED
|
||||
self.execute_command = self._language["execute_command"].\
|
||||
format(exe_path=self._exe_path, max_memory=max_memory * 3).split(" ")
|
||||
else:
|
||||
self._max_memory = self._real_max_memory
|
||||
self.execute_command = self._language["execute_command"].format(exe_path=self._exe_path).split(" ")
|
||||
|
||||
self._test_case_dir = test_case_dir
|
||||
# 进程池
|
||||
self._pool = Pool(processes=max_running_number)
|
||||
# 测试用例配置项
|
||||
self._test_case_info = self._load_test_case_info()
|
||||
self._judge_base_path = judge_base_path
|
||||
self._spj_path = spj_path
|
||||
|
||||
def _load_test_case_info(self):
|
||||
# 读取测试用例信息 转换为dict
|
||||
try:
|
||||
f = open(os.path.join(self._test_case_dir, "info"))
|
||||
return json.loads(f.read())
|
||||
except IOError:
|
||||
raise JudgeClientError("Test case config file not found")
|
||||
except ValueError:
|
||||
raise JudgeClientError("Test case config file format error")
|
||||
|
||||
def _compare_output(self, test_case_id):
|
||||
test_case_config = self._test_case_info["test_cases"][str(test_case_id)]
|
||||
output_path = os.path.join(self._judge_base_path, str(test_case_id) + ".out")
|
||||
|
||||
try:
|
||||
f = open(output_path, "rb")
|
||||
except IOError:
|
||||
# 文件不存在等引发的异常 返回结果错误
|
||||
return "", False
|
||||
|
||||
if "striped_output_md5" not in test_case_config:
|
||||
# 计算输出文件的md5 和之前测试用例文件的md5进行比较
|
||||
# 兼容之前没有striped_output_md5的测试用例
|
||||
# 现在比较的是完整的文件
|
||||
md5 = hashlib.md5()
|
||||
while True:
|
||||
data = f.read(2 ** 8)
|
||||
if not data:
|
||||
break
|
||||
md5.update(data)
|
||||
output_md5 = md5.hexdigest()
|
||||
|
||||
return output_md5, output_md5 == test_case_config["output_md5"]
|
||||
else:
|
||||
# 这时候需要去除用户输出最后的空格和换行 再去比较md5
|
||||
md5 = hashlib.md5()
|
||||
# 比较和返回去除空格后的md5比较结果
|
||||
md5.update(f.read().rstrip())
|
||||
output_md5 = md5.hexdigest()
|
||||
return output_md5, output_md5 == test_case_config["striped_output_md5"]
|
||||
|
||||
def _judge_one(self, test_case_id):
|
||||
in_file = os.path.join(self._test_case_dir, str(test_case_id) + ".in")
|
||||
out_file = os.path.join(self._judge_base_path, str(test_case_id) + ".out")
|
||||
run_result = judger.run(path=self.execute_command[0],
|
||||
max_cpu_time=self._max_cpu_time,
|
||||
max_memory=self._max_memory,
|
||||
in_file=in_file,
|
||||
out_file=out_file,
|
||||
args=self.execute_command[1:],
|
||||
env=["PATH=" + os.environ["PATH"]],
|
||||
use_sandbox=self._language["use_sandbox"],
|
||||
use_nobody=True)
|
||||
run_result["test_case"] = test_case_id
|
||||
|
||||
# 对Java的特殊处理, 详见__init__函数中注释
|
||||
if self._language["name"] == "java" and run_result["memory"] > self._real_max_memory * 1.5:
|
||||
run_result["flag"] = 3
|
||||
|
||||
# 将judger返回的结果标志转换为本系统中使用的
|
||||
if run_result["flag"] == 0:
|
||||
if self._spj_path is None:
|
||||
output_md5, r = self._compare_output(test_case_id)
|
||||
if r:
|
||||
run_result["result"] = result["accepted"]
|
||||
else:
|
||||
run_result["result"] = result["wrong_answer"]
|
||||
run_result["output_md5"] = output_md5
|
||||
else:
|
||||
spj_result = spj_client.spj(path=self._spj_path,
|
||||
max_cpu_time=3 * self._max_cpu_time,
|
||||
max_memory=3 * self._real_max_memory,
|
||||
in_path=in_file,
|
||||
user_out_path=out_file)
|
||||
if spj_result["spj_result"] == spj_client.AC:
|
||||
run_result["result"] = result["accepted"]
|
||||
elif spj_result["spj_result"] == spj_client.WA:
|
||||
run_result["result"] = result["wrong_answer"]
|
||||
else:
|
||||
run_result["result"] = result["system_error"]
|
||||
run_result["error"] = "SPJ Crashed, return: %d, signal: %d" % \
|
||||
(spj_result["spj_result"], spj_result["signal"])
|
||||
|
||||
elif run_result["flag"] in [1, 2]:
|
||||
run_result["result"] = result["time_limit_exceeded"]
|
||||
elif run_result["flag"] == 3:
|
||||
run_result["result"] = result["memory_limit_exceeded"]
|
||||
elif run_result["flag"] == 4:
|
||||
run_result["result"] = result["runtime_error"]
|
||||
elif run_result["flag"] == 5:
|
||||
run_result["result"] = result["system_error"]
|
||||
return run_result
|
||||
|
||||
def run(self):
|
||||
# 添加到任务队列
|
||||
_results = []
|
||||
results = []
|
||||
for i in range(self._test_case_info["test_case_number"]):
|
||||
_results.append(self._pool.apply_async(_run, (self, i + 1)))
|
||||
self._pool.close()
|
||||
self._pool.join()
|
||||
for item in _results:
|
||||
# 注意多进程中的异常只有在get()的时候才会被引发
|
||||
# http://stackoverflow.com/questions/22094852/how-to-catch-exceptions-in-workers-in-multiprocessing
|
||||
try:
|
||||
results.append(item.get())
|
||||
except Exception as e:
|
||||
logger.error("system error")
|
||||
logger.error(e)
|
||||
results.append({"result": result["system_error"]})
|
||||
return results
|
||||
|
||||
def __getstate__(self):
|
||||
# 不同的pool之间进行pickle的时候要排除自己,否则报错
|
||||
# http://stackoverflow.com/questions/25382455/python-notimplementederror-pool-objects-cannot-be-passed-between-processes
|
||||
self_dict = self.__dict__.copy()
|
||||
del self_dict['_pool']
|
||||
return self_dict
|
@ -1,40 +0,0 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
import judger
|
||||
from judge_exceptions import CompileError
|
||||
from logger import logger
|
||||
|
||||
|
||||
def compile_(language_item, src_path, exe_path, judge_base_path, compile_spj=False):
|
||||
command_item = "spj_compile_command" if compile_spj else "compile_command"
|
||||
compile_command = language_item[command_item].format(src_path=src_path, exe_path=exe_path).split(" ")
|
||||
compiler = compile_command[0]
|
||||
compile_args = compile_command[1:]
|
||||
compiler_output_file = os.path.join(judge_base_path, "compiler.out")
|
||||
|
||||
compile_result = judger.run(path=compiler,
|
||||
in_file="/dev/null",
|
||||
out_file=compiler_output_file,
|
||||
max_cpu_time=language_item["compile_max_cpu_time"],
|
||||
max_memory=language_item["compile_max_memory"],
|
||||
args=compile_args,
|
||||
env=["PATH=" + os.environ["PATH"]],
|
||||
use_sandbox=False,
|
||||
use_nobody=True)
|
||||
|
||||
compile_output_handler = open(compiler_output_file)
|
||||
compile_output = compile_output_handler.read().strip()
|
||||
compile_output_handler.close()
|
||||
|
||||
if compile_result["flag"] != 0:
|
||||
logger.error("Compiler error")
|
||||
logger.error(compile_output)
|
||||
logger.error(str(compile_result))
|
||||
if compile_output:
|
||||
raise CompileError(compile_output)
|
||||
else:
|
||||
raise CompileError("Compile error, info: " + str(compile_result))
|
||||
else:
|
||||
if "error" in compile_output:
|
||||
raise CompileError(compile_output)
|
||||
return exe_path
|
@ -1,9 +0,0 @@
|
||||
# coding=utf-8
|
||||
|
||||
|
||||
class JudgeClientError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CompileError(Exception):
|
||||
pass
|
@ -1,42 +0,0 @@
|
||||
# coding=utf-8
|
||||
|
||||
|
||||
languages = {
|
||||
1: {
|
||||
"name": "c",
|
||||
"src_name": "main.c",
|
||||
"code": 1,
|
||||
"compile_max_cpu_time": 3000,
|
||||
"compile_max_memory": 128 * 1024 * 1024,
|
||||
"compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}/main",
|
||||
"spj_compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -Werror -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}",
|
||||
"execute_command": "{exe_path}/main",
|
||||
"use_sandbox": True
|
||||
},
|
||||
2: {
|
||||
"name": "cpp",
|
||||
"src_name": "main.cpp",
|
||||
"code": 2,
|
||||
"compile_max_cpu_time": 3000,
|
||||
"compile_max_memory": 256 * 1024 * 1024,
|
||||
"compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}/main",
|
||||
"spj_compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -Werror -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}",
|
||||
"execute_command": "{exe_path}/main",
|
||||
"use_sandbox": True
|
||||
},
|
||||
3: {
|
||||
"name": "java",
|
||||
"src_name": "Main.java",
|
||||
"code": 3,
|
||||
"compile_max_cpu_time": 3000,
|
||||
"compile_max_memory": 1024 * 1024 * 1024,
|
||||
"compile_command": "/usr/bin/javac {src_path} -d {exe_path} -J-Xss1m -J-XX:MaxPermSize=16M "
|
||||
"-J-XX:PermSize=8M -J-Xms16m -J-Xmx1024m -encoding UTF8",
|
||||
"execute_command": "/usr/bin/java -cp {exe_path} -Xss1M -XX:MaxPermSize=16M "
|
||||
"-XX:PermSize=8M -Xms16M -Xmx{max_memory} -Djava.security.manager "
|
||||
"-Djava.security.policy==policy -Djava.awt.headless=true Main",
|
||||
"use_sandbox": False
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s',
|
||||
filename='log/judge.log')
|
||||
|
||||
logger = logging
|
@ -1,15 +0,0 @@
|
||||
# coding=utf-8
|
||||
|
||||
|
||||
# 这个映射关系是前后端通用的,判题服务器提供接口,也应该遵守这个,可能需要一些转换
|
||||
result = {
|
||||
"accepted": 0,
|
||||
"runtime_error": 1,
|
||||
"time_limit_exceeded": 2,
|
||||
"memory_limit_exceeded": 3,
|
||||
"compile_error": 4,
|
||||
"format_error": 5,
|
||||
"wrong_answer": 6,
|
||||
"system_error": 7,
|
||||
"waiting": 8
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
import socket
|
||||
import shutil
|
||||
|
||||
from logger import logger
|
||||
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,
|
||||
spj, spj_language, spj_code, spj_version):
|
||||
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"):
|
||||
if token:
|
||||
logger.info("Invalid token: " + 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_item=language, src_path=src_path,
|
||||
exe_path=judge_base_path, judge_base_path=judge_base_path, compile_spj=False)
|
||||
except Exception as e:
|
||||
shutil.rmtree(judge_base_path, ignore_errors=True)
|
||||
return {"code": 1, "data": {"error": str(e), "server": host_name}}
|
||||
|
||||
test_case_dir = os.path.join(judger_workspace, "test_case", test_case_id)
|
||||
|
||||
# SPJ相关
|
||||
if spj:
|
||||
spj_path = os.path.join(test_case_dir, "spj-" + spj_version)
|
||||
if "spj-" + spj_version not in os.listdir(test_case_dir):
|
||||
spj_language_item = languages[spj_language]
|
||||
spj_code_path = os.path.join(test_case_dir, "spj-" + spj_language_item["src_name"])
|
||||
|
||||
f = open(spj_code_path, "w")
|
||||
f.write(spj_code.encode("utf8"))
|
||||
f.close()
|
||||
|
||||
try:
|
||||
compile_(language_item=languages[spj_language], src_path=spj_code_path,
|
||||
exe_path=spj_path,
|
||||
judge_base_path=judge_base_path, compile_spj=True)
|
||||
except Exception as e:
|
||||
return {"code": 2, "data": {"error": "SPJ Compile error: " + str(e), "server": host_name}}
|
||||
else:
|
||||
spj_path = None
|
||||
|
||||
# 运行
|
||||
try:
|
||||
client = JudgeClient(language_code=language_code,
|
||||
exe_path=exe_path,
|
||||
max_cpu_time=int(time_limit),
|
||||
max_memory=int(memory_limit) * 1024 * 1024,
|
||||
test_case_dir=test_case_dir,
|
||||
judge_base_path=judge_base_path, spj_path=spj_path)
|
||||
judge_result = {"result": result["accepted"], "info": client.run(),
|
||||
"accepted_answer_time": None, "server": host_name}
|
||||
|
||||
for item in judge_result["info"]:
|
||||
if item["result"] != 0:
|
||||
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)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user