feat: add reporting event actor attribution

This commit is contained in:
Shivam Mishra
2026-07-01 17:11:48 +05:30
parent 3c4225cf57
commit 6ba86788b2
7 changed files with 90 additions and 33 deletions
+48 -23
View File
@@ -16,7 +16,8 @@ class ReportingEventListener < BaseListener
user_id: conversation.assignee_id,
conversation_id: conversation.id,
event_start_time: conversation.created_at,
event_end_time: event_end_time
event_end_time: event_end_time,
**actor_attributes(actor_from_event(event))
)
create_bot_resolved_event(conversation, reporting_event)
@@ -39,7 +40,8 @@ class ReportingEventListener < BaseListener
user_id: message.sender_id,
conversation_id: conversation.id,
event_start_time: last_non_human_activity(conversation),
event_end_time: message.created_at
event_end_time: message.created_at,
**actor_attributes(actor_from_event(event) || message.sender)
)
reporting_event.save!
@@ -65,7 +67,8 @@ class ReportingEventListener < BaseListener
user_id: conversation.assignee_id,
conversation_id: conversation.id,
event_start_time: waiting_since,
event_end_time: message.created_at
event_end_time: message.created_at,
**actor_attributes(actor_from_event(event) || message.sender)
)
reporting_event.save!
safe_rollup(reporting_event)
@@ -92,7 +95,8 @@ class ReportingEventListener < BaseListener
user_id: conversation.assignee_id,
conversation_id: conversation.id,
event_start_time: conversation.created_at,
event_end_time: event_end_time
event_end_time: event_end_time,
**actor_attributes(actor_from_event(event))
)
reporting_event.save!
safe_rollup(reporting_event)
@@ -116,34 +120,44 @@ class ReportingEventListener < BaseListener
name: 'conversation_resolved'
).where('event_end_time <= ?', event_end_time).order(event_end_time: :desc).first
# For first-time openings, value is 0
# For reopenings, calculate time since resolution
if last_resolved_event
time_since_resolved = event_end_time.to_i - last_resolved_event.event_end_time.to_i
business_hours_value = business_hours(conversation.inbox, last_resolved_event.event_end_time, event_end_time)
start_time = last_resolved_event.event_end_time
else
time_since_resolved = 0
business_hours_value = 0
start_time = conversation.created_at
end
create_conversation_opened_event(conversation, time_since_resolved, business_hours_value, start_time, event_end_time)
create_conversation_opened_event(
conversation,
conversation_opened_event_attributes(conversation, last_resolved_event, event_end_time),
actor_from_event(event)
)
end
private
def create_conversation_opened_event(conversation, time_since_resolved, business_hours_value, start_time, event_end_time)
def conversation_opened_event_attributes(conversation, last_resolved_event, event_end_time)
return first_conversation_opened_event_attributes(conversation, event_end_time) if last_resolved_event.blank?
{
value: event_end_time.to_i - last_resolved_event.event_end_time.to_i,
value_in_business_hours: business_hours(conversation.inbox, last_resolved_event.event_end_time, event_end_time),
event_start_time: last_resolved_event.event_end_time,
event_end_time: event_end_time
}
end
def first_conversation_opened_event_attributes(conversation, event_end_time)
{
value: 0,
value_in_business_hours: 0,
event_start_time: conversation.created_at,
event_end_time: event_end_time
}
end
def create_conversation_opened_event(conversation, event_attributes, actor)
reporting_event = ReportingEvent.new(
name: 'conversation_opened',
value: time_since_resolved,
value_in_business_hours: business_hours_value,
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
user_id: conversation.assignee_id,
conversation_id: conversation.id,
event_start_time: start_time,
event_end_time: event_end_time
**event_attributes,
**actor_attributes(actor)
)
reporting_event.save!
end
@@ -160,7 +174,8 @@ class ReportingEventListener < BaseListener
user_id: conversation.assignee_id,
conversation_id: conversation.id,
event_start_time: conversation.created_at,
event_end_time: event.timestamp
event_end_time: event.timestamp,
**actor_attributes(actor_from_event(event))
)
end
@@ -175,6 +190,16 @@ class ReportingEventListener < BaseListener
safe_rollup(bot_resolved_event)
end
def actor_from_event(event)
event.data[:performed_by]
end
def actor_attributes(actor)
return {} if actor.blank? || actor.id.blank?
{ actor_type: actor.class.name, actor_id: actor.id }
end
def safe_rollup(reporting_event)
# Rollups are derived from the raw reporting event. If a transient rollup write
# failure bubbles out here, Sidekiq retries the dispatcher job and can insert the
+1 -1
View File
@@ -343,7 +343,7 @@ class Conversation < ApplicationRecord
def dispatcher_dispatch(event_name, changed_attributes = nil)
Rails.configuration.dispatcher.dispatch(event_name, Time.zone.now, conversation: self, notifiable_assignee_change: notifiable_assignee_change?,
changed_attributes: changed_attributes,
performed_by: Current.executed_by)
performed_by: Current.executed_by || Current.user)
end
def set_unread_count_deletion_data
+12 -8
View File
@@ -3,6 +3,7 @@
# Table name: reporting_events
#
# id :bigint not null, primary key
# actor_type :string
# event_end_time :datetime
# event_start_time :datetime
# name :string
@@ -11,20 +12,22 @@
# created_at :datetime not null
# updated_at :datetime not null
# account_id :integer
# actor_id :bigint
# conversation_id :integer
# inbox_id :integer
# user_id :integer
#
# Indexes
#
# index_reporting_events_for_response_distribution (account_id,name,inbox_id,created_at)
# index_reporting_events_on_account_id (account_id)
# index_reporting_events_on_conversation_id (conversation_id)
# index_reporting_events_on_created_at (created_at)
# index_reporting_events_on_inbox_id (inbox_id)
# index_reporting_events_on_name (name)
# index_reporting_events_on_user_id (user_id)
# reporting_events__account_id__name__created_at (account_id,name,created_at)
# idx_reporting_events_on_account_actor_name_created (account_id,actor_type,actor_id,name,created_at)
# index_reporting_events_for_response_distribution (account_id,name,inbox_id,created_at)
# index_reporting_events_on_account_id (account_id)
# index_reporting_events_on_conversation_id (conversation_id)
# index_reporting_events_on_created_at (created_at)
# index_reporting_events_on_inbox_id (inbox_id)
# index_reporting_events_on_name (name)
# index_reporting_events_on_user_id (user_id)
# reporting_events__account_id__name__created_at (account_id,name,created_at)
#
class ReportingEvent < ApplicationRecord
@@ -36,6 +39,7 @@ class ReportingEvent < ApplicationRecord
belongs_to :user, optional: true
belongs_to :inbox, optional: true
belongs_to :conversation, optional: true
belongs_to :actor, polymorphic: true, optional: true
# Scopes for filtering
scope :filter_by_date_range, lambda { |range|
@@ -0,0 +1,13 @@
class AddActorToReportingEvents < ActiveRecord::Migration[7.1]
disable_ddl_transaction!
def change
add_column :reporting_events, :actor_type, :string
add_column :reporting_events, :actor_id, :bigint
add_index :reporting_events,
[:account_id, :actor_type, :actor_id, :name, :created_at],
name: 'idx_reporting_events_on_account_actor_name_created',
algorithm: :concurrently
end
end
+4 -1
View File
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2026_06_30_000000) do
ActiveRecord::Schema[7.1].define(version: 2026_07_01_000000) do
# These extensions should be enabled to support this database
enable_extension "pg_stat_statements"
enable_extension "pg_trgm"
@@ -1167,6 +1167,9 @@ ActiveRecord::Schema[7.1].define(version: 2026_06_30_000000) do
t.float "value_in_business_hours"
t.datetime "event_start_time", precision: nil
t.datetime "event_end_time", precision: nil
t.string "actor_type"
t.bigint "actor_id"
t.index ["account_id", "actor_type", "actor_id", "name", "created_at"], name: "idx_reporting_events_on_account_actor_name_created"
t.index ["account_id", "name", "created_at"], name: "reporting_events__account_id__name__created_at"
t.index ["account_id", "name", "inbox_id", "created_at"], name: "index_reporting_events_for_response_distribution"
t.index ["account_id"], name: "index_reporting_events_on_account_id"
@@ -7,6 +7,8 @@ json.event_end_time reporting_event.event_end_time
json.account_id reporting_event.account_id
json.inbox_id reporting_event.inbox_id
json.user_id reporting_event.user_id
json.actor_type reporting_event.actor_type
json.actor_id reporting_event.actor_id
json.conversation_id reporting_event.conversation_id
json.created_at reporting_event.created_at
json.updated_at reporting_event.updated_at
@@ -40,6 +40,16 @@ properties:
- number
- 'null'
description: ID of the user/agent
actor_type:
type:
- string
- 'null'
description: Type of the actor that caused the reporting event
actor_id:
type:
- number
- 'null'
description: ID of the actor that caused the reporting event
created_at:
type: string
format: date-time