Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG/v2.3.0-beta.2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ v2.3 - Shiroko (砂狼白子) beta 2
- 优化 **浮窗**,切换同步收纳显示
- 优化 **计时器**,倒计时页面居中显示,秒表居中与所有时钟字距优化
- 优化 **更新安装**,管理员静默安装并退出
- 优化 **ClassIsland 联动**,现在可以传递更多信息

## 🐛 修复问题

Expand Down
107 changes: 94 additions & 13 deletions app/common/IPC_URL/csharp_ipc_handler.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import sys
import asyncio
import threading
from typing import Optional
from typing import Optional, Any, TypedDict
from loguru import logger

from app.tools.path_utils import get_data_path

CSHARP_AVAILABLE = False


class SelectedStudentNotificationInfo(TypedDict):
student_id: int
student_name: str
display_text: str
exists: bool
group_name: str
lottery_name: str


if sys.platform == "win32":
try:
sys.path.append(str(get_data_path("dlls")))
Expand All @@ -31,7 +41,12 @@
GeneratedIpcFactory,
)
from SecRandom4Ci.Interface.Enums import ResultType
from SecRandom4Ci.Interface.Models import CallResult, NotificationData, Student
from SecRandom4Ci.Interface.Models import (
CallResult,
Student,
NotificationData,
NotificationItem,
)
from SecRandom4Ci.Interface.Services import ISecRandomService

CSHARP_AVAILABLE = True
Expand Down Expand Up @@ -117,7 +132,7 @@ def stop_ipc_client(self):
def send_notification(
self,
class_name,
selected_students,
selected_students: list[SelectedStudentNotificationInfo],
draw_count=1,
settings=None,
settings_group=None,
Expand All @@ -130,6 +145,13 @@ def send_notification(
if not self.is_connected:
return False

coerced_students: list[SelectedStudentNotificationInfo] = []
for student in selected_students or []:
item = self._coerce_student(student)
if item is None:
continue
coerced_students.append(item)

if settings:
display_duration = settings.get("notification_display_duration", 5)
else:
Expand All @@ -156,11 +178,11 @@ def send_notification(
result.ClassName = class_name
result.DrawCount = draw_count
result.DisplayDuration = display_duration
for student in selected_students:
for student in coerced_students:
cs_student = Student()
cs_student.StudentId = student[0]
cs_student.StudentName = student[1]
cs_student.Exists = student[2]
cs_student.StudentId = student["student_id"]
cs_student.StudentName = student["display_text"]
cs_student.Exists = student["exists"]
result.SelectedStudents.Add(cs_student)
randomService.NotifyResult(result)

Expand Down Expand Up @@ -193,12 +215,16 @@ def send_notification(
data.ClassName = class_name
data.DrawCount = draw_count
data.DisplayDuration = display_duration
for student in selected_students:
cs_student = Student()
cs_student.StudentId = student[0]
cs_student.StudentName = student[1]
cs_student.Exists = student[2]
data.Items.Add(cs_student)
for student in coerced_students:
item = NotificationItem()
item.StudentId = student["student_id"]
item.StudentName = student["student_name"]
item.Exists = student["exists"]
item.HasGroup = bool(student["group_name"])
item.GroupName = student["group_name"]
item.IsLottery = bool(student["lottery_name"])
item.LotteryName = student["lottery_name"]
data.Items.Add(item)

randomService.ShowNotification(data)
return True
Expand Down Expand Up @@ -452,6 +478,61 @@ def check_plugin_alive(self) -> bool:
return randomService.IsAlive() == "Yes"
except Exception:
return False

@staticmethod
def _safe_int(value: Any) -> int:
try:
if value is None:
return 0
return int(value)
except Exception:
return 0

def _coerce_student(self, value: Any) -> SelectedStudentNotificationInfo | None:
if isinstance(value, dict):
student_id = self._safe_int(value.get("student_id", value.get("id", 0)))
student_name = str(
value.get("student_name", value.get("name", "")) or ""
)
display_text = str(
value.get(
"display_text", value.get("display", value.get("text", ""))
)
or student_name
)
exists = bool(value.get("exists", value.get("exist", True)))
group_name = str(value.get("group_name", value.get("group", "")) or "")
lottery_name = str(
value.get(
"lottery_name",
value.get(
"lottery",
value.get("prize_name", value.get("prize", "")),
),
)
or ""
)
return {
"student_id": student_id,
"student_name": student_name,
"display_text": display_text,
"exists": exists,
"group_name": group_name,
"lottery_name": lottery_name,
}

if isinstance(value, (list, tuple)) and len(value) >= 3:
student_name = str(value[1] or "")
return {
"student_id": self._safe_int(value[0]),
"student_name": student_name,
"display_text": student_name,
"exists": bool(value[2]),
"group_name": "",
"lottery_name": "",
}

return None
else:

class CSharpIPCHandler:
Expand Down
118 changes: 114 additions & 4 deletions app/common/lottery/lottery_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,75 +363,88 @@
self.current_group_filter,
)

def get_random_items(self, count):
"""
获取随机项(用于动画帧)
"""
if not self.prizes:
return []

# 简单的随机选择用于动画效果
try:
# 允许重复用于动画效果
selected_prizes = [system_random.choice(self.prizes) for _ in range(count)]

if not self.enable_student_assignment or not self.current_class_name:
return selected_prizes
prizes_with_meta = []
for prize in selected_prizes:
prize_copy = dict(prize)
prize_name = prize_copy.get("name", "")
prize_copy["ipc_lottery_name"] = str(prize_name or "")
prize_copy["ipc_group_name"] = ""
prize_copy["ipc_student_name"] = ""
prize_copy["ipc_display_text"] = str(prize_name or "")
prizes_with_meta.append(prize_copy)
return prizes_with_meta

candidates = RollCallUtils._get_filtered_candidates(
self.current_class_name,
self.current_group_index,
self.current_group_filter,
self.current_gender_index,
self.current_gender_filter,
)
if not candidates:
return selected_prizes

show_random = readme_settings_async("lottery_settings", "show_random")
try:
show_random = int(show_random or 0)
except Exception:
show_random = 0

from app.common.data.list import get_group_members

prizes_with_students = []
for prize in selected_prizes:
prize_copy = dict(prize)
prize_name = prize_copy.get("name", "")
prize_copy["ipc_lottery_name"] = str(prize_name or "")

group_name = ""
student_name = ""
if self.current_group_index == 1:
raw_group = system_random.choice(candidates).get("name", "")
include_group = show_random in (0, 1, 2, 5, 6, 7, 8, 9)
include_name = show_random in (0, 1, 2, 3, 4, 7, 8, 9, 10, 11)

group_name = raw_group if include_group else ""

if include_name and raw_group:
group_members = get_group_members(
self.current_class_name, raw_group
)
if group_members:
selected_member = system_random.choice(group_members)
student_name = (selected_member or {}).get("name", "")
if not student_name:
student_name = raw_group
else:
student_name = system_random.choice(candidates).get("name", "")

prize_copy["ipc_group_name"] = str(group_name or "")
prize_copy["ipc_student_name"] = str(student_name or "")
if group_name or student_name:
prize_copy["name"] = self._format_prize_student_text(
prize_name, group_name, student_name, show_random
)
prize_copy["ipc_display_text"] = str(prize_copy.get("name", "") or "")

prizes_with_students.append(prize_copy)

return prizes_with_students
except Exception:
return []

Check notice on line 447 in app/common/lottery/lottery_manager.py

View check run for this annotation

codefactor.io / CodeFactor

app/common/lottery/lottery_manager.py#L366-L447

Complex Method

def draw_final_items(self, count):
"""
Expand Down Expand Up @@ -502,6 +515,7 @@
except Exception:
show_random = 0

include_group = show_random in (0, 1, 2, 5, 6, 7, 8, 9)
from app.common.data.list import get_group_members

for idx, prize in enumerate(selected_prizes_dict):
Expand All @@ -522,6 +536,9 @@
)

display_name = prize_name
ipc_group_name = ""
ipc_student_name = ""
ipc_display_text = str(prize_name or "")
if student_tuple and len(student_tuple) >= 2 and student_tuple[1]:
group_name = ""
student_name = ""
Expand All @@ -545,18 +562,29 @@
else:
student_name = str(student_tuple[1])

ipc_group_name = str(group_name or "")
ipc_student_name = str(student_name or "")
display_name = self._format_prize_student_text(
prize_name, group_name, student_name, show_random
)
ipc_display_text = str(display_name or "")

selected_prizes_with_students.append((prize_id, display_name, prize_exist))

prize_copy = dict(prize)
prize_copy["ipc_lottery_name"] = str(prize_name or "")
prize_copy["ipc_group_name"] = ipc_group_name if include_group else ""
prize_copy["ipc_student_name"] = ipc_student_name
prize_copy["ipc_display_text"] = ipc_display_text
if isinstance(student_dict, dict):
prize_copy["student"] = student_dict
prize_copy["student_id"] = student_dict.get("id", "")
prize_copy["student_name"] = student_dict.get("name", "")
prize_copy["student_exist"] = student_dict.get("exist", True)
if include_group and not prize_copy["ipc_group_name"]:
prize_copy["ipc_group_name"] = str(
student_dict.get("group", "") or ""
)
updated_prizes_dict.append(prize_copy)

result["selected_prizes"] = selected_prizes_with_students
Expand Down Expand Up @@ -852,100 +880,154 @@
widget._do_reset_count()


def stop_animation(widget):
if hasattr(widget, "animation_timer") and widget.animation_timer.isActive():
widget.animation_timer.stop()
widget.start_button.setText(
get_content_pushbutton_name_async("lottery", "start_button")
)
widget.start_button.setEnabled(True)
widget.is_animating = False
try:
widget.start_button.clicked.disconnect()
except Exception as e:
logger.exception(
"Error disconnecting start_button clicked during stop_animation (ignored): {}",
e,
)
widget.start_button.clicked.connect(lambda: widget.start_draw())

music_player.stop_music(fade_out=True)

result = widget.manager.finalize_draw(
widget.current_count,
group_filter=widget.range_combobox.currentText(),
gender_filter=widget.gender_combobox.currentText(),
parent=widget,
)

if isinstance(result, dict) and result.get("reset_required"):
update_many_count_label(widget)
if (
hasattr(widget, "remaining_list_page")
and widget.remaining_list_page is not None
and hasattr(widget.remaining_list_page, "count_changed")
):
widget.remaining_list_page.count_changed.emit(widget.remaining_count)
QTimer.singleShot(APP_INIT_DELAY, widget._update_remaining_list_delayed)
return

widget.final_selected_students = result.get("selected_prizes") or result.get(
"selected_students"
)
widget.final_pool_name = result["pool_name"]
widget.final_selected_students_dict = result.get(
"selected_prizes_dict"
) or result.get("selected_students_dict")

widget.final_group_filter = widget.range_combobox.currentText()
widget.final_gender_filter = widget.gender_combobox.currentText()

plan = widget._draw_plan
result_music = plan.result_music if plan else None
if result_music:
music_player.play_music(
music_file=result_music,
settings_group="lottery_settings",
loop=False,
fade_in=True,
)

if result.get("save_temp"):
update_many_count_label(widget)

if (
hasattr(widget, "remaining_list_page")
and widget.remaining_list_page is not None
and hasattr(widget.remaining_list_page, "count_changed")
):
widget.remaining_list_page.count_changed.emit(widget.remaining_count)

QTimer.singleShot(APP_INIT_DELAY, widget._update_remaining_list_delayed)

if widget.final_selected_students is not None:
actual_draw_count = (
len(widget.final_selected_students) if widget.final_selected_students else 0
)
if actual_draw_count <= 0:
actual_draw_count = widget.current_count
display_result(
widget,
widget.final_selected_students,
widget.final_pool_name,
draw_count=actual_draw_count,
)

settings = widget.manager.get_notification_settings(refresh=True)
if settings is not None:
settings_for_notify = (
dict(settings) if isinstance(settings, dict) else settings
)
show_random = readme_settings_async("lottery_settings", "show_random")
try:
show_random = int(show_random or 0)
except Exception:
show_random = 0
include_group = show_random in (0, 1, 2, 5, 6, 7, 8, 9)
ipc_selected_students = []
for prize in widget.final_selected_students_dict or []:
if not isinstance(prize, dict):
continue
student = prize.get("student")
if not isinstance(student, dict):
student = None
try:
student_id = int(prize.get("student_id", 0) or 0)
except Exception:
student_id = 0
if not student_id and student is not None:
try:
student_id = int(student.get("id", 0) or 0)
except Exception:
student_id = 0
student_name = str(
prize.get("ipc_student_name", "")
or prize.get("student_name", "")
or ""
)
display_text = str(
prize.get("ipc_display_text", "") or prize.get("name", "") or ""
)
group_name = str(prize.get("ipc_group_name", "") or "")
if not include_group:
group_name = ""
lottery_name = str(
prize.get("ipc_lottery_name", "") or prize.get("name", "") or ""
)
exists = bool(prize.get("student_exist", prize.get("exist", True)))
ipc_selected_students.append(
{
"student_id": student_id,
"student_name": student_name,
"display_text": display_text,
"exists": exists,
"group_name": group_name,
"lottery_name": lottery_name,
}
)

if ipc_selected_students and isinstance(settings_for_notify, dict):
settings_for_notify["ipc_selected_students"] = ipc_selected_students

ResultDisplayUtils.show_notification_if_enabled(
widget.final_pool_name,
widget.final_selected_students,
actual_draw_count,
settings,
settings_for_notify,
settings_group="lottery_notification_settings",
)

play_voice_result(widget)

Check notice on line 1030 in app/common/lottery/lottery_manager.py

View check run for this annotation

codefactor.io / CodeFactor

app/common/lottery/lottery_manager.py#L883-L1030

Complex Method


def play_voice_result(widget):
Expand Down Expand Up @@ -991,13 +1073,32 @@
display_count = min(display_count, remaining_count)

prizes = widget.manager.get_random_items(display_count)
ipc_selected_students = []
for p in prizes or []:
if not isinstance(p, dict):
continue
ipc_selected_students.append(
{
"student_id": 0,
"student_name": str(p.get("ipc_student_name", "") or ""),
"display_text": str(
p.get("ipc_display_text", p.get("name", "")) or ""
),
"exists": bool(p.get("exist", True)),
"group_name": str(p.get("ipc_group_name", "") or ""),
"lottery_name": str(
p.get("ipc_lottery_name", p.get("name", "")) or ""
),
}
)
selected_prizes = [(p["id"], p["name"], p.get("exist", True)) for p in prizes]

display_result_animated(
widget,
selected_prizes,
widget.manager.current_pool_name,
draw_count=display_count,
ipc_selected_students=ipc_selected_students,
)


Expand Down Expand Up @@ -1034,7 +1135,9 @@
ResultDisplayUtils.display_results_in_grid(widget.result_grid, student_labels)


def display_result_animated(widget, selected_students, pool_name, draw_count=None):
def display_result_animated(
widget, selected_students, pool_name, draw_count=None, ipc_selected_students=None
):
render_settings = widget.manager.get_render_settings(refresh=False)
if draw_count is None:
draw_count = widget.current_count
Expand Down Expand Up @@ -1069,11 +1172,18 @@

settings = widget.manager.get_notification_settings(refresh=False)
if settings is not None:
settings_for_notify = dict(settings) if isinstance(settings, dict) else settings
if (
ipc_selected_students
and isinstance(settings_for_notify, dict)
and isinstance(ipc_selected_students, list)
):
settings_for_notify["ipc_selected_students"] = ipc_selected_students
ResultDisplayUtils.show_notification_if_enabled(
pool_name,
selected_students,
draw_count,
settings,
settings_for_notify,
settings_group="lottery_notification_settings",
is_animating=True,
)
Expand Down
Loading
Loading