FEATURE: add a Top Categories section to the user summary page, showing the categories in which a user has the most activity

This commit is contained in:
Neil Lalonde 2018-07-18 16:37:50 -04:00
parent 2dc3a50dac
commit afc94ac9e4
8 changed files with 130 additions and 3 deletions

View File

@ -115,6 +115,35 @@
{{/user-summary-section}}
</div>
{{#if model.top_categories.length}}
<div class='top-section top-categories-section'>
{{#user-summary-section title="top_categories" class="summary-category-list pull-left"}}
<table>
<thead>
<th class="category-link"></th>
<th class='topic-count'>{{i18n "user.summary.topics"}}</th>
<th class='reply-count'>{{i18n "user.summary.replies"}}</th>
</thead>
<tbody>
{{#each model.top_categories as |category|}}
<tr>
<td class="category-link">
{{category-link category allowUncategorized="true" hideParent=false}}
</td>
<td class='topic-count'>
{{category.topic_count}}
</td>
<td class='reply-count'>
{{category.post_count}}
</td>
</tr>
{{/each}}
</tbody>
</table>
{{/user-summary-section}}
</div>
{{/if}}
{{#if siteSettings.enable_badges}}
<div class='top-section badges-section'>
<h3 class='stats-title'>{{i18n "user.summary.top_badges"}}</h3>

View File

@ -502,6 +502,23 @@
}
}
.top-categories-section {
table {
max-width: 95%;
tr {
border: none;
}
td,
th {
padding: 0.5em;
&.topic-count,
&.reply-count {
text-align: center;
}
}
}
}
.summary-user-list {
li {
height: 40px;

View File

@ -155,6 +155,56 @@ class UserSummary
@user.recent_time_read
end
class CategoryWithCounts < OpenStruct
include ActiveModel::SerializerSupport
KEYS = [:id, :name, :color, :text_color, :slug, :read_restricted, :parent_category_id]
end
def top_categories
post_count_query = Post
.joins(:topic)
.includes(:topic)
.secured(@guardian)
.merge(Topic.listable_topics.visible.secured(@guardian))
.where(user: @user)
.group('topics.category_id')
.order('COUNT(*) DESC')
top_categories = {}
Category.where(id: post_count_query.limit(MAX_SUMMARY_RESULTS).pluck('category_id'))
.pluck(:id, :name, :color, :text_color, :slug, :read_restricted, :parent_category_id)
.each do |c|
top_categories[c[0].to_i] = CategoryWithCounts.new(
Hash[CategoryWithCounts::KEYS.zip(c)].merge(
topic_count: 0,
post_count: 0
)
)
end
post_count_query.where('post_number > 1')
.where('topics.category_id in (?)', top_categories.keys)
.pluck('category_id, COUNT(*)')
.each do |r|
top_categories[r[0].to_i].post_count = r[1]
end
Topic.listable_topics.visible.secured(@guardian)
.where('topics.category_id in (?)', top_categories.keys)
.where(user: @user)
.group('topics.category_id')
.order('COUNT(*) DESC')
.pluck('category_id, COUNT(*)')
.each do |r|
top_categories[r[0].to_i].topic_count = r[1]
end
top_categories.values.sort_by do |r|
-(r[:post_count] + r[:topic_count])
end
end
delegate :likes_given,
:likes_received,
:days_visited,

View File

@ -22,6 +22,12 @@ class UserSummarySerializer < ApplicationSerializer
attributes :count, :name
end
class CategoryWithCountsSerializer < ApplicationSerializer
attributes :topic_count, :post_count,
:id, :name, :color, :text_color, :slug,
:read_restricted, :parent_category_id
end
has_many :topics, serializer: TopicSerializer
has_many :replies, serializer: ReplySerializer, embed: :object
has_many :links, serializer: LinkSerializer, embed: :object
@ -29,6 +35,7 @@ class UserSummarySerializer < ApplicationSerializer
has_many :most_liked_users, serializer: UserWithCountSerializer, embed: :object
has_many :most_replied_to_users, serializer: UserWithCountSerializer, embed: :object
has_many :badges, serializer: UserBadgeSerializer, embed: :object
has_many :top_categories, serializer: CategoryWithCountsSerializer, embed: :object
attributes :likes_given,
:likes_received,

View File

@ -1004,7 +1004,9 @@ en:
most_liked_users: "Most Liked"
most_replied_to_users: "Most Replied To"
no_likes: "No likes yet."
top_categories: "Top Categories"
topics: "Topics"
replies: "Replies"
associated_accounts: "Logins"
ip_address:

View File

@ -11,16 +11,21 @@ describe UserSummary do
expect(summary.topics.length).to eq(1)
expect(summary.replies.length).to eq(1)
expect(summary.top_categories.length).to eq(1)
expect(summary.top_categories.first[:topic_count]).to eq(1)
expect(summary.top_categories.first[:post_count]).to eq(1)
topic.update_columns(deleted_at: Time.now)
expect(summary.topics.length).to eq(0)
expect(summary.replies.length).to eq(0)
expect(summary.top_categories.length).to eq(0)
topic.update_columns(deleted_at: nil, visible: false)
expect(summary.topics.length).to eq(0)
expect(summary.replies.length).to eq(0)
expect(summary.top_categories.length).to eq(0)
category = Fabricate(:category)
topic.update_columns(category_id: category.id, deleted_at: nil, visible: true)
@ -30,7 +35,7 @@ describe UserSummary do
expect(summary.topics.length).to eq(0)
expect(summary.replies.length).to eq(0)
expect(summary.top_categories.length).to eq(0)
end
end

View File

@ -46,5 +46,9 @@ QUnit.test("Viewing Summary", assert => {
assert.ok(exists(".liked-by-section .user-info"), "liked by");
assert.ok(exists(".liked-section .user-info"), "liked");
assert.ok(exists(".badges-section .badge-card"), "badges");
assert.ok(
exists(".top-categories-section .category-link"),
"top categories"
);
});
});

View File

@ -88,7 +88,20 @@ export default function() {
most_replied_to_users: [{ id: 333 }],
most_liked_by_users: [{ id: 333 }],
most_liked_users: [{ id: 333 }],
badges: [{ badge_id: 444 }]
badges: [{ badge_id: 444 }],
top_categories: [
{
id: 1,
name: "bug",
color: "e9dd00",
text_color: "000000",
slug: "bug",
read_restricted: false,
parent_category_id: null,
topic_count: 1,
post_count: 1
}
]
},
badges: [{ id: 444, count: 1 }],
topics: [{ id: 1234, title: "cool title", url: "/t/1234/cool-title" }]