Merge branch 'dev' into sxw-dev

Conflicts:
	template/admin/admin.html
This commit is contained in:
sxw
2015-08-07 19:28:24 +08:00
17 changed files with 288 additions and 211 deletions

View File

@@ -97,6 +97,7 @@ class UsernameCheckAPIView(APIView):
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
class EmailCheckAPIView(APIView): class EmailCheckAPIView(APIView):
def post(self, request): def post(self, request):
""" """

View File

@@ -22,3 +22,9 @@ class AnnouncementSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Announcement model = Announcement
class EditAnnouncementSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=50)
content = serializers.CharField(max_length=10000)
visible = serializers.BooleanField()

View File

@@ -4,9 +4,10 @@ from django.core.urlresolvers import reverse
from rest_framework.test import APITestCase, APIClient from rest_framework.test import APITestCase, APIClient
from account.models import User from account.models import User
from announcement.models import Announcement
class AnnouncementAPITest(APITestCase): class AnnouncementAdminAPITest(APITestCase):
def setUp(self): def setUp(self):
self.client = APIClient() self.client = APIClient()
self.url = reverse("announcement_admin_api") self.url = reverse("announcement_admin_api")
@@ -14,6 +15,7 @@ class AnnouncementAPITest(APITestCase):
user.set_password("test") user.set_password("test")
user.save() user.save()
# 以下是发布公告的测试
def test_invalid_format(self): def test_invalid_format(self):
self.client.login(username="test", password="test") self.client.login(username="test", password="test")
data = {"title": "test1"} data = {"title": "test1"}
@@ -25,3 +27,41 @@ class AnnouncementAPITest(APITestCase):
data = {"title": "title0", "content": "content0"} data = {"title": "title0", "content": "content0"}
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"})
def test_post_invalid_data(self):
self.client.login(username="test", password="test")
data = {"title": "test"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
# 以下是编辑公告的测试
def test_put_invalid_data(self):
self.client.login(username="test", password="test")
data = {"title": "test0", "content": "test0", "visible": "True"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_announcement_does_not_exist(self):
announcement = Announcement.objects.create(title="aa",
content="AA",
created_by=User.objects.get(username="test"))
data = {"id": announcement.id + 1, "title": "11", "content": "22", "visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"该公告不存在!"})
def test_success_edit_announcement(self):
announcement = Announcement.objects.create(title="bb",
content="BB",
created_by=User.objects.get(username="test"))
data = {"id": announcement.id, "title": "11", "content": "22", "visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
class AnnouncementAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse("announcement_list_api")
def test_success_get_data(self):
self.assertEqual(self.client.get(self.url).data["code"], 0)

View File

@@ -6,7 +6,8 @@ from utils.shortcuts import serializer_invalid_response, error_response, success
from account.models import User from account.models import User
from utils.shortcuts import paginate from utils.shortcuts import paginate
from .models import Announcement from .models import Announcement
from .serializers import CreateAnnouncementSerializer, AnnouncementSerializer from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer,
EditAnnouncementSerializer)
class AnnouncementAdminAPIView(APIView): class AnnouncementAdminAPIView(APIView):
@@ -26,6 +27,28 @@ class AnnouncementAdminAPIView(APIView):
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
def put(self, request):
"""
公告编辑json api接口
---
request_serializer: EditAnnouncementSerializer
response_serializer: AnnouncementSerializer
"""
serializer = EditAnnouncementSerializer(data=request.DATA)
if serializer.is_valid():
data = serializer.data
try:
announcement = Announcement.objects.get(id=data["id"])
except Announcement.DoesNotExist:
return error_response(u"该公告不存在!")
announcement.title = data["title"]
announcement.content = data["content"]
announcement.visible = data["visible"]
announcement.save()
return success_response(AnnouncementSerializer(announcement).data)
else:
return serializer_invalid_response(serializer)
class AnnouncementAPIView(APIView): class AnnouncementAPIView(APIView):
def get(self, request): def get(self, request):

View File

@@ -1,25 +1,15 @@
@import url("global.css");
@import url("bootstrap/bootstrap.min.css"); @import url("bootstrap/bootstrap.min.css");
@import url("bootstrap/todc-bootstrap.min.css"); @import url("bootstrap/todc-bootstrap.min.css");
@import url("codeMirror/codemirror.css"); @import url("codeMirror/codemirror.css");
@import url("simditor/simditor.css"); @import url("simditor/simditor.css");
@import url("webuploader/webuploader.css"); @import url("webuploader/webuploader.css");
@import url("datetime_picker/bootstrap-datetimepicker.css"); @import url("datetime_picker/bootstrap-datetimepicker.css");
html, body {
height: 100%;
}
img { #loading-gif{
max-width: 100%; width: 40px;
height: auto; height: 40px;
} margin: auto;
position: absolute;
.footer { top: 0; left: 0; bottom: 0; right: 0;
padding-top: 30px;
padding-bottom: 30px;
float: bottom;
bottom: 0;
}
label {
font-size: 16px;
} }

27
static/src/css/global.css Normal file
View File

@@ -0,0 +1,27 @@
html{
height: 100%;
}
body{
height:100%; /*使内容高度和body一样*/
margin-bottom:-80px;/*向上缩减80像素不至于footer超出屏幕可视范围*/
}
.main{
padding-bottom: 120px;
}
img {
max-width: 100%;
height: auto;
}
.footer {
left: 0;
right: 0;
height: 80px
}
label {
font-size: 16px;
}

View File

@@ -1,25 +1,8 @@
@import url("global.css");
@import url("bootstrap/bootstrap.min.css"); @import url("bootstrap/bootstrap.min.css");
@import url("bootstrap/todc-bootstrap.min.css"); @import url("bootstrap/todc-bootstrap.min.css");
@import url("codeMirror/codemirror.css"); @import url("codeMirror/codemirror.css");
html, body {
height: 100%;
}
img {
max-width: 100%;
height: auto;
}
.footer {
padding-top: 30px;
padding-bottom: 30px;
float: bottom;
bottom: 0;
}
label {
font-size: 16px;
}
#language-selector { #language-selector {
width: 130px; width: 130px;
@@ -47,7 +30,6 @@ label {
font-size: 15px; font-size: 15px;
} }
/* index css */
.jumbotron { .jumbotron {
text-align: center; text-align: center;
background-color: transparent; background-color: transparent;

View File

@@ -7,25 +7,36 @@ define("admin", ["jquery", "avalon"], function($, avalon){
$(".list-group-item").attr("class", "list-group-item"); $(".list-group-item").attr("class", "list-group-item");
} }
function show_template(url){
$("#loading-gif").show();
vm.template_url = url;
}
var vm = avalon.define({
$id: "admin",
template_url: "template/index/index.html",
hide_loading: function(){
$("#loading-gif").hide();
}
});
var hash = window.location.hash.substring(1); var hash = window.location.hash.substring(1);
if(hash){ if(hash){
li_active("#li-" + hash); li_active("#li-" + hash.replace("/", "-"));
show_template("template/" + hash + ".html");
}else { }else {
li_active("#li-index"); li_active("#li-index-index");
} }
window.onhashchange = function() { window.onhashchange = function() {
var hash = window.location.hash.substring(1); var hash = window.location.hash.substring(1);
if(hash){ if(hash){
li_inactive(".list-group-item"); li_inactive(".list-group-item");
li_active("#li-" + hash); li_active("#li-" + hash.replace("/", "-"));
vm.template_url = "template/index/" + hash + ".html"; show_template("template/" + hash + ".html");
} }
}; };
var vm = avalon.define({
$id: "admin",
template_url: "template/index/index.html"
});
}); });

View File

@@ -2,6 +2,7 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker",
"validation" "validation"
], ],
function ($, avalon, editor, uploader) { function ($, avalon, editor, uploader) {
avalon.vmodels.add_contest = null;
$("#add-contest-form") $("#add-contest-form")
.formValidation({ .formValidation({
framework: "bootstrap", framework: "bootstrap",

View File

@@ -67,23 +67,25 @@
<![endif]--> <![endif]-->
<!-- browser happy end --> <!-- browser happy end -->
<div class="container" ms-controller="admin"> <div class="container main" ms-controller="admin">
<div class="row"> <div class="row">
<!-- admin left begin--> <!-- admin left begin-->
<div class="col-md-2"> <div class="col-md-2">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-header">List header</li> <li class="list-group-header">List header</li>
<li class="list-group-item" id="li-index"><a href="#index">主页</a></li> <li class="list-group-item" id="li-index-index"><a href="#index/index">主页</a></li>
<li class="list-group-item" id="li-announcement"><a href="#announcement">公告</a></li> <li class="list-group-item" id="li-announcement-announcement"><a href="#announcement/announcement">公告</a></li>
<li class="list-group-item"><a href="#">Applications</a></li> <li class="list-group-item"><a href="#">Applications</a></li>
<li class="list-group-header">Another list header</li> <li class="list-group-header">比赛管理</li>
<li class="list-group-item"><a href="#">Help</a></li> <li class="list-group-item" id="li-contest-add_contest"><a href="#contest/add_contest">创建比赛</a></li>
</ul> </ul>
</div> </div>
<!-- admin left end --> <!-- admin left end -->
<img src="/static/img/loading.gif" id="loading-gif">
<!-- custom body begin --> <!-- custom body begin -->
<div class='col-md-8'ms-include-src="template_url"></div>
<div class='col-md-8' ms-include-src="template_url" data-include-rendered="hide_loading"></div>
<!-- custom body end --> <!-- custom body end -->
</div> </div>
</div> </div>

View File

@@ -1,185 +1,179 @@
{% extends "admin_base.html" %} <div ms-controller="add_contest">
{% block body %}
{% verbatim %}
<div ms-controller="add_contest">
<form id="add-contest-form"> <form id="add-contest-form">
<div class="col-md-9"> <div class="col-md-9">
<div class="col-md-12"> <div class="col-md-12">
<label>比赛题目</label> <label>比赛题目</label>
</div>
<div class="col-md-12">
<div class="form-group">
<input type="text" name="name" class="form-control">
</div> </div>
<div class="col-md-12"> </div>
<div class="form-group"> <div class="col-md-12">
<input type="text" name="name" class="form-control"> <label>说明</label>
</div> </div>
<div class="col-md-12">
<div class="form-group">
<textarea id="editor" placeholder="这里输入内容" autofocus></textarea>
</div> </div>
<div class="col-md-12"> </div>
<label>说明</label> <div class="col-md-6">
<label>开始时间</label>
</div>
<div class="col-md-6">
<label>结束时间</label>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" class="form-control" name="start_time" id="contest_start_time">
</div> </div>
<div class="col-md-12"> </div>
<div class="form-group"> <div class="col-md-6">
<textarea id="editor" placeholder="这里输入内容" autofocus></textarea> <div class="form-group">
</div> <input type="text" class="form-control" name="end_time" id="contest_end_time">
</div>
<div class="col-md-6">
<label>开始时间</label>
</div>
<div class="col-md-6">
<label>结束时间</label>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" class="form-control" name="start_time" id="contest_start_time">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" class="form-control" name="end_time" id="contest_end_time">
</div>
</div> </div>
</div>
<div class="col-md-6"> <div class="col-md-6">
<label> <label>
密码保护 密码保护
</label> </label>
</div>
<div class="col-md-3">
<label>
模式
</label>
</div>
<div class="col-md-3">
<label>
结束前是否开放排名
</label>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛">
</div> </div>
<div class="col-md-3"> </div>
<label> <div class="col-md-3">
模式 <div class="form-group">
</label> <input type="radio" name="mode">OI
<input type="radio" name="mode">ACM
</div> </div>
<div class="col-md-3"> </div>
<label> <div class="col-md-3">
结束前是否开放排名 <div class="form-group">
</label> <input type="checkbox" value="open_rank">开放排名
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<input type="radio" name="mode">OI
<input type="radio" name="mode">ACM
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<input type="checkbox" value="open_rank">开放排名
</div>
</div> </div>
</div>
<div class="col-md-12"> <div class="col-md-12">
<label>添加题目</label> <label>添加题目</label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="add_problem()">添加</a> <a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="add_problem()">添加</a>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<div class="problem" ms-repeat-problem="problems"> <div class="problem" ms-repeat-problem="problems">
<div class="panel panel-default problem-panel" ms-attr-id="problem-{{ problem.id }}"> <div class="panel panel-default problem-panel" ms-attr-id="problem-{{ problem.id }}">
<div class="panel-heading"> <div class="panel-heading">
<span class="panel-title">题目{{$index + 1}} </span> <span class="panel-title">题目{{$index + 1}} </span>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" <a href="javascript:void(0)" class="btn btn-primary btn-sm"
ms-click="toggle_problem(problem)"> ms-click="toggle_problem(problem)">
{{ problem.toggle_string }} {{ problem.toggle_string }}
</a> </a>
<a href="javascript:void(0)" class="btn btn-danger btn-sm" <a href="javascript:void(0)" class="btn btn-danger btn-sm"
ms-click="del_problem(problem)"> ms-click="del_problem(problem)">
删除 删除
</a> </a>
</div>
<div class="panel-body" ms-attr-id="problem-{{ problem.id }}-body">
<div class="col-md-12">
<label>题目</label>
</div> </div>
<div class="panel-body" ms-attr-id="problem-{{ problem.id }}-body">
<div class="col-md-12">
<label>题目</label>
</div>
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group"> <div class="form-group">
<input type="text" name="problem_name[]" class="form-control"> <input type="text" name="problem_name[]" class="form-control">
</div>
</div> </div>
<div class="col-md-6"> </div>
<label>cpu</label> <div class="col-md-6">
<label>cpu</label>
</div>
<div class="col-md-6">
<label>内存</label>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" name="cpu[]" class="form-control">
</div> </div>
<div class="col-md-6"> </div>
<label>内存</label> <div class="col-md-6">
<div class="form-group">
<input type="text" name="memory[]" class="form-control">
</div> </div>
<div class="col-md-6"> </div>
<div class="form-group"> <div class="col-md-12">
<input type="text" name="cpu[]" class="form-control">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" name="memory[]" class="form-control">
</div>
</div>
<div class="col-md-12">
<label>样例</label> <label>样例</label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" <a href="javascript:void(0)" class="btn btn-primary btn-sm"
ms-click="add_sample(problem)"> ms-click="add_sample(problem)">
添加 添加
</a> </a>
<div class="sample"> <div class="sample">
<div class="panel panel-default sample-panel" <div class="panel panel-default sample-panel"
ms-repeat-sample="problem.samples"> ms-repeat-sample="problem.samples">
<div class="panel-heading"> <div class="panel-heading">
<span class="panel-title">样例{{$index + 1}}</span> <span class="panel-title">样例{{$index + 1}}</span>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" <a href="javascript:void(0)" class="btn btn-primary btn-sm"
ms-click="toggle_sample(problem, sample)"> ms-click="toggle_sample(problem, sample)">
{{ sample.toggle_string }} {{ sample.toggle_string }}
</a> </a>
<a href="javascript:void(0)" class="btn btn-danger btn-sm" <a href="javascript:void(0)" class="btn btn-danger btn-sm"
ms-click="del_sample(problem, sample)"> ms-click="del_sample(problem, sample)">
删除 删除
</a> </a>
</div>
<div class="panel-body"
ms-attr-id="problem-{{ problem.id }}-sampleio-{{ sample.id }}-body">
<div class="col-md-12">
<label>样例输入</label>
</div> </div>
<div class="panel-body" <div class="col-md-12">
ms-attr-id="problem-{{ problem.id }}-sampleio-{{ sample.id }}-body"> <div class="form-group">
<div class="col-md-12"> <textarea class="form-control" rows="5"></textarea>
<label>样例输入</label>
</div> </div>
<div class="col-md-12"> </div>
<div class="form-group"> <div class="col-md-12">
<textarea class="form-control" rows="5"></textarea> <label>样例输出</label>
</div> </div>
</div> <div class="col-md-12">
<div class="col-md-12"> <div class="form-group">
<label>样例输出</label> <textarea class="form-control" rows="5"></textarea>
</div>
<div class="col-md-12">
<div class="form-group">
<textarea class="form-control" rows="5"></textarea>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-12"> </div>
<label>测试数据</label> <div class="col-md-12">
</div> <label>测试数据</label>
<div class="col-md-12"> </div>
<div class="form-group"> <div class="col-md-12">
<div ms-attr-id="problem-{{ problem.id }}-uploader">选择文件</div> <div class="form-group">
<div ms-attr-id="problem-{{ problem.id }}-uploader">选择文件</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</form> </div>
</div> </form>
{% endverbatim %} </div>
{% endblock %}
{% block js_block %} <script src="/static/js/app/admin/contest/contest.js"></script>
<script src="/static/js/app/admin/contest/contest.js"></script>
{% endblock %}

View File

@@ -1,6 +1,6 @@
{% extends "oj_base.html" %} {% extends "oj_base.html" %}
{% block body %} {% block body %}
<div class="container"> <div class="container main">
<div class="col-md-6 col-md-offset-3"> <div class="col-md-6 col-md-offset-3">
<h2 class="text-center">修改密码</h2> <h2 class="text-center">修改密码</h2>

View File

@@ -1,6 +1,6 @@
{% extends "oj_base.html" %} {% extends "oj_base.html" %}
{% block body %} {% block body %}
<div class="container"> <div class="container main">
<div class="col-md-6 col-md-offset-3"> <div class="col-md-6 col-md-offset-3">
<h2 class="text-center">用户登录</h2> <h2 class="text-center">用户登录</h2>

View File

@@ -1,6 +1,6 @@
{% extends "oj_base.html" %} {% extends "oj_base.html" %}
{% block body %} {% block body %}
<div class="container"> <div class="container main">
<div class="col-md-6 col-md-offset-3"> <div class="col-md-6 col-md-offset-3">
<h2 class="text-center">用户注册</h2> <h2 class="text-center">用户注册</h2>

View File

@@ -1,6 +1,6 @@
{% extends "oj_base.html" %} {% extends "oj_base.html" %}
{% block body %} {% block body %}
<div class="container"> <div class="container main">
<ul class="nav nav-tabs nav-tabs-google"> <ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="problem.html">题目</a></li> <a href="problem.html">题目</a></li>

View File

@@ -1,7 +1,7 @@
{% extends 'oj_base.html' %} {% extends 'oj_base.html' %}
{% block body %} {% block body %}
<div class="container"> <div class="container main">
<ul class="nav nav-tabs nav-tabs-google"> <ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="problem.html">题目</a></li> <a href="problem.html">题目</a></li>

View File

@@ -1,6 +1,6 @@
{% extends "oj_base.html" %} {% extends "oj_base.html" %}
{% block body %} {% block body %}
<div class="container" ms-controller="problem_list"> <div class="container main" ms-controller="problem_list">
<div class="row"> <div class="row">
<div class="col-lg-9"> <div class="col-lg-9">
<div class="row"> <div class="row">