+
+ <%= content_for(:title) %>
+
+
+
+ <%= link_to(
+ "New Import",
+ new_admin_school_import_result_path,
+ class: "button button--primary",
+ ) %>
+
+
+
+
+
+ <%= content_for(:title) %>
+
+
+ <%= link_to(
+ "View Import History",
+ admin_school_import_results_path,
+ class: "button"
+ ) %>
+
+
+
+
+ <% if flash[:error] %>
+
+ <%= flash[:error] %>
+
+ <% if @error_details %>
+ <% if @error_details[:row_errors] %>
+
+
Row Errors:
+
+
+
+ | Row |
+ Errors |
+
+
+
+ <% @error_details[:row_errors].each do |error| %>
+
+ | <%= error[:row] || error['row'] %> |
+
+ <% errors = error[:errors] || error['errors'] || [] %>
+ <% errors.each do |err| %>
+ <%= err[:field] || err['field'] %>: <%= err[:message] || err['message'] %>
+ <% end %>
+ |
+
+ <% end %>
+
+
+
+ <% elsif @error_details[:duplicate_emails] %>
+
+
Duplicate Owner Emails Found:
+
+ <% @error_details[:duplicate_emails].each do |email| %>
+ - <%= email %>
+ <% end %>
+
+
+ <% end %>
+ <% end %>
+
+ <% end %>
+
+
+
+ Use this form to import multiple schools from a CSV file.
+ The import will run in the background and you can track its progress on the Import History page.
+
+
+
+
+
+ <%= form_with url: admin_school_import_results_path, method: :post, multipart: true, class: "form" do |f| %>
+
+ <%= f.label :csv_file, "CSV File", class: "field-unit__label" %>
+ <%= f.file_field :csv_file, accept: ".csv", required: true, class: "field-unit__field" %>
+
+ Select a CSV file containing school data to import.
+ Download the <%= link_to "CSV template", "/docs/import/school_import_template.csv" %> to see the required format.
+
+
+
+
+ <%= f.submit "Upload and Start Import", class: "button button--primary" %>
+
+ <% end %>
+
+
+
+
+
CSV Format Requirements
+
+
+
+ | Column |
+ Required |
+ Description |
+
+
+
+
+ name |
+ Yes |
+ School name |
+
+
+ website |
+ Yes |
+ School website URL (must include https://) |
+
+
+ address_line_1 |
+ Yes |
+ Street address |
+
+
+ municipality |
+ Yes |
+ City/town |
+
+
+ country_code |
+ Yes |
+ 2-letter ISO country code (e.g., US, GB, CA) |
+
+
+ owner_email |
+ Yes |
+ Email of school owner (must have existing RPF account) |
+
+
+ address_line_2 |
+ No |
+ Additional address information |
+
+
+ administrative_area |
+ No |
+ State/province |
+
+
+ postal_code |
+ No |
+ ZIP/postal code |
+
+
+ reference |
+ No |
+ School reference number (must be unique if provided) |
+
+
+
+
+
+
+
Important Notes
+
+
+
+ - All owner emails must correspond to existing Code Editor for Education accounts.
+ - Each owner can only create one school.
+ - The same owner email cannot appear multiple times in the CSV.
+ - Imported schools are automatically verified and receive school codes.
+ - Owner roles are automatically created for each school.
+ - The import runs in the background and may take several minutes for large files.
+
+
+
diff --git a/app/views/admin/school_import_results/show.html.erb b/app/views/admin/school_import_results/show.html.erb
new file mode 100644
index 000000000..cfe872a17
--- /dev/null
+++ b/app/views/admin/school_import_results/show.html.erb
@@ -0,0 +1,166 @@
+<% content_for(:title) { "School Import Job #{page.resource.job_id}" } %>
+
+
+
+ <%= content_for(:title) %>
+
+
+ <%= link_to(
+ "Download CSV",
+ admin_school_import_result_path(page.resource, format: :csv),
+ class: "button button--primary",
+ style: "margin-right: 10px;"
+ ) %>
+ <%= link_to(
+ "Back to Import History",
+ admin_school_import_results_path,
+ class: "button"
+ ) %>
+
+
+
+
+
+ - Job ID
+ - <%= page.resource.job_id %>
+
+ - User
+ -
+ <% user_field = UserInfoField.new(:user_id, page.resource.user_id, "show") %>
+ <%= user_field.user_display %>
+
+
+ - Status
+ -
+ <% status_field = StatusField.new(:job_id, page.resource.job_id, "show") %>
+
+ <%= status_field.job_status.upcase %>
+
+
+
+ - Created At
+ - <%= page.resource.created_at.strftime("%B %d, %Y at %I:%M %p") %>
+
+ <% if page.resource.updated_at != page.resource.created_at %>
+ - Updated At
+ - <%= page.resource.updated_at.strftime("%B %d, %Y at %I:%M %p") %>
+ <% end %>
+
+
+ <% results = page.resource.results %>
+ <% if results.present? %>
+
+ - Results
+ -
+ <% results_field = ResultsSummaryField.new(:results, page.resource.results, "show") %>
+ <%= render "fields/results_summary_field/show", field: results_field %>
+
+
+
+ <% successful = results['successful'] || [] %>
+ <% failed = results['failed'] || [] %>
+
+ <% if successful.any? %>
+ Successful Imports (<%= successful.count %>)
+
+
+
+ | School Name |
+ School Code |
+ School ID |
+ Owner Email |
+ Actions |
+
+
+
+ <% successful.each do |school| %>
+
+ | <%= school['name'] %> |
+ <%= school['code'] %> |
+ <%= school['id'] %> |
+ <%= school['owner_email'] %> |
+
+ <%= link_to "View School", admin_school_path(school['id']), class: "button button--small" if school['id'] %>
+ |
+
+ <% end %>
+
+
+ <% end %>
+
+ <% if failed.any? %>
+ Failed Imports (<%= failed.count %>)
+
+
+
+ | School Name |
+ Owner Email |
+ Error Code |
+ Error Message |
+
+
+
+ <% failed.each do |school| %>
+
+ | <%= school['name'] %> |
+ <%= school['owner_email'] %> |
+ <%= school['error_code'] %> |
+ <%= school['error'] %> |
+
+ <% end %>
+
+
+
+
+
Common Error Codes
+
+ OWNER_NOT_FOUND
+ - The owner email doesn't correspond to an existing account. The owner needs to create a Code Editor for Education account first.
+
+ OWNER_ALREADY_CREATOR
+ - This owner has already created a school. Each person can only create one school.
+
+ SCHOOL_VALIDATION_FAILED
+ - The school data failed validation. Check the error message for details about which fields are invalid.
+
+
+ <% end %>
+ <% else %>
+
+
No results available yet. The job may still be running or may have failed to store results.
+
+ <% end %>
+
+
+
diff --git a/app/views/api/school_import_jobs/show.json.jbuilder b/app/views/api/school_import_jobs/show.json.jbuilder
new file mode 100644
index 000000000..b675ed52b
--- /dev/null
+++ b/app/views/api/school_import_jobs/show.json.jbuilder
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+json.id @job.active_job_id
+json.status @status
+json.created_at @job.created_at
+json.finished_at @job.finished_at
+json.job_class @job.job_class
+
+if @result.present?
+ json.results @result.results
+elsif @status == 'succeeded'
+ json.message 'Job completed successfully'
+end
+
+json.error @job.error if @job.error.present?
diff --git a/app/views/api/schools/import.json.jbuilder b/app/views/api/schools/import.json.jbuilder
new file mode 100644
index 000000000..b56dc59d5
--- /dev/null
+++ b/app/views/api/schools/import.json.jbuilder
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+json.job_id @job_id
+json.total_schools @total_schools
+json.message 'Import job started successfully'
diff --git a/app/views/fields/results_summary_field/_index.html.erb b/app/views/fields/results_summary_field/_index.html.erb
new file mode 100644
index 000000000..563ba43e6
--- /dev/null
+++ b/app/views/fields/results_summary_field/_index.html.erb
@@ -0,0 +1,6 @@
+<%#
+Administrate field partial for ResultsSummaryField in index view
+%>
+