diff --git a/conf/tests.py b/conf/tests.py index c526489c..2a4451b2 100644 --- a/conf/tests.py +++ b/conf/tests.py @@ -90,7 +90,7 @@ class JudgeServerStatusAPITest(APITestCase): self.assertEqual(JudgeServerToken.objects.first().token, resp.data["data"]["token"]) -class JudgeServerHeartbeatest(APITestCase): +class JudgeServerHeartbeatTest(APITestCase): def setUp(self): self.url = self.reverse("judge_server_heartbeat_api") self.data = {"hostname": "testhostname", "judger_version": "1.0.4", "cpu_core": 4, @@ -123,3 +123,9 @@ class JudgeServerHeartbeatest(APITestCase): resp = self.client.post(self.url, data=data, **{"HTTP_X_JUDGE_SERVER_TOKEN": self.hashed_token}) self.assertSuccess(resp) self.assertEqual(JudgeServer.objects.get(hostname=self.data["hostname"]).judger_version, data["judger_version"]) + + +class LanguageListAPITest(APITestCase): + def test_get_languages(self): + resp = self.client.get(self.reverse("language_list_api")) + self.assertSuccess(resp) diff --git a/conf/urls/oj.py b/conf/urls/oj.py index db3ede37..b5a06e5b 100644 --- a/conf/urls/oj.py +++ b/conf/urls/oj.py @@ -1,8 +1,9 @@ from django.conf.urls import url -from ..views import JudgeServerHeartbeatAPI, WebsiteConfigAPI +from ..views import JudgeServerHeartbeatAPI, LanguagesAPI, WebsiteConfigAPI urlpatterns = [ url(r"^website$", WebsiteConfigAPI.as_view(), name="website_info_api"), - url(r"^judge_server_heartbeat$", JudgeServerHeartbeatAPI.as_view(), name="judge_server_heartbeat_api") + url(r"^judge_server_heartbeat$", JudgeServerHeartbeatAPI.as_view(), name="judge_server_heartbeat_api"), + url(r"^languages$", LanguagesAPI.as_view(), name="language_list_api") ] diff --git a/conf/views.py b/conf/views.py index 4e9f12e1..1a6118f6 100644 --- a/conf/views.py +++ b/conf/views.py @@ -3,6 +3,7 @@ import hashlib from django.utils import timezone from account.decorators import super_admin_required +from judge.languages import languages, spj_languages from utils.api import APIView, CSRFExemptAPIView, validate_serializer from utils.shortcuts import rand_str @@ -84,7 +85,7 @@ class JudgeServerAPI(APIView): @super_admin_required def delete(self, request): - pass + return self.success() class JudgeServerHeartbeatAPI(CSRFExemptAPIView): @@ -125,3 +126,8 @@ class JudgeServerHeartbeatAPI(CSRFExemptAPIView): last_heartbeat=timezone.now(), ) return self.success() + + +class LanguagesAPI(APIView): + def get(self, request): + return self.success({"languages": languages, "spj_languages": spj_languages}) diff --git a/contest/models.py b/contest/models.py index d620a4c2..13e3fc76 100644 --- a/contest/models.py +++ b/contest/models.py @@ -9,21 +9,20 @@ from utils.models import RichTextField class ContestType(object): - GROUP_CONTEST = 0 - PUBLIC_CONTEST = 1 - PASSWORD_PROTECTED_CONTEST = 2 - PASSWORD_PROTECTED_GROUP_CONTEST = 3 + GROUP_CONTEST = "group_contest" + PUBLIC_CONTEST = "public_contest" + PASSWORD_PROTECTED_CONTEST = "password_protected_contest" class ContestStatus(object): - CONTEST_NOT_START = 1 - CONTEST_ENDED = -1 - CONTEST_UNDERWAY = 0 + CONTEST_NOT_START = "contest_not_start" + CONTEST_ENDED = "contest_ended" + CONTEST_UNDERWAY = "contest_underway" class ContestRuleType(object): - ACM = 0 - OI = 1 + ACM = "acm" + OI = "oi" class Contest(models.Model): @@ -33,9 +32,9 @@ class Contest(models.Model): real_time_rank = models.BooleanField() password = models.CharField(max_length=30, blank=True, null=True) # enum of ContestType - contest_type = models.IntegerField() + contest_type = models.CharField(max_length=36) # enum of ContestRuleType - rule_type = models.IntegerField() + rule_type = models.CharField(max_length=36) start_time = models.DateTimeField() end_time = models.DateTimeField() create_time = models.DateTimeField(auto_now_add=True) @@ -100,4 +99,4 @@ class OIContestRank(ContestRank): submission_info = JSONField(default={}) class Meta: - db_table = "oi_contenst_rank" + db_table = "oi_contest_rank" diff --git a/judge/__init__.py b/judge/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/judge/languages.py b/judge/languages.py new file mode 100644 index 00000000..e21179a9 --- /dev/null +++ b/judge/languages.py @@ -0,0 +1,89 @@ + + +_c_lang_config = { + "compile": { + "src_name": "main.c", + "exe_name": "main", + "max_cpu_time": 3000, + "max_real_time": 5000, + "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}", + }, + "run": { + "command": "{exe_path}", + "seccomp_rule": "c_cpp", + } +} + +_c_lang_spj_compile = { + "src_name": "spj-{spj_version}.c", + "exe_name": "spj-{spj_version}", + "max_cpu_time": 3000, + "max_real_time": 5000, + "max_memory": 1024 * 1024 * 1024, + "compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}" +} + +_c_lang_spj_config = { + "exe_name": "spj-{spj_version}", + "command": "{exe_path} {in_file_path} {user_out_file_path}", + "seccomp_rule": "c_cpp" +} + +_cpp_lang_config = { + "compile": { + "src_name": "main.cpp", + "exe_name": "main", + "max_cpu_time": 3000, + "max_real_time": 5000, + "max_memory": 128 * 1024 * 1024, + "compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}", + }, + "run": { + "command": "{exe_path}", + "seccomp_rule": "c_cpp" + } +} + +_java_lang_config = { + "compile": { + "src_name": "Main.java", + "exe_name": "Main", + "max_cpu_time": 3000, + "max_real_time": 5000, + "max_memory": -1, + "compile_command": "/usr/bin/javac {src_path} -d {exe_dir} -encoding UTF8" + }, + "run": { + "command": "/usr/bin/java -cp {exe_dir} -Xss1M -XX:MaxPermSize=16M -XX:PermSize=8M -Xms16M -Xmx{max_memory}k " + "-Djava.security.manager -Djava.security.policy==/etc/java_policy -Djava.awt.headless=true Main", + "seccomp_rule": None, + "env": ["MALLOC_ARENA_MAX=1"] + } +} + + +_py2_lang_config = { + "compile": { + "src_name": "solution.py", + "exe_name": "solution.pyc", + "max_cpu_time": 3000, + "max_real_time": 5000, + "max_memory": 128 * 1024 * 1024, + "compile_command": "/usr/bin/python -m py_compile {src_path}", + }, + "run": { + "command": "/usr/bin/python {exe_path}", + "seccomp_rule": None, + } +} + +languages = [ + {"config": _c_lang_config, "spj": {"compile": _c_lang_spj_compile, "config": _c_lang_spj_config}, + "name": "C", "description": "GCC 4.8"}, + {"config": _cpp_lang_config, "name": "C++", "description": "G++ 4.8"}, + {"config": _java_lang_config, "description": "OpenJDK 1.7"}, + {"config": _py2_lang_config, "description": "Python 2.7"} +] + +spj_languages = list(filter(lambda item: "spj" in item, languages)) diff --git a/oj/settings.py b/oj/settings.py index 1ececa5c..42b6a464 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -42,6 +42,7 @@ INSTALLED_APPS = ( 'account', 'announcement', 'conf', + 'problem', 'utils', 'rest_framework', @@ -57,7 +58,8 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'account.middleware.AdminRequiredMiddleware', - 'account.middleware.SessionSecurityMiddleware' + 'account.middleware.SessionSecurityMiddleware', + 'account.middleware.TimezoneMiddleware' ) ROOT_URLCONF = 'oj.urls' @@ -85,7 +87,7 @@ WSGI_APPLICATION = 'oj.wsgi.application' LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'Asia/Shanghai' +TIME_ZONE = 'UTC' USE_I18N = True diff --git a/oj/urls.py b/oj/urls.py index 1e132799..a591a0b8 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -5,5 +5,6 @@ urlpatterns = [ url(r"^api/admin/", include("account.urls.admin")), url(r"^api/admin/", include("announcement.urls.admin")), url(r"^api/", include("conf.urls.oj")), - url(r"^api/admin/", include("conf.urls.admin")) + url(r"^api/admin/", include("conf.urls.admin")), + url(r"^api/", include("problem.urls.oj")), ] diff --git a/problem/serializers.py b/problem/serializers.py new file mode 100644 index 00000000..e69de29b diff --git a/problem/tests.py b/problem/tests.py index e69de29b..5db014c5 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -0,0 +1,12 @@ +from utils.api.tests import APITestCase + +from .models import ProblemTag + + +class ProblemTagListAPITest(APITestCase): + def test_get_tag_list(self): + ProblemTag.objects.create(name="name1") + ProblemTag.objects.create(name="name2") + resp = self.client.get(self.reverse("problem_tag_list_api")) + self.assertSuccess(resp) + self.assertEqual(resp.data["data"], ["name1", "name2"]) diff --git a/problem/urls/__init__.py b/problem/urls/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/problem/urls/oj.py b/problem/urls/oj.py new file mode 100644 index 00000000..c918fbde --- /dev/null +++ b/problem/urls/oj.py @@ -0,0 +1,7 @@ +from django.conf.urls import url + +from ..views import ProblemTagAPI + +urlpatterns = [ + url(r"^tags$", ProblemTagAPI.as_view(), name="problem_tag_list_api") +] diff --git a/problem/views.py b/problem/views.py index e69de29b..355df7d9 100644 --- a/problem/views.py +++ b/problem/views.py @@ -0,0 +1,8 @@ +from utils.api import APIView + +from .models import ProblemTag + + +class ProblemTagAPI(APIView): + def get(self, request): + return self.success([item.name for item in ProblemTag.objects.all().order_by("id")]) diff --git a/utils/api/tests.py b/utils/api/tests.py index 50b12c78..32b0db38 100644 --- a/utils/api/tests.py +++ b/utils/api/tests.py @@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse from django.test.testcases import TestCase from rest_framework.test import APIClient -from account.models import AdminType, User +from account.models import AdminType, User, UserProfile class APITestCase(TestCase): @@ -11,6 +11,7 @@ class APITestCase(TestCase): def create_user(self, username, password, admin_type=AdminType.REGULAR_USER, login=True): user = User.objects.create(username=username, admin_type=admin_type) user.set_password(password) + UserProfile.objects.create(user=user, time_zone="Asia/Shanghai") user.save() if login: self.client.login(username=username, password=password)