Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
- Add Logistics component with progress tracking - Add OrderDetail component for order information - Support data-driven steps and actions - Add blue color scale to widget SCSS - Fix node overflow and progress bar rendering issues - Add English translations for dashboard components Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
113 lines
3.7 KiB
Ruby
113 lines
3.7 KiB
Ruby
class V2::Reports::LabelSummaryBuilder < V2::Reports::BaseSummaryBuilder
|
|
attr_reader :account, :params
|
|
|
|
# rubocop:disable Lint/MissingSuper
|
|
# the parent class has no initialize
|
|
def initialize(account:, params:)
|
|
@account = account
|
|
@params = params
|
|
|
|
timezone_offset = (params[:timezone_offset] || 0).to_f
|
|
@timezone = ActiveSupport::TimeZone[timezone_offset]&.name
|
|
end
|
|
# rubocop:enable Lint/MissingSuper
|
|
|
|
def build
|
|
labels = account.labels.to_a
|
|
return [] if labels.empty?
|
|
|
|
report_data = collect_report_data
|
|
labels.map { |label| build_label_report(label, report_data) }
|
|
end
|
|
|
|
private
|
|
|
|
def collect_report_data
|
|
conversation_filter = build_conversation_filter
|
|
use_business_hours = use_business_hours?
|
|
|
|
{
|
|
conversation_counts: fetch_conversation_counts(conversation_filter),
|
|
resolved_counts: fetch_resolved_counts,
|
|
resolution_metrics: fetch_metrics(conversation_filter, 'conversation_resolved', use_business_hours),
|
|
first_response_metrics: fetch_metrics(conversation_filter, 'first_response', use_business_hours),
|
|
reply_metrics: fetch_metrics(conversation_filter, 'reply_time', use_business_hours)
|
|
}
|
|
end
|
|
|
|
def build_label_report(label, report_data)
|
|
{
|
|
id: label.id,
|
|
name: label.title,
|
|
conversations_count: report_data[:conversation_counts][label.title] || 0,
|
|
avg_resolution_time: report_data[:resolution_metrics][label.title] || 0,
|
|
avg_first_response_time: report_data[:first_response_metrics][label.title] || 0,
|
|
avg_reply_time: report_data[:reply_metrics][label.title] || 0,
|
|
resolved_conversations_count: report_data[:resolved_counts][label.title] || 0
|
|
}
|
|
end
|
|
|
|
def use_business_hours?
|
|
ActiveModel::Type::Boolean.new.cast(params[:business_hours])
|
|
end
|
|
|
|
def build_conversation_filter
|
|
conversation_filter = { account_id: account.id }
|
|
conversation_filter[:created_at] = range if range.present?
|
|
|
|
conversation_filter
|
|
end
|
|
|
|
def fetch_conversation_counts(conversation_filter)
|
|
fetch_counts(conversation_filter)
|
|
end
|
|
|
|
def fetch_resolved_counts
|
|
# Count resolution events, not conversations currently in resolved status
|
|
# Filter by reporting_event.created_at, not conversation.created_at
|
|
reporting_event_filter = { name: 'conversation_resolved', account_id: account.id }
|
|
reporting_event_filter[:created_at] = range if range.present?
|
|
|
|
ReportingEvent
|
|
.joins(conversation: { taggings: :tag })
|
|
.where(
|
|
reporting_event_filter.merge(
|
|
taggings: { taggable_type: 'Conversation', context: 'labels' }
|
|
)
|
|
)
|
|
.group('tags.name')
|
|
.count
|
|
end
|
|
|
|
def fetch_counts(conversation_filter)
|
|
ActsAsTaggableOn::Tagging
|
|
.joins('INNER JOIN conversations ON taggings.taggable_id = conversations.id')
|
|
.joins('INNER JOIN tags ON taggings.tag_id = tags.id')
|
|
.where(
|
|
taggable_type: 'Conversation',
|
|
context: 'labels',
|
|
conversations: conversation_filter
|
|
)
|
|
.select('tags.name, COUNT(taggings.*) AS count')
|
|
.group('tags.name')
|
|
.each_with_object({}) { |record, hash| hash[record.name] = record.count }
|
|
end
|
|
|
|
def fetch_metrics(conversation_filter, event_name, use_business_hours)
|
|
ReportingEvent
|
|
.joins(conversation: { taggings: :tag })
|
|
.where(
|
|
conversations: conversation_filter,
|
|
name: event_name,
|
|
taggings: { taggable_type: 'Conversation', context: 'labels' }
|
|
)
|
|
.group('tags.name')
|
|
.order('tags.name')
|
|
.select(
|
|
'tags.name',
|
|
use_business_hours ? 'AVG(reporting_events.value_in_business_hours) as avg_value' : 'AVG(reporting_events.value) as avg_value'
|
|
)
|
|
.each_with_object({}) { |record, hash| hash[record.name] = record.avg_value.to_f }
|
|
end
|
|
end
|