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

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

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

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

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

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

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

表结构

程序运行界面

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

预览结果b层次

层次a预览结果

完整程序

python 复制代码
import sys

import pandas as pd

import random

import openpyxl

from openpyxl.worksheet.pagebreak import Break

from openpyxl.styles import Font, Alignment, PatternFill, Border, Side

from openpyxl.worksheet.page import PageMargins

from datetime import datetime

from PyQt5.QtWidgets import (

    QApplication, QLabel, QLineEdit, QPushButton, QFileDialog,

    QVBoxLayout, QWidget, QMessageBox

)

from PyQt5.QtGui import QFont as QPyQtFont


def load_data(filename):
    return pd.read_excel(filename)


def save_data(df, filename, exam_name):
    df.to_excel(filename, index=False)

    format_excel(filename, df, exam_name)


def assign_seats(df, total_halls, start_hall=1):
    grouped = df.groupby('班级')

    groups = [group.sample(frac=1).reset_index(drop=True) for _, group in grouped]

    groups.sort(key=lambda x: len(x), reverse=True)

    iterators = [iter(group.iterrows()) for group in groups]

    arranged_data = []

    hall_number = start_hall

    seat_number = 1

    total_students = len(df)

    students_per_hall = total_students // total_halls

    extra_students = total_students % total_halls

    while iterators:

        random.shuffle(iterators)

        for it in iterators[:]:

            try:

                _, student = next(it)

                student_data = student.to_dict()

                student_data["考场"] = hall_number

                student_data["考号"] = f"{seat_number:02d}"

                arranged_data.append(student_data)

                seat_number += 1

                if seat_number > students_per_hall + (1 if hall_number - start_hall + 1 <= extra_students else 0):
                    hall_number += 1

                    seat_number = 1

                if hall_number >= start_hall + total_halls:
                    hall_number = start_hall

            except StopIteration:

                iterators.remove(it)

    return arranged_data


def check_and_adjust_seating(arranged_data):
    def has_adjacent_same_class(data):

        for i in range(len(data) - 1):

            if data[i]['班级'] == data[i + 1]['班级']:
                return i

        return -1

    def find_valid_swap(index, data):

        current_class = data[index]['班级']

        for j in range(len(data)):

            if j != index and data[j]['班级'] != current_class:

                if (j == 0 or data[j - 1]['班级'] != current_class) and (

                        j == len(data) - 1 or data[j + 1]['班级'] != current_class):
                    return j

        return -1

    swap_operations = []

    while True:

        index = has_adjacent_same_class(arranged_data)

        if index == -1:
            break

        swap_index = find_valid_swap(index + 1, arranged_data)

        if swap_index == -1:
            raise ValueError("Cannot find a valid swap to adjust the seating arrangement.")

        swap_operations.append((index + 1, swap_index))

        arranged_data[index + 1], arranged_data[swap_index] = arranged_data[swap_index], arranged_data[index + 1]

    return arranged_data, swap_operations


def reassign_seats(arranged_data, total_halls, start_hall=1):
    hall_number = start_hall

    seat_number = 1

    total_students = len(arranged_data)

    students_per_hall = total_students // total_halls

    extra_students = total_students % total_halls

    for i, student in enumerate(arranged_data):

        student['考场'] = hall_number

        student['考号'] = f"{seat_number:02d}"

        seat_number += 1

        if seat_number > students_per_hall + (1 if hall_number - start_hall + 1 <= extra_students else 0):
            hall_number += 1

            seat_number = 1

        if hall_number >= start_hall + total_halls:
            hall_number = start_hall

    return arranged_data


def format_excel(filename, df, exam_name):

    if '层次' in df.columns:

        df = df.drop(columns=['层次'])



    wb = openpyxl.Workbook()

    ws = wb.active

    ws.title = "考场安排结果"



    # 将标题从第一行开始写入

    for col_num, column_title in enumerate(df.columns, 1):

        cell = ws.cell(row=1, column=col_num, value=column_title)

        cell.font = Font(bold=True, color="FFFFFF", size=16)

        cell.fill = PatternFill(start_color="4F81BD", end_color="4F81BD", fill_type="solid")

        cell.alignment = Alignment(horizontal="center", vertical="center")



    for row_num, row_data in enumerate(df.itertuples(index=False, name=None), 2):

        for col_num, cell_value in enumerate(row_data, 1):

            ws.cell(row=row_num, column=col_num, value=cell_value)



    for col in ws.columns:

        max_length = 0

        column = col[0].column_letter

        # Check if the column header is "考号"

        if ws.cell(row=1, column=col[0].col_idx).value == "考号":

            adjusted_width = 20  # 设置考号列的宽度为20

        else:

            for cell in col:

                if cell.value is not None:

                    max_length = max(max_length, len(str(cell.value)))

            adjusted_width = max_length + 10

        ws.column_dimensions[column].width = adjusted_width



    # 调整页面边距

    ws.page_margins = PageMargins(left=1.5, right=0.75, top=1.2, bottom=0.5)



    ws.print_title_rows = '1:1'  # 标题行在第一行开始

    ws.page_setup.orientation = 'portrait'

    ws.page_setup.paperSize = ws.PAPERSIZE_A4

    ws.page_setup.fitToPage = True

    ws.page_setup.fitToWidth = 1

    ws.page_setup.fitToHeight = False

    ws.page_setup.horizontalCentered = True

    ws.page_setup.verticalCentered = True



    page_height_in_inches = 11.69 - ws.page_margins.top - ws.page_margins.bottom

    page_height_in_points = page_height_in_inches * 72

    header_height_in_points = 50

    available_row_height_in_points = page_height_in_points - header_height_in_points



    hall_groups = df.groupby('考场')

    previous_max_row = 1  # 前一个最大行号调整为1以考虑空行

    thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))

    for hall_number, (hall_id, hall_df) in enumerate(hall_groups):

        row_count = len(hall_df) + 1

        max_row = hall_df.index[-1] + 2  # 从第二行开始每个考场的数据

        row_height = available_row_height_in_points / row_count

        row_height = max(row_height, 15)

        for row in range(previous_max_row, max_row + 1):

            ws.row_dimensions[row].height = row_height

            for cell in ws[row]:

                cell.border = thin_border

                cell.font = Font(size=15)

                cell.alignment = Alignment(horizontal="center", vertical="center")

        if max_row < ws.max_row:

            ws.row_breaks.append(Break(max_row))

        previous_max_row = max_row + 1



    # 添加页眉和页脚,并使用制表符来向左移动页眉

    ws.oddHeader.center.text = f"&\"微软雅黑,Bold\"&20\t{exam_name}"  # 添加制表符以向左移动

    ws.oddFooter.center.text = "第 &P 页,共 &N 页"

    ws.oddFooter.right.text = f"&D &T"



    wb.save(filename)


def dataframe_to_rows(df, index=True, header=True):
    if header:

        rows = [list(df.columns)]

    else:

        rows = [[]]

    for row in df.itertuples(index=index, name=None):
        rows.append(list(row)[1:])

    return rows


def run_allocation(input_filename, a_total_halls, b_total_halls, start_level, exam_name):
    df = load_data(input_filename)

    if start_level == 'b':

        other_level = 'a'

        first_total_halls = b_total_halls

        second_total_halls = a_total_halls

    else:

        other_level = 'b'

        first_total_halls = a_total_halls

        second_total_halls = b_total_halls

    first_level_students = df[df['层次'] == start_level]

    arranged_first_students = assign_seats(first_level_students, first_total_halls, start_hall=1)

    adjusted_first_students, _ = check_and_adjust_seating(arranged_first_students)

    final_first_students = reassign_seats(adjusted_first_students, first_total_halls, start_hall=1)

    second_level_students = df[df['层次'] == other_level]

    arranged_second_students = assign_seats(second_level_students, second_total_halls, start_hall=first_total_halls + 1)

    adjusted_second_students, _ = check_and_adjust_seating(arranged_second_students)

    final_second_students = reassign_seats(adjusted_second_students, second_total_halls,
                                           start_hall=first_total_halls + 1)

    combined_students = final_first_students + final_second_students

    arranged_df = pd.DataFrame(combined_students)

    current_time = datetime.now().strftime("%Y%m%d_%H%M%S")

    output_filename = f"考场安排结果_{current_time}.xlsx"

    save_data(arranged_df, output_filename, exam_name)

    return output_filename


class ExamArrangementApp(QWidget):

    def __init__(self):

        super().__init__()

        self.initUI()

    def initUI(self):

        layout = QVBoxLayout()

        font = QPyQtFont("Arial", 14)

        self.file_label = QLabel('选择文件:')

        self.file_label.setFont(font)

        layout.addWidget(self.file_label)

        self.file_btn = QPushButton('选择文件')

        self.file_btn.setFont(font)

        self.file_btn.clicked.connect(self.select_file)

        layout.addWidget(self.file_btn)

        self.exam_name_label = QLabel('考试名称:')

        self.exam_name_label.setFont(font)

        layout.addWidget(self.exam_name_label)

        self.exam_name_input = QLineEdit()

        self.exam_name_input.setFont(font)

        layout.addWidget(self.exam_name_input)

        self.a_halls_label = QLabel('A层次考场数:')

        self.a_halls_label.setFont(font)

        layout.addWidget(self.a_halls_label)

        self.a_halls_input = QLineEdit()

        self.a_halls_input.setFont(font)

        layout.addWidget(self.a_halls_input)

        self.b_halls_label = QLabel('B层次考场数:')

        self.b_halls_label.setFont(font)

        layout.addWidget(self.b_halls_label)

        self.b_halls_input = QLineEdit()

        self.b_halls_input.setFont(font)

        layout.addWidget(self.b_halls_input)

        self.start_level_label = QLabel('首先开始编号的层次 (a/b):')

        self.start_level_label.setFont(font)

        layout.addWidget(self.start_level_label)

        self.start_level_input = QLineEdit()

        self.start_level_input.setFont(font)

        layout.addWidget(self.start_level_input)

        self.run_btn = QPushButton('运行')

        self.run_btn.setFont(font)

        self.run_btn.clicked.connect(self.run)

        layout.addWidget(self.run_btn)

        self.setLayout(layout)

        self.setWindowTitle('考场安排工具,By Bobo googaobo@gmail.com')

        self.resize(900, 630)

        self.center()

    def select_file(self):

        options = QFileDialog.Options()

        options |= QFileDialog.DontUseNativeDialog

        file_name, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx);;All Files (*)",
                                                   options=options)

        if file_name:
            self.file_label.setText(f'文件: {file_name}')

            self.input_filename = file_name

    def run(self):

        try:

            a_total_halls = int(self.a_halls_input.text())

            b_total_halls = int(self.b_halls_input.text())

            start_level = self.start_level_input.text()

            exam_name = self.exam_name_input.text()

            output_filename = run_allocation(self.input_filename, a_total_halls, b_total_halls, start_level, exam_name)

            QMessageBox.information(self, "成功", f"已成功生成文件:{output_filename}", QMessageBox.Ok)

        except Exception as e:

            QMessageBox.critical(self, "错误", str(e), QMessageBox.Ok)

    def center(self):

        qr = self.frameGeometry()

        cp = self.screen().availableGeometry().center()

        qr.moveCenter(cp)

        self.move(qr.topLeft())


if __name__ == '__main__':
    app = QApplication(sys.argv)

    ex = ExamArrangementApp()

    ex.show()

    sys.exit(app.exec_())

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

相关推荐
幽络源小助理1 分钟前
Python使用requests_html库爬取掌阅书籍(附完整源码及使用说明)
python·html·python爬虫·爬虫教程·requests_html·爬取书籍·掌阅
取个名字真难呐3 分钟前
LossMaskMatrix损失函数掩码矩阵
python·深度学习·矩阵
南宫理的日知录4 分钟前
「Python数据科学」标量、向量、矩阵、张量与多维数组的辨析
python·numpy·数据科学
xlsw_5 分钟前
java全栈day21--Web后端实战之利用Mybaits查询数据
java·开发语言
Murphy202320 分钟前
.net4.0 调用API(form-data)上传文件及传参
开发语言·c#·api·httpwebrequest·form-data·uploadfile·multipart/form-
GZ同学21 分钟前
Arcgis中python工具箱制造要点及统计要素图层字段信息工具分享
python·arcgis
我曾经是个程序员30 分钟前
C#Directory类文件夹基本操作大全
服务器·开发语言·c#
白云~️32 分钟前
uniappX 移动端单行/多行文字隐藏显示省略号
开发语言·前端·javascript
编码浪子38 分钟前
构建一个rust生产应用读书笔记7-确认邮件2
开发语言·后端·rust
天之涯上上1 小时前
JAVA开发 在 Spring Boot 中集成 Swagger
java·开发语言·spring boot