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
60 changed files with 1344 additions and 602 deletions

View File

@@ -25,7 +25,6 @@ class UserRegisterSerializer(serializers.Serializer):
class UserChangePasswordSerializer(serializers.Serializer): class UserChangePasswordSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
old_password = serializers.CharField() old_password = serializers.CharField()
new_password = serializers.CharField(max_length=30, min_length=6) 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 = User.objects.create(username="test")
user.set_password("aaabbb") user.set_password("aaabbb")
user.save() user.save()
self.client.login(username="test",password="aaabbb")
def test_error_old_password(self): 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) response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"密码不正确,请重新修改!"}) 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): def test_success_change_password(self):
data = {"username": "test", "old_password": "aaabbb", "new_password": "aaaccc"} data = {"username": "test", "old_password": "aaabbb", "new_password": "aaaccc"}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
@@ -369,3 +360,18 @@ class AdminRequiredDecoratorTest(TestCase):
self.client.login(username="test", password="test") self.client.login(username="test", password="test")
response = self.client.get("/admin_required_test/cbv/1024/") response = self.client.get("/admin_required_test/cbv/1024/")
self.assertEqual(response.content, "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 # coding=utf-8
from django import http
from django.contrib import auth from django.contrib import auth
from django.shortcuts import render from django.shortcuts import render
from django.db.models import Q from django.db.models import Q
@@ -34,6 +35,10 @@ class UserLoginAPIView(APIView):
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
@login_required
def logout(request):
auth.logout(request)
return http.HttpResponseRedirect("/")
class UserRegisterAPIView(APIView): class UserRegisterAPIView(APIView):
def post(self, request): def post(self, request):
@@ -64,6 +69,7 @@ class UserRegisterAPIView(APIView):
class UserChangePasswordAPIView(APIView): class UserChangePasswordAPIView(APIView):
@login_required
def post(self, request): def post(self, request):
""" """
用户修改密码json api接口 用户修改密码json api接口
@@ -73,7 +79,8 @@ class UserChangePasswordAPIView(APIView):
serializer = UserChangePasswordSerializer(data=request.data) serializer = UserChangePasswordSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
data = serializer.data 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: if user:
user.set_password(data["new_password"]) user.set_password(data["new_password"])
user.save() user.save()

View File

@@ -1,7 +1,8 @@
# coding=utf-8 # coding=utf-8
import json import json
from rest_framework import serializers from rest_framework import serializers
from django.utils import timezone
import datetime
from account.models import User from account.models import User
from account.serializers import UserSerializer from account.serializers import UserSerializer
from .models import Contest, ContestProblem from .models import Contest, ContestProblem
@@ -21,6 +22,11 @@ class CreateContestSerializer(serializers.Serializer):
visible = serializers.BooleanField() visible = serializers.BooleanField()
class DateTimeLocal(serializers.DateTimeField):
def to_representation(self, value):
return timezone.localtime(value)
class ContestSerializer(serializers.ModelSerializer): class ContestSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@@ -28,6 +34,8 @@ class ContestSerializer(serializers.ModelSerializer):
fields = ["username"] fields = ["username"]
created_by = UserSerializer() created_by = UserSerializer()
start_time = DateTimeLocal()
end_time = DateTimeLocal()
class Meta: class Meta:
model = Contest model = Contest

View File

@@ -138,7 +138,7 @@ class ContestAdminAPITest(APITestCase):
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0) self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["title"], "titlez") 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): def test_edit_group_contest_successfully(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
@@ -149,7 +149,7 @@ class ContestAdminAPITest(APITestCase):
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0) self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["title"], "titleyyy") 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) self.assertEqual(response.data["data"]["visible"], False)
def test_edit_group_contest_unsuccessfully(self): 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) keyword = request.GET.get("keyword", None)
if keyword: 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) join = request.GET.get("join", None)
if join: 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()) filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now())
paginator = Paginator(contests, 20) 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. # 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 submission.models import Submission
from .serializers import CreateContestSubmissionSerializer from .serializers import CreateContestSubmissionSerializer
from submission.serializers import SubmissionSerializer
class ContestSubmissionAPIView(APIView): 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) contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except Problem.DoesNotExist: except Problem.DoesNotExist:
return error_page(request, u"比赛问题不存在") 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") values("id", "result", "create_time", "accepted_answer_time", "language")
return render(request, "oj/contest/my_submissions_list.html", return render(request, "oj/contest/my_submissions_list.html",
{"submissions": submissions, "problem": contest_problem}) {"submissions": submissions, "problem": contest_problem})
@@ -113,3 +115,37 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
{"submissions": current_page, "page": int(page), {"submissions": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
"contest": contest}) "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) create_time = models.DateTimeField(auto_now_add=True)
# 是否处理 # 是否处理
status = models.BooleanField(default=False) status = models.BooleanField(default=False)
accepted = models.BooleanField(default=False)
class Meta: class Meta:
db_table = "join_group_request" db_table = "join_group_request"

View File

@@ -21,7 +21,7 @@ class EditGroupSerializer(serializers.Serializer):
class CreateJoinGroupRequestSerializer(serializers.Serializer): class CreateJoinGroupRequestSerializer(serializers.Serializer):
group_id = serializers.IntegerField() group_id = serializers.IntegerField()
message = serializers.CharField(max_length=30) message = serializers.CharField(max_length=30, required=False)
class JoinGroupRequestSerializer(serializers.ModelSerializer): 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 account.models import User, REGULAR_USER, ADMIN, SUPER_ADMIN
from group.models import Group, UserGroupRelation, JoinGroupRequest from group.models import Group, UserGroupRelation, JoinGroupRequest
from django.test import TestCase, Client
class GroupAPITest(APITestCase): class GroupAPITest(APITestCase):
pass pass
@@ -148,7 +150,7 @@ class JoinGroupAPITest(APITestCase):
def setUp(self): def setUp(self):
self.client = APIClient() 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 = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa") self.user.set_password("testaa")
self.user.save() self.user.save()
@@ -242,7 +244,7 @@ class JoinGroupRequestAdminAPITest(APITestCase):
self.assertEqual(JoinGroupRequest.objects.get(id=self.request.id).status, True) self.assertEqual(JoinGroupRequest.objects.get(id=self.request.id).status, True)
def test_join_group_successfully(self): 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) response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 0, "data": u"加入成功"}) self.assertEqual(response.data, {"code": 0, "data": u"加入成功"})
UserGroupRelation.objects.get(group=self.group, user=self.user1) UserGroupRelation.objects.get(group=self.group, user=self.user1)
@@ -254,3 +256,51 @@ class JoinGroupRequestAdminAPITest(APITestCase):
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"加入失败,已经在本小组内"}) 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 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.models import REGULAR_USER, ADMIN, SUPER_ADMIN
from account.decorators import login_required from account.decorators import login_required
@@ -13,6 +13,9 @@ from .serializers import (CreateGroupSerializer, EditGroupSerializer,
CreateJoinGroupRequestSerializer, GroupSerializer, CreateJoinGroupRequestSerializer, GroupSerializer,
GroupMemberSerializer, EditGroupMemberSerializer, GroupMemberSerializer, EditGroupMemberSerializer,
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer) JoinGroupRequestSerializer, PutJoinGroupRequestSerializer)
from announcement.models import Announcement
from django.core.paginator import Paginator
from django.db.models import Q
class GroupAPIViewBase(object): class GroupAPIViewBase(object):
@@ -180,6 +183,8 @@ class JoinGroupAPIView(APIView):
else: else:
return error_response(u"你已经是小组成员了") return error_response(u"你已经是小组成员了")
elif group.join_group_setting == 1: elif group.join_group_setting == 1:
if not data["message"]:
return error_response(u"message : 该字段是必填项。")
try: try:
JoinGroupRequest.objects.get(user=request.user, group=group, status=False) JoinGroupRequest.objects.get(user=request.user, group=group, status=False)
return error_response(u"你已经提交过申请了,请等待审核") return error_response(u"你已经提交过申请了,请等待审核")
@@ -233,9 +238,10 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
join_request.status = True join_request.status = True
join_request.save() join_request.save()
if data["status"]: if data["status"]:
if join_group(join_request.user, join_request.group): if join_group(join_request.user, join_request.group):
join_request.accepted = True
join_request.save()
return success_response(u"加入成功") return success_response(u"加入成功")
else: else:
return error_response(u"加入失败,已经在本小组内") return error_response(u"加入失败,已经在本小组内")
@@ -244,3 +250,70 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
else: else:
return serializer_invalid_response(serializer) 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 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上 # 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = { DATABASES = {
@@ -18,7 +18,7 @@ DATABASES = {
'NAME': 'oj_submission', 'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'HOST': "121.42.32.129", 'HOST': "121.42.32.129",
'POST': 3306, 'PORT': 3306,
'USER': 'root', 'USER': 'root',
'PASSWORD': 'mypwd' 'PASSWORD': 'mypwd'
} }
@@ -29,5 +29,5 @@ DEBUG = True
# 同理 这是 web 服务器的上传路径 # 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/') 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 # 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, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os import os
# todo 判断运行环境 # 判断运行环境
ENV = os.environ.get("oj_env", "local") ENV = os.environ.get("oj_env", "local")
if ENV == "local": if ENV == "local":
from .local_settings import * from .local_settings import *
elif ENV == "server": elif ENV == "server":
from .server_settings import * from .server_settings import *
elif ENV == "daocloud":
from .daocloud_settings import *
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 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! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'hzfp^8mbgapc&x%$#xv)0=t8s7_ilingw(q3!@h&2fty6v6fxz' SECRET_KEY = 'hzfp^8mbgapc&x%$#xv)0=t8s7_ilingw(q3!@h&2fty6v6fxz'
ALLOWED_HOSTS = []
# Application definition # Application definition
@@ -171,3 +169,5 @@ LOGGING = {
REST_FRAMEWORK = { REST_FRAMEWORK = {
'TEST_REQUEST_DEFAULT_FORMAT': 'json' 'TEST_REQUEST_DEFAULT_FORMAT': 'json'
} }
DATABASE_ROUTERS = ['oj.db_router.DBRouter']

View File

@@ -1,11 +1,11 @@
# coding=utf-8 # coding=utf-8
from django.conf.urls import include, url from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView from django.views.generic import TemplateView
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
UserChangePasswordAPIView, EmailCheckAPIView, UserChangePasswordAPIView, EmailCheckAPIView,
UserAdminAPIView, UserInfoAPIView) UserAdminAPIView, UserInfoAPIView)
from announcement.views import AnnouncementAdminAPIView from announcement.views import AnnouncementAdminAPIView
from contest.views import ContestAdminAPIView, ContestProblemAdminAPIView, ContestPasswordVerifyAPIView from contest.views import ContestAdminAPIView, ContestProblemAdminAPIView, ContestPasswordVerifyAPIView
@@ -17,7 +17,7 @@ from admin.views import AdminTemplateView
from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView
from submission.views import SubmissionAPIView, SubmissionAdminAPIView 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 monitor.views import QueueLengthMonitorAPIView
from contest_submission.views import contest_problem_my_submissions_list_page from contest_submission.views import contest_problem_my_submissions_list_page
@@ -34,6 +34,7 @@ urlpatterns = [
name="admin_template"), name="admin_template"),
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), 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"), url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"),
name="user_register_page"), name="user_register_page"),
url(r'^change_password/$', TemplateView.as_view(template_name="oj/account/change_password.html"), 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/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_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/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/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"), url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"), url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"), url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"), url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"),
url(r'^api/admin/group_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/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"), url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_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"), name="join_group_request_admin_api"),
url(r'^api/admin/submission/$', SubmissionAdminAPIView.as_view(), name="submission_admin_api_view"), 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/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", 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'^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'^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/$', "problem.views.problem_list_page", name="problem_list_page"),
url(r'^problems/(?P<page>\d+)/$', "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/$', "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'^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) keyword = request.GET.get("keyword", None)
if keyword: 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) tag_text = request.GET.get("tag", None)

View File

@@ -55,3 +55,12 @@
list-style-type: none; list-style-type: none;
margin: 5px; 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({ var vm = avalon.define({
$id: "admin", $id: "admin",
template_url: "template/" + hash + ".html", template_url: "template/" + hash + ".html",
username: "",
groupId: -1, groupId: -1,
problemId: -1, problemId: -1,
adminNavList: [], adminNavList: [],
@@ -93,6 +94,7 @@ define("admin", ["jquery", "avalon"], function ($, avalon) {
dataType: "json", dataType: "json",
success: function(data){ success: function(data){
if(!data.code){ if(!data.code){
vm.username = data.data.username;
if (data.data.admin_type == 2){ if (data.data.admin_type == 2){
vm.adminNavList = superAdminNav; vm.adminNavList = superAdminNav;
} }
@@ -133,11 +135,17 @@ define("admin", ["jquery", "avalon"], function ($, avalon) {
vm.template_url = "template/contest/edit_problem.html"; vm.template_url = "template/contest/edit_problem.html";
}); });
vm.$watch("showContestListPage", function (problemId) { vm.$watch("showContestListPage", function () {
vm.problemId = problemId;
vm.template_url = "template/contest/contest_list.html"; 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(); avalon.scan();
window.onhashchange = function () { window.onhashchange = function () {

View File

@@ -1,10 +1,14 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
function ($, avalon, csrfTokenHeader, bsAlert, editor) { function ($, avalon, csrfTokenHeader, bsAlert, editor) {
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.announcement = null;
var createAnnouncementEditor = editor("#create-announcement-editor"); var createAnnouncementEditor = editor("#create-announcement-editor");
var editAnnouncementEditor = editor("#edit-announcement-editor"); var editAnnouncementEditor = editor("#edit-announcement-editor");
if (avalon.vmodels.announcement){
var vm = avalon.vmodels.announcement;
announcementList = [];
}
else {
var vm = avalon.define({ var vm = avalon.define({
$id: "announcement", $id: "announcement",
@@ -22,12 +26,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
showGlobalViewRadio: true, showGlobalViewRadio: true,
isGlobal: true, isGlobal: true,
allGroups: [], allGroups: [],
getState: function (el) { //获取公告当前状态,显示
if (el.visible)
return "可见";
else
return "隐藏";
},
getNext: function () { getNext: function () {
if (!vm.nextPage) if (!vm.nextPage)
return; return;
@@ -45,7 +43,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
else { else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
} }
}, },
editAnnouncement: function (announcement) { editAnnouncement: function (announcement) {
vm.newTitle = announcement.title; vm.newTitle = announcement.title;
@@ -122,9 +119,23 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
vm.$watch("showVisibleOnly", function () { vm.$watch("showVisibleOnly", function () {
getPageData(1); getPageData(1);
}); });
}
getPageData(1); getPageData(1);
$.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({ $.ajax({
url: "/api/admin/group/", url: "/api/admin/group/",
method: "get", method: "get",
@@ -132,6 +143,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
if (!data.data.length) { if (!data.data.length) {
if (admin_type != 2)
bsAlert("您的用户权限只能创建组内公告,但是您还没有创建过小组"); bsAlert("您的用户权限只能创建组内公告,但是您还没有创建过小组");
return; return;
} }
@@ -146,18 +158,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
} }
} }
}); });
$.ajax({
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (data.data.admin_type == 1) {
vm.isGlobal = false;
vm.showGlobalViewRadio = false;
}
}
} }
}); });

View File

@@ -43,7 +43,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
contentType: "application/json", contentType: "application/json",
data: JSON.stringify(ajaxData), data: JSON.stringify(ajaxData),
method: "post", method: "post",
contentType: "application/json",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)"); bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)");
@@ -131,7 +130,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
if (!data.data.length) { if (!data.data.length) {
bsAlert("您的用户权限只能创建组内比赛,但是您还没有创建过小组");
return; return;
} }
for (var i = 0; i < data.data.length; i++) { 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.groupList[vm.choseGroupList[groupIndex].index].chose = false;
vm.choseGroupList.remove(vm.choseGroupList[groupIndex]); vm.choseGroupList.remove(vm.choseGroupList[groupIndex]);
}, },
add_problem: function () { addProblem: function () {
vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode); 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); vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode);
}, },
getYesOrNo: function(yORn) { showSubmissionPage: function(el) {
if (yORn) return "是"; var problemId = 0
return "否"; if (el)
problemId = el.id;
vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode);
} }
}); });
vm.$watch("showVisibleOnly", function() { vm.$watch("showVisibleOnly", function() {
@@ -289,6 +291,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
} }
}); });
} }
// Get group list // Get group list
$.ajax({ // Get current user type $.ajax({ // Get current user type
url: "/api/user/", 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,8 +1,12 @@
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) { require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.group = null; //avalon.vmodels.group = null;
if (avalon.vmodels.group) {
var vm = avalon.vmodels.group;
}
else {
var vm = avalon.define({ var vm = avalon.define({
$id: "group", $id: "group",
//通用变量 //通用变量
@@ -42,10 +46,12 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
vm.$fire("up!showGroupDetailPage", groupId); vm.$fire("up!showGroupDetailPage", groupId);
} }
}); });
}
getPageData(1); getPageData(1);
function getPageData(page) { 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) if (vm.keyword)
url += "&keyword=" + vm.keyword; url += "&keyword=" + vm.keyword;
$.ajax({ $.ajax({

View File

@@ -3,7 +3,11 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
// avalon:定义模式 group_list // avalon:定义模式 group_list
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.groupDetail = null;
if (avalon.vmodels.groupDetail) {
var vm = avalon.vmodels.groupDetail;
}
else {
var vm = avalon.define({ var vm = avalon.define({
$id: "groupDetail", $id: "groupDetail",
//通用变量 //通用变量
@@ -52,6 +56,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
vm.$fire("up!showGroupListPage"); vm.$fire("up!showGroupListPage");
} }
}); });
}
avalon.scan(); avalon.scan();
getPageData(1); getPageData(1);

View File

@@ -1,8 +1,13 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "formValidation"], function ($, avalon, csrfTokenHeader, bsAlert) { require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
// avalon:定义模式 group_list // avalon:定义模式 group_list
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.requestList = null;
if (avalon.vmodels.requestList) {
var vm = avalon.vmodels.requestList;
}
else {
var vm = avalon.define({ var vm = avalon.define({
$id: "requestList", $id: "requestList",
//通用变量 //通用变量
@@ -46,6 +51,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "formValidation"], function
}) })
} }
}); });
}
avalon.scan(); avalon.scan();
getPageData(1); 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 () { avalon.ready(function () {
if(avalon.vmodels.problemList){ if(avalon.vmodels.problemList){
vm = avalon.vmodels.problemList; vm = avalon.vmodels.problemList;
problemList = [];
} }
else { else {
var vm = avalon.define({ var vm = avalon.define({
@@ -13,6 +14,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
page: 1, page: 1,
totalPage: 1, totalPage: 1,
keyword: "", keyword: "",
showVisibleOnly: false,
getNext: function () { getNext: function () {
if (!vm.nextPage) if (!vm.nextPage)
return; return;
@@ -41,12 +43,17 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
vm.$fire("up!showProblemSubmissionPage", problemId); vm.$fire("up!showProblemSubmissionPage", problemId);
} }
}); });
vm.$watch("showVisibleOnly", function () {
getPageData(1);
});
} }
getPageData(1); getPageData(1);
function getPageData(page) { function getPageData(page) {
var url = "/api/admin/problem/?paging=true&page=" + page + "&page_size=10"; var url = "/api/admin/problem/?paging=true&page=" + page + "&page_size=10";
if (vm.keyword != "") if (vm.keyword != "")
url += "&keyword=" + vm.keyword; url += "&keyword=" + vm.keyword;
if (vm.showVisibleOnly)
url += "&visible=true";
$.ajax({ $.ajax({
url: url, url: url,
dataType: "json", dataType: "json",

View File

@@ -1,7 +1,12 @@
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) { require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.submissionList = null;
if (avalon.vmodels.submissionList){
var vm = avalon.vmodels.submissionList;
}
else {
var vm = avalon.define({ var vm = avalon.define({
$id: "submissionList", $id: "submissionList",
submissionList: [], submissionList: [],
@@ -48,6 +53,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
vm.$fire("up!showProblemListPage"); vm.$fire("up!showProblemListPage");
} }
}); });
}
getPageData(1); getPageData(1);

View File

@@ -3,12 +3,12 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
// avalon:定义模式 userList // avalon:定义模式 userList
avalon.ready(function () { avalon.ready(function () {
//avalon.vmodels.userList = null;
if (avalon.vmodels.userList) { if (avalon.vmodels.userList) {
var vm = avalon.vmodels.userList; var vm = avalon.vmodels.userList;
// initialize avalon object // initialize avalon object
userList = []; previousPage= 0; nextPage= 0; page = 1; userList = []; //previousPage= 0; nextPage= 0; page = 1;
editingUserId= 0; totalPage = 1; keyword= ""; showAdminOnly= false; //editingUserId= 0; totalPage = 1; keyword= ""; showAdminOnly= false;
//user editor fields //user editor fields
username= ""; realName= ""; email= ""; adminType= 0; id= 0; 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) { $('form').validator().on('submit', function (e) {
e.preventDefault(); e.preventDefault();
var username = $("#username").val();
var newPassword = $("#new_password ").val(); var newPassword = $("#new_password ").val();
var password = $("#password").val(); var password = $("#password").val();
$.ajax({ $.ajax({
beforeSend: csrfTokenHeader, beforeSend: csrfTokenHeader,
url: "/api/change_password/", url: "/api/change_password/",
data: {username: username, new_password: newPassword, old_password: password}, data: {new_password: newPassword, old_password: password},
dataType: "json", dataType: "json",
method: "post", method: "post",
success: function (data) { 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.set_password("666666")
self.user.save() self.user.save()
self.user2.save() self.user2.save()
# self.client.login(username="gogoing", password="666666")
self.submission = Submission.objects.create(user_id=self.user.id, self.submission = Submission.objects.create(user_id=self.user.id,
language=1, language=1,
code='#include "stdio.h"\nint main(){\n\treturn 0;\n}', code='#include "stdio.h"\nint main(){\n\treturn 0;\n}',
@@ -29,6 +28,16 @@ class SubmissionsListPageTest(TestCase):
response = self.client.get('/submissions/1/') response = self.client.get('/submissions/1/')
self.assertEqual(response.status_code, 200) 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): def test_visit_submissionsListPage_without_page_successfully(self):
self.client.login(username="gogoing", password="666666") self.client.login(username="gogoing", password="666666")
response = self.client.get('/submissions/') response = self.client.get('/submissions/')
@@ -41,7 +50,7 @@ class SubmissionsListPageTest(TestCase):
def test_submissionsListPage_page_not_exist(self): def test_submissionsListPage_page_not_exist(self):
self.client.login(username="gogoing", password="666666") 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") self.assertTemplateUsed(response, "utils/error.html")
def test_submissionsListPage_have_no_submission(self): def test_submissionsListPage_have_no_submission(self):
@@ -137,10 +146,3 @@ class ContestSubmissionAPITest(APITestCase):
data = {"language": 1} data = {"language": 1}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
pass pass

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,7 @@
<div class="form-group col-md-12"> <div class="form-group col-md-12">
<label>题目描述</label> <label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea> <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> </div>
@@ -130,7 +130,7 @@
<textarea id="hint" placeholder="这里输入内容" ms-duplex="hint"></textarea> <textarea id="hint" placeholder="这里输入内容" ms-duplex="hint"></textarea>
</div> </div>
<div class="col-md-12"> <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> </div>
</form> </form>
</div> </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"> <div class="form-group col-md-12">
<label>题目描述</label> <label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea> <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> </div>

View File

@@ -17,7 +17,7 @@
<div class="form-group col-md-12"> <div class="form-group col-md-12">
<label>题目描述</label> <label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea> <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> </div>

View File

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

View File

@@ -21,7 +21,7 @@
<td>{{ el.user }}</td> <td>{{ el.user }}</td>
<td>{{ results[el.result] }}</td> <td>{{ results[el.result] }}</td>
<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> </td>
</tr> </tr>
</table> </table>

View File

@@ -5,11 +5,6 @@
<h2 class="text-center">修改密码</h2> <h2 class="text-center">修改密码</h2>
<form id="change_password-form"> <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"> <div class="form-group">
<label for="password">旧密码</label> <label for="password">旧密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码" data-error="请填写旧密码" maxlength="30" required> <input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码" data-error="请填写旧密码" maxlength="30" required>

View File

@@ -7,17 +7,23 @@
<form id="login-form"> <form id="login-form">
<div class="form-group"> <div class="form-group">
<label for="username">用户名</label> <label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" maxlength="30" data-error="请填写用户名" placeholder="用户名" autofocus required> <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 class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password">密码</label> <label for="password">密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" maxlength="30" data-error="请填写密码" placeholder="密码" required> <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 class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary">提交</button> <button type="submit" class="btn btn-primary">提交</button>
</div> </div>
<a href="/register/">还没有帐号?点击注册</a>
</form> </form>
</div> </div>
</div> </div>

View File

@@ -7,28 +7,28 @@
<form id="register-form"> <form id="register-form">
<div class="form-group"> <div class="form-group">
<label for="username">用户名</label> <label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-error="请输入用户名" required autofocus> <input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-error="请填写用户名" required autofocus>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="real_name">真实姓名</label> <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 class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">邮箱地址</label> <label for="email">邮箱地址</label>
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-error="请填写邮箱" required> <input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-error="请填写正确的邮箱地址" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password">密码</label> <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 class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="confirm_password">确认密码</label> <label for="confirm_password">确认密码</label>
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password" <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 class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
{% if result %}
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
<tr> <tr>
@@ -49,6 +50,9 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %}
<p>还没有结果</p>
{% endif %}
</div> </div>
</div> </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"> <link rel="stylesheet" type="text/css" href="/static/css/fullpage/jquery.fullPage.css">
<style> <style>
html, textarea, input, option, select, button { 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; color: #FFF;
} }
@@ -84,21 +85,29 @@
loopBottom: true loopBottom: true
}); });
}); });
</script> </script>
</head> </head>
<body> <body>
<div id="header"> <div id="header">
<span id="name">qduoj</span> <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>
<div id="fullpage"> <div id="fullpage">
<div class="section" id="section0"> <div class="section" id="section0">
<div class="index-section-text animated bounceInUp"> <div class="index-section-text animated bounceInUp">
<h1>青岛大学在线评测平台</h1> <h1>青岛大学 Online Judge</h1>
<h3>新面貌,新的开始~</h3> <h3>面貌,新的开始~</h3>
</div> </div>
<div class="bottom-pointer">↓继续滚动~</div> <div class="bottom-pointer">↓继续滚动~</div>
</div> </div>
@@ -128,7 +137,7 @@
<div class="section-text"> <div class="section-text">
<img class="icon" id="img3" src="/static/img/index/m.png"> <img class="icon" id="img3" src="/static/img/index/m.png">
<h1>自由举办小组赛</h1> <h1>自由举办小组赛(10月上线)</h1>
<h3>内部比赛,日常作业,期末考试,通通搞定</h3> <h3>内部比赛,日常作业,期末考试,通通搞定</h3>
</div> </div>

View File

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

View File

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

View File

@@ -4,24 +4,56 @@
{% load submission %} {% load submission %}
<div class="container main"> <div class="container main">
{% if submissions %} <div class="col-md-9 col-lg-9">
<table class="table table-bordered"> <table class="table table-striped">
<thead> <thead>
<tr class="" success> <tr>
<th>#</th> <th>#</th>
<th>提交时间</th> <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>运行时间</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> </tr>
</thead> </thead>
<tbody> <tbody>
{% for item in submissions %} {% for item in submissions %}
<tr class="{{ item.result|translate_result_class }}"> <tr>
<th scope="row"><a href="/my_submission/{{ item.id }}/" id="id_{{ forloop.counter }}"> <th scope="row"><a href="/submission/{{ item.id }}/" id="id_{{ forloop.counter }}">
{{ forloop.counter |add:start_id }}</a></th> {{ forloop.counter |add:start_id }}</a></th>
<td>{{ item.create_time }}</td> <td>{{ item.create_time }}</td>
<td>{{ item.result|translate_result }}</td> <td>
{{ item.language|translate_language }}
</td>
<td> <td>
{% if item.accepted_answer_time %} {% if item.accepted_answer_time %}
{{ item.accepted_answer_time }}ms {{ item.accepted_answer_time }}ms
@@ -29,30 +61,35 @@
-- --
{% endif %} {% endif %}
</td> </td>
<td> <td class="alert-{{ item.result|translate_result_class }}">
{{ item.language|translate_language }} <strong>{{ item.result|translate_result }}</strong>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %}
<p>你还没有提交记录!</p>
{% endif %}
<nav> <nav>
<ul class="pager"> <ul class="pager">
{% if previous_page %} {% if previous_page %}
<li class="previous"><a <li class="previous"><a
href="/submissions/{{ previous_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}"> href="/submissions/{{ previous_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页</a></li> <span aria-hidden="true">&larr;</span> 上一页</a></li>
{% endif %} {% endif %}
{% if next_page %} {% if next_page %}
<li class="next"><a <li class="next"><a
href="/submissions/{{ next_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">下一页 <span href="/submissions/{{ next_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% endif %}">下一页 <span
aria-hidden="true">&rarr;</span></a></li> aria-hidden="true">&rarr;</span></a></li>
{% endif %} {% endif %}
</ul> </ul>
</nav> </nav>
{% if not submissions %}
<p>你还没有提交记录!</p>
{% endif %}
</div>
<div class="col-md-3 col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div> </div>
{% endblock %} {% endblock %}

View File

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