mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-12-28 16:12:13 +00:00
Merge pull request #118 from QingdaoU/zemal_dev
This commit is contained in:
commit
ef6679acb0
@ -1,10 +1,7 @@
|
||||
import functools
|
||||
|
||||
from utils.api import JSONResponse
|
||||
|
||||
from .models import ProblemPermission
|
||||
|
||||
from contest.models import Contest, ContestType, ContestStatus, ContestRuleType
|
||||
from utils.api import JSONResponse, APIError
|
||||
from .models import ProblemPermission
|
||||
|
||||
|
||||
class BasePermissionDecorator(object):
|
||||
@ -90,8 +87,7 @@ def check_contest_permission(check_type="details"):
|
||||
if not user.is_authenticated():
|
||||
return self.error("Please login first.")
|
||||
# password error
|
||||
if ("accessible_contests" not in request.session) or \
|
||||
(self.contest.id not in request.session["accessible_contests"]):
|
||||
if self.contest.id not in request.session.get("accessible_contests", []):
|
||||
return self.error("Password is required.")
|
||||
|
||||
# regular user get contest problems, ranks etc. before contest started
|
||||
@ -104,7 +100,10 @@ def check_contest_permission(check_type="details"):
|
||||
return self.error(f"No permission to get {check_type}")
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return _check_permission
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def ensure_created_by(obj, user):
|
||||
if not user.is_admin_role() or (user.is_admin() and obj.created_by != user):
|
||||
raise APIError(msg=f"{obj.__class__.__name__} does not exist")
|
||||
|
@ -86,7 +86,7 @@ class UserProfileSerializer(serializers.ModelSerializer):
|
||||
class EditUserSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
username = serializers.CharField(max_length=32)
|
||||
real_name = serializers.CharField(max_length=32, allow_blank=True)
|
||||
real_name = serializers.CharField(max_length=32, allow_blank=True, allow_null=True)
|
||||
password = serializers.CharField(min_length=6, allow_blank=True, required=False, default=None)
|
||||
email = serializers.EmailField(max_length=64)
|
||||
admin_type = serializers.ChoiceField(choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN))
|
||||
|
@ -153,3 +153,32 @@ class TestCasePruneAPITest(APITestCase):
|
||||
resp = self.client.delete(self.url)
|
||||
self.assertSuccess(resp)
|
||||
mocked_delete_one.assert_called_once_with(valid_id)
|
||||
|
||||
|
||||
class ReleaseNoteAPITest(APITestCase):
|
||||
def setUp(self):
|
||||
self.url = self.reverse("get_release_notes_api")
|
||||
self.create_super_admin()
|
||||
self.latest_data = {"update": [
|
||||
{
|
||||
"version": "2099-12-25",
|
||||
"level": 1,
|
||||
"title": "Update at 2099-12-25",
|
||||
"details": ["test get", ]
|
||||
}
|
||||
]}
|
||||
|
||||
def test_get_versions(self):
|
||||
resp = self.client.get(self.url)
|
||||
self.assertSuccess(resp)
|
||||
|
||||
|
||||
class DashboardInfoAPITest(APITestCase):
|
||||
def setUp(self):
|
||||
self.url = self.reverse("dashboard_info_api")
|
||||
self.create_admin()
|
||||
|
||||
def test_get_info(self):
|
||||
resp = self.client.get(self.url)
|
||||
self.assertSuccess(resp)
|
||||
self.assertEqual(resp.data["data"]["user_count"], 1)
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from ..views import SMTPAPI, JudgeServerAPI, WebsiteConfigAPI, TestCasePruneAPI, SMTPTestAPI
|
||||
from ..views import ReleaseNotesAPI, DashboardInfoAPI
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^smtp/?$", SMTPAPI.as_view(), name="smtp_admin_api"),
|
||||
@ -8,4 +9,6 @@ urlpatterns = [
|
||||
url(r"^website/?$", WebsiteConfigAPI.as_view(), name="website_config_api"),
|
||||
url(r"^judge_server/?$", JudgeServerAPI.as_view(), name="judge_server_api"),
|
||||
url(r"^prune_test_case/?$", TestCasePruneAPI.as_view(), name="prune_test_case_api"),
|
||||
url(r"^versions/?$", ReleaseNotesAPI.as_view(), name="get_release_notes_api"),
|
||||
url(r"^dashboard_info", DashboardInfoAPI.as_view(), name="dashboard_info_api"),
|
||||
]
|
||||
|
@ -2,17 +2,25 @@ import os
|
||||
import re
|
||||
import hashlib
|
||||
import shutil
|
||||
import json
|
||||
import pytz
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
from account.decorators import super_admin_required
|
||||
from problem.models import Problem
|
||||
from account.models import User
|
||||
from submission.models import Submission
|
||||
from contest.models import Contest
|
||||
from judge.dispatcher import process_pending_task
|
||||
from judge.languages import languages, spj_languages
|
||||
from options.options import SysOptions
|
||||
from utils.api import APIView, CSRFExemptAPIView, validate_serializer
|
||||
from utils.shortcuts import send_email
|
||||
from utils.shortcuts import send_email, get_env
|
||||
from utils.xss_filter import XSSHtml
|
||||
from .models import JudgeServer
|
||||
from .serializers import (CreateEditWebsiteConfigSerializer,
|
||||
@ -30,14 +38,14 @@ class SMTPAPI(APIView):
|
||||
smtp.pop("password")
|
||||
return self.success(smtp)
|
||||
|
||||
@validate_serializer(CreateSMTPConfigSerializer)
|
||||
@super_admin_required
|
||||
@validate_serializer(CreateSMTPConfigSerializer)
|
||||
def post(self, request):
|
||||
SysOptions.smtp_config = request.data
|
||||
return self.success()
|
||||
|
||||
@validate_serializer(EditSMTPConfigSerializer)
|
||||
@super_admin_required
|
||||
@validate_serializer(EditSMTPConfigSerializer)
|
||||
def put(self, request):
|
||||
smtp = SysOptions.smtp_config
|
||||
data = request.data
|
||||
@ -81,8 +89,8 @@ class WebsiteConfigAPI(APIView):
|
||||
"website_footer", "allow_register", "submission_list_show_all"]}
|
||||
return self.success(ret)
|
||||
|
||||
@validate_serializer(CreateEditWebsiteConfigSerializer)
|
||||
@super_admin_required
|
||||
@validate_serializer(CreateEditWebsiteConfigSerializer)
|
||||
def post(self, request):
|
||||
for k, v in request.data.items():
|
||||
if k == "website_footer":
|
||||
@ -156,7 +164,7 @@ class TestCasePruneAPI(APIView):
|
||||
@super_admin_required
|
||||
def get(self, request):
|
||||
"""
|
||||
return isolated test_case list
|
||||
return orphan test_case list
|
||||
"""
|
||||
ret_data = []
|
||||
dir_to_be_removed = self.get_orphan_ids()
|
||||
@ -190,3 +198,36 @@ class TestCasePruneAPI(APIView):
|
||||
test_case_dir = os.path.join(settings.TEST_CASE_DIR, id)
|
||||
if os.path.isdir(test_case_dir):
|
||||
shutil.rmtree(test_case_dir, ignore_errors=True)
|
||||
|
||||
|
||||
class ReleaseNotesAPI(APIView):
|
||||
def get(self, request):
|
||||
try:
|
||||
resp = requests.get("https://raw.githubusercontent.com/QingdaoU/OnlineJudge/master/docs/data.json",
|
||||
timeout=3)
|
||||
releases = resp.json()
|
||||
except (RequestException, ValueError):
|
||||
return self.success()
|
||||
with open("docs/data.json", "r") as f:
|
||||
local_version = json.load(f)["update"][0]["version"]
|
||||
releases["local_version"] = local_version
|
||||
return self.success(releases)
|
||||
|
||||
|
||||
class DashboardInfoAPI(APIView):
|
||||
def get(self, request):
|
||||
today = datetime.today()
|
||||
today_submission_count = Submission.objects.filter(
|
||||
create_time__gte=datetime(today.year, today.month, today.day, 0, 0, tzinfo=pytz.UTC)).count()
|
||||
recent_contest_count = Contest.objects.exclude(end_time__lt=timezone.now()).count()
|
||||
judge_server_count = len(list(filter(lambda x: x.status == "normal", JudgeServer.objects.all())))
|
||||
return self.success({
|
||||
"user_count": User.objects.count(),
|
||||
"recent_contest_count": recent_contest_count,
|
||||
"today_submission_count": today_submission_count,
|
||||
"judge_server_count": judge_server_count,
|
||||
"env": {
|
||||
"FORCE_HTTPS": get_env("FORCE_HTTPS", default=False),
|
||||
"STATIC_CDN_HOST": get_env("STATIC_CDN_HOST", default="")
|
||||
}
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ from utils.api import APIView, validate_serializer
|
||||
from utils.cache import cache
|
||||
from utils.constants import CacheKey
|
||||
|
||||
from account.decorators import check_contest_permission
|
||||
from account.decorators import check_contest_permission, ensure_created_by
|
||||
from ..models import Contest, ContestAnnouncement, ACMContestRank
|
||||
from ..serializers import (ContestAnnouncementSerializer, ContestAdminSerializer,
|
||||
CreateConetestSeriaizer, CreateContestAnnouncementSerializer,
|
||||
@ -37,8 +37,7 @@ class ContestAPI(APIView):
|
||||
data = request.data
|
||||
try:
|
||||
contest = Contest.objects.get(id=data.pop("id"))
|
||||
if request.user.is_admin() and contest.created_by != request.user:
|
||||
return self.error("Contest does not exist")
|
||||
ensure_created_by(contest, request.user)
|
||||
except Contest.DoesNotExist:
|
||||
return self.error("Contest does not exist")
|
||||
data["start_time"] = dateutil.parser.parse(data["start_time"])
|
||||
@ -66,20 +65,18 @@ class ContestAPI(APIView):
|
||||
if contest_id:
|
||||
try:
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
if request.user.is_admin() and contest.created_by != request.user:
|
||||
return self.error("Contest does not exist")
|
||||
ensure_created_by(contest, request.user)
|
||||
return self.success(ContestAdminSerializer(contest).data)
|
||||
except Contest.DoesNotExist:
|
||||
return self.error("Contest does not exist")
|
||||
|
||||
contests = Contest.objects.all().order_by("-create_time")
|
||||
if request.user.is_admin():
|
||||
contests = contests.filter(created_by=request.user)
|
||||
|
||||
keyword = request.GET.get("keyword")
|
||||
if keyword:
|
||||
contests = contests.filter(title__contains=keyword)
|
||||
|
||||
if request.user.is_admin():
|
||||
contests = contests.filter(created_by=request.user)
|
||||
return self.success(self.paginate_data(request, contests, ContestAdminSerializer))
|
||||
|
||||
|
||||
@ -92,8 +89,7 @@ class ContestAnnouncementAPI(APIView):
|
||||
data = request.data
|
||||
try:
|
||||
contest = Contest.objects.get(id=data.pop("contest_id"))
|
||||
if request.user.is_admin() and contest.created_by != request.user:
|
||||
return self.error("Contest does not exist")
|
||||
ensure_created_by(contest, request.user)
|
||||
data["contest"] = contest
|
||||
data["created_by"] = request.user
|
||||
except Contest.DoesNotExist:
|
||||
@ -109,8 +105,7 @@ class ContestAnnouncementAPI(APIView):
|
||||
data = request.data
|
||||
try:
|
||||
contest_announcement = ContestAnnouncement.objects.get(id=data.pop("id"))
|
||||
if request.user.is_admin() and contest_announcement.created_by != request.user:
|
||||
return self.error("Contest announcement does not exist")
|
||||
ensure_created_by(contest_announcement, request.user)
|
||||
except ContestAnnouncement.DoesNotExist:
|
||||
return self.error("Contest announcement does not exist")
|
||||
for k, v in data.items():
|
||||
@ -139,15 +134,14 @@ class ContestAnnouncementAPI(APIView):
|
||||
if contest_announcement_id:
|
||||
try:
|
||||
contest_announcement = ContestAnnouncement.objects.get(id=contest_announcement_id)
|
||||
if request.user.is_admin() and contest_announcement.created_by != request.user:
|
||||
return self.error("Contest announcement does not exist")
|
||||
ensure_created_by(contest_announcement, request.user)
|
||||
return self.success(ContestAnnouncementSerializer(contest_announcement).data)
|
||||
except ContestAnnouncement.DoesNotExist:
|
||||
return self.error("Contest announcement does not exist")
|
||||
|
||||
contest_id = request.GET.get("contest_id")
|
||||
if not contest_id:
|
||||
return self.error("Paramater error")
|
||||
return self.error("Parameter error")
|
||||
contest_announcements = ContestAnnouncement.objects.filter(contest_id=contest_id)
|
||||
if request.user.is_admin():
|
||||
contest_announcements = contest_announcements.filter(created_by=request.user)
|
||||
@ -177,12 +171,10 @@ class ACMContestHelper(APIView):
|
||||
results.sort(key=lambda x: -x["ac_info"]["ac_time"])
|
||||
return self.success(results)
|
||||
|
||||
@validate_serializer(ACMContesHelperSerializer)
|
||||
@check_contest_permission(check_type="ranks")
|
||||
@validate_serializer(ACMContesHelperSerializer)
|
||||
def put(self, request):
|
||||
data = request.data
|
||||
if not request.user.is_contest_admin(self.contest):
|
||||
return self.error("You are not contest admin")
|
||||
try:
|
||||
rank = ACMContestRank.objects.get(pk=data["rank_id"])
|
||||
except ACMContestRank.DoesNotExist:
|
||||
|
@ -31,10 +31,17 @@ else
|
||||
ln -sf https_redirect.conf http_locations.conf
|
||||
fi
|
||||
|
||||
if [ ! -z "$LOWER_IP_HEADER" ]; then
|
||||
sed -i "s/__IP_HEADER__/\$http_$LOWER_IP_HEADER/g" api_proxy.conf;
|
||||
else
|
||||
sed -i "s/__IP_HEADER__/\$remote_addr/g" api_proxy.conf;
|
||||
fi
|
||||
|
||||
cd $APP/dist
|
||||
if [ ! -z "$STATIC_CDN_HOST" ]; then
|
||||
find . -name index.html -exec sed -i "s/link href=\/static/link href=\/\/$STATIC_CDN_HOST\/static/g" {} \;
|
||||
find . -name index.html -exec sed -i "s/script type=text\/javascript src=\/static/script type=text\/javascript src=\/\/$STATIC_CDN_HOST\/static/g" {} \;
|
||||
find . -name index.html -exec sed -i "s/__STATIC_CDN_HOST__/\/\/$STATIC_CDN_HOST/g" {} \;
|
||||
else
|
||||
find . -name index.html -exec sed -i "s/__STATIC_CDN_HOST__//g" {} \;
|
||||
fi
|
||||
|
||||
cd $APP
|
||||
@ -44,13 +51,13 @@ while [ $n -lt 5 ]
|
||||
do
|
||||
python manage.py migrate --no-input &&
|
||||
python manage.py inituser --username=root --password=rootroot --action=create_super_admin &&
|
||||
echo "from options.options import SysOptions; SysOptions.judge_server_token='$JUDGE_SERVER_TOKEN'" | python manage.py shell &&
|
||||
break
|
||||
n=$(($n+1))
|
||||
echo "Failed to migrate, going to retry..."
|
||||
sleep 8
|
||||
done
|
||||
|
||||
echo "from options.options import SysOptions; SysOptions.judge_server_token='$JUDGE_SERVER_TOKEN'" | python manage.py shell || exit 1
|
||||
|
||||
chown -R nobody:nogroup $DATA $APP/dist
|
||||
exec supervisord -c /app/deploy/supervisord.conf
|
||||
exec supervisord -c /app/deploy/supervisord.conf
|
||||
|
5
deploy/nginx/api_proxy.conf
Normal file
5
deploy/nginx/api_proxy.conf
Normal file
@ -0,0 +1,5 @@
|
||||
proxy_pass http://backend;
|
||||
proxy_set_header X-Real-IP __IP_HEADER__;
|
||||
proxy_set_header Host $http_host;client_max_body_size 200M;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection '';
|
@ -1,3 +1,7 @@
|
||||
location /api/judge_server_heartbeat {
|
||||
include api_proxy.conf;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
@ -3,10 +3,7 @@ location /public {
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://backend;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
client_max_body_size 200M;
|
||||
include api_proxy.conf;
|
||||
}
|
||||
|
||||
location /data/ {
|
||||
|
@ -1,14 +1,15 @@
|
||||
{
|
||||
"update": [
|
||||
{
|
||||
"version": "2017-12-25",
|
||||
"level": 1,
|
||||
"title": "Update at 2017-12-25",
|
||||
"version": "2017-01-04",
|
||||
"level": "Recommend",
|
||||
"title": "Update at 2018-01-04",
|
||||
"details": [
|
||||
"Fix some issues under IE/Edge",
|
||||
"Add backend error reporter",
|
||||
"New email template",
|
||||
"A more flexible throttling function",
|
||||
"Add admin dashboard (this page)",
|
||||
"Other bugs and enhancements"
|
||||
]
|
||||
}
|
||||
|
@ -1,9 +1,4 @@
|
||||
import os
|
||||
|
||||
|
||||
def get_env(name, default=""):
|
||||
return os.environ.get(name, default)
|
||||
|
||||
from utils.shortcuts import get_env
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
|
@ -12,14 +12,15 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
import os
|
||||
import raven
|
||||
from copy import deepcopy
|
||||
from utils.shortcuts import get_env
|
||||
from .custom_settings import *
|
||||
|
||||
if os.environ.get("OJ_ENV") == "production":
|
||||
production_env = get_env("OJ_ENV", "dev") == "production"
|
||||
if production_env:
|
||||
from .production_settings import *
|
||||
else:
|
||||
from .dev_settings import *
|
||||
|
||||
from .custom_settings import *
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Applications
|
||||
@ -128,7 +129,7 @@ UPLOAD_DIR = f"{DATA_DIR}{UPLOAD_PREFIX}"
|
||||
STATICFILES_DIRS = [os.path.join(DATA_DIR, "public")]
|
||||
|
||||
|
||||
LOGGING_HANDLERS = ['console'] if DEBUG else ['console', 'sentry']
|
||||
LOGGING_HANDLERS = ['console', 'sentry'] if production_env else ['console']
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
@ -204,13 +205,6 @@ BROKER_URL = f"{REDIS_URL}/3"
|
||||
CELERY_TASK_SOFT_TIME_LIMIT = CELERY_TASK_TIME_LIMIT = 180
|
||||
CELERY_ACCEPT_CONTENT = ["json"]
|
||||
CELERY_TASK_SERIALIZER = "json"
|
||||
|
||||
# 用于限制用户恶意提交大量代码
|
||||
TOKEN_BUCKET_DEFAULT_CAPACITY = 10
|
||||
|
||||
# 单位:每分钟
|
||||
TOKEN_BUCKET_FILL_RATE = 2
|
||||
|
||||
RAVEN_CONFIG = {
|
||||
'dsn': 'https://b200023b8aed4d708fb593c5e0a6ad3d:1fddaba168f84fcf97e0d549faaeaff0@sentry.io/263057'
|
||||
}
|
@ -8,7 +8,7 @@ from wsgiref.util import FileWrapper
|
||||
from django.conf import settings
|
||||
from django.http import StreamingHttpResponse, HttpResponse
|
||||
|
||||
from account.decorators import problem_permission_required
|
||||
from account.decorators import problem_permission_required, ensure_created_by
|
||||
from judge.dispatcher import SPJCompiler
|
||||
from contest.models import Contest, ContestStatus
|
||||
from submission.models import Submission
|
||||
@ -49,7 +49,6 @@ class TestCaseAPI(CSRFExemptAPIView):
|
||||
else:
|
||||
return sorted(ret, key=natural_sort_key)
|
||||
|
||||
@problem_permission_required
|
||||
def get(self, request):
|
||||
problem_id = request.GET.get("problem_id")
|
||||
if not problem_id:
|
||||
@ -59,6 +58,11 @@ class TestCaseAPI(CSRFExemptAPIView):
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem does not exists")
|
||||
|
||||
if problem.contest:
|
||||
ensure_created_by(problem.contest, request.user)
|
||||
else:
|
||||
ensure_created_by(problem, request.user)
|
||||
|
||||
test_case_dir = os.path.join(settings.TEST_CASE_DIR, problem.test_case_id)
|
||||
if not os.path.isdir(test_case_dir):
|
||||
return self.error("Test case does not exists")
|
||||
@ -79,7 +83,6 @@ class TestCaseAPI(CSRFExemptAPIView):
|
||||
response["Content-Length"] = os.path.getsize(file_name)
|
||||
return response
|
||||
|
||||
@problem_permission_required
|
||||
def post(self, request):
|
||||
form = TestCaseUploadForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
@ -147,7 +150,6 @@ class TestCaseAPI(CSRFExemptAPIView):
|
||||
|
||||
class CompileSPJAPI(APIView):
|
||||
@validate_serializer(CompileSPJSerializer)
|
||||
@problem_permission_required
|
||||
def post(self, request):
|
||||
data = request.data
|
||||
spj_version = rand_str(8)
|
||||
@ -186,11 +188,12 @@ class ProblemBase(APIView):
|
||||
def delete(self, request):
|
||||
id = request.GET.get("id")
|
||||
if not id:
|
||||
return self.error("Invalid parameter, id is requred")
|
||||
return self.error("Invalid parameter, id is required")
|
||||
try:
|
||||
problem = Problem.objects.get(id=id)
|
||||
problem = Problem.objects.get(id=id, contest_id__isnull=True)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem does not exists")
|
||||
ensure_created_by(problem, request.user)
|
||||
if Submission.objects.filter(problem=problem).exists():
|
||||
return self.error("Can't delete the problem as it has submissions")
|
||||
d = os.path.join(settings.TEST_CASE_DIR, problem.test_case_id)
|
||||
@ -201,11 +204,10 @@ class ProblemBase(APIView):
|
||||
|
||||
|
||||
class ProblemAPI(ProblemBase):
|
||||
@validate_serializer(CreateProblemSerializer)
|
||||
@problem_permission_required
|
||||
@validate_serializer(CreateProblemSerializer)
|
||||
def post(self, request):
|
||||
data = request.data
|
||||
|
||||
_id = data["_id"]
|
||||
if not _id:
|
||||
return self.error("Display ID is required")
|
||||
@ -236,8 +238,7 @@ class ProblemAPI(ProblemBase):
|
||||
if problem_id:
|
||||
try:
|
||||
problem = Problem.objects.get(id=problem_id)
|
||||
if not user.can_mgmt_all_problem() and problem.created_by != user:
|
||||
return self.error("Problem does not exist")
|
||||
ensure_created_by(problem, request.user)
|
||||
return self.success(ProblemAdminSerializer(problem).data)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem does not exist")
|
||||
@ -256,17 +257,15 @@ class ProblemAPI(ProblemBase):
|
||||
problems = problems.filter(title__contains=keyword)
|
||||
return self.success(self.paginate_data(request, problems, ProblemAdminSerializer))
|
||||
|
||||
@validate_serializer(EditProblemSerializer)
|
||||
@problem_permission_required
|
||||
@validate_serializer(EditProblemSerializer)
|
||||
def put(self, request):
|
||||
data = request.data
|
||||
problem_id = data.pop("id")
|
||||
user = request.user
|
||||
|
||||
try:
|
||||
problem = Problem.objects.get(id=problem_id)
|
||||
if not user.can_mgmt_all_problem() and problem.created_by != user:
|
||||
return self.error("Problem does not exist")
|
||||
ensure_created_by(problem, request.user)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem does not exist")
|
||||
|
||||
@ -300,13 +299,11 @@ class ProblemAPI(ProblemBase):
|
||||
|
||||
class ContestProblemAPI(ProblemBase):
|
||||
@validate_serializer(CreateContestProblemSerializer)
|
||||
@problem_permission_required
|
||||
def post(self, request):
|
||||
data = request.data
|
||||
try:
|
||||
contest = Contest.objects.get(id=data.pop("contest_id"))
|
||||
if request.user.is_admin() and contest.created_by != request.user:
|
||||
return self.error("Contest does not exist")
|
||||
ensure_created_by(contest, request.user)
|
||||
except Contest.DoesNotExist:
|
||||
return self.error("Contest does not exist")
|
||||
|
||||
@ -345,8 +342,7 @@ class ContestProblemAPI(ProblemBase):
|
||||
if problem_id:
|
||||
try:
|
||||
problem = Problem.objects.get(id=problem_id)
|
||||
if user.is_admin() and problem.contest.created_by != user:
|
||||
return self.error("Problem does not exist")
|
||||
ensure_created_by(problem, user)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem does not exist")
|
||||
return self.success(ProblemAdminSerializer(problem).data)
|
||||
@ -366,10 +362,11 @@ class ContestProblemAPI(ProblemBase):
|
||||
@problem_permission_required
|
||||
def put(self, request):
|
||||
data = request.data
|
||||
user = request.user
|
||||
|
||||
try:
|
||||
contest = Contest.objects.get(id=data.pop("contest_id"))
|
||||
if request.user.is_admin() and contest.created_by != request.user:
|
||||
return self.error("Contest does not exist")
|
||||
ensure_created_by(contest, user)
|
||||
except Contest.DoesNotExist:
|
||||
return self.error("Contest does not exist")
|
||||
|
||||
@ -377,12 +374,10 @@ class ContestProblemAPI(ProblemBase):
|
||||
return self.error("Invalid rule type")
|
||||
|
||||
problem_id = data.pop("id")
|
||||
user = request.user
|
||||
|
||||
try:
|
||||
problem = Problem.objects.get(id=problem_id)
|
||||
if not user.can_mgmt_all_problem() and problem.created_by != user:
|
||||
return self.error("Problem does not exist")
|
||||
ensure_created_by(problem, user)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem does not exist")
|
||||
|
||||
@ -428,7 +423,7 @@ class MakeContestProblemPublicAPIView(APIView):
|
||||
return self.error("Problem does not exist")
|
||||
|
||||
if not problem.contest or problem.is_public:
|
||||
return self.error("Alreay be a public problem")
|
||||
return self.error("Already be a public problem")
|
||||
problem.is_public = True
|
||||
problem.save()
|
||||
# https://docs.djangoproject.com/en/1.11/topics/db/queries/#copying-model-instances
|
||||
|
@ -11,6 +11,13 @@ from django.views.generic import View
|
||||
logger = logging.getLogger("")
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
def __init__(self, msg, err=None):
|
||||
self.err = err
|
||||
self.msg = msg
|
||||
super().__init__(err, msg)
|
||||
|
||||
|
||||
class ContentType(object):
|
||||
json_request = "application/json"
|
||||
json_response = "application/json;charset=UTF-8"
|
||||
@ -137,6 +144,11 @@ class APIView(View):
|
||||
return self.error(err="invalid-request", msg=str(e))
|
||||
try:
|
||||
return super(APIView, self).dispatch(request, *args, **kwargs)
|
||||
except APIError as e:
|
||||
ret = {"msg": e.msg}
|
||||
if e.err:
|
||||
ret["err"] = e.err
|
||||
return self.error(**ret)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return self.server_error()
|
||||
|
@ -1,3 +1,4 @@
|
||||
import os
|
||||
import re
|
||||
import datetime
|
||||
import random
|
||||
@ -76,3 +77,7 @@ def send_email(smtp_config, from_name, to_email, to_name, subject, content):
|
||||
password=smtp_config["password"],
|
||||
port=smtp_config["port"],
|
||||
tls=smtp_config["tls"])
|
||||
|
||||
|
||||
def get_env(name, default=""):
|
||||
return os.environ.get(name, default)
|
||||
|
Loading…
Reference in New Issue
Block a user