背景需求

昨天做了全园所有班级的“涂氟单”PDF

【办公类-18-08】20251217(Python)全园幼儿“口腔检查涂氟信息”批量生成打印https://mp.csdn.net/mp_blog/creation/editor/156028065

今天再同理做一份“验血单”

【办公类-18-04】20250520(Python)“验血单信息”批量生成打印(学校、班级、姓名、性别)https://mp.csdn.net/mp_blog/creation/editor/148099329

一、数据情况

二、下载园园通名单

三、合并exlce

'''
20251220验血单制作
1、下载园园通新生名册.xls,把两个新生名册合并在一起
2、部分插班生信息手动添加
deepseek,阿夏
20251124
'''

import pandas as pd
import os
import re

def format_long_number(value):
    """处理长数字,避免科学计数法和精度丢失"""
    if pd.isna(value) or value == '':
        return value
    
    str_value = str(value).strip()
    
    # 处理科学计数法
    if 'e+' in str_value.lower() or 'e-' in str_value.lower():
        try:
            # 将科学计数法转换为完整数字
            num_value = float(str_value)
            return str(int(num_value)) if num_value.is_integer() else str(num_value)
        except:
            return str_value
    
    # 处理浮点数.0的情况
    if '.' in str_value and str_value.endswith('.0'):
        return str_value[:-2]
    
    return str_value

def format_class_name(class_name):
    """格式化班级名称"""
    if pd.isna(class_name) or class_name == '':
        return class_name
    
    class_str = str(class_name)
    
    # 定义班级类型和对应的前缀
    class_types = {
        '托班': '托', '托': '托',
        '小班': '小', '小': '小', 
        '中班': '中', '中': '中',
        '大班': '大', '大': '大'
    }
    
    # 中文数字到阿拉伯数字的映射
    chinese_to_arabic = {
        '一': '1', '二': '2', '三': '3', '四': '4', '五': '5',
        '六': '6', '七': '7', '八': '8', '九': '9', '十': '10',
        '1': '1', '2': '2', '3': '3', '4': '4', '5': '5',
        '6': '6', '7': '7', '8': '8', '9': '9', '10': '10'
    }
    
    # 尝试匹配各种班级格式
    patterns = [
        r'([托小中大])班(\S+?)班',  # 如:大班一班
        r'([托小中大])(\S+?)班',    # 如:大一班
        r'(\S+?)([托小中大])班',    # 如:一班大班
        r'([托小中大])班',          # 如:大班(没有具体班号)
    ]
    
    for pattern in patterns:
        match = re.search(pattern, class_str)
        if match:
            if len(match.groups()) >= 0:
                # 有班级类型和班号
                class_type = match.group(1)
                class_num = match.group(2)
                
                # 获取班级前缀
                prefix = class_types.get(class_type, class_type)
                
                # 转换班号
                if class_num in chinese_to_arabic:
                    num = chinese_to_arabic[class_num]
                    return f"{prefix}{num}班"
                else:
                    return f"{prefix}{class_num}班"
            else:
                # 只有班级类型,没有班号
                class_type = match.group(1)
                prefix = class_types.get(class_type, class_type)
                return f"{prefix}班"
    
    # 如果没有匹配到任何模式,返回原值
    return class_str

def merge_and_format_excel():
    # 定义文件路径
    path = r'C:\Users\jg2yXRZ\OneDrive\桌面\20251220验血单全园'
    folder_path = path + r"\01园园通下载"
    output_path = path + r'\02园园通全部班级信息合并.xlsx'
    
    # 获取文件夹中的所有Excel文件
    excel_files = [f for f in os.listdir(folder_path) if f.endswith(('.xlsx', '.xls'))]
    
    if len(excel_files) < 2:
        print("234文件夹中Excel文件数量不足2个")
        return
    
    # 读取所有Excel文件时指定数据类型为字符串,避免数字精度丢失
    dfs = []
    for file in excel_files[:3]:  # 只处理前两个文件
        file_path = os.path.join(folder_path, file)
        
        # 首先读取第一行来检测列的数据类型
        temp_df = pd.read_excel(file_path, nrows=1)
        
        # 创建dtype字典,将所有列都设置为字符串类型
        dtype_dict = {col: str for col in temp_df.columns}
        
        # 用字符串类型重新读取整个文件
        df = pd.read_excel(file_path, dtype=dtype_dict, keep_default_na=False)
        
        # 特别处理身份证号、学号等可能的长数字列
        for col in df.columns:
            if any(keyword in col for keyword in ['身份证', '证件号', '学号', '幼儿编号', 'ID']):
                # 去除可能的空格和特殊字符
                df[col] = df[col].astype(str).str.strip()
                # 如果是以科学计数法形式出现的数字,进行转换
                df[col] = df[col].apply(lambda x: format_long_number(x))
        
        dfs.append(df)
        print(f"已读取文件: {file}")
    
    # 合并数据
    merged_df = pd.concat(dfs, ignore_index=True)
    
    # 查找包含班级信息的列
    class_columns = [col for col in merged_df.columns if any(keyword in col for keyword in ['班', 'class', '班级'])]
    
    if class_columns:
        for class_col in class_columns:
            print(f"检测到班级列: {class_col}")
            merged_df[class_col] = merged_df[class_col].apply(format_class_name)
    else:
        print("未检测到班级列,跳过格式转换")
    
    # 再次处理所有列,确保数字格式正确
    for col in merged_df.columns:
        merged_df[col] = merged_df[col].apply(format_long_number)
    
    # 保存合并后的文件
    # 使用openpyxl引擎,避免xlsxwriter对长数字的处理问题
    with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
        merged_df.to_excel(writer, index=False)
    
    print(f"文件已保存至: {output_path}")
    print(f"合并完成,共{len(merged_df)}条记录")
    
    # 显示一些样本数据以供验证
    print("\n前5条数据的班级信息:")
    if class_columns:
        for class_col in class_columns:
            print(f"{class_col}列的前5个值:")
            print(merged_df[class_col].head().tolist())
    
    # 特别显示可能的长数字列
    long_num_cols = [col for col in merged_df.columns if any(keyword in col for keyword in ['身份证', '证件号', '学号', '幼儿编号', 'ID'])]
    if long_num_cols:
        print("\n长数字列样本数据:")
        for col in long_num_cols:
            print(f"{col}列的前3个值:")
            print(merged_df[col].head(3).tolist())

# 执行函数
if __name__ == "__main__":
    merge_and_format_excel()

四、匹配学号,重新排列

'''
20251220验血单制作
1、用合并xls与班级信息表的学号匹配,按学号排队,保存03匹配学号.xlsx

deepseek,阿夏
20251217
'''


import pandas as pd
import os

def format_long_number(value):
    """处理长数字,避免科学计数法和精度丢失"""
    if pd.isna(value) or value == '':
        return value
    
    str_value = str(value).strip()
    
    # 处理科学计数法
    if 'e+' in str_value.lower() or 'e-' in str_value.lower():
        try:
            # 将科学计数法转换为完整数字
            num_value = float(str_value)
            return str(int(num_value)) if num_value.is_integer() else str(num_value)
        except:
            return str_value
    
    # 处理浮点数.0的情况
    if '.' in str_value and str_value.endswith('.0'):
        return str_value[:-2]
    
    # 处理浮点数显示为整数的情况
    if '.' in str_value:
        try:
            # 检查是否可以转换为整数
            float_val = float(str_value)
            if float_val.is_integer():
                return str(int(float_val))
        except:
            pass
    
    return str_value

# 设置路径
path =r'C:\Users\jg2yXRZ\OneDrive\桌面\20251220验血单全园'

# 1. 读取园园通信息表 - 所有列都作为字符串读取
df_main = pd.read_excel(path + r'\02园园通全部班级信息合并.xlsx', dtype=str, keep_default_na=False)

# 2. 读取班级信息表的所有工作表 - 所有列都作为字符串读取
xls = pd.ExcelFile(path + r'\00班级信息表.xlsx')
sheet_names = xls.sheet_names

# 3. 合并所有班级信息表
df_list = []
for sheet in sheet_names:
    df_sheet = pd.read_excel(xls, sheet_name=sheet, dtype=str, keep_default_na=False)
    df_sheet['班级'] = sheet  # 添加班级列
    df_list.append(df_sheet)

df_info = pd.concat(df_list, ignore_index=True)

# 4. 匹配学号
df_merged = pd.merge(df_main, 
                    df_info[['班级', '姓名', '学号']], 
                    left_on=['所在班级', '姓名'], 
                    right_on=['班级', '姓名'], 
                    how='left')

# 5. 移动学号列到最前面
cols = df_merged.columns.tolist()
cols = ['学号'] + [col for col in cols if col != '学号']
df_merged = df_merged[cols]

# 6. 处理所有数字列,避免精度丢失
for col in df_merged.columns:
    df_merged[col] = df_merged[col].apply(format_long_number)

# 7. 对完整匹配结果进行排序并保存
# 确保学号列是数值类型用于排序,但保存时保持字符串格式
df_merged['学号_排序'] = pd.to_numeric(df_merged['学号'], errors='coerce')

# 创建班级排序的映射关系
class_order = {
    '托1班': 1, '托2班': 2, '托3班': 3, '托4班': 4,
    '小1班': 5, '小2班': 6, '小3班': 7, '小4班': 8,
    '中1班': 9, '中2班': 10, '中3班': 11, '中4班': 12,
    '大1班': 13, '大2班': 14, '大3班': 15, '大4班': 16
}

# 添加临时排序列
df_merged['班级排序'] = df_merged['所在班级'].map(class_order)

# 先按班级排序,再按学号排序
df_merged_sorted = df_merged.sort_values(by=['班级排序', '学号_排序'])

# 删除临时排序列
df_merged_sorted = df_merged_sorted.drop(['班级排序', '学号_排序'], axis=1)

# 保存排序后的完整表格,使用openpyxl引擎避免数字格式问题
with pd.ExcelWriter(path + r'\03匹配学号.xlsx', engine='openpyxl') as writer:
    df_merged_sorted.to_excel(writer, index=False)

五、制作需要信息的EXCLE,分割成一个个班级,并把一个个班级作为一个工作簿的多个工作表


'''
20251220验血单制作
1、将03匹配学号.xlsx里面的学号、班级、姓名、身份证号,幼儿编号等数据写入到医保模版指定的列里
2、先生成每个班级的独立Excel文件,再合并所有文件到一个工作簿(每个班级一个工作表)
3、合并完成后删除存放单个文件的文件夹
deepseek,阿夏
20251216
'''

import pandas as pd
import numpy as np
from openpyxl import load_workbook, Workbook
from openpyxl.styles import Alignment
import os
import glob
import shutil  # 用于删除文件夹(含子文件)

# 全局定义上海行政区列表(避免作用域问题)
OUTER_DISTRICT_LIST = [
    '黄浦区', '徐汇区', '长宁区', '静安区', '普陀区', '虹口区', '杨浦区',
    '浦东新区', '宝山区', '嘉定区', '金山区', '松江区', '青浦区', '奉贤区', '崇明区'
]
SHANGHAI_DISTRICTS = ['闵行区'] + OUTER_DISTRICT_LIST

# ===================== 核心修改:统一配置路径 =====================
# 请根据你的实际文件位置修改这个路径!!!
BASE_PATH = r'C:\Users\jg2yXRZ\OneDrive\桌面\20251220验血单全园'

def batch_process_all_classes():
    """批量处理所有班级的医保表格数据(生成单个班级文件)"""
    main_data_file = os.path.join(BASE_PATH, '03匹配学号.xlsx')
    # 增加文件存在性检查
    if not os.path.exists(main_data_file):
        print(f"❌ 主数据文件不存在:{main_data_file}")
        print("请检查:1. 文件路径是否正确 2. 文件名是否为'03匹配学号.xlsx'")
        return
    
    df_main = pd.read_excel(main_data_file, sheet_name=0)
    
    valid_classes = [cls for cls in df_main['所在班级'].unique() if pd.notna(cls)]
    print(f"✅ 成功识别 {len(valid_classes)} 个班级:{valid_classes}")
    
    for class_name in valid_classes:
        process_single_class(df_main, class_name, BASE_PATH)
    
    print(f"\n🎉 所有班级独立文件生成完成!累计处理 {len(valid_classes)} 个班级")
    
    # 生成完成后自动执行合并操作
    merge_class_files_to_one_workbook(BASE_PATH)
    
    # 合并完成后删除单个文件的文件夹
    output_dir = os.path.join(BASE_PATH, '2025全校学生验血单')
    delete_folder(output_dir)

def process_single_class(main_df, class_name, base_path):
    """处理单个班级:加载模板、设置格式、填充数据、保存文件"""
    class_students = main_df[main_df['所在班级'] == class_name].copy()
    student_count_total = len(class_students)
    print(f"\n📌 开始处理班级:{class_name}(共{student_count_total}名学生)")
    
    template_file = os.path.join(base_path, '00模版验血单表格.xlsx')
    # 增加模板文件检查
    if not os.path.exists(template_file):
        print(f"❌ 模板文件缺失:{template_file}")
        print("请检查:1. 模板文件是否在指定路径 2. 文件名是否为'00模版验血单表格.xlsx'")
        return
    
    try:
        workbook = load_workbook(template_file)
        worksheet = workbook.active
    except Exception as e:
        print(f"❌ 加载模板文件失败:{str(e)}")
        return
    
    # 全局统一文本格式(E/F列)
    for col_letter in ['E', 'F']:
        for cell in worksheet[col_letter]:
            cell.number_format = '@'
            cell.alignment = Alignment(horizontal='left')
    
    start_row = 2
    max_student_limit = 35
    filled_count = 0
    
    for row_idx, (_, student_info) in enumerate(class_students.iterrows(), start=start_row):
        if filled_count >= max_student_limit:
            print(f"⚠️  班级 {class_name} 学生数超过{max_student_limit}人,仅保留前{max_student_limit}条数据")
            break
        
        filled_count += 1
        print(f"   🔄 处理第{filled_count}名学生:{student_info['姓名']}")
        fill_student_details(worksheet, row_idx, student_info, class_name)
    
    # 创建输出目录并保存文件
    output_dir = os.path.join(base_path, '2025全校学生验血单')
    os.makedirs(output_dir, exist_ok=True)
    
    output_filename = f'{class_name}_2025园园通学生验血单.xlsx'
    output_path = os.path.join(output_dir, output_filename)
    
    try:
        workbook.save(output_path)
        print(f"✅ 班级 {class_name} 处理完成,文件保存至:{output_path}")
    except PermissionError:
        print(f"❌ 保存失败:{output_path} 已被打开,请关闭后重试")
    except Exception as e:
        print(f"❌ 保存文件失败:{str(e)}")

def fill_student_details(worksheet, row_num, student_data, class_name):
    """填充单个学生的详细信息到表格指定位置"""
    # A列:学号
    if '学号' in student_data and pd.notna(student_data['学号']):
        worksheet[f'A{row_num}'] = str(student_data['学号'])
    
    # B列:姓名
    if '姓名' in student_data and pd.notna(student_data['姓名']):
        worksheet[f'B{row_num}'] = student_data['姓名']

    # C列:班级
    if '所在班级' in student_data and pd.notna(student_data['所在班级']):
        worksheet[f'C{row_num}'] = student_data['所在班级']

    # D列:性别
    if '性别' in student_data and pd.notna(student_data['性别']):
        worksheet[f'D{row_num}'] = str(student_data['性别'])

    # 以下为注释掉的字段,如需启用可取消注释
    # # I列:证件号码(身份证号)
    # if '证件号码' in student_data and pd.notna(student_data['证件号码']):
    #     worksheet[f'I{row_num}'] = str(student_data['证件号码'])
    #     worksheet[f'I{row_num}'].number_format = '@'
  
    # # F/G/H列:出生日期(拆分为年/月/日,补零至2位)
    # if '出生日期' in student_data and pd.notna(student_data['出生日期']):
    #     try:
    #         birth_dt = pd.to_datetime(student_data['出生日期'])
    #         worksheet[f'F{row_num}'] = birth_dt.year  # 年
    #         worksheet[f'G{row_num}'] = f"{birth_dt.month:02d}"  # 月(补零)
    #         worksheet[f'H{row_num}'] = f"{birth_dt.day:02d}"    # 日(补零)
    #         worksheet[f'G{row_num}'].number_format = '@'
    #         worksheet[f'H{row_num}'].number_format = '@'
    #     except Exception as e:
    #         worksheet[f'F{row_num}'] = str(student_data['出生日期'])
    #         print(f"   ⚠️ {student_data['姓名']} 出生日期格式异常:{student_data['出生日期']},错误:{str(e)}")
    
    # # J/K列:户口所在地(上海→J列勾,非上海→K列勾)
    # if '户口所在地区' in student_data and pd.notna(student_data['户口所在地区']):
    #     hukou_district = str(student_data['户口所在地区']).strip()
    #     if hukou_district in SHANGHAI_DISTRICTS:
    #         worksheet[f'J{row_num}'] = '√'
    #     else:
    #         worksheet[f'K{row_num}'] = '√'

def merge_class_files_to_one_workbook(base_path):
    """将2025全校学生验血单文件夹中的所有班级文件合并到一个工作簿(仅复制值,避免格式报错)"""
    # 1. 定义文件夹路径和文件匹配规则
    output_dir = os.path.join(base_path, '2025全校学生验血单')
    if not os.path.exists(output_dir):
        print(f"\n⚠️  待合并的文件夹不存在:{output_dir}")
        return
    
    file_pattern = os.path.join(output_dir, '*_2025园园通学生验血单.xlsx')
    class_files = glob.glob(file_pattern)
    
    if not class_files:
        print(f"\n⚠️  未找到待合并的班级文件,检查路径:{output_dir}")
        return
    
    print(f"\n📁 找到 {len(class_files)} 个待合并的班级文件:")
    for f in class_files:
        print(f"   - {os.path.basename(f)}")
    
    # 2. 创建新的整合工作簿
    merged_wb = Workbook()
    # 删除默认的Sheet工作表
    if 'Sheet' in merged_wb.sheetnames:
        merged_wb.remove(merged_wb['Sheet'])
    
    # 3. 逐个读取班级文件,仅复制值到整合工作簿
    for file_path in class_files:
        try:
            # 读取单个班级文件(仅读值,不加载格式,提升效率)
            class_wb = load_workbook(file_path, data_only=True)
            class_ws = class_wb.active
            
            # 提取班级名(从文件名中截取)
            file_name = os.path.basename(file_path)
            class_name = file_name.replace('_2025园园通学生验血单.xlsx', '')
            
            # 在整合工作簿中新建工作表,命名为班级名
            new_ws = merged_wb.create_sheet(title=class_name)
            
            # 仅复制单元格的值
            for row_idx, row in enumerate(class_ws.iter_rows(values_only=True), start=1):
                for col_idx, cell_value in enumerate(row, start=1):
                    new_ws.cell(row=row_idx, column=col_idx, value=cell_value)
            
            # 对关键列设置文本格式
            key_cols = ['A', 'E', 'F', 'G', 'H', 'I']  # 学号、班级、出生日期、身份证号列
            for col_letter in key_cols:
                for row in new_ws.iter_rows(min_row=1, max_row=100):
                    cell = row[ord(col_letter)-ord('A')]
                    cell.number_format = '@'
                    cell.alignment = Alignment(horizontal='left')
            
            print(f"✅ 已合并班级:{class_name}")
        except Exception as e:
            print(f"❌ 合并文件 {file_path} 失败:{str(e)}")
    
    # 4. 保存整合后的工作簿
    merged_file_path = os.path.join(base_path, '04园园通学生验血单_班级合并版.xlsx')
    try:
        merged_wb.save(merged_file_path)
        print(f"\n🎉 所有班级文件合并完成!")
        print(f"📑 合并文件保存至:{merged_file_path}")
        print(f"📊 工作簿包含工作表:{merged_wb.sheetnames}")
    except PermissionError:
        print(f"\n❌ 合并文件保存失败:{merged_file_path} 已被打开,请关闭后重试")
    except Exception as e:
        print(f"\n❌ 保存合并文件失败:{str(e)}")

def delete_folder(folder_path):
    """删除指定文件夹(含所有子文件/子文件夹),处理权限问题"""
    if not os.path.exists(folder_path):
        print(f"\n📂 文件夹 {folder_path} 不存在,无需删除")
        return
    
    try:
        # rmtree 递归删除文件夹及所有内容
        shutil.rmtree(folder_path)
        print(f"\n🗑️  已成功删除文件夹:{folder_path}")
    except PermissionError:
        print(f"\n❌ 删除文件夹失败:{folder_path} 或其中的文件被占用,请关闭相关Excel文件后手动删除")
    except Exception as e:
        print(f"\n❌ 删除文件夹 {folder_path} 失败:{str(e)}")

# 主程序入口
if __name__ == "__main__":
    print("="*60)
    print("📋 程序开始执行 - 验血单批量生成工具")
    print(f"   基础路径:{BASE_PATH}")
    print("="*60)
    
    try:
        main_data_path = os.path.join(BASE_PATH, '03匹配学号.xlsx')
        if not os.path.exists(main_data_path):
            raise FileNotFoundError(f"主数据文件不存在:{main_data_path}")
        
        df_check = pd.read_excel(main_data_path)
        # 调整必要列清单,适配实际业务
        required_columns = [
            '所在班级', '学号', '姓名', '性别'
        ]
        missing_cols = [col for col in required_columns if col not in df_check.columns]
        if missing_cols:
            raise ValueError(f"主数据文件缺少必要列:{', '.join(missing_cols)}")
        
        print("📋 主数据文件信息:")
        print(f"   列名:{df_check.columns.tolist()}")
        print(f"   总行数:{len(df_check)}")
        print(f"   涉及班级:{[cls for cls in df_check['所在班级'].unique() if pd.notna(cls)]}")
        print("="*60)
        
        batch_process_all_classes()
        print("\n✅ 程序执行完毕!")
    
    except Exception as main_err:
        print(f"\n❌ 程序执行失败:{str(main_err)}")
        print("请优先检查:")
        print("  1. BASE_PATH 变量是否指向正确的文件夹")
        print("  2. 文件夹内是否有 '03匹配学号.xlsx' 和 '00模版验血单表格.xlsx' 文件")
        print("  3. 所有Excel文件都已关闭(避免权限问题)")

六、每个班级内容都做成PDF

# -*- coding:utf-8 -*- 
'''
目的:验血单(一个班级单独打印)
作者:deepseek,阿夏
日期:20251220
功能:遍历Excel多工作表,每个班级生成独立的合并PDF(统一存放至“合并PDF”文件夹)
'''

# 一、导入相关模块,设定路径
from docxtpl import DocxTemplate
import pandas as pd
import os
import time
from docx2pdf import convert
from PyPDF2 import PdfFileMerger
import shutil

# 配置参数
l = 2  # 1=有背景图,2=无背景图
zpath = r'C:\Users\jg2yXRZ\OneDrive\桌面\20251220验血单全园\\'
temp_word_path = zpath + r'零时Word'  # 临时Word/PDF文件夹
pdf_output_dir = os.path.join(zpath, "02合并PDF")  # 最终PDF统一存放文件夹

# 二、核心函数:处理单个班级(工作表)的数据
def process_class_sheet(sheet_name, sheet_data):
    """
    处理单个班级的Excel数据
    :param sheet_name: 工作表名(班级名)
    :param sheet_data: 该班级的DataFrame数据
    """
    # 1. 创建临时文件夹(每个班级单独的临时目录,避免冲突)
    class_temp_path = os.path.join(temp_word_path, sheet_name)
    try:
        os.makedirs(class_temp_path)
    except FileExistsError:
        pass
    
    # 2. 获取班级数据
    total_people = sheet_data.shape[0]
    print(f"\n===== 开始处理【{sheet_name}】班,共 {total_people} 人 =====")
    
    # 提取列数据(做空值处理,避免报错)
    sheet_data = sheet_data.fillna("")  # 空值替换为空字符串
    num = sheet_data["num"]
    name = sheet_data["name"]
    # school = sheet_data["school"]
    sex = sheet_data["sex"]
    classroom = sheet_data["classroom"].astype(str).str.replace('\n', '')  # 去掉换行符
    # y = sheet_data["y"]
    # m = sheet_data["m"]
    # d = sheet_data["d"]
    # ID = sheet_data["ID"]
    # SH = sheet_data["SH"]
    # FSH = sheet_data["FSH"]
    # M = sheet_data["M"]
    # F = sheet_data["F"]
    # dis = sheet_data["dis"]
    # nation = sheet_data["nation"]
    
    # 3. 遍历生成单个Word和PDF
    pdf_files = []  # 存储该班级的所有PDF路径
    for i in range(total_people):
        try:
            # 构造渲染数据
            context = {
                "num": str(num.iloc[i]),
                "sex": sex.iloc[i],
                # "dis": dis.iloc[i],
                "name": name.iloc[i],
                # "school": school.iloc[i],
                "classroom": classroom.iloc[i],
                # "y": y.iloc[i],
                # "m": m.iloc[i],
                # "d": d.iloc[i],
                # "ID": ID.iloc[i],
                # "SH": SH.iloc[i],
                # "FSH": FSH.iloc[i],
                # "M": M.iloc[i],
                # "F": F.iloc[i],
                # "nation": nation.iloc[i],
            }
            
            # 加载模板
            if l == 1:
                tpl = DocxTemplate(zpath + '打印单字(有背景).docx')
            else:
                tpl = DocxTemplate(zpath + '打印单字(无背景).docx')
            
            # 生成Word
            word_file = os.path.join(class_temp_path, f'{num.iloc[i]:02}.docx')
            tpl.render(context)
            tpl.save(word_file)
            
            # 转换为PDF
            pdf_file = os.path.join(class_temp_path, f'{num.iloc[i]:02}.pdf')
            convert(word_file, pdf_file)
            pdf_files.append(pdf_file)
            
            print(f"  已生成:{sheet_name}班 - {num.iloc[i]:02}.pdf")
            time.sleep(1)  # 避免转换过快报错
        except Exception as e:
            print(f"  ❌ 处理第{i+1}条数据失败:{str(e)}")
            continue
    
    # 4. 合并该班级的所有PDF(保存到“合并PDF”文件夹)
    if pdf_files:
        # 创建“合并PDF”文件夹(确保存在)
        os.makedirs(pdf_output_dir, exist_ok=True)
        
        pdf_files.sort()  # 按序号排序
        merger = PdfFileMerger()
        for pdf in pdf_files:
            try:
                merger.append(pdf)
            except:
                pass
        
        # 定义最终PDF文件名(保存到“合并PDF”文件夹)
        bg_flag = "有背景图" if l == 1 else "无背景图"
        final_pdf_name = f"{sheet_name}班验血单({total_people}人)({bg_flag}).pdf"
        final_pdf_path = os.path.join(pdf_output_dir, final_pdf_name)
        
        # 保存合并后的PDF
        merger.write(final_pdf_path)
        merger.close()
        print(f"\n✅ 【{sheet_name}】班合并PDF完成:{final_pdf_path}")
    else:
        print(f"\n⚠️  【{sheet_name}】班无有效PDF文件,跳过合并")
    
    # 5. 删除该班级的临时文件夹
    try:
        shutil.rmtree(class_temp_path)
        print(f"🗑️  已删除【{sheet_name}】班临时文件")
    except:
        print(f"⚠️  【{sheet_name}】班临时文件删除失败,请手动清理:{class_temp_path}")

# 三、主流程:遍历Excel所有工作表
if __name__ == "__main__":
    # 1. 检查Excel文件是否存在
    excel_path = zpath + '04园园通学生验血单_班级合并版.xlsx'
    if not os.path.exists(excel_path):
        print(f"❌ 未找到Excel文件:{excel_path}")
        exit()
    
    # 2. 提前创建“合并PDF”文件夹(确保目录存在)
    os.makedirs(pdf_output_dir, exist_ok=True)
    print(f"📂 最终PDF将保存至:{pdf_output_dir}")
    
    # 3. 获取所有工作表名称
    excel_file = pd.ExcelFile(excel_path)
    sheet_names = excel_file.sheet_names
    print(f"📋 找到Excel工作表(班级):{sheet_names}")
    
    # 4. 遍历每个工作表(班级)处理
    for sheet in sheet_names:
        # 读取该工作表的数据
        df = pd.read_excel(excel_path, sheet_name=sheet)
        # 处理该班级
        process_class_sheet(sheet, df)
    
    # 5. 删除总临时文件夹
    try:
        shutil.rmtree(temp_word_path)
        print(f"\n🗑️  已删除总临时文件夹:{temp_word_path}")
    except:
        print(f"\n⚠️  总临时文件夹删除失败,请手动清理:{temp_word_path}")
    
    print(f"\n🎉 所有班级PDF生成完成!所有PDF文件已保存至:{pdf_output_dir}")

因为20个班级,每个班级20-35人之间,所以生成时间很长。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐