|
4 | 4 | import json |
5 | 5 | from random import SystemRandom |
6 | 6 |
|
7 | | -from app.tools.list import get_group_list, get_student_list, filter_students_data |
8 | | -from app.tools.history import calculate_weight |
| 7 | +from app.tools.list import get_group_list, get_student_list, filter_students_data, get_pool_list |
| 8 | +from app.tools.history import calculate_weight, load_history_data |
9 | 9 | from app.tools.config import ( |
10 | 10 | calculate_remaining_count, |
11 | 11 | read_drawn_record, |
| 12 | + read_drawn_record_simple, |
12 | 13 | reset_drawn_record, |
13 | 14 | ) |
14 | 15 | from app.tools.path_utils import get_resources_path, open_file |
@@ -68,7 +69,7 @@ def update_many_count_label_text( |
68 | 69 | tuple: (总人数, 剩余人数, 格式化文本) |
69 | 70 | """ |
70 | 71 | # 根据范围计算实际人数 |
71 | | - total_count = RollCallUtils.get_total_count( |
| 72 | + total_count = LotteryUtils.get_total_count( |
72 | 73 | list_combobox_text, range_combobox_index, range_combobox_text |
73 | 74 | ) |
74 | 75 |
|
@@ -243,6 +244,121 @@ def draw_random_students( |
243 | 244 | "gender_filter": gender_filter, |
244 | 245 | } |
245 | 246 |
|
| 247 | + @staticmethod |
| 248 | + def get_prize_total_count(pool_name: str) -> int: |
| 249 | + """获取奖池奖品总数""" |
| 250 | + try: |
| 251 | + from app.tools.list import get_pool_list |
| 252 | + return len(get_pool_list(pool_name)) |
| 253 | + except Exception: |
| 254 | + return 0 |
| 255 | + |
| 256 | + @staticmethod |
| 257 | + def update_prize_many_count_label_text(pool_name: str): |
| 258 | + """生成奖品总数/剩余显示文本""" |
| 259 | + total_count = LotteryUtils.get_prize_total_count(pool_name) |
| 260 | + remaining_count = LotteryUtils.calculate_prize_remaining_count(pool_name) |
| 261 | + if remaining_count == 0: |
| 262 | + remaining_count = total_count |
| 263 | + text_template = get_any_position_value("lottery", "many_count_label", "text_0") |
| 264 | + formatted_text = text_template.format(total_count=total_count, remaining_count=remaining_count) |
| 265 | + return total_count, remaining_count, formatted_text |
| 266 | + |
| 267 | + @staticmethod |
| 268 | + def draw_random_prizes(pool_name: str, current_count: int): |
| 269 | + """按权重抽取奖品""" |
| 270 | + try: |
| 271 | + from app.tools.config import read_drawn_record |
| 272 | + items = get_pool_list(pool_name) |
| 273 | + if not items: |
| 274 | + return {"selected_prizes": [], "pool_name": pool_name, "selected_prizes_dict": []} |
| 275 | + # 非重复/半重复处理:根据 TEMP 记录过滤已达阈值的奖品(与 roll_call 一致) |
| 276 | + threshold = LotteryUtils._get_prize_draw_threshold() |
| 277 | + if threshold is not None: |
| 278 | + drawn_records = read_drawn_record_simple(pool_name) |
| 279 | + drawn_counts = {name: cnt for name, cnt in drawn_records} |
| 280 | + available = [] |
| 281 | + for i in items: |
| 282 | + name = i.get("name", "") |
| 283 | + cnt = int(drawn_counts.get(name, 0)) |
| 284 | + if cnt < threshold: |
| 285 | + available.append(i) |
| 286 | + items = available |
| 287 | + if not items: |
| 288 | + return {"reset_required": True} |
| 289 | + # 准备权重 |
| 290 | + weights = [float(i.get("weight", 1)) for i in items] |
| 291 | + draw = min(current_count, len(items)) |
| 292 | + selected = [] |
| 293 | + selected_dict = [] |
| 294 | + for _ in range(draw): |
| 295 | + if not items: |
| 296 | + break |
| 297 | + total_weight = sum(weights) |
| 298 | + if total_weight <= 0: |
| 299 | + idx = system_random.randint(0, len(items) - 1) |
| 300 | + else: |
| 301 | + rv = system_random.uniform(0, total_weight) |
| 302 | + cum = 0 |
| 303 | + idx = 0 |
| 304 | + for i, w in enumerate(weights): |
| 305 | + cum += w |
| 306 | + if rv <= cum: |
| 307 | + idx = i |
| 308 | + break |
| 309 | + chosen = items[idx] |
| 310 | + selected.append((chosen.get("id"), chosen.get("name"), chosen.get("exist", True))) |
| 311 | + selected_dict.append(chosen) |
| 312 | + items.pop(idx) |
| 313 | + weights.pop(idx) |
| 314 | + return { |
| 315 | + "selected_prizes": selected, |
| 316 | + "pool_name": pool_name, |
| 317 | + "selected_prizes_dict": selected_dict, |
| 318 | + } |
| 319 | + except Exception: |
| 320 | + return {"selected_prizes": [], "pool_name": pool_name, "selected_prizes_dict": []} |
| 321 | + |
| 322 | + @staticmethod |
| 323 | + def _get_prize_draw_threshold(): |
| 324 | + """获取奖品抽取阈值:None 表示可重复;1 表示不重复;半重复返回次数阈值""" |
| 325 | + try: |
| 326 | + mode = readme_settings_async("lottery_settings", "draw_mode") |
| 327 | + if mode == 1: |
| 328 | + return 1 |
| 329 | + elif mode == 2: |
| 330 | + hr = readme_settings_async("lottery_settings", "half_repeat") |
| 331 | + try: |
| 332 | + return int(hr) if hr else 1 |
| 333 | + except Exception: |
| 334 | + return 1 |
| 335 | + else: |
| 336 | + return None |
| 337 | + except Exception: |
| 338 | + return None |
| 339 | + |
| 340 | + @staticmethod |
| 341 | + def calculate_prize_remaining_count(pool_name: str) -> int: |
| 342 | + """计算剩余可抽奖品数量,考虑不重复/半重复设置""" |
| 343 | + try: |
| 344 | + from app.tools.list import get_pool_list |
| 345 | + from app.tools.config import read_drawn_record |
| 346 | + threshold = LotteryUtils._get_prize_draw_threshold() |
| 347 | + total = len(get_pool_list(pool_name)) |
| 348 | + if threshold is None: |
| 349 | + return total |
| 350 | + drawn_records = read_drawn_record_simple(pool_name) |
| 351 | + drawn_counts = {name: cnt for name, cnt in drawn_records} |
| 352 | + remain = 0 |
| 353 | + for i in get_pool_list(pool_name): |
| 354 | + name = i.get("name", "") |
| 355 | + cnt = int(drawn_counts.get(name, 0)) |
| 356 | + if cnt < threshold: |
| 357 | + remain += 1 |
| 358 | + return remain |
| 359 | + except Exception: |
| 360 | + return 0 |
| 361 | + |
246 | 362 | @staticmethod |
247 | 363 | def draw_random_groups(students_dict_list, current_count, draw_type): |
248 | 364 | """ |
|
0 commit comments