Files
playwrite/convert_excel.py

214 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
将 Excel 报表数据转换为数据库记录形式
"""
import pandas as pd
import openpyxl
from typing import List, Dict
import os
def parse_sheet(ws) -> List[Dict]:
"""
解析一个工作表,返回所有订单的数据
每个订单包含:
- order_info: 订单头信息
- materials: 物料数据列表
"""
orders = []
all_rows = list(ws.iter_rows(values_only=True))
# 查找所有空行,用于分割订单
empty_rows = [i for i, row in enumerate(all_rows) if all(cell is None or str(cell).strip() == "" for cell in row)]
print(f"检测到空行索引: {empty_rows}")
print(f"总行数: {len(all_rows)}")
# 逐行扫描,按订单结构解析
i = 0
while i < len(all_rows):
row = all_rows[i]
# 检查是否是订单标题行
if row and '离散备料计划' in str(row[0]):
print(f"\n在行 {i + 1} 发现订单标题")
# 解析订单头信息接下来的4行
order_info = {}
for j in range(1, 5):
if i + j < len(all_rows) and all_rows[i + j]:
parse_header_row(all_rows[i + j], order_info)
print(f"订单头信息: {order_info}")
# 跳过空行,找到表格标题行
table_row = i + 5
while table_row < len(all_rows) and (not all_rows[table_row] or not all_rows[table_row][0]):
table_row += 1
# 检查是否是表格标题行
if table_row < len(all_rows) and all_rows[table_row] and all_rows[table_row][0] == '序号':
print(f"在行 {table_row + 1} 发现表格标题")
# 解析物料数据
materials = []
footer_info = {} # 页脚信息
data_row = table_row + 1
while data_row < len(all_rows) and all_rows[data_row]:
# 检查是否是页脚信息(制单人、打印人)
if all_rows[data_row][0] and ('制单人' in str(all_rows[data_row][0]) or '打印人' in str(all_rows[data_row][0])):
print(f"在行 {data_row + 1} 发现页脚信息")
# 解析页脚信息
parse_header_row(all_rows[data_row], footer_info)
# 检查下一行是否也是页脚信息
if data_row + 1 < len(all_rows) and all_rows[data_row + 1]:
parse_header_row(all_rows[data_row + 1], footer_info)
print(f"页脚信息: {footer_info}")
break
# 检查是否是物料行(第一列是数字)
if all_rows[data_row][0] and str(all_rows[data_row][0]).strip().isdigit():
material_row = all_rows[data_row]
material = {
'序号': material_row[0],
'材料编码': material_row[1],
'材料名称': material_row[2],
'规格': material_row[3],
'型号': material_row[4],
'图号': material_row[5],
'物料材质': material_row[6],
'计划数量': material_row[7],
'单位': material_row[8],
'需用日期': material_row[9],
'发料仓库': material_row[10],
'单位用量': material_row[11],
'累计出库数量': material_row[12],
}
materials.append(material)
print(f" 添加物料: {material['材料编码']} - {material['材料名称']}")
data_row += 1
print(f"共解析到 {len(materials)} 条物料数据")
orders.append({
'order_info': {**order_info, **footer_info},
'materials': materials
})
i += 1
return orders
def parse_header_row(row: tuple, info: Dict):
"""
解析订单头信息的一行(字段名和值交错排列)
"""
i = 0
while i < len(row):
cell = row[i]
if cell and str(cell).strip() and '' in str(cell):
# 找到字段名
field_name = str(cell).replace('', '').strip()
# 重命名冲突字段
field_name_mapping = {
'计划数量': '产品计划数量',
'单位': '产品单位'
}
if field_name in field_name_mapping:
field_name = field_name_mapping[field_name]
# 跳过空单元格,找到第一个非字段名的值
j = i + 1
while j < len(row) and (not row[j] or not str(row[j]).strip() or '' in str(row[j])):
j += 1
if j < len(row) and row[j] and not '' in str(row[j]):
info[field_name] = str(row[j]).strip()
# 跳过已处理的值,继续找下一个字段名
i = j + 1
else:
i += 1
def convert_to_dataframe(orders: List[Dict]) -> pd.DataFrame:
"""
将订单数据转换为扁平化的 DataFrame
"""
all_records = []
for order in orders:
order_info = order['order_info']
materials = order['materials']
for material in materials:
record = {
**order_info,
**material
}
all_records.append(record)
return pd.DataFrame(all_records)
def main():
input_file = "data/导出文件.xlsx"
output_file = "data/导出文件_转换.xlsx"
# 如果输出文件存在,先删除
if os.path.exists(output_file):
try:
os.remove(output_file)
except PermissionError:
print(f"警告: 无法删除 {output_file},可能文件被其他程序打开")
output_file = "data/导出文件_转换_new.xlsx"
print("=" * 80)
print("开始转换 Excel 数据")
print("=" * 80)
# 读取工作表
wb = openpyxl.load_workbook(input_file)
ws = wb.active
# 解析订单数据
orders = parse_sheet(ws)
print(f"\n\n共解析到 {len(orders)} 个订单")
# 打印每个订单的摘要
for i, order in enumerate(orders, 1):
order_info = order['order_info']
materials = order['materials']
print(f"\n订单 {i}:")
print(f" 备料计划单号: {order_info.get('备料计划单号', 'N/A')}")
print(f" 来源单号: {order_info.get('来源单号', 'N/A')}")
print(f" 产品编码: {order_info.get('产品编码', 'N/A')}")
print(f" 产品名称: {order_info.get('产品名称', 'N/A')}")
print(f" 计划数量: {order_info.get('计划数量', 'N/A')}")
print(f" 物料数量: {len(materials)}")
# 转换为 DataFrame
df = convert_to_dataframe(orders)
print(f"\n转换后的数据形状: {df.shape}")
if not df.empty:
print(f"列名: {list(df.columns)}")
# 保存为 Excel
df.to_excel(output_file, index=False)
print(f"\n数据已保存到: {output_file}")
# 显示前几行数据
print("\n数据预览:")
pd.set_option('display.max_columns', None)
pd.set_option('display.width', 200)
pd.set_option('display.max_colwidth', 30)
print(df.head(20))
pd.reset_option('display.max_columns')
pd.reset_option('display.width')
pd.reset_option('display.max_colwidth')
else:
print("警告: 没有数据可保存")
if __name__ == "__main__":
main()