当前位置:   article > 正文

gpt-4o继续迭代考场安排程序 一键生成考场清单

gpt-4o继续迭代考场安排程序 一键生成考场清单

        接上两篇gpt-4o考场安排-CSDN博客考场分层次安排,最终exe版-CSDN博客

当然你也可以只看这一篇。

今天又添加了以下功能,程序见后。

1、自动分页,每个考场打印一页

2、添加了打印试场单页眉

3、添加了页脚 第X页,其X页, 打印时间

表结构

程序运行界面

测试分配考场环境,共15个班分为两个层次由字段“层次”指定a, b。a层次9个考场,b层次15个,从b层开始编号,a层次考场号从16开编。

预览结果b层次

层次a预览结果

完整程序

  1. import sys
  2. import pandas as pd
  3. import random
  4. import openpyxl
  5. from openpyxl.worksheet.pagebreak import Break
  6. from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
  7. from openpyxl.worksheet.page import PageMargins
  8. from datetime import datetime
  9. from PyQt5.QtWidgets import (
  10. QApplication, QLabel, QLineEdit, QPushButton, QFileDialog,
  11. QVBoxLayout, QWidget, QMessageBox
  12. )
  13. from PyQt5.QtGui import QFont as QPyQtFont
  14. def load_data(filename):
  15. return pd.read_excel(filename)
  16. def save_data(df, filename, exam_name):
  17. df.to_excel(filename, index=False)
  18. format_excel(filename, df, exam_name)
  19. def assign_seats(df, total_halls, start_hall=1):
  20. grouped = df.groupby('班级')
  21. groups = [group.sample(frac=1).reset_index(drop=True) for _, group in grouped]
  22. groups.sort(key=lambda x: len(x), reverse=True)
  23. iterators = [iter(group.iterrows()) for group in groups]
  24. arranged_data = []
  25. hall_number = start_hall
  26. seat_number = 1
  27. total_students = len(df)
  28. students_per_hall = total_students // total_halls
  29. extra_students = total_students % total_halls
  30. while iterators:
  31. random.shuffle(iterators)
  32. for it in iterators[:]:
  33. try:
  34. _, student = next(it)
  35. student_data = student.to_dict()
  36. student_data["考场"] = hall_number
  37. student_data["考号"] = f"{seat_number:02d}"
  38. arranged_data.append(student_data)
  39. seat_number += 1
  40. if seat_number > students_per_hall + (1 if hall_number - start_hall + 1 <= extra_students else 0):
  41. hall_number += 1
  42. seat_number = 1
  43. if hall_number >= start_hall + total_halls:
  44. hall_number = start_hall
  45. except StopIteration:
  46. iterators.remove(it)
  47. return arranged_data
  48. def check_and_adjust_seating(arranged_data):
  49. def has_adjacent_same_class(data):
  50. for i in range(len(data) - 1):
  51. if data[i]['班级'] == data[i + 1]['班级']:
  52. return i
  53. return -1
  54. def find_valid_swap(index, data):
  55. current_class = data[index]['班级']
  56. for j in range(len(data)):
  57. if j != index and data[j]['班级'] != current_class:
  58. if (j == 0 or data[j - 1]['班级'] != current_class) and (
  59. j == len(data) - 1 or data[j + 1]['班级'] != current_class):
  60. return j
  61. return -1
  62. swap_operations = []
  63. while True:
  64. index = has_adjacent_same_class(arranged_data)
  65. if index == -1:
  66. break
  67. swap_index = find_valid_swap(index + 1, arranged_data)
  68. if swap_index == -1:
  69. raise ValueError("Cannot find a valid swap to adjust the seating arrangement.")
  70. swap_operations.append((index + 1, swap_index))
  71. arranged_data[index + 1], arranged_data[swap_index] = arranged_data[swap_index], arranged_data[index + 1]
  72. return arranged_data, swap_operations
  73. def reassign_seats(arranged_data, total_halls, start_hall=1):
  74. hall_number = start_hall
  75. seat_number = 1
  76. total_students = len(arranged_data)
  77. students_per_hall = total_students // total_halls
  78. extra_students = total_students % total_halls
  79. for i, student in enumerate(arranged_data):
  80. student['考场'] = hall_number
  81. student['考号'] = f"{seat_number:02d}"
  82. seat_number += 1
  83. if seat_number > students_per_hall + (1 if hall_number - start_hall + 1 <= extra_students else 0):
  84. hall_number += 1
  85. seat_number = 1
  86. if hall_number >= start_hall + total_halls:
  87. hall_number = start_hall
  88. return arranged_data
  89. def format_excel(filename, df, exam_name):
  90. if '层次' in df.columns:
  91. df = df.drop(columns=['层次'])
  92. wb = openpyxl.Workbook()
  93. ws = wb.active
  94. ws.title = "考场安排结果"
  95. # 将标题从第一行开始写入
  96. for col_num, column_title in enumerate(df.columns, 1):
  97. cell = ws.cell(row=1, column=col_num, value=column_title)
  98. cell.font = Font(bold=True, color="FFFFFF", size=16)
  99. cell.fill = PatternFill(start_color="4F81BD", end_color="4F81BD", fill_type="solid")
  100. cell.alignment = Alignment(horizontal="center", vertical="center")
  101. for row_num, row_data in enumerate(df.itertuples(index=False, name=None), 2):
  102. for col_num, cell_value in enumerate(row_data, 1):
  103. ws.cell(row=row_num, column=col_num, value=cell_value)
  104. for col in ws.columns:
  105. max_length = 0
  106. column = col[0].column_letter
  107. # Check if the column header is "考号"
  108. if ws.cell(row=1, column=col[0].col_idx).value == "考号":
  109. adjusted_width = 20 # 设置考号列的宽度为20
  110. else:
  111. for cell in col:
  112. if cell.value is not None:
  113. max_length = max(max_length, len(str(cell.value)))
  114. adjusted_width = max_length + 10
  115. ws.column_dimensions[column].width = adjusted_width
  116. # 调整页面边距
  117. ws.page_margins = PageMargins(left=1.5, right=0.75, top=1.2, bottom=0.5)
  118. ws.print_title_rows = '1:1' # 标题行在第一行开始
  119. ws.page_setup.orientation = 'portrait'
  120. ws.page_setup.paperSize = ws.PAPERSIZE_A4
  121. ws.page_setup.fitToPage = True
  122. ws.page_setup.fitToWidth = 1
  123. ws.page_setup.fitToHeight = False
  124. ws.page_setup.horizontalCentered = True
  125. ws.page_setup.verticalCentered = True
  126. page_height_in_inches = 11.69 - ws.page_margins.top - ws.page_margins.bottom
  127. page_height_in_points = page_height_in_inches * 72
  128. header_height_in_points = 50
  129. available_row_height_in_points = page_height_in_points - header_height_in_points
  130. hall_groups = df.groupby('考场')
  131. previous_max_row = 1 # 前一个最大行号调整为1以考虑空行
  132. thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
  133. for hall_number, (hall_id, hall_df) in enumerate(hall_groups):
  134. row_count = len(hall_df) + 1
  135. max_row = hall_df.index[-1] + 2 # 从第二行开始每个考场的数据
  136. row_height = available_row_height_in_points / row_count
  137. row_height = max(row_height, 15)
  138. for row in range(previous_max_row, max_row + 1):
  139. ws.row_dimensions[row].height = row_height
  140. for cell in ws[row]:
  141. cell.border = thin_border
  142. cell.font = Font(size=15)
  143. cell.alignment = Alignment(horizontal="center", vertical="center")
  144. if max_row < ws.max_row:
  145. ws.row_breaks.append(Break(max_row))
  146. previous_max_row = max_row + 1
  147. # 添加页眉和页脚,并使用制表符来向左移动页眉
  148. ws.oddHeader.center.text = f"&\"微软雅黑,Bold\"&20\t{exam_name}" # 添加制表符以向左移动
  149. ws.oddFooter.center.text = "第 &P 页,共 &N 页"
  150. ws.oddFooter.right.text = f"&D &T"
  151. wb.save(filename)
  152. def dataframe_to_rows(df, index=True, header=True):
  153. if header:
  154. rows = [list(df.columns)]
  155. else:
  156. rows = [[]]
  157. for row in df.itertuples(index=index, name=None):
  158. rows.append(list(row)[1:])
  159. return rows
  160. def run_allocation(input_filename, a_total_halls, b_total_halls, start_level, exam_name):
  161. df = load_data(input_filename)
  162. if start_level == 'b':
  163. other_level = 'a'
  164. first_total_halls = b_total_halls
  165. second_total_halls = a_total_halls
  166. else:
  167. other_level = 'b'
  168. first_total_halls = a_total_halls
  169. second_total_halls = b_total_halls
  170. first_level_students = df[df['层次'] == start_level]
  171. arranged_first_students = assign_seats(first_level_students, first_total_halls, start_hall=1)
  172. adjusted_first_students, _ = check_and_adjust_seating(arranged_first_students)
  173. final_first_students = reassign_seats(adjusted_first_students, first_total_halls, start_hall=1)
  174. second_level_students = df[df['层次'] == other_level]
  175. arranged_second_students = assign_seats(second_level_students, second_total_halls, start_hall=first_total_halls + 1)
  176. adjusted_second_students, _ = check_and_adjust_seating(arranged_second_students)
  177. final_second_students = reassign_seats(adjusted_second_students, second_total_halls,
  178. start_hall=first_total_halls + 1)
  179. combined_students = final_first_students + final_second_students
  180. arranged_df = pd.DataFrame(combined_students)
  181. current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
  182. output_filename = f"考场安排结果_{current_time}.xlsx"
  183. save_data(arranged_df, output_filename, exam_name)
  184. return output_filename
  185. class ExamArrangementApp(QWidget):
  186. def __init__(self):
  187. super().__init__()
  188. self.initUI()
  189. def initUI(self):
  190. layout = QVBoxLayout()
  191. font = QPyQtFont("Arial", 14)
  192. self.file_label = QLabel('选择文件:')
  193. self.file_label.setFont(font)
  194. layout.addWidget(self.file_label)
  195. self.file_btn = QPushButton('选择文件')
  196. self.file_btn.setFont(font)
  197. self.file_btn.clicked.connect(self.select_file)
  198. layout.addWidget(self.file_btn)
  199. self.exam_name_label = QLabel('考试名称:')
  200. self.exam_name_label.setFont(font)
  201. layout.addWidget(self.exam_name_label)
  202. self.exam_name_input = QLineEdit()
  203. self.exam_name_input.setFont(font)
  204. layout.addWidget(self.exam_name_input)
  205. self.a_halls_label = QLabel('A层次考场数:')
  206. self.a_halls_label.setFont(font)
  207. layout.addWidget(self.a_halls_label)
  208. self.a_halls_input = QLineEdit()
  209. self.a_halls_input.setFont(font)
  210. layout.addWidget(self.a_halls_input)
  211. self.b_halls_label = QLabel('B层次考场数:')
  212. self.b_halls_label.setFont(font)
  213. layout.addWidget(self.b_halls_label)
  214. self.b_halls_input = QLineEdit()
  215. self.b_halls_input.setFont(font)
  216. layout.addWidget(self.b_halls_input)
  217. self.start_level_label = QLabel('首先开始编号的层次 (a/b):')
  218. self.start_level_label.setFont(font)
  219. layout.addWidget(self.start_level_label)
  220. self.start_level_input = QLineEdit()
  221. self.start_level_input.setFont(font)
  222. layout.addWidget(self.start_level_input)
  223. self.run_btn = QPushButton('运行')
  224. self.run_btn.setFont(font)
  225. self.run_btn.clicked.connect(self.run)
  226. layout.addWidget(self.run_btn)
  227. self.setLayout(layout)
  228. self.setWindowTitle('考场安排工具,By Bobo googaobo@gmail.com')
  229. self.resize(900, 630)
  230. self.center()
  231. def select_file(self):
  232. options = QFileDialog.Options()
  233. options |= QFileDialog.DontUseNativeDialog
  234. file_name, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx);;All Files (*)",
  235. options=options)
  236. if file_name:
  237. self.file_label.setText(f'文件: {file_name}')
  238. self.input_filename = file_name
  239. def run(self):
  240. try:
  241. a_total_halls = int(self.a_halls_input.text())
  242. b_total_halls = int(self.b_halls_input.text())
  243. start_level = self.start_level_input.text()
  244. exam_name = self.exam_name_input.text()
  245. output_filename = run_allocation(self.input_filename, a_total_halls, b_total_halls, start_level, exam_name)
  246. QMessageBox.information(self, "成功", f"已成功生成文件:{output_filename}", QMessageBox.Ok)
  247. except Exception as e:
  248. QMessageBox.critical(self, "错误", str(e), QMessageBox.Ok)
  249. def center(self):
  250. qr = self.frameGeometry()
  251. cp = self.screen().availableGeometry().center()
  252. qr.moveCenter(cp)
  253. self.move(qr.topLeft())
  254. if __name__ == '__main__':
  255. app = QApplication(sys.argv)
  256. ex = ExamArrangementApp()
  257. ex.show()
  258. sys.exit(app.exec_())

程序已打包exe,点赞留言QQ,发送程序。程序运行中有问题请评论区留言交流!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/637834
推荐阅读
相关标签
  

闽ICP备14008679号