Skip to content

Commit 076b0b8

Browse files
authored
feat: add School Roll Number field for Ireland schools Add optional s… (#632)
Add School Roll Number field for Ireland schools Add optional school_roll_number field with alphanumeric validation, following the same pattern as UK URN and US district fields. Includes database migration, API endpoints, admin dashboard integration, and comprehensive test coverage. ## Status Closes: https://github.com/orgs/RaspberryPiFoundation/projects/51/views/11?pane=issue&itemId=132315256&issue=RaspberryPiFoundation%7Cdigital-editor-issues%7C925 Related to: https://github.com/RaspberryPiFoundation/editor-standalone/pull/686 ## Points for consideration: ### Security - Field has unique index to prevent duplicate entries - Input validation enforces alphanumeric format (numbers followed by letters) - Case-insensitive uniqueness check prevents bypass attempts ### Performance - Database index added on `school_roll_number` column for efficient lookups - Optional field (nullable) - no impact on existing schools ## What's changed? **Database:** - Added `school_roll_number` string column to `schools` table - Added unique index on `school_roll_number` **Model (School):** - Added validation: uniqueness (case-insensitive), alphanumeric format - Added normalisation callback to handle blank values - Added I18n error message for validation **API:** - Added `school_roll_number` to permitted parameters in SchoolsController - Included `school_roll_number` in JSON API responses **Admin Dashboard:** - Added field to admin show page, form, and attributes list - Admins can view School Roll Numbers **Tests:** - 8 comprehensive validation tests (optional, uniqueness, format validation, normalisation) - Updated factory to support the new field **Locales:** - Added validation message to `config/locales/en.yml`
1 parent 1f96b90 commit 076b0b8

11 files changed

Lines changed: 114 additions & 2 deletions

File tree

app/controllers/api/schools_controller.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def school_params
8080
:reference,
8181
:district_name,
8282
:district_nces_id,
83+
:school_roll_number,
8384
:address_line_1,
8485
:address_line_2,
8586
:municipality,

app/dashboards/school_dashboard.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class SchoolDashboard < Administrate::BaseDashboard
2828
reference: Field::String,
2929
district_name: Field::String,
3030
district_nces_id: Field::String,
31+
school_roll_number: Field::String,
3132
verified_at: Field::DateTime,
3233
rejected_at: Field::DateTime,
3334
created_at: Field::DateTime,
@@ -62,6 +63,7 @@ class SchoolDashboard < Administrate::BaseDashboard
6263
reference
6364
district_name
6465
district_nces_id
66+
school_roll_number
6567
website
6668
address_line_1
6769
address_line_2
@@ -86,6 +88,7 @@ class SchoolDashboard < Administrate::BaseDashboard
8688
reference
8789
district_name
8890
district_nces_id
91+
school_roll_number
8992
website
9093
address_line_1
9194
address_line_2

app/models/school.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ class School < ApplicationRecord
1818
validates :country_code, presence: true, inclusion: { in: ISO3166::Country.codes }
1919
validates :reference, uniqueness: { case_sensitive: false, allow_nil: true }, presence: false
2020
validates :district_nces_id, uniqueness: { case_sensitive: false, allow_nil: true }, presence: false
21+
validates :school_roll_number,
22+
uniqueness: { conditions: -> { where(rejected_at: nil) }, case_sensitive: false, allow_nil: true },
23+
presence: { if: :ireland? },
24+
format: { with: /\A[0-9]+[A-Z]+\z/, allow_nil: true, message: I18n.t('validations.school.school_roll_number') }
2125
validates :creator_id, presence: true, uniqueness: true
2226
validates :creator_agree_authority, presence: true, acceptance: true
2327
validates :creator_agree_terms_and_conditions, presence: true, acceptance: true
@@ -34,6 +38,7 @@ class School < ApplicationRecord
3438

3539
before_validation :normalize_reference
3640
before_validation :normalize_district_fields
41+
before_validation :normalize_school_roll_number
3742

3843
before_save :format_uk_postal_code, if: :should_format_uk_postal_code?
3944

@@ -102,6 +107,12 @@ def normalize_district_fields
102107
self.district_nces_id = nil if district_nces_id.blank?
103108
end
104109

110+
# Ensure the school_roll_number is nil, not an empty string
111+
# Also normalize to uppercase for consistent validation
112+
def normalize_school_roll_number
113+
self.school_roll_number = school_roll_number.blank? ? nil : school_roll_number.upcase
114+
end
115+
105116
def verified_at_cannot_be_changed
106117
errors.add(:verified_at, 'cannot be changed after verification') if verified_at_was.present? && verified_at_changed?
107118
end
@@ -118,6 +129,10 @@ def should_format_uk_postal_code?
118129
country_code == 'GB' && postal_code.to_s.length >= 5
119130
end
120131

132+
def ireland?
133+
country_code == 'IE'
134+
end
135+
121136
def format_uk_postal_code
122137
cleaned_postal_code = postal_code.delete(' ')
123138
# insert a space as the third-from-last character in the postcode, eg. SW1A1AA -> SW1A 1AA

app/views/api/schools/_school.json.jbuilder

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ json.call(
88
:reference,
99
:district_name,
1010
:district_nces_id,
11+
:school_roll_number,
1112
:address_line_1,
1213
:address_line_2,
1314
:municipality,

config/locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ en:
1515
validations:
1616
school:
1717
website: "must be a valid URL"
18+
school_roll_number: "must be numbers followed by letters (e.g., 01572D)"
1819
invitation:
1920
email_address: "'%<value>s' is invalid"
2021
activerecord:
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
class AddSchoolRollNumberToSchools < ActiveRecord::Migration[7.2]
4+
def change
5+
add_column :schools, :school_roll_number, :string
6+
7+
add_index :schools, :school_roll_number, unique: true
8+
end
9+
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# frozen_string_literal: true
2+
3+
class ChangeSchoolRollNumberIndexToPartial < ActiveRecord::Migration[7.2]
4+
def change
5+
remove_index :schools, :school_roll_number
6+
add_index :schools, :school_roll_number, unique: true, where: 'rejected_at IS NULL'
7+
end
8+
end

db/schema.rb

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

spec/factories/school.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
address_line_1 { 'Address Line 1' }
88
municipality { 'Greater London' }
99
country_code { 'GB' }
10+
school_roll_number { nil }
1011
creator_id { SecureRandom.uuid }
1112
creator_agree_authority { true }
1213
creator_agree_terms_and_conditions { true }

spec/features/my_school/showing_my_school_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
it "includes the school details and user's roles in the JSON" do
2020
school_json = school.to_json(only: %i[
21-
id name website reference address_line_1 address_line_2 municipality administrative_area postal_code country_code code verified_at created_at updated_at district_name district_nces_id
21+
id name website reference address_line_1 address_line_2 municipality administrative_area postal_code country_code code verified_at created_at updated_at district_name district_nces_id school_roll_number
2222
])
2323
expected_data = JSON.parse(school_json, symbolize_names: true).merge(roles: ['owner'], import_in_progress: school.import_in_progress?)
2424

0 commit comments

Comments
 (0)