Accept Merge Request #244 细节更新 : (virusdefender-dev -> dev)

Merge Request: 细节更新
Created By: @virusdefender
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/244
This commit is contained in:
virusdefender 2015-09-16 19:58:02 +08:00
commit 25379e1c1d
26 changed files with 215 additions and 133 deletions

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
static/src/js/lib/* linguist-vendored
static/src/js/require.js linguist-vendored
static/src/js/r.js linguist-vendored

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Qingdao 青岛大学信息工程学院创新实验室
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,33 @@
# OnlineJudge
基于 Python 和 Django的在线评测平台。
文档https://www.zybuluo.com/virusdefender/note/171932
TODO
- 完善文档,目前还差很多
- 完善测试
- 搭建 demo 站点
![oj_previewindex.png][1]
![preview.jpeg][2]
![oj_preview_submission.png][3]
![contest][4]
![contest_rank_edit][5]
![admin_problem][6]
![admin_contest][7]
[1]: https://dn-virusdefender-blog-cdn.qbox.me/oj_previewindex.png
[2]: https://dn-virusdefender-blog-cdn.qbox.me/oj_previewproblem.png
[3]: https://dn-virusdefender-blog-cdn.qbox.me/oj_previewsubmission.png
[4]: https://dn-virusdefender-blog-cdn.qbox.me/oj_previewcontest.png
[5]: https://dn-virusdefender-blog-cdn.qbox.me/oj_previewcontest_rank.png?edit
[6]: https://dn-virusdefender-blog-cdn.qbox.me/oj_previewadmin_problem.png
[7]: https://dn-virusdefender-blog-cdn.qbox.me/oj_previewadmin_contest.png

View File

@ -433,7 +433,9 @@ def contest_rank_page(request, contest_id):
except ContestSubmission.DoesNotExist:
result[i]["problems"].append({})
result[i]["total_ac"] = submissions.filter(ac=True).count()
result[i]["username"] = User.objects.get(id=result[i]["user_id"]).username
user= User.objects.get(id=result[i]["user_id"])
result[i]["username"] = user.username
result[i]["real_name"] = user.real_name
result[i]["total_time"] = get_the_time_format(submissions.filter(ac=True).aggregate(total_time=Sum("total_time"))["total_time"])
result = sorted(result, cmp=_cmp, reverse=True)
r.set("contest_rank_" + contest_id, json.dumps(list(result)))
@ -449,4 +451,5 @@ def contest_rank_page(request, contest_id):
{"contest": contest, "contest_problems": contest_problems,
"result": result,
"auto_refresh": request.GET.get("auto_refresh", None) == "true",
"show_real_name": result.GET.get("show_real_name", None) == "true",
"real_time_rank": contest.real_time_rank})

View File

View File

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,23 +0,0 @@
# coding=utf-8
from django.shortcuts import render
from django.http import HttpResponse
from account.models import User
from group.models import Group, UserGroupRelation, JoinGroupRequest
def install(request):
for i in range(10):
user = User.objects.create(username="root" + str(i), admin_type=2, real_name="real_name", email="11111@qq.com")
user.set_password("root")
user.save()
for i in range(10):
group = Group.objects.create(name="group" + str(i),
description="description",
admin=User.objects.get(username="root0"))
for i in range(7):
UserGroupRelation.objects.create(user=User.objects.get(username="root" + str(i)), group=group)
for i in range(7, 10):
JoinGroupRequest.objects.create(user=User.objects.get(username="root" + str(i)),
group=group, message=u"你好啊")
return HttpResponse("success")

View File

@ -1,3 +0,0 @@
/usr/bin/docker run -t -i --privileged -v /var/test_case/:/var/judger/test_case/ -v /var/code/:/var/judger/code/ judger /bin/bash
python judge/judger/run.py -solution_id 1 -max_cpu_time 1 -max_memory 1 -test_case_id 1

View File

@ -1,7 +1,12 @@
# coding=utf-8
"""
注意
此文件包含 celery 的部分配置但是 celery 并不是运行在docker 中的所以本配置文件中的 redis和 MySQL 的地址就应该是
运行 redis MySQL docker 容器的地址了怎么获取这个地址见帮助文档测试用例的路径和源代码路径同理
"""
# 这个redis 是 celery 使用的,包括存储队列信息还有部分统计信息
redis_config = {
"host": "121.42.32.129",
"host": "192.168.42.23",
"port": 6379,
"db": 0
}
@ -9,23 +14,23 @@ redis_config = {
# 判题的 docker 容器的配置参数
docker_config = {
"image_name": "3da0e526934e",
"image_name": "judger",
"docker_path": "docker",
"shell": True
}
# 测试用例的路径,是主机上的实际路径
test_case_dir = "/var/mnt/source/test_case/"
test_case_dir = "/root/test_case/"
# 源代码路径,也就是 manage.py 所在的实际路径
source_code_dir = "/var/mnt/source/OnlineJudge/"
source_code_dir = "/root/qduoj/"
# 日志文件夹路径
log_dir = "/var/log/"
log_dir = "/root/log/"
# 存储提交信息的数据库,是 celery 使用的,与 oj.settings/local_settings 等区分,那是 web 服务器访问的地址
submission_db = {
"host": "127.0.0.1",
"host": "192.168.42.32",
"port": 3306,
"db": "oj_submission",
"user": "root",

View File

@ -11,7 +11,8 @@ from settings import docker_config, source_code_dir, test_case_dir, log_dir, sub
@app.task
def judge(submission_id, time_limit, memory_limit, test_case_id):
try:
command = "%s run -t -i --privileged --rm=true " \
command = "%s run -t -i --privileged --rm " \
"--link mysql " \
"-v %s:/var/judger/test_case/ " \
"-v %s:/var/judger/code/ " \
"-v %s:/var/judger/code/log/ " \

View File

@ -4,6 +4,8 @@ import logging
import redis
import json
from django.db import transaction
from judge.judger_controller.settings import redis_config
from judge.judger.result import result
from submission.models import Submission
@ -37,6 +39,7 @@ class MessageQueue(object):
problem.save()
except Problem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
continue
# 更新该用户的解题状态
try:
user = User.objects.get(pk=submission.user_id)
@ -69,15 +72,11 @@ class MessageQueue(object):
contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest,
problem_id=contest_problem.id)
# 提交次数加1
with transaction.atomic():
if submission.result == result["accepted"]:
# 避免这道题已经 ac 了,但是又重新提交了一遍
if not contest_submission.ac:
# 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时
# logger.debug(contest.start_time)
# logger.debug(submission.create_time)
# logger.debug((submission.create_time - contest.start_time).total_seconds())
# logger.debug(int((submission.create_time - contest.start_time).total_seconds() / 60))
contest_submission.ac_time = int((submission.create_time - contest.start_time).total_seconds())
contest_submission.total_time += contest_submission.ac_time
contest_submission.total_submission_number += 1
@ -95,6 +94,7 @@ class MessageQueue(object):
contest_problem.save()
except ContestSubmission.DoesNotExist:
# 第一次提交
with transaction.atomic():
is_ac = submission.result == result["accepted"]
first_achieved = False
ac_time = 0
@ -113,6 +113,5 @@ class MessageQueue(object):
ac=is_ac, total_time=total_time, first_achieved=first_achieved,
ac_time=ac_time)
logger.debug("Start message queue")
MessageQueue().listen_task()

View File

@ -1,4 +1,3 @@
@import url("global.css");
@import url("bootstrap/bootstrap.min.css");
@import url("bootstrap/todc-bootstrap.min.css");
@import url("codeMirror/codemirror.css");
@ -6,6 +5,7 @@
@import url("webuploader/webuploader.css");
@import url("datetime_picker/bootstrap-datetimepicker.css");
@import url("tagEditor/jquery.tag-editor.css");
@import url("global.css");
#loading-gif {
width: 40px;

View File

@ -1,3 +1,7 @@
body, button, input, select, textarea, h1, h2, h3, h4, h5, h6 {
font-family: Georgia, STHeiti, "Microsoft Yahei", SimSun, "Droid Sans";
}
html {
height: 100%;
}
@ -34,6 +38,7 @@ label {
float: right;
}
.CodeMirror pre {
font-family: "Consolas","Bitstream Vera Sans Mono","Courier New", Courier, monospace !important;
pre {
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
}

View File

@ -1,8 +1,7 @@
@import url("global.css");
@import url("bootstrap/bootstrap.min.css");
@import url("bootstrap/todc-bootstrap.min.css");
@import url("codeMirror/codemirror.css");
@import url("global.css");
#language-selector {
width: 130px;

BIN
static/src/img/chrome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
static/src/img/firefox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
static/src/img/ie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>不支持的浏览器</title>
<style>
body{
padding-top: 50px;;
}
div {
margin: 20px;
display: inline;
}
</style>
</head>
<body>
很抱歉,我们无法完全兼容低版本的 IE 浏览器,您可以
<a href="http://down.tech.sina.com.cn/page/40975.html">
<div>
<img src="/static/img/chrome.png">使用Chrome
</div>
</a>
<a href="https://www.mozilla.org/zh-CN/firefox/new/?scene=2#download-fx">
<div>
<img src="/static/img/firefox.png">使用FireFox
</div>
</a>
<a href="http://windows.microsoft.com/en-us/internet-explorer/download-ie">
<div>
<img src="/static/img/ie.png">升级IE
</div>
</a>
</body>
</html>

View File

@ -54,13 +54,11 @@
</nav>
<!-- nav end -->
<!--browser happy begin -->
<!--[if lt IE 9]>
<div class="alert alert-danger text-center" role="alert">
当前网页 <strong>不支持</strong> 你正在使用的浏览器. 为了正常的访问, 请 <a href="http://browsehappy.com/">升级你的浏览器</a>.
</div>
<![endif]-->
<!-- browser happy end -->
<script>
if(navigator.userAgent.indexOf("MSIE") > -1){
location.href = "/static/img/unsupported_browser.html";
}
</script>
<div class="container main">
<div class="row">

View File

@ -45,7 +45,12 @@
{% for item in result %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td>{{ item.username }}</td>
<td>
{{ item.username }}
{% if show_real_name %}
{{ item.real_name }}
{% endif %}
</td>
<td>{{ item.total_ac }} / {{ item.total_submit }}</td>
<td>{% if item.total_time %}{{ item.total_time }}{% else %}--{% endif %}</td>
{% for problem in item.problems %}

View File

@ -59,6 +59,12 @@
{% endfor %}
</p>
</div>
{% if problem.source %}
<div class="problem-section hide">
<label class="problem-label">来源</label>
<div class="problem-detail">{{ problem.source }}</div>
</div>
{% endif %}
<div>
<label>选择语言</label>
<div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -21,13 +21,11 @@
<body>
<!--browser happy begin -->
<!--[if lt IE 9]>
<div class="alert alert-danger text-center" role="alert">
当前网页 <strong>不支持</strong> 你正在使用的浏览器. 为了正常的访问, 请 <a href="http://browsehappy.com/">升级你的浏览器</a>.
</div>
<![endif]-->
<!-- browser happy end -->
<script>
if(navigator.userAgent.indexOf("MSIE") > -1){
location.href = "/static/img/unsupported_browser.html";
}
</script>
<!-- nav begin -->
<nav class="navbar navbar-masthead navbar-default navbar-static-top">

View File

@ -22,37 +22,29 @@
<li>System Error: 判题系统发生故障,请等待重判</li>
<li>Waiting: 你的提交正在等待处理</li>
</ul>
<h4>支持的语言</h4>
<ul>
<li>C (GCC 4.8)</li>
<li>C++ (G++ 4.3)</li>
<li>Java (Oracle JDK 1.7)</li>
</ul>
<h4>编译参数</h4>
<ul>
<li>C</li>
<pre>gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main</pre></p>
<li>C++</li>
<li>CGCC 4.8</li>
<pre>gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main</pre>
<li>C++G++ 4.3</li>
<pre>g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main</pre>
<li>Java</li>
<pre>javac {src_path} -d {exe_path}</pre>
<pre>java -cp {exe_path} Main</pre>
</ul>
<li>JavaOracle JDK 1.7</li>
<pre>
//编译
javac {src_path} -d {exe_path}
//运行
java -cp {exe_path} Main</pre>
</ul>
<h2 class="text-center">常见问题</h2>
<ul>
<li>输入输出</li>
<p>无特殊说明,请使用标准输入输出</p>
<li>C/C++的64位整数类型</li>
<p>请使用long long声明使用cin/cout或 %lld输入输出</p>
<li>判题结果与本地执行结果不一致</li>
<p>是否使用了不同版本的编译器VC和TC并不完全符合C/C++标准)</p>
<p>判题时可能使用了与您测试时不同的测试数据(不仅限于样例中展示的数据)</p>
<li>程序执行时间和占用的内存如何计算</li>
<p>执行时间指CPU时间占用内存按执行过程中内存消耗的峰值计有多组测试数据时以最大的时间和内存消耗为准</p>
<li>无特殊说明,请使用标准输入输出。</li>
<li>Java 代码需使用 Main 作为主类名。C/C++代码使用<code>int main()</code>,并且需要<code>return 0;</code></li>
<li>C/C++ 的64位整数类型请使用 <code>long long</code> 声明,使用 <code>cin/cout</code><code>%lld</code> 输入输出。
使用<code>__int64</code>会导致编译错误。</li>
<li>程序执行时间指CPU时间占用内存按执行过程中内存消耗的峰值计有多组测试数据时以最大的时间和内存消耗为准</li>
</ul>
</div>

View File

@ -9,6 +9,12 @@ template_release_path = "template/release/"
static_src_path = "static/src/"
static_release_path = "static/release/"
print "Begin to compress js"
if os.system("node static/src/js/r.js -o static/src/js/build.js"):
print "Failed to compress js, exit"
exit()
try:
# 删除模板的 release 文件夹
shutil.rmtree(template_release_path)
@ -30,7 +36,6 @@ name_map = {}
def process(match):
file_path = match.group(1).replace("/static/", "")
# print file_path, match.group(), match.group(1)
if not os.path.exists(static_release_path + file_path):
return match.group(0)
@ -49,9 +54,11 @@ def process(match):
return match.group(0)
print "Begin to add md5 stamp in html"
for root, dirs, files in os.walk(template_release_path):
for name in files:
html_path = os.path.join(root, name)
print "Processing: " + html_path
html_content = open(html_path, "r").read()
js_replaced_html_content = re.sub(js_re, process, html_content)
css_replaced_html_content = re.sub(css_re, process, js_replaced_html_content)
@ -59,3 +66,5 @@ for root, dirs, files in os.walk(template_release_path):
f = open(html_path, "w")
f.write(css_replaced_html_content)
f.close()
print "Done"