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