Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update top_project_contributions to project_contributions params for queries #19

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/controllers/user_classification_count_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ def query

def validate_params
super
raise ValidationError, 'Cannot query top projects and query by project/workflow' if params[:top_project_contributions] && (params[:workflow_id] || params[:project_id])
raise ValidationError, 'Cannot query top projects and query by project/workflow' if params[:project_contributions] && (params[:workflow_id] || params[:project_id])
end

def sanitize_params
params[:top_project_contributions] = params[:top_project_contributions].to_i if params[:top_project_contributions]
params[:project_contributions] = params[:project_contributions].casecmp?('true') if params[:project_contributions]
params[:time_spent] = params[:time_spent].casecmp?('true') if params[:time_spent]
end

def serializer_opts_from_params
{ period: params[:period],
time_spent: params[:time_spent],
top_project_contributions: params[:top_project_contributions] }
project_contributions: params[:project_contributions] }
end

def user_classification_count_params
params.permit(:id, :start_date, :end_date, :period, :workflow_id, :project_id, :top_project_contributions, :time_spent)
params.permit(:id, :start_date, :end_date, :period, :workflow_id, :project_id, :project_contributions, :time_spent)
end
end
6 changes: 3 additions & 3 deletions app/queries/count_user_classifications.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ def initial_scope(relation, params)
end

def group_by_clause(params)
params[:top_project_contributions] ? 'period, project_id' : 'period'
params[:project_contributions] ? 'period, project_id' : 'period'
end

def select_clause(params)
period = params[:period]
clause = select_by(period, 'classification')
clause += ', SUM(total_session_time)::float AS session_time' if params[:time_spent]
clause += ', project_id' if params[:top_project_contributions]
clause += ', project_id' if params[:project_contributions]
clause
end

def relation(params)
if params[:project_id] || params[:top_project_contributions]
if params[:project_id] || params[:project_contributions]
UserClassificationCounts::DailyUserProjectClassificationCount
elsif params[:workflow_id]
UserClassificationCounts::DailyUserWorkflowClassificationCount
Expand Down
17 changes: 9 additions & 8 deletions app/serializers/user_classification_counts_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ def initialize(counts_scope)
def as_json(options)
serializer_options = options[:serializer_options]
show_time_spent = serializer_options[:time_spent]
show_project_contributions = serializer_options[:project_contributions]
total_count = user_classification_counts.sum(&:count).to_i
response = { total_count: }
calculate_time_spent(user_classification_counts, response) if show_time_spent
show_proj_contributions(response, serializer_options[:top_project_contributions]) if serializer_options[:top_project_contributions]
response[:data] = response_data(user_classification_counts, serializer_options[:top_project_contributions], show_time_spent:) if serializer_options[:period]
display_project_contributions(response) if show_project_contributions
response[:data] = response_data(user_classification_counts, show_project_contributions:, show_time_spent:) if serializer_options[:period]
yuenmichelle1 marked this conversation as resolved.
Show resolved Hide resolved
response
end

Expand All @@ -25,11 +26,11 @@ def calculate_time_spent(counts, response)
response[:time_spent] = total_time_spent
end

def response_data(user_counts, num_top_projects, show_time_spent:)
def response_data(user_counts, show_project_contributions:, show_time_spent:)
# when calculating top projects, our records returned from query will be counts (and session time) per user per project bucketed by time
# eg. { period: '01-01-2020', count: 38, project_id: 1 }, { period: '01-01-2020', count: 40, project_id: 2}
# vs. Our desired response format which is counts (and session time) grouped by bucketed time. { period: '01-02-2020', count: 78 }
if num_top_projects
if show_project_contributions
counts_grouped_by_period = user_counts.group_by { |user_proj_class_count| user_proj_class_count[:period] }.transform_values do |counts_in_period|
total_in_period = { count: counts_in_period.sum(&:count) }
total_in_period[:session_time] = counts_in_period.sum(&:session_time) if show_time_spent
Expand All @@ -41,19 +42,19 @@ def response_data(user_counts, num_top_projects, show_time_spent:)
end
end

def show_proj_contributions(response, num_top_projects_to_show)
def display_project_contributions(response)
response[:unique_project_contributions] = unique_projects_count
response[:top_project_contributions] = top_project_contributions(num_top_projects_to_show)
response[:project_contributions] = project_contributions
end

def unique_projects_count
@user_classification_counts.map(&:project_id).uniq.count
end

def top_project_contributions(num_top_projects)
def project_contributions
project_contributions = @user_classification_counts.group_by(&:project_id).transform_values do |counts|
counts.sum(&:count)
end
project_contributions.map { |project_id, count| { project_id:, count: } }.sort_by { |proj_contribution| proj_contribution[:count] }.reverse.first(num_top_projects)
project_contributions.map { |project_id, count| { project_id:, count: } }.sort_by { |proj_contribution| proj_contribution[:count] }.reverse
yuenmichelle1 marked this conversation as resolved.
Show resolved Hide resolved
end
end
25 changes: 15 additions & 10 deletions spec/controllers/user_classification_count_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@
expect(response_body['data'][0]['session_time']).to eq(classification_event.session_time)
end

it 'returns top contributions and unique project contributions if querying for top_project_contributions' do
get :query, params: { id: classification_event.user_id, top_project_contributions: 10 }
it 'returns unique project contributions count if querying for project_contributions' do
get :query, params: { id: classification_event.user_id, project_contributions: true }
expect(response.status).to eq(200)
response_body = JSON.parse(response.body)
expect(response_body['unique_project_contributions']).to eq(1)
expect(response_body['top_project_contributions'].length).to eq(1)
expect(response_body['top_project_contributions'][0]['project_id']).to eq(classification_event.project_id)
expect(response_body['project_contributions'].length).to eq(1)
expect(response_body['project_contributions'][0]['project_id']).to eq(classification_event.project_id)
end
end

Expand Down Expand Up @@ -97,21 +97,26 @@
context 'param validations' do
it_behaves_like 'ensure valid query params', :query, id: 1

it 'ensures you cannot query by workflow and top_project_contributions' do
get :query, params: { id: 1, top_project_contributions: 10, workflow_id: 1 }
it 'ensures you cannot query by workflow and project_contributions' do
get :query, params: { id: 1, project_contributions: true, workflow_id: 1 }
expect(response.status).to eq(400)
expect(response.body).to include('Cannot query top projects and query by project/workflow')
end

it 'ensures you cannot query by project and top project contributions' do
get :query, params: { id: 1, top_project_contributions: 10, project_id: 1 }
get :query, params: { id: 1, project_contributions: true, project_id: 1 }
expect(response.status).to eq(400)
expect(response.body).to include('Cannot query top projects and query by project/workflow')
end

it 'ensures top_project_contributions is an integer' do
get :query, params: { id: 1, top_project_contributions: '20' }
expect(controller.params[:top_project_contributions]).to eq(20)
it 'ensures project_contributions is an boolean' do
get :query, params: { id: 1, project_contributions: '100' }
expect(controller.params[:project_contributions]).to eq(false)
end

it 'ensures project_contributions is true if given string true' do
get :query, params: { id: 1, project_contributions: 'true' }
expect(controller.params[:project_contributions]).to eq(true)
end

it 'ensures time_spent is a boolean' do
Expand Down
10 changes: 5 additions & 5 deletions spec/queries/count_user_classifications_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
let(:params) { {} }
let(:count_user_classifications) { described_class.new(params) }
describe 'relation' do
it 'returns DailyUserClassificationCount if not given workflow, project ids or top_project_contributions' do
it 'returns DailyUserClassificationCount if not given workflow, project ids or project_contributions' do
expect(count_user_classifications.counts.model).to be UserClassificationCounts::DailyUserClassificationCount
end

Expand All @@ -20,8 +20,8 @@
expect(count_user_classifications.counts.model).to be UserClassificationCounts::DailyUserProjectClassificationCount
end

it 'returns DailyUserProjectClassificationCount if querying top_project_contributions' do
params[:top_project_contributions] = 2
it 'returns DailyUserProjectClassificationCount if querying project_contributions' do
params[:project_contributions] = true
expect(count_user_classifications.counts.model).to be UserClassificationCounts::DailyUserProjectClassificationCount
end
end
Expand All @@ -47,8 +47,8 @@
expect(counts.to_sql).to eq(expected_select_query)
end

it 'queries for project_id if querying for top_project_contributions' do
params[:top_project_contributions] = 10
it 'queries for project_id if querying for project_contributions' do
params[:project_contributions] = true
counts = count_user_classifications.call(params)
expected_select_query = "SELECT time_bucket('1 year', day) AS period, SUM(classification_count)::integer AS count, project_id FROM \"daily_user_classification_count_and_time_per_project\" GROUP BY period, project_id ORDER BY period"
expect(counts.to_sql).to eq(expected_select_query)
Expand Down
41 changes: 17 additions & 24 deletions spec/serializers/user_classification_counts_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
expect(serialized).not_to have_key(:data)
expect(serialized).not_to have_key(:time_spent)
expect(serialized).not_to have_key(:unique_project_contributions)
expect(serialized).not_to have_key(:top_project_contributions)
expect(serialized).not_to have_key(:project_contributions)
expect(serialized[:total_count]).to eq(user_classification_count.count)
end

Expand Down Expand Up @@ -46,45 +46,38 @@
expect(serialized[:time_spent]).to eq(classification_counts.sum(&:session_time))
end

it 'returns unique_project_contributions and top_project_contributions if top_project_contributions' do
serialized = count_serializer.as_json(serializer_options: { top_project_contributions: 10 })
it 'returns unique_project_contributions count and project_contributions if project_contributions' do
serialized = count_serializer.as_json(serializer_options: { project_contributions: true })
expect(serialized).to have_key(:total_count)
expect(serialized).to have_key(:unique_project_contributions)
expect(serialized).to have_key(:top_project_contributions)
expect(serialized).to have_key(:project_contributions)
expect(serialized[:unique_project_contributions]).to eq(1)
expect(serialized[:top_project_contributions].length).to eq(1)
expect(serialized[:project_contributions].length).to eq(1)
expected_project_contributions = { project_id: user_classification_count.project_id, count: user_classification_count.count }
expect(serialized[:top_project_contributions][0]).to eq(expected_project_contributions)
expect(serialized[:project_contributions][0]).to eq(expected_project_contributions)
end

context 'top_project_contributions param calculations' do
context 'project_contributions param calculations' do
let(:user_diff_proj_count) { build(:user_diff_proj_classification_count) }
let(:user_diff_period_classification_count) { build(:user_diff_period_classification_count) }
let(:serializer) { described_class.new([user_diff_period_classification_count, user_classification_count, user_diff_proj_count]) }

it 'correctly counts unique_projects' do
serialized = serializer.as_json(serializer_options: { top_project_contributions: 10 })
serialized = serializer.as_json(serializer_options: { project_contributions: true })
expect(serialized[:unique_project_contributions]).to eq(2)
end

it 'shows top_project_contributions ordered desc by count' do
serialized = serializer.as_json(serializer_options: { top_project_contributions: 10 })
expect(serialized[:top_project_contributions].length).to eq(2)
expect(serialized[:top_project_contributions][0][:project_id]).to eq(user_classification_count.project_id)
expect(serialized[:top_project_contributions][0][:count]).to eq(user_classification_count.count + user_diff_period_classification_count.count)
expect(serialized[:top_project_contributions][1][:project_id]).to eq(user_diff_proj_count.project_id)
expect(serialized[:top_project_contributions][1][:count]).to eq(user_diff_proj_count.count)
end

it 'shows the first N top_poject_contributions' do
serialized = serializer.as_json(serializer_options: { top_project_contributions: 1 })
expect(serialized[:top_project_contributions].length).to eq(1)
expect(serialized[:top_project_contributions][0][:project_id]).to eq(user_classification_count.project_id)
expect(serialized[:top_project_contributions][0][:count]).to eq(user_classification_count.count + user_diff_period_classification_count.count)
it 'shows project_contributions ordered desc by count' do
serialized = serializer.as_json(serializer_options: { project_contributions: true })
expect(serialized[:project_contributions].length).to eq(2)
expect(serialized[:project_contributions][0][:project_id]).to eq(user_classification_count.project_id)
expect(serialized[:project_contributions][0][:count]).to eq(user_classification_count.count + user_diff_period_classification_count.count)
expect(serialized[:project_contributions][1][:project_id]).to eq(user_diff_proj_count.project_id)
expect(serialized[:project_contributions][1][:count]).to eq(user_diff_proj_count.count)
end

it 'shows response data bucketed by period when querying top_projects' do
serialized = serializer.as_json(serializer_options: { top_project_contributions: 10, period: 'day' })
serialized = serializer.as_json(serializer_options: { project_contributions: true, period: 'day' })
expect(serialized[:data].length).to eq(2)
expect(serialized[:data][0][:period]).to eq(user_diff_period_classification_count.period)
expect(serialized[:data][0][:count]).to eq(user_diff_period_classification_count.count)
Expand All @@ -94,7 +87,7 @@
end

it 'shows response data with session_times bucketed by period' do
serialized = serializer.as_json(serializer_options: { top_project_contributions: 10, period: 'day', time_spent: true })
serialized = serializer.as_json(serializer_options: { project_contributions: true, period: 'day', time_spent: true })
expect(serialized[:data].length).to eq(2)
expect(serialized[:data][0][:session_time]).to eq(user_diff_period_classification_count.session_time)
expect(serialized[:data][1][:session_time]).to eq(user_classification_count.session_time + user_diff_proj_count.session_time)
Expand Down