Skip to content

Commit

Permalink
CV2-4985: add export to articles dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
melsawy committed Jan 24, 2025
1 parent 6cbb317 commit 0f21981
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 56 deletions.
65 changes: 65 additions & 0 deletions app/models/concerns/team_private.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,69 @@ def empty_data_structure
data_structure["Org"] = self.name
[data_structure]
end

def get_dashboard_export_headers(ts, dashboard_type)
# Get dashboard headers for both types (articles_dashboard & tipline_dashboard) in format { key: value }
# key(string): header label
# Value(Hash): { method_name: 'callback that should apply to the method output'}
# In some cases, the value may be empty ({}) as some methods will populate more than one row.

# Common header between articles_dashboard and tipline_dashboard
header = {
'Articles Sent': { number_of_articles_sent: 'to_i' },
'Matched Results (Fact-Checks)': { number_of_matched_results_by_article_type: 'values' },
'Matched Results (Explainers)': {},
}
# Hash to include top items as the header label depend on top_items size
top_items = {}
# tipline_dashboard columns
if dashboard_type == 'tipline_dashboard'
header.merge!({
'Conversations': { number_of_conversations: 'to_i' },
'Messages': { number_of_messages: 'to_i' },
'Conversations (Positive)': { number_of_search_results_by_feedback_type: 'values' },
'Conversations (Negative)': {}, 'Conversations (No Response)': {},
'Avg. Response Time': { average_response_time: 'to_i' },
'Users (Total)': { number_of_total_users: 'to_i' },
'Users (Unique)': { number_of_unique_users: 'to_i' },
'Users (Returning)': { number_of_returning_users: 'to_i' },
'Subscribers': { number_of_subscribers: 'to_i' },
'Subscribers (New)': { number_of_new_subscribers: 'to_i' },
'Newsletters (Sent)': { number_of_newsletters_sent: 'to_i' },
'Newsletters (Delivered)': { number_of_newsletters_delivered: 'to_i' },
'Media Received (Text)': { number_of_media_received_by_media_type: 'values' },
'Media Received (Link)': {}, 'Media Received (Audio)': {}, 'Media Received (Image)': {}, 'Media Received (Video)': {},
})
top_items = { top_media_tags: 'Top tag', top_requested_media_clusters: 'Top Requested' }
else
# article_dashboard columns
header.merge!({
'Published Fact-Checks': { number_of_published_fact_checks: 'to_i' },
'Explainers Created': { number_of_explainers_created: 'to_i' },
'Fact-Checks Created': { number_of_fact_checks_created: 'to_i' },
})
rates = ts.send('number_of_fact_checks_by_rating').keys
# Get the first element to fill the label and callback methods as other element will calling with empty callbacks
f_rate = rates.delete_at(0)
header.merge!({ "Claim & Fact-Checks (#{f_rate})": { number_of_fact_checks_by_rating: 'values' }})
rates.each{ |rate| header.merge!({"Claim & Fact-Checks (#{rate})": {}}) }
top_items = { top_articles_tags: 'Top Article Tags', top_articles_sent: 'Top Fact-Checks Sent' }
end
unless top_items.blank?
top_callback = proc { |output| output.collect{|item| "#{item[:label]} (#{item[:value]})"} }
# Append Top tags/requested header based on result count
top_items.each do |method, prefix|
col_numbers = ts.send(method).size
if col_numbers > 0
# Add a first one with method callback
header.merge!({"#{prefix} (1)": { "#{method}": top_callback } })
(col_numbers - 1).times do |i|
# Append other columns with empty method
header.merge!({"#{prefix} (#{i+2})": { "#{method}": {} } })
end
end
end
end
header
end
end
63 changes: 13 additions & 50 deletions app/models/team.rb
Original file line number Diff line number Diff line change
Expand Up @@ -611,61 +611,24 @@ def get_shorten_outgoing_urls
self.settings.to_h.with_indifferent_access[:shorten_outgoing_urls] || self.tipline_newsletters.where(content_type: 'rss', enabled: true).exists?
end

def get_dashboard_exported_data(filters)
def get_dashboard_exported_data(filters, dashboard_type)
ts = TeamStatistics.new(self, filters[:period], filters[:language], filters[:platform])
header = [
'Conversations',
'Messages',
'Conversations(Positive)', 'Conversations(Negative)', 'Conversations(No Response)',
'Avg. Response Time',
'Users(Total)',
'Users(Unique)',
'Users(Returning)',
'Subscribers',
'Subscribers(New)',
'Newsletters(Sent)',
'Newsletters(Delivered)',
'Media Received(Text)','Media Received(Link)','Media Received(Audio)','Media Received(Image)','Media Received(Video)',
'Articles Sent',
'Matched Results(Fact-Checks)', 'Matched Results(Explainers)',
]
# Append Top tags/requested header based on result count
{ top_media_tags: 'Top tag', top_requested_media_clusters: 'Top Requested' }.each do |method, prefix|
column_headers = []
ts.send(method).each_with_index do |_v, i|
column_headers << "#{prefix}(#{i+1})"
end
header << column_headers
end
headers = get_dashboard_export_headers(ts, dashboard_type)
data = []
data << header.flatten
header_methods = {
number_of_conversations: 'to_i',
number_of_messages: 'to_i',
number_of_search_results_by_feedback_type: 'values',
average_response_time: 'to_i',
number_of_total_users: 'to_i',
number_of_unique_users: 'to_i',
number_of_returning_users: 'to_i',
number_of_subscribers: 'to_i',
number_of_new_subscribers: 'to_i',
number_of_newsletters_sent: 'to_i',
number_of_newsletters_delivered: 'to_i',
number_of_media_received_by_media_type: 'values',
number_of_articles_sent: 'to_i',
number_of_matched_results_by_article_type: 'values',
top_media_tags: 'collect',
top_requested_media_clusters: 'collect'
}
# Add header labels
data << headers.keys
header_methods = headers.values.delete_if{|v| v.blank?}
raw = []
header_methods.each do |method, type|
output = ts.send(method) if ts.respond_to?(method)
if type == 'collect'
output = output.collect{|item| "#{item[:label]}(#{item[:value]})"}
else
output = output.send(type)
unless type.blank?
output = ts.send(method) if ts.respond_to?(method)
if type.is_a?(Proc)
output = type.call(output)
else
output = output.send(type)
end
raw << output
end
raw << output
end
data << raw.flatten
data
Expand Down
10 changes: 5 additions & 5 deletions lib/list_export.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class ListExport
TYPES = [:media, :feed, :fact_check, :explainer, :dashboard]
TYPES = [:media, :feed, :fact_check, :explainer, :articles_dashboard, :tipline_dashboard ]

def initialize(type, query, team_id)
@type = type
Expand All @@ -21,8 +21,8 @@ def number_of_rows
@team.filtered_fact_checks(@parsed_query).count
when :explainer
@team.filtered_explainers(@parsed_query).count
when :dashboard
1
when :articles_dashboard, :tipline_dashboard
1 # Always maintain one row for dashboard data, but use different columns for export.
end
end

Expand Down Expand Up @@ -64,8 +64,8 @@ def export_data
FactCheck.get_exported_data(@parsed_query, @team)
when :explainer
Explainer.get_exported_data(@parsed_query, @team)
when :dashboard
@team.get_dashboard_exported_data(@parsed_query)
when :articles_dashboard, :tipline_dashboard
@team.get_dashboard_exported_data(@parsed_query, @type)
end
end
end
11 changes: 10 additions & 1 deletion test/lib/list_export_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,16 @@ def teardown

test "should export dashboard CSV" do
t = create_team
export = ListExport.new(:dashboard, { period: "past_week", platform: "whatsapp", language: "en" }.to_json, t.id)
# tipline_dashboard
export = ListExport.new(:tipline_dashboard, { period: "past_week", platform: "whatsapp", language: "en" }.to_json, t.id)
csv_url = export.generate_csv_and_send_email(create_user)
response = Net::HTTP.get_response(URI(csv_url))
assert_equal 200, response.code.to_i
csv_content = CSV.parse(response.body, headers: true)
assert_equal 1, csv_content.size
assert_equal 1, export.number_of_rows
# articles_dashboard
export = ListExport.new(:articles_dashboard, { period: "past_week", platform: "whatsapp", language: "en" }.to_json, t.id)
csv_url = export.generate_csv_and_send_email(create_user)
response = Net::HTTP.get_response(URI(csv_url))
assert_equal 200, response.code.to_i
Expand Down

0 comments on commit 0f21981

Please sign in to comment.