Merge branch 'dev-sxw' of git.coding.net:virusdefender/qduoj into dev-sxw

i
This commit is contained in:
sxw@401 2015-09-05 18:55:10 +08:00
commit e871e51f0b
60 changed files with 1344 additions and 602 deletions

View File

@ -25,7 +25,6 @@ class UserRegisterSerializer(serializers.Serializer):
class UserChangePasswordSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
old_password = serializers.CharField()
new_password = serializers.CharField(max_length=30, min_length=6)

View File

@ -123,22 +123,13 @@ class UserChangePasswordAPITest(APITestCase):
user = User.objects.create(username="test")
user.set_password("aaabbb")
user.save()
self.client.login(username="test",password="aaabbb")
def test_error_old_password(self):
data = {"username": "test", "old_password": "aaaccc", "new_password": "aaaddd"}
data = {"old_password": "aaaccc", "new_password": "aaaddd"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"密码不正确,请重新修改!"})
def test_invalid_data_format(self):
data = {"old_password": "aaa", "new_password": "aaaddd"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_username_does_not_exist(self):
data = {"username": "test1", "old_password": "aaabbb", "new_password": "aaaddd"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_success_change_password(self):
data = {"username": "test", "old_password": "aaabbb", "new_password": "aaaccc"}
response = self.client.post(self.url, data=data)
@ -369,3 +360,18 @@ class AdminRequiredDecoratorTest(TestCase):
self.client.login(username="test", password="test")
response = self.client.get("/admin_required_test/cbv/1024/")
self.assertEqual(response.content, "1024")
class UserLogoutTest(TestCase):
def setUp(self):
self.client = Client()
user = User.objects.create(username="test")
user.admin_type = 1
user.set_password("1")
user.save()
def logout_success(self):
self.client = Client()
self.client.login(username="test", password="1")
response = self.client.get("/logout/")
self.assertEqual(response.status_code, 302)

View File

@ -1,4 +1,5 @@
# coding=utf-8
from django import http
from django.contrib import auth
from django.shortcuts import render
from django.db.models import Q
@ -34,6 +35,10 @@ class UserLoginAPIView(APIView):
else:
return serializer_invalid_response(serializer)
@login_required
def logout(request):
auth.logout(request)
return http.HttpResponseRedirect("/")
class UserRegisterAPIView(APIView):
def post(self, request):
@ -64,6 +69,7 @@ class UserRegisterAPIView(APIView):
class UserChangePasswordAPIView(APIView):
@login_required
def post(self, request):
"""
用户修改密码json api接口
@ -73,7 +79,8 @@ class UserChangePasswordAPIView(APIView):
serializer = UserChangePasswordSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
user = auth.authenticate(username=data["username"], password=data["old_password"])
username = request.user.username
user = auth.authenticate(username=username, password=data["old_password"])
if user:
user.set_password(data["new_password"])
user.save()

View File

@ -1,7 +1,8 @@
# 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
@ -21,6 +22,11 @@ class CreateContestSerializer(serializers.Serializer):
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:
@ -28,6 +34,8 @@ class ContestSerializer(serializers.ModelSerializer):
fields = ["username"]
created_by = UserSerializer()
start_time = DateTimeLocal()
end_time = DateTimeLocal()
class Meta:
model = Contest

View File

@ -138,7 +138,7 @@ class ContestAdminAPITest(APITestCase):
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["title"], "titlez")
self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z")
#self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z")
def test_edit_group_contest_successfully(self):
self.client.login(username="test1", password="testaa")
@ -149,7 +149,7 @@ class ContestAdminAPITest(APITestCase):
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["title"], "titleyyy")
self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z")
#self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z")
self.assertEqual(response.data["data"]["visible"], False)
def test_edit_group_contest_unsuccessfully(self):

View File

@ -343,12 +343,12 @@ def contest_list_page(request, page=1):
# 搜索的情况
keyword = request.GET.get("keyword", None)
if keyword:
contests = contests.filter(title__contains=keyword)
contests = contests.filter(Q(title__contains=keyword) | Q(description__contains=keyword))
# 筛选我能参加的比赛
join = request.GET.get("join", None)
if join:
contests = Contest.objects.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())).\
contests = contests.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())).\
filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now())
paginator = Paginator(contests, 20)

View File

@ -1,3 +1,84 @@
from django.test import TestCase
import json
from django.core.urlresolvers import reverse
from account.models import User, ADMIN, SUPER_ADMIN
from contest.models import Contest, ContestProblem
from submission.models import Submission
from rest_framework.test import APITestCase, APIClient
# Create your tests here.
class SubmissionAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('contest_submission_admin_api_view')
self.userA = User.objects.create(username="test1", admin_type=ADMIN)
self.userA.set_password("testaa")
self.userA.save()
self.userS = User.objects.create(username="test2", admin_type=SUPER_ADMIN)
self.userS.set_password("testbb")
self.userS.save()
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=2, show_rank=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z",
password="aacc", created_by=self.userS
)
self.problem = ContestProblem.objects.create(title="title1",
description="description1",
input_description="input1_description",
output_description="output1_description",
test_case_id="1",
sort_index="1",
samples=json.dumps([{"input": "1 1", "output": "2"}]),
time_limit=100,
memory_limit=1000,
hint="hint1",
contest=self.global_contest,
created_by=self.userS)
self.submission = Submission.objects.create(user_id=self.userA.id,
language=1,
code='#include "stdio.h"\nint main(){\n\treturn 0;\n}',
problem_id=self.problem.id)
self.submissionS = Submission.objects.create(user_id=self.userS.id,
language=2,
code='#include "stdio.h"\nint main(){\n\treturn 0;\n}',
problem_id=self.problem.id)
def test_submission_contest_does_not_exist(self):
self.client.login(username="test2", password="testbb")
response = self.client.get(self.url + "?contest_id=99")
self.assertEqual(response.data["code"], 1)
def test_submission_contest_parameter_error(self):
self.client.login(username="test2", password="testbb")
response = self.client.get(self.url)
self.assertEqual(response.data["code"], 1)
def test_submission_access_denied(self):
self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?problem_id=" + str(self.problem.id))
self.assertEqual(response.data["code"], 1)
def test_submission_access_denied_with_contest_id(self):
self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?contest_id=" + str(self.global_contest.id))
self.assertEqual(response.data["code"], 1)
def test_get_submission_successfully(self):
self.client.login(username="test2", password="testbb")
response = self.client.get(
self.url + "?contest_id=" + str(self.global_contest.id) + "&problem_id=" + str(self.problem.id))
self.assertEqual(response.data["code"], 0)
def test_get_submission_successfully_problem(self):
self.client.login(username="test2", password="testbb")
response = self.client.get(self.url + "?problem_id=" + str(self.problem.id))
self.assertEqual(response.data["code"], 0)
def test_get_submission_problem_do_not_exist(self):
self.client.login(username="test2", password="testbb")
response = self.client.get(self.url + "?problem_id=9999")
self.assertEqual(response.data["code"], 1)

View File

@ -21,6 +21,7 @@ from utils.shortcuts import serializer_invalid_response, error_response, success
from submission.models import Submission
from .serializers import CreateContestSubmissionSerializer
from submission.serializers import SubmissionSerializer
class ContestSubmissionAPIView(APIView):
@ -76,7 +77,8 @@ def contest_problem_my_submissions_list_page(request, contest_id, contest_proble
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except Problem.DoesNotExist:
return error_page(request, u"比赛问题不存在")
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by("-create_time"). \
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by(
"-create_time"). \
values("id", "result", "create_time", "accepted_answer_time", "language")
return render(request, "oj/contest/my_submissions_list.html",
{"submissions": submissions, "problem": contest_problem})
@ -112,4 +114,38 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
return render(request, "oj/contest/submissions_list.html",
{"submissions": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
"contest": contest})
"contest": contest})
class ContestSubmissionAdminAPIView(APIView):
def get(self, request):
"""
查询比赛提交,单个比赛题目提交的adminAPI
---
response_serializer: SubmissionSerializer
"""
problem_id = request.GET.get("problem_id", None)
contest_id = request.GET.get("contest_id", None)
if contest_id:
try:
contest = Contest.objects.get(pk=contest_id)
except Contest.DoesNotExist:
return error_response(u"比赛不存在!")
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
return error_response(u"您无权查看该信息!")
submissions = Submission.objects.filter(contest_id=contest_id).order_by("-create_time")
else:
if problem_id:
try:
contest_problem = ContestProblem.objects.get(pk=problem_id)
except ContestProblem.DoesNotExist:
return error_response(u"问题不存在!")
if request.user.admin_type != SUPER_ADMIN and contest_problem.contest.created_by != request.user:
return error_response(u"您无权查看该信息!")
submissions = Submission.objects.filter(contest_id=contest_problem.contest_id).order_by("-create_time")
else:
return error_response(u"参数错误!")
if problem_id:
submissions = submissions.filter(problem_id=problem_id)
return paginate(request, submissions, SubmissionSerializer)

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('group', '0004_merge'),
]
operations = [
migrations.AddField(
model_name='joingrouprequest',
name='accepted',
field=models.BooleanField(default=False),
),
]

View File

@ -36,6 +36,6 @@ class JoinGroupRequest(models.Model):
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"

View File

@ -21,7 +21,7 @@ class EditGroupSerializer(serializers.Serializer):
class CreateJoinGroupRequestSerializer(serializers.Serializer):
group_id = serializers.IntegerField()
message = serializers.CharField(max_length=30)
message = serializers.CharField(max_length=30, required=False)
class JoinGroupRequestSerializer(serializers.ModelSerializer):

View File

@ -8,6 +8,8 @@ from rest_framework.test import APITestCase, APIClient
from account.models import User, REGULAR_USER, ADMIN, SUPER_ADMIN
from group.models import Group, UserGroupRelation, JoinGroupRequest
from django.test import TestCase, Client
class GroupAPITest(APITestCase):
pass
@ -148,7 +150,7 @@ class JoinGroupAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('group_join_admin_api')
self.url = reverse('group_join_api')
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
@ -242,7 +244,7 @@ class JoinGroupRequestAdminAPITest(APITestCase):
self.assertEqual(JoinGroupRequest.objects.get(id=self.request.id).status, True)
def test_join_group_successfully(self):
data = {"request_id": self.request.id, "status": True}
data = {"request_id": self.request.id, "status": True, "": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 0, "data": u"加入成功"})
UserGroupRelation.objects.get(group=self.group, user=self.user1)
@ -254,3 +256,51 @@ class JoinGroupRequestAdminAPITest(APITestCase):
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"加入失败,已经在本小组内"})
class GroupListPageTest(TestCase):
def setUp(self):
self.client = Client()
self.url = reverse('group_list_page')
self.url_with_argument = reverse('group_page', kwargs={"page": 1})
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.group = Group.objects.create(name="group1",
description="description1",
# 0是公开 1是需要申请后加入 2是不允许任何人加入
join_group_setting=1,
admin=User.objects.get(username="test"))
def get_group_list_page_successful(self):
self.client.login(username="test", password="testaa")
response = self.client.get(self.url)
self.assertEqual(response.status_coed, 200)
def get_group_list_page_successful_with_keyword(self):
self.client.login(username="test", password="testaa")
response = self.client.get(self.url + "?keyword=gro")
self.assertEqual(response.status_coed, 200)
def get_group_list_page_successful_with_page_argument(self):
self.client.login(username="test", password="testaa")
response = self.client.get(self.url_with_argument + "?keyword=gro")
self.assertEqual(response.status_coed, 200)
class GroupPageTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.group = Group.objects.create(name="group1",
description="description1",
# 0是公开 1是需要申请后加入 2是不允许任何人加入
join_group_setting=1,
admin=User.objects.get(username="test"))
self.url = reverse('group_page', kwargs={"group_id": self.group.id})
def get_group_list_page_successful(self):
self.client.login(username="test", password="testaa")
response = self.client.get(self.url)
self.assertEqual(response.status_coed, 200)

View File

@ -4,7 +4,7 @@ from django.db import IntegrityError
from rest_framework.views import APIView
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate, error_page
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
from account.decorators import login_required
@ -13,6 +13,9 @@ from .serializers import (CreateGroupSerializer, EditGroupSerializer,
CreateJoinGroupRequestSerializer, GroupSerializer,
GroupMemberSerializer, EditGroupMemberSerializer,
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer)
from announcement.models import Announcement
from django.core.paginator import Paginator
from django.db.models import Q
class GroupAPIViewBase(object):
@ -26,7 +29,7 @@ class GroupAPIViewBase(object):
else:
group = Group.objects.get(id=group_id, visible=True, admin=request.user)
return group
def get_groups(self, request):
"""
如果是超级管理员就返回全部的小组
@ -113,8 +116,8 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase):
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):
"""
@ -129,9 +132,9 @@ class GroupMemberAdminAPIView(APIView, GroupAPIViewBase):
group = self.get_group(request, group_id)
except Group.DoesNotExist:
return error_response(u"小组不存在")
return paginate(request, UserGroupRelation.objects.filter(group=group), GroupMemberSerializer)
def put(self, request):
"""
删除小组成员的api接口
@ -180,6 +183,8 @@ class JoinGroupAPIView(APIView):
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"你已经提交过申请了,请等待审核")
@ -190,7 +195,7 @@ class JoinGroupAPIView(APIView):
return error_response(u"该小组不允许任何人加入")
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
搜索小组的api需要传递keyword参数
@ -233,9 +238,10 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
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"加入失败,已经在本小组内")
@ -244,3 +250,70 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
else:
return serializer_invalid_response(serializer)
@login_required
def group_list_page(request, page=1):
# 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
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, "announcements": announcements,
"contests": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "announcements": announcements,
})
@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"小组不存在")
return render(request, "oj/group/group.html", {"group": group})
@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})

View File

@ -5,7 +5,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 下面是需要自己修改的
LOG_PATH = "LOG/"
LOG_PATH = "log/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = {
@ -18,7 +18,7 @@ DATABASES = {
'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql',
'HOST': "121.42.32.129",
'POST': 3306,
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'mypwd'
}
@ -29,5 +29,5 @@ DEBUG = True
# 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/')
DATABASE_ROUTERS = ['oj.db_router.DBRouter']
ALLOWED_HOSTS = []

View File

@ -1 +1,37 @@
# coding=utf-8
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 下面是需要自己修改的
LOG_PATH = "/var/log/oj/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "oj",
'CONN_MAX_AGE': 0.3,
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'mypwd'
},
'submission': {
'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql',
'HOST': "127.0.0.1",
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'mypwd'
}
}
DEBUG = True
# 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = '/root/test_case/'
ALLOWED_HOSTS = ['*']

View File

@ -14,15 +14,13 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
# todo 判断运行环境
# 判断运行环境
ENV = os.environ.get("oj_env", "local")
if ENV == "local":
from .local_settings import *
elif ENV == "server":
from .server_settings import *
elif ENV == "daocloud":
from .daocloud_settings import *
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -33,7 +31,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'hzfp^8mbgapc&x%$#xv)0=t8s7_ilingw(q3!@h&2fty6v6fxz'
ALLOWED_HOSTS = []
# Application definition
@ -170,4 +168,6 @@ LOGGING = {
REST_FRAMEWORK = {
'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}
}
DATABASE_ROUTERS = ['oj.db_router.DBRouter']

View File

@ -1,11 +1,11 @@
# coding=utf-8
from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
UserChangePasswordAPIView, EmailCheckAPIView,
UserAdminAPIView, UserInfoAPIView)
from announcement.views import AnnouncementAdminAPIView
from contest.views import ContestAdminAPIView, ContestProblemAdminAPIView, ContestPasswordVerifyAPIView
@ -17,7 +17,7 @@ from admin.views import AdminTemplateView
from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView
from submission.views import SubmissionAPIView, SubmissionAdminAPIView
from contest_submission.views import ContestSubmissionAPIView
from contest_submission.views import ContestSubmissionAPIView, ContestSubmissionAdminAPIView
from monitor.views import QueueLengthMonitorAPIView
from contest_submission.views import contest_problem_my_submissions_list_page
@ -34,6 +34,7 @@ urlpatterns = [
name="admin_template"),
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
url(r'^logout/$', "account.views.logout", name="user_logout_api"),
url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"),
name="user_register_page"),
url(r'^change_password/$', TemplateView.as_view(template_name="oj/account/change_password.html"),
@ -50,13 +51,14 @@ urlpatterns = [
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_api"),
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
url(r'^api/group_join/$', JoinGroupAPIView.as_view(), name="group_join_api"),
url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"),
url(r'^api/admin/group_join/$', JoinGroupAPIView.as_view(), name="group_join_admin_api"),
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"),
@ -65,7 +67,7 @@ urlpatterns = [
name="join_group_request_admin_api"),
url(r'^api/admin/submission/$', SubmissionAdminAPIView.as_view(), name="submission_admin_api_view"),
url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"),
url(r'^api/admin/contest_submission/$', ContestSubmissionAdminAPIView.as_view(), name="contest_submission_admin_api_view"),
url(r'^contest/(?P<contest_id>\d+)/problem/(?P<contest_problem_id>\d+)/$', "contest.views.contest_problem_page",
@ -87,7 +89,6 @@ urlpatterns = [
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"),
url(r'^problems/(?P<page>\d+)/$', "problem.views.problem_list_page", name="problem_list_page"),
@ -99,6 +100,11 @@ urlpatterns = [
url(r'^submissions/$', "submission.views.my_submission_list_page", name="my_submission_list_page"),
url(r'^submissions/(?P<page>\d+)/$', "submission.views.my_submission_list_page", name="my_submission_list_page"),
url(r'^contest/(?P<contest_id>\d+)/rank/$', "contest.views.contest_rank_page", name="contest_rank_page")
url(r'^contest/(?P<contest_id>\d+)/rank/$', "contest.views.contest_rank_page", name="contest_rank_page"),
url(r'^groups/$', "group.views.group_list_page", name="group_list_page"),
url(r'^groups/(?P<page>\d+)/$', "group.views.group_list_page", name="group_list_page"),
url(r'^group/(?P<group_id>\d+)/$', "group.views.group_page", name="group_page"),
url(r'^group/(?P<group_id>\d+)/applications/$', "group.views.application_list_page", name="group_application_page"),
url(r'^group/application/(?P<request_id>\d+)/$', "group.views.application_page", name="group_application")
]

View File

@ -226,7 +226,7 @@ def problem_list_page(request, page=1):
# 搜索的情况
keyword = request.GET.get("keyword", None)
if keyword:
problems = problems.filter(title__contains=keyword)
problems = problems.filter(Q(title__contains=keyword) | Q(description__contains=keyword))
# 按照标签筛选
tag_text = request.GET.get("tag", None)

View File

@ -55,3 +55,12 @@
list-style-type: none;
margin: 5px;
}
.error-info {
color: #dd4b39;
font-family:
Arial,Helvetica,sans-serif;
font-size: 13px;
line-height: 1.4;
font-weight: 600;
}

View File

@ -72,6 +72,7 @@ define("admin", ["jquery", "avalon"], function ($, avalon) {
var vm = avalon.define({
$id: "admin",
template_url: "template/" + hash + ".html",
username: "",
groupId: -1,
problemId: -1,
adminNavList: [],
@ -93,6 +94,7 @@ define("admin", ["jquery", "avalon"], function ($, avalon) {
dataType: "json",
success: function(data){
if(!data.code){
vm.username = data.data.username;
if (data.data.admin_type == 2){
vm.adminNavList = superAdminNav;
}
@ -133,11 +135,17 @@ define("admin", ["jquery", "avalon"], function ($, avalon) {
vm.template_url = "template/contest/edit_problem.html";
});
vm.$watch("showContestListPage", function (problemId) {
vm.problemId = problemId;
vm.$watch("showContestListPage", function () {
vm.template_url = "template/contest/contest_list.html";
});
vm.$watch("showContestSubmissionPage", function (problemId, contestId, contestMode) {
vm.$problemId = problemId;
vm.$contestId = contestId;
vm.$contestMode = contestMode
vm.template_url = "template/contest/submission_list.html";
});
avalon.scan();
window.onhashchange = function () {

View File

@ -1,163 +1,163 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
function ($, avalon, csrfTokenHeader, bsAlert, editor) {
avalon.ready(function () {
avalon.vmodels.announcement = null;
var createAnnouncementEditor = editor("#create-announcement-editor");
var editAnnouncementEditor = editor("#edit-announcement-editor");
if (avalon.vmodels.announcement){
var vm = avalon.vmodels.announcement;
announcementList = [];
}
else {
var vm = avalon.define({
$id: "announcement",
//通用变量
announcementList: [], // 公告列表数据项
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
editingAnnouncementId: 0, // 正在编辑的公告的ID 为零说明未在编辑
totalPage: 1, // 总页数
showVisibleOnly: false, //仅显示可见公告
// 编辑
newTitle: "",
announcementVisible: 0,
showGlobalViewRadio: true,
isGlobal: true,
allGroups: [],
getState: function (el) { //获取公告当前状态,显示
if (el.visible)
return "可见";
else
return "隐藏";
},
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btnType) {
if (btnType == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
editAnnouncement: function (announcement) {
vm.newTitle = announcement.title;
editAnnouncementEditor.setValue(announcement.content);
vm.announcementVisible = announcement.visible;
if (vm.editingAnnouncementId == announcement.id)
var vm = avalon.define({
$id: "announcement",
//通用变量
announcementList: [], // 公告列表数据项
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
editingAnnouncementId: 0, // 正在编辑的公告的ID 为零说明未在编辑
totalPage: 1, // 总页数
showVisibleOnly: false, //仅显示可见公告
// 编辑
newTitle: "",
announcementVisible: 0,
showGlobalViewRadio: true,
isGlobal: true,
allGroups: [],
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btnType) {
if (btnType == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
editAnnouncement: function (announcement) {
vm.newTitle = announcement.title;
editAnnouncementEditor.setValue(announcement.content);
vm.announcementVisible = announcement.visible;
if (vm.editingAnnouncementId == announcement.id)
vm.editingAnnouncementId = 0;
else
vm.editingAnnouncementId = announcement.id;
vm.isGlobal = announcement.is_global;
for (var i = 0; i < announcement.groups.length; i++) {
for (var j = 0; j < vm.allGroups.length; j++) {
if (announcement.groups[i] == vm.allGroups[j].id) {
vm.allGroups[j].isSelected = true;
}
}
}
editAnnouncementEditor.focus();
},
cancelEdit: function () {
vm.editingAnnouncementId = 0;
else
vm.editingAnnouncementId = announcement.id;
vm.isGlobal = announcement.is_global;
for (var i = 0; i < announcement.groups.length; i++) {
for (var j = 0; j < vm.allGroups.length; j++) {
if (announcement.groups[i] == vm.allGroups[j].id) {
vm.allGroups[j].isSelected = true;
},
submitChange: function () {
var title = vm.newTitle;
var content = editAnnouncementEditor.getValue();
if (content == "" || title == "") {
bsAlert("标题和内容都不能为空");
return false;
}
var selectedGroups = [];
if (!vm.isGlobal) {
for (var i = 0; i < vm.allGroups.length; i++) {
if (vm.allGroups[i].isSelected) {
selectedGroups.push(vm.allGroups[i].id);
}
}
}
}
editAnnouncementEditor.focus();
},
cancelEdit: function () {
vm.editingAnnouncementId = 0;
},
submitChange: function () {
var title = vm.newTitle;
var content = editAnnouncementEditor.getValue();
if (content == "" || title == "") {
bsAlert("标题和内容都不能为空");
return false;
}
var selectedGroups = [];
if (!vm.isGlobal) {
for (var i = 0; i < vm.allGroups.length; i++) {
if (vm.allGroups[i].isSelected) {
selectedGroups.push(vm.allGroups[i].id);
}
if (!vm.isGlobal && !selectedGroups.length) {
bsAlert("请至少选择一个小组");
return false;
}
}
if (!vm.isGlobal && !selectedGroups.length) {
bsAlert("请至少选择一个小组");
return false;
}
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/announcement/",
contentType: "application/json",
dataType: "json",
method: "put",
data: JSON.stringify({
id: vm.editingAnnouncementId,
title: title,
content: content,
visible: vm.announcementVisible,
is_global: vm.isGlobal,
groups: selectedGroups
}),
success: function (data) {
if (!data.code) {
bsAlert("修改成功");
vm.editingAnnouncementId = 0;
getPageData(1);
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/announcement/",
contentType: "application/json",
dataType: "json",
method: "put",
data: JSON.stringify({
id: vm.editingAnnouncementId,
title: title,
content: content,
visible: vm.announcementVisible,
is_global: vm.isGlobal,
groups: selectedGroups
}),
success: function (data) {
if (!data.code) {
bsAlert("修改成功");
vm.editingAnnouncementId = 0;
getPageData(1);
}
else {
bsAlert(data.data);
}
}
else {
bsAlert(data.data);
}
}
});
});
}
});
vm.$watch("showVisibleOnly", function () {
getPageData(1);
});
}
});
vm.$watch("showVisibleOnly", function () {
getPageData(1);
});
}
getPageData(1);
$.ajax({
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
bsAlert("您的用户权限只能创建组内公告,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["isSelected"] = false;
vm.allGroups.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
$.ajax({
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
var admin_type = data.data.admin_type;
if (data.data.admin_type == 1) {
vm.isGlobal = false;
vm.showGlobalViewRadio = false;
}
}
$.ajax({
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
if (admin_type != 2)
bsAlert("您的用户权限只能创建组内公告,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["isSelected"] = false;
vm.allGroups.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
}
});

View File

@ -43,7 +43,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
contentType: "application/json",
data: JSON.stringify(ajaxData),
method: "post",
contentType: "application/json",
success: function (data) {
if (!data.code) {
bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)");
@ -131,7 +130,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
success: function (data) {
if (!data.code) {
if (!data.data.length) {
bsAlert("您的用户权限只能创建组内比赛,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {

View File

@ -234,15 +234,17 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
vm.groupList[vm.choseGroupList[groupIndex].index].chose = false;
vm.choseGroupList.remove(vm.choseGroupList[groupIndex]);
},
add_problem: function () {
addProblem: function () {
vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode);
},
showProblemEditor: function(el) {
showProblemEditPage: function(el) {
vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode);
},
getYesOrNo: function(yORn) {
if (yORn) return "是";
return "否";
showSubmissionPage: function(el) {
var problemId = 0
if (el)
problemId = el.id;
vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode);
}
});
vm.$watch("showVisibleOnly", function() {
@ -289,6 +291,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
}
});
}
// Get group list
$.ajax({ // Get current user type
url: "/api/user/",

View File

@ -0,0 +1,88 @@
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
avalon.ready(function () {
if (avalon.vmodels.contestSubmissionList){
var vm = avalon.vmodels.contestSubmissionList;
}
else {
var vm = avalon.define({
$id: "contestSubmissionList",
submissionList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
results : {
0: "Accepted",
1: "Runtime Error",
2: "Time Limit Exceeded",
3: "Memory Limit Exceeded",
4: "Compile Error",
5: "Format Error",
6: "Wrong Answer",
7: "System Error",
8: "Waiting"
},
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
if (!page_index)
var page_index = vm.page;
getPageData(page_index);
},
showSubmissionDetailPage: function (submissionId) {
},
goBack: function(check){
vm.$fire("up!showContestListPage");
}
});
}
getPageData(1);
function getPageData(page) {
var url = "/api/admin/contest_submission/?paging=true&page=" + page + "&page_size=10&contest_id=" + avalon.vmodels.admin.$contestId;
if (avalon.vmodels.admin.$problemId)
url += "&problem_id=" + avalon.vmodels.admin.$problemId
$.ajax({
url: url,
dataType: "json",
method: "get",
success: function (data) {
if (!data.code) {
vm.submissionList = data.data.results;
vm.totalPage = data.data.total_page;
vm.previousPage = data.data.previous_page;
vm.nextPage = data.data.next_page;
vm.page = page;
}
else {
bsAlert(data.data);
}
}
});
}
});
avalon.scan();
});

View File

@ -1,51 +1,57 @@
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
avalon.ready(function () {
avalon.vmodels.group = null;
var vm = avalon.define({
$id: "group",
//通用变量
groupList: [], // 用户列表数据项
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
totalPage: 1, // 总页数
keyword: "",
//avalon.vmodels.group = null;
if (avalon.vmodels.group) {
var vm = avalon.vmodels.group;
}
else {
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btnType) {
if (btnType == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
var vm = avalon.define({
$id: "group",
//通用变量
groupList: [], // 用户列表数据项
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
totalPage: 1, // 总页数
keyword: "",
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btnType) {
if (btnType == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
search: function(){
getPageData(1);
},
getGroupSettingString: function (setting) {
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
},
showGroupDetailPage: function (groupId) {
vm.$fire("up!showGroupDetailPage", groupId);
}
});
}
},
search: function(){
getPageData(1);
},
getGroupSettingString: function (setting) {
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
},
showGroupDetailPage: function (groupId) {
vm.$fire("up!showGroupDetailPage", groupId);
}
});
getPageData(1);
function getPageData(page) {
var url = "/api/admin/group/?paging=true&page=" + page + "&page_size=2";
var url = "/api/admin/group/?paging=true&page=" + page + "&page_size=10";
if (vm.keyword)
url += "&keyword=" + vm.keyword;
$.ajax({

View File

@ -3,55 +3,60 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
// avalon:定义模式 group_list
avalon.ready(function () {
avalon.vmodels.groupDetail = null;
var vm = avalon.define({
$id: "groupDetail",
//通用变量
memberList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
name: "",
description: "",
checkedSetting: "0",
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
if (avalon.vmodels.groupDetail) {
var vm = avalon.vmodels.groupDetail;
}
else {
var vm = avalon.define({
$id: "groupDetail",
//通用变量
memberList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
name: "",
description: "",
checkedSetting: "0",
removeMember: function (relation) {
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/group_member/",
method: "put",
data: JSON.stringify({group_id: relation.group, members: [relation.user.id]}),
contentType: "application/json",
success: function (data) {
vm.memberList.remove(relation);
bsAlert(data.data);
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
})
},
showGroupListPage: function () {
vm.$fire("up!showGroupListPage");
}
});
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
removeMember: function (relation) {
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/group_member/",
method: "put",
data: JSON.stringify({group_id: relation.group, members: [relation.user.id]}),
contentType: "application/json",
success: function (data) {
vm.memberList.remove(relation);
bsAlert(data.data);
}
})
},
showGroupListPage: function () {
vm.$fire("up!showGroupListPage");
}
});
}
avalon.scan();
getPageData(1);

View File

@ -1,51 +1,57 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "formValidation"], function ($, avalon, csrfTokenHeader, bsAlert) {
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
// avalon:定义模式 group_list
avalon.ready(function () {
avalon.vmodels.requestList = null;
var vm = avalon.define({
$id: "requestList",
//通用变量
requestList: [], // 列表数据项
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
totalPage: 1, // 总页数
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
processRequest: function(request, status){
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/join_group_request/",
method: "put",
data: {request_id: request.id, status: status},
success: function(data){
vm.requestList.remove(request);
bsAlert(data.data);
if (avalon.vmodels.requestList) {
var vm = avalon.vmodels.requestList;
}
else {
var vm = avalon.define({
$id: "requestList",
//通用变量
requestList: [], // 列表数据项
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
totalPage: 1, // 总页数
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
})
}
});
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
processRequest: function(request, status){
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/join_group_request/",
method: "put",
data: {request_id: request.id, status: status},
success: function(data){
vm.requestList.remove(request);
bsAlert(data.data);
}
})
}
});
}
avalon.scan();
getPageData(1);
@ -72,78 +78,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "formValidation"], function
});
}
/*$("#edit_user-form")
.formValidation({
framework: "bootstrap",
fields: {
username: {
validators: {
notEmpty: {
message: "请填写用户名"
},
stringLength: {
min: 3,
max: 30,
message: '用户名长度必须在3到30位之间'
}
}
},
real_name: {
validators: {
notEmpty: {
message: "请填写真实姓名"
}
}
},
email: {
validators: {
notEmpty: {
message: "请填写电子邮箱邮箱地址"
},
emailAddress: {
message: "请填写有效的邮箱地址"
}
}
},
password: {
validators: {
stringLength: {
min: 6,
max: 30,
message: '密码长度必须在6到30位之间'
}
}
}
}
}
).on('success.form.fv', function (e) {
e.preventDefault();
var data = {
username: vm.username,
real_name: vm.real_name,
email: vm.email,
id: vm.id,
admin_type: vm.admin_type
};
if ($("#password").val() !== "")
data.password = $("#password").val();
$.ajax({
beforeSend: csrfHeader,
url: "/api/admin/user/",
data: data,
dataType: "json",
method: "put",
success: function (data) {
if (!data.code) {
bsAlert("提交成功!");
getPageData(1);
$("#password").val("");
} else {
bsAlert(data.data);
}
}
})
});*/
});
});

View File

@ -3,6 +3,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
avalon.ready(function () {
if(avalon.vmodels.problemList){
vm = avalon.vmodels.problemList;
problemList = [];
}
else {
var vm = avalon.define({
@ -13,6 +14,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
page: 1,
totalPage: 1,
keyword: "",
showVisibleOnly: false,
getNext: function () {
if (!vm.nextPage)
return;
@ -41,12 +43,17 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
vm.$fire("up!showProblemSubmissionPage", problemId);
}
});
vm.$watch("showVisibleOnly", function () {
getPageData(1);
});
}
getPageData(1);
function getPageData(page) {
var url = "/api/admin/problem/?paging=true&page=" + page + "&page_size=10";
if (vm.keyword != "")
url += "&keyword=" + vm.keyword;
if (vm.showVisibleOnly)
url += "&visible=true";
$.ajax({
url: url,
dataType: "json",

View File

@ -1,53 +1,59 @@
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
avalon.ready(function () {
avalon.vmodels.submissionList = null;
var vm = avalon.define({
$id: "submissionList",
submissionList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
results : {
0: "Accepted",
1: "Runtime Error",
2: "Time Limit Exceeded",
3: "Memory Limit Exceeded",
4: "Compile Error",
5: "Format Error",
6: "Wrong Answer",
7: "System Error",
8: "Waiting"
},
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
showSubmissionDetailPage: function (submissionId) {
},
showProblemListPage: function(){
vm.$fire("up!showProblemListPage");
}
});
if (avalon.vmodels.submissionList){
var vm = avalon.vmodels.submissionList;
}
else {
var vm = avalon.define({
$id: "submissionList",
submissionList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
results : {
0: "Accepted",
1: "Runtime Error",
2: "Time Limit Exceeded",
3: "Memory Limit Exceeded",
4: "Compile Error",
5: "Format Error",
6: "Wrong Answer",
7: "System Error",
8: "Waiting"
},
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
showSubmissionDetailPage: function (submissionId) {
},
showProblemListPage: function(){
vm.$fire("up!showProblemListPage");
}
});
}
getPageData(1);

View File

@ -3,12 +3,12 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
// avalon:定义模式 userList
avalon.ready(function () {
//avalon.vmodels.userList = null;
if (avalon.vmodels.userList) {
var vm = avalon.vmodels.userList;
// initialize avalon object
userList = []; previousPage= 0; nextPage= 0; page = 1;
editingUserId= 0; totalPage = 1; keyword= ""; showAdminOnly= false;
userList = []; //previousPage= 0; nextPage= 0; page = 1;
//editingUserId= 0; totalPage = 1; keyword= ""; showAdminOnly= false;
//user editor fields
username= ""; realName= ""; email= ""; adminType= 0; id= 0;
}

View File

@ -2,13 +2,12 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
$('form').validator().on('submit', function (e) {
e.preventDefault();
var username = $("#username").val();
var newPassword = $("#new_password ").val();
var password = $("#password").val();
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/change_password/",
data: {username: username, new_password: newPassword, old_password: password},
data: {new_password: newPassword, old_password: password},
dataType: "json",
method: "post",
success: function (data) {

View File

@ -0,0 +1,30 @@
require(["jquery", "csrfToken", "bsAlert"], function ($, csrfTokenHeader, bsAlert) {
$("#sendApplication").click(function (){
var message;
if ($("#applyMessage").length) {
message = $("#applyMessage").val();
if (!message)
bsAlert("提交失败,请填写申请信息!");
return false;
}
var groupId = window.location.pathname.split("/")[2];
data = {group_id: groupId,message:message}
$.ajax({
url: "/api/group_join/",
method: "post",
dataType: "json",
beforeSend: csrfTokenHeader,
data: JSON.stringify(data),
contentType: "application/json",
success: function (data) {
if (data.code) {
bsAlert(data.data);
}
else {
bsAlert("申请已提交!");
}
}
})
})
})

View File

@ -18,7 +18,6 @@ class SubmissionsListPageTest(TestCase):
self.user.set_password("666666")
self.user.save()
self.user2.save()
# self.client.login(username="gogoing", password="666666")
self.submission = Submission.objects.create(user_id=self.user.id,
language=1,
code='#include "stdio.h"\nint main(){\n\treturn 0;\n}',
@ -29,6 +28,16 @@ class SubmissionsListPageTest(TestCase):
response = self.client.get('/submissions/1/')
self.assertEqual(response.status_code, 200)
def test_visit_submissionsListPage_successfully_language_filter(self):
self.client.login(username="gogoing", password="666666")
response = self.client.get('/submissions/?language=1')
self.assertEqual(response.status_code, 200)
def test_visit_submissionsListPage_successfully_result_filter(self):
self.client.login(username="gogoing", password="666666")
response = self.client.get('/submissions/?result=1')
self.assertEqual(response.status_code, 200)
def test_visit_submissionsListPage_without_page_successfully(self):
self.client.login(username="gogoing", password="666666")
response = self.client.get('/submissions/')
@ -41,7 +50,7 @@ class SubmissionsListPageTest(TestCase):
def test_submissionsListPage_page_not_exist(self):
self.client.login(username="gogoing", password="666666")
response = self.client.get('/submissions/5/')
response = self.client.get('/submissions/999/')
self.assertTemplateUsed(response, "utils/error.html")
def test_submissionsListPage_have_no_submission(self):
@ -137,10 +146,3 @@ class ContestSubmissionAPITest(APITestCase):
data = {"language": 1}
response = self.client.post(self.url, data=data)
pass

View File

@ -12,15 +12,14 @@ from judge.judger_controller.settings import redis_config
from account.decorators import login_required
from account.models import SUPER_ADMIN
from contest.decorators import check_user_contest_permission
from problem.models import Problem
from contest.models import Contest, ContestProblem
from contest.models import ContestProblem
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
from .models import Submission
from .serializers import CreateSubmissionSerializer, SubmissionSerializer
from announcement.models import Announcement
class SubmissionAPIView(APIView):
@login_required
@ -80,8 +79,10 @@ def problem_my_submissions_list_page(request, problem_id):
problem = Problem.objects.get(id=problem_id, visible=True)
except Problem.DoesNotExist:
return error_page(request, u"问题不存在")
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id, contest_id__isnull=True).order_by("-create_time"). \
values("id", "result", "create_time", "accepted_answer_time", "language")
return render(request, "oj/problem/my_submissions_list.html",
{"submissions": submissions, "problem": problem})
@ -137,8 +138,17 @@ def my_submission_list_page(request, page=1):
"""
我的所有提交的列表页
"""
submissions = Submission.objects.filter(user_id=request.user.id). \
submissions = Submission.objects.filter(user_id=request.user.id, contest_id__isnull=True). \
values("id", "result", "create_time", "accepted_answer_time", "language").order_by("-create_time")
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:
current_page = paginator.page(int(page))
@ -154,6 +164,10 @@ def my_submission_list_page(request, page=1):
except Exception:
pass
# 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
return render(request, "oj/submission/my_submissions_list.html",
{"submissions": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20})
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
"announcements": announcements, "filter":filter})

View File

@ -15,7 +15,7 @@
</head>
<body>
<body ms-controller="admin">
<!-- nav begin -->
<nav class="navbar navbar-masthead navbar-default navbar-static-top">
@ -39,13 +39,13 @@
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
李扬
{{ username }}
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">我的提交</a></li>
<li><a href="/submissions/">我的提交</a></li>
<li><a href="#">我的资料</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">退出</a></li>
<li><a href="/logout/">退出</a></li>
</ul>
</li>
</ul>
@ -62,7 +62,7 @@
<![endif]-->
<!-- browser happy end -->
<div class="container main" ms-controller="admin">
<div class="container main">
<div class="row">
<!-- admin left begin-->
<div class="col-md-2">

View File

@ -7,8 +7,8 @@
<th>创建时间</th>
<th>更新时间</th>
<th>创建者</th>
<td>可见范围</td>
<th>状态</th>
<th>类型</th>
<th>可见</th>
<th></th>
</tr>
<tr ms-repeat="announcementList">
@ -18,7 +18,7 @@
<td>{{ el.last_update_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.created_by.username }}</td>
<td ms-text="el.is_global?'全局可见':'组内可见'"></td>
<td>{{ getState(el)}}</td>
<td ms-text="el.visible?'可见':'不可见'"></td>
<td>
<button class="btn-sm btn-info" ms-click="editAnnouncement(el)">编辑</button>
</td>
@ -38,7 +38,8 @@
<div class="form-group">
<label>标题</label>
<input name="title" type="text" class="form-control" id="newTitle" placeholder="公告标题" value="" ms-duplex="newTitle"></div>
<input name="title" type="text" class="form-control" id="newTitle" placeholder="公告标题" value=""
ms-duplex="newTitle"></div>
<div class="form-group">
<label>内容</label>
<textarea id="edit-announcement-editor"></textarea>
@ -66,7 +67,7 @@
</div>
</div>
<div class="form-group">
<button ms-click="submitChange()" class="btn btn-primary">提交</button>
<button ms-click="submitChange()" class="btn btn-success">保存修改</button>
&nbsp;&nbsp;
<button ms-click="cancelEdit()" class="btn btn-danger">取消</button>
</div>
@ -78,12 +79,14 @@
<label>标题</label>
<input name="title" type="text" class="form-control" id="title" placeholder="公告标题"
data-error="请填写公告标题(标题不得超过50字)" maxlength="50" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label>内容</label>
<textarea id="create-announcement-editor" placeholder="公告内容" maxlength="10000" required>
</textarea>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
@ -91,10 +94,16 @@
<div>
<span ms-if="showGlobalViewRadio">
<input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
<label>
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
</small>
</label>
</span>
<span>
<input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
<label>
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
</small>
</label>
</span>
</div>
@ -106,7 +115,7 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
<button type="submit" class="btn btn-success">发布公告</button>
</div>
</form>
</div>

View File

@ -18,9 +18,7 @@
<div class="col-md-12">
<div class="form-group">
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="description"></textarea>
<div class="help-block with-errors"></div>
<small ms-visible="description==''" style="color:red">请填写比赛描述</small>
<p class="error-info" ms-visible="description==''">请填写比赛描述</p>
</div>
</div>
<div class="col-md-6">

View File

@ -15,22 +15,22 @@
<tr>
<th>ID</th>
<th>比赛</th>
<th>公开排名</th>
<th>可见</th>
<th>排名</th>
<th>创建时间</th>
<th>创建者</th>
<th>可见</th>
<th></th>
</tr>
<tr ms-repeat="contestList">
<td>{{ el.id }}</td>
<td>{{ el.title }}</td>
<td>{{ getYesOrNo(el.show_rank) }}</td>
<td>{{ getYesOrNo(el.visible) }}</td>
<td ms-text="el.show_rank?'公开':'不公开'"></td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.created_by.username }}</td>
<td ms-text="el.visible?'可见':'不可见'"></td>
<td>
<a class="btn btn-info" href="javascript:void(0)" ms-click="showEditContestArea($index+1)">编辑</a>
<a class="btn btn-primary" href="javascript:void(0)" ms-click="showEditProblemArea($index+1)">编辑问</a>
<a class="btn btn-info btn-sm" href="javascript:void(0)" ms-click="showEditContestArea($index+1)">编辑</a>
<a class="btn btn-info btn-sm" href="javascript:void(0)" ms-click="showEditProblemArea($index+1)"></a>
</td>
</tr>
</table>
@ -63,7 +63,7 @@
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="editDescription"></textarea>
<div class="help-block with-errors"></div>
<small ms-visible="editDescription==''" style="color:red">请填写比赛描述</small>
<p class="error-info" ms-visible="editDescription==''" >请填写比赛描述</p>
</div>
</div>
<div class="col-md-6">
@ -152,13 +152,14 @@
</div>
</div>
<div class="col-md-12">
<button class="btn btn-primary" type="submit">保存修改</button>
<button class="btn btn-success" type="submit">保存修改</button>
</div>
</form>
</div>
<div class="col-md-12" ms-visible="editingProblemContestIndex">
<label>题目列表</label>
<a href="javascript:void(0)" class="btn btn-success btn-sm" ms-click="add_problem()">添加</a>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="addProblem()">添加题目</a>
<a href="javascript:void(0)" class="btn btn-info btn-sm" ms-click="showSubmissionPage()">查看提交</a>
<table class="table table-striped">
<tr>
<th>编号</th>
@ -172,11 +173,13 @@
<td>{{ el.sort_index }}</td>
<td>{{ el.title }}</td>
<td ms-visible="editMode=='2'">{{ el.score}}</td>
<td>{{ getYesOrNo(el.visible) }}</td>
<td ms-text="el.visible?'可见':'不可见'"></td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss") }}</td>
<td>
<a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="showProblemEditor(el)">编辑</a>
ms-click="showProblemEditPage(el)">编辑</a>
<a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="showSubmissionPage(el)">提交</a>
</td>
</tr>
</table>

View File

@ -23,7 +23,7 @@
<div class="form-group col-md-12">
<label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea>
<small ms-visible="description==''" style="color:red">请填写题目描述</small>
<p class="error-info" ms-visible="description==''">请填写题目描述</p>
</div>
@ -130,7 +130,7 @@
<textarea id="hint" placeholder="这里输入内容" ms-duplex="hint"></textarea>
</div>
<div class="col-md-12">
<input type="submit" class="btn btn-success btn-lg" value="发布题目" id="submitBtn">
<button type="submit" class="btn btn-success btn-lg">发布题目</button>
</div>
</form>
</div>

View File

@ -0,0 +1,37 @@
<div ms-controller="contestSubmissionList" class="col-md-9">
<nav>
<ul class="pager">
<li class="previous" ms-click="goBack()"><a href="javascript:void(0)"><span
aria-hidden="true">&larr;</span> 返回</a></li>
</ul>
</nav>
<h1>提交列表</h1>
<a href="javascript:void(0)" class="btn btn-sm btn-primary" ms-click="getPage(1)">
<span class="glyphicon glyphicon-refresh"></span> 刷新
</a>
<table class="table table-striped">
<tr>
<th>ID</th>
<th>创建时间</th>
<th>作者</th>
<td>结果</td>
<td></td>
</tr>
<tr ms-repeat="submissionList">
<td>{{ el.id }}</td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.user }}</td>
<td>{{ results[el.result] }}</td>
<td>
<a class="btn btn-info" ms-attr-href="'/submission/' + el.id + '/'" target="_blank">详情</a>
</td>
</tr>
</table>
<div class="text-right">
页数:{{ page }}/{{ totalPage }}&nbsp;&nbsp;
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
</div>
</div>
<script src="/static/js/app/admin/contest/submissionList.js"></script>

View File

@ -11,7 +11,7 @@
<div class="form-group col-md-12">
<label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea>
<small ms-visible="description==''" style="color:red">请填写题目描述</small>
<p class="error-info" ms-visible="description==''">请填写题目描述</p>
</div>

View File

@ -17,7 +17,7 @@
<div class="form-group col-md-12">
<label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea>
<small ms-visible="description==''" style="color:red">请填写题目描述</small>
<p class="error-info" ms-visible="description==''">请填写题目描述</p>
</div>

View File

@ -17,7 +17,8 @@
<th>题目</th>
<th>创建时间</th>
<th>作者</th>
<td>通过次数/提交总数</td>
<th>可见</th>
<th>通过次数/提交总数</th>
<td></td>
</tr>
<tr ms-repeat="problemList">
@ -25,6 +26,7 @@
<td>{{ el.title }}</td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.created_by.username }}</td>
<td ms-text="el.visible?'可见':'不可见'"></td>
<td>{{ el.total_accepted_number }}/{{ el.total_submit_number }}</td>
<td>
<button class="btn-sm btn-info" ms-click="showEditProblemPage(el.id)">编辑</button>
@ -32,6 +34,9 @@
</td>
</tr>
</table>
<div class="form-group">
<label>仅显示可见 <input ms-duplex-checked="showVisibleOnly" type="checkbox"/></label>
</div>
<div class="text-right">
页数:{{ page }}/{{ totalPage }}&nbsp;&nbsp;
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>

View File

@ -21,7 +21,7 @@
<td>{{ el.user }}</td>
<td>{{ results[el.result] }}</td>
<td>
<a class="btn btn-info" ms-attr-href="'/my_submission/' + el.id + '/'" target="_blank">详情</a>
<a class="btn btn-info" ms-attr-href="'/submission/' + el.id + '/'" target="_blank">详情</a>
</td>
</tr>
</table>

View File

@ -5,11 +5,6 @@
<h2 class="text-center">修改密码</h2>
<form id="change_password-form">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-error="请填写用户名" maxlength="30" autofocus required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="password">旧密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码" data-error="请填写旧密码" maxlength="30" required>

View File

@ -1,27 +1,33 @@
{% extends "oj_base.html" %}
{% block body %}
<div class="container main">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">用户登录</h2>
<div class="container main">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">用户登录</h2>
<form id="login-form">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" maxlength="30" data-error="请填写用户名" placeholder="用户名" autofocus required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" maxlength="30" data-error="请填写密码" placeholder="密码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</form>
</div>
<form id="login-form">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" maxlength="30"
data-error="请填写用户名" placeholder="用户名" autofocus required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" maxlength="30"
data-error="请填写密码" placeholder="密码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
<a href="/register/">还没有帐号?点击注册</a>
</form>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/account/login.js"></script>
<script src="/static/js/app/oj/account/login.js"></script>
{% endblock %}

View File

@ -7,28 +7,28 @@
<form id="register-form">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-error="请输入用户名" required autofocus>
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-error="请填写用户名" required autofocus>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="real_name">真实姓名</label>
<input type="text" class="form-control input-lg" id="real_name" name="real_name" placeholder="真实姓名" data-error="请输入姓名" required>
<input type="text" class="form-control input-lg" id="real_name" name="real_name" placeholder="真实姓名" data-error="请填写姓名" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="email">邮箱地址</label>
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-error="请填写邮箱" required>
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-error="请填写正确的邮箱地址" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码" data-minlength="6" data-minlength-error="密码不得少于6位" required>
<input type="password" class="form-control input-lg" id="password" name="password" data-error="请填写密码" placeholder="密码" data-minlength="6" data-minlength-error="密码不得少于6位" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="confirm_password">确认密码</label>
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
placeholder="确认密码" data-match="#password" data-match-error="两个密码不一致" required>
placeholder="确认密码" data-match="#password" data-match-error="两个密码不一致" data-error="请填写确认密码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">

View File

@ -5,8 +5,13 @@
公告
</h3></div>
<div class="panel-body">
{% for item in announcements %}
<p>{{ forloop.counter }}.&nbsp;&nbsp;<a href="/announcement/{{ item.id }}/" target="_blank">{{ item.title }}</a></p>
{% endfor %}
{% if announcements %}
{% for item in announcements %}
<p>{{ forloop.counter }}.&nbsp;&nbsp;<a href="/announcement/{{ item.id }}/" target="_blank">{{ item.title }}</a>
</p>
{% endfor %}
{% else %}
<p>暂无可显示的公告</p>
{% endif %}
</div>
</div>

View File

@ -3,7 +3,7 @@
{% load contest %}
<div class="container main">
<div class="row">
<div class="col-lg-9">
<div class="col-md-9 col-lg-9">
<div class="row">
<div class="right">
<form class="form-inline" method="get">
@ -15,6 +15,7 @@
</div>
</div>
<div>
{% if contests %}
<table class="table table-striped">
<thead>
<tr>
@ -47,6 +48,7 @@
{% endfor %}
</tbody>
</table>
<div class="form-group">
<label>仅显示当前可参加的比赛
<input id="join" type="checkbox" {% if join %}checked{% endif %} onchange="if(this.checked){location.href='/contests/?join=True'}else{location.href='/contests/'}">
@ -66,10 +68,13 @@
{% endif %}
</ul>
</nav>
{% else %}
<p>当前没有合适的比赛,你可以尝试到<a href="/groups/">小组列表</a>申请加入一些小组,以便参加小组内部的比赛</p>
{% endif %}
</div>
</div>
<div class="col-lg-3">
<div class="col-md-3 col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div>

View File

@ -26,7 +26,7 @@
</div>
<div class="col-lg-9">
<div class="col-md-9 col-lg-9">
<div>
<table class="table table-striped">
<thead>
@ -55,7 +55,7 @@
</div>
</div>
<div class="col-lg-3">
<div class="col-md-3 col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div>

View File

@ -20,6 +20,7 @@
</div>
<div class="row">
<div class="col-lg-12">
{% if result %}
<table class="table table-bordered">
<thead>
<tr>
@ -49,6 +50,9 @@
{% endfor %}
</tbody>
</table>
{% else %}
<p>还没有结果</p>
{% endif %}
</div>
</div>
</div>

View File

@ -0,0 +1,47 @@
{% extends 'oj_base.html' %}
{% block body %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active">
<a href="/group/{{ group.id }}/">详细信息</a>
</li>
{% if group.join_group_setting %}
<li role="presentation"><a href="/group/{{ group.id }}/applications/">我的申请</a></li>
{% endif %}
</ul>
<h2 class="text-center">{{ group.name }}</h2>
<p class="text-muted text-center">发布时间 : {{ group.create_time }}&nbsp;&nbsp;
创建者 : {{ group.admin }}
</p>
<div>
<div class="group-section">
<label class="group-label">描述</label>
<p class="group-detail">{{ group.description|safe }}</p>
</div>
</div>
<hr>
<div>
{% if group.join_group_setting %}
<div class="form-group">
<input id="groupId" value="{{ group.id }" type="hidden">
<label>申请信息</label>
<textarea class="form-control" id="applyMessage" rows="10"></textarea>
</div>
{% endif %}
<div class="form-group">
<button class="btn btn-primary" id="sendApplication">
{% if group.join_group_setting %}
申请
{% endif %}
加入</button>
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/group/group.js"></script>
{% endblock %}

View File

@ -0,0 +1,71 @@
{% extends "oj_base.html" %}
{% block body %}
{% load problem %}
<div class="container main">
<div class="row">
<div class="col-md-9 col-lg-9">
<div class="row">
<div class="right">
<form class="form-inline" method="get">
<div class="form-group-sm">
<input name="keyword" class="form-control" placeholder="请输入关键词">
<input type="submit" value="搜索" class="btn btn-primary">
</div>
</form>
</div>
</div>
<div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>名称</th>
<th>加入方式</th>
<th>创建者</th>
<th>创建时间</th>
</tr>
</thead>
<tbody>
{% for item in groups %}
<tr>
<th scope="row"><a href="/group/{{ item.id }}/" target="_blank">{{ item.id }}</a></th>
<td><a href="/group/{{ item.id }}/" target="_blank">{{ item.name }}</a></td>
<td>
{% if item.join_group_setting %}
需要申请
{% else %}
无需申请
{% endif %}
</td>
<td>{{ item.admin }}</td>
<td>{{ item.create_time }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<nav>
<ul class="pager">
{% if previous_page %}
<li class="previous"><a
href="/groups/{{ previous_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页</a>
</li>
{% endif %}
{% if next_page %}
<li class="next">
<a href="/groups/{{ next_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}">
下一页 <span aria-hidden="true">&rarr;</span>
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
<div class="col-md-3 col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends 'oj_base.html' %}
{% block body %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation">
<a href="/group/{{ application.group.id }}/">详细信息</a></li>
<li role="presentation" class="active">
<a href="/group/{{ application.group.id }}/applications/">我的申请</a>
</li>
</ul>
<label>内容</label>
<p>{{ application.message|safe }}</p>
<label>结果</label>
{% if application.status %}
{% if application.accepted %}
<p>管理员接受了你的请求</p>
{% else %}
<p>管理员拒绝了你的请求</p>
{% endif %}
{% else %}
<p>待审核</p>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,44 @@
{% extends 'oj_base.html' %}
{% block body %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation">
<a href="/group/{{ group.id }}/">详细信息</a></li>
<li role="presentation" class="active">
<a href="/group/{{ group.id }}/applications/">我的申请</a></li>
</ul>
{% if applications %}
<table class="table table-bordered">
<thead>
<tr>
<th>#</th>
<th>提交时间</th>
<th>结果</th>
</tr>
</thead>
<tbody>
{% for item in applications %}
<tr>
<th scope="row"><a href="/group/application/{{ item.id }}/">{{ forloop.counter }}</a></th>
<td>{{ item.create_time }}</td>
{% if item.status %}
{% if item.accepted %}
<td class="alert-success">通过</td>
{% else %}
<td class="alert-danger">拒绝</td>
{% endif %}
{% else %}
<td class="alert-warning">未处理</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>你还没有申请该小组</p>
{% endif %}
</div>
{% endblock %}

View File

@ -8,7 +8,8 @@
<link rel="stylesheet" type="text/css" href="/static/css/fullpage/jquery.fullPage.css">
<style>
html, textarea, input, option, select, button {
font: 1em "Helvetica Neue", Helvetica, "Lantinghei SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", "STHeiti", "WenQuanYi Micro Hei", SimSun, sans-serif;
font: 1em "Helvetica Neue", Helvetica, "Lantinghei SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑",
"STHeiti", "WenQuanYi Micro Hei", SimSun, sans-serif;
color: #FFF;
}
@ -84,21 +85,29 @@
loopBottom: true
});
});
</script>
</head>
<body>
<div id="header">
<span id="name">qduoj</span>
<a href="/problems/">题目</a>&nbsp;&nbsp;<a href="/contests/">比赛</a>&nbsp;&nbsp;<a href="#">小组</a>
<a href="/problems/">题目</a>&nbsp;&nbsp;
<a href="/submissions/">提交</a>&nbsp;&nbsp;
<a href="/contests/">比赛</a>&nbsp;&nbsp;
<a href="/groups/">小组</a>&nbsp;&nbsp;
<a href="#">关于</a>
</div>
<div id="fullpage">
<div class="section" id="section0">
<div class="index-section-text animated bounceInUp">
<h1>青岛大学在线评测平台</h1>
<h1>青岛大学 Online Judge</h1>
<h3>新面貌,新的开始~</h3>
<h3>面貌,新的开始~</h3>
</div>
<div class="bottom-pointer">↓继续滚动~</div>
</div>
@ -128,7 +137,7 @@
<div class="section-text">
<img class="icon" id="img3" src="/static/img/index/m.png">
<h1>自由举办小组赛</h1>
<h1>自由举办小组赛(10月上线)</h1>
<h3>内部比赛,日常作业,期末考试,通通搞定</h3>
</div>

View File

@ -11,22 +11,25 @@
<a href="/problem/{{ problem.id }}/submissions/">我的提交</a></li>
</ul>
{% include "oj/problem/_problem_header.html" %}
{% if submissions %}
<table class="table table-bordered">
<thead>
<tr class="" success>
<th>#</th>
<th>提交时间</th>
<th>结果</th>
<th>运行时间</th>
<th>语言</th>
<th>运行时间</th>
<th>结果</th>
</tr>
</thead>
<tbody>
{% for item in submissions %}
<tr class="{{ item.result|translate_result_class }}">
<tr>
<th scope="row"><a href="/submission/{{ item.id }}/">{{ forloop.counter }}</a></th>
<td>{{ item.create_time }}</td>
<td>{{ item.result|translate_result }}</td>
<td>
{{ item.language|translate_language }}
</td>
<td>
{% if item.accepted_answer_time %}
{{ item.accepted_answer_time }}ms
@ -34,13 +37,16 @@
--
{% endif %}
</td>
<td>
{{ item.language|translate_language }}
<td class="alert-{{ item.result|translate_result_class }}">
<strong>{{ item.result|translate_result }}</strong>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>你还没有提交该题目</p>
{% endif %}
</div>
{% endblock %}

View File

@ -3,7 +3,7 @@
{% load problem %}
<div class="container main">
<div class="row">
<div class="col-lg-9">
<div class="col-md-9 col-lg-9">
<div class="row">
<div class="right">
<form class="form-inline" method="get">
@ -54,7 +54,7 @@
</div>
</div>
<div class="col-lg-3">
<div class="col-md-3 col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
<div class="panel panel-info">
<div class="panel-heading">

View File

@ -2,57 +2,94 @@
{% block body %}
{% load submission %}
<div class="container main">
{% if submissions %}
<table class="table table-bordered">
<thead>
<tr class="" success>
<th>#</th>
<th>提交时间</th>
<th>结果</th>
<th>运行时间</th>
<th>语言</th>
</tr>
</thead>
<tbody>
{% for item in submissions %}
<tr class="{{ item.result|translate_result_class }}">
<th scope="row"><a href="/my_submission/{{ item.id }}/" id="id_{{ forloop.counter }}">
{{ forloop.counter |add:start_id }}</a></th>
<td>{{ item.create_time }}</td>
<td>{{ item.result|translate_result }}</td>
<td>
{% if item.accepted_answer_time %}
{{ item.accepted_answer_time }}ms
{% else %}
--
{% endif %}
</td>
<td>
{{ item.language|translate_language }}
</td>
</tr>
{% endfor %}
{% load submission %}
<div class="container main">
<div class="col-md-9 col-lg-9">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>提交时间</th>
<th>
<div class="dropdown">
<a href="#" class="dropdown-toggle" id="languageFilter" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
语言<span class="caret"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="languageFilter">
<li><a href="/submissions/?language=1">C</a></li>
<li><a href="/submissions/?language=2">C++</a></li>
<li><a href="/submissions/?language=3">Java</a></li>
<li><a href="/submissions/">取消筛选</a></li>
</ul>
</div>
</th>
<th>运行时间</th>
<th>
<div class="dropdown">
<a href="#" class="dropdown-toggle" id="resultFilter" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
结果<span class="caret"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="resultFilter">
<li><a href="/submissions/?result=0">Accepted</a></li>
<li><a href="/submissions/?result=6">Wrong Answer</a></li>
<li><a href="/submissions/?result=1">Runtime Error</a></li>
<li><a href="/submissions/?result=2">Time Limit Exceeded</a></li>
<li><a href="/submissions/?result=3">Memory Limit Exceeded</a></li>
<li><a href="/submissions/?result=4">Compile Error</a></li>
<li><a href="/submissions/?result=5">Format Error</a></li>
<li><a href="/submissions/">取消筛选</a></li>
</ul>
</div>
</th>
</tr>
</thead>
<tbody>
{% for item in submissions %}
<tr>
<th scope="row"><a href="/submission/{{ item.id }}/" id="id_{{ forloop.counter }}">
{{ forloop.counter |add:start_id }}</a></th>
<td>{{ item.create_time }}</td>
<td>
{{ item.language|translate_language }}
</td>
<td>
{% if item.accepted_answer_time %}
{{ item.accepted_answer_time }}ms
{% else %}
--
{% endif %}
</td>
<td class="alert-{{ item.result|translate_result_class }}">
<strong>{{ item.result|translate_result }}</strong>
</td>
</tbody>
</table>
{% else %}
<p>你还没有提交记录!</p>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
<nav>
<ul class="pager">
{% if previous_page %}
<li class="previous"><a
href="/submissions/{{ previous_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页</a></li>
<li class="previous"><a
href="/submissions/{{ previous_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页</a></li>
{% endif %}
{% if next_page %}
<li class="next"><a
href="/submissions/{{ next_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">下一页 <span
aria-hidden="true">&rarr;</span></a></li>
<li class="next"><a
href="/submissions/{{ next_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% endif %}">下一页 <span
aria-hidden="true">&rarr;</span></a></li>
{% endif %}
</ul>
</nav>
{% if not submissions %}
<p>你还没有提交记录!</p>
{% endif %}
</div>
<div class="col-md-3 col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div>
{% endblock %}

View File

@ -45,31 +45,32 @@
<li><a href="/problems/">题目</a></li>
<li><a href="/submissions/">提交</a></li>
<li><a href="/contests/">比赛</a></li>
<li><a href="/about/">关于</a></li>
<li><a href="/groups/">小组</a></li>
<li><a href="#">关于</a></li>
</ul>
{% if request.user.is_authenticated %}
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
李扬
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">我的提交</a></li>
<li><a href="#">我的资料</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">退出</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
{{ request.user.username }}
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/submission/">我的提交</a></li>
<li><a href="#">我的资料</a></li>
<li role="separator" class="divider"></li>
<li><a href="/logout/">退出</a></li>
</ul>
</li>
</ul>
{% else %}
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="/login/" class="dropdown-toggle">
登录
</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<a href="/login/">
登录
</a>
</li>
</ul>
{% endif %}
</div>
</div>
@ -84,6 +85,10 @@
<script src="/static/js/require.js"></script>
<script>
require(["bootstrap"]);
</script>
{% block js_block %}{% endblock %}
<!-- footer begin -->