M18: implement operational workspaces
This commit is contained in:
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import UTC, datetime
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from difflib import SequenceMatcher
|
||||
|
||||
from sqlalchemy import select, update
|
||||
@@ -28,6 +28,15 @@ REQUIRED_VEHICLE_FIELDS = ("registration_number", "make", "model", "location")
|
||||
DUPLICATE_THRESHOLD = 70
|
||||
|
||||
|
||||
def issue_due_at(detected_at: datetime, severity: str) -> datetime:
|
||||
"""Return the local operational SLA deadline for a newly detected issue."""
|
||||
return detected_at + {
|
||||
"high": timedelta(hours=4),
|
||||
"medium": timedelta(days=1),
|
||||
"low": timedelta(days=3),
|
||||
}.get(severity, timedelta(days=1))
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScanResult:
|
||||
created: dict[str, int] = field(default_factory=dict)
|
||||
@@ -112,6 +121,7 @@ def _open_issue(
|
||||
evidence_json=evidence,
|
||||
proposed_action_json={},
|
||||
detected_at=now,
|
||||
due_at=issue_due_at(now, severity),
|
||||
)
|
||||
db.add(issue)
|
||||
db.flush()
|
||||
@@ -280,9 +290,7 @@ def _scan_odometer_regressions(db: Session, scan: ScanResult) -> None:
|
||||
vehicles = {v.id: v for v in db.scalars(select(Vehicle)).all()}
|
||||
bookings_by_vehicle: dict[uuid.UUID, list[Booking]] = {}
|
||||
for booking in db.scalars(
|
||||
select(Booking).where(
|
||||
Booking.status == "returned", Booking.end_odometer_km.is_not(None)
|
||||
)
|
||||
select(Booking).where(Booking.status == "returned", Booking.end_odometer_km.is_not(None))
|
||||
).all():
|
||||
bookings_by_vehicle.setdefault(booking.vehicle_id, []).append(booking)
|
||||
|
||||
@@ -346,9 +354,7 @@ def run_scan(
|
||||
|
||||
|
||||
def _load_open_issue(db: Session, public_ref: str) -> DataQualityIssue:
|
||||
issue = db.scalar(
|
||||
select(DataQualityIssue).where(DataQualityIssue.public_ref == public_ref)
|
||||
)
|
||||
issue = db.scalar(select(DataQualityIssue).where(DataQualityIssue.public_ref == public_ref))
|
||||
if issue is None:
|
||||
raise AppError("ISSUE_NOT_FOUND", "Data quality issue not found.", status_code=404)
|
||||
if issue.status != "open":
|
||||
@@ -750,9 +756,7 @@ def apply_recommended_status(
|
||||
# recommendation alone for the post-condition.
|
||||
post_facts = gather_vehicle_status_facts(db, vehicle, exclude_issue_id=issue.id)
|
||||
post_check = evaluate_vehicle_status(vehicle, post_facts)
|
||||
if post_check.recommendation_code not in (
|
||||
RECOMMENDATION_CODE_NO_CONFLICT,
|
||||
):
|
||||
if post_check.recommendation_code not in (RECOMMENDATION_CODE_NO_CONFLICT,):
|
||||
raise AppError(
|
||||
"CONFLICT_STILL_PRESENT",
|
||||
"Applying the recommended status did not resolve the conflict.",
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
@@ -255,6 +255,7 @@ def register_vehicle_return(
|
||||
},
|
||||
proposed_action_json={},
|
||||
detected_at=now,
|
||||
due_at=now + timedelta(days=1),
|
||||
)
|
||||
db.add(issue)
|
||||
db.flush()
|
||||
|
||||
Reference in New Issue
Block a user