diff --git a/.gitignore b/.gitignore index 6573c82..bce5504 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,10 @@ data/daily_logs.db *.pyc *.pyo +# Debug output +debug/ +layout_output.txt + # OS .DS_Store Thumbs.db diff --git a/AGENTS.md b/AGENTS.md index eefd151..bb88d24 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,15 +60,21 @@ OrbitIn/ ## 命令 ```bash -# 获取并处理数据(带数据库存储) +# 默认:获取、提取、解析并保存到数据库 python3 main.py -# 不存储到数据库 -python3 main.py --no-db +# 仅获取HTML并提取文本(保存到debug目录) +python3 main.py fetch + +# 获取并保存带时间戳的debug文件 +python3 main.py fetch-debug # 生成日报 -python3 -c "from src.report import DailyReportGenerator; g = DailyReportGenerator(); g.print_report('2025-12-28'); g.close()" +python3 main.py report 2025-12-28 -# 测试解析模块 -python3 -c "from src.parser import HandoverLogParser; p = HandoverLogParser(); print(p.parse(open('layout_output.txt').read())[:3])" +# 解析测试 +python3 main.py parse-test + +# 添加未统计数据 +python3 main.py --unaccounted 118 --month 2025-12 ``` diff --git a/README.md b/README.md index dc2c522..fee6ead 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ OrbitIn/ ├── README.md # 项目说明 ├── AGENTS.md # AI助手开发文档 ├── layout_output.txt # 缓存的布局文本 +├── debug/ # 调试输出目录 +│ └── layout_output_*.txt # 带时间戳的调试文件 ├── data/ # 数据目录 │ └── daily_logs.db # SQLite3 数据库 └── src/ # 代码模块 @@ -50,17 +52,29 @@ CONFLUENCE_API_TOKEN = "your-api-token" ### 使用方法 ```bash -# 获取并处理数据(带数据库存储) +# 默认:获取HTML、提取、解析并保存到数据库 python3 main.py -# 不存储到数据库 -python3 main.py --no-db +# 仅获取HTML并提取文本(保存到debug目录) +python3 main.py fetch + +# 获取并保存带时间戳的debug文件 +python3 main.py fetch-debug # 生成日报(指定日期) -python3 -c "from src.report import DailyReportGenerator; g = DailyReportGenerator(); g.print_report('2025-12-28'); g.close()" +python3 main.py report 2025-12-28 -# 测试解析模块 -python3 -c "from src.parser import HandoverLogParser; p = HandoverLogParser(); print(p.parse(open('layout_output.txt').read())[:3])" +# 生成今日日报 +python3 main.py report-today + +# 解析测试(使用已有的layout_output.txt) +python3 main.py parse-test + +# 添加未统计数据 +python3 main.py --unaccounted 118 --month 2025-12 + +# 显示帮助 +python3 main.py --help ``` ## 数据格式 diff --git a/main.py b/main.py index 49fd979..2564a4b 100644 --- a/main.py +++ b/main.py @@ -1,75 +1,243 @@ #!/usr/bin/env python3 """ +码头作业日志管理工具 从 Confluence 获取交接班日志并保存到数据库 """ import argparse import sys +import os +from datetime import datetime from src.confluence import ConfluenceClient from src.extractor import HTMLTextExtractor from src.parser import HandoverLogParser from src.database import DailyLogsDatabase +from src.report import DailyReportGenerator + +# 配置 +CONF_BASE_URL = 'https://confluence.westwell-lab.com/rest/api' +CONF_TOKEN = 'NDE1NTcwMDE1ODQ0OiinqS5HLm12v2orWEYyjJcI1bl5' +CONF_CONTENT_ID = '155764524' + +DEBUG_DIR = 'debug' -def run(save_db: bool = True): - """运行主流程""" - # 配置 - CONTENT_ID = '155764524' - BASE_URL = 'https://confluence.westwell-lab.com/rest/api' - TOKEN = 'NDE1NTcwMDE1ODQ0OiinqS5HLm12v2orWEYyjJcI1bl5' - +def ensure_debug_dir(): + """确保debug目录存在""" + if not os.path.exists(DEBUG_DIR): + os.makedirs(DEBUG_DIR) + + +def get_timestamp(): + """获取时间戳用于文件名""" + return datetime.now().strftime('%Y%m%d_%H%M%S') + + +def fetch_html(): + """获取HTML内容""" print('正在从 Confluence 获取 HTML 内容...') - - # 获取 HTML - client = ConfluenceClient(BASE_URL, TOKEN) - html = client.get_html(CONTENT_ID) - + client = ConfluenceClient(CONF_BASE_URL, CONF_TOKEN) + html = client.get_html(CONF_CONTENT_ID) if not html: print('错误:未获取到 HTML 内容') sys.exit(1) - + print(f'获取成功,共 {len(html)} 字符') + return html + + +def extract_text(html): + """提取布局文本""" print('正在提取布局文本...') - - # 提取文本 extractor = HTMLTextExtractor() layout_text = extractor.extract(html) + print(f'提取完成,共 {len(layout_text)} 字符') + return layout_text + + +def save_debug_file(content, suffix=''): + """保存调试文件到debug目录""" + ensure_debug_dir() + filename = f'layout_output{suffix}.txt' if suffix else 'layout_output.txt' + filepath = os.path.join(DEBUG_DIR, filename) + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + print(f'已保存到 {filepath}') + return filepath + + +def parse_logs(text): + """解析日志数据""" + print('正在解析日志数据...') + parser = HandoverLogParser() + logs = parser.parse(text) + print(f'解析到 {len(logs)} 条记录') + return logs + + +def save_to_db(logs): + """保存到数据库""" + if not logs: + print('没有记录可保存') + return 0 - print(f'\n提取完成,共 {len(layout_text)} 字符\n') + db = DailyLogsDatabase() + count = db.insert_many([log.to_dict() for log in logs]) + print(f'已保存 {count} 条记录到数据库') - # 保存到文件(可选) - with open('layout_output.txt', 'w', encoding='utf-8') as f: - f.write(layout_text) - print('布局文本已保存到 layout_output.txt') + stats = db.get_stats() + print(f'\n数据库统计:') + print(f' 总记录: {stats["total"]}') + print(f' 船次: {len(stats["ships"])}') + print(f' 日期范围: {stats["date_range"]["start"]} ~ {stats["date_range"]["end"]}') - # 保存到数据库(可选) - if save_db: - print('\n正在解析日志数据...') - - parser = HandoverLogParser() - logs = parser.parse(layout_text) - - if not logs: - print('未解析到任何记录') - return - - print(f'解析到 {len(logs)} 条记录') - - db = DailyLogsDatabase() - count = db.insert_many([log.to_dict() for log in logs]) - print(f'已保存 {count} 条记录到数据库') - - stats = db.get_stats() - print(f'\n数据库统计:') - print(f' 总记录: {stats["total"]}') - print(f' 船次: {len(stats["ships"])}') - print(f' 日期范围: {stats["date_range"]["start"]} ~ {stats["date_range"]["end"]}') - - db.close() + db.close() + return count + + +def add_unaccounted(year_month, teu, note=''): + """添加未统计数据""" + db = DailyLogsDatabase() + result = db.insert_unaccounted(year_month, teu, note) + if result: + print(f'已添加 {year_month} 月未统计数据: {teu}TEU') + else: + print('添加失败') + db.close() + + +def show_stats(date): + """显示指定日期的统计""" + g = DailyReportGenerator() + g.print_report(date) + g.close() + + +def run_fetch(): + """执行:获取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(): + """执行:获取、提取、保存到debug目录""" + html = fetch_html() + text = extract_text(html) + suffix = f'_{get_timestamp()}' + save_debug_file(text, suffix) + return text + + +def run_report(date=None): + """执行:生成日报""" + if not date: + date = datetime.now().strftime('%Y-%m-%d') + show_stats(date) + + +def run_parser_test(): + """执行:解析测试""" + ensure_debug_file_path = os.path.join(DEBUG_DIR, 'layout_output.txt') + if os.path.exists('layout_output.txt'): + filepath = 'layout_output.txt' + elif os.path.exists(ensure_debug_file_path): + filepath = ensure_debug_file_path + else: + print('未找到 layout_output.txt 文件') + return + + print(f'使用文件: {filepath}') + with open(filepath, 'r', encoding='utf-8') as f: + text = f.read() + + parser = HandoverLogParser() + logs = parser.parse(text) + print(f'解析到 {len(logs)} 条记录') + for log in logs[:5]: + print(f' {log.date} {log.shift} {log.ship_name}: {log.teu}TEU') + + +# 功能映射 +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')), + 'parse-test': run_parser_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 生成今天日报 + parse-test 解析测试(使用已有的layout_output.txt) + stats 显示今日统计 + +示例: + python3 main.py fetch + python3 main.py fetch-save + python3 main.py report 2025-12-28 + python3 main.py parse-test +''' + ) + parser.add_argument( + 'function', + nargs='?', + default='fetch-save', + choices=list(FUNCTIONS.keys()), + 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 配合使用)' + ) + + args = parser.parse_args() + + # 添加未统计数据 + if args.unaccounted: + year_month = args.month or datetime.now().strftime('%Y-%m') + add_unaccounted(year_month, args.unaccounted) + return + + # 执行功能 + if args.function == 'report' and args.date: + run_report(args.date) + else: + FUNCTIONS[args.function]() if __name__ == '__main__': - parser = argparse.ArgumentParser(description='从 Confluence 获取交接班日志') - parser.add_argument('--no-db', action='store_true', help='不保存到数据库') - args = parser.parse_args() - - run(save_db=not args.no_db) + main()