Files
2026-01-25 15:05:03 +08:00

291 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Excel数据导入脚本
从docs/example.xls导入工程项目数据到数据库
"""
import sys
import xlrd
import mysql.connector
from mysql.connector import Error
from datetime import datetime
import os
# 配置
EXCEL_FILE = '../docs/example.xls'
DB_CONFIG = {
'host': 'localhost',
'user': 'root',
'password': 'rootpassword',
'database': 'project_manager',
'charset': 'utf8mb4'
}
def get_database_connection():
"""获取数据库连接"""
try:
connection = mysql.connector.connect(**DB_CONFIG)
return connection
except Error as e:
print(f'数据库连接错误: {e}', file=sys.stderr)
sys.exit(1)
def parse_date_value(cell_value, workbook):
"""解析Excel日期值"""
if cell_value == '':
return None
# 检查是否是日期类型
try:
date_tuple = xlrd.xldate_as_tuple(cell_value, workbook.datemode)
return f'{date_tuple[0]}-{date_tuple[1]:02d}-{date_tuple[2]:02d}'
except:
pass
# 检查特殊值
if cell_value in ['未到期', '可退质保金', '未开工']:
return None
# 尝试解析字符串日期
try:
date_obj = datetime.strptime(str(cell_value), '%Y-%m-%d')
return date_obj.strftime('%Y-%m-%d')
except:
pass
return None
def parse_number_value(cell_value):
"""解析数字值"""
if cell_value == '':
return None
try:
return float(cell_value)
except:
return None
def parse_int_value(cell_value):
"""解析整数值"""
num = parse_number_value(cell_value)
if num is not None:
return int(num)
return None
def parse_enum_value(cell_value, allowed_values):
"""解析枚举值"""
if cell_value == '':
return None
if cell_value in allowed_values:
return cell_value
return None
def clean_text_value(cell_value):
"""清理文本值"""
if cell_value == '':
return None
return str(cell_value).strip()
def import_projects_from_sheet(sheet, connection, cursor, sheet_name):
"""从工作表导入项目数据"""
print(f'\n正在导入工作表: {sheet_name}')
print(f'总行数: {sheet.nrows}')
# 获取管理员用户ID(用于created_by字段)
cursor.execute("SELECT id FROM users WHERE username='admin' LIMIT 1")
result = cursor.fetchone()
if not result:
print('错误: 未找到管理员用户', file=sys.stderr)
return False
admin_id = result[0]
success_count = 0
skip_count = 0
error_count = 0
# 从第6行开始(前5行是表头)
for row_idx in range(5, sheet.nrows):
try:
# 读取数据
row_data = [sheet.cell_value(row_idx, col_idx) for col_idx in range(min(61, sheet.ncols))]
# 检查是否是合计行或空行
if row_data[0] == '' or str(row_data[0]).strip() == '':
skip_count += 1
continue
# 准备SQL插入语句
sql = """
INSERT INTO projects (
project_no, power_contract_no, name, subitem_count, subitem_code,
total_investment, contract_amount, warranty_ratio, settlement_amount,
total_cost_estimated, voltage_level, engineering_type, owner_unit,
owner_contact, bidding_type, signing_date, start_date, planned_end_date,
actual_end_date, warranty_amount, warranty_expiry_date, actual_warranty_refund_date,
project_department, project_leader, payment_method, total_cost_control,
is_adjusted, labor_cost_control, labor_cost_planned, labor_cost_paid,
material_cost_control, material_cost_payable, material_cost_actual,
material_cost_paid, other_cost_control, other_cost_payable, other_cost_actual,
tax_amount, profit, actual_profit, cost_settlement_amount, cumulative_progress,
receivable_amount, invoice_amount, actual_receipt_amount, receipt_completion_rate,
payable_amount, actual_payment_amount, unpaid_amount, payment_completion_rate,
labor_debt_amount, settlement_cost_amount, settlement_labor_cost,
settlement_material_cost, settlement_other_cost, due_settlement_count,
unsettlement_count, problems, suggestions, remarks, status, created_by
) VALUES (
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
)
"""
# 准备参数
params = (
clean_text_value(row_data[1]), # project_no
clean_text_value(row_data[2]), # power_contract_no
clean_text_value(row_data[3]), # name
parse_int_value(row_data[4]), # subitem_count
clean_text_value(row_data[5]), # subitem_code
parse_number_value(row_data[6]), # total_investment
parse_number_value(row_data[7]), # contract_amount
parse_number_value(row_data[8]), # warranty_ratio
parse_number_value(row_data[9]), # settlement_amount
parse_number_value(row_data[10]), # total_cost_estimated
clean_text_value(row_data[11]), # voltage_level
clean_text_value(row_data[12]), # engineering_type
clean_text_value(row_data[13]), # owner_unit
clean_text_value(row_data[14]), # owner_contact
clean_text_value(row_data[15]), # bidding_type
parse_date_value(row_data[16], connection._connection.workbook), # signing_date
parse_date_value(row_data[17], connection._connection.workbook), # start_date
parse_date_value(row_data[18], connection._connection.workbook), # planned_end_date
parse_date_value(row_data[19], connection._connection.workbook), # actual_end_date
parse_number_value(row_data[20]), # warranty_amount
parse_date_value(row_data[21], connection._connection.workbook), # warranty_expiry_date
parse_date_value(row_data[22], connection._connection.workbook), # actual_warranty_refund_date
clean_text_value(row_data[23]), # project_department
clean_text_value(row_data[24]), # project_leader
clean_text_value(row_data[25]), # payment_method
parse_number_value(row_data[26]), # total_cost_control
parse_enum_value(row_data[27], ['是', '否']), # is_adjusted
parse_number_value(row_data[28]), # labor_cost_control
parse_number_value(row_data[29]), # labor_cost_planned
parse_number_value(row_data[30]), # labor_cost_paid
parse_number_value(row_data[31]), # material_cost_control
parse_number_value(row_data[32]), # material_cost_payable
parse_number_value(row_data[33]), # material_cost_actual
parse_number_value(row_data[34]), # material_cost_paid
parse_number_value(row_data[35]), # other_cost_control
parse_number_value(row_data[36]), # other_cost_payable
parse_number_value(row_data[37]), # other_cost_actual
parse_number_value(row_data[38]), # tax_amount
parse_number_value(row_data[39]), # profit
parse_number_value(row_data[40]), # actual_profit
parse_number_value(row_data[41]), # cost_settlement_amount
parse_number_value(row_data[42]), # cumulative_progress
parse_number_value(row_data[43]), # receivable_amount
parse_number_value(row_data[44]), # invoice_amount
parse_number_value(row_data[45]), # actual_receipt_amount
parse_number_value(row_data[46]), # receipt_completion_rate
parse_number_value(row_data[47]), # payable_amount
parse_number_value(row_data[48]), # actual_payment_amount
parse_number_value(row_data[49]), # unpaid_amount
parse_number_value(row_data[50]), # payment_completion_rate
parse_number_value(row_data[51]), # labor_debt_amount
parse_number_value(row_data[52]), # settlement_cost_amount
parse_number_value(row_data[53]), # settlement_labor_cost
parse_number_value(row_data[54]), # settlement_material_cost
parse_number_value(row_data[55]), # settlement_other_cost
parse_int_value(row_data[56]), # due_settlement_count
parse_int_value(row_data[57]), # unsettlement_count
clean_text_value(row_data[58]), # problems
clean_text_value(row_data[59]), # suggestions
clean_text_value(row_data[60]), # remarks
'新建', # status (默认新建)
admin_id # created_by
)
# 跳过没有项目名称的行
if not params[2]:
skip_count += 1
continue
cursor.execute(sql, params)
success_count += 1
except Error as e:
print(f'第{row_idx+1}行导入错误: {e}', file=sys.stderr)
error_count += 1
continue
print(f'导入完成: 成功 {success_count} 条, 跳过 {skip_count} 条, 错误 {error_count} 条')
return True
def main():
"""主函数"""
print('='*60)
print('工程项目数据导入脚本')
print('='*60)
# 检查Excel文件是否存在
if not os.path.exists(EXCEL_FILE):
print(f'错误: Excel文件不存在: {EXCEL_FILE}', file=sys.stderr)
sys.exit(1)
# 打开Excel文件
try:
workbook = xlrd.open_workbook(EXCEL_FILE)
print(f'已打开Excel文件: {EXCEL_FILE}')
print(f'工作表数量: {len(workbook.sheet_names())}')
except Exception as e:
print(f'错误: 无法打开Excel文件: {e}', file=sys.stderr)
sys.exit(1)
# 获取数据库连接
connection = get_database_connection()
cursor = connection.cursor()
try:
# 导入主要工作表
main_sheets = [
'1-1基建',
'1-2业扩',
'1-3客户',
'1-4营销',
'2检修、技改、应急抢修项目'
]
for sheet_name in main_sheets:
if sheet_name in workbook.sheet_names():
sheet = workbook.sheet_by_name(sheet_name)
import_projects_from_sheet(sheet, connection, cursor, sheet_name)
else:
print(f'警告: 工作表不存在: {sheet_name}')
# 提交事务
connection.commit()
# 显示统计信息
cursor.execute("SELECT COUNT(*) FROM projects")
total_count = cursor.fetchone()[0]
print(f'\n数据库中共有 {total_count} 个项目')
cursor.execute("SELECT engineering_type, COUNT(*) as count FROM projects GROUP BY engineering_type")
print('\n按工程类别统计:')
for row in cursor.fetchall():
print(f' {row[0]}: {row[1]} 个')
print('\n' + '='*60)
print('数据导入完成!')
print('='*60)
except Exception as e:
print(f'错误: {e}', file=sys.stderr)
connection.rollback()
sys.exit(1)
finally:
cursor.close()
connection.close()
if __name__ == '__main__':
main()