From 61ab910d5386519cf424e59fc8db1f18a8af9147 Mon Sep 17 00:00:00 2001 From: virusdefender Date: Sun, 25 Sep 2016 14:07:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/locale/zh_CN/LC_MESSAGES/django.po | 56 -- account/middleware.py | 16 +- account/migrations/0001_initial.py | 49 +- account/migrations/0002_auto_20160925_1337.py | 36 ++ .../migrations/0002_user_problems_status.py | 19 - account/migrations/0003_auto_20150915_2025.py | 19 - ...217_0920.py => 0003_auto_20160925_1402.py} | 13 +- .../0004_remove_user_problems_status.py | 18 - .../migrations/0005_user_problems_status.py | 19 - account/migrations/0006_auto_20150924_1530.py | 20 - account/migrations/0007_auto_20150929_2320.py | 25 - .../0008_user_login_failed_counter.py | 19 - ...9_user_reset_password_token_create_time.py | 19 - .../0010_remove_user_login_failed_counter.py | 18 - account/migrations/0011_user_auth_token.py | 19 - account/migrations/0012_auto_20151012_1546.py | 19 - account/migrations/0013_userprofile.py | 37 -- account/migrations/0014_auto_20151110_1037.py | 24 - .../migrations/0015_userprofile_student_id.py | 20 - account/migrations/0016_auto_20151211_2230.py | 25 - account/migrations/0017_auto_20151212_2139.py | 20 - account/migrations/0019_user_is_forbidden.py | 20 - account/migrations/0020_auto_20160424_1241.py | 24 - account/migrations/0021_auto_20160424_1243.py | 20 - account/migrations/0022_auto_20160613_1606.py | 40 -- account/models.py | 15 +- account/serializers.py | 55 +- account/tasks.py | 8 - {admin => account/urls}/__init__.py | 0 account/urls/admin.py | 8 + account/urls/oj.py | 10 + account/views.py | 531 --------------- .../migrations => account/views}/__init__.py | 0 account/views/admin.py | 107 +++ account/views/oj.py | 107 +++ admin/middleware.py | 16 - admin/tests.py | 0 admin/views.py | 15 - announcement/migrations/0001_initial.py | 13 +- .../migrations/0002_auto_20150818_1445.py | 26 - .../migrations/0003_auto_20150922_1703.py | 20 - .../migrations/0004_auto_20151015_1555.py | 22 - announcement/models.py | 2 +- announcement/serializers.py | 5 +- {contest => announcement/urls}/__init__.py | 0 announcement/urls/admin.py | 8 + announcement/views.py | 20 +- {contest/migrations => conf}/__init__.py | 0 {problem => conf}/admin.py | 0 {group => conf/migrations}/__init__.py | 0 {admin => conf}/models.py | 2 + conf/tests.py | 3 + conf/views.py | 3 + contest/decorators.py | 113 ---- contest/migrations/0001_initial.py | 74 --- contest/migrations/0002_contest_visible.py | 19 - .../0003_contestproblem_difficulty.py | 20 - .../0004_remove_contestproblem_difficulty.py | 18 - .../migrations/0005_contestproblem_score.py | 19 - contest/migrations/0005_contestsubmission.py | 31 - contest/migrations/0006_merge.py | 15 - .../0007_contestsubmission_ac_time.py | 19 - contest/migrations/0008_auto_20150912_1912.py | 19 - .../0009_contestsubmission_first_achieved.py | 19 - contest/migrations/0010_auto_20150922_1703.py | 25 - contest/migrations/0011_contestrank.py | 29 - contest/migrations/0012_auto_20151008_1124.py | 24 - contest/migrations/0013_auto_20151017_1511.py | 64 -- contest/migrations/0014_auto_20160404_1509.py | 30 - contest/migrations/0015_auto_20160404_1641.py | 20 - .../0016_contestproblem_spj_version.py | 20 - contest/models.py | 148 ----- contest/serializers.py | 119 ---- contest/tests.py | 0 contest/views.py | 612 ------------------ dockerfiles/oj_web_server/requirements.txt | 2 +- frontend/admin/src/views/account/editUser.vue | 4 +- frontend/admin/src/views/account/userList.vue | 2 +- .../views/announcement/announcementList.vue | 2 +- group/migrations/0001_initial.py | 61 -- group/migrations/0002_auto_20150811_1456.py | 28 - group/migrations/0002_auto_20150811_1649.py | 28 - group/migrations/0003_auto_20150811_1906.py | 19 - group/migrations/0004_merge.py | 15 - .../0005_joingrouprequest_accepted.py | 19 - group/migrations/0006_auto_20151209_1834.py | 20 - group/migrations/0007_auto_20151209_1836.py | 38 -- group/migrations/__init__.py | 0 group/models.py | 53 -- group/serializers.py | 81 --- group/tests.py | 0 group/views.py | 355 ---------- judge/__init__.py | 0 judge/client.py | 176 ----- judge/compiler.py | 40 -- judge/judge_exceptions.py | 9 - judge/language.py | 42 -- judge/logger.py | 8 - judge/result.py | 15 - judge/runner.py | 92 --- judge/server.py | 13 - judge/settings.py | 9 - judge/spj_client.py | 26 - judge_dispatcher/__init__.py | 1 - judge_dispatcher/migrations/0001_initial.py | 41 -- .../migrations/0002_auto_20151207_2310.py | 32 - .../migrations/0003_auto_20151223_0029.py | 25 - .../migrations/0004_auto_20160407_1816.py | 35 - .../migrations/0005_auto_20160519_1627.py | 32 - judge_dispatcher/migrations/__init__.py | 0 judge_dispatcher/models.py | 33 - judge_dispatcher/rpc_client.py | 24 - judge_dispatcher/serializers.py | 29 - judge_dispatcher/tasks.py | 199 ------ judge_dispatcher/views.py | 70 -- manage.py | 3 + oj/settings.py | 9 +- oj/urls.py | 145 +---- problem/__init__.py | 0 problem/decorators.py | 32 - problem/migrations/0001_initial.py | 55 -- .../0002_remove_problemtag_description.py | 18 - problem/migrations/0003_auto_20150810_2233.py | 19 - problem/migrations/0004_auto_20150812_2254.py | 19 - problem/migrations/0004_auto_20150813_1459.py | 26 - problem/migrations/0005_auto_20150813_1807.py | 24 - problem/migrations/0006_merge.py | 15 - .../0007_remove_problem_last_update_time.py | 18 - problem/migrations/0008_auto_20150922_1702.py | 20 - problem/migrations/0009_auto_20151008_1125.py | 18 - problem/migrations/0010_auto_20151017_1226.py | 24 - problem/migrations/0011_auto_20151017_1227.py | 20 - problem/migrations/0012_auto_20160404_1509.py | 30 - problem/migrations/0013_auto_20160404_1641.py | 20 - .../migrations/0014_problem_spj_version.py | 20 - problem/migrations/__init__.py | 0 problem/models.py | 72 --- problem/serizalizers.py | 80 --- problem/tests.py | 0 problem/views.py | 449 ------------- submission/__init__.py | 0 submission/migrations/0001_initial.py | 32 - .../migrations/0002_submission_is_counted.py | 19 - .../migrations/0003_auto_20150821_1654.py | 24 - .../0004_remove_submission_is_counted.py | 18 - .../migrations/0005_submission_contest_id.py | 19 - .../migrations/0006_submission_shared.py | 19 - .../migrations/0007_auto_20151207_1645.py | 24 - .../migrations/0008_auto_20151208_2106.py | 25 - submission/migrations/__init__.py | 0 submission/models.py | 33 - submission/serializers.py | 53 -- submission/tasks.py | 9 - submission/tests.py | 0 submission/views.py | 372 ----------- template/src/404.html | 151 ----- template/src/500.html | 151 ----- template/src/admin/admin.html | 100 --- .../src/admin/announcement/announcement.html | 76 --- template/src/admin/contest/add_contest.html | 100 --- template/src/admin/contest/contest_list.html | 45 -- template/src/admin/contest/edit_contest.html | 118 ---- template/src/admin/contest/edit_problem.html | 111 ---- template/src/admin/contest/problem_list.html | 35 - template/src/admin/group/group.html | 72 --- template/src/admin/group/group_detail.html | 72 --- .../admin/group/join_group_request_list.html | 31 - template/src/admin/index/index.html | 1 - template/src/admin/judges/judges.html | 128 ---- template/src/admin/problem/add_problem.html | 118 ---- template/src/admin/problem/edit_problem.html | 125 ---- template/src/admin/problem/problem_list.html | 44 -- .../src/admin/problem/submission_list.html | 33 - template/src/admin/user/user_list.html | 100 --- .../src/oj/account/apply_reset_password.html | 40 -- template/src/oj/account/avatar.html | 54 -- template/src/oj/account/change_password.html | 62 -- template/src/oj/account/login.html | 43 -- template/src/oj/account/register.html | 60 -- template/src/oj/account/reset_password.html | 53 -- template/src/oj/account/settings.html | 112 ---- template/src/oj/account/sso.html | 20 - template/src/oj/account/two_factor_auth.html | 41 -- template/src/oj/account/user_index.html | 93 --- .../oj/announcement/_announcement_panel.html | 19 - .../src/oj/announcement/announcement.html | 23 - .../contest/_contest_auto_refresh_check.html | 3 - template/src/oj/contest/_contest_filter.html | 5 - template/src/oj/contest/_contest_header.html | 43 -- .../src/oj/contest/_countdown_template.html | 19 - template/src/oj/contest/contest_index.html | 27 - template/src/oj/contest/contest_list.html | 90 --- .../src/oj/contest/contest_problems_list.html | 70 -- template/src/oj/contest/contest_rank.html | 92 --- .../src/oj/contest/no_contest_permission.html | 45 -- template/src/oj/contest/submissions_list.html | 125 ---- template/src/oj/group/group.html | 49 -- template/src/oj/group/group_list.html | 78 --- template/src/oj/group/my_application.html | 28 - .../src/oj/group/my_application_list.html | 46 -- template/src/oj/index.html | 192 ------ template/src/oj/problem/_mathjax_js.html | 14 - template/src/oj/problem/_problem_header.html | 11 - .../src/oj/problem/_problem_main_info.html | 29 - template/src/oj/problem/_submit_problem.html | 25 - template/src/oj/problem/contest_problem.html | 43 -- template/src/oj/problem/problem.html | 52 -- template/src/oj/problem/problem_list.html | 95 --- template/src/oj/submission/my_submission.html | 142 ---- .../problem_my_submissions_list.html | 65 -- .../src/oj/submission/submissions_list.html | 105 --- template/src/oj_base.html | 108 ---- template/src/utils/about.html | 34 - template/src/utils/error.html | 151 ----- template/src/utils/help.html | 54 -- template/src/utils/rank.html | 52 -- template/src/utils/reset_password_email.html | 78 --- tools/runtest.sh | 0 utils/management/commands/initadmin.py | 6 +- 219 files changed, 384 insertions(+), 10351 deletions(-) delete mode 100644 account/locale/zh_CN/LC_MESSAGES/django.po create mode 100644 account/migrations/0002_auto_20160925_1337.py delete mode 100644 account/migrations/0002_user_problems_status.py delete mode 100644 account/migrations/0003_auto_20150915_2025.py rename account/migrations/{0018_auto_20160217_0920.py => 0003_auto_20160925_1402.py} (50%) delete mode 100644 account/migrations/0004_remove_user_problems_status.py delete mode 100644 account/migrations/0005_user_problems_status.py delete mode 100644 account/migrations/0006_auto_20150924_1530.py delete mode 100644 account/migrations/0007_auto_20150929_2320.py delete mode 100644 account/migrations/0008_user_login_failed_counter.py delete mode 100644 account/migrations/0009_user_reset_password_token_create_time.py delete mode 100644 account/migrations/0010_remove_user_login_failed_counter.py delete mode 100644 account/migrations/0011_user_auth_token.py delete mode 100644 account/migrations/0012_auto_20151012_1546.py delete mode 100644 account/migrations/0013_userprofile.py delete mode 100644 account/migrations/0014_auto_20151110_1037.py delete mode 100644 account/migrations/0015_userprofile_student_id.py delete mode 100644 account/migrations/0016_auto_20151211_2230.py delete mode 100644 account/migrations/0017_auto_20151212_2139.py delete mode 100644 account/migrations/0019_user_is_forbidden.py delete mode 100644 account/migrations/0020_auto_20160424_1241.py delete mode 100644 account/migrations/0021_auto_20160424_1243.py delete mode 100644 account/migrations/0022_auto_20160613_1606.py delete mode 100644 account/tasks.py rename {admin => account/urls}/__init__.py (100%) create mode 100644 account/urls/admin.py create mode 100644 account/urls/oj.py delete mode 100644 account/views.py rename {admin/migrations => account/views}/__init__.py (100%) create mode 100644 account/views/admin.py create mode 100644 account/views/oj.py delete mode 100644 admin/middleware.py delete mode 100644 admin/tests.py delete mode 100644 admin/views.py delete mode 100644 announcement/migrations/0002_auto_20150818_1445.py delete mode 100644 announcement/migrations/0003_auto_20150922_1703.py delete mode 100644 announcement/migrations/0004_auto_20151015_1555.py rename {contest => announcement/urls}/__init__.py (100%) create mode 100644 announcement/urls/admin.py rename {contest/migrations => conf}/__init__.py (100%) rename {problem => conf}/admin.py (100%) rename {group => conf/migrations}/__init__.py (100%) rename {admin => conf}/models.py (58%) create mode 100644 conf/tests.py create mode 100644 conf/views.py delete mode 100644 contest/decorators.py delete mode 100644 contest/migrations/0001_initial.py delete mode 100644 contest/migrations/0002_contest_visible.py delete mode 100644 contest/migrations/0003_contestproblem_difficulty.py delete mode 100644 contest/migrations/0004_remove_contestproblem_difficulty.py delete mode 100644 contest/migrations/0005_contestproblem_score.py delete mode 100644 contest/migrations/0005_contestsubmission.py delete mode 100644 contest/migrations/0006_merge.py delete mode 100644 contest/migrations/0007_contestsubmission_ac_time.py delete mode 100644 contest/migrations/0008_auto_20150912_1912.py delete mode 100644 contest/migrations/0009_contestsubmission_first_achieved.py delete mode 100644 contest/migrations/0010_auto_20150922_1703.py delete mode 100644 contest/migrations/0011_contestrank.py delete mode 100644 contest/migrations/0012_auto_20151008_1124.py delete mode 100644 contest/migrations/0013_auto_20151017_1511.py delete mode 100644 contest/migrations/0014_auto_20160404_1509.py delete mode 100644 contest/migrations/0015_auto_20160404_1641.py delete mode 100644 contest/migrations/0016_contestproblem_spj_version.py delete mode 100644 contest/models.py delete mode 100644 contest/serializers.py delete mode 100644 contest/tests.py delete mode 100644 contest/views.py delete mode 100644 group/migrations/0001_initial.py delete mode 100644 group/migrations/0002_auto_20150811_1456.py delete mode 100644 group/migrations/0002_auto_20150811_1649.py delete mode 100644 group/migrations/0003_auto_20150811_1906.py delete mode 100644 group/migrations/0004_merge.py delete mode 100644 group/migrations/0005_joingrouprequest_accepted.py delete mode 100644 group/migrations/0006_auto_20151209_1834.py delete mode 100644 group/migrations/0007_auto_20151209_1836.py delete mode 100644 group/migrations/__init__.py delete mode 100644 group/models.py delete mode 100644 group/serializers.py delete mode 100644 group/tests.py delete mode 100644 group/views.py delete mode 100644 judge/__init__.py delete mode 100644 judge/client.py delete mode 100644 judge/compiler.py delete mode 100644 judge/judge_exceptions.py delete mode 100644 judge/language.py delete mode 100644 judge/logger.py delete mode 100644 judge/result.py delete mode 100644 judge/runner.py delete mode 100644 judge/server.py delete mode 100644 judge/settings.py delete mode 100644 judge/spj_client.py delete mode 100644 judge_dispatcher/__init__.py delete mode 100644 judge_dispatcher/migrations/0001_initial.py delete mode 100644 judge_dispatcher/migrations/0002_auto_20151207_2310.py delete mode 100644 judge_dispatcher/migrations/0003_auto_20151223_0029.py delete mode 100644 judge_dispatcher/migrations/0004_auto_20160407_1816.py delete mode 100644 judge_dispatcher/migrations/0005_auto_20160519_1627.py delete mode 100644 judge_dispatcher/migrations/__init__.py delete mode 100644 judge_dispatcher/models.py delete mode 100644 judge_dispatcher/rpc_client.py delete mode 100644 judge_dispatcher/serializers.py delete mode 100644 judge_dispatcher/tasks.py delete mode 100644 judge_dispatcher/views.py delete mode 100644 problem/__init__.py delete mode 100644 problem/decorators.py delete mode 100644 problem/migrations/0001_initial.py delete mode 100644 problem/migrations/0002_remove_problemtag_description.py delete mode 100644 problem/migrations/0003_auto_20150810_2233.py delete mode 100644 problem/migrations/0004_auto_20150812_2254.py delete mode 100644 problem/migrations/0004_auto_20150813_1459.py delete mode 100644 problem/migrations/0005_auto_20150813_1807.py delete mode 100644 problem/migrations/0006_merge.py delete mode 100644 problem/migrations/0007_remove_problem_last_update_time.py delete mode 100644 problem/migrations/0008_auto_20150922_1702.py delete mode 100644 problem/migrations/0009_auto_20151008_1125.py delete mode 100644 problem/migrations/0010_auto_20151017_1226.py delete mode 100644 problem/migrations/0011_auto_20151017_1227.py delete mode 100644 problem/migrations/0012_auto_20160404_1509.py delete mode 100644 problem/migrations/0013_auto_20160404_1641.py delete mode 100644 problem/migrations/0014_problem_spj_version.py delete mode 100644 problem/migrations/__init__.py delete mode 100644 problem/models.py delete mode 100644 problem/serizalizers.py delete mode 100644 problem/tests.py delete mode 100644 problem/views.py delete mode 100644 submission/__init__.py delete mode 100644 submission/migrations/0001_initial.py delete mode 100644 submission/migrations/0002_submission_is_counted.py delete mode 100644 submission/migrations/0003_auto_20150821_1654.py delete mode 100644 submission/migrations/0004_remove_submission_is_counted.py delete mode 100644 submission/migrations/0005_submission_contest_id.py delete mode 100644 submission/migrations/0006_submission_shared.py delete mode 100644 submission/migrations/0007_auto_20151207_1645.py delete mode 100644 submission/migrations/0008_auto_20151208_2106.py delete mode 100644 submission/migrations/__init__.py delete mode 100644 submission/models.py delete mode 100644 submission/serializers.py delete mode 100644 submission/tasks.py delete mode 100644 submission/tests.py delete mode 100644 submission/views.py delete mode 100644 template/src/404.html delete mode 100644 template/src/500.html delete mode 100644 template/src/admin/admin.html delete mode 100644 template/src/admin/announcement/announcement.html delete mode 100644 template/src/admin/contest/add_contest.html delete mode 100644 template/src/admin/contest/contest_list.html delete mode 100644 template/src/admin/contest/edit_contest.html delete mode 100644 template/src/admin/contest/edit_problem.html delete mode 100644 template/src/admin/contest/problem_list.html delete mode 100644 template/src/admin/group/group.html delete mode 100644 template/src/admin/group/group_detail.html delete mode 100644 template/src/admin/group/join_group_request_list.html delete mode 100644 template/src/admin/index/index.html delete mode 100644 template/src/admin/judges/judges.html delete mode 100644 template/src/admin/problem/add_problem.html delete mode 100644 template/src/admin/problem/edit_problem.html delete mode 100644 template/src/admin/problem/problem_list.html delete mode 100644 template/src/admin/problem/submission_list.html delete mode 100644 template/src/admin/user/user_list.html delete mode 100644 template/src/oj/account/apply_reset_password.html delete mode 100644 template/src/oj/account/avatar.html delete mode 100644 template/src/oj/account/change_password.html delete mode 100644 template/src/oj/account/login.html delete mode 100644 template/src/oj/account/register.html delete mode 100644 template/src/oj/account/reset_password.html delete mode 100644 template/src/oj/account/settings.html delete mode 100644 template/src/oj/account/sso.html delete mode 100644 template/src/oj/account/two_factor_auth.html delete mode 100644 template/src/oj/account/user_index.html delete mode 100644 template/src/oj/announcement/_announcement_panel.html delete mode 100644 template/src/oj/announcement/announcement.html delete mode 100644 template/src/oj/contest/_contest_auto_refresh_check.html delete mode 100644 template/src/oj/contest/_contest_filter.html delete mode 100644 template/src/oj/contest/_contest_header.html delete mode 100644 template/src/oj/contest/_countdown_template.html delete mode 100644 template/src/oj/contest/contest_index.html delete mode 100644 template/src/oj/contest/contest_list.html delete mode 100644 template/src/oj/contest/contest_problems_list.html delete mode 100644 template/src/oj/contest/contest_rank.html delete mode 100644 template/src/oj/contest/no_contest_permission.html delete mode 100644 template/src/oj/contest/submissions_list.html delete mode 100644 template/src/oj/group/group.html delete mode 100644 template/src/oj/group/group_list.html delete mode 100644 template/src/oj/group/my_application.html delete mode 100644 template/src/oj/group/my_application_list.html delete mode 100644 template/src/oj/index.html delete mode 100644 template/src/oj/problem/_mathjax_js.html delete mode 100644 template/src/oj/problem/_problem_header.html delete mode 100644 template/src/oj/problem/_problem_main_info.html delete mode 100644 template/src/oj/problem/_submit_problem.html delete mode 100644 template/src/oj/problem/contest_problem.html delete mode 100644 template/src/oj/problem/problem.html delete mode 100644 template/src/oj/problem/problem_list.html delete mode 100644 template/src/oj/submission/my_submission.html delete mode 100644 template/src/oj/submission/problem_my_submissions_list.html delete mode 100644 template/src/oj/submission/submissions_list.html delete mode 100644 template/src/oj_base.html delete mode 100644 template/src/utils/about.html delete mode 100644 template/src/utils/error.html delete mode 100644 template/src/utils/help.html delete mode 100644 template/src/utils/rank.html delete mode 100644 template/src/utils/reset_password_email.html mode change 100644 => 100755 tools/runtest.sh diff --git a/account/locale/zh_CN/LC_MESSAGES/django.po b/account/locale/zh_CN/LC_MESSAGES/django.po deleted file mode 100644 index 6b650075..00000000 --- a/account/locale/zh_CN/LC_MESSAGES/django.po +++ /dev/null @@ -1,56 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-06-13 21:24+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: account/decorators.py:29 account/decorators.py:31 -msgid "Your account is disabled" -msgstr "您的账号已被禁用" - -#: account/decorators.py:35 account/middleware.py:21 -msgid "Please login in first" -msgstr "请先登录" - -#: account/tests.py:36 account/tests.py:59 account/tests.py:114 -#: account/views.py:51 account/views.py:59 account/views.py:96 -#: account/views.py:118 -msgid "Succeeded" -msgstr "" - -#: account/tests.py:45 account/views.py:63 -msgid "Invalid username or password" -msgstr "" - -#: account/tests.py:70 account/views.py:61 -msgid "Invalid two factor verification code" -msgstr "" - -#: account/tests.py:106 account/views.py:78 account/views.py:112 -msgid "Invalid captcha" -msgstr "" - -#: account/tests.py:122 account/views.py:81 -msgid "Username already exists" -msgstr "" - -#: account/tests.py:130 account/views.py:86 account/views.py:89 -msgid "Email already exists" -msgstr "" - -#: account/views.py:120 -msgid "Invalid old password" -msgstr "" diff --git a/account/middleware.py b/account/middleware.py index d5f58ef1..64ee3dca 100644 --- a/account/middleware.py +++ b/account/middleware.py @@ -1,7 +1,6 @@ # coding=utf-8 import time import json -import urllib from django.http import HttpResponse from django.utils.translation import ugettext as _ @@ -23,5 +22,18 @@ class SessionSecurityMiddleware(object): content_type="application/json") else: return redirect_to_login(request) - # 更新最后活动日期 + # update last active time request.session["last_activity"] = time.time() + + +class AdminRequiredMiddleware(object): + def process_request(self, request): + path = request.path_info + if path.startswith("/admin/") or path.startswith("/api/admin/"): + if not(request.user.is_authenticated() and request.user.is_admin()): + if request.is_ajax(): + return HttpResponse(json.dumps({"code": 1, "data": _("Please login in first")}), + content_type="application/json") + else: + return HttpResponse(json.dumps({"code": 1, "data": _("Admin required")}), + content_type="application/json") \ No newline at end of file diff --git a/account/migrations/0001_initial.py b/account/migrations/0001_initial.py index 03d55846..b57be4b6 100644 --- a/account/migrations/0001_initial.py +++ b/account/migrations/0001_initial.py @@ -1,12 +1,18 @@ # -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-09-25 05:30 from __future__ import unicode_literals -from django.db import models, migrations import account.models +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import jsonfield.fields class Migration(migrations.Migration): + initial = True + dependencies = [ ] @@ -14,14 +20,24 @@ class Migration(migrations.Migration): migrations.CreateModel( name='User', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)), - ('username', models.CharField(unique=True, max_length=30)), - ('real_name', models.CharField(max_length=30, null=True, blank=True)), - ('email', models.EmailField(max_length=254, null=True, blank=True)), - ('create_time', models.DateTimeField(auto_now_add=True)), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('username', models.CharField(max_length=30, unique=True)), + ('real_name', models.CharField(blank=True, max_length=30, null=True)), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('create_time', models.DateTimeField(auto_now_add=True, null=True)), ('admin_type', models.IntegerField(default=0)), + ('admin_extra_permission', jsonfield.fields.JSONField(default=[])), + ('problems_status', jsonfield.fields.JSONField(default={})), + ('reset_password_token', models.CharField(blank=True, max_length=40, null=True)), + ('reset_password_token_create_time', models.DateTimeField(blank=True, null=True)), + ('auth_token', models.CharField(blank=True, max_length=40, null=True)), + ('two_factor_auth', models.BooleanField(default=False)), + ('tfa_token', models.CharField(blank=True, max_length=40, null=True)), + ('open_api', models.BooleanField(default=False)), + ('open_api_appkey', models.CharField(blank=True, max_length=35, null=True)), + ('is_disabled', models.BooleanField(default=False)), ], options={ 'db_table': 'user', @@ -31,9 +47,24 @@ class Migration(migrations.Migration): ], ), migrations.CreateModel( - name='AdminGroup', + name='UserProfile', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('avatar', models.CharField(default=account.models._random_avatar, max_length=50)), + ('blog', models.URLField(blank=True, null=True)), + ('mood', models.CharField(blank=True, max_length=200, null=True)), + ('hduoj_username', models.CharField(blank=True, max_length=30, null=True)), + ('bestcoder_username', models.CharField(blank=True, max_length=30, null=True)), + ('codeforces_username', models.CharField(blank=True, max_length=30, null=True)), + ('accepted_problem_number', models.IntegerField(default=0)), + ('submission_number', models.IntegerField(default=0)), + ('phone_number', models.CharField(blank=True, max_length=15, null=True)), + ('school', models.CharField(blank=True, max_length=200, null=True)), + ('student_id', models.CharField(blank=True, max_length=15, null=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], + options={ + 'db_table': 'user_profile', + }, ), ] diff --git a/account/migrations/0002_auto_20160925_1337.py b/account/migrations/0002_auto_20160925_1337.py new file mode 100644 index 00000000..84a6ea3b --- /dev/null +++ b/account/migrations/0002_auto_20160925_1337.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-09-25 05:37 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='real_name', + ), + migrations.RemoveField( + model_name='userprofile', + name='bestcoder_username', + ), + migrations.RemoveField( + model_name='userprofile', + name='codeforces_username', + ), + migrations.RemoveField( + model_name='userprofile', + name='hduoj_username', + ), + migrations.AddField( + model_name='userprofile', + name='real_name', + field=models.CharField(blank=True, max_length=30, null=True), + ), + ] diff --git a/account/migrations/0002_user_problems_status.py b/account/migrations/0002_user_problems_status.py deleted file mode 100644 index 58c8a31c..00000000 --- a/account/migrations/0002_user_problems_status.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='problems_status', - field=models.TextField(blank=True), - ), - ] diff --git a/account/migrations/0003_auto_20150915_2025.py b/account/migrations/0003_auto_20150915_2025.py deleted file mode 100644 index ff6b0ea1..00000000 --- a/account/migrations/0003_auto_20150915_2025.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0002_user_problems_status'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='problems_status', - field=models.TextField(default=b'{}'), - ), - ] diff --git a/account/migrations/0018_auto_20160217_0920.py b/account/migrations/0003_auto_20160925_1402.py similarity index 50% rename from account/migrations/0018_auto_20160217_0920.py rename to account/migrations/0003_auto_20160925_1402.py index 218e8007..a14d553a 100644 --- a/account/migrations/0018_auto_20160217_0920.py +++ b/account/migrations/0003_auto_20160925_1402.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-02-17 01:20 +# Generated by Django 1.9.5 on 2016-09-25 06:02 from __future__ import unicode_literals from django.db import migrations, models @@ -8,16 +8,17 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('account', '0017_auto_20151212_2139'), + ('account', '0002_auto_20160925_1337'), ] operations = [ - migrations.DeleteModel( - name='AdminGroup', + migrations.RemoveField( + model_name='userprofile', + name='real_name', ), migrations.AddField( model_name='user', - name='openapi_appkey', - field=models.CharField(blank=True, max_length=35, null=True), + name='real_name', + field=models.CharField(blank=True, max_length=30, null=True), ), ] diff --git a/account/migrations/0004_remove_user_problems_status.py b/account/migrations/0004_remove_user_problems_status.py deleted file mode 100644 index 64f8ae03..00000000 --- a/account/migrations/0004_remove_user_problems_status.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0003_auto_20150915_2025'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='problems_status', - ), - ] diff --git a/account/migrations/0005_user_problems_status.py b/account/migrations/0005_user_problems_status.py deleted file mode 100644 index bfb1db09..00000000 --- a/account/migrations/0005_user_problems_status.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0004_remove_user_problems_status'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='problems_status', - field=models.TextField(default=b'{}'), - ), - ] diff --git a/account/migrations/0006_auto_20150924_1530.py b/account/migrations/0006_auto_20150924_1530.py deleted file mode 100644 index 29a7222e..00000000 --- a/account/migrations/0006_auto_20150924_1530.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0005_user_problems_status'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='problems_status', - field=utils.models.JsonField(default={}), - ), - ] diff --git a/account/migrations/0007_auto_20150929_2320.py b/account/migrations/0007_auto_20150929_2320.py deleted file mode 100644 index ceef75e6..00000000 --- a/account/migrations/0007_auto_20150929_2320.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import jsonfield.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0006_auto_20150924_1530'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='reset_password_token', - field=models.CharField(max_length=40, null=True, blank=True), - ), - migrations.AlterField( - model_name='user', - name='problems_status', - field=jsonfield.fields.JSONField(default={}), - ), - ] diff --git a/account/migrations/0008_user_login_failed_counter.py b/account/migrations/0008_user_login_failed_counter.py deleted file mode 100644 index 383508a1..00000000 --- a/account/migrations/0008_user_login_failed_counter.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0007_auto_20150929_2320'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='login_failed_counter', - field=models.IntegerField(default=0), - ), - ] diff --git a/account/migrations/0009_user_reset_password_token_create_time.py b/account/migrations/0009_user_reset_password_token_create_time.py deleted file mode 100644 index 15c8be45..00000000 --- a/account/migrations/0009_user_reset_password_token_create_time.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0008_user_login_failed_counter'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='reset_password_token_create_time', - field=models.DateTimeField(null=True, blank=True), - ), - ] diff --git a/account/migrations/0010_remove_user_login_failed_counter.py b/account/migrations/0010_remove_user_login_failed_counter.py deleted file mode 100644 index 61e09f17..00000000 --- a/account/migrations/0010_remove_user_login_failed_counter.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0009_user_reset_password_token_create_time'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='login_failed_counter', - ), - ] diff --git a/account/migrations/0011_user_auth_token.py b/account/migrations/0011_user_auth_token.py deleted file mode 100644 index 01a3dc51..00000000 --- a/account/migrations/0011_user_auth_token.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0010_remove_user_login_failed_counter'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='auth_token', - field=models.CharField(max_length=40, null=True, blank=True), - ), - ] diff --git a/account/migrations/0012_auto_20151012_1546.py b/account/migrations/0012_auto_20151012_1546.py deleted file mode 100644 index a4f937d0..00000000 --- a/account/migrations/0012_auto_20151012_1546.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0011_user_auth_token'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='create_time', - field=models.DateTimeField(auto_now_add=True, null=True), - ), - ] diff --git a/account/migrations/0013_userprofile.py b/account/migrations/0013_userprofile.py deleted file mode 100644 index 02689e3d..00000000 --- a/account/migrations/0013_userprofile.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import jsonfield.fields -import account.models -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0012_auto_20151012_1546'), - ] - - operations = [ - migrations.CreateModel( - name='UserProfile', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('avatar', models.CharField(default=account.models._random_avatar, max_length=50)), - ('blog', models.URLField(null=True, blank=True)), - ('mood', models.CharField(max_length=200, null=True, blank=True)), - ('hduoj_username', models.CharField(max_length=30, null=True, blank=True)), - ('bestcoder_username', models.CharField(max_length=30, null=True, blank=True)), - ('codeforces_username', models.CharField(max_length=30, null=True, blank=True)), - ('rank', models.IntegerField(default=65535)), - ('accepted_number', models.IntegerField(default=0)), - ('submissions_number', models.IntegerField(default=0)), - ('problems_status', jsonfield.fields.JSONField(default={})), - ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'user_profile', - }, - ), - ] diff --git a/account/migrations/0014_auto_20151110_1037.py b/account/migrations/0014_auto_20151110_1037.py deleted file mode 100644 index 288e4712..00000000 --- a/account/migrations/0014_auto_20151110_1037.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0013_userprofile'), - ] - - operations = [ - migrations.AddField( - model_name='userprofile', - name='phone_number', - field=models.CharField(max_length=15, null=True, blank=True), - ), - migrations.AddField( - model_name='userprofile', - name='school', - field=models.CharField(max_length=200, null=True, blank=True), - ), - ] diff --git a/account/migrations/0015_userprofile_student_id.py b/account/migrations/0015_userprofile_student_id.py deleted file mode 100644 index cb8991bf..00000000 --- a/account/migrations/0015_userprofile_student_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2015-12-08 06:22 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0014_auto_20151110_1037'), - ] - - operations = [ - migrations.AddField( - model_name='userprofile', - name='student_id', - field=models.CharField(blank=True, max_length=15, null=True), - ), - ] diff --git a/account/migrations/0016_auto_20151211_2230.py b/account/migrations/0016_auto_20151211_2230.py deleted file mode 100644 index 9c2f8798..00000000 --- a/account/migrations/0016_auto_20151211_2230.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2015-12-11 14:30 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0015_userprofile_student_id'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='tfa_token', - field=models.CharField(blank=True, max_length=10, null=True), - ), - migrations.AddField( - model_name='user', - name='two_factor_auth', - field=models.BooleanField(default=False), - ), - ] diff --git a/account/migrations/0017_auto_20151212_2139.py b/account/migrations/0017_auto_20151212_2139.py deleted file mode 100644 index a34d2fa8..00000000 --- a/account/migrations/0017_auto_20151212_2139.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2015-12-12 13:39 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0016_auto_20151211_2230'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='tfa_token', - field=models.CharField(blank=True, max_length=40, null=True), - ), - ] diff --git a/account/migrations/0019_user_is_forbidden.py b/account/migrations/0019_user_is_forbidden.py deleted file mode 100644 index a0266121..00000000 --- a/account/migrations/0019_user_is_forbidden.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2016-03-26 16:39 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0018_auto_20160217_0920'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='is_forbidden', - field=models.BooleanField(default=False), - ), - ] diff --git a/account/migrations/0020_auto_20160424_1241.py b/account/migrations/0020_auto_20160424_1241.py deleted file mode 100644 index 842098fd..00000000 --- a/account/migrations/0020_auto_20160424_1241.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2016-04-24 04:41 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0019_user_is_forbidden'), - ] - - operations = [ - migrations.RenameField( - model_name='userprofile', - old_name='accepted_number', - new_name='accepted_problem_number', - ), - migrations.RemoveField( - model_name='userprofile', - name='rank', - ), - ] diff --git a/account/migrations/0021_auto_20160424_1243.py b/account/migrations/0021_auto_20160424_1243.py deleted file mode 100644 index 87a818d8..00000000 --- a/account/migrations/0021_auto_20160424_1243.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2016-04-24 04:43 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0020_auto_20160424_1241'), - ] - - operations = [ - migrations.RenameField( - model_name='userprofile', - old_name='submissions_number', - new_name='submission_number', - ), - ] diff --git a/account/migrations/0022_auto_20160613_1606.py b/account/migrations/0022_auto_20160613_1606.py deleted file mode 100644 index e6c307d5..00000000 --- a/account/migrations/0022_auto_20160613_1606.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2016-06-13 08:06 -from __future__ import unicode_literals - -from django.db import migrations, models -import jsonfield.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0021_auto_20160424_1243'), - ] - - operations = [ - migrations.RenameField( - model_name='user', - old_name='is_forbidden', - new_name='is_disabled', - ), - migrations.RenameField( - model_name='user', - old_name='openapi_appkey', - new_name='open_api_appkey', - ), - migrations.RemoveField( - model_name='userprofile', - name='problems_status', - ), - migrations.AddField( - model_name='user', - name='admin_extra_permission', - field=jsonfield.fields.JSONField(default=[]), - ), - migrations.AddField( - model_name='user', - name='open_api', - field=models.BooleanField(default=False), - ), - ] diff --git a/account/models.py b/account/models.py index cfa0ff8e..b4ce0ac1 100644 --- a/account/models.py +++ b/account/models.py @@ -1,14 +1,8 @@ # coding=utf-8 -from django.db import models from django.contrib.auth.models import AbstractBaseUser - +from django.db import models from jsonfield import JSONField -# TODO remove these -REGULAR_USER = 0 -ADMIN = 1 -SUPER_ADMIN = 2 - class AdminType(object): REGULAR_USER = 0 @@ -65,6 +59,9 @@ class User(AbstractBaseUser): objects = UserManager() + def is_admin(self): + return self.admin_type > AdminType.REGULAR_USER + class Meta: db_table = "user" @@ -79,13 +76,11 @@ class UserProfile(models.Model): avatar = models.CharField(max_length=50, default=_random_avatar) blog = models.URLField(blank=True, null=True) mood = models.CharField(max_length=200, blank=True, null=True) - hduoj_username = models.CharField(max_length=30, blank=True, null=True) - bestcoder_username = models.CharField(max_length=30, blank=True, null=True) - codeforces_username = models.CharField(max_length=30, blank=True, null=True) accepted_problem_number = models.IntegerField(default=0) submission_number = models.IntegerField(default=0) phone_number = models.CharField(max_length=15, blank=True, null=True) school = models.CharField(max_length=200, blank=True, null=True) + student_id = models.CharField(max_length=15, blank=True, null=True) def add_accepted_problem_number(self): diff --git a/account/serializers.py b/account/serializers.py index c4077e05..9be94a36 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -1,8 +1,8 @@ # coding=utf-8 from rest_framework import serializers -from utils.serializers import DateTimeTZField, JSONField -from .models import User, UserProfile +from utils.serializers import DateTimeTZField +from .models import User class UserLoginSerializer(serializers.Serializer): @@ -11,22 +11,11 @@ class UserLoginSerializer(serializers.Serializer): tfa_code = serializers.CharField(min_length=6, max_length=6, required=False) -class UsernameCheckSerializer(serializers.Serializer): - username = serializers.CharField(max_length=30) - - -class EmailCheckSerializer(serializers.Serializer): - email = serializers.EmailField(max_length=254) - - class UserRegisterSerializer(serializers.Serializer): username = serializers.CharField(max_length=30) - real_name = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30, min_length=6) email = serializers.EmailField(max_length=254) captcha = serializers.CharField(max_length=4, min_length=4) - school = serializers.CharField(max_length=200, required=False, default=None) - student_id = serializers.CharField(max_length=15, required=False, default=None) class UserChangePasswordSerializer(serializers.Serializer): @@ -58,43 +47,3 @@ class EditUserSerializer(serializers.Serializer): is_disabled = serializers.BooleanField() admin_extra_permission = serializers.ListField(required=False, default=[], child=serializers.IntegerField()) - - -class ApplyResetPasswordSerializer(serializers.Serializer): - email = serializers.EmailField() - captcha = serializers.CharField(max_length=4, min_length=4) - - -class ResetPasswordSerializer(serializers.Serializer): - token = serializers.CharField(min_length=1, max_length=40) - password = serializers.CharField(min_length=6, max_length=30) - captcha = serializers.CharField(max_length=4, min_length=4) - - -class SSOSerializer(serializers.Serializer): - appkey = serializers.CharField(max_length=35) - token = serializers.CharField(max_length=40) - - -class EditUserProfileSerializer(serializers.Serializer): - avatar = serializers.CharField(max_length=50, required=False, default=None) - blog = serializers.URLField(required=False, allow_blank=True, default='') - mood = serializers.CharField(max_length=60, required=False, allow_blank=True, default='') - hduoj_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='') - bestcoder_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='') - codeforces_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='') - school = serializers.CharField(max_length=200, required=False, allow_blank=True, default='') - phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='') - student_id = serializers.CharField(max_length=15, required=False, allow_blank=True, default="") - - -class UserProfileSerializer(serializers.ModelSerializer): - - class Meta: - model = UserProfile - fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username", - "rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school", "student_id"] - - -class TwoFactorAuthCodeSerializer(serializers.Serializer): - code = serializers.IntegerField() diff --git a/account/tasks.py b/account/tasks.py deleted file mode 100644 index 58cb8fc1..00000000 --- a/account/tasks.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 -from celery import shared_task -from utils.mail import send_email - - -@shared_task -def _send_email(from_name, to_email, to_name, subject, content): - send_email(from_name, to_email, to_name, subject, content) \ No newline at end of file diff --git a/admin/__init__.py b/account/urls/__init__.py similarity index 100% rename from admin/__init__.py rename to account/urls/__init__.py diff --git a/account/urls/admin.py b/account/urls/admin.py new file mode 100644 index 00000000..946bbc0a --- /dev/null +++ b/account/urls/admin.py @@ -0,0 +1,8 @@ +# coding=utf-8 +from django.conf.urls import url + +from ..views.admin import UserAdminAPIView + +urlpatterns = [ + url(r'^user/$', UserAdminAPIView.as_view(), name="user_admin_api"), +] diff --git a/account/urls/oj.py b/account/urls/oj.py new file mode 100644 index 00000000..f2d5aedb --- /dev/null +++ b/account/urls/oj.py @@ -0,0 +1,10 @@ +# coding=utf-8 +from django.conf.urls import url + +from ..views.oj import UserLoginAPIView, UserRegisterAPIView, UserChangePasswordAPIView + +urlpatterns = [ + url(r'^login/$', UserLoginAPIView.as_view(), name="user_login_api"), + url(r'^register/$', UserRegisterAPIView.as_view(), name="user_register_api"), + url(r'^change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api") +] diff --git a/account/views.py b/account/views.py deleted file mode 100644 index 18bd6775..00000000 --- a/account/views.py +++ /dev/null @@ -1,531 +0,0 @@ -# coding=utf-8 -from __future__ import unicode_literals -import StringIO -import codecs -import os - -import qrcode -from django.conf import settings -from django.contrib import auth -from django.core.exceptions import MultipleObjectsReturned -from django.core.paginator import Paginator -from django.db.models import Q -from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render -from django.utils.timezone import now -from django.utils.translation import ugettext as _ -from rest_framework.response import Response -from rest_framework.views import APIView - -from utils.captcha import Captcha -from utils.otp_auth import OtpAuth -from utils.shortcuts import (serializer_invalid_response, error_response, - success_response, error_page, paginate, rand_str) -from .decorators import login_required -from .decorators import super_admin_required -from .models import User, UserProfile, AdminType -from .serializers import (UserLoginSerializer, UserRegisterSerializer, - UserChangePasswordSerializer, - UserSerializer, EditUserSerializer, - ApplyResetPasswordSerializer, ResetPasswordSerializer, - SSOSerializer, EditUserProfileSerializer, - TwoFactorAuthCodeSerializer) -from .tasks import _send_email - - -class UserLoginAPIView(APIView): - def post(self, request): - """ - User login api - """ - serializer = UserLoginSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - user = auth.authenticate(username=data["username"], password=data["password"]) - # None is returned if username or password is wrong - if user: - if not user.two_factor_auth: - auth.login(request, user) - return success_response(_("Succeeded")) - - # `tfa_code` not in post data - if user.two_factor_auth and "tfa_code" not in data: - return success_response("tfa_required") - - if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]): - auth.login(request, user) - return success_response(_("Succeeded")) - else: - return error_response(_("Invalid two factor verification code")) - else: - return error_response(_("Invalid username or password")) - else: - return serializer_invalid_response(serializer) - - # todo remove this - def get(self, request): - auth.login(request, auth.authenticate(username="root", password="123456")) - return success_response({}) - - -class UserRegisterAPIView(APIView): - def post(self, request): - """ - User register api - """ - serializer = UserRegisterSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - captcha = Captcha(request) - if not captcha.check(data["captcha"]): - return error_response(_("Invalid captcha")) - try: - User.objects.get(username=data["username"]) - return error_response(_("Username already exists")) - except User.DoesNotExist: - pass - try: - User.objects.get(email=data["email"]) - return error_response(_("Email already exists")) - # Some old data has duplicate email - except MultipleObjectsReturned: - return error_response(_("Email already exists")) - except User.DoesNotExist: - user = User.objects.create(username=data["username"], real_name=data["real_name"], - email=data["email"]) - user.set_password(data["password"]) - user.save() - UserProfile.objects.create(user=user, school=data["school"], student_id=data["student_id"]) - return success_response(_("Succeeded")) - else: - return serializer_invalid_response(serializer) - - -class UserChangePasswordAPIView(APIView): - @login_required - def post(self, request): - """ - User change password api - """ - serializer = UserChangePasswordSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - captcha = Captcha(request) - if not captcha.check(data["captcha"]): - return error_response(_("Invalid captcha")) - username = request.user.username - user = auth.authenticate(username=username, password=data["old_password"]) - if user: - user.set_password(data["new_password"]) - user.save() - return success_response(_("Succeeded")) - else: - return error_response(_("Invalid old password")) - else: - return serializer_invalid_response(serializer) - - -class UserAdminAPIView(APIView): - @super_admin_required - def put(self, request): - """ - Edit user api - """ - 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(_("User does not exist")) - try: - user = User.objects.get(username=data["username"]) - if user.id != data["id"]: - return error_response(_("Username already exists")) - except User.DoesNotExist: - pass - - try: - user = User.objects.get(email=data["email"]) - if user.id != data["id"]: - return error_response(_("Email already exists")) - # Some old data has duplicate email - except MultipleObjectsReturned: - return error_response(_("Email already exists")) - except User.DoesNotExist: - pass - - user.username = data["username"] - user.real_name = data["real_name"] - user.email = data["email"] - user.admin_type = data["admin_type"] - user.is_disabled = data["is_disabled"] - - if data["password"]: - user.set_password(data["password"]) - - if data["open_api"]: - # Avoid reset user appkey after saving changes - if not user.open_api: - user.open_api_appkey = rand_str() - else: - user.open_api_appkey = None - user.open_api = data["open_api"] - - if data["two_factor_auth"]: - # Avoid reset user tfa_token after saving changes - if not user.two_factor_auth: - user.tfa_token = rand_str() - else: - user.tfa_token = None - user.two_factor_auth = data["two_factor_auth"] - - if data["admin_type"] == AdminType.ADMIN: - user.admin_extra_permission = list(set(data["admin_extra_permission"])) - else: - user.admin_extra_permission = [] - - user.save() - return success_response(UserSerializer(user).data) - else: - return serializer_invalid_response(serializer) - - @super_admin_required - def get(self, request): - """ - User list api / Get user by id - """ - user_id = request.GET.get("user_id") - if user_id: - try: - user = User.objects.get(id=user_id) - except User.DoesNotExist: - return error_response(_("User does not exist")) - return success_response(UserSerializer(user).data) - - user = User.objects.all().order_by("-create_time") - - admin_type = request.GET.get("admin_type", None) - if admin_type: - try: - user = user.filter(admin_type__gte=int(admin_type)) - except ValueError: - return error_response(_("Invalid parameter")) - keyword = request.GET.get("keyword", None) - if keyword: - user = user.filter(Q(username__contains=keyword) | - Q(real_name__contains=keyword) | - Q(email__contains=keyword)) - return paginate(request, user, UserSerializer) - - -def logout(request): - auth.logout(request) - return HttpResponseRedirect("/") - - -def index_page(request): - if not request.user.is_authenticated(): - return render(request, "oj/index.html") - - if request.META.get('HTTP_REFERER') or request.GET.get("index"): - return render(request, "oj/index.html") - else: - return HttpResponseRedirect('/problems/') - - -class UsernameCheckAPIView(APIView): - def get(self, request): - """ - 检测用户名是否存在,存在返回状态码400,不存在返回200 - --- - """ - username = request.GET.get("username", None) - if username: - try: - User.objects.get(username=username) - return Response(status=400) - except User.DoesNotExist: - return Response(status=200) - return Response(status=200) - - -class EmailCheckAPIView(APIView): - def get(self, request): - """ - 检测邮箱是否存在,用状态码标识结果 - --- - """ - # 这里是为了适应前端表单验证空间的要求 - reset = request.GET.get("reset", None) - # 如果reset为true说明该请求是重置密码页面发出的,要返回的状态码应正好相反 - if reset: - existed = 200 - does_not_existed = 400 - else: - existed = 400 - does_not_existed = 200 - - email = request.GET.get("email", None) - if email: - try: - User.objects.get(email=email) - return Response(status=existed) - except Exception: - return Response(status=does_not_existed) - return Response(status=does_not_existed) - - -class UserInfoAPIView(APIView): - @login_required - def get(self, request): - """ - 返回这个用户的个人信息 - --- - response_serializer: UserSerializer - """ - return success_response(UserSerializer(request.user).data) - - -class UserProfileAPIView(APIView): - @login_required - def get(self, request): - """ - 返回这个用户的个人信息 - --- - response_serializer: UserSerializer - """ - return success_response(UserSerializer(request.user).data) - - @login_required - def put(self, request): - serializer = EditUserProfileSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - user_profile = request.user.userprofile - if data["avatar"]: - user_profile.avatar = data["avatar"] - else: - user_profile.mood = data["mood"] - user_profile.hduoj_username = data["hduoj_username"] - user_profile.bestcoder_username = data["bestcoder_username"] - user_profile.codeforces_username = data["codeforces_username"] - user_profile.blog = data["blog"] - user_profile.school = data["school"] - user_profile.student_id = data["student_id"] - user_profile.phone_number = data["phone_number"] - user_profile.save() - return success_response(u"修改成功") - else: - return serializer_invalid_response(serializer) - - -class ApplyResetPasswordAPIView(APIView): - def post(self, request): - """ - 提交请求重置密码 - --- - request_serializer: ApplyResetPasswordSerializer - """ - serializer = ApplyResetPasswordSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - captcha = Captcha(request) - if not captcha.check(data["captcha"]): - return error_response(u"验证码错误") - try: - user = User.objects.get(email=data["email"]) - except User.DoesNotExist: - return error_response(u"用户不存在") - if user.reset_password_token_create_time and ( - now() - user.reset_password_token_create_time).total_seconds() < 20 * 60: - return error_response(u"20分钟内只能找回一次密码") - user.reset_password_token = rand_str() - user.reset_password_token_create_time = now() - user.save() - email_template = codecs.open(settings.TEMPLATES[0]["DIRS"][0] + "utils/reset_password_email.html", "r", - "utf-8").read() - - email_template = email_template.replace("{{ username }}", user.username). \ - replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]). \ - replace("{{ link }}", settings.WEBSITE_INFO["url"] + "/reset_password/t/" + - user.reset_password_token) - - _send_email.delay(settings.WEBSITE_INFO["website_name"], - user.email, - user.username, - settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件", - email_template) - return success_response(u"邮件发送成功,请前往您的邮箱查收") - else: - return serializer_invalid_response(serializer) - - -class ResetPasswordAPIView(APIView): - def post(self, request): - serializer = ResetPasswordSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - captcha = Captcha(request) - if not captcha.check(data["captcha"]): - return error_response(u"验证码错误") - try: - user = User.objects.get(reset_password_token=data["token"]) - except User.DoesNotExist: - return error_response(u"token 不存在") - if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60: - return error_response(u"token 已经过期,请在30分钟内重置密码") - user.reset_password_token = None - user.set_password(data["password"]) - user.save() - return success_response(u"密码重置成功") - else: - return serializer_invalid_response(serializer) - - -def user_index_page(request, username): - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - return error_page(request, u"用户不存在") - - blog_link = "" - - if user.userprofile.blog: - blog_link = user.userprofile.blog.replace("http://", "").replace("https://", "") - - return render(request, "oj/account/user_index.html", {"user": user, "blog_link": blog_link}) - - -class SSOAPIView(APIView): - def post(self, request): - serializer = SSOSerializer(data=request.data) - if serializer.is_valid(): - try: - User.objects.get(openapi_appkey=serializer.data["appkey"]) - except User.DoesNotExist: - return error_response(u"appkey无效") - try: - user = User.objects.get(auth_token=serializer.data["token"]) - user.auth_token = None - user.save() - return success_response({"username": user.username, - "id": user.id, - "admin_type": user.admin_type, - "avatar": user.userprofile.avatar}) - except User.DoesNotExist: - return error_response(u"用户不存在") - else: - return serializer_invalid_response(serializer) - - @login_required - def get(self, request): - callback = request.GET.get("callback", None) - if not callback: - return error_page(request, u"参数错误") - token = rand_str() - request.user.auth_token = token - request.user.save() - return render(request, "oj/account/sso.html", - {"redirect_url": callback + "?token=" + token, "callback": callback}) - - -def reset_password_page(request, token): - try: - user = User.objects.get(reset_password_token=token) - except User.DoesNotExist: - return error_page(request, u"链接已失效") - if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60: - return error_page(request, u"链接已过期") - return render(request, "oj/account/reset_password.html", {"user": user}) - - -class TwoFactorAuthAPIView(APIView): - @login_required - def get(self, request): - """ - 获取绑定二维码 - """ - user = request.user - if user.two_factor_auth: - return error_response(u"已经开启两步验证了") - token = rand_str() - user.tfa_token = token - user.save() - - image = qrcode.make(OtpAuth(token).to_uri("totp", settings.WEBSITE_INFO["url"], "OnlineJudgeAdmin")) - buf = StringIO.StringIO() - image.save(buf, 'gif') - - return HttpResponse(buf.getvalue(), 'image/gif') - - @login_required - def post(self, request): - """ - 开启两步验证 - """ - serializer = TwoFactorAuthCodeSerializer(data=request.data) - if serializer.is_valid(): - code = serializer.data["code"] - user = request.user - if OtpAuth(user.tfa_token).valid_totp(code): - user.two_factor_auth = True - user.save() - return success_response(u"开启两步验证成功") - else: - return error_response(u"验证码错误") - else: - return serializer_invalid_response(serializer) - - @login_required - def put(self, request): - serializer = TwoFactorAuthCodeSerializer(data=request.data) - if serializer.is_valid(): - user = request.user - code = serializer.data["code"] - if OtpAuth(user.tfa_token).valid_totp(code): - user.two_factor_auth = False - user.save() - else: - return error_response(u"验证码错误") - else: - return serializer_invalid_response(serializer) - - -def user_rank_page(request, page=1): - ranks = UserProfile.objects.filter(submission_number__gt=0).order_by("-accepted_problem_number", "-submission_number") - paginator = Paginator(ranks, 20) - try: - ranks = paginator.page(int(page)) - except Exception: - return error_page(request, u"不存在的页码") - previous_page = next_page = None - try: - previous_page = ranks.previous_page_number() - except Exception: - pass - try: - next_page = ranks.next_page_number() - except Exception: - pass - return render(request, "utils/rank.html", {"ranks": ranks, "page": page, - "previous_page": previous_page, - "next_page": next_page, - "start_id": int(page) * 20 - 20,}) - - -class AvatarUploadAPIView(APIView): - def post(self, request): - if "file" not in request.FILES: - return error_response(u"文件上传失败") - - f = request.FILES["file"] - if f.size > 1024 * 1024: - return error_response(u"图片过大") - if os.path.splitext(f.name)[-1].lower() not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]: - return error_response(u"需要上传图片格式") - name = "avatar_" + rand_str(5) + os.path.splitext(f.name)[-1] - with open(os.path.join(settings.IMAGE_UPLOAD_DIR, name), "wb") as img: - for chunk in request.FILES["file"]: - img.write(chunk) - return success_response({"path": "/static/upload/" + name}) diff --git a/admin/migrations/__init__.py b/account/views/__init__.py similarity index 100% rename from admin/migrations/__init__.py rename to account/views/__init__.py diff --git a/account/views/admin.py b/account/views/admin.py new file mode 100644 index 00000000..521ed47e --- /dev/null +++ b/account/views/admin.py @@ -0,0 +1,107 @@ +# coding=utf-8 +from __future__ import unicode_literals + +from django.core.exceptions import MultipleObjectsReturned +from django.db.models import Q +from django.utils.translation import ugettext as _ +from rest_framework.views import APIView + +from utils.shortcuts import (serializer_invalid_response, error_response, + success_response, paginate, rand_str) +from ..decorators import super_admin_required +from ..models import User, AdminType +from ..serializers import (UserSerializer, EditUserSerializer) + + +class UserAdminAPIView(APIView): + @super_admin_required + def put(self, request): + """ + Edit user api + """ + 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(_("User does not exist")) + try: + user = User.objects.get(username=data["username"]) + if user.id != data["id"]: + return error_response(_("Username already exists")) + except User.DoesNotExist: + pass + + try: + user = User.objects.get(email=data["email"]) + if user.id != data["id"]: + return error_response(_("Email already exists")) + # Some old data has duplicate email + except MultipleObjectsReturned: + return error_response(_("Email already exists")) + except User.DoesNotExist: + pass + + user.username = data["username"] + user.real_name = data["real_name"] + user.email = data["email"] + user.admin_type = data["admin_type"] + user.is_disabled = data["is_disabled"] + + if data["password"]: + user.set_password(data["password"]) + + if data["open_api"]: + # Avoid reset user appkey after saving changes + if not user.open_api: + user.open_api_appkey = rand_str() + else: + user.open_api_appkey = None + user.open_api = data["open_api"] + + if data["two_factor_auth"]: + # Avoid reset user tfa_token after saving changes + if not user.two_factor_auth: + user.tfa_token = rand_str() + else: + user.tfa_token = None + user.two_factor_auth = data["two_factor_auth"] + + if data["admin_type"] == AdminType.ADMIN: + user.admin_extra_permission = list(set(data["admin_extra_permission"])) + else: + user.admin_extra_permission = [] + + user.save() + return success_response(UserSerializer(user).data) + else: + return serializer_invalid_response(serializer) + + @super_admin_required + def get(self, request): + """ + User list api / Get user by id + """ + user_id = request.GET.get("user_id") + if user_id: + try: + user = User.objects.get(id=user_id) + except User.DoesNotExist: + return error_response(_("User does not exist")) + return success_response(UserSerializer(user).data) + + user = User.objects.all().order_by("-create_time") + + admin_type = request.GET.get("admin_type", None) + if admin_type: + try: + user = user.filter(admin_type__gte=int(admin_type)) + except ValueError: + return error_response(_("Invalid parameter")) + keyword = request.GET.get("keyword", None) + if keyword: + user = user.filter(Q(username__contains=keyword) | + Q(real_name__contains=keyword) | + Q(email__contains=keyword)) + return paginate(request, user, UserSerializer) diff --git a/account/views/oj.py b/account/views/oj.py new file mode 100644 index 00000000..6f64662d --- /dev/null +++ b/account/views/oj.py @@ -0,0 +1,107 @@ +# coding=utf-8 +from __future__ import unicode_literals + +from django.contrib import auth +from django.core.exceptions import MultipleObjectsReturned +from django.utils.translation import ugettext as _ +from rest_framework.views import APIView + +from utils.captcha import Captcha +from utils.otp_auth import OtpAuth +from utils.shortcuts import (serializer_invalid_response, error_response, + success_response) +from ..decorators import login_required +from ..models import User, UserProfile +from ..serializers import (UserLoginSerializer, UserRegisterSerializer, + UserChangePasswordSerializer) + + +class UserLoginAPIView(APIView): + def post(self, request): + """ + User login api + """ + serializer = UserLoginSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + user = auth.authenticate(username=data["username"], password=data["password"]) + # None is returned if username or password is wrong + if user: + if not user.two_factor_auth: + auth.login(request, user) + return success_response(_("Succeeded")) + + # `tfa_code` not in post data + if user.two_factor_auth and "tfa_code" not in data: + return success_response("tfa_required") + + if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]): + auth.login(request, user) + return success_response(_("Succeeded")) + else: + return error_response(_("Invalid two factor verification code")) + else: + return error_response(_("Invalid username or password")) + else: + return serializer_invalid_response(serializer) + + # todo remove this, only for debug use + def get(self, request): + auth.login(request, auth.authenticate(username=request.GET["username"], password=request.GET["password"])) + return success_response({}) + + +class UserRegisterAPIView(APIView): + def post(self, request): + """ + User register api + """ + serializer = UserRegisterSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + captcha = Captcha(request) + if not captcha.check(data["captcha"]): + return error_response(_("Invalid captcha")) + try: + User.objects.get(username=data["username"]) + return error_response(_("Username already exists")) + except User.DoesNotExist: + pass + try: + User.objects.get(email=data["email"]) + return error_response(_("Email already exists")) + # Some old data has duplicate email + except MultipleObjectsReturned: + return error_response(_("Email already exists")) + except User.DoesNotExist: + user = User.objects.create(username=data["username"], email=data["email"]) + user.set_password(data["password"]) + user.save() + UserProfile.objects.create(user=user) + return success_response(_("Succeeded")) + else: + return serializer_invalid_response(serializer) + + +class UserChangePasswordAPIView(APIView): + @login_required + def post(self, request): + """ + User change password api + """ + serializer = UserChangePasswordSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + captcha = Captcha(request) + if not captcha.check(data["captcha"]): + return error_response(_("Invalid captcha")) + username = request.user.username + user = auth.authenticate(username=username, password=data["old_password"]) + if user: + user.set_password(data["new_password"]) + user.save() + return success_response(_("Succeeded")) + else: + return error_response(_("Invalid old password")) + else: + return serializer_invalid_response(serializer) diff --git a/admin/middleware.py b/admin/middleware.py deleted file mode 100644 index ccb689bc..00000000 --- a/admin/middleware.py +++ /dev/null @@ -1,16 +0,0 @@ -# coding=utf-8 -import json - -from django.http import HttpResponse, HttpResponseRedirect - - -class AdminRequiredMiddleware(object): - def process_request(self, request): - path = request.path_info - if path.startswith("/admin/") or path.startswith("/api/admin/"): - if not(request.user.is_authenticated() and request.user.admin_type): - if request.is_ajax(): - return HttpResponse(json.dumps({"code": 1, "data": u"请先登录"}), - content_type="application/json") - else: - return HttpResponseRedirect("/login/") \ No newline at end of file diff --git a/admin/tests.py b/admin/tests.py deleted file mode 100644 index e69de29b..00000000 diff --git a/admin/views.py b/admin/views.py deleted file mode 100644 index deb5ec60..00000000 --- a/admin/views.py +++ /dev/null @@ -1,15 +0,0 @@ -# coding=utf-8 -import os -from django.conf import settings -from django.http import HttpResponse, Http404 - -from rest_framework.views import APIView - - -class AdminTemplateView(APIView): - def get(self, request, template_dir, template_name): - path = os.path.join(settings.TEMPLATES[0]["DIRS"][0], "admin", template_dir, template_name + ".html") - try: - return HttpResponse(open(path).read(), content_type="text/html") - except IOError: - return HttpResponse(u"模板不存在", content_type="text/html") diff --git a/announcement/migrations/0001_initial.py b/announcement/migrations/0001_initial.py index 7e8d5838..4a09bb95 100644 --- a/announcement/migrations/0001_initial.py +++ b/announcement/migrations/0001_initial.py @@ -1,12 +1,17 @@ # -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-09-25 05:32 from __future__ import unicode_literals -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import utils.models class Migration(migrations.Migration): + initial = True + dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -15,13 +20,13 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Announcement', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=50)), - ('content', models.TextField()), + ('content', utils.models.RichTextField()), ('create_time', models.DateTimeField(auto_now_add=True)), ('last_update_time', models.DateTimeField(auto_now=True)), ('visible', models.BooleanField(default=True)), - ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'db_table': 'announcement', diff --git a/announcement/migrations/0002_auto_20150818_1445.py b/announcement/migrations/0002_auto_20150818_1445.py deleted file mode 100644 index 771b466a..00000000 --- a/announcement/migrations/0002_auto_20150818_1445.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('group', '0004_merge'), - ('announcement', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='announcement', - name='groups', - field=models.ManyToManyField(to='group.Group'), - ), - migrations.AddField( - model_name='announcement', - name='is_global', - field=models.BooleanField(default=True), - preserve_default=False, - ), - ] diff --git a/announcement/migrations/0003_auto_20150922_1703.py b/announcement/migrations/0003_auto_20150922_1703.py deleted file mode 100644 index 4019377d..00000000 --- a/announcement/migrations/0003_auto_20150922_1703.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('announcement', '0002_auto_20150818_1445'), - ] - - operations = [ - migrations.AlterField( - model_name='announcement', - name='content', - field=utils.models.RichTextField(), - ), - ] diff --git a/announcement/migrations/0004_auto_20151015_1555.py b/announcement/migrations/0004_auto_20151015_1555.py deleted file mode 100644 index bb8e6e16..00000000 --- a/announcement/migrations/0004_auto_20151015_1555.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('announcement', '0003_auto_20150922_1703'), - ] - - operations = [ - migrations.RemoveField( - model_name='announcement', - name='groups', - ), - migrations.RemoveField( - model_name='announcement', - name='is_global', - ), - ] diff --git a/announcement/models.py b/announcement/models.py index 2532e98c..7d1e43bf 100644 --- a/announcement/models.py +++ b/announcement/models.py @@ -1,8 +1,8 @@ # coding=utf-8 +from __future__ import unicode_literals from django.db import models from account.models import User -from group.models import Group from utils.models import RichTextField diff --git a/announcement/serializers.py b/announcement/serializers.py index 8e0c5ee8..d21ce903 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -1,9 +1,10 @@ # coding=utf-8 +from __future__ import unicode_literals + from rest_framework import serializers from account.models import User from utils.serializers import DateTimeTZField - from .models import Announcement @@ -31,4 +32,4 @@ class EditAnnouncementSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=50) content = serializers.CharField(max_length=10000) - visible = serializers.BooleanField() \ No newline at end of file + visible = serializers.BooleanField() diff --git a/contest/__init__.py b/announcement/urls/__init__.py similarity index 100% rename from contest/__init__.py rename to announcement/urls/__init__.py diff --git a/announcement/urls/admin.py b/announcement/urls/admin.py new file mode 100644 index 00000000..c8b3a675 --- /dev/null +++ b/announcement/urls/admin.py @@ -0,0 +1,8 @@ +# coding=utf-8 +from django.conf.urls import url + +from ..views import AnnouncementAdminAPIView + +urlpatterns = [ + url(r'^$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"), +] diff --git a/announcement/views.py b/announcement/views.py index a0baa87a..3f726f47 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -1,29 +1,17 @@ # coding=utf-8 from __future__ import unicode_literals + +from django.utils.translation import ugettext as _ from rest_framework.views import APIView -from django.shortcuts import render -from django.utils.translation import ugettext as _ -from utils.shortcuts import serializer_invalid_response, error_response, success_response - -from utils.shortcuts import paginate, error_page from account.decorators import super_admin_required +from utils.shortcuts import paginate +from utils.shortcuts import serializer_invalid_response, error_response, success_response from .models import Announcement from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer, EditAnnouncementSerializer) -def announcement_page(request, announcement_id): - """ - announcement detail page - """ - try: - announcement = Announcement.objects.get(id=announcement_id, visible=True) - except Announcement.DoesNotExist: - return error_page(request, _("Announcement does not exist")) - return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) - - class AnnouncementAdminAPIView(APIView): @super_admin_required def post(self, request): diff --git a/contest/migrations/__init__.py b/conf/__init__.py similarity index 100% rename from contest/migrations/__init__.py rename to conf/__init__.py diff --git a/problem/admin.py b/conf/admin.py similarity index 100% rename from problem/admin.py rename to conf/admin.py diff --git a/group/__init__.py b/conf/migrations/__init__.py similarity index 100% rename from group/__init__.py rename to conf/migrations/__init__.py diff --git a/admin/models.py b/conf/models.py similarity index 58% rename from admin/models.py rename to conf/models.py index 71a83623..bd4b2abe 100644 --- a/admin/models.py +++ b/conf/models.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.db import models # Create your models here. diff --git a/conf/tests.py b/conf/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/conf/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/conf/views.py b/conf/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/conf/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/contest/decorators.py b/contest/decorators.py deleted file mode 100644 index dca63bf4..00000000 --- a/contest/decorators.py +++ /dev/null @@ -1,113 +0,0 @@ -# coding=utf-8 -import urllib -from functools import wraps - -from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render -from django.core.urlresolvers import reverse - -from utils.shortcuts import error_response, error_page - -from account.models import SUPER_ADMIN, ADMIN -from .models import (Contest, PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST, PUBLIC_CONTEST, GROUP_CONTEST, - CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY) - - -def check_user_contest_permission(func): - @wraps(func) - def _check_user_contest_permission(*args, **kwargs): - """ - 这个函数检查当前的这个比赛对于 request 的用户来说能不能参加 - 需要比较:比赛的开始和结束时间、比赛是否有密码、比赛是不是限定指定小组参加 - 如果是有密码或者限定指定小组参加的话,即使比赛已经结束,那么也是可以看到所有的题目和结果的 - 否则不能看到这个比赛的题目结果排名等等 - """ - # CBV 的情况,第一个参数是self,第二个参数是request - if len(args) == 2: - request = args[-1] - else: - request = args[0] - - if not request.user.is_authenticated(): - if request.is_ajax(): - return error_response(u"请先登录") - else: - return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.path)) - - # kwargs 就包含了 url 里面的参数 - if "contest_id" in kwargs: - contest_id = kwargs["contest_id"] - elif "contest_id" in request.data: - contest_id = request.data["contest_id"] - elif "contest_id" in request.GET: - contest_id = request.GET["contest_id"] - else: - if request.is_ajax(): - return error_response(u"参数错误") - else: - return error_page(request, u"参数错误") - - try: - contest = Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - if request.is_ajax(): - return error_response(u"比赛不存在") - else: - return error_page(request, u"比赛不存在") - - if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by: - return func(*args, **kwargs) - if request.user.admin_type == ADMIN: - contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all()) - if contest in contest_set: - return func(*args, **kwargs) - # 管理员可见隐藏的比赛,已经先判断了身份 - if not contest.visible: - if request.is_ajax(): - return error_response(u"比赛不存在") - else: - return error_page(request, u"比赛不存在") - - # 有密码的公开赛 - if contest.contest_type == PASSWORD_PROTECTED_CONTEST: - # 没有输入过密码 - if contest.id not in request.session.get("contests", []): - if request.is_ajax(): - return error_response(u"请先输入密码") - else: - return render(request, "oj/contest/no_contest_permission.html", - {"reason": "password_protect", "show_tab": False, "contest": contest}) - - # 指定小组参加的 - if contest.contest_type == GROUP_CONTEST: - if not contest.groups.filter(id__in=request.user.group_set.all()).exists(): - if request.is_ajax(): - return error_response(u"只有指定小组的可以参加这场比赛") - else: - return render(request, "oj/contest/no_contest_permission.html", - {"reason": "group_limited", "show_tab": False, "contest": contest}) - - if contest.contest_type == PASSWORD_PROTECTED_GROUP_CONTEST: - if not contest.groups.filter(id__in=request.user.group_set.all()).exists(): - if contest.id not in request.session.get("contests", []): - if request.is_ajax(): - return error_response(u"请先输入密码") - else: - return render(request, "oj/contest/no_contest_permission.html", - {"reason": "password_protect", "show_tab": False, "contest": contest}) - - # 比赛没有开始 - if contest.status == CONTEST_NOT_START: - if request.is_ajax(): - return error_response(u"比赛还没有开始") - else: - return render(request, "oj/contest/no_contest_permission.html", - {"reason": "contest_not_start", "show_tab": False, "contest": contest}) - - # 比赛已经结束了,只拦截 ajax 的答案提交 - if contest.status == CONTEST_ENDED and request.path == reverse("contest_submission_api") and request.is_ajax(): - return error_response(u"比赛已经结束") - - return func(*args, **kwargs) - - return _check_user_contest_permission diff --git a/contest/migrations/0001_initial.py b/contest/migrations/0001_initial.py deleted file mode 100644 index 01df2877..00000000 --- a/contest/migrations/0001_initial.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('group', '0004_merge'), - ] - - operations = [ - migrations.CreateModel( - name='Contest', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(unique=True, max_length=40)), - ('description', models.TextField()), - ('mode', models.IntegerField()), - ('show_rank', models.BooleanField()), - ('show_user_submission', models.BooleanField()), - ('password', models.CharField(max_length=30, null=True, blank=True)), - ('contest_type', models.IntegerField()), - ('start_time', models.DateTimeField()), - ('end_time', models.DateTimeField()), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('last_updated_time', models.DateTimeField(auto_now=True)), - ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ('groups', models.ManyToManyField(to='group.Group')), - ], - options={ - 'db_table': 'contest', - }, - ), - migrations.CreateModel( - name='ContestProblem', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=50)), - ('description', models.TextField()), - ('input_description', models.CharField(max_length=10000)), - ('output_description', models.CharField(max_length=10000)), - ('samples', models.TextField(blank=True)), - ('test_case_id', models.CharField(max_length=40)), - ('hint', models.TextField(null=True, blank=True)), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('time_limit', models.IntegerField()), - ('memory_limit', models.IntegerField()), - ('visible', models.BooleanField(default=True)), - ('total_submit_number', models.IntegerField(default=0)), - ('total_accepted_number', models.IntegerField(default=0)), - ('sort_index', models.CharField(max_length=30)), - ('contest', models.ForeignKey(to='contest.Contest')), - ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'contest_problem', - }, - ), - migrations.CreateModel( - name='ContestProblemTestCase', - fields=[ - ('id', models.CharField(max_length=40, serialize=False, primary_key=True, db_index=True)), - ('score', models.IntegerField()), - ('problem', models.ForeignKey(to='contest.ContestProblem')), - ], - options={ - 'db_table': 'contest_problem_test_case', - }, - ), - ] diff --git a/contest/migrations/0002_contest_visible.py b/contest/migrations/0002_contest_visible.py deleted file mode 100644 index 7ee207d7..00000000 --- a/contest/migrations/0002_contest_visible.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='contest', - name='visible', - field=models.BooleanField(default=True), - ), - ] diff --git a/contest/migrations/0003_contestproblem_difficulty.py b/contest/migrations/0003_contestproblem_difficulty.py deleted file mode 100644 index 9d33b591..00000000 --- a/contest/migrations/0003_contestproblem_difficulty.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0002_contest_visible'), - ] - - operations = [ - migrations.AddField( - model_name='contestproblem', - name='difficulty', - field=models.IntegerField(default=1), - preserve_default=False, - ), - ] diff --git a/contest/migrations/0004_remove_contestproblem_difficulty.py b/contest/migrations/0004_remove_contestproblem_difficulty.py deleted file mode 100644 index 41ee147b..00000000 --- a/contest/migrations/0004_remove_contestproblem_difficulty.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0003_contestproblem_difficulty'), - ] - - operations = [ - migrations.RemoveField( - model_name='contestproblem', - name='difficulty', - ), - ] diff --git a/contest/migrations/0005_contestproblem_score.py b/contest/migrations/0005_contestproblem_score.py deleted file mode 100644 index e0a025dc..00000000 --- a/contest/migrations/0005_contestproblem_score.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0004_remove_contestproblem_difficulty'), - ] - - operations = [ - migrations.AddField( - model_name='contestproblem', - name='score', - field=models.IntegerField(default=0), - ), - ] diff --git a/contest/migrations/0005_contestsubmission.py b/contest/migrations/0005_contestsubmission.py deleted file mode 100644 index f1018d89..00000000 --- a/contest/migrations/0005_contestsubmission.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('contest', '0004_remove_contestproblem_difficulty'), - ] - - operations = [ - migrations.CreateModel( - name='ContestSubmission', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('total_submission_number', models.IntegerField(default=1)), - ('ac', models.BooleanField()), - ('total_time', models.IntegerField(default=0)), - ('contest', models.ForeignKey(to='contest.Contest')), - ('problem', models.ForeignKey(to='contest.ContestProblem')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'contest_submission', - }, - ), - ] diff --git a/contest/migrations/0006_merge.py b/contest/migrations/0006_merge.py deleted file mode 100644 index 9a4fe596..00000000 --- a/contest/migrations/0006_merge.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0005_contestsubmission'), - ('contest', '0005_contestproblem_score'), - ] - - operations = [ - ] diff --git a/contest/migrations/0007_contestsubmission_ac_time.py b/contest/migrations/0007_contestsubmission_ac_time.py deleted file mode 100644 index d7bc9049..00000000 --- a/contest/migrations/0007_contestsubmission_ac_time.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0006_merge'), - ] - - operations = [ - migrations.AddField( - model_name='contestsubmission', - name='ac_time', - field=models.IntegerField(default=0), - ), - ] diff --git a/contest/migrations/0008_auto_20150912_1912.py b/contest/migrations/0008_auto_20150912_1912.py deleted file mode 100644 index ee2a8f50..00000000 --- a/contest/migrations/0008_auto_20150912_1912.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0007_contestsubmission_ac_time'), - ] - - operations = [ - migrations.RenameField( - model_name='contest', - old_name='show_rank', - new_name='real_time_rank', - ), - ] diff --git a/contest/migrations/0009_contestsubmission_first_achieved.py b/contest/migrations/0009_contestsubmission_first_achieved.py deleted file mode 100644 index ce9529ce..00000000 --- a/contest/migrations/0009_contestsubmission_first_achieved.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0008_auto_20150912_1912'), - ] - - operations = [ - migrations.AddField( - model_name='contestsubmission', - name='first_achieved', - field=models.BooleanField(default=False), - ), - ] diff --git a/contest/migrations/0010_auto_20150922_1703.py b/contest/migrations/0010_auto_20150922_1703.py deleted file mode 100644 index 1a1609e5..00000000 --- a/contest/migrations/0010_auto_20150922_1703.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0009_contestsubmission_first_achieved'), - ] - - operations = [ - migrations.AlterField( - model_name='contest', - name='description', - field=utils.models.RichTextField(), - ), - migrations.AlterField( - model_name='contestproblem', - name='description', - field=utils.models.RichTextField(), - ), - ] diff --git a/contest/migrations/0011_contestrank.py b/contest/migrations/0011_contestrank.py deleted file mode 100644 index c014d7a9..00000000 --- a/contest/migrations/0011_contestrank.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings -import utils.models - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('contest', '0010_auto_20150922_1703'), - ] - - operations = [ - migrations.CreateModel( - name='ContestRank', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('total_submission_number', models.IntegerField(default=0)), - ('total_ac_number', models.IntegerField(default=0)), - ('total_time', models.IntegerField(default=0)), - ('submission_info', utils.models.JsonField(default={})), - ('contest', models.ForeignKey(to='contest.Contest')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/contest/migrations/0012_auto_20151008_1124.py b/contest/migrations/0012_auto_20151008_1124.py deleted file mode 100644 index 4bb09bfe..00000000 --- a/contest/migrations/0012_auto_20151008_1124.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import jsonfield.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0011_contestrank'), - ] - - operations = [ - migrations.AlterField( - model_name='contestrank', - name='submission_info', - field=jsonfield.fields.JSONField(default={}), - ), - migrations.AlterModelTable( - name='contestrank', - table='contest_rank', - ), - ] diff --git a/contest/migrations/0013_auto_20151017_1511.py b/contest/migrations/0013_auto_20151017_1511.py deleted file mode 100644 index ba3b201f..00000000 --- a/contest/migrations/0013_auto_20151017_1511.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0012_auto_20151008_1124'), - ] - - operations = [ - migrations.RemoveField( - model_name='contestproblemtestcase', - name='problem', - ), - migrations.RemoveField( - model_name='contestsubmission', - name='contest', - ), - migrations.RemoveField( - model_name='contestsubmission', - name='problem', - ), - migrations.RemoveField( - model_name='contestsubmission', - name='user', - ), - migrations.RemoveField( - model_name='contest', - name='mode', - ), - migrations.RemoveField( - model_name='contest', - name='show_user_submission', - ), - migrations.RemoveField( - model_name='contestproblem', - name='score', - ), - migrations.AddField( - model_name='contestproblem', - name='is_public', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='contestproblem', - name='last_update_time', - field=models.DateTimeField(null=True, blank=True), - ), - migrations.AlterField( - model_name='contestproblem', - name='hint', - field=utils.models.RichTextField(null=True, blank=True), - ), - migrations.DeleteModel( - name='ContestProblemTestCase', - ), - migrations.DeleteModel( - name='ContestSubmission', - ), - ] diff --git a/contest/migrations/0014_auto_20160404_1509.py b/contest/migrations/0014_auto_20160404_1509.py deleted file mode 100644 index fc78c36a..00000000 --- a/contest/migrations/0014_auto_20160404_1509.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-04-04 07:09 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0013_auto_20151017_1511'), - ] - - operations = [ - migrations.AddField( - model_name='contestproblem', - name='spj', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='contestproblem', - name='spj_code', - field=models.TextField(blank=True, null=True), - ), - migrations.AddField( - model_name='contestproblem', - name='spj_code_language', - field=models.IntegerField(blank=True, null=True), - ), - ] diff --git a/contest/migrations/0015_auto_20160404_1641.py b/contest/migrations/0015_auto_20160404_1641.py deleted file mode 100644 index 5b59ef2b..00000000 --- a/contest/migrations/0015_auto_20160404_1641.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-04-04 08:41 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0014_auto_20160404_1509'), - ] - - operations = [ - migrations.RenameField( - model_name='contestproblem', - old_name='spj_code_language', - new_name='spj_language', - ), - ] diff --git a/contest/migrations/0016_contestproblem_spj_version.py b/contest/migrations/0016_contestproblem_spj_version.py deleted file mode 100644 index 749654e5..00000000 --- a/contest/migrations/0016_contestproblem_spj_version.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-04-06 04:20 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('contest', '0015_auto_20160404_1641'), - ] - - operations = [ - migrations.AddField( - model_name='contestproblem', - name='spj_version', - field=models.CharField(blank=True, max_length=32, null=True), - ), - ] diff --git a/contest/models.py b/contest/models.py deleted file mode 100644 index 95c97bea..00000000 --- a/contest/models.py +++ /dev/null @@ -1,148 +0,0 @@ -# coding=utf-8 -import logging - -from django.db import models -from django.utils.timezone import now - -from account.models import User -from problem.models import AbstractProblem -from group.models import Group -from utils.models import RichTextField -from jsonfield import JSONField -from judge.result import result - - -GROUP_CONTEST = 0 -PUBLIC_CONTEST = 1 -PASSWORD_PROTECTED_CONTEST = 2 -PASSWORD_PROTECTED_GROUP_CONTEST = 3 - -CONTEST_NOT_START = 1 -CONTEST_ENDED = -1 -CONTEST_UNDERWAY = 0 - - -logger = logging.getLogger("app_info") - - -class Contest(models.Model): - title = models.CharField(max_length=40, unique=True) - description = RichTextField() - # 是否显示实时排名结果 - real_time_rank = models.BooleanField() - # 只能超级管理员创建公开赛,管理员只能创建小组内部的比赛 - # 如果这一项不为空,即为有密码的公开赛,没有密码的可以为小组赛或者是公开赛(此时用比赛的类型来表示) - password = models.CharField(max_length=30, blank=True, null=True) - # 比赛的类型: 0 即为是小组赛(GROUP_CONTEST),1 即为是无密码的公开赛(PUBLIC_CONTEST), - # 2 即为是有密码的公开赛(PASSWORD_PUBLIC_CONTEST) - contest_type = models.IntegerField() - # 开始时间 - start_time = models.DateTimeField() - # 结束时间 - end_time = models.DateTimeField() - # 创建时间 - create_time = models.DateTimeField(auto_now_add=True) - # 最后修改时间 - last_updated_time = models.DateTimeField(auto_now=True) - # 这个比赛是谁创建的 - created_by = models.ForeignKey(User) - groups = models.ManyToManyField(Group) - # 是否可见 false的话相当于删除 - visible = models.BooleanField(default=True) - - @property - def status(self): - if self.start_time > now(): - # 没有开始 返回1 - return CONTEST_NOT_START - elif self.end_time < now(): - # 已经结束 返回-1 - return CONTEST_ENDED - else: - # 正在进行 返回0 - return CONTEST_UNDERWAY - - class Meta: - db_table = "contest" - - -class ContestProblem(AbstractProblem): - contest = models.ForeignKey(Contest) - # 比如A B 或者1 2 或者 a b 将按照这个排序 - sort_index = models.CharField(max_length=30) - # 是否已经公开了题目,防止重复公开 - is_public = models.BooleanField(default=False) - - class Meta: - db_table = "contest_problem" - - -class ContestRank(models.Model): - user = models.ForeignKey(User) - contest = models.ForeignKey(Contest) - total_submission_number = models.IntegerField(default=0) - total_ac_number = models.IntegerField(default=0) - # ac 的题目才要加到这个字段里面 = ac 时间 + 错误次数 * 20 * 60 - # 没有 ac 的题目不计算罚时 单位是秒 - total_time = models.IntegerField(default=0) - # 数据结构{23: {"is_ac": True, "ac_time": 8999, "error_number": 2, "is_first_ac": True}} - # key 是比赛题目的id - submission_info = JSONField(default={}) - - class Meta: - db_table = "contest_rank" - - def update_rank(self, submission): - if not submission.contest_id or submission.contest_id != self.contest_id: - raise ValueError("Error submission type") - - if submission.result == result["system_error"]: - logger.warning("submission " + submission.id + " result is system error, update rank operation is ignored") - return - - # 这道题以前提交过 - if str(submission.problem_id) in self.submission_info: - info = self.submission_info[str(submission.problem_id)] - # 如果这道题目已经 ac 了就跳过 - if info["is_ac"]: - return - - self.total_submission_number += 1 - - if submission.result == result["accepted"]: - - self.total_ac_number += 1 - - info["is_ac"] = True - info["ac_time"] = (submission.create_time - self.contest.start_time).total_seconds() - - # 之前已经提交过,但是是错误的,这次提交是正确的。错误的题目不计入罚时 - self.total_time += (info["ac_time"] + info["error_number"] * 20 * 60) - problem = ContestProblem.objects.get(id=submission.problem_id) - # 更新题目计数器在前 所以是1 - if problem.total_accepted_number == 1: - info["is_first_ac"] = True - - else: - info["error_number"] += 1 - info["is_ac"] = False - - else: - # 第一次提交这道题目 - self.total_submission_number += 1 - info = {"is_ac": False, "ac_time": 0, "error_number": 0, "is_first_ac": False} - if submission.result == result["accepted"]: - self.total_ac_number += 1 - info["is_ac"] = True - info["ac_time"] = (submission.create_time - self.contest.start_time).total_seconds() - self.total_time += info["ac_time"] - problem = ContestProblem.objects.get(id=submission.problem_id) - - if problem.total_accepted_number == 1: - info["is_first_ac"] = True - - else: - info["is_ac"] = False - info["error_number"] = 1 - self.submission_info[str(submission.problem_id)] = info - self.save() diff --git a/contest/serializers.py b/contest/serializers.py deleted file mode 100644 index 317eb76e..00000000 --- a/contest/serializers.py +++ /dev/null @@ -1,119 +0,0 @@ -# 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 - - -class CreateContestSerializer(serializers.Serializer): - title = serializers.CharField(max_length=40) - description = serializers.CharField(max_length=5000) - contest_type = serializers.IntegerField() - real_time_rank = serializers.BooleanField() - password = serializers.CharField(max_length=30, required=False, default=None) - start_time = serializers.DateTimeField() - end_time = serializers.DateTimeField() - groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) - 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: - model = User - fields = ["username"] - - created_by = UserSerializer() - start_time = DateTimeLocal() - end_time = DateTimeLocal() - - class Meta: - model = Contest - - -class EditContestSerializer(serializers.Serializer): - id = serializers.IntegerField() - title = serializers.CharField(max_length=40) - description = serializers.CharField(max_length=10000) - contest_type = serializers.IntegerField() - real_time_rank = serializers.BooleanField() - password = serializers.CharField(max_length=30, required=False, default=None) - start_time = serializers.DateTimeField() - end_time = serializers.DateTimeField() - groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) - visible = serializers.BooleanField() - - -class ContestProblemSampleSerializer(serializers.ListField): - input = serializers.CharField(max_length=3000) - output = serializers.CharField(max_length=3000) - - -class JSONField(serializers.Field): - def to_representation(self, value): - return json.loads(value) - - -class CreateContestProblemSerializer(serializers.Serializer): - contest_id = serializers.IntegerField() - title = serializers.CharField(max_length=50) - description = serializers.CharField(max_length=10000) - input_description = serializers.CharField(max_length=10000) - output_description = serializers.CharField(max_length=10000) - # [{"input": "1 1", "output": "2"}] - samples = ContestProblemSampleSerializer() - test_case_id = serializers.CharField(max_length=40) - time_limit = serializers.IntegerField() - memory_limit = serializers.IntegerField() - spj = serializers.BooleanField() - spj_language = serializers.IntegerField(required=False, default=None) - spj_code = serializers.CharField(max_length=10000, required=False, default=None) - hint = serializers.CharField(max_length=3000, allow_blank=True) - score = serializers.IntegerField(required=False, default=0) - sort_index = serializers.CharField(max_length=30) - - -class ContestProblemSerializer(serializers.ModelSerializer): - class ContestSerializer(serializers.ModelSerializer): - class Meta: - model = Contest - fields = ["title", "id"] - - samples = JSONField() - contest = ContestSerializer() - created_by = UserSerializer() - - class Meta: - model = ContestProblem - - -class EditContestProblemSerializer(serializers.Serializer): - id = serializers.IntegerField() - title = serializers.CharField(max_length=50) - description = serializers.CharField(max_length=10000) - input_description = serializers.CharField(max_length=10000) - output_description = serializers.CharField(max_length=10000) - test_case_id = serializers.CharField(max_length=40) - time_limit = serializers.IntegerField() - memory_limit = serializers.IntegerField() - spj = serializers.BooleanField() - spj_language = serializers.IntegerField(required=False, default=None) - spj_code = serializers.CharField(max_length=10000, required=False, default=None) - samples = ContestProblemSampleSerializer() - hint = serializers.CharField(max_length=3000, allow_blank=True) - visible = serializers.BooleanField() - sort_index = serializers.CharField(max_length=30) - score = serializers.IntegerField(required=False, default=0) - - -class ContestPasswordVerifySerializer(serializers.Serializer): - contest_id = serializers.IntegerField() - password = serializers.CharField(max_length=30) \ No newline at end of file diff --git a/contest/tests.py b/contest/tests.py deleted file mode 100644 index e69de29b..00000000 diff --git a/contest/views.py b/contest/views.py deleted file mode 100644 index 80e86699..00000000 --- a/contest/views.py +++ /dev/null @@ -1,612 +0,0 @@ -# coding=utf-8 -import json -import os -import datetime -import hashlib - -from django.shortcuts import render -from django.db import IntegrityError -from django.utils import dateparse -from django.db.models import Q, Sum -from django.core.paginator import Paginator -from django.utils.timezone import now -from django.conf import settings - -from rest_framework.views import APIView - -from utils.shortcuts import (serializer_invalid_response, error_response, - success_response, paginate, error_page, paginate_data) -from account.models import SUPER_ADMIN, User -from account.decorators import login_required, super_admin_required -from group.models import Group, AdminGroupRelation, UserGroupRelation -from utils.cache import get_cache_redis -from submission.models import Submission -from problem.models import Problem -from .models import (Contest, ContestProblem, CONTEST_ENDED, - CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank) -from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST -from .decorators import check_user_contest_permission -from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, - CreateContestProblemSerializer, ContestProblemSerializer, - ContestPasswordVerifySerializer, - EditContestProblemSerializer) - - -class ContestAdminAPIView(APIView): - def post(self, request): - """ - 比赛发布json api接口 - --- - request_serializer: CreateContestSerializer - response_serializer: ContestSerializer - """ - serializer = CreateContestSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - groups = [] - # 首先判断比赛的类型: 0 即为是小组赛(GROUP_CONTEST),1 即为是无密码的公开赛(PUBLIC_CONTEST), - # 2 即为是有密码的公开赛(PASSWORD_PUBLIC_CONTEST) - # 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛 - if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]: - if request.user.admin_type != SUPER_ADMIN: - return error_response(u"只有超级管理员才可创建公开赛") - - if data["contest_type"] in [PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]: - if not data["password"]: - return error_response(u"此比赛为有密码的比赛,密码不可为空") - # 没有密码的公开赛 没有密码的小组赛 - if data["contest_type"] == GROUP_CONTEST or data["contest_type"] == PASSWORD_PROTECTED_GROUP_CONTEST: - if request.user.admin_type == SUPER_ADMIN: - groups = Group.objects.filter(id__in=data["groups"]) - else: - groups = Group.objects.filter(id__in=data["groups"], admin=request.user) - if not groups.count(): - return error_response(u"请至少选择一个小组") - if data["start_time"] >= data["end_time"]: - return error_response(u"比赛的开始时间必须早于比赛结束的时间") - try: - contest = Contest.objects.create(title=data["title"], description=data["description"], - contest_type=data["contest_type"], - real_time_rank=data["real_time_rank"], password=data["password"], - start_time=dateparse.parse_datetime(data["start_time"]), - end_time=dateparse.parse_datetime(data["end_time"]), - created_by=request.user, visible=data["visible"]) - except IntegrityError: - return error_response(u"比赛名已经存在") - contest.groups.add(*groups) - return success_response(ContestSerializer(contest).data) - else: - return serializer_invalid_response(serializer) - - def put(self, request): - """ - 比赛编辑json api接口 - --- - request_serializer: EditContestSerializer - response_serializer: ContestSerializer - """ - serializer = EditContestSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - groups = [] - try: - # 超级管理员可以编辑所有的 - contest = Contest.objects.get(id=data["id"]) - if request.user.admin_type != SUPER_ADMIN: - contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all()) - if contest not in contest_set: - return error_response(u"无权访问!") - except Contest.DoesNotExist: - return error_response(u"该比赛不存在!") - try: - contest = Contest.objects.get(title=data["title"]) - if contest.id != data["id"]: - return error_response(u"该比赛名称已经存在") - except Contest.DoesNotExist: - pass - if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]: - if request.user.admin_type != SUPER_ADMIN: - return error_response(u"只有超级管理员才可创建公开赛") - if data["contest_type"] == PASSWORD_PROTECTED_CONTEST: - if not data["password"]: - return error_response(u"此比赛为有密码的公开赛,密码不可为空") - elif data["contest_type"] in [GROUP_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]: - if request.user.admin_type == SUPER_ADMIN: - groups = Group.objects.filter(id__in=data["groups"]) - else: - groups = Group.objects.filter(id__in=data["groups"], admin=request.user) - if not groups.count(): - return error_response(u"请至少选择一个小组") - if data["start_time"] >= data["end_time"]: - return error_response(u"比赛的开始时间必须早于比赛结束的时间") - - # 之前是封榜,现在要开放,需要清除缓存 - if contest.real_time_rank == False and data["real_time_rank"] == True: - r = get_cache_redis() - cache_key = str(contest.id) + "_rank_cache" - r.delete(cache_key) - - contest.title = data["title"] - contest.description = data["description"] - contest.contest_type = data["contest_type"] - contest.real_time_rank = data["real_time_rank"] - contest.start_time = dateparse.parse_datetime(data["start_time"]) - contest.end_time = dateparse.parse_datetime(data["end_time"]) - contest.visible = data["visible"] - contest.password = data["password"] - contest.save() - - contest.groups.clear() - contest.groups.add(*groups) - return success_response(ContestSerializer(contest).data) - else: - return serializer_invalid_response(serializer) - - def get(self, request): - """ - 比赛分页json api接口 - --- - response_serializer: ContestSerializer - """ - contest_id = request.GET.get("contest_id", None) - if contest_id: - try: - # 普通管理员只能获取自己创建的题目 - # 超级管理员可以获取全部的题目 - contest = Contest.objects.get(id=contest_id) - if request.user.admin_type != SUPER_ADMIN: - contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all()) - if contest not in contest_set: - return error_response(u"比赛不存在") - return success_response(ContestSerializer(contest).data) - except Contest.DoesNotExist: - return error_response(u"比赛不存在") - - if request.user.admin_type == SUPER_ADMIN: - contest = Contest.objects.all().order_by("-create_time") - else: - contest = Contest.objects.filter(groups__in=request.user.managed_groups.all()).distinct().order_by("-create_time") - visible = request.GET.get("visible", None) - if visible: - contest = contest.filter(visible=(visible == "true")) - keyword = request.GET.get("keyword", None) - if keyword: - contest = contest.filter(Q(title__contains=keyword) | - Q(description__contains=keyword)) - return paginate(request, contest, ContestSerializer) - - -class ContestProblemAdminAPIView(APIView): - def _spj_version(self, code): - if code is None: - return None - return hashlib.md5(code.encode("utf-8")).hexdigest() - - def post(self, request): - """ - 比赛题目发布json api接口 - --- - request_serializer: CreateContestProblemSerializer - response_serializer: ContestProblemSerializer - """ - serializer = CreateContestProblemSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - contest = Contest.objects.get(id=data["contest_id"]) - if request.user.admin_type != SUPER_ADMIN: - contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all()) - if contest not in contest_set: - return error_response(u"比赛不存在") - except Contest.DoesNotExist: - return error_response(u"比赛不存在") - contest_problem = ContestProblem.objects.create(title=data["title"], - description=data["description"], - input_description=data["input_description"], - output_description=data["output_description"], - test_case_id=data["test_case_id"], - samples=json.dumps(data["samples"]), - time_limit=data["time_limit"], - memory_limit=data["memory_limit"], - spj=data["spj"], - spj_language=data["spj_language"], - spj_code=data["spj_code"], - spj_version=self._spj_version(data["spj_code"]), - created_by=request.user, - hint=data["hint"], - contest=contest, - sort_index=data["sort_index"]) - return success_response(ContestProblemSerializer(contest_problem).data) - else: - return serializer_invalid_response(serializer) - - def put(self, request): - """ - 比赛题目编辑json api接口 - --- - request_serializer: EditContestProblemSerializer - response_serializer: ContestProblemSerializer - """ - serializer = EditContestProblemSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - - try: - contest_problem = ContestProblem.objects.get(id=data["id"]) - except ContestProblem.DoesNotExist: - return error_response(u"该比赛题目不存在!") - - contest = Contest.objects.get(id=contest_problem.contest_id) - if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user: - return error_response(u"比赛不存在") - contest_problem.title = data["title"] - contest_problem.description = data["description"] - contest_problem.input_description = data["input_description"] - contest_problem.output_description = data["output_description"] - contest_problem.test_case_id = data["test_case_id"] - contest_problem.time_limit = data["time_limit"] - contest_problem.memory_limit = data["memory_limit"] - contest_problem.spj = data["spj"] - contest_problem.spj_language = data["spj_language"] - contest_problem.spj_code = data["spj_code"] - contest_problem.spj_version = self._spj_version(data["spj_code"]) - contest_problem.samples = json.dumps(data["samples"]) - contest_problem.hint = data["hint"] - contest_problem.visible = data["visible"] - contest_problem.sort_index = data["sort_index"] - contest_problem.last_update_time = now() - contest_problem.save() - return success_response(ContestProblemSerializer(contest_problem).data) - else: - return serializer_invalid_response(serializer) - - def get(self, request): - """ - 比赛题目分页json api接口 - --- - response_serializer: ContestProblemSerializer - """ - contest_problem_id = request.GET.get("contest_problem_id", None) - if contest_problem_id: - try: - contest_problem = ContestProblem.objects.get(id=contest_problem_id) - if request.user.admin_type != SUPER_ADMIN and contest_problem.created_by != request.user: - return error_response(u"比赛题目不存在") - return success_response(ContestProblemSerializer(contest_problem).data) - except ContestProblem.DoesNotExist: - return error_response(u"比赛题目不存在") - - contest_problems = ContestProblem.objects.all().order_by("sort_index") - if request.user.admin_type != SUPER_ADMIN: - contest_problems = contest_problems.filter(created_by=request.user).order_by("sort_index") - visible = request.GET.get("visible", None) - if visible: - contest_problems = contest_problems.filter(visible=(visible == "true")) - keyword = request.GET.get("keyword", None) - if keyword: - contest_problems = contest_problems.filter(Q(title__contains=keyword) | - Q(description__contains=keyword)) - contest_id = request.GET.get("contest_id", None) - if contest_id: - contest_problems = contest_problems.filter(contest__id=contest_id).order_by("sort_index") - - return paginate(request, contest_problems, ContestProblemSerializer) - - -class MakeContestProblemPublicAPIView(APIView): - @super_admin_required - def post(self, request): - problem_id = request.data.get("problem_id", -1) - try: - problem = ContestProblem.objects.get(id=problem_id, is_public=False) - if problem.contest.status != CONTEST_ENDED: - return error_response(u"比赛还没有结束,不能公开题目") - problem.is_public = True - problem.save() - except ContestProblem.DoesNotExist: - return error_response(u"比赛不存在") - Problem.objects.create(title=problem.title, description=problem.description, - input_description=problem.input_description, - output_description=problem.output_description, - samples=problem.samples, - test_case_id=problem.test_case_id, - hint=problem.hint, created_by=problem.created_by, - time_limit=problem.time_limit, memory_limit=problem.memory_limit, - visible=False, difficulty=-1, source=problem.contest.title) - problem.is_public = True - problem.save() - return success_response(u"创建成功") - - -class ContestPasswordVerifyAPIView(APIView): - @login_required - def post(self, request): - serializer = ContestPasswordVerifySerializer(data=request.data) - if serializer.is_valid(): - data = request.data - try: - contest = Contest.objects.get(id=data["contest_id"], contest_type__in=[PASSWORD_PROTECTED_CONTEST,PASSWORD_PROTECTED_GROUP_CONTEST]) - except Contest.DoesNotExist: - return error_response(u"比赛不存在") - - if data["password"] != contest.password: - return error_response(u"密码错误") - else: - if "contests" not in request.session: - request.session["contests"] = [] - request.session["contests"].append(int(data["contest_id"])) - # https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved - request.session.modified = True - - return success_response(True) - else: - return serializer_invalid_response(serializer) - - -@check_user_contest_permission -def contest_page(request, contest_id): - """ - 单个比赛的详情页 - """ - contest = Contest.objects.get(id=contest_id) - return render(request, "oj/contest/contest_index.html", {"contest": contest}) - - -@check_user_contest_permission -def contest_problem_page(request, contest_id, contest_problem_id): - """ - 单个比赛题目的详情页 - """ - contest = Contest.objects.get(id=contest_id) - try: - problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) - except ContestProblem.DoesNotExist: - return error_page(request, u"比赛题目不存在") - warning = u"您已经提交过本题的正确答案,重复提交可能造成时间累计。" - show_warning = False - - try: - rank = ContestRank.objects.get(user=request.user, contest=contest) - # 提示已经 ac 过这道题了 - show_warning = rank.submission_info.get(str(problem.id), {"is_ac": False})["is_ac"] - except ContestRank.DoesNotExist: - pass - - # 已经结束 - if contest.status == CONTEST_ENDED: - show_warning = True - warning = u"比赛已经结束" - elif contest.status == CONTEST_NOT_START: - show_warning = True - warning = u"比赛没有开始,您是管理员,可以提交和测试题目,但是目前的提交不会计入排名。" - - show_submit_code_area = False - if contest.status == CONTEST_UNDERWAY or \ - request.user.admin_type == SUPER_ADMIN or \ - request.user == contest.created_by: - show_submit_code_area = True - else: - contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all()) - if contest in contest_set: - show_submit_code_area = True - return render(request, "oj/problem/contest_problem.html", {"problem": problem, - "contest": contest, - "samples": json.loads(problem.samples), - "show_warning": show_warning, - "warning": warning, - "show_submit_code_area": show_submit_code_area}) - - -@check_user_contest_permission -def contest_problems_list_page(request, contest_id): - """ - 比赛所有题目的列表页 - """ - contest = Contest.objects.get(id=contest_id) - contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).select_related("contest").order_by("sort_index") - return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems, - "contest": {"id": contest_id}}) - - -def contest_list_page(request, page=1): - """ - 所有比赛的列表页 - """ - # 正常情况 - contests = Contest.objects.filter(visible=True).order_by("-create_time") - - # 搜索的情况 - keyword = request.GET.get("keyword", "").strip() - if keyword: - contests = contests.filter(Q(title__contains=keyword) | Q(description__contains=keyword)) - - # 筛选我能参加的比赛 - join = request.GET.get("join", None) - if request.user.is_authenticated() and join: - contests = contests.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())). \ - filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now()) - paginator = Paginator(contests, 20) - try: - current_page = paginator.page(int(page)) - except Exception: - return error_page(request, u"不存在的页码") - - previous_page = next_page = None - - try: - previous_page = current_page.previous_page_number() - except Exception: - pass - - try: - next_page = current_page.next_page_number() - except Exception: - pass - - return render(request, "oj/contest/contest_list.html", - {"contests": current_page, "page": int(page), - "previous_page": previous_page, "next_page": next_page, - "keyword": keyword, "join": join}) - - -def _get_rank(contest_id): - rank = ContestRank.objects.filter(contest_id=contest_id). \ - select_related("user"). \ - order_by("-total_ac_number", "total_time"). \ - values("id", "user__id", "user__username", "user__real_name", "user__userprofile__student_id", - "contest_id", "submission_info", "total_submission_number", "total_ac_number", "total_time") - rank_number = 1 - for item in rank: - # 只有有ac的题目而且不是打星的队伍才参与排名 - if item["total_ac_number"] > 0 and item["user__username"][0] != "*": - item["rank_number"] = rank_number - rank_number += 1 - return rank - - -@check_user_contest_permission -def contest_rank_page(request, contest_id): - contest = Contest.objects.get(id=contest_id) - contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).order_by("sort_index") - - force_real_time_rank = False - if request.GET.get("force_real_time_rank") == "true" and (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by): - rank = _get_rank(contest_id) - force_real_time_rank = True - else: - r = get_cache_redis() - cache_key = str(contest_id) + "_rank_cache" - rank = r.get(cache_key) - - if not rank: - rank = _get_rank(contest_id) - r.set(cache_key, json.dumps([dict(item) for item in rank])) - else: - rank = json.loads(rank) - - # 2016-05-19 增加了缓存项目,以前的缓存主动失效 - if rank and "rank_number" not in rank[0]: - rank = _get_rank(contest_id) - r.set(cache_key, json.dumps([dict(item) for item in rank])) - - return render(request, "oj/contest/contest_rank.html", - {"rank": rank, "contest": contest, - "contest_problems": contest_problems, - "auto_refresh": request.GET.get("auto_refresh", None) == "true", - "show_real_name": request.GET.get("show_real_name", None) == "true", - "force_real_time_rank": force_real_time_rank}) - - -class ContestTimeAPIView(APIView): - """ - 获取比赛开始或者结束的倒计时,返回毫秒数字 - """ - - def get(self, request): - contest_id = request.GET.get("contest_id", -1) - try: - contest = Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - return error_response(u"比赛不存在") - return success_response({"start": int((contest.start_time - now()).total_seconds() * 1000), - "end": int((contest.end_time - now()).total_seconds() * 1000), - "status": contest.status}) - - -@login_required -def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id): - """ - 我比赛单个题目的所有提交列表 - """ - try: - Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - return error_page(request, u"比赛不存在") - try: - contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) - except ContestProblem.DoesNotExist: - return error_page(request, u"比赛问题不存在") - submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id, contest_id=contest_id). \ - order_by("-create_time"). \ - values("id", "result", "create_time", "accepted_answer_time", "language") - return render(request, "oj/submission/problem_my_submissions_list.html", - {"submissions": submissions, "problem": contest_problem}) - - -@check_user_contest_permission -def contest_problem_submissions_list_page(request, contest_id, page=1): - """ - 单个比赛中的所有提交(包含自己和别人,自己可查提交结果,其他人不可查) - """ - try: - contest = Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - return error_page(request, u"比赛不存在") - - submissions = Submission.objects.filter(contest_id=contest_id). \ - values("id", "contest_id", "problem_id", "result", "create_time", - "accepted_answer_time", "language", "user_id").order_by("-create_time") - - # 如果比赛已经开始,就不再显示之前测试题目的提交 - if contest.status != CONTEST_NOT_START: - submissions = submissions.filter(create_time__gte=contest.start_time) - - user_id = request.GET.get("user_id", None) - if user_id: - submissions = submissions.filter(user_id=request.GET.get("user_id")) - - problem_id = request.GET.get("problem_id", None) - if problem_id: - submissions = submissions.filter(problem_id=problem_id) - - # 封榜的时候只能看到自己的提交 - if not contest.real_time_rank: - if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by): - submissions = submissions.filter(user_id=request.user.id) - - language = request.GET.get("language", None) - filter = None - if language: - submissions = submissions.filter(language=int(language)) - filter = {"name": "language", "content": language} - result = request.GET.get("result", None) - if result: - submissions = submissions.filter(result=int(result)) - filter = {"name": "result", "content": result} - - paginator = Paginator(submissions, 20) - try: - submissions = paginator.page(int(page)) - except Exception: - return error_page(request, u"不存在的页码") - - # 为查询题目标题创建新字典 - title = {} - contest_problems = ContestProblem.objects.filter(contest=contest) - for item in contest_problems: - title[item.id] = item.title - for item in submissions: - item['title'] = title[item['problem_id']] - - previous_page = next_page = None - try: - previous_page = submissions.previous_page_number() - except Exception: - pass - try: - next_page = submissions.next_page_number() - except Exception: - pass - - for item in submissions: - # 自己提交的 管理员和创建比赛的可以看到所有的提交链接 - if item["user_id"] == request.user.id or request.user.admin_type == SUPER_ADMIN or \ - request.user == contest.created_by: - item["show_link"] = True - else: - item["show_link"] = False - - return render(request, "oj/contest/submissions_list.html", - {"submissions": submissions, "page": int(page), - "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, - "contest": contest, "filter": filter, "user_id": user_id, "problem_id": problem_id}) diff --git a/dockerfiles/oj_web_server/requirements.txt b/dockerfiles/oj_web_server/requirements.txt index 1e328e34..e94e007a 100644 --- a/dockerfiles/oj_web_server/requirements.txt +++ b/dockerfiles/oj_web_server/requirements.txt @@ -1,4 +1,4 @@ -django +django<1.10 MySQL-python redis django-redis-sessions diff --git a/frontend/admin/src/views/account/editUser.vue b/frontend/admin/src/views/account/editUser.vue index 4103e863..86f45f82 100644 --- a/frontend/admin/src/views/account/editUser.vue +++ b/frontend/admin/src/views/account/editUser.vue @@ -125,7 +125,7 @@ } } this.request({ - url: "/api/admin/user/", + url: "/api/admin/account/user/", method: "PUT", data: data }) @@ -134,7 +134,7 @@ route: { data() { this.request({ - url: "/api/admin/user/?user_id=" + this.$route.params["userId"], + url: "/api/admin/account/user/?user_id=" + this.$route.params["userId"], method: "GET", success: (data)=> { this.user = data.data; diff --git a/frontend/admin/src/views/account/userList.vue b/frontend/admin/src/views/account/userList.vue index 0abf0c12..ca9498de 100644 --- a/frontend/admin/src/views/account/userList.vue +++ b/frontend/admin/src/views/account/userList.vue @@ -82,7 +82,7 @@ }, methods: { loadData() { - var url = "/api/admin/user/?paging=true&page_size=2&page=" + this.pagination.currentPage; + var url = "/api/admin/account/user/?paging=true&page_size=2&page=" + this.pagination.currentPage; if (this.keyword) { url += ("&keyword=" + this.keyword) } diff --git a/frontend/admin/src/views/announcement/announcementList.vue b/frontend/admin/src/views/announcement/announcementList.vue index 269f1b29..e9b6bba4 100644 --- a/frontend/admin/src/views/announcement/announcementList.vue +++ b/frontend/admin/src/views/announcement/announcementList.vue @@ -16,7 +16,7 @@ {{ announcement.create_time }} {{ announcement.last_update_time }} {{ announcement.created_by.username }} - {{ $t(announcementStatus[announcement.visible?1:0]) }} + {{ $t(announcementStatus[announcement.visible?0:1]) }} diff --git a/group/migrations/0001_initial.py b/group/migrations/0001_initial.py deleted file mode 100644 index 68da3ae6..00000000 --- a/group/migrations/0001_initial.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Group', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=30)), - ('description', models.TextField()), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('join_group_setting', models.IntegerField()), - ('visible', models.BooleanField(default=True)), - ('admin', models.ForeignKey(related_name='my_groups', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'group', - }, - ), - migrations.CreateModel( - name='JoinGroupRequest', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('message', models.TextField()), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('status', models.BooleanField(default=False)), - ('group', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ('user', models.ForeignKey(related_name='my_join_group_requests', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'join_group_request', - }, - ), - migrations.CreateModel( - name='UserGroupRelation', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('join_time', models.DateTimeField(auto_now_add=True)), - ('group', models.ForeignKey(to='group.Group')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'user_group_relation', - }, - ), - migrations.AddField( - model_name='group', - name='members', - field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='group.UserGroupRelation'), - ), - ] diff --git a/group/migrations/0002_auto_20150811_1456.py b/group/migrations/0002_auto_20150811_1456.py deleted file mode 100644 index 2b6a65e6..00000000 --- a/group/migrations/0002_auto_20150811_1456.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('group', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='group', - name='name', - field=models.CharField(unique=True, max_length=30), - ), - migrations.AlterField( - model_name='joingrouprequest', - name='group', - field=models.ForeignKey(to='group.Group'), - ), - migrations.AlterUniqueTogether( - name='usergrouprelation', - unique_together=set([('group', 'user')]), - ), - ] diff --git a/group/migrations/0002_auto_20150811_1649.py b/group/migrations/0002_auto_20150811_1649.py deleted file mode 100644 index 2b6a65e6..00000000 --- a/group/migrations/0002_auto_20150811_1649.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('group', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='group', - name='name', - field=models.CharField(unique=True, max_length=30), - ), - migrations.AlterField( - model_name='joingrouprequest', - name='group', - field=models.ForeignKey(to='group.Group'), - ), - migrations.AlterUniqueTogether( - name='usergrouprelation', - unique_together=set([('group', 'user')]), - ), - ] diff --git a/group/migrations/0003_auto_20150811_1906.py b/group/migrations/0003_auto_20150811_1906.py deleted file mode 100644 index 242a62e2..00000000 --- a/group/migrations/0003_auto_20150811_1906.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('group', '0002_auto_20150811_1456'), - ] - - operations = [ - migrations.AlterField( - model_name='group', - name='join_group_setting', - field=models.IntegerField(default=1), - ), - ] diff --git a/group/migrations/0004_merge.py b/group/migrations/0004_merge.py deleted file mode 100644 index febb5e2e..00000000 --- a/group/migrations/0004_merge.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('group', '0003_auto_20150811_1906'), - ('group', '0002_auto_20150811_1649'), - ] - - operations = [ - ] diff --git a/group/migrations/0005_joingrouprequest_accepted.py b/group/migrations/0005_joingrouprequest_accepted.py deleted file mode 100644 index 1091ce04..00000000 --- a/group/migrations/0005_joingrouprequest_accepted.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- 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), - ), - ] diff --git a/group/migrations/0006_auto_20151209_1834.py b/group/migrations/0006_auto_20151209_1834.py deleted file mode 100644 index 3e5a6e95..00000000 --- a/group/migrations/0006_auto_20151209_1834.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2015-12-09 10:34 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('group', '0005_joingrouprequest_accepted'), - ] - - operations = [ - migrations.RenameField( - model_name='group', - old_name='admin', - new_name='created_by', - ), - ] diff --git a/group/migrations/0007_auto_20151209_1836.py b/group/migrations/0007_auto_20151209_1836.py deleted file mode 100644 index 937d6f07..00000000 --- a/group/migrations/0007_auto_20151209_1836.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2015-12-09 10:36 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('group', '0006_auto_20151209_1834'), - ] - - operations = [ - migrations.CreateModel( - name='AdminGroupRelation', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='group.Group')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'admin_group_relation', - }, - ), - migrations.AddField( - model_name='group', - name='admin', - field=models.ManyToManyField(related_name='managed_groups', through='group.AdminGroupRelation', to=settings.AUTH_USER_MODEL), - ), - migrations.AlterUniqueTogether( - name='admingrouprelation', - unique_together=set([('user', 'group')]), - ), - ] diff --git a/group/migrations/__init__.py b/group/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/group/models.py b/group/models.py deleted file mode 100644 index 26059f7e..00000000 --- a/group/models.py +++ /dev/null @@ -1,53 +0,0 @@ -# coding=utf-8 -from django.db import models - -from account.models import User - - -class Group(models.Model): - name = models.CharField(max_length=30, unique=True) - description = models.TextField() - create_time = models.DateTimeField(auto_now_add=True) - created_by = models.ForeignKey(User, related_name="my_groups") - # 0是公开 1是需要申请后加入 2是不允许任何人加入 - join_group_setting = models.IntegerField(default=1) - members = models.ManyToManyField(User, through="UserGroupRelation") - admin = models.ManyToManyField(User, through="AdminGroupRelation", related_name="managed_groups") - # 解散小组后,这一项改为False - visible = models.BooleanField(default=True) - - class Meta: - db_table = "group" - - -class UserGroupRelation(models.Model): - group = models.ForeignKey(Group) - user = models.ForeignKey(User) - join_time = models.DateTimeField(auto_now_add=True) - - class Meta: - db_table = "user_group_relation" - unique_together = ("group", "user") - - - -class AdminGroupRelation(models.Model): - user = models.ForeignKey(User) - group = models.ForeignKey(Group) - - class Meta: - db_table = "admin_group_relation" - unique_together = ("user", "group") - - -class JoinGroupRequest(models.Model): - group = models.ForeignKey(Group) - user = models.ForeignKey(User, related_name="my_join_group_requests") - message = models.TextField() - 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" diff --git a/group/serializers.py b/group/serializers.py deleted file mode 100644 index 377d56d6..00000000 --- a/group/serializers.py +++ /dev/null @@ -1,81 +0,0 @@ -# coding=utf-8 -from rest_framework import serializers - -from account.models import User -from account.serializers import UserSerializer -from .models import Group, UserGroupRelation, JoinGroupRequest - - -class CreateGroupSerializer(serializers.Serializer): - name = serializers.CharField(max_length=20) - description = serializers.CharField(max_length=300) - join_group_setting = serializers.IntegerField(min_value=0, max_value=2) - - -class EditGroupSerializer(serializers.Serializer): - group_id = serializers.IntegerField() - name = serializers.CharField(max_length=20) - description = serializers.CharField(max_length=300) - join_group_setting = serializers.IntegerField() - visible = serializers.BooleanField() - - -class CreateJoinGroupRequestSerializer(serializers.Serializer): - group_id = serializers.IntegerField() - message = serializers.CharField(max_length=30, required=False) - - -class JoinGroupRequestSerializer(serializers.ModelSerializer): - class GroupSerializer(serializers.ModelSerializer): - class Meta: - model = Group - fields = ["id", "name"] - - class UserSerializer(serializers.ModelSerializer): - class Meta: - model = User - fields = ["username"] - - group = GroupSerializer() - user = UserSerializer() - - class Meta: - model = JoinGroupRequest - - -class GroupSerializer(serializers.ModelSerializer): - members_number = serializers.SerializerMethodField("_get_group_members_number") - - def _get_group_members_number(self, group): - return group.members.all().count() - - class Meta: - model = Group - exclude = ["members"] - - -class GroupMemberSerializer(serializers.ModelSerializer): - class UserSerializer(serializers.ModelSerializer): - class Meta: - model = User - fields = ["id", "username", "real_name"] - - user = UserSerializer() - - class Meta: - model = UserGroupRelation - exclude = ["id"] - - -class EditGroupMemberSerializer(serializers.Serializer): - group_id = serializers.IntegerField() - members = serializers.ListField(child=serializers.IntegerField()) - - -class PutJoinGroupRequestSerializer(serializers.Serializer): - request_id = serializers.IntegerField() - status = serializers.BooleanField() - -class GroupPromoteAdminSerializer(serializers.Serializer): - user_id = serializers.IntegerField() - group_id = serializers.IntegerField() diff --git a/group/tests.py b/group/tests.py deleted file mode 100644 index e69de29b..00000000 diff --git a/group/views.py b/group/views.py deleted file mode 100644 index 83b430c3..00000000 --- a/group/views.py +++ /dev/null @@ -1,355 +0,0 @@ -# coding=utf-8 -from django.shortcuts import render -from django.db import IntegrityError - -from rest_framework.views import APIView - -from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate, error_page -from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User -from account.decorators import login_required - -from .models import Group, JoinGroupRequest, UserGroupRelation, AdminGroupRelation -from .serializers import (CreateGroupSerializer, EditGroupSerializer, - CreateJoinGroupRequestSerializer, GroupSerializer, - GroupMemberSerializer, EditGroupMemberSerializer, - JoinGroupRequestSerializer, PutJoinGroupRequestSerializer, GroupPromoteAdminSerializer) -from announcement.models import Announcement -from django.core.paginator import Paginator -from django.db.models import Q - - -class GroupAPIViewBase(object): - def get_group(self, request, group_id): - """ - 根据group_id查询指定的小组的信息,结合判断用户权限 - 管理员可以查询所有的小组,其他用户查询自己创建的小组 - """ - if request.user.admin_type == SUPER_ADMIN: - group = Group.objects.get(id=group_id) - else: - group = Group.objects.get(id=group_id, visible=True, admin=request.user) - return group - - def get_groups(self, request): - """ - 如果是超级管理员,就返回全部的小组 - 如果是管理员,就返回他创建的全部小组 - """ - if request.user.admin_type == SUPER_ADMIN: - groups = Group.objects.filter() - else: - groups = Group.objects.filter(admin=request.user, visible=True) - return groups - - -class GroupAdminAPIView(APIView, GroupAPIViewBase): - def post(self, request): - """ - 创建小组的api - --- - request_serializer: CreateGroupSerializer - response_serializer: GroupSerializer - """ - serializer = CreateGroupSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - group = Group.objects.create(name=data["name"], - description=data["description"], - join_group_setting=data["join_group_setting"], - created_by=request.user) - except IntegrityError: - return error_response(u"小组名已经存在") - AdminGroupRelation.objects.create(group=group, user=request.user) - return success_response(GroupSerializer(group).data) - else: - return serializer_invalid_response(serializer) - - def put(self, request): - """ - 修改小组信息的api - --- - request_serializer: EditGroupSerializer - response_serializer: GroupSerializer - """ - serializer = EditGroupSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - group = self.get_group(request, data["group_id"]) - except Group.DoesNotExist: - return error_response(u"小组不存在") - try: - group.name = data["name"] - group.description = data["description"] - group.join_group_setting = data["join_group_setting"] - group.visible = data["visible"] - group.save() - except IntegrityError: - return error_response(u"小组名已经存在") - - return success_response(GroupSerializer(group).data) - else: - return serializer_invalid_response(serializer) - - def get(self, request): - """ - 查询小组列表或者单个小组的信息,查询单个小组需要传递group_id参数,否则返回全部 - --- - response_serializer: GroupSerializer - """ - group_id = request.GET.get("group_id", None) - # 根据 id 查询小组信息 - if group_id: - try: - group = self.get_group(request, group_id) - return success_response(GroupSerializer(group).data) - except Group.DoesNotExist: - 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) - - -class GroupMemberAdminAPIView(APIView, GroupAPIViewBase): - def get(self, request): - """ - 查询小组成员的api,需要传递group_id参数 - --- - response_serializer: GroupMemberSerializer - """ - group_id = request.GET.get("group_id", None) - if not group_id: - return error_response(u"参数错误") - try: - group = self.get_group(request, group_id) - except Group.DoesNotExist: - return error_response(u"小组不存在") - admin_only = request.GET.get("admin_only", None) - if admin_only: - members = AdminGroupRelation.objects.filter(group=group) - else: - members = UserGroupRelation.objects.filter(group=group) - - return paginate(request, members, GroupMemberSerializer) - - def put(self, request): - """ - 删除小组成员的api接口 - --- - request_serializer: EditGroupMemberSerializer - """ - serializer = EditGroupMemberSerializer(data=request.data) - if serializer.is_valid(): - try: - group = self.get_group(request, serializer.data["group_id"]) - except Group.DoesNotExist: - return error_response(u"小组不存在") - user_id_list = serializer.data["members"] - UserGroupRelation.objects.filter(group=group, user__id__in=user_id_list).delete() - return success_response(u"删除成功") - else: - return serializer_invalid_response(serializer) - - -def join_group(user, group): - try: - UserGroupRelation.objects.create(user=user, group=group) - return True - except IntegrityError: - return False - - -class JoinGroupAPIView(APIView): - # @login_required - def post(self, request): - """ - 加入某个小组的api - --- - request_serializer: CreateJoinGroupRequestSerializer - """ - serializer = CreateJoinGroupRequestSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - group = Group.objects.get(id=data["group_id"]) - except Group.DoesNotExist: - return error_response(u"小组不存在") - if group.join_group_setting == 0: - if join_group(request.user, group): - return success_response(u"你已经成功的加入该小组") - else: - return error_response(u"你已经是小组成员了") - elif group.join_group_setting == 1: - if not data["message"]: - return error_response(u"message : 该字段是必填项。") - try: - JoinGroupRequest.objects.get(user=request.user, group=group, status=False) - return error_response(u"你已经提交过申请了,请等待审核") - except JoinGroupRequest.DoesNotExist: - JoinGroupRequest.objects.create(user=request.user, group=group, message=data["message"]) - return success_response(u"申请提交成功,请等待审核") - elif group.join_group_setting == 2: - return error_response(u"该小组不允许任何人加入") - else: - return serializer_invalid_response(serializer) - - def get(self, request): - """ - 搜索小组的api,需要传递keyword参数 - --- - response_serializer: GroupSerializer - """ - keyword = request.GET.get("keyword", None) - if not keyword: - return error_response(u"参数错误") - # 搜索包含这个关键词的 没有解散的 而且允许加入的小组 - groups = Group.objects.filter(name__contains=keyword, visible=True, join_group_setting__lte=2) - return paginate(request, groups, GroupSerializer) - - -class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase): - def get(self, request): - """ - 返回管理的群的加群请求 - --- - response_serializer: JoinGroupRequestSerializer - """ - requests = JoinGroupRequest.objects.filter(group__in=Group.objects.filter(admin=request.user, visible=True), - status=False) - return paginate(request, requests, JoinGroupRequestSerializer) - - def put(self, request): - """ - 同意或者拒绝加入小组请求 - --- - request_serializer: PutJoinGroupRequestSerializer - """ - serializer = PutJoinGroupRequestSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - join_request = JoinGroupRequest.objects.get(id=data["request_id"], group__admin=request.user, - status=False) - except JoinGroupRequest.DoesNotExist: - return error_response(u"请求不存在") - - 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"加入失败,已经在本小组内") - else: - return success_response(u"已拒绝") - - else: - return serializer_invalid_response(serializer) - - -@login_required -def group_list_page(request, page=1): - groups = Group.objects.filter(visible=True, join_group_setting__lte=2) - # 搜索的情况 - keyword = request.GET.get("keyword", None) - if keyword: - groups = groups.filter(Q(name__contains=keyword) | Q(description__contains=keyword)) - - paginator = Paginator(groups, 20) - try: - current_page = paginator.page(int(page)) - except Exception: - return error_page(request, u"不存在的页码") - - previous_page = next_page = None - - try: - previous_page = current_page.previous_page_number() - except Exception: - pass - next_page = None - try: - next_page = current_page.next_page_number() - except Exception: - pass - - return render(request, "oj/group/group_list.html", { - "groups": groups, - "contests": current_page, "page": int(page), - "previous_page": previous_page, "next_page": next_page, - "keyword": keyword - }) - - -@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"小组不存在") - joined = True - try: - UserGroupRelation.objects.get(user=request.user, group=group) - except UserGroupRelation.DoesNotExist: - joined = False - return render(request, "oj/group/group.html", {"group": group, "joined": joined}) - - -@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}) - - -class GroupPrometAdminAPIView(APIView): - def post(self, request): - """ - 创建小组管理员的api - --- - request_serializer: GroupPromoteAdminSerializer - """ - serializer = GroupPromoteAdminSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - group = Group.objects.get(id=data["group_id"]) - except Group.DoesNotExist: - return error_response(u"小组不存在") - try: - user = User.objects.get(id=data["user_id"]) - except User.DoesNotExist: - return error_response(u"用户不存在") - try: - AdminGroupRelation.objects.create(user=user, group=group) - except IntegrityError: - return error_response(u"该用户已经是管理员了") - return success_response(u"操作成功") - else: - return serializer_invalid_response(serializer) diff --git a/judge/__init__.py b/judge/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/judge/client.py b/judge/client.py deleted file mode 100644 index faab51ab..00000000 --- a/judge/client.py +++ /dev/null @@ -1,176 +0,0 @@ -# coding=utf-8 -import os -import json -import hashlib -import judger -import spj_client - -from multiprocessing import Pool - -from settings import max_running_number -from language import languages -from result import result -from judge_exceptions import JudgeClientError -from logger import logger - - -# 下面这个函数作为代理访问实例变量,否则Python2会报错,是Python2的已知问题 -# http://stackoverflow.com/questions/1816958/cant-pickle-type-instancemethod-when-using-pythons-multiprocessing-pool-ma/7309686 -def _run(instance, test_case_id): - return instance._judge_one(test_case_id) - - -class JudgeClient(object): - def __init__(self, language_code, exe_path, max_cpu_time, max_memory, test_case_dir, judge_base_path, spj_path): - """ - :param language_code: 语言编号 - :param exe_path: 可执行文件路径 - :param max_cpu_time: 最大cpu时间,单位ms - :param max_memory: 最大内存,单位字节,直接传给judger.run方法 - :param test_case_dir: 测试用例文件夹路径 - :return:返回结果list - """ - self._language = languages[language_code] - self._exe_path = exe_path - self._max_cpu_time = max_cpu_time - - # 如果是Java, 就不在judger中限制内存分配了, 而是转移到Java运行参数中, - # 参见 https://github.com/QingdaoU/OnlineJudge/issues/23 - # 这里给出3倍的限制, 是为了防止出现OutOfMemory异常导致误判为Runtime Error, - # 如果实际使用超过了3倍, 就只能得到Runtime Error的结果了 - # 而最后会比较Java实际使用的内存和1.5倍的设定内存的大小 - self._real_max_memory = max_memory - if self._language["name"] == "java": - self._max_memory = judger.MEMORY_UNLIMITED - self.execute_command = self._language["execute_command"].\ - format(exe_path=self._exe_path, max_memory=max_memory * 3).split(" ") - else: - self._max_memory = self._real_max_memory - self.execute_command = self._language["execute_command"].format(exe_path=self._exe_path).split(" ") - - self._test_case_dir = test_case_dir - # 进程池 - self._pool = Pool(processes=max_running_number) - # 测试用例配置项 - self._test_case_info = self._load_test_case_info() - self._judge_base_path = judge_base_path - self._spj_path = spj_path - - def _load_test_case_info(self): - # 读取测试用例信息 转换为dict - try: - f = open(os.path.join(self._test_case_dir, "info")) - return json.loads(f.read()) - except IOError: - raise JudgeClientError("Test case config file not found") - except ValueError: - raise JudgeClientError("Test case config file format error") - - def _compare_output(self, test_case_id): - test_case_config = self._test_case_info["test_cases"][str(test_case_id)] - output_path = os.path.join(self._judge_base_path, str(test_case_id) + ".out") - - try: - f = open(output_path, "rb") - except IOError: - # 文件不存在等引发的异常 返回结果错误 - return "", False - - if "striped_output_md5" not in test_case_config: - # 计算输出文件的md5 和之前测试用例文件的md5进行比较 - # 兼容之前没有striped_output_md5的测试用例 - # 现在比较的是完整的文件 - md5 = hashlib.md5() - while True: - data = f.read(2 ** 8) - if not data: - break - md5.update(data) - output_md5 = md5.hexdigest() - - return output_md5, output_md5 == test_case_config["output_md5"] - else: - # 这时候需要去除用户输出最后的空格和换行 再去比较md5 - md5 = hashlib.md5() - # 比较和返回去除空格后的md5比较结果 - md5.update(f.read().rstrip()) - output_md5 = md5.hexdigest() - return output_md5, output_md5 == test_case_config["striped_output_md5"] - - def _judge_one(self, test_case_id): - in_file = os.path.join(self._test_case_dir, str(test_case_id) + ".in") - out_file = os.path.join(self._judge_base_path, str(test_case_id) + ".out") - run_result = judger.run(path=self.execute_command[0], - max_cpu_time=self._max_cpu_time, - max_memory=self._max_memory, - in_file=in_file, - out_file=out_file, - args=self.execute_command[1:], - env=["PATH=" + os.environ["PATH"]], - use_sandbox=self._language["use_sandbox"], - use_nobody=True) - run_result["test_case"] = test_case_id - - # 对Java的特殊处理, 详见__init__函数中注释 - if self._language["name"] == "java" and run_result["memory"] > self._real_max_memory * 1.5: - run_result["flag"] = 3 - - # 将judger返回的结果标志转换为本系统中使用的 - if run_result["flag"] == 0: - if self._spj_path is None: - output_md5, r = self._compare_output(test_case_id) - if r: - run_result["result"] = result["accepted"] - else: - run_result["result"] = result["wrong_answer"] - run_result["output_md5"] = output_md5 - else: - spj_result = spj_client.spj(path=self._spj_path, - max_cpu_time=3 * self._max_cpu_time, - max_memory=3 * self._real_max_memory, - in_path=in_file, - user_out_path=out_file) - if spj_result["spj_result"] == spj_client.AC: - run_result["result"] = result["accepted"] - elif spj_result["spj_result"] == spj_client.WA: - run_result["result"] = result["wrong_answer"] - else: - run_result["result"] = result["system_error"] - run_result["error"] = "SPJ Crashed, return: %d, signal: %d" % \ - (spj_result["spj_result"], spj_result["signal"]) - - elif run_result["flag"] in [1, 2]: - run_result["result"] = result["time_limit_exceeded"] - elif run_result["flag"] == 3: - run_result["result"] = result["memory_limit_exceeded"] - elif run_result["flag"] == 4: - run_result["result"] = result["runtime_error"] - elif run_result["flag"] == 5: - run_result["result"] = result["system_error"] - return run_result - - def run(self): - # 添加到任务队列 - _results = [] - results = [] - for i in range(self._test_case_info["test_case_number"]): - _results.append(self._pool.apply_async(_run, (self, i + 1))) - self._pool.close() - self._pool.join() - for item in _results: - # 注意多进程中的异常只有在get()的时候才会被引发 - # http://stackoverflow.com/questions/22094852/how-to-catch-exceptions-in-workers-in-multiprocessing - try: - results.append(item.get()) - except Exception as e: - logger.error("system error") - logger.error(e) - results.append({"result": result["system_error"]}) - return results - - def __getstate__(self): - # 不同的pool之间进行pickle的时候要排除自己,否则报错 - # 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 diff --git a/judge/compiler.py b/judge/compiler.py deleted file mode 100644 index 266b9e83..00000000 --- a/judge/compiler.py +++ /dev/null @@ -1,40 +0,0 @@ -# coding=utf-8 -import os -import judger -from judge_exceptions import CompileError -from logger import logger - - -def compile_(language_item, src_path, exe_path, judge_base_path, compile_spj=False): - command_item = "spj_compile_command" if compile_spj else "compile_command" - compile_command = language_item[command_item].format(src_path=src_path, exe_path=exe_path).split(" ") - compiler = compile_command[0] - compile_args = compile_command[1:] - compiler_output_file = os.path.join(judge_base_path, "compiler.out") - - compile_result = judger.run(path=compiler, - in_file="/dev/null", - out_file=compiler_output_file, - max_cpu_time=language_item["compile_max_cpu_time"], - max_memory=language_item["compile_max_memory"], - args=compile_args, - env=["PATH=" + os.environ["PATH"]], - use_sandbox=False, - use_nobody=True) - - compile_output_handler = open(compiler_output_file) - compile_output = compile_output_handler.read().strip() - compile_output_handler.close() - - if compile_result["flag"] != 0: - logger.error("Compiler error") - logger.error(compile_output) - logger.error(str(compile_result)) - if compile_output: - raise CompileError(compile_output) - else: - raise CompileError("Compile error, info: " + str(compile_result)) - else: - if "error" in compile_output: - raise CompileError(compile_output) - return exe_path diff --git a/judge/judge_exceptions.py b/judge/judge_exceptions.py deleted file mode 100644 index 3a9bc608..00000000 --- a/judge/judge_exceptions.py +++ /dev/null @@ -1,9 +0,0 @@ -# coding=utf-8 - - -class JudgeClientError(Exception): - pass - - -class CompileError(Exception): - pass \ No newline at end of file diff --git a/judge/language.py b/judge/language.py deleted file mode 100644 index 63f0519a..00000000 --- a/judge/language.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding=utf-8 - - -languages = { - 1: { - "name": "c", - "src_name": "main.c", - "code": 1, - "compile_max_cpu_time": 3000, - "compile_max_memory": 128 * 1024 * 1024, - "compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}/main", - "spj_compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -Werror -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}", - "execute_command": "{exe_path}/main", - "use_sandbox": True - }, - 2: { - "name": "cpp", - "src_name": "main.cpp", - "code": 2, - "compile_max_cpu_time": 3000, - "compile_max_memory": 256 * 1024 * 1024, - "compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}/main", - "spj_compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -Werror -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}", - "execute_command": "{exe_path}/main", - "use_sandbox": True - }, - 3: { - "name": "java", - "src_name": "Main.java", - "code": 3, - "compile_max_cpu_time": 3000, - "compile_max_memory": 1024 * 1024 * 1024, - "compile_command": "/usr/bin/javac {src_path} -d {exe_path} -J-Xss1m -J-XX:MaxPermSize=16M " - "-J-XX:PermSize=8M -J-Xms16m -J-Xmx1024m -encoding UTF8", - "execute_command": "/usr/bin/java -cp {exe_path} -Xss1M -XX:MaxPermSize=16M " - "-XX:PermSize=8M -Xms16M -Xmx{max_memory} -Djava.security.manager " - "-Djava.security.policy==policy -Djava.awt.headless=true Main", - "use_sandbox": False - } -} - - diff --git a/judge/logger.py b/judge/logger.py deleted file mode 100644 index c71a5425..00000000 --- a/judge/logger.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 -import logging - -logging.basicConfig(level=logging.DEBUG, - format='%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s', - filename='log/judge.log') - -logger = logging diff --git a/judge/result.py b/judge/result.py deleted file mode 100644 index f42e63fd..00000000 --- a/judge/result.py +++ /dev/null @@ -1,15 +0,0 @@ -# coding=utf-8 - - -# 这个映射关系是前后端通用的,判题服务器提供接口,也应该遵守这个,可能需要一些转换 -result = { - "accepted": 0, - "runtime_error": 1, - "time_limit_exceeded": 2, - "memory_limit_exceeded": 3, - "compile_error": 4, - "format_error": 5, - "wrong_answer": 6, - "system_error": 7, - "waiting": 8 -} diff --git a/judge/runner.py b/judge/runner.py deleted file mode 100644 index cb73fb15..00000000 --- a/judge/runner.py +++ /dev/null @@ -1,92 +0,0 @@ -# coding=utf-8 -import os -import socket -import shutil - -from logger import logger -from client import JudgeClient -from language import languages -from compiler import compile_ -from result import result -from settings import judger_workspace - - -class JudgeInstanceRunner(object): - - def run(self, token, submission_id, language_code, code, time_limit, memory_limit, test_case_id, - spj, spj_language, spj_code, spj_version): - language = languages[language_code] - host_name = socket.gethostname() - judge_base_path = os.path.join(judger_workspace, "run", submission_id) - - if not token or token != os.environ.get("rpc_token"): - if token: - logger.info("Invalid token: " + token) - return {"code": 2, "data": {"error": "Invalid token", "server": host_name}} - - try: - os.mkdir(judge_base_path) - os.chmod(judge_base_path, 0777) - - # 将代码写入文件 - src_path = os.path.join(judge_base_path, language["src_name"]) - f = open(src_path, "w") - f.write(code.encode("utf8")) - f.close() - except Exception as e: - shutil.rmtree(judge_base_path, ignore_errors=True) - return {"code": 2, "data": {"error": str(e), "server": host_name}} - - # 编译 - try: - exe_path = compile_(language_item=language, src_path=src_path, - exe_path=judge_base_path, judge_base_path=judge_base_path, compile_spj=False) - except Exception as e: - shutil.rmtree(judge_base_path, ignore_errors=True) - return {"code": 1, "data": {"error": str(e), "server": host_name}} - - test_case_dir = os.path.join(judger_workspace, "test_case", test_case_id) - - # SPJ相关 - if spj: - spj_path = os.path.join(test_case_dir, "spj-" + spj_version) - if "spj-" + spj_version not in os.listdir(test_case_dir): - spj_language_item = languages[spj_language] - spj_code_path = os.path.join(test_case_dir, "spj-" + spj_language_item["src_name"]) - - f = open(spj_code_path, "w") - f.write(spj_code.encode("utf8")) - f.close() - - try: - compile_(language_item=languages[spj_language], src_path=spj_code_path, - exe_path=spj_path, - judge_base_path=judge_base_path, compile_spj=True) - except Exception as e: - return {"code": 2, "data": {"error": "SPJ Compile error: " + str(e), "server": host_name}} - else: - spj_path = None - - # 运行 - try: - client = JudgeClient(language_code=language_code, - exe_path=exe_path, - max_cpu_time=int(time_limit), - max_memory=int(memory_limit) * 1024 * 1024, - test_case_dir=test_case_dir, - judge_base_path=judge_base_path, spj_path=spj_path) - judge_result = {"result": result["accepted"], "info": client.run(), - "accepted_answer_time": None, "server": host_name} - - for item in judge_result["info"]: - if item["result"] != 0: - judge_result["result"] = item["result"] - break - else: - l = sorted(judge_result["info"], key=lambda k: k["cpu_time"]) - judge_result["accepted_answer_time"] = l[-1]["cpu_time"] - return {"code": 0, "data": judge_result} - except Exception as e: - return {"code": 2, "data": {"error": str(e), "server": host_name}} - finally: - shutil.rmtree(judge_base_path, ignore_errors=True) \ No newline at end of file diff --git a/judge/server.py b/judge/server.py deleted file mode 100644 index 477cc1e4..00000000 --- a/judge/server.py +++ /dev/null @@ -1,13 +0,0 @@ -# coding=utf-8 -import SocketServer -from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler -from runner import JudgeInstanceRunner - - -class AsyncXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer): - pass - - -server = AsyncXMLRPCServer(('0.0.0.0', 8080), SimpleXMLRPCRequestHandler, allow_none=True) -server.register_instance(JudgeInstanceRunner()) -server.serve_forever() \ No newline at end of file diff --git a/judge/settings.py b/judge/settings.py deleted file mode 100644 index 3dd1268e..00000000 --- a/judge/settings.py +++ /dev/null @@ -1,9 +0,0 @@ -# coding=utf-8 -# 单个判题端最多同时运行的程序个数,因为判题端会同时运行多组测试数据,比如一共有5组测试数据 -# 如果MAX_RUNNING_NUMBER大于等于5,那么这5组数据就会同时进行评测,然后返回结果。 -# 如果MAX_RUNNING_NUMBER小于5,为3,那么就会同时运行前三组测试数据,然后再运行后两组数据 -# 这样可以避免同时运行的程序过多导致的cpu占用太高 -max_running_number = 10 - -# judger工作目录 -judger_workspace = "/var/judger/" diff --git a/judge/spj_client.py b/judge/spj_client.py deleted file mode 100644 index 5e24aba1..00000000 --- a/judge/spj_client.py +++ /dev/null @@ -1,26 +0,0 @@ -# coding=utf-8 -import os -import judger - -WA = 1 -AC = 0 -SPJ_ERROR = -1 - - -def file_exists(path): - return os.path.exists(path) - - -def spj(path, max_cpu_time, max_memory, in_path, user_out_path): - if file_exists(in_path) and file_exists(user_out_path): - result = judger.run(path=path, in_file=in_path, out_file="/tmp/spj.out", - max_cpu_time=max_cpu_time, max_memory=max_memory, - args=[in_path, user_out_path], env=["PATH=" + os.environ.get("PATH", "")], - use_sandbox=True, use_nobody=True) - if result["signal"] == 0 and result["exit_status"] in [AC, WA, SPJ_ERROR]: - result["spj_result"] = result["exit_status"] - else: - result["spj_result"] = SPJ_ERROR - return result - else: - raise ValueError("in_path or user_out_path does not exist") \ No newline at end of file diff --git a/judge_dispatcher/__init__.py b/judge_dispatcher/__init__.py deleted file mode 100644 index 9bad5790..00000000 --- a/judge_dispatcher/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# coding=utf-8 diff --git a/judge_dispatcher/migrations/0001_initial.py b/judge_dispatcher/migrations/0001_initial.py deleted file mode 100644 index e85330ed..00000000 --- a/judge_dispatcher/migrations/0001_initial.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='JudgeServer', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('ip', models.GenericIPAddressField()), - ('port', models.IntegerField()), - ('max_instance_number', models.IntegerField()), - ('left_instance_number', models.IntegerField()), - ('workload', models.IntegerField(default=0)), - ('token', models.CharField(max_length=30)), - ('lock', models.BooleanField(default=False)), - ('status', models.BooleanField(default=True)), - ], - options={ - 'db_table': 'judge_server', - }, - ), - migrations.CreateModel( - name='JudgeWaitingQueue', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('submission_id', models.CharField(max_length=40)), - ('create_time', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'db_table': 'judge_waiting_queue', - }, - ), - ] diff --git a/judge_dispatcher/migrations/0002_auto_20151207_2310.py b/judge_dispatcher/migrations/0002_auto_20151207_2310.py deleted file mode 100644 index 1597919c..00000000 --- a/judge_dispatcher/migrations/0002_auto_20151207_2310.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('judge_dispatcher', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='judgewaitingqueue', - name='memory_limit', - field=models.IntegerField(default=1), - preserve_default=False, - ), - migrations.AddField( - model_name='judgewaitingqueue', - name='test_case_id', - field=models.CharField(default=1, max_length=40), - preserve_default=False, - ), - migrations.AddField( - model_name='judgewaitingqueue', - name='time_limit', - field=models.IntegerField(default=1), - preserve_default=False, - ), - ] diff --git a/judge_dispatcher/migrations/0003_auto_20151223_0029.py b/judge_dispatcher/migrations/0003_auto_20151223_0029.py deleted file mode 100644 index fe2e13af..00000000 --- a/judge_dispatcher/migrations/0003_auto_20151223_0029.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('judge_dispatcher', '0002_auto_20151207_2310'), - ] - - operations = [ - migrations.AddField( - model_name='judgeserver', - name='create_time', - field=models.DateTimeField(auto_now_add=True, null=True), - ), - migrations.AddField( - model_name='judgeserver', - name='name', - field=models.CharField(default='judger', max_length=30), - preserve_default=False, - ), - ] diff --git a/judge_dispatcher/migrations/0004_auto_20160407_1816.py b/judge_dispatcher/migrations/0004_auto_20160407_1816.py deleted file mode 100644 index 67a7c2e9..00000000 --- a/judge_dispatcher/migrations/0004_auto_20160407_1816.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-04-07 10:16 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('judge_dispatcher', '0003_auto_20151223_0029'), - ] - - operations = [ - migrations.AddField( - model_name='judgewaitingqueue', - name='spj', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='judgewaitingqueue', - name='spj_code', - field=models.TextField(blank=True, null=True), - ), - migrations.AddField( - model_name='judgewaitingqueue', - name='spj_language', - field=models.IntegerField(blank=True, null=True), - ), - migrations.AddField( - model_name='judgewaitingqueue', - name='spj_version', - field=models.CharField(blank=True, max_length=32, null=True), - ), - ] diff --git a/judge_dispatcher/migrations/0005_auto_20160519_1627.py b/judge_dispatcher/migrations/0005_auto_20160519_1627.py deleted file mode 100644 index 280ce659..00000000 --- a/judge_dispatcher/migrations/0005_auto_20160519_1627.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2016-05-19 08:27 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('judge_dispatcher', '0004_auto_20160407_1816'), - ] - - operations = [ - migrations.RemoveField( - model_name='judgeserver', - name='left_instance_number', - ), - migrations.RemoveField( - model_name='judgeserver', - name='lock', - ), - migrations.RemoveField( - model_name='judgeserver', - name='workload', - ), - migrations.AddField( - model_name='judgeserver', - name='used_instance_number', - field=models.IntegerField(default=0), - ), - ] diff --git a/judge_dispatcher/migrations/__init__.py b/judge_dispatcher/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/judge_dispatcher/models.py b/judge_dispatcher/models.py deleted file mode 100644 index 50b9c08d..00000000 --- a/judge_dispatcher/models.py +++ /dev/null @@ -1,33 +0,0 @@ -# coding=utf-8 -from django.db import models - - -class JudgeServer(models.Model): - name = models.CharField(max_length=30) - ip = models.GenericIPAddressField() - port = models.IntegerField() - # 这个服务器最大可能运行的判题实例数量 - max_instance_number = models.IntegerField() - used_instance_number = models.IntegerField(default=0) - token = models.CharField(max_length=30) - # status 为 false 的时候代表不使用这个服务器 - status = models.BooleanField(default=True) - create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True) - - class Meta: - db_table = "judge_server" - - -class JudgeWaitingQueue(models.Model): - submission_id = models.CharField(max_length=40) - time_limit = models.IntegerField() - memory_limit = models.IntegerField() - test_case_id = models.CharField(max_length=40) - create_time = models.DateTimeField(auto_now_add=True) - spj = models.BooleanField(default=False) - spj_language = models.IntegerField(blank=True, null=True) - spj_code = models.TextField(blank=True, null=True) - spj_version = models.CharField(max_length=32, blank=True, null=True) - - class Meta: - db_table = "judge_waiting_queue" diff --git a/judge_dispatcher/rpc_client.py b/judge_dispatcher/rpc_client.py deleted file mode 100644 index c095cb5e..00000000 --- a/judge_dispatcher/rpc_client.py +++ /dev/null @@ -1,24 +0,0 @@ -# coding=utf-8 -import xmlrpclib -import httplib - - -class TimeoutHTTPConnection(httplib.HTTPConnection): - def __init__(self, host, timeout=10): - httplib.HTTPConnection.__init__(self, host, timeout=timeout) - - -class TimeoutTransport(xmlrpclib.Transport): - def __init__(self, timeout=10, *args, **kwargs): - xmlrpclib.Transport.__init__(self, *args, **kwargs) - self.timeout = timeout - - def make_connection(self, host): - conn = TimeoutHTTPConnection(host, self.timeout) - return conn - - -class TimeoutServerProxy(xmlrpclib.ServerProxy): - def __init__(self, uri, timeout=10, *args, **kwargs): - kwargs['transport'] = TimeoutTransport(timeout=timeout, use_datetime=kwargs.get('use_datetime', 0)) - xmlrpclib.ServerProxy.__init__(self, uri, *args, **kwargs) diff --git a/judge_dispatcher/serializers.py b/judge_dispatcher/serializers.py deleted file mode 100644 index 4328eee9..00000000 --- a/judge_dispatcher/serializers.py +++ /dev/null @@ -1,29 +0,0 @@ -# coding=utf-8 -import json -from rest_framework import serializers -from .models import JudgeServer - - -class CreateJudgesSerializer(serializers.Serializer): - name = serializers.CharField(max_length=30) - ip = serializers.IPAddressField() - port = serializers.IntegerField() - # 这个服务器最大可能运行的判题实例数量 - max_instance_number = serializers.IntegerField() - token = serializers.CharField(max_length=30) - - -class EditJudgesSerializer(serializers.Serializer): - id = serializers.IntegerField() - name = serializers.CharField(max_length=30) - ip = serializers.IPAddressField() - port = serializers.IntegerField() - # 这个服务器最大可能运行的判题实例数量 - max_instance_number = serializers.IntegerField() - token = serializers.CharField(max_length=30) - status = serializers.BooleanField() - - -class JudgesSerializer(serializers.ModelSerializer): - class Meta: - model = JudgeServer diff --git a/judge_dispatcher/tasks.py b/judge_dispatcher/tasks.py deleted file mode 100644 index 5fab390f..00000000 --- a/judge_dispatcher/tasks.py +++ /dev/null @@ -1,199 +0,0 @@ -# coding=utf-8 -import json -import logging -import time - -from django.db import transaction -from django.db.models import F - -from rpc_client import TimeoutServerProxy - -from judge.result import result -from contest.models import ContestProblem, ContestRank, Contest, CONTEST_UNDERWAY -from problem.models import Problem -from submission.models import Submission -from account.models import User -from utils.cache import get_cache_redis - -from .models import JudgeServer, JudgeWaitingQueue - -logger = logging.getLogger("app_info") - - -class JudgeDispatcher(object): - def _none_to_false(self, value): - # xml rpc不能使用None - if value is None: - return False - else: - return value - - def __init__(self, submission_id, time_limit, memory_limit, test_case_id, spj, spj_language, spj_code, spj_version): - self.submission = Submission.objects.get(id=submission_id) - self.time_limit = time_limit - self.memory_limit = memory_limit - self.test_case_id = test_case_id - self.spj = spj - self.spj_language = spj_language - self.spj_code = spj_code - self.spj_version = spj_version - - def choose_judge_server(self): - with transaction.atomic(): - servers = JudgeServer.objects.select_for_update().filter(used_instance_number__lt=F("max_instance_number"), status=True).order_by("max_instance_number") - if servers.exists(): - server = servers.first() - server.used_instance_number = F("used_instance_number") + 1 - server.save() - return server - - def release_judge_instance(self, judge_server_id): - with transaction.atomic(): - # 使用原子操作, 同时因为use和release中间间隔了判题过程,需要重新查询一下 - server = JudgeServer.objects.select_for_update().get(id=judge_server_id) - server.used_instance_number = F("used_instance_number") - 1 - server.save() - - def judge(self): - self.submission.judge_start_time = int(time.time() * 1000) - - judge_server = self.choose_judge_server() - - # 如果没有合适的判题服务器,就放入等待队列中等待判题 - if not judge_server: - JudgeWaitingQueue.objects.create(submission_id=self.submission.id, time_limit=self.time_limit, - memory_limit=self.memory_limit, test_case_id=self.test_case_id, - spj=self.spj, spj_language=self.spj_language, spj_code=self.spj_code, - spj_version=self.spj_version) - return - - try: - s = TimeoutServerProxy("http://" + judge_server.ip + ":" + str(judge_server.port), - timeout=30) - - data = s.run(judge_server.token, self.submission.id, self.submission.language, - self.submission.code, self.time_limit, self.memory_limit, self.test_case_id, - self.spj, self._none_to_false(self.spj_language), - self._none_to_false(self.spj_code), self._none_to_false(self.spj_version)) - # 编译错误 - if data["code"] == 1: - self.submission.result = result["compile_error"] - self.submission.info = data["data"]["error"] - # system error - elif data["code"] == 2: - self.submission.result = result["system_error"] - self.submission.info = data["data"]["error"] - elif data["code"] == 0: - self.submission.result = data["data"]["result"] - self.submission.info = json.dumps(data["data"]["info"]) - self.submission.accepted_answer_time = data["data"]["accepted_answer_time"] - except Exception as e: - self.submission.result = result["system_error"] - self.submission.info = str(e) - finally: - self.release_judge_instance(judge_server.id) - - self.submission.judge_end_time = int(time.time() * 1000) - self.submission.save(update_fields=["judge_start_time", "result", "info", "accepted_answer_time", "judge_end_time"]) - - if self.submission.contest_id: - self.update_contest_problem_status() - else: - self.update_problem_status() - - with transaction.atomic(): - waiting_submissions = JudgeWaitingQueue.objects.select_for_update().all() - if waiting_submissions.exists(): - # 防止循环依赖 - from submission.tasks import _judge - - waiting_submission = waiting_submissions.first() - waiting_submission.delete() - _judge.delay(submission_id=waiting_submission.submission_id, - time_limit=waiting_submission.time_limit, - memory_limit=waiting_submission.memory_limit, - test_case_id=waiting_submission.test_case_id, - spj=waiting_submission.spj, - spj_language=waiting_submission.spj_language, - spj_code=waiting_submission.spj_code, - spj_version=waiting_submission.spj_version) - - def update_problem_status(self): - with transaction.atomic(): - problem = Problem.objects.select_for_update().get(id=self.submission.problem_id) - # 更新普通题目的计数器 - problem.add_submission_number() - - # 更新用户做题状态 - user = User.objects.select_for_update().get(id=self.submission.user_id) - - problems_status = user.problems_status - if "problems" not in problems_status: - problems_status["problems"] = {} - - # 增加用户提交计数器 - user.userprofile.add_submission_number() - - # 之前状态不是ac, 现在是ac了 需要更新用户ac题目数量计数器,这里需要判重 - if problems_status["problems"].get(str(problem.id), -1) != 1 and self.submission.result == result["accepted"]: - user.userprofile.add_accepted_problem_number() - - # 之前状态是ac, 现在不是ac了 需要用户ac题目数量计数器-1, 否则上一个逻辑胡重复增加ac计数器 - if problems_status["problems"].get(str(problem.id), -1) == 1 and self.submission.result != result["accepted"]: - user.userprofile.minus_accepted_problem_number() - - if self.submission.result == result["accepted"]: - problem.add_ac_number() - problems_status["problems"][str(problem.id)] = 1 - else: - problems_status["problems"][str(problem.id)] = 2 - user.problems_status = problems_status - user.save(update_fields=["problems_status"]) - # 普通题目的话,到这里就结束了 - - def update_contest_problem_status(self): - # 能运行到这里的都是比赛题目 - contest = Contest.objects.get(id=self.submission.contest_id) - if contest.status != CONTEST_UNDERWAY: - logger.info("Contest debug mode, id: " + str(contest.id) + ", submission id: " + self.submission.id) - return - - with transaction.atomic(): - contest_problem = ContestProblem.objects.select_for_update().get(contest=contest, id=self.submission.problem_id) - contest_problem.add_submission_number() - - user = User.objects.select_for_update().get(id=self.submission.user_id) - problems_status = user.problems_status - - if "contest_problems" not in problems_status: - problems_status["contest_problems"] = {} - - # 增加用户提交计数器 - user.userprofile.add_submission_number() - - # 之前状态不是ac, 现在是ac了 需要更新用户ac题目数量计数器,这里需要判重 - if problems_status["contest_problems"].get(str(contest_problem.id), -1) != 1 and \ - self.submission.result == result["accepted"]: - user.userprofile.add_accepted_problem_number() - - if self.submission.result == result["accepted"]: - contest_problem.add_ac_number() - problems_status["contest_problems"][str(contest_problem.id)] = 1 - else: - problems_status["contest_problems"][str(contest_problem.id)] = 2 - - user.problems_status = problems_status - user.save(update_fields=["problems_status"]) - - self.update_contest_rank(contest) - - def update_contest_rank(self, contest): - if contest.real_time_rank: - get_cache_redis().delete(str(contest.id) + "_rank_cache") - - with transaction.atomic(): - try: - contest_rank = ContestRank.objects.select_for_update().get(contest=contest, user_id=self.submission.user_id) - contest_rank.update_rank(self.submission) - except ContestRank.DoesNotExist: - ContestRank.objects.create(contest=contest, user_id=self.submission.user_id).update_rank(self.submission) diff --git a/judge_dispatcher/views.py b/judge_dispatcher/views.py deleted file mode 100644 index dfcfdfa0..00000000 --- a/judge_dispatcher/views.py +++ /dev/null @@ -1,70 +0,0 @@ -# coding=utf-8 -from rest_framework.views import APIView - -from account.decorators import super_admin_required -from utils.shortcuts import success_response, serializer_invalid_response, error_response, paginate -from .serializers import CreateJudgesSerializer, JudgesSerializer, EditJudgesSerializer -from .models import JudgeServer - - -class AdminJudgeServerAPIView(APIView): - @super_admin_required - def post(self, request): - """ - 添加判题服务器 json api接口 - --- - request_serializer: CreateJudgesSerializer - response_serializer: JudgesSerializer - """ - serializer = CreateJudgesSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - judge_server = JudgeServer.objects.create(name=data["name"], ip=data["ip"], port=data["port"], - max_instance_number=data["max_instance_number"], - token=data["token"]) - return success_response(JudgesSerializer(judge_server).data) - else: - return serializer_invalid_response(serializer) - - @super_admin_required - def put(self, request): - """ - 修改判题服务器信息 json api接口 - --- - request_serializer: EditJudgesSerializer - response_serializer: JudgesSerializer - """ - serializer = EditJudgesSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - judge_server = JudgeServer.objects.get(pk=data["id"]) - except JudgeServer.DoesNotExist: - return error_response(u"此判题服务器不存在!") - - judge_server.name = data["name"] - judge_server.ip = data["ip"] - judge_server.port = data["port"] - judge_server.max_instance_number = data["max_instance_number"] - judge_server.token = data["token"] - judge_server.status = data["status"] - judge_server.save() - return success_response(JudgesSerializer(judge_server).data) - else: - return serializer_invalid_response(serializer) - - @super_admin_required - def get(self, request): - """ - 获取全部判题服务器 - """ - judge_server_id = request.GET.get("judge_server_id", None) - if judge_server_id: - try: - judge_server = JudgeServer.objects.get(id=judge_server_id) - except JudgeServer.DoesNotExist: - return error_response(u"判题服务器不存在") - return success_response(JudgesSerializer(judge_server).data) - judge_server = JudgeServer.objects.all() - - return paginate(request, judge_server, JudgesSerializer) diff --git a/manage.py b/manage.py index 31881f3b..066fc698 100755 --- a/manage.py +++ b/manage.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import unicode_literals import os import sys @@ -6,5 +7,7 @@ if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oj.settings") from django.core.management import execute_from_command_line + import django + sys.stdout.write("Django VERSION " + str(django.VERSION) + "\n") execute_from_command_line(sys.argv) diff --git a/oj/settings.py b/oj/settings.py index 64eee69d..697bc1fa 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -42,13 +42,6 @@ INSTALLED_APPS = ( 'account', 'announcement', 'utils', - 'group', - 'problem', - 'admin', - 'submission', - 'contest', - 'judge', - 'judge_dispatcher', 'rest_framework', ) @@ -62,7 +55,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', - 'admin.middleware.AdminRequiredMiddleware', + 'account.middleware.AdminRequiredMiddleware', 'account.middleware.SessionSecurityMiddleware' ) diff --git a/oj/urls.py b/oj/urls.py index eb76850f..13351cdb 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -1,147 +1,8 @@ # coding=utf-8 -from django.conf import settings from django.conf.urls import include, url -from django.views.generic import TemplateView - -from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, - UserChangePasswordAPIView, EmailCheckAPIView, - UserAdminAPIView, UserInfoAPIView, ResetPasswordAPIView, - ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView, - TwoFactorAuthAPIView, AvatarUploadAPIView) - -from announcement.views import AnnouncementAdminAPIView - -from contest.views import (ContestAdminAPIView, ContestProblemAdminAPIView, - ContestPasswordVerifyAPIView, ContestTimeAPIView, - MakeContestProblemPublicAPIView) - -from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, - JoinGroupAPIView, JoinGroupRequestAdminAPIView, GroupPrometAdminAPIView) - -from admin.views import AdminTemplateView - -from problem.views import TestCaseUploadAPIView, TestCaseDownloadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView, OpenAPIProblemAPI -from submission.views import (SubmissionAPIView, SubmissionAdminAPIView, ContestSubmissionAPIView, - SubmissionShareAPIView, SubmissionRejudgeAdminAPIView, OpenAPISubmitCodeAPI) -from judge_dispatcher.views import AdminJudgeServerAPIView -from utils.views import SimditorImageUploadAPIView urlpatterns = [ - url("^$", "account.views.index_page", name="index_page"), - - url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"), - url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), - name="add_contest_page"), - url(r'^admin/template/(?P\w+)/(?P\w+).html$', AdminTemplateView.as_view(), - name="admin_template"), - - # account app - url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), - url(r'^logout/$', "account.views.logout", name="user_logout_api"), - 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'^announcement/(?P\d+)/$', "announcement.views.announcement_page", - name="announcement_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"), - url(r'^api/username_check/$', UsernameCheckAPIView.as_view(), name="username_check_api"), - url(r'^api/email_check/$', EmailCheckAPIView.as_view(), name="email_check_api"), - - - 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/upload_image/$', SimditorImageUploadAPIView.as_view(), name="simditor_upload_image"), - 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/promot_as_admin/$', GroupPrometAdminAPIView.as_view(), name="group_promote_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/contest_problem/public/', MakeContestProblemPublicAPIView.as_view(), - name="make_contest_problem_public"), - url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"), - url(r'^api/admin/test_case_download/$', TestCaseDownloadAPIView.as_view(), name="test_case_download_api"), - url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), - url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), - name="join_group_request_admin_api"), - url(r'^api/admin/submission/$', SubmissionAdminAPIView.as_view(), name="submission_admin_api_view"), - - url(r'^api/admin/judges/$', AdminJudgeServerAPIView.as_view(), name="judges_admin_api"), - - url(r'^contest/(?P\d+)/problem/(?P\d+)/$', "contest.views.contest_problem_page", - name="contest_problem_page"), - url(r'^contest/(?P\d+)/problem/(?P\d+)/submissions/$', - "contest.views.contest_problem_my_submissions_list_page", - name="contest_problem_my_submissions_list_page"), - - url(r'^contest/(?P\d+)/$', "contest.views.contest_page", name="contest_page"), - url(r'^contest/(?P\d+)/problems/$', "contest.views.contest_problems_list_page", - name="contest_problems_list_page"), - url(r'^contest/(?P\d+)/submissions/$', "contest.views.contest_problem_submissions_list_page", - name="contest_problem_submissions_list_page"), - url(r'^contest/(?P\d+)/submissions/(?P\d+)/$', - "contest.views.contest_problem_submissions_list_page", name="contest_problem_submissions_list_page"), - - url(r'^contests/$', "contest.views.contest_list_page", name="contest_list_page"), - url(r'^contests/(?P\d+)/$', "contest.views.contest_list_page", name="contest_list_page"), - - url(r'^api/open/problem/$', OpenAPIProblemAPI.as_view(), name="openapi_problem_api"), - - url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), - url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"), - url(r'^problems/(?P\d+)/$', "problem.views.problem_list_page", name="problem_list_page"), - url(r'^problem/(?P\d+)/submissions/$', "submission.views.problem_my_submissions_list_page", - name="problem_my_submissions_page"), - - url(r'^api/open/submission/$', OpenAPISubmitCodeAPI.as_view(), name="openapi_submit_code"), - - url(r'^submission/(?P\w+)/$', "submission.views.my_submission", name="my_submission_page"), - url(r'^submissions/$', "submission.views.submission_list_page", name="submission_list_page"), - url(r'^submissions/(?P\d+)/$', "submission.views.submission_list_page", name="my_submission_list_page"), - - url(r'^contest/(?P\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\d+)/$', "group.views.group_list_page", name="group_list_page"), - url(r'^group/(?P\d+)/$', "group.views.group_page", name="group_page"), - url(r'^group/(?P\d+)/applications/$', "group.views.application_list_page", name="group_application_page"), - url(r'^group/application/(?P\d+)/$', "group.views.application_page", name="group_application"), - - url(r'^about/$', TemplateView.as_view(template_name="utils/about.html"), name="about_page"), - url(r'^help/$', TemplateView.as_view(template_name="utils/help.html"), name="help_page"), - - url(r'^api/submission/share/$', SubmissionShareAPIView.as_view(), name="submission_share_api"), - - url(r'^captcha/$', "utils.captcha.views.show_captcha", name="show_captcha"), - - url(r'^api/contest/time/$', ContestTimeAPIView.as_view(), name="contest_time_api_view"), - url(r'^api/admin/rejudge/$', SubmissionRejudgeAdminAPIView.as_view(), name="submission_rejudge_api"), - - url(r'^user/(?P.+)/$', "account.views.user_index_page"), - - url(r'^api/apply_reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"), - url(r'^api/reset_password/$', ResetPasswordAPIView.as_view(), name="apply_reset_password_api"), - url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), - name="account_setting_page"), - url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), - name="avatar_settings_page"), - url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"), - url(r'^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_api"), - url(r'^reset_password/$', TemplateView.as_view(template_name="oj/account/apply_reset_password.html"), name="apply_reset_password_page"), - url(r'^reset_password/t/(?P\w+)/$', "account.views.reset_password_page", name="reset_password_page"), - url(r'^api/two_factor_auth/$', TwoFactorAuthAPIView.as_view(), name="two_factor_auth_api"), - url(r'^two_factor_auth/$', TemplateView.as_view(template_name="oj/account/two_factor_auth.html"), name="two_factor_auth_page"), - url(r'^rank/(?P\d+)/$', "account.views.user_rank_page", name="user_rank_page"), - url(r'^rank/$', "account.views.user_rank_page", name="user_rank_page"), - url(r'^api/avatar/upload/', AvatarUploadAPIView.as_view(), name="avatar_upload_api"), + url(r'^api/', include("account.urls.oj")), + url(r'^api/admin/account/', include("account.urls.admin")), + url(r'^api/admin/announcement/', include("announcement.urls.admin")), ] diff --git a/problem/__init__.py b/problem/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/problem/decorators.py b/problem/decorators.py deleted file mode 100644 index cc2ac2a4..00000000 --- a/problem/decorators.py +++ /dev/null @@ -1,32 +0,0 @@ -# coding=utf-8 -from functools import wraps - -from account.models import SUPER_ADMIN -from utils.shortcuts import error_response -from .models import Problem - - -def check_user_problem_permission(func): - @wraps(func) - def check(*args, **kwargs): - # 在class based views 里面,args 有两个元素,一个是self, 第二个才是request, - # 在function based views 里面,args 只有request 一个参数 - if len(args) == 2: - request = args[-1] - else: - request = args[0] - - # 这是在后台使用的url middleware 已经确保用户是登录状态的了 - try: - problem = Problem.objects.get(id=request.data.get("id", -1)) - except Problem.DoesNotExist: - return error_response(u"问题不存在") - - if request.user.admin_type == SUPER_ADMIN: - return func(*args, **kwargs) - else: - if problem.created_by != request.user: - return error_response(u"问题不存在") - return func(*args, **kwargs) - - return check diff --git a/problem/migrations/0001_initial.py b/problem/migrations/0001_initial.py deleted file mode 100644 index 05c38f22..00000000 --- a/problem/migrations/0001_initial.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Problem', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=50)), - ('description', models.TextField()), - ('sample', models.TextField(blank=True)), - ('test_case_id', models.CharField(max_length=40)), - ('hint', models.TextField(null=True, blank=True)), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('last_update_time', models.DateTimeField(auto_now=True)), - ('source', models.CharField(max_length=30, null=True, blank=True)), - ('time_limit', models.IntegerField()), - ('memory_limit', models.IntegerField()), - ('visible', models.BooleanField(default=True)), - ('total_submit_number', models.IntegerField(default=0)), - ('total_accepted_number', models.IntegerField(default=0)), - ('difficulty', models.IntegerField()), - ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='ProblemTag', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=30)), - ('description', models.CharField(max_length=50)), - ], - options={ - 'db_table': 'problem_tag', - }, - ), - migrations.AddField( - model_name='problem', - name='tags', - field=models.ManyToManyField(to='problem.ProblemTag', null=True), - ), - ] diff --git a/problem/migrations/0002_remove_problemtag_description.py b/problem/migrations/0002_remove_problemtag_description.py deleted file mode 100644 index 2953e680..00000000 --- a/problem/migrations/0002_remove_problemtag_description.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='problemtag', - name='description', - ), - ] diff --git a/problem/migrations/0003_auto_20150810_2233.py b/problem/migrations/0003_auto_20150810_2233.py deleted file mode 100644 index 709633fe..00000000 --- a/problem/migrations/0003_auto_20150810_2233.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0002_remove_problemtag_description'), - ] - - operations = [ - migrations.RenameField( - model_name='problem', - old_name='sample', - new_name='samples', - ), - ] diff --git a/problem/migrations/0004_auto_20150812_2254.py b/problem/migrations/0004_auto_20150812_2254.py deleted file mode 100644 index 75f85d4a..00000000 --- a/problem/migrations/0004_auto_20150812_2254.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- 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/0004_auto_20150813_1459.py b/problem/migrations/0004_auto_20150813_1459.py deleted file mode 100644 index 387a87c6..00000000 --- a/problem/migrations/0004_auto_20150813_1459.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- 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.AddField( - model_name='problem', - name='description_input', - field=models.CharField(default='hello', max_length=10000), - preserve_default=False, - ), - migrations.AddField( - model_name='problem', - name='description_output', - field=models.CharField(default='hello', max_length=10000), - preserve_default=False, - ), - ] diff --git a/problem/migrations/0005_auto_20150813_1807.py b/problem/migrations/0005_auto_20150813_1807.py deleted file mode 100644 index 7d50f750..00000000 --- a/problem/migrations/0005_auto_20150813_1807.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0004_auto_20150813_1459'), - ] - - operations = [ - migrations.RenameField( - model_name='problem', - old_name='description_input', - new_name='input_description', - ), - migrations.RenameField( - model_name='problem', - old_name='description_output', - new_name='output_description', - ), - ] diff --git a/problem/migrations/0006_merge.py b/problem/migrations/0006_merge.py deleted file mode 100644 index f24e12dd..00000000 --- a/problem/migrations/0006_merge.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- 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/migrations/0007_remove_problem_last_update_time.py b/problem/migrations/0007_remove_problem_last_update_time.py deleted file mode 100644 index eac32527..00000000 --- a/problem/migrations/0007_remove_problem_last_update_time.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0006_merge'), - ] - - operations = [ - migrations.RemoveField( - model_name='problem', - name='last_update_time', - ), - ] diff --git a/problem/migrations/0008_auto_20150922_1702.py b/problem/migrations/0008_auto_20150922_1702.py deleted file mode 100644 index ae499a0f..00000000 --- a/problem/migrations/0008_auto_20150922_1702.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0007_remove_problem_last_update_time'), - ] - - operations = [ - migrations.AlterField( - model_name='problem', - name='description', - field=utils.models.RichTextField(), - ), - ] diff --git a/problem/migrations/0009_auto_20151008_1125.py b/problem/migrations/0009_auto_20151008_1125.py deleted file mode 100644 index 7a228e5d..00000000 --- a/problem/migrations/0009_auto_20151008_1125.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0008_auto_20150922_1702'), - ] - - operations = [ - migrations.AlterModelTable( - name='problem', - table='problem', - ), - ] diff --git a/problem/migrations/0010_auto_20151017_1226.py b/problem/migrations/0010_auto_20151017_1226.py deleted file mode 100644 index 743497d0..00000000 --- a/problem/migrations/0010_auto_20151017_1226.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0009_auto_20151008_1125'), - ] - - operations = [ - migrations.AddField( - model_name='problem', - name='last_update_time', - field=models.DateTimeField(null=True, blank=True), - ), - migrations.AlterField( - model_name='problem', - name='source', - field=models.CharField(max_length=200, null=True, blank=True), - ), - ] diff --git a/problem/migrations/0011_auto_20151017_1227.py b/problem/migrations/0011_auto_20151017_1227.py deleted file mode 100644 index ae8b7d0d..00000000 --- a/problem/migrations/0011_auto_20151017_1227.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0010_auto_20151017_1226'), - ] - - operations = [ - migrations.AlterField( - model_name='problem', - name='hint', - field=utils.models.RichTextField(null=True, blank=True), - ), - ] diff --git a/problem/migrations/0012_auto_20160404_1509.py b/problem/migrations/0012_auto_20160404_1509.py deleted file mode 100644 index 260f8bfb..00000000 --- a/problem/migrations/0012_auto_20160404_1509.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-04-04 07:09 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0011_auto_20151017_1227'), - ] - - operations = [ - migrations.AddField( - model_name='problem', - name='spj', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='problem', - name='spj_code', - field=models.TextField(blank=True, null=True), - ), - migrations.AddField( - model_name='problem', - name='spj_code_language', - field=models.IntegerField(blank=True, null=True), - ), - ] diff --git a/problem/migrations/0013_auto_20160404_1641.py b/problem/migrations/0013_auto_20160404_1641.py deleted file mode 100644 index 23f9e508..00000000 --- a/problem/migrations/0013_auto_20160404_1641.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-04-04 08:41 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0012_auto_20160404_1509'), - ] - - operations = [ - migrations.RenameField( - model_name='problem', - old_name='spj_code_language', - new_name='spj_language', - ), - ] diff --git a/problem/migrations/0014_problem_spj_version.py b/problem/migrations/0014_problem_spj_version.py deleted file mode 100644 index 92bf6a46..00000000 --- a/problem/migrations/0014_problem_spj_version.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-04-06 04:20 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('problem', '0013_auto_20160404_1641'), - ] - - operations = [ - migrations.AddField( - model_name='problem', - name='spj_version', - field=models.CharField(blank=True, max_length=32, null=True), - ), - ] diff --git a/problem/migrations/__init__.py b/problem/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/problem/models.py b/problem/models.py deleted file mode 100644 index 008b50a9..00000000 --- a/problem/models.py +++ /dev/null @@ -1,72 +0,0 @@ -# coding=utf-8 -from django.db import models - -from account.models import User -from utils.models import RichTextField - - -class ProblemTag(models.Model): - name = models.CharField(max_length=30) - - class Meta: - db_table = "problem_tag" - - -class AbstractProblem(models.Model): - # 标题 - title = models.CharField(max_length=50) - # 问题描述 HTML 格式 - description = RichTextField() - # 输入描述 - input_description = models.CharField(max_length=10000) - # 输出描述 - output_description = models.CharField(max_length=10000) - # 样例输入 可能会存储 json 格式的数据 - samples = models.TextField(blank=True) - # 测试用例id 这个id 可以用来拼接得到测试用例的文件存储位置 - test_case_id = models.CharField(max_length=40) - # 提示 - hint = RichTextField(blank=True, null=True) - # 创建时间 - create_time = models.DateTimeField(auto_now_add=True) - # 最后更新时间,不适用auto_now,因为本 model 里面的提交数量是变化的,导致每次 last_update_time 也更新了 - # 需要每次编辑后手动赋值 - last_update_time = models.DateTimeField(blank=True, null=True) - # 这个题是谁创建的 - created_by = models.ForeignKey(User) - # 时间限制 单位是毫秒 - time_limit = models.IntegerField() - # 内存限制 单位是MB - memory_limit = models.IntegerField() - # special judge - spj = models.BooleanField(default=False) - spj_language = models.IntegerField(blank=True, null=True) - spj_code = models.TextField(blank=True, null=True) - spj_version = models.CharField(max_length=32, blank=True, null=True) - # 是否可见 false的话相当于删除 - visible = models.BooleanField(default=True) - # 总共提交数量 - total_submit_number = models.IntegerField(default=0) - # 通过数量 - total_accepted_number = models.IntegerField(default=0) - - class Meta: - db_table = "problem" - abstract = True - - def add_submission_number(self): - self.total_submit_number += 1 - self.save(update_fields=["total_submit_number"]) - - def add_ac_number(self): - self.total_accepted_number += 1 - self.save(update_fields=["total_accepted_number"]) - - -class Problem(AbstractProblem): - # 难度 0 - n - difficulty = models.IntegerField() - # 标签 - tags = models.ManyToManyField(ProblemTag) - # 来源 - source = models.CharField(max_length=200, blank=True, null=True) diff --git a/problem/serizalizers.py b/problem/serizalizers.py deleted file mode 100644 index 6cdd515d..00000000 --- a/problem/serizalizers.py +++ /dev/null @@ -1,80 +0,0 @@ -# coding=utf-8 -from rest_framework import serializers - -from account.models import User -from utils.serializers import JSONField -from .models import Problem, ProblemTag - - -class ProblemSampleSerializer(serializers.ListField): - input = serializers.CharField(max_length=3000) - output = serializers.CharField(max_length=3000) - - -class CreateProblemSerializer(serializers.Serializer): - title = serializers.CharField(max_length=50) - description = serializers.CharField(max_length=10000) - input_description = serializers.CharField(max_length=10000) - output_description = serializers.CharField(max_length=10000) - # [{"input": "1 1", "output": "2"}] - samples = ProblemSampleSerializer() - test_case_id = serializers.CharField(max_length=40) - time_limit = serializers.IntegerField(min_value=1, max_value=10000) - memory_limit = serializers.IntegerField(min_value=16) - spj = serializers.BooleanField() - spj_language = serializers.IntegerField(required=False, default=None) - spj_code = serializers.CharField(max_length=10000, required=False, default=None) - difficulty = serializers.IntegerField() - tags = serializers.ListField(child=serializers.CharField(max_length=10)) - hint = serializers.CharField(max_length=3000, allow_blank=True) - source = serializers.CharField(max_length=100, required=False, default=None) - visible = serializers.BooleanField() - - -class ProblemTagSerializer(serializers.ModelSerializer): - class Meta: - model = ProblemTag - - -class BaseProblemSerializer(serializers.ModelSerializer): - samples = JSONField() - tags = ProblemTagSerializer(many=True) - - class UserSerializer(serializers.ModelSerializer): - class Meta: - model = User - fields = ["username"] - - created_by = UserSerializer() - - -class ProblemSerializer(BaseProblemSerializer): - class Meta: - model = Problem - - -class OpenAPIProblemSerializer(BaseProblemSerializer): - - class Meta: - model = Problem - exclude = ["visible", "test_case_id", "spj_language", "spj_code", "spj_version"] - - -class EditProblemSerializer(serializers.Serializer): - id = serializers.IntegerField() - title = serializers.CharField(max_length=50) - description = serializers.CharField(max_length=10000) - input_description = serializers.CharField(max_length=10000) - output_description = serializers.CharField(max_length=10000) - test_case_id = serializers.CharField(max_length=40) - source = serializers.CharField(max_length=100) - time_limit = serializers.IntegerField(min_value=1) - memory_limit = serializers.IntegerField(min_value=1) - spj = serializers.BooleanField() - spj_language = serializers.IntegerField(required=False, default=None) - spj_code = serializers.CharField(max_length=10000, required=False, default=None) - difficulty = serializers.IntegerField() - tags = serializers.ListField(child=serializers.CharField(max_length=20)) - samples = ProblemSampleSerializer() - hint = serializers.CharField(max_length=3000, allow_blank=True) - visible = serializers.BooleanField() \ No newline at end of file diff --git a/problem/tests.py b/problem/tests.py deleted file mode 100644 index e69de29b..00000000 diff --git a/problem/views.py b/problem/views.py deleted file mode 100644 index 7fc351c1..00000000 --- a/problem/views.py +++ /dev/null @@ -1,449 +0,0 @@ -# coding=utf-8 -import zipfile -import re -import os -import hashlib -import json -import logging - -from django.http import StreamingHttpResponse -from django.shortcuts import render -from django.db.models import Q, Count -from django.core.paginator import Paginator -from django.utils.timezone import now -from django.conf import settings -from rest_framework.views import APIView - -from account.models import SUPER_ADMIN, User -from account.decorators import super_admin_required, admin_required -from contest.models import ContestProblem -from utils.shortcuts import (serializer_invalid_response, error_response, - success_response, paginate, rand_str, error_page) -from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer, - ProblemTagSerializer, OpenAPIProblemSerializer) -from .models import Problem, ProblemTag -from .decorators import check_user_problem_permission - -logger = logging.getLogger("app_info") - - -def problem_page(request, problem_id): - """ - 前台题目详情页 - """ - try: - problem = Problem.objects.get(id=problem_id, visible=True) - except Problem.DoesNotExist: - return error_page(request, u"题目不存在") - return render(request, "oj/problem/problem.html", {"problem": problem, "samples": json.loads(problem.samples)}) - - -class OpenAPIProblemAPI(APIView): - def get(sell, request): - """ - openapi 获取题目内容 - """ - problem_id = request.GET.get("problem_id", None) - appkey = request.GET.get("appkey", None) - if not (problem_id and appkey): - return error_response(u"参数错误") - try: - User.objects.get(openapi_appkey=appkey) - except User.DoesNotExist: - return error_response(u"appkey无效") - try: - problem = Problem.objects.get(id=problem_id, visible=True) - except Problem.DoesNotExist: - return error_page(request, u"题目不存在") - return success_response(OpenAPIProblemSerializer(problem).data) - - -class ProblemTagAdminAPIView(APIView): - """ - 获取所有标签的列表 - """ - - def get(self, request): - return success_response(ProblemTagSerializer(ProblemTag.objects.all(), many=True).data) - - -class ProblemAdminAPIView(APIView): - def _spj_version(self, code): - if code is None: - return None - return hashlib.md5(code.encode("utf-8")).hexdigest() - - @super_admin_required - def post(self, request): - """ - 题目发布json api接口 - --- - request_serializer: CreateProblemSerializer - response_serializer: ProblemSerializer - """ - serializer = CreateProblemSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - Problem.objects.get(title=data["title"]) - return error_response(u"添加失败,存在重复的题目") - except Problem.DoesNotExist: - pass - problem = Problem.objects.create(title=data["title"], - description=data["description"], - input_description=data["input_description"], - output_description=data["output_description"], - test_case_id=data["test_case_id"], - source=data["source"], - samples=json.dumps(data["samples"]), - time_limit=data["time_limit"], - memory_limit=data["memory_limit"], - spj=data["spj"], - spj_language=data["spj_language"], - spj_code=data["spj_code"], - spj_version=self._spj_version(data["spj_code"]), - difficulty=data["difficulty"], - created_by=request.user, - hint=data["hint"], - visible=data["visible"]) - for tag in data["tags"]: - try: - tag = ProblemTag.objects.get(name=tag) - except ProblemTag.DoesNotExist: - tag = ProblemTag.objects.create(name=tag) - problem.tags.add(tag) - return success_response(ProblemSerializer(problem).data) - else: - return serializer_invalid_response(serializer) - - @check_user_problem_permission - def put(self, request): - """ - 题目编辑json api接口 - --- - request_serializer: EditProblemSerializer - response_serializer: ProblemSerializer - """ - serializer = EditProblemSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - problem = Problem.objects.get(id=data["id"]) - - problem.title = data["title"] - problem.description = data["description"] - problem.input_description = data["input_description"] - problem.output_description = data["output_description"] - problem.test_case_id = data["test_case_id"] - problem.source = data["source"] - problem.time_limit = data["time_limit"] - problem.memory_limit = data["memory_limit"] - problem.spj = data["spj"] - problem.spj_language = data["spj_language"] - problem.spj_code = data["spj_code"] - problem.spj_version = self._spj_version(data["spj_code"]) - problem.difficulty = data["difficulty"] - problem.samples = json.dumps(data["samples"]) - problem.hint = data["hint"] - problem.visible = data["visible"] - problem.last_update_time = now() - - # 删除原有的标签的对应关系 - problem.tags.remove(*problem.tags.all()) - # 重新添加所有的标签 - for tag in data["tags"]: - try: - tag = ProblemTag.objects.get(name=tag) - except ProblemTag.DoesNotExist: - tag = ProblemTag.objects.create(name=tag) - problem.tags.add(tag) - - problem.save() - - return success_response(ProblemSerializer(problem).data) - else: - return serializer_invalid_response(serializer) - - def get(self, request): - """ - 题目分页json api接口 - --- - response_serializer: ProblemSerializer - """ - problem_id = request.GET.get("problem_id", None) - if problem_id: - try: - # 普通管理员只能获取自己创建的题目 - # 超级管理员可以获取全部的题目 - problem = Problem.objects.get(id=problem_id) - if request.user.admin_type != SUPER_ADMIN and problem.created_by != request.user: - return error_response(u"题目不存在") - return success_response(ProblemSerializer(problem).data) - except Problem.DoesNotExist: - return error_response(u"题目不存在") - - # 获取问题列表 - problems = Problem.objects.all().order_by("-create_time") - - if request.user.admin_type != SUPER_ADMIN: - problems = problems.filter(created_by=request.user) - - visible = request.GET.get("visible", None) - if visible: - problems = problems.filter(visible=(visible == "true")) - keyword = request.GET.get("keyword", None) - if keyword: - problems = problems.filter(Q(title__contains=keyword) | - Q(description__contains=keyword)) - - return paginate(request, problems, ProblemSerializer) - - -class TestCaseUploadAPIView(APIView): - """ - 上传题目的测试用例 - """ - - def _is_legal_test_case_file_name(self, file_name): - # 正整数开头的 .in 或者.out 结尾的 - regex = r"^[1-9]\d*\.(in|out)$" - return re.compile(regex).match(file_name) is not None - - def post(self, request): - if "file" not in request.FILES: - return error_response(u"文件上传失败") - - f = request.FILES["file"] - - tmp_zip = "/tmp/" + rand_str() + ".zip" - try: - with open(tmp_zip, "wb") as test_case_zip: - for chunk in f: - test_case_zip.write(chunk) - except IOError as e: - logger.error(e) - return error_response(u"上传失败") - try: - test_case_file = zipfile.ZipFile(tmp_zip, 'r') - except Exception: - return error_response(u"解压失败") - name_list = test_case_file.namelist() - - # 如果文件是直接打包的,那么name_list 就是["1.in", "1.out"]这样的 - if len(name_list) == 0: - return error_response(u"压缩包内没有文件") - - for item in name_list: - if not self._is_legal_test_case_file_name(item): - return error_response(u"%s 文件名不符合规范" % item) - - # 排序,这样name_list就是[1.in, 1.out, 2.in, 2.out]的形式了 - name_list.sort() - - spj = False - - for item in name_list: - # 代表里面有.out文件,所以应该是普通题目的测试用例 - if item.endswith(".out"): - break - else: - # 否则就应该是spj的测试用例 - spj = True - - if not spj: - if len(name_list) % 2 == 1: - return error_response(u"测试用例文件格式错误,文件数目为奇数") - - for index in range(1, len(name_list) / 2 + 1): - if not (str(index) + ".in" in name_list and str(index) + ".out" in name_list): - return error_response(u"测试用例文件格式错误,缺少" + str(index) + u".in/.out文件") - test_case_number = len(name_list) / 2 - else: - for index in range(1, len(name_list) + 1): - if str(index) + ".in" not in name_list: - return error_response(u"测试用例文件格式错误,缺少" + str(index) + u".in文件") - test_case_number = len(name_list) - - problem_test_dir = rand_str() - test_case_dir = os.path.join(settings.TEST_CASE_DIR, problem_test_dir) - - # 得到了合法的测试用例文件列表 然后去解压缩 - os.mkdir(test_case_dir) - for name in name_list: - f = open(os.path.join(test_case_dir, name), "wb") - try: - f.write(test_case_file.read(name).replace("\r\n", "\n")) - except MemoryError: - return error_response(u"单个测试数据体积过大!") - finally: - f.close() - - file_info = {"test_case_number": test_case_number, "test_cases": {}, "spj": spj} - - # 计算输出文件的md5 - for i in range(1, test_case_number + 1): - if not spj: - md5 = hashlib.md5() - striped_md5 = hashlib.md5() - f = open(os.path.join(test_case_dir, str(i) + ".out"), "r") - # 完整文件的md5 - while True: - data = f.read(2 ** 8) - if not data: - break - md5.update(data) - - # 删除标准输出最后的空格和换行 - # 这时只能一次全部读入了,分块读的话,没办法确定文件结尾 - f.seek(0) - striped_md5.update(f.read().rstrip()) - - output_md5 = md5.hexdigest() - striped_output_md5 = striped_md5.hexdigest() - output_name = str(i) + ".out" - output_size = os.path.getsize(os.path.join(test_case_dir, output_name)) - else: - output_md5 = striped_output_md5 = output_name = output_size = None - - file_info["test_cases"][str(i)] = {"input_name": str(i) + ".in", - "output_name": output_name, - "output_md5": output_md5, - "striped_output_md5": striped_output_md5, - "input_size": os.path.getsize(os.path.join(test_case_dir, str(i) + ".in")), - "output_size": output_size} - # 写入配置文件 - with open(os.path.join(test_case_dir, "info"), "w") as f: - f.write(json.dumps(file_info)) - - return success_response({"test_case_id": problem_test_dir, - "file_list": file_info["test_cases"], - "spj": spj}) - - def get(self, request): - test_case_id = request.GET.get("test_case_id", None) - if not test_case_id: - return error_response(u"参数错误") - test_case_config = os.path.join(settings.TEST_CASE_DIR, test_case_id, "info") - try: - f = open(test_case_config) - config = json.loads(f.read()) - f.close() - except Exception as e: - return error_response(u"读取测试用例出错") - return success_response({"file_list": config["test_cases"], "spj": config.get("spj", False)}) - - -class TestCaseDownloadAPIView(APIView): - """ - 下载题目的测试数据 - """ - - def _is_legal_test_case_file_name(self, file_name): - regex = r"^[1-9]\d*\.(in|out)$" - return re.compile(regex).match(file_name) is not None - - def file_iterator(self, big_file, chunk_size=512): - with open(big_file) as f: - while True: - c = f.read(chunk_size) - if c: - yield c - else: - break - - @admin_required - def get(self, request): - test_case_id = request.GET.get("test_case_id", None) - if not test_case_id: - return error_response(u"参数错误") - # 防止URL./../../.上层目录遍历 - if not re.compile(r"^[0-9a-zA-Z]+$").match(test_case_id): - return error_response(u"参数错误") - - try: - # 超级管理员可以下载全部的题目的测试数据 - # 普通管理员只能下载自己创建的题目的测试数据 - if request.user.admin_type != SUPER_ADMIN: - ContestProblem.objects.get(test_case_id=test_case_id, created_by=request.user) - - test_case_dir = os.path.join(settings.TEST_CASE_DIR, test_case_id) - if not os.path.exists(test_case_dir): - return error_response(u"测试用例不存在") - - # 压缩测试用例,命名规则为 "test_case" + test_case_id + ".zip" - test_case_zip = os.path.join("/tmp", "test_case-" + test_case_id + ".zip") - - zf = zipfile.ZipFile(test_case_zip, "w", zipfile.ZIP_DEFLATED) - for filename in os.listdir(test_case_dir): - # 避免存在文件链接,导致真实文件被打包 - if self._is_legal_test_case_file_name(filename) and not os.path.islink(os.path.join(test_case_dir, filename)): - zf.write(os.path.join(test_case_dir, filename), filename) - zf.close() - - # 大文件传输 - response = StreamingHttpResponse(self.file_iterator(test_case_zip)) - response['Content-Type'] = 'application/octet-stream' - response['Content-Disposition'] = 'attachment;filename=test_case-%s.zip' % test_case_id - return response - except ContestProblem.DoesNotExist: - return error_response(u"题目不存在") - - -def problem_list_page(request, page=1): - """ - 前台的问题列表 - """ - # 正常情况 - problems = Problem.objects.filter(visible=True) - - # 搜索的情况 - keyword = request.GET.get("keyword", "").strip() - if keyword: - problems = problems.filter(Q(title__contains=keyword) | Q(description__contains=keyword)) - - difficulty_order = request.GET.get("order_by", None) - if difficulty_order: - if difficulty_order[0] == "-": - problems = problems.order_by("-difficulty") - difficulty_order = "difficulty" - else: - problems = problems.order_by("difficulty") - difficulty_order = "-difficulty" - else: - difficulty_order = "difficulty" - - # 按照标签筛选 - tag_text = request.GET.get("tag", None) - if tag_text: - try: - tag = ProblemTag.objects.get(name=tag_text) - except ProblemTag.DoesNotExist: - return error_page(request, u"标签不存在") - problems = tag.problem_set.all().filter(visible=True) - - paginator = Paginator(problems, 40) - try: - current_page = paginator.page(int(page)) - except Exception: - return error_page(request, u"不存在的页码") - - previous_page = next_page = None - - try: - previous_page = current_page.previous_page_number() - except Exception: - pass - - try: - next_page = current_page.next_page_number() - except Exception: - pass - - # 右侧标签列表 按照关联的题目的数量排序 排除题目数量为0的 - tags = ProblemTag.objects.annotate(problem_number=Count("problem")).filter(problem_number__gt=0).order_by( - "-problem_number") - - return render(request, "oj/problem/problem_list.html", - {"problems": current_page, "page": int(page), - "previous_page": previous_page, "next_page": next_page, - "keyword": keyword, "tag": tag_text, - "tags": tags, "difficulty_order": difficulty_order}) diff --git a/submission/__init__.py b/submission/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/submission/migrations/0001_initial.py b/submission/migrations/0001_initial.py deleted file mode 100644 index 10e8ff8f..00000000 --- a/submission/migrations/0001_initial.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.shortcuts - - -class Migration(migrations.Migration): - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Submission', - fields=[ - ('id', models.CharField(default=utils.shortcuts.rand_str, max_length=32, serialize=False, primary_key=True, db_index=True)), - ('user_id', models.IntegerField()), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('result', models.IntegerField(default=8)), - ('language', models.IntegerField()), - ('code', models.TextField()), - ('problem_id', models.IntegerField()), - ('info', models.TextField(null=True, blank=True)), - ('accepted_answer_time', models.IntegerField(null=True, blank=True)), - ('accepted_answer_info', models.TextField(null=True, blank=True)), - ], - options={ - 'db_table': 'submission', - }, - ), - ] diff --git a/submission/migrations/0002_submission_is_counted.py b/submission/migrations/0002_submission_is_counted.py deleted file mode 100644 index 169a9a36..00000000 --- a/submission/migrations/0002_submission_is_counted.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='submission', - name='is_counted', - field=models.BooleanField(default=False), - ), - ] diff --git a/submission/migrations/0003_auto_20150821_1654.py b/submission/migrations/0003_auto_20150821_1654.py deleted file mode 100644 index 22ab59dd..00000000 --- a/submission/migrations/0003_auto_20150821_1654.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0002_submission_is_counted'), - ] - - operations = [ - migrations.AlterField( - model_name='submission', - name='problem_id', - field=models.IntegerField(db_index=True), - ), - migrations.AlterField( - model_name='submission', - name='user_id', - field=models.IntegerField(db_index=True), - ), - ] diff --git a/submission/migrations/0004_remove_submission_is_counted.py b/submission/migrations/0004_remove_submission_is_counted.py deleted file mode 100644 index d4f27bd8..00000000 --- a/submission/migrations/0004_remove_submission_is_counted.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0003_auto_20150821_1654'), - ] - - operations = [ - migrations.RemoveField( - model_name='submission', - name='is_counted', - ), - ] diff --git a/submission/migrations/0005_submission_contest_id.py b/submission/migrations/0005_submission_contest_id.py deleted file mode 100644 index d6005aae..00000000 --- a/submission/migrations/0005_submission_contest_id.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0004_remove_submission_is_counted'), - ] - - operations = [ - migrations.AddField( - model_name='submission', - name='contest_id', - field=models.IntegerField(null=True, blank=True), - ), - ] diff --git a/submission/migrations/0006_submission_shared.py b/submission/migrations/0006_submission_shared.py deleted file mode 100644 index 6d0b4a1a..00000000 --- a/submission/migrations/0006_submission_shared.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0005_submission_contest_id'), - ] - - operations = [ - migrations.AddField( - model_name='submission', - name='shared', - field=models.BooleanField(default=False), - ), - ] diff --git a/submission/migrations/0007_auto_20151207_1645.py b/submission/migrations/0007_auto_20151207_1645.py deleted file mode 100644 index a053d98c..00000000 --- a/submission/migrations/0007_auto_20151207_1645.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0006_submission_shared'), - ] - - operations = [ - migrations.AddField( - model_name='submission', - name='judge_end_time', - field=models.IntegerField(null=True, blank=True), - ), - migrations.AddField( - model_name='submission', - name='judge_start_time', - field=models.IntegerField(null=True, blank=True), - ), - ] diff --git a/submission/migrations/0008_auto_20151208_2106.py b/submission/migrations/0008_auto_20151208_2106.py deleted file mode 100644 index b3a85763..00000000 --- a/submission/migrations/0008_auto_20151208_2106.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2015-12-08 13:06 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0007_auto_20151207_1645'), - ] - - operations = [ - migrations.AlterField( - model_name='submission', - name='judge_end_time', - field=models.BigIntegerField(blank=True, null=True), - ), - migrations.AlterField( - model_name='submission', - name='judge_start_time', - field=models.BigIntegerField(blank=True, null=True), - ), - ] diff --git a/submission/migrations/__init__.py b/submission/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/submission/models.py b/submission/models.py deleted file mode 100644 index 0480ae71..00000000 --- a/submission/models.py +++ /dev/null @@ -1,33 +0,0 @@ -# coding=utf-8 -from django.db import models -from utils.shortcuts import rand_str -from judge.result import result - - -class Submission(models.Model): - id = models.CharField(max_length=32, default=rand_str, primary_key=True, db_index=True) - user_id = models.IntegerField(db_index=True) - create_time = models.DateTimeField(auto_now_add=True) - # 判题开始时间 - judge_start_time = models.BigIntegerField(blank=True, null=True) - # 判题结束时间 - judge_end_time = models.BigIntegerField(blank=True, null=True) - result = models.IntegerField(default=result["waiting"]) - language = models.IntegerField() - code = models.TextField() - contest_id = models.IntegerField(blank=True, null=True) - problem_id = models.IntegerField(db_index=True) - # 这个字段可能存储很多数据 比如编译错误、系统错误的时候,存储错误原因字符串 - # 正常运行的时候存储判题结果,比如cpu时间内存之类的 - info = models.TextField(blank=True, null=True) - accepted_answer_time = models.IntegerField(blank=True, null=True) - # 这个字段只有在题目是accepted 的时候才会用到,比赛题目的提交可能还会有得分等信息,存储在这里面 - accepted_answer_info = models.TextField(blank=True, null=True) - # 是否可以分享 - shared = models.BooleanField(default=False) - - class Meta: - db_table = "submission" - - def __unicode__(self): - return self.id diff --git a/submission/serializers.py b/submission/serializers.py deleted file mode 100644 index c09c01e3..00000000 --- a/submission/serializers.py +++ /dev/null @@ -1,53 +0,0 @@ -# coding=utf-8 -from rest_framework import serializers - -from account.models import User -from .models import Submission - - -class CreateSubmissionSerializer(serializers.Serializer): - problem_id = serializers.IntegerField() - language = serializers.IntegerField() - code = serializers.CharField(max_length=20000) - - -class OpenAPICreateSubmissionSerializer(serializers.Serializer): - appkey = serializers.CharField(max_length=35) - problem_id = serializers.IntegerField() - language = serializers.IntegerField() - code = serializers.CharField(max_length=20000) - - -class SubmissionSerializer(serializers.ModelSerializer): - user = serializers.SerializerMethodField("_get_submission_user") - - class Meta: - model = Submission - fields = ["id", "result", "create_time", "language", "user"] - - def _get_submission_user(self, obj): - return User.objects.get(id=obj.user_id).username - - -class OpenAPISubmissionSerializer(serializers.ModelSerializer): - - class Meta: - model = Submission - fields = ["id", "result", "create_time", "language", "info"] - - -class SubmissionhareSerializer(serializers.Serializer): - submission_id = serializers.CharField(max_length=40) - - -class SubmissionRejudgeSerializer(serializers.Serializer): - submission_id = serializers.CharField(max_length=40) - - -class CreateContestSubmissionSerializer(serializers.Serializer): - contest_id = serializers.IntegerField() - problem_id = serializers.IntegerField() - language = serializers.IntegerField() - code = serializers.CharField(max_length=20000) - - diff --git a/submission/tasks.py b/submission/tasks.py deleted file mode 100644 index 34ab63d1..00000000 --- a/submission/tasks.py +++ /dev/null @@ -1,9 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import -from celery import shared_task -from judge_dispatcher.tasks import JudgeDispatcher - - -@shared_task -def _judge(submission_id, time_limit, memory_limit, test_case_id, spj=False, spj_language=None, spj_code=None, spj_version=None): - JudgeDispatcher(submission_id, time_limit, memory_limit, test_case_id, spj, spj_language, spj_code, spj_version).judge() \ No newline at end of file diff --git a/submission/tests.py b/submission/tests.py deleted file mode 100644 index e69de29b..00000000 diff --git a/submission/views.py b/submission/views.py deleted file mode 100644 index 57c5312b..00000000 --- a/submission/views.py +++ /dev/null @@ -1,372 +0,0 @@ -# coding=utf-8 -import json -import logging - -import redis - -from django.shortcuts import render -from django.core.paginator import Paginator -from django.conf import settings -from django.views.decorators.csrf import csrf_exempt -from rest_framework.views import APIView - -from account.decorators import login_required, super_admin_required -from account.models import SUPER_ADMIN, User -from problem.models import Problem -from contest.models import ContestProblem, Contest -from contest.decorators import check_user_contest_permission -from utils.shortcuts import (serializer_invalid_response, error_response, - success_response, error_page, paginate, build_query_string) -from utils.throttling import TokenBucket, BucketController -from judge.result import result as judge_result -from .tasks import _judge -from .models import Submission -from .serializers import (CreateSubmissionSerializer, SubmissionSerializer, - SubmissionhareSerializer, SubmissionRejudgeSerializer, - CreateContestSubmissionSerializer, OpenAPICreateSubmissionSerializer, - OpenAPISubmissionSerializer) - -logger = logging.getLogger("app_info") - - -def _submit_code(user, problem_id, language, code): - controller = BucketController(user_id=user.id, - redis_conn=redis.Redis(host=settings.REDIS_CACHE["host"], - port=settings.REDIS_CACHE["port"], - db=settings.REDIS_CACHE["db"]), - default_capacity=settings.TOKEN_BUCKET_DEFAULT_CAPACITY) - bucket = TokenBucket(fill_rate=settings.TOKEN_BUCKET_FILL_RATE, - capacity=settings.TOKEN_BUCKET_DEFAULT_CAPACITY, - last_capacity=controller.last_capacity, - last_timestamp=controller.last_timestamp) - if bucket.consume(): - controller.last_capacity -= 1 - else: - return error_response(u"您提交的频率过快, 请等待%d秒" % int(bucket.expected_time() + 1)) - - try: - problem = Problem.objects.get(id=problem_id) - except Problem.DoesNotExist: - return error_response(u"题目不存在") - submission = Submission.objects.create(user_id=user.id, - language=language, - code=code, - problem_id=problem.id) - - try: - _judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id, - problem.spj, problem.spj_language, problem.spj_code, problem.spj_version) - except Exception as e: - logger.error(e) - return error_response(u"提交判题任务失败") - return success_response({"submission_id": submission.id}) - - -class OpenAPISubmitCodeAPI(APIView): - def post(self, request): - """ - openapi 创建提交 - """ - serializer = OpenAPICreateSubmissionSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - try: - user = User.objects.get(openapi_appkey=data["appkey"]) - except User.DoesNotExist: - return error_response(u"appkey无效") - return _submit_code(user, data["problem_id"], data["language"], data["code"]) - else: - return serializer_invalid_response(serializer) - - def get(self, request): - """ - openapi 获取提交详情 - """ - submission_id = request.GET.get("submission_id", None) - appkey = request.GET.get("appkey", None) - if not (submission_id and appkey): - return error_response(u"参数错误") - try: - user = User.objects.get(openapi_appkey=appkey) - except User.DoesNotExist: - return error_response(u"appkey无效") - try: - submission = Submission.objects.get(id=submission_id, user_id=user.id) - return success_response(OpenAPISubmissionSerializer(submission).data) - except Submission.DoesNotExist: - return error_response(u"提交不存在") - - -class SubmissionAPIView(APIView): - @login_required - def post(self, request): - """ - 提交代码 - --- - request_serializer: CreateSubmissionSerializer - """ - serializer = CreateSubmissionSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - return _submit_code(request.user, data["problem_id"], data["language"], data["code"]) - 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"参数错误") - try: - submission = Submission.objects.get(id=submission_id, user_id=request.user.id) - except Submission.DoesNotExist: - return error_response(u"提交不存在") - response_data = {"result": submission.result} - if submission.result == 0: - response_data["accepted_answer_time"] = submission.accepted_answer_time - return success_response(response_data) - - -class ContestSubmissionAPIView(APIView): - @check_user_contest_permission - def post(self, request): - """ - 创建比赛的提交 - --- - request_serializer: CreateContestSubmissionSerializer - """ - serializer = CreateContestSubmissionSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - contest = Contest.objects.get(id=data["contest_id"]) - try: - problem = ContestProblem.objects.get(contest=contest, id=data["problem_id"]) - except ContestProblem.DoesNotExist: - return error_response(u"题目不存在") - submission = Submission.objects.create(user_id=request.user.id, - language=int(data["language"]), - contest_id=contest.id, - code=data["code"], - problem_id=problem.id) - try: - _judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id, - problem.spj, problem.spj_language, problem.spj_code, problem.spj_version) - except Exception as e: - logger.error(e) - return error_response(u"提交判题任务失败") - return success_response({"submission_id": submission.id}) - else: - return serializer_invalid_response(serializer) - - -@login_required -def problem_my_submissions_list_page(request, problem_id): - """ - 我单个题目所有提交的列表页 - """ - try: - problem = Problem.objects.get(id=problem_id, visible=True) - except Problem.DoesNotExist: - return error_page(request, u"问题不存在") - - submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id, contest_id__isnull=True). \ - order_by("-create_time"). \ - values("id", "result", "create_time", "accepted_answer_time", "language") - - return render(request, "oj/submission/problem_my_submissions_list.html", - {"submissions": submissions, "problem": problem}) - - -def _get_submission(submission_id, user): - """ - 判断用户权限 看能否获取这个提交详情页面 - """ - submission = Submission.objects.get(id=submission_id) - # 超级管理员或者提交者自己或者是一个分享的提交 - if user.admin_type == SUPER_ADMIN or submission.user_id == user.id: - return {"submission": submission, "can_share": True} - if submission.contest_id: - contest = Contest.objects.get(id=submission.contest_id) - # 比赛提交的话,比赛创建者也可见 - if contest.created_by == user: - return {"submission": submission, "can_share": True} - if submission.shared: - return {"submission": submission, "can_share": False} - else: - raise Submission.DoesNotExist - - -@login_required -def my_submission(request, submission_id): - """ - 单个题目的提交详情页 - """ - try: - result = _get_submission(submission_id, request.user) - submission = result["submission"] - except Submission.DoesNotExist: - return error_page(request, u"提交不存在") - - try: - if submission.contest_id: - problem = ContestProblem.objects.get(id=submission.problem_id, visible=True) - else: - problem = Problem.objects.get(id=submission.problem_id, visible=True) - except Exception: - return error_page(request, u"提交不存在") - - if submission.result in [judge_result["compile_error"], judge_result["system_error"], judge_result["waiting"]]: - info = submission.info - else: - info = json.loads(submission.info) - if "test_case" in info[0]: - info = sorted(info, key=lambda x: x["test_case"]) - - user = User.objects.get(id=submission.user_id) - return render(request, "oj/submission/my_submission.html", - {"submission": submission, "problem": problem, "info": info, - "user": user, "can_share": result["can_share"], "website_base_url": settings.WEBSITE_INFO["url"]}) - - -class SubmissionAdminAPIView(APIView): - @super_admin_required - def get(self, request): - problem_id = request.GET.get("problem_id", None) - if not problem_id: - return error_response(u"参数错误") - submissions = Submission.objects.filter(problem_id=problem_id, contest_id__isnull=True).order_by("-create_time") - return paginate(request, submissions, SubmissionSerializer) - - -@login_required -def submission_list_page(request, page=1): - """ - 所有提交的列表页 - """ - - submission_filter = {"my": None, "user_id": None} - # 是否显示所有人的提交 - show_all = False - - # url中如果存在user_id参数,说明只显示这个人的提交,忽略其他参数 - user_id = request.GET.get("user_id", None) - if user_id and request.user.admin_type == SUPER_ADMIN: - submission_filter["user_id"] = user_id - submissions = Submission.objects.filter(user_id=user_id, contest_id__isnull=True) - else: - # 兼容部分版本,设置中没有这一项 - try: - show_all = settings.SHOW_ALL_SUBMISSIONS_LIST - except Exception: - pass - - # url中my=true可以只显示自己的 - if request.GET.get("my", None) == "true": - submission_filter["my"] = "true" - show_all = False - if show_all: - submissions = Submission.objects.filter(contest_id__isnull=True) - else: - submissions = Submission.objects.filter(user_id=request.user.id, contest_id__isnull=True) - - submissions = submissions.values("id", "user_id", "problem_id", "result", "create_time", - "accepted_answer_time", "language").order_by("-create_time") - - language = request.GET.get("language", None) - if language: - submissions = submissions.filter(language=int(language)) - submission_filter["language"] = language - - result = request.GET.get("result", None) - if result: - submissions = submissions.filter(result=int(result)) - submission_filter["result"] = result - - paginator = Paginator(submissions, 20) - try: - submissions = paginator.page(int(page)) - except Exception: - return error_page(request, u"不存在的页码") - - # 因为提交页面经常会有重复的题目和用户,缓存一下查询结果 - cache_result = {"problem": {}, "user": {}} - for item in submissions: - problem_id = item["problem_id"] - if problem_id not in cache_result["problem"]: - problem = Problem.objects.get(id=problem_id) - cache_result["problem"][problem_id] = problem.title - item["title"] = cache_result["problem"][problem_id] - - user_id = item["user_id"] - if user_id not in cache_result["user"]: - user = User.objects.get(id=user_id) - cache_result["user"][user_id] = user - item["user"] = cache_result["user"][user_id] - - if item["user_id"] == request.user.id or request.user.admin_type == SUPER_ADMIN: - item["show_link"] = True - else: - item["show_link"] = False - - previous_page = next_page = None - try: - previous_page = submissions.previous_page_number() - except Exception: - pass - try: - next_page = submissions.next_page_number() - except Exception: - pass - - return render(request, "oj/submission/submissions_list.html", - {"submissions": submissions, "page": int(page), - "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, - "query": build_query_string(submission_filter), - "submission_filter": submission_filter, - "show_all": show_all}) - - -class SubmissionShareAPIView(APIView): - def post(self, request): - serializer = SubmissionhareSerializer(data=request.data) - if serializer.is_valid(): - submission_id = serializer.data["submission_id"] - try: - result = _get_submission(submission_id, request.user) - except Submission.DoesNotExist: - return error_response(u"提交不存在") - if not result["can_share"]: - return error_page(request, u"提交不存在") - submission = result["submission"] - submission.shared = not submission.shared - submission.save() - return success_response(submission.shared) - else: - return serializer_invalid_response(serializer) - - -class SubmissionRejudgeAdminAPIView(APIView): - @super_admin_required - def post(self, request): - serializer = SubmissionRejudgeSerializer(data=request.data) - if serializer.is_valid(): - submission_id = serializer.data["submission_id"] - # 目前只考虑前台公开题目的重新判题 - try: - submission = Submission.objects.get(id=submission_id, contest_id__isnull=True) - except Submission.DoesNotExist: - return error_response(u"提交不存在") - - try: - problem = Problem.objects.get(id=submission.problem_id) - except Problem.DoesNotExist: - return error_response(u"题目不存在") - try: - _judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id, - problem.spj, problem.spj_language, problem.spj_code, problem.spj_version) - except Exception as e: - logger.error(e) - return error_response(u"提交判题任务失败") - return success_response(u"任务提交成功") - else: - return serializer_invalid_response(serializer) diff --git a/template/src/404.html b/template/src/404.html deleted file mode 100644 index 3d1a5051..00000000 --- a/template/src/404.html +++ /dev/null @@ -1,151 +0,0 @@ -{% extends "oj_base.html" %} -{% block css_block %} - -{% endblock %} -{% block body %} -
-

404 - Page Not Found

-
-
- -
// C
-#include <stdio.h>
-int main()
-{
-    printf("404 - Page Not Found");
-    return 0;
-}
-
// C++
-#include <iostream>
-int main()
-{
-    std::cout << "404 - Page Not Found" << std::endl;
-    return 0;
-}
-
// Java
-public class Main
-{
-    public static void main(String[] args)
-    {
-        System.out.println("404 - Page Not Found");
-    }
-}
-
// JavaScript
-console.log("404 - Page Not Found")
-
// Python
-print "404 - Page Not Found"
- -
-
-{% endblock %} \ No newline at end of file diff --git a/template/src/500.html b/template/src/500.html deleted file mode 100644 index 28349ba1..00000000 --- a/template/src/500.html +++ /dev/null @@ -1,151 +0,0 @@ -{% extends "oj_base.html" %} -{% block css_block %} - -{% endblock %} -{% block body %} -
-

500 - Server Error

-
-
- -
// C
-#include <stdio.h>
-int main()
-{
-    printf("500 - Server Error");
-    return 0;
-}
-
// C++
-#include <iostream>
-int main()
-{
-    std::cout << "500 - Server Error" << std::endl;
-    return 0;
-}
-
// Java
-public class Main
-{
-    public static void main(String[] args)
-    {
-        System.out.println("500 - Server Error");
-    }
-}
-
// JavaScript
-console.log("500 - Server Error")
-
// Python
-print "500 - Server Error"
- -
-
-{% endblock %} \ No newline at end of file diff --git a/template/src/admin/admin.html b/template/src/admin/admin.html deleted file mode 100644 index d601692d..00000000 --- a/template/src/admin/admin.html +++ /dev/null @@ -1,100 +0,0 @@ -{% load website_info %} -{% verbatim %} - - - - - - - - - - 在线评测系统 - 后台管理 - - - - - - - - - - - - - - - -
-
- -
- -
- - - - -
- - -
-
- -{% endverbatim %} - - - - - - - - - \ No newline at end of file diff --git a/template/src/admin/announcement/announcement.html b/template/src/admin/announcement/announcement.html deleted file mode 100644 index ab19406a..00000000 --- a/template/src/admin/announcement/announcement.html +++ /dev/null @@ -1,76 +0,0 @@ -
-

公告管理

- - - - - - - - - - - - - - - - - - - -
编号标题创建时间更新时间创建者可见
{{ el.id }}{{ el.title }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.last_update_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.created_by.username }} - -
-
- -
-
- -
- -
-

编辑公告

- -
- -
-
- - -
-
- -
- -
- -    - -
-
-

发布公告

- -
-
- - - -
-
-
- - - - -
-
- -
- -
-
-
- \ No newline at end of file diff --git a/template/src/admin/contest/add_contest.html b/template/src/admin/contest/add_contest.html deleted file mode 100644 index bd7bd821..00000000 --- a/template/src/admin/contest/add_contest.html +++ /dev/null @@ -1,100 +0,0 @@ -
-
-
- -
- -
-
-
- - -
-
-
-
- -
-
-
- -
-
-
- -
- -
-
-
-
- -
- -
-
-
-
-
- - -
- - - - - - -
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-   {{ el.name }} -
-
- -
- -
- -
-
-
- -
-
- -
-
- - diff --git a/template/src/admin/contest/contest_list.html b/template/src/admin/contest/contest_list.html deleted file mode 100644 index 1781eacd..00000000 --- a/template/src/admin/contest/contest_list.html +++ /dev/null @@ -1,45 +0,0 @@ -
-

比赛列表

- -
-
-
- - - -
-
-
-
- - - - - - - - - - - - - - - - - - - -
ID比赛排名创建时间创建者可见
{{ el.id }}{{ el.title }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.created_by.username }} - 编辑 - 题目 -
-
- -
-
- -
- -
- diff --git a/template/src/admin/contest/edit_contest.html b/template/src/admin/contest/edit_contest.html deleted file mode 100644 index 2eda4cd1..00000000 --- a/template/src/admin/contest/edit_contest.html +++ /dev/null @@ -1,118 +0,0 @@ -
-
- - -
- -
-
-
- - -
-
-
-
- -
-
-
- -
-
-
- - -
- - -
-
-
-
- - -
- - -
-
-
-
-
- - -
- - - - - - -
- -
-
-
-
- - -
- -
-
-
- -
- -
-
-
-
- -
-   {{ el.name }} -
-
- -
- - -
- -
-
-
- - -
- -
-
-
- -
-
-
- - diff --git a/template/src/admin/contest/edit_problem.html b/template/src/admin/contest/edit_problem.html deleted file mode 100644 index 313d60fd..00000000 --- a/template/src/admin/contest/edit_problem.html +++ /dev/null @@ -1,111 +0,0 @@ -
-
- - -
- - -
-
-
- - -
-
- -
- - -
- - -
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
- -
-
-

- - 添加 - -
-
- -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- - -
- - -
-
- -
-
-
- - diff --git a/template/src/admin/contest/problem_list.html b/template/src/admin/contest/problem_list.html deleted file mode 100644 index a59bb6c0..00000000 --- a/template/src/admin/contest/problem_list.html +++ /dev/null @@ -1,35 +0,0 @@ -
- -

比赛题目列表

-
- -
- - - - - - - - - - - - - - - - - -
ID题目创建时间可见通过次数/提交总数
{{ el.sort_index }}{{ el.title }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.total_accepted_number }}/{{ el.total_submit_number }} - - -
-
- \ No newline at end of file diff --git a/template/src/admin/group/group.html b/template/src/admin/group/group.html deleted file mode 100644 index 3dcb7eaa..00000000 --- a/template/src/admin/group/group.html +++ /dev/null @@ -1,72 +0,0 @@ -
-

小组管理

- -
-
-
- - - -
-
-
- - - - - - - - - - - - - - - - - - - -
ID名称创建时间人数设置
{{ el.id }}{{ el.name }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.members_number }}{{ getGroupSettingString(el.join_group_setting) }} - -
- -
- 页数:{{ page }}/{{ totalPage }}   - - -
-

创建小组

-
-
-
- -
-
-
-
-
- -
-
-
- -
-
- - 允许任何人加入 - 提交请求后管理员审核 - 不允许任何人加入 - -
- -
-
- -
- \ No newline at end of file diff --git a/template/src/admin/group/group_detail.html b/template/src/admin/group/group_detail.html deleted file mode 100644 index 71e54b5c..00000000 --- a/template/src/admin/group/group_detail.html +++ /dev/null @@ -1,72 +0,0 @@ -
- -

小组成员管理

- - - - - - - - - - - - - - - - -
ID用户名真实姓名加入时间
{{ el.user.id }}{{ el.user.username }}{{ el.user.real_name }}{{ el.join_time|date("yyyy-MM-dd HH:mm:ss")}} - - -
-
- 页数:{{ page }}/{{ totalPage }}   - - -
-

修改小组信息

- -
-
-
- -
-
-
-
-
- -
-
-
-
-
-
- - - -
- -
-
-
- -
-
- -
-
-
- diff --git a/template/src/admin/group/join_group_request_list.html b/template/src/admin/group/join_group_request_list.html deleted file mode 100644 index 09c611f1..00000000 --- a/template/src/admin/group/join_group_request_list.html +++ /dev/null @@ -1,31 +0,0 @@ -
-

加入小组请求管理

- - - - - - - - - - - - - - - - -
创建时间小组用户附加消息
{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.group.name }}{{ el.user.username }}{{ el.message }} - - -
- -
- 页数:{{ page }}/{{ totalPage }}   - - -
- -
- \ No newline at end of file diff --git a/template/src/admin/index/index.html b/template/src/admin/index/index.html deleted file mode 100644 index 257eab15..00000000 --- a/template/src/admin/index/index.html +++ /dev/null @@ -1 +0,0 @@ -

Online Judge Admin

\ No newline at end of file diff --git a/template/src/admin/judges/judges.html b/template/src/admin/judges/judges.html deleted file mode 100644 index 2319d359..00000000 --- a/template/src/admin/judges/judges.html +++ /dev/null @@ -1,128 +0,0 @@ -
-

判题服务器管理

- - - - - - - - - - - - - - - - - - - -
编号名字最大实例数量负载创建时间状态
{{ el.id }}{{ el.name }}{{ el.max_instance_number }}{{ el.used_instance_number / el.max_instance_number * 100 }}%{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}} - -
-
- -
- -
-

编辑判题服务器

- -
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- - -
-
-
-
- -
- -
- -    - 取消 -
-
-
-
- -

添加判题服务器

- -
-
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- - -
-
-
-
- -
-
- -
- \ No newline at end of file diff --git a/template/src/admin/problem/add_problem.html b/template/src/admin/problem/add_problem.html deleted file mode 100644 index 396de7fc..00000000 --- a/template/src/admin/problem/add_problem.html +++ /dev/null @@ -1,118 +0,0 @@ -
-
- -
- - -
-
- -
- - -
- -
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
- -
-
-

- - 添加 - -
-
- -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- - -
- - -
-
- - -
- -
- -
-
-
- - \ No newline at end of file diff --git a/template/src/admin/problem/edit_problem.html b/template/src/admin/problem/edit_problem.html deleted file mode 100644 index ab8e5d62..00000000 --- a/template/src/admin/problem/edit_problem.html +++ /dev/null @@ -1,125 +0,0 @@ -
-
- - -
- - -
-
- -
- - -
- - -
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
- -
-
-

- - 添加 - -
-
- -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- - -
- - -
-
- - -
- -
- -
-
-
- - diff --git a/template/src/admin/problem/problem_list.html b/template/src/admin/problem/problem_list.html deleted file mode 100644 index 3fa5ac54..00000000 --- a/template/src/admin/problem/problem_list.html +++ /dev/null @@ -1,44 +0,0 @@ -
-

题目列表

- -
-
-
- - - -
-
-
-
- - - - - - - - - - - - - - - - - - - -
ID题目创建时间作者可见通过次数/提交总数
{{ el.id }}{{ el.title }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.created_by.username }}{{ el.total_accepted_number }}/{{ el.total_submit_number }} - - -
-
- -
-
- -
-
- \ No newline at end of file diff --git a/template/src/admin/problem/submission_list.html b/template/src/admin/problem/submission_list.html deleted file mode 100644 index 8c0840ab..00000000 --- a/template/src/admin/problem/submission_list.html +++ /dev/null @@ -1,33 +0,0 @@ -
- -

提交列表

- - - - - - - - - - - - - - - - -
ID创建时间作者结果
{{ el.id }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.user }}{{ results[el.result] }} - 详情 - 重判 -
-
- -
-
- \ No newline at end of file diff --git a/template/src/admin/user/user_list.html b/template/src/admin/user/user_list.html deleted file mode 100644 index 604a3970..00000000 --- a/template/src/admin/user/user_list.html +++ /dev/null @@ -1,100 +0,0 @@ -
-

用户管理

- -
-
-
- - - -
-
-
-
- - - - - - - - - - - - - - - - - - - -
ID用户名注册时间真实姓名电子邮箱用户类型管理
{{ el.id }}{{ el.username }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.real_name }}{{ el.email }}{{ userType[el.admin_type]}} - - 提交 -
-
- -
-
- -
-
-

修改用户信息

- -
-
-
- -
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
- - -
-
- - -
-
- - -
-
-
- -
-
-
-
- diff --git a/template/src/oj/account/apply_reset_password.html b/template/src/oj/account/apply_reset_password.html deleted file mode 100644 index 5d65ccc2..00000000 --- a/template/src/oj/account/apply_reset_password.html +++ /dev/null @@ -1,40 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 找回登录信息 -{% endblock %} -{% block body %} -
-
-

找回登录信息


-
-

请输入你注册时使用的邮箱地址,系统将自动向你的邮箱发送一封含有您登录信息的电子邮件, - 你可以看到你的用户名,并可以选择重新设置登录密码,注意为了你的账户安全,重置密码链接仅在30分钟内有效

-
-
-
-
- - - -
-
-
-    -

- - -
-
-
- -
-
-
-
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/avatar.html b/template/src/oj/account/avatar.html deleted file mode 100644 index c4032a58..00000000 --- a/template/src/oj/account/avatar.html +++ /dev/null @@ -1,54 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 更换头像 -{% endblock %} -{% block body %} -
- -
- -
-
- -
-
- -
-
-
-

选择已有头像

-
-
- {% for i in "aaaaaaaaaaaaaaaaaaaa" %} -
-
- -
-
- {% endfor %} - -
- -
- - 水果头像由coding.net提供。 -
- - -
-

上传自定义头像

-
选择头像
-
-
-{% endblock %} - -{% block js_block %} - -{% endblock %} diff --git a/template/src/oj/account/change_password.html b/template/src/oj/account/change_password.html deleted file mode 100644 index 4edc0b1c..00000000 --- a/template/src/oj/account/change_password.html +++ /dev/null @@ -1,62 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} -用户设置 -{% endblock %} -{% block body %} -
- -
- -
-
- -
-
-
-
- - - -
-
-
- - - -
-
-
- - - -
-
-
- -    -

- - -
-
-
- -
-
-
-
-
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/login.html b/template/src/oj/account/login.html deleted file mode 100644 index 04302f17..00000000 --- a/template/src/oj/account/login.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 用户登录 -{% endblock %} -{% block body %} -
-
-

用户登录

- -
-
- - - -
-
-
- - - -
-
-
- - -
-
-
- -
- 忘记用户名/密码
- 还没有帐号?点击注册 - -
-
-
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/register.html b/template/src/oj/account/register.html deleted file mode 100644 index b3d95a53..00000000 --- a/template/src/oj/account/register.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 用户注册 -{% endblock %} -{% block body %} -
-
-

用户注册

- -
-
- - -
-
-
- - -
-
-
- - -
-
- - -
-
- - -
-
-
- - -
-
-
- - -
-
-
- -   

- -
-
-
- -
-
-
-
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/reset_password.html b/template/src/oj/account/reset_password.html deleted file mode 100644 index 703ff6fc..00000000 --- a/template/src/oj/account/reset_password.html +++ /dev/null @@ -1,53 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 找回登录信息 -{% endblock %} -{% block body %} -
-
-

找回登录信息


-
- -
-
- - -
-
- - -
-
- - - -
-
-
- - - -
-
-
-    -

- - -
-
-
- -
-
-
-
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/settings.html b/template/src/oj/account/settings.html deleted file mode 100644 index 678c9eab..00000000 --- a/template/src/oj/account/settings.html +++ /dev/null @@ -1,112 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} -用户设置 -{% endblock %} -{% block body %} -
- -
- -
-
- -
-
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - -
-
-
- - - -
-
-
- - - -
-
-
- - - -
-
-
- - -
- -
-
- - -
- -
-
- - - -
-
-
- - -
- -
- -
-
-
-
-
-{% endblock %} - -{% block js_block %} - -{% endblock %} diff --git a/template/src/oj/account/sso.html b/template/src/oj/account/sso.html deleted file mode 100644 index 23f04455..00000000 --- a/template/src/oj/account/sso.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 授权登录 -{% endblock %} -{% block body %} -
-
-

3秒钟后将使用账号{{ request.user.username }}登录{{ callback }}

- - - -
-
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/two_factor_auth.html b/template/src/oj/account/two_factor_auth.html deleted file mode 100644 index fe35d74d..00000000 --- a/template/src/oj/account/two_factor_auth.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 两步验证 -{% endblock %} -{% block body %} -
- -
- -
- -
- {% if not request.user.two_factor_auth %} -

扫描二维码开启两步验证

- - -
-
- - -
- -
- - {% else %} - - {% endif %} -
-
-{% endblock %} - -{% block js_block %} - -{% endblock %} diff --git a/template/src/oj/account/user_index.html b/template/src/oj/account/user_index.html deleted file mode 100644 index 9bf4edd4..00000000 --- a/template/src/oj/account/user_index.html +++ /dev/null @@ -1,93 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} -{{ user.username }}的主页 -{% endblock %} -{% block body %} -
-
-
- -
-
-

- {{ user.username }} - {% ifequal user.admin_type 2 %} - - {% endifequal %} - {% ifequal user.admin_type 1 %} - - {% endifequal %} - -

- {% if user.userprofile.mood %} -

{{ user.userprofile.mood }}

- {% endif %} - -
-
- {% if user.userprofile.school %} -

- {{ user.userprofile.school }} -

- {% endif %} - {% if user.userprofile.blog %} -

- {{ blog_link }} -

- {% endif %} - - {% if user.userprofile.hduoj_username %} -

- - - {{ user.userprofile.hduoj_username }} - -

- {% endif %} - - {% if user.userprofile.bestcoder_username %} -

- - - {{ user.userprofile.bestcoder_username }} - -

- {% endif %} - - {% if user.userprofile.codeforces_username %} -

- - - {{ user.userprofile.codeforces_username }} - -

- {% endif %} - -

- - {{ user.create_time }} -

-
- -
-
-
-
- -
-
-{% endblock %} \ No newline at end of file diff --git a/template/src/oj/announcement/_announcement_panel.html b/template/src/oj/announcement/_announcement_panel.html deleted file mode 100644 index fbe2fcae..00000000 --- a/template/src/oj/announcement/_announcement_panel.html +++ /dev/null @@ -1,19 +0,0 @@ -{% load announcement_list %} -
-
-

- - 公告 -

-
- {% public_announcement_list as announcements %} - {% if announcements %} - {% for item in announcements%} -

{{ forloop.counter }}.  {{ item.title }} -

- {% endfor %} - {% else %} -

暂无可显示的公告

- {% endif %} -
-
\ No newline at end of file diff --git a/template/src/oj/announcement/announcement.html b/template/src/oj/announcement/announcement.html deleted file mode 100644 index 8cbc2392..00000000 --- a/template/src/oj/announcement/announcement.html +++ /dev/null @@ -1,23 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - {{ announcement.title }} -{% endblock %} -{% block body %} -
-

{{ announcement.title }}

- -

- 作者:{{ announcement.created_by }} -     - 创建时间:{{ announcement.create_time }} - {% ifnotequal announcement.create_time announcement.last_update_time %} -     - 最后更新:{{ announcement.last_update_time }} - {% endifnotequal %} -

- -
-

{{ announcement.content|safe }}

-
-
-{% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/_contest_auto_refresh_check.html b/template/src/oj/contest/_contest_auto_refresh_check.html deleted file mode 100644 index cfa5e206..00000000 --- a/template/src/oj/contest/_contest_auto_refresh_check.html +++ /dev/null @@ -1,3 +0,0 @@ - -自动刷新 \ No newline at end of file diff --git a/template/src/oj/contest/_contest_filter.html b/template/src/oj/contest/_contest_filter.html deleted file mode 100644 index b4feda61..00000000 --- a/template/src/oj/contest/_contest_filter.html +++ /dev/null @@ -1,5 +0,0 @@ -
- -
\ No newline at end of file diff --git a/template/src/oj/contest/_contest_header.html b/template/src/oj/contest/_contest_header.html deleted file mode 100644 index 2e1fdf3c..00000000 --- a/template/src/oj/contest/_contest_header.html +++ /dev/null @@ -1,43 +0,0 @@ -{% load contest %} -

{{ contest.title }}

- -
- -
-
- - - - - - - - - - - - - - - - {% ifequal contest.contest_type 0 %} - - {% endifequal %} - {% ifequal contest.contest_type 1 %} - - {% endifequal %} - {% ifequal contest.contest_type 2 %} - - {% endifequal %} - {% ifequal contest.contest_type 3 %} - - {% endifequal %} - - - - -
开始时间结束时间状态比赛类型创建者
{{ contest.start_time }}{{ contest.end_time }}{{ contest|contest_status }}私有小组赛公开赛公开赛(密码保护)小组邀请赛{{ contest.created_by.username }}
-
-
{{ contest.description|safe }}
-
-

\ No newline at end of file diff --git a/template/src/oj/contest/_countdown_template.html b/template/src/oj/contest/_countdown_template.html deleted file mode 100644 index 951cdffe..00000000 --- a/template/src/oj/contest/_countdown_template.html +++ /dev/null @@ -1,19 +0,0 @@ - \ No newline at end of file diff --git a/template/src/oj/contest/contest_index.html b/template/src/oj/contest/contest_index.html deleted file mode 100644 index 95714f50..00000000 --- a/template/src/oj/contest/contest_index.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - {{ contest.title }} -{% endblock %} -{% block body %} -
- - {% include "oj/contest/_contest_header.html" %} -
-{% endblock %} -{% block js_block %} - {% include "oj/contest/_countdown_template.html" %} - -{% endblock %} diff --git a/template/src/oj/contest/contest_list.html b/template/src/oj/contest/contest_list.html deleted file mode 100644 index 91ba292d..00000000 --- a/template/src/oj/contest/contest_list.html +++ /dev/null @@ -1,90 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 比赛列表 -{% endblock %} -{% block body %} - {% load contest %} -
-
-
-
-
-
-
- - -
-
-
-
-
- {% if contests %} - - - - - - - - - - - - {% for item in contests %} - - - - - - {% ifequal item.contest_type 0 %} - - {% endifequal %} - {% ifequal item.contest_type 1 %} - - {% endifequal %} - {% ifequal item.contest_type 2 %} - - {% endifequal %} - {% ifequal item.contest_type 3 %} - - {% endifequal %} - - - - {% endfor %} - -
#比赛名称开始时间比赛类型状态
{{ item.id }}{{ item.title }}{{ item.start_time }}私有小组赛公开赛公开赛(密码保护)小组邀请赛{{ item|contest_status }}
- {% if request.user.is_authenticated %} - {% include "oj/contest/_contest_filter.html" %} - {% endif %} - - {% else %} -

当前没有合适的比赛,你可以尝试到小组列表申请加入一些小组,以便参加小组内部的比赛

- {% include "oj/contest/_contest_filter.html" %} - {% endif %} -
-
- -
- {% include "oj/announcement/_announcement_panel.html" %} -
-
-
-{% endblock %} - - -{% block js_block %} -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/contest_problems_list.html b/template/src/oj/contest/contest_problems_list.html deleted file mode 100644 index 469b366e..00000000 --- a/template/src/oj/contest/contest_problems_list.html +++ /dev/null @@ -1,70 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 比赛题目列表 -{% endblock %} -{% block body %} - {% load problem %} -
- - -
-
- - - - -
- - -
-
- - - - - - - - - - - {% for item in contest_problems %} - - - - - - - {% endfor %} - -
#题目通过率
- - - {{ item.sort_index }} - - {{ item.title }} - {{ item|accepted_radio }}
- -
-
- -
-
-{% endblock %} - -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/contest_rank.html b/template/src/oj/contest/contest_rank.html deleted file mode 100644 index 8dcdc4f6..00000000 --- a/template/src/oj/contest/contest_rank.html +++ /dev/null @@ -1,92 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 比赛排名 -{% endblock %} -{% block body %} - {% load contest %} -
- - - -
-
-

排名({% if contest.real_time_rank %}实时{% else %}已封榜{% endif %}) -

- {% if force_real_time_rank %} - - {% endif %} - {% if rank %} - - - - - - - - {% for item in contest_problems %} - - {% endfor %} - - - - {% for item in rank %} - - - - - - {% autoescape off %} - {% for problem in contest_problems %} - - {% endfor %} - {% endautoescape %} - - {% endfor %} - -
#用户名AC / 总提交用时 + 罚时 - {{ item.sort_index }} -
{% if item.rank_number %}{{ item.rank_number }}{% else %}-{% endif %} - - {{ item.user__username }} - - {% if show_real_name %} - ({{ item.user__real_name }}) - {% endif %} - {{ item.total_ac_number }} / {{ item.total_submission_number }}{% if item.total_time %}{{ item.total_time|format_seconds }}{% else %}--{% endif %} - {% get_submission_content item problem %} -
- {% include "oj/contest/_contest_auto_refresh_check.html" %} - {% else %} -

还没有结果

- {% include "oj/contest/_contest_auto_refresh_check.html" %} - {% endif %} - -
-
-
-{% endblock %} - - -{% block js_block %} - {% if auto_refresh %} - - {% endif %} -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/no_contest_permission.html b/template/src/oj/contest/no_contest_permission.html deleted file mode 100644 index c6ff67c2..00000000 --- a/template/src/oj/contest/no_contest_permission.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - {{ contest.title }} -{% endblock %} -{% block body %} -
- - {% include "oj/contest/_contest_header.html" %} - {% ifequal reason "password_protect" %} -
-
- - -
- -
- {% endifequal %} - {% ifequal reason "group_limited" %} - - {% endifequal %} - {% ifequal reason "contest_not_start" %} - - {% endifequal %} -
-{% endblock %} -{% block js_block %} - {% include "oj/contest/_countdown_template.html" %} - - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/submissions_list.html b/template/src/oj/contest/submissions_list.html deleted file mode 100644 index 0a90c7b7..00000000 --- a/template/src/oj/contest/submissions_list.html +++ /dev/null @@ -1,125 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 比赛提交列表 -{% endblock %} -{% block body %} - - {% load submission %} - {% load user %} -
-
- -
- - - {% if submissions %} - - - - - - - - - - - - - {% for item in submissions %} - - - - - - - - - - {% endfor %} - - {% else %} -

本场比赛还没有提交记录

- {% endif %} -
#题目名称用户提交时间 - - 运行时间 - -
- {% if item.show_link %} - {{ forloop.counter |add:start_id }} - {% else %} - {{ forloop.counter |add:start_id }} - {% endif %} - - {{ item.title }} - {{ item.user_id|get_username }}{{ item.create_time }} - {{ item.language|translate_language }} - - {% if item.accepted_answer_time %} - {{ item.accepted_answer_time }}ms - {% else %} - -- - {% endif %} - - {{ item.result|translate_result }} -
- - -
-{% endblock %} diff --git a/template/src/oj/group/group.html b/template/src/oj/group/group.html deleted file mode 100644 index bbf6c32b..00000000 --- a/template/src/oj/group/group.html +++ /dev/null @@ -1,49 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - {{ group.name }} -{% endblock %} -{% block body %} -
- -

{{ group.name }}

- -

发布时间 : {{ group.create_time }}   - 创建者 : {{ group.created_by }} -

- -
-
- - -

{{ group.description|safe }}

-
-
- {% if not joined %} -
-
- {% ifequal group.join_group_setting 1 %} -
- - - -
- {% endifequal %} - -
- -
- -
- {% endif %} -
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/group/group_list.html b/template/src/oj/group/group_list.html deleted file mode 100644 index 575102bd..00000000 --- a/template/src/oj/group/group_list.html +++ /dev/null @@ -1,78 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 小组列表 -{% endblock %} -{% block body %} - {% load problem %} -
-
-
-
-
-
-
- - -
-
-
-
-
- - - - - - - - - - - - {% for item in groups %} - - - - - - - - {% endfor %} - -
#名称加入方式创建者创建时间
{{ item.id }}{{ item.name }} - {% ifequal item.join_group_setting 1 %} - 需要申请 - {% endifequal %} - {% ifequal item.join_group_setting 0 %} - 无需申请 - {% endifequal %} - {% ifequal item.join_group_setting 2 %} - 不允许加入 - {% endifequal %} - {{ item.created_by }}{{ item.create_time }}
- -
-
- -
- {% include "oj/announcement/_announcement_panel.html" %} -
-
-
-{% endblock %} diff --git a/template/src/oj/group/my_application.html b/template/src/oj/group/my_application.html deleted file mode 100644 index 44b286c6..00000000 --- a/template/src/oj/group/my_application.html +++ /dev/null @@ -1,28 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 我的申请详情 -{% endblock %} -{% block body %} - -
- - -

{{ application.message|safe }}

- - {% if application.status %} - {% if application.accepted %} -

管理员接受了你的请求

- {% else %} -

管理员拒绝了你的请求

- {% endif %} - {% else %} -

待审核

- {% endif %} -
-{% endblock %} \ No newline at end of file diff --git a/template/src/oj/group/my_application_list.html b/template/src/oj/group/my_application_list.html deleted file mode 100644 index e5ea6271..00000000 --- a/template/src/oj/group/my_application_list.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 我的申请列表 -{% endblock %} -{% block body %} - -
- - {% if applications %} - - - - - - - - - - {% for item in applications %} - - - - {% if item.status %} - {% if item.accepted %} - - {% else %} - - {% endif %} - {% else %} - - {% endif %} - - {% endfor %} - - -
#提交时间结果
{{ forloop.counter }}{{ item.create_time }}通过拒绝未处理
- {% else %} -

你还没有申请该小组

- {% endif %} -
-{% endblock %} \ No newline at end of file diff --git a/template/src/oj/index.html b/template/src/oj/index.html deleted file mode 100644 index feef7b46..00000000 --- a/template/src/oj/index.html +++ /dev/null @@ -1,192 +0,0 @@ - - -{% load website_info %} - - - {% show_website_info "website_name" %} - 首页 - - - - - - - - - - - - - - - - -
-
-
-

{% show_website_info "website_name" %}

- -

新的面貌,新的开始~

-
-
↓继续滚动~
-
-
- -
- - -

分布式评测

- -

再也不怕一直是 waiting 了~~

-
- -
-
- -
- - -

多种比赛模式

- -

ACM 模式,AC 数量模式,单题得分模式等

-
-
-
- -
- - -

自由举办小组赛

- -

内部比赛,日常作业,期末考试,通通搞定

-
-
-
- - - diff --git a/template/src/oj/problem/_mathjax_js.html b/template/src/oj/problem/_mathjax_js.html deleted file mode 100644 index 9c64ef68..00000000 --- a/template/src/oj/problem/_mathjax_js.html +++ /dev/null @@ -1,14 +0,0 @@ - - \ No newline at end of file diff --git a/template/src/oj/problem/_problem_header.html b/template/src/oj/problem/_problem_header.html deleted file mode 100644 index dc809416..00000000 --- a/template/src/oj/problem/_problem_header.html +++ /dev/null @@ -1,11 +0,0 @@ -

{{ problem.title }}

- -

- 发布时间: {{ problem.create_time }}   - {% if problem.last_update_time %}最后更新: {{ problem.last_update_time }}  {% endif %} - 时间限制: {{ problem.time_limit }}ms   - 内存限制: {{ problem.memory_limit }}M - {% if problem.spj %} -   SPJ - {% endif %} -

\ No newline at end of file diff --git a/template/src/oj/problem/_problem_main_info.html b/template/src/oj/problem/_problem_main_info.html deleted file mode 100644 index cdc80a0e..00000000 --- a/template/src/oj/problem/_problem_main_info.html +++ /dev/null @@ -1,29 +0,0 @@ -
- - -
{{ problem.description|safe }}
-
-
- - -

{{ problem.input_description }}

-
-
- - -

{{ problem.output_description }}

-
- {% for item in samples %} -
- - 复制 -
-{{ item.input }}
- -
-
- -
-{{ item.output }}
-
- {% endfor %} \ No newline at end of file diff --git a/template/src/oj/problem/_submit_problem.html b/template/src/oj/problem/_submit_problem.html deleted file mode 100644 index f21f45df..00000000 --- a/template/src/oj/problem/_submit_problem.html +++ /dev/null @@ -1,25 +0,0 @@ -
- -
- - - -
-
-
- - -
-
-
- - -
\ No newline at end of file diff --git a/template/src/oj/problem/contest_problem.html b/template/src/oj/problem/contest_problem.html deleted file mode 100644 index a2409bb2..00000000 --- a/template/src/oj/problem/contest_problem.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - {{ problem.title }} -{% endblock %} -{% block body %} -
- - {% include "oj/problem/_problem_header.html" %} -
- {% include "oj/problem/_problem_main_info.html" %} - {% if problem.hint %} -
- - -
{{ problem.hint|safe }}
-
- {% endif %} - {% if show_submit_code_area %} - {% include "oj/problem/_submit_problem.html" %} - {% endif %} - {% if show_warning %} - - {% endif %} -
-
-
-
-
-{% endblock %} -{% block js_block %} - - {% include "oj/problem/_mathjax_js.html" %} -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/problem/problem.html b/template/src/oj/problem/problem.html deleted file mode 100644 index 15e93d2b..00000000 --- a/template/src/oj/problem/problem.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - {{ problem.title }} -{% endblock %} -{% block body %} -
- - {% include "oj/problem/_problem_header.html" %} - -
- {% include "oj/problem/_problem_main_info.html" %} -
- -
- {% if problem.hint %} -
- -
{{ problem.hint|safe }}
-
- {% endif %} -
- -

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

-
- {% if problem.source %} -
- -
{{ problem.source }}
-
- {% endif %} - {% include "oj/problem/_submit_problem.html" %} -
-
-
-
-
-{% endblock %} -{% block js_block %} - - {% include "oj/problem/_mathjax_js.html" %} -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/problem/problem_list.html b/template/src/oj/problem/problem_list.html deleted file mode 100644 index 8968dc3a..00000000 --- a/template/src/oj/problem/problem_list.html +++ /dev/null @@ -1,95 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 题目列表 -{% endblock %} -{% block body %} - {% load problem %} -
-
-
-
-
-
-
- - -
-
-
-
-
- - - - - - - - - - - - {% for item in problems %} - - - - - - - - {% endfor %} - -
#题目难度通过率
{{ item.id }}{{ item.title }} - {% ifequal item.difficulty 1 %} - 简单 - {% else %} - {% ifequal item.difficulty 2 %} - 中等 - {% else %} - 难 - {% endifequal %} - {% endifequal %}{{ item|accepted_radio }}
- -
-
- -
- {% include "oj/announcement/_announcement_panel.html" %} -
-
-

- - 分类 -

-
-
    - {% for item in tags %} -
  • - {{ item.name }} - {{ item.problem_number }} -
  • - {% endfor %} -
-
- -
-
-
-{% endblock %} - -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/submission/my_submission.html b/template/src/oj/submission/my_submission.html deleted file mode 100644 index 46e2b2fc..00000000 --- a/template/src/oj/submission/my_submission.html +++ /dev/null @@ -1,142 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 我的提交详情 -{% endblock %} -{% block css_block %} - -{% endblock %} -{% block body %} - {% load submission %} -
- - {% include "oj/problem/_problem_header.html" %} -
-
-

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

- {% ifnotequal request.user.id submission.user_id %} -

作者:{{ user.username }}

- {% endifnotequal %} - {% ifequal submission.result 0 %} -

时间: {{ submission.accepted_answer_time }}ms 语言 : - {{ submission.language|translate_language }} -

- {% endifequal %} - {% ifequal submission.result 4 %} -
{{ submission.info }}
- {% endifequal %} -

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

-
-
- {% ifequal request.user.admin_type 2 %} -

本调试信息仅超级管理员可见

- {% ifequal submission.result 7 %} -
System Error: {{ submission.info }}
- {% else %} - {% ifequal submission.result 4 %} -
Compile error: {{ info }}
- {% else %} - {% ifequal submission.result 8 %} -
Waiting
- {% else %} - - - - - - - - - - - - {% for item in info %} - - - - - - - - {% endfor %} - -
测试用例实际运行时间CPU时间内存结果
{{ item.test_case }}{{ item.real_time }} ms{{ item.cpu_time }} ms{{ item.memory }} Byte{{ item.result|translate_result }} {% if item.signal %}({{ item.signal|translate_signal }}){% endif %}
- {% endifequal %} - {% endifequal %} - {% endifequal %} - {% endifequal %} -
- -
- {% if can_share %} -
- {% if submission.shared %} - - {% else %} - - {% endif %} - -
- {% endif %} - - -
- -{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/submission/problem_my_submissions_list.html b/template/src/oj/submission/problem_my_submissions_list.html deleted file mode 100644 index c7183963..00000000 --- a/template/src/oj/submission/problem_my_submissions_list.html +++ /dev/null @@ -1,65 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 我的提交列表 -{% endblock %} -{% block body %} - - {% load submission %} -
- - {% include "oj/problem/_problem_header.html" %} - {% if submissions %} - - - - - - - - - - - - {% for item in submissions %} - - - - - - - - {% endfor %} - - -
#提交时间语言运行时间结果
- {{ forloop.counter }} - {{ item.create_time }} - {{ item.language|translate_language }} - - {% if item.accepted_answer_time != None %} - {{ item.accepted_answer_time }}ms - {% else %} - -- - {% endif %} - - {{ item.result|translate_result }} -
- {% else %} -

你还没有提交该题目

- {% endif %} -
-{% endblock %} \ No newline at end of file diff --git a/template/src/oj/submission/submissions_list.html b/template/src/oj/submission/submissions_list.html deleted file mode 100644 index b74ddcfc..00000000 --- a/template/src/oj/submission/submissions_list.html +++ /dev/null @@ -1,105 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 我的提交列表 -{% endblock %} -{% block body %} -{% load submission %} -
-
- - - - - - - - - - - - - - {% for item in submissions %} - - - - - - - - - - - {% endfor %} - - -
#用户题目名称提交时间 - - 运行时间 - -
- {% if item.show_link %} - {{ forloop.counter |add:start_id }} - {% else %} - {{ forloop.counter |add:start_id }} - {% endif %} - {{ item.user.username }} - {{ item.title }} - {{ item.create_time }} - {{ item.language|translate_language }} - - {% if item.accepted_answer_time != None %} - {{ item.accepted_answer_time }}ms - {% else %} - -- - {% endif %} - - {{ item.result|translate_result }} -
- - {% if not submissions %} -

你还没有提交记录!

- {% endif %} -
- -
-{% endblock %} \ No newline at end of file diff --git a/template/src/oj_base.html b/template/src/oj_base.html deleted file mode 100644 index 3f709f24..00000000 --- a/template/src/oj_base.html +++ /dev/null @@ -1,108 +0,0 @@ - - -{% load website_info %} - - - - - - - - {% block title %} - {% show_website_info "website_name" %} - {% endblock %} - - - - - - - - {% block css_block %}{% endblock %} - - - - - - - - - - - - - - - -{% block body %}{% endblock %} - - - - - -{% block js_block %}{% endblock %} - - - - - diff --git a/template/src/utils/about.html b/template/src/utils/about.html deleted file mode 100644 index e9d5be07..00000000 --- a/template/src/utils/about.html +++ /dev/null @@ -1,34 +0,0 @@ -{% extends "oj_base.html" %} -{% block body %} -
- -
-

ACM 简介

- - -

- ACM国际大学生程序设计竞赛(英语:ACM International Collegiate Programming Contest, - ICPC)是由美国电脑协会(ACM)主办的,一项旨在展示大学生创新能力、团队精神和在压力下编写程序、分析和解决问题能力的年度竞赛。 - 经过30多年的发展,ACM国际大学生程序设计竞赛已经发展成为最具影响力的大学生计算机竞赛。赛事目前由IBM公司赞助。 -

- - -

开源

- -

- GitHub - 问题反馈 - 开发团队 -

- - -
-
-{% endblock %} diff --git a/template/src/utils/error.html b/template/src/utils/error.html deleted file mode 100644 index 98fbeefc..00000000 --- a/template/src/utils/error.html +++ /dev/null @@ -1,151 +0,0 @@ -{% extends "oj_base.html" %} -{% block css_block %} - -{% endblock %} -{% block body %} -
-

出错了~~ {{ error }}

-
-
- -
// C
-#include <stdio.h>
-int main()
-{
-    printf("An error occurred\n");
-    return 0;
-}
-
// C++
-#include <iostream>
-int main()
-{
-    std::cout << "An error occurred" << std::endl;
-    return 0;
-}
-
// Java
-public class Main
-{
-    public static void main(String[] args)
-    {
-        System.out.println("An error occurred");
-    }
-}
-
// JavaScript
-console.log("An error occurred\n")
-
// Python
-print "An error occurred"
- -
-
-{% endblock %} \ No newline at end of file diff --git a/template/src/utils/help.html b/template/src/utils/help.html deleted file mode 100644 index 3c9a1539..00000000 --- a/template/src/utils/help.html +++ /dev/null @@ -1,54 +0,0 @@ -{% extends "oj_base.html" %} -{% block body %} -
- -
-

判题系统

-

判题结果

-
    -
  • Accepted: 你的答案符合判题标准
  • -
  • Runtime Error: 你的程序运行时出现错误(指针越界,栈溢出,有未处理的异常,主函数返回值非零等)
  • -
  • Time Limit Exceeded: 你的程序执行时间超出题目要求
  • -
  • Memory Limit Exceeded: 你的程序内存使用超出题目要求
  • -
  • Compile Error: 你的程序在编译(包括链接)时出现错误
  • -
  • Wrong Answer: 你的程序输出的答案不符合判题标准
  • -
  • System Error: 判题系统发生故障,请等待重判
  • -
  • Waiting: 你的提交正在等待处理
  • -
- -

编译参数

-
    -
  • C(GCC 4.8)
  • -
    gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main
    -
  • C++(G++ 4.3)
  • -
    g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main
    -
  • Java(Oracle JDK 1.7)
  • -
    -//编译
    -javac {src_path} -d {exe_path}
    -//运行
    -java -cp {exe_path} Main
    - -
- -

常见问题

-
    -
  • 无特殊说明,请使用标准输入输出。
  • -
  • Java 代码需使用 Main 作为主类名。C/C++代码使用int main(),并且需要return 0;
  • -
  • C/C++ 的64位整数类型,请使用 long long 声明,使用 cin/cout%lld 输入输出。 - 使用__int64会导致编译错误。
  • -
  • 程序执行时间指CPU时间,占用内存按执行过程中内存消耗的峰值计,有多组测试数据时以最大的时间和内存消耗为准
  • -
  • 判题的时候会去除你的输出的最后的换行和空格,然后与去除最后的换行和空格的答案做比较,如果不一致就是 Wrong Answer。 - 其余的行末空格和空行不去除,看清楚题目的要求。没有格式错误。 -
  • -
-
-
-{% endblock %} diff --git a/template/src/utils/rank.html b/template/src/utils/rank.html deleted file mode 100644 index 434b48f2..00000000 --- a/template/src/utils/rank.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "oj_base.html" %} -{% block body %} - -
-
- - {% if request.user.is_authenticated %} - - - {% endif %} - - - - - - - - - - - - {% for rank in ranks %} - - - - - - - - - {% endfor %} - - -
我的数据: 通过的题目数量: {{ request.user.userprofile.accepted_problem_number }} - 总提交数量: {{ request.user.userprofile.submission_number }}
#用户名签名通过的题目数量总提交数量AC 率
{{ forloop.counter |add:start_id }}{{ rank.user.username }}{% if rank.mood %}{{ rank.mood }}{% endif %}{{ rank.accepted_problem_number }}{{ rank.submission_number }}{% widthratio rank.accepted_problem_number rank.submission_number 100 %}%
- -
-
-{% endblock %} diff --git a/template/src/utils/reset_password_email.html b/template/src/utils/reset_password_email.html deleted file mode 100644 index 5a0b5915..00000000 --- a/template/src/utils/reset_password_email.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - -
- - - - - - -
- {{ website_name }} 登录信息找回 -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Hello, {{ username }}: -
- 您刚刚在 {{ website_name }} 申请了找回登录信息服务。 -
- 请在30分钟内点击下面链接设置您的新密码: -
- 重置密码 -
- 如果上面的链接点击无效,请复制以下链接至浏览器的地址栏直接打开。 -
- - {{ link }} - -
- 如果您没有提出过该申请,请忽略此邮件。有可能是其他用户误填了您的邮件地址,我们不会对你的帐户进行任何修改。 - 请不要向他人透露本邮件的内容,否则可能会导致您的账号被盗。 -
-
\ No newline at end of file diff --git a/tools/runtest.sh b/tools/runtest.sh old mode 100644 new mode 100755 diff --git a/utils/management/commands/initadmin.py b/utils/management/commands/initadmin.py index d23e7f18..cfac5d4e 100644 --- a/utils/management/commands/initadmin.py +++ b/utils/management/commands/initadmin.py @@ -1,6 +1,6 @@ # coding=utf-8 from django.core.management.base import BaseCommand -from account.models import User, SUPER_ADMIN, UserProfile +from account.models import User, UserProfile, AdminType from utils.shortcuts import rand_str @@ -8,7 +8,7 @@ class Command(BaseCommand): def handle(self, *args, **options): try: admin = User.objects.get(username="root") - if admin.admin_type == SUPER_ADMIN: + if admin.admin_type == AdminType.SUPER_ADMIN: self.stdout.write(self.style.WARNING("Super admin user 'root' already exists, " "would you like to reset it's password?\n" "Input yes to confirm: ")) @@ -25,7 +25,7 @@ class Command(BaseCommand): else: self.stdout.write(self.style.ERROR("User 'root' is not super admin.")) except User.DoesNotExist: - user = User.objects.create(username="root", real_name="root", email="root@oj.com", admin_type=SUPER_ADMIN) + user = User.objects.create(username="root", email="root@oj.com", admin_type=AdminType.SUPER_ADMIN) rand_password = rand_str(length=6) user.set_password(rand_password) user.save()