@@ -21,22 +21,52 @@ class RegistrationsController < ApplicationController
2121 before_action -> { redirect_to_root_unless_user ( :can_manage_competition? , competition_from_params ) } ,
2222 except : %i[ index psych_sheet psych_sheet_event register payment_completion load_payment_intent stripe_webhook payment_denomination capture_paypal_payment ]
2323
24- before_action :competition_must_be_using_wca_registration! , except : %i[ import do_import add do_add index psych_sheet psych_sheet_event stripe_webhook payment_denomination ]
24+ before_action :competition_must_be_using_wca_registration! , except : %i[ import do_import validate_and_convert_registrations add do_add index psych_sheet psych_sheet_event stripe_webhook payment_denomination ]
2525 private def competition_must_be_using_wca_registration!
2626 return if competition_from_params . use_wca_registration?
2727
2828 flash [ :danger ] = I18n . t ( 'registrations.flash.not_using_wca' )
2929 redirect_to competition_path ( competition_from_params )
3030 end
3131
32- before_action :competition_must_not_be_using_wca_registration! , only : %i[ import do_import ]
32+ before_action :competition_must_not_be_using_wca_registration! , only : %i[ import do_import validate_and_convert_registrations ]
3333 private def competition_must_not_be_using_wca_registration!
3434 redirect_to competition_path ( competition_from_params ) if competition_from_params . use_wca_registration?
3535 end
3636
3737 before_action :validate_import_registration , only : %i[ do_import ]
3838 private def validate_import_registration
3939 @competition = competition_from_params
40+
41+ registration_rows = params . require ( :registrations )
42+
43+ return render status : :unprocessable_content , json : { error : "Expected array of registrations" } unless registration_rows . is_a? ( Array )
44+
45+ errors = [
46+ validate_registrations ( registration_rows , @competition ) ,
47+ competitor_limit_error ( @competition , registration_rows . length ) ,
48+ ] . compact . flatten
49+
50+ return render status : :unprocessable_content , json : { error : errors . compact . join ( ", " ) } if errors . any?
51+
52+ @registration_rows = registration_rows . map do |row |
53+ country = Country . c_find_by_iso2 ( row [ :countryIso2 ] )
54+
55+ {
56+ name : row [ :name ] ,
57+ wca_id : row [ :wcaId ] ,
58+ country : country . id ,
59+ gender : row [ :gender ] ,
60+ birth_date : row [ :birthdate ] ,
61+ email : row [ :email ] ,
62+ event_ids : row . dig ( :registration , :eventIds ) || [ ] ,
63+ }
64+ end
65+ end
66+
67+ before_action :validate_and_parse_registration_data , only : %i[ validate_and_convert_registrations ]
68+ private def validate_and_parse_registration_data
69+ @competition = competition_from_params
4070 file = params . require ( :csv_registration_file )
4171
4272 @registration_rows = parse_csv_file ( file . path , @competition )
@@ -71,10 +101,23 @@ class RegistrationsController < ApplicationController
71101
72102 filtered_rows . map do |row |
73103 event_ids = competition . competition_events . filter_map do |competition_event |
74- competition_event . id if row [ competition_event . event_id . to_sym ] == "1"
104+ competition_event . event_id if row [ competition_event . event_id . to_sym ] == "1"
75105 end
76106
77- row . to_hash . merge ( event_ids : event_ids )
107+ {
108+ name : row [ :name ] ,
109+ wcaId : row [ :wca_id ] &.upcase ,
110+ countryIso2 : Country . c_find ( row [ :country ] ) . iso2 ,
111+ gender : row [ :gender ] ,
112+ birthdate : row [ :birth_date ] ,
113+ email : row [ :email ] &.downcase ,
114+ registration : {
115+ eventIds : event_ids ,
116+ status : "accepted" ,
117+ isCompeting : true ,
118+ registeredAt : Time . now . utc ,
119+ } ,
120+ }
78121 end
79122 end
80123
@@ -105,29 +148,40 @@ class RegistrationsController < ApplicationController
105148 end
106149 end
107150
108- dob_column_error = column_check ( csv_rows , :birth_date , 'wrong_dob_format' , :raw_dobs ) do |raw_dob |
109- Date . safe_parse ( raw_dob ) &.to_fs != raw_dob
110- end
111-
112- email_duplicate_error = column_check ( csv_rows , :email , 'email_duplicates' , :emails ) do |email , emails |
113- emails . count ( email ) > 1
114- end
115-
116- wca_id_duplicate_error = column_check ( csv_rows , :wca_id , "wca_id_duplicates" , :wca_ids ) do |wca_id , wca_ids |
117- wca_id . present? && wca_ids . count ( wca_id ) > 1
118- end
119-
120- [ event_column_errors , dob_column_error , email_duplicate_error , wca_id_duplicate_error ] . flatten
151+ [
152+ event_column_errors ,
153+ validate_dob_formats ( csv_rows . pluck ( :birth_date ) ) ,
154+ validate_no_duplicates ( csv_rows . pluck ( :email ) , 'email_duplicates' , :emails ) ,
155+ validate_no_duplicates ( csv_rows . pluck ( :wca_id ) . select ( &:present? ) , 'wca_id_duplicates' , :wca_ids ) ,
156+ ] . flatten . compact
121157 end
122158
123- private def column_check ( csv_rows , column_name , error_key , i18n_keyword )
124- column_values = csv_rows . pluck ( column_name )
159+ private def validate_registrations ( registration_rows , competition )
160+ # Validate country codes
161+ invalid_countries = registration_rows . filter_map { |e | e [ :countryIso2 ] } . uniq . reject { |iso2 | Country . c_find_by_iso2 ( iso2 ) }
162+
163+ # Validate event IDs against competition events
164+ valid_event_ids = competition . competition_events . pluck ( :event_id )
165+ all_event_ids = registration_rows . flat_map { |e | e . dig ( :registration , :eventIds ) || [ ] } . uniq
166+ invalid_event_ids = all_event_ids - valid_event_ids
167+
168+ [
169+ invalid_countries . any? && "Invalid country codes: #{ invalid_countries . join ( ', ' ) } " ,
170+ invalid_event_ids . any? && "Invalid event IDs for this competition: #{ invalid_event_ids . join ( ', ' ) } " ,
171+ validate_dob_formats ( registration_rows . filter_map { |e | e [ :birthdate ] } ) ,
172+ validate_no_duplicates ( registration_rows . filter_map { |e | e [ :email ] &.downcase } , 'email_duplicates' , :emails ) ,
173+ validate_no_duplicates ( registration_rows . filter_map { |e | e [ :wcaId ] &.upcase } , 'wca_id_duplicates' , :wca_ids ) ,
174+ ] . flatten . compact
175+ end
125176
126- malformed_values = column_values . select do |value |
127- yield value , column_values
128- end . uniq
177+ private def validate_dob_formats ( dobs )
178+ malformed = dobs . compact . select { |dob | Date . safe_parse ( dob ) &.to_fs != dob } . uniq
179+ I18n . t ( "registrations.import.errors.wrong_dob_format" , raw_dobs : malformed . join ( ", " ) ) if malformed . any?
180+ end
129181
130- I18n . t ( "registrations.import.errors.#{ error_key } " , i18n_keyword => malformed_values . join ( ", " ) ) if malformed_values . any?
182+ private def validate_no_duplicates ( values , error_key , i18n_keyword )
183+ duplicates = values . compact . select { |v | values . count ( v ) > 1 } . uniq
184+ I18n . t ( "registrations.import.errors.#{ error_key } " , i18n_keyword => duplicates . join ( ", " ) ) if duplicates . any?
131185 end
132186
133187 private def competitor_limit_error ( competition , competitor_count )
@@ -202,7 +256,8 @@ def do_import
202256 registration . assign_attributes ( competing_status : Registrations ::Helper ::STATUS_ACCEPTED ) unless registration . accepted?
203257 registration . registration_competition_events = [ ]
204258 registration_row [ :event_ids ] . each do |event_id |
205- registration . registration_competition_events . build ( competition_event_id : event_id )
259+ competition_event = @competition . competition_events . find { |ce | ce . event_id == event_id }
260+ registration . registration_competition_events . build ( competition_event_id : competition_event . id )
206261 end
207262 registration . save!
208263 registration . add_history_entry ( { event_ids : registration . event_ids } , "user" , current_user . id , "CSV Import" )
@@ -218,6 +273,10 @@ def do_import
218273 render status : :unprocessable_content , json : { error : e . to_s }
219274 end
220275
276+ def validate_and_convert_registrations
277+ render status : :ok , json : @registration_rows
278+ end
279+
221280 def add
222281 @competition = competition_from_params
223282 end
0 commit comments