【办公类-112-08】20261009园园通信息合并配学号拆班(数据更新,用年月日期区分版本)

一、背景需求

10月的插班生信息进入园园通了,我需要更新班级点名册,把这些插班生班级的EXCEL发给班主任

每月都有孩子转入转出,原来写的园园通信息合并配学号拆班的代码每个月都要生成一次(发给插班生的班级),但是都是同一个文件名(20260806)所以每次刷新会替换掉excle内部的数据,转出孩子的信息就不见了。

【办公类-112-07】20260828园园通信息合并配学号拆班(园园通市级编号在excel中的位置调整,调整最适合列宽)https://mp.csdn.net/mp_blog/creation/editor/164063926

因为更新后,转走的孩子的信息就没有了,所以这次我想保留每个月的更新数据。

二、下载相关数据

1.园园通数据(所有孩子的所有信息),excle文件名并添加日期

2.班级信息表(匹配序号)

把更新后的班级点名册下载,文件名增加日期

三、代码补充日期

1.更改班级的汉字为数字

python 复制代码
'''
2025082520250825_2025_学年出入所(园)儿童基本情况登记  表一空  01
1、下载园园通新生名册.xls,把两个新生名册合并在一起
2、部分插班生信息手动添加
deepseek,阿夏
20261009
'''

import pandas as pd
import os
import re

date='20261009'

def merge_and_format_excel():
    # 定义文件路径
    path = r'D:\Python最终内容\20260902 2026园园通幼儿信息提取配学号拆班发班主任'
    # 设置路径
    folder_path = path + r"\00 园园通下载"
    output_path = path + fr'\02 {date}园园通全部班级班信息合并.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)
        df = pd.read_excel(file_path)
        dfs.append(df)
        print(f"已读取文件: {file}")
    
    # 合并数据
    merged_df = pd.concat(dfs, ignore_index=True)
    
    # 将列名"ID"改为"市登记编号"(如果存在)
    if 'ID' in merged_df.columns:
        merged_df.rename(columns={'ID': '市登记编号'}, inplace=True)
        print("已将列名'ID'改为'市登记编号'")
    else:
        print("未找到名为'ID'的列,跳过重命名")
    
    # 格式化班级列(统一改为"托1班"、"小1班"、"中1班"、"大1班"格式)
    def format_class_name(class_name):
        if pd.isna(class_name):
            return class_name
        
        class_str = str(class_name)
        
        # 定义班级类型映射
        class_type_map = {
            '托': '托', '托大': '托',
            '小': '小', 
            '中': '中',
            '大': '大'
        }
        
        # 中文数字到阿拉伯数字的映射
        chinese_to_arabic = {
            '一': '1', '二': '2', '三': '3', '四': '4', '五': '5',
            '六': '6', '七': '7', '八': '8', '九': '9', '十': '10',
            '十一': '11', '十二': '12'
        }
        
        # 先提取班级类型
        class_type = None
        class_num = None
        
        # 匹配各种格式
        # 1. 匹配"托大X班"、"小X班"、"中X班"、"大X班"格式
        match = re.search(r'(托大|小|中|大)([一二三四五六七八九十\d]+)班', class_str)
        if match:
            class_type = match.group(1)
            class_num = match.group(2)
        else:
            # 2. 匹配"大一班"、"托一班"格式
            match = re.search(r'(托|小|中|大)([一二三四五六七八九十])班', class_str)
            if match:
                class_type = match.group(1)
                class_num = match.group(2)
            else:
                # 3. 匹配"大班一班"格式
                match = re.search(r'(托大|小|中|大)班([一二三四五六七八九十]+)班', class_str)
                if match:
                    class_type = match.group(1)
                    class_num = match.group(2)
                else:
                    # 4. 匹配"一班大班"格式
                    match = re.search(r'([一二三四五六七八九十]+)班(托大|小|中|大)班', class_str)
                    if match:
                        class_num = match.group(1)
                        class_type = match.group(2)
                    else:
                        # 5. 匹配只有班级类型没有班号的情况
                        match = re.search(r'(托大|小|中|大)班', class_str)
                        if match:
                            class_type = match.group(1)
                            class_num = '1'  # 默认班号为1
                        else:
                            # 6. 匹配只有班级类型没有"班"字的情况
                            match = re.search(r'(托大|小|中|大)', class_str)
                            if match:
                                class_type = match.group(1)
                                class_num = '1'  # 默认班号为1
        
        if class_type:
            # 标准化班级类型
            if class_type == '托大':
                std_type = '托'
            else:
                std_type = class_type
            
            # 转换班号
            if class_num:
                if class_num in chinese_to_arabic:
                    num = chinese_to_arabic[class_num]
                else:
                    num = class_num
                return f"{std_type}{num}班"
        
        # 如果没有匹配到任何模式,尝试直接提取数字
        # 检查是否已经是"托1班"这样的格式
        match = re.match(r'([托小中大])(\d+)班', class_str)
        if match:
            return class_str  # 已经是正确格式
        
        # 最后尝试:如果字符串中包含"托"、"小"、"中"、"大",且包含数字
        for t in ['托', '小', '中', '大']:
            if t in class_str:
                # 提取所有数字
                nums = re.findall(r'\d+', class_str)
                if nums:
                    return f"{t}{nums[0]}班"
        
        # 如果还是无法处理,返回原值
        print(f"警告:无法识别的班级格式 - {class_str}")
        return class_str
    
    # 查找包含班级信息的列
    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("未检测到班级列,跳过格式转换")
    
    # 保存合并后的文件
    merged_df.to_excel(output_path, index=False)
    print(f"文件已保存至: {output_path}")
    print(f"合并完成,共{len(merged_df)}条记录")
    
    # 显示一些样本数据以供验证
    print("\n前10条数据的班级信息:")
    if class_columns:
        for class_col in class_columns:
            print(f"\n{class_col}列的前10个值:")
            sample_values = merged_df[class_col].head(10).tolist()
            for i, val in enumerate(sample_values, 1):
                print(f"  {i}. {val}")
    else:
        print("未找到班级列")
    
    # 显示所有列名
    print("\n合并后的所有列名:")
    print(merged_df.columns.tolist())

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

生成文件名有日期(内部知识班级变成了数字)

2、拆分班级(日期)文件夹

python 复制代码
'''
20250825_2025_学年出入所(园)儿童基本情况登记  表一空  01
1、用合并xls与班级信息表的学号匹配,按学号排队
2、按班级拆分成一个个班级,最适合列宽一行(非自动换行)
3、按指定的列名顺序重新排列列的位置
20261009
'''

import pandas as pd
import os
from openpyxl.utils import get_column_letter
from openpyxl.styles import Alignment
from openpyxl.styles import numbers

# 设置路径
path = r'D:\Python最终内容\20260902 2026园园通幼儿信息提取配学号拆班发班主任'

date='20261009'

# 定义目标列名顺序
target_columns = [
    '学号', '班级', '姓名', '市登记编号',  '性别','出生地', '证件类型', '证件号码', 
    '姓名拼音', '曾用名/英文名', '出生日期', '年级', '班级名称', '入园日期', 
    '就读方式', '国籍/地区', '健康状况', '血型', '户口性质', '非农业户口类型', 
    '港澳台侨外', '民族', '出生地-省份', '籍贯-省份', '是否人户一致', 
    '户口所在地-省份', '户口所在地-城市', '户口所在地-区县', '户口所在地-街道', 
    '户口所在地-居委', '户口所在地-详细地址', '现居地-省份', '现居地-城市', 
    '现居地-区县', '现居地-街道', '现居地-居委', '现居地邮政编码', '现居地-详细地址', 
    '监护人姓名', '监护人证件类型', '监护人证件号', '监护人手机', '与幼儿关系', 
    '是否监护人', '是否是第一监护人', '是否是军烈子女', '是否是优抚子女', 
    '是否是进城务工人员随迁子女', '是否是部队子女', '是否是孤儿', '是否是残疾幼儿', 
    '残疾幼儿类别', '更新时间', '机构所在地区', '机构ID', '机构名称', '办园点ID', 
    '办园点编码', '办园点名称', '班级昵称', '出生地-区县', '机构编码', '籍贯-区县', 
    '籍贯-城市', '幼儿ID', '创建时间', '班级ID', '版本号', '幼儿特长', 
    '户口所在地-居委-其他', '现居地-街道-其他', '户口所在地-街道-其他', 
    '现居地-居委-其他', '特异体质', '出生地-城市', '原幼儿编号', '班级排序'
]

def calculate_display_width(text):
    """
    计算字符串在Excel中的显示宽度
    中文字符占2个单位,英文字符和数字占1个单位
    """
    if text is None or pd.isna(text):
        return 0
    
    text = str(text)
    width = 0
    for char in text:
        # 判断是否为中文字符(包括中文标点)
        if '\u4e00' <= char <= '\u9fff' or char in ',。;:""''!?【】()《》':
            width += 2
        else:
            width += 1
    return width

def reorder_columns(df, target_columns):
    """
    按照目标列名顺序重新排列DataFrame的列
    """
    # 获取DataFrame中实际存在的列
    existing_columns = []
    missing_columns = []
    
    for col in target_columns:
        if col in df.columns:
            existing_columns.append(col)
        else:
            missing_columns.append(col)
    
    # 获取目标列中不存在的其他列(放在最后)
    other_columns = [col for col in df.columns if col not in existing_columns]
    
    if missing_columns:
        print(f"警告:以下目标列在数据中不存在,将被忽略:{missing_columns}")
    
    if other_columns:
        print(f"注意:以下额外列将放在最后:{other_columns}")
    
    # 重新排列列顺序
    return df[existing_columns + other_columns]

# 1. 读取园园通信息表,将所有列作为字符串读取,保持原始格式
df_main = pd.read_excel(path + fr'\02 {date}园园通全部班级班信息合并.xlsx', dtype=str)

# 2. 读取班级信息表的所有工作表,将所有列作为字符串读取
xls = pd.ExcelFile(path + fr'\01 {date}班级信息表.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)  # 所有列作为字符串读取
    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. 对完整匹配结果进行排序并保存
# 学号列转换为数值类型用于排序,但保存时保持原始文本格式
df_merged['学号_排序'] = pd.to_numeric(df_merged['学号'], errors='coerce')

# 先按班级排序(托1班、托2班、小1班、小2班...)
# 创建班级排序的映射关系
class_order = {
    '托1班': 1, '托2班': 2, '托3班': 3, '托4班': 4,
    '小1班': 11, '小2班': 12, '小3班': 13, '小4班': 14, '小5班': 15,
    '中1班': 21, '中2班': 22, '中3班': 23, '中4班': 24,'中5班': 25,'中6班': 26,
    '大1班': 31, '大2班': 32, '大3班': 33, '大4班': 34,'大5班': 35,
}

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

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

# 删除临时排序列(班级排序保留,因为它在目标列中)
df_merged_sorted = df_merged_sorted.drop(['学号_排序'], axis=1)

# 按照目标列顺序重新排列
df_merged_sorted = reorder_columns(df_merged_sorted, target_columns)

# 保存排序后的完整表格,并调整列宽
with pd.ExcelWriter(path + r'\03 匹配学号.xlsx', engine='openpyxl') as writer:
    df_merged_sorted.to_excel(writer, sheet_name='匹配结果', index=False)
    
    # 获取工作表并调整列宽
    worksheet = writer.sheets['匹配结果']
    
    # 计算每列的最大显示宽度
    for column in worksheet.columns:
        max_width = 0
        column_letter = get_column_letter(column[0].column)
        
        # 获取列标题和所有单元格的最大显示宽度
        for cell in column:
            try:
                # 计算单元格内容的显示宽度
                cell_width = calculate_display_width(cell.value)
                if cell_width > max_width:
                    max_width = cell_width
            except:
                pass
        
        # 设置列宽(添加一点额外空间)
        # Excel列宽单位是标准字符宽度,中文字符需要额外空间
        adjusted_width = max_width + 2  # 添加2个字符的边距
        # 限制最小宽度为8,最大宽度为100
        worksheet.column_dimensions[column_letter].width = min(max(adjusted_width, 8), 100)
    
    # 将所有单元格设置为文本格式
    for row in worksheet.iter_rows():
        for cell in row:
            cell.number_format = '@'  # 设置文本格式

# 7. 按班级拆分并保存为单独工作簿
# 创建输出文件夹
output_folder = path + fr'\04 拆分班级\{date}'
if not os.path.exists(output_folder):
    os.makedirs(output_folder)

# 按班级拆分
for class_name in df_merged['班级名称'].unique():
    # 筛选当前班级的数据
    class_df = df_merged[df_merged['班级名称'] == class_name]
    
    # 创建临时排序列用于排序
    class_df = class_df.copy()  # 避免警告
    class_df['学号_排序'] = pd.to_numeric(class_df['学号'], errors='coerce')
    
    # 对每个班级内的数据按学号排序
    class_df_sorted = class_df.sort_values(by='学号_排序')
    
    # 删除临时排序列
    class_df_sorted = class_df_sorted.drop('学号_排序', axis=1)
    
    # 按照目标列顺序重新排列
    class_df_sorted = reorder_columns(class_df_sorted, target_columns)
    
    # 创建文件名(去除可能存在的非法字符)
    safe_class_name = "".join([c for c in class_name if c not in r'\/:*?"<>|'])
    output_path = os.path.join(output_folder, f'05_{date}_园园通幼儿全部信息_{safe_class_name}.xlsx')
    
    # 保存为单独的工作簿,并调整列宽
    with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
        sheet_name = f'{class_name}园园通幼儿数据'
        # 工作表名称不能超过31个字符
        if len(sheet_name) > 31:
            sheet_name = sheet_name[:31]
        
        class_df_sorted.to_excel(writer, sheet_name=sheet_name, index=False)
        
        # 获取工作表并调整列宽
        worksheet = writer.sheets[sheet_name]
        
        # 计算每列的最大显示宽度
        for column in worksheet.columns:
            max_width = 0
            column_letter = get_column_letter(column[0].column)
            
            # 获取列标题和所有单元格的最大显示宽度
            for cell in column:
                try:
                    # 计算单元格内容的显示宽度
                    cell_width = calculate_display_width(cell.value)
                    if cell_width > max_width:
                        max_width = cell_width
                except:
                    pass
            
            # 设置列宽(根据最长字符的实际显示宽度)
            adjusted_width = max_width + 2  # 添加2个字符的边距
            # 限制最小宽度为8,最大宽度为100
            worksheet.column_dimensions[column_letter].width = min(max(adjusted_width, 8), 100)
        
        # 设置所有单元格为文本格式
        for row in worksheet.iter_rows():
            for cell in row:
                cell.number_format = '@'  # 设置所有单元格为文本格式

print("处理完成!")
print(f"1. 已生成完整匹配表: 02匹配学号(分园新生).xlsx(已按班级和学号排序,按指定列名顺序排列,所有单元格保持文本格式,根据实际显示宽度计算最适合列宽)")
print(f"2. 已按班级拆分保存到 {output_folder} 文件夹,每个班级内按学号排序,按指定列名顺序排列,所有单元格保持文本格式,根据实际显示宽度计算最适合列宽")

三、私发有插班生的班主任

标注日期后,就可以避免数据被替换,保留每个月的幼儿信息。

相关推荐
阿狗童鞋1 小时前
Python爬虫进阶实战指南
开发语言·爬虫·python
中原第一高手1 小时前
fofatoto 1.8.0 发布:启动即知新版本、中文进度面板与更干净的 Web 日志
python·网络安全·开源·资产测绘·fofa
APIshop1 小时前
淘宝详情接口全解析:从官方开放平台到第三方数据服务
java·python·api
老歌老听老掉牙1 小时前
两个平面旋转平移坐标系间的坐标变换关系
python·算法·平面·旋转·平移
FITA阿泽要努力1 小时前
第 1 周·第 3 讲|工具如何交给模型:工具定义、参数与结构化调用
服务器·数据库·python·agent
W.A委员会1 小时前
PID与PID整定
python·算法
蜗牛互联网2 小时前
Java HttpClient 调用 Gemini 图像理解与金额字段校验
java·开发语言·人工智能·后端·python
蜗牛互联网2 小时前
Python Responses API视觉输入与本地金额校验最小实现
java·开发语言·人工智能·后端·python
Python图像识别2 小时前
35-【2027毕设】YOLO11中草药检测识别系统 - Python完整源码+PyQt5界面+训练模型+数据集
python·qt·课程设计