From 52bb491c0d5e814a494bbb7f6b99fc5e0e81a35d Mon Sep 17 00:00:00 2001 From: yuenmichelle1 Date: Fri, 18 Oct 2024 15:54:01 -0500 Subject: [PATCH] Add option to order user project contributions by most recently contributed (#76) * update to include ordering project contributions by recents * remove print statements * adding specs * fixing hound sniffs * Update app/serializers/user_classification_counts_serializer.rb Co-authored-by: Zach Wolfenbarger * Update app/serializers/user_classification_counts_serializer.rb Co-authored-by: Zach Wolfenbarger --------- Co-authored-by: Zach Wolfenbarger --- .../user_classification_count_controller.rb | 15 +++++++++-- .../user_classification_counts_serializer.rb | 21 ++++++++++++--- ...er_classification_count_controller_spec.rb | 18 +++++++++++++ ...r_classification_counts_serializer_spec.rb | 26 +++++++++++++++++-- 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/app/controllers/user_classification_count_controller.rb b/app/controllers/user_classification_count_controller.rb index bcce57a..c3c170a 100644 --- a/app/controllers/user_classification_count_controller.rb +++ b/app/controllers/user_classification_count_controller.rb @@ -5,6 +5,11 @@ class UserClassificationCountController < ApplicationController before_action :sanitize_params before_action :require_login + PROJ_CONTRIBUTIONS_ORDER_OPTIONS = { + recents: 'recents', + count: 'count' + }.freeze + def query current_user['queried_user_id'] = params[:id] authorize :queried_user_context, :show? @@ -17,6 +22,11 @@ def query def validate_params super raise ValidationError, 'Cannot query for project contributions and query by project/workflow' if params[:project_contributions] && (params[:workflow_id] || params[:project_id]) + validate_order_proj_contributions if params[:order_project_contributions_by] + end + + def validate_order_proj_contributions + raise ValidationError, 'Can only order project contributions by recents or count. Default behavior is count' unless PROJ_CONTRIBUTIONS_ORDER_OPTIONS.keys.include? params[:order_project_contributions_by].downcase.to_sym end def sanitize_params @@ -27,10 +37,11 @@ def sanitize_params def serializer_opts_from_params { period: params[:period], time_spent: params[:time_spent], - project_contributions: params[:project_contributions] } + project_contributions: params[:project_contributions], + order_project_contributions_by: params[:order_project_contributions_by] } end def user_classification_count_params - params.permit(:id, :start_date, :end_date, :period, :workflow_id, :project_id, :project_contributions, :time_spent) + params.permit(:id, :start_date, :end_date, :period, :workflow_id, :project_id, :project_contributions, :time_spent, :order_project_contributions_by) end end diff --git a/app/serializers/user_classification_counts_serializer.rb b/app/serializers/user_classification_counts_serializer.rb index ff92905..f0480ff 100644 --- a/app/serializers/user_classification_counts_serializer.rb +++ b/app/serializers/user_classification_counts_serializer.rb @@ -11,16 +11,22 @@ def as_json(options) serializer_options = options[:serializer_options] show_time_spent = serializer_options[:time_spent] show_project_contributions = serializer_options[:project_contributions] + order_project_contributions_by = serializer_options[:order_project_contributions_by] + total_count = user_classification_counts.sum(&:count).to_i response = { total_count: } calculate_time_spent(user_classification_counts, response) if show_time_spent - response[:project_contributions] = project_contributions if show_project_contributions + response[:project_contributions] = project_contributions(order_project_contributions_by) if show_project_contributions response[:data] = response_data(user_classification_counts, show_project_contributions:, show_time_spent:) if serializer_options[:period] response end private + def order_project_contributions_by_recents?(order_project_contributions_by) + order_project_contributions_by && order_project_contributions_by.downcase == 'recents' + end + def calculate_time_spent(counts, response) total_time_spent = counts.sum(&:session_time).to_f response[:time_spent] = total_time_spent @@ -48,10 +54,19 @@ def response_data(user_counts, show_project_contributions:, show_time_spent:) end end - def project_contributions + def project_contributions(order_by) 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 + + if order_project_contributions_by_recents?(order_by) + period_to_contributed_project_ids = @user_classification_counts.sort_by(&:period).reverse.group_by(&:period).transform_values do |uccs| + uccs.map(&:project_id) + end + recently_contributed_project_ids = period_to_contributed_project_ids.values.flatten.uniq + recently_contributed_project_ids.map { |project_id| { project_id: , count: project_contributions[project_id] } } + else + project_contributions.map { |project_id, count| { project_id:, count: } }.sort_by { |proj_contribution| proj_contribution[:count] }.reverse + end end end diff --git a/spec/controllers/user_classification_count_controller_spec.rb b/spec/controllers/user_classification_count_controller_spec.rb index 0f3aa54..aed046f 100644 --- a/spec/controllers/user_classification_count_controller_spec.rb +++ b/spec/controllers/user_classification_count_controller_spec.rb @@ -126,6 +126,24 @@ get :query, params: { id: 1, time_spent: 'true' } expect(controller.params[:time_spent]).to eq(true) end + + context 'order_project_contributions validations' do + it 'errors if not a valid order_project_contributions_by option' do + get :query, params: { id: 1, order_project_contributions_by: 'blank' } + expect(response.status).to eq(400) + expect(response.body).to include('Can only order project contributions by recents or count') + end + + it 'allows allowable order_project_contributions_by option' do + get :query, params: { id: 1, order_project_contributions_by: 'recents' } + expect(response.status).to eq(200) + end + + it 'allows order_project_contributions_by to be case-insensitive' do + get :query, params: { id: 1, order_project_contributions_by: 'COUNT' } + expect(response.status).to eq(200) + end + end end end end diff --git a/spec/serializers/user_classification_counts_serializer_spec.rb b/spec/serializers/user_classification_counts_serializer_spec.rb index a3fce9a..b31a866 100644 --- a/spec/serializers/user_classification_counts_serializer_spec.rb +++ b/spec/serializers/user_classification_counts_serializer_spec.rb @@ -59,7 +59,7 @@ 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 'shows project_contributions ordered desc by count' do + it 'shows project_contributions ordered desc by count when order_proj_contribution_by by not given' 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) @@ -68,7 +68,29 @@ 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 + context 'when order_project_contributions_by param is given' do + it 'shows project_contributions ordered desc by count when order_proj_contribution_by is count' do + serialized = serializer.as_json(serializer_options: { project_contributions: true, order_project_contributions_by: 'count' }) + 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 project_contributions ordered by recents when order_proj_contribution_by is recents' do + classification_count_diff_project_created_yesterday = build(:user_diff_proj_classification_count, period: Date.today - 1) + serializer = described_class.new([classification_count_diff_project_created_yesterday, user_classification_count]) + serialized = serializer.as_json(serializer_options: { project_contributions: true, order_project_contributions_by: 'recents' }) + 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) + expect(serialized[:project_contributions][1][:project_id]).to eq(classification_count_diff_project_created_yesterday.project_id) + expect(serialized[:project_contributions][1][:count]).to eq(classification_count_diff_project_created_yesterday.count) + end + end + + it 'shows response data bucketed by period when querying project_contributions by count' do 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)