OnlineJudge/conf/views.py

242 lines
9.0 KiB
Python
Raw Permalink Normal View History

import hashlib
import json
2017-12-23 06:16:39 +00:00
import os
import re
import shutil
import smtplib
2018-05-05 14:42:03 +00:00
import time
2018-01-04 03:42:20 +00:00
from datetime import datetime
2016-11-20 11:18:25 +00:00
import pytz
import requests
2017-12-23 06:16:39 +00:00
from django.conf import settings
from django.utils import timezone
from requests.exceptions import RequestException
2016-09-25 06:07:45 +00:00
from account.decorators import super_admin_required
2018-01-04 03:42:20 +00:00
from account.models import User
from contest.models import Contest
2017-05-10 11:40:26 +00:00
from judge.dispatcher import process_pending_task
2017-10-01 19:54:34 +00:00
from options.options import SysOptions
from problem.models import Problem
from submission.models import Submission
2016-11-20 11:18:25 +00:00
from utils.api import APIView, CSRFExemptAPIView, validate_serializer
2018-01-04 03:42:20 +00:00
from utils.shortcuts import send_email, get_env
2017-12-24 07:34:22 +00:00
from utils.xss_filter import XSSHtml
2017-10-01 19:54:34 +00:00
from .models import JudgeServer
2017-01-23 08:48:04 +00:00
from .serializers import (CreateEditWebsiteConfigSerializer,
CreateSMTPConfigSerializer, EditSMTPConfigSerializer,
2017-01-23 08:48:04 +00:00
JudgeServerHeartbeatSerializer,
2017-12-24 04:10:02 +00:00
JudgeServerSerializer, TestSMTPConfigSerializer, EditJudgeServerSerializer)
class SMTPAPI(APIView):
@super_admin_required
def get(self, request):
2017-10-01 19:54:34 +00:00
smtp = SysOptions.smtp_config
if not smtp:
return self.success(None)
2017-10-01 19:54:34 +00:00
smtp.pop("password")
return self.success(smtp)
2017-01-23 08:01:56 +00:00
@super_admin_required
2018-01-04 11:27:41 +00:00
@validate_serializer(CreateSMTPConfigSerializer)
def post(self, request):
2017-10-01 19:54:34 +00:00
SysOptions.smtp_config = request.data
return self.success()
2017-01-23 08:01:56 +00:00
@super_admin_required
2018-01-04 11:27:41 +00:00
@validate_serializer(EditSMTPConfigSerializer)
def put(self, request):
2017-10-01 19:54:34 +00:00
smtp = SysOptions.smtp_config
data = request.data
2017-10-01 19:54:34 +00:00
for item in ["server", "port", "email", "tls"]:
smtp[item] = data[item]
if "password" in data:
smtp["password"] = data["password"]
SysOptions.smtp_config = smtp
return self.success()
class SMTPTestAPI(APIView):
@super_admin_required
@validate_serializer(TestSMTPConfigSerializer)
def post(self, request):
2017-12-24 03:34:40 +00:00
if not SysOptions.smtp_config:
return self.error("Please setup SMTP config at first")
try:
send_email(smtp_config=SysOptions.smtp_config,
from_name=SysOptions.website_name_shortcut,
to_name=request.user.username,
to_email=request.data["email"],
subject="You have successfully configured SMTP",
content="You have successfully configured SMTP")
2018-04-23 23:06:33 +00:00
except smtplib.SMTPResponseException as e:
2017-12-24 03:34:40 +00:00
# guess error message encoding
msg = b"Failed to send email"
2017-12-24 03:34:40 +00:00
try:
msg = e.smtp_error
2017-12-24 03:34:40 +00:00
# qq mail
msg = msg.decode("gbk")
except Exception:
msg = msg.decode("utf-8", "ignore")
return self.error(msg)
except Exception as e:
msg = str(e)
return self.error(msg)
2017-12-24 03:34:40 +00:00
return self.success()
class WebsiteConfigAPI(APIView):
def get(self, request):
2017-10-01 19:54:34 +00:00
ret = {key: getattr(SysOptions, key) for key in
["website_base_url", "website_name", "website_name_shortcut",
"website_footer", "allow_register", "submission_list_show_all"]}
return self.success(ret)
@super_admin_required
2018-01-04 11:27:41 +00:00
@validate_serializer(CreateEditWebsiteConfigSerializer)
def post(self, request):
2017-10-01 19:54:34 +00:00
for k, v in request.data.items():
2017-12-24 07:34:22 +00:00
if k == "website_footer":
with XSSHtml() as parser:
v = parser.clean(v)
2017-10-01 19:54:34 +00:00
setattr(SysOptions, k, v)
return self.success()
2016-11-20 11:18:25 +00:00
class JudgeServerAPI(APIView):
@super_admin_required
def get(self, request):
servers = JudgeServer.objects.all().order_by("-last_heartbeat")
2017-10-01 19:54:34 +00:00
return self.success({"token": SysOptions.judge_server_token,
2016-11-20 11:18:25 +00:00
"servers": JudgeServerSerializer(servers, many=True).data})
@super_admin_required
def delete(self, request):
2017-01-24 08:48:39 +00:00
hostname = request.GET.get("hostname")
if hostname:
JudgeServer.objects.filter(hostname=hostname).delete()
return self.success()
2016-11-20 11:18:25 +00:00
2017-12-24 04:10:02 +00:00
@validate_serializer(EditJudgeServerSerializer)
@super_admin_required
def put(self, request):
is_disabled = request.data.get("is_disabled", False)
JudgeServer.objects.filter(id=request.data["id"]).update(is_disabled=is_disabled)
if not is_disabled:
process_pending_task()
2017-12-24 04:10:02 +00:00
return self.success()
2016-11-20 11:18:25 +00:00
class JudgeServerHeartbeatAPI(CSRFExemptAPIView):
@validate_serializer(JudgeServerHeartbeatSerializer)
def post(self, request):
data = request.data
2017-01-26 05:55:31 +00:00
client_token = request.META.get("HTTP_X_JUDGE_SERVER_TOKEN")
2017-10-01 19:54:34 +00:00
if hashlib.sha256(SysOptions.judge_server_token.encode("utf-8")).hexdigest() != client_token:
2016-11-20 11:18:25 +00:00
return self.error("Invalid token")
try:
server = JudgeServer.objects.get(hostname=data["hostname"])
server.judger_version = data["judger_version"]
server.cpu_core = data["cpu_core"]
server.memory_usage = data["memory"]
server.cpu_usage = data["cpu"]
2017-12-24 04:08:56 +00:00
server.service_url = data["service_url"]
2018-01-06 15:53:41 +00:00
server.ip = request.ip
2016-11-20 11:18:25 +00:00
server.last_heartbeat = timezone.now()
server.save(update_fields=["judger_version", "cpu_core", "memory_usage", "service_url", "ip", "last_heartbeat"])
2016-11-20 11:18:25 +00:00
except JudgeServer.DoesNotExist:
JudgeServer.objects.create(hostname=data["hostname"],
judger_version=data["judger_version"],
cpu_core=data["cpu_core"],
memory_usage=data["memory"],
cpu_usage=data["cpu"],
2017-01-24 08:22:22 +00:00
ip=request.META["REMOTE_ADDR"],
2017-12-24 04:08:56 +00:00
service_url=data["service_url"],
2016-11-20 11:18:25 +00:00
last_heartbeat=timezone.now(),
)
# 新server上线 处理队列中的防止没有新的提交而导致一直waiting
process_pending_task()
2016-11-20 11:18:25 +00:00
return self.success()
class LanguagesAPI(APIView):
def get(self, request):
return self.success({"languages": SysOptions.languages, "spj_languages": SysOptions.spj_languages})
2017-12-23 06:16:39 +00:00
class TestCasePruneAPI(APIView):
@super_admin_required
def get(self, request):
"""
2018-01-02 12:05:33 +00:00
return orphan test_case list
2017-12-23 06:16:39 +00:00
"""
ret_data = []
dir_to_be_removed = self.get_orphan_ids()
# return an iterator
for d in os.scandir(settings.TEST_CASE_DIR):
if d.name in dir_to_be_removed:
2017-12-27 12:12:38 +00:00
ret_data.append({"id": d.name, "create_time": d.stat().st_mtime})
2017-12-23 06:16:39 +00:00
return self.success(ret_data)
@super_admin_required
def delete(self, request):
test_case_id = request.GET.get("id")
if test_case_id:
self.delete_one(test_case_id)
return self.success()
for id in self.get_orphan_ids():
self.delete_one(id)
return self.success()
@staticmethod
def get_orphan_ids():
db_ids = Problem.objects.all().values_list("test_case_id", flat=True)
disk_ids = os.listdir(settings.TEST_CASE_DIR)
test_case_re = re.compile(r"^[a-zA-Z0-9]{32}$")
disk_ids = filter(lambda f: test_case_re.match(f), disk_ids)
return list(set(disk_ids) - set(db_ids))
@staticmethod
def delete_one(id):
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)
2018-01-02 12:05:33 +00:00
2018-01-04 03:42:20 +00:00
class ReleaseNotesAPI(APIView):
2018-01-02 12:05:33 +00:00
def get(self, request):
try:
2018-05-05 14:42:03 +00:00
resp = requests.get("https://raw.githubusercontent.com/QingdaoU/OnlineJudge/master/docs/data.json?_=" + str(time.time()),
2018-01-02 12:05:33 +00:00
timeout=3)
2018-01-04 03:42:20 +00:00
releases = resp.json()
2018-01-02 12:05:33 +00:00
except (RequestException, ValueError):
return self.success()
with open("docs/data.json", "r") as f:
2018-01-04 03:42:20 +00:00
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="")
}
})