Files
Orbitin/main.py

362 lines
9.5 KiB
Python
Raw Normal View History

2025-12-28 23:31:22 +08:00
#!/usr/bin/env python3
"""
码头作业日志管理工具
2025-12-28 23:31:22 +08:00
Confluence 获取交接班日志并保存到数据库
更新依赖使用新的模块结构
2025-12-28 23:31:22 +08:00
"""
import argparse
import sys
import os
from datetime import datetime
from typing import Optional, List
2025-12-28 23:31:22 +08:00
from src.config import config
from src.logging_config import setup_logging, get_logger
from src.confluence import ConfluenceClient, ConfluenceClientError, HTMLTextExtractor, HTMLTextExtractorError, HandoverLogParser, ShipLog, LogParserError
from src.database.daily_logs import DailyLogsDatabase
from src.report import DailyReportGenerator, ReportGeneratorError
2025-12-28 23:31:22 +08:00
# 初始化日志
logger = get_logger(__name__)
def ensure_debug_dir():
"""确保debug目录存在"""
if not os.path.exists(config.DEBUG_DIR):
os.makedirs(config.DEBUG_DIR)
logger.info(f"创建调试目录: {config.DEBUG_DIR}")
def get_timestamp() -> str:
"""获取时间戳用于文件名"""
return datetime.now().strftime('%Y%m%d_%H%M%S')
def fetch_html() -> str:
"""
获取HTML内容
返回:
HTML字符串
异常:
SystemExit: 配置错误或获取失败
"""
# 验证配置
if not config.validate():
logger.error("配置验证失败,请检查 .env 文件")
sys.exit(1)
try:
logger.info("正在从 Confluence 获取 HTML 内容...")
client = ConfluenceClient()
html = client.get_html(config.CONFLUENCE_CONTENT_ID)
logger.info(f"获取成功,共 {len(html)} 字符")
return html
except ConfluenceClientError as e:
logger.error(f"获取HTML失败: {e}")
sys.exit(1)
except Exception as e:
logger.error(f"未知错误: {e}")
2025-12-28 23:31:22 +08:00
sys.exit(1)
def extract_text(html: str) -> str:
"""
提取布局文本
参数:
html: HTML字符串
返回:
提取的文本
"""
try:
logger.info("正在提取布局文本...")
extractor = HTMLTextExtractor()
layout_text = extractor.extract(html)
logger.info(f"提取完成,共 {len(layout_text)} 字符")
return layout_text
except HTMLTextExtractorError as e:
logger.error(f"提取文本失败: {e}")
raise
except Exception as e:
logger.error(f"未知错误: {e}")
raise
def save_debug_file(content: str, suffix: str = '') -> str:
"""
保存调试文件到debug目录
参数:
content: 要保存的内容
suffix: 文件名后缀
返回:
保存的文件路径
"""
ensure_debug_dir()
filename = f'layout_output{suffix}.txt' if suffix else 'layout_output.txt'
filepath = os.path.join(config.DEBUG_DIR, filename)
try:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
logger.info(f"已保存到 {filepath}")
return filepath
except Exception as e:
logger.error(f"保存调试文件失败: {e}")
raise
def parse_logs(text: str) -> List[ShipLog]:
"""
解析日志数据
参数:
text: 日志文本
返回:
船次日志列表
"""
try:
logger.info("正在解析日志数据...")
parser = HandoverLogParser()
logs = parser.parse(text)
logger.info(f"解析到 {len(logs)} 条记录")
return logs
except LogParserError as e:
logger.error(f"解析日志失败: {e}")
raise
except Exception as e:
logger.error(f"未知错误: {e}")
raise
def save_to_db(logs: List[ShipLog]) -> int:
"""
保存到数据库
参数:
logs: 船次日志列表
返回:
保存的记录数
"""
if not logs:
logger.warning("没有记录可保存")
return 0
2025-12-28 23:31:22 +08:00
try:
db = DailyLogsDatabase()
count = db.insert_many([log.to_dict() for log in logs])
logger.info(f"已保存 {count} 条记录到数据库")
stats = db.get_stats()
logger.info(f"数据库统计: 总记录={stats['total']}, 船次={len(stats['ships'])}, "
f"日期范围={stats['date_range']['start']}~{stats['date_range']['end']}")
return count
except Exception as e:
logger.error(f"保存到数据库失败: {e}")
raise
def add_unaccounted(year_month: str, teu: int, note: str = ''):
"""
添加未统计数据
2025-12-28 23:31:22 +08:00
参数:
year_month: 年月字符串格式 "2025-12"
teu: 未统计TEU数量
note: 备注
"""
try:
db = DailyLogsDatabase()
result = db.insert_unaccounted(year_month, teu, note)
if result:
logger.info(f"已添加 {year_month} 月未统计数据: {teu}TEU")
else:
logger.error("添加失败")
except Exception as e:
logger.error(f"添加未统计数据失败: {e}")
raise
def show_stats(date: str):
"""
显示指定日期的统计
2025-12-28 23:31:22 +08:00
参数:
date: 日期字符串格式 "YYYY-MM-DD"
"""
try:
generator = DailyReportGenerator()
generator.print_report(date)
except ReportGeneratorError as e:
logger.error(f"生成统计失败: {e}")
except Exception as e:
logger.error(f"未知错误: {e}")
def run_fetch() -> str:
"""执行获取HTML并提取文本"""
html = fetch_html()
text = extract_text(html)
save_debug_file(text)
return text
def run_fetch_and_save():
"""执行:获取、提取、解析、保存到数据库"""
text = run_fetch()
logs = parse_logs(text)
save_to_db(logs)
def run_fetch_save_debug() -> str:
"""执行获取、提取、保存到debug目录"""
html = fetch_html()
text = extract_text(html)
suffix = f'_{get_timestamp()}'
save_debug_file(text, suffix)
return text
def run_report(date: Optional[str] = None):
"""执行:生成日报"""
if not date:
date = datetime.now().strftime('%Y-%m-%d')
show_stats(date)
def run_config_test():
"""执行:配置测试"""
logger.info("配置测试:")
config.print_summary()
# 测试Confluence连接
try:
client = ConfluenceClient()
if client.test_connection():
logger.info("Confluence连接测试: 成功")
else:
logger.warning("Confluence连接测试: 失败")
except Exception as e:
logger.error(f"Confluence连接测试失败: {e}")
# 测试数据库连接
try:
db = DailyLogsDatabase()
stats = db.get_stats()
logger.info(f"数据库连接测试: 成功,总记录: {stats['total']}")
except Exception as e:
logger.error(f"数据库连接测试失败: {e}")
# 功能映射
FUNCTIONS = {
'fetch': run_fetch,
'fetch-save': run_fetch_and_save,
'fetch-debug': run_fetch_save_debug,
'report': lambda: run_report(),
'report-today': lambda: run_report(datetime.now().strftime('%Y-%m-%d')),
'config-test': run_config_test,
'stats': lambda: show_stats(datetime.now().strftime('%Y-%m-%d')),
}
def main():
parser = argparse.ArgumentParser(
description='码头作业日志管理工具',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
可选功能:
fetch 获取HTML并提取文本保存到debug目录
fetch-save 获取提取解析并保存到数据库
fetch-debug 获取提取并保存带时间戳的debug文件
report 生成日报默认今天
report-today 生成今日日报
config-test 配置测试
stats 显示今日统计
示例:
python3 main.py fetch
python3 main.py fetch-save
python3 main.py report 2025-12-28
python3 main.py config-test
'''
)
parser.add_argument(
'function',
nargs='?',
default='fetch-save',
choices=['fetch', 'fetch-save', 'fetch-debug', 'report', 'report-today', 'config-test', 'stats'],
help='要执行的功能 (默认: fetch-save)'
)
parser.add_argument(
'date',
nargs='?',
help='日期 (格式: YYYY-MM-DD),用于 report 功能'
)
parser.add_argument(
'--unaccounted',
'-u',
metavar='TEU',
type=int,
help='添加未统计数据(需同时指定月份,如 -u 118 2025-12'
)
parser.add_argument(
'--month',
'-m',
metavar='YEAR-MONTH',
help='指定月份(与 --unaccounted 配合使用)'
)
2025-12-28 23:31:22 +08:00
args = parser.parse_args()
# 添加未统计数据
if args.unaccounted:
year_month = args.month or datetime.now().strftime('%Y-%m')
try:
add_unaccounted(year_month, args.unaccounted)
except Exception as e:
logger.error(f"添加未统计数据失败: {e}")
sys.exit(1)
return
# 执行功能
try:
if args.function == 'report' and args.date:
run_report(args.date)
else:
FUNCTIONS[args.function]()
except KeyboardInterrupt:
logger.info("用户中断操作")
sys.exit(0)
except Exception as e:
logger.error(f"执行功能失败: {e}")
sys.exit(1)
if __name__ == '__main__':
# 初始化日志系统
setup_logging()
# 打印启动信息
logger.info("=" * 50)
logger.info("码头作业日志管理工具 - OrbitIn")
logger.info(f"启动时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
logger.info("=" * 50)
# 运行主程序
main()