From dd5a08bcc02669802d4ba2242fda4b6e162a253d Mon Sep 17 00:00:00 2001 From: Finn Bacall Date: Thu, 2 Jul 2026 11:07:37 +0100 Subject: [PATCH 1/3] Also query profiles in people autocomplete. Fixes #1294 --- app/controllers/autocomplete_controller.rb | 28 +++++++++---- app/models/profile.rb | 15 +++++++ .../autocomplete_controller_test.rb | 6 +-- test/models/profile_test.rb | 40 +++++++++++++++++++ 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 9d316890c..2f1ce7efd 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -7,15 +7,29 @@ def suggestions end def people_suggestions - people = Person.query(params[:query]) - suggestions = people.map do |p| - { value: p.name, - data: { - orcid: p.orcid, - profile_id: p.profile_id + people = Person.query(params[:query], 20) + profiles = Profile.query(params[:query], 20) + unique_map = {} + people.each do |p| + unique_map[p.profile_id || p.orcid || p.name] = + { value: p.name, + data: { + orcid: p.orcid, + profile_id: p.profile_id + } } - } end + profiles.each do |p| + unique_map[p.id || p.orcid || p.full_name] = + { value: p.full_name, + data: { + orcid: p.orcid_authenticated? ? p.orcid : nil, + profile_id: p.id + } + } + end + + suggestions = unique_map.values.sort_by { |s| [s[:value].downcase, s[:data][:orcid] || 'z'] } respond_with({ suggestions: suggestions }) end end diff --git a/app/models/profile.rb b/app/models/profile.rb index b09f98925..eae3afca8 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -62,6 +62,21 @@ def authenticate_orcid(orcid) out end + def self.visible + joins(:user).merge(User.visible) + end + + # For autocomplete + def self.starting_with(query) + where('lower(firstname) LIKE ?', "#{query.downcase}%").or(where('lower(surname) LIKE ?', "#{query.downcase}%")) + end + + def self.query(query, limit = nil) + q = visible.select(:id, :firstname, :surname, :orcid, :orcid_authenticated).starting_with(query).distinct + q = q.limit(limit) if limit + q.order(firstname: :asc, surname: :asc, orcid: :asc) + end + private def check_public diff --git a/test/controllers/autocomplete_controller_test.rb b/test/controllers/autocomplete_controller_test.rb index 51198ac42..3f9581649 100644 --- a/test/controllers/autocomplete_controller_test.rb +++ b/test/controllers/autocomplete_controller_test.rb @@ -51,14 +51,14 @@ class AutocompleteControllerTest < ActionController::TestCase res = JSON.parse(response.body) suggestions = res['suggestions'] assert_equal 3, suggestions.length, "Should be 3 - 2 with ORCIDs and 1 without. Should not include duplicates." - assert_equal ['0000-0002-1694-233X', '0000-0002-1825-0097', nil], suggestions.map { |s| s['data']['orcid'] } - assert_equal ['John Doe', 'John Doe', 'John Doe'], suggestions.map { |s| s['value'] } + assert_equal ['0000-0002-1694-233X', nil, '0000-0002-1825-0097'], suggestions.map { |s| s['data']['orcid'] } + assert_equal ['John Doe', 'John Doe', 'Josiah Carberry'], suggestions.map { |s| s['value'] }, "Should use Josiah Carberry's name from his profile rather than John Doe from the author" get :people_suggestions, params: { query: 'j' }, format: :json assert_response :success res = JSON.parse(response.body) suggestions = res['suggestions'] - assert_equal ['jane Doe', 'John Doe', 'John Doe', 'John Doe'], suggestions.map { |s| s['value'] } + assert_equal ['jane Doe', 'John Doe', 'John Doe', 'Josiah Carberry'], suggestions.map { |s| s['value'] } get :people_suggestions, params: { query: 'FRED' }, format: :json assert_response :success diff --git a/test/models/profile_test.rb b/test/models/profile_test.rb index 901f07b4b..194cf98f5 100644 --- a/test/models/profile_test.rb +++ b/test/models/profile_test.rb @@ -154,4 +154,44 @@ class ProfileTest < ActiveSupport::TestCase refute Profile.new.authenticate_orcid('0009-0006-0987-5702') end end + + test 'visible' do + visible = Profile.visible + assert_includes visible, profiles(:one) + assert_includes visible, profiles(:two) + refute_includes visible, profiles(:basic_user_profile) + refute_includes visible, profiles(:banned_user_profile) + end + + test 'starting_with' do + regi = Profile.starting_with('regi') + assert_includes regi, profiles(:one) + refute_includes regi, profiles(:two) + + user = Profile.starting_with('user') + assert_includes user, profiles(:one) + refute_includes user, profiles(:two) + + bla = Profile.starting_with('bla') + refute_includes bla, profiles(:one) + refute_includes bla, profiles(:two) + + ad = Profile.starting_with('ad') + refute_includes regi, profiles(:two) + assert_includes ad, profiles(:three) + assert_includes ad, profiles(:admin_trainer_profile) + end + + test 'query' do + regi = Profile.query('regi') + assert_equal 1, regi.length + assert_includes regi, profiles(:one) + + # Excludes non-visible: + banned = profiles(:banned_user_profile) + banned.update!(firstname: 'Banned') + assert_includes Profile.starting_with('banned'), banned + banned = Profile.query('Banned') + assert_equal 0, banned.length + end end From 7de41971092c0fc10586d2c775530ca223db50e9 Mon Sep 17 00:00:00 2001 From: Finn Bacall Date: Thu, 2 Jul 2026 13:46:15 +0100 Subject: [PATCH 2/3] Only de-duplicate on profile ID and ORCID --- app/controllers/autocomplete_controller.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 2f1ce7efd..2930bd31a 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -20,10 +20,12 @@ def people_suggestions } end profiles.each do |p| - unique_map[p.id || p.orcid || p.full_name] = + orcid = p.orcid_authenticated? ? p.orcid : nil + unique_map.delete(orcid) if orcid + unique_map[p.id] = { value: p.full_name, data: { - orcid: p.orcid_authenticated? ? p.orcid : nil, + orcid: orcid, profile_id: p.id } } From 0c351749ddaab26363beb0ca9c5d2d7199f0093e Mon Sep 17 00:00:00 2001 From: Finn Bacall Date: Thu, 2 Jul 2026 14:10:54 +0100 Subject: [PATCH 3/3] Fix copy and paste --- test/models/profile_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/models/profile_test.rb b/test/models/profile_test.rb index 194cf98f5..4632093c7 100644 --- a/test/models/profile_test.rb +++ b/test/models/profile_test.rb @@ -177,7 +177,7 @@ class ProfileTest < ActiveSupport::TestCase refute_includes bla, profiles(:two) ad = Profile.starting_with('ad') - refute_includes regi, profiles(:two) + refute_includes ad, profiles(:two) assert_includes ad, profiles(:three) assert_includes ad, profiles(:admin_trainer_profile) end