-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Classification user groups individual stats (#25)
* individual stats breakdown per group. add session time to user group projects breakdown. * update sort of group member stats breakdown * Update user_group_member_stats_breakdown_serializer.rb * add specs user group classification count controller for individual stats breakdown stats * add specs for group member breakdown query stats * add spec for serializer * update session time to be included with top contributors
- Loading branch information
1 parent
364f28d
commit d0ec241
Showing
10 changed files
with
246 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# frozen_string_literal: true | ||
|
||
class CountGroupMemberBreakdown | ||
include Filterable | ||
attr_reader :counts | ||
|
||
def initialize | ||
@counts = initial_scope(relation) | ||
end | ||
|
||
def call(params={}) | ||
scoped = @counts | ||
scoped = filter_by_user_group_id(scoped, params[:id]) | ||
filter_by_date_range(scoped, params[:start_date], params[:end_date]) | ||
end | ||
|
||
private | ||
|
||
def initial_scope(relation) | ||
relation.select(select_clause).group('user_id, project_id') | ||
end | ||
|
||
def select_clause | ||
'user_id, project_id, SUM(classification_count)::integer AS count, SUM(total_session_time)::float AS session_time' | ||
end | ||
|
||
def relation | ||
UserGroupClassificationCounts::DailyGroupUserProjectClassificationCount | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
app/serializers/user_group_member_stats_breakdown_serializer.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# frozen_string_literal: true | ||
|
||
class UserGroupMemberStatsBreakdownSerializer | ||
attr_reader :group_member_classification_counts | ||
|
||
def initialize(counts_scope) | ||
@group_member_classification_counts = counts_scope | ||
end | ||
|
||
def as_json(_options) | ||
{ | ||
group_member_stats_breakdown: counts_grouped_by_user.sort_by { |member_stat| member_stat[:count] }.reverse | ||
} | ||
end | ||
|
||
private | ||
|
||
def counts_grouped_by_user | ||
counts_grouped_by_member = group_member_classification_counts.group_by { |member_proj_contribution| member_proj_contribution[:user_id] }.transform_values do |member_counts_per_project| | ||
total_per_member = { count: member_counts_per_project.sum(&:count) } | ||
total_per_member[:session_time] = member_counts_per_project.sum(&:session_time) | ||
total_per_member[:project_contributions] = individual_member_project_contributions(member_counts_per_project) | ||
total_per_member | ||
end | ||
counts_grouped_by_member.map { |user_id, totals| { user_id: }.merge(totals) } | ||
end | ||
|
||
def individual_member_project_contributions(member_counts_per_project) | ||
member_counts_per_project.sort_by(&:count).reverse.map { |member_count| member_count.as_json.except('user_id') } | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe CountGroupMemberBreakdown do | ||
let(:params) { {} } | ||
let(:group_member_breakdown_query) { described_class.new } | ||
describe 'relation' do | ||
it 'returns DailyGroupUserProjectClassificationCount' do | ||
expect(group_member_breakdown_query.counts.model).to be UserGroupClassificationCounts::DailyGroupUserProjectClassificationCount | ||
end | ||
end | ||
|
||
describe 'select_clause' do | ||
it 'selects user_id, project_id, sum of counts and time' do | ||
counts = group_member_breakdown_query.call(params) | ||
expected_select_query = 'SELECT user_id, project_id, SUM(classification_count)::integer AS count, SUM(total_session_time)::float AS session_time ' | ||
expected_select_query += 'FROM "daily_group_classification_count_and_time_per_user_per_project" ' | ||
expected_select_query += 'GROUP BY user_id, project_id' | ||
expect(counts.to_sql).to eq(expected_select_query) | ||
end | ||
end | ||
|
||
describe '#call' do | ||
let!(:classification_user_group) { create(:classification_user_group) } | ||
let!(:diff_workflow_event) { create(:cug_with_diff_workflow) } | ||
let!(:diff_project_event) { create(:cug_with_diff_project) } | ||
let!(:diff_time_event) { create(:cug_created_yesterday) } | ||
let!(:diff_user_classification) { create(:cug_with_diff_user) } | ||
let!(:diff_user_group_classification) { create(:cug_with_diff_user_group) } | ||
|
||
before(:each) do | ||
params[:id] = classification_user_group.user_group_id.to_s | ||
end | ||
|
||
it_behaves_like 'is filterable by date range' do | ||
let(:counts_query) { described_class.new } | ||
end | ||
|
||
it 'filters by given user_group_id' do | ||
counts = group_member_breakdown_query.call(params) | ||
expect(counts.to_sql).to include(".\"user_group_id\" = #{classification_user_group.user_id}") | ||
end | ||
|
||
it 'returns classification counts of given user group grouped by user and project' do | ||
counts = group_member_breakdown_query.call(params) | ||
# because default is grouped by project_id and user_id, we expect results to look something like: | ||
# [ | ||
# <UserGroupClassificationCounts::DailyGroupUserProjectClassificationCount user_id: 1, project_id: 1, count: 3, session_time: 10>, | ||
# <UserGroupClassificationCounts::DailyGroupUserProjectClassificationCount user_id: 1, project_id: 2, count: 1, session_time: 10>, | ||
# <UserGroupClassificationCounts::DailyGroupUserProjectClassificationCount user_id: 2, project_id: 1, count: 3, session_time: 10> | ||
# ] | ||
expect(counts.length).to eq(3) | ||
# the 3 for user_id: 1, project_id:1 being | ||
# [classification_user_group, diff_workflow_event, diff_time_event] | ||
expect(counts[0].count).to eq(3) | ||
expect(counts[1].count).to eq(1) | ||
expect(counts[1].count).to eq(1) | ||
end | ||
|
||
it 'returns counts of events within given date range' do | ||
last_week = Date.today - 7 | ||
yesterday = Date.today - 1 | ||
params[:start_date] = last_week.to_s | ||
params[:end_date] = yesterday.to_s | ||
counts = group_member_breakdown_query.call(params) | ||
expect(counts.length).to eq(1) | ||
expect(counts[0].count).to eq(1) | ||
expect(counts[0].project_id).to eq(diff_time_event.project_id) | ||
expect(counts[0].user_id).to eq(diff_time_event.user_id) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
spec/serializers/user_group_member_stats_breakdown_serializer_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe UserGroupMemberStatsBreakdownSerializer do | ||
let(:user_project_classification_count) { build(:daily_user_project_classification_count) } | ||
|
||
let(:count_serializer) { described_class.new([user_project_classification_count]) } | ||
|
||
it 'returns group_member_stats as array' do | ||
serialized = count_serializer.as_json({}) | ||
expect(serialized).to have_key(:group_member_stats_breakdown) | ||
expect(serialized[:group_member_stats_breakdown].length).to eq(1) | ||
expect(serialized[:group_member_stats_breakdown][0]).to have_key(:user_id) | ||
expect(serialized[:group_member_stats_breakdown][0]).to have_key(:count) | ||
expect(serialized[:group_member_stats_breakdown][0]).to have_key(:session_time) | ||
expect(serialized[:group_member_stats_breakdown][0]).to have_key(:project_contributions) | ||
end | ||
|
||
it 'sums up total_count per user correctly' do | ||
count2 = build(:user_diff_proj_classification_count) | ||
classification_counts = [user_project_classification_count, count2] | ||
serializer = described_class.new(classification_counts) | ||
serialized = serializer.as_json({}) | ||
expect(serialized[:group_member_stats_breakdown][0][:count]).to eq(classification_counts.sum(&:count)) | ||
end | ||
|
||
it 'sums up time_spent per user correctly' do | ||
count2 = build(:user_diff_proj_classification_count) | ||
classification_counts = [user_project_classification_count, count2] | ||
serializer = described_class.new(classification_counts) | ||
serialized = serializer.as_json({}) | ||
expect(serialized[:group_member_stats_breakdown][0][:session_time]).to eq(classification_counts.sum(&:session_time)) | ||
end | ||
|
||
it 'shows project contributions per user correctly' do | ||
count2 = build(:user_diff_proj_classification_count) | ||
classification_counts = [user_project_classification_count, count2] | ||
serializer = described_class.new(classification_counts) | ||
serialized = serializer.as_json({}) | ||
member_project_contributions = serialized[:group_member_stats_breakdown][0][:project_contributions] | ||
expect(member_project_contributions.length).to eq(2) | ||
expect(member_project_contributions[0]).not_to have_key('user_id') | ||
expect(member_project_contributions[0]).to have_key('project_id') | ||
expect(member_project_contributions[0]).to have_key('count') | ||
expect(member_project_contributions[0]).to have_key('session_time') | ||
end | ||
|
||
it 'shows user project contributions in order by count desc' do | ||
count2 = build(:user_diff_proj_classification_count) | ||
count2.count = user_project_classification_count.count + 100 | ||
classification_counts = [user_project_classification_count, count2] | ||
serializer = described_class.new(classification_counts) | ||
serialized = serializer.as_json({}) | ||
member_project_contributions = serialized[:group_member_stats_breakdown][0][:project_contributions] | ||
expect(member_project_contributions[0]['project_id']).to eq(count2.project_id) | ||
expect(member_project_contributions[0]['count']).to eq(count2.count) | ||
expect(member_project_contributions[0]['session_time']).to eq(count2.session_time) | ||
expect(member_project_contributions[1]['project_id']).to eq(user_project_classification_count.project_id) | ||
expect(member_project_contributions[1]['count']).to eq(user_project_classification_count.count) | ||
expect(member_project_contributions[1]['session_time']).to eq(user_project_classification_count.session_time) | ||
end | ||
|
||
it 'shows group_memer_stats_breakdown in order by top contributors' do | ||
diff_group_member_stats = build(:daily_user_project_classification_count) | ||
diff_group_member_stats.user_id = 2 | ||
diff_group_member_stats.count = user_project_classification_count.count + 100 | ||
classification_counts = [user_project_classification_count, diff_group_member_stats] | ||
serializer = described_class.new(classification_counts) | ||
serialized = serializer.as_json({}) | ||
expect(serialized[:group_member_stats_breakdown].length).to eq(2) | ||
expect(serialized[:group_member_stats_breakdown][0][:user_id]).to eq(diff_group_member_stats.user_id) | ||
expect(serialized[:group_member_stats_breakdown][1][:user_id]).to eq(user_project_classification_count.user_id) | ||
end | ||
end |