Skip to content

Commit 62bb9ea

Browse files
committed
Add SchoolImportResultsController to show results in the UI.
This commit also adds code to new.html.erb to show validation errors if the uploaded CSV can't be enqueued as a batch.
1 parent 5e113f4 commit 62bb9ea

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# frozen_string_literal: true
2+
3+
require 'csv'
4+
5+
module Admin
6+
class SchoolImportResultsController < Admin::ApplicationController
7+
def index
8+
search_term = params[:search].to_s.strip
9+
resources = Administrate::Search.new(
10+
SchoolImportResult.all,
11+
dashboard_class,
12+
search_term
13+
).run
14+
resources = apply_collection_includes(resources)
15+
resources = order.apply(resources)
16+
resources = resources.page(params[:_page]).per(records_per_page)
17+
18+
# Batch load user info to avoid N+1 queries
19+
user_ids = resources.map(&:user_id).compact.uniq
20+
RequestStore.store[:user_info_cache] = fetch_users_batch(user_ids)
21+
22+
page = Administrate::Page::Collection.new(dashboard, order: order)
23+
24+
render locals: {
25+
resources: resources,
26+
search_term: search_term,
27+
page: page,
28+
show_search_bar: show_search_bar?
29+
}
30+
end
31+
32+
def show
33+
respond_to do |format|
34+
format.html do
35+
render locals: {
36+
page: Administrate::Page::Show.new(dashboard, requested_resource)
37+
}
38+
end
39+
format.csv do
40+
send_data generate_csv(requested_resource),
41+
filename: "school_import_#{requested_resource.job_id}_#{Date.current.strftime('%Y-%m-%d')}.csv",
42+
type: 'text/csv'
43+
end
44+
end
45+
end
46+
47+
def new
48+
@error_details = flash[:error_details]
49+
render locals: {
50+
page: Administrate::Page::Form.new(dashboard, SchoolImportResult.new)
51+
}
52+
end
53+
54+
def create
55+
if params[:csv_file].blank?
56+
flash[:error] = 'CSV file is required'
57+
redirect_to new_admin_school_import_result_path
58+
return
59+
end
60+
61+
# Call the same service that the API endpoint uses, ensuring all validation is applied
62+
result = School::ImportBatch.call(
63+
csv_file: params[:csv_file],
64+
current_user: current_user
65+
)
66+
67+
if result.success?
68+
flash[:notice] = "Import job started successfully. Job ID: #{result[:job_id]}"
69+
redirect_to admin_school_import_results_path
70+
else
71+
# Display error inline on the page
72+
flash.now[:error] = format_error_message(result[:error])
73+
@error_details = extract_error_details(result[:error])
74+
render :new, locals: {
75+
page: Administrate::Page::Form.new(dashboard, SchoolImportResult.new)
76+
}
77+
end
78+
end
79+
80+
private
81+
82+
def default_sorting_attribute
83+
:created_at
84+
end
85+
86+
def default_sorting_direction
87+
:desc
88+
end
89+
90+
def format_error_message(error)
91+
return error.to_s unless error.is_a?(Hash)
92+
93+
error[:message] || error['message'] || 'Import failed'
94+
end
95+
96+
def extract_error_details(error)
97+
return nil unless error.is_a?(Hash)
98+
99+
error[:details] || error['details']
100+
end
101+
102+
def generate_csv(import_result)
103+
104+
CSV.generate(headers: true) do |csv|
105+
# Header row
106+
csv << ['Status', 'School Name', 'School Code', 'School ID', 'Owner Email', 'Error Code', 'Error Message']
107+
108+
results = import_result.results
109+
successful = results['successful'] || []
110+
failed = results['failed'] || []
111+
112+
# Successful schools
113+
successful.each do |school|
114+
csv << [
115+
'Success',
116+
school['name'],
117+
school['code'],
118+
school['id'],
119+
school['owner_email'],
120+
'',
121+
''
122+
]
123+
end
124+
125+
# Failed schools
126+
failed.each do |school|
127+
csv << [
128+
'Failed',
129+
school['name'],
130+
'',
131+
'',
132+
school['owner_email'],
133+
school['error_code'],
134+
school['error']
135+
]
136+
end
137+
end
138+
end
139+
140+
def fetch_users_batch(user_ids)
141+
return {} if user_ids.empty?
142+
143+
users = UserInfoApiClient.fetch_by_ids(user_ids)
144+
users.each_with_object({}) do |user, hash|
145+
hash[user[:id]] = user
146+
end
147+
rescue StandardError => e
148+
Rails.logger.error("Failed to batch fetch user info: #{e.message}")
149+
{}
150+
end
151+
end
152+
end

app/views/admin/school_import_results/new.html.erb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,50 @@
1414
</header>
1515

1616
<section class="main-content__body">
17+
<% if flash[:error] %>
18+
<div class="flash flash-alert" style="margin-bottom: 2rem;">
19+
<%= flash[:error] %>
20+
21+
<% if @error_details %>
22+
<% if @error_details[:row_errors] %>
23+
<div style="margin-top: 1rem;">
24+
<strong>Row Errors:</strong>
25+
<table class="collection-data" style="margin-top: 0.5rem;">
26+
<thead>
27+
<tr>
28+
<th>Row</th>
29+
<th>Errors</th>
30+
</tr>
31+
</thead>
32+
<tbody>
33+
<% @error_details[:row_errors].each do |error| %>
34+
<tr>
35+
<td><%= error[:row] || error['row'] %></td>
36+
<td>
37+
<% errors = error[:errors] || error['errors'] || [] %>
38+
<% errors.each do |err| %>
39+
<div><strong><%= err[:field] || err['field'] %>:</strong> <%= err[:message] || err['message'] %></div>
40+
<% end %>
41+
</td>
42+
</tr>
43+
<% end %>
44+
</tbody>
45+
</table>
46+
</div>
47+
<% elsif @error_details[:duplicate_emails] %>
48+
<div style="margin-top: 1rem;">
49+
<strong>Duplicate Owner Emails Found:</strong>
50+
<ul>
51+
<% @error_details[:duplicate_emails].each do |email| %>
52+
<li><%= email %></li>
53+
<% end %>
54+
</ul>
55+
</div>
56+
<% end %>
57+
<% end %>
58+
</div>
59+
<% end %>
60+
1761
<div class="field-unit">
1862
<p>
1963
Use this form to import multiple schools from a CSV file.

0 commit comments

Comments
 (0)