refactor
parent
154490d395
commit
e27c7659e0
517
main.py
517
main.py
|
|
@ -6,18 +6,39 @@ from solve import solve_program
|
|||
from datetime import datetime
|
||||
import traceback
|
||||
|
||||
# 约束条件配置
|
||||
CONSTRAINTS_CONFIG = [
|
||||
("每班次人数:", "4", "8"),
|
||||
("每班次电脑或电器的老人数:", "", "2"),
|
||||
("每班次老人数:", "1", ""),
|
||||
("每班次人资小朋友数:", "1", ""),
|
||||
("每班次小朋友数:", "2", "")
|
||||
]
|
||||
|
||||
# 参数调优顺序和尝试值
|
||||
PARAM_CONFIGS = [
|
||||
('num_tech_min', [1, 0]),
|
||||
('num_tech_max', [1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
('num_old_min', [2, 1, 0]),
|
||||
('num_old_max', [1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
('num_min', [4, 3, 2, 1]),
|
||||
('num_max', [8, 9, 10, 11, 12, 13, 14, 15]),
|
||||
('num_new_min', [1, 0]),
|
||||
('num_hr_min', [1, 0]),
|
||||
('num_hr_max', [1, 2, 3, 4, 5, 6, 7, 8])
|
||||
]
|
||||
|
||||
class MyWidget(QtWidgets.QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.main_layout = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(self.main_layout)
|
||||
self.main_layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
bold_font = QFont()
|
||||
bold_font.setBold(True) # 设置字体加粗
|
||||
bold_font.setBold(True)
|
||||
|
||||
thin_font = QFont()
|
||||
thin_font.setBold(False) # 设置字体不加粗
|
||||
thin_font.setBold(False)
|
||||
|
||||
# 选择文件部分
|
||||
self._excel_dir = None
|
||||
|
|
@ -55,151 +76,18 @@ class MyWidget(QtWidgets.QWidget):
|
|||
self.cond_layout_0.addWidget(self.auto_switch)
|
||||
self.cond_layout_overall.addLayout(self.cond_layout_0)
|
||||
|
||||
# 限制条件1
|
||||
self.cond_layout_1 = QtWidgets.QHBoxLayout()
|
||||
# 文字1
|
||||
self.label_cond_1_1 = QtWidgets.QLabel("每班次人数:", self)
|
||||
self.label_cond_1_1.setFont(thin_font)
|
||||
# 数字输入框1
|
||||
self.line_edit_1_1 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_1_1.setFont(thin_font)
|
||||
self.line_edit_1_1.setValidator(QtGui.QIntValidator()) # 设置只接受整数
|
||||
self.line_edit_1_1.setPlaceholderText("无限制")
|
||||
self.line_edit_1_1.setText("4")
|
||||
self.line_edit_1_1.setEnabled(False)
|
||||
# 文字2
|
||||
self.label_cond_1_2 = QtWidgets.QLabel("到", self)
|
||||
self.label_cond_1_2.setFont(thin_font)
|
||||
# 数字输入框2
|
||||
self.line_edit_1_2 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_1_2.setFont(thin_font)
|
||||
self.line_edit_1_2.setValidator(QtGui.QIntValidator()) # 设置只接受整数
|
||||
self.line_edit_1_2.setPlaceholderText("无限制")
|
||||
self.line_edit_1_2.setText("8")
|
||||
self.line_edit_1_2.setEnabled(False)
|
||||
|
||||
self.cond_layout_1.addWidget(self.label_cond_1_1)
|
||||
self.cond_layout_1.addWidget(self.line_edit_1_1)
|
||||
self.cond_layout_1.addWidget(self.label_cond_1_2)
|
||||
self.cond_layout_1.addWidget(self.line_edit_1_2)
|
||||
self.cond_layout_overall.addLayout(self.cond_layout_1)
|
||||
|
||||
# 限制条件2
|
||||
self.cond_layout_2 = QtWidgets.QHBoxLayout()
|
||||
# 文字1
|
||||
self.label_cond_2_1 = QtWidgets.QLabel("每班次电脑或电器的老人数:", self)
|
||||
self.label_cond_2_1.setFont(thin_font)
|
||||
# 数字输入框1
|
||||
self.line_edit_2_1 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_2_1.setFont(thin_font)
|
||||
self.line_edit_2_1.setValidator(QtGui.QIntValidator()) # 设置只接受整数
|
||||
self.line_edit_2_1.setPlaceholderText("无限制")
|
||||
self.line_edit_2_1.setEnabled(False)
|
||||
# 文字2
|
||||
self.label_cond_2_2 = QtWidgets.QLabel("到", self)
|
||||
self.label_cond_2_2.setFont(thin_font)
|
||||
# 数字输入框2
|
||||
self.line_edit_2_2 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_2_2.setFont(thin_font)
|
||||
self.line_edit_2_2.setValidator(QtGui.QIntValidator()) # 设置只接受整数
|
||||
self.line_edit_2_2.setPlaceholderText("无限制")
|
||||
self.line_edit_2_2.setText("2")
|
||||
self.line_edit_2_2.setEnabled(False)
|
||||
|
||||
self.cond_layout_2.addWidget(self.label_cond_2_1)
|
||||
self.cond_layout_2.addWidget(self.line_edit_2_1)
|
||||
self.cond_layout_2.addWidget(self.label_cond_2_2)
|
||||
self.cond_layout_2.addWidget(self.line_edit_2_2)
|
||||
self.cond_layout_overall.addLayout(self.cond_layout_2)
|
||||
|
||||
# 限制条件3
|
||||
self.cond_layout_3 = QtWidgets.QHBoxLayout()
|
||||
# 文字1
|
||||
self.label_cond_3_1 = QtWidgets.QLabel("每班次老人数:", self)
|
||||
self.label_cond_3_1.setFont(thin_font)
|
||||
# 数字输入框1
|
||||
self.line_edit_3_1 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_3_1.setFont(thin_font)
|
||||
self.line_edit_3_1.setValidator(QtGui.QIntValidator())
|
||||
self.line_edit_3_1.setPlaceholderText("无限制")
|
||||
self.line_edit_3_1.setText("1")
|
||||
self.line_edit_3_1.setEnabled(False)
|
||||
# 文字2
|
||||
self.label_cond_3_2 = QtWidgets.QLabel("到", self)
|
||||
self.label_cond_3_2.setFont(thin_font)
|
||||
# 数字输入框2
|
||||
self.line_edit_3_2 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_3_2.setFont(thin_font)
|
||||
self.line_edit_3_2.setValidator(QtGui.QIntValidator())
|
||||
self.line_edit_3_2.setPlaceholderText("无限制")
|
||||
self.line_edit_3_2.setEnabled(False)
|
||||
|
||||
self.cond_layout_3.addWidget(self.label_cond_3_1)
|
||||
self.cond_layout_3.addWidget(self.line_edit_3_1)
|
||||
self.cond_layout_3.addWidget(self.label_cond_3_2)
|
||||
self.cond_layout_3.addWidget(self.line_edit_3_2)
|
||||
self.cond_layout_overall.addLayout(self.cond_layout_3)
|
||||
|
||||
# 限制条件4
|
||||
self.cond_layout_4 = QtWidgets.QHBoxLayout()
|
||||
# 文字1
|
||||
self.label_cond_4_1 = QtWidgets.QLabel("每班次人资小朋友数:", self)
|
||||
self.label_cond_4_1.setFont(thin_font)
|
||||
# 数字输入框1
|
||||
self.line_edit_4_1 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_4_1.setFont(thin_font)
|
||||
self.line_edit_4_1.setValidator(QtGui.QIntValidator())
|
||||
self.line_edit_4_1.setPlaceholderText("无限制")
|
||||
self.line_edit_4_1.setText("1")
|
||||
self.line_edit_4_1.setEnabled(False)
|
||||
# 文字2
|
||||
self.label_cond_4_2 = QtWidgets.QLabel("到", self)
|
||||
self.label_cond_4_2.setFont(thin_font)
|
||||
# 数字输入框2
|
||||
self.line_edit_4_2 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_4_2.setFont(thin_font)
|
||||
self.line_edit_4_2.setValidator(QtGui.QIntValidator())
|
||||
self.line_edit_4_2.setPlaceholderText("无限制")
|
||||
self.line_edit_4_2.setEnabled(False)
|
||||
|
||||
self.cond_layout_4.addWidget(self.label_cond_4_1)
|
||||
self.cond_layout_4.addWidget(self.line_edit_4_1)
|
||||
self.cond_layout_4.addWidget(self.label_cond_4_2)
|
||||
self.cond_layout_4.addWidget(self.line_edit_4_2)
|
||||
self.cond_layout_overall.addLayout(self.cond_layout_4)
|
||||
|
||||
# 限制条件5
|
||||
self.cond_layout_5 = QtWidgets.QHBoxLayout()
|
||||
# 文字1
|
||||
self.label_cond_5_1 = QtWidgets.QLabel("每班次小朋友数:", self)
|
||||
self.label_cond_5_1.setFont(thin_font)
|
||||
# 数字输入框1
|
||||
self.line_edit_5_1 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_5_1.setFont(thin_font)
|
||||
self.line_edit_5_1.setValidator(QtGui.QIntValidator())
|
||||
self.line_edit_5_1.setPlaceholderText("无限制")
|
||||
self.line_edit_5_1.setText("2")
|
||||
self.line_edit_5_1.setEnabled(False)
|
||||
# 文字2
|
||||
self.label_cond_5_2 = QtWidgets.QLabel("到", self)
|
||||
self.label_cond_5_2.setFont(thin_font)
|
||||
# 数字输入框2
|
||||
self.line_edit_5_2 = QtWidgets.QLineEdit(self)
|
||||
self.line_edit_5_2.setFont(thin_font)
|
||||
self.line_edit_5_2.setValidator(QtGui.QIntValidator())
|
||||
self.line_edit_5_2.setPlaceholderText("无限制")
|
||||
self.line_edit_5_2.setEnabled(False)
|
||||
|
||||
self.cond_layout_5.addWidget(self.label_cond_5_1)
|
||||
self.cond_layout_5.addWidget(self.line_edit_5_1)
|
||||
self.cond_layout_5.addWidget(self.label_cond_5_2)
|
||||
self.cond_layout_5.addWidget(self.line_edit_5_2)
|
||||
self.cond_layout_overall.addLayout(self.cond_layout_5)
|
||||
# 创建限制条件输入框
|
||||
self.constraint_widgets = {}
|
||||
|
||||
for i, (label_text, min_value, max_value) in enumerate(CONSTRAINTS_CONFIG, 1):
|
||||
layout, min_edit, max_edit = self._create_constraint_row(label_text, min_value, max_value, thin_font)
|
||||
self.constraint_widgets[f'layout_{i}'] = layout
|
||||
self.constraint_widgets[f'min_{i}'] = min_edit
|
||||
self.constraint_widgets[f'max_{i}'] = max_edit
|
||||
self.cond_layout_overall.addLayout(layout)
|
||||
|
||||
self.main_layout.addWidget(self.group_box_2)
|
||||
|
||||
|
||||
# 处理文件部分
|
||||
self.group_box_3 = QtWidgets.QGroupBox("Step 3. 获取最优结果")
|
||||
self.group_box_3.setFont(bold_font)
|
||||
|
|
@ -223,41 +111,50 @@ class MyWidget(QtWidgets.QWidget):
|
|||
self.group_box_3.setLayout(self.solve_layout)
|
||||
self.main_layout.addWidget(self.group_box_3)
|
||||
|
||||
def open_file(self):
|
||||
# 弹出文件选择对话框
|
||||
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开文件", "", "所有文件 (*.*);;文本文件 (*.txt)")
|
||||
def _create_constraint_row(self, label_text, min_value, max_value, font):
|
||||
"""创建一行限制条件输入框"""
|
||||
layout = QtWidgets.QHBoxLayout()
|
||||
|
||||
# 如果用户选择了文件,则更新标签显示文件路径
|
||||
label = QtWidgets.QLabel(label_text, self)
|
||||
label.setFont(font)
|
||||
|
||||
# 创建输入框的辅助函数
|
||||
def create_input(value):
|
||||
edit = QtWidgets.QLineEdit(self)
|
||||
edit.setFont(font)
|
||||
edit.setValidator(QtGui.QIntValidator())
|
||||
edit.setPlaceholderText("无限制")
|
||||
if value:
|
||||
edit.setText(value)
|
||||
edit.setEnabled(False)
|
||||
return edit
|
||||
|
||||
min_edit = create_input(min_value)
|
||||
to_label = QtWidgets.QLabel("到", self)
|
||||
to_label.setFont(font)
|
||||
max_edit = create_input(max_value)
|
||||
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(min_edit)
|
||||
layout.addWidget(to_label)
|
||||
layout.addWidget(max_edit)
|
||||
|
||||
return layout, min_edit, max_edit
|
||||
|
||||
def open_file(self):
|
||||
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开文件", "", "所有文件 (*.*);;文本文件 (*.txt)")
|
||||
if file_path:
|
||||
self.label_openfile.setText(f"选中文件: {file_path}")
|
||||
self._excel_dir = file_path
|
||||
|
||||
def on_switch_toggled(self, state):
|
||||
"""处理开关状态的变化事件"""
|
||||
if state == 0: # 关闭
|
||||
self._auto_mode = False
|
||||
self.line_edit_1_1.setEnabled(True)
|
||||
self.line_edit_1_2.setEnabled(True)
|
||||
self.line_edit_2_1.setEnabled(True)
|
||||
self.line_edit_2_2.setEnabled(True)
|
||||
self.line_edit_3_1.setEnabled(True)
|
||||
self.line_edit_3_2.setEnabled(True)
|
||||
self.line_edit_4_1.setEnabled(True)
|
||||
self.line_edit_4_2.setEnabled(True)
|
||||
self.line_edit_5_1.setEnabled(True)
|
||||
self.line_edit_5_2.setEnabled(True)
|
||||
elif state == 2: # 打开
|
||||
self._auto_mode = True
|
||||
self.line_edit_1_1.setEnabled(False)
|
||||
self.line_edit_1_2.setEnabled(False)
|
||||
self.line_edit_2_1.setEnabled(False)
|
||||
self.line_edit_2_2.setEnabled(False)
|
||||
self.line_edit_3_1.setEnabled(False)
|
||||
self.line_edit_3_2.setEnabled(False)
|
||||
self.line_edit_4_1.setEnabled(False)
|
||||
self.line_edit_4_2.setEnabled(False)
|
||||
self.line_edit_5_1.setEnabled(False)
|
||||
self.line_edit_5_2.setEnabled(False)
|
||||
enabled = state == 0
|
||||
self._auto_mode = not enabled
|
||||
|
||||
for i in range(1, 6):
|
||||
self.constraint_widgets[f'min_{i}'].setEnabled(enabled)
|
||||
self.constraint_widgets[f'max_{i}'].setEnabled(enabled)
|
||||
|
||||
|
||||
@QtCore.Slot()
|
||||
|
|
@ -275,28 +172,21 @@ class MyWidget(QtWidgets.QWidget):
|
|||
all_data, index_to_name_dict, preference_mat, want_num_array, is_new_array, is_tech_array, is_hr_array = read_excel(self._excel_dir)
|
||||
self.text_solve.append("读取文件成功!")
|
||||
|
||||
# 计算并打印一些统计信息
|
||||
self.text_solve.append("信息统计:")
|
||||
# 计算并打印统计信息
|
||||
stu_num = len(index_to_name_dict)
|
||||
self.text_solve.append(f"\t学生总人数:{stu_num}")
|
||||
|
||||
class_num = len(preference_mat[0])
|
||||
self.text_solve.append(f"\t班次总数:{class_num}")
|
||||
|
||||
tech_sum = 0
|
||||
for i in range(len(preference_mat)):
|
||||
if not is_new_array[i] and is_tech_array[i]:
|
||||
tech_sum += want_num_array[i]
|
||||
self.text_solve.append(f"\t电脑部或电气部的所有老人的意愿班次之和:{tech_sum}")
|
||||
|
||||
tech_sum = sum(want_num_array[i] for i in range(len(preference_mat))
|
||||
if not is_new_array[i] and is_tech_array[i])
|
||||
all_sum = sum(want_num_array)
|
||||
min_prefer = min(sum(preference_mat[i][j] for i in range(len(preference_mat)))
|
||||
for j in range(class_num))
|
||||
|
||||
self.text_solve.append("信息统计:")
|
||||
self.text_solve.append(f"\t学生总人数:{stu_num}")
|
||||
self.text_solve.append(f"\t班次总数:{class_num}")
|
||||
self.text_solve.append(f"\t电脑部或电气部的所有老人的意愿班次之和:{tech_sum}")
|
||||
self.text_solve.append(f"\t所有人的意愿班次之和:{all_sum}")
|
||||
self.text_solve.append(f"\t平均每班人数:{all_sum/20}")
|
||||
|
||||
min_prefer = 100
|
||||
for j in range(len(preference_mat[0])):
|
||||
prefer_num = sum([preference_mat[i][j] for i in range(len(preference_mat))])
|
||||
min_prefer = min(min_prefer, prefer_num)
|
||||
self.text_solve.append(f"\t所有班次中最少拥有意愿数:{min_prefer}")
|
||||
|
||||
vars = []
|
||||
|
|
@ -304,201 +194,78 @@ class MyWidget(QtWidgets.QWidget):
|
|||
if self._auto_mode:
|
||||
self.text_solve.append(f"自动调参开始...")
|
||||
|
||||
# 至少?个电脑或电器的老人
|
||||
tech_old_min_try_list = [1,0]
|
||||
tech_old_min_num = None
|
||||
for _tech_min in tech_old_min_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=None, num_max=None,
|
||||
num_tech_min=_tech_min, num_tech_max=None,
|
||||
num_old_min=None, num_old_max=None,
|
||||
num_hr_min=None, num_hr_max=None,
|
||||
num_new_min=None, num_new_max=None)
|
||||
if vars:
|
||||
tech_old_min_num = _tech_min
|
||||
break
|
||||
|
||||
# 至多?个电脑或电器的老人
|
||||
tech_old_max_try_list = [1,2,3,4,5,6,7,8]
|
||||
tech_old_max_num = None
|
||||
for _tech_max in tech_old_max_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=None, num_max=None,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=_tech_max,
|
||||
num_old_min=None, num_old_max=None,
|
||||
num_hr_min=None, num_hr_max=None,
|
||||
num_new_min=None, num_new_max=None)
|
||||
if vars:
|
||||
tech_old_max_num = _tech_max
|
||||
break
|
||||
|
||||
# 每班至少?个老人
|
||||
num_old_min_try_list = [2,1,0]
|
||||
num_old_min = None
|
||||
for _num_old_min in num_old_min_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=None, num_max=None,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num,
|
||||
num_old_min=_num_old_min, num_old_max=None,
|
||||
num_hr_min=None, num_hr_max=None,
|
||||
num_new_min=None, num_new_max=None)
|
||||
if vars:
|
||||
num_old_min = _num_old_min
|
||||
break
|
||||
|
||||
# 每班至多?个老人
|
||||
num_old_max_try_list = [1,2,3,4,5,6,7,8]
|
||||
num_old_max = None
|
||||
for _num_old_max in num_old_max_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=None, num_max=None,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num,
|
||||
num_old_min=num_old_min, num_old_max=_num_old_max,
|
||||
num_hr_min=None, num_hr_max=None,
|
||||
num_new_min=None, num_new_max=None)
|
||||
if vars:
|
||||
num_old_max = _num_old_max
|
||||
break
|
||||
|
||||
# 每班至少?人
|
||||
num_min_try_list = [4,3,2,1]
|
||||
num_min = None
|
||||
for _num_min in num_min_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=_num_min, num_max=None,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num,
|
||||
num_old_min=num_old_min, num_old_max=num_old_max,
|
||||
num_hr_min=None, num_hr_max=None,
|
||||
num_new_min=None, num_new_max=None)
|
||||
if vars:
|
||||
num_min = _num_min
|
||||
break
|
||||
|
||||
# 每班至多?人
|
||||
num_max_try_list = [8,9,10,11,12,13,14,15]
|
||||
num_max = None
|
||||
for _num_max in num_max_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=num_min, num_max=_num_max,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num,
|
||||
num_old_min=num_old_min, num_old_max=num_old_max,
|
||||
num_hr_min=None, num_hr_max=None,
|
||||
num_new_min=None, num_new_max=None)
|
||||
if vars:
|
||||
num_max = _num_max
|
||||
break
|
||||
|
||||
# 每班至少?个小朋友
|
||||
num_new_min_try_list = [1,0]
|
||||
num_new_min = None
|
||||
for _num_new_min in num_new_min_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=num_min, num_max=num_max,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num,
|
||||
num_old_min=num_old_min, num_old_max=num_old_max,
|
||||
num_hr_min=None, num_hr_max=None,
|
||||
num_new_min=_num_new_min, num_new_max=None)
|
||||
if vars:
|
||||
num_new_min = _num_new_min
|
||||
break
|
||||
|
||||
# 每班至少?个人资小朋友
|
||||
num_hr_min_try_list = [1,0]
|
||||
num_hr_min = None
|
||||
for _num_hr_min in num_hr_min_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=num_min, num_max=num_max,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num,
|
||||
num_old_min=num_old_min, num_old_max=num_old_max,
|
||||
num_hr_min=_num_hr_min, num_hr_max=None,
|
||||
num_new_min=num_new_min, num_new_max=None)
|
||||
if vars:
|
||||
num_hr_min = _num_hr_min
|
||||
break
|
||||
# 自动调参配置
|
||||
auto_params: dict[str, int | None] = {
|
||||
'num_min': None, 'num_max': None,
|
||||
'num_tech_min': None, 'num_tech_max': None,
|
||||
'num_old_min': None, 'num_old_max': None,
|
||||
'num_hr_min': None, 'num_hr_max': None,
|
||||
'num_new_min': None, 'num_new_max': None
|
||||
}
|
||||
|
||||
# 每班至多?个人资小朋友
|
||||
num_hr_max_try_list = [1,2,3,4,5,6,7,8]
|
||||
num_hr_max = None
|
||||
for _num_hr_max in num_hr_max_try_list:
|
||||
vars = solve_program(preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
num_min=num_min, num_max=num_max,
|
||||
num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num,
|
||||
num_old_min=num_old_min, num_old_max=num_old_max,
|
||||
num_hr_min=num_hr_min, num_hr_max=_num_hr_max,
|
||||
num_new_min=num_new_min, num_new_max=None)
|
||||
if vars:
|
||||
num_hr_max = _num_hr_max
|
||||
# 逐步优化参数
|
||||
for param_name, try_values in PARAM_CONFIGS:
|
||||
value = None
|
||||
vars = None
|
||||
for try_value in try_values:
|
||||
test_params = auto_params.copy()
|
||||
test_params[param_name] = try_value
|
||||
vars = solve_program(
|
||||
preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
**test_params
|
||||
)
|
||||
if vars:
|
||||
value = try_value
|
||||
break
|
||||
if value:
|
||||
auto_params[param_name] = value
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
if vars is None:
|
||||
self.text_solve.append("自动求解失败!请尝试手动调参!")
|
||||
return
|
||||
else:
|
||||
self.text_solve.append("自动计算成功!")
|
||||
self.text_solve.append("参数:")
|
||||
self.text_solve.append(f"\t每班人数:[{num_min}, {num_max}]")
|
||||
self.text_solve.append(f"\t每班电脑或电器的老人数:[{tech_old_min_num}, {tech_old_max_num}]")
|
||||
self.text_solve.append(f"\t每班老人数:[{num_old_min}, {num_old_max}]")
|
||||
self.text_solve.append(f"\t每班人资部小朋友数:[{num_hr_min}, {num_hr_max}]")
|
||||
self.text_solve.append(f"\t每班小朋友数:[{num_new_min}, inf]")
|
||||
self.text_solve.append(f"\t每班人数:[{auto_params['num_min']}, {auto_params['num_max']}]")
|
||||
self.text_solve.append(f"\t每班电脑或电器的老人数:[{auto_params['num_tech_min']}, {auto_params['num_tech_max']}]")
|
||||
self.text_solve.append(f"\t每班老人数:[{auto_params['num_old_min']}, {auto_params['num_old_max']}]")
|
||||
self.text_solve.append(f"\t每班人资部小朋友数:[{auto_params['num_hr_min']}, {auto_params['num_hr_max']}]")
|
||||
self.text_solve.append(f"\t每班小朋友数:[{auto_params['num_new_min']}, inf]")
|
||||
|
||||
|
||||
else:
|
||||
# 读取限制条件
|
||||
num_min = int(self.line_edit_1_1.text()) if self.line_edit_1_1.text() else None
|
||||
num_max = int(self.line_edit_1_2.text()) if self.line_edit_1_2.text() else None
|
||||
num_tech_min = int(self.line_edit_2_1.text()) if self.line_edit_2_1.text() else None
|
||||
num_tech_max = int(self.line_edit_2_2.text()) if self.line_edit_2_2.text() else None
|
||||
num_old_min = int(self.line_edit_3_1.text()) if self.line_edit_3_1.text() else None
|
||||
num_old_max = int(self.line_edit_3_2.text()) if self.line_edit_3_2.text() else None
|
||||
num_hr_min = int(self.line_edit_4_1.text()) if self.line_edit_4_1.text() else None
|
||||
num_hr_max = int(self.line_edit_4_2.text()) if self.line_edit_4_2.text() else None
|
||||
num_new_min = int(self.line_edit_5_1.text()) if self.line_edit_5_1.text() else None
|
||||
num_new_max = int(self.line_edit_5_2.text()) if self.line_edit_5_2.text() else None
|
||||
def get_value(key):
|
||||
text = self.constraint_widgets[key].text()
|
||||
return int(text) if text else None
|
||||
|
||||
manual_params = {
|
||||
'num_min': get_value('min_1'),
|
||||
'num_max': get_value('max_1'),
|
||||
'num_tech_min': get_value('min_2'),
|
||||
'num_tech_max': get_value('max_2'),
|
||||
'num_old_min': get_value('min_3'),
|
||||
'num_old_max': get_value('max_3'),
|
||||
'num_hr_min': get_value('min_4'),
|
||||
'num_hr_max': get_value('max_4'),
|
||||
'num_new_min': get_value('min_5'),
|
||||
'num_new_max': get_value('max_5')
|
||||
}
|
||||
|
||||
self.text_solve.append("计算最优解中...")
|
||||
vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array,
|
||||
num_min=num_min, num_max=num_max, num_tech_min=num_tech_min, num_tech_max=num_tech_max,
|
||||
num_old_min=num_old_min, num_old_max=num_old_max, num_hr_min=num_hr_min, num_hr_max=num_hr_max, num_new_min=num_new_min, num_new_max=num_new_max)
|
||||
vars = solve_program(
|
||||
preference_mat=preference_mat,
|
||||
want_num_array=want_num_array,
|
||||
is_new_array=is_new_array,
|
||||
is_tech_array=is_tech_array,
|
||||
is_hr_array=is_hr_array,
|
||||
**manual_params
|
||||
)
|
||||
|
||||
if vars is None:
|
||||
self.text_solve.append("在目前限制条件下无解!请尝试更改限制条件!")
|
||||
|
|
@ -508,12 +275,10 @@ class MyWidget(QtWidgets.QWidget):
|
|||
|
||||
# 保存结果到 excel
|
||||
self.text_solve.append("保存结果中...")
|
||||
time_str = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
save_dir = f"result_{time_str}.xlsx"
|
||||
if vars is not None:
|
||||
save_to_excel(vars, all_data, index_to_name_dict, save_dir)
|
||||
save_dir = f"result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
save_to_excel(vars, all_data, index_to_name_dict, save_dir)
|
||||
self.text_solve.append(f"保存结果成功!保存路径:{save_dir}")
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
self.text_solve.append("程序出现严重错误,请联系开发者解决问题!!!")
|
||||
self.text_solve.append(f"Error Details:\n{traceback.format_exc()}")
|
||||
|
||||
|
|
|
|||
133
solve.py
133
solve.py
|
|
@ -18,128 +18,87 @@ def solve_program(preference_mat:list,
|
|||
):
|
||||
|
||||
is_old_array = [not is_new for is_new in is_new_array]
|
||||
is_not_tech_array = [not is_tech for is_tech in is_tech_array]
|
||||
|
||||
# 这是一个组合优化问题,我们使用 SCIP 求解器来解决这个问题
|
||||
# 使用 SCIP 求解器求解组合优化问题
|
||||
solver = pywraplp.Solver.CreateSolver("SCIP")
|
||||
if not solver:
|
||||
return
|
||||
|
||||
# 定义变量的规模,一共有 N*M 个变量需要求解器求解
|
||||
N = len(preference_mat) # 学生人数
|
||||
M = len(preference_mat[0]) # 班次数
|
||||
avg_num = sum(want_num_array) / M # 每班次的平均人数
|
||||
avg_num = sum(want_num_array) / M
|
||||
print(f"平均人数:{avg_num}")
|
||||
|
||||
variables = []
|
||||
aux_vars = [] # 辅助变量
|
||||
infinity = solver.infinity()
|
||||
for i in range(N):
|
||||
row_vars = []
|
||||
for j in range(M):
|
||||
var = solver.IntVar(0.0, 1.0, f"choice_{i}_{j}")
|
||||
row_vars.append(var)
|
||||
variables.append(row_vars)
|
||||
for j in range(M):
|
||||
aux_var = solver.NumVar(0.0, infinity, f"aux_{j}")
|
||||
aux_vars.append(aux_var)
|
||||
# 创建决策变量:variables[i][j] 表示第i个学生是否选择第j个班次
|
||||
variables = [[solver.IntVar(0.0, 1.0, f"choice_{i}_{j}")
|
||||
for j in range(M)] for i in range(N)]
|
||||
|
||||
# 创建辅助变量用于优化目标(均衡班次人数)
|
||||
aux_vars = [solver.NumVar(0.0, solver.infinity(), f"aux_{j}")
|
||||
for j in range(M)]
|
||||
print("Number of variables =", solver.NumVariables())
|
||||
|
||||
# 添加约束:每个同学意愿一定要满足
|
||||
# 约束:每个同学意愿一定要满足
|
||||
for i in range(N):
|
||||
for j in range(M):
|
||||
solver.Add(variables[i][j] <= preference_mat[i][j])
|
||||
|
||||
# 辅助变量添加约束:
|
||||
# 约束:辅助变量用于计算与平均人数的偏差(绝对值)
|
||||
for j in range(M):
|
||||
actual_num = sum(variables[i][j] for i in range(N)) # 每班次的实际人数
|
||||
actual_num = sum(variables[i][j] for i in range(N))
|
||||
solver.Add(aux_vars[j] >= actual_num - avg_num)
|
||||
solver.Add(aux_vars[j] >= avg_num - actual_num)
|
||||
|
||||
# 添加约束:满足每位同学的意愿班次
|
||||
# 约束:满足每位同学的意愿班次数
|
||||
for i in range(N):
|
||||
total_shifts = sum(variables[i])
|
||||
if want_num_array[i] == 3:
|
||||
solver.Add(sum(variables[i]) <= 3)
|
||||
solver.Add(sum(variables[i]) >= 2)
|
||||
solver.Add(total_shifts >= 2)
|
||||
solver.Add(total_shifts <= 3)
|
||||
else:
|
||||
solver.Add(sum(variables[i]) == want_num_array[i])
|
||||
solver.Add(total_shifts == want_num_array[i])
|
||||
|
||||
# 添加约束:每个班次至少有?位同学
|
||||
if num_min is not None:
|
||||
# 添加班次人数约束的辅助函数
|
||||
def add_shift_constraint(array, min_val, max_val):
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j] for i in range(N)) >= num_min)
|
||||
shift_count = sum(variables[i][j] * array[i] for i in range(N))
|
||||
if min_val is not None:
|
||||
solver.Add(shift_count >= min_val)
|
||||
if max_val is not None:
|
||||
solver.Add(shift_count <= max_val)
|
||||
|
||||
# 添加约束:每个班次至多有?位同学
|
||||
if num_max is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j] for i in range(N)) <= num_max)
|
||||
# 约束:每个班次的总人数
|
||||
add_shift_constraint([1] * N, num_min, num_max)
|
||||
|
||||
# 约束:每班次电脑或电器的老人数量
|
||||
tech_old_array = [is_old_array[i] * is_tech_array[i] for i in range(N)]
|
||||
add_shift_constraint(tech_old_array, num_tech_min, num_tech_max)
|
||||
|
||||
# 约束:每班次老人数量
|
||||
add_shift_constraint(is_old_array, num_old_min, num_old_max)
|
||||
|
||||
# 约束:每班次人资部小朋友数量
|
||||
add_shift_constraint(is_hr_array, num_hr_min, num_hr_max)
|
||||
|
||||
# 约束:每班次小朋友数量
|
||||
add_shift_constraint(is_new_array, num_new_min, num_new_max)
|
||||
|
||||
# 添加约束:每班次最少包含?个电脑或电器的老人
|
||||
if num_tech_min is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_old_array[i]*is_tech_array[i] for i in range(N)) >= num_tech_min)
|
||||
# 优化目标:最小化各班次人数与平均人数的偏差总和
|
||||
solver.Minimize(sum(aux_vars))
|
||||
|
||||
# 添加约束:每班次最多包含?个电脑或电器的老人
|
||||
if num_tech_max is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_old_array[i]*is_tech_array[i] for i in range(N)) <= num_tech_max)
|
||||
|
||||
# 添加约束:每班次至少包含?个老人
|
||||
if num_old_min is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_old_array[i] for i in range(N)) >= num_old_min)
|
||||
|
||||
# 添加约束:每班次至多包含?个老人
|
||||
if num_old_max is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_old_array[i] for i in range(N)) <= num_old_max)
|
||||
|
||||
# 添加约束:每班次至少包含?个人资部小朋友
|
||||
if num_hr_min is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_hr_array[i] for i in range(N)) >= num_hr_min)
|
||||
|
||||
# 添加约束:每班次至多包含?个人资部小朋友
|
||||
if num_hr_max is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_hr_array[i] for i in range(N)) <= num_hr_max)
|
||||
|
||||
# 添加约束:每班次至少包含?个小朋友
|
||||
if num_new_min is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_new_array[i] for i in range(N)) >= num_new_min)
|
||||
|
||||
# 添加约束:每班次至多包含?个小朋友
|
||||
if num_new_max is not None:
|
||||
for j in range(M):
|
||||
solver.Add(sum(variables[i][j]*is_new_array[i] for i in range(N)) <= num_new_max)
|
||||
|
||||
# 优化目标:每班次人数尽可能平均
|
||||
solver.Minimize(sum(aux_vars)) # Maximize the sum of these variables.
|
||||
|
||||
# 求解优化问题
|
||||
# 求解
|
||||
status = solver.Solve()
|
||||
|
||||
# 输出结果
|
||||
variables_return = []
|
||||
aux_vars_return = []
|
||||
if status == pywraplp.Solver.OPTIMAL:
|
||||
print("Optimal solution found:")
|
||||
for i in range(N):
|
||||
row_solution = [variables[i][j].solution_value() for j in range(M)]
|
||||
variables_return.append(row_solution)
|
||||
|
||||
for j in range(M):
|
||||
aux_vars_return.append(aux_vars[j].solution_value())
|
||||
|
||||
# Print the optimized value of the objective function.
|
||||
variables_return = [[variables[i][j].solution_value() for j in range(M)]
|
||||
for i in range(N)]
|
||||
aux_vars_return = [aux_vars[j].solution_value() for j in range(M)]
|
||||
|
||||
print(f"Optimized objective value: {solver.Objective().Value()}")
|
||||
print(aux_vars_return)
|
||||
|
||||
return variables_return
|
||||
|
||||
else:
|
||||
print("No solution found.")
|
||||
|
||||
return None
|
||||
|
||||
130
utils.py
130
utils.py
|
|
@ -31,7 +31,6 @@ index_to_type = { # 老人 / 小朋友和对应的编号
|
|||
}
|
||||
|
||||
def read_excel(file_path):
|
||||
# 读取 Excel 文件
|
||||
df = pd.read_excel(file_path)
|
||||
|
||||
# 待返回的所有信息。N 为学生人数,M 为班次数
|
||||
|
|
@ -46,66 +45,68 @@ def read_excel(file_path):
|
|||
考虑到原始填表信息中可能有某位同学多次提交的记录,先过滤一下冗余信息
|
||||
'''
|
||||
all_data = {} # 储存过滤后的数据
|
||||
format = "%Y/%m/%d %H:%M:%S"
|
||||
time_format = "%Y/%m/%d %H:%M:%S"
|
||||
for index, row in df.iterrows():
|
||||
# 读取学生姓名,比较时间戳
|
||||
name = row[col_name]
|
||||
time_this = datetime.strptime(row[col_timestamp], format)
|
||||
if (name not in all_data) or (time_this > datetime.strptime(all_data[name][col_timestamp], format)):
|
||||
info_list = row.tolist()
|
||||
all_data[name] = info_list
|
||||
time_this = datetime.strptime(row[col_timestamp], time_format)
|
||||
if name not in all_data or time_this > datetime.strptime(all_data[name][col_timestamp], time_format):
|
||||
all_data[name] = row.tolist()
|
||||
|
||||
# 构建返回的数据结构
|
||||
index_to_name_dict = {}
|
||||
preference_mat = []
|
||||
want_num_array = []
|
||||
is_new_array = []
|
||||
is_tech_array = []
|
||||
is_hr_array = []
|
||||
|
||||
# 遍历过滤后的数据,组建待返回的信息
|
||||
for index, (name, info_list) in enumerate(all_data.items()):
|
||||
# 学生姓名
|
||||
index_to_name_dict[index] = name
|
||||
# 意愿度
|
||||
preference_mat.append(info_list[col_dutystart:col_dutyend + 1])
|
||||
# 想要值班的次数
|
||||
want_num_array.append(info_list[col_dutyfreq])
|
||||
# 是否是小朋友
|
||||
is_new_array.append(index_to_type[info_list[col_member]] == "小朋友")
|
||||
# 是否是电脑部或电器部成员
|
||||
is_tech_array.append(index_to_departments[info_list[col_department]] in ["电脑部", "电器部"])
|
||||
# 是否是人资部成员
|
||||
is_hr_array.append(index_to_departments[info_list[col_department]] == "人资部")
|
||||
|
||||
return all_data, index_to_name_dict, preference_mat, want_num_array, is_new_array, is_tech_array, is_hr_array
|
||||
|
||||
def save_to_excel(variables, all_data, index_to_name_dict, file_path):
|
||||
|
||||
# 用一个 list 储存每一班的值班人员,该 list 中每个元素是一个存有若干 dict 的 list,每个 dict 表示某一班的某一值班人员信息
|
||||
# 收集每个班次的值班人员信息
|
||||
all_result = []
|
||||
max_single_class_num = 0 # 用于记录最大的班次人数,以便后写入 excel
|
||||
max_single_class_num = 0
|
||||
|
||||
for j in range(len(variables[0])):
|
||||
on_duty_list = []
|
||||
single_class_num = 0
|
||||
hr_new_index = []
|
||||
none_tech_new_index = []
|
||||
new_index = []
|
||||
all_index = []
|
||||
_cnt = 0
|
||||
|
||||
for i in range(len(variables)):
|
||||
if variables[i][j] == 1:
|
||||
single_stu_info = {}
|
||||
single_stu_info["name"] = index_to_name_dict[i]
|
||||
single_stu_info["department"] = index_to_departments[all_data[index_to_name_dict[i]][col_department]]
|
||||
single_stu_info["type"] = index_to_type[all_data[index_to_name_dict[i]][col_member]]
|
||||
single_stu_info["duty_monitor"] = False
|
||||
name = index_to_name_dict[i]
|
||||
department = index_to_departments[all_data[name][col_department]]
|
||||
member_type = index_to_type[all_data[name][col_member]]
|
||||
|
||||
single_stu_info = {
|
||||
"name": name,
|
||||
"department": department,
|
||||
"type": member_type,
|
||||
"duty_monitor": False
|
||||
}
|
||||
on_duty_list.append(single_stu_info)
|
||||
single_class_num += 1
|
||||
|
||||
idx = len(all_index)
|
||||
all_index.append(idx)
|
||||
if member_type == "小朋友":
|
||||
new_index.append(idx)
|
||||
if department in ["人资部", "财外部", "文宣部"]:
|
||||
none_tech_new_index.append(idx)
|
||||
if department == "人资部":
|
||||
hr_new_index.append(idx)
|
||||
|
||||
all_index.append(_cnt)
|
||||
if single_stu_info["type"] == "小朋友":
|
||||
new_index.append(_cnt)
|
||||
if single_stu_info["department"] in ["人资部", "财外部", "文宣部"]:
|
||||
none_tech_new_index.append(_cnt)
|
||||
if single_stu_info["department"] == 3:
|
||||
hr_new_index.append(_cnt)
|
||||
_cnt += 1
|
||||
|
||||
# 为每一班的值班组长打上标记
|
||||
duty_monitor_index = -1
|
||||
# 选择值班组长(优先级:人资部小朋友 > 非技术部小朋友 > 小朋友 > 所有人)
|
||||
if hr_new_index:
|
||||
duty_monitor_index = random.choice(hr_new_index)
|
||||
elif none_tech_new_index:
|
||||
|
|
@ -116,56 +117,43 @@ def save_to_excel(variables, all_data, index_to_name_dict, file_path):
|
|||
duty_monitor_index = random.choice(all_index)
|
||||
on_duty_list[duty_monitor_index]["duty_monitor"] = True
|
||||
|
||||
on_duty_list = sorted(on_duty_list, key=lambda x: x["department"]) # 按部门编号升序排序
|
||||
on_duty_list.sort(key=lambda x: x["department"])
|
||||
all_result.append(on_duty_list)
|
||||
max_single_class_num = max(max_single_class_num, single_class_num)
|
||||
max_single_class_num = max(max_single_class_num, len(on_duty_list))
|
||||
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import PatternFill
|
||||
|
||||
# 创建一个新的工作簿
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
if ws is None:
|
||||
ws = wb.create_sheet(title="Sheet1")
|
||||
ws = wb.active if wb.active else wb.create_sheet(title="Sheet1")
|
||||
|
||||
# 写入表头
|
||||
width = 25
|
||||
ws['B1'] = "星期一"
|
||||
ws.column_dimensions['B'].width = width
|
||||
ws['C1'] = "星期二"
|
||||
ws.column_dimensions['C'].width = width
|
||||
ws['D1'] = "星期三"
|
||||
ws.column_dimensions['D'].width = width
|
||||
ws['E1'] = "星期四"
|
||||
ws.column_dimensions['E'].width = width
|
||||
ws['F1'] = "星期五"
|
||||
ws.column_dimensions['F'].width = width
|
||||
ws['G1'] = "星期六"
|
||||
ws.column_dimensions['G'].width = width
|
||||
ws['H1'] = "星期日"
|
||||
ws.column_dimensions['H'].width = width
|
||||
# 设置表头
|
||||
weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
|
||||
for idx, day in enumerate(weekdays, start=2):
|
||||
col = chr(ord('A') + idx - 1)
|
||||
ws[f'{col}1'] = day
|
||||
ws.column_dimensions[col].width = 25
|
||||
|
||||
# 设置班次标题和起始行
|
||||
shift_names = ["第一班", "第二班", "第三班"]
|
||||
start_index = []
|
||||
ws.merge_cells(f'A2:A{2 + max_single_class_num}')
|
||||
ws['A2'] = "第一班"
|
||||
start_index.append(2)
|
||||
ws.merge_cells(f'A{2 + max_single_class_num + 1}:A{2 + 2 * max_single_class_num + 1}')
|
||||
ws[f'A{2 + max_single_class_num + 1}'] = "第二班"
|
||||
start_index.append(2 + max_single_class_num + 1)
|
||||
ws.merge_cells(f'A{2 + 2 * max_single_class_num + 2}:A{2 + 3 * max_single_class_num + 2}')
|
||||
ws[f'A{2 + 2 * max_single_class_num + 2}'] = "第三班"
|
||||
start_index.append(2 + 2 * max_single_class_num + 2)
|
||||
for shift_idx, shift_name in enumerate(shift_names):
|
||||
start_row = 2 + shift_idx * (max_single_class_num + 1)
|
||||
end_row = start_row + max_single_class_num
|
||||
ws.merge_cells(f'A{start_row}:A{end_row}')
|
||||
ws[f'A{start_row}'] = shift_name
|
||||
start_index.append(start_row)
|
||||
|
||||
# 填写值班人员信息
|
||||
yellow_fill = PatternFill(fill_type='solid', start_color='FFFF00', end_color='FFFF00')
|
||||
for duty_index, duty in enumerate(all_result):
|
||||
col = chr(ord('B') + duty_index // 3)
|
||||
for stu_index, stu in enumerate(duty):
|
||||
col = chr(ord('B') + duty_index // 3)
|
||||
row = start_index[duty_index % 3] + stu_index
|
||||
str_info = f"{stu['name']} {stu['department']} {stu['type']}"
|
||||
cell = ws[f'{col}{row}']
|
||||
cell.value = f"{stu['name']} {stu['department']} {stu['type']}"
|
||||
if stu['duty_monitor']:
|
||||
ws[col + str(row)].fill = PatternFill(fill_type='solid', start_color='FFFF00', end_color='FFFF00')
|
||||
ws[col + str(row)] = str_info
|
||||
cell.fill = yellow_fill
|
||||
|
||||
# 保存文件
|
||||
wb.save(file_path)
|
||||
Loading…
Reference in New Issue