Class: Tasker::WorkflowStepTransition

Inherits:
ApplicationRecord show all
Defined in:
app/models/tasker/workflow_step_transition.rb

Overview

WorkflowStepTransition represents state transitions for WorkflowStep entities using Statesman

This model stores the audit trail of all workflow step state changes, providing a complete history of step lifecycle events with metadata and timestamps.

Defined Under Namespace

Classes: TransitionDescriptionFormatter

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

configure_database_connections, database_configuration_exists?

Class Method Details

.for_attempt(attempt) ⇒ ActiveRecord::Relation

Find transitions by attempt number

Parameters:

  • attempt (Integer)

    The attempt number to filter by

Returns:

  • (ActiveRecord::Relation)

    Transitions for the given attempt



93
94
95
# File 'app/models/tasker/workflow_step_transition.rb', line 93

def for_attempt(attempt)
  ('attempt_number', attempt)
end

.in_time_range(start_time, end_time) ⇒ ActiveRecord::Relation

Find all transitions that occurred within a time range

Parameters:

  • start_time (Time)

    The start of the time range

  • end_time (Time)

    The end of the time range

Returns:

  • (ActiveRecord::Relation)

    Transitions within the time range



67
68
69
# File 'app/models/tasker/workflow_step_transition.rb', line 67

def in_time_range(start_time, end_time)
  where(created_at: start_time..end_time)
end

.most_recent_to_state(state) ⇒ WorkflowStepTransition?

Find the most recent transition to a specific state

Parameters:

  • state (String, Symbol)

    The state to find the most recent transition to

Returns:



58
59
60
# File 'app/models/tasker/workflow_step_transition.rb', line 58

def most_recent_to_state(state)
  to_state(state.to_s).order(sort_key: :desc).first
end

.retry_transitionsActiveRecord::Relation

Find retry transitions

Returns:

  • (ActiveRecord::Relation)

    Transitions from error back to pending (retries)



83
84
85
86
87
# File 'app/models/tasker/workflow_step_transition.rb', line 83

def retry_transitions
  joins("INNER JOIN #{table_name} prev ON prev.workflow_step_id = #{table_name}.workflow_step_id")
    .where(to_state: 'pending')
    .where('prev.to_state = ? AND prev.sort_key < ?', 'error', arel_table[:sort_key])
end

.statisticsHash

Get transition statistics for analytics

Returns:

  • (Hash)

    Statistics about transitions



100
101
102
103
104
105
106
107
108
109
# File 'app/models/tasker/workflow_step_transition.rb', line 100

def statistics
  {
    total_transitions: count,
    states: group(:to_state).count,
    recent_activity: where(created_at: 24.hours.ago..Time.current).count,
    retry_attempts: retry_transitions.count,
    average_execution_time: average_execution_time,
    average_time_between_transitions: average_time_between_transitions
  }
end

.with_metadata_value(key, value) ⇒ ActiveRecord::Relation

Find transitions with specific metadata values

Parameters:

  • key (String, Symbol)

    The metadata key to search for

  • value (Object)

    The value to match

Returns:

  • (ActiveRecord::Relation)

    Transitions with matching metadata



76
77
78
# File 'app/models/tasker/workflow_step_transition.rb', line 76

def (key, value)
  where('metadata->:key = :value', key: key.to_s, value: value.to_json.delete('"'))
end

Instance Method Details

#attempt_numberInteger

Get the attempt number for this transition

Returns:

  • (Integer)

    The attempt number



199
200
201
# File 'app/models/tasker/workflow_step_transition.rb', line 199

def attempt_number
  ('attempt_number', 1)
end

#backoff_infoHash?

Get backoff information if this is an error transition

Returns:

  • (Hash, nil)

    Backoff information or nil if not applicable



263
264
265
266
267
268
269
270
271
# File 'app/models/tasker/workflow_step_transition.rb', line 263

def backoff_info
  return nil unless error_transition? && has_metadata?('backoff_until')

  {
    backoff_until: Time.zone.parse(('backoff_until')),
    backoff_seconds: ('backoff_seconds'),
    retry_available: ('retry_available', false)
  }
end

#cancellation_transition?Boolean

Check if this transition represents cancellation

Returns:

  • (Boolean)

    True if transitioning to cancelled state



185
186
187
# File 'app/models/tasker/workflow_step_transition.rb', line 185

def cancellation_transition?
  to_state == 'cancelled'
end

#completion_transition?Boolean

Check if this transition represents completion

Returns:

  • (Boolean)

    True if transitioning to a completion state



178
179
180
# File 'app/models/tasker/workflow_step_transition.rb', line 178

def completion_transition?
  %w[complete resolved_manually].include?(to_state)
end

#descriptionString

Get human-readable description of the transition

Returns:

  • (String)

    Description of what this transition represents



213
214
215
# File 'app/models/tasker/workflow_step_transition.rb', line 213

def description
  TransitionDescriptionFormatter.format(self)
end

#duration_since_previousFloat?

Get the duration since the previous transition

Returns:

  • (Float, nil)

    Duration in seconds since previous transition



157
158
159
160
161
162
163
164
165
166
# File 'app/models/tasker/workflow_step_transition.rb', line 157

def duration_since_previous
  previous_transition = self.class.where(workflow_step_id: workflow_step_id)
                            .where(sort_key: ...sort_key)
                            .order(sort_key: :desc)
                            .first

  return nil unless previous_transition

  created_at - previous_transition.created_at
end

#error_transition?Boolean

Check if this transition represents an error state

Returns:

  • (Boolean)

    True if transitioning to an error state



171
172
173
# File 'app/models/tasker/workflow_step_transition.rb', line 171

def error_transition?
  to_state == 'error'
end

#execution_durationFloat?

Get the execution duration if available

Returns:

  • (Float, nil)

    Execution duration in seconds



206
207
208
# File 'app/models/tasker/workflow_step_transition.rb', line 206

def execution_duration
  ('execution_duration')
end

#formatted_metadataHash

Get formatted metadata for display

Returns:

  • (Hash)

    Formatted metadata with additional computed fields



220
221
222
223
224
225
226
227
228
229
230
231
# File 'app/models/tasker/workflow_step_transition.rb', line 220

def 
   = .dup

  # Add computed fields
  ['duration_since_previous'] = duration_since_previous
  ['transition_description'] = description
  ['transition_timestamp'] = created_at.iso8601
  ['step_name'] = step_name
  ['task_id'] = workflow_step.task_id

  
end

#get_metadata(key, default = nil) ⇒ Object

Get metadata value with default

Parameters:

  • key (String, Symbol)

    The metadata key

  • default (Object) (defaults to: nil)

    Default value if key not found

Returns:

  • (Object)

    The metadata value or default



246
247
248
# File 'app/models/tasker/workflow_step_transition.rb', line 246

def (key, default = nil)
  .fetch(key.to_s, default)
end

#has_metadata?(key) ⇒ Boolean

Check if transition has specific metadata

Parameters:

  • key (String, Symbol)

    The metadata key to check for

Returns:

  • (Boolean)

    True if the metadata contains the key



237
238
239
# File 'app/models/tasker/workflow_step_transition.rb', line 237

def has_metadata?(key)
  .key?(key.to_s)
end

#retry_transition?Boolean

Check if this transition represents a retry attempt

Returns:

  • (Boolean)

    True if this is a retry transition



192
193
194
# File 'app/models/tasker/workflow_step_transition.rb', line 192

def retry_transition?
  to_state == 'pending' && has_metadata?('retry_attempt')
end

#set_metadata(key, value) ⇒ Object

Set metadata value

Parameters:

  • key (String, Symbol)

    The metadata key

  • value (Object)

    The value to set

Returns:

  • (Object)

    The set value



255
256
257
258
# File 'app/models/tasker/workflow_step_transition.rb', line 255

def (key, value)
  self. = .merge(key.to_s => value)
  value
end

#step_nameString

Get the step name through the workflow step

Returns:

  • (String)

    The name of the workflow step



150
151
152
# File 'app/models/tasker/workflow_step_transition.rb', line 150

def step_name
  workflow_step.name
end

#taskTask

Get the associated task through the workflow step

Returns:

  • (Task)

    The task that owns this workflow step



145
# File 'app/models/tasker/workflow_step_transition.rb', line 145

delegate :task, to: :workflow_step