diff --git a/account/migrations/0005_alter_user_is_disabled_alter_user_open_api_and_more.py b/account/migrations/0005_alter_user_is_disabled_alter_user_open_api_and_more.py new file mode 100644 index 0000000..4c1e462 --- /dev/null +++ b/account/migrations/0005_alter_user_is_disabled_alter_user_open_api_and_more.py @@ -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), + ), + ] diff --git a/account/models.py b/account/models.py index e37cdc0..88f92e6 100644 --- a/account/models.py +++ b/account/models.py @@ -36,13 +36,13 @@ class User(AbstractBaseUser): reset_password_token_expire_time = models.DateTimeField(null=True) # SSO auth token 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) - session_keys = JSONField(default=list) + session_keys = JSONField(default=list, db_default=models.Value([])) # 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) - 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="明文密码") 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 - oi_problems_status = JSONField(default=dict) + oi_problems_status = JSONField(default=dict, db_default=models.Value({})) real_name = models.TextField(null=True) avatar = models.TextField(default=f"{settings.AVATAR_URI_PREFIX}/default.png") @@ -106,24 +106,24 @@ class UserProfile(models.Model): major = models.TextField(null=True) language = models.TextField(null=True) # for ACM - accepted_number = models.IntegerField(default=0) + accepted_number = models.IntegerField(default=0, db_default=0) # for OI - total_score = models.BigIntegerField(default=0) - submission_number = models.IntegerField(default=0) + total_score = models.BigIntegerField(default=0, db_default=0) + submission_number = models.IntegerField(default=0, db_default=0) def add_accepted_problem_number(self): self.accepted_number = models.F("accepted_number") + 1 - self.save() + self.save(update_fields=["accepted_number"]) def add_submission_number(self): 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): last_time_score = last_time_score or 0 self.total_score = models.F("total_score") - last_time_score + this_time_score - self.save() + self.save(update_fields=["total_score"]) class Meta: db_table = "user_profile" diff --git a/contest/migrations/0005_alter_acmcontestrank_accepted_number_and_more.py b/contest/migrations/0005_alter_acmcontestrank_accepted_number_and_more.py new file mode 100644 index 0000000..b09df5e --- /dev/null +++ b/contest/migrations/0005_alter_acmcontestrank_accepted_number_and_more.py @@ -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), + ), + ] diff --git a/contest/models.py b/contest/models.py index a8094d4..9b17e6a 100644 --- a/contest/models.py +++ b/contest/models.py @@ -55,19 +55,19 @@ class Contest(models.Model): class AbstractContestRank(models.Model): user = models.ForeignKey(User, 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: abstract = True 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 = 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}} # key is problem id - submission_info = JSONField(default=dict) + submission_info = JSONField(default=dict, db_default=models.Value({})) class Meta: db_table = "acm_contest_rank" @@ -81,10 +81,10 @@ class ACMContestRank(AbstractContestRank): class OIContestRank(AbstractContestRank): - total_score = models.IntegerField(default=0) + total_score = models.IntegerField(default=0, db_default=0) # {"23": 333} # key is problem id, value is current score - submission_info = JSONField(default=dict) + submission_info = JSONField(default=dict, db_default=models.Value({})) class Meta: db_table = "oi_contest_rank" diff --git a/judge/dispatcher.py b/judge/dispatcher.py index 312ae3b..be008f5 100644 --- a/judge/dispatcher.py +++ b/judge/dispatcher.py @@ -1,11 +1,13 @@ import hashlib import json import logging +from datetime import timedelta from urllib.parse import urljoin import requests from django.db import IntegrityError, transaction from django.db.models import F +from django.utils import timezone from account.models import User from conf.models import JudgeServer @@ -38,14 +40,23 @@ class ChooseJudgeServer: def __enter__(self) -> [JudgeServer, None]: with transaction.atomic(): - servers = JudgeServer.objects.select_for_update().filter(is_disabled=False).order_by("task_number") - servers = [s for s in servers if s.status == "normal"] - for server in servers: - if server.task_number <= server.cpu_core * 2: - server.task_number = F("task_number") + 1 - server.save(update_fields=["task_number"]) - self.server = server - return server + cutoff = timezone.now() - timedelta(seconds=6) + server = ( + JudgeServer.objects + .select_for_update(skip_locked=True) + .filter( + is_disabled=False, + last_heartbeat__gte=cutoff, + 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 def __exit__(self, exc_type, exc_val, exc_tb): @@ -196,8 +207,8 @@ class JudgeDispatcher(DispatcherBase): self.submission.result = error_test_case[0]["result"] else: self.submission.result = JudgeStatus.PARTIALLY_ACCEPTED - self.submission.save() - + self.submission.save(update_fields=["result", "info", "statistic_info"]) + # 推送判题完成状态 try: push_submission_update( @@ -241,7 +252,7 @@ class JudgeDispatcher(DispatcherBase): # update problem status 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: - problem.accepted_number += 1 + problem.accepted_number = F("accepted_number") + 1 problem_info = problem.statistic_info problem_info[self.last_result] = problem_info.get(self.last_result, 1) - 1 problem_info[result] = problem_info.get(result, 0) + 1 @@ -277,9 +288,9 @@ class JudgeDispatcher(DispatcherBase): with transaction.atomic(): # update problem status 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: - problem.accepted_number += 1 + problem.accepted_number = F("accepted_number") + 1 problem_info = problem.statistic_info problem_info[result] = problem_info.get(result, 0) + 1 problem.save(update_fields=["accepted_number", "submission_number", "statistic_info"]) @@ -287,7 +298,7 @@ class JudgeDispatcher(DispatcherBase): # update_userprofile user = User.objects.select_for_update().get(id=self.submission.user_id) user_profile = user.userprofile - user_profile.submission_number += 1 + user_profile.submission_number = F("submission_number") + 1 if problem.rule_type == ProblemRuleType.ACM: acm_problems_status = user_profile.acm_problems_status.get("problems", {}) if problem_id not in acm_problems_status: @@ -356,9 +367,9 @@ class JudgeDispatcher(DispatcherBase): result = str(self.submission.result) problem_info = problem.statistic_info 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: - problem.accepted_number += 1 + problem.accepted_number = F("accepted_number") + 1 problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"]) def update_contest_rank(self): @@ -422,7 +433,7 @@ class JudgeDispatcher(DispatcherBase): elif self.submission.result != JudgeStatus.COMPILE_ERROR: info["error_number"] = 1 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): problem_id = str(self.submission.problem_id) @@ -433,4 +444,4 @@ class JudgeDispatcher(DispatcherBase): else: rank.total_score = rank.total_score + current_score rank.submission_info[problem_id] = current_score - rank.save() + rank.save(update_fields=["submission_info", "total_score", "submission_number"]) diff --git a/problem/migrations/0009_alter_problem_accepted_number_and_more.py b/problem/migrations/0009_alter_problem_accepted_number_and_more.py new file mode 100644 index 0000000..7d18cde --- /dev/null +++ b/problem/migrations/0009_alter_problem_accepted_number_and_more.py @@ -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), + ), + ] diff --git a/problem/models.py b/problem/models.py index 0f8a205..60ca5d4 100644 --- a/problem/models.py +++ b/problem/models.py @@ -36,7 +36,7 @@ class Problem(models.Model): _id = models.TextField(db_index=True) contest = models.ForeignKey(Contest, null=True, on_delete=models.CASCADE) # for contest problem - is_public = models.BooleanField(default=False) + is_public = models.BooleanField(default=False, db_default=False) title = models.TextField() # HTML description = RichTextField() @@ -61,7 +61,7 @@ class Problem(models.Model): # io mode io_mode = models.JSONField(default=_default_io_mode) 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) tags = models.ManyToManyField(ProblemTag) source = models.TextField(null=True) @@ -69,19 +69,19 @@ class Problem(models.Model): # [{language: "python", code: "..."}] answers = models.JSONField(null=True) # for OI mode - total_score = models.IntegerField(default=0) - submission_number = models.BigIntegerField(default=0) - accepted_number = models.BigIntegerField(default=0) + total_score = models.IntegerField(default=0, db_default=0) + submission_number = models.BigIntegerField(default=0, db_default=0) + accepted_number = models.BigIntegerField(default=0, db_default=0) # {JudgeStatus.ACCEPTED: 3, JudgeStatus.WRONG_ANSWER: 11}, the number means count - statistic_info = models.JSONField(default=dict) - share_submission = models.BooleanField(default=False) + statistic_info = models.JSONField(default=dict, db_default=models.Value({})) + share_submission = models.BooleanField(default=False, db_default=False) # 流程图相关字段 - allow_flowchart = models.BooleanField(default=False) # 是否允许/需要提交流程图 - mermaid_code = models.TextField(null=True, blank=True) # 流程图答案(Mermaid代码) - flowchart_data = models.JSONField(default=dict) # 流程图答案元数据(JSON格式) - flowchart_hint = models.TextField(null=True, blank=True) # 流程图提示信息 - show_flowchart = models.BooleanField(default=False) # 是否显示流程图答案数据,如果True,这样就不需要提交流程图了,说明就是给学生看的 + allow_flowchart = models.BooleanField(default=False, db_default=False) + mermaid_code = models.TextField(null=True, blank=True) + flowchart_data = models.JSONField(default=dict, db_default=models.Value({})) + flowchart_hint = models.TextField(null=True, blank=True) + show_flowchart = models.BooleanField(default=False, db_default=False) class Meta: db_table = "problem" diff --git a/problemset/models.py b/problemset/models.py index 98b74e6..7feeeba 100644 --- a/problemset/models.py +++ b/problemset/models.py @@ -118,11 +118,11 @@ class ProblemSetBadge(models.Model): 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 - if self.condition_type == "problem_count": + if self.condition_type == BadgeConditionType.PROBLEM_COUNT: 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 False diff --git a/problemset/views/oj.py b/problemset/views/oj.py index 7e15cd9..6818607 100644 --- a/problemset/views/oj.py +++ b/problemset/views/oj.py @@ -4,10 +4,12 @@ from django.utils import timezone from account.models import User from problem.models import Problem from problemset.models import ( + BadgeConditionType, ProblemSet, ProblemSetBadge, ProblemSetProblem, ProblemSetProgress, + ProblemSetStatus, ProblemSetSubmission, UserBadge, ) @@ -31,7 +33,7 @@ class ProblemSetAPI(APIView): 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查询 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): """获取题单详情""" 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: return self.error("题单不存在") @@ -104,7 +106,7 @@ class ProblemSetProblemAPI(APIView): def get(self, request, problem_set_id): """获取题单中的题目列表""" 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: return self.error("题单不存在") @@ -128,7 +130,7 @@ class ProblemSetProgressAPI(APIView): """加入题单""" data = request.data 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: return self.error("题单不存在") @@ -144,7 +146,7 @@ class ProblemSetProgressAPI(APIView): def get(self, request, problem_set_id): """获取题单进度""" 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: return self.error("题单不存在") @@ -161,7 +163,7 @@ class ProblemSetProgressAPI(APIView): """更新进度""" data = request.data 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: return self.error("题单不存在") @@ -223,13 +225,13 @@ class ProblemSetProgressAPI(APIView): if UserBadge.objects.filter(user=progress.user, badge=badge).exists(): continue - if badge.condition_type == "all_problems": + if badge.condition_type == BadgeConditionType.ALL_PROBLEMS: if progress.completed_problems_count == progress.total_problems_count: 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: 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: UserBadge.objects.create(user=progress.user, badge=badge) @@ -273,7 +275,7 @@ class ProblemSetBadgeAPI(APIView): def get(self, request, problem_set_id): """获取题单的奖章列表""" 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: return self.error("题单不存在") @@ -288,7 +290,7 @@ class ProblemSetUserProgressAPI(APIView): def get(self, request, problem_set_id: int): """获取题单的用户进度列表""" 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: return self.error("题单不存在") diff --git a/submission/migrations/0006_alter_submission_info_alter_submission_result_and_more.py b/submission/migrations/0006_alter_submission_info_alter_submission_result_and_more.py new file mode 100644 index 0000000..9752b0b --- /dev/null +++ b/submission/migrations/0006_alter_submission_info_alter_submission_result_and_more.py @@ -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), + ), + ] diff --git a/submission/models.py b/submission/models.py index cfe7cdd..fb2e14c 100644 --- a/submission/models.py +++ b/submission/models.py @@ -29,14 +29,14 @@ class Submission(models.Model): user_id = models.IntegerField(db_index=True) username = 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返回的判题详情 - info = JSONField(default=dict) + info = JSONField(default=dict, db_default=models.Value({})) language = models.TextField() - shared = models.BooleanField(default=False) + shared = models.BooleanField(default=False, db_default=False) # 存储该提交所用时间和内存值,方便提交列表显示 # {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) def check_user_permission(self, user, check_share=True): diff --git a/submission/serializers.py b/submission/serializers.py index 3e27db0..27ff680 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -2,7 +2,7 @@ from django.db import models from django.db.models import F from django.utils import timezone -from problemset.models import ProblemSetProgress +from problemset.models import ProblemSetProgress, ProblemSetStatus from utils.api import serializers from utils.serializers import LanguageNameChoiceField @@ -16,7 +16,7 @@ def bulk_fetch_problemset_progress(user, problem_ids): rows = ( ProblemSetProgress.objects.filter( user=user, - problemset__status="active", + problemset__status=ProblemSetStatus.ACTIVE, problemset__problemsetproblem__problem_id__in=problem_ids, ) .filter( @@ -108,7 +108,7 @@ class SubmissionListSerializer(serializers.ModelSerializer): self._problemset_progress_cache[problem_id] = ( ProblemSetProgress.objects.filter( user=self.user, - problemset__status="active", + problemset__status=ProblemSetStatus.ACTIVE, problemset__problemsetproblem__problem_id=problem_id, ) .filter(