mirror of
https://devops.liangqichi.top/qichi.liang/Orbitin.git
synced 2026-02-10 07:41:29 +08:00
feat: 添加转堆作业解析功能
- 新增 _parse_relocation() 方法解析转堆作业 - 新增 _parse_log_entry() 方法复用解析逻辑 - 转堆作业使用 '转堆作业' 作为船名标识
This commit is contained in:
@@ -241,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:
|
||||||
@@ -259,11 +265,107 @@ class HandoverLogParser:
|
|||||||
# 移除二次靠泊等标注
|
# 移除二次靠泊等标注
|
||||||
ship_name = re.sub(r'(二次靠泊)|(再次靠泊)|\(二次靠泊\)|\(再次靠泊\)', '', ship_name).strip()
|
ship_name = re.sub(r'(二次靠泊)|(再次靠泊)|\(二次靠泊\)|\(再次靠泊\)', '', ship_name).strip()
|
||||||
|
|
||||||
|
# 解析车辆数、TEU、尺寸箱量
|
||||||
|
self._parse_log_entry(cleaned, date, shift, ship_name, logs)
|
||||||
|
|
||||||
|
except Exception as 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)
|
vehicles_match = re.search(r'上场车辆数:(\d+)', cleaned)
|
||||||
teu_eff_match = re.search(
|
teu_eff_match = re.search(r'作业量/效率:(\d+)TEU', cleaned)
|
||||||
r'作业量/效率:(\d+)TEU[,,\s]*', 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 = 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)
|
||||||
|
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}")
|
||||||
|
|
||||||
|
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='转堆作业',
|
||||||
|
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
|
||||||
teu = None
|
teu = None
|
||||||
if teu_eff_match:
|
if teu_eff_match:
|
||||||
@@ -285,15 +387,12 @@ class HandoverLogParser:
|
|||||||
forty_feet = None
|
forty_feet = None
|
||||||
|
|
||||||
# 查找作业量/效率后面的括号内的尺寸信息
|
# 查找作业量/效率后面的括号内的尺寸信息
|
||||||
# 匹配模式:TEU数字后跟括号,括号内包含尺寸信息
|
|
||||||
size_pattern = re.search(r'TEU[,,\s]*(([^)]+))', cleaned)
|
size_pattern = re.search(r'TEU[,,\s]*(([^)]+))', cleaned)
|
||||||
if not size_pattern:
|
if not size_pattern:
|
||||||
# 也尝试匹配没有逗号的情况
|
|
||||||
size_pattern = re.search(r'TEU\s*(([^)]+))', cleaned)
|
size_pattern = re.search(r'TEU\s*(([^)]+))', cleaned)
|
||||||
|
|
||||||
if size_pattern:
|
if size_pattern:
|
||||||
size_text = size_pattern.group(1)
|
size_text = size_pattern.group(1)
|
||||||
# 匹配20尺*数字
|
|
||||||
twenty_match = re.search(r'20尺\*(\d+)', size_text)
|
twenty_match = re.search(r'20尺\*(\d+)', size_text)
|
||||||
if twenty_match:
|
if twenty_match:
|
||||||
try:
|
try:
|
||||||
@@ -301,7 +400,6 @@ class HandoverLogParser:
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.warning(f"20尺箱量解析失败: {twenty_match.group(1)}, 错误: {e}")
|
logger.warning(f"20尺箱量解析失败: {twenty_match.group(1)}, 错误: {e}")
|
||||||
|
|
||||||
# 匹配40尺*数字
|
|
||||||
forty_match = re.search(r'40尺\*(\d+)', size_text)
|
forty_match = re.search(r'40尺\*(\d+)', size_text)
|
||||||
if forty_match:
|
if forty_match:
|
||||||
try:
|
try:
|
||||||
@@ -314,7 +412,7 @@ class HandoverLogParser:
|
|||||||
shift=shift,
|
shift=shift,
|
||||||
ship_name=ship_name,
|
ship_name=ship_name,
|
||||||
teu=teu,
|
teu=teu,
|
||||||
efficiency=None, # 目前日志中没有效率数据
|
efficiency=None,
|
||||||
vehicles=vehicles,
|
vehicles=vehicles,
|
||||||
twenty_feet=twenty_feet,
|
twenty_feet=twenty_feet,
|
||||||
forty_feet=forty_feet
|
forty_feet=forty_feet
|
||||||
@@ -322,7 +420,7 @@ class HandoverLogParser:
|
|||||||
logs.append(log)
|
logs.append(log)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"解析船次失败: {date} {shift}, 错误: {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]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user