Accept Merge Request #298 好像好久没有合并过了。。。 : (dev-s -> dev)
Merge Request: 好像好久没有合并过了。。。 Created By: @esp Accepted By: @virusdefender URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/298
This commit is contained in:
@@ -8,9 +8,10 @@ demo: https://qduoj.com
|
||||
|
||||
TODO:
|
||||
|
||||
- 将判题服务器改为 rpc 通信
|
||||
- 重构消息队列
|
||||
- 完善测试
|
||||
- 完善小组功能
|
||||
- 后台重构
|
||||
|
||||
![oj_previewindex.png][1]
|
||||
|
||||
|
||||
24
account/middleware.py
Normal file
24
account/middleware.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# coding=utf-8
|
||||
import time
|
||||
import json
|
||||
import urllib
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.contrib import auth
|
||||
from utils.shortcuts import error_response, error_page
|
||||
from .models import ADMIN
|
||||
|
||||
|
||||
class SessionSecurityMiddleware(object):
|
||||
def process_request(self, request):
|
||||
if request.user.is_authenticated() and request.user.admin_type >= ADMIN:
|
||||
if "last_activity" in request.session:
|
||||
# 24个小时没有活动
|
||||
if time.time() - request.session["last_activity"] >= 24 * 60 * 60:
|
||||
auth.logout(request)
|
||||
if request.is_ajax():
|
||||
return HttpResponse(json.dumps({"code": 1, "data": u"请先登录"}),
|
||||
content_type="application/json")
|
||||
else:
|
||||
return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.build_absolute_uri()))
|
||||
# 更新最后活动日期
|
||||
request.session["last_activity"] = time.time()
|
||||
24
account/migrations/0014_auto_20151110_1037.py
Normal file
24
account/migrations/0014_auto_20151110_1037.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0013_userprofile'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='phone_number',
|
||||
field=models.CharField(max_length=15, null=True, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='school',
|
||||
field=models.CharField(max_length=200, null=True, blank=True),
|
||||
),
|
||||
]
|
||||
@@ -68,6 +68,9 @@ class UserProfile(models.Model):
|
||||
submissions_number = models.IntegerField(default=0)
|
||||
# JSON字典用来表示该用户的问题的解决状态 1为ac,2为正在进行
|
||||
problems_status = JSONField(default={})
|
||||
phone_number = models.CharField(max_length=15, blank=True, null=True)
|
||||
school = models.CharField(max_length=200, blank=True, null=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
db_table = "user_profile"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# coding=utf-8
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import User
|
||||
from .models import User, UserProfile
|
||||
|
||||
|
||||
class UserLoginSerializer(serializers.Serializer):
|
||||
@@ -21,6 +21,7 @@ class EmailCheckSerializer(serializers.Serializer):
|
||||
class UserRegisterSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(max_length=30)
|
||||
real_name = serializers.CharField(max_length=30)
|
||||
school = serializers.CharField(max_length=200)
|
||||
password = serializers.CharField(max_length=30, min_length=6)
|
||||
email = serializers.EmailField(max_length=254)
|
||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||
@@ -49,7 +50,6 @@ class EditUserSerializer(serializers.Serializer):
|
||||
|
||||
|
||||
class ApplyResetPasswordSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(max_length=30)
|
||||
email = serializers.EmailField()
|
||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||
|
||||
@@ -61,4 +61,24 @@ class ResetPasswordSerializer(serializers.Serializer):
|
||||
|
||||
|
||||
class SSOSerializer(serializers.Serializer):
|
||||
token = serializers.CharField(max_length=40)
|
||||
token = serializers.CharField(max_length=40)
|
||||
|
||||
|
||||
class EditUserProfileSerializer(serializers.Serializer):
|
||||
|
||||
avatar = serializers.CharField(max_length=50, required=False, default=None)
|
||||
blog = serializers.URLField(required=False, allow_blank=True, default='')
|
||||
mood = serializers.CharField(max_length=60, required=False, default='')
|
||||
hduoj_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
||||
bestcoder_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
||||
codeforces_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
||||
school = serializers.CharField(max_length=200, required=False, allow_blank=True, default='')
|
||||
phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='')
|
||||
|
||||
|
||||
class UserProfileSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username",
|
||||
"rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school"]
|
||||
|
||||
@@ -17,12 +17,14 @@ from utils.captcha import Captcha
|
||||
from mail.tasks import send_email
|
||||
|
||||
from .decorators import login_required
|
||||
from .models import User
|
||||
from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
|
||||
UserRegisterSerializer, UserChangePasswordSerializer,
|
||||
EmailCheckSerializer, UserSerializer, EditUserSerializer,
|
||||
from .models import User, UserProfile
|
||||
|
||||
from .serializers import (UserLoginSerializer, UserRegisterSerializer,
|
||||
UserChangePasswordSerializer,
|
||||
UserSerializer, EditUserSerializer,
|
||||
ApplyResetPasswordSerializer, ResetPasswordSerializer,
|
||||
SSOSerializer)
|
||||
SSOSerializer, EditUserProfileSerializer, UserProfileSerializer)
|
||||
|
||||
from .decorators import super_admin_required
|
||||
|
||||
|
||||
@@ -60,10 +62,9 @@ def index_page(request):
|
||||
if not request.user.is_authenticated():
|
||||
return render(request, "oj/index.html")
|
||||
|
||||
try:
|
||||
if request.META['HTTP_REFERER']:
|
||||
if request.META.get('HTTP_REFERER') or request.GET.get("index"):
|
||||
return render(request, "oj/index.html")
|
||||
except KeyError:
|
||||
else:
|
||||
return http.HttpResponseRedirect('/problems/')
|
||||
|
||||
|
||||
@@ -96,6 +97,7 @@ class UserRegisterAPIView(APIView):
|
||||
email=data["email"])
|
||||
user.set_password(data["password"])
|
||||
user.save()
|
||||
UserProfile.objects.create(user=user, school=data["school"])
|
||||
return success_response(u"注册成功!")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
@@ -146,17 +148,27 @@ class UsernameCheckAPIView(APIView):
|
||||
class EmailCheckAPIView(APIView):
|
||||
def get(self, request):
|
||||
"""
|
||||
检测邮箱是否存在,存在返回状态码400,不存在返回200
|
||||
检测邮箱是否存在,用状态码标识结果
|
||||
---
|
||||
"""
|
||||
#这里是为了适应前端表单验证空间的要求
|
||||
reset = request.GET.get("reset", None)
|
||||
#如果reset为true说明该请求是重置密码页面发出的,要返回的状态码应正好相反
|
||||
if reset:
|
||||
existed = 200
|
||||
does_not_existed = 400
|
||||
else:
|
||||
existed = 400
|
||||
does_not_existed = 200
|
||||
|
||||
email = request.GET.get("email", None)
|
||||
if email:
|
||||
try:
|
||||
User.objects.get(email=email)
|
||||
return Response(status=400)
|
||||
return Response(status=existed)
|
||||
except Exception:
|
||||
return Response(status=200)
|
||||
return Response(status=200)
|
||||
return Response(status=does_not_existed)
|
||||
return Response(status=does_not_existed)
|
||||
|
||||
|
||||
class UserAdminAPIView(APIView):
|
||||
@@ -225,6 +237,38 @@ class UserInfoAPIView(APIView):
|
||||
return success_response(UserSerializer(request.user).data)
|
||||
|
||||
|
||||
class UserProfileAPIView(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
"""
|
||||
返回这个用户的个人信息
|
||||
---
|
||||
response_serializer: UserSerializer
|
||||
"""
|
||||
return success_response(UserSerializer(request.user).data)
|
||||
|
||||
@login_required
|
||||
def put(self, request):
|
||||
serializer = EditUserProfileSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
user_profile = request.user.userprofile
|
||||
if data["avatar"]:
|
||||
user_profile.avatar = data["avatar"]
|
||||
else:
|
||||
user_profile.mood = data["mood"]
|
||||
user_profile.hduoj_username = data["hduoj_username"]
|
||||
user_profile.bestcoder_username = data["bestcoder_username"]
|
||||
user_profile.codeforces_username = data["codeforces_username"]
|
||||
user_profile.blog = data["blog"]
|
||||
user_profile.school = data["school"]
|
||||
user_profile.phone_number = data["phone_number"]
|
||||
user_profile.save()
|
||||
return success_response(u"修改成功")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
|
||||
class ApplyResetPasswordAPIView(APIView):
|
||||
def post(self, request):
|
||||
"""
|
||||
@@ -239,7 +283,7 @@ class ApplyResetPasswordAPIView(APIView):
|
||||
if not captcha.check(data["captcha"]):
|
||||
return error_response(u"验证码错误")
|
||||
try:
|
||||
user = User.objects.get(username=data["username"], email=data["email"])
|
||||
user = User.objects.get(email=data["email"])
|
||||
except User.DoesNotExist:
|
||||
return error_response(u"用户不存在")
|
||||
if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60:
|
||||
@@ -251,14 +295,14 @@ class ApplyResetPasswordAPIView(APIView):
|
||||
|
||||
email_template = email_template.replace("{{ username }}", user.username).\
|
||||
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]).\
|
||||
replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/?token=" + user.reset_password_token)
|
||||
replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/t/" + user.reset_password_token)
|
||||
|
||||
send_email(settings.WEBSITE_INFO["website_name"],
|
||||
user.email,
|
||||
user.username,
|
||||
settings.WEBSITE_INFO["website_name"] + u" 密码找回邮件",
|
||||
settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件",
|
||||
email_template)
|
||||
return success_response(u"邮件发送成功")
|
||||
return success_response(u"邮件发送成功,请前往您的邮箱查收")
|
||||
else:
|
||||
return serializer_invalid_response(serializer)
|
||||
|
||||
@@ -319,4 +363,14 @@ class SSOAPIView(APIView):
|
||||
token = rand_str()
|
||||
request.user.auth_token = token
|
||||
request.user.save()
|
||||
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback})
|
||||
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback})
|
||||
|
||||
|
||||
def reset_password_page(request, token):
|
||||
try:
|
||||
user = User.objects.get(reset_password_token=token)
|
||||
except User.DoesNotExist:
|
||||
return error_page(request, u"链接已失效")
|
||||
if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60:
|
||||
return error_page(request, u"链接已过期")
|
||||
return render(request, "oj/account/reset_password.html", {"user": user})
|
||||
|
||||
@@ -4,6 +4,7 @@ from functools import wraps
|
||||
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from utils.shortcuts import error_response, error_page
|
||||
|
||||
@@ -92,7 +93,7 @@ def check_user_contest_permission(func):
|
||||
{"reason": "contest_not_start", "show_tab": False, "contest": contest})
|
||||
|
||||
# 比赛已经结束了,只拦截 ajax 的答案提交
|
||||
if contest.status == CONTEST_ENDED and request.is_ajax():
|
||||
if contest.status == CONTEST_ENDED and request.path == reverse("contest_submission_api") and request.is_ajax():
|
||||
return error_response(u"比赛已经结束")
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@@ -264,7 +264,7 @@ class ContestProblemAdminAPIView(APIView):
|
||||
keyword = request.GET.get("keyword", None)
|
||||
if keyword:
|
||||
contest_problems = contest_problems.filter(Q(title__contains=keyword) |
|
||||
Q(description__contains=keyword))
|
||||
Q(description__contains=keyword))
|
||||
contest_id = request.GET.get("contest_id", None)
|
||||
if contest_id:
|
||||
contest_problems = contest_problems.filter(contest__id=contest_id).order_by("sort_index")
|
||||
@@ -278,6 +278,8 @@ class MakeContestProblemPublicAPIView(APIView):
|
||||
problem_id = request.data.get("problem_id", -1)
|
||||
try:
|
||||
problem = ContestProblem.objects.get(id=problem_id)
|
||||
problem.is_public = True
|
||||
problem.save()
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_response(u"比赛不存在")
|
||||
Problem.objects.create(title=problem.title, description=problem.description,
|
||||
@@ -288,10 +290,11 @@ class MakeContestProblemPublicAPIView(APIView):
|
||||
hint=problem.hint, created_by=problem.created_by,
|
||||
time_limit=problem.time_limit, memory_limit=problem.memory_limit,
|
||||
visible=False, difficulty=-1, source=problem.contest.title)
|
||||
problem.is_public = True
|
||||
problem.save()
|
||||
return success_response(u"创建成功")
|
||||
|
||||
|
||||
|
||||
class ContestPasswordVerifyAPIView(APIView):
|
||||
@login_required
|
||||
def post(self, request):
|
||||
@@ -374,7 +377,7 @@ def contest_problems_list_page(request, contest_id):
|
||||
比赛所有题目的列表页
|
||||
"""
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest).select_related("contest").order_by("sort_index")
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).select_related("contest").order_by("sort_index")
|
||||
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
|
||||
"contest": {"id": contest_id}})
|
||||
|
||||
@@ -423,16 +426,16 @@ def contest_list_page(request, page=1):
|
||||
@check_user_contest_permission
|
||||
def contest_rank_page(request, contest_id):
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).order_by("sort_index")
|
||||
|
||||
r = get_cache_redis()
|
||||
cache_key = str(contest_id) + "_rank_cache"
|
||||
rank = r.get(cache_key)
|
||||
|
||||
if not rank:
|
||||
rank = ContestRank.objects.filter(contest_id=contest_id).\
|
||||
select_related("user").\
|
||||
order_by("-total_ac_number", "total_time").\
|
||||
rank = ContestRank.objects.filter(contest_id=contest_id). \
|
||||
select_related("user"). \
|
||||
order_by("-total_ac_number", "total_time"). \
|
||||
values("id", "user__id", "user__username", "user__real_name", "contest_id", "submission_info",
|
||||
"total_submission_number", "total_ac_number", "total_time")
|
||||
r.set(cache_key, json.dumps([dict(item) for item in rank]))
|
||||
@@ -459,13 +462,14 @@ def contest_rank_page(request, contest_id):
|
||||
"contest_problems": contest_problems,
|
||||
"paging_info": paging_info,
|
||||
"auto_refresh": request.GET.get("auto_refresh", None) == "true",
|
||||
"show_real_name": request.GET.get("show_real_name", None) == "true",})
|
||||
"show_real_name": request.GET.get("show_real_name", None) == "true", })
|
||||
|
||||
|
||||
class ContestTimeAPIView(APIView):
|
||||
"""
|
||||
获取比赛开始或者结束的倒计时,返回毫秒数字
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
contest_id = request.GET.get("contest_id", -1)
|
||||
try:
|
||||
@@ -490,8 +494,8 @@ def contest_problem_my_submissions_list_page(request, contest_id, contest_proble
|
||||
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
|
||||
except ContestProblem.DoesNotExist:
|
||||
return error_page(request, u"比赛问题不存在")
|
||||
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).\
|
||||
order_by("-create_time").\
|
||||
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id). \
|
||||
order_by("-create_time"). \
|
||||
values("id", "result", "create_time", "accepted_answer_time", "language")
|
||||
return render(request, "oj/submission/problem_my_submissions_list.html",
|
||||
{"submissions": submissions, "problem": contest_problem})
|
||||
@@ -507,10 +511,14 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
|
||||
except Contest.DoesNotExist:
|
||||
return error_page(request, u"比赛不存在")
|
||||
|
||||
submissions = Submission.objects.filter(contest_id=contest_id).\
|
||||
submissions = Submission.objects.filter(contest_id=contest_id). \
|
||||
values("id", "contest_id", "problem_id", "result", "create_time",
|
||||
"accepted_answer_time", "language", "user_id").order_by("-create_time")
|
||||
|
||||
# 如果比赛已经开始,就不再显示之前测试题目的提交
|
||||
if contest.status != CONTEST_NOT_START:
|
||||
submissions = submissions.filter(create_time__gte=contest.start_time)
|
||||
|
||||
user_id = request.GET.get("user_id", None)
|
||||
if user_id:
|
||||
submissions = submissions.filter(user_id=request.GET.get("user_id"))
|
||||
@@ -568,4 +576,4 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
|
||||
return render(request, "oj/contest/submissions_list.html",
|
||||
{"submissions": current_page, "page": int(page),
|
||||
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
|
||||
"contest": contest, "filter": filter, "user_id": user_id, "problem_id": problem_id})
|
||||
"contest": contest, "filter": filter, "user_id": user_id, "problem_id": problem_id})
|
||||
|
||||
@@ -71,7 +71,8 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'admin.middleware.AdminRequiredMiddleware'
|
||||
'admin.middleware.AdminRequiredMiddleware',
|
||||
'account.middleware.SessionSecurityMiddleware'
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'oj.urls'
|
||||
|
||||
11
oj/urls.py
11
oj/urls.py
@@ -5,8 +5,8 @@ from django.views.generic import TemplateView
|
||||
|
||||
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
|
||||
UserChangePasswordAPIView, EmailCheckAPIView,
|
||||
UserAdminAPIView, UserInfoAPIView,
|
||||
ApplyResetPasswordAPIView, SSOAPIView)
|
||||
UserAdminAPIView, UserInfoAPIView, ResetPasswordAPIView,
|
||||
ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView)
|
||||
|
||||
from announcement.views import AnnouncementAdminAPIView
|
||||
|
||||
@@ -122,11 +122,14 @@ urlpatterns = [
|
||||
|
||||
url(r'^user/(?P<username>.+)/$', "account.views.user_index_page"),
|
||||
|
||||
url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
||||
|
||||
url(r'^api/apply_reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
||||
url(r'^api/reset_password/$', ResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
||||
url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"),
|
||||
url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
|
||||
url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"),
|
||||
url(r'^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_api"),
|
||||
url(r'^reset_password/$', TemplateView.as_view(template_name="oj/account/apply_reset_password.html"), name="apply_reset_password_page"),
|
||||
url(r'^reset_password/t/(?P<token>\w+)/$', "account.views.reset_password_page", name="reset_password_page")
|
||||
]
|
||||
|
||||
|
||||
|
||||
112
problem/views.py
112
problem/views.py
@@ -40,6 +40,7 @@ class ProblemTagAdminAPIView(APIView):
|
||||
"""
|
||||
获取所有标签的列表
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
return success_response(ProblemTagSerializer(ProblemTag.objects.all(), many=True).data)
|
||||
|
||||
@@ -189,75 +190,63 @@ class TestCaseUploadAPIView(APIView):
|
||||
return error_response(u"解压失败")
|
||||
name_list = test_case_file.namelist()
|
||||
|
||||
l = []
|
||||
|
||||
# 如果文件是直接打包的,那么name_list 就是["1.in", "1.out"]这样的
|
||||
if len(name_list) == 0:
|
||||
return error_response(u"压缩包内没有文件")
|
||||
|
||||
if "1.in" in name_list and "1.out" in name_list:
|
||||
for file_name in name_list:
|
||||
if self._is_legal_test_case_file_name(file_name):
|
||||
name = file_name.split(".")
|
||||
# 有了.in 判断对应的.out 在不在
|
||||
if name[1] == "in":
|
||||
if (name[0] + ".out") in name_list:
|
||||
l.append(file_name)
|
||||
else:
|
||||
return error_response(u"测试用例文件不完整,缺少" + name[0] + ".out")
|
||||
else:
|
||||
# 有了.out 判断对应的 .in 在不在
|
||||
if (name[0] + ".in") in name_list:
|
||||
l.append(file_name)
|
||||
else:
|
||||
return error_response(u"测试用例文件不完整,缺少" + name[0] + ".in")
|
||||
if len(name_list) % 2 == 1:
|
||||
return error_response(u"测试用例文件格式错误,文件数目为奇数")
|
||||
|
||||
problem_test_dir = rand_str()
|
||||
test_case_dir = settings.TEST_CASE_DIR + problem_test_dir + "/"
|
||||
for index in range(1, len(name_list) / 2 + 1):
|
||||
if not (str(index) + ".in" in name_list and str(index) + ".out" in name_list):
|
||||
return error_response(u"测试用例文件格式错误,缺少" + str(index) + u".in/.out文件")
|
||||
|
||||
# 得到了合法的测试用例文件列表 然后去解压缩
|
||||
os.mkdir(test_case_dir)
|
||||
for name in l:
|
||||
f = open(test_case_dir + name, "wb")
|
||||
try:
|
||||
f.write(test_case_file.read(name).replace("\r\n", "\n"))
|
||||
except MemoryError:
|
||||
return error_response(u"单个测试数据体积过大!")
|
||||
finally:
|
||||
f.close()
|
||||
l.sort()
|
||||
problem_test_dir = rand_str()
|
||||
test_case_dir = settings.TEST_CASE_DIR + problem_test_dir + "/"
|
||||
|
||||
file_info = {"test_case_number": len(l) / 2, "test_cases": {}}
|
||||
# 得到了合法的测试用例文件列表 然后去解压缩
|
||||
os.mkdir(test_case_dir)
|
||||
for name in name_list:
|
||||
f = open(test_case_dir + name, "wb")
|
||||
try:
|
||||
f.write(test_case_file.read(name).replace("\r\n", "\n"))
|
||||
except MemoryError:
|
||||
return error_response(u"单个测试数据体积过大!")
|
||||
finally:
|
||||
f.close()
|
||||
name_list.sort()
|
||||
|
||||
# 计算输出文件的md5
|
||||
for i in range(len(l) / 2):
|
||||
md5 = hashlib.md5()
|
||||
striped_md5 = hashlib.md5()
|
||||
f = open(test_case_dir + str(i + 1) + ".out", "r")
|
||||
# 完整文件的md5
|
||||
while True:
|
||||
data = f.read(2 ** 8)
|
||||
if not data:
|
||||
break
|
||||
md5.update(data)
|
||||
file_info = {"test_case_number": len(name_list) / 2, "test_cases": {}}
|
||||
|
||||
# 删除标准输出最后的空格和换行
|
||||
# 这时只能一次全部读入了,分块读的话,没办法确定文件结尾
|
||||
f.seek(0)
|
||||
striped_md5.update(f.read().rstrip())
|
||||
# 计算输出文件的md5
|
||||
for i in range(1, len(name_list) / 2 + 1):
|
||||
md5 = hashlib.md5()
|
||||
striped_md5 = hashlib.md5()
|
||||
f = open(test_case_dir + str(i) + ".out", "r")
|
||||
# 完整文件的md5
|
||||
while True:
|
||||
data = f.read(2 ** 8)
|
||||
if not data:
|
||||
break
|
||||
md5.update(data)
|
||||
|
||||
file_info["test_cases"][str(i + 1)] = {"input_name": str(i + 1) + ".in",
|
||||
"output_name": str(i + 1) + ".out",
|
||||
"output_md5": md5.hexdigest(),
|
||||
"striped_output_md5": striped_md5.hexdigest(),
|
||||
"input_size": os.path.getsize(test_case_dir + str(i + 1) + ".in"),
|
||||
"output_size": os.path.getsize(test_case_dir + str(i + 1) + ".out")}
|
||||
# 写入配置文件
|
||||
with open(test_case_dir + "info", "w") as f:
|
||||
f.write(json.dumps(file_info))
|
||||
# 删除标准输出最后的空格和换行
|
||||
# 这时只能一次全部读入了,分块读的话,没办法确定文件结尾
|
||||
f.seek(0)
|
||||
striped_md5.update(f.read().rstrip())
|
||||
|
||||
return success_response({"test_case_id": problem_test_dir,
|
||||
"file_list": file_info["test_cases"]})
|
||||
else:
|
||||
return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩")
|
||||
file_info["test_cases"][str(i)] = {"input_name": str(i) + ".in",
|
||||
"output_name": str(i) + ".out",
|
||||
"output_md5": md5.hexdigest(),
|
||||
"striped_output_md5": striped_md5.hexdigest(),
|
||||
"input_size": os.path.getsize(test_case_dir + str(i) + ".in"),
|
||||
"output_size": os.path.getsize(test_case_dir + str(i) + ".out")}
|
||||
# 写入配置文件
|
||||
with open(test_case_dir + "info", "w") as f:
|
||||
f.write(json.dumps(file_info))
|
||||
|
||||
return success_response({"test_case_id": problem_test_dir,
|
||||
"file_list": file_info["test_cases"]})
|
||||
|
||||
def get(self, request):
|
||||
test_case_id = request.GET.get("test_case_id", None)
|
||||
@@ -273,7 +262,6 @@ class TestCaseUploadAPIView(APIView):
|
||||
return success_response({"file_list": config["test_cases"]})
|
||||
|
||||
|
||||
|
||||
def problem_list_page(request, page=1):
|
||||
"""
|
||||
前台的问题列表
|
||||
@@ -306,7 +294,7 @@ def problem_list_page(request, page=1):
|
||||
return error_page(request, u"标签不存在")
|
||||
problems = tag.problem_set.all().filter(visible=True)
|
||||
|
||||
paginator = Paginator(problems, 20)
|
||||
paginator = Paginator(problems, 40)
|
||||
try:
|
||||
current_page = paginator.page(int(page))
|
||||
except Exception:
|
||||
|
||||
BIN
static/src/img/index/bg/bg0.jpg
Normal file
BIN
static/src/img/index/bg/bg0.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
static/src/img/index/bg/bg1.jpg
Normal file
BIN
static/src/img/index/bg/bg1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 530 KiB |
BIN
static/src/img/index/bg/bg2.jpg
Normal file
BIN
static/src/img/index/bg/bg2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
static/src/img/index/bg/bg3.jpg
Normal file
BIN
static/src/img/index/bg/bg3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@@ -1,4 +1,5 @@
|
||||
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "editorComponent"],
|
||||
require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
"csrfToken", "tagEditor", "validator", "editorComponent", "testCaseUploader"],
|
||||
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
|
||||
|
||||
avalon.ready(function () {
|
||||
@@ -7,7 +8,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
.on('submit', function (e) {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
e.preventDefault();
|
||||
if (vm.testCaseId == "") {
|
||||
if (!avalon.vmodels.testCaseUploader.uploaded) {
|
||||
bsAlert("你还没有上传测试数据!");
|
||||
return false;
|
||||
}
|
||||
@@ -35,7 +36,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
time_limit: vm.timeLimit,
|
||||
memory_limit: vm.memoryLimit,
|
||||
samples: [],
|
||||
test_case_id: vm.testCaseId,
|
||||
test_case_id: avalon.vmodels.testCaseUploader.testCaseId,
|
||||
hint: avalon.vmodels.contestProblemHintEditor.content,
|
||||
visible: vm.visible,
|
||||
contest_id: avalon.vmodels.admin.contestId,
|
||||
@@ -71,6 +72,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert(alertContent);
|
||||
avalon.vmodels.admin.template_url = "template/contest/problem_list.html";
|
||||
}
|
||||
else {
|
||||
bsAlert(data.data);
|
||||
@@ -97,7 +99,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
outputDescription: "",
|
||||
testCaseId: "",
|
||||
testCaseList: [],
|
||||
uploadSuccess: false,
|
||||
|
||||
contestProblemDescriptionEditor: {
|
||||
editorId: "contest-problem-description-editor",
|
||||
@@ -134,38 +135,20 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
});
|
||||
else {
|
||||
var vm = avalon.vmodels.editProblem;
|
||||
title = "";
|
||||
description = "";
|
||||
timeLimit = 1000;
|
||||
memoryLimit = 128;
|
||||
samples = [];
|
||||
hint = "";
|
||||
sortIndex = "";
|
||||
visible = true;
|
||||
inputDescription = "";
|
||||
outputDescription = "";
|
||||
testCaseId = "";
|
||||
testCaseList = [];
|
||||
uploadSuccess = false;
|
||||
vm.title = "";
|
||||
vm.description = "";
|
||||
vm.timeLimit = 1000;
|
||||
vm.memoryLimit = 128;
|
||||
vm.samples = [];
|
||||
vm.hint = "";
|
||||
vm.sortIndex = "";
|
||||
vm.visible = true;
|
||||
vm.inputDescription = "";
|
||||
vm.outputDescription = "";
|
||||
vm.testCaseId = "";
|
||||
vm.testCaseList = [];
|
||||
}
|
||||
|
||||
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) {
|
||||
if (response.code)
|
||||
bsAlert(response.data);
|
||||
else {
|
||||
vm.testCaseId = response.data.test_case_id;
|
||||
vm.testCaseList = [];
|
||||
for (var key in response.data.file_list) {
|
||||
vm.testCaseList.push({
|
||||
input: response.data.file_list[key].input_name,
|
||||
output: response.data.file_list[key].output_name
|
||||
})
|
||||
}
|
||||
vm.uploadSuccess = true;
|
||||
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
|
||||
}
|
||||
});
|
||||
|
||||
if (avalon.vmodels.admin.contestProblemStatus == "edit") {
|
||||
$.ajax({
|
||||
url: "/api/admin/contest_problem/?contest_problem_id=" + avalon.vmodels.admin.problemId,
|
||||
@@ -188,7 +171,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
vm.inputDescription = problem.input_description;
|
||||
vm.outputDescription = problem.output_description;
|
||||
vm.score = problem.score;
|
||||
vm.testCaseId = problem.test_case_id;
|
||||
avalon.vmodels.testCaseUploader.setTestCase(problem.test_case_id);
|
||||
vm.samples = [];
|
||||
for (var i = 0; i < problem.samples.length; i++) {
|
||||
vm.samples.push({
|
||||
@@ -198,26 +181,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
})
|
||||
}
|
||||
avalon.vmodels.contestProblemHintEditor.content = problem.hint;
|
||||
$.ajax({
|
||||
url: "/api/admin/test_case_upload/?test_case_id=" + vm.testCaseId,
|
||||
method: "get",
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
if (response.code) {
|
||||
bsAlert(response.data);
|
||||
}
|
||||
else {
|
||||
vm.testCaseList = [];
|
||||
for (var key in response.data.file_list) {
|
||||
vm.testCaseList.push({
|
||||
input: response.data.file_list[key].input_name,
|
||||
output: response.data.file_list[key].output_name
|
||||
})
|
||||
}
|
||||
vm.uploadSuccess = true;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent"],
|
||||
require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
"csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent", "testCaseUploader"],
|
||||
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
|
||||
avalon.ready(function () {
|
||||
|
||||
$("#add-problem-form").validator()
|
||||
.on('submit', function (e) {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
if (vm.testCaseId == "") {
|
||||
if (!avalon.vmodels.testCaseUploader.uploaded) {
|
||||
bsAlert("你还没有上传测试数据!");
|
||||
return false;
|
||||
}
|
||||
@@ -39,7 +40,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
time_limit: vm.timeLimit,
|
||||
memory_limit: vm.memoryLimit,
|
||||
samples: [],
|
||||
test_case_id: vm.testCaseId,
|
||||
test_case_id: avalon.vmodels.testCaseUploader.testCaseId,
|
||||
hint: avalon.vmodels.problemHintEditor.content,
|
||||
source: vm.source,
|
||||
visible: vm.visible,
|
||||
@@ -139,29 +140,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
}
|
||||
});
|
||||
|
||||
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/",
|
||||
function (file, response) {
|
||||
if (response.code) {
|
||||
vm.uploadProgress = 0;
|
||||
bsAlert(response.data);
|
||||
}
|
||||
else {
|
||||
vm.testCaseId = response.data.test_case_id;
|
||||
vm.uploadSuccess = true;
|
||||
vm.testCaseList = [];
|
||||
for (var key in response.data.file_list) {
|
||||
vm.testCaseList.push({
|
||||
input: response.data.file_list[key].input_name,
|
||||
output: response.data.file_list[key].output_name
|
||||
})
|
||||
}
|
||||
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
|
||||
}
|
||||
},
|
||||
function (file, percentage) {
|
||||
vm.uploadProgress = parseInt(percentage * 100);
|
||||
});
|
||||
|
||||
var tagAutoCompleteList = [];
|
||||
|
||||
$.ajax({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent"],
|
||||
require(["jquery", "avalon", "editor", "uploader", "bsAlert",
|
||||
"csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent", "testCaseUploader"],
|
||||
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
|
||||
|
||||
avalon.ready(function () {
|
||||
@@ -6,7 +7,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
$("#edit-problem-form").validator()
|
||||
.on('submit', function (e) {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
if (vm.testCaseId == "") {
|
||||
if (!avalon.vmodels.testCaseUploader.uploaded) {
|
||||
bsAlert("你还没有上传测试数据!");
|
||||
return false;
|
||||
}
|
||||
@@ -40,7 +41,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
time_limit: vm.timeLimit,
|
||||
memory_limit: vm.memoryLimit,
|
||||
samples: [],
|
||||
test_case_id: vm.testCaseId,
|
||||
test_case_id: avalon.vmodels.testCaseUploader.testCaseId,
|
||||
hint: avalon.vmodels.problemHintEditor.content,
|
||||
source: vm.source,
|
||||
visible: vm.visible,
|
||||
@@ -127,29 +128,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
avalon.vmodels.admin.template_url = "template/problem/problem_list.html";
|
||||
}
|
||||
});
|
||||
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/",
|
||||
function (file, response) {
|
||||
if (response.code) {
|
||||
vm.uploadProgress = 0;
|
||||
bsAlert(response.data);
|
||||
}
|
||||
else {
|
||||
vm.testCaseId = response.data.test_case_id;
|
||||
vm.uploadSuccess = true;
|
||||
vm.testCaseList = [];
|
||||
for(var key in response.data.file_list){
|
||||
vm.testCaseList.push({
|
||||
input: response.data.file_list[key].input_name,
|
||||
output: response.data.file_list[key].output_name
|
||||
})
|
||||
}
|
||||
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
|
||||
}
|
||||
},
|
||||
function (file, percentage) {
|
||||
vm.uploadProgress = parseInt(percentage * 100);
|
||||
}
|
||||
);
|
||||
|
||||
$.ajax({
|
||||
url: "/api/admin/problem/?problem_id=" + avalon.vmodels.admin.problemId,
|
||||
@@ -178,7 +156,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
vm.difficulty = problem.difficulty;
|
||||
vm.inputDescription = problem.input_description;
|
||||
vm.outputDescription = problem.output_description;
|
||||
vm.testCaseId = problem.test_case_id;
|
||||
avalon.vmodels.testCaseUploader.setTestCase(problem.test_case_id);
|
||||
vm.source = problem.source;
|
||||
var problemTags = problem.tags;
|
||||
$.ajax({
|
||||
|
||||
39
static/src/js/app/oj/account/applyResetPassword.js
Normal file
39
static/src/js/app/oj/account/applyResetPassword.js
Normal file
@@ -0,0 +1,39 @@
|
||||
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
||||
var applied_captcha = false;
|
||||
$('form').validator().on('submit', function (e) {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
var email = $("#email").val();
|
||||
var captcha = $("#captcha").val();
|
||||
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/apply_reset_password/",
|
||||
data: {email: email, captcha: captcha},
|
||||
dataType: "json",
|
||||
method: "post",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
refresh_captcha();
|
||||
bsAlert(data.data);
|
||||
}
|
||||
else {
|
||||
refresh_captcha();
|
||||
bsAlert(data.data);
|
||||
}
|
||||
},
|
||||
error: function(){
|
||||
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
|
||||
}
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
function refresh_captcha(){
|
||||
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
||||
$("#captcha")[0].value = "";
|
||||
}
|
||||
$("#captcha-img").click(function(){
|
||||
refresh_captcha();
|
||||
});
|
||||
});
|
||||
39
static/src/js/app/oj/account/avatar.js
Normal file
39
static/src/js/app/oj/account/avatar.js
Normal file
@@ -0,0 +1,39 @@
|
||||
require(["jquery", "bsAlert", "csrfToken"], function ($, bsAlert, csrfTokenHeader) {
|
||||
var avatar = "";
|
||||
|
||||
function changeAvatar(event) {
|
||||
avatar = $(event.target).attr('src');
|
||||
$('#current_avatar').attr('src', avatar);
|
||||
}
|
||||
|
||||
$('.avatar-item').click(changeAvatar);
|
||||
|
||||
$('#save_avatar').click(function () {
|
||||
if (avatar)
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/account/userprofile/",
|
||||
data: {
|
||||
avatar: avatar
|
||||
},
|
||||
dataType: "json",
|
||||
method: "put",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert("已保存!");
|
||||
}
|
||||
else {
|
||||
bsAlert(data.data);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
|
||||
}
|
||||
|
||||
});
|
||||
else
|
||||
bsAlert("请选择一个头像");
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,13 +3,14 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
||||
if (!e.isDefaultPrevented()) {
|
||||
var username = $("#username").val();
|
||||
var realName = $("#real_name").val();
|
||||
var school = $('#school').val();
|
||||
var password = $("#password").val();
|
||||
var email = $("#email").val();
|
||||
var captcha = $("#captcha").val();
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/register/",
|
||||
data: {username: username, real_name: realName, password: password, email: email, captcha:captcha},
|
||||
data: {username: username, school: school, real_name: realName, password: password, email: email, captcha:captcha},
|
||||
dataType: "json",
|
||||
method: "post",
|
||||
success: function (data) {
|
||||
|
||||
41
static/src/js/app/oj/account/resetPassword.js
Normal file
41
static/src/js/app/oj/account/resetPassword.js
Normal file
@@ -0,0 +1,41 @@
|
||||
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
||||
var applied_captcha = false;
|
||||
$('form').validator().on('submit', function (e) {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
var index = location.href.indexOf("/t/");
|
||||
var token = location.href.substr(36+3, 32);
|
||||
var captcha = $("#captcha").val();
|
||||
var password = $("#new_password").val();
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/reset_password/",
|
||||
data: {password: password, captcha: captcha, token:token},
|
||||
dataType: "json",
|
||||
method: "post",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
refresh_captcha();
|
||||
bsAlert(data.data);
|
||||
window.location.href = "/login/";
|
||||
}
|
||||
else {
|
||||
refresh_captcha();
|
||||
bsAlert(data.data);
|
||||
}
|
||||
},
|
||||
error: function(){
|
||||
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
|
||||
}
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
function refresh_captcha(){
|
||||
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
||||
$("#captcha")[0].value = "";
|
||||
}
|
||||
$("#captcha-img").click(function(){
|
||||
refresh_captcha();
|
||||
});
|
||||
});
|
||||
43
static/src/js/app/oj/account/settings.js
Normal file
43
static/src/js/app/oj/account/settings.js
Normal file
@@ -0,0 +1,43 @@
|
||||
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
||||
$('form').validator().on('submit', function (e) {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
var phone = $("#phone").val();
|
||||
var hduoj_username = $("#hduoj_username").val();
|
||||
var bestcoder_username = $("#bestcoder_username").val();
|
||||
var codeforces_username = $("#codeforces_username").val();
|
||||
var blog = $("#blog").val();
|
||||
var mood = $("#mood").val();
|
||||
var school = $("#school").val();
|
||||
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/account/userprofile/",
|
||||
data: {
|
||||
phone_number: phone,
|
||||
hduoj_username: hduoj_username,
|
||||
bestcoder_username: bestcoder_username,
|
||||
codeforces_username: codeforces_username,
|
||||
blog: blog,
|
||||
mood: mood,
|
||||
school: school
|
||||
},
|
||||
dataType: "json",
|
||||
method: "put",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert("修改成功");
|
||||
}
|
||||
else{
|
||||
bsAlert(data.data);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
|
||||
}
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
// ------ admin web 组件 ----------
|
||||
pager: "components/pager",
|
||||
editorComponent: "components/editorComponent",
|
||||
testCaseUploader: "components/testCaseUploader",
|
||||
|
||||
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
|
||||
//富文本编辑器simditor -> editor
|
||||
@@ -74,6 +75,7 @@
|
||||
groupDetail_20_pack: "app/admin/group/groupDetail",
|
||||
editContest_21_pack: "app/admin/contest/editContest",
|
||||
group_22_pack: "app/admin/group/group",
|
||||
settings_23_pack: "app/oj/account/settings"
|
||||
},
|
||||
shim: {
|
||||
avalon: {
|
||||
@@ -158,7 +160,10 @@
|
||||
},
|
||||
{
|
||||
name: "group_22_pack"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "settings_23_pack"
|
||||
},
|
||||
],
|
||||
optimizeCss: "standard",
|
||||
})
|
||||
83
static/src/js/components/testCaseUploader.js
Normal file
83
static/src/js/components/testCaseUploader.js
Normal file
@@ -0,0 +1,83 @@
|
||||
define("testCaseUploader", ["avalon", "uploader", "bsAlert", "jquery"], function(avalon, uploader, bsAlert, $){
|
||||
avalon.component("ms:testcaseuploader", {
|
||||
$template: '<div class="col-md-12">' +
|
||||
'<br> ' +
|
||||
'<label>测试数据</label><br> ' +
|
||||
'<small class="text-info">' +
|
||||
'请将所有测试用例打包在一个文件中上传,' +
|
||||
'所有文件要在压缩包的根目录,' +
|
||||
'且输入输出文件名要以从1开始连续数字标识要对应例如:' +
|
||||
'<br>1.in 1.out 2.in 2.out </small> ' +
|
||||
'<p>上传进度<span ms-text="uploadProgress"></span>%</p> ' +
|
||||
'<table class="table table-striped" ms-visible="uploaded"> ' +
|
||||
'<tr> <td>编号</td> <td>输入文件名</td> <td>输出文件名</td> </tr> ' +
|
||||
'<tr ms-repeat="testCaseList"> ' +
|
||||
'<td>{{ $index + 1 }}</td> ' +
|
||||
'<td>{{ el.input }}</td> ' +
|
||||
'<td>{{ el.output }}</td> </tr> ' +
|
||||
'</table> ' +
|
||||
'</div> ' +
|
||||
'<div class="col-md-12"> ' +
|
||||
'<div class="form-group">' +
|
||||
' <div id="testCaseFileSelector">选择文件</div> ' +
|
||||
'</div> ' +
|
||||
'</div>',
|
||||
testCaseId: "",
|
||||
testCaseList: [],
|
||||
uploaded: false,
|
||||
uploadProgress: 0,
|
||||
|
||||
setTestCaseId: function(){},
|
||||
|
||||
$init: function(vm, el){
|
||||
vm.setTestCase = function(testCaseId){
|
||||
vm.testCaseId = testCaseId;
|
||||
$.ajax({
|
||||
url: "/api/admin/test_case_upload/?test_case_id=" + testCaseId,
|
||||
method: "get",
|
||||
success: function(data){
|
||||
if(data.code){
|
||||
bsAlert("获取测试用例列表失败");
|
||||
}
|
||||
else{
|
||||
for(var key in data.data.file_list){
|
||||
vm.testCaseList.push({
|
||||
input: data.data.file_list[key].input_name,
|
||||
output: data.data.file_list[key].output_name
|
||||
})
|
||||
}
|
||||
vm.uploaded = true;
|
||||
vm.uploadProgress = 100;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
$ready: function(vm, el){
|
||||
el.msRetain = true;
|
||||
var testCaseUploader = uploader("#testCaseFileSelector", "/api/admin/test_case_upload/",
|
||||
function (file, response) {
|
||||
if (response.code) {
|
||||
bsAlert(response.data);
|
||||
}
|
||||
else {
|
||||
vm.testCaseId = response.data.test_case_id;
|
||||
vm.uploaded = true;
|
||||
vm.testCaseList = [];
|
||||
for(var key in response.data.file_list){
|
||||
vm.testCaseList.push({
|
||||
input: response.data.file_list[key].input_name,
|
||||
output: response.data.file_list[key].output_name
|
||||
})
|
||||
}
|
||||
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
|
||||
}
|
||||
},
|
||||
function (file, percentage) {
|
||||
vm.uploadProgress = parseInt(percentage * 100);
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -27,6 +27,7 @@ var require = {
|
||||
// ------ admin web 组件 ----------
|
||||
pager: "components/pager",
|
||||
editorComponent: "components/editorComponent",
|
||||
testCaseUploader: "components/testCaseUploader",
|
||||
|
||||
|
||||
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
|
||||
@@ -76,6 +77,7 @@ var require = {
|
||||
groupDetail_20_pack: "app/admin/group/groupDetail",
|
||||
editContest_21_pack: "app/admin/contest/editContest",
|
||||
group_22_pack: "app/admin/group/group",
|
||||
settings_23_pack: "app/oj/account/settings"
|
||||
},
|
||||
shim: {
|
||||
avalon: {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<div ms-controller="add_contest">
|
||||
<form id="add-contest-form">
|
||||
<div class="col-md-9">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>注意!管理员在测试比赛题目的时候请务必保持比赛状态为没有开始,这时只有管理员可以正常查看和提交题目,而且不会产生排名。</p>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label>比赛名称</label>
|
||||
</div>
|
||||
|
||||
@@ -41,153 +41,5 @@
|
||||
<ms:pager $id="contestListPager" config="pager"></ms:pager>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div ms-visible="editingContestId">
|
||||
<form id="edit-contest-form">
|
||||
<div class="col-md-12">
|
||||
<label>比赛名称</label>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<input type="text" name="name" class="form-control" ms-duplex="editTitle"
|
||||
data-error="请填写比赛名称(名称不能超过50个字)" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label>说明</label>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="editDescription"></textarea>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
<p class="error-info" ms-visible="editDescription==''">请填写比赛描述</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label>开始时间</label>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="start_time" id="contest_start_time"
|
||||
ms-duplex="editStartTime" data-error="请填写比赛开始时间" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label>结束时间</label>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="end_time" id="contest_end_time"
|
||||
ms-duplex="editEndTime" data-error="请填写比赛结束时间" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>可见范围</label>
|
||||
<span ms-if="showGlobalViewRadio">
|
||||
<label>
|
||||
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
|
||||
</small>
|
||||
</label>
|
||||
</span>
|
||||
<span>
|
||||
<label>
|
||||
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
|
||||
</small>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6" ms-visible="isGlobal">
|
||||
<label>密码保护</label>
|
||||
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword">
|
||||
</div>
|
||||
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
||||
|
||||
<div ms-repeat="allGroups" class="col-md-4">
|
||||
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected"> {{ el.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label>排名方式</label>
|
||||
|
||||
<div class="form-group">
|
||||
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="0">
|
||||
<small>ACM</small>
|
||||
</label>
|
||||
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="1">
|
||||
<small>分数</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label>是否可见</label>
|
||||
<label><input type="checkbox" ms-duplex-checked="editVisible">
|
||||
<small> 可见</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label>公开提交记录</label>
|
||||
<div class="form-group">
|
||||
<label class="text"><input type="checkbox" ms-duplex-checked="editShowSubmission">
|
||||
<small>公开</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label>实时排名</label>
|
||||
<div class="form-group">
|
||||
<label class="text"><input type="checkbox" ms-duplex-checked="editRealTimeRank">
|
||||
<small>是</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-success" type="submit">保存修改</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-12" ms-visible="editingProblemContestIndex">
|
||||
<label>题目列表</label>
|
||||
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="addProblem()">添加题目</a>
|
||||
<a href="javascript:void(0)" class="btn btn-info btn-sm" ms-click="showSubmissionPage()">查看提交</a>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>题目</th>
|
||||
<th ms-visible="editMode=='2'">分值</th>
|
||||
<th>可见</th>
|
||||
<th>创建时间</th>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr ms-repeat="editProblemList">
|
||||
<td>{{ el.sort_index }}</td>
|
||||
<td>{{ el.title }}</td>
|
||||
<td ms-visible="editMode=='2'">{{ el.score }}</td>
|
||||
<td ms-text="el.visible?'可见':'不可见'"></td>
|
||||
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss") }}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)" class="btn-sm btn-info"
|
||||
ms-click="showProblemEditPage(el)">编辑</a>
|
||||
<a href="javascript:void(0)" class="btn-sm btn-info"
|
||||
ms-click="showSubmissionPage(el)">提交</a>
|
||||
<a href="javascript:void(0)" class="btn-sm btn-info"
|
||||
ms-click="addToProblemList(el)" ms-visible="admin_type=='2'">添加到前台</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
-->
|
||||
|
||||
|
||||
</div>
|
||||
<script src="/static/js/app/admin/contest/contestList.js"></script>
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
aria-hidden="true">←</span> 返回</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>注意!管理员在测试比赛题目的时候请务必保持比赛状态为没有开始,这时只有管理员可以正常查看和提交题目,而且不会产生排名。</p>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label>比赛名称</label>
|
||||
</div>
|
||||
|
||||
@@ -96,29 +96,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12"><br>
|
||||
<label>测试数据<span ms-if="uploadSuccess">(当前已上传,继续上传将覆盖原有测试用例)</span></label><br>
|
||||
<small class="text-info">请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:<br>
|
||||
1.in 1.out 2.in 2.out
|
||||
</small>
|
||||
<table class="table table-striped" ms-visible="uploadSuccess">
|
||||
<tr>
|
||||
<td>编号</td>
|
||||
<td>输入文件名</td>
|
||||
<td>输出文件名</td>
|
||||
</tr>
|
||||
<tr ms-repeat="testCaseList">
|
||||
<td>{{ $index + 1 }}</td>
|
||||
<td>{{ el.input }}</td>
|
||||
<td>{{ el.output }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<div id="testCaseFile">选择文件</div>
|
||||
</div>
|
||||
</div>
|
||||
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
|
||||
<div class="form-group col-md-12">
|
||||
<label>提示</label>
|
||||
<ms:editor $id="contestProblemHintEditor" config="contestProblemHintEditor"></ms:editor>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<td>{{ el.total_accepted_number }}/{{ el.total_submit_number }}</td>
|
||||
<td>
|
||||
<button class="btn-sm btn-info" ms-click="showEditProblemPage(el.id)">编辑</button>
|
||||
<button class="btn-sm btn-primary" ms-if="!el.is_public && adminType == 2" ms-click="makeProblemPublic(el)">公开</button>
|
||||
<button class="btn-sm btn-primary" ms-if="!el.is_public && adminType == 2 && el.visible" ms-click="makeProblemPublic(el)">公开</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -99,30 +99,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12"><br>
|
||||
<label>测试数据</label><br>
|
||||
<small class="text-info">请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:<br>
|
||||
1.in 1.out 2.in 2.out
|
||||
</small>
|
||||
<p>上传进度<span ms-text="uploadProgress"></span>%</p>
|
||||
<table class="table table-striped" ms-visible="uploadSuccess">
|
||||
<tr>
|
||||
<td>编号</td>
|
||||
<td>输入文件名</td>
|
||||
<td>输出文件名</td>
|
||||
</tr>
|
||||
<tr ms-repeat="testCaseList">
|
||||
<td>{{$index + 1}}</td>
|
||||
<td>{{ el.input }}</td>
|
||||
<td>{{ el.output }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<div id="testCaseFile">选择文件</div>
|
||||
</div>
|
||||
</div>
|
||||
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
|
||||
<div class="form-group col-md-12">
|
||||
<label>提示</label>
|
||||
<ms:editor $id="problemHintEditor" config="problemHintEditor"></ms:editor>
|
||||
|
||||
@@ -105,30 +105,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12"><br>
|
||||
<label>测试数据(当前已上传,继续上传将覆盖原有测试用例)</label><br>
|
||||
<small class="text-info">请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:<br>
|
||||
1.in 1.out 2.in 2.out
|
||||
</small>
|
||||
<p>上传进度<span ms-text="uploadProgress"></span>%</p>
|
||||
<table class="table table-striped" ms-visible="uploadSuccess">
|
||||
<tr>
|
||||
<td>编号</td>
|
||||
<td>输入文件名</td>
|
||||
<td>输出文件名</td>
|
||||
</tr>
|
||||
<tr ms-repeat="testCaseList">
|
||||
<td>{{ $index + 1 }}</td>
|
||||
<td>{{ el.input }}</td>
|
||||
<td>{{ el.output }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<div id="testCaseFile">选择文件</div>
|
||||
</div>
|
||||
</div>
|
||||
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
|
||||
<div class="form-group col-md-12">
|
||||
<label>提示</label>
|
||||
<ms:editor $id="problemHintEditor" config="problemHintEditor"></ms:editor>
|
||||
|
||||
40
template/src/oj/account/apply_reset_password.html
Normal file
40
template/src/oj/account/apply_reset_password.html
Normal file
@@ -0,0 +1,40 @@
|
||||
{% extends "oj_base.html" %}
|
||||
{% block title %}
|
||||
找回登录信息
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="container main">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<h2 class="text-center">找回登录信息</h2><br>
|
||||
<div>
|
||||
<p>请输入你注册时使用的邮箱地址,系统将自动向你的邮箱发送一封含有您登录信息的电子邮件,
|
||||
你可以看到你的用户名,并可以选择重新设置登录密码,注意为了你的账户安全,重置密码链接仅在30分钟内有效</p>
|
||||
</div>
|
||||
<br>
|
||||
<form id="login-form">
|
||||
<div class="form-group">
|
||||
<label for="email">注册电子邮件地址</label>
|
||||
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址"
|
||||
data-remote="/api/email_check/?reset=true" data-remote-error="该邮箱未被注册!" data-error="请填写正确的邮箱地址"
|
||||
required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group" id="captcha-area">
|
||||
<label for="captcha">验证码</label> <img src="/captcha/" id="captcha-img">
|
||||
<small><p></p></small>
|
||||
<input type="text" class="form-control input-lg" id="captcha" name="captcha"
|
||||
placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js_block %}
|
||||
<script src="/static/js/app/oj/account/applyResetPassword.js"></script>
|
||||
{% endblock %}
|
||||
@@ -13,28 +13,39 @@
|
||||
<li class="list-group-item"><a href="/change_password/">修改密码</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<img src="https://coding.net/static/fruit_avatar/Fruit-1.png" class="img-responsive"
|
||||
style="height: 200px;width: 200px;">
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<form>
|
||||
<div class="row">
|
||||
{% for i in "aaaaaaaaaaaaaaaaaaaa" %}
|
||||
<div class="col-lg-2 avatar-item">
|
||||
<div style="border: 1px solid red;" >
|
||||
<img src="/static/img/avatar/avatar-{{ forloop.counter }}.png">
|
||||
<div class="col-lg-3">
|
||||
<img src="{{ user.userprofile.avatar }}" class="img-responsive"
|
||||
style="height: 200px;width: 200px;" id="current_avatar">
|
||||
<br>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div>
|
||||
<div class="row">
|
||||
{% for i in "aaaaaaaaaaaaaaaaaaaa" %}
|
||||
<div class="col-lg-2 avatar-item">
|
||||
<div>
|
||||
<img src="/static/img/avatar/avatar-{{ forloop.counter }}.png" style="cursor:pointer">
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
|
||||
<div title="Coding.net 是深圳市扣钉网络科技有限公司打造的国内最大的一站式云端开发平台,提供包括代码托管,项目管理,产品演示,WebIDE 等工具。">
|
||||
<img src="https://coding.net/static/d0aa9dd75e2500930b9e6ef61d9bcfa0.jpg"
|
||||
style="height: 100px;width: 150px">
|
||||
水果头像由<a href="https://coding.net">coding.net</a>提供。
|
||||
</div>
|
||||
<div class="form-group left">
|
||||
<button id="save_avatar" type="submit" class="btn btn-primary">保存</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
<div title="Coding.net 是深圳市扣钉网络科技有限公司打造的国内最大的一站式云端开发平台,提供包括代码托管,项目管理,产品演示,WebIDE 等工具。">
|
||||
<img src="https://coding.net/static/d0aa9dd75e2500930b9e6ef61d9bcfa0.jpg" style="height: 100px;width: 150px">
|
||||
水果头像由<a href="https://coding.net">coding.net</a>提供。
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_block %}
|
||||
<script src="/static/js/app/oj/account/avatar.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,41 +1,61 @@
|
||||
{% extends "oj_base.html" %}
|
||||
{% block title %}
|
||||
用户修改密码
|
||||
用户设置
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="container main">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<h2 class="text-center">修改密码</h2>
|
||||
<div class="container main">
|
||||
|
||||
<form id="change_password-form">
|
||||
<div class="form-group">
|
||||
<label for="password">旧密码</label>
|
||||
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码" data-error="请填写旧密码" maxlength="30" required>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new_password">新密码</label>
|
||||
<input type="password" class="form-control input-lg" id="new_password" name="new_password" placeholder="新密码" maxlength="30" data-minlength="6" data-error="密码不得少于6位" required>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">确认密码</label>
|
||||
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password" placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两个密码不一致" required>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="captcha">验证码</label>
|
||||
<img src="/captcha/" id="captcha-img">
|
||||
<small><p></p></small>
|
||||
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-header">通用设置</li>
|
||||
<li class="list-group-item"><a href="/account/settings/">个人信息</a></li>
|
||||
<li class="list-group-item"><a href="/account/settings/avatar/">更换头像</a></li>
|
||||
<li class="list-group-item active"><a href="/change_password/">修改密码</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<img src="{{ user.userprofile.avatar }}" class="img-responsive"
|
||||
style="height: 200px;width: 200px;">
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<form id="change_password-form">
|
||||
<div class="form-group">
|
||||
<label for="password">旧密码</label>
|
||||
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码"
|
||||
data-error="请填写旧密码" maxlength="30" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new_password">新密码</label>
|
||||
<input type="password" class="form-control input-lg" id="new_password" name="new_password"
|
||||
placeholder="新密码" maxlength="30" data-minlength="6" data-error="密码不得少于6位" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">确认密码</label>
|
||||
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
|
||||
placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两次密码不一致" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="captcha">验证码</label>
|
||||
<img src="/captcha/" id="captcha-img">
|
||||
<small><p></p></small>
|
||||
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码"
|
||||
maxlength="4" data-error="请填写验证码" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js_block %}
|
||||
<script src="/static/js/app/oj/account/changePassword.js"></script>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
<a href="/reset_password/">忘记用户名/密码</a><br>
|
||||
<a href="/register/">还没有帐号?点击注册</a>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
<input type="text" class="form-control input-lg" id="real_name" name="real_name" placeholder="真实姓名" data-error="请填写姓名" required>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="real_name">学校</label>
|
||||
<input type="text" class="form-control input-lg" id="school" name="school" placeholder="学校" data-error="请填写学校" required>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">邮箱地址</label>
|
||||
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-remote="/api/email_check/" data-remote-error="该邮箱已被注册!" data-error="请填写正确的邮箱地址" required>
|
||||
|
||||
@@ -1,10 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
{% extends "oj_base.html" %}
|
||||
{% block title %}
|
||||
找回登录信息
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="container main">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<h2 class="text-center">找回登录信息</h2><br>
|
||||
<br>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<form id="login-form">
|
||||
<div class="form-group">
|
||||
<label for="email">注册电子邮件地址</label>
|
||||
<input type="text" class="form-control input-lg" name="email" value="{{ user.email }}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" class="form-control input-lg" name="username" value="{{ user.username }}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new_password">新密码</label>
|
||||
<input type="password" class="form-control input-lg" id="new_password" name="new_password"
|
||||
placeholder="新密码" maxlength="30" data-minlength="6" data-error="密码不得少于6位" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">确认密码</label>
|
||||
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
|
||||
placeholder="确认密码" maxlength="30" data-match="#new_password" data-error="请输入确认密码"
|
||||
data-match-error="两次密码不一致"
|
||||
required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group" id="captcha-area">
|
||||
<label for="captcha">验证码</label> <img src="/captcha/" id="captcha-img">
|
||||
<small><p></p></small>
|
||||
<input type="text" class="form-control input-lg" id="captcha" name="captcha"
|
||||
placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js_block %}
|
||||
<script src="/static/js/app/oj/account/resetPassword.js"></script>
|
||||
{% endblock %}
|
||||
@@ -1,21 +1,20 @@
|
||||
{% extends "oj_base.html" %}
|
||||
{% block title %}
|
||||
用户设置
|
||||
用户设置
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="container main">
|
||||
<div class="container main">
|
||||
|
||||
<div class="col-lg-2">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-header">通用设置</li>
|
||||
<li class="list-group-item active"><a href="/account/settings/">个人信息</a></li>
|
||||
<li class="list-group-item"><a href="/account/settings/avatar/">更换头像</a></li>
|
||||
<li class="list-group-item"><a href="/change_password/">修改密码</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-header">通用设置</li>
|
||||
<li class="list-group-item active"><a href="/account/settings/">个人信息</a></li>
|
||||
<li class="list-group-item"><a href="/account/settings/avatar/">更换头像</a></li>
|
||||
<li class="list-group-item"><a href="/change_password/">修改密码</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<img src="https://coding.net/static/fruit_avatar/Fruit-1.png" class="img-responsive"
|
||||
style="height: 200px;width: 200px;">
|
||||
<img src="{{ user.userprofile.avatar }}" class="img-responsive index-avatar">
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<form>
|
||||
@@ -23,57 +22,78 @@
|
||||
<div class="form-group col-md-6"><label>用户名</label>
|
||||
<input name="username" type="text" class="form-control"
|
||||
value="{{ request.user.username }}" readonly>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-6"><label>真实姓名</label>
|
||||
<input name="real_name" type="text" class="form-control"
|
||||
value="{{ request.user.real_name }}" readonly>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6"><label>电子邮箱</label>
|
||||
<input name="email" type="email" class="form-control"
|
||||
value="{{ request.user.email }}" readonly>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group col-md-6"><label>手机</label>
|
||||
<input name="phone" type="text" class="form-control"
|
||||
data-error="请填写手机号码" value="{{ request.user.phone }}">
|
||||
<div class="help-block with-errors"></div>
|
||||
<input name="phone" type="text" maxlength="11" id="phone"
|
||||
class="form-control"
|
||||
value="{% if request.user.userprofile.phone_number %}{{ request.user.userprofile.phone_number }}{% endif %}">
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>学校</label>
|
||||
<input name="school" type="text" class="form-control" id="school"
|
||||
value="{% if request.user.userprofile.school %}{{ request.user.userprofile.school }}{% endif %}">
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>hduoj 用户名</label>
|
||||
<input name=hduoj" type="text" class="form-control"
|
||||
value="{{ request.user.hduoj_username }}">
|
||||
<input name="hduoj" type="text" class="form-control" id="hduoj_username"
|
||||
value="{{ request.user.userprofile.hduoj_username }}">
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>BestCoder 用户名</label>
|
||||
<input name=bestcoder" type="text" class="form-control"
|
||||
value="{{ request.user.bestcoder_username }}">
|
||||
<input name="bestcoder" type="text" class="form-control" id="bestcoder_username"
|
||||
value="{{ request.user.userprofile.bestcoder_username }}">
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>Codeforces 用户名</label>
|
||||
<input name=codeforces" type="text" class="form-control"
|
||||
value="{{ request.user.bestcoder_username }}">
|
||||
<input name="codeforces" type="text" class="form-control" id="codeforce_username"
|
||||
value="{{ request.user.userprofile.bestcoder_username }}">
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group col-md-12"><label>blog</label>
|
||||
<input name=blog" type="url" class="form-control"
|
||||
value="{{ request.user.blog }}">
|
||||
<input name="blog" type="url" class="form-control" id="blog"
|
||||
value="{{ request.user.userprofile.blog }}">
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label>签名</label>
|
||||
<input name="mood" type="text" maxlength="60" class="form-control" id="mood"
|
||||
value="{% if request.user.userprofile.mood %}{{ request.user.userprofile.mood }}{% endif %}"
|
||||
data-error="字数限制在30字以内">
|
||||
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_block %}
|
||||
<script src="/static/js/app/oj/account/settings.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
{% endifequal %}
|
||||
|
||||
</h2>
|
||||
<p id="user-mood">{{ user.userprofile.mood }}</p>
|
||||
{% if user.userprofile.mood %}
|
||||
<p id="user-mood">{{ user.userprofile.mood }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="list-group col-lg-9">
|
||||
|
||||
@@ -32,7 +34,7 @@
|
||||
{% if user.userprofile.hduoj_username %}
|
||||
<p class="list-group-item">
|
||||
<img src="/static/img/oj_logo/hdu_logo.png" id="oj-logo">
|
||||
<a href="http://bestcoder.hdu.edu.cn/rating.php?user={{ user.userprofile.hduoj_username }}" target="_blank">
|
||||
<a href="http://acm.hdu.edu.cn/userstatus.php?user={{ user.userprofile.hduoj_username }}" target="_blank">
|
||||
{{ user.userprofile.hduoj_username }}
|
||||
</a>
|
||||
</p>
|
||||
@@ -61,15 +63,17 @@
|
||||
{{ user.create_time }}
|
||||
</p>
|
||||
<div class="rows">
|
||||
<!--
|
||||
<div class="col-lg-4 text-center">
|
||||
<strong id="user-data-number">{{ user.userprofile.rank }}</strong>
|
||||
<span id="user-data-text">Rank</span>
|
||||
</div>
|
||||
<div class="col-lg-4 text-center">
|
||||
-->
|
||||
<div class="col-lg-6 text-center">
|
||||
<strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
|
||||
<span id="user-data-text">AC</span>
|
||||
</div>
|
||||
<div class="col-lg-4 text-center">
|
||||
<div class="col-lg-6 text-center">
|
||||
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
|
||||
<span id="user-data-text">Submissions</span>
|
||||
</div>
|
||||
|
||||
@@ -72,24 +72,27 @@
|
||||
onchange="if(this.checked){location.href=location.href + '&auto_refresh=true'}else{location.href=location.href=location.href.replace('&auto_refresh=true', '')}">
|
||||
自动刷新
|
||||
<nav>
|
||||
<ul class="pager">
|
||||
{% if paging_info.previous_page %}
|
||||
<li class="previous">
|
||||
<a href="/contest/{{ contest.id }}/rank/?paging=true&page={{ paging_info.previous_page }}&page_size={{ paging_info.page_size }}{% if auto_refresh %}&auto_refresh=true{% endif %}">
|
||||
<ul class="pager">
|
||||
{% if paging_info.previous_page %}
|
||||
<li class="previous">
|
||||
<a href="/contest/{{ contest.id }}/rank/?paging=true&page={{ paging_info.previous_page }}&page_size={{ paging_info.page_size }}{% if auto_refresh %}&auto_refresh=true{% endif %}">
|
||||
|
||||
<span aria-hidden="true">←</span> 上一页
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if paging_info.next_page %}
|
||||
<li class="next">
|
||||
<a href="/contest/{{ contest.id }}/rank/?paging=true&page={{ paging_info.next_page }}&page_size={{ paging_info.page_size }}{% if auto_refresh %}&auto_refresh=true{% endif %}">
|
||||
下一页 <span aria-hidden="true">→</span>
|
||||
</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
<span aria-hidden="true">←</span> 上一页
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if paging_info.next_page %}
|
||||
<li class="next">
|
||||
<a href="/contest/{{ contest.id }}/rank/?paging=true&page={{ paging_info.next_page }}&page_size={{ paging_info.page_size }}{% if auto_refresh %}&auto_refresh=true{% endif %}">
|
||||
下一页 <span aria-hidden="true">→</span>
|
||||
</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% else %}
|
||||
<p>还没有结果</p>
|
||||
<input type="checkbox" id="auto-refresh" {% if auto_refresh %}checked{% endif %}
|
||||
onchange="if(this.checked){location.href=location.href + '&auto_refresh=true'}else{location.href=location.href=location.href.replace('&auto_refresh=true', '')}">
|
||||
自动刷新
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -100,6 +100,15 @@
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.section{
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#section0{
|
||||
background-image: url(/static/img/index/bg/bg3.jpg);
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
<link rel="stylesheet" href="/static/css/animate/animate.css">
|
||||
|
||||
@@ -110,12 +119,21 @@
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('#fullpage').fullpage({
|
||||
sectionsColor: ['#28ac72', '#2f7ddb', '#FAC832', '#B01414'],
|
||||
sectionsColor: ['#ffffff', '#2f7ddb', '#FAC832', '#B01414'],
|
||||
css3: true,
|
||||
navigation: true,
|
||||
loopBottom: true
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
var c = 0;
|
||||
|
||||
setInterval(function(){
|
||||
//alert(1);
|
||||
$("#section0").css("background-image", "url(/static/img/index/bg/bg" + (c++ % 4).toString() + ".jpg)")
|
||||
}, 1000)
|
||||
*/
|
||||
</script>
|
||||
|
||||
</head>
|
||||
@@ -172,7 +190,7 @@
|
||||
<div class="section-text">
|
||||
<img class="icon" id="img3" src="/static/img/index/m.png">
|
||||
|
||||
<h1>自由举办小组赛(10月上线)</h1>
|
||||
<h1>自由举办小组赛(12月上线)</h1>
|
||||
|
||||
<h3>内部比赛,日常作业,期末考试,通通搞定</h3>
|
||||
</div>
|
||||
|
||||
@@ -61,9 +61,11 @@
|
||||
<ul class="dropdown-menu">
|
||||
{% if request.user.admin_type > 0 %}
|
||||
<li><a href="/admin/">后台管理</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="/user/{{ request.user.username }}/">我的主页</a></li>
|
||||
<li><a href="/submissions/">我的提交</a></li>
|
||||
<li><a href="#">我的资料</a></li>
|
||||
<li><a href="/account/settings/">设置</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="/logout/">退出</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<tbody>
|
||||
<tr height="39" style="background-color:#50a5e6;">
|
||||
<td style="padding-left:15px;font-family:'微软雅黑','黑体',arial;">
|
||||
{{ website_name }} 密码找回邮件
|
||||
{{ website_name }} 登录信息找回
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -32,12 +32,12 @@
|
||||
</tr>
|
||||
<tr height="30">
|
||||
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
|
||||
您刚刚在 {{ website_name }} 使用了找回密码功能。
|
||||
您刚刚在 {{ website_name }} 申请了找回登录信息服务。
|
||||
</td>
|
||||
</tr>
|
||||
<tr height="30">
|
||||
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
|
||||
请在<span style="color:rgb(255,0,0)">60分钟</span>内点击下面链接设置您的新密码:
|
||||
请在<span style="color:rgb(255,0,0)">30分钟</span>内点击下面链接设置您的新密码:
|
||||
</td>
|
||||
</tr>
|
||||
<tr height="60">
|
||||
@@ -63,7 +63,8 @@
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:12px;">
|
||||
如果你没有提出过密码修改申请,请忽略此邮件。有可能是其他用户误填了你的用户名。我们不会对你的帐户进行任何修改。
|
||||
如果您没有提出过该申请,请忽略此邮件。有可能是其他用户误填了您的邮件地址,我们不会对你的帐户进行任何修改。
|
||||
请不要向他人透露本邮件的内容,否则可能会导致您的账号被盗。
|
||||
</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
|
||||
Reference in New Issue
Block a user