update for django6

This commit is contained in:
2026-05-09 05:57:30 -06:00
parent b297eb5475
commit e4e8b7759d
12 changed files with 284 additions and 69 deletions

View File

@@ -0,0 +1,58 @@
# Generated by Django 6.0.4 on 2026-05-09 11:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0004_alter_user_admin_type_alter_user_problem_permission'),
]
operations = [
migrations.AlterField(
model_name='user',
name='is_disabled',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='user',
name='open_api',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='user',
name='session_keys',
field=models.JSONField(db_default=models.Value([]), default=list),
),
migrations.AlterField(
model_name='user',
name='two_factor_auth',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='userprofile',
name='accepted_number',
field=models.IntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='userprofile',
name='acm_problems_status',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
migrations.AlterField(
model_name='userprofile',
name='oi_problems_status',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
migrations.AlterField(
model_name='userprofile',
name='submission_number',
field=models.IntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='userprofile',
name='total_score',
field=models.BigIntegerField(db_default=0, default=0),
),
]

View File

@@ -36,13 +36,13 @@ class User(AbstractBaseUser):
reset_password_token_expire_time = models.DateTimeField(null=True) reset_password_token_expire_time = models.DateTimeField(null=True)
# SSO auth token # SSO auth token
auth_token = models.TextField(null=True) auth_token = models.TextField(null=True)
two_factor_auth = models.BooleanField(default=False) two_factor_auth = models.BooleanField(default=False, db_default=False)
tfa_token = models.TextField(null=True) tfa_token = models.TextField(null=True)
session_keys = JSONField(default=list) session_keys = JSONField(default=list, db_default=models.Value([]))
# open api key # open api key
open_api = models.BooleanField(default=False) open_api = models.BooleanField(default=False, db_default=False)
open_api_appkey = models.TextField(null=True) open_api_appkey = models.TextField(null=True)
is_disabled = models.BooleanField(default=False) is_disabled = models.BooleanField(default=False, db_default=False)
raw_password = models.CharField(max_length=20, null=True, blank=True, verbose_name="明文密码") raw_password = models.CharField(max_length=20, null=True, blank=True, verbose_name="明文密码")
USERNAME_FIELD = "username" USERNAME_FIELD = "username"
@@ -93,9 +93,9 @@ class UserProfile(models.Model):
# } # }
# } # }
# } # }
acm_problems_status = JSONField(default=dict) acm_problems_status = JSONField(default=dict, db_default=models.Value({}))
# like acm_problems_status, merely add "score" field # like acm_problems_status, merely add "score" field
oi_problems_status = JSONField(default=dict) oi_problems_status = JSONField(default=dict, db_default=models.Value({}))
real_name = models.TextField(null=True) real_name = models.TextField(null=True)
avatar = models.TextField(default=f"{settings.AVATAR_URI_PREFIX}/default.png") avatar = models.TextField(default=f"{settings.AVATAR_URI_PREFIX}/default.png")
@@ -106,24 +106,24 @@ class UserProfile(models.Model):
major = models.TextField(null=True) major = models.TextField(null=True)
language = models.TextField(null=True) language = models.TextField(null=True)
# for ACM # for ACM
accepted_number = models.IntegerField(default=0) accepted_number = models.IntegerField(default=0, db_default=0)
# for OI # for OI
total_score = models.BigIntegerField(default=0) total_score = models.BigIntegerField(default=0, db_default=0)
submission_number = models.IntegerField(default=0) submission_number = models.IntegerField(default=0, db_default=0)
def add_accepted_problem_number(self): def add_accepted_problem_number(self):
self.accepted_number = models.F("accepted_number") + 1 self.accepted_number = models.F("accepted_number") + 1
self.save() self.save(update_fields=["accepted_number"])
def add_submission_number(self): def add_submission_number(self):
self.submission_number = models.F("submission_number") + 1 self.submission_number = models.F("submission_number") + 1
self.save() self.save(update_fields=["submission_number"])
# 计算总分时, 应先减掉上次该题所得分数, 然后再加上本次所得分数 # 计算总分时, 应先减掉上次该题所得分数, 然后再加上本次所得分数
def add_score(self, this_time_score, last_time_score=None): def add_score(self, this_time_score, last_time_score=None):
last_time_score = last_time_score or 0 last_time_score = last_time_score or 0
self.total_score = models.F("total_score") - last_time_score + this_time_score self.total_score = models.F("total_score") - last_time_score + this_time_score
self.save() self.save(update_fields=["total_score"])
class Meta: class Meta:
db_table = "user_profile" db_table = "user_profile"

View File

@@ -0,0 +1,48 @@
# Generated by Django 6.0.4 on 2026-05-09 11:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contest', '0004_alter_acmcontestrank_unique_together_and_more'),
]
operations = [
migrations.AlterField(
model_name='acmcontestrank',
name='accepted_number',
field=models.IntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='acmcontestrank',
name='submission_info',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
migrations.AlterField(
model_name='acmcontestrank',
name='submission_number',
field=models.IntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='acmcontestrank',
name='total_time',
field=models.IntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='oicontestrank',
name='submission_info',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
migrations.AlterField(
model_name='oicontestrank',
name='submission_number',
field=models.IntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='oicontestrank',
name='total_score',
field=models.IntegerField(db_default=0, default=0),
),
]

View File

@@ -55,19 +55,19 @@ class Contest(models.Model):
class AbstractContestRank(models.Model): class AbstractContestRank(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
contest = models.ForeignKey(Contest, on_delete=models.CASCADE) contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
submission_number = models.IntegerField(default=0) submission_number = models.IntegerField(default=0, db_default=0)
class Meta: class Meta:
abstract = True abstract = True
class ACMContestRank(AbstractContestRank): class ACMContestRank(AbstractContestRank):
accepted_number = models.IntegerField(default=0) accepted_number = models.IntegerField(default=0, db_default=0)
# total_time is only for ACM contest, total_time = ac time + none-ac times * 20 * 60 # total_time is only for ACM contest, total_time = ac time + none-ac times * 20 * 60
total_time = models.IntegerField(default=0) total_time = models.IntegerField(default=0, db_default=0)
# {"23": {"is_ac": True, "ac_time": 8999, "error_number": 2, "is_first_ac": True}} # {"23": {"is_ac": True, "ac_time": 8999, "error_number": 2, "is_first_ac": True}}
# key is problem id # key is problem id
submission_info = JSONField(default=dict) submission_info = JSONField(default=dict, db_default=models.Value({}))
class Meta: class Meta:
db_table = "acm_contest_rank" db_table = "acm_contest_rank"
@@ -81,10 +81,10 @@ class ACMContestRank(AbstractContestRank):
class OIContestRank(AbstractContestRank): class OIContestRank(AbstractContestRank):
total_score = models.IntegerField(default=0) total_score = models.IntegerField(default=0, db_default=0)
# {"23": 333} # {"23": 333}
# key is problem id, value is current score # key is problem id, value is current score
submission_info = JSONField(default=dict) submission_info = JSONField(default=dict, db_default=models.Value({}))
class Meta: class Meta:
db_table = "oi_contest_rank" db_table = "oi_contest_rank"

View File

@@ -1,11 +1,13 @@
import hashlib import hashlib
import json import json
import logging import logging
from datetime import timedelta
from urllib.parse import urljoin from urllib.parse import urljoin
import requests import requests
from django.db import IntegrityError, transaction from django.db import IntegrityError, transaction
from django.db.models import F from django.db.models import F
from django.utils import timezone
from account.models import User from account.models import User
from conf.models import JudgeServer from conf.models import JudgeServer
@@ -38,14 +40,23 @@ class ChooseJudgeServer:
def __enter__(self) -> [JudgeServer, None]: def __enter__(self) -> [JudgeServer, None]:
with transaction.atomic(): with transaction.atomic():
servers = JudgeServer.objects.select_for_update().filter(is_disabled=False).order_by("task_number") cutoff = timezone.now() - timedelta(seconds=6)
servers = [s for s in servers if s.status == "normal"] server = (
for server in servers: JudgeServer.objects
if server.task_number <= server.cpu_core * 2: .select_for_update(skip_locked=True)
server.task_number = F("task_number") + 1 .filter(
server.save(update_fields=["task_number"]) is_disabled=False,
self.server = server last_heartbeat__gte=cutoff,
return server task_number__lte=F("cpu_core") * 2,
)
.order_by("task_number")
.first()
)
if server:
server.task_number = F("task_number") + 1
server.save(update_fields=["task_number"])
self.server = server
return server
return None return None
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):
@@ -196,8 +207,8 @@ class JudgeDispatcher(DispatcherBase):
self.submission.result = error_test_case[0]["result"] self.submission.result = error_test_case[0]["result"]
else: else:
self.submission.result = JudgeStatus.PARTIALLY_ACCEPTED self.submission.result = JudgeStatus.PARTIALLY_ACCEPTED
self.submission.save() self.submission.save(update_fields=["result", "info", "statistic_info"])
# 推送判题完成状态 # 推送判题完成状态
try: try:
push_submission_update( push_submission_update(
@@ -241,7 +252,7 @@ class JudgeDispatcher(DispatcherBase):
# update problem status # update problem status
problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id) problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id)
if self.last_result != JudgeStatus.ACCEPTED and self.submission.result == JudgeStatus.ACCEPTED: if self.last_result != JudgeStatus.ACCEPTED and self.submission.result == JudgeStatus.ACCEPTED:
problem.accepted_number += 1 problem.accepted_number = F("accepted_number") + 1
problem_info = problem.statistic_info problem_info = problem.statistic_info
problem_info[self.last_result] = problem_info.get(self.last_result, 1) - 1 problem_info[self.last_result] = problem_info.get(self.last_result, 1) - 1
problem_info[result] = problem_info.get(result, 0) + 1 problem_info[result] = problem_info.get(result, 0) + 1
@@ -277,9 +288,9 @@ class JudgeDispatcher(DispatcherBase):
with transaction.atomic(): with transaction.atomic():
# update problem status # update problem status
problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id) problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id)
problem.submission_number += 1 problem.submission_number = F("submission_number") + 1
if self.submission.result == JudgeStatus.ACCEPTED: if self.submission.result == JudgeStatus.ACCEPTED:
problem.accepted_number += 1 problem.accepted_number = F("accepted_number") + 1
problem_info = problem.statistic_info problem_info = problem.statistic_info
problem_info[result] = problem_info.get(result, 0) + 1 problem_info[result] = problem_info.get(result, 0) + 1
problem.save(update_fields=["accepted_number", "submission_number", "statistic_info"]) problem.save(update_fields=["accepted_number", "submission_number", "statistic_info"])
@@ -287,7 +298,7 @@ class JudgeDispatcher(DispatcherBase):
# update_userprofile # update_userprofile
user = User.objects.select_for_update().get(id=self.submission.user_id) user = User.objects.select_for_update().get(id=self.submission.user_id)
user_profile = user.userprofile user_profile = user.userprofile
user_profile.submission_number += 1 user_profile.submission_number = F("submission_number") + 1
if problem.rule_type == ProblemRuleType.ACM: if problem.rule_type == ProblemRuleType.ACM:
acm_problems_status = user_profile.acm_problems_status.get("problems", {}) acm_problems_status = user_profile.acm_problems_status.get("problems", {})
if problem_id not in acm_problems_status: if problem_id not in acm_problems_status:
@@ -356,9 +367,9 @@ class JudgeDispatcher(DispatcherBase):
result = str(self.submission.result) result = str(self.submission.result)
problem_info = problem.statistic_info problem_info = problem.statistic_info
problem_info[result] = problem_info.get(result, 0) + 1 problem_info[result] = problem_info.get(result, 0) + 1
problem.submission_number += 1 problem.submission_number = F("submission_number") + 1
if self.submission.result == JudgeStatus.ACCEPTED: if self.submission.result == JudgeStatus.ACCEPTED:
problem.accepted_number += 1 problem.accepted_number = F("accepted_number") + 1
problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"]) problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"])
def update_contest_rank(self): def update_contest_rank(self):
@@ -422,7 +433,7 @@ class JudgeDispatcher(DispatcherBase):
elif self.submission.result != JudgeStatus.COMPILE_ERROR: elif self.submission.result != JudgeStatus.COMPILE_ERROR:
info["error_number"] = 1 info["error_number"] = 1
rank.submission_info[str(self.submission.problem_id)] = info rank.submission_info[str(self.submission.problem_id)] = info
rank.save() rank.save(update_fields=["submission_info", "total_time", "accepted_number", "submission_number"])
def _update_oi_contest_rank(self, rank): def _update_oi_contest_rank(self, rank):
problem_id = str(self.submission.problem_id) problem_id = str(self.submission.problem_id)
@@ -433,4 +444,4 @@ class JudgeDispatcher(DispatcherBase):
else: else:
rank.total_score = rank.total_score + current_score rank.total_score = rank.total_score + current_score
rank.submission_info[problem_id] = current_score rank.submission_info[problem_id] = current_score
rank.save() rank.save(update_fields=["submission_info", "total_score", "submission_number"])

View File

@@ -0,0 +1,63 @@
# Generated by Django 6.0.4 on 2026-05-09 11:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('problem', '0008_alter_problem_unique_together_and_more'),
]
operations = [
migrations.AlterField(
model_name='problem',
name='accepted_number',
field=models.BigIntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='problem',
name='allow_flowchart',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='problem',
name='flowchart_data',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
migrations.AlterField(
model_name='problem',
name='is_public',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='problem',
name='share_submission',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='problem',
name='show_flowchart',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='problem',
name='statistic_info',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
migrations.AlterField(
model_name='problem',
name='submission_number',
field=models.BigIntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='problem',
name='total_score',
field=models.IntegerField(db_default=0, default=0),
),
migrations.AlterField(
model_name='problem',
name='visible',
field=models.BooleanField(db_default=True, default=True),
),
]

View File

@@ -36,7 +36,7 @@ class Problem(models.Model):
_id = models.TextField(db_index=True) _id = models.TextField(db_index=True)
contest = models.ForeignKey(Contest, null=True, on_delete=models.CASCADE) contest = models.ForeignKey(Contest, null=True, on_delete=models.CASCADE)
# for contest problem # for contest problem
is_public = models.BooleanField(default=False) is_public = models.BooleanField(default=False, db_default=False)
title = models.TextField() title = models.TextField()
# HTML # HTML
description = RichTextField() description = RichTextField()
@@ -61,7 +61,7 @@ class Problem(models.Model):
# io mode # io mode
io_mode = models.JSONField(default=_default_io_mode) io_mode = models.JSONField(default=_default_io_mode)
rule_type = models.TextField(choices=ProblemRuleType.choices) rule_type = models.TextField(choices=ProblemRuleType.choices)
visible = models.BooleanField(default=True) visible = models.BooleanField(default=True, db_default=True)
difficulty = models.TextField(choices=Difficulty.choices) difficulty = models.TextField(choices=Difficulty.choices)
tags = models.ManyToManyField(ProblemTag) tags = models.ManyToManyField(ProblemTag)
source = models.TextField(null=True) source = models.TextField(null=True)
@@ -69,19 +69,19 @@ class Problem(models.Model):
# [{language: "python", code: "..."}] # [{language: "python", code: "..."}]
answers = models.JSONField(null=True) answers = models.JSONField(null=True)
# for OI mode # for OI mode
total_score = models.IntegerField(default=0) total_score = models.IntegerField(default=0, db_default=0)
submission_number = models.BigIntegerField(default=0) submission_number = models.BigIntegerField(default=0, db_default=0)
accepted_number = models.BigIntegerField(default=0) accepted_number = models.BigIntegerField(default=0, db_default=0)
# {JudgeStatus.ACCEPTED: 3, JudgeStatus.WRONG_ANSWER: 11}, the number means count # {JudgeStatus.ACCEPTED: 3, JudgeStatus.WRONG_ANSWER: 11}, the number means count
statistic_info = models.JSONField(default=dict) statistic_info = models.JSONField(default=dict, db_default=models.Value({}))
share_submission = models.BooleanField(default=False) share_submission = models.BooleanField(default=False, db_default=False)
# 流程图相关字段 # 流程图相关字段
allow_flowchart = models.BooleanField(default=False) # 是否允许/需要提交流程图 allow_flowchart = models.BooleanField(default=False, db_default=False)
mermaid_code = models.TextField(null=True, blank=True) # 流程图答案(Mermaid代码) mermaid_code = models.TextField(null=True, blank=True)
flowchart_data = models.JSONField(default=dict) # 流程图答案元数据(JSON格式) flowchart_data = models.JSONField(default=dict, db_default=models.Value({}))
flowchart_hint = models.TextField(null=True, blank=True) # 流程图提示信息 flowchart_hint = models.TextField(null=True, blank=True)
show_flowchart = models.BooleanField(default=False) # 是否显示流程图答案数据如果True这样就不需要提交流程图了说明就是给学生看的 show_flowchart = models.BooleanField(default=False, db_default=False)
class Meta: class Meta:
db_table = "problem" db_table = "problem"

View File

@@ -118,11 +118,11 @@ class ProblemSetBadge(models.Model):
def _is_eligible(self, progress): def _is_eligible(self, progress):
"""判断用户进度是否满足该徽章条件(纯逻辑,不查数据库)""" """判断用户进度是否满足该徽章条件(纯逻辑,不查数据库)"""
if self.condition_type == "all_problems": if self.condition_type == BadgeConditionType.ALL_PROBLEMS:
return progress.completed_problems_count == progress.total_problems_count return progress.completed_problems_count == progress.total_problems_count
if self.condition_type == "problem_count": if self.condition_type == BadgeConditionType.PROBLEM_COUNT:
return progress.completed_problems_count >= self.condition_value return progress.completed_problems_count >= self.condition_value
if self.condition_type == "score": if self.condition_type == BadgeConditionType.SCORE:
return progress.total_score >= self.condition_value return progress.total_score >= self.condition_value
return False return False

View File

@@ -4,10 +4,12 @@ from django.utils import timezone
from account.models import User from account.models import User
from problem.models import Problem from problem.models import Problem
from problemset.models import ( from problemset.models import (
BadgeConditionType,
ProblemSet, ProblemSet,
ProblemSetBadge, ProblemSetBadge,
ProblemSetProblem, ProblemSetProblem,
ProblemSetProgress, ProblemSetProgress,
ProblemSetStatus,
ProblemSetSubmission, ProblemSetSubmission,
UserBadge, UserBadge,
) )
@@ -31,7 +33,7 @@ class ProblemSetAPI(APIView):
def get(self, request): def get(self, request):
"""获取题单列表""" """获取题单列表"""
# 预加载创建者信息 # 预加载创建者信息
problem_sets = ProblemSet.objects.filter(visible=True).exclude(status="draft").select_related("created_by") problem_sets = ProblemSet.objects.filter(visible=True).exclude(status=ProblemSetStatus.DRAFT).select_related("created_by")
# 使用annotate在查询时计算题目数量避免N+1查询 # 使用annotate在查询时计算题目数量避免N+1查询
problem_sets = problem_sets.annotate(problems_count=Count("problemsetproblem", distinct=True)) problem_sets = problem_sets.annotate(problems_count=Count("problemsetproblem", distinct=True))
@@ -90,7 +92,7 @@ class ProblemSetDetailAPI(APIView):
def get(self, request, problem_set_id): def get(self, request, problem_set_id):
"""获取题单详情""" """获取题单详情"""
try: try:
problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status="draft").get() problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status=ProblemSetStatus.DRAFT).get()
except ProblemSet.DoesNotExist: except ProblemSet.DoesNotExist:
return self.error("题单不存在") return self.error("题单不存在")
@@ -104,7 +106,7 @@ class ProblemSetProblemAPI(APIView):
def get(self, request, problem_set_id): def get(self, request, problem_set_id):
"""获取题单中的题目列表""" """获取题单中的题目列表"""
try: try:
problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status="draft").get() problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status=ProblemSetStatus.DRAFT).get()
except ProblemSet.DoesNotExist: except ProblemSet.DoesNotExist:
return self.error("题单不存在") return self.error("题单不存在")
@@ -128,7 +130,7 @@ class ProblemSetProgressAPI(APIView):
"""加入题单""" """加入题单"""
data = request.data data = request.data
try: try:
problem_set = ProblemSet.objects.filter(id=data["problemset_id"], visible=True).exclude(status="draft").get() problem_set = ProblemSet.objects.filter(id=data["problemset_id"], visible=True).exclude(status=ProblemSetStatus.DRAFT).get()
except ProblemSet.DoesNotExist: except ProblemSet.DoesNotExist:
return self.error("题单不存在") return self.error("题单不存在")
@@ -144,7 +146,7 @@ class ProblemSetProgressAPI(APIView):
def get(self, request, problem_set_id): def get(self, request, problem_set_id):
"""获取题单进度""" """获取题单进度"""
try: try:
problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status="draft").get() problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status=ProblemSetStatus.DRAFT).get()
except ProblemSet.DoesNotExist: except ProblemSet.DoesNotExist:
return self.error("题单不存在") return self.error("题单不存在")
@@ -161,7 +163,7 @@ class ProblemSetProgressAPI(APIView):
"""更新进度""" """更新进度"""
data = request.data data = request.data
try: try:
problem_set = ProblemSet.objects.filter(id=data["problemset_id"], visible=True).exclude(status="draft").get() problem_set = ProblemSet.objects.filter(id=data["problemset_id"], visible=True).exclude(status=ProblemSetStatus.DRAFT).get()
except ProblemSet.DoesNotExist: except ProblemSet.DoesNotExist:
return self.error("题单不存在") return self.error("题单不存在")
@@ -223,13 +225,13 @@ class ProblemSetProgressAPI(APIView):
if UserBadge.objects.filter(user=progress.user, badge=badge).exists(): if UserBadge.objects.filter(user=progress.user, badge=badge).exists():
continue continue
if badge.condition_type == "all_problems": if badge.condition_type == BadgeConditionType.ALL_PROBLEMS:
if progress.completed_problems_count == progress.total_problems_count: if progress.completed_problems_count == progress.total_problems_count:
UserBadge.objects.create(user=progress.user, badge=badge) UserBadge.objects.create(user=progress.user, badge=badge)
elif badge.condition_type == "problem_count": elif badge.condition_type == BadgeConditionType.PROBLEM_COUNT:
if progress.completed_problems_count >= badge.condition_value: if progress.completed_problems_count >= badge.condition_value:
UserBadge.objects.create(user=progress.user, badge=badge) UserBadge.objects.create(user=progress.user, badge=badge)
elif badge.condition_type == "score": elif badge.condition_type == BadgeConditionType.SCORE:
if progress.total_score >= badge.condition_value: if progress.total_score >= badge.condition_value:
UserBadge.objects.create(user=progress.user, badge=badge) UserBadge.objects.create(user=progress.user, badge=badge)
@@ -273,7 +275,7 @@ class ProblemSetBadgeAPI(APIView):
def get(self, request, problem_set_id): def get(self, request, problem_set_id):
"""获取题单的奖章列表""" """获取题单的奖章列表"""
try: try:
problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status="draft").get() problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status=ProblemSetStatus.DRAFT).get()
except ProblemSet.DoesNotExist: except ProblemSet.DoesNotExist:
return self.error("题单不存在") return self.error("题单不存在")
@@ -288,7 +290,7 @@ class ProblemSetUserProgressAPI(APIView):
def get(self, request, problem_set_id: int): def get(self, request, problem_set_id: int):
"""获取题单的用户进度列表""" """获取题单的用户进度列表"""
try: try:
problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status="draft").get() problem_set = ProblemSet.objects.filter(id=problem_set_id, visible=True).exclude(status=ProblemSetStatus.DRAFT).get()
except ProblemSet.DoesNotExist: except ProblemSet.DoesNotExist:
return self.error("题单不存在") return self.error("题单不存在")

View File

@@ -0,0 +1,33 @@
# Generated by Django 6.0.4 on 2026-05-09 11:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('submission', '0005_alter_submission_result'),
]
operations = [
migrations.AlterField(
model_name='submission',
name='info',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
migrations.AlterField(
model_name='submission',
name='result',
field=models.IntegerField(choices=[(-2, 'Compile Error'), (-1, 'Wrong Answer'), (0, 'Accepted'), (1, 'CPU Time Limit Exceeded'), (2, 'Real Time Limit Exceeded'), (3, 'Memory Limit Exceeded'), (4, 'Runtime Error'), (5, 'System Error'), (6, 'Pending'), (7, 'Judging'), (8, 'Partially Accepted')], db_default=6, db_index=True, default=6),
),
migrations.AlterField(
model_name='submission',
name='shared',
field=models.BooleanField(db_default=False, default=False),
),
migrations.AlterField(
model_name='submission',
name='statistic_info',
field=models.JSONField(db_default=models.Value({}), default=dict),
),
]

View File

@@ -29,14 +29,14 @@ class Submission(models.Model):
user_id = models.IntegerField(db_index=True) user_id = models.IntegerField(db_index=True)
username = models.TextField() username = models.TextField()
code = models.TextField() code = models.TextField()
result = models.IntegerField(choices=JudgeStatus.choices, db_index=True, default=JudgeStatus.PENDING) result = models.IntegerField(choices=JudgeStatus.choices, db_index=True, default=JudgeStatus.PENDING, db_default=JudgeStatus.PENDING)
# 从JudgeServer返回的判题详情 # 从JudgeServer返回的判题详情
info = JSONField(default=dict) info = JSONField(default=dict, db_default=models.Value({}))
language = models.TextField() language = models.TextField()
shared = models.BooleanField(default=False) shared = models.BooleanField(default=False, db_default=False)
# 存储该提交所用时间和内存值,方便提交列表显示 # 存储该提交所用时间和内存值,方便提交列表显示
# {time_cost: "", memory_cost: "", err_info: "", score: 0} # {time_cost: "", memory_cost: "", err_info: "", score: 0}
statistic_info = JSONField(default=dict) statistic_info = JSONField(default=dict, db_default=models.Value({}))
ip = models.TextField(null=True) ip = models.TextField(null=True)
def check_user_permission(self, user, check_share=True): def check_user_permission(self, user, check_share=True):

View File

@@ -2,7 +2,7 @@ from django.db import models
from django.db.models import F from django.db.models import F
from django.utils import timezone from django.utils import timezone
from problemset.models import ProblemSetProgress from problemset.models import ProblemSetProgress, ProblemSetStatus
from utils.api import serializers from utils.api import serializers
from utils.serializers import LanguageNameChoiceField from utils.serializers import LanguageNameChoiceField
@@ -16,7 +16,7 @@ def bulk_fetch_problemset_progress(user, problem_ids):
rows = ( rows = (
ProblemSetProgress.objects.filter( ProblemSetProgress.objects.filter(
user=user, user=user,
problemset__status="active", problemset__status=ProblemSetStatus.ACTIVE,
problemset__problemsetproblem__problem_id__in=problem_ids, problemset__problemsetproblem__problem_id__in=problem_ids,
) )
.filter( .filter(
@@ -108,7 +108,7 @@ class SubmissionListSerializer(serializers.ModelSerializer):
self._problemset_progress_cache[problem_id] = ( self._problemset_progress_cache[problem_id] = (
ProblemSetProgress.objects.filter( ProblemSetProgress.objects.filter(
user=self.user, user=self.user,
problemset__status="active", problemset__status=ProblemSetStatus.ACTIVE,
problemset__problemsetproblem__problem_id=problem_id, problemset__problemsetproblem__problem_id=problem_id,
) )
.filter( .filter(