Add reports for IncomingLinks on admin dashboard
This commit is contained in:
parent
5f4dbd6ddc
commit
38ed86d0c5
|
@ -31,6 +31,9 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({
|
|||
c.set('admins', d.admins);
|
||||
c.set('moderators', d.moderators);
|
||||
c.set('problems', d.problems);
|
||||
c.set('top_referrers', d.top_referrers);
|
||||
c.set('top_traffic_sources', d.top_traffic_sources);
|
||||
c.set('top_referred_topics', d.top_referred_topics);
|
||||
c.set('loading', false);
|
||||
});
|
||||
} else if( !c.get('problemsFetchedAt') || Date.create(c.problemsCheckInterval, 'en') > c.get('problemsFetchedAt') ) {
|
||||
|
|
|
@ -167,7 +167,77 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-right">
|
||||
{{ render admin_github_commits githubCommits }}
|
||||
|
||||
<div class="dashboard-stats">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title">{{top_referrers.title}}</th>
|
||||
<th>{{top_referrers.ytitles.num_visits}}</th>
|
||||
<th>{{top_referrers.ytitles.num_topics}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#unless loading}}
|
||||
{{#each top_referrers.data}}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="title">{{#linkTo adminUser username}}{{username}}{{/linkTo}}</td>
|
||||
<td class="value">{{num_visits}}</td>
|
||||
<td class="value">{{num_topics}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{{/each}}
|
||||
{{/unless}}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-stats">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title">{{top_referred_topics.title}}</th>
|
||||
<th>{{top_referred_topics.ytitles.num_visits}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#unless loading}}
|
||||
{{#each data in top_referred_topics.data}}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="title"><a href="/t/{{unbound data.topic_slug}}/{{unbound data.topic_id}}">{{shorten data.topic_title}}</a></td>
|
||||
<td class="value">{{data.num_visits}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{{/each}}
|
||||
{{/unless}}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-stats">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title">{{top_traffic_sources.title}}</th>
|
||||
<th>{{top_traffic_sources.ytitles.num_visits}}</th>
|
||||
<th>{{top_traffic_sources.ytitles.num_topics}}</th>
|
||||
<th>{{top_traffic_sources.ytitles.num_users}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#unless loading}}
|
||||
{{#each top_traffic_sources.data}}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="title">{{domain}}</td>
|
||||
<td class="value">{{num_visits}}</td>
|
||||
<td class="value">{{num_topics}}</td>
|
||||
<td class="value">{{num_users}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{{/each}}
|
||||
{{/unless}}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
|
|
|
@ -289,6 +289,11 @@ table {
|
|||
@include small-width {
|
||||
width: 390px;
|
||||
}
|
||||
|
||||
.dashboard-stats {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.version-check {
|
||||
|
@ -455,7 +460,8 @@ table {
|
|||
.commits-widget {
|
||||
border: solid 1px #ccc;
|
||||
width: 500px;
|
||||
height: 700px;
|
||||
height: 300px;
|
||||
margin-bottom: 36px;
|
||||
|
||||
@include medium-width {
|
||||
width: 430px;
|
||||
|
@ -500,7 +506,7 @@ table {
|
|||
}
|
||||
|
||||
.commits-list {
|
||||
height: 669px;
|
||||
height: 269px;
|
||||
overflow-y:auto;
|
||||
|
||||
li {
|
||||
|
|
|
@ -48,7 +48,10 @@ class AdminDashboardData
|
|||
reports: REPORTS.map { |type| Report.find(type) },
|
||||
problems: problems,
|
||||
admins: User.admins.count,
|
||||
moderators: User.moderators.count
|
||||
moderators: User.moderators.count,
|
||||
top_referrers: IncomingLinksReport.find('top_referrers'),
|
||||
top_traffic_sources: IncomingLinksReport.find('top_traffic_sources'),
|
||||
top_referred_topics: IncomingLinksReport.find('top_referred_topics')
|
||||
}.merge(
|
||||
SiteSetting.version_checks? ? {version_check: DiscourseUpdates.check_version} : {}
|
||||
)
|
||||
|
@ -117,4 +120,5 @@ class AdminDashboardData
|
|||
def title_check
|
||||
I18n.t('dashboard.title_nag') if SiteSetting.title == SiteSetting.defaults[:title]
|
||||
end
|
||||
|
||||
end
|
|
@ -1,5 +1,6 @@
|
|||
class IncomingLink < ActiveRecord::Base
|
||||
belongs_to :topic
|
||||
belongs_to :user
|
||||
|
||||
validates :url, presence: true
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
class IncomingLinksReport
|
||||
|
||||
attr_accessor :type, :data, :y_titles
|
||||
|
||||
def initialize(type)
|
||||
@type = type
|
||||
@y_titles = {}
|
||||
@data = nil
|
||||
end
|
||||
|
||||
def as_json
|
||||
{
|
||||
type: self.type,
|
||||
title: I18n.t("reports.#{self.type}.title"),
|
||||
xaxis: I18n.t("reports.#{self.type}.xaxis"),
|
||||
ytitles: self.y_titles,
|
||||
data: self.data
|
||||
}
|
||||
end
|
||||
|
||||
def self.find(type, opts={})
|
||||
report_method = :"report_#{type}"
|
||||
return nil unless respond_to?(report_method)
|
||||
|
||||
# Load the report
|
||||
report = IncomingLinksReport.new(type)
|
||||
send(report_method, report)
|
||||
report
|
||||
end
|
||||
|
||||
# Return top 10 users who brought traffic to the site within the last 30 days
|
||||
def self.report_top_referrers(report)
|
||||
report.y_titles[:num_visits] = I18n.t("reports.#{report.type}.num_visits")
|
||||
report.y_titles[:num_topics] = I18n.t("reports.#{report.type}.num_topics")
|
||||
|
||||
num_visits = link_count_per_user
|
||||
num_topics = topic_count_per_user
|
||||
report.data = []
|
||||
num_visits.keys.each do |username|
|
||||
report.data << {username: username, num_visits: num_visits[username], num_topics: num_topics[username]}
|
||||
end
|
||||
report.data.sort_by! {|x| x[:num_visits]}.reverse![0,10]
|
||||
end
|
||||
|
||||
def self.per_user
|
||||
@per_user_query ||= IncomingLink.where('incoming_links.created_at > ? AND incoming_links.user_id IS NOT NULL', 30.days.ago).joins(:user).group('users.username')
|
||||
end
|
||||
|
||||
def self.link_count_per_user
|
||||
per_user.count
|
||||
end
|
||||
|
||||
def self.topic_count_per_user
|
||||
per_user.count('incoming_links.topic_id', distinct: true)
|
||||
end
|
||||
|
||||
|
||||
# Return top 10 domains that brought traffic to the site within the last 30 days
|
||||
def self.report_top_traffic_sources(report)
|
||||
report.y_titles[:num_visits] = I18n.t("reports.#{report.type}.num_visits")
|
||||
report.y_titles[:num_topics] = I18n.t("reports.#{report.type}.num_topics")
|
||||
report.y_titles[:num_users] = I18n.t("reports.#{report.type}.num_users")
|
||||
|
||||
num_visits = link_count_per_domain
|
||||
num_topics = topic_count_per_domain
|
||||
num_users = user_count_per_domain
|
||||
report.data = []
|
||||
num_visits.keys.each do |domain|
|
||||
report.data << {domain: domain, num_visits: num_visits[domain], num_topics: num_topics[domain], num_users: num_users[domain]}
|
||||
end
|
||||
report.data.sort_by! {|x| x[:num_visits]}.reverse![0,10]
|
||||
end
|
||||
|
||||
def self.per_domain
|
||||
@per_domain_query ||= IncomingLink.where('created_at > ? AND domain IS NOT NULL', 30.days.ago).group('domain')
|
||||
end
|
||||
|
||||
def self.link_count_per_domain
|
||||
per_domain.count
|
||||
end
|
||||
|
||||
def self.topic_count_per_domain
|
||||
per_domain.count('topic_id', distinct: true)
|
||||
end
|
||||
|
||||
def self.user_count_per_domain
|
||||
per_domain.count('user_id', distinct: true)
|
||||
end
|
||||
|
||||
|
||||
def self.report_top_referred_topics(report)
|
||||
report.y_titles[:num_visits] = I18n.t("reports.#{report.type}.num_visits")
|
||||
num_visits = link_count_per_topic
|
||||
num_visits = num_visits.to_a.sort_by {|x| x[1]}.last(10).reverse # take the top 10
|
||||
report.data = []
|
||||
topics = Topic.select('id, slug, title').find(num_visits.map {|z| z[0]})
|
||||
num_visits.each do |topic_id, num_visits|
|
||||
topic = topics.find {|t| t.id == topic_id}
|
||||
report.data << {topic_id: topic_id, topic_title: topic.title, topic_slug: topic.slug, num_visits: num_visits}
|
||||
end
|
||||
report.data.sort_by! {|x| x[:num_visits]}.reverse![0,10]
|
||||
end
|
||||
|
||||
def self.link_count_per_topic
|
||||
IncomingLink.where('created_at > ? AND topic_id IS NOT NULL', 30.days.ago).group('topic_id').count
|
||||
end
|
||||
end
|
|
@ -334,6 +334,21 @@ en:
|
|||
title: "Nofity User"
|
||||
xaxis: "Day"
|
||||
yaxis: "Number of private messages"
|
||||
top_referrers:
|
||||
title: "Top Referrers"
|
||||
xaxis: "User"
|
||||
num_visits: "Visits"
|
||||
num_topics: "Topics"
|
||||
top_traffic_sources:
|
||||
title: "Top Traffic Sources"
|
||||
xaxis: "Domain"
|
||||
num_visits: "Visits"
|
||||
num_topics: "Topics"
|
||||
num_users: "Users"
|
||||
top_referred_topics:
|
||||
title: "Top Referred Topics"
|
||||
xaxis: "Topic"
|
||||
num_visits: "Visits"
|
||||
|
||||
dashboard:
|
||||
rails_env_warning: "Your server is running in %{env} mode."
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe IncomingLinksReport do
|
||||
|
||||
describe 'top_referrers' do
|
||||
subject(:top_referrers) { IncomingLinksReport.find('top_referrers').as_json }
|
||||
|
||||
def stub_empty_referrers_data
|
||||
IncomingLinksReport.stubs(:link_count_per_user).returns({})
|
||||
IncomingLinksReport.stubs(:topic_count_per_user).returns({})
|
||||
end
|
||||
|
||||
it 'returns localized titles' do
|
||||
stub_empty_referrers_data
|
||||
top_referrers[:title].should be_present
|
||||
top_referrers[:xaxis].should be_present
|
||||
top_referrers[:ytitles].should be_present
|
||||
top_referrers[:ytitles][:num_visits].should be_present
|
||||
top_referrers[:ytitles][:num_topics].should be_present
|
||||
end
|
||||
|
||||
it 'with no IncomingLink records, it returns correct data' do
|
||||
stub_empty_referrers_data
|
||||
top_referrers[:data].should have(0).records
|
||||
end
|
||||
|
||||
it 'with some IncomingLink records, it returns correct data' do
|
||||
IncomingLinksReport.stubs(:link_count_per_user).returns({'luke' => 4, 'chewie' => 2})
|
||||
IncomingLinksReport.stubs(:topic_count_per_user).returns({'luke' => 2, 'chewie' => 1})
|
||||
top_referrers[:data][0].should == {username: 'luke', num_visits: 4, num_topics: 2}
|
||||
top_referrers[:data][1].should == {username: 'chewie', num_visits: 2, num_topics: 1}
|
||||
end
|
||||
end
|
||||
|
||||
describe 'top_traffic_sources' do
|
||||
subject(:top_traffic_sources) { IncomingLinksReport.find('top_traffic_sources').as_json }
|
||||
|
||||
def stub_empty_traffic_source_data
|
||||
IncomingLinksReport.stubs(:link_count_per_domain).returns({})
|
||||
IncomingLinksReport.stubs(:topic_count_per_domain).returns({})
|
||||
IncomingLinksReport.stubs(:user_count_per_domain).returns({})
|
||||
end
|
||||
|
||||
it 'returns localized titles' do
|
||||
stub_empty_traffic_source_data
|
||||
top_traffic_sources[:title].should be_present
|
||||
top_traffic_sources[:xaxis].should be_present
|
||||
top_traffic_sources[:ytitles].should be_present
|
||||
top_traffic_sources[:ytitles][:num_visits].should be_present
|
||||
top_traffic_sources[:ytitles][:num_topics].should be_present
|
||||
top_traffic_sources[:ytitles][:num_users].should be_present
|
||||
end
|
||||
|
||||
it 'with no IncomingLink records, it returns correct data' do
|
||||
stub_empty_traffic_source_data
|
||||
top_traffic_sources[:data].should have(0).records
|
||||
end
|
||||
|
||||
it 'with some IncomingLink records, it returns correct data' do
|
||||
IncomingLinksReport.stubs(:link_count_per_domain).returns({'twitter.com' => 8, 'facebook.com' => 3})
|
||||
IncomingLinksReport.stubs(:topic_count_per_domain).returns({'twitter.com' => 2, 'facebook.com' => 3})
|
||||
IncomingLinksReport.stubs(:user_count_per_domain).returns({'twitter.com' => 4, 'facebook.com' => 1})
|
||||
top_traffic_sources[:data][0].should == {domain: 'twitter.com', num_visits: 8, num_topics: 2, num_users: 4}
|
||||
top_traffic_sources[:data][1].should == {domain: 'facebook.com', num_visits: 3, num_topics: 3, num_users: 1}
|
||||
end
|
||||
end
|
||||
|
||||
describe 'top_referred_topics' do
|
||||
subject(:top_referred_topics) { IncomingLinksReport.find('top_referred_topics').as_json }
|
||||
|
||||
def stub_empty_referred_topics_data
|
||||
IncomingLinksReport.stubs(:link_count_per_topic).returns({})
|
||||
end
|
||||
|
||||
it 'returns localized titles' do
|
||||
stub_empty_referred_topics_data
|
||||
top_referred_topics[:title].should be_present
|
||||
top_referred_topics[:xaxis].should be_present
|
||||
top_referred_topics[:ytitles].should be_present
|
||||
top_referred_topics[:ytitles][:num_visits].should be_present
|
||||
end
|
||||
|
||||
it 'with no IncomingLink records, it returns correct data' do
|
||||
stub_empty_referred_topics_data
|
||||
top_referred_topics[:data].should have(0).records
|
||||
end
|
||||
|
||||
it 'with some IncomingLink records, it returns correct data' do
|
||||
topic1 = Fabricate.build(:topic, id: 123); topic2 = Fabricate.build(:topic, id: 234)
|
||||
IncomingLinksReport.stubs(:link_count_per_topic).returns({topic1.id => 8, topic2.id => 3})
|
||||
Topic.stubs(:select).returns(Topic) # bypass the select method
|
||||
Topic.stubs(:find).returns([topic1, topic2])
|
||||
top_referred_topics[:data][0].should == {topic_id: topic1.id, topic_title: topic1.title, topic_slug: topic1.slug, num_visits: 8 }
|
||||
top_referred_topics[:data][1].should == {topic_id: topic2.id, topic_title: topic2.title, topic_slug: topic2.slug, num_visits: 3 }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue