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

Nothing
This commit is contained in:
lsxwxs
2015-09-02 09:18:48 +08:00
64 changed files with 1352 additions and 621 deletions

View File

@@ -1,13 +1,7 @@
FROM python:2.7 FROM python:2.7
ENV PYTHONUNBUFFERED 1 ENV PYTHONBUFFERED 1
ENV oj_env daocloud RUN mkdir -p /code/log /code/test_case
RUN mkdir /var/oj WORKDIR /code
COPY . /var/oj/ ADD requirements.txt /code/
WORKDIR /var/oj/
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
EXPOSE 8080 EXPOSE 8010
RUN mkdir LOG
RUN mkdir test_case
RUN mkdir tmp
RUN python manage.py migrate
CMD python manage.py runserver 0.0.0.0:8080

View File

@@ -369,3 +369,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
@@ -35,6 +36,10 @@ class UserLoginAPIView(APIView):
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
def logout(request):
auth.logout(request)
return http.HttpResponseRedirect("/")
class UserRegisterAPIView(APIView): class UserRegisterAPIView(APIView):
def post(self, request): def post(self, request):
""" """

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})
@@ -112,4 +114,38 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
return render(request, "oj/contest/submissions_list.html", return render(request, "oj/contest/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,
"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

@@ -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):
@@ -26,7 +29,7 @@ class GroupAPIViewBase(object):
else: else:
group = Group.objects.get(id=group_id, visible=True, admin=request.user) group = Group.objects.get(id=group_id, visible=True, admin=request.user)
return group return group
def get_groups(self, request): def get_groups(self, request):
""" """
如果是超级管理员,就返回全部的小组 如果是超级管理员,就返回全部的小组
@@ -113,8 +116,8 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase):
elif request.GET.get("admin_id", None): elif request.GET.get("admin_id", None):
groups = groups.filter(admin__id=request.GET["admin_id"]) groups = groups.filter(admin__id=request.GET["admin_id"])
return paginate(request, groups, GroupSerializer) return paginate(request, groups, GroupSerializer)
class GroupMemberAdminAPIView(APIView, GroupAPIViewBase): class GroupMemberAdminAPIView(APIView, GroupAPIViewBase):
def get(self, request): def get(self, request):
""" """
@@ -129,9 +132,9 @@ class GroupMemberAdminAPIView(APIView, GroupAPIViewBase):
group = self.get_group(request, group_id) group = self.get_group(request, group_id)
except Group.DoesNotExist: except Group.DoesNotExist:
return error_response(u"小组不存在") return error_response(u"小组不存在")
return paginate(request, UserGroupRelation.objects.filter(group=group), GroupMemberSerializer) return paginate(request, UserGroupRelation.objects.filter(group=group), GroupMemberSerializer)
def put(self, request): def put(self, request):
""" """
删除小组成员的api接口 删除小组成员的api接口
@@ -190,7 +193,7 @@ class JoinGroupAPIView(APIView):
return error_response(u"该小组不允许任何人加入") return error_response(u"该小组不允许任何人加入")
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
def get(self, request): def get(self, request):
""" """
搜索小组的api需要传递keyword参数 搜索小组的api需要传递keyword参数
@@ -233,9 +236,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 +248,68 @@ 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})

19
judge/Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
FROM ubuntu:14.04
MAINTAINER virusdefender<qduliyang@outlook.com>
RUN mkdir /var/install/
WORKDIR /var/install/
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install software-properties-common python-software-properties
RUN add-apt-repository -y ppa:webupd8team/java
RUN apt-get update
RUN apt-get -y install python gcc g++ rake pkg-config git make autoconf automake libtool python-pip python2.7-mysqldb
RUN echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
RUN echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections
RUN apt-get install -y oracle-java7-installer
RUN apt-get -y install libseccomp-dev
RUN git clone https://github.com/quark-zju/lrun.git
RUN cd lrun && make install
RUN mkdir -p /var/judger/run/ && mkdir /var/judger/test_case/ && mkdir /var/judger/code/
RUN chmod -R 777 /var/judger/run/
WORKDIR /var/judger/code/

View File

@@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
# 这个redis 是 celery 使用的,包括存储队列信息还有部分统计信息
redis_config = { redis_config = {
"host": "121.42.32.129", "host": "121.42.32.129",
"port": 6379, "port": 6379,
@@ -6,17 +7,21 @@ redis_config = {
} }
# 判题的 docker 容器的配置参数
docker_config = { docker_config = {
"image_name": " a7673b55d263", "image_name": "3da0e526934e",
"docker_path": "docker", "docker_path": "docker",
"shell": True "shell": True
} }
test_case_dir = "/root/test_case/" # 测试用例的路径,是主机上的实际路径
source_code_dir = "/root/" test_case_dir = "/var/mnt/source/test_case/"
# 源代码路径,也就是 manage.py 所在的实际路径
source_code_dir = "/var/mnt/source/OnlineJudge/"
# 存储提交信息的数据库,是 celery 使用的,与 oj.settings/local_settings 等区分,那是 web 服务器访问的地址
submission_db = { submission_db = {
"host": "127.0.0.1", "host": "127.0.0.1",
"port": 3306, "port": 3306,

View File

@@ -1,19 +0,0 @@
# coding=utf-8
import os
LOG_PATH = "LOG/"
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'CONN_MAX_AGE': 1,
}
}
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

View File

@@ -5,22 +5,23 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 下面是需要自己修改的 # 下面是需要自己修改的
LOG_PATH = "LOG/" LOG_PATH = "log/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上 # 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'CONN_MAX_AGE': 0.3,
}, },
# submission 的 name 和 engine 请勿修改,其他代码会用到
'submission': { 'submission': {
'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',
'CONN_MAX_AGE': 0.1,
} }
} }
@@ -29,5 +30,4 @@ 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.1,
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'mypwd'
},
'submission': {
'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 0.1,
'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
@@ -115,10 +113,6 @@ STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static/src/"),) STATICFILES_DIRS = (os.path.join(BASE_DIR, "static/src/"),)
TEMPLATE_DIRS = (
os.path.join(BASE_DIR, "template/src"),
)
AUTH_USER_MODEL = 'account.User' AUTH_USER_MODEL = 'account.User'
LOGGING = { LOGGING = {
@@ -170,4 +164,6 @@ 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

@@ -26,8 +26,8 @@ class CreateProblemSerializer(serializers.Serializer):
samples = ProblemSampleSerializer() samples = ProblemSampleSerializer()
test_case_id = serializers.CharField(max_length=40) test_case_id = serializers.CharField(max_length=40)
source = serializers.CharField(max_length=30, required=False, default=None) source = serializers.CharField(max_length=30, required=False, default=None)
time_limit = serializers.IntegerField() time_limit = serializers.IntegerField(min_value=1)
memory_limit = serializers.IntegerField() memory_limit = serializers.IntegerField(min_value=1)
difficulty = serializers.IntegerField() difficulty = serializers.IntegerField()
tags = serializers.ListField(child=serializers.CharField(max_length=10)) tags = serializers.ListField(child=serializers.CharField(max_length=10))
hint = serializers.CharField(max_length=3000, allow_blank=True) hint = serializers.CharField(max_length=3000, allow_blank=True)
@@ -61,8 +61,8 @@ class EditProblemSerializer(serializers.Serializer):
output_description = serializers.CharField(max_length=10000) output_description = serializers.CharField(max_length=10000)
test_case_id = serializers.CharField(max_length=40) test_case_id = serializers.CharField(max_length=40)
source = serializers.CharField(max_length=30) source = serializers.CharField(max_length=30)
time_limit = serializers.IntegerField() time_limit = serializers.IntegerField(min_value=1)
memory_limit = serializers.IntegerField() memory_limit = serializers.IntegerField(min_value=1)
difficulty = serializers.IntegerField() difficulty = serializers.IntegerField()
tags = serializers.ListField(child=serializers.CharField(max_length=20)) tags = serializers.ListField(child=serializers.CharField(max_length=20))
samples = ProblemSampleSerializer() samples = ProblemSampleSerializer()

View File

@@ -222,7 +222,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

@@ -7,4 +7,5 @@ django-rest-swagger
celery celery
gunicorn gunicorn
coverage coverage
django-extensions django-extensions
supervisor

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

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

View File

@@ -131,7 +131,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,51 +1,57 @@
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;
var vm = avalon.define({ if (avalon.vmodels.group) {
$id: "group", var vm = avalon.vmodels.group;
//通用变量 }
groupList: [], // 用户列表数据项 else {
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
totalPage: 1, // 总页数
keyword: "",
getNext: function () { var vm = avalon.define({
if (!vm.nextPage) $id: "group",
return; //通用变量
getPageData(vm.page + 1); groupList: [], // 用户列表数据项
}, previousPage: 0, // 之前的页数
getPrevious: function () { nextPage: 0, // 之后的页数
if (!vm.previousPage) page: 1, // 当前页数
return; totalPage: 1, // 总页数
getPageData(vm.page - 1); keyword: "",
},
getBtnClass: function (btnType) { getNext: function () {
if (btnType == "next") { if (!vm.nextPage)
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; return;
} getPageData(vm.page + 1);
else { },
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; getPrevious: function () {
} if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btnType) {
if (btnType == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
search: function(){
getPageData(1);
},
getGroupSettingString: function (setting) {
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
},
showGroupDetailPage: function (groupId) {
vm.$fire("up!showGroupDetailPage", groupId);
}
});
}
},
search: function(){
getPageData(1);
},
getGroupSettingString: function (setting) {
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
},
showGroupDetailPage: function (groupId) {
vm.$fire("up!showGroupDetailPage", groupId);
}
});
getPageData(1); 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,55 +3,60 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
// avalon:定义模式 group_list // avalon:定义模式 group_list
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.groupDetail = null;
var vm = avalon.define({
$id: "groupDetail",
//通用变量
memberList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
name: "",
description: "",
checkedSetting: "0",
getNext: function () { if (avalon.vmodels.groupDetail) {
if (!vm.nextPage) var vm = avalon.vmodels.groupDetail;
return; }
getPageData(vm.page + 1); else {
}, var vm = avalon.define({
getPrevious: function () { $id: "groupDetail",
if (!vm.previousPage) //通用变量
return; memberList: [],
getPageData(vm.page - 1); previousPage: 0,
}, nextPage: 0,
getBtnClass: function (btn) { page: 1,
if (btn == "next") { totalPage: 1,
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; name: "",
} description: "",
else { checkedSetting: "0",
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
removeMember: function (relation) { getNext: function () {
$.ajax({ if (!vm.nextPage)
beforeSend: csrfTokenHeader, return;
url: "/api/admin/group_member/", getPageData(vm.page + 1);
method: "put", },
data: JSON.stringify({group_id: relation.group, members: [relation.user.id]}), getPrevious: function () {
contentType: "application/json", if (!vm.previousPage)
success: function (data) { return;
vm.memberList.remove(relation); getPageData(vm.page - 1);
bsAlert(data.data); },
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";
showGroupListPage: function () { }
vm.$fire("up!showGroupListPage"); },
}
}); removeMember: function (relation) {
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/group_member/",
method: "put",
data: JSON.stringify({group_id: relation.group, members: [relation.user.id]}),
contentType: "application/json",
success: function (data) {
vm.memberList.remove(relation);
bsAlert(data.data);
}
})
},
showGroupListPage: function () {
vm.$fire("up!showGroupListPage");
}
});
}
avalon.scan(); avalon.scan();
getPageData(1); getPageData(1);

View File

@@ -1,51 +1,57 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "formValidation"], function ($, avalon, csrfTokenHeader, bsAlert) { require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
// avalon:定义模式 group_list // avalon:定义模式 group_list
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.requestList = null;
var vm = avalon.define({
$id: "requestList",
//通用变量
requestList: [], // 列表数据项
previousPage: 0, // 之前的页数
nextPage: 0, // 之后的页数
page: 1, // 当前页数
totalPage: 1, // 总页数
getNext: function () { if (avalon.vmodels.requestList) {
if (!vm.nextPage) var vm = avalon.vmodels.requestList;
return; }
getPageData(vm.page + 1); else {
},
getPrevious: function () { var vm = avalon.define({
if (!vm.previousPage) $id: "requestList",
return; //通用变量
getPageData(vm.page - 1); requestList: [], // 列表数据项
}, previousPage: 0, // 之前的页数
getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 nextPage: 0, // 之后的页数
if (btn == "next") { page: 1, // 当前页数
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; totalPage: 1, // 总页数
}
else { getNext: function () {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; if (!vm.nextPage)
} return;
}, getPageData(vm.page + 1);
getPage: function (page_index) { },
getPageData(page_index); getPrevious: function () {
}, if (!vm.previousPage)
processRequest: function(request, status){ return;
$.ajax({ getPageData(vm.page - 1);
beforeSend: csrfTokenHeader, },
url: "/api/admin/join_group_request/", getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑
method: "put", if (btn == "next") {
data: {request_id: request.id, status: status}, return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
success: function(data){
vm.requestList.remove(request);
bsAlert(data.data);
} }
}) else {
} return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}); }
},
getPage: function (page_index) {
getPageData(page_index);
},
processRequest: function(request, status){
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/join_group_request/",
method: "put",
data: {request_id: request.id, status: status},
success: function(data){
vm.requestList.remove(request);
bsAlert(data.data);
}
})
}
});
}
avalon.scan(); 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

@@ -13,10 +13,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
bsAlert("题目描述不能为空!"); bsAlert("题目描述不能为空!");
return false; return false;
} }
if (vm.timeLimit < 1000 || vm.timeLimit > 5000) {
bsAlert("保证时间限制是一个1000-5000的合法整数");
return false;
}
if (vm.samples.length == 0) { if (vm.samples.length == 0) {
bsAlert("请至少添加一组样例!"); bsAlert("请至少添加一组样例!");
return false; return false;

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

@@ -0,0 +1,25 @@
require(["jquery", "csrfToken", "bsAlert"], function ($, csrfTokenHeader, bsAlert) {
$("#sendApplication").click(function (){
var message = $("#applyMessage").val();
console.log(message);
var groupId = window.location.pathname.split("/")[2];
console.log(groupId);
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

@@ -48,7 +48,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
if (!data.result) { if (!data.result) {
html += "CPU time: " + data.accepted_answer_time + "ms &nbsp;&nbsp;"; html += "CPU time: " + data.accepted_answer_time + "ms &nbsp;&nbsp;";
} }
html += ('<a href="/my_submission/' + submissionId + '/" target="_blank">查看详情</a></div> </div>'); html += ('<a href="/submission/' + submissionId + '/" target="_blank">查看详情</a></div> </div>');
return html; return html;
} }

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

@@ -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

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

View File

@@ -5,8 +5,13 @@
公告 公告
</h3></div> </h3></div>
<div class="panel-body"> <div class="panel-body">
{% for item in announcements %} {% if announcements %}
<p>{{ forloop.counter }}.&nbsp;&nbsp;<a href="/announcement/{{ item.id }}/" target="_blank">{{ item.title }}</a></p> {% for item in announcements %}
{% endfor %} <p>{{ forloop.counter }}.&nbsp;&nbsp;<a href="/announcement/{{ item.id }}/" target="_blank">{{ item.title }}</a>
</p>
{% 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>当前没有合适的比赛</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,40 @@
{% 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>
<li role="presentation"><a href="/group/{{ group.id }}/applications/">我的申请</a></li>
</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">申请加入</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="/about/">关于</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

@@ -23,7 +23,7 @@
<div class="problem-section"> <div class="problem-section">
<label class="problem-label">输出</label> <label class="problem-label">输出</label>
<p class="problem-detail">{{ problem.output_description }}k</p> <p class="problem-detail">{{ problem.output_description }}</p>
</div> </div>
{% for item in samples %} {% for item in samples %}
<div class="problem-section"> <div class="problem-section">

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

@@ -2,57 +2,94 @@
{% block body %} {% block body %}
{% 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>
<th>运行时间</th> <div class="dropdown">
<th>语言</th> <a href="#" class="dropdown-toggle" id="languageFilter" data-toggle="dropdown"
</tr> aria-haspopup="true" aria-expanded="true">
</thead> 语言<span class="caret"></span>
<tbody> </a>
{% for item in submissions %} <ul class="dropdown-menu" aria-labelledby="languageFilter">
<tr class="{{ item.result|translate_result_class }}"> <li><a href="/submissions/?language=1">C</a></li>
<th scope="row"><a href="/my_submission/{{ item.id }}/" id="id_{{ forloop.counter }}"> <li><a href="/submissions/?language=2">C++</a></li>
{{ forloop.counter |add:start_id }}</a></th> <li><a href="/submissions/?language=3">Java</a></li>
<td>{{ item.create_time }}</td> <li><a href="/submissions/">取消筛选</a></li>
<td>{{ item.result|translate_result }}</td> </ul>
<td> </div>
{% if item.accepted_answer_time %} </th>
{{ item.accepted_answer_time }}ms <th>运行时间</th>
{% else %} <th>
-- <div class="dropdown">
{% endif %} <a href="#" class="dropdown-toggle" id="resultFilter" data-toggle="dropdown"
</td> aria-haspopup="true" aria-expanded="true">
<td> 结果<span class="caret"></span>
{{ item.language|translate_language }} </a>
</td> <ul class="dropdown-menu" aria-labelledby="resultFilter">
</tr> <li><a href="/submissions/?result=0">Accepted</a></li>
{% endfor %} <li><a href="/submissions/?result=6">Wrong Answer</a></li>
<li><a href="/submissions/?result=1">Runtime Error</a></li>
<li><a href="/submissions/?result=2">Time Limit Exceeded</a></li>
<li><a href="/submissions/?result=3">Memory Limit Exceeded</a></li>
<li><a href="/submissions/?result=4">Compile Error</a></li>
<li><a href="/submissions/?result=5">Format Error</a></li>
<li><a href="/submissions/">取消筛选</a></li>
</ul>
</div>
</th>
</tr>
</thead>
<tbody>
{% for item in submissions %}
<tr>
<th scope="row"><a href="/submission/{{ item.id }}/" id="id_{{ forloop.counter }}">
{{ forloop.counter |add:start_id }}</a></th>
<td>{{ item.create_time }}</td>
<td>
{{ item.language|translate_language }}
</td>
<td>
{% if item.accepted_answer_time %}
{{ item.accepted_answer_time }}ms
{% else %}
--
{% endif %}
</td>
<td class="alert-{{ item.result|translate_result_class }}">
<strong>{{ item.result|translate_result }}</strong>
</td>
</tbody> </tr>
</table> {% endfor %}
{% else %}
<p>你还没有提交记录!</p> </tbody>
{% endif %} </table>
<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>
<div class="col-md-3 col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div>
{% endblock %} {% endblock %}

View File

@@ -45,31 +45,32 @@
<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="/groups/">小组</a></li>
<li><a href="/about/">关于</a></li> <li><a href="/about/">关于</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="/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>
{% 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>
</ul> </ul>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@@ -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 -->

View File

@@ -4,7 +4,7 @@
def get_problem_accepted_radio(problem): def get_problem_accepted_radio(problem):
if problem.total_submit_number: if problem.total_submit_number:
return str(int((problem.total_accepted_number * 100) / problem.total_submit_number)) \ return str(int((problem.total_accepted_number * 100) / problem.total_submit_number)) \
+ "% (" + str(problem.total_accepted_number) + "/" + str(problem.total_submit_number) + ")" + "% (" + str(problem.total_accepted_number) + " / " + str(problem.total_submit_number) + ")"
return "0%" return "0%"