taibai_shopping/export_code.py
2025-07-04 19:07:35 +08:00

306 lines
9.9 KiB
Python
Raw Permalink 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.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
项目代码导出工具
用于将整个电商项目的代码导出到文本文件中
"""
import os
import datetime
from pathlib import Path
class CodeExporter:
def __init__(self, project_root=None):
"""
初始化代码导出器
:param project_root: 项目根目录,默认为当前目录
"""
self.project_root = Path(project_root) if project_root else Path('.')
self.output_file = None
# 需要导出的文件扩展名
self.include_extensions = {
'.py', '.html', '.css', '.js', '.sql', '.txt', '.md',
'.yml', '.yaml', '.json', '.xml', '.ini', '.cfg'
}
# 需要排除的目录
self.exclude_dirs = {
'venv', '.venv', 'env', '.env', '__pycache__', '.git',
'.idea', '.vscode', 'node_modules', 'logs', 'temp', 'tmp',
'.pytest_cache', '.coverage', 'htmlcov', 'dist', 'build'
}
# 需要排除的文件
self.exclude_files = {
'.DS_Store', 'Thumbs.db', '.gitignore', '*.pyc', '*.pyo',
'*.log', '*.tmp', '*.bak', '*.swp', '*.swo'
}
# 特殊处理的文件(即使没有扩展名也要包含)
self.special_files = {
'Dockerfile', 'requirements.txt', 'README', 'LICENSE',
'Makefile', 'Procfile', '.dockerignore'
}
def should_include_file(self, file_path):
"""
判断文件是否应该被包含在导出中
:param file_path: 文件路径
:return: bool
"""
file_name = file_path.name
file_suffix = file_path.suffix.lower()
# 检查特殊文件
if file_name in self.special_files:
return True
# 检查扩展名
if file_suffix in self.include_extensions:
return True
return False
def should_exclude_dir(self, dir_path):
"""
判断目录是否应该被排除
:param dir_path: 目录路径
:return: bool
"""
dir_name = dir_path.name
return dir_name in self.exclude_dirs or dir_name.startswith('.')
def get_file_info(self, file_path):
"""
获取文件信息
:param file_path: 文件路径
:return: dict
"""
try:
stat = file_path.stat()
return {
'size': stat.st_size,
'modified': datetime.datetime.fromtimestamp(stat.st_mtime),
'relative_path': file_path.relative_to(self.project_root)
}
except Exception as e:
return {
'size': 0,
'modified': datetime.datetime.now(),
'relative_path': file_path.relative_to(self.project_root),
'error': str(e)
}
def read_file_content(self, file_path):
"""
读取文件内容
:param file_path: 文件路径
:return: str
"""
try:
# 尝试用UTF-8编码读取
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except UnicodeDecodeError:
try:
# 如果UTF-8失败尝试GBK编码
with open(file_path, 'r', encoding='gbk') as f:
return f.read()
except UnicodeDecodeError:
try:
# 如果还是失败尝试latin-1编码
with open(file_path, 'r', encoding='latin-1') as f:
return f.read()
except Exception as e:
return f"[无法读取文件内容: {str(e)}]"
except Exception as e:
return f"[读取文件时发生错误: {str(e)}]"
def scan_project(self):
"""
扫描项目目录,获取所有需要导出的文件
:return: list
"""
files_to_export = []
for root, dirs, files in os.walk(self.project_root):
root_path = Path(root)
# 过滤掉需要排除的目录
dirs[:] = [d for d in dirs if not self.should_exclude_dir(root_path / d)]
for file in files:
file_path = root_path / file
if self.should_include_file(file_path):
file_info = self.get_file_info(file_path)
files_to_export.append({
'path': file_path,
'info': file_info
})
# 按相对路径排序
files_to_export.sort(key=lambda x: str(x['info']['relative_path']))
return files_to_export
def export_to_file(self, output_filename=None):
"""
导出代码到文件
:param output_filename: 输出文件名
"""
if not output_filename:
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = f"project_code_export_{timestamp}.txt"
self.output_file = output_filename
files_to_export = self.scan_project()
print(f"开始导出项目代码...")
print(f"项目根目录: {self.project_root.absolute()}")
print(f"找到 {len(files_to_export)} 个文件需要导出")
print(f"输出文件: {output_filename}")
with open(output_filename, 'w', encoding='utf-8') as output:
# 写入文件头
self.write_header(output, files_to_export)
# 写入每个文件的内容
for i, file_data in enumerate(files_to_export, 1):
file_path = file_data['path']
file_info = file_data['info']
print(f"正在处理 ({i}/{len(files_to_export)}): {file_info['relative_path']}")
self.write_file_section(output, file_path, file_info)
# 写入文件尾
self.write_footer(output)
print(f"\n✅ 导出完成!")
print(f"输出文件: {output_filename}")
print(f"文件大小: {os.path.getsize(output_filename) / 1024:.2f} KB")
def write_header(self, output, files_to_export):
"""
写入文件头部信息
"""
output.write("=" * 80 + "\n")
output.write("项目代码导出文件\n")
output.write("=" * 80 + "\n")
output.write(f"项目名称: 基于Python的线上电商系统\n")
output.write(f"导出时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
output.write(f"项目路径: {self.project_root.absolute()}\n")
output.write(f"文件总数: {len(files_to_export)}\n")
output.write("=" * 80 + "\n\n")
# 写入文件目录
output.write("📁 文件目录:\n")
output.write("-" * 50 + "\n")
for file_data in files_to_export:
file_info = file_data['info']
size_kb = file_info['size'] / 1024 if file_info['size'] > 0 else 0
output.write(f"{file_info['relative_path']} ({size_kb:.1f} KB)\n")
output.write("\n" + "=" * 80 + "\n\n")
def write_file_section(self, output, file_path, file_info):
"""
写入单个文件的内容
"""
relative_path = file_info['relative_path']
# 文件分隔符
output.write("🔸" + "=" * 78 + "\n")
output.write(f"📄 文件: {relative_path}\n")
output.write(f"📊 大小: {file_info['size']} bytes ({file_info['size'] / 1024:.2f} KB)\n")
output.write(f"🕒 修改时间: {file_info['modified'].strftime('%Y-%m-%d %H:%M:%S')}\n")
if 'error' in file_info:
output.write(f"⚠️ 错误: {file_info['error']}\n")
output.write("🔸" + "=" * 78 + "\n\n")
# 文件内容
content = self.read_file_content(file_path)
output.write(content)
# 确保文件结尾有换行
if not content.endswith('\n'):
output.write('\n')
output.write("\n\n")
def write_footer(self, output):
"""
写入文件尾部信息
"""
output.write("=" * 80 + "\n")
output.write("导出完成\n")
output.write(f"导出时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
output.write("=" * 80 + "\n")
def export_summary(self):
"""
导出项目摘要信息
"""
files_to_export = self.scan_project()
# 按文件类型统计
type_stats = {}
total_size = 0
for file_data in files_to_export:
file_path = file_data['path']
file_info = file_data['info']
ext = file_path.suffix.lower() or '无扩展名'
if ext not in type_stats:
type_stats[ext] = {'count': 0, 'size': 0}
type_stats[ext]['count'] += 1
type_stats[ext]['size'] += file_info['size']
total_size += file_info['size']
print("\n📊 项目统计信息:")
print("-" * 50)
print(f"总文件数: {len(files_to_export)}")
print(f"总大小: {total_size / 1024:.2f} KB")
print("\n📋 文件类型统计:")
for ext, stats in sorted(type_stats.items(), key=lambda x: x[1]['count'], reverse=True):
print(f"{ext:>10}: {stats['count']:>3} 个文件, {stats['size'] / 1024:>6.1f} KB")
def main():
"""
主函数
"""
print("🚀 项目代码导出工具")
print("=" * 50)
# 创建导出器
exporter = CodeExporter()
# 显示项目摘要
exporter.export_summary()
# 询问是否继续导出
print("\n" + "=" * 50)
choice = input("是否继续导出完整代码到文件? (y/n): ").lower().strip()
if choice in ['y', 'yes', '']:
# 询问输出文件名
output_name = input("请输入输出文件名 (直接回车使用默认名称): ").strip()
if not output_name:
output_name = None
# 开始导出
exporter.export_to_file(output_name)
else:
print("取消导出。")
if __name__ == "__main__":
main()