Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 486313044c | |||
| 84e907b93e | |||
| 5fd05fcd3c | |||
| f04478fd8f | |||
| f5ba5624aa | |||
| 071a3f05f3 | |||
| bb3f25a643 | |||
| 53eef800b4 | |||
| 1b688c1603 | |||
| 0cbc587bf3 | |||
| 9b19015156 | |||
| 929c4b836f |
@@ -5,5 +5,12 @@ CONFLUENCE_CONTENT_ID=155764524
|
|||||||
|
|
||||||
# 飞书表格配置
|
# 飞书表格配置
|
||||||
FEISHU_BASE_URL=https://open.feishu.cn/open-apis/sheets/v3
|
FEISHU_BASE_URL=https://open.feishu.cn/open-apis/sheets/v3
|
||||||
FEISHU_TOKEN=your-feishu-api-token
|
|
||||||
FEISHU_SPREADSHEET_TOKEN=EgNPssi2ghZ7BLtGiTxcIBUmnVh
|
FEISHU_SPREADSHEET_TOKEN=EgNPssi2ghZ7BLtGiTxcIBUmnVh
|
||||||
|
|
||||||
|
# 飞书应用凭证(推荐方式,自动获取tenant_access_token)
|
||||||
|
# 创建飞书自建应用后获取app_id和app_secret
|
||||||
|
FEISHU_APP_ID=your-feishu-app-id
|
||||||
|
FEISHU_APP_SECRET=your-feishu-app-secret
|
||||||
|
|
||||||
|
# 备选:手动配置token(不推荐,token会过期)
|
||||||
|
# FEISHU_TOKEN=your-feishu-api-token
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -23,6 +23,8 @@ debug/
|
|||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
AGENTS.md
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.vscode/
|
.vscode/
|
||||||
|
plans/
|
||||||
@@ -68,6 +68,7 @@ OrbitIn/
|
|||||||
- `get_ships_with_monthly_teu(year_month)` - 获取当月每艘船的作业量
|
- `get_ships_with_monthly_teu(year_month)` - 获取当月每艘船的作业量
|
||||||
- `insert_unaccounted(year_month, teu, note)` - 添加未统计数据
|
- `insert_unaccounted(year_month, teu, note)` - 添加未统计数据
|
||||||
- `get_unaccounted(year_month)` - 获取未统计数据
|
- `get_unaccounted(year_month)` - 获取未统计数据
|
||||||
|
- `delete_unaccounted(year_month)` - 去除未统计数据(对称功能)
|
||||||
|
|
||||||
### DailyReportGenerator (src/report.py:15)
|
### DailyReportGenerator (src/report.py:15)
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ OrbitIn/
|
|||||||
|
|
||||||
- tkinter 图形界面
|
- tkinter 图形界面
|
||||||
- 支持获取数据、生成日报、添加未统计数据
|
- 支持获取数据、生成日报、添加未统计数据
|
||||||
|
- 支持去除多余统计数据(对称功能)
|
||||||
- 日报内容可复制
|
- 日报内容可复制
|
||||||
|
|
||||||
### FeishuScheduleManager (src/feishu.py:150)
|
### FeishuScheduleManager (src/feishu.py:150)
|
||||||
@@ -118,6 +120,9 @@ python3 main.py parse-test
|
|||||||
# 添加未统计数据
|
# 添加未统计数据
|
||||||
python3 main.py --unaccounted 118 --month 2025-12
|
python3 main.py --unaccounted 118 --month 2025-12
|
||||||
|
|
||||||
|
# 去除未统计数据
|
||||||
|
python3 main.py --remove-unaccounted --month 2025-12
|
||||||
|
|
||||||
# GUI界面
|
# GUI界面
|
||||||
python3 src/gui.py
|
python3 src/gui.py
|
||||||
```
|
```
|
||||||
|
|||||||
323
README.md
323
README.md
@@ -1,17 +1,44 @@
|
|||||||
# OrbitIn - 码头作业日志管理系统
|
# OrbitIn
|
||||||
|
|
||||||
从 Confluence API 获取交接班日志,提取作业数据并生成统计报表。
|
码头作业日志管理工具。从 Confluence API 获取交接班日志,提取作业数据并生成统计报表。
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
- 从 Confluence 获取交接班日志 HTML
|
- **数据获取**:从 Confluence API 获取交接班日志 HTML
|
||||||
- 提取保留布局的文本内容
|
- **文本提取**:提取保留布局的文本内容,支持表格格式化
|
||||||
- SQLite3 数据库存储
|
- **数据库存储**:SQLite3 数据库存储,支持二次靠泊记录合并
|
||||||
- 生成日报和月度统计
|
- **报表生成**:生成日报和月度统计,包含完成率计算
|
||||||
- 支持未统计数据手动录入
|
- **数据调整**:支持手动添加/剔除统计数据,月底数据自动转移
|
||||||
- 支持二次靠泊记录合并
|
- **智能调整**:月底最后一天自动询问剔除12点后数据,月初自动添加上月数据
|
||||||
- GUI 图形界面(可选)
|
- **GUI界面**:tkinter 图形界面,支持一键操作
|
||||||
- 飞书排班表集成(自动获取班次人员)
|
- **飞书集成**:自动获取排班人员信息,支持应用凭证自动刷新token
|
||||||
|
- **月份页面映射**:支持配置各月份的Confluence页面ID,解决每月页面ID变化问题
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install requests beautifulsoup4 python-dotenv
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# 编辑 .env 文件,填入配置
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# GUI 方式
|
||||||
|
python3 src/gui.py
|
||||||
|
|
||||||
|
# CLI 方式
|
||||||
|
python3 main.py fetch-save # 获取并保存数据
|
||||||
|
python3 main.py report 2025-12-28 # 生成日报
|
||||||
|
```
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
@@ -19,12 +46,8 @@
|
|||||||
OrbitIn/
|
OrbitIn/
|
||||||
├── main.py # CLI 入口
|
├── main.py # CLI 入口
|
||||||
├── README.md # 项目说明
|
├── README.md # 项目说明
|
||||||
├── AGENTS.md # AI助手开发文档
|
|
||||||
├── .env # 环境配置(敏感信息)
|
├── .env # 环境配置(敏感信息)
|
||||||
├── .env.example # 环境配置示例
|
├── .env.example # 环境配置示例
|
||||||
├── layout_output.txt # 缓存的布局文本
|
|
||||||
├── debug/ # 调试输出目录
|
|
||||||
│ └── layout_output_*.txt # 带时间戳的调试文件
|
|
||||||
├── data/ # 数据目录
|
├── data/ # 数据目录
|
||||||
│ ├── daily_logs.db # SQLite3 数据库
|
│ ├── daily_logs.db # SQLite3 数据库
|
||||||
│ └── schedule_cache.json # 排班数据缓存
|
│ └── schedule_cache.json # 排班数据缓存
|
||||||
@@ -45,174 +68,130 @@ OrbitIn/
|
|||||||
│ ├── parser.py # HTML 内容解析器
|
│ ├── parser.py # HTML 内容解析器
|
||||||
│ ├── text.py # HTML 文本提取器
|
│ ├── text.py # HTML 文本提取器
|
||||||
│ ├── log_parser.py # 日志解析器
|
│ ├── log_parser.py # 日志解析器
|
||||||
│ ├── manager.py # 内容管理器
|
│ └── manager.py # 内容管理器
|
||||||
│ └── __init__.py # 模块导出
|
|
||||||
└── feishu/ # 飞书 API 模块
|
└── feishu/ # 飞书 API 模块
|
||||||
├── client.py # 飞书 API 客户端
|
├── client.py # 飞书 API 客户端
|
||||||
├── parser.py # 排班数据解析器
|
├── parser.py # 排班数据解析器
|
||||||
├── manager.py # 飞书排班管理器
|
└── manager.py # 飞书排班管理器
|
||||||
└── __init__.py # 模块导出
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 快速开始
|
## 核心模块
|
||||||
|
|
||||||
### 安装依赖
|
### ConfluenceClient (src/confluence/client.py)
|
||||||
|
|
||||||
```bash
|
- `fetch_content(content_id, expand)` - 获取页面内容
|
||||||
pip install requests beautifulsoup4 python-dotenv
|
- `get_html(content_id)` - 获取 HTML 字符串
|
||||||
```
|
|
||||||
|
|
||||||
### 配置
|
### HTMLTextExtractor (src/confluence/text.py)
|
||||||
|
|
||||||
|
- `extract(html)` - 从 HTML 提取保留布局的文本
|
||||||
|
- 使用 `html.parser`(非 lxml)
|
||||||
|
- 移除带 `ac:name` 属性的 Confluence 宏元素
|
||||||
|
- 表格格式化使用 `ljust()` 列对齐
|
||||||
|
|
||||||
|
### HandoverLogParser (src/confluence/log_parser.py)
|
||||||
|
|
||||||
|
- `parse(text)` - 解析日志文本,返回 `ShipLog` 列表
|
||||||
|
- 自动合并同日期同班次同船名的记录(二次靠泊)
|
||||||
|
- `ShipLog` 数据类:date, shift, ship_name, teu, efficiency, vehicles
|
||||||
|
|
||||||
|
### DailyLogsDatabase (src/database/daily_logs.py)
|
||||||
|
|
||||||
|
- `insert(log)` - 插入单条记录(存在则跳过)
|
||||||
|
- `insert_many(logs)` - 批量插入
|
||||||
|
- `query_by_date(date)` - 按日期查询
|
||||||
|
- `query_by_ship(ship_name)` - 按船名查询
|
||||||
|
- `query_all(limit)` - 查询所有
|
||||||
|
- `get_stats()` - 获取统计信息
|
||||||
|
- `get_ships_with_monthly_teu(year_month)` - 获取当月每艘船的作业量
|
||||||
|
- `insert_cross_month_exclusion()` - 跨月数据调整
|
||||||
|
- `insert_confluence_page()` - 保存月份页面ID映射
|
||||||
|
- `get_confluence_page_for_date()` - 获取指定日期对应的页面ID
|
||||||
|
|
||||||
|
### DailyReportGenerator (src/report.py)
|
||||||
|
|
||||||
|
- `generate_report(date)` - 生成日报
|
||||||
|
- `print_report(date)` - 打印日报
|
||||||
|
- `get_shift_personnel(date)` - 获取班次人员(从飞书排班表获取)
|
||||||
|
|
||||||
|
### OrbitInGUI (src/gui.py)
|
||||||
|
|
||||||
|
- tkinter 图形界面
|
||||||
|
- 支持获取数据、生成日报
|
||||||
|
- 支持手动剔除次月多统计的船
|
||||||
|
- 日报内容可复制
|
||||||
|
|
||||||
|
### FeishuScheduleManager (src/feishu/manager.py)
|
||||||
|
|
||||||
|
- `get_schedule_for_date(date)` - 获取指定日期的排班信息
|
||||||
|
- `get_schedule_for_today()` - 获取今天的排班信息
|
||||||
|
- `refresh_all_schedules(days)` - 批量刷新排班信息
|
||||||
|
|
||||||
|
## 文本格式约定
|
||||||
|
|
||||||
|
- 列表前缀:`•` 用于 `ul`,数字+点用于 `ol`
|
||||||
|
- 粗体使用 `**text**`,斜体使用 `*text*`
|
||||||
|
- 水平线使用 `─` (U+2500) 字符
|
||||||
|
- 链接渲染为 `text (url)`
|
||||||
|
|
||||||
|
## 配置
|
||||||
|
|
||||||
在 `.env` 文件中配置:
|
在 `.env` 文件中配置:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# .env
|
|
||||||
# Confluence 配置
|
# Confluence 配置
|
||||||
CONFLUENCE_BASE_URL=https://your-confluence.atlassian.net/rest/api
|
CONFLUENCE_BASE_URL=https://confluence.westwell-lab.com/rest/api
|
||||||
CONFLUENCE_TOKEN=your-api-token
|
CONFLUENCE_TOKEN=your-api-token
|
||||||
CONFLUENCE_CONTENT_ID=155764524
|
CONFLUENCE_CONTENT_ID=155764524
|
||||||
|
|
||||||
# 飞书表格配置(用于获取排班人员信息)
|
# 飞书表格配置
|
||||||
FEISHU_BASE_URL=https://open.feishu.cn/open-apis/sheets/v3
|
FEISHU_BASE_URL=https://open.feishu.cn/open-apis/sheets/v3
|
||||||
FEISHU_TOKEN=your-feishu-api-token
|
FEISHU_TOKEN=your-feishu-api-token
|
||||||
FEISHU_SPREADSHEET_TOKEN=EgNPssi2ghZ7BLtGiTxcIBUmnVh
|
FEISHU_SPREADSHEET_TOKEN=EgNPssi2ghZ7BLtGiTxcIBUmnVh
|
||||||
|
FEISHU_APP_ID=your-feishu-app-id
|
||||||
# 数据库配置
|
FEISHU_APP_SECRET=your-feishu-app-secret
|
||||||
DATABASE_PATH=data/daily_logs.db
|
|
||||||
|
|
||||||
# 业务配置
|
# 业务配置
|
||||||
DAILY_TARGET_TEU=300 # 每日目标TEU数量,用于计算完成率
|
DAILY_TARGET_TEU=300
|
||||||
DUTY_PHONE=13107662315 # 值班电话,显示在日报中
|
DUTY_PHONE=13107662315
|
||||||
SEPARATOR_CHAR=─ # 分隔线字符,用于格式化输出
|
|
||||||
SEPARATOR_LENGTH=50 # 分隔线长度
|
|
||||||
SCHEDULE_REFRESH_DAYS=30 # 排班数据刷新间隔(天)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
参考 `.env.example` 文件创建 `.env` 文件。
|
## 命令
|
||||||
|
|
||||||
### 使用方法
|
|
||||||
|
|
||||||
#### 命令行方式
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 默认:获取、提取、解析并保存到数据库
|
# 默认:获取、提取、解析并保存到数据库
|
||||||
python3 main.py fetch-save
|
python3 main.py
|
||||||
|
|
||||||
# 仅获取HTML并提取文本(保存到debug目录)
|
# 仅获取HTML并提取文本
|
||||||
python3 main.py fetch
|
python3 main.py fetch
|
||||||
|
|
||||||
# 获取并保存带时间戳的debug文件
|
# 生成日报
|
||||||
python3 main.py fetch-debug
|
|
||||||
|
|
||||||
# 生成日报(指定日期)
|
|
||||||
python3 main.py report 2025-12-28
|
python3 main.py report 2025-12-28
|
||||||
|
|
||||||
# 生成今日日报
|
# 生成昨日日报
|
||||||
python3 main.py report-today
|
python3 main.py report-today
|
||||||
|
|
||||||
# 配置测试(验证所有连接)
|
# 手动剔除次月多统计的船
|
||||||
|
python3 main.py --cross-exclude --source-date 2025-12-31 --target-date 2026-01-01 --ship-name "学友洋山" --teu 100
|
||||||
|
|
||||||
|
# 配置测试
|
||||||
python3 main.py config-test
|
python3 main.py config-test
|
||||||
|
|
||||||
# 添加未统计数据
|
# GUI界面
|
||||||
python3 main.py --unaccounted 118 --month 2025-12
|
|
||||||
|
|
||||||
# 显示帮助
|
|
||||||
python3 main.py --help
|
|
||||||
```
|
|
||||||
|
|
||||||
#### GUI 方式
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 src/gui.py
|
python3 src/gui.py
|
||||||
```
|
```
|
||||||
|
|
||||||
GUI 功能:
|
## 测试模式
|
||||||
- 获取并处理数据
|
|
||||||
- 获取 (Debug模式)
|
|
||||||
- 生成日报
|
|
||||||
- 今日日报(自动获取前一天数据)
|
|
||||||
- 添加未统计数据
|
|
||||||
- 数据库统计(显示当月每艘船的作业量)
|
|
||||||
- 日报内容可复制
|
|
||||||
- 自动刷新排班信息
|
|
||||||
|
|
||||||
## 数据格式
|
如果设置了环境变量 `DEBUG_MODE=true`,系统会使用本地 `layout_output.txt` 文件而不是从 Confluence API 获取数据,方便离线测试。
|
||||||
|
|
||||||
### 日报表 (daily_handover_logs)
|
## 注意事项
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
1. 二次靠泊记录会在解析时自动合并
|
||||||
|------|------|------|
|
2. 重复获取数据不会累加TEU(会跳过已存在的记录)
|
||||||
| id | INTEGER | 主键 |
|
3. 未统计数据在报表中不显示,但会计算到当月实际作业量
|
||||||
| date | TEXT | 日期 YYYY-MM-DD |
|
4. 昨日日报按钮默认获取前一天的数据(因为通常在第二天汇报)
|
||||||
| shift | TEXT | 班次 (白班/夜班) |
|
5. 月底数据调整适用于**每个月最后一天**,而不仅限12月
|
||||||
| ship_name | TEXT | 船名(不含船号前缀) |
|
|
||||||
| teu | INTEGER | 作业量 TEU |
|
|
||||||
| efficiency | REAL | 效率 |
|
|
||||||
| vehicles | INTEGER | 上场车辆数 |
|
|
||||||
| created_at | TEXT | 创建时间 |
|
|
||||||
|
|
||||||
### 未统计表 (monthly_unaccounted)
|
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| id | INTEGER | 主键 |
|
|
||||||
| year_month | TEXT | 年月 YYYY-MM |
|
|
||||||
| teu | INTEGER | 未统计的 TEU |
|
|
||||||
| note | TEXT | 备注 |
|
|
||||||
| created_at | TEXT | 创建时间 |
|
|
||||||
|
|
||||||
## 特性说明
|
|
||||||
|
|
||||||
### 二次靠泊合并
|
|
||||||
|
|
||||||
解析时会自动合并同一天的二次靠泊记录:
|
|
||||||
|
|
||||||
- 夜班 学友洋山: 273TEU
|
|
||||||
- 夜班 学友洋山(二次靠泊): 14TEU
|
|
||||||
- 合并后: 夜班 学友洋山: 287TEU
|
|
||||||
|
|
||||||
### 未统计数据
|
|
||||||
|
|
||||||
可以在数据库统计中查看当月每艘船的作业量总计,便于跟踪船舶运营情况。
|
|
||||||
|
|
||||||
## 示例输出
|
|
||||||
|
|
||||||
```
|
|
||||||
日期:12/28
|
|
||||||
|
|
||||||
船名:学友洋山
|
|
||||||
作业量:246TEU
|
|
||||||
|
|
||||||
当日实际作业量:246TEU
|
|
||||||
|
|
||||||
当月计划作业量:8400TEU (用天数*300TEU)
|
|
||||||
当月实际作业量:12632TEU
|
|
||||||
当月完成比例:150.38%
|
|
||||||
|
|
||||||
12/29 白班人员:
|
|
||||||
12/29 夜班人员:
|
|
||||||
24小时值班手机:13107662315
|
|
||||||
```
|
|
||||||
|
|
||||||
## 核心模块说明
|
|
||||||
|
|
||||||
### Confluence 模块 (`src/confluence/`)
|
|
||||||
- **`client.py`** - Confluence API 客户端,负责 HTTP 请求和连接管理
|
|
||||||
- **`text.py`** - HTML 文本提取器,保留布局结构
|
|
||||||
- **`log_parser.py`** - 日志解析器,解析船次作业数据
|
|
||||||
- **`parser.py`** - HTML 内容解析器,提取链接、图片、表格
|
|
||||||
- **`manager.py`** - 内容管理器,提供高级内容管理功能
|
|
||||||
|
|
||||||
### 飞书模块 (`src/feishu/`)
|
|
||||||
- **`client.py`** - 飞书 API 客户端
|
|
||||||
- **`parser.py`** - 排班数据解析器
|
|
||||||
- **`manager.py`** - 飞书排班管理器,缓存和刷新排班信息
|
|
||||||
|
|
||||||
### 数据库模块 (`src/database/`)
|
|
||||||
- **`base.py`** - 数据库基类,提供统一的连接管理
|
|
||||||
- **`daily_logs.py`** - 每日交接班日志数据库
|
|
||||||
- **`schedules.py`** - 排班数据库
|
|
||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
|
|
||||||
@@ -220,65 +199,9 @@ GUI 功能:
|
|||||||
- SQLite3
|
- SQLite3
|
||||||
- Requests (HTTP 客户端)
|
- Requests (HTTP 客户端)
|
||||||
- BeautifulSoup4 (HTML 解析)
|
- BeautifulSoup4 (HTML 解析)
|
||||||
- tkinter (GUI,可选)
|
- tkinter (GUI)
|
||||||
- 类型提示 (Python 3.5+)
|
- 类型提示
|
||||||
|
|
||||||
## 架构特点
|
## 许可证
|
||||||
|
|
||||||
1. **模块化设计** - 每个模块职责单一,便于测试和维护
|
|
||||||
2. **统一配置** - 集中管理所有环境变量和业务配置
|
|
||||||
3. **统一日志** - 标准化的日志配置和文件轮转
|
|
||||||
4. **异常处理** - 详细的错误处理和日志记录
|
|
||||||
5. **类型安全** - 全面的 Python 类型提示
|
|
||||||
|
|
||||||
## 开发指南
|
|
||||||
|
|
||||||
### 添加新功能
|
|
||||||
|
|
||||||
1. **配置管理**: 所有配置项应在 `src/config.py` 中定义
|
|
||||||
2. **日志记录**: 使用 `from src.logging_config import get_logger` 获取日志器
|
|
||||||
3. **异常处理**: 为每个模块创建自定义异常类
|
|
||||||
4. **类型提示**: 所有函数和方法都应包含完整的类型提示
|
|
||||||
5. **数据库操作**: 使用 `src/database/base.py` 中的基类确保连接管理
|
|
||||||
|
|
||||||
### 测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 运行配置测试
|
|
||||||
python3 main.py config-test
|
|
||||||
|
|
||||||
# 测试特定功能
|
|
||||||
python3 main.py fetch
|
|
||||||
python3 main.py report-today
|
|
||||||
```
|
|
||||||
|
|
||||||
### 调试
|
|
||||||
|
|
||||||
1. **日志查看**: 查看 `logs/app.log` 获取详细运行信息
|
|
||||||
2. **调试文件**: 使用 `python3 main.py fetch-debug` 生成带时间戳的调试文件
|
|
||||||
|
|
||||||
### 代码规范
|
|
||||||
|
|
||||||
- 遵循 PEP 8 编码规范
|
|
||||||
- 使用 Black 格式化代码(可选)
|
|
||||||
- 使用 isort 排序导入
|
|
||||||
- 所有公开 API 应有文档字符串
|
|
||||||
|
|
||||||
## 故障排除
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
1. **连接失败**: 检查 `.env` 文件中的 API 令牌和 URL
|
|
||||||
2. **数据库错误**: 确保 `data/` 目录存在且有写入权限
|
|
||||||
3. **解析错误**: 检查 Confluence 页面结构是否发生变化
|
|
||||||
4. **飞书数据获取失败**: 验证飞书表格权限和 token 有效性
|
|
||||||
|
|
||||||
### 日志级别
|
|
||||||
|
|
||||||
- 默认日志级别: INFO
|
|
||||||
- 调试日志级别: DEBUG (设置环境变量 `LOG_LEVEL=DEBUG`)
|
|
||||||
- 日志文件: `logs/app.log`,自动轮转
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|||||||
665
logs/2026-01/2026-01-04.log
Normal file
665
logs/2026-01/2026-01-04.log
Normal file
@@ -0,0 +1,665 @@
|
|||||||
|
2026-01-04 01:09:29 - root - INFO - logging_config.py:110 - 控制台日志级别: INFO
|
||||||
|
2026-01-04 01:09:29 - root - INFO - logging_config.py:111 - 文件日志级别: DEBUG
|
||||||
|
2026-01-04 01:09:29 - root - INFO - logging_config.py:110 - 控制台日志级别: INFO
|
||||||
|
2026-01-04 01:09:29 - root - INFO - logging_config.py:111 - 文件日志级别: DEBUG
|
||||||
|
2026-01-04 01:09:29 - root - INFO - <string>:10 - 测试日期分片日志 - 时间: 2026-01-04 01:09:29
|
||||||
|
2026-01-04 01:09:29 - root - DEBUG - <string>:11 - Debug级别测试
|
||||||
|
2026-01-04 01:09:29 - root - WARNING - <string>:12 - Warning级别测试
|
||||||
|
2026-01-04 01:10:13 - root - INFO - logging_config.py:110 - 控制台日志级别: INFO
|
||||||
|
2026-01-04 01:10:13 - root - INFO - logging_config.py:111 - 文件日志级别: DEBUG
|
||||||
|
2026-01-04 01:10:13 - __main__ - INFO - gui.py:614 - GUI启动,开始自动获取新数据...
|
||||||
|
2026-01-04 01:10:13 - __main__ - INFO - gui.py:627 - 正在刷新排班信息...
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - INFO - manager.py:53 - 使用飞书应用凭证自动获取token
|
||||||
|
2026-01-04 01:10:13 - src.feishu.client - DEBUG - client.py:56 - 飞书客户端初始化完成,基础URL: https://open.feishu.cn/open-apis/sheets/v3
|
||||||
|
2026-01-04 01:10:13 - src.feishu.client - DEBUG - client.py:57 - 使用应用ID: cli_a9d9... 如果配置
|
||||||
|
2026-01-04 01:10:13 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:13 - src.database.schedules - DEBUG - schedules.py:71 - 排班数据库表结构初始化完成
|
||||||
|
2026-01-04 01:10:13 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - INFO - manager.py:41 - 飞书排班管理器初始化完成
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - INFO - manager.py:230 - 开始刷新未来 7 天的排班信息
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-04 的排班信息...
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-04 的排班信息 (格式: 01/04/1月4日)
|
||||||
|
2026-01-04 01:10:13 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:13 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-04 的排班信息
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-05 的排班信息...
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-05 的排班信息 (格式: 01/05/1月5日)
|
||||||
|
2026-01-04 01:10:13 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:13 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:13 - src.feishu.manager - INFO - manager.py:145 - 数据库中没有 2026-01-05 的排班信息,从飞书获取
|
||||||
|
2026-01-04 01:10:13 - src.feishu.client - INFO - client.py:98 - 正在获取tenant_access_token,应用ID: cli_a9d9...
|
||||||
|
2026-01-04 01:10:14 - src.feishu.client - INFO - client.py:114 - 成功获取tenant_access_token,有效期: 4411秒
|
||||||
|
2026-01-04 01:10:14 - src.feishu.client - INFO - client.py:156 - token获取成功,将在 4411 秒后过期
|
||||||
|
2026-01-04 01:10:14 - src.feishu.client - INFO - client.py:203 - 获取到 7 个表格
|
||||||
|
2026-01-04 01:10:14 - src.feishu.manager - INFO - manager.py:90 - 找到2026年年度表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:14 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4410秒
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:252 - 获取表格数据成功: R35cIj, 范围: A:AF
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.schedules - DEBUG - schedules.py:144 - 表格无更新: 2026年排班表 (ID: R35cIj)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:192 - 使用日期格式: 1月5日 解析表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - INFO - parser.py:267 - 使用年度表格解析器: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - DEBUG - parser.py:201 - 找到月份块: 1月 (行: 1)
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - DEBUG - parser.py:108 - 找到日期列: 1月5日 -> 1月5日 (索引: 5)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.database.schedules - DEBUG - schedules.py:182 - 保存排班信息: 2026-01-05
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:198 - 已保存 2026-01-05 的排班信息到数据库
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-06 的排班信息...
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-06 的排班信息 (格式: 01/06/1月6日)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:145 - 数据库中没有 2026-01-06 的排班信息,从飞书获取
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4409秒
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - INFO - client.py:203 - 获取到 7 个表格
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:90 - 找到2026年年度表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4409秒
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:252 - 获取表格数据成功: R35cIj, 范围: A:AF
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.schedules - DEBUG - schedules.py:144 - 表格无更新: 2026年排班表 (ID: R35cIj)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:192 - 使用日期格式: 1月6日 解析表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - INFO - parser.py:267 - 使用年度表格解析器: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - DEBUG - parser.py:201 - 找到月份块: 1月 (行: 1)
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - DEBUG - parser.py:108 - 找到日期列: 1月6日 -> 1月6日 (索引: 6)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.database.schedules - DEBUG - schedules.py:182 - 保存排班信息: 2026-01-06
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:198 - 已保存 2026-01-06 的排班信息到数据库
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-07 的排班信息...
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-07 的排班信息 (格式: 01/07/1月7日)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:145 - 数据库中没有 2026-01-07 的排班信息,从飞书获取
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4409秒
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - INFO - client.py:203 - 获取到 7 个表格
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:90 - 找到2026年年度表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4409秒
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:252 - 获取表格数据成功: R35cIj, 范围: A:AF
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.schedules - DEBUG - schedules.py:144 - 表格无更新: 2026年排班表 (ID: R35cIj)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:192 - 使用日期格式: 1月7日 解析表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - INFO - parser.py:267 - 使用年度表格解析器: 2026年排班表
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - DEBUG - parser.py:201 - 找到月份块: 1月 (行: 1)
|
||||||
|
2026-01-04 01:10:15 - src.feishu.parser - DEBUG - parser.py:108 - 找到日期列: 1月7日 -> 1月7日 (索引: 7)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.database.schedules - DEBUG - schedules.py:182 - 保存排班信息: 2026-01-07
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:198 - 已保存 2026-01-07 的排班信息到数据库
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-08 的排班信息...
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-08 的排班信息 (格式: 01/08/1月8日)
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:15 - src.feishu.manager - INFO - manager.py:145 - 数据库中没有 2026-01-08 的排班信息,从飞书获取
|
||||||
|
2026-01-04 01:10:15 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4409秒
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - INFO - client.py:203 - 获取到 7 个表格
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:90 - 找到2026年年度表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4408秒
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - DEBUG - client.py:252 - 获取表格数据成功: R35cIj, 范围: A:AF
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:16 - src.database.schedules - DEBUG - schedules.py:144 - 表格无更新: 2026年排班表 (ID: R35cIj)
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:192 - 使用日期格式: 1月8日 解析表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:16 - src.feishu.parser - INFO - parser.py:267 - 使用年度表格解析器: 2026年排班表
|
||||||
|
2026-01-04 01:10:16 - src.feishu.parser - DEBUG - parser.py:201 - 找到月份块: 1月 (行: 1)
|
||||||
|
2026-01-04 01:10:16 - src.feishu.parser - DEBUG - parser.py:108 - 找到日期列: 1月8日 -> 1月8日 (索引: 8)
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:16 - src.database.schedules - DEBUG - schedules.py:182 - 保存排班信息: 2026-01-08
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:198 - 已保存 2026-01-08 的排班信息到数据库
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-09 的排班信息...
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-09 的排班信息 (格式: 01/09/1月9日)
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:145 - 数据库中没有 2026-01-09 的排班信息,从飞书获取
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4408秒
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - INFO - client.py:203 - 获取到 7 个表格
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:90 - 找到2026年年度表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4408秒
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - DEBUG - client.py:252 - 获取表格数据成功: R35cIj, 范围: A:AF
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:16 - src.database.schedules - DEBUG - schedules.py:144 - 表格无更新: 2026年排班表 (ID: R35cIj)
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:192 - 使用日期格式: 1月9日 解析表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:16 - src.feishu.parser - INFO - parser.py:267 - 使用年度表格解析器: 2026年排班表
|
||||||
|
2026-01-04 01:10:16 - src.feishu.parser - DEBUG - parser.py:201 - 找到月份块: 1月 (行: 1)
|
||||||
|
2026-01-04 01:10:16 - src.feishu.parser - DEBUG - parser.py:108 - 找到日期列: 1月9日 -> 1月9日 (索引: 9)
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:16 - src.database.schedules - DEBUG - schedules.py:182 - 保存排班信息: 2026-01-09
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:198 - 已保存 2026-01-09 的排班信息到数据库
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-10 的排班信息...
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-10 的排班信息 (格式: 01/10/1月10日)
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:16 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:16 - src.feishu.manager - INFO - manager.py:145 - 数据库中没有 2026-01-10 的排班信息,从飞书获取
|
||||||
|
2026-01-04 01:10:16 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4407秒
|
||||||
|
2026-01-04 01:10:17 - src.feishu.client - INFO - client.py:203 - 获取到 7 个表格
|
||||||
|
2026-01-04 01:10:17 - src.feishu.manager - INFO - manager.py:90 - 找到2026年年度表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:17 - src.feishu.client - DEBUG - client.py:142 - token仍然有效,剩余时间: 4407秒
|
||||||
|
2026-01-04 01:10:17 - src.feishu.client - DEBUG - client.py:252 - 获取表格数据成功: R35cIj, 范围: A:AF
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.schedules - DEBUG - schedules.py:144 - 表格无更新: 2026年排班表 (ID: R35cIj)
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.feishu.manager - INFO - manager.py:192 - 使用日期格式: 1月10日 解析表格: 2026年排班表
|
||||||
|
2026-01-04 01:10:17 - src.feishu.parser - INFO - parser.py:267 - 使用年度表格解析器: 2026年排班表
|
||||||
|
2026-01-04 01:10:17 - src.feishu.parser - DEBUG - parser.py:201 - 找到月份块: 1月 (行: 1)
|
||||||
|
2026-01-04 01:10:17 - src.feishu.parser - DEBUG - parser.py:108 - 找到日期列: 1月10日 -> 1月10日 (索引: 10)
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.schedules - DEBUG - schedules.py:182 - 保存排班信息: 2026-01-10
|
||||||
|
2026-01-04 01:10:17 - src.feishu.manager - INFO - manager.py:198 - 已保存 2026-01-10 的排班信息到数据库
|
||||||
|
2026-01-04 01:10:17 - src.feishu.manager - INFO - manager.py:246 - 排班信息刷新完成,成功: 7, 失败: 0
|
||||||
|
2026-01-04 01:10:17 - __main__ - INFO - gui.py:632 - 排班信息刷新完成
|
||||||
|
2026-01-04 01:10:17 - __main__ - INFO - gui.py:648 - 正在尝试获取最新作业数据...
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - __main__ - INFO - gui.py:673 - 正在从 Confluence 获取 HTML...
|
||||||
|
2026-01-04 01:10:17 - src.confluence.client - DEBUG - client.py:48 - Confluence客户端初始化完成,基础URL: https://confluence.westwell-lab.com/rest/api
|
||||||
|
2026-01-04 01:10:17 - src.confluence.client - DEBUG - client.py:69 - 获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:10:17 - src.confluence.client - INFO - client.py:74 - 成功获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:10:17 - src.confluence.client - INFO - client.py:115 - 获取到Confluence HTML内容,长度: 11084 字符
|
||||||
|
2026-01-04 01:10:17 - __main__ - INFO - gui.py:679 - 获取成功,共 11084 字符
|
||||||
|
2026-01-04 01:10:17 - __main__ - INFO - gui.py:683 - 正在提取布局文本...
|
||||||
|
2026-01-04 01:10:17 - src.confluence.text - DEBUG - text.py:60 - 开始解析HTML,长度: 11084 字符
|
||||||
|
2026-01-04 01:10:17 - src.confluence.text - INFO - text.py:83 - HTML提取完成,输出长度: 2104 字符
|
||||||
|
2026-01-04 01:10:17 - __main__ - INFO - gui.py:689 - 正在解析日志数据...
|
||||||
|
2026-01-04 01:10:17 - src.confluence.log_parser - INFO - log_parser.py:356 - 解析转堆作业: 2026-01-02 白班 2TEU
|
||||||
|
2026-01-04 01:10:17 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 14 条记录
|
||||||
|
2026-01-04 01:10:17 - __main__ - INFO - gui.py:696 - 正在保存到数据库...
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 金祥源
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 东方吉
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 转堆作业
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 学友洋山
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 嘉洋16
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 泽远
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 华信长和
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 华信长和
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 瀚旺
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 金祥源
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:17 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 弘旭968
|
||||||
|
2026-01-04 01:10:17 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 东方祥
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 学友洋山
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 夜班 学友洋山
|
||||||
|
2026-01-04 01:10:18 - src.database.daily_logs - INFO - daily_logs.py:202 - 批量插入完成,成功 14/14 条记录
|
||||||
|
2026-01-04 01:10:18 - __main__ - INFO - gui.py:700 - 已保存 14 条新记录
|
||||||
|
2026-01-04 01:10:18 - __main__ - INFO - gui.py:728 - 正在生成今日日报...
|
||||||
|
2026-01-04 01:10:18 - __main__ - INFO - gui.py:572 - 生成 2026-01-03 的日报...
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.report - INFO - report.py:34 - 日报生成器初始化完成
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.feishu.manager - INFO - manager.py:53 - 使用飞书应用凭证自动获取token
|
||||||
|
2026-01-04 01:10:18 - src.feishu.client - DEBUG - client.py:56 - 飞书客户端初始化完成,基础URL: https://open.feishu.cn/open-apis/sheets/v3
|
||||||
|
2026-01-04 01:10:18 - src.feishu.client - DEBUG - client.py:57 - 使用应用ID: cli_a9d9... 如果配置
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.schedules - DEBUG - schedules.py:71 - 排班数据库表结构初始化完成
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.feishu.manager - INFO - manager.py:41 - 飞书排班管理器初始化完成
|
||||||
|
2026-01-04 01:10:18 - src.report - INFO - report.py:235 - 获取 2026-01-03 日报的班次人员,对应排班表日期: 2026-01-04
|
||||||
|
2026-01-04 01:10:18 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-04 的排班信息 (格式: 01/04/1月4日)
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:10:18 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:10:18 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-04 的排班信息
|
||||||
|
2026-01-04 01:10:18 - src.report - INFO - report.py:340 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:10:18 - __main__ - INFO - gui.py:589 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:10:18 - __main__ - INFO - gui.py:733 - 自动获取完成,GUI已就绪
|
||||||
|
2026-01-04 01:14:19 - __main__ - INFO - gui.py:253 - 开始获取数据...
|
||||||
|
2026-01-04 01:14:19 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:19 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:14:19 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:19 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:19 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:19 - __main__ - INFO - gui.py:282 - 使用页面ID映射: 2026-01-03 -> 159049182
|
||||||
|
2026-01-04 01:14:19 - __main__ - INFO - gui.py:287 - 正在从 Confluence 获取 HTML...
|
||||||
|
2026-01-04 01:14:19 - src.confluence.client - DEBUG - client.py:48 - Confluence客户端初始化完成,基础URL: https://confluence.westwell-lab.com/rest/api
|
||||||
|
2026-01-04 01:14:19 - src.confluence.client - DEBUG - client.py:69 - 获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:14:20 - src.confluence.client - INFO - client.py:74 - 成功获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:14:20 - src.confluence.client - INFO - client.py:115 - 获取到Confluence HTML内容,长度: 12190 字符
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:297 - 获取成功,共 12190 字符
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:301 - 正在提取布局文本...
|
||||||
|
2026-01-04 01:14:20 - src.confluence.text - DEBUG - text.py:60 - 开始解析HTML,长度: 12190 字符
|
||||||
|
2026-01-04 01:14:20 - src.confluence.text - INFO - text.py:83 - HTML提取完成,输出长度: 2312 字符
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:305 - 提取完成,共 2311 字符
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:309 - 正在解析日志数据...
|
||||||
|
2026-01-04 01:14:20 - src.confluence.log_parser - INFO - log_parser.py:356 - 解析转堆作业: 2026-01-02 白班 2TEU
|
||||||
|
2026-01-04 01:14:20 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 16 条记录
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:313 - 解析到 16 条记录
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:318 - 正在保存到数据库...
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 金祥源
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 东方吉
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 夜班 德盛6
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 夜班 海顺丰7
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 转堆作业
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 学友洋山
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 嘉洋16
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 泽远
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 华信长和
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 华信长和
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 瀚旺
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 金祥源
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 弘旭968
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 东方祥
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 学友洋山
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 夜班 学友洋山
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - INFO - daily_logs.py:202 - 批量插入完成,成功 16/16 条记录
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:322 - 已保存 16 条记录
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:327 - 数据库总计: 147 条记录, 30 艘船
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:572 - 生成 2026-01-03 的日报...
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.report - INFO - report.py:34 - 日报生成器初始化完成
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.feishu.manager - INFO - manager.py:53 - 使用飞书应用凭证自动获取token
|
||||||
|
2026-01-04 01:14:20 - src.feishu.client - DEBUG - client.py:56 - 飞书客户端初始化完成,基础URL: https://open.feishu.cn/open-apis/sheets/v3
|
||||||
|
2026-01-04 01:14:20 - src.feishu.client - DEBUG - client.py:57 - 使用应用ID: cli_a9d9... 如果配置
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.schedules - DEBUG - schedules.py:71 - 排班数据库表结构初始化完成
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.feishu.manager - INFO - manager.py:41 - 飞书排班管理器初始化完成
|
||||||
|
2026-01-04 01:14:20 - src.report - INFO - report.py:235 - 获取 2026-01-03 日报的班次人员,对应排班表日期: 2026-01-04
|
||||||
|
2026-01-04 01:14:20 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-04 的排班信息 (格式: 01/04/1月4日)
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:14:20 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:14:20 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-04 的排班信息
|
||||||
|
2026-01-04 01:14:20 - src.report - INFO - report.py:340 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:589 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:14:20 - __main__ - INFO - gui.py:336 - 数据获取完成
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:253 - 开始获取数据...
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:282 - 使用页面ID映射: 2026-01-03 -> 159049182
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:287 - 正在从 Confluence 获取 HTML...
|
||||||
|
2026-01-04 01:16:15 - src.confluence.client - DEBUG - client.py:48 - Confluence客户端初始化完成,基础URL: https://confluence.westwell-lab.com/rest/api
|
||||||
|
2026-01-04 01:16:15 - src.confluence.client - DEBUG - client.py:69 - 获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:16:15 - src.confluence.client - INFO - client.py:74 - 成功获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:16:15 - src.confluence.client - INFO - client.py:115 - 获取到Confluence HTML内容,长度: 12190 字符
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:297 - 获取成功,共 12190 字符
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:301 - 正在提取布局文本...
|
||||||
|
2026-01-04 01:16:15 - src.confluence.text - DEBUG - text.py:60 - 开始解析HTML,长度: 12190 字符
|
||||||
|
2026-01-04 01:16:15 - src.confluence.text - INFO - text.py:83 - HTML提取完成,输出长度: 2312 字符
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:305 - 提取完成,共 2311 字符
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:309 - 正在解析日志数据...
|
||||||
|
2026-01-04 01:16:15 - src.confluence.log_parser - INFO - log_parser.py:356 - 解析转堆作业: 2026-01-02 白班 2TEU
|
||||||
|
2026-01-04 01:16:15 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 16 条记录
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:313 - 解析到 16 条记录
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:318 - 正在保存到数据库...
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 金祥源
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 东方吉
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 夜班 德盛6
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 夜班 海顺丰7
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 转堆作业
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 学友洋山
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 嘉洋16
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 泽远
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 华信长和
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 华信长和
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 瀚旺
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 金祥源
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 弘旭968
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 东方祥
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 学友洋山
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 夜班 学友洋山
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - INFO - daily_logs.py:202 - 批量插入完成,成功 16/16 条记录
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:322 - 已保存 16 条记录
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:327 - 数据库总计: 147 条记录, 30 艘船
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:572 - 生成 2026-01-03 的日报...
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.report - INFO - report.py:34 - 日报生成器初始化完成
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.feishu.manager - INFO - manager.py:53 - 使用飞书应用凭证自动获取token
|
||||||
|
2026-01-04 01:16:15 - src.feishu.client - DEBUG - client.py:56 - 飞书客户端初始化完成,基础URL: https://open.feishu.cn/open-apis/sheets/v3
|
||||||
|
2026-01-04 01:16:15 - src.feishu.client - DEBUG - client.py:57 - 使用应用ID: cli_a9d9... 如果配置
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.schedules - DEBUG - schedules.py:71 - 排班数据库表结构初始化完成
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.feishu.manager - INFO - manager.py:41 - 飞书排班管理器初始化完成
|
||||||
|
2026-01-04 01:16:15 - src.report - INFO - report.py:235 - 获取 2026-01-03 日报的班次人员,对应排班表日期: 2026-01-04
|
||||||
|
2026-01-04 01:16:15 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-04 的排班信息 (格式: 01/04/1月4日)
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:16:15 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:16:15 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-04 的排班信息
|
||||||
|
2026-01-04 01:16:15 - src.report - INFO - report.py:340 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:589 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:16:15 - __main__ - INFO - gui.py:336 - 数据获取完成
|
||||||
|
2026-01-04 01:17:20 - root - INFO - logging_config.py:110 - 控制台日志级别: INFO
|
||||||
|
2026-01-04 01:17:20 - root - INFO - logging_config.py:111 - 文件日志级别: DEBUG
|
||||||
|
2026-01-04 01:17:20 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 1 条记录
|
||||||
|
2026-01-04 01:17:20 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 1 条记录
|
||||||
|
2026-01-04 01:17:20 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 1 条记录
|
||||||
|
2026-01-04 01:17:20 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 1 条记录
|
||||||
|
2026-01-04 01:17:53 - root - INFO - logging_config.py:110 - 控制台日志级别: INFO
|
||||||
|
2026-01-04 01:17:53 - root - INFO - logging_config.py:111 - 文件日志级别: DEBUG
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:614 - GUI启动,开始自动获取新数据...
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:627 - 正在刷新排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:53 - 使用飞书应用凭证自动获取token
|
||||||
|
2026-01-04 01:17:53 - src.feishu.client - DEBUG - client.py:56 - 飞书客户端初始化完成,基础URL: https://open.feishu.cn/open-apis/sheets/v3
|
||||||
|
2026-01-04 01:17:53 - src.feishu.client - DEBUG - client.py:57 - 使用应用ID: cli_a9d9... 如果配置
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.schedules - DEBUG - schedules.py:71 - 排班数据库表结构初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:41 - 飞书排班管理器初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:230 - 开始刷新未来 7 天的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-04 的排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-04 的排班信息 (格式: 01/04/1月4日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-04 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-05 的排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-05 的排班信息 (格式: 01/05/1月5日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-05 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-06 的排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-06 的排班信息 (格式: 01/06/1月6日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-06 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-07 的排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-07 的排班信息 (格式: 01/07/1月7日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-07 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-08 的排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-08 的排班信息 (格式: 01/08/1月8日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-08 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-09 的排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-09 的排班信息 (格式: 01/09/1月9日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-09 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - DEBUG - manager.py:239 - 刷新 2026-01-10 的排班信息...
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-10 的排班信息 (格式: 01/10/1月10日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-10 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:246 - 排班信息刷新完成,成功: 7, 失败: 0
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:632 - 排班信息刷新完成
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:648 - 正在尝试获取最新作业数据...
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:673 - 正在从 Confluence 获取 HTML...
|
||||||
|
2026-01-04 01:17:53 - src.confluence.client - DEBUG - client.py:48 - Confluence客户端初始化完成,基础URL: https://confluence.westwell-lab.com/rest/api
|
||||||
|
2026-01-04 01:17:53 - src.confluence.client - DEBUG - client.py:69 - 获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:17:53 - src.confluence.client - INFO - client.py:74 - 成功获取Confluence内容: 159049182
|
||||||
|
2026-01-04 01:17:53 - src.confluence.client - INFO - client.py:115 - 获取到Confluence HTML内容,长度: 12190 字符
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:679 - 获取成功,共 12190 字符
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:683 - 正在提取布局文本...
|
||||||
|
2026-01-04 01:17:53 - src.confluence.text - DEBUG - text.py:60 - 开始解析HTML,长度: 12190 字符
|
||||||
|
2026-01-04 01:17:53 - src.confluence.text - INFO - text.py:83 - HTML提取完成,输出长度: 2312 字符
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:689 - 正在解析日志数据...
|
||||||
|
2026-01-04 01:17:53 - src.confluence.log_parser - INFO - log_parser.py:390 - 解析转堆作业: 2026-01-02 白班 2TEU
|
||||||
|
2026-01-04 01:17:53 - src.confluence.log_parser - INFO - log_parser.py:209 - 日志解析完成,共 16 条记录
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:696 - 正在保存到数据库...
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 金祥源
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 白班 东方吉
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 夜班 德盛6
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-03 夜班 海顺丰7
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 转堆作业
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 学友洋山
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 嘉洋16
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 泽远
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 白班 华信长和
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 华信长和
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 瀚旺
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-02 夜班 金祥源
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 弘旭968
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 东方祥
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 白班 学友洋山
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:180 - 插入记录: 2026-01-01 夜班 学友洋山
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - INFO - daily_logs.py:202 - 批量插入完成,成功 16/16 条记录
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:700 - 已保存 16 条新记录
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:728 - 正在生成今日日报...
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:572 - 生成 2026-01-03 的日报...
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.daily_logs - DEBUG - daily_logs.py:155 - 数据库表结构初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.report - INFO - report.py:34 - 日报生成器初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:53 - 使用飞书应用凭证自动获取token
|
||||||
|
2026-01-04 01:17:53 - src.feishu.client - DEBUG - client.py:56 - 飞书客户端初始化完成,基础URL: https://open.feishu.cn/open-apis/sheets/v3
|
||||||
|
2026-01-04 01:17:53 - src.feishu.client - DEBUG - client.py:57 - 使用应用ID: cli_a9d9... 如果配置
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.schedules - DEBUG - schedules.py:71 - 排班数据库表结构初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:41 - 飞书排班管理器初始化完成
|
||||||
|
2026-01-04 01:17:53 - src.report - INFO - report.py:235 - 获取 2026-01-03 日报的班次人员,对应排班表日期: 2026-01-04
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:136 - 获取 2026-01-04 的排班信息 (格式: 01/04/1月4日)
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:57 - 数据库连接已建立: data/daily_logs.db
|
||||||
|
2026-01-04 01:17:53 - src.database.base - DEBUG - base.py:87 - 数据库连接已关闭
|
||||||
|
2026-01-04 01:17:53 - src.feishu.manager - INFO - manager.py:141 - 从数据库获取 2026-01-04 的排班信息
|
||||||
|
2026-01-04 01:17:53 - src.report - INFO - report.py:340 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:589 - 日报生成完成: 2026-01-03
|
||||||
|
2026-01-04 01:17:53 - __main__ - INFO - gui.py:733 - 自动获取完成,GUI已就绪
|
||||||
180
main.py
180
main.py
@@ -189,6 +189,70 @@ def add_unaccounted(year_month: str, teu: int, note: str = ''):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def remove_unaccounted(year_month: str, teu_to_reduce: int = None):
|
||||||
|
"""
|
||||||
|
去除未统计数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
year_month: 年月字符串,格式 "2025-12"
|
||||||
|
teu_to_reduce: 要减少的TEU数量,如果为None则删除整个记录
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
db = DailyLogsDatabase()
|
||||||
|
|
||||||
|
if teu_to_reduce is None:
|
||||||
|
# 如果没有指定减少数量,则删除整个记录
|
||||||
|
result = db.delete_unaccounted(year_month)
|
||||||
|
if result:
|
||||||
|
logger.info(f"已删除 {year_month} 月未统计数据")
|
||||||
|
else:
|
||||||
|
logger.error("删除失败")
|
||||||
|
else:
|
||||||
|
# 减少指定数量的TEU
|
||||||
|
result = db.reduce_unaccounted(year_month, teu_to_reduce)
|
||||||
|
if result:
|
||||||
|
logger.info(f"已减少 {year_month} 月未统计数据: {teu_to_reduce}TEU")
|
||||||
|
else:
|
||||||
|
logger.error("减少失败")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"去除未统计数据失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def add_cross_month_exclusion(source_date: str, target_date: str, ship_name: str, teu: int,
|
||||||
|
twenty_feet: int = 0, forty_feet: int = 0, reason: str = ''):
|
||||||
|
"""
|
||||||
|
添加跨月剔除调整(手动剔除次月多统计的船)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
source_date: 源日期(上月底日期)
|
||||||
|
target_date: 目标日期(次月日期)
|
||||||
|
ship_name: 船名
|
||||||
|
teu: TEU数量
|
||||||
|
twenty_feet: 20尺箱量
|
||||||
|
forty_feet: 40尺箱量
|
||||||
|
reason: 调整原因
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
db = DailyLogsDatabase()
|
||||||
|
success = db.insert_cross_month_exclusion(
|
||||||
|
source_date=source_date,
|
||||||
|
target_date=target_date,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet,
|
||||||
|
reason=reason
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
logger.info(f"已添加跨月剔除调整: {source_date} -> {target_date} {ship_name} {teu}TEU")
|
||||||
|
else:
|
||||||
|
logger.error("添加跨月剔除调整失败")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"添加跨月剔除调整失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def show_stats(date: str):
|
def show_stats(date: str):
|
||||||
"""
|
"""
|
||||||
显示指定日期的统计
|
显示指定日期的统计
|
||||||
@@ -288,11 +352,30 @@ def main():
|
|||||||
config-test 配置测试
|
config-test 配置测试
|
||||||
stats 显示今日统计
|
stats 显示今日统计
|
||||||
|
|
||||||
|
参数:
|
||||||
|
--unaccounted, -u TEU 添加未统计数据(需同时指定月份)
|
||||||
|
--remove-unaccounted, -r [TEU] 去除未统计数据(需同时指定月份)。如果指定TEU值,则减少该数量;如果不指定,则删除整个记录
|
||||||
|
--month, -m YEAR-MONTH 指定月份(与 -u 或 -r 配合使用)
|
||||||
|
--cross-exclude, -c 手动剔除次月多统计的船(需指定源日期、目标日期、船名和TEU)
|
||||||
|
|
||||||
|
跨月剔除参数:
|
||||||
|
--source-date DATE 源日期(上月底日期),格式: YYYY-MM-DD
|
||||||
|
--target-date DATE 目标日期(次月日期),格式: YYYY-MM-DD
|
||||||
|
--ship-name NAME 船名
|
||||||
|
--teu TEU TEU数量
|
||||||
|
--twenty-feet COUNT 20尺箱量(可选,默认0)
|
||||||
|
--forty-feet COUNT 40尺箱量(可选,默认0)
|
||||||
|
--reason REASON 调整原因(可选)
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
python3 main.py fetch
|
python3 main.py fetch
|
||||||
python3 main.py fetch-save
|
python3 main.py fetch-save
|
||||||
python3 main.py report 2025-12-28
|
python3 main.py report 2025-12-28
|
||||||
python3 main.py config-test
|
python3 main.py config-test
|
||||||
|
python3 main.py --unaccounted 118 --month 2025-12
|
||||||
|
python3 main.py --remove-unaccounted --month 2025-12 # 删除整个记录
|
||||||
|
python3 main.py --remove-unaccounted 118 --month 2025-12 # 减少118TEU
|
||||||
|
python3 main.py --cross-exclude --source-date 2025-12-31 --target-date 2026-01-01 --ship-name "学友洋山" --teu 100
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -314,11 +397,67 @@ def main():
|
|||||||
type=int,
|
type=int,
|
||||||
help='添加未统计数据(需同时指定月份,如 -u 118 2025-12)'
|
help='添加未统计数据(需同时指定月份,如 -u 118 2025-12)'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--remove-unaccounted',
|
||||||
|
'-r',
|
||||||
|
metavar='TEU',
|
||||||
|
nargs='?',
|
||||||
|
const=None,
|
||||||
|
type=int,
|
||||||
|
help='去除未统计数据(需同时指定月份,如 -r 118 2025-12)。如果指定TEU值,则减少该数量;如果不指定,则删除整个记录'
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--month',
|
'--month',
|
||||||
'-m',
|
'-m',
|
||||||
metavar='YEAR-MONTH',
|
metavar='YEAR-MONTH',
|
||||||
help='指定月份(与 --unaccounted 配合使用)'
|
help='指定月份(与 --unaccounted 或 --remove-unaccounted 配合使用)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--cross-exclude',
|
||||||
|
'-c',
|
||||||
|
action='store_true',
|
||||||
|
help='手动剔除次月多统计的船'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--source-date',
|
||||||
|
metavar='DATE',
|
||||||
|
help='源日期(上月底日期),格式: YYYY-MM-DD'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--target-date',
|
||||||
|
metavar='DATE',
|
||||||
|
help='目标日期(次月日期),格式: YYYY-MM-DD'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--ship-name',
|
||||||
|
metavar='NAME',
|
||||||
|
help='船名'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--teu',
|
||||||
|
metavar='TEU',
|
||||||
|
type=int,
|
||||||
|
help='TEU数量'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--twenty-feet',
|
||||||
|
metavar='COUNT',
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help='20尺箱量(可选,默认0)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--forty-feet',
|
||||||
|
metavar='COUNT',
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help='40尺箱量(可选,默认0)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--reason',
|
||||||
|
metavar='REASON',
|
||||||
|
default='手动剔除次月多统计的船',
|
||||||
|
help='调整原因(可选,默认: "手动剔除次月多统计的船")'
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -333,6 +472,45 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 去除未统计数据
|
||||||
|
# 检查是否提供了 --remove-unaccounted 或 -r 参数
|
||||||
|
has_remove_arg = any(arg in sys.argv for arg in ['--remove-unaccounted', '-r'])
|
||||||
|
if has_remove_arg:
|
||||||
|
year_month = args.month or datetime.now().strftime('%Y-%m')
|
||||||
|
try:
|
||||||
|
# args.remove_unaccounted 可能是整数(指定TEU)或 None(未指定)
|
||||||
|
if isinstance(args.remove_unaccounted, int):
|
||||||
|
# 指定了TEU值,减少指定数量
|
||||||
|
remove_unaccounted(year_month, args.remove_unaccounted)
|
||||||
|
else:
|
||||||
|
# 未指定TEU值,删除整个记录
|
||||||
|
remove_unaccounted(year_month)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"去除未统计数据失败: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 跨月剔除功能
|
||||||
|
if args.cross_exclude:
|
||||||
|
if not all([args.source_date, args.target_date, args.ship_name, args.teu]):
|
||||||
|
logger.error("跨月剔除功能需要指定以下参数: --source-date, --target-date, --ship-name, --teu")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
add_cross_month_exclusion(
|
||||||
|
source_date=args.source_date,
|
||||||
|
target_date=args.target_date,
|
||||||
|
ship_name=args.ship_name,
|
||||||
|
teu=args.teu,
|
||||||
|
twenty_feet=args.twenty_feet,
|
||||||
|
forty_feet=args.forty_feet,
|
||||||
|
reason=args.reason
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"跨月剔除失败: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
# 执行功能
|
# 执行功能
|
||||||
try:
|
try:
|
||||||
if args.function == 'report' and args.date:
|
if args.function == 'report' and args.date:
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class Config:
|
|||||||
FEISHU_BASE_URL = os.getenv('FEISHU_BASE_URL', 'https://open.feishu.cn/open-apis/sheets/v3')
|
FEISHU_BASE_URL = os.getenv('FEISHU_BASE_URL', 'https://open.feishu.cn/open-apis/sheets/v3')
|
||||||
FEISHU_TOKEN = os.getenv('FEISHU_TOKEN')
|
FEISHU_TOKEN = os.getenv('FEISHU_TOKEN')
|
||||||
FEISHU_SPREADSHEET_TOKEN = os.getenv('FEISHU_SPREADSHEET_TOKEN')
|
FEISHU_SPREADSHEET_TOKEN = os.getenv('FEISHU_SPREADSHEET_TOKEN')
|
||||||
|
FEISHU_APP_ID = os.getenv('FEISHU_APP_ID')
|
||||||
|
FEISHU_APP_SECRET = os.getenv('FEISHU_APP_SECRET')
|
||||||
|
|
||||||
# 数据库配置
|
# 数据库配置
|
||||||
DATABASE_PATH = os.getenv('DATABASE_PATH', 'data/daily_logs.db')
|
DATABASE_PATH = os.getenv('DATABASE_PATH', 'data/daily_logs.db')
|
||||||
@@ -46,7 +48,7 @@ class Config:
|
|||||||
# GUI 配置
|
# GUI 配置
|
||||||
GUI_FONT_FAMILY = os.getenv('GUI_FONT_FAMILY', 'SimHei')
|
GUI_FONT_FAMILY = os.getenv('GUI_FONT_FAMILY', 'SimHei')
|
||||||
GUI_FONT_SIZE = int(os.getenv('GUI_FONT_SIZE', '10'))
|
GUI_FONT_SIZE = int(os.getenv('GUI_FONT_SIZE', '10'))
|
||||||
GUI_WINDOW_SIZE = os.getenv('GUI_WINDOW_SIZE', '900x700')
|
GUI_WINDOW_SIZE = os.getenv('GUI_WINDOW_SIZE', '1600x900')
|
||||||
|
|
||||||
# 排班刷新配置
|
# 排班刷新配置
|
||||||
SCHEDULE_REFRESH_DAYS = int(os.getenv('SCHEDULE_REFRESH_DAYS', '30'))
|
SCHEDULE_REFRESH_DAYS = int(os.getenv('SCHEDULE_REFRESH_DAYS', '30'))
|
||||||
@@ -70,8 +72,17 @@ class Config:
|
|||||||
errors.append("CONFLUENCE_CONTENT_ID 未配置")
|
errors.append("CONFLUENCE_CONTENT_ID 未配置")
|
||||||
|
|
||||||
# 检查飞书配置(可选,但建议配置)
|
# 检查飞书配置(可选,但建议配置)
|
||||||
if not cls.FEISHU_TOKEN:
|
has_feishu_token = bool(cls.FEISHU_TOKEN)
|
||||||
print("警告: FEISHU_TOKEN 未配置,排班功能将不可用")
|
has_app_credentials = bool(cls.FEISHU_APP_ID and cls.FEISHU_APP_SECRET)
|
||||||
|
|
||||||
|
if not has_feishu_token and not has_app_credentials:
|
||||||
|
print("警告: 飞书认证未配置,排班功能将不可用")
|
||||||
|
print(" 请配置 FEISHU_TOKEN 或 FEISHU_APP_ID + FEISHU_APP_SECRET")
|
||||||
|
elif has_app_credentials:
|
||||||
|
print("信息: 使用飞书应用凭证自动获取token")
|
||||||
|
elif has_feishu_token:
|
||||||
|
print("信息: 使用手动配置的FEISHU_TOKEN")
|
||||||
|
|
||||||
if not cls.FEISHU_SPREADSHEET_TOKEN:
|
if not cls.FEISHU_SPREADSHEET_TOKEN:
|
||||||
print("警告: FEISHU_SPREADSHEET_TOKEN 未配置,排班功能将不可用")
|
print("警告: FEISHU_SPREADSHEET_TOKEN 未配置,排班功能将不可用")
|
||||||
|
|
||||||
@@ -88,7 +99,21 @@ class Config:
|
|||||||
"""打印配置摘要"""
|
"""打印配置摘要"""
|
||||||
print("配置摘要:")
|
print("配置摘要:")
|
||||||
print(f" Confluence: {'已配置' if cls.CONFLUENCE_BASE_URL else '未配置'}")
|
print(f" Confluence: {'已配置' if cls.CONFLUENCE_BASE_URL else '未配置'}")
|
||||||
print(f" 飞书: {'已配置' if cls.FEISHU_TOKEN else '未配置'}")
|
|
||||||
|
# 飞书配置详情
|
||||||
|
has_feishu_token = bool(cls.FEISHU_TOKEN)
|
||||||
|
has_app_credentials = bool(cls.FEISHU_APP_ID and cls.FEISHU_APP_SECRET)
|
||||||
|
has_spreadsheet_token = bool(cls.FEISHU_SPREADSHEET_TOKEN)
|
||||||
|
|
||||||
|
if has_app_credentials:
|
||||||
|
feishu_status = f"应用凭证 (ID: {cls.FEISHU_APP_ID[:8]}...)"
|
||||||
|
elif has_feishu_token:
|
||||||
|
feishu_status = "手动token"
|
||||||
|
else:
|
||||||
|
feishu_status = "未配置"
|
||||||
|
|
||||||
|
print(f" 飞书认证: {feishu_status}")
|
||||||
|
print(f" 飞书表格: {'已配置' if has_spreadsheet_token else '未配置'}")
|
||||||
print(f" 数据库路径: {cls.DATABASE_PATH}")
|
print(f" 数据库路径: {cls.DATABASE_PATH}")
|
||||||
print(f" 每日目标TEU: {cls.DAILY_TARGET_TEU}")
|
print(f" 每日目标TEU: {cls.DAILY_TARGET_TEU}")
|
||||||
print(f" 排班刷新天数: {cls.SCHEDULE_REFRESH_DAYS}")
|
print(f" 排班刷新天数: {cls.SCHEDULE_REFRESH_DAYS}")
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class ShipLog:
|
|||||||
teu: Optional[int] = None
|
teu: Optional[int] = None
|
||||||
efficiency: Optional[float] = None
|
efficiency: Optional[float] = None
|
||||||
vehicles: Optional[int] = None
|
vehicles: Optional[int] = None
|
||||||
|
twenty_feet: Optional[int] = None # 20尺箱量
|
||||||
|
forty_feet: Optional[int] = None # 40尺箱量
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
"""转换为字典"""
|
"""转换为字典"""
|
||||||
@@ -108,7 +110,26 @@ class HandoverLogParser:
|
|||||||
try:
|
try:
|
||||||
logs: List[ShipLog] = []
|
logs: List[ShipLog] = []
|
||||||
|
|
||||||
# 预处理:移除单行分隔符(前后都是空行的分隔符)
|
# 预处理:修复日期格式和特殊字符
|
||||||
|
# 1. 修复日期格式:将 "2026.1.1" 转换为 "2026.01.01"
|
||||||
|
def fix_date_format(match):
|
||||||
|
date_str = match.group(1)
|
||||||
|
parts = date_str.split('.')
|
||||||
|
if len(parts) == 3:
|
||||||
|
year, month, day = parts
|
||||||
|
# 补零
|
||||||
|
month = month.zfill(2)
|
||||||
|
day = day.zfill(2)
|
||||||
|
return f"日期:{year}.{month}.{day}"
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
# 修复日期格式
|
||||||
|
text = re.sub(r'日期:(\d{4}\.\d{1,2}\.\d{1,2})', fix_date_format, text)
|
||||||
|
|
||||||
|
# 2. 修复特殊空格字符(\xa0 转换为普通空格)
|
||||||
|
text = text.replace('\xa0', ' ')
|
||||||
|
|
||||||
|
# 3. 移除单行分隔符(前后都是空行的分隔符)
|
||||||
# 保留真正的内容分隔符(前后有内容的)
|
# 保留真正的内容分隔符(前后有内容的)
|
||||||
lines = text.split('\n')
|
lines = text.split('\n')
|
||||||
processed_lines: List[str] = []
|
processed_lines: List[str] = []
|
||||||
@@ -129,19 +150,21 @@ class HandoverLogParser:
|
|||||||
processed_text = '\n'.join(processed_lines)
|
processed_text = '\n'.join(processed_lines)
|
||||||
blocks = processed_text.split(self.SEPARATOR)
|
blocks = processed_text.split(self.SEPARATOR)
|
||||||
|
|
||||||
|
current_date = None
|
||||||
for block in blocks:
|
for block in blocks:
|
||||||
if not block.strip() or '日期:' not in block:
|
if not block.strip():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 解析日期
|
# 检查块中是否包含日期(使用改进后的正则表达式)
|
||||||
date_match = re.search(r'日期:(\d{4}\.\d{2}\.\d{2})', block)
|
date_match = re.search(r'日期:(\d{4}\.\d{2}\.\d{2})', block)
|
||||||
if not date_match:
|
if date_match:
|
||||||
continue
|
current_date = self.parse_date(date_match.group(1))
|
||||||
|
|
||||||
date = self.parse_date(date_match.group(1))
|
# 如果当前有日期,解析该块
|
||||||
self._parse_block(block, date, logs)
|
if current_date:
|
||||||
|
self._parse_block(block, current_date, logs)
|
||||||
|
|
||||||
# 合并同日期同班次同船名的记录(累加TEU)
|
# 合并同日期同班次同船名的记录(累加TEU和尺寸箱量)
|
||||||
merged: Dict[Tuple[str, str, str], ShipLog] = {}
|
merged: Dict[Tuple[str, str, str], ShipLog] = {}
|
||||||
for log in logs:
|
for log in logs:
|
||||||
key = (log.date, log.shift, log.ship_name)
|
key = (log.date, log.shift, log.ship_name)
|
||||||
@@ -152,7 +175,9 @@ class HandoverLogParser:
|
|||||||
ship_name=log.ship_name,
|
ship_name=log.ship_name,
|
||||||
teu=log.teu,
|
teu=log.teu,
|
||||||
efficiency=log.efficiency,
|
efficiency=log.efficiency,
|
||||||
vehicles=log.vehicles
|
vehicles=log.vehicles,
|
||||||
|
twenty_feet=log.twenty_feet,
|
||||||
|
forty_feet=log.forty_feet
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# 累加TEU
|
# 累加TEU
|
||||||
@@ -167,6 +192,18 @@ class HandoverLogParser:
|
|||||||
merged[key].vehicles = log.vehicles
|
merged[key].vehicles = log.vehicles
|
||||||
else:
|
else:
|
||||||
merged[key].vehicles += log.vehicles
|
merged[key].vehicles += log.vehicles
|
||||||
|
# 累加20尺箱量
|
||||||
|
if log.twenty_feet:
|
||||||
|
if merged[key].twenty_feet is None:
|
||||||
|
merged[key].twenty_feet = log.twenty_feet
|
||||||
|
else:
|
||||||
|
merged[key].twenty_feet += log.twenty_feet
|
||||||
|
# 累加40尺箱量
|
||||||
|
if log.forty_feet:
|
||||||
|
if merged[key].forty_feet is None:
|
||||||
|
merged[key].forty_feet = log.forty_feet
|
||||||
|
else:
|
||||||
|
merged[key].forty_feet += log.forty_feet
|
||||||
|
|
||||||
result = list(merged.values())
|
result = list(merged.values())
|
||||||
logger.info(f"日志解析完成,共 {len(result)} 条记录")
|
logger.info(f"日志解析完成,共 {len(result)} 条记录")
|
||||||
@@ -204,6 +241,12 @@ class HandoverLogParser:
|
|||||||
def _parse_ships(self, content: str, date: str, shift: str, logs: List[ShipLog]) -> None:
|
def _parse_ships(self, content: str, date: str, shift: str, logs: List[ShipLog]) -> None:
|
||||||
"""解析船次"""
|
"""解析船次"""
|
||||||
try:
|
try:
|
||||||
|
# 首先解析转堆作业(无船名)
|
||||||
|
relocation_content = self._parse_relocation(content, date, shift)
|
||||||
|
if relocation_content:
|
||||||
|
logs.append(relocation_content)
|
||||||
|
|
||||||
|
# 然后解析实船作业(按 "实船作业:" 分割)
|
||||||
parts = content.split('实船作业:')
|
parts = content.split('实船作业:')
|
||||||
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
@@ -222,40 +265,229 @@ class HandoverLogParser:
|
|||||||
# 移除二次靠泊等标注
|
# 移除二次靠泊等标注
|
||||||
ship_name = re.sub(r'(二次靠泊)|(再次靠泊)|\(二次靠泊\)|\(再次靠泊\)', '', ship_name).strip()
|
ship_name = re.sub(r'(二次靠泊)|(再次靠泊)|\(二次靠泊\)|\(再次靠泊\)', '', ship_name).strip()
|
||||||
|
|
||||||
vehicles_match = re.search(r'上场车辆数:(\d+)', cleaned)
|
# 解析车辆数、TEU、尺寸箱量
|
||||||
teu_eff_match = re.search(
|
self._parse_log_entry(cleaned, date, shift, ship_name, logs)
|
||||||
r'作业量/效率:(\d+)TEU[,,\s]*', cleaned
|
|
||||||
)
|
|
||||||
|
|
||||||
# 解析TEU
|
|
||||||
teu = None
|
|
||||||
if teu_eff_match:
|
|
||||||
try:
|
|
||||||
teu = int(teu_eff_match.group(1))
|
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"TEU解析失败: {teu_eff_match.group(1)}, 错误: {e}")
|
|
||||||
|
|
||||||
# 解析车辆数
|
|
||||||
vehicles = None
|
|
||||||
if vehicles_match:
|
|
||||||
try:
|
|
||||||
vehicles = int(vehicles_match.group(1))
|
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"车辆数解析失败: {vehicles_match.group(1)}, 错误: {e}")
|
|
||||||
|
|
||||||
log = ShipLog(
|
|
||||||
date=date,
|
|
||||||
shift=shift,
|
|
||||||
ship_name=ship_name,
|
|
||||||
teu=teu,
|
|
||||||
efficiency=None, # 目前日志中没有效率数据
|
|
||||||
vehicles=vehicles
|
|
||||||
)
|
|
||||||
logs.append(log)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"解析船次失败: {date} {shift}, 错误: {e}")
|
logger.warning(f"解析船次失败: {date} {shift}, 错误: {e}")
|
||||||
|
|
||||||
|
def _parse_relocation(self, content: str, date: str, shift: str) -> Optional[ShipLog]:
|
||||||
|
"""
|
||||||
|
解析转堆作业(无船名的作业类型)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
content: 班次内容
|
||||||
|
date: 日期
|
||||||
|
shift: 班次
|
||||||
|
|
||||||
|
返回:
|
||||||
|
ShipLog 对象,如果未找到转堆作业则返回 None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查是否包含转堆作业
|
||||||
|
if '转堆作业:' not in content:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 提取转堆作业部分(从 "转堆作业:" 到下一个空行或下一个实船作业)
|
||||||
|
relocation_start = content.find('转堆作业:') + len('转堆作业:')
|
||||||
|
|
||||||
|
# 查找下一个 "实船作业:" 作为结束标记
|
||||||
|
next_ship = content.find('实船作业:', relocation_start)
|
||||||
|
if next_ship == -1:
|
||||||
|
relocation_content = content[relocation_start:]
|
||||||
|
else:
|
||||||
|
relocation_content = content[relocation_start:next_ship]
|
||||||
|
|
||||||
|
cleaned = relocation_content.replace('\xa0', ' ').strip()
|
||||||
|
|
||||||
|
# 解析车辆数、TEU、尺寸箱量
|
||||||
|
vehicles_match = re.search(r'上场车辆数:(\d+)', cleaned)
|
||||||
|
teu_eff_match = re.search(r'作业量/效率:(\d+)TEU', cleaned)
|
||||||
|
|
||||||
|
teu = None
|
||||||
|
if teu_eff_match:
|
||||||
|
try:
|
||||||
|
teu = int(teu_eff_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"转堆作业 TEU 解析失败: {teu_eff_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
vehicles = None
|
||||||
|
if vehicles_match:
|
||||||
|
try:
|
||||||
|
vehicles = int(vehicles_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"转堆作业车辆数解析失败: {vehicles_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
# 解析尺寸箱量
|
||||||
|
twenty_feet = None
|
||||||
|
forty_feet = None
|
||||||
|
|
||||||
|
# 尝试多种格式匹配
|
||||||
|
size_pattern = None
|
||||||
|
|
||||||
|
# 格式1: TEU,(20尺*2,40尺*3) 或 TEU(20尺*2,40尺*3)
|
||||||
|
size_pattern = re.search(r'TEU[,,\s]*(([^)]+))', cleaned)
|
||||||
|
if not size_pattern:
|
||||||
|
# 格式2: TEU(20尺*2,40尺*3)- 无空格版本
|
||||||
|
size_pattern = re.search(r'TEU[((]([^))]+)[))]', cleaned)
|
||||||
|
if not size_pattern:
|
||||||
|
# 格式3: 作业量/效率:100TEU,20尺*2,40尺*3
|
||||||
|
size_pattern = re.search(r'(\d+)尺\*(\d+)', cleaned)
|
||||||
|
|
||||||
|
if size_pattern:
|
||||||
|
size_text = size_pattern.group(1)
|
||||||
|
|
||||||
|
# 尝试直接匹配 20尺*数字 或 40尺*数字
|
||||||
|
twenty_match = re.search(r'20尺\s*[×x*]?\s*(\d+)', size_text)
|
||||||
|
if not twenty_match:
|
||||||
|
twenty_match = re.search(r'(\d+)\s*×\s*20尺', size_text)
|
||||||
|
|
||||||
|
if twenty_match:
|
||||||
|
try:
|
||||||
|
twenty_feet = int(twenty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"转堆作业 20尺箱量解析失败: {twenty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
forty_match = re.search(r'40尺\s*[×x*]?\s*(\d+)', size_text)
|
||||||
|
if not forty_match:
|
||||||
|
forty_match = re.search(r'(\d+)\s*×\s*40尺', size_text)
|
||||||
|
|
||||||
|
if forty_match:
|
||||||
|
try:
|
||||||
|
forty_feet = int(forty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"转堆作业 40尺箱量解析失败: {forty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
# 备用解析:直接在cleaned中查找尺寸信息
|
||||||
|
if twenty_feet is None:
|
||||||
|
twenty_match = re.search(r'20尺\s*[×x*]?\s*(\d+)', cleaned)
|
||||||
|
if twenty_match:
|
||||||
|
try:
|
||||||
|
twenty_feet = int(twenty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"转堆作业 20尺箱量解析失败: {twenty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
if forty_feet is None:
|
||||||
|
forty_match = re.search(r'40尺\s*[×x*]?\s*(\d+)', cleaned)
|
||||||
|
if forty_match:
|
||||||
|
try:
|
||||||
|
forty_feet = int(forty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"转堆作业 40尺箱量解析失败: {forty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
# 使用 "转堆作业" 作为船名标识
|
||||||
|
log = ShipLog(
|
||||||
|
date=date,
|
||||||
|
shift=shift,
|
||||||
|
ship_name='转堆作业',
|
||||||
|
teu=teu,
|
||||||
|
efficiency=None,
|
||||||
|
vehicles=vehicles,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"解析转堆作业: {date} {shift} {log.teu}TEU")
|
||||||
|
return log
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"解析转堆作业失败: {date} {shift}, 错误: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_log_entry(self, cleaned: str, date: str, shift: str, ship_name: str, logs: List[ShipLog]) -> None:
|
||||||
|
"""解析单条日志记录(车辆数、TEU、尺寸箱量)"""
|
||||||
|
try:
|
||||||
|
vehicles_match = re.search(r'上场车辆数:(\d+)', cleaned)
|
||||||
|
teu_eff_match = re.search(r'作业量/效率:(\d+)TEU[,,\s]*', cleaned)
|
||||||
|
|
||||||
|
# 解析TEU
|
||||||
|
teu = None
|
||||||
|
if teu_eff_match:
|
||||||
|
try:
|
||||||
|
teu = int(teu_eff_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"TEU解析失败: {teu_eff_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
# 解析车辆数
|
||||||
|
vehicles = None
|
||||||
|
if vehicles_match:
|
||||||
|
try:
|
||||||
|
vehicles = int(vehicles_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"车辆数解析失败: {vehicles_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
# 解析尺寸箱量
|
||||||
|
twenty_feet = None
|
||||||
|
forty_feet = None
|
||||||
|
|
||||||
|
# 尝试多种格式匹配
|
||||||
|
size_pattern = None
|
||||||
|
|
||||||
|
# 格式1: TEU,(20尺*2,40尺*3) 或 TEU(20尺*2,40尺*3)
|
||||||
|
size_pattern = re.search(r'TEU[,,\s]*(([^)]+))', cleaned)
|
||||||
|
if not size_pattern:
|
||||||
|
# 格式2: TEU(20尺*2,40尺*3)- 无空格版本
|
||||||
|
size_pattern = re.search(r'TEU[((]([^))]+)[))]', cleaned)
|
||||||
|
if not size_pattern:
|
||||||
|
# 格式3: 作业量/效率:100TEU,20尺*2,40尺*3
|
||||||
|
size_pattern = re.search(r'(\d+)尺\*(\d+)', cleaned)
|
||||||
|
|
||||||
|
if size_pattern:
|
||||||
|
size_text = size_pattern.group(1)
|
||||||
|
|
||||||
|
# 尝试直接匹配 20尺*数字 或 40尺*数字
|
||||||
|
twenty_match = re.search(r'20尺\s*[×x*]?\s*(\d+)', size_text)
|
||||||
|
if not twenty_match:
|
||||||
|
twenty_match = re.search(r'(\d+)\s*×\s*20尺', size_text)
|
||||||
|
|
||||||
|
if twenty_match:
|
||||||
|
try:
|
||||||
|
twenty_feet = int(twenty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"20尺箱量解析失败: {twenty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
forty_match = re.search(r'40尺\s*[×x*]?\s*(\d+)', size_text)
|
||||||
|
if not forty_match:
|
||||||
|
forty_match = re.search(r'(\d+)\s*×\s*40尺', size_text)
|
||||||
|
|
||||||
|
if forty_match:
|
||||||
|
try:
|
||||||
|
forty_feet = int(forty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"40尺箱量解析失败: {forty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
# 备用解析:直接在cleaned中查找尺寸信息
|
||||||
|
if twenty_feet is None:
|
||||||
|
twenty_match = re.search(r'20尺\s*[×x*]?\s*(\d+)', cleaned)
|
||||||
|
if twenty_match:
|
||||||
|
try:
|
||||||
|
twenty_feet = int(twenty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"20尺箱量解析失败: {twenty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
if forty_feet is None:
|
||||||
|
forty_match = re.search(r'40尺\s*[×x*]?\s*(\d+)', cleaned)
|
||||||
|
if forty_match:
|
||||||
|
try:
|
||||||
|
forty_feet = int(forty_match.group(1))
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"40尺箱量解析失败: {forty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
|
log = ShipLog(
|
||||||
|
date=date,
|
||||||
|
shift=shift,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
efficiency=None,
|
||||||
|
vehicles=vehicles,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet
|
||||||
|
)
|
||||||
|
logs.append(log)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"解析日志记录失败: {date} {shift} {ship_name}, 错误: {e}")
|
||||||
|
|
||||||
def parse_from_file(self, filepath: str) -> List[ShipLog]:
|
def parse_from_file(self, filepath: str) -> List[ShipLog]:
|
||||||
"""
|
"""
|
||||||
从文件解析日志
|
从文件解析日志
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
teu INTEGER,
|
teu INTEGER,
|
||||||
efficiency REAL,
|
efficiency REAL,
|
||||||
vehicles INTEGER,
|
vehicles INTEGER,
|
||||||
|
twenty_feet INTEGER, -- 20尺箱量
|
||||||
|
forty_feet INTEGER, -- 40尺箱量
|
||||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||||
UNIQUE(date, shift, ship_name) ON CONFLICT REPLACE
|
UNIQUE(date, shift, ship_name) ON CONFLICT REPLACE
|
||||||
)
|
)
|
||||||
@@ -48,8 +50,8 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
# 检查是否需要迁移旧表结构
|
# 检查是否需要迁移旧表结构
|
||||||
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='daily_handover_logs'")
|
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='daily_handover_logs'")
|
||||||
table_sql = cursor.fetchone()[0]
|
table_sql = cursor.fetchone()[0]
|
||||||
if 'UNIQUE' not in table_sql:
|
if 'twenty_feet' not in table_sql or 'forty_feet' not in table_sql:
|
||||||
logger.warning("检测到旧表结构,正在迁移...")
|
logger.warning("检测到旧表结构,正在迁移以添加尺寸箱量字段...")
|
||||||
|
|
||||||
# 重命名旧表
|
# 重命名旧表
|
||||||
cursor.execute('ALTER TABLE daily_handover_logs RENAME TO daily_handover_logs_old')
|
cursor.execute('ALTER TABLE daily_handover_logs RENAME TO daily_handover_logs_old')
|
||||||
@@ -64,6 +66,8 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
teu INTEGER,
|
teu INTEGER,
|
||||||
efficiency REAL,
|
efficiency REAL,
|
||||||
vehicles INTEGER,
|
vehicles INTEGER,
|
||||||
|
twenty_feet INTEGER, -- 20尺箱量
|
||||||
|
forty_feet INTEGER, -- 40尺箱量
|
||||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||||
UNIQUE(date, shift, ship_name) ON CONFLICT REPLACE
|
UNIQUE(date, shift, ship_name) ON CONFLICT REPLACE
|
||||||
)
|
)
|
||||||
@@ -79,7 +83,7 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
|
|
||||||
# 删除旧表
|
# 删除旧表
|
||||||
cursor.execute('DROP TABLE daily_handover_logs_old')
|
cursor.execute('DROP TABLE daily_handover_logs_old')
|
||||||
logger.info("迁移完成!")
|
logger.info("迁移完成!已添加尺寸箱量字段")
|
||||||
|
|
||||||
# 创建索引
|
# 创建索引
|
||||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_date ON daily_handover_logs(date)')
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_date ON daily_handover_logs(date)')
|
||||||
@@ -96,6 +100,57 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
# 创建手动调整数据表
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS manual_adjustments (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
date TEXT NOT NULL, -- 调整适用的日期(目标日期)
|
||||||
|
ship_name TEXT NOT NULL, -- 船名
|
||||||
|
teu INTEGER NOT NULL, -- TEU数量
|
||||||
|
twenty_feet INTEGER DEFAULT 0, -- 20尺箱量
|
||||||
|
forty_feet INTEGER DEFAULT 0, -- 40尺箱量
|
||||||
|
adjustment_type TEXT NOT NULL, -- 'add' 或 'exclude'
|
||||||
|
note TEXT, -- 备注
|
||||||
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# 检查是否需要添加新字段
|
||||||
|
cursor.execute("PRAGMA table_info(manual_adjustments)")
|
||||||
|
columns = [col[1] for col in cursor.fetchall()]
|
||||||
|
|
||||||
|
# 添加缺失的字段
|
||||||
|
if 'source_date' not in columns:
|
||||||
|
cursor.execute('ALTER TABLE manual_adjustments ADD COLUMN source_date TEXT')
|
||||||
|
logger.info("已添加 source_date 字段到 manual_adjustments 表")
|
||||||
|
|
||||||
|
if 'reason' not in columns:
|
||||||
|
cursor.execute('ALTER TABLE manual_adjustments ADD COLUMN reason TEXT')
|
||||||
|
logger.info("已添加 reason 字段到 manual_adjustments 表")
|
||||||
|
|
||||||
|
if 'status' not in columns:
|
||||||
|
cursor.execute('ALTER TABLE manual_adjustments ADD COLUMN status TEXT DEFAULT "pending"')
|
||||||
|
logger.info("已添加 status 字段到 manual_adjustments 表")
|
||||||
|
|
||||||
|
# 创建索引
|
||||||
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_manual_date ON manual_adjustments(date)')
|
||||||
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_manual_type ON manual_adjustments(adjustment_type)')
|
||||||
|
|
||||||
|
# 创建Confluence页面ID映射表
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS confluence_pages (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
month_key TEXT NOT NULL UNIQUE, -- 月份键,格式:'2025-12', '2026-01'
|
||||||
|
page_id TEXT NOT NULL, -- Confluence页面ID
|
||||||
|
page_title TEXT, -- 页面标题(可选)
|
||||||
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# 创建索引
|
||||||
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_confluence_month ON confluence_pages(month_key)')
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
logger.debug("数据库表结构初始化完成")
|
logger.debug("数据库表结构初始化完成")
|
||||||
|
|
||||||
@@ -112,12 +167,13 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
try:
|
try:
|
||||||
query = '''
|
query = '''
|
||||||
INSERT OR REPLACE INTO daily_handover_logs
|
INSERT OR REPLACE INTO daily_handover_logs
|
||||||
(date, shift, ship_name, teu, efficiency, vehicles, created_at)
|
(date, shift, ship_name, teu, efficiency, vehicles, twenty_feet, forty_feet, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||||
'''
|
'''
|
||||||
params = (
|
params = (
|
||||||
log['date'], log['shift'], log['ship_name'],
|
log['date'], log['shift'], log['ship_name'],
|
||||||
log.get('teu'), log.get('efficiency'), log.get('vehicles')
|
log.get('teu'), log.get('efficiency'), log.get('vehicles'),
|
||||||
|
log.get('twenty_feet'), log.get('forty_feet')
|
||||||
)
|
)
|
||||||
|
|
||||||
self.execute_update(query, params)
|
self.execute_update(query, params)
|
||||||
@@ -287,6 +343,74 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
result = self.execute_query(query, (year_month,))
|
result = self.execute_query(query, (year_month,))
|
||||||
return result[0]['teu'] if result else 0
|
return result[0]['teu'] if result else 0
|
||||||
|
|
||||||
|
def reduce_unaccounted(self, year_month: str, teu_to_reduce: int) -> bool:
|
||||||
|
"""
|
||||||
|
减少指定月份的未统计数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
year_month: 年月字符串,格式 "2025-12"
|
||||||
|
teu_to_reduce: 要减少的TEU数量
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 先获取当前值
|
||||||
|
current_teu = self.get_unaccounted(year_month)
|
||||||
|
|
||||||
|
# 计算新值(允许负数)
|
||||||
|
new_teu = current_teu - teu_to_reduce
|
||||||
|
|
||||||
|
if new_teu == 0:
|
||||||
|
# 如果减少后等于0,则删除记录
|
||||||
|
query = 'DELETE FROM monthly_unaccounted WHERE year_month = ?'
|
||||||
|
self.execute_update(query, (year_month,))
|
||||||
|
logger.info(f"减少未统计数据后删除记录: {year_month},原值: {current_teu}TEU,减少: {teu_to_reduce}TEU,新值: 0TEU")
|
||||||
|
else:
|
||||||
|
# 更新记录(允许负数)
|
||||||
|
query = '''
|
||||||
|
INSERT OR REPLACE INTO monthly_unaccounted
|
||||||
|
(year_month, teu, note, created_at)
|
||||||
|
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
||||||
|
'''
|
||||||
|
# 保留原有备注
|
||||||
|
note_query = 'SELECT note FROM monthly_unaccounted WHERE year_month = ?'
|
||||||
|
note_result = self.execute_query(note_query, (year_month,))
|
||||||
|
note = note_result[0]['note'] if note_result else ''
|
||||||
|
|
||||||
|
self.execute_update(query, (year_month, new_teu, note))
|
||||||
|
logger.info(f"减少未统计数据: {year_month},原值: {current_teu}TEU,减少: {teu_to_reduce}TEU,新值: {new_teu}TEU")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"减少未统计数据失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_unaccounted(self, year_month: str) -> bool:
|
||||||
|
"""
|
||||||
|
删除指定月份的未统计数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
year_month: 年月字符串,格式 "2025-12"
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功删除(如果记录不存在也返回True)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
query = 'DELETE FROM monthly_unaccounted WHERE year_month = ?'
|
||||||
|
result = self.execute_update(query, (year_month,))
|
||||||
|
if result > 0:
|
||||||
|
logger.info(f"删除未统计数据: {year_month}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"未找到 {year_month} 月的未统计数据")
|
||||||
|
return True # 记录不存在也算成功
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除未统计数据失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def delete_by_date(self, date: str) -> int:
|
def delete_by_date(self, date: str) -> int:
|
||||||
"""
|
"""
|
||||||
删除指定日期的记录
|
删除指定日期的记录
|
||||||
@@ -300,6 +424,458 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
query = 'DELETE FROM daily_handover_logs WHERE date = ?'
|
query = 'DELETE FROM daily_handover_logs WHERE date = ?'
|
||||||
return self.execute_update(query, (date,))
|
return self.execute_update(query, (date,))
|
||||||
|
|
||||||
|
def insert_manual_adjustment(self, date: str, ship_name: str, teu: int,
|
||||||
|
twenty_feet: int = 0, forty_feet: int = 0,
|
||||||
|
adjustment_type: str = 'add', note: str = '',
|
||||||
|
source_date: str = None, reason: str = '',
|
||||||
|
status: str = 'pending') -> bool:
|
||||||
|
"""
|
||||||
|
插入手动调整数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
date: 日期字符串(目标日期)
|
||||||
|
ship_name: 船名
|
||||||
|
teu: TEU数量
|
||||||
|
twenty_feet: 20尺箱量
|
||||||
|
forty_feet: 40尺箱量
|
||||||
|
adjustment_type: 调整类型 'add' 或 'exclude'
|
||||||
|
note: 备注
|
||||||
|
source_date: 源日期(上月底日期,可选)
|
||||||
|
reason: 调整原因
|
||||||
|
status: 调整状态:'pending', 'processed'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
query = '''
|
||||||
|
INSERT INTO manual_adjustments
|
||||||
|
(date, source_date, ship_name, teu, twenty_feet, forty_feet,
|
||||||
|
adjustment_type, note, reason, status, created_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||||
|
'''
|
||||||
|
params = (date, source_date, ship_name, teu, twenty_feet, forty_feet,
|
||||||
|
adjustment_type, note, reason, status)
|
||||||
|
self.execute_update(query, params)
|
||||||
|
logger.info(f"插入手动调整数据: {date} {ship_name} {teu}TEU ({adjustment_type})")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"插入手动调整数据失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_manual_adjustments(self, date: str) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取指定日期的所有手动调整数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
date: 日期字符串
|
||||||
|
|
||||||
|
返回:
|
||||||
|
手动调整数据列表
|
||||||
|
"""
|
||||||
|
query = '''
|
||||||
|
SELECT * FROM manual_adjustments
|
||||||
|
WHERE date = ? ORDER BY created_at DESC
|
||||||
|
'''
|
||||||
|
return self.execute_query(query, (date,))
|
||||||
|
|
||||||
|
def get_manual_adjustments_by_type(self, date: str, adjustment_type: str) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取指定日期和类型的调整数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
date: 日期字符串
|
||||||
|
adjustment_type: 调整类型 'add' 或 'exclude'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
手动调整数据列表
|
||||||
|
"""
|
||||||
|
query = '''
|
||||||
|
SELECT * FROM manual_adjustments
|
||||||
|
WHERE date = ? AND adjustment_type = ? ORDER BY created_at DESC
|
||||||
|
'''
|
||||||
|
return self.execute_query(query, (date, adjustment_type))
|
||||||
|
|
||||||
|
def get_cross_month_adjustments(self, source_date: str = None, target_date: str = None,
|
||||||
|
status: str = None) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取跨月调整数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
source_date: 源日期(上月底日期)
|
||||||
|
target_date: 目标日期(次月日期)
|
||||||
|
status: 调整状态
|
||||||
|
|
||||||
|
返回:
|
||||||
|
跨月调整数据列表
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
conditions = []
|
||||||
|
params = []
|
||||||
|
|
||||||
|
if source_date:
|
||||||
|
conditions.append("source_date = ?")
|
||||||
|
params.append(source_date)
|
||||||
|
|
||||||
|
if target_date:
|
||||||
|
conditions.append("date = ?")
|
||||||
|
params.append(target_date)
|
||||||
|
|
||||||
|
if status:
|
||||||
|
conditions.append("status = ?")
|
||||||
|
params.append(status)
|
||||||
|
|
||||||
|
where_clause = " AND ".join(conditions) if conditions else "1=1"
|
||||||
|
query = f'''
|
||||||
|
SELECT * FROM manual_adjustments
|
||||||
|
WHERE {where_clause} ORDER BY created_at DESC
|
||||||
|
'''
|
||||||
|
|
||||||
|
return self.execute_query(query, tuple(params))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取跨月调整数据失败: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_pending_cross_month_adjustments(self) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取待处理的跨月调整数据
|
||||||
|
|
||||||
|
返回:
|
||||||
|
待处理的跨月调整数据列表
|
||||||
|
"""
|
||||||
|
return self.get_cross_month_adjustments(status='pending')
|
||||||
|
|
||||||
|
def update_adjustment_status(self, adjustment_id: int, status: str) -> bool:
|
||||||
|
"""
|
||||||
|
更新调整状态
|
||||||
|
|
||||||
|
参数:
|
||||||
|
adjustment_id: 调整记录ID
|
||||||
|
status: 新状态
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
query = 'UPDATE manual_adjustments SET status = ? WHERE id = ?'
|
||||||
|
result = self.execute_update(query, (status, adjustment_id))
|
||||||
|
if result > 0:
|
||||||
|
logger.info(f"更新调整状态: ID={adjustment_id} -> {status}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"未找到调整记录: ID={adjustment_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"更新调整状态失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def insert_cross_month_exclusion(self, source_date: str, target_date: str,
|
||||||
|
ship_name: str, teu: int,
|
||||||
|
twenty_feet: int = 0, forty_feet: int = 0,
|
||||||
|
reason: str = '') -> bool:
|
||||||
|
"""
|
||||||
|
插入跨月剔除调整(手动剔除次月多统计的船)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
source_date: 源日期(上月底日期)
|
||||||
|
target_date: 目标日期(次月日期)
|
||||||
|
ship_name: 船名
|
||||||
|
teu: TEU数量
|
||||||
|
twenty_feet: 20尺箱量
|
||||||
|
forty_feet: 40尺箱量
|
||||||
|
reason: 调整原因
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 1. 插入剔除记录(从月底最后一天扣除)
|
||||||
|
exclude_success = self.insert_manual_adjustment(
|
||||||
|
date=source_date,
|
||||||
|
source_date=source_date,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet,
|
||||||
|
adjustment_type='exclude',
|
||||||
|
note=f"手动剔除次月多统计的船,目标日期: {target_date}",
|
||||||
|
reason=reason,
|
||||||
|
status='pending'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exclude_success:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 2. 自动将相同数据添加到次月1号
|
||||||
|
add_success = self.insert_manual_adjustment(
|
||||||
|
date=target_date,
|
||||||
|
source_date=source_date,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet,
|
||||||
|
adjustment_type='add',
|
||||||
|
note=f"从{source_date}转移的数据: {reason}",
|
||||||
|
reason=reason,
|
||||||
|
status='pending'
|
||||||
|
)
|
||||||
|
|
||||||
|
if add_success:
|
||||||
|
logger.info(f"插入跨月剔除调整: {source_date} -> {target_date} {ship_name} {teu}TEU")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error(f"插入跨月剔除调整失败: 添加数据到次月1号失败")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"插入跨月剔除调整失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_manual_adjustment(self, adjustment_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
删除指定ID的手动调整数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
adjustment_id: 调整记录ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功删除
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
query = 'DELETE FROM manual_adjustments WHERE id = ?'
|
||||||
|
result = self.execute_update(query, (adjustment_id,))
|
||||||
|
if result > 0:
|
||||||
|
logger.info(f"删除手动调整数据: ID={adjustment_id}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"未找到手动调整数据: ID={adjustment_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除手动调整数据失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def clear_manual_adjustments(self, date: str) -> int:
|
||||||
|
"""
|
||||||
|
清除指定日期的所有手动调整数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
date: 日期字符串
|
||||||
|
|
||||||
|
返回:
|
||||||
|
删除的记录数
|
||||||
|
"""
|
||||||
|
query = 'DELETE FROM manual_adjustments WHERE date = ?'
|
||||||
|
result = self.execute_update(query, (date,))
|
||||||
|
logger.info(f"清除 {date} 的手动调整数据: {result} 条记录")
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_daily_data_with_adjustments(self, date: str) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
获取指定日期的数据(包含手动调整)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
date: 日期字符串
|
||||||
|
|
||||||
|
返回:
|
||||||
|
包含调整的每日数据字典
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 获取原始数据
|
||||||
|
logs = self.query_by_date(date)
|
||||||
|
|
||||||
|
# 获取手动调整数据
|
||||||
|
adjustments = self.get_manual_adjustments(date)
|
||||||
|
|
||||||
|
# 按船名汇总原始数据
|
||||||
|
ships: Dict[str, Dict[str, Any]] = {}
|
||||||
|
for log in logs:
|
||||||
|
ship = log['ship_name']
|
||||||
|
if ship not in ships:
|
||||||
|
ships[ship] = {
|
||||||
|
'teu': 0,
|
||||||
|
'twenty_feet': 0,
|
||||||
|
'forty_feet': 0,
|
||||||
|
'adjustments': []
|
||||||
|
}
|
||||||
|
if log.get('teu'):
|
||||||
|
ships[ship]['teu'] += log['teu']
|
||||||
|
if log.get('twenty_feet'):
|
||||||
|
ships[ship]['twenty_feet'] += log['twenty_feet']
|
||||||
|
if log.get('forty_feet'):
|
||||||
|
ships[ship]['forty_feet'] += log['forty_feet']
|
||||||
|
|
||||||
|
# 应用调整数据
|
||||||
|
total_adjustment_teu = 0
|
||||||
|
for adj in adjustments:
|
||||||
|
ship = adj['ship_name']
|
||||||
|
if ship not in ships:
|
||||||
|
ships[ship] = {
|
||||||
|
'teu': 0,
|
||||||
|
'twenty_feet': 0,
|
||||||
|
'forty_feet': 0,
|
||||||
|
'adjustments': []
|
||||||
|
}
|
||||||
|
|
||||||
|
# 记录调整
|
||||||
|
ships[ship]['adjustments'].append(adj)
|
||||||
|
|
||||||
|
# 根据调整类型计算
|
||||||
|
if adj['adjustment_type'] == 'add':
|
||||||
|
ships[ship]['teu'] += adj['teu']
|
||||||
|
ships[ship]['twenty_feet'] += adj['twenty_feet']
|
||||||
|
ships[ship]['forty_feet'] += adj['forty_feet']
|
||||||
|
total_adjustment_teu += adj['teu']
|
||||||
|
elif adj['adjustment_type'] == 'exclude':
|
||||||
|
ships[ship]['teu'] -= adj['teu']
|
||||||
|
ships[ship]['twenty_feet'] -= adj['twenty_feet']
|
||||||
|
ships[ship]['forty_feet'] -= adj['forty_feet']
|
||||||
|
total_adjustment_teu -= adj['teu']
|
||||||
|
|
||||||
|
# 计算总TEU
|
||||||
|
total_teu = sum(ship_data['teu'] for ship_data in ships.values())
|
||||||
|
|
||||||
|
return {
|
||||||
|
'date': date,
|
||||||
|
'ships': ships,
|
||||||
|
'total_teu': total_teu,
|
||||||
|
'ship_count': len(ships),
|
||||||
|
'adjustments': adjustments,
|
||||||
|
'total_adjustment_teu': total_adjustment_teu
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取包含调整的每日数据失败: {date}, 错误: {e}")
|
||||||
|
return {
|
||||||
|
'date': date,
|
||||||
|
'ships': {},
|
||||||
|
'total_teu': 0,
|
||||||
|
'ship_count': 0,
|
||||||
|
'adjustments': [],
|
||||||
|
'total_adjustment_teu': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
def insert_confluence_page(self, month_key: str, page_id: str, page_title: str = '') -> bool:
|
||||||
|
"""
|
||||||
|
插入或更新Confluence页面ID映射
|
||||||
|
|
||||||
|
参数:
|
||||||
|
month_key: 月份键,格式:'2025-12', '2026-01'
|
||||||
|
page_id: Confluence页面ID
|
||||||
|
page_title: 页面标题(可选)
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
query = '''
|
||||||
|
INSERT OR REPLACE INTO confluence_pages
|
||||||
|
(month_key, page_id, page_title, updated_at)
|
||||||
|
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
||||||
|
'''
|
||||||
|
params = (month_key, page_id, page_title)
|
||||||
|
self.execute_update(query, params)
|
||||||
|
logger.info(f"插入Confluence页面映射: {month_key} -> {page_id}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"插入Confluence页面映射失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_confluence_page(self, month_key: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取指定月份的Confluence页面ID映射
|
||||||
|
|
||||||
|
参数:
|
||||||
|
month_key: 月份键,格式:'2025-12', '2026-01'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
页面映射字典,如果不存在则返回None
|
||||||
|
"""
|
||||||
|
query = 'SELECT * FROM confluence_pages WHERE month_key = ?'
|
||||||
|
result = self.execute_query(query, (month_key,))
|
||||||
|
return result[0] if result else None
|
||||||
|
|
||||||
|
def get_all_confluence_pages(self) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取所有Confluence页面ID映射
|
||||||
|
|
||||||
|
返回:
|
||||||
|
页面映射列表
|
||||||
|
"""
|
||||||
|
query = 'SELECT * FROM confluence_pages ORDER BY month_key DESC'
|
||||||
|
return self.execute_query(query)
|
||||||
|
|
||||||
|
def delete_confluence_page(self, month_key: str) -> bool:
|
||||||
|
"""
|
||||||
|
删除指定月份的Confluence页面ID映射
|
||||||
|
|
||||||
|
参数:
|
||||||
|
month_key: 月份键,格式:'2025-12', '2026-01'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功删除
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
query = 'DELETE FROM confluence_pages WHERE month_key = ?'
|
||||||
|
result = self.execute_update(query, (month_key,))
|
||||||
|
if result > 0:
|
||||||
|
logger.info(f"删除Confluence页面映射: {month_key}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"未找到Confluence页面映射: {month_key}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除Confluence页面映射失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_confluence_page_for_date(self, date: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
根据日期获取对应的Confluence页面ID
|
||||||
|
|
||||||
|
参数:
|
||||||
|
date: 日期字符串,格式:'2025-12-31'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
Confluence页面ID,如果不存在则返回None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 从日期中提取年月
|
||||||
|
year_month = date[:7] # '2025-12-31' -> '2025-12'
|
||||||
|
|
||||||
|
# 查询数据库
|
||||||
|
page_info = self.get_confluence_page(year_month)
|
||||||
|
if page_info:
|
||||||
|
return page_info['page_id']
|
||||||
|
|
||||||
|
# 如果没有找到,尝试从环境变量中获取
|
||||||
|
import os
|
||||||
|
from src.config import config
|
||||||
|
|
||||||
|
# 检查环境变量中的配置
|
||||||
|
env_key = f"CONFLUENCE_PAGE_{year_month.replace('-', '_')}"
|
||||||
|
page_id = os.getenv(env_key)
|
||||||
|
if page_id:
|
||||||
|
# 保存到数据库以便下次使用
|
||||||
|
self.insert_confluence_page(year_month, page_id, f"从环境变量获取: {env_key}")
|
||||||
|
return page_id
|
||||||
|
|
||||||
|
# 使用默认配置
|
||||||
|
default_page_id = config.CONFLUENCE_CONTENT_ID
|
||||||
|
if default_page_id:
|
||||||
|
logger.warning(f"未找到 {year_month} 的Confluence页面映射,使用默认页面ID: {default_page_id}")
|
||||||
|
return default_page_id
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取Confluence页面ID失败: {date}, 错误: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 测试代码
|
# 测试代码
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
"""
|
"""
|
||||||
飞书表格 API 客户端模块
|
飞书表格 API 客户端模块
|
||||||
统一版本,支持月度表格和年度表格
|
统一版本,支持月度表格和年度表格
|
||||||
|
支持自动获取和刷新 tenant_access_token
|
||||||
"""
|
"""
|
||||||
import requests
|
import requests
|
||||||
from typing import Dict, List, Optional
|
import time
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from src.config import config
|
from src.config import config
|
||||||
@@ -22,31 +24,146 @@ class FeishuSheetsClient:
|
|||||||
"""飞书表格 API 客户端"""
|
"""飞书表格 API 客户端"""
|
||||||
|
|
||||||
def __init__(self, base_url: Optional[str] = None, token: Optional[str] = None,
|
def __init__(self, base_url: Optional[str] = None, token: Optional[str] = None,
|
||||||
spreadsheet_token: Optional[str] = None):
|
spreadsheet_token: Optional[str] = None, app_id: Optional[str] = None,
|
||||||
|
app_secret: Optional[str] = None):
|
||||||
"""
|
"""
|
||||||
初始化客户端
|
初始化客户端
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
base_url: 飞书 API 基础URL,如果为None则使用配置
|
base_url: 飞书 API 基础URL,如果为None则使用配置
|
||||||
token: Bearer 认证令牌,如果为None则使用配置
|
token: Bearer 认证令牌,如果为None则使用配置或自动获取
|
||||||
spreadsheet_token: 表格 token,如果为None则使用配置
|
spreadsheet_token: 表格 token,如果为None则使用配置
|
||||||
|
app_id: 飞书应用ID,用于获取tenant_access_token
|
||||||
|
app_secret: 飞书应用密钥,用于获取tenant_access_token
|
||||||
"""
|
"""
|
||||||
self.base_url = (base_url or config.FEISHU_BASE_URL).rstrip('/')
|
self.base_url = (base_url or config.FEISHU_BASE_URL).rstrip('/')
|
||||||
self.spreadsheet_token = spreadsheet_token or config.FEISHU_SPREADSHEET_TOKEN
|
self.spreadsheet_token = spreadsheet_token or config.FEISHU_SPREADSHEET_TOKEN
|
||||||
self.token = token or config.FEISHU_TOKEN
|
self.app_id = app_id or config.FEISHU_APP_ID
|
||||||
|
self.app_secret = app_secret or config.FEISHU_APP_SECRET
|
||||||
|
|
||||||
self.headers = {
|
# Token管理相关属性
|
||||||
'Authorization': f'Bearer {self.token}',
|
self._token = token or config.FEISHU_TOKEN
|
||||||
'Content-Type': 'application/json',
|
self._token_expire_time = 0 # token过期时间戳
|
||||||
'Accept': 'application/json'
|
self._token_obtained_time = 0 # token获取时间戳
|
||||||
}
|
|
||||||
|
|
||||||
# 使用 Session 重用连接
|
# 使用 Session 重用连接
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.headers.update(self.headers)
|
|
||||||
self.session.timeout = config.REQUEST_TIMEOUT
|
self.session.timeout = config.REQUEST_TIMEOUT
|
||||||
|
|
||||||
|
# 初始化headers
|
||||||
|
self._update_session_headers()
|
||||||
|
|
||||||
logger.debug(f"飞书客户端初始化完成,基础URL: {self.base_url}")
|
logger.debug(f"飞书客户端初始化完成,基础URL: {self.base_url}")
|
||||||
|
logger.debug(f"使用应用ID: {self.app_id[:8]}... 如果配置" if self.app_id else "未配置应用ID")
|
||||||
|
|
||||||
|
def _update_session_headers(self):
|
||||||
|
"""更新session的headers"""
|
||||||
|
if self._token:
|
||||||
|
self.session.headers.update({
|
||||||
|
'Authorization': f'Bearer {self._token}',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
# 如果没有token,移除Authorization头
|
||||||
|
if 'Authorization' in self.session.headers:
|
||||||
|
del self.session.headers['Authorization']
|
||||||
|
|
||||||
|
def _get_tenant_access_token(self) -> Tuple[str, int]:
|
||||||
|
"""
|
||||||
|
获取tenant_access_token
|
||||||
|
|
||||||
|
返回:
|
||||||
|
(token, expire_time): token字符串和过期时间(秒)
|
||||||
|
|
||||||
|
异常:
|
||||||
|
requests.exceptions.RequestException: 网络请求失败
|
||||||
|
ValueError: API返回错误
|
||||||
|
"""
|
||||||
|
if not self.app_id or not self.app_secret:
|
||||||
|
raise ValueError("未配置飞书应用ID和密钥,无法获取tenant_access_token")
|
||||||
|
|
||||||
|
token_url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"app_id": self.app_id,
|
||||||
|
"app_secret": self.app_secret
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json; charset=utf-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"正在获取tenant_access_token,应用ID: {self.app_id[:8]}...")
|
||||||
|
response = requests.post(token_url, json=payload, headers=headers, timeout=config.REQUEST_TIMEOUT)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get('code') != 0:
|
||||||
|
error_msg = f"获取tenant_access_token失败: {data.get('msg')}"
|
||||||
|
logger.error(error_msg)
|
||||||
|
raise ValueError(error_msg)
|
||||||
|
|
||||||
|
token = data.get('tenant_access_token')
|
||||||
|
expire = data.get('expire', 7200) # 默认2小时
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
raise ValueError("API返回的token为空")
|
||||||
|
|
||||||
|
logger.info(f"成功获取tenant_access_token,有效期: {expire}秒")
|
||||||
|
return token, expire
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"获取tenant_access_token网络请求失败: {e}")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取tenant_access_token失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _ensure_valid_token(self):
|
||||||
|
"""
|
||||||
|
确保当前token有效,如果无效则重新获取
|
||||||
|
|
||||||
|
返回:
|
||||||
|
bool: token是否有效
|
||||||
|
"""
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
# 如果有手动配置的token,直接使用
|
||||||
|
if config.FEISHU_TOKEN and self._token == config.FEISHU_TOKEN:
|
||||||
|
logger.debug("使用手动配置的FEISHU_TOKEN")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 检查token是否过期(提前30分钟刷新)
|
||||||
|
if self._token and self._token_expire_time > 0:
|
||||||
|
time_remaining = self._token_expire_time - current_time
|
||||||
|
if time_remaining > 1800: # 剩余时间大于30分钟
|
||||||
|
logger.debug(f"token仍然有效,剩余时间: {int(time_remaining)}秒")
|
||||||
|
return True
|
||||||
|
elif time_remaining > 0: # 剩余时间小于30分钟但大于0
|
||||||
|
logger.info(f"token即将过期,剩余时间: {int(time_remaining)}秒,重新获取")
|
||||||
|
else: # 已过期
|
||||||
|
logger.info("token已过期,重新获取")
|
||||||
|
|
||||||
|
# 需要获取新token
|
||||||
|
try:
|
||||||
|
token, expire = self._get_tenant_access_token()
|
||||||
|
self._token = token
|
||||||
|
self._token_obtained_time = current_time
|
||||||
|
self._token_expire_time = current_time + expire
|
||||||
|
self._update_session_headers()
|
||||||
|
logger.info(f"token获取成功,将在 {expire} 秒后过期")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取token失败: {e}")
|
||||||
|
# 如果配置了备用token,尝试使用
|
||||||
|
if config.FEISHU_TOKEN and config.FEISHU_TOKEN != self._token:
|
||||||
|
logger.warning("使用备用FEISHU_TOKEN")
|
||||||
|
self._token = config.FEISHU_TOKEN
|
||||||
|
self._update_session_headers()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_sheets_info(self) -> List[Dict[str, str]]:
|
def get_sheets_info(self) -> List[Dict[str, str]]:
|
||||||
"""
|
"""
|
||||||
@@ -59,6 +176,10 @@ class FeishuSheetsClient:
|
|||||||
requests.exceptions.RequestException: 网络请求失败
|
requests.exceptions.RequestException: 网络请求失败
|
||||||
ValueError: API返回错误
|
ValueError: API返回错误
|
||||||
"""
|
"""
|
||||||
|
# 确保token有效
|
||||||
|
if not self._ensure_valid_token():
|
||||||
|
raise FeishuClientError("无法获取有效的飞书token")
|
||||||
|
|
||||||
url = f'{self.base_url}/spreadsheets/{self.spreadsheet_token}/sheets/query'
|
url = f'{self.base_url}/spreadsheets/{self.spreadsheet_token}/sheets/query'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -104,6 +225,10 @@ class FeishuSheetsClient:
|
|||||||
requests.exceptions.RequestException: 网络请求失败
|
requests.exceptions.RequestException: 网络请求失败
|
||||||
ValueError: API返回错误
|
ValueError: API返回错误
|
||||||
"""
|
"""
|
||||||
|
# 确保token有效
|
||||||
|
if not self._ensure_valid_token():
|
||||||
|
raise FeishuClientError("无法获取有效的飞书token")
|
||||||
|
|
||||||
if range_ is None:
|
if range_ is None:
|
||||||
range_ = config.SHEET_RANGE
|
range_ = config.SHEET_RANGE
|
||||||
|
|
||||||
@@ -134,6 +259,26 @@ class FeishuSheetsClient:
|
|||||||
logger.error(f"解析表格数据失败: {e}, sheet_id: {sheet_id}")
|
logger.error(f"解析表格数据失败: {e}, sheet_id: {sheet_id}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def get_token_info(self) -> Dict[str, any]:
|
||||||
|
"""
|
||||||
|
获取当前token信息
|
||||||
|
|
||||||
|
返回:
|
||||||
|
token信息字典
|
||||||
|
"""
|
||||||
|
current_time = time.time()
|
||||||
|
time_remaining = self._token_expire_time - current_time if self._token_expire_time > 0 else 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
'has_token': bool(self._token),
|
||||||
|
'token_preview': self._token[:20] + '...' if self._token and len(self._token) > 20 else self._token,
|
||||||
|
'token_obtained_time': self._token_obtained_time,
|
||||||
|
'token_expire_time': self._token_expire_time,
|
||||||
|
'time_remaining': max(0, time_remaining),
|
||||||
|
'using_app_credentials': bool(self.app_id and self.app_secret),
|
||||||
|
'using_manual_token': bool(config.FEISHU_TOKEN and self._token == config.FEISHU_TOKEN)
|
||||||
|
}
|
||||||
|
|
||||||
def test_connection(self) -> bool:
|
def test_connection(self) -> bool:
|
||||||
"""
|
"""
|
||||||
测试飞书连接是否正常
|
测试飞书连接是否正常
|
||||||
@@ -142,6 +287,12 @@ class FeishuSheetsClient:
|
|||||||
连接是否正常
|
连接是否正常
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
# 首先测试token获取
|
||||||
|
if not self._ensure_valid_token():
|
||||||
|
logger.error("无法获取有效的飞书token")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 然后测试表格访问
|
||||||
sheets = self.get_sheets_info()
|
sheets = self.get_sheets_info()
|
||||||
if sheets:
|
if sheets:
|
||||||
logger.info(f"飞书连接测试成功,找到 {len(sheets)} 个表格")
|
logger.info(f"飞书连接测试成功,找到 {len(sheets)} 个表格")
|
||||||
@@ -153,6 +304,27 @@ class FeishuSheetsClient:
|
|||||||
logger.error(f"飞书连接测试失败: {e}")
|
logger.error(f"飞书连接测试失败: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def refresh_token(self) -> bool:
|
||||||
|
"""
|
||||||
|
强制刷新token
|
||||||
|
|
||||||
|
返回:
|
||||||
|
刷新是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info("强制刷新token...")
|
||||||
|
current_time = time.time()
|
||||||
|
token, expire = self._get_tenant_access_token()
|
||||||
|
self._token = token
|
||||||
|
self._token_obtained_time = current_time
|
||||||
|
self._token_expire_time = current_time + expire
|
||||||
|
self._update_session_headers()
|
||||||
|
logger.info(f"token刷新成功,将在 {expire} 秒后过期")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"强制刷新token失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 测试代码
|
# 测试代码
|
||||||
@@ -164,8 +336,17 @@ if __name__ == '__main__':
|
|||||||
# 测试连接
|
# 测试连接
|
||||||
client = FeishuSheetsClient()
|
client = FeishuSheetsClient()
|
||||||
|
|
||||||
|
# 显示token信息
|
||||||
|
token_info = client.get_token_info()
|
||||||
|
print("当前token信息:")
|
||||||
|
print(f" 是否有token: {token_info['has_token']}")
|
||||||
|
print(f" token预览: {token_info['token_preview']}")
|
||||||
|
print(f" 剩余时间: {int(token_info['time_remaining'])}秒")
|
||||||
|
print(f" 使用应用凭证: {token_info['using_app_credentials']}")
|
||||||
|
print(f" 使用手动token: {token_info['using_manual_token']}")
|
||||||
|
|
||||||
if client.test_connection():
|
if client.test_connection():
|
||||||
print("飞书连接测试成功")
|
print("\n飞书连接测试成功")
|
||||||
|
|
||||||
# 获取表格信息
|
# 获取表格信息
|
||||||
sheets = client.get_sheets_info()
|
sheets = client.get_sheets_info()
|
||||||
@@ -177,6 +358,10 @@ if __name__ == '__main__':
|
|||||||
sheet_id = sheets[0]['sheet_id']
|
sheet_id = sheets[0]['sheet_id']
|
||||||
data = client.get_sheet_data(sheet_id, 'A1:C5')
|
data = client.get_sheet_data(sheet_id, 'A1:C5')
|
||||||
print(f"获取到表格数据,版本: {data.get('revision', '未知')}")
|
print(f"获取到表格数据,版本: {data.get('revision', '未知')}")
|
||||||
|
|
||||||
|
# 再次显示token信息
|
||||||
|
token_info = client.get_token_info()
|
||||||
|
print(f"\n测试后token剩余时间: {int(token_info['time_remaining'])}秒")
|
||||||
else:
|
else:
|
||||||
print("飞书连接测试失败")
|
print("\n飞书连接测试失败")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -42,8 +42,17 @@ class FeishuScheduleManager:
|
|||||||
|
|
||||||
def _check_config(self, token: Optional[str], spreadsheet_token: Optional[str]) -> None:
|
def _check_config(self, token: Optional[str], spreadsheet_token: Optional[str]) -> None:
|
||||||
"""检查必要配置"""
|
"""检查必要配置"""
|
||||||
if not token and not config.FEISHU_TOKEN:
|
# 检查是否有任何可用的认证方式
|
||||||
logger.warning("飞书令牌未配置,排班功能将不可用")
|
has_token = bool(token or config.FEISHU_TOKEN)
|
||||||
|
has_app_credentials = bool(config.FEISHU_APP_ID and config.FEISHU_APP_SECRET)
|
||||||
|
|
||||||
|
if not has_token and not has_app_credentials:
|
||||||
|
logger.warning("飞书认证未配置,排班功能将不可用")
|
||||||
|
logger.warning("请配置 FEISHU_TOKEN 或 FEISHU_APP_ID + FEISHU_APP_SECRET")
|
||||||
|
elif has_app_credentials:
|
||||||
|
logger.info("使用飞书应用凭证自动获取token")
|
||||||
|
elif has_token:
|
||||||
|
logger.info("使用手动配置的FEISHU_TOKEN")
|
||||||
|
|
||||||
if not spreadsheet_token and not config.FEISHU_SPREADSHEET_TOKEN:
|
if not spreadsheet_token and not config.FEISHU_SPREADSHEET_TOKEN:
|
||||||
logger.warning("飞书表格令牌未配置,排班功能将不可用")
|
logger.warning("飞书表格令牌未配置,排班功能将不可用")
|
||||||
@@ -105,6 +114,8 @@ class FeishuScheduleManager:
|
|||||||
"""
|
"""
|
||||||
获取指定日期的排班信息
|
获取指定日期的排班信息
|
||||||
|
|
||||||
|
修复:每次都从飞书获取最新数据并覆盖数据库,确保日报中显示最新排班信息
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
date_str: 日期字符串,格式 "2025-12-30"
|
date_str: 日期字符串,格式 "2025-12-30"
|
||||||
|
|
||||||
@@ -126,22 +137,13 @@ class FeishuScheduleManager:
|
|||||||
|
|
||||||
logger.info(f"获取 {date_str} 的排班信息 (格式: {target_date_mm_dd}/{target_date_chinese})")
|
logger.info(f"获取 {date_str} 的排班信息 (格式: {target_date_mm_dd}/{target_date_chinese})")
|
||||||
|
|
||||||
# 1. 首先尝试从数据库获取
|
# 1. 获取表格信息
|
||||||
cached_schedule = self.db.get_schedule(date_str)
|
|
||||||
if cached_schedule:
|
|
||||||
logger.info(f"从数据库获取 {date_str} 的排班信息")
|
|
||||||
return self._format_db_result(cached_schedule)
|
|
||||||
|
|
||||||
# 2. 数据库中没有,需要从飞书获取
|
|
||||||
logger.info(f"数据库中没有 {date_str} 的排班信息,从飞书获取")
|
|
||||||
|
|
||||||
# 获取表格信息
|
|
||||||
sheets = self.client.get_sheets_info()
|
sheets = self.client.get_sheets_info()
|
||||||
if not sheets:
|
if not sheets:
|
||||||
logger.error("未获取到表格信息")
|
logger.error("未获取到表格信息")
|
||||||
return self._empty_result()
|
return self._empty_result()
|
||||||
|
|
||||||
# 选择最合适的表格
|
# 2. 选择最合适的表格
|
||||||
selected_sheet = self._select_sheet_for_date(sheets, target_year_month)
|
selected_sheet = self._select_sheet_for_date(sheets, target_year_month)
|
||||||
if not selected_sheet:
|
if not selected_sheet:
|
||||||
logger.error("未找到合适的表格")
|
logger.error("未找到合适的表格")
|
||||||
@@ -157,23 +159,12 @@ class FeishuScheduleManager:
|
|||||||
return self._empty_result()
|
return self._empty_result()
|
||||||
|
|
||||||
values = sheet_data.get('valueRange', {}).get('values', [])
|
values = sheet_data.get('valueRange', {}).get('values', [])
|
||||||
revision = sheet_data.get('revision', 0)
|
|
||||||
|
|
||||||
if not values:
|
if not values:
|
||||||
logger.error("表格数据为空")
|
logger.error("表格数据为空")
|
||||||
return self._empty_result()
|
return self._empty_result()
|
||||||
|
|
||||||
# 4. 检查表格是否有更新
|
# 4. 解析数据 - 根据表格类型选择合适的日期格式
|
||||||
need_update = self.db.check_sheet_update(
|
|
||||||
sheet_id, sheet_title, revision, {'values': values}
|
|
||||||
)
|
|
||||||
|
|
||||||
if not need_update and cached_schedule:
|
|
||||||
# 表格无更新,且数据库中有缓存,直接返回
|
|
||||||
logger.info(f"表格无更新,使用数据库缓存")
|
|
||||||
return self._format_db_result(cached_schedule)
|
|
||||||
|
|
||||||
# 5. 解析数据 - 根据表格类型选择合适的日期格式
|
|
||||||
# 如果是年度表格,使用中文日期格式;否则使用mm/dd格式
|
# 如果是年度表格,使用中文日期格式;否则使用mm/dd格式
|
||||||
if '年' in sheet_title and '排班表' in sheet_title:
|
if '年' in sheet_title and '排班表' in sheet_title:
|
||||||
target_date = target_date_chinese # "1月1日"
|
target_date = target_date_chinese # "1月1日"
|
||||||
@@ -183,10 +174,12 @@ class FeishuScheduleManager:
|
|||||||
logger.info(f"使用日期格式: {target_date} 解析表格: {sheet_title}")
|
logger.info(f"使用日期格式: {target_date} 解析表格: {sheet_title}")
|
||||||
result = self.parser.parse(values, target_date, sheet_title)
|
result = self.parser.parse(values, target_date, sheet_title)
|
||||||
|
|
||||||
# 6. 保存到数据库
|
# 5. 每次都保存到数据库,覆盖旧数据,确保人员变动能及时更新
|
||||||
if result['day_shift'] or result['night_shift']:
|
if result['day_shift'] or result['night_shift']:
|
||||||
self.db.save_schedule(date_str, result, sheet_id, sheet_title)
|
self.db.save_schedule(date_str, result, sheet_id, sheet_title)
|
||||||
logger.info(f"已保存 {date_str} 的排班信息到数据库")
|
logger.info(f"已更新 {date_str} 的排班信息到数据库: 白班={result['day_shift']}, 夜班={result['night_shift']}")
|
||||||
|
else:
|
||||||
|
logger.warning(f"解析结果为空,{date_str} 未保存到数据库")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
1280
src/gui.py
1280
src/gui.py
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,13 @@
|
|||||||
"""
|
"""
|
||||||
统一日志配置模块
|
统一日志配置模块
|
||||||
提供统一的日志配置,避免各模块自行配置
|
提供统一的日志配置,避免各模块自行配置
|
||||||
|
支持按日期分片存储日志
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from logging.handlers import RotatingFileHandler
|
from datetime import datetime
|
||||||
|
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from src.config import config
|
from src.config import config
|
||||||
@@ -16,6 +18,8 @@ def setup_logging(
|
|||||||
log_file: Optional[str] = None,
|
log_file: Optional[str] = None,
|
||||||
console_level: int = logging.INFO,
|
console_level: int = logging.INFO,
|
||||||
file_level: int = logging.DEBUG,
|
file_level: int = logging.DEBUG,
|
||||||
|
use_date_split: bool = True,
|
||||||
|
date_folder_format: str = "%Y-%m", # 按月份分文件夹
|
||||||
max_bytes: int = 10 * 1024 * 1024, # 10MB
|
max_bytes: int = 10 * 1024 * 1024, # 10MB
|
||||||
backup_count: int = 5
|
backup_count: int = 5
|
||||||
) -> logging.Logger:
|
) -> logging.Logger:
|
||||||
@@ -26,21 +30,31 @@ def setup_logging(
|
|||||||
log_file: 日志文件路径,如果为None则使用默认路径
|
log_file: 日志文件路径,如果为None则使用默认路径
|
||||||
console_level: 控制台日志级别
|
console_level: 控制台日志级别
|
||||||
file_level: 文件日志级别
|
file_level: 文件日志级别
|
||||||
|
use_date_split: 是否使用日期分片
|
||||||
|
date_folder_format: 日期文件夹格式(默认按月份,如 logs/2025-12/)
|
||||||
max_bytes: 单个日志文件最大大小
|
max_bytes: 单个日志文件最大大小
|
||||||
backup_count: 备份文件数量
|
backup_count: 备份文件数量
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
配置好的根日志器
|
配置好的根日志器
|
||||||
"""
|
"""
|
||||||
# 创建日志目录
|
# 获取当前日期用于构建路径
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
if log_file is None:
|
if log_file is None:
|
||||||
log_dir = 'logs'
|
log_dir = 'logs'
|
||||||
log_file = os.path.join(log_dir, 'app.log')
|
if use_date_split:
|
||||||
|
# 按日期分片:logs/2025-12/2025-12-30.log
|
||||||
|
date_folder = now.strftime(date_folder_format)
|
||||||
|
log_dir = os.path.join('logs', date_folder)
|
||||||
|
log_file = os.path.join(log_dir, now.strftime('%Y-%m-%d.log'))
|
||||||
|
else:
|
||||||
|
log_file = os.path.join(log_dir, 'app.log')
|
||||||
else:
|
else:
|
||||||
log_dir = os.path.dirname(log_file)
|
log_dir = os.path.dirname(log_file)
|
||||||
|
|
||||||
if log_dir and not os.path.exists(log_dir):
|
if log_dir and not os.path.exists(log_dir):
|
||||||
os.makedirs(log_dir)
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
|
||||||
# 获取根日志器
|
# 获取根日志器
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -59,13 +73,28 @@ def setup_logging(
|
|||||||
console_handler.setFormatter(console_formatter)
|
console_handler.setFormatter(console_formatter)
|
||||||
logger.addHandler(console_handler)
|
logger.addHandler(console_handler)
|
||||||
|
|
||||||
# 文件handler(轮转)
|
# 文件handler(日期分片或大小轮转)
|
||||||
file_handler = RotatingFileHandler(
|
if use_date_split:
|
||||||
log_file,
|
# 使用TimedRotatingFileHandler,每天午夜轮转
|
||||||
maxBytes=max_bytes,
|
file_handler = TimedRotatingFileHandler(
|
||||||
backupCount=backup_count,
|
log_file,
|
||||||
encoding='utf-8'
|
when='midnight',
|
||||||
)
|
interval=1,
|
||||||
|
backupCount=backup_count,
|
||||||
|
encoding='utf-8',
|
||||||
|
atTime=datetime.strptime('00:00:00', '%H:%M:%S')
|
||||||
|
)
|
||||||
|
logger.info(f"日志系统已初始化,使用日期分片: {log_file}")
|
||||||
|
else:
|
||||||
|
# 使用RotatingFileHandler,按大小轮转
|
||||||
|
file_handler = RotatingFileHandler(
|
||||||
|
log_file,
|
||||||
|
maxBytes=max_bytes,
|
||||||
|
backupCount=backup_count,
|
||||||
|
encoding='utf-8'
|
||||||
|
)
|
||||||
|
logger.info(f"日志系统已初始化,使用大小轮转: {log_file}")
|
||||||
|
|
||||||
file_handler.setLevel(file_level)
|
file_handler.setLevel(file_level)
|
||||||
file_formatter = logging.Formatter(
|
file_formatter = logging.Formatter(
|
||||||
'%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s',
|
'%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s',
|
||||||
@@ -78,7 +107,6 @@ def setup_logging(
|
|||||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||||
logging.getLogger('requests').setLevel(logging.WARNING)
|
logging.getLogger('requests').setLevel(logging.WARNING)
|
||||||
|
|
||||||
logger.info(f"日志系统已初始化,日志文件: {log_file}")
|
|
||||||
logger.info(f"控制台日志级别: {logging.getLevelName(console_level)}")
|
logger.info(f"控制台日志级别: {logging.getLevelName(console_level)}")
|
||||||
logger.info(f"文件日志级别: {logging.getLevelName(file_level)}")
|
logger.info(f"文件日志级别: {logging.getLevelName(file_level)}")
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class DailyReportGenerator:
|
|||||||
|
|
||||||
def get_daily_data(self, date: str) -> Dict[str, Any]:
|
def get_daily_data(self, date: str) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
获取指定日期的数据
|
获取指定日期的数据(包含手动调整)
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
date: 日期字符串,格式 "YYYY-MM-DD"
|
date: 日期字符串,格式 "YYYY-MM-DD"
|
||||||
@@ -61,22 +61,39 @@ class DailyReportGenerator:
|
|||||||
每日数据字典
|
每日数据字典
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
# 使用数据库的新方法获取包含调整的数据
|
||||||
|
if hasattr(self.db, 'get_daily_data_with_adjustments'):
|
||||||
|
return self.db.get_daily_data_with_adjustments(date)
|
||||||
|
|
||||||
|
# 降级处理:如果没有新方法,使用原始逻辑
|
||||||
logs = self.db.query_by_date(date)
|
logs = self.db.query_by_date(date)
|
||||||
|
|
||||||
# 按船名汇总
|
# 按船名汇总TEU和尺寸箱量
|
||||||
ships: Dict[str, int] = {}
|
ships: Dict[str, Dict[str, Any]] = {}
|
||||||
for log in logs:
|
for log in logs:
|
||||||
ship = log['ship_name']
|
ship = log['ship_name']
|
||||||
if ship not in ships:
|
if ship not in ships:
|
||||||
ships[ship] = 0
|
ships[ship] = {
|
||||||
|
'teu': 0,
|
||||||
|
'twenty_feet': 0,
|
||||||
|
'forty_feet': 0
|
||||||
|
}
|
||||||
if log.get('teu'):
|
if log.get('teu'):
|
||||||
ships[ship] += log['teu']
|
ships[ship]['teu'] += log['teu']
|
||||||
|
if log.get('twenty_feet'):
|
||||||
|
ships[ship]['twenty_feet'] += log['twenty_feet']
|
||||||
|
if log.get('forty_feet'):
|
||||||
|
ships[ship]['forty_feet'] += log['forty_feet']
|
||||||
|
|
||||||
|
total_teu = sum(ship_data['teu'] for ship_data in ships.values())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'date': date,
|
'date': date,
|
||||||
'ships': ships,
|
'ships': ships,
|
||||||
'total_teu': sum(ships.values()),
|
'total_teu': total_teu,
|
||||||
'ship_count': len(ships)
|
'ship_count': len(ships),
|
||||||
|
'adjustments': [],
|
||||||
|
'total_adjustment_teu': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -85,7 +102,9 @@ class DailyReportGenerator:
|
|||||||
'date': date,
|
'date': date,
|
||||||
'ships': {},
|
'ships': {},
|
||||||
'total_teu': 0,
|
'total_teu': 0,
|
||||||
'ship_count': 0
|
'ship_count': 0,
|
||||||
|
'adjustments': [],
|
||||||
|
'total_adjustment_teu': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_monthly_stats(self, date: str) -> Dict[str, Any]:
|
def get_monthly_stats(self, date: str) -> Dict[str, Any]:
|
||||||
@@ -111,7 +130,7 @@ class DailyReportGenerator:
|
|||||||
and datetime.strptime(log['date'], '%Y-%m-%d').date() <= target_date
|
and datetime.strptime(log['date'], '%Y-%m-%d').date() <= target_date
|
||||||
]
|
]
|
||||||
|
|
||||||
# 按日期汇总
|
# 按日期汇总原始数据
|
||||||
daily_totals: Dict[str, int] = {}
|
daily_totals: Dict[str, int] = {}
|
||||||
for log in monthly_logs:
|
for log in monthly_logs:
|
||||||
d = log['date']
|
d = log['date']
|
||||||
@@ -120,6 +139,25 @@ class DailyReportGenerator:
|
|||||||
if log.get('teu'):
|
if log.get('teu'):
|
||||||
daily_totals[d] += log['teu']
|
daily_totals[d] += log['teu']
|
||||||
|
|
||||||
|
# 获取当月所有日期的调整数据
|
||||||
|
total_adjustment_teu = 0
|
||||||
|
adjustment_details: Dict[str, Dict[str, int]] = {}
|
||||||
|
|
||||||
|
# 获取当月所有日期的调整数据
|
||||||
|
for day in range(1, target_date.day + 1):
|
||||||
|
day_str = f"{year_month}-{day:02d}"
|
||||||
|
if day_str <= date: # 只统计到指定日期
|
||||||
|
# 获取该日期的调整数据
|
||||||
|
if hasattr(self.db, 'get_daily_data_with_adjustments'):
|
||||||
|
daily_data = self.db.get_daily_data_with_adjustments(day_str)
|
||||||
|
adjustment_teu = daily_data.get('total_adjustment_teu', 0)
|
||||||
|
if adjustment_teu != 0:
|
||||||
|
total_adjustment_teu += adjustment_teu
|
||||||
|
adjustment_details[day_str] = {
|
||||||
|
'adjustment_teu': adjustment_teu,
|
||||||
|
'total_teu': daily_data.get('total_teu', 0)
|
||||||
|
}
|
||||||
|
|
||||||
# 计算当月天数(已过的天数)
|
# 计算当月天数(已过的天数)
|
||||||
current_date = datetime.strptime(date, '%Y-%m-%d')
|
current_date = datetime.strptime(date, '%Y-%m-%d')
|
||||||
if current_date.day == config.FIRST_DAY_OF_MONTH_SPECIAL:
|
if current_date.day == config.FIRST_DAY_OF_MONTH_SPECIAL:
|
||||||
@@ -131,7 +169,8 @@ class DailyReportGenerator:
|
|||||||
unaccounted = self.db.get_unaccounted(year_month)
|
unaccounted = self.db.get_unaccounted(year_month)
|
||||||
|
|
||||||
planned = days_passed * config.DAILY_TARGET_TEU
|
planned = days_passed * config.DAILY_TARGET_TEU
|
||||||
actual = sum(daily_totals.values()) + unaccounted
|
# 实际作业量 = 原始数据总计 + 未统计数据 + 调整数据总计
|
||||||
|
actual = sum(daily_totals.values()) + unaccounted + total_adjustment_teu
|
||||||
|
|
||||||
completion = round(actual / planned * 100, 2) if planned > 0 else 0
|
completion = round(actual / planned * 100, 2) if planned > 0 else 0
|
||||||
|
|
||||||
@@ -141,8 +180,10 @@ class DailyReportGenerator:
|
|||||||
'planned': planned,
|
'planned': planned,
|
||||||
'actual': actual,
|
'actual': actual,
|
||||||
'unaccounted': unaccounted,
|
'unaccounted': unaccounted,
|
||||||
|
'adjustment_total': total_adjustment_teu,
|
||||||
'completion': completion,
|
'completion': completion,
|
||||||
'daily_totals': daily_totals
|
'daily_totals': daily_totals,
|
||||||
|
'adjustment_details': adjustment_details
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -153,8 +194,10 @@ class DailyReportGenerator:
|
|||||||
'planned': 0,
|
'planned': 0,
|
||||||
'actual': 0,
|
'actual': 0,
|
||||||
'unaccounted': 0,
|
'unaccounted': 0,
|
||||||
|
'adjustment_total': 0,
|
||||||
'completion': 0,
|
'completion': 0,
|
||||||
'daily_totals': {}
|
'daily_totals': {},
|
||||||
|
'adjustment_details': {}
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_shift_personnel(self, date: str) -> Dict[str, str]:
|
def get_shift_personnel(self, date: str) -> Dict[str, str]:
|
||||||
@@ -171,9 +214,15 @@ class DailyReportGenerator:
|
|||||||
班次人员字典
|
班次人员字典
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 检查飞书配置
|
# 检查飞书配置(支持应用凭证和手动token两种方式)
|
||||||
if not config.FEISHU_TOKEN or not config.FEISHU_SPREADSHEET_TOKEN:
|
has_feishu_config = bool(config.FEISHU_SPREADSHEET_TOKEN) and (
|
||||||
|
bool(config.FEISHU_APP_ID and config.FEISHU_APP_SECRET) or
|
||||||
|
bool(config.FEISHU_TOKEN)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not has_feishu_config:
|
||||||
logger.warning("飞书配置不完整,跳过排班信息获取")
|
logger.warning("飞书配置不完整,跳过排班信息获取")
|
||||||
|
logger.warning("需要配置 FEISHU_SPREADSHEET_TOKEN 和 (FEISHU_APP_ID+FEISHU_APP_SECRET 或 FEISHU_TOKEN)")
|
||||||
return self._empty_personnel()
|
return self._empty_personnel()
|
||||||
|
|
||||||
# 初始化飞书排班管理器
|
# 初始化飞书排班管理器
|
||||||
@@ -245,9 +294,25 @@ class DailyReportGenerator:
|
|||||||
# 船次信息
|
# 船次信息
|
||||||
if daily_data['ships']:
|
if daily_data['ships']:
|
||||||
ship_lines: List[str] = []
|
ship_lines: List[str] = []
|
||||||
for ship, teu in sorted(daily_data['ships'].items(), key=lambda x: -x[1]):
|
for ship, ship_data in sorted(daily_data['ships'].items(), key=lambda x: -x[1]['teu']):
|
||||||
ship_lines.append(f"船名:{ship}")
|
ship_lines.append(f"船名:{ship}")
|
||||||
ship_lines.append(f"作业量:{teu}TEU")
|
teu = ship_data['teu']
|
||||||
|
twenty_feet = ship_data.get('twenty_feet', 0)
|
||||||
|
forty_feet = ship_data.get('forty_feet', 0)
|
||||||
|
|
||||||
|
# 构建尺寸箱量字符串
|
||||||
|
size_parts = []
|
||||||
|
if twenty_feet > 0:
|
||||||
|
size_parts.append(f"20尺*{twenty_feet}")
|
||||||
|
if forty_feet > 0:
|
||||||
|
size_parts.append(f"40尺*{forty_feet}")
|
||||||
|
|
||||||
|
if size_parts:
|
||||||
|
size_str = " ".join(size_parts)
|
||||||
|
ship_lines.append(f"作业量:{teu}TEU({size_str})")
|
||||||
|
else:
|
||||||
|
ship_lines.append(f"作业量:{teu}TEU")
|
||||||
|
|
||||||
lines.extend(ship_lines)
|
lines.extend(ship_lines)
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user