diff --git a/Dockerfile b/Dockerfile index 6dbbdd6..f10b84c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,5 +7,7 @@ WORKDIR /var/oj/ RUN pip install -r requirements.txt EXPOSE 8080 RUN mkdir LOG +RUN mkdir test_case +RUN mkdir tmp RUN python manage.py migrate CMD python manage.py runserver 0.0.0.0:8080 diff --git a/account/decorators.py b/account/decorators.py index 01cdd68..33634e5 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -1,12 +1,14 @@ # coding=utf-8 +from functools import wraps from django.http import HttpResponse from django.shortcuts import render -from utils.shortcuts import error_response +from utils.shortcuts import error_response, error_page from .models import User def login_required(func): + @wraps(func) def check(*args, **kwargs): # 在class based views 里面,args 有两个元素,一个是self, 第二个才是request, # 在function based views 里面,args 只有request 一个参数 @@ -16,11 +18,12 @@ def login_required(func): if request.is_ajax(): return error_response(u"请先登录") else: - return render(request, "utils/error.html", {"error": u"请先登录"}) + return error_page(request, u"请先登录") return check def admin_required(func): + @wraps(func) def check(*args, **kwargs): request = args[-1] if request.user.is_authenticated() and request.user.admin_type: @@ -28,5 +31,5 @@ def admin_required(func): if request.is_ajax(): return error_response(u"需要管理员权限") else: - return render(request, "utils/error.html", {"error": "需要管理员权限"}) + return error_page(request, u"需要管理员权限") return check diff --git a/account/tests.py b/account/tests.py index aca8971..aa21bc7 100644 --- a/account/tests.py +++ b/account/tests.py @@ -145,10 +145,10 @@ class UserChangePasswordAPITest(APITestCase): self.assertEqual(response.data, {"code": 0, "data": u"用户密码修改成功!"}) -class UserAPITest(APITestCase): +class UserAdminAPITest(APITestCase): def setUp(self): self.client = APIClient() - self.url = reverse("user_list_api") + self.url = reverse("user_admin_api") user = User.objects.create(username="testx", real_name="xx", admin_type=SUPER_ADMIN) user.set_password("testxx") user.save() diff --git a/account/views.py b/account/views.py index fe14464..f2c6ca3 100644 --- a/account/views.py +++ b/account/views.py @@ -7,9 +7,11 @@ from rest_framework.views import APIView from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate +from .decorators import login_required from .models import User -from .serializers import UserLoginSerializer, UsernameCheckSerializer, UserRegisterSerializer, \ - UserChangePasswordSerializer, EmailCheckSerializer, UserSerializer, EditUserSerializer +from .serializers import (UserLoginSerializer, UsernameCheckSerializer, + UserRegisterSerializer, UserChangePasswordSerializer, + EmailCheckSerializer, UserSerializer, EditUserSerializer) class UserLoginAPIView(APIView): @@ -118,7 +120,38 @@ class EmailCheckAPIView(APIView): return serializer_invalid_response(serializer) -class UserAPIView(APIView): +class UserAdminAPIView(APIView): + def put(self, request): + """ + 用户编辑json api接口 + --- + request_serializer: EditUserSerializer + response_serializer: UserSerializer + """ + serializer = EditUserSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + try: + user = User.objects.get(id=data["id"]) + except User.DoesNotExist: + return error_response(u"该用户不存在!") + try: + user = User.objects.get(username=data["username"]) + if user.id != data["id"]: + return error_response(u"昵称已经存在") + except User.DoesNotExist: + pass + user.username = data["username"] + user.real_name = data["real_name"] + user.email = data["email"] + user.admin_type = data["admin_type"] + if data["password"]: + user.set_password(data["password"]) + user.save() + return success_response(UserSerializer(user).data) + else: + return serializer_invalid_response(serializer) + def get(self, request): """ 用户分页json api接口 @@ -140,28 +173,12 @@ class UserAPIView(APIView): return paginate(request, user, UserSerializer) -class UserAdminAPIView(APIView): - def put(self, request): +class UserInfoAPIView(APIView): + @login_required + def get(self, request): """ - 用户编辑json api接口 + 返回这个用户的个人信息 --- - request_serializer: EditUserSerializer response_serializer: UserSerializer """ - serializer = EditUserSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - user = User.objects.get(id=data["id"]) - except User.DoesNotExist: - return error_response(u"该用户不存在!") - user.username = data["username"] - user.real_name = data["real_name"] - user.email = data["email"] - user.admin_type = data["admin_type"] - if data["password"]: - user.set_password(data["password"]) - user.save() - return success_response(UserSerializer(user).data) - else: - return serializer_invalid_response(serializer) + return success_response(UserSerializer(request.user).data) diff --git a/announcement/views.py b/announcement/views.py index cdcd8a0..07d1e64 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -4,7 +4,7 @@ from rest_framework.views import APIView from django.shortcuts import render from utils.shortcuts import serializer_invalid_response, error_response, success_response -from utils.shortcuts import paginate +from utils.shortcuts import paginate, error_page from .models import Announcement from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer, EditAnnouncementSerializer) @@ -14,7 +14,7 @@ def announcement_page(request, announcement_id): try: announcement = Announcement.objects.get(id=announcement_id, visible=True) except Announcement.DoesNotExist: - return render(request, "utils/error.html", {"error": u"模板不存在"}) + return error_page(request, u"模板不存在") return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) diff --git a/group/views.py b/group/views.py index 79ccbd4..a9ec8d2 100644 --- a/group/views.py +++ b/group/views.py @@ -94,6 +94,7 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): response_serializer: GroupSerializer """ group_id = request.GET.get("group_id", None) + # 根据 id 查询小组信息 if group_id: try: group = self.get_group(request, group_id) @@ -102,8 +103,15 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): return error_response(u"小组不存在") else: groups = self.get_groups(request) + # 搜索小组 if request.GET.get("keyword", None): groups = groups.filter(name__contains=request.GET["keyword"]) + # 只返回我创建的小组 适用于超级管理员 + if request.GET.get("my_group", None): + groups = groups.filter(admin=request.user) + # 只返回指定用户的小组 适用于管理员 + elif request.GET.get("admin_id", None): + groups = groups.filter(admin__id=request.GET["admin_id"]) return paginate(request, groups, GroupSerializer) diff --git a/judge/README.md b/judge/README.md new file mode 100644 index 0000000..c40444b --- /dev/null +++ b/judge/README.md @@ -0,0 +1,3 @@ +/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 diff --git a/judge/controller/celery.py b/judge/controller/celery.py deleted file mode 100644 index 8e776e2..0000000 --- a/judge/controller/celery.py +++ /dev/null @@ -1,5 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import -from celery import Celery - -app = Celery("judge", broker="redis://localhost:6379/0", include=["judge.controller.tasks"]) \ No newline at end of file diff --git a/judge/controller/tasks.py b/judge/controller/tasks.py deleted file mode 100644 index f0c32bc..0000000 --- a/judge/controller/tasks.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import -from judge.controller.celery import app - - -@app.task -def judge(source_code, language, test_case_id): - print source_code, language, test_case_id \ No newline at end of file diff --git a/judge/judger/__init__.py b/judger/__init__.py similarity index 100% rename from judge/judger/__init__.py rename to judger/__init__.py diff --git a/judge/judger/client.py b/judger/client.py similarity index 81% rename from judge/judger/client.py rename to judger/client.py index cdac0b2..fc7d0b9 100644 --- a/judge/judger/client.py +++ b/judger/client.py @@ -30,7 +30,7 @@ class JudgeClient(object): :param test_case_dir: 测试用例文件夹路径 :return:返回结果list """ - self._language = languages[str(language_code)] + self._language = languages[language_code] self._exe_path = exe_path self._max_cpu_time = max_cpu_time self._max_real_time = max_real_time @@ -58,7 +58,7 @@ class JudgeClient(object): # todo 系统调用白名单 chroot等参数 command = "lrun" + \ " --max-cpu-time " + str(self._max_cpu_time / 1000.0) + \ - " --max-real-time " + str(self._max_real_time / 1000.0) + \ + " --max-real-time " + str(self._max_real_time / 1000.0 * 2) + \ " --max-memory " + str(self._max_memory * 1000 * 1000) + \ " --network false" + \ " --uid " + str(lrun_uid) + \ @@ -170,79 +170,4 @@ class JudgeClient(object): # http://stackoverflow.com/questions/25382455/python-notimplementederror-pool-objects-cannot-be-passed-between-processes self_dict = self.__dict__.copy() del self_dict['_pool'] - return self_dict - - - -c_src = r""" -#include -#include -int main() -{ - FILE *fp; - fp = NULL; - fprintf(fp, "This is testing for fprintf...\n"); - fputs("This is testing for fputs...\n", fp); - fclose(fp); - printf("111111"); - return 0; -} -""" - -cpp_src = r""" -#include - -using namespace std; - -int main() -{ - int a,b; - cin >> a >> b; - cout << a+b; - return 0; -} -""" - -java_src = r""" -import java.io.*; -import java.util.*; -11 -public class Main -{ - public static void main(String[] args) - { - Scanner in = new Scanner(System.in); - PrintWriter out = new PrintWriter(System.out); - - int a = in.nextInt(); - int b = in.nextInt(); - out.print(a + b); - throw new EmptyStackException(); - - } -} -""" -def judge(languege_code, source_string): - language = languages[str(languege_code)] - src_path = judger_workspace + language["src_name"] - f = open(src_path, "w") - f.write(source_string) - f.close() - - try: - exe_path = compile_(languages[str(languege_code)], src_path, judger_workspace) - except Exception as e: - print e - return [{"result": result["compile_error"]}] - - client = JudgeClient(language_code=languege_code, - exe_path=exe_path, - max_cpu_time=1000000, - max_real_time=200000, - max_memory=1000, - test_case_dir="/var/test_cases/1/") - return client.run() - -print judge(1, c_src) -print judge(2, cpp_src) -print judge(3, java_src) \ No newline at end of file + return self_dict \ No newline at end of file diff --git a/judge/judger/compiler.py b/judger/compiler.py similarity index 99% rename from judge/judger/compiler.py rename to judger/compiler.py index d4bca4b..7f40ff1 100644 --- a/judge/judger/compiler.py +++ b/judger/compiler.py @@ -32,5 +32,4 @@ def compile_(language_item, src_path, exe_path): if parse_result["exit_code"] or parse_result["term_sig"] or parse_result["siginaled"] or parse_result["exceed"]: raise CompileError("Compile error") - return exe_path diff --git a/judge/judger/judge_exceptions.py b/judger/judge_exceptions.py similarity index 100% rename from judge/judger/judge_exceptions.py rename to judger/judge_exceptions.py diff --git a/judge/judger/language.py b/judger/language.py similarity index 65% rename from judge/judger/language.py rename to judger/language.py index 5923edb..a61bd32 100644 --- a/judge/judger/language.py +++ b/judger/language.py @@ -2,21 +2,21 @@ languages = { - "1": { + 1: { "name": "c", "src_name": "main.c", "code": 1, - "compile_command": "gcc -DONLINE_JUDGE -O2 -Wall -std=c99 -pipe {src_path} -lm -o {exe_path}main", + "compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, - "2": { + 2: { "name": "cpp", "src_name": "main.cpp", "code": 2, - "compile_command": "g++ -DONLINE_JUDGE -O2 -Wall -std=c++11 -pipe {src_path} -lm -o {exe_path}main", + "compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, - "3": { + 3: { "name": "java", "src_name": "Main.java", "code": 3, diff --git a/judge/judger/result.py b/judger/result.py similarity index 100% rename from judge/judger/result.py rename to judger/result.py diff --git a/judger/run.py b/judger/run.py new file mode 100644 index 0000000..d14fc61 --- /dev/null +++ b/judger/run.py @@ -0,0 +1,81 @@ +# coding=utf-8 +import sys +import pymongo + +from bson.objectid import ObjectId + +from client import JudgeClient +from language import languages +from compiler import compile_ +from result import result +from settings import judger_workspace, mongodb_config + + +# 简单的解析命令行参数 +# 参数有 -solution_id -time_limit -memory_limit -test_case_id +# 获取到的值是['xxx.py', '-solution_id', '1111', '-time_limit', '1000', '-memory_limit', '100', '-test_case_id', 'aaaa'] +args = sys.argv +submission_id = args[2] +time_limit = args[4] +memory_limit = args[6] +test_case_id = args[8] + +connection = pymongo.MongoClient(host=mongodb_config["host"], port=mongodb_config["port"]) +collection = connection["oj"]["oj_submission"] + +submission = collection.find_one({"_id": ObjectId(submission_id)}) +if not submission: + exit() +connection.close() + +# 将代码写入文件 +language = languages[submission["language"]] +src_path = judger_workspace + "run/" + language["src_name"] +f = open(src_path, "w") +f.write(submission["code"].encode("utf8")) +f.close() + +# 编译 +try: + exe_path = compile_(language, src_path, judger_workspace + "run/") +except Exception as e: + print e + connection = pymongo.MongoClient(host=mongodb_config["host"], port=mongodb_config["port"]) + collection = connection["oj"]["oj_submission"] + data = {"result": result["compile_error"], "info": str(e)} + collection.find_one_and_update({"_id": ObjectId(submission_id)}, {"$set": data}) + connection.close() + exit() + +print "Compile successfully" +# 运行 +try: + client = JudgeClient(language_code=submission["language"], + exe_path=exe_path, + max_cpu_time=int(time_limit), + max_real_time=int(time_limit) * 2, + max_memory=int(memory_limit), + test_case_dir=judger_workspace + "test_case/" + test_case_id + "/") + judge_result = {"result": result["accepted"], "info": client.run()} + + for item in judge_result["info"]: + if item["result"]: + judge_result["result"] = item["result"] + break + else: + l = sorted(judge_result["info"], key=lambda k: k["cpu_time"]) + judge_result["accepted_answer_info"] = {"time": l[-1]["cpu_time"]} + +except Exception as e: + print e + judge_result = {"result": result["system_error"], "info": str(e)} + +print "Run successfully" +print judge_result +connection = pymongo.MongoClient(host=mongodb_config["host"], port=mongodb_config["port"]) +collection = connection["oj"]["oj_submission"] +collection.find_one_and_update({"_id": ObjectId(submission_id)}, {"$set": judge_result}) +connection.close() + + + diff --git a/judge/judger/settings.py b/judger/settings.py similarity index 81% rename from judge/judger/settings.py rename to judger/settings.py index 8fea94b..b70f454 100644 --- a/judge/judger/settings.py +++ b/judger/settings.py @@ -11,5 +11,12 @@ lrun_uid = 1001 # lrun用户组gid lrun_gid = 1002 -#judger工作目录 +# judger工作目录 judger_workspace = "/var/judger/" + +mongodb_config = { + "host": "192.168.59.3", + "username": "root", + "password": "root", + "port": 27017 +} diff --git a/judge/judger/utils.py b/judger/utils.py similarity index 100% rename from judge/judger/utils.py rename to judger/utils.py diff --git a/judge/controller/README.md b/judger_controller/README.md similarity index 100% rename from judge/controller/README.md rename to judger_controller/README.md diff --git a/judge/controller/__init__.py b/judger_controller/__init__.py similarity index 100% rename from judge/controller/__init__.py rename to judger_controller/__init__.py diff --git a/judger_controller/celery.py b/judger_controller/celery.py new file mode 100644 index 0000000..35ba2ac --- /dev/null +++ b/judger_controller/celery.py @@ -0,0 +1,10 @@ +# coding=utf-8 +from __future__ import absolute_import +from celery import Celery +from .settings import redis_config + +app = Celery("judge", broker="redis://" + + redis_config["host"] + ":" + + str(redis_config["port"]) + + "/" + str(redis_config["db"]), + include=["judger_controller.tasks"]) diff --git a/judger_controller/settings.py b/judger_controller/settings.py new file mode 100644 index 0000000..4d492eb --- /dev/null +++ b/judger_controller/settings.py @@ -0,0 +1,6 @@ +# coding=utf-8 +redis_config = { + "host": "127.0.0.1", + "port": 6379, + "db": 0 +} \ No newline at end of file diff --git a/judger_controller/tasks.py b/judger_controller/tasks.py new file mode 100644 index 0000000..3f4e255 --- /dev/null +++ b/judger_controller/tasks.py @@ -0,0 +1,20 @@ +# coding=utf-8 +from __future__ import absolute_import +from judger_controller.celery import app +import subprocess32 as subprocess + + +@app.task +def judge(solution_id, time_limit, memory_limit, test_case_id): + try: + subprocess.call("docker run -t -i --privileged --rm=true " + "-v /Users/virusdefender/Desktop/test_case/:/var/judger/test_case/ " + "-v /Users/virusdefender/Desktop/:/var/judger/code/ " + "judger " + "python judger/run.py " + "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % + (solution_id, str(time_limit), str(memory_limit), test_case_id), + # 设置最长运行时间是5倍的 cpu 时间 + timeout=(time_limit / 1000.0 * 5), shell=True) + except subprocess.TimeoutExpired: + print "docker timeout" diff --git a/oj/local_settings.py b/oj/local_settings.py index a99e009..7a7249c 100644 --- a/oj/local_settings.py +++ b/oj/local_settings.py @@ -1,19 +1,29 @@ # coding=utf-8 import os -LOG_PATH = "LOG/" - -# Database -# https://docs.djangoproject.com/en/1.8/ref/settings/#databases BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# 下面是需要自己修改的 +LOG_PATH = "LOG/" + +# 注意这是web 服务器访问的地址,判题度武器访问的地址不一定一样,因为可能不在一台机器上 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - 'CONN_MAX_AGE': 1, + 'CONN_MAX_AGE': 0.3, } } -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True \ No newline at end of file +mongodb_setting = { + 'HOST': '127.0.0.1', + 'USERNAME': 'root', + 'PASSWORD': 'root', + 'PORT': 27017 +} + +DEBUG = True + +# 同理 这是 web 服务器的上传路径 +TEST_CASE_DIR = "/Users/virusdefender/Desktop/test_case/" diff --git a/oj/settings.py b/oj/settings.py index 01bbadc..8d012d0 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -51,6 +51,7 @@ INSTALLED_APPS = ( 'group', 'problem', 'admin', + 'submission', 'rest_framework', 'rest_framework_swagger', diff --git a/oj/urls.py b/oj/urls.py index e3fbcda..e2fd51c 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -5,16 +5,16 @@ from django.views.generic import TemplateView from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, EmailCheckAPIView, - UserAPIView, UserAdminAPIView) + UserAdminAPIView, UserInfoAPIView) from announcement.views import AnnouncementAPIView, AnnouncementAdminAPIView -from group.views import GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView +from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, + JoinGroupAPIView, JoinGroupRequestAdminAPIView) from admin.views import AdminTemplateView -from problem.views import ProblemAdminAPIView -from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView - +from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView +from submission.views import SubmissionnAPIView urlpatterns = [ url(r'^install/$', "install.views.install"), @@ -22,8 +22,11 @@ urlpatterns = [ url(r'^docs/', include('rest_framework_swagger.urls')), url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"), url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), - url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"), name="user_register_page"), - url(r'^change_password/$', TemplateView.as_view(template_name="oj/account/change_password.html"), name="user_change_password_page"), + url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"), + name="user_register_page"), + url(r'^change_password/$', TemplateView.as_view(template_name="oj/account/change_password.html"), + name="user_change_password_page"), + url(r'^api/user/$', UserInfoAPIView.as_view(), name="user_info_api"), url(r'^api/login/$', UserLoginAPIView.as_view(), name="user_login_api"), url(r'^api/register/$', UserRegisterAPIView.as_view(), name="user_register_api"), url(r'^api/change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api"), @@ -32,23 +35,27 @@ urlpatterns = [ url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"), url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), - url(r'^announcement/(?P\d+)/$', "announcement.views.announcement_page", name="announcement_page"), - + url(r'^announcement/(?P\d+)/$', "announcement.views.announcement_page", + name="announcement_page"), url(r'^api/announcements/$', AnnouncementAPIView.as_view(), name="announcement_list_api"), - url(r'^api/admin/users/$', UserAPIView.as_view(), name="user_list_api"), - - url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"), - url(r'^problems/$', TemplateView.as_view(template_name="oj/problem/problem_list.html"), name="problem_list_page"), - url(r'^admin/template/(?P\w+)/(?P\w+).html', AdminTemplateView.as_view(), name="admin_template"), + url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), + name="add_contest_page"), + url(r'^problems/$', TemplateView.as_view(template_name="oj/problem/problem_list.html"), + name="problem_list_page"), + url(r'^admin/template/(?P\w+)/(?P\w+).html$', AdminTemplateView.as_view(), + name="admin_template"), 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/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"), url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), - url(r'^problem/(?P\d+)/my_solutions/', "problem.views.problem_my_solutions_list_page", name="problem_my_solutions_page"), - url(r'^my_solution/(?P\d+)/$', "problem.views.my_solution", name="my_solution_page"), + url(r'^problem/(?P\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page", + name="problem_my_submissions_page"), + url(r'^my_submission/(?P\w+)/$', "submission.views.my_submission", name="my_submission_page"), - url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), name="join_group_request_admin_api"), + url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), + name="join_group_request_admin_api"), + url(r'^api/submission/$', SubmissionnAPIView.as_view(), name="submission_api"), ] diff --git a/problem/migrations/0004_auto_20150812_2254.py b/problem/migrations/0004_auto_20150812_2254.py new file mode 100644 index 0000000..75f85d4 --- /dev/null +++ b/problem/migrations/0004_auto_20150812_2254.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0003_auto_20150810_2233'), + ] + + operations = [ + migrations.AlterField( + model_name='problem', + name='tags', + field=models.ManyToManyField(to='problem.ProblemTag'), + ), + ] diff --git a/problem/migrations/0006_merge.py b/problem/migrations/0006_merge.py new file mode 100644 index 0000000..f24e12d --- /dev/null +++ b/problem/migrations/0006_merge.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0005_auto_20150813_1807'), + ('problem', '0004_auto_20150812_2254'), + ] + + operations = [ + ] diff --git a/problem/models.py b/problem/models.py index 3be3783..25c85d3 100644 --- a/problem/models.py +++ b/problem/models.py @@ -53,4 +53,4 @@ class Problem(AbstractProblem): # 难度 0 - n difficulty = models.IntegerField() # 标签 - tags = models.ManyToManyField(ProblemTag, null=True) + tags = models.ManyToManyField(ProblemTag) diff --git a/problem/views.py b/problem/views.py index 0f618cd..4d13d44 100644 --- a/problem/views.py +++ b/problem/views.py @@ -10,7 +10,10 @@ from django.db.models import Q from rest_framework.views import APIView -from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate, rand_str +from django.conf import settings + +from utils.shortcuts import (serializer_invalid_response, error_response, + success_response, paginate, rand_str, error_page) from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer, ProblemTagSerializer, CreateProblemTagSerializer) from .models import Problem, ProblemTag @@ -35,32 +38,16 @@ class ProblemTagAdminAPIView(APIView): def get(self, request): return success_response(ProblemTagSerializer(ProblemTag.objects.all(), many=True).data) - keyword = request.GET.get("keyword", None) - if not keyword: - return error_response(u"参数错误") - tags = ProblemTag.objects.filter(name__contains=keyword) - return success_response(ProblemTagSerializer(tags, many=True).data) - - def problem_page(request, problem_id): try: problem = Problem.objects.get(id=problem_id) except Problem.DoesNotExist: - return render(request, "utils/error.html", {"error": u"题目不存在"}) + return error_page(request, u"题目不存在") return render(request, "oj/problem/problem.html", {"problem": problem, "samples": json.loads(problem.samples)}) -def problem_my_solutions_list_page(request, problem_id): - return render(request, "oj/problem/my_solutions_list.html") - - -def my_solution(request, solution_id): - return render(request, "oj/problem/my_solution.html") - - - class ProblemAdminAPIView(APIView): def post(self, request): """ @@ -163,7 +150,7 @@ class TestCaseUploadAPIView(APIView): f = request.FILES["file"] - tmp_zip = "tmp/" + rand_str() + ".zip" + tmp_zip = "/tmp/" + rand_str() + ".zip" with open(tmp_zip, "wb") as test_case_zip: for chunk in f: test_case_zip.write(chunk) @@ -196,13 +183,13 @@ class TestCaseUploadAPIView(APIView): return error_response(u"测试用例文件不完整,缺少" + name[0] + ".in") problem_test_dir = rand_str() - test_case_dir = "test_case/" + problem_test_dir + "/" + test_case_dir = settings.TEST_CASE_DIR + problem_test_dir + "/" # 得到了合法的测试用例文件列表 然后去解压缩 os.mkdir(test_case_dir) for name in l: f = open(test_case_dir + name, "wb") - f.write(test_case_file.read(name)) + f.write(test_case_file.read(name).replace("\r\n", "\n")) f.close() l.sort() diff --git a/requirements.txt b/requirements.txt index bb0fce3..324d7b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,6 @@ djangorestframework django-rest-swagger celery gunicorn -coverage \ No newline at end of file +coverage +subprocess32 +pymongo \ No newline at end of file diff --git a/solution/views.py b/solution/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/solution/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/static/src/js/app/admin/user/user_list.js b/static/src/js/app/admin/user/user_list.js index 910c24a..1e5f254 100644 --- a/static/src/js/app/admin/user/user_list.js +++ b/static/src/js/app/admin/user/user_list.js @@ -63,7 +63,7 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava getPageData(1); //用户列表初始化 //Ajax get数据 function getPageData(page) { - var url = "/api/admin/users/?paging=true&page=" + page + "&page_size=10"; + var url = "/api/admin/user/?paging=true&page=" + page + "&page_size=10"; if (vm.showAdminOnly == true) url += "&admin_type=1"; if (vm.key_word != "") diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index a713c07..abcf39c 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -1,12 +1,19 @@ -require(["jquery", "code_mirror"], function ($, code_mirror) { +require(["jquery", "code_mirror", "csrf", "bs_alert"], function ($, code_mirror, csrfHeader, bs_alert) { var code_editor = code_mirror($("#code-editor")[0], "text/x-csrc"); + var language = $("input[name='language'][checked]").val(); + var submission_id; - $("#language-selector").change(function () { - var language = $("#language-selector").val(); - var language_types = {c: "text/x-csrc", cpp: "text/x-c++src", java: "text/x-java"}; + $("input[name='language']").change(function () { + language = this.value; + var language_types = {"1": "text/x-csrc", "2": "text/x-c++src", "3": "text/x-java"}; code_editor.setOption("mode", language_types[language]); }); + $("#show-more-btn").click(function () { + $(".hide").attr("class", "problem-section"); + $("#show-more-btn").hide(); + }); + function show_loading() { $("#submit-code-button").attr("disabled", "disabled"); $("#loading-gif").show(); @@ -17,17 +24,109 @@ require(["jquery", "code_mirror"], function ($, code_mirror) { $("#loading-gif").hide(); } + + function get_result_html(data) { + // 0 结果正确 1 运行错误 2 超时 3 超内存 4 编译错误 + // 5 格式错误 6 结果错误 7 系统错误 8 等待判题 + var results = { + 0: {"alert_class": "success", message: "Accepted"}, + 1: {"alert_class": "danger", message: "Runtime Error"}, + 2: {"alert_class": "warning", message: "Time Limit Exceeded"}, + 3: {"alert_class": "warning", message: "Memory Limit Exceeded"}, + 4: {"alert_class": "danger", message: "Compile Error"}, + 5: {"alert_class": "warning", message: "Format Error"}, + 6: {"alert_class": "danger", message: "Wrong Answer"}, + 7: {"alert_class": "danger", message: "System Error"}, + 8: {"alert_class": "info", message: "Waiting"} + }; + + var html = ''); + + return html; + } + + function get_result() { + $.ajax({ + url: "/api/submission/?submission_id=" + submission_id, + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + // 8是还没有完成判题 + if (data.data.result == 8) { + // 1秒之后重新去获取 + setTimeout(get_result, 1000); + } + else { + hide_loading(); + $("#result").html(get_result_html(data.data)); + } + } + else { + bs_alert(data.data); + hide_loading(); + } + } + }) + } + $("#submit-code-button").click(function () { + var problem_id = window.location.pathname.split("/")[2]; + var code = code_editor.getValue(); + show_loading(); - setTimeout( - function () { - $("#a").animate({opacity: '1'}) - }, 3); + + if(!code.trim()){ + bs_alert("请填写代码!"); + hide_loading(); + return false; + } + + $("#result").html(""); + + $.ajax({ + beforeSend: csrfHeader, + url: "/api/submission/", + method: "post", + data: JSON.stringify({ + problem_id: window.location.pathname.split("/")[2], + language: language, + code: code_editor.getValue() + }), + contentType: "application/json", + success: function (data) { + if (!data.code) { + submission_id = data.data.submission_id; + // 获取到id 之后2秒去查询一下判题结果 + setTimeout(get_result, 2000); + } + else { + bs_alert(data.data); + hide_loading(); + } + } + }); + }); - $("#show-more-btn").click(function(){ - $(".hide").attr("class", "problem-section"); - $("#show-more-btn").hide(); + $.ajax({ + url : "/api/user/", + method: "get", + dataType: "json", + success: function(data){ + if(data.code){ + $("#submit-code-button").attr("disabled", "disabled"); + $("#result").html(''); + } + } }) - }); diff --git a/static/src/js/utils/bs_alert.js b/static/src/js/utils/bs_alert.js index b35f06d..547b919 100644 --- a/static/src/js/utils/bs_alert.js +++ b/static/src/js/utils/bs_alert.js @@ -1,7 +1,17 @@ define("bs_alert", ["jquery", "bootstrap"], function($){ function bs_alert(content){ + if(!$("#alert-modal").length) { + var html = ''; + $("body").append(html); + } $("#modal-text").html(content); - $("#modal").modal(); + $("#alert-modal").modal(); } return bs_alert; }); \ No newline at end of file diff --git a/solution/__init__.py b/submission/__init__.py similarity index 100% rename from solution/__init__.py rename to submission/__init__.py diff --git a/solution/admin.py b/submission/admin.py similarity index 100% rename from solution/admin.py rename to submission/admin.py diff --git a/solution/migrations/__init__.py b/submission/migrations/__init__.py similarity index 100% rename from solution/migrations/__init__.py rename to submission/migrations/__init__.py diff --git a/solution/models.py b/submission/models.py similarity index 100% rename from solution/models.py rename to submission/models.py diff --git a/submission/serializers.py b/submission/serializers.py new file mode 100644 index 0000000..c64ada6 --- /dev/null +++ b/submission/serializers.py @@ -0,0 +1,10 @@ +# coding=utf-8 +from rest_framework import serializers + + + +class CreateSubmissionSerializer(serializers.Serializer): + problem_id = serializers.IntegerField() + language = serializers.IntegerField() + code = serializers.CharField(max_length=3000) + diff --git a/solution/tests.py b/submission/tests.py similarity index 100% rename from solution/tests.py rename to submission/tests.py diff --git a/submission/views.py b/submission/views.py new file mode 100644 index 0000000..0a923e0 --- /dev/null +++ b/submission/views.py @@ -0,0 +1,96 @@ +# coding=utf-8 +import datetime +import pymongo +from bson.objectid import ObjectId + +from django.shortcuts import render + +from rest_framework.views import APIView + +from django.conf import settings + +from judger.result import result +from judger_controller.tasks import judge +from account.decorators import login_required +from problem.models import Problem +from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page +from .serializers import CreateSubmissionSerializer + + +def _create_mondodb_connection(): + mongodb_setting = settings["mongodb_setting"] + connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) + return connection["oj"]["oj_submission"] + + +class SubmissionnAPIView(APIView): + @login_required + def post(self, request): + """ + 提交代码 + --- + request_serializer: CreateSubmissionSerializer + """ + serializer = CreateSubmissionSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + # data["language"] = int(data["language"]) + data["user_id"] = request.user.id + data["result"] = result["waiting"] + data["create_time"] = datetime.datetime.now() + try: + problem = Problem.objects.get(id=data["problem_id"]) + except Problem.DoesNotExist: + return error_response(u"题目不存在") + mongodb_setting = settings.DATABASES["mongodb"] + connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) + collection = connection["oj"]["oj_submission"] + submission_id = str(collection.insert_one(data).inserted_id) + judge.delay(submission_id, problem.time_limit, problem.memory_limit, problem.test_case_id) + return success_response({"submission_id": submission_id}) + else: + return serializer_invalid_response(serializer) + + @login_required + def get(self, request): + submission_id = request.GET.get("submission_id", None) + if not submission_id: + return error_response(u"参数错误") + submission = _create_mondodb_connection().find_one({"_id": ObjectId(submission_id), "user_id": request.user.id}) + if submission: + response_data = {"result": submission["result"]} + if submission["result"] == 0: + response_data["accepted_answer_info"] = submission["accepted_answer_info"] + return success_response(response_data) + else: + return error_response(u"提交不存在") + + +@login_required +def problem_my_submissions_list_page(request, problem_id): + collection = _create_mondodb_connection() + submissions = collection.find({"problem_id": int(problem_id), "user_id": request.user.id}, + projection=["result", "accepted_answer_info", "create_time", "language"], + sort=[["create_time", -pymongo.ASCENDING]]) + try: + problem = Problem.objects.get(id=problem_id, visible=True) + except Problem.DoesNotExist: + return error_page(request, u"问题不存在") + return render(request, "oj/problem/my_submissions_list.html", + {"submissions": submissions, "problem": problem}) + + +@login_required +def my_submission(request, submission_id): + collection = _create_mondodb_connection() + submission = collection.find_one({"user_id": request.user.id, "_id": ObjectId(submission_id)}, + projection=["result", "accepted_answer_info", "create_time", + "language", "code", "problem_id", "info"]) + if not submission: + return error_page(request, u"提交不存在") + try: + problem = Problem.objects.get(id=submission["problem_id"], visible=True) + except Problem.DoesNotExist: + return error_page(request, u"提交不存在") + + return render(request, "oj/problem/my_submission.html", {"submission": submission, "problem": problem}) \ No newline at end of file diff --git a/template/admin/admin.html b/template/admin/admin.html index 69aba41..d5a0e18 100644 --- a/template/admin/admin.html +++ b/template/admin/admin.html @@ -124,23 +124,7 @@ - + diff --git a/template/admin_base.html b/template/admin_base.html deleted file mode 100644 index 86cf7e2..0000000 --- a/template/admin_base.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - 在线评测系统 - 后台管理 - - - {% block css_block %}{% endblock %} - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - {% block body %}{% endblock %} - -
-
- - - - - - - -{% block js_block %}{% endblock %} - - - - - \ No newline at end of file diff --git a/template/oj/problem/_problem_header.html b/template/oj/problem/_problem_header.html new file mode 100644 index 0000000..82d118f --- /dev/null +++ b/template/oj/problem/_problem_header.html @@ -0,0 +1,7 @@ + +

{{ problem.title }}

+ +

发布时间 : {{ problem.create_time }}   + 时间限制 : {{ problem.time_limit }}ms   + 内存限制 : {{ problem.memory_limit }}M +

\ No newline at end of file diff --git a/template/oj/problem/my_solution.html b/template/oj/problem/my_solution.html deleted file mode 100644 index 8f05b10..0000000 --- a/template/oj/problem/my_solution.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends 'oj_base.html' %} - -{% block body %} - -
- -

Battle Over Cities - Hard Version

-

cpu: 1000ms 内存: 256M

-
-
-

运行结果:Accepted

-

cpu: 1000ms 内存: 256M 语言:python

-
-
-
- -
-
- -{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/my_solutions_list.html b/template/oj/problem/my_solutions_list.html deleted file mode 100644 index 613410a..0000000 --- a/template/oj/problem/my_solutions_list.html +++ /dev/null @@ -1,53 +0,0 @@ -{% extends 'oj_base.html' %} - -{% block body %} - -
- -

Battle Over Cities - Hard Version

-

cpu: 1000ms 内存: 256M

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#提交时间结果运行时间运行内存语言
11Error Format333
2WrongWrong Answer@fat33
3LarryAccepted@twitter33
-
-{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/my_submission.html b/template/oj/problem/my_submission.html new file mode 100644 index 0000000..e7f6ce4 --- /dev/null +++ b/template/oj/problem/my_submission.html @@ -0,0 +1,57 @@ +{% extends 'oj_base.html' %} +{% block css_block %} + +{% endblock %} +{% block body %} + {% load submission %} +
+ + {% include "oj/problem/_problem_header.html" %} +
+
+

运行结果 : + {{ submission.result|translate_result }} + +

+ {% ifequal submission.result 0 %} +

时间 : {{ submission.accepted_answer_info.time }}ms 语言 : + {{ submission.language|translate_language }} +

+ {% endifequal %} + {% ifequal submission.result 4 %} +

{{ submission.info }}

+ {% endifequal %} +

提交时间 : {{ submission.create_time }}

+
+
+
+ +
+
+ +{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/my_submissions_list.html b/template/oj/problem/my_submissions_list.html new file mode 100644 index 0000000..b1c6388 --- /dev/null +++ b/template/oj/problem/my_submissions_list.html @@ -0,0 +1,50 @@ +{% extends 'oj_base.html' %} + +{% block body %} + + {% load submission %} +
+ +

{{ problem.title }}

+ +

发布时间: {{ problem.create_time }}   + 时间限制: {{ problem.time_limit }}ms   + 内存限制: {{ problem.memory_limit }}M

+ + + + + + + + + + + + {% for item in submissions %} + + + + + + + + {% endfor %} + + +
#提交时间结果运行时间语言
{{ forloop.counter }}{{ item.create_time }}{{ item.result|translate_result }} + {% if item.accepted_answer_info.time %} + {{ item.accepted_answer_info.time }}ms + {% else %} + -- + {% endif %} + + {{ item.language|translate_language }} +
+
+{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index 5a7a3b6..2d8ddbb 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -5,11 +5,9 @@ -

{{ problem.title }}

- -

发布时间: {{ problem.create_time }} CPU: {{ problem.time_limit }}ms 内存: {{ problem.memory_limit }}M

+ {% include "oj/problem/_problem_header.html" %}
@@ -20,43 +18,43 @@
-

第一行包括两个数n,k

+

{{ problem.input_description }}

-

第一行包括两个数n,k

+

{{ problem.output_description }}k

{% for item in samples %} -
- +
+
 {{ item.input }}
-
-
+
+
- +
 {{ item.output }}
-
+
{% endfor %}
- {% if problem.hind %} -
- + {% if problem.hint %} +
+ -

{{ problem.hint|safe }}

-
- {% endif %} +

{{ problem.hint|safe }}

+
+ {% endif %}

{% for tag in problem.tags.all %} - {{ tag.name }} + {{ tag.name }} {% endfor %}

@@ -66,13 +64,13 @@
@@ -89,24 +87,10 @@
-
-
- - - - + +
+
{% endblock %} diff --git a/template/oj_base.html b/template/oj_base.html index 0cfcf82..4a17157 100644 --- a/template/oj_base.html +++ b/template/oj_base.html @@ -8,13 +8,13 @@ 在线评测系统 - - {% block css_block %}{% endblock %} - - + + + {% block css_block %}{% endblock %} + @@ -47,7 +47,7 @@
  • 比赛
  • 关于
  • - {% if not request.user.is_authenticated %} + {% if request.user.is_authenticated %}