From edcbae07069f13b5a367f795fd07a3beba49973d Mon Sep 17 00:00:00 2001 From: pskl Date: Wed, 15 Jan 2025 15:49:16 +0100 Subject: [PATCH] Fix student search (#1331) - [x] use distinct instead of uniq - [x] use regex - [x] simplify query --- app/controllers/students_controller.rb | 26 ++++----------------- app/models/establishment.rb | 20 ++++++++++++++++ app/views/students/search_results.html.haml | 1 - config/initializers/version.rb | 2 +- db/schema.rb | 4 ++-- features/recherche.feature | 7 +++--- spec/models/establishment_spec.rb | 16 +++++++++++++ 7 files changed, 47 insertions(+), 29 deletions(-) diff --git a/app/controllers/students_controller.rb b/app/controllers/students_controller.rb index e94cab977..df05b7035 100644 --- a/app/controllers/students_controller.rb +++ b/app/controllers/students_controller.rb @@ -2,10 +2,11 @@ class StudentsController < ApplicationController before_action :set_student, :check_student!, only: :show - before_action :sanitize_search, :set_search_result, only: :search_results rescue_from ActiveRecord::RecordNotFound, with: :redirect_to_class + before_action :set_search_result, only: :search_results + def show @schoolings = @student.schoolings @@ -30,28 +31,9 @@ def redirect_to_class redirect_to school_year_classes_path(selected_school_year), alert: t("errors.students.not_found") end - # rubocop:disable Layout/LineLength def set_search_result - search_str = "%#{@name}%" - @students = current_establishment.students.where( - " - TRANSLATE(TRANSLATE(UNACCENT(last_name), $$',$$, ''), '-', ' ') ILIKE TRANSLATE(TRANSLATE(UNACCENT(:search_str), $$',$$, ''), '-', ' ') OR - TRANSLATE(TRANSLATE(UNACCENT(first_name), $$',$$, ''), '-', ' ') ILIKE TRANSLATE(TRANSLATE(UNACCENT(:search_str), $$',$$, ''), '-', ' ') OR - TRANSLATE(TRANSLATE(UNACCENT(CONCAT(last_name, ' ', first_name)), $$',$$, ''), '-', ' ') ILIKE TRANSLATE(TRANSLATE(UNACCENT(:search_str), $$',$$, ''), '-', ' ') OR - TRANSLATE(TRANSLATE(UNACCENT(CONCAT(first_name, ' ', last_name)), $$',$$, ''), '-', ' ') ILIKE TRANSLATE(TRANSLATE(UNACCENT(:search_str), $$',$$, ''), '-', ' ') - ", search_str: - ).uniq - # Dans l'idéal, utiliser la REGEX '%' : - # TRANSLATE(UNACCENT(last_name), $$-', $$, $$%$$) ILIKE TRANSLATE(UNACCENT(:search_str), $$-', $$, $$%$$) OR - # TRANSLATE(UNACCENT(first_name), $$-', $$, $$%$$) ILIKE TRANSLATE(UNACCENT(:search_str), $$-', $$, $$%$$) OR - # TRANSLATE(UNACCENT(CONCAT(last_name, '%', first_name)), $$-', $$, $$%$$) ILIKE TRANSLATE(UNACCENT(:search_str), $$-', $$, $$%$$) OR - # TRANSLATE(UNACCENT(CONCAT(first_name, '%', last_name)), $$-', $$, $$%$$) ILIKE TRANSLATE(UNACCENT(:search_str), $$-', $$, $$%$$) - end - # rubocop:enable Layout/LineLength - - def sanitize_search - return if params[:name].blank? - @name = params[:name] + + @students = current_establishment.find_students(@name) end end diff --git a/app/models/establishment.rb b/app/models/establishment.rb index 0aa90c735..d36b6f965 100644 --- a/app/models/establishment.rb +++ b/app/models/establishment.rb @@ -89,6 +89,26 @@ def invites?(email) invitations.exists?(email: email) end + def find_students(name) + search_terms = name + .strip + .gsub(/[^[:alnum:]\s]/, "") + .split + .map { |term| "%#{Student.sanitize_sql_like(term)}%" } + + students + .includes(current_schooling: :classe) + .where( + search_terms.map do + "(regexp_replace(unaccent(first_name), '[^[:alnum:]]', '', 'g') ILIKE ? OR " \ + "regexp_replace(unaccent(last_name), '[^[:alnum:]]', '', 'g') ILIKE ?)" + end.join(" OR "), + *search_terms.flat_map { |term| [term, term] } + ) + .unscope(:order) + .distinct + end + def select_label [uai, name].compact.join(" - ") end diff --git a/app/views/students/search_results.html.haml b/app/views/students/search_results.html.haml index f399ff308..6685a7547 100644 --- a/app/views/students/search_results.html.haml +++ b/app/views/students/search_results.html.haml @@ -12,7 +12,6 @@ %tr %td= dsfr_link_to student.full_name, student_path(student) %td - // TODO: Risque de N+1 ? -if student.current_schooling.nil? Aucune scolarité active -else diff --git a/config/initializers/version.rb b/config/initializers/version.rb index e37a02f0a..05d1ed040 100644 --- a/config/initializers/version.rb +++ b/config/initializers/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Aplypro - VERSION = "2.0.11" + VERSION = "2.1.0" end diff --git a/db/schema.rb b/db/schema.rb index d6b3e6fbc..b1a7cbdbd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,9 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_01_13_111229) do +ActiveRecord::Schema[8.0].define(version: 2025_01_13_111229) do # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" + enable_extension "pg_catalog.plpgsql" enable_extension "unaccent" create_table "active_storage_attachments", force: :cascade do |t| diff --git a/features/recherche.feature b/features/recherche.feature index 2aff0eb16..7d3f6ad03 100644 --- a/features/recherche.feature +++ b/features/recherche.feature @@ -1,5 +1,5 @@ # language: fr -Fonctionnalité: Recherche +Fonctionnalité: Recherche d'étudiants par nom et prénom Contexte: Sachant que je suis un personnel MENJ directeur de l'établissement "DINUM" Et que l'API SYGNE renvoie 10 élèves dans une classe "1MELEC" dont "Jüãn-Frânçois Mîchäèl-d'Estaing" pour l'établissement "DINUM" @@ -7,7 +7,7 @@ Fonctionnalité: Recherche Et que je passe l'écran d'accueil Et que toutes les tâches de fond sont terminées - Scénario: Le personnel veut rechercher un élève + Scénario: Le personnel veut rechercher un élève en utilisant nom et/ou prénom Quand je recherche l'élève "a" Alors la page contient "Recherche d'un élève" Et la page contient "résultats trouvés pour la recherche : a" @@ -15,4 +15,5 @@ Fonctionnalité: Recherche Alors la page contient "Aucun résultat trouvé pour la recherche : XRTEZEDE" Quand je recherche l'élève "juan francois michael dest" Alors la page contient "Jüãn-Frânçois Mîchäèl-d'Estaing" - \ No newline at end of file + Quand je recherche l'élève "juan-francois" + Alors la page contient "Jüãn-Frânçois Mîchäèl-d'Estaing" diff --git a/spec/models/establishment_spec.rb b/spec/models/establishment_spec.rb index d9a140169..da8d9c51a 100644 --- a/spec/models/establishment_spec.rb +++ b/spec/models/establishment_spec.rb @@ -96,4 +96,20 @@ end end end + + describe "#find_students" do + let(:etab) { create(:classe, :with_students, students_count: 5).establishment } + + before do + etab.students.first.update!(first_name: "Tibal", last_name: "N'Guy-ôme") + end + + it "strips apostrophes" do + expect(etab.find_students("nguyom")).to contain_exactly(Student.find_by!(last_name: "N'Guy-ôme")) + end + + it "finds students with partial names" do + expect(etab.find_students("ibal")).to contain_exactly(Student.find_by!(first_name: "Tibal")) + end + end end