fix error in gitignore

This commit is contained in:
2026-09-24 12:32:59 +03:30
commit 86c117ee2a
165 changed files with 5963 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
from django.db import transaction
from django.utils import timezone
from apps.core.errors import ApiError
from apps.processing.models import StepExecution
class ProcessingService:
"""Step execution state machine. Transitions are validated server-side."""
_STARTABLE = {"pending", "ready", "rework_required"}
_PAUSABLE = {"in_progress"}
_RESUMABLE = {"paused"}
_COMPLETABLE = {"in_progress"}
_REJECTABLE = {"in_progress", "waiting"}
@transaction.atomic
def start(self, execution, user) -> StepExecution:
execution = self._locked(execution)
if execution.state not in self._STARTABLE:
raise ApiError("INVALID_STEP_TRANSITION", f"Cannot start from state '{execution.state}'.")
execution.state = StepExecution.State.IN_PROGRESS
execution.started_at = timezone.now()
execution.assigned_to = user
execution.save()
return execution
@transaction.atomic
def pause(self, execution) -> StepExecution:
execution = self._locked(execution)
if execution.state not in self._PAUSABLE:
raise ApiError("INVALID_STEP_TRANSITION", f"Cannot pause from state '{execution.state}'.")
execution.state = StepExecution.State.PAUSED
execution.save()
return execution
@transaction.atomic
def resume(self, execution) -> StepExecution:
execution = self._locked(execution)
if execution.state not in self._RESUMABLE:
raise ApiError("INVALID_STEP_TRANSITION", f"Cannot resume from state '{execution.state}'.")
execution.state = StepExecution.State.IN_PROGRESS
execution.save()
return execution
@transaction.atomic
def complete(self, execution, measurements=None, notes="") -> StepExecution:
execution = self._locked(execution)
if execution.state not in self._COMPLETABLE:
raise ApiError("INVALID_STEP_TRANSITION", f"Cannot complete from state '{execution.state}'.")
execution.state = StepExecution.State.COMPLETED
execution.completed_at = timezone.now()
if measurements is not None:
execution.measurements = measurements
if notes:
execution.notes = notes
execution.save()
return execution
@transaction.atomic
def reject(self, execution, notes="") -> StepExecution:
execution = self._locked(execution)
if execution.state not in self._REJECTABLE:
raise ApiError("INVALID_STEP_TRANSITION", f"Cannot reject from state '{execution.state}'.")
execution.state = StepExecution.State.REJECTED
execution.notes = notes or execution.notes
execution.save()
return execution
@transaction.atomic
def rework(self, execution, target_step) -> StepExecution:
"""Create a new execution on `target_step`; preserve history via parent link."""
execution = self._locked(execution)
if execution.state != StepExecution.State.COMPLETED:
raise ApiError("INVALID_STEP_TRANSITION", "Only a completed step can trigger rework.")
execution.state = StepExecution.State.REWORK_REQUIRED
execution.save()
latest = (
StepExecution.objects.filter(order=execution.order, step=target_step)
.order_by("-attempt")
.first()
)
attempt = (latest.attempt if latest else 0) + 1
return StepExecution.objects.create(
order=execution.order,
step=target_step,
state=StepExecution.State.READY,
attempt=attempt,
parent=execution,
)
@staticmethod
def _locked(execution) -> StepExecution:
return StepExecution.objects.select_for_update().get(pk=execution.pk)