Accept Merge Request #148 添加前台加入小组系列页面;从后端统一比赛时间 : (dev-sxw -> dev)

Merge Request: 添加前台加入小组系列页面;从后端统一比赛时间
Created By: @esp
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/148
This commit is contained in:
virusdefender 2015-08-31 22:24:57 +08:00
commit fe7d429dba
13 changed files with 238 additions and 62 deletions

View File

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

View File

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

View File

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

View File

@ -36,6 +36,6 @@ class JoinGroupRequest(models.Model):
create_time = models.DateTimeField(auto_now_add=True)
# 是否处理
status = models.BooleanField(default=False)
accepted = models.BooleanField(default=False)
class Meta:
db_table = "join_group_request"

View File

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

View File

@ -236,9 +236,10 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
join_request.status = True
join_request.save()
if data["status"]:
if join_group(join_request.user, join_request.group):
join_request.accepted = True
join_request.save()
return success_response(u"加入成功")
else:
return error_response(u"加入失败,已经在本小组内")
@ -248,6 +249,7 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
else:
return serializer_invalid_response(serializer)
@login_required
def group_list_page(request, page=1):
# 右侧的公告列表
@ -283,3 +285,31 @@ def group_list_page(request, page=1):
"previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "announcements": announcements,
})
@login_required
def group_page(request, group_id):
try:
group = Group.objects.get(id=group_id, visible=True)
except Group.DoesNotExist:
return error_page(request, u"小组不存在")
return render(request, "oj/group/group.html", {"group": group})
@login_required
def application_list_page(request, group_id):
try:
group = Group.objects.get(id=group_id, visible=True)
except Group.DoesNotExist:
return error_page(request, u"小组不存在")
applications = JoinGroupRequest.objects.filter(user=request.user, group=group)
return render(request, "oj/group/my_application_list.html",
{"group": group, "applications": applications})
@login_required
def application_page(request, request_id):
try:
application = JoinGroupRequest.objects.get(user=request.user, pk=request_id)
except JoinGroupRequest.DoesNotExist:
return error_page(request, u"申请不存在")
return render(request, "oj/group/my_application.html",
{"application": application})

View File

@ -51,13 +51,14 @@ urlpatterns = [
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_api"),
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
url(r'^api/group_join/$', JoinGroupAPIView.as_view(), name="group_join_api"),
url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"),
url(r'^api/admin/group_join/$', JoinGroupAPIView.as_view(), name="group_join_admin_api"),
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"),
@ -88,7 +89,6 @@ urlpatterns = [
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"),
url(r'^problems/(?P<page>\d+)/$', "problem.views.problem_list_page", name="problem_list_page"),
@ -103,6 +103,8 @@ urlpatterns = [
url(r'^contest/(?P<contest_id>\d+)/rank/$', "contest.views.contest_rank_page", name="contest_rank_page"),
url(r'^groups/$', "group.views.group_list_page", name="group_list_page"),
url(r'^groups/(?P<page>\d+)/$', "group.views.group_list_page", name="group_list_page")
url(r'^groups/(?P<page>\d+)/$', "group.views.group_list_page", name="group_list_page"),
url(r'^group/(?P<group_id>\d+)/$', "group.views.group_page", name="group_page"),
url(r'^group/(?P<group_id>\d+)/applications/$', "group.views.application_list_page", name="group_application_page"),
url(r'^group/application/(?P<request_id>\d+)/$', "group.views.application_page", name="group_application")
]

View File

@ -149,16 +149,8 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
vm.editingContestId = contestId;
vm.editTitle = vm.contestList[contestId-1].title;
vm.editPassword = vm.contestList[contestId-1].password;
//var startTime = new Date(), endTime = new Date();
//startTime.setFullYear(parseInt(vm.contestList[contestId-1].start_time.substring(0,4)))
//startTime.setMonth(parseInt(vm.contestList[contestId-1].start_time.substring(5,7)))
//startTime.setDate(parseInt(vm.contestList[contestId-1].start_time.substring(8,10)))
//startTime.setHours(parseInt(vm.contestList[contestId-1].start_time.substring(11,13)))
//startTime.setMinutes(parseInt(vm.contestList[contestId-1].start_time.substring(14,16)))
//startTime = new Date(startTime + 8 * 60 * 60 * 1000)
vm.editStartTime = add8Hours(vm.contestList[contestId-1].start_time);
vm.editEndTime = add8Hours(vm.contestList[contestId-1].end_time);//.substring(0,16).replace("T"," ");
vm.editStartTime = vm.contestList[contestId-1].start_time.substring(0,16).replace("T"," ");
vm.editEndTime = vm.contestList[contestId-1].end_time.substring(0,16).replace("T"," ");
vm.editMode = vm.contestList[contestId-1].mode;
vm.editVisible = vm.contestList[contestId-1].visible;
if (vm.contestList[contestId-1].contest_type == 0) { //contest type == 0, contest in group
@ -300,39 +292,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
});
}
function add8Hours(UtcString) {
console.log(UtcString);
var M = [31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
/*time.setFullYear(parseInt(UtcString.substring(0,4)))
time.setMonth(parseInt(UtcString.substring(5,7)))
time.setDate(parseInt(UtcString.substring(8,10)))
time.setHours(parseInt(UtcString.substring(11,13)))
time.setMinutes(parseInt(UtcString.substring(14,16)))
time = new Date(time + 8 * 60 * 60 * 1000)*/
var year =UtcString.substring(0,4);
var month =UtcString.substring(5,7);
var day =UtcString.substring(8,10);
var hour =parseInt(UtcString.substring(11,13)) + 8;
var minute = UtcString.substring(14,16);
if (hour > 23) {
hour -= 24; day = parseInt(day)+1; month = parseInt(month);
if (month == 2) if (!(year%400)||(!(year%4)&&year%100)) M[2] = 29;
if (day > M[month]) {
day = 1;
month = parseInt(month)+1;
if (month > 12) {
year=parseInt(year)+1; month = 1;
}
}
month = month.toString();
day = day.toString();
if (month.length==1) month = "0"+month;
if (day.length==1) day = "0"+day;
}
hour = hour.toString();
if (hour.length==1) hour="0"+hour;
return year+"-"+month+"-"+day+" "+hour+":"+minute;
}
// Get group list
$.ajax({ // Get current user type
url: "/api/user/",

View File

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

View File

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

View File

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

View File

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

View File

@ -48,7 +48,7 @@
<tbody>
{% for item in submissions %}
<tr>
<th scope="row"><a href="/my_submission/{{ item.id }}/" id="id_{{ forloop.counter }}">
<th scope="row"><a href="/submission/{{ item.id }}/" id="id_{{ forloop.counter }}">
{{ forloop.counter |add:start_id }}</a></th>
<td>{{ item.create_time }}</td>
<td>