From 625f2466e50c4501de3f2955ceba3e7f26449785 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Wed, 5 Aug 2026 23:58:13 -0600 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=BB=9F=E4=B8=80=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=90=8D=E7=9A=84=20ks=20=E7=8F=AD=E7=BA=A7=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同一个剥前缀函数在 submission 和 flowchart 的管理端各有一份逐字相同的 拷贝,conf 里还有一份内联写法,合并到 utils/shortcuts.strip_class_prefix。 改名是因为原名 get_real_name 和 UserProfile.real_name 字段、以及三个 serializer 里的同名方法都容易混。 行为上修了两处: - 剥前缀改用 removeprefix,不再按长度硬切。班级号对不上时原样返回, 旧写法会从中间截出乱码(ks999王五 配 class_name=251 会切成「王五」) - get_class_name 的正则从 \d+ 收紧到 \d{3,4},与前端 ButtonWithSearch 的 /^ks\d{3,4}/ 对齐。旧的贪婪匹配在姓名部分是纯 数字时会吃掉整串(ks251001 返回 251001 而不是 251)。顺带 re.search 换成 re.match,外层的 startswith 判断并进正则 正常数据(ks251张三、ks2510李四)新旧结果一致,已逐例对拍。 Co-Authored-By: Claude Opus 5 --- account/views/admin.py | 15 +++++---------- conf/views.py | 7 ++----- flowchart/views/admin.py | 9 ++------- submission/views/admin.py | 9 ++------- utils/shortcuts.py | 13 +++++++++++++ 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/account/views/admin.py b/account/views/admin.py index 5a4be72..67b47b6 100644 --- a/account/views/admin.py +++ b/account/views/admin.py @@ -22,17 +22,12 @@ from ..serializers import ( ) -# ks251XXX 或者 ks2510XX 返回 251 或者 2510 -# 其他返回 None +# ks251XXX 或者 ks2510XX 返回 251 或者 2510,其他返回 None。 +# 班级号限定 3~4 位,与前端 ButtonWithSearch 的 /^ks\d{3,4}/ 保持一致; +# 原来的 \d+ 会贪婪吃掉后面的数字(ks251001 会返回 251001 而不是 251)。 def get_class_name(username): - if username.startswith("ks"): - result = re.search(r"ks\d+", username) - if result: - return result.group(0)[2:] - else: - return None - else: - return None + result = re.match(r"ks(\d{3,4})", username) + return result.group(1) if result else None class UserAdminAPI(APIView): diff --git a/conf/views.py b/conf/views.py index dddce53..bc812fa 100644 --- a/conf/views.py +++ b/conf/views.py @@ -19,7 +19,7 @@ from problem.models import Problem from submission.models import Submission from utils.api import APIView, AsyncAPIView, CSRFExemptAPIView, validate_serializer from utils.cache import JsonDataLoader -from utils.shortcuts import get_env +from utils.shortcuts import get_env, strip_class_prefix from utils.websocket import push_config_update from utils.xss_filter import XSSHtml @@ -239,8 +239,5 @@ class ClassUsernamesAPI(AsyncAPIView): classroom = request.GET.get("classroom", "") if not classroom: return self.error("需要班级号") - prefix = f"ks{classroom}" - names = [ - user.username[len(prefix) :] if user.username.startswith(prefix) else user.username async for user in User.objects.filter(class_name=classroom).order_by("-create_time") - ] + names = [strip_class_prefix(user.username, classroom) async for user in User.objects.filter(class_name=classroom).order_by("-create_time")] return self.success(names) diff --git a/flowchart/views/admin.py b/flowchart/views/admin.py index d0d1880..1f2f101 100644 --- a/flowchart/views/admin.py +++ b/flowchart/views/admin.py @@ -8,6 +8,7 @@ from account.decorators import teacher_admin_required from account.models import AdminType, User from problem.models import Problem from utils.api import APIView +from utils.shortcuts import strip_class_prefix from ..models import FlowchartSubmission, FlowchartSubmissionStatus @@ -64,12 +65,6 @@ for _w in CUSTOM_WORDS: jieba.add_word(_w, freq=9999) -def get_real_name(username, class_name): - if class_name and username.startswith("ks"): - return username[len(f"ks{class_name}") :] - return username - - class FlowchartStatisticsAPI(APIView): @teacher_admin_required def get(self, request): @@ -169,7 +164,7 @@ class FlowchartStatisticsAPI(APIView): if all_users_dict: for uname in set(all_users_dict.keys()) - submitted_users: class_name = all_users_dict[uname] - real_name = get_real_name(uname, class_name) + real_name = strip_class_prefix(uname, class_name) unaccepted.append({"username": uname, "real_name": real_name}) # 5. Word cloud from feedback + suggestions + criteria comments diff --git a/submission/views/admin.py b/submission/views/admin.py index 928ed93..35acfb0 100644 --- a/submission/views/admin.py +++ b/submission/views/admin.py @@ -5,16 +5,11 @@ from account.models import AdminType, User from judge.tasks import judge_task from problem.models import Problem from utils.api import APIView +from utils.shortcuts import strip_class_prefix from ..models import JudgeStatus, Submission -def get_real_name(username, class_name): - if class_name and username.startswith("ks"): - return username[len(f"ks{class_name}") :] - return username - - class SubmissionRejudgeAPI(APIView): @super_admin_required def get(self, request): @@ -132,7 +127,7 @@ class SubmissionStatisticsAPI(APIView): unaccepted_usernames = set(all_users_dict.keys()) - submitted_usernames for username in unaccepted_usernames: class_name = all_users_dict[username] - real_name = get_real_name(username, class_name) + real_name = strip_class_prefix(username, class_name) unaccepted.append({"username": username, "real_name": real_name}) # 计算人数完成率 diff --git a/utils/shortcuts.py b/utils/shortcuts.py index 09b2af6..d3f3850 100644 --- a/utils/shortcuts.py +++ b/utils/shortcuts.py @@ -36,6 +36,19 @@ def build_query_string(kv_data, ignore_none=True): return query_string +def strip_class_prefix(username, class_name): + """ + 去掉用户名里的 ks<班级号> 前缀,得到学生本人那一段。 + 用户名形如 ks251张三,class_name 为 251 时返回 张三。 + + 用 removeprefix 而不是按长度切片:前缀对不上时原样返回, + 不会从中间截出乱码。 + """ + if not class_name: + return username + return username.removeprefix(f"ks{class_name}") + + def datetime2str(value, format="iso-8601"): if format.lower() == "iso-8601": value = value.isoformat()