Improve manual catalog UX
This commit is contained in:
+63
-11
@@ -33,11 +33,19 @@ SYNC_LOCK = threading.Lock()
|
||||
SCHEDULE_LOCK = threading.Lock()
|
||||
MYSQL_CONFIG_LOCK = threading.Lock()
|
||||
INDEX_ALIAS_LOCK = threading.Lock()
|
||||
MANUAL_REBUILD_LOCK = threading.Lock()
|
||||
NORMALIZE_RE = re.compile(r"[^0-9a-z\u4e00-\u9fff]+")
|
||||
SCHEDULE_TIME_RE = re.compile(r"^(?:[01]?\d|2[0-3]):[0-5]\d$")
|
||||
SCHEDULER_POLL_SECONDS = 20
|
||||
INDEX_DEVICE_NAME_ALIAS_MAP: dict[str, list[str]] | None = None
|
||||
DEVICE_TYPES = {"phone", "tablet", "wear", "tv", "computer", "other"}
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS: dict[str, object] = {
|
||||
"running": False,
|
||||
"last_started_at": None,
|
||||
"last_finished_at": None,
|
||||
"last_status": None,
|
||||
"last_message": None,
|
||||
}
|
||||
|
||||
|
||||
def truthy_env(name: str, default: str = "0") -> bool:
|
||||
@@ -471,7 +479,34 @@ def count_manual_alias_conflicts(device: dict[str, object]) -> int:
|
||||
return len(conflicts)
|
||||
|
||||
|
||||
def rebuild_generated_outputs() -> dict[str, object]:
|
||||
def _manual_mysql_loader_task() -> None:
|
||||
with MANUAL_REBUILD_LOCK:
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["running"] = True
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_started_at"] = local_now().isoformat(timespec="seconds")
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_status"] = "running"
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_message"] = "手动补录触发的 MySQL 刷新进行中。"
|
||||
try:
|
||||
load_proc = run_command(["python3", str(MYSQL_LOADER)])
|
||||
message = "\n".join(part for part in [load_proc.stdout.strip(), load_proc.stderr.strip()] if part).strip() or "MySQL 已刷新。"
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_status"] = "success" if load_proc.returncode == 0 else "failed"
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_message"] = message
|
||||
except Exception as err:
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_status"] = "failed"
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_message"] = str(err)
|
||||
finally:
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["running"] = False
|
||||
LAST_MANUAL_MYSQL_LOAD_STATUS["last_finished_at"] = local_now().isoformat(timespec="seconds")
|
||||
|
||||
|
||||
def start_manual_mysql_loader() -> bool:
|
||||
if LAST_MANUAL_MYSQL_LOAD_STATUS.get("running"):
|
||||
return False
|
||||
thread = threading.Thread(target=_manual_mysql_loader_task, name="manual-mysql-loader", daemon=True)
|
||||
thread.start()
|
||||
return True
|
||||
|
||||
|
||||
def rebuild_generated_outputs(*, defer_mysql_load: bool = False) -> dict[str, object]:
|
||||
build_proc = run_command(
|
||||
[
|
||||
"python3",
|
||||
@@ -504,16 +539,26 @@ def rebuild_generated_outputs() -> dict[str, object]:
|
||||
mysql_loaded = False
|
||||
mysql_message = "MySQL 未刷新。"
|
||||
if mysql_auto_load_enabled():
|
||||
load_proc = run_command(["python3", str(MYSQL_LOADER)])
|
||||
mysql_message = "\n".join(part for part in [load_proc.stdout.strip(), load_proc.stderr.strip()] if part).strip() or "MySQL 已刷新。"
|
||||
if load_proc.returncode != 0:
|
||||
raise RuntimeError(mysql_message)
|
||||
mysql_loaded = True
|
||||
if defer_mysql_load:
|
||||
started = start_manual_mysql_loader()
|
||||
mysql_message = "MySQL 后台刷新中。" if started else "MySQL 后台刷新已在进行中。"
|
||||
else:
|
||||
load_proc = run_command(["python3", str(MYSQL_LOADER)])
|
||||
mysql_message = "\n".join(part for part in [load_proc.stdout.strip(), load_proc.stderr.strip()] if part).strip() or "MySQL 已刷新。"
|
||||
if load_proc.returncode != 0:
|
||||
raise RuntimeError(mysql_message)
|
||||
mysql_loaded = True
|
||||
return {
|
||||
"index_updated": True,
|
||||
"mysql_seed_updated": True,
|
||||
"mysql_loaded": mysql_loaded,
|
||||
"message": "本地覆盖库已保存,索引与 MySQL seed 已刷新。" if mysql_loaded else "本地覆盖库已保存,索引与 MySQL seed 已刷新,MySQL 未自动装载。",
|
||||
"message": (
|
||||
"本地覆盖库已保存,索引与 MySQL seed 已刷新,MySQL 正在后台刷新。"
|
||||
if defer_mysql_load and mysql_auto_load_enabled()
|
||||
else "本地覆盖库已保存,索引与 MySQL seed 已刷新。"
|
||||
if mysql_loaded
|
||||
else "本地覆盖库已保存,索引与 MySQL seed 已刷新,MySQL 未自动装载。"
|
||||
),
|
||||
"build_output": build_output,
|
||||
"mysql_seed_output": seed_output,
|
||||
"mysql_message": mysql_message,
|
||||
@@ -532,6 +577,13 @@ def manual_catalog_payload() -> dict[str, object]:
|
||||
"device_count": len(devices),
|
||||
},
|
||||
"catalog_file": str(MANUAL_CATALOG_PATH.relative_to(PROJECT_ROOT)),
|
||||
"mysql_refresh": {
|
||||
"running": bool(LAST_MANUAL_MYSQL_LOAD_STATUS.get("running")),
|
||||
"last_started_at": LAST_MANUAL_MYSQL_LOAD_STATUS.get("last_started_at"),
|
||||
"last_finished_at": LAST_MANUAL_MYSQL_LOAD_STATUS.get("last_finished_at"),
|
||||
"last_status": LAST_MANUAL_MYSQL_LOAD_STATUS.get("last_status"),
|
||||
"last_message": LAST_MANUAL_MYSQL_LOAD_STATUS.get("last_message"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -552,7 +604,7 @@ def upsert_manual_brand(payload: dict[str, object]) -> dict[str, object]:
|
||||
catalog["brands"].append(incoming)
|
||||
validated = validate_manual_catalog(catalog)
|
||||
write_manual_catalog(validated)
|
||||
rebuild_result = rebuild_generated_outputs()
|
||||
rebuild_result = rebuild_generated_outputs(defer_mysql_load=True)
|
||||
return {
|
||||
"saved_brand": incoming,
|
||||
"catalog": manual_catalog_payload(),
|
||||
@@ -576,7 +628,7 @@ def upsert_manual_device(payload: dict[str, object]) -> dict[str, object]:
|
||||
catalog["devices"].append(device_payload)
|
||||
validated = validate_manual_catalog(catalog)
|
||||
write_manual_catalog(validated)
|
||||
rebuild_result = rebuild_generated_outputs()
|
||||
rebuild_result = rebuild_generated_outputs(defer_mysql_load=True)
|
||||
return {
|
||||
"saved_device": device_payload,
|
||||
"alias_conflict_count": count_manual_alias_conflicts(device_payload),
|
||||
@@ -602,7 +654,7 @@ def delete_manual_brand(payload: dict[str, object]) -> dict[str, object]:
|
||||
if len(next_brands) == len(catalog["brands"]):
|
||||
raise RuntimeError(f"未找到品牌: {brand_name}")
|
||||
write_manual_catalog({"brands": next_brands, "devices": catalog["devices"]})
|
||||
rebuild_result = rebuild_generated_outputs()
|
||||
rebuild_result = rebuild_generated_outputs(defer_mysql_load=True)
|
||||
return {
|
||||
"deleted_brand": brand_name,
|
||||
"catalog": manual_catalog_payload(),
|
||||
@@ -624,7 +676,7 @@ def delete_manual_device(payload: dict[str, object]) -> dict[str, object]:
|
||||
if len(next_devices) == len(catalog["devices"]):
|
||||
raise RuntimeError(f"未找到设备: {device_id}")
|
||||
write_manual_catalog({"brands": catalog["brands"], "devices": next_devices})
|
||||
rebuild_result = rebuild_generated_outputs()
|
||||
rebuild_result = rebuild_generated_outputs(defer_mysql_load=True)
|
||||
return {
|
||||
"deleted_device": device_id,
|
||||
"catalog": manual_catalog_payload(),
|
||||
|
||||
Reference in New Issue
Block a user