text: import hashlib
  import json
  import os
  import re
  import shutil
  import smtplib
  import time
  from datetime import datetime
  
  import pytz
  import requests
  from django.conf import settings
  from django.utils import timezone
  from requests.exceptions import RequestException
  
  from account.decorators import super_admin_required
  from account.models import User, UserProfile
  from contest.models import Contest
  from judge.dispatcher import process_pending_task
  from options.options import SysOptions
  from problem.models import Problem
  from submission.models import Submission
  from utils.api import APIView, CSRFExemptAPIView, validate_serializer
  from utils.shortcuts import send_email, get_env
  from utils.xss_filter import XSSHtml
  from .models import JudgeServer
  from .serializers import (CreateEditWebsiteConfigSerializer,
                            CreateSMTPConfigSerializer, EditSMTPConfigSerializer,
                            JudgeServerHeartbeatSerializer,
                            JudgeServerSerializer, TestSMTPConfigSerializer, EditJudgeServerSerializer)
  
  
  class SMTPAPI(APIView):
      @super_admin_required
      def get(self, request):
          smtp = SysOptions.smtp_config
          if not smtp:
              return self.success(None)
          smtp.pop("password")
          return self.success(smtp)
  
      @super_admin_required
      @validate_serializer(CreateSMTPConfigSerializer)
      def post(self, request):
          SysOptions.smtp_config = request.data
          return self.success()
  
      @super_admin_required
      @validate_serializer(EditSMTPConfigSerializer)
      def put(self, request):
          smtp = SysOptions.smtp_config
          data = request.data
          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):
          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")
          except smtplib.SMTPResponseException as e:
              # guess error message encoding
              msg = b"Failed to send email"
              try:
                  msg = e.smtp_error
                  # 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)
          return self.success()
  
  
  class WebsiteConfigAPI(APIView):
      def get(self, request):
          ret = {key: getattr(SysOptions, key) for key in
                 ["website_base_url", "website_name", "website_name_shortcut",
                  "allow_register"]}
          return self.success(ret)
  
      @super_admin_required
      @validate_serializer(CreateEditWebsiteConfigSerializer)
      def post(self, request):
          for k, v in request.data.items():
              setattr(SysOptions, k, v)
          return self.success()
  
  
  class JudgeServerAPI(APIView):
      @super_admin_required
      def get(self, request):
          servers = JudgeServer.objects.all().order_by("-last_heartbeat")
          return self.success({"token": SysOptions.judge_server_token,
                               "servers": JudgeServerSerializer(servers, many=True).data})
  
      @super_admin_required
      def delete(self, request):
          hostname = request.GET.get("hostname")
          if hostname:
              JudgeServer.objects.filter(hostname=hostname).delete()
          return self.success()
  
      @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()
          return self.success()
  
  
  class JudgeServerHeartbeatAPI(CSRFExemptAPIView):
      @validate_serializer(JudgeServerHeartbeatSerializer)
      def post(self, request):
          data = request.data
          client_token = request.META.get("HTTP_X_JUDGE_SERVER_TOKEN")
          if hashlib.sha256(SysOptions.judge_server_token.encode("utf-8")).hexdigest() != client_token:
              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"]
              server.service_url = data["service_url"]
              server.ip = request.ip
              server.last_heartbeat = timezone.now()
              server.save(update_fields=["judger_version", "cpu_core", "memory_usage", "service_url", "ip", "last_heartbeat"])
          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"],
                                         ip=request.META["REMOTE_ADDR"],
                                         service_url=data["service_url"],
                                         last_heartbeat=timezone.now(),
                                         )
          # 新server上线 处理队列中的，防止没有新的提交而导致一直waiting
          process_pending_task()
  
          return self.success()
  
  
  class LanguagesAPI(APIView):
      def get(self, request):
          return self.success({"languages": SysOptions.languages, "spj_languages": SysOptions.spj_languages})
  
  
  class TestCasePruneAPI(APIView):
      @super_admin_required
      def get(self, request):
          """
          return orphan test_case list
          """
          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:
                  ret_data.append({"id": d.name, "create_time": d.stat().st_mtime})
          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)
  
  
  class ReleaseNotesAPI(APIView):
      def get(self, request):
          try:
              resp = requests.get("https://raw.githubusercontent.com/chenhongqiao/TeamsCodeOJ/main/server/docs/data.json?_=" + str(time.time()),
                                  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):
      @super_admin_required
      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())))
          all_teams = UserProfile.objects.all()
          people_count = 0
          for team in all_teams:
              people_count += len(team.team_members)
  
          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,
              "people_count": people_count,
              "env": {
                  "FORCE_HTTPS": get_env("FORCE_HTTPS", default=False),
                  "STATIC_CDN_HOST": get_env("STATIC_CDN_HOST", default="")
              }
          })
  