feat(measure): memorizza tempo di inserimento misura (input_duration_ms)
Colonna nullable su measurements + migration 003, campo opzionale negli schemi API (create singola e batch), passthrough nel proxy Flask e timer lato client in task_execute: parte all'attivazione del subtask, si resetta su auto-advance e navigazione manuale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -311,6 +311,7 @@ def save_measurement():
|
||||
"lot_number": data.get("lot_number", session.get("lot_number", "")),
|
||||
"serial_number": data.get("serial_number", session.get("serial_number", "")),
|
||||
"input_method": data.get("input_method", "manual"),
|
||||
"input_duration_ms": data.get("input_duration_ms"),
|
||||
}
|
||||
|
||||
resp = api_client.post("/api/measurements", data=payload)
|
||||
|
||||
@@ -729,6 +729,9 @@ function taskExecute() {
|
||||
// ---- Value from numpad / caliper ----
|
||||
currentValue: null,
|
||||
|
||||
// ---- Input timing (ms spent on the active subtask) ----
|
||||
inputStartedAt: null,
|
||||
|
||||
// ---- Image switching logic ----
|
||||
get currentSubtaskImage() {
|
||||
return this.currentSubtask?.image_path || null;
|
||||
@@ -808,6 +811,7 @@ function taskExecute() {
|
||||
// ---- Init ----
|
||||
init() {
|
||||
this.subtasks.sort((a, b) => (a.order_index || 0) - (b.order_index || 0));
|
||||
this.inputStartedAt = Date.now();
|
||||
},
|
||||
|
||||
// ---- Check if a subtask has been measured ----
|
||||
@@ -834,6 +838,9 @@ function taskExecute() {
|
||||
|
||||
const pf = this.passFailStatus;
|
||||
const dev = this.deviation;
|
||||
const inputDurationMs = this.inputStartedAt !== null
|
||||
? Math.max(0, Math.round(Date.now() - this.inputStartedAt))
|
||||
: null;
|
||||
|
||||
this.saving = true;
|
||||
|
||||
@@ -853,6 +860,7 @@ function taskExecute() {
|
||||
lot_number: this.lotNumber,
|
||||
serial_number: this.serialNumber,
|
||||
input_method: inputMethod || 'manual',
|
||||
input_duration_ms: inputDurationMs,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -911,6 +919,7 @@ function taskExecute() {
|
||||
// ---- Advance to next unmeasured subtask ----
|
||||
advanceToNext() {
|
||||
this.currentValue = null;
|
||||
this.inputStartedAt = Date.now();
|
||||
|
||||
for (let i = this.currentIndex + 1; i < this.totalSubtasks; i++) {
|
||||
if (!this.isMeasured(this.subtasks[i].id)) {
|
||||
@@ -932,6 +941,7 @@ function taskExecute() {
|
||||
this.currentIndex = index;
|
||||
this.currentValue = null;
|
||||
this.errorMessage = '';
|
||||
this.inputStartedAt = Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -80,6 +80,45 @@ class TestSaveMeasurement:
|
||||
assert data["id"] == 1
|
||||
assert data["pass_fail"] == "pass"
|
||||
|
||||
def test_save_measurement_forwards_input_duration(
|
||||
self, logged_in_client, mock_api_client
|
||||
):
|
||||
"""input_duration_ms is forwarded to the backend payload."""
|
||||
mock_api_client.post.return_value = {"id": 1, "pass_fail": "pass"}
|
||||
|
||||
resp = logged_in_client.post(
|
||||
"/measure/save-measurement",
|
||||
json={
|
||||
"subtask_id": 10,
|
||||
"version_id": 3,
|
||||
"value": 9.95,
|
||||
"input_duration_ms": 4250,
|
||||
},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
payload = mock_api_client.post.call_args.kwargs["data"]
|
||||
assert payload["input_duration_ms"] == 4250
|
||||
|
||||
def test_save_measurement_omits_input_duration_when_absent(
|
||||
self, logged_in_client, mock_api_client
|
||||
):
|
||||
"""Without input_duration_ms the payload sends null (backend default)."""
|
||||
mock_api_client.post.return_value = {"id": 1, "pass_fail": "pass"}
|
||||
|
||||
resp = logged_in_client.post(
|
||||
"/measure/save-measurement",
|
||||
json={
|
||||
"subtask_id": 10,
|
||||
"version_id": 3,
|
||||
"value": 9.95,
|
||||
},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
payload = mock_api_client.post.call_args.kwargs["data"]
|
||||
assert payload.get("input_duration_ms") is None
|
||||
|
||||
def test_save_measurement_missing_fields(self, logged_in_client, mock_api_client):
|
||||
"""Missing required fields return 400."""
|
||||
resp = logged_in_client.post(
|
||||
|
||||
Reference in New Issue
Block a user