mirror of
https://devops.liangqichi.top/qichi.liang/Orbitin.git
synced 2026-02-10 07:41:29 +08:00
Refactor: modular main.py with selectable functions, save debug output to debug/ dir
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,6 +12,10 @@ data/daily_logs.db
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
|
|
||||||
|
# Debug output
|
||||||
|
debug/
|
||||||
|
layout_output.txt
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|||||||
18
AGENTS.md
18
AGENTS.md
@@ -60,15 +60,21 @@ OrbitIn/
|
|||||||
## 命令
|
## 命令
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 获取并处理数据(带数据库存储)
|
# 默认:获取、提取、解析并保存到数据库
|
||||||
python3 main.py
|
python3 main.py
|
||||||
|
|
||||||
# 不存储到数据库
|
# 仅获取HTML并提取文本(保存到debug目录)
|
||||||
python3 main.py --no-db
|
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
|
||||||
```
|
```
|
||||||
|
|||||||
26
README.md
26
README.md
@@ -18,6 +18,8 @@ OrbitIn/
|
|||||||
├── README.md # 项目说明
|
├── README.md # 项目说明
|
||||||
├── AGENTS.md # AI助手开发文档
|
├── AGENTS.md # AI助手开发文档
|
||||||
├── layout_output.txt # 缓存的布局文本
|
├── layout_output.txt # 缓存的布局文本
|
||||||
|
├── debug/ # 调试输出目录
|
||||||
|
│ └── layout_output_*.txt # 带时间戳的调试文件
|
||||||
├── data/ # 数据目录
|
├── data/ # 数据目录
|
||||||
│ └── daily_logs.db # SQLite3 数据库
|
│ └── daily_logs.db # SQLite3 数据库
|
||||||
└── src/ # 代码模块
|
└── src/ # 代码模块
|
||||||
@@ -50,17 +52,29 @@ CONFLUENCE_API_TOKEN = "your-api-token"
|
|||||||
### 使用方法
|
### 使用方法
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 获取并处理数据(带数据库存储)
|
# 默认:获取HTML、提取、解析并保存到数据库
|
||||||
python3 main.py
|
python3 main.py
|
||||||
|
|
||||||
# 不存储到数据库
|
# 仅获取HTML并提取文本(保存到debug目录)
|
||||||
python3 main.py --no-db
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
## 数据格式
|
## 数据格式
|
||||||
|
|||||||
232
main.py
232
main.py
@@ -1,58 +1,84 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
|
码头作业日志管理工具
|
||||||
从 Confluence 获取交接班日志并保存到数据库
|
从 Confluence 获取交接班日志并保存到数据库
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from src.confluence import ConfluenceClient
|
from src.confluence import ConfluenceClient
|
||||||
from src.extractor import HTMLTextExtractor
|
from src.extractor import HTMLTextExtractor
|
||||||
from src.parser import HandoverLogParser
|
from src.parser import HandoverLogParser
|
||||||
from src.database import DailyLogsDatabase
|
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):
|
def ensure_debug_dir():
|
||||||
"""运行主流程"""
|
"""确保debug目录存在"""
|
||||||
# 配置
|
if not os.path.exists(DEBUG_DIR):
|
||||||
CONTENT_ID = '155764524'
|
os.makedirs(DEBUG_DIR)
|
||||||
BASE_URL = 'https://confluence.westwell-lab.com/rest/api'
|
|
||||||
TOKEN = 'NDE1NTcwMDE1ODQ0OiinqS5HLm12v2orWEYyjJcI1bl5'
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_timestamp():
|
||||||
|
"""获取时间戳用于文件名"""
|
||||||
|
return datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_html():
|
||||||
|
"""获取HTML内容"""
|
||||||
print('正在从 Confluence 获取 HTML 内容...')
|
print('正在从 Confluence 获取 HTML 内容...')
|
||||||
|
client = ConfluenceClient(CONF_BASE_URL, CONF_TOKEN)
|
||||||
# 获取 HTML
|
html = client.get_html(CONF_CONTENT_ID)
|
||||||
client = ConfluenceClient(BASE_URL, TOKEN)
|
|
||||||
html = client.get_html(CONTENT_ID)
|
|
||||||
|
|
||||||
if not html:
|
if not html:
|
||||||
print('错误:未获取到 HTML 内容')
|
print('错误:未获取到 HTML 内容')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
print(f'获取成功,共 {len(html)} 字符')
|
||||||
|
return html
|
||||||
|
|
||||||
|
|
||||||
|
def extract_text(html):
|
||||||
|
"""提取布局文本"""
|
||||||
print('正在提取布局文本...')
|
print('正在提取布局文本...')
|
||||||
|
|
||||||
# 提取文本
|
|
||||||
extractor = HTMLTextExtractor()
|
extractor = HTMLTextExtractor()
|
||||||
layout_text = extractor.extract(html)
|
layout_text = extractor.extract(html)
|
||||||
|
print(f'提取完成,共 {len(layout_text)} 字符')
|
||||||
|
return layout_text
|
||||||
|
|
||||||
print(f'\n提取完成,共 {len(layout_text)} 字符\n')
|
|
||||||
|
|
||||||
# 保存到文件(可选)
|
def save_debug_file(content, suffix=''):
|
||||||
with open('layout_output.txt', 'w', encoding='utf-8') as f:
|
"""保存调试文件到debug目录"""
|
||||||
f.write(layout_text)
|
ensure_debug_dir()
|
||||||
print('布局文本已保存到 layout_output.txt')
|
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
|
||||||
|
|
||||||
# 保存到数据库(可选)
|
|
||||||
if save_db:
|
|
||||||
print('\n正在解析日志数据...')
|
|
||||||
|
|
||||||
|
def parse_logs(text):
|
||||||
|
"""解析日志数据"""
|
||||||
|
print('正在解析日志数据...')
|
||||||
parser = HandoverLogParser()
|
parser = HandoverLogParser()
|
||||||
logs = parser.parse(layout_text)
|
logs = parser.parse(text)
|
||||||
|
|
||||||
if not logs:
|
|
||||||
print('未解析到任何记录')
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f'解析到 {len(logs)} 条记录')
|
print(f'解析到 {len(logs)} 条记录')
|
||||||
|
return logs
|
||||||
|
|
||||||
|
|
||||||
|
def save_to_db(logs):
|
||||||
|
"""保存到数据库"""
|
||||||
|
if not logs:
|
||||||
|
print('没有记录可保存')
|
||||||
|
return 0
|
||||||
|
|
||||||
db = DailyLogsDatabase()
|
db = DailyLogsDatabase()
|
||||||
count = db.insert_many([log.to_dict() for log in logs])
|
count = db.insert_many([log.to_dict() for log in logs])
|
||||||
@@ -65,11 +91,153 @@ def run(save_db: bool = True):
|
|||||||
print(f' 日期范围: {stats["date_range"]["start"]} ~ {stats["date_range"]["end"]}')
|
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__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='从 Confluence 获取交接班日志')
|
main()
|
||||||
parser.add_argument('--no-db', action='store_true', help='不保存到数据库')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
run(save_db=not args.no_db)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user