Conflicts:
	contest/views.py
	template/src/oj/contest/contest_rank.html
	template/src/oj/contest/submissions_list.html
This commit is contained in:
sxw@401
2015-09-15 13:18:36 +08:00
48 changed files with 1117 additions and 2892 deletions

2
.gitignore vendored
View File

@@ -62,3 +62,5 @@ static/src/upload_image/*
build.txt build.txt
tmp/ tmp/
test_case/ test_case/
release/
upload/

View File

@@ -1 +0,0 @@
__author__ = 'root'

View File

@@ -1,74 +0,0 @@
import django
from contest.models import *
from problem.models import *
from submission.models import Submission
import redis
from judge.judger_controller.tasks import judge
from judge.judger_controller.settings import redis_config
django.setup()
def rejudge(submission):
# for submission in submission:
# submission_id = submission.id
# try:
# command = "%s run -t -i --privileged --rm=true " \
# "-v %s:/var/judger/test_case/ " \
# "-v %s:/var/judger/code/ " \
# "%s " \
# "python judge/judger/run.py " \
# "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % \
# (docker_config["docker_path"],
# test_case_dir,
# source_code_dir,
# docker_config["image_name"],
# submission_id, str(time_limit), str(memory_limit), test_case_id)
# subprocess.call(command, shell=docker_config["shell"])
# except Exception as e:
# print e
return
def easy_rejudge(submissions, map_table, user_id, contest_id=None):
try:
user = User.objects.get(pk=user_id)
except User.DoesNotExist:
print "User.DoesNotExist!"
return
problemDict = {}
for oldSubmission in submission:
problem_id = map_table[oldSubmission.problem_id]
if problem_id in problemDict:
problem = problemDict[problem_id]
else:
try:
p = Problem.objects.get(pk=problem_id)
except Problem.DoesNotExist:
print " Problem.DoesNotExist!" + str(problem_id)
continue
problem = p
problemDict[problem_id] = p
submission = Submission.objects.create(
user_id=user_id,
language=oldSubmission.language,
code=oldSubmission.code,
contest_id=contest_id,
problem_id=problem_id,
originResult=oldSubmission.result
)
try:
judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception:
print "error!"
continue
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
r.incr("judge_queue_length")
return

View File

@@ -1,61 +0,0 @@
import django
from contest.models import *
from problem.models import *
django.setup()
def add_exist_problem_to_contest(problems, contest_id):
try:
contest = Contest.objects.get(pk=contest_id)
except Contest.DoesNotExist:
print "Contest Doesn't Exist!"
return
i = 1
for problem in problems:
print "Add the problem:"
print problem.title
print "The sort Index is" + str(i) + " You Can modify it latter as you like~"
ContestProblem.objects.create(contest=contest, sort_index=str(i),
title=problem.title, description=problem.description,
input_description=problem.input_description,
output_description=problem.output_description,
samples=problem.samples,
test_case_id=problem.test_case_id,
hint=problem.hint,
created_by=problem.created_by,
time_limit=problem.time_limit,
memory_limit=problem.memory_limit)
i += 1
return
def add_contest_problem_to_problem(contest_id):
try:
contest = Contest.objects.get(pk=contest_id)
except Contest.DoesNotExist:
print "Contest Doesn't Exist!"
return
#Get all problems in this contest
problems = ContestProblem.objects.filter(contest=contest)
#get a tag
try:
tag = ProblemTag.objects.get(name=contest.title)
except ProblemTag.DoesNotExist:
tag = ProblemTag.objects.create(name=contest.title)
#for each problem
for problem in problems:
print "Add problem to problem list:"
print problem.title
p = Problem.objects.create(title=problem.title,
description=problem.description,
input_description=problem.input_description,
output_description=problem.output_description,
samples=problem.samples,
test_case_id=problem.test_case_id,
hint=problem.hint,
created_by=problem.created_by,
time_limit=problem.time_limit,
memory_limit=problem.memory_limit,
visible = False,
difficulty = 0,
source = contest.title)
p.tags.add(tag)
return

View File

@@ -1,6 +1,6 @@
FROM python:2.7 FROM python:2.7
ENV PYTHONBUFFERED 1 ENV PYTHONBUFFERED 1
RUN mkdir -p /code/log /code/test_case RUN mkdir -p /code/log /code/test_case /code/upload
WORKDIR /code WORKDIR /code
ADD requirements.txt /code/ ADD requirements.txt /code/
RUN pip install -r requirements.txt RUN pip install -r requirements.txt

View File

@@ -36,11 +36,24 @@ class UserLoginAPIView(APIView):
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
@login_required @login_required
def logout(request): def logout(request):
auth.logout(request) auth.logout(request)
return http.HttpResponseRedirect("/") return http.HttpResponseRedirect("/")
def page_jump(request):
if not request.user.is_authenticated():
return render(request, "oj/index.html")
try:
if request.META['HTTP_REFERER']:
return render(request, "oj/index.html")
except KeyError:
return http.HttpResponseRedirect('/problems/')
class UserRegisterAPIView(APIView): class UserRegisterAPIView(APIView):
def post(self, request): def post(self, request):
""" """

View File

@@ -16,8 +16,21 @@ def announcement_page(request, announcement_id):
try: try:
announcement = Announcement.objects.get(id=announcement_id, visible=True) announcement = Announcement.objects.get(id=announcement_id, visible=True)
except Announcement.DoesNotExist: except Announcement.DoesNotExist:
return error_page(request, u"模板不存在") return error_page(request, u"公告不存在")
# 公开的公告
if announcement.is_global == 0:
return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) return render(request, "oj/announcement/announcement.html", {"announcement": announcement})
else:
if not request.user.is_authenticated():
return error_page(request, u"公告不存在")
# 判断是不是在组里面
if request.user.admin_type == SUPER_ADMIN or request.user == announcement.created_by:
return render(request, "oj/announcement/announcement.html", {"announcement": announcement})
else:
if request.user.groups.filter(id__in=[item.id for item in announcement.groups.all()]).exists():
return render(request, "oj/announcement/announcement.html", {"announcement": announcement})
else:
return error_page(request, u"公告不存在")
class AnnouncementAdminAPIView(APIView): class AnnouncementAdminAPIView(APIView):

View File

@@ -6,10 +6,9 @@ from account.models import User
from problem.models import AbstractProblem from problem.models import AbstractProblem
from group.models import Group from group.models import Group
GROUP_CONTEST = 0 GROUP_CONTEST = 0
PUBLIC_CONTEST = 1 PUBLIC_CONTEST = 1
PASSWORD_PUBLIC_CONTEST = 2 PASSWORD_PROTECTED_CONTEST = 2
class Contest(models.Model): class Contest(models.Model):

View File

@@ -1,19 +1,16 @@
# coding=utf-8 # coding=utf-8
import json import json
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import TestCase, Client from django.test import TestCase, Client
from django.http import HttpResponse
from rest_framework.test import APITestCase, APIClient from rest_framework.test import APITestCase, APIClient
from account.models import User from account.models import User
from group.models import Group from group.models import Group
from contest.models import Contest, ContestProblem from contest.models import Contest, ContestProblem
from .models import ContestSubmission from .models import ContestSubmission
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PUBLIC_CONTEST from .models import GROUP_CONTEST, PASSWORD_PROTECTED_CONTEST
from announcement.models import Announcement
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
from decorators import check_user_contest_permission
class ContestAdminAPITest(APITestCase): class ContestAdminAPITest(APITestCase):
@@ -36,13 +33,13 @@ class ContestAdminAPITest(APITestCase):
join_group_setting=0, visible=True, join_group_setting=0, visible=True,
admin=user1) admin=user1)
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
password="aacc", created_by=User.objects.get(username="test1")) password="aacc", created_by=User.objects.get(username="test1"))
self.group_contest = Contest.objects.create(title="titley", description="descriptiony", mode=1, self.group_contest = Contest.objects.create(title="titley", description="descriptiony", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
@@ -57,7 +54,7 @@ class ContestAdminAPITest(APITestCase):
def test_global_contest_does_not_has_privileges(self): def test_global_contest_does_not_has_privileges(self):
self.client.login(username="test2", password="testbb") self.client.login(username="test2", password="testbb")
data = {"title": "title0", "description": "description0", "mode": 1, "contest_type": PASSWORD_PUBLIC_CONTEST, data = {"title": "title0", "description": "description0", "mode": 1, "contest_type": PASSWORD_PROTECTED_CONTEST,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True} "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
@@ -65,7 +62,7 @@ class ContestAdminAPITest(APITestCase):
def test_global_contest_password_exists(self): def test_global_contest_password_exists(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"title": "title0", "description": "description0", "mode": 1, "contest_type": PASSWORD_PUBLIC_CONTEST, data = {"title": "title0", "description": "description0", "mode": 1, "contest_type": PASSWORD_PROTECTED_CONTEST,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "visible": True} "end_time": "2015-08-15T12:00:00.000Z", "visible": True}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
@@ -81,7 +78,7 @@ class ContestAdminAPITest(APITestCase):
def test_global_contest_successfully(self): def test_global_contest_successfully(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"title": "title1", "description": "description1", "mode": 1, "contest_type": PASSWORD_PUBLIC_CONTEST, data = {"title": "title1", "description": "description1", "mode": 1, "contest_type": PASSWORD_PROTECTED_CONTEST,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True} "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
@@ -105,7 +102,7 @@ class ContestAdminAPITest(APITestCase):
def test_time_error(self): def test_time_error(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"title": "title2", "description": "description2", "mode": 1, "contest_type": PASSWORD_PUBLIC_CONTEST, data = {"title": "title2", "description": "description2", "mode": 1, "contest_type": PASSWORD_PROTECTED_CONTEST,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T12:00:00.000Z", "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T12:00:00.000Z",
"end_time": "2015-08-15T10:00:00.000Z", "password": "aabb", "visible": True} "end_time": "2015-08-15T10:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
@@ -113,7 +110,7 @@ class ContestAdminAPITest(APITestCase):
def test_contest_has_exists(self): def test_contest_has_exists(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"title": "titlex", "description": "descriptionx", "mode": 1, "contest_type": PASSWORD_PUBLIC_CONTEST, data = {"title": "titlex", "description": "descriptionx", "mode": 1, "contest_type": PASSWORD_PROTECTED_CONTEST,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True} "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
@@ -129,7 +126,7 @@ class ContestAdminAPITest(APITestCase):
def test_contest_does_not_exist(self): def test_contest_does_not_exist(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id + 10, "title": "title2", "description": "description2", "mode": 1, data = {"id": self.global_contest.id + 10, "title": "title2", "description": "description2", "mode": 1,
"contest_type": PASSWORD_PUBLIC_CONTEST, "show_rank": True, "show_user_submission": True, "contest_type": PASSWORD_PROTECTED_CONTEST, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb",
"visible": True} "visible": True}
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
@@ -138,7 +135,7 @@ class ContestAdminAPITest(APITestCase):
def test_edit_global_contest_successfully(self): def test_edit_global_contest_successfully(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "titlez", "description": "descriptionz", "mode": 1, data = {"id": self.global_contest.id, "title": "titlez", "description": "descriptionz", "mode": 1,
"contest_type": PASSWORD_PUBLIC_CONTEST, "show_rank": True, "show_user_submission": True, "contest_type": PASSWORD_PROTECTED_CONTEST, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z", "password": "aabb", "start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z", "password": "aabb",
"visible": True} "visible": True}
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
@@ -178,7 +175,7 @@ class ContestAdminAPITest(APITestCase):
def test_edit_contest_has_exists(self): def test_edit_contest_has_exists(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "titley", "description": "descriptiony", "mode": 1, data = {"id": self.global_contest.id, "title": "titley", "description": "descriptiony", "mode": 1,
"contest_type": PASSWORD_PUBLIC_CONTEST, "show_rank": True, "show_user_submission": True, "contest_type": PASSWORD_PROTECTED_CONTEST, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb",
"visible": True} "visible": True}
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
@@ -187,7 +184,7 @@ class ContestAdminAPITest(APITestCase):
def test_edit_global_contest_does_not_has_privileges(self): def test_edit_global_contest_does_not_has_privileges(self):
self.client.login(username="test2", password="testbb") self.client.login(username="test2", password="testbb")
data = {"id": self.global_contest.id, "title": "titlexxxxxxxxx", "description": "descriptionxxxxxx", "mode": 1, data = {"id": self.global_contest.id, "title": "titlexxxxxxxxx", "description": "descriptionxxxxxx", "mode": 1,
"contest_type": PASSWORD_PUBLIC_CONTEST, "show_rank": True, "show_user_submission": True, "contest_type": PASSWORD_PROTECTED_CONTEST, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb",
"visible": True} "visible": True}
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
@@ -196,7 +193,7 @@ class ContestAdminAPITest(APITestCase):
def test_edit_global_contest_password_exists(self): def test_edit_global_contest_password_exists(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "title0", "description": "description0", "mode": 1, data = {"id": self.global_contest.id, "title": "title0", "description": "description0", "mode": 1,
"contest_type": PASSWORD_PUBLIC_CONTEST, "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "contest_type": PASSWORD_PROTECTED_CONTEST, "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "visible": True} "end_time": "2015-08-15T12:00:00.000Z", "visible": True}
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"此比赛为有密码的公开赛,密码不可为空"})
@@ -204,7 +201,7 @@ class ContestAdminAPITest(APITestCase):
def test_edit_time_error(self): def test_edit_time_error(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "titleaaaa", "description": "descriptionaaaaa", "mode": 1, data = {"id": self.global_contest.id, "title": "titleaaaa", "description": "descriptionaaaaa", "mode": 1,
"contest_type": PASSWORD_PUBLIC_CONTEST, "show_rank": True, "show_user_submission": True, "contest_type": PASSWORD_PROTECTED_CONTEST, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T12:00:00.000Z", "end_time": "2015-08-15T10:00:00.000Z", "password": "aabb", "start_time": "2015-08-15T12:00:00.000Z", "end_time": "2015-08-15T10:00:00.000Z", "password": "aabb",
"visible": True} "visible": True}
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
@@ -247,7 +244,7 @@ class ContestProblemAdminAPItEST(APITestCase):
self.user3.save() self.user3.save()
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
@@ -417,7 +414,7 @@ class ContestPasswordVerifyAPITest(APITestCase):
self.user2.save() self.user2.save()
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
@@ -457,7 +454,7 @@ class ContestPageTest(TestCase):
self.user1.save() self.user1.save()
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
@@ -481,7 +478,7 @@ class ContestProblemPageTest(TestCase):
self.user1.save() self.user1.save()
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
@@ -525,7 +522,7 @@ class ContestProblemListPageTest(TestCase):
self.user1.save() self.user1.save()
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
@@ -562,7 +559,7 @@ class ContestListPageTest(TestCase):
self.url = reverse('contest_list_page') self.url = reverse('contest_list_page')
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",

View File

@@ -1,26 +1,25 @@
# coding=utf-8 # coding=utf-8
import json import json
import datetime import datetime
from django.shortcuts import render from django.shortcuts import render
from django.db import IntegrityError from django.db import IntegrityError
from django.utils import dateparse from django.utils import dateparse
from django.db.models import Q, Count, Sum from django.db.models import Q, Sum
from django.core.paginator import Paginator from django.core.paginator import Paginator
from rest_framework.views import APIView from rest_framework.views import APIView
from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, rand_str, error_page)
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, error_page)
from account.models import SUPER_ADMIN, User
from account.decorators import login_required from account.decorators import login_required
from group.models import Group from group.models import Group
from announcement.models import Announcement
from .models import Contest, ContestProblem, ContestSubmission from .models import Contest, ContestProblem, ContestSubmission
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PUBLIC_CONTEST from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST
from .decorators import check_user_contest_permission from .decorators import check_user_contest_permission
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
CreateContestProblemSerializer, ContestProblemSerializer, CreateContestProblemSerializer, ContestProblemSerializer,
EditContestProblemSerializer, ContestPasswordVerifySerializer, ContestPasswordVerifySerializer,
EditContestProblemSerializer) EditContestProblemSerializer)
from oj.settings import REDIS_CACHE from oj.settings import REDIS_CACHE
import redis import redis
@@ -41,10 +40,10 @@ class ContestAdminAPIView(APIView):
# 首先判断比赛的类型: 0 即为是小组赛(GROUP_CONTEST)1 即为是无密码的公开赛(PUBLIC_CONTEST) # 首先判断比赛的类型: 0 即为是小组赛(GROUP_CONTEST)1 即为是无密码的公开赛(PUBLIC_CONTEST)
# 2 即为是有密码的公开赛(PASSWORD_PUBLIC_CONTEST) # 2 即为是有密码的公开赛(PASSWORD_PUBLIC_CONTEST)
# 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛 # 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛
if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PUBLIC_CONTEST]: if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]:
if request.user.admin_type != SUPER_ADMIN: if request.user.admin_type != SUPER_ADMIN:
return error_response(u"只有超级管理员才可创建公开赛") return error_response(u"只有超级管理员才可创建公开赛")
if data["contest_type"] == PASSWORD_PUBLIC_CONTEST: if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
if not data["password"]: if not data["password"]:
return error_response(u"此比赛为有密码的公开赛,密码不可为空") return error_response(u"此比赛为有密码的公开赛,密码不可为空")
@@ -94,10 +93,10 @@ class ContestAdminAPIView(APIView):
return error_response(u"该比赛名称已经存在") return error_response(u"该比赛名称已经存在")
except Contest.DoesNotExist: except Contest.DoesNotExist:
pass pass
if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PUBLIC_CONTEST]: if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]:
if request.user.admin_type != SUPER_ADMIN: if request.user.admin_type != SUPER_ADMIN:
return error_response(u"只有超级管理员才可创建公开赛") return error_response(u"只有超级管理员才可创建公开赛")
if data["contest_type"] == PASSWORD_PUBLIC_CONTEST: if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
if not data["password"]: if not data["password"]:
return error_response(u"此比赛为有密码的公开赛,密码不可为空") return error_response(u"此比赛为有密码的公开赛,密码不可为空")
elif data["contest_type"] == GROUP_CONTEST: elif data["contest_type"] == GROUP_CONTEST:
@@ -258,7 +257,7 @@ class ContestPasswordVerifyAPIView(APIView):
if serializer.is_valid(): if serializer.is_valid():
data = request.data data = request.data
try: try:
contest = Contest.objects.get(id=data["contest_id"], contest_type=PASSWORD_PUBLIC_CONTEST) contest = Contest.objects.get(id=data["contest_id"], contest_type=PASSWORD_PROTECTED_CONTEST)
except Contest.DoesNotExist: except Contest.DoesNotExist:
return error_response(u"比赛不存在") return error_response(u"比赛不存在")

View File

@@ -1,14 +1,15 @@
# coding=utf-8 # coding=utf-8
import json import json
from django.test import TestCase, Client from django.test import TestCase, Client
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from account.models import User, REGULAR_USER, ADMIN, SUPER_ADMIN
from problem.models import Problem
from contest.models import Contest, ContestProblem
from contest.models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PUBLIC_CONTEST
from submission.models import Submission
from rest_framework.test import APITestCase, APIClient from rest_framework.test import APITestCase, APIClient
from account.models import User, REGULAR_USER, ADMIN, SUPER_ADMIN
from contest.models import Contest, ContestProblem
from contest.models import PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST
from submission.models import Submission
class ContestSubmissionAPITest(APITestCase): class ContestSubmissionAPITest(APITestCase):
def setUp(self): def setUp(self):
@@ -107,7 +108,7 @@ class SubmissionAPITest(APITestCase):
self.userS.set_password("testbb") self.userS.set_password("testbb")
self.userS.save() self.userS.save()
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True,
show_user_submission=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",

View File

@@ -120,12 +120,13 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
except Exception: except Exception:
pass pass
# 如果该用户是超级管理员那么他可以查看所有的提交记录详情 for item in current_page:
if request.user.admin_type > 1: # 自己提交的 管理员和创建比赛的可以看到所有的提交链接
return render(request, "oj/contest/submissions_list_admin.html", if item["user_id"] == request.user.id or request.user.admin_type == SUPER_ADMIN or \
{"submissions": current_page, "page": int(page), request.user == contest.created_by:
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, item["show_link"] = True
"contest": contest}) else:
item["show_link"] = False
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),

View File

@@ -3,6 +3,8 @@ MAINTAINER virusdefender<qduliyang@outlook.com>
RUN mkdir /var/install/ RUN mkdir /var/install/
WORKDIR /var/install/ WORKDIR /var/install/
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
RUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/
RUN apt-get update RUN apt-get update
RUN apt-get -y install software-properties-common python-software-properties RUN apt-get -y install software-properties-common python-software-properties
RUN add-apt-repository -y ppa:webupd8team/java RUN add-apt-repository -y ppa:webupd8team/java

View File

@@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import os
# 单个判题端最多同时运行的程序个数因为判题端会同时运行多组测试数据比如一共有5组测试数据 # 单个判题端最多同时运行的程序个数因为判题端会同时运行多组测试数据比如一共有5组测试数据
# 如果MAX_RUNNING_NUMBER大于等于5那么这5组数据就会同时进行评测然后返回结果。 # 如果MAX_RUNNING_NUMBER大于等于5那么这5组数据就会同时进行评测然后返回结果。
# 如果MAX_RUNNING_NUMBER小于5为3那么就会同时运行前三组测试数据然后再运行后两组数据 # 如果MAX_RUNNING_NUMBER小于5为3那么就会同时运行前三组测试数据然后再运行后两组数据
@@ -14,12 +15,10 @@ lrun_gid = 1002
# judger工作目录 # judger工作目录
judger_workspace = "/var/judger/" judger_workspace = "/var/judger/"
# 这个是在docker 中访问数据库 ip 不一定和web服务器还有celery的一样
submission_db = { submission_db = {
"host": "192.168.42.1", "host": os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
"port": 3306, "port": 3306,
"db": "oj_submission", "db": "oj_submission",
"user": "root", "user": "root",
"password": "mypwd" "password": os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
} }

10
judge/sources.list Normal file
View File

@@ -0,0 +1,10 @@
deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse

View File

@@ -3,10 +3,6 @@ import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 下面是需要自己修改的
LOG_PATH = "log/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上 # 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = { DATABASES = {
'default': { 'default': {
@@ -17,11 +13,11 @@ DATABASES = {
'submission': { 'submission': {
'NAME': 'oj_submission', 'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'HOST': "121.42.32.129", 'CONN_MAX_AGE': 0.1,
'HOST': "127.0.0.1",
'PORT': 3306, 'PORT': 3306,
'USER': 'root', 'USER': 'root',
'PASSWORD': 'mypwd', 'PASSWORD': 'root',
'CONN_MAX_AGE': 0.1,
} }
} }
@@ -33,9 +29,11 @@ REDIS_CACHE = {
DEBUG = True DEBUG = True
# 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/')
ALLOWED_HOSTS = [] ALLOWED_HOSTS = []
IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'static/src/upload_image/') # 在 debug 关闭的情况下,静态文件不是有 django runserver 来处理的,应该由 nginx 返回
# 在 debug 开启的情况下django 会在下面两个文件夹中寻找对应的静态文件。
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/src/"), BASE_DIR]
# 模板文件夹
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')]

View File

@@ -3,43 +3,41 @@ import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 下面是需要自己修改的
LOG_PATH = "/var/log/oj/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上 # 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': "oj", 'NAME': "oj",
'CONN_MAX_AGE': 0.1, 'CONN_MAX_AGE': 0.1,
'HOST': '127.0.0.1', 'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
'PORT': 3306, 'PORT': 3306,
'USER': 'root', 'USER': 'root',
'PASSWORD': 'mypwd' 'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
}, },
'submission': { 'submission': {
'NAME': 'oj_submission', 'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 0.1, 'CONN_MAX_AGE': 0.1,
'HOST': "127.0.0.1", 'HOST': os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
'PORT': 3306, 'PORT': 3306,
'USER': 'root', 'USER': 'root',
'PASSWORD': 'mypwd' 'PASSWORD': os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
} }
} }
REDIS_CACHE = { REDIS_CACHE = {
"host": "127.0.0.1", "host": os.environ.get("REDIS_PORT_6379_TCP_ADDR", "127.0.0.1"),
"port": 6379, "port": 6379,
"db": 1 "db": 1
} }
DEBUG = True DEBUG = False
# 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = '/root/test_case/'
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
IMAGE_UPLOAD_DIR = '/var/mnt/source/OnlineJudge/static/src/upload_image/' # 在 debug 关闭的情况下,静态文件不是有 django runserver 来处理的,应该由 nginx 返回
# 在 debug 开启的情况下django 会在下面两个文件夹中寻找对应的静态文件。
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/release/"), os.path.join(BASE_DIR, "static/release/")]
# 模板文件夹
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/release/')]

View File

@@ -32,8 +32,6 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'hzfp^8mbgapc&x%$#xv)0=t8s7_ilingw(q3!@h&2fty6v6fxz' SECRET_KEY = 'hzfp^8mbgapc&x%$#xv)0=t8s7_ilingw(q3!@h&2fty6v6fxz'
# Application definition # Application definition
INSTALLED_APPS = ( INSTALLED_APPS = (
@@ -76,7 +74,7 @@ ROOT_URLCONF = 'oj.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'template/src')], 'DIRS': TEMPLATE_DIRS,
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@@ -91,7 +89,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'oj.wsgi.application' WSGI_APPLICATION = 'oj.wsgi.application'
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/ # https://docs.djangoproject.com/en/1.8/topics/i18n/
@@ -111,10 +108,11 @@ USE_TZ = True
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static/src/"),)
AUTH_USER_MODEL = 'account.User' AUTH_USER_MODEL = 'account.User'
LOG_PATH = "log/"
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': True, 'disable_existing_loggers': True,
@@ -167,3 +165,7 @@ REST_FRAMEWORK = {
} }
DATABASE_ROUTERS = ['oj.db_router.DBRouter'] DATABASE_ROUTERS = ['oj.db_router.DBRouter']
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/')
IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'upload/')

View File

@@ -26,7 +26,7 @@ from contest_submission.views import contest_problem_my_submissions_list_page
urlpatterns = [ urlpatterns = [
url(r'^install/$', "install.views.install"), url(r'^install/$', "install.views.install"),
url("^$", TemplateView.as_view(template_name="oj/index.html"), name="index_page"), url("^$", "account.views.page_jump", name="page_jump_api"),
url(r'^docs/', include('rest_framework_swagger.urls')), url(r'^docs/', include('rest_framework_swagger.urls')),
url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"), url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"),
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"),

View File

@@ -1,13 +0,0 @@
/**
* Created by virusdefender on 8/25/15.
*/
fis.match('*.{js,css,png,gif}', {
useHash: true // 开启 md5 戳
});
fis.config.set(
'roadmap.path',
[{reg:'*.html',isHtmlLike : true}
])
;

View File

@@ -1,4 +1,4 @@
define("admin", ["jquery", "avalon"], function ($, avalon) { require(["jquery", "avalon", "bootstrap"], function ($, avalon) {
avalon.ready(function () { avalon.ready(function () {

View File

@@ -1,6 +0,0 @@
require(["jquery", "avalon"], function($, avalon){
var vm = avalon.define({
$id: "problem_list",
problem_list: []
})
});

View File

@@ -1,11 +1,10 @@
({ ({
// RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。 // RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。
baseUrl: "/static/js/", baseUrl: "./js",
// 第三方脚本模块的别名,jquery比libs/jquery-1.11.1.min.js简洁明了 // 第三方脚本模块的别名,jquery比libs/jquery-1.11.1.min.js简洁明了
paths: { paths: {
jquery: "empty:",
jquery: "lib/jquery/jquery", avalon: "empty:",
avalon: "lib/avalon/avalon",
editor: "utils/editor", editor: "utils/editor",
uploader: "utils/uploader", uploader: "utils/uploader",
formValidation: "utils/formValidation", formValidation: "utils/formValidation",
@@ -19,9 +18,9 @@
tagEditor: "lib/tagEditor/jquery.tag-editor.min", tagEditor: "lib/tagEditor/jquery.tag-editor.min",
jqueryUI: "lib/jqueryUI/jquery-ui", jqueryUI: "lib/jqueryUI/jquery-ui",
bootstrap: "lib/bootstrap/bootstrap", bootstrap: "lib/bootstrap/bootstrap",
datetimePicker: "lib/datetime_picker/bootstrap-datetimepicker.zh-CN", datetimePicker: "lib/datetime_picker/bootstrap-datetimepicker",
validator: "lib/validator/validator", validator: "lib/validator/validator",
ZeroClipboard: "lib/ZeroClipboard/ZeroClipboard",
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
//富文本编辑器simditor -> editor //富文本编辑器simditor -> editor
@@ -34,35 +33,109 @@
_codeMirror: "lib/codeMirror/codemirror", _codeMirror: "lib/codeMirror/codemirror",
codeMirrorClang: "lib/codeMirror/language/clike", codeMirrorClang: "lib/codeMirror/language/clike",
// bootstrap组件
modal: "lib/bootstrap/modal",
dropdown: "lib/bootstrap/dropdown",
transition: "lib/bootstrap/transition",
//百度webuploader -> uploader //百度webuploader -> uploader
webUploader: "lib/webuploader/webuploader", webUploader: "lib/webuploader/webuploader",
"_datetimePicker": "lib/datetime_picker/bootstrap-datetimepicker" //"_datetimePicker": "lib/datetime_picker/bootstrap-datetimepicker",
},
shim: { //以下都是页面 script 标签引用的js
bootstrap: {deps: ["jquery"]}, addProblem_0_pack: "app/admin/problem/addProblem",
_datetimePicker: {dep: ["jquery"]}, addContest_1_pack: "app/admin/contest/addContest",
datetimePicker: {deps: ["_datetimePicker"]}, problem_2_pack: "app/admin/problem/problem",
validator: ["jquery"] register_3_pack: "app/oj/account/register",
contestList_4_pack: "app/admin/contest/contestList",
group_5_pack: "app/oj/group/group",
editProblem_6_pack: "app/admin/problem/editProblem",
announcement_7_pack: "app/admin/announcement/announcement",
monitor_8_pack: "app/admin/monitor/monitor",
groupDetail_9_pack: "app/admin/group/groupDetail",
admin_10_pack: "app/admin/admin",
problem_11_pack: "app/oj/problem/problem",
submissionList_12_pack: "app/admin/problem/submissionList",
editProblem_13_pack: "app/admin/contest/editProblem",
joinGroupRequestList_14_pack: "app/admin/group/joinGroupRequestList",
changePassword_15_pack: "app/oj/account/changePassword",
group_16_pack: "app/admin/group/group",
submissionList_17_pack: "app/admin/contest/submissionList",
login_18_pack: "app/oj/account/login",
contestPassword_19_pack: "app/oj/contest/contestPassword",
userList_20_pack: "app/admin/user/userList"
}, },
findNestedDependencies: true, findNestedDependencies: true,
appDir: "../", appDir: "../",
dir: "../../release/", dir: "../../release/",
modules: [ modules: [
{ {
name: "submit_code" name: "bootstrap",
}, },
{ {
name: "validation" name: "addProblem_0_pack"
}, },
{ {
name: "editor" name: "addContest_1_pack"
}, },
{ {
name: "code_mirror" name: "problem_2_pack"
}, },
{ {
name: "datetimepicker" name: "register_3_pack"
},
{
name: "contestList_4_pack"
},
{
name: "group_5_pack"
},
{
name: "editProblem_6_pack"
},
{
name: "announcement_7_pack"
},
{
name: "monitor_8_pack"
},
{
name: "groupDetail_9_pack"
},
{
name: "admin_10_pack"
},
{
name: "problem_11_pack"
},
{
name: "submissionList_12_pack"
},
{
name: "editProblem_13_pack"
},
{
name: "joinGroupRequestList_14_pack"
},
{
name: "changePassword_15_pack"
},
{
name: "group_16_pack"
},
{
name: "submissionList_17_pack"
},
{
name: "login_18_pack"
},
{
name: "contestPassword_19_pack"
},
{
name: "userList_20_pack"
} }
] ],
optimizeCss: "standard",
}) })

View File

@@ -2,7 +2,6 @@ var require = {
// RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。 // RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。
baseUrl: "/static/js/", baseUrl: "/static/js/",
paths: { paths: {
jquery: "lib/jquery/jquery", jquery: "lib/jquery/jquery",
avalon: "lib/avalon/avalon", avalon: "lib/avalon/avalon",
editor: "utils/editor", editor: "utils/editor",
@@ -18,7 +17,7 @@ var require = {
tagEditor: "lib/tagEditor/jquery.tag-editor.min", tagEditor: "lib/tagEditor/jquery.tag-editor.min",
jqueryUI: "lib/jqueryUI/jquery-ui", jqueryUI: "lib/jqueryUI/jquery-ui",
bootstrap: "lib/bootstrap/bootstrap", bootstrap: "lib/bootstrap/bootstrap",
datetimePicker: "lib/datetime_picker/bootstrap-datetimepicker.zh-CN", datetimePicker: "lib/datetime_picker/bootstrap-datetimepicker",
validator: "lib/validator/validator", validator: "lib/validator/validator",
ZeroClipboard: "lib/ZeroClipboard/ZeroClipboard", ZeroClipboard: "lib/ZeroClipboard/ZeroClipboard",
@@ -34,15 +33,37 @@ var require = {
_codeMirror: "lib/codeMirror/codemirror", _codeMirror: "lib/codeMirror/codemirror",
codeMirrorClang: "lib/codeMirror/language/clike", codeMirrorClang: "lib/codeMirror/language/clike",
// bootstrap组件
modal: "lib/bootstrap/modal",
dropdown: "lib/bootstrap/dropdown",
transition: "lib/bootstrap/transition",
//百度webuploader -> uploader //百度webuploader -> uploader
webUploader: "lib/webuploader/webuploader", webUploader: "lib/webuploader/webuploader",
"_datetimePicker": "lib/datetime_picker/bootstrap-datetimepicker" // "_datetimePicker": "lib/datetime_picker/bootstrap-datetimepicker",
},
shim: { //以下都是页面 script 标签引用的js
bootstrap: {deps: ["jquery"]}, addProblem_0_pack: "app/admin/problem/addProblem",
_datetimePicker: {dep: ["jquery"]}, addContest_1_pack: "app/admin/contest/addContest",
datetimePicker: {deps: ["_datetimePicker"]}, problem_2_pack: "app/admin/problem/problem",
validator: ["jquery"] register_3_pack: "app/oj/account/register",
contestList_4_pack: "app/admin/contest/contestList",
group_5_pack: "app/oj/group/group",
editProblem_6_pack: "app/admin/problem/editProblem",
announcement_7_pack: "app/admin/announcement/announcement",
monitor_8_pack: "app/admin/monitor/monitor",
groupDetail_9_pack: "app/admin/group/groupDetail",
admin_10_pack: "app/admin/admin",
problem_11_pack: "app/oj/problem/problem",
submissionList_12_pack: "app/admin/problem/submissionList",
editProblem_13_pack: "app/admin/contest/editProblem",
joinGroupRequestList_14_pack: "app/admin/group/joinGroupRequestList",
changePassword_15_pack: "app/oj/account/changePassword",
group_16_pack: "app/admin/group/group",
submissionList_17_pack: "app/admin/contest/submissionList",
login_18_pack: "app/oj/account/login",
contestPassword_19_pack: "app/oj/contest/contestPassword",
userList_20_pack: "app/admin/user/userList"
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
define([ 'jquery', 'transition' ], function ( jQuery ) {
/* ========================================================================
* Bootstrap: dropdown.js v3.3.5
* http://getbootstrap.com/javascript/#dropdowns
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// DROPDOWN CLASS DEFINITION
// =========================
var backdrop = '.dropdown-backdrop'
var toggle = '[data-toggle="dropdown"]'
var Dropdown = function (element) {
$(element).on('click.bs.dropdown', this.toggle)
}
Dropdown.VERSION = '3.3.5'
function getParent($this) {
var selector = $this.attr('data-target')
if (!selector) {
selector = $this.attr('href')
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
var $parent = selector && $(selector)
return $parent && $parent.length ? $parent : $this.parent()
}
function clearMenus(e) {
if (e && e.which === 3) return
$(backdrop).remove()
$(toggle).each(function () {
var $this = $(this)
var $parent = getParent($this)
var relatedTarget = { relatedTarget: this }
if (!$parent.hasClass('open')) return
if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
if (e.isDefaultPrevented()) return
$this.attr('aria-expanded', 'false')
$parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
})
}
Dropdown.prototype.toggle = function (e) {
var $this = $(this)
if ($this.is('.disabled, :disabled')) return
var $parent = getParent($this)
var isActive = $parent.hasClass('open')
clearMenus()
if (!isActive) {
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
// if mobile we use a backdrop because click events don't delegate
$(document.createElement('div'))
.addClass('dropdown-backdrop')
.insertAfter($(this))
.on('click', clearMenus)
}
var relatedTarget = { relatedTarget: this }
$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
if (e.isDefaultPrevented()) return
$this
.trigger('focus')
.attr('aria-expanded', 'true')
$parent
.toggleClass('open')
.trigger($.Event('shown.bs.dropdown', relatedTarget))
}
return false
}
Dropdown.prototype.keydown = function (e) {
if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
var $this = $(this)
e.preventDefault()
e.stopPropagation()
if ($this.is('.disabled, :disabled')) return
var $parent = getParent($this)
var isActive = $parent.hasClass('open')
if (!isActive && e.which != 27 || isActive && e.which == 27) {
if (e.which == 27) $parent.find(toggle).trigger('focus')
return $this.trigger('click')
}
var desc = ' li:not(.disabled):visible a'
var $items = $parent.find('.dropdown-menu' + desc)
if (!$items.length) return
var index = $items.index(e.target)
if (e.which == 38 && index > 0) index-- // up
if (e.which == 40 && index < $items.length - 1) index++ // down
if (!~index) index = 0
$items.eq(index).trigger('focus')
}
// DROPDOWN PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.dropdown')
if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
var old = $.fn.dropdown
$.fn.dropdown = Plugin
$.fn.dropdown.Constructor = Dropdown
// DROPDOWN NO CONFLICT
// ====================
$.fn.dropdown.noConflict = function () {
$.fn.dropdown = old
return this
}
// APPLY TO STANDARD DROPDOWN ELEMENTS
// ===================================
$(document)
.on('click.bs.dropdown.data-api', clearMenus)
.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
.on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
.on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
}(jQuery);
});

View File

@@ -0,0 +1,340 @@
define([ 'jquery', 'transition' ], function ( jQuery ) {
/* ========================================================================
* Bootstrap: modal.js v3.3.5
* http://getbootstrap.com/javascript/#modals
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// MODAL CLASS DEFINITION
// ======================
var Modal = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.ignoreBackdropClick = false
if (this.options.remote) {
this.$element
.find('.modal-content')
.load(this.options.remote, $.proxy(function () {
this.$element.trigger('loaded.bs.modal')
}, this))
}
}
Modal.VERSION = '3.3.5'
Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150
Modal.DEFAULTS = {
backdrop: true,
keyboard: true,
show: true
}
Modal.prototype.toggle = function (_relatedTarget) {
return this.isShown ? this.hide() : this.show(_relatedTarget)
}
Modal.prototype.show = function (_relatedTarget) {
var that = this
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
this.isShown = true
this.checkScrollbar()
this.setScrollbar()
this.$body.addClass('modal-open')
this.escape()
this.resize()
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
})
})
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(that.$body) // don't move modals dom position
}
that.$element
.show()
.scrollTop(0)
that.adjustDialog()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
that.enforceFocus()
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ?
that.$dialog // wait for modal to slide in
.one('bsTransitionEnd', function () {
that.$element.trigger('focus').trigger(e)
})
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
that.$element.trigger('focus').trigger(e)
})
}
Modal.prototype.hide = function (e) {
if (e) e.preventDefault()
e = $.Event('hide.bs.modal')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
this.escape()
this.resize()
$(document).off('focusin.bs.modal')
this.$element
.removeClass('in')
.off('click.dismiss.bs.modal')
.off('mouseup.dismiss.bs.modal')
this.$dialog.off('mousedown.dismiss.bs.modal')
$.support.transition && this.$element.hasClass('fade') ?
this.$element
.one('bsTransitionEnd', $.proxy(this.hideModal, this))
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
this.hideModal()
}
Modal.prototype.enforceFocus = function () {
$(document)
.off('focusin.bs.modal') // guard against infinite focus loop
.on('focusin.bs.modal', $.proxy(function (e) {
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
this.$element.trigger('focus')
}
}, this))
}
Modal.prototype.escape = function () {
if (this.isShown && this.options.keyboard) {
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
e.which == 27 && this.hide()
}, this))
} else if (!this.isShown) {
this.$element.off('keydown.dismiss.bs.modal')
}
}
Modal.prototype.resize = function () {
if (this.isShown) {
$(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
} else {
$(window).off('resize.bs.modal')
}
}
Modal.prototype.hideModal = function () {
var that = this
this.$element.hide()
this.backdrop(function () {
that.$body.removeClass('modal-open')
that.resetAdjustments()
that.resetScrollbar()
that.$element.trigger('hidden.bs.modal')
})
}
Modal.prototype.removeBackdrop = function () {
this.$backdrop && this.$backdrop.remove()
this.$backdrop = null
}
Modal.prototype.backdrop = function (callback) {
var that = this
var animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $(document.createElement('div'))
.addClass('modal-backdrop ' + animate)
.appendTo(this.$body)
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
if (this.ignoreBackdropClick) {
this.ignoreBackdropClick = false
return
}
if (e.target !== e.currentTarget) return
this.options.backdrop == 'static'
? this.$element[0].focus()
: this.hide()
}, this))
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
if (!callback) return
doAnimate ?
this.$backdrop
.one('bsTransitionEnd', callback)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
var callbackRemove = function () {
that.removeBackdrop()
callback && callback()
}
$.support.transition && this.$element.hasClass('fade') ?
this.$backdrop
.one('bsTransitionEnd', callbackRemove)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callbackRemove()
} else if (callback) {
callback()
}
}
// these following methods are used to handle overflowing modals
Modal.prototype.handleUpdate = function () {
this.adjustDialog()
}
Modal.prototype.adjustDialog = function () {
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
this.$element.css({
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
})
}
Modal.prototype.resetAdjustments = function () {
this.$element.css({
paddingLeft: '',
paddingRight: ''
})
}
Modal.prototype.checkScrollbar = function () {
var fullWindowWidth = window.innerWidth
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
var documentElementRect = document.documentElement.getBoundingClientRect()
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
}
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
this.scrollbarWidth = this.measureScrollbar()
}
Modal.prototype.setScrollbar = function () {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
this.originalBodyPad = document.body.style.paddingRight || ''
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
Modal.prototype.resetScrollbar = function () {
this.$body.css('padding-right', this.originalBodyPad)
}
Modal.prototype.measureScrollbar = function () { // thx walsh
var scrollDiv = document.createElement('div')
scrollDiv.className = 'modal-scrollbar-measure'
this.$body.append(scrollDiv)
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
this.$body[0].removeChild(scrollDiv)
return scrollbarWidth
}
// MODAL PLUGIN DEFINITION
// =======================
function Plugin(option, _relatedTarget) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.modal')
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option](_relatedTarget)
else if (options.show) data.show(_relatedTarget)
})
}
var old = $.fn.modal
$.fn.modal = Plugin
$.fn.modal.Constructor = Modal
// MODAL NO CONFLICT
// =================
$.fn.modal.noConflict = function () {
$.fn.modal = old
return this
}
// MODAL DATA-API
// ==============
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
var $this = $(this)
var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
if ($this.is('a')) e.preventDefault()
$target.one('show.bs.modal', function (showEvent) {
if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
$target.one('hidden.bs.modal', function () {
$this.is(':visible') && $this.trigger('focus')
})
})
Plugin.call($target, option, this)
})
}(jQuery);
});

View File

@@ -0,0 +1,62 @@
define([ 'jquery', 'transition' ], function ( jQuery ) {
/* ========================================================================
* Bootstrap: transition.js v3.3.5
* http://getbootstrap.com/javascript/#transitions
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
// ============================================================
function transitionEnd() {
var el = document.createElement('bootstrap')
var transEndEventNames = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return { end: transEndEventNames[name] }
}
}
return false // explicit for ie8 ( ._.)
}
// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false
var $el = this
$(this).one('bsTransitionEnd', function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration)
return this
}
$(function () {
$.support.transition = transitionEnd()
if (!$.support.transition) return
$.event.special.bsTransitionEnd = {
bindType: $.support.transition.end,
delegateType: $.support.transition.end,
handle: function (e) {
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
}
}
})
}(jQuery);
});

View File

@@ -26,8 +26,15 @@
* *
* Make it work in bootstrap v3 * Make it work in bootstrap v3
*/ */
(function(factory){
!function ($) { if (typeof define === "function" && define.amd) {
define(["jquery"], factory);
} else if (typeof exports === 'object') {
factory(require('jquery'));
} else {
factory(jQuery);
}
}(function ($, undefined) {
function UTCDate() { function UTCDate() {
return new Date(Date.UTC.apply(Date, arguments)); return new Date(Date.UTC.apply(Date, arguments));
@@ -1764,6 +1771,17 @@
'</div>'; '</div>';
$.fn.datetimepicker.DPGlobal = DPGlobal; $.fn.datetimepicker.DPGlobal = DPGlobal;
$.fn.datetimepicker.dates['zh-CN'] = {
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
today: "今天",
suffix: [],
meridiem: ["上午", "下午"]
};
/* DATETIMEPICKER NO CONFLICT /* DATETIMEPICKER NO CONFLICT
* =================== */ * =================== */
@@ -1790,4 +1808,4 @@
$('[data-provide="datetimepicker-inline"]').datetimepicker(); $('[data-provide="datetimepicker-inline"]').datetimepicker();
}); });
}(window.jQuery); }));

View File

@@ -2,7 +2,13 @@
* Simplified Chinese translation for bootstrap-datetimepicker * Simplified Chinese translation for bootstrap-datetimepicker
* Yuan Cheung <advanimal@gmail.com> * Yuan Cheung <advanimal@gmail.com>
*/ */
;(function($){ !function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery', '_datetimePicker'], factory);
} else {
factory(root.jQuery);
}
}(this, function($){
$.fn.datetimepicker.dates['zh-CN'] = { $.fn.datetimepicker.dates['zh-CN'] = {
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"], daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
@@ -13,4 +19,4 @@
suffix: [], suffix: [],
meridiem: ["上午", "下午"] meridiem: ["上午", "下午"]
}; };
}(jQuery)); });

View File

@@ -25,8 +25,13 @@
* THE SOFTWARE. * THE SOFTWARE.
* ======================================================================== */ * ======================================================================== */
!function(root, factory) {
+function ($) { if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(root.jQuery);
}
}(this, function ($) {
'use strict'; 'use strict';
// VALIDATOR CLASS DEFINITION // VALIDATOR CLASS DEFINITION
@@ -322,4 +327,4 @@
}) })
}) })
}(jQuery); });

View File

@@ -127,9 +127,7 @@
<script src="/static/js/config.js"></script> <script src="/static/js/config.js"></script>
<script src="/static/js/require.js"></script> <script src="/static/js/require.js"></script>
<script> <script src="/static/js/app/admin/admin.js"></script>
require(["bootstrap", "admin"]);
</script>
<!-- footer begin --> <!-- footer begin -->
<div class="footer"> <div class="footer">

View File

@@ -34,5 +34,5 @@
</div> </div>
{% endblock %} {% endblock %}
{% block js_block %} {% block js_block %}
<script src="/static/js/app/oj/account/change_password.js"></script> <script src="/static/js/app/oj/account/changePassword.js"></script>
{% endblock %} {% endblock %}

View File

@@ -78,5 +78,5 @@
{% endblock %} {% endblock %}
{% block js_block %} {% block js_block %}
<script src="/static/js/app/oj/problem/problem_list.js"></script>
{% endblock %} {% endblock %}

View File

@@ -2,8 +2,8 @@
{% block body %} {% block body %}
{% load submission %} {% load submission %}
<div class="container main"> <div class="container main">
<div class="col-lg-12 contest-tab">
<ul class="nav nav-tabs nav-tabs-google"> <ul class="nav nav-tabs nav-tabs-google contest-tab">
<li role="presentation"> <li role="presentation">
<a href="/contest/{{ contest.id }}/">比赛详情</a> <a href="/contest/{{ contest.id }}/">比赛详情</a>
</li> </li>
@@ -17,7 +17,7 @@
<a href="/contest/{{ contest.id }}/rank/">排名</a> <a href="/contest/{{ contest.id }}/rank/">排名</a>
</li> </li>
</ul> </ul>
</div>
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<h2 class="text-center">排名( <h2 class="text-center">排名(
@@ -36,7 +36,8 @@
<th class="text-center">AC / 总提交</th> <th class="text-center">AC / 总提交</th>
<th class="text-center">用时 + 罚时</th> <th class="text-center">用时 + 罚时</th>
{% for item in contest_problems %} {% for item in contest_problems %}
<th class="text-center"><a href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a> <th class="text-center"><a
href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a>
</th> </th>
{% endfor %} {% endfor %}
</tr> </tr>
@@ -49,7 +50,8 @@
<td>{{ item.total_ac }} / {{ item.total_submit }}</td> <td>{{ item.total_ac }} / {{ item.total_submit }}</td>
<td>{% if item.total_time %}{{ item.total_time }}{% else %}--{% endif %}</td> <td>{% if item.total_time %}{{ item.total_time }}{% else %}--{% endif %}</td>
{% for problem in item.problems %} {% for problem in item.problems %}
<td class="{% if problem %}{% if problem.ac %}{% if problem.first_achieved %}first-achieved{% else %}alert-success{% endif %}{% else %}alert-danger{% endif %}{% endif %}"> <td class="
{% if problem %}{% if problem.ac %}{% if problem.first_achieved %}first-achieved{% else %}alert-success{% endif %}{% else %}alert-danger{% endif %}{% endif %}">
{% if problem.ac %}{{ problem.ac_time }}{% endif %} {% if problem.ac %}{{ problem.ac_time }}{% endif %}
{% if problem.failed_number %} {% if problem.failed_number %}
(-{{ problem.failed_number }}) (-{{ problem.failed_number }})
@@ -60,10 +62,13 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<input type="checkbox" id="auto-refresh" {% if auto_refresh %}checked{% endif %} onchange="if(this.checked){location.href='?auto_refresh=true'}else{location.href=location.href.split('?')[0]}"> 自动刷新 <input type="checkbox" id="auto-refresh" {% if auto_refresh %}checked{% endif %}
onchange="if(this.checked){location.href='?auto_refresh=true'}else{location.href=location.href.split('?')[0]}">
自动刷新
{% else %} {% else %}
<p>还没有结果</p> <p>还没有结果</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -37,5 +37,5 @@
</div> </div>
{% endblock %} {% endblock %}
{% block js_block %} {% block js_block %}
<script src="/static/js/app/oj/contest/contest_password.js"></script> <script src="/static/js/app/oj/contest/contestPassword.js"></script>
{% endblock %} {% endblock %}

View File

@@ -67,13 +67,13 @@
<tbody> <tbody>
{% for item in submissions %} {% for item in submissions %}
<tr> <tr>
<th scope="row">
{% if item.user_id == request.user.id and request.user.admin_type == 2%} {% if item.show_link %}
<th scope="row"><a href="/submission/{{ item.id }}/"> <a href="/submission/{{ item.id }}/">{{ forloop.counter |add:start_id }}</a>
{{ forloop.counter |add:start_id }}</a></th>
{% else %} {% else %}
<th scope="row">{{ forloop.counter |add:start_id }}</th> {{ forloop.counter |add:start_id }}
{% endifequal %} {% endif %}
</th>
<th scope="row"> <th scope="row">
<a href="/contest/{{ item.contest_id }}/problem/{{ item.problem_id }}/">{{ item.title }}</a> <a href="/contest/{{ item.contest_id }}/problem/{{ item.problem_id }}/">{{ item.title }}</a>
</th> </th>

View File

@@ -1,114 +0,0 @@
{% extends 'oj_base.html' %}
{% block body %}
{% load submission %}
{% load user %}
<div class="container main">
<div class="contest-tab">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation">
<a href="/contest/{{ contest.id }}/">比赛详情</a>
</li>
<li role="presentation">
<a href="/contest/{{ contest.id }}/problems/">题目</a>
</li>
<li role="presentation" class="active">
<a href="/contest/{{ contest.id }}/submissions/">提交</a>
</li>
<li role="presentation">
<a href="/contest/{{ contest.id }}/rank/">排名</a>
</li>
</ul>
</div>
<table class="table table-bordered">
<thead>
<tr class="" success>
<th>#</th>
<th>题目名称</th>
<th>用户</th>
<th>提交时间</th>
<th>
<div class="dropdown">
<a href="#" class="dropdown-toggle" id="languageFilter" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
语言<span class="caret"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="languageFilter">
<li><a href="/contest/{{ contest.id }}/submissions/?language=1">C</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?language=2">C++</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?language=3">Java</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/">取消筛选</a></li>
</ul>
</div>
</th>
<th>运行时间</th>
<th>
<div class="dropdown">
<a href="#" class="dropdown-toggle" id="resultFilter" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
结果<span class="caret"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="resultFilter">
<li><a href="/contest/{{ contest.id }}/submissions/?result=0">Accepted</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?result=6">Wrong Answer</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?result=1">Runtime Error</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?result=2">Time Limit Exceeded</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?result=3">Memory Limit Exceeded</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?result=4">Compile Error</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/?result=5">Format Error</a></li>
<li><a href="/contest/{{ contest.id }}/submissions/">取消筛选</a></li>
</ul>
</div>
</th>
</tr>
</thead>
{% if submissions %}
<tbody>
{% for item in submissions %}
<tr>
<th scope="row"><a href="/submission/{{ item.id }}/">
{{ forloop.counter |add:start_id }}</a></th>
<th scope="row">
<a href="/contest/{{ item.contest_id }}/problem/{{ item.problem_id }}/">{{ item.title }}</a>
</th>
<td>{{ item.user_id|get_username }}</td>
<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>
</tr>
{% endfor %}
</tbody>
{% else %}
<p>本场比赛还没有提交记录</p>
{% endif %}
</table>
<nav>
<ul class="pager">
{% if previous_page %}
<li class="previous"><a
href="/contest/{{ contest.id }}/submissions/{{ previous_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页</a></li>
{% endif %}
{% if next_page %}
<li class="next">
<a href="/contest/{{ contest.id }}/submissions/{{ next_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% endif %}">
下一页 <span aria-hidden="true">&rarr;</span></a></li>
{% endif %}
</ul>
</nav>
</div>
{% endblock %}

View File

@@ -5,7 +5,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>青岛大学在线评测平台 - 首页</title> <title>青岛大学在线评测平台 - 首页</title>
<link rel="stylesheet" type="text/css" href="/static/css/fullpage/jquery.fullPage.css"> <link href="/static/css/fullpage/jquery.fullPage.css" rel="stylesheet">
<link rel="shortcut icon" href="/static/img/favicon.ico"> <link rel="shortcut icon" href="/static/img/favicon.ico">
<style> <style>
html, textarea, input, option, select, button { html, textarea, input, option, select, button {
@@ -29,6 +29,36 @@
padding: 20px 20px 0 20px; padding: 20px 20px 0 20px;
} }
#login {
float: right;
position: relative;
height: 30px;
line-height: 64px;
margin-right: 30px;
z-index: 10;
margin-top: 4px;
}
.login-zone .button:first-child {
margin-right: 5px;
}
.button {
display: inline-block;
padding: 0 2em;
text-transform: none;
line-height: 2em;
height: 2em;
border-radius: 2em;
border: 1px solid #FFF;
outline: 0;
cursor: pointer;
}
.button:first-child {
margin-right: 5px;
}
#name { #name {
font-size: 45px; font-size: 45px;
margin-right: 20px; margin-right: 20px;
@@ -86,8 +116,6 @@
loopBottom: true loopBottom: true
}); });
}); });
</script> </script>
</head> </head>
@@ -100,6 +128,12 @@
<a href="/groups/">小组</a>&nbsp;&nbsp; <a href="/groups/">小组</a>&nbsp;&nbsp;
<a href="/about/">关于</a> <a href="/about/">关于</a>
{% if not request.user.is_authenticated %}
<div id="login">
<a href="/login/" class="login button">登录</a>
<a href="/register/" class="register button">注册</a>
</div>
{% endif %}
</div> </div>

View File

@@ -62,7 +62,6 @@
<div id="code-field"> <div id="code-field">
<textarea id="code-editor">{{ submission.code }}</textarea> <textarea id="code-editor">{{ submission.code }}</textarea>
</div> </div>
{% ifequal request.user.id submission.user_id %}
<div id="share-code" class="col-lg-6 col-md-6"> <div id="share-code" class="col-lg-6 col-md-6">
{% if submission.shared %} {% if submission.shared %}
@@ -74,7 +73,7 @@
{% if not submission.shared %}style="display: none" {% endif %}>{{ problem.title }} {% if not submission.shared %}style="display: none" {% endif %}>{{ problem.title }}
{{ request.build_absolute_uri }}</textarea> {{ request.build_absolute_uri }}</textarea>
</div> </div>
{% endifequal %}
</div> </div>

View File

@@ -89,5 +89,5 @@
{% endblock %} {% endblock %}
{% block js_block %} {% block js_block %}
<script src="/static/js/app/oj/problem/problem_list.js"></script>
{% endblock %} {% endblock %}

View File

@@ -86,10 +86,6 @@
<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

@@ -20,8 +20,7 @@
</p> </p>
<p> <p>
青岛大学 ACM 队是一支优秀的队伍,一支充满活力与激情的队伍,它满载着光辉与荣誉。在过去的几年里,集训队的队员曾代表我校多次 青岛大学 ACM 队是一支优秀的队伍,一支充满活力与激情的队伍,它满载着光辉与荣誉。在过去的几年里,集训队的队员曾代表我校多次参加竞赛,获得了佳绩。
参加竞赛,获得了佳绩。
</p> </p>
<p> <p>

61
tools/release_static.py Normal file
View File

@@ -0,0 +1,61 @@
# coding=utf-8
import hashlib
import re
import os
import shutil
template_src_path = "template/src/"
template_release_path = "template/release/"
static_src_path = "static/src/"
static_release_path = "static/release/"
try:
# 删除模板的 release 文件夹
shutil.rmtree(template_release_path)
except Exception:
pass
# 复制一份模板文件夹到 release
shutil.copytree(template_src_path, template_release_path)
# 删除静态文件的 release 文件夹
# shutil.rmtree(static_release_path)
# 复制一份静态文件文件夹到 release
# shutil.copytree(static_src_path, static_release_path)
js_re = re.compile(r'<script src="(.+)"></script>')
css_re = re.compile(r'<link href="(.+)" rel="stylesheet">')
name_map = {}
def process(match):
file_path = match.group(1).replace("/static/", "")
# print file_path, match.group(), match.group(1)
if not os.path.exists(static_release_path + file_path):
return match.group(0)
if file_path in name_map:
md5 = name_map[file_path]
else:
# rename
md5 = hashlib.md5(open(static_release_path + file_path, "r").read()).hexdigest()
# os.rename(static_release_path + js_path, static_release_path + js_path + "?v=" + md5)
if ".js" in file_path:
return '<script src="/static/%s"></script>' % (file_path + "?v=" + md5[:6], )
elif ".css" in file_path:
return '<link rel="stylesheet" type="text/css" href="/static/%s">' % (file_path + "?v=" + md5[:6], )
else:
return match.group(0)
for root, dirs, files in os.walk(template_release_path):
for name in files:
html_path = os.path.join(root, name)
html_content = open(html_path, "r").read()
js_replaced_html_content = re.sub(js_re, process, html_content)
css_replaced_html_content = re.sub(css_re, process, js_replaced_html_content)
f = open(html_path, "w")
f.write(css_replaced_html_content)
f.close()

37
tools/template_js_path.py Normal file
View File

@@ -0,0 +1,37 @@
# coding=utf-8
import hashlib
import re
import os
import shutil
template_src_path = "template/src/"
total_file = []
module_list = []
print "\n\n-------------\n\n"
print u" //以下都是页面 script 标签引用的js"
for root, dirs, files in os.walk(template_src_path):
for name in files:
html_path = os.path.join(root, name)
html_content = open(html_path, "r").read()
r = re.findall(re.compile('<script src="(.*)"></script>'), html_content)
if r:
for item in r:
if item and "app" in item:
total_file.append(item)
i = 0
for item in set(total_file):
module_name = item.replace("/static/js/", "").split("/")[-1].replace(".js", "") + "_" + str(i)
module_list.append(module_name)
print " " + module_name + "_pack" + ": \"" + item.replace("/static/js/", "").replace(".js", "") + "\","
i += 1
print "\n\n-------------\n\n"
for item in module_list:
print " {"
print " name: \"" + item + "_pack\""
print " },"

View File

@@ -29,8 +29,8 @@ class SimditorImageUploadAPIView(APIView):
return Response(data={ return Response(data={
"success": True, "success": True,
"msg": "上传错误", "msg": "上传错误",
"file_path": "/static/upload_image/" + image_name}) "file_path": "/static/upload/" + image_name})
return Response(data={ return Response(data={
"success": True, "success": True,
"msg": "", "msg": "",
"file_path": "/static/upload_image/" + image_name}) "file_path": "/static/upload/" + image_name})