- task_process_undelivered_data now accepts site_name (default 顺心)
and reads/writes {site}-{应到/实到/应到未到}货物数据.xlsx
- Menu option 5 prompts for site name before running reconciliation
- Update 顺心 site outputs to 顺心-应到/实到货物数据.xlsx naming
- Remove redundant inline comments in site_shunxin.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
360 lines
15 KiB
Python
360 lines
15 KiB
Python
# site_shunxin.py
|
||
|
||
import os
|
||
import re
|
||
from datetime import datetime
|
||
import pandas as pd
|
||
|
||
|
||
def shunxin_expected_download(page):
|
||
"""顺心:应到货物数据下载"""
|
||
print("\n▶ 开始执行【顺心 - 应到货物数据下载】任务...")
|
||
|
||
# 初始化并创建下载目录
|
||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||
if not os.path.exists(download_dir):
|
||
os.makedirs(download_dir)
|
||
print(f">> 已创建专属下载文件夹: {download_dir}")
|
||
|
||
export_times = []
|
||
target_task_timestamps = []
|
||
|
||
try:
|
||
# 1. 导航与页面加载判断
|
||
print(">> 正在进入【车辆点到】界面...")
|
||
page.locator("span.ant-pro-menu-item-title:has-text('派件管理')").click()
|
||
page.locator("div.ant-pro-menu-item:has-text('车辆点到')").click()
|
||
|
||
page.get_by_role("button", name="点到").wait_for(state="visible")
|
||
page.get_by_role("button", name="打印交接单").wait_for(state="visible")
|
||
page.get_by_role("button", name="强卸").wait_for(state="visible")
|
||
print("✅ 车辆点到界面加载完毕")
|
||
|
||
# 2. 筛选条件:1天、状态=已发 -> 已到、查询
|
||
print(">> 正在设置筛选条件: 选择【1天】...")
|
||
page.get_by_role("radio", name="1天").check()
|
||
|
||
print(">> 正在展开【状态】下拉菜单...")
|
||
page.locator(
|
||
".ant-select-selection-item", has_text=re.compile(r"已发|已到")
|
||
).click()
|
||
|
||
print(">> 正在选择状态为【已到】...")
|
||
page.locator(".ant-select-item-option", has_text="已到").click()
|
||
|
||
print(">> 正在点击【查询】按钮...")
|
||
page.get_by_role("button", name="search 查询").click()
|
||
page.wait_for_timeout(2000)
|
||
|
||
# 3. 获取所有【运单列表】按钮并循环处理
|
||
waybill_btns = page.get_by_role("button", name="运单列表")
|
||
count = waybill_btns.count()
|
||
print(f">> 共发现 {count} 个班次需要导出。")
|
||
|
||
for i in range(count):
|
||
print(f" ⏳ 正在处理第 {i+1}/{count} 个班次...")
|
||
|
||
waybill_btns.nth(i).click()
|
||
page.locator("label[title='运单查询']").wait_for(state="visible")
|
||
|
||
# 4. 执行导出流程
|
||
page.get_by_role("button", name="export 导出").click()
|
||
|
||
page.locator(
|
||
"span.ant-transfer-list-header-title:has-text('待选导出列')"
|
||
).wait_for(state="visible")
|
||
page.locator(".ant-transfer-list").first.locator(
|
||
".ant-transfer-list-header label"
|
||
).click()
|
||
|
||
page.get_by_label("导出").get_by_role("button", name="right").click()
|
||
page.get_by_role("button", name="export 导出数据").click()
|
||
|
||
export_times.append(datetime.now())
|
||
|
||
page.locator("text=任务添加成功!").wait_for(state="visible")
|
||
page.get_by_role("button", name="知道了").click()
|
||
|
||
page.get_by_role("tab", name="车辆点到").click()
|
||
page.wait_for_timeout(500)
|
||
|
||
if count > 0:
|
||
print("✅ 所有班次的导出任务已成功提交!")
|
||
else:
|
||
print("⚠️ 未发现任何运单列表,直接跳转至下载环节。")
|
||
|
||
# 6. 前往数据导出页面去下载
|
||
print(">> 正在前往【数据导出】界面...")
|
||
page.locator("a[href='/dataExport']").click()
|
||
|
||
page.get_by_role("columnheader", name="任务标题").wait_for(state="visible")
|
||
page.wait_for_timeout(2000)
|
||
|
||
page.get_by_role("button", name="search 查询").click()
|
||
page.wait_for_timeout(2000)
|
||
|
||
# 7. 轮询任务状态
|
||
print(">> 列表中已渲染,开始匹配并检查后端处理状态...")
|
||
while True:
|
||
rows = page.locator(".ant-table-tbody > tr.ant-table-row")
|
||
row_count = rows.count()
|
||
pending_tasks = 0
|
||
current_ready_timestamps = []
|
||
|
||
for i in range(row_count):
|
||
tds = rows.nth(i).locator("td")
|
||
if tds.count() < 9:
|
||
continue
|
||
|
||
submit_time_str = tds.nth(2).inner_text().strip()
|
||
title_str = tds.nth(5).inner_text().strip()
|
||
status_str = tds.nth(6).inner_text().strip()
|
||
|
||
if title_str == "发车管理运单列表":
|
||
try:
|
||
row_time = datetime.strptime(
|
||
submit_time_str, "%Y-%m-%d %H:%M:%S"
|
||
)
|
||
matched = any(
|
||
abs((row_time - et).total_seconds()) <= 60
|
||
for et in export_times
|
||
)
|
||
|
||
if matched:
|
||
if status_str != "执行完成":
|
||
pending_tasks += 1
|
||
if submit_time_str not in current_ready_timestamps:
|
||
print(
|
||
f" ⏳ 任务 [{submit_time_str}] 状态为【{status_str}】,数据生成中..."
|
||
)
|
||
else:
|
||
if submit_time_str not in current_ready_timestamps:
|
||
current_ready_timestamps.append(submit_time_str)
|
||
except Exception as e:
|
||
print(f" ⚠️ 解析时间时出错: {e}")
|
||
|
||
if pending_tasks > 0:
|
||
print(
|
||
f">> 共有 {pending_tasks} 个匹配任务还在处理中,等待 5 秒后刷新..."
|
||
)
|
||
page.wait_for_timeout(5000)
|
||
page.get_by_role("button", name="search 查询").click()
|
||
page.wait_for_timeout(2000)
|
||
else:
|
||
target_task_timestamps = current_ready_timestamps
|
||
if len(target_task_timestamps) > 0:
|
||
print(">> ✅ 所有目标任务已就绪!开始并行下载...")
|
||
break
|
||
|
||
# 8. 下载逻辑
|
||
downloaded_files = []
|
||
for time_str in target_task_timestamps:
|
||
try:
|
||
target_row = page.locator(".ant-table-tbody > tr.ant-table-row").filter(
|
||
has=page.locator(f"td:nth-child(3):has-text('{time_str}')")
|
||
)
|
||
print(f" 🎯 触发下载 -> 任务 [{time_str}] ...")
|
||
|
||
with page.expect_download() as download_info:
|
||
target_row.locator("td").nth(8).locator(
|
||
"button", has_text=re.compile(r"下\s*载")
|
||
).click()
|
||
|
||
download = download_info.value
|
||
save_path = os.path.join(download_dir, download.suggested_filename)
|
||
download.save_as(save_path)
|
||
downloaded_files.append(save_path)
|
||
print(f" ⬇️ 文件已落盘: downloads/{download.suggested_filename}")
|
||
except Exception as e:
|
||
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
|
||
|
||
# 9. 合并数据 (修改了文件名前缀)
|
||
if downloaded_files:
|
||
print("\n>> 🧪 正在开始执行扁平数据高能合并流程...")
|
||
all_data_frames = []
|
||
for file_path in downloaded_files:
|
||
try:
|
||
df = pd.read_excel(file_path)
|
||
if not df.empty:
|
||
all_data_frames.append(df)
|
||
except Exception as e:
|
||
pass
|
||
|
||
if all_data_frames:
|
||
combined_df = pd.concat(all_data_frames, ignore_index=True)
|
||
final_output_path = os.path.join(download_dir, "顺心-应到货物数据.xlsx")
|
||
combined_df.to_excel(final_output_path, index=False)
|
||
print(f"====================================================")
|
||
print(f" 🎉 恭喜!合并成功!最终输出路径: {final_output_path}")
|
||
print(f"====================================================")
|
||
|
||
for file_path in downloaded_files:
|
||
os.remove(file_path)
|
||
print("✅ 临时数据清理完毕。")
|
||
print("\n🎉 【顺心 - 应到货物数据下载】全流程测试完毕!")
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ 任务执行过程中发生异常: {e}")
|
||
|
||
|
||
def shunxin_actual_download(page):
|
||
"""顺心:实到货物数据下载"""
|
||
print("\n▶ 开始执行【顺心 - 实到货物数据下载】任务...")
|
||
|
||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||
if not os.path.exists(download_dir):
|
||
os.makedirs(download_dir)
|
||
|
||
export_times = []
|
||
target_task_timestamps = []
|
||
|
||
try:
|
||
# 1. 导航与页面加载
|
||
print(">> 正在进入【卸车扫描记录】界面...")
|
||
page.locator("span.ant-pro-menu-item-title:has-text('派件管理')").click()
|
||
page.locator("div.ant-pro-menu-item:has-text('卸车扫描记录')").click()
|
||
|
||
page.get_by_role("radio", name="1天").wait_for(state="visible")
|
||
print("✅ 卸车扫描记录界面加载完毕")
|
||
|
||
# 2. 筛选条件:1天、查询
|
||
print(">> 正在设置筛选条件: 选择【1天】...")
|
||
page.get_by_role("radio", name="1天").check()
|
||
|
||
print(">> 正在点击【查询】按钮...")
|
||
page.get_by_role("button", name="search 查询").click()
|
||
page.wait_for_timeout(2000)
|
||
|
||
# 3. 直接发起全局导出
|
||
print(">> 正在发起导出请求...")
|
||
page.get_by_role("button", name="export 导出").click()
|
||
|
||
page.locator(
|
||
"span.ant-transfer-list-header-title:has-text('待选导出列')"
|
||
).wait_for(state="visible")
|
||
page.locator(".ant-transfer-list").first.locator(
|
||
".ant-transfer-list-header label"
|
||
).click()
|
||
|
||
page.get_by_label("导出").get_by_role("button", name="right").click()
|
||
page.get_by_role("button", name="export 导出数据").click()
|
||
|
||
export_times.append(datetime.now())
|
||
|
||
page.locator("text=任务添加成功!").wait_for(state="visible")
|
||
page.get_by_role("button", name="知道了").click()
|
||
print("✅ 卸车扫描记录导出任务已成功提交!")
|
||
|
||
# 4. 前往数据导出页面去下载
|
||
print(">> 正在前往【数据导出】界面...")
|
||
page.locator("a[href='/dataExport']").click()
|
||
|
||
page.get_by_role("columnheader", name="任务标题").wait_for(state="visible")
|
||
page.wait_for_timeout(2000)
|
||
|
||
page.get_by_role("button", name="search 查询").click()
|
||
page.wait_for_timeout(2000)
|
||
|
||
# 5. 轮询任务状态
|
||
print(">> 列表中已渲染,开始匹配并检查后端处理状态...")
|
||
while True:
|
||
rows = page.locator(".ant-table-tbody > tr.ant-table-row")
|
||
row_count = rows.count()
|
||
pending_tasks = 0
|
||
current_ready_timestamps = []
|
||
|
||
for i in range(row_count):
|
||
tds = rows.nth(i).locator("td")
|
||
if tds.count() < 9:
|
||
continue
|
||
|
||
submit_time_str = tds.nth(2).inner_text().strip()
|
||
title_str = tds.nth(5).inner_text().strip()
|
||
status_str = tds.nth(6).inner_text().strip()
|
||
|
||
if title_str.startswith("卸车扫描记录"):
|
||
try:
|
||
row_time = datetime.strptime(
|
||
submit_time_str, "%Y-%m-%d %H:%M:%S"
|
||
)
|
||
matched = any(
|
||
abs((row_time - et).total_seconds()) <= 60
|
||
for et in export_times
|
||
)
|
||
|
||
if matched:
|
||
if status_str != "执行完成":
|
||
pending_tasks += 1
|
||
if submit_time_str not in current_ready_timestamps:
|
||
print(
|
||
f" ⏳ 任务 [{submit_time_str}] 状态为【{status_str}】,数据生成中..."
|
||
)
|
||
else:
|
||
if submit_time_str not in current_ready_timestamps:
|
||
current_ready_timestamps.append(submit_time_str)
|
||
except Exception as e:
|
||
print(f" ⚠️ 解析时间时出错: {e}")
|
||
|
||
if pending_tasks > 0:
|
||
print(
|
||
f">> 共有 {pending_tasks} 个匹配任务还在处理中,等待 5 秒后刷新..."
|
||
)
|
||
page.wait_for_timeout(5000)
|
||
page.get_by_role("button", name="search 查询").click()
|
||
page.wait_for_timeout(2000)
|
||
else:
|
||
target_task_timestamps = current_ready_timestamps
|
||
if len(target_task_timestamps) > 0:
|
||
print(">> ✅ 目标任务已就绪!开始下载...")
|
||
break
|
||
|
||
# 6. 下载逻辑
|
||
downloaded_files = []
|
||
for time_str in target_task_timestamps:
|
||
try:
|
||
target_row = page.locator(".ant-table-tbody > tr.ant-table-row").filter(
|
||
has=page.locator(f"td:nth-child(3):has-text('{time_str}')")
|
||
)
|
||
print(f" 🎯 触发下载 -> 任务 [{time_str}] ...")
|
||
|
||
with page.expect_download() as download_info:
|
||
target_row.locator("td").nth(8).locator(
|
||
"button", has_text=re.compile(r"下\s*载")
|
||
).click()
|
||
|
||
download = download_info.value
|
||
save_path = os.path.join(download_dir, download.suggested_filename)
|
||
download.save_as(save_path)
|
||
downloaded_files.append(save_path)
|
||
print(f" ⬇️ 文件已落盘: downloads/{download.suggested_filename}")
|
||
except Exception as e:
|
||
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
|
||
|
||
# 7. 合并数据 (修改了文件名前缀)
|
||
if downloaded_files:
|
||
print("\n>> 🧪 正在开始执行数据归档整理...")
|
||
all_data_frames = []
|
||
for file_path in downloaded_files:
|
||
try:
|
||
df = pd.read_excel(file_path)
|
||
if not df.empty:
|
||
all_data_frames.append(df)
|
||
except Exception as e:
|
||
pass
|
||
|
||
if all_data_frames:
|
||
combined_df = pd.concat(all_data_frames, ignore_index=True)
|
||
final_output_path = os.path.join(download_dir, "顺心-实到货物数据.xlsx")
|
||
combined_df.to_excel(final_output_path, index=False)
|
||
print(f"====================================================")
|
||
print(f" 🎉 恭喜!处理成功!最终输出路径: {final_output_path}")
|
||
print(f"====================================================")
|
||
|
||
for file_path in downloaded_files:
|
||
os.remove(file_path)
|
||
print("✅ 临时数据清理完毕。")
|
||
print("\n🎉 【顺心 - 实到货物数据下载】全流程测试完毕!")
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ 任务执行过程中发生异常: {e}")
|