Skip to content

Commit 49f5c96

Browse files
committed
fix: _parse_date 兼容 ISO datetime + postprocess 阶段进度反馈 + skipped date_time 填充
1 parent c0bbda2 commit 49f5c96

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

quantclass_sync_internal/data_query.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ def infer_local_date_from_csv(data_root: Path, product: str, rule) -> Optional[s
7878

7979

8080
def _parse_date(date_str: Optional[str]) -> Optional[date]:
81-
"""解析 YYYY-MM-DD 日期字符串。"""
81+
"""解析日期字符串,支持 YYYY-MM-DD 和 YYYY-MM-DDTHH:MM:SS 两种格式。"""
8282
if not date_str:
8383
return None
8484
try:
85-
return datetime.strptime(date_str, "%Y-%m-%d").date()
85+
# 优先尝试 ISO datetime(checked_at 字段),截取日期部分
86+
return datetime.strptime(date_str[:10], "%Y-%m-%d").date()
8687
except ValueError:
8788
return None
8889

quantclass_sync_internal/gui/assets/app.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ document.addEventListener('alpine:init', () => {
5959
syncProducts: [], // 同步过程中已处理产品列表(每项含 name/status/elapsed/files_count/error)
6060
allProducts: [], // 全部待同步产品名(用于计算等待中列表)
6161
showWaiting: false, // 等待中产品列表是否展开
62+
postprocessing: false, // 后处理阶段标志
6263
estimateData: null, // API 调用量预估数据(confirm_needed 时填充,用于展示确认卡片)
6364

6465
// ===== 初始化 =====
@@ -179,6 +180,7 @@ document.addEventListener('alpine:init', () => {
179180
this.syncProducts = [];
180181
this.allProducts = [];
181182
this.showWaiting = false;
183+
this.postprocessing = false;
182184
this.estimateData = null;
183185
try {
184186
const result = await window.pywebview.api.start_sync();
@@ -210,6 +212,7 @@ document.addEventListener('alpine:init', () => {
210212
this.syncProducts = [];
211213
this.allProducts = [];
212214
this.showWaiting = false;
215+
this.postprocessing = false;
213216
this.estimateData = null;
214217
try {
215218
// true 表示仅重试失败产品
@@ -257,7 +260,10 @@ document.addEventListener('alpine:init', () => {
257260
// confirm_needed:后台线程等待用户确认,展示确认卡片
258261
if (p.status === 'confirm_needed' && p.estimate) {
259262
this.estimateData = p.estimate;
263+
this.postprocessing = false;
260264
// 不切换 syncStatus,继续轮询等待用户点击确认/取消
265+
} else if (p.status === 'postprocessing') {
266+
this.postprocessing = true;
261267
} else if (p.status === 'done') {
262268
this.syncStatus = 'done';
263269
this.estimateData = null;

quantclass_sync_internal/gui/assets/index.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,11 @@ <h2 class="setup-title">QuantClass Sync 初始设置</h2>
389389
</div>
390390
<span class="qs-progress-text" x-text="total > 0 ? completed + ' / ' + total : '准备中...'"></span>
391391
</div>
392-
<!-- 进度指示行(仅在有产品时显示详情,准备中状态由进度条文字承担) -->
393-
<div x-show="total > 0" style="font-size:0.92em; color:#6b7280; margin:6px 0 2px;">
392+
<!-- 进度指示行 -->
393+
<div x-show="postprocessing" style="font-size:0.92em; color:#6b7280; margin:6px 0 2px;">
394+
正在后处理数据...
395+
</div>
396+
<div x-show="total > 0 && !postprocessing" style="font-size:0.92em; color:#6b7280; margin:6px 0 2px;">
394397
<span x-text="'已完成 ' + completed + '/' + total"></span>
395398
<span x-show="currentProduct"> · 最新: <span style="font-weight:600" x-text="currentProduct"></span></span>
396399
</div>

quantclass_sync_internal/orchestrator.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,10 @@ def _run_one_plan(plan: ProductPlan) -> Tuple[bool, float, SyncStats, str, str]:
11601160
if not requested_dates_for_plan:
11611161
if catch_up_to_latest:
11621162
elapsed = time.time() - t_product_start
1163+
# 读取本地 timestamp,用于填充 date_time,避免 GUI 用 today 计算落后天数
1164+
local_date_for_report = read_local_timestamp_date(
1165+
command_ctx.data_root, normalize_product_name(plan.name)
1166+
)
11631167
# 并发路径:写 report 需加锁,防止 list.append 与其他线程竞争
11641168
with _lock:
11651169
_append_result(
@@ -1168,6 +1172,7 @@ def _run_one_plan(plan: ProductPlan) -> Tuple[bool, float, SyncStats, str, str]:
11681172
status="skipped",
11691173
strategy=plan.strategy,
11701174
reason_code=REASON_NO_DATA_FOR_DATE,
1175+
date_time=local_date_for_report or "",
11711176
elapsed=elapsed,
11721177
mode="gate",
11731178
error="catch-up 日期队列为空,已跳过本产品。",
@@ -1560,6 +1565,12 @@ def run_update_with_settings(
15601565
max_workers=max_workers,
15611566
progress_callback=progress_callback,
15621567
)
1568+
# 后处理前通知前端,postprocessing 状态用于 GUI 显示"正在后处理数据..."
1569+
if progress_callback is not None:
1570+
try:
1571+
progress_callback("", len(plans), len(plans), status="postprocessing")
1572+
except Exception:
1573+
pass
15631574
preprocess_has_error = _maybe_run_coin_preprocess(
15641575
command_ctx=command_ctx,
15651576
report=report,
@@ -1580,6 +1591,12 @@ def run_update_with_settings(
15801591
max_workers=max_workers,
15811592
progress_callback=progress_callback,
15821593
)
1594+
# 后处理前通知前端,postprocessing 状态用于 GUI 显示"正在后处理数据..."
1595+
if progress_callback is not None:
1596+
try:
1597+
progress_callback("", len(plans), len(plans), status="postprocessing")
1598+
except Exception:
1599+
pass
15831600
preprocess_has_error = _maybe_run_coin_preprocess(
15841601
command_ctx=command_ctx,
15851602
report=report,

0 commit comments

Comments
 (0)