Skip to content

Commit b1c6c43

Browse files
committed
1069: Descoped duplication
1 parent f0856d5 commit b1c6c43

6 files changed

Lines changed: 1 addition & 144 deletions

File tree

app/models/school.rb

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# frozen_string_literal: true
22

33
class School < ApplicationRecord
4-
class DuplicateSchoolError < StandardError; end
5-
64
has_many :classes, class_name: :SchoolClass, inverse_of: :school, dependent: :destroy
75
has_many :lessons, dependent: :nullify
86
has_many :projects, dependent: :nullify
@@ -42,7 +40,6 @@ class DuplicateSchoolError < StandardError; end
4240
before_validation :normalize_district_fields
4341
before_validation :normalize_school_roll_number
4442

45-
before_save :prevent_duplicate_school
4643
before_save :format_uk_postal_code, if: :should_format_uk_postal_code?
4744

4845
after_commit :generate_code!, on: :create, if: -> { FeatureFlags.immediate_school_onboarding? }
@@ -155,29 +152,4 @@ def format_uk_postal_code
155152
# ensures UK postcodes are always formatted correctly (as the inward code is always 3 chars long)
156153
self.postal_code = "#{cleaned_postal_code[0..-4]} #{cleaned_postal_code[-3..]}"
157154
end
158-
159-
def prevent_duplicate_school
160-
name_threshold = 0.6
161-
municipality_threshold = 0.7
162-
postal_code_threshold = 0.8
163-
max_edit_distance = 3
164-
165-
normalized_postal = postal_code.to_s.gsub(/\s+/, '')
166-
167-
duplicate_school = School
168-
.where.not(id:)
169-
.where(country_code:)
170-
.where(
171-
'similarity(lower(name), ?) >= ? AND levenshtein(lower(name), ?) <= ?',
172-
name.to_s, name_threshold, name.to_s, max_edit_distance
173-
)
174-
.where('similarity(lower(municipality), ?) >= ?', municipality.to_s, municipality_threshold)
175-
.where(
176-
"similarity(lower(replace(postal_code, ' ', '')), ?) >= ?",
177-
normalized_postal, postal_code_threshold
178-
)
179-
.first
180-
181-
raise DuplicateSchoolError, I18n.t('validations.school.duplicate_school') if duplicate_school
182-
end
183155
end

db/migrate/20251218143924_enable_pg_trgm_extension.rb

Lines changed: 0 additions & 6 deletions
This file was deleted.

db/schema.rb

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/concepts/school/operations/create.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ def call(school_params:, creator_id:, token:)
1313
SchoolOnboardingService.new(response[:school]).onboard(token:) if FeatureFlags.immediate_school_onboarding?
1414
end
1515

16-
response
17-
rescue School::DuplicateSchoolError => e
18-
response[:error] = e.message
19-
response[:error_type] = :duplication_error
2016
response
2117
rescue StandardError => e
2218
Sentry.capture_exception(e)

lib/concepts/school/operations/update.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ def call(school:, school_params:)
99
response[:school].assign_attributes(school_params)
1010
response[:school].save!
1111
response
12-
rescue School::DuplicateSchoolError => e
13-
Sentry.capture_exception(e)
14-
response[:error] = e.message
15-
response
1612
rescue StandardError => e
1713
Sentry.capture_exception(e)
1814
errors = response[:school].errors.full_messages.join(',')

spec/models/school_spec.rb

Lines changed: 0 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -330,105 +330,6 @@
330330
it 'sets the user_origin to for_education by default' do
331331
expect(school.user_origin).to eq('for_education')
332332
end
333-
334-
describe 'duplicate detection' do
335-
let(:existing_school) do
336-
create(:school,
337-
name: 'Riverside Academy',
338-
municipality: 'Greenville',
339-
postal_code: 'GV1 2XY',
340-
administrative_area: 'Greenshire')
341-
end
342-
343-
before { existing_school }
344-
345-
it 'allows schools with the same name in different countries' do
346-
duplicate = build(:school,
347-
name: existing_school.name,
348-
municipality: existing_school.municipality,
349-
postal_code: existing_school.postal_code,
350-
administrative_area: existing_school.administrative_area,
351-
country_code: 'US')
352-
expect(duplicate).to be_valid
353-
end
354-
355-
it 'allows schools with the same name in different cities' do
356-
duplicate = build(:school,
357-
name: existing_school.name,
358-
municipality: 'Bluetown',
359-
postal_code: 'BT3 4ZW',
360-
administrative_area: 'Blueshire')
361-
expect(duplicate).to be_valid
362-
end
363-
364-
it 'allows schools with the same name but very different postal codes' do
365-
duplicate = build(:school,
366-
name: existing_school.name,
367-
municipality: existing_school.municipality,
368-
postal_code: 'NE1 7RU',
369-
administrative_area: existing_school.administrative_area)
370-
expect(duplicate).to be_valid
371-
end
372-
373-
it 'blocks duplicate with missing apostrophe in name' do
374-
duplicate = build(:school,
375-
name: 'Riverside Acadmy',
376-
municipality: existing_school.municipality,
377-
postal_code: existing_school.postal_code,
378-
administrative_area: existing_school.administrative_area)
379-
expect { duplicate.save! }.to raise_error(School::DuplicateSchoolError, I18n.t('validations.school.duplicate_school'))
380-
end
381-
382-
it 'blocks duplicate with typo in name' do
383-
duplicate = build(:school,
384-
name: 'Riversid Academy',
385-
municipality: existing_school.municipality,
386-
postal_code: existing_school.postal_code,
387-
administrative_area: existing_school.administrative_area)
388-
expect { duplicate.save! }.to raise_error(School::DuplicateSchoolError, I18n.t('validations.school.duplicate_school'))
389-
end
390-
391-
it 'blocks duplicate with similar municipality spelling' do
392-
duplicate = build(:school,
393-
name: existing_school.name,
394-
municipality: 'Greenvile',
395-
postal_code: existing_school.postal_code,
396-
administrative_area: existing_school.administrative_area)
397-
expect { duplicate.save! }.to raise_error(School::DuplicateSchoolError, I18n.t('validations.school.duplicate_school'))
398-
end
399-
400-
it 'blocks duplicate with slightly different postal code spacing' do
401-
duplicate = build(:school,
402-
name: existing_school.name,
403-
municipality: existing_school.municipality,
404-
postal_code: 'GV12XY',
405-
administrative_area: existing_school.administrative_area)
406-
expect { duplicate.save! }.to raise_error(School::DuplicateSchoolError, I18n.t('validations.school.duplicate_school'))
407-
end
408-
409-
it 'allows updating existing school without triggering duplicate detection' do
410-
existing_school.update(address_line_1: Faker::Address.street_address)
411-
expect(existing_school).to be_valid
412-
end
413-
414-
it 'allows school with very different name despite same location' do
415-
duplicate = build(:school,
416-
name: 'Completely Different Academy',
417-
municipality: existing_school.municipality,
418-
postal_code: existing_school.postal_code,
419-
administrative_area: existing_school.administrative_area)
420-
expect(duplicate).to be_valid
421-
end
422-
423-
it 'allows schools with similar but sufficiently different names in same location' do
424-
duplicate = build(:school,
425-
name: 'Riverside Institute',
426-
municipality: existing_school.municipality,
427-
postal_code: existing_school.postal_code,
428-
administrative_area: existing_school.administrative_area)
429-
expect(duplicate).to be_valid
430-
end
431-
end
432333
end
433334

434335
describe '#creator' do

0 commit comments

Comments
 (0)