feat: 添加尺寸箱量解析和显示功能

- 更新ShipLog数据类以支持20尺和40尺箱量字段
- 修改日志解析器提取尺寸箱量数据(支持格式如'95TEU(20尺*95)'和'90TEU(20尺*52 40尺*19)')
- 更新数据库表结构存储尺寸箱量
- 修改报告生成器在日报中显示尺寸箱量信息
- 修复解析器分隔符处理逻辑
- 确保二次靠泊记录尺寸箱量正确合并
This commit is contained in:
2025-12-31 05:21:16 +08:00
parent 5345dc75f2
commit 929c4b836f
3 changed files with 104 additions and 25 deletions

View File

@@ -22,6 +22,8 @@ class ShipLog:
teu: Optional[int] = None
efficiency: Optional[float] = None
vehicles: Optional[int] = None
twenty_feet: Optional[int] = None # 20尺箱量
forty_feet: Optional[int] = None # 40尺箱量
def to_dict(self) -> Dict[str, Any]:
"""转换为字典"""
@@ -129,19 +131,21 @@ class HandoverLogParser:
processed_text = '\n'.join(processed_lines)
blocks = processed_text.split(self.SEPARATOR)
current_date = None
for block in blocks:
if not block.strip() or '日期:' not in block:
if not block.strip():
continue
# 解析日期
# 检查块中是否包含日期
date_match = re.search(r'日期:(\d{4}\.\d{2}\.\d{2})', block)
if not date_match:
continue
if date_match:
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] = {}
for log in logs:
key = (log.date, log.shift, log.ship_name)
@@ -152,7 +156,9 @@ class HandoverLogParser:
ship_name=log.ship_name,
teu=log.teu,
efficiency=log.efficiency,
vehicles=log.vehicles
vehicles=log.vehicles,
twenty_feet=log.twenty_feet,
forty_feet=log.forty_feet
)
else:
# 累加TEU
@@ -167,6 +173,18 @@ class HandoverLogParser:
merged[key].vehicles = log.vehicles
else:
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())
logger.info(f"日志解析完成,共 {len(result)} 条记录")
@@ -243,13 +261,44 @@ class HandoverLogParser:
except ValueError as e:
logger.warning(f"车辆数解析失败: {vehicles_match.group(1)}, 错误: {e}")
# 解析尺寸箱量
twenty_feet = None
forty_feet = None
# 查找作业量/效率后面的括号内的尺寸信息
# 匹配模式TEU数字后跟括号括号内包含尺寸信息
size_pattern = re.search(r'TEU[,\s]*([^]+)', cleaned)
if not size_pattern:
# 也尝试匹配没有逗号的情况
size_pattern = re.search(r'TEU\s*([^]+)', cleaned)
if size_pattern:
size_text = size_pattern.group(1)
# 匹配20尺*数字
twenty_match = re.search(r'20尺\*(\d+)', 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}")
# 匹配40尺*数字
forty_match = re.search(r'40尺\*(\d+)', 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}")
log = ShipLog(
date=date,
shift=shift,
ship_name=ship_name,
teu=teu,
efficiency=None, # 目前日志中没有效率数据
vehicles=vehicles
vehicles=vehicles,
twenty_feet=twenty_feet,
forty_feet=forty_feet
)
logs.append(log)

View File

@@ -40,6 +40,8 @@ class DailyLogsDatabase(DatabaseBase):
teu INTEGER,
efficiency REAL,
vehicles INTEGER,
twenty_feet INTEGER, -- 20尺箱量
forty_feet INTEGER, -- 40尺箱量
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
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'")
table_sql = cursor.fetchone()[0]
if 'UNIQUE' not in table_sql:
logger.warning("检测到旧表结构,正在迁移...")
if 'twenty_feet' not in table_sql or 'forty_feet' not in table_sql:
logger.warning("检测到旧表结构,正在迁移以添加尺寸箱量字段...")
# 重命名旧表
cursor.execute('ALTER TABLE daily_handover_logs RENAME TO daily_handover_logs_old')
@@ -64,6 +66,8 @@ class DailyLogsDatabase(DatabaseBase):
teu INTEGER,
efficiency REAL,
vehicles INTEGER,
twenty_feet INTEGER, -- 20尺箱量
forty_feet INTEGER, -- 40尺箱量
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(date, shift, ship_name) ON CONFLICT REPLACE
)
@@ -79,7 +83,7 @@ class DailyLogsDatabase(DatabaseBase):
# 删除旧表
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)')
@@ -112,12 +116,13 @@ class DailyLogsDatabase(DatabaseBase):
try:
query = '''
INSERT OR REPLACE INTO daily_handover_logs
(date, shift, ship_name, teu, efficiency, vehicles, created_at)
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
(date, shift, ship_name, teu, efficiency, vehicles, twenty_feet, forty_feet, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
'''
params = (
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)

View File

@@ -63,19 +63,29 @@ class DailyReportGenerator:
try:
logs = self.db.query_by_date(date)
# 按船名汇总
ships: Dict[str, int] = {}
# 按船名汇总TEU和尺寸箱量
ships: Dict[str, Dict[str, Any]] = {}
for log in logs:
ship = log['ship_name']
if ship not in ships:
ships[ship] = 0
ships[ship] = {
'teu': 0,
'twenty_feet': 0,
'forty_feet': 0
}
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 {
'date': date,
'ships': ships,
'total_teu': sum(ships.values()),
'total_teu': total_teu,
'ship_count': len(ships)
}
@@ -245,8 +255,23 @@ class DailyReportGenerator:
# 船次信息
if daily_data['ships']:
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}")
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.append("")