Skip to content

Commit

Permalink
Merge pull request #54 from BiteSpeed-Dev/analytics-exports-fixes
Browse files Browse the repository at this point in the history
feat: conversation exports job
  • Loading branch information
amanmishra123 authored Jun 11, 2024
2 parents 00d968a + 9fb9bdc commit 95981f7
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 9 deletions.
1 change: 1 addition & 0 deletions app/helpers/api/v2/accounts/reports_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def generate_conversation_report(account_id, range)
conversations.id AS conversation_id,
conversations.display_id AS conversation_display_id,
conversations.created_at AS created_at,
contacts.created_at AS customer_created_at,
inboxes.name AS inbox_name,
contacts.name AS customer_name,
REPLACE(contacts.phone_number, '+', '') AS customer_phone_number,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<template>
<div class="flex-1 overflow-auto p-4">
<woot-button
<!-- <woot-button
color-scheme="success"
class-names="button--fixed-top"
icon="arrow-download"
@click="downloadConversationReports"
>
{{ 'Download conversation reports' }}
</woot-button>
</woot-button> -->
<report-filter-selector
:show-agents-filter="false"
:show-group-by-filter="true"
Expand Down
145 changes: 145 additions & 0 deletions app/jobs/daily_conversation_report_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
require 'json'
require 'csv'

class DailyConversationReportJob < ApplicationJob
queue_as :scheduled_jobs

def perform
job_data = [{
account_id: 138,
frequency: 'daily'
}, {
account_id: 138,
frequency: 'weekly' # should trigger only on Mondays
},
{
account_id: 504,
frequency: 'daily'
}]

job_data.each do |job|
current_date = Date.current
current_day = current_date.wday

next if job[:frequency] == 'weekly' && current_day != 0

current_date = Date.current

process_account(job[:account_id], current_date, job[:frequency])
end
end

private

def process_account(account_id, current_date, frequency)
report = generate_report(account_id, frequency)

if report.present?
Rails.logger.info "Data found for account_id: #{account_id}"

csv_content = generate_csv(report)
upload_csv(account_id, current_date, csv_content, frequency)
else
Rails.logger.info "No data found for account_id: #{account_id}"
end
end

# rubocop:disable Metrics/MethodLength
def generate_report(account_id, frequency = 'daily')
range = if frequency == 'weekly'
{ since: 1.week.ago, until: Time.current }
else
{ since: 1.day.ago, until: Time.current }
end

# Using ActiveRecord::Base directly for sanitization
sql = ActiveRecord::Base.send(:sanitize_sql_array, [<<-SQL.squish, { account_id: account_id, since: range[:since], until: range[:until] }])
SELECT
conversations.id AS conversation_id,
conversations.display_id AS conversation_display_id,
conversations.created_at AS conversation_created_at,
contacts.created_at AS customer_created_at,
inboxes.name AS inbox_name,
REPLACE(contacts.phone_number, '+', '') AS customer_phone_number,
contacts.name AS customer_name,
users.name AS agent_name,
CASE
WHEN conversations.status = 0 THEN 'open'
WHEN conversations.status = 1 THEN 'resolved'
WHEN conversations.status = 2 THEN 'pending'
WHEN conversations.status = 3 THEN 'snoozed'
END AS conversation_status,
reporting_events_first_response.value / 60.0 AS first_response_time_minutes,
latest_conversation_resolved.value / 60.0 AS resolution_time_minutes,
conversations.cached_label_list AS labels
FROM
conversations
JOIN inboxes ON conversations.inbox_id = inboxes.id
JOIN contacts ON conversations.contact_id = contacts.id
JOIN account_users ON conversations.assignee_id = account_users.user_id
JOIN users ON account_users.user_id = users.id
LEFT JOIN reporting_events AS reporting_events_first_response
ON conversations.id = reporting_events_first_response.conversation_id
AND reporting_events_first_response.name = 'first_response'
LEFT JOIN LATERAL (
SELECT value
FROM reporting_events AS re
WHERE re.conversation_id = conversations.id
AND re.name = 'conversation_resolved'
ORDER BY re.created_at DESC
LIMIT 1
) AS latest_conversation_resolved ON true
WHERE
conversations.account_id = :account_id
AND conversations.updated_at BETWEEN :since AND :until
SQL

ActiveRecord::Base.connection.exec_query(sql).to_a
end
# rubocop:enable Metrics/MethodLength

def generate_csv(results)
CSV.generate(headers: true) do |csv|
csv << [
'Conversation ID', 'Conversation Created At', 'Contact Created At', 'Inbox Name',
'Customer Phone Number', 'Customer Name', 'Agent Name', 'Conversation Status',
'First Response Time (minutes)', 'Resolution Time (minutes)', 'Labels'
]
results.each do |row|
csv << [
row['conversation_display_id'], row['conversation_created_at'], row['customer_created_at'], row['inbox_name'],
row['customer_phone_number'], row['customer_name'], row['agent_name'], row['conversation_status'],
row['first_response_time_minutes'], row['resolution_time_minutes'], row['labels']
]
end
end
end

def upload_csv(account_id, current_date, csv_content, frequency)
# Determine the file name based on the frequency
file_name = "#{frequency}_conversation_report_#{account_id}_#{current_date}.csv"

# For testing locally, uncomment below
# puts csv_content
# csv_url = file_name
# File.write(csv_url, csv_content)

# Upload csv_content via ActiveStorage and print the URL
blob = ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(csv_content),
filename: file_name,
content_type: 'text/csv'
)

csv_url = Rails.application.routes.url_helpers.url_for(blob)

# Send email with the CSV URL
mailer = AdministratorNotifications::ChannelNotificationsMailer.with(account: Account.find(account_id))

if frequency == 'weekly'
mailer.weekly_conversation_report(csv_url, current_date - 7.days, current_date).deliver_now
else
mailer.daily_conversation_report(csv_url, current_date).deliver_now
end
end
end
13 changes: 7 additions & 6 deletions app/jobs/zeroharm_daily_conversation_report_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def generate_report(account_id)
SELECT
conversations.id AS conversation_id,
conversations.display_id AS conversation_display_id,
conversations.created_at AS created_at,
conversations.created_at AS conversation_created_at,
contacts.created_at AS customer_created_at,
inboxes.name AS inbox_name,
REPLACE(contacts.phone_number, '+', '') AS customer_phone_number,
contacts.name AS customer_name,
Expand Down Expand Up @@ -116,13 +117,13 @@ def update_report_with_order_ids(report, order_ids)
def generate_csv(results)
CSV.generate(headers: true) do |csv|
csv << [
'Conversation ID', 'Inbox Name',
'Conversation ID', 'Conversation Created At', 'Contact Created At', 'Inbox Name',
'Customer Phone Number', 'Customer Name', 'Agent Name', 'Conversation Status',
'First Response Time (minutes)', 'Resolution Time (minutes)', 'Labels', 'Order ID'
]
results.each do |row|
csv << [
row['conversation_display_id'], row['inbox_name'],
row['conversation_display_id'], row['conversation_created_at'], row['customer_created_at'], row['inbox_name'],
row['customer_phone_number'], row['customer_name'], row['agent_name'], row['conversation_status'],
row['first_response_time_minutes'], row['resolution_time_minutes'], row['labels'], row['order_id']
]
Expand All @@ -133,8 +134,8 @@ def generate_csv(results)
def upload_csv(account_id, current_date, csv_content)
# # for testing locally uncomment below
# puts csv_content
# local_file_path = "daily_conversation_report_#{account_id}_#{current_date}.csv"
# File.write(local_file_path, csv_content)
# csv_url = "daily_conversation_report_#{account_id}_#{current_date}.csv"
# File.write(csv_url, csv_content)

# Upload csv_content via ActiveStorage and print the URL
blob = ActiveStorage::Blob.create_and_upload!(
Expand All @@ -147,6 +148,6 @@ def upload_csv(account_id, current_date, csv_content)

# Send email with the CSV URL
mailer = AdministratorNotifications::ChannelNotificationsMailer.with(account: Account.find(account_id))
mailer.zeroharm_daily_conversation_report(csv_url, current_date).deliver_now
mailer.daily_conversation_report(csv_url, current_date).deliver_now
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ def contact_import_complete(resource)
send_mail_with_liquid(to: admin_emails, subject: subject) and return
end

def zeroharm_daily_conversation_report(csv_url, current_date)
def daily_conversation_report(csv_url, current_date)
return unless smtp_config_set_or_development?

subject = "Daily Conversation Report for #{current_date}"
@action_url = csv_url
send_mail_with_liquid(to: admin_emails + ['jaideep+chatwootreports@bitespeed.co'], subject: subject) and return
end

def weekly_conversation_report(csv_url, since_date, until_date)
return unless smtp_config_set_or_development?

subject = "Weekly Conversation Report from #{since_date} to #{until_date}"
@action_url = csv_url
send_mail_with_liquid(to: admin_emails + ['jaideep+chatwootreports@bitespeed.co'], subject: subject) and return
end

def contact_import_failed
return unless smtp_config_set_or_development?

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<p>Hello,</p>

<p>Here is the weekly conversation report. </p>

<p>
Click <a href="{{action_url}}">here</a> to download.
</p>

<p>Regards,<br>BiteSpeed</p>
5 changes: 5 additions & 0 deletions config/schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,8 @@ zeroharm_daily_conversation_report_job:
cron: '30 3 * * *'
class: 'ZeroharmDailyConversationReportJob'
queue: scheduled_jobs

daily_conversation_report_job:
cron: '30 3 * * *'
class: 'DailyConversationReportJob'
queue: scheduled_jobs

0 comments on commit 95981f7

Please sign in to comment.