190 lines
6.5 KiB
Python
190 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sqlite3
|
|
import sys
|
|
import time
|
|
from typing import Any, Dict
|
|
|
|
|
|
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
if PROJECT_ROOT not in sys.path:
|
|
sys.path.insert(0, PROJECT_ROOT)
|
|
|
|
from config import MONITORING_DB_PATH
|
|
|
|
|
|
def _safe_loads(value: Any) -> Dict[str, Any]:
|
|
if not value:
|
|
return {}
|
|
try:
|
|
payload = json.loads(value)
|
|
except (TypeError, ValueError):
|
|
return {}
|
|
return payload if isinstance(payload, dict) else {}
|
|
|
|
|
|
def _clear_payload_last_updated(raw_payload: Any) -> str:
|
|
payload = _safe_loads(raw_payload)
|
|
if not payload:
|
|
return str(raw_payload or "")
|
|
payload["last_updated"] = ""
|
|
original_row = payload.get("original_row")
|
|
if isinstance(original_row, dict):
|
|
original_row = dict(original_row)
|
|
original_row["last_updated"] = ""
|
|
original_row["最后更新"] = ""
|
|
original_row["更新时间"] = ""
|
|
payload["original_row"] = original_row
|
|
return json.dumps(payload, ensure_ascii=False)
|
|
|
|
|
|
def reset_catalog_update_state(args) -> int:
|
|
db_path = str(args.db_path or "").strip()
|
|
apply_changes = bool(args.apply)
|
|
clear_payload = bool(args.clear_payload)
|
|
now_iso = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
|
with sqlite3.connect(db_path) as connection:
|
|
connection.row_factory = sqlite3.Row
|
|
table_row = connection.execute(
|
|
"""
|
|
SELECT name
|
|
FROM sqlite_master
|
|
WHERE type = 'table'
|
|
AND name = 'app_catalog'
|
|
"""
|
|
).fetchone()
|
|
if not table_row:
|
|
raise SystemExit(f"app_catalog not found in {db_path}")
|
|
|
|
counts = connection.execute(
|
|
"""
|
|
SELECT
|
|
COUNT(*) AS total,
|
|
COALESCE(SUM(CASE WHEN COALESCE(last_updated, '') != '' THEN 1 ELSE 0 END), 0) AS last_updated_rows,
|
|
COALESCE(SUM(CASE WHEN COALESCE(last_update_interval_days, 0) != 0 THEN 1 ELSE 0 END), 0) AS interval_rows
|
|
FROM app_catalog
|
|
"""
|
|
).fetchone()
|
|
task_counts = connection.execute(
|
|
"""
|
|
SELECT
|
|
COALESCE(SUM(CASE WHEN COALESCE(is_new_app, 1) = 0 THEN 1 ELSE 0 END), 0) AS app_update_rows,
|
|
COALESCE(
|
|
SUM(
|
|
CASE
|
|
WHEN task_status = 'pending'
|
|
AND COALESCE(is_new_app, 1) = 0
|
|
AND COALESCE(error_reason, '') LIKE 'catalog_version_update:%'
|
|
THEN 1
|
|
ELSE 0
|
|
END
|
|
),
|
|
0
|
|
) AS version_pending_rows
|
|
FROM collection_task
|
|
"""
|
|
).fetchone()
|
|
|
|
if not apply_changes:
|
|
print(
|
|
"dry_run=1 db={db} total={total} clear_last_updated={last_updated_rows} "
|
|
"reset_interval_rows={interval_rows} app_update_tasks={app_update_rows} "
|
|
"delete_version_pending={version_pending_rows}".format(
|
|
db=db_path,
|
|
total=int(counts["total"] or 0),
|
|
last_updated_rows=int(counts["last_updated_rows"] or 0),
|
|
interval_rows=int(counts["interval_rows"] or 0),
|
|
app_update_rows=int(task_counts["app_update_rows"] or 0),
|
|
version_pending_rows=int(task_counts["version_pending_rows"] or 0),
|
|
)
|
|
)
|
|
return 0
|
|
|
|
cursor = connection.execute(
|
|
"""
|
|
DELETE FROM collection_task
|
|
WHERE task_status = 'pending'
|
|
AND COALESCE(is_new_app, 1) = 0
|
|
AND COALESCE(error_reason, '') LIKE 'catalog_version_update:%'
|
|
"""
|
|
)
|
|
deleted_version_pending = int(cursor.rowcount or 0)
|
|
|
|
cursor = connection.execute(
|
|
"""
|
|
UPDATE app_catalog
|
|
SET last_update_interval_days = 0,
|
|
updated_at = ?
|
|
WHERE COALESCE(last_update_interval_days, 0) != 0
|
|
""",
|
|
(now_iso,),
|
|
)
|
|
reset_interval_rows = int(cursor.rowcount or 0)
|
|
|
|
if clear_payload:
|
|
payload_rows = connection.execute(
|
|
"""
|
|
SELECT package_name, task_payload_json
|
|
FROM app_catalog
|
|
WHERE COALESCE(task_payload_json, '') != ''
|
|
"""
|
|
).fetchall()
|
|
for row in payload_rows:
|
|
connection.execute(
|
|
"""
|
|
UPDATE app_catalog
|
|
SET task_payload_json = ?,
|
|
updated_at = ?
|
|
WHERE package_name = ?
|
|
""",
|
|
(_clear_payload_last_updated(row["task_payload_json"]), now_iso, row["package_name"]),
|
|
)
|
|
|
|
cursor = connection.execute(
|
|
"""
|
|
UPDATE app_catalog
|
|
SET last_updated = '',
|
|
updated_at = ?
|
|
WHERE COALESCE(last_updated, '') != ''
|
|
""",
|
|
(now_iso,),
|
|
)
|
|
cleared_last_updated = int(cursor.rowcount or 0)
|
|
print(
|
|
"dry_run=0 db={db} cleared_last_updated={cleared_last_updated} "
|
|
"reset_interval_rows={reset_interval_rows} deleted_version_pending={deleted_version_pending} "
|
|
"clear_payload={clear_payload}".format(
|
|
db=db_path,
|
|
cleared_last_updated=cleared_last_updated,
|
|
reset_interval_rows=reset_interval_rows,
|
|
deleted_version_pending=deleted_version_pending,
|
|
clear_payload=int(clear_payload),
|
|
)
|
|
)
|
|
return 0
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(
|
|
description="Clear app_catalog last_updated and delete pending catalog_version_update collection_task rows."
|
|
)
|
|
parser.add_argument("--db-path", default=MONITORING_DB_PATH, help="Target monitoring sqlite path.")
|
|
parser.add_argument("--apply", action="store_true", help="Apply changes. Without this, only prints counts.")
|
|
parser.add_argument(
|
|
"--clear-payload",
|
|
action="store_true",
|
|
help="Also clear last_updated inside task_payload_json/original_row.",
|
|
)
|
|
return parser
|
|
|
|
|
|
def main() -> int:
|
|
return reset_catalog_update_state(build_parser().parse_args())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|