同一个剥前缀函数在 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 <noreply@anthropic.com>
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
import os
|
||
import random
|
||
import re
|
||
|
||
from django.utils.crypto import get_random_string
|
||
|
||
|
||
def rand_str(length=32, type="lower_hex"):
|
||
"""
|
||
生成指定长度的随机字符串或者数字, 可以用于密钥等安全场景
|
||
:param length: 字符串或者数字的长度
|
||
:param type: str 代表随机字符串,num 代表随机数字
|
||
:return: 字符串
|
||
"""
|
||
if type == "str":
|
||
return get_random_string(length, allowed_chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
||
elif type == "lower_str":
|
||
return get_random_string(length, allowed_chars="abcdefghijklmnopqrstuvwxyz0123456789")
|
||
elif type == "lower_hex":
|
||
return random.choice("123456789abcdef") + get_random_string(length - 1, allowed_chars="0123456789abcdef")
|
||
else:
|
||
return random.choice("123456789") + get_random_string(length - 1, allowed_chars="0123456789")
|
||
|
||
|
||
def build_query_string(kv_data, ignore_none=True):
|
||
# {"a": 1, "b": "test"} -> "?a=1&b=test"
|
||
query_string = ""
|
||
for k, v in kv_data.items():
|
||
if ignore_none is True and kv_data[k] is None:
|
||
continue
|
||
if query_string != "":
|
||
query_string += "&"
|
||
else:
|
||
query_string = "?"
|
||
query_string += k + "=" + str(v)
|
||
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()
|
||
if value.endswith("+00:00"):
|
||
value = value[:-6] + "Z"
|
||
return value
|
||
return value.strftime(format)
|
||
|
||
|
||
def natural_sort_key(s, _nsre=re.compile(r"(\d+)")):
|
||
return [int(text) if text.isdigit() else text.lower() for text in re.split(_nsre, s)]
|
||
|
||
|
||
def get_env(name, default=""):
|
||
return os.environ.get(name, default)
|
||
|
||
|
||
def DRAMATIQ_WORKER_ARGS(time_limit=3600_000, max_retries=0, max_age=7200_000):
|
||
return {"max_retries": max_retries, "time_limit": time_limit, "max_age": max_age}
|
||
|
||
|
||
def check_is_id(value):
|
||
try:
|
||
return int(value) > 0
|
||
except Exception:
|
||
return False
|