autotrain/nextgen/autotrain_next/profiles.py
2026-07-24 10:37:32 +08:00

66 lines
2.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""每台手机独立的、可移植的设备档案。坐标全部是 0..1 的归一化值。"""
from __future__ import annotations
import json
import re
import hashlib
from datetime import datetime, timezone
from pathlib import Path
from .paths import PROFILES, ensure_workdirs
class ProfileError(RuntimeError):
pass
# 核验/确认常处于 WebViewXML 录制会被标记为 other这三页足以确认设备、列表和结果路径。
REQUIRED_SAMPLE_PAGES = {"list", "invoice_info", "success"}
def safe_id(serial: str) -> str:
return re.sub(r"[^A-Za-z0-9_.-]+", "_", serial)
def path_for(serial: str) -> Path:
return PROFILES / f"{safe_id(serial)}_{hashlib.sha256(serial.encode()).hexdigest()[:10]}.json"
def load(serial: str) -> dict | None:
path = path_for(serial)
if not path.exists():
return None
return json.loads(path.read_text(encoding="utf-8"))
def save(profile: dict) -> Path:
serial = str(profile.get("device", {}).get("serial", ""))
if not serial:
raise ProfileError("设备档案缺少 device.serial")
ensure_workdirs()
profile["updated_at"] = datetime.now(timezone.utc).isoformat()
destination = path_for(serial)
destination.write_text(json.dumps(profile, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
return destination
def normalized_point(point: tuple[float, float], screen: tuple[int, int]) -> tuple[int, int]:
x, y = point
if not 0 <= x <= 1 or not 0 <= y <= 1:
raise ProfileError("坐标必须是 0 到 1 的归一化值")
width, height = screen
return round(x * (width - 1)), round(y * (height - 1))
def compatibility(profile: dict | None, current: dict[str, str]) -> dict:
if not profile:
return {"status": "missing", "reasons": ["该手机尚未采样"]}
saved = profile.get("device", {})
reasons: list[str] = []
if saved.get("serial") != current.get("serial"):
reasons.append("设备序列号不一致")
for key, label in (("orientation", "屏幕方向"), ("screen", "屏幕尺寸"), ("density", "屏幕密度")):
if saved.get(key) and saved.get(key) != current.get(key):
reasons.append(f"{label}已变化")
return {"status": "ready" if not reasons and profile.get("status") == "verified" else "draft", "reasons": reasons}