'''
大班四大教育4个主题,每个主题4个 共20个 - 完整工作流
1. 生成占位符Excel
2. 根据Excel生成Word文档
3. 处理Word文档(aaa转手动换行符、空格缩进)
4. 打包成RAR
豆包、Deepseek、阿夏
20260909
'''
import pandas as pd
from docxtpl import DocxTemplate
from docx import Document
from docx.enum.text import WD_BREAK
from docx.shared import Pt
from docx.oxml.ns import qn
import os
import sys
import subprocess
import zipfile
import shutil
# ========== 全局路径配置(只需修改这里) ==========
# 根路径 - 所有文件都基于这个路径
BASE_PATH = r'D:\Python最终内容\20260906四大教育\总园\总园大班四大教育'
# 班级名称
CLASS_NAME = "总园大班"
# 教育主题
EDU_THEME = "全部主题"
date='20260909'
# ==============================================
# 根据基础路径生成所有子路径
EXCEL_PATH = os.path.join(BASE_PATH, f"{date}{CLASS_NAME}四大教育({EDU_THEME}).xlsx")
TEMPLATE_PATH = os.path.join(BASE_PATH, f"01 {CLASS_NAME}四大教育模版.docx")
OUTPUT_FOLDER = os.path.join(BASE_PATH, f"01 ({CLASS_NAME})四大教育_{EDU_THEME}")
PROCESSED_FOLDER = os.path.join(BASE_PATH, f"01 ({CLASS_NAME})四大教育_{EDU_THEME}最终")
RAR_PATH = os.path.join(BASE_PATH, f"01 ({CLASS_NAME})四大教育_{EDU_THEME}最终.rar")
ZIP_PATH = os.path.join(BASE_PATH, f"01 ({CLASS_NAME})四大教育_{EDU_THEME}最终.zip")
# 设置控制台编码
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# def step1_generate_excel():
# """第一步:生成占位符Excel文件"""
# print("\n" + "="*60)
# print("第一步:生成占位符Excel文件")
# print("="*60)
# # 创建数据框
# df = pd.DataFrame(columns=[
# 'garden','name0','name1','grade','name2','year', 'month', 'topic', 'goal', 'procedure', 'reflection','ts',
# ])
# # 保存为Excel文件
# df.to_excel(EXCEL_PATH, index=False)
# print(f"✅ Excel文件已生成:{EXCEL_PATH}")
# input('\n请先在Excel中填入AI生成的内容,然后按回车键继续...')
# return True
def step2_generate_word_from_excel():
"""第二步:根据Excel生成Word文档"""
print("\n" + "="*60)
print("第二步:根据Excel生成Word文档")
print("="*60)
# 创建输出文件夹
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
# 存储生成的文件路径
generated_files = []
# 检查关键文件是否存在
if not os.path.exists(EXCEL_PATH):
print(f"❌ 错误:安排表不存在 - {EXCEL_PATH}")
return False
if not os.path.exists(TEMPLATE_PATH):
print(f"❌ 错误:模板文件不存在 - {TEMPLATE_PATH}")
return False
# 读取安排表
schedule = pd.read_excel(EXCEL_PATH)
# 确保列是字符串类型,避免nan显示
new_columns = ['garden','name0','name1','grade','name2','year', 'month', 'topic', 'goal', 'procedure', 'reflection', 'classroom', 'teachers','ts']
for col in new_columns:
if col in schedule.columns:
schedule[col] = schedule[col].astype(str).replace('nan', '')
# 设置默认值
if 'year' not in schedule.columns:
schedule['year'] = '2026'
if 'month' not in schedule.columns:
schedule['month'] = '3'
if 'garden' not in schedule.columns:
schedule['garden'] = '大班'
if 'classroom' not in schedule.columns:
schedule['classroom'] = CLASS_NAME
if 'teachers' not in schedule.columns:
schedule['teachers'] = '待定'
# 保存安排表(覆盖原文件)
schedule.to_excel(EXCEL_PATH, index=False)
print(f"✅ 已保存安排表")
print(f"📊 安排表共有 {len(schedule)} 行数据")
# 验证模板文件
try:
test_doc = Document(TEMPLATE_PATH)
print("✅ 模板文件验证通过,开始生成文档...")
except Exception as e:
print(f"❌ 模板文件无效: {e}")
return False
# 批量生成文档
for index, row in schedule.iterrows():
try:
tpl = DocxTemplate(TEMPLATE_PATH)
# 获取班级名
classroom = str(row['classroom']) if pd.notna(row['classroom']) else CLASS_NAME
original_classroom = classroom.strip()
# 格式化班级名称
classroom_str = original_classroom
if len(classroom_str) >= 3 and classroom_str[1].isdigit():
formatted_class = f"{classroom_str[0]}({classroom_str[1]}){classroom_str[2:]}"
elif '(' in classroom_str or '(' in classroom_str:
formatted_class = classroom_str
else:
formatted_class = classroom_str
# 获取教师姓名
teacher_name = str(row['teachers']) if pd.notna(row['teachers']) and str(row['teachers']) != 'nan' else "待定"
# 构建模板上下文
context = {
"grade": str(row['grade']) if pd.notna(row['grade']) and str(row['grade']) != 'nan' else '',
"name0": str(row['name0']) if pd.notna(row['name0']) and str(row['name0']) != 'nan' else '',
"name1": str(row['name1']) if pd.notna(row['name1']) and str(row['name1']) != 'nan' else '',
"name2": str(row['name2']) if pd.notna(row['name2']) and str(row['name2']) != 'nan' else '',
"year": str(row['year']) if pd.notna(row['year']) and str(row['year']) != 'nan' else '2026',
"month": str(row['month']) if pd.notna(row['month']) and str(row['month']) != 'nan' else '3',
"garden": str(row['garden']) if pd.notna(row['garden']) and str(row['garden']) != 'nan' else '大班',
"topic": str(row['topic']) if pd.notna(row['topic']) and str(row['topic']) != 'nan' else '',
"goal": str(row['goal']) if pd.notna(row['goal']) and str(row['goal']) != 'nan' else '',
"procedure": str(row['procedure']) if pd.notna(row['procedure']) and str(row['procedure']) != 'nan' else '',
"reflection": str(row['reflection']) if pd.notna(row['reflection']) and str(row['reflection']) != 'nan' else '',
"classroom": formatted_class,
"teachers": teacher_name,
"date": str(row['date']) if 'date' in row and pd.notna(row['date']) else '',
"week": str(row['week']) if 'week' in row and pd.notna(row['week']) else '',
"leader": str(row['leader']) if 'leader' in row and pd.notna(row['leader']) else '',
"ts": str(row['ts']) if 'ts' in row and pd.notna(row['ts']) else '',
}
# 渲染模板
tpl.render(context)
# 保存文档到临时文件
temp_path = os.path.join(OUTPUT_FOLDER, f"temp_{index}.docx")
tpl.save(temp_path)
# 打开文档进行样式修复
doc = Document(temp_path)
# 修复反思部分的字体大小(确保是小四12磅)
modified = False
if doc.tables:
for table in doc.tables:
if len(table.rows) >= 6:
# 反思部分在第6行(索引5)
reflection_row = table.rows[5]
# 反思部分通常是合并单元格,所以取第一个单元格
if len(reflection_row.cells) >= 1:
reflection_cell = reflection_row.cells[0]
for paragraph in reflection_cell.paragraphs:
for run in paragraph.runs:
# 设置为小四(12磅)
run.font.size = Pt(12)
# 确保是宋体
run.font.name = '宋体'
# 中文字体设置
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
modified = True
if modified:
print(f" ✅ 修复反思部分字体为小四宋体")
# 生成文件名
month_str = str(row['month']) if pd.notna(row['month']) and str(row['month']) != 'nan' else str(index+1)
# try:
# month_formatted = f"{int(month_str):02d}月"
# except:
month_formatted = f"{month_str}月"
topic_short = str(row['name0'])[:] if pd.notna(row['name0']) and str(row['name0']) != 'nan' else '四大教育'
# filename = f"{month_formatted}_{kindgarden}_{original_classroom}_四大教育_{topic_short}_{CLASS_NAME[2]}()班.docx"
# 客户要求样式大班XX教育(3月).docx
filename = f"{topic_short}({month_formatted}).docx"
file_full_path = os.path.join(OUTPUT_FOLDER, filename)
# 保存修复后的文档
doc.save(file_full_path)
# 删除临时文件
if os.path.exists(temp_path):
os.remove(temp_path)
generated_files.append(file_full_path)
print(f"✅ 生成文档: {filename}")
except Exception as e:
print(f"❌ 生成第{index+1}行文档失败: {str(e)}")
import traceback
traceback.print_exc()
continue
print(f"\n📊 文档生成完成!共生成 {len(generated_files)} 个文档")
print(f"📂 文档保存在: {OUTPUT_FOLDER}")
return True
def process_word_document(input_path, output_path):
"""处理单个Word文档(aaa转手动换行符、空格缩进 + 修复字体大小)"""
try:
doc = Document(input_path)
modified = False
# 修复反思部分的字体大小(确保是小四)
if doc.tables:
for table in doc.tables:
if len(table.rows) >= 6:
reflection_row = table.rows[5]
if len(reflection_row.cells) >= 1:
reflection_cell = reflection_row.cells[0]
for paragraph in reflection_cell.paragraphs:
for run in paragraph.runs:
# 设置为小四(12磅)
if run.font.size != Pt(12):
run.font.size = Pt(12)
run.font.name = '宋体'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
modified = True
for table in doc.tables:
for row_idx, row in enumerate(table.rows, 1):
for col_idx, cell in enumerate(row.cells, 1):
# 收集所有需要处理的段落
paragraphs_to_process = []
for para_idx, para in enumerate(cell.paragraphs):
if 'aaa' in para.text:
paragraphs_to_process.append((para_idx, para))
# 添加空格缩进(第2列第7行)
if col_idx == 2 and row_idx == 7:
if para.text and not para.text.startswith(''):
para.text = '' + para.text
modified = True
# 处理包含aaa的段落
for para_idx, para in paragraphs_to_process:
# 保存原格式
runs_info = []
for run in para.runs:
runs_info.append({
'text': run.text,
'bold': run.bold,
'italic': run.italic,
'underline': run.underline,
'font_name': run.font.name,
'font_size': run.font.size,
'color': run.font.color.rgb if run.font.color else None
})
# 按aaa分割文本
full_text = para.text
parts = full_text.split('aaa')
# 清除原段落
para.clear()
# 重新添加分段内容
for i, part in enumerate(parts):
if part.strip(): # 如果有内容
# 创建新run并尝试保留原格式
run = para.add_run(part)
if runs_info:
# 使用第一个run的格式(简化处理)
run.bold = runs_info[0]['bold']
run.italic = runs_info[0]['italic']
run.underline = runs_info[0]['underline']
if runs_info[0]['font_name']:
run.font.name = runs_info[0]['font_name']
if runs_info[0]['font_size']:
run.font.size = runs_info[0]['font_size']
if runs_info[0]['color']:
run.font.color.rgb = runs_info[0]['color']
# 如果不是最后一段,添加手动换行符
if i < len(parts) - 1:
para.add_run().add_break(WD_BREAK.LINE)
modified = True
# 保存文档
doc.save(output_path)
# 如果保存成功
if os.path.exists(output_path):
return True, modified
else:
return False, False
except Exception as e:
print(f" 错误: {str(e)}")
import traceback
traceback.print_exc()
return False, False
def step3_process_documents():
"""第三步:处理生成的Word文档"""
print("\n" + "="*60)
print("第三步:处理Word文档(aaa转手动换行符、空格缩进)")
print("="*60)
# 创建输出文件夹
os.makedirs(PROCESSED_FOLDER, exist_ok=True)
print(f"输入文件夹: {OUTPUT_FOLDER}")
print(f"输出文件夹: {PROCESSED_FOLDER}")
print("-" * 60)
# 检查输入文件夹
if not os.path.exists(OUTPUT_FOLDER):
print(f"❌ 错误: 输入文件夹不存在!")
return False
# 获取所有docx文件
docx_files = [f for f in os.listdir(OUTPUT_FOLDER)
if f.lower().endswith('.docx')]
if not docx_files:
print(f"⚠️ 没有找到docx文件!")
return False
print(f"\n✅ 找到 {len(docx_files)} 个docx文件")
print("-" * 60)
# 处理文件
success = 0
for i, filename in enumerate(docx_files, 1):
print(f"[{i}/{len(docx_files)}] {filename}")
input_path = os.path.join(OUTPUT_FOLDER, filename)
output_filename = filename.replace('.docx', '.doc')
output_path = os.path.join(PROCESSED_FOLDER, output_filename)
result, modified = process_word_document(input_path, output_path)
if result:
if modified:
print(f" ✅ 已处理并保存")
else:
print(f" ⚠️ 无修改内容,已复制")
success += 1
else:
print(f" ❌ 处理失败")
print()
print(f"\n✅ 处理完成!成功处理 {success}/{len(docx_files)} 个文件")
print(f"📂 处理后文档保存在: {PROCESSED_FOLDER}")
return True
def check_winrar_installed():
"""检查WinRAR是否安装"""
possible_paths = [
r"C:\Program Files\WinRAR\WinRAR.exe",
r"C:\Program Files (x86)\WinRAR\WinRAR.exe"
]
for path in possible_paths:
if os.path.exists(path):
return path
return None
def step4_pack_to_rar():
"""第四步:打包成RAR文件"""
print("\n" + "="*60)
print("第四步:打包成RAR文件")
print("="*60)
# 检查最终文件夹是否存在
if not os.path.exists(PROCESSED_FOLDER):
print(f"❌ 错误:最终文件夹不存在 - {PROCESSED_FOLDER}")
return False
# 检查文件夹内是否有文件
files = os.listdir(PROCESSED_FOLDER)
if not files:
print(f"⚠️ 警告:最终文件夹为空,没有文件可打包")
return False
print(f"📁 待打包文件夹: {PROCESSED_FOLDER}")
print(f"📊 文件夹内共有 {len(files)} 个文件")
# 方法1:尝试使用WinRAR(如果已安装)
winrar_path = check_winrar_installed()
if winrar_path:
try:
print("🔄 正在使用WinRAR打包...")
# 构建WinRAR命令
cmd = [
winrar_path,
'a', # 添加到压缩文件
'-ep1', # 不保存路径信息
'-r', # 递归子目录
RAR_PATH,
f'{PROCESSED_FOLDER}\\*.*'
]
# 执行命令
result = subprocess.run(cmd, capture_output=True, text=True)
if os.path.exists(RAR_PATH):
size = os.path.getsize(RAR_PATH) / 1024 # KB
print(f"✅ 打包成功!")
print(f"📦 RAR文件: {RAR_PATH}")
print(f"📏 文件大小: {size:.2f} KB")
return True
else:
print("⚠️ WinRAR打包失败,尝试使用ZIP替代...")
return create_zip_backup()
except Exception as e:
print(f"⚠️ WinRAR打包出错: {str(e)}")
print("⚠️ 尝试使用ZIP替代...")
return create_zip_backup()
else:
print("⚠️ 未找到WinRAR,将使用ZIP格式打包...")
return create_zip_backup()
def create_zip_backup():
"""创建ZIP备份(当WinRAR不可用时)"""
try:
print("🔄 正在创建ZIP压缩文件...")
# 创建ZIP文件
with zipfile.ZipFile(ZIP_PATH, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(PROCESSED_FOLDER):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, PROCESSED_FOLDER)
zipf.write(file_path, arcname)
if os.path.exists(ZIP_PATH):
size = os.path.getsize(ZIP_PATH) / 1024 # KB
print(f"✅ ZIP打包成功!")
print(f"📦 ZIP文件: {ZIP_PATH}")
print(f"📏 文件大小: {size:.2f} KB")
# 询问是否要重命名为RAR(虽然实际上是ZIP)
print("\n注:由于系统没有安装WinRAR,已生成ZIP格式文件")
print("如需RAR格式,请手动安装WinRAR后重新运行此步骤")
return True
else:
print("❌ ZIP打包失败")
return False
except Exception as e:
print(f"❌ ZIP打包出错: {str(e)}")
return False
def main():
"""主函数 - 执行完整工作流"""
print("\n" + "="*60)
print("大班四大教育 - 完整工作流")
print("="*60)
print(f"基础路径: {BASE_PATH}")
print(f"班级: {CLASS_NAME}")
print(f"主题: {EDU_THEME}")
print("="*60)
# 自动执行完整工作流(不需要用户选择)
print("\n开始执行完整工作流...")
# # 第一步:生成Excel
# if not step1_generate_excel():
# print("❌ 第一步失败,程序终止")
# return
# 第二步:生成Word文档
if not step2_generate_word_from_excel():
print("❌ 第二步失败,程序终止")
return
# 第三步:处理文档
if not step3_process_documents():
print("❌ 第三步失败,程序终止")
return
# 第四步:打包成RAR
step4_pack_to_rar()
print("\n" + "="*60)
print("🎉 完整工作流执行完成!")
print("="*60)
print(f"\n📂 生成的文件位置:")
print(f"1. Excel占位符文件: {EXCEL_PATH}")
print(f"2. 原始Word文档: {OUTPUT_FOLDER}")
print(f"3. 处理后的Word文档: {PROCESSED_FOLDER}")
# 显示打包文件
if os.path.exists(RAR_PATH):
print(f"4. RAR压缩包: {RAR_PATH}")
elif os.path.exists(ZIP_PATH):
print(f"4. ZIP压缩包: {ZIP_PATH}")
print("="*60)
if __name__ == "__main__":
main()