2026-10-06 19:33:17 +00:00
|
|
|
"""Data controls tab: model-improvement switch (read-only otherwise).
|
|
|
|
|
|
2026-10-07 00:25:51 +00:00
|
|
|
The switch label is pinned from live recon; resolution prefers it
|
2026-10-07 00:49:23 +00:00
|
|
|
and falls back to single-switch, then keyword match. Sets use a
|
|
|
|
|
trusted click: synthetic clicks are proven no-ops here (2026-10-06).
|
|
|
|
|
Import/Delete rows are inventoried, never touched.
|
2026-10-06 19:33:17 +00:00
|
|
|
"""
|
2026-10-07 00:49:23 +00:00
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
from approvals import cdp_evaluate
|
2026-10-06 19:33:17 +00:00
|
|
|
from hatch_menu import controls, dialog
|
2026-10-07 00:49:23 +00:00
|
|
|
from hatch_menu.mouse import real_click
|
2026-10-06 19:33:17 +00:00
|
|
|
|
|
|
|
|
TAB = "Data controls"
|
2026-10-07 00:25:51 +00:00
|
|
|
SWITCH_LABEL = "Help improve our AI models"
|
2026-10-06 19:33:17 +00:00
|
|
|
_KEYWORDS = ("improv", "train", "model", "data", "usage")
|
|
|
|
|
|
2026-10-07 00:49:23 +00:00
|
|
|
JS_AI_RECT_TMPL = """((label) => {
|
|
|
|
|
const d = document.querySelector('[role="dialog"]');
|
|
|
|
|
if (!d) return null;
|
|
|
|
|
const rowOf = (s) => {
|
|
|
|
|
let el = s.parentElement, depth = 0;
|
|
|
|
|
while (el && el !== d && depth < 6) {
|
|
|
|
|
const t = (el.innerText || '').trim();
|
|
|
|
|
if (t && t.length < 250) return t.toLowerCase();
|
|
|
|
|
el = el.parentElement;
|
|
|
|
|
depth += 1;
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
};
|
|
|
|
|
const hits = Array.from(d.querySelectorAll('[role="switch"]'))
|
|
|
|
|
.filter(s => rowOf(s).includes(label.toLowerCase())
|
|
|
|
|
|| (s.getAttribute('aria-label') || '').toLowerCase()
|
|
|
|
|
.includes(label.toLowerCase()));
|
|
|
|
|
if (hits.length !== 1) return null;
|
|
|
|
|
const r = hits[0].getBoundingClientRect();
|
|
|
|
|
return {x: r.x + r.width / 2, y: r.y + r.height / 2};
|
|
|
|
|
})('%s')"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _eval(ws, js, timeout=8.0):
|
|
|
|
|
try:
|
|
|
|
|
return cdp_evaluate(ws, js, timeout=timeout)
|
|
|
|
|
except Exception:
|
|
|
|
|
return None
|
|
|
|
|
|
2026-10-06 19:33:17 +00:00
|
|
|
|
|
|
|
|
def _resolve(ws):
|
|
|
|
|
"""The improvement switch dict, or None when not resolvable."""
|
|
|
|
|
if not dialog.goto_tab(ws, TAB):
|
|
|
|
|
return None
|
|
|
|
|
switches = controls.list_switches(ws)
|
2026-10-07 00:25:51 +00:00
|
|
|
for s in switches:
|
|
|
|
|
blob = ((s.get("label") or "") + " "
|
|
|
|
|
+ (s.get("aria") or "")).lower()
|
|
|
|
|
if SWITCH_LABEL.lower() in blob:
|
|
|
|
|
return s
|
2026-10-06 19:33:17 +00:00
|
|
|
if len(switches) == 1:
|
|
|
|
|
return switches[0]
|
|
|
|
|
for kw in _KEYWORDS:
|
|
|
|
|
for s in switches:
|
|
|
|
|
blob = ((s.get("label") or "") + " "
|
|
|
|
|
+ (s.get("aria") or "")).lower()
|
|
|
|
|
if kw in blob:
|
|
|
|
|
return s
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ai_improvement(ws):
|
|
|
|
|
"""Improvement-switch state: True/False, None when unreadable."""
|
|
|
|
|
sw = _resolve(ws)
|
|
|
|
|
return None if sw is None else bool(sw.get("checked"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_ai_improvement(ws, on):
|
2026-10-07 00:49:23 +00:00
|
|
|
"""Set via one trusted click; synthetic clicks are no-ops. Bool."""
|
2026-10-06 19:33:17 +00:00
|
|
|
sw = _resolve(ws)
|
|
|
|
|
if sw is None:
|
|
|
|
|
return False
|
2026-10-07 00:49:23 +00:00
|
|
|
if bool(sw.get("checked")) == bool(on):
|
|
|
|
|
return True
|
|
|
|
|
rect = _eval(ws, JS_AI_RECT_TMPL % SWITCH_LABEL)
|
|
|
|
|
if not rect or "x" not in rect:
|
2026-10-06 19:33:17 +00:00
|
|
|
return False
|
2026-10-07 00:49:23 +00:00
|
|
|
try:
|
|
|
|
|
real_click(ws, rect["x"], rect["y"])
|
|
|
|
|
except Exception:
|
|
|
|
|
return False
|
|
|
|
|
for _ in range(8):
|
|
|
|
|
time.sleep(2.0)
|
|
|
|
|
if ai_improvement(ws) is bool(on):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
2026-10-06 19:33:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def describe(ws):
|
|
|
|
|
"""Inventory: switch state + row names (import/delete read-only)."""
|
|
|
|
|
state = ai_improvement(ws)
|
|
|
|
|
return {"ai_improvement":
|
|
|
|
|
("on" if state else "off") if state is not None else None,
|
|
|
|
|
"rows": dialog.describe_rows(ws, TAB)}
|