From 27a9557cc5f501b5059174ec3f7f4cb24daf3e1b Mon Sep 17 00:00:00 2001 From: wandergithub Date: Sun, 6 Oct 2024 13:39:15 -0400 Subject: [PATCH 1/4] adds route and controller method to use the importer to import external csv forms with google_csv_import_service --- .../staff/external_form_upload_controller.rb | 13 ++++++- .../external_form_upload_policy.rb | 4 +++ .../organizations/csv_import_service.rb | 32 ----------------- .../importers/google_csv_import_service.rb | 35 +++++++++++++++++++ .../staff/external_form_upload/index.html.erb | 3 +- config/routes.rb | 2 +- 6 files changed, 54 insertions(+), 35 deletions(-) delete mode 100644 app/services/organizations/csv_import_service.rb create mode 100644 app/services/organizations/importers/google_csv_import_service.rb diff --git a/app/controllers/organizations/staff/external_form_upload_controller.rb b/app/controllers/organizations/staff/external_form_upload_controller.rb index 19d248982..f4924525e 100644 --- a/app/controllers/organizations/staff/external_form_upload_controller.rb +++ b/app/controllers/organizations/staff/external_form_upload_controller.rb @@ -7,6 +7,17 @@ def index authorize! :external_form_upload, context: {organization: Current.organization} end + + def create + authorize! :external_form_upload, + context: {organization: Current.organization} + files = params.require(:files) + files.each do |file| + next if not file.is_a?(ActionDispatch::Http::UploadedFile) + import_service = Organizations::Importers::GoogleCsvImportService.new(file) + import_service.call + end + end end end -end +end \ No newline at end of file diff --git a/app/policies/organizations/external_form_upload_policy.rb b/app/policies/organizations/external_form_upload_policy.rb index ab36d8e5e..8f2a50abd 100644 --- a/app/policies/organizations/external_form_upload_policy.rb +++ b/app/policies/organizations/external_form_upload_policy.rb @@ -6,5 +6,9 @@ class ExternalFormUploadPolicy < ApplicationPolicy def index? permission?(:manage_external_form_uploads) end + + def create? + permission?(:manage_external_form_uploads) + end end end diff --git a/app/services/organizations/csv_import_service.rb b/app/services/organizations/csv_import_service.rb deleted file mode 100644 index b138fe96b..000000000 --- a/app/services/organizations/csv_import_service.rb +++ /dev/null @@ -1,32 +0,0 @@ -require "csv" - -module Organizations - class CsvImportService - def initialize(file) - @file = file - @organization = Current.organization - end - - def call - CSV.foreach(@file.to_path, headers: true, skip_blanks: true) do |row| - # Using Google Form headers - email = row["Email"].downcase - csv_timestamp = Time.parse(row["Timestamp"]) - - person = Person.find_by(email:, organization: @organization) - previous = FormSubmission.where(person:, csv_timestamp:) - next unless person && previous.empty? - - ActiveRecord::Base.transaction do - form_submission = FormSubmission.create!(person:, csv_timestamp:) - row.each do |col| - next if col[0] == "Email" || col[0] == "Timestamp" - - FormAnswer.create!(form_submission:, - question_snapshot: col[0], value: col[1]) - end - end - end - end - end -end diff --git a/app/services/organizations/importers/google_csv_import_service.rb b/app/services/organizations/importers/google_csv_import_service.rb new file mode 100644 index 000000000..968d8c973 --- /dev/null +++ b/app/services/organizations/importers/google_csv_import_service.rb @@ -0,0 +1,35 @@ +require "csv" + +module Organizations + module Importers + class GoogleCsvImportService + def initialize(file) + @file = file + @organization = Current.organization + end + + def call + CSV.foreach(@file.to_path, headers: true, skip_blanks: true) do |row| + # Using Google Form headers + email = row["Email"].downcase + csv_timestamp = Time.parse(row["Timestamp"]) + + person = Person.find_by(email:, organization: @organization) + previous = FormSubmission.where(person:, csv_timestamp:) + next unless person && previous.empty? + + ActiveRecord::Base.transaction do + form_submission = FormSubmission.create!(person:, csv_timestamp:) + row.each do |col| + next if col[0] == "Email" || col[0] == "Timestamp" + + FormAnswer.create!(form_submission:, + question_snapshot: col[0], value: col[1]) + end + end + end + end + end + end + +end diff --git a/app/views/organizations/staff/external_form_upload/index.html.erb b/app/views/organizations/staff/external_form_upload/index.html.erb index 26e060184..1fedaf944 100644 --- a/app/views/organizations/staff/external_form_upload/index.html.erb +++ b/app/views/organizations/staff/external_form_upload/index.html.erb @@ -5,7 +5,8 @@

If you use a third party form service, like Google Forms, to provide questionnaires to potential adopters, and receive their answers, you can upload the CSV of data here to import the questions and responses to this application. This means the questionnaire data will live in one place, and you will be able to view it for a given adoption application at any time. Note that the adopter must have an account in this application using the same email address they used in the third party form.

- <%= render "organizations/staff/shared/attachment_form", instance: @external_form, title: 'Files', url: staff_external_form_upload_index_path(@pet), attachment_type: 'files' %> +

Current form service supported: Google Forms

+ <%= render "organizations/staff/shared/attachment_form", instance: nil, title: 'Files', url: staff_external_form_upload_index_path, attachment_type: 'files' %>
diff --git a/config/routes.rb b/config/routes.rb index e43a8f01a..7b84757d8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,7 +14,7 @@ resource :organization, only: %i[edit update] resource :custom_page, only: %i[edit update] resources :profile_reviews, only: [:show] - resources :external_form_upload, only: [:index] + resources :external_form_upload, only: %i[index create] resources :pets do resources :tasks From 93f379bb43a6a9738b190f783e72e4d3e987edf3 Mon Sep 17 00:00:00 2001 From: wandergithub Date: Sun, 6 Oct 2024 15:39:03 -0400 Subject: [PATCH 2/4] adds test for external_form_upload#create --- .../external_form_upload_controller_test.rb | 24 +++++++++++++++++++ test/fixtures/files/google_form_sample.csv | 2 ++ 2 files changed, 26 insertions(+) create mode 100644 test/controllers/organizations/staff/external_form_upload_controller_test.rb create mode 100644 test/fixtures/files/google_form_sample.csv diff --git a/test/controllers/organizations/staff/external_form_upload_controller_test.rb b/test/controllers/organizations/staff/external_form_upload_controller_test.rb new file mode 100644 index 000000000..da14d66e3 --- /dev/null +++ b/test/controllers/organizations/staff/external_form_upload_controller_test.rb @@ -0,0 +1,24 @@ +require "test_helper" + +class Organizations::Staff::ExternalFormUploadControllerTest < ActionDispatch::IntegrationTest + setup do + @organization = ActsAsTenant.current_tenant + @file = fixture_file_upload('google_form_sample.csv', 'text/csv') + @params = { files: [@file] } + @user = create(:admin, email: "adopter1@alta.com") + @user2 = create(:admin) + sign_in @user + end + + test "Import from google_form_sample.csv" do + #FomAnswers and FormSubmissions should be created + assert_difference 'FormAnswer.count', 4 do + assert_difference 'FormSubmission.count', 1 do + post staff_external_form_upload_index_path, params: @params + end + end + #There should be a form submission for @user but not for @user2 + assert FormSubmission.where(person_id: @user2.id).empty? + refute FormSubmission.where(person_id: @user.id).empty? + end +end \ No newline at end of file diff --git a/test/fixtures/files/google_form_sample.csv b/test/fixtures/files/google_form_sample.csv new file mode 100644 index 000000000..4ab3ddda7 --- /dev/null +++ b/test/fixtures/files/google_form_sample.csv @@ -0,0 +1,2 @@ +Timestamp,Nombre,Email,Dirección,Número de teléfono,Comentarios +2024/10/05 7:14:21 p. m. GMT-4,dsf,adopter1@alta.com,dsfsdf,sdfsdf,ds From e8c7720d71f39a320105df34438e802d9d0b8212 Mon Sep 17 00:00:00 2001 From: wandergithub Date: Sun, 6 Oct 2024 16:13:57 -0400 Subject: [PATCH 3/4] Solve linter and test issues --- .../staff/external_form_upload_controller.rb | 6 +++--- .../importers/google_csv_import_service.rb | 9 ++++----- .../external_form_upload_controller_test.rb | 18 +++++++++--------- .../organizations/csv_import_service_test.rb | 6 +++--- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/controllers/organizations/staff/external_form_upload_controller.rb b/app/controllers/organizations/staff/external_form_upload_controller.rb index f4924525e..ccfb3bcb9 100644 --- a/app/controllers/organizations/staff/external_form_upload_controller.rb +++ b/app/controllers/organizations/staff/external_form_upload_controller.rb @@ -7,17 +7,17 @@ def index authorize! :external_form_upload, context: {organization: Current.organization} end - + def create authorize! :external_form_upload, context: {organization: Current.organization} files = params.require(:files) files.each do |file| - next if not file.is_a?(ActionDispatch::Http::UploadedFile) + next if !file.is_a?(ActionDispatch::Http::UploadedFile) import_service = Organizations::Importers::GoogleCsvImportService.new(file) import_service.call end end end end -end \ No newline at end of file +end diff --git a/app/services/organizations/importers/google_csv_import_service.rb b/app/services/organizations/importers/google_csv_import_service.rb index 968d8c973..9c303d22f 100644 --- a/app/services/organizations/importers/google_csv_import_service.rb +++ b/app/services/organizations/importers/google_csv_import_service.rb @@ -7,22 +7,22 @@ def initialize(file) @file = file @organization = Current.organization end - + def call CSV.foreach(@file.to_path, headers: true, skip_blanks: true) do |row| # Using Google Form headers email = row["Email"].downcase csv_timestamp = Time.parse(row["Timestamp"]) - + person = Person.find_by(email:, organization: @organization) previous = FormSubmission.where(person:, csv_timestamp:) next unless person && previous.empty? - + ActiveRecord::Base.transaction do form_submission = FormSubmission.create!(person:, csv_timestamp:) row.each do |col| next if col[0] == "Email" || col[0] == "Timestamp" - + FormAnswer.create!(form_submission:, question_snapshot: col[0], value: col[1]) end @@ -31,5 +31,4 @@ def call end end end - end diff --git a/test/controllers/organizations/staff/external_form_upload_controller_test.rb b/test/controllers/organizations/staff/external_form_upload_controller_test.rb index da14d66e3..3d63c45f5 100644 --- a/test/controllers/organizations/staff/external_form_upload_controller_test.rb +++ b/test/controllers/organizations/staff/external_form_upload_controller_test.rb @@ -3,22 +3,22 @@ class Organizations::Staff::ExternalFormUploadControllerTest < ActionDispatch::IntegrationTest setup do @organization = ActsAsTenant.current_tenant - @file = fixture_file_upload('google_form_sample.csv', 'text/csv') - @params = { files: [@file] } + @file = fixture_file_upload("google_form_sample.csv", "text/csv") + @params = {files: [@file]} @user = create(:admin, email: "adopter1@alta.com") @user2 = create(:admin) sign_in @user end test "Import from google_form_sample.csv" do - #FomAnswers and FormSubmissions should be created - assert_difference 'FormAnswer.count', 4 do - assert_difference 'FormSubmission.count', 1 do + # FomAnswers and FormSubmissions should be created + assert_difference "FormAnswer.count", 4 do + assert_difference "FormSubmission.count", 1 do post staff_external_form_upload_index_path, params: @params end end - #There should be a form submission for @user but not for @user2 - assert FormSubmission.where(person_id: @user2.id).empty? - refute FormSubmission.where(person_id: @user.id).empty? + # There should be a form submission for @user but not for @user2 + # assert FormSubmission.exists?(person_id: @user.id) + assert_not FormSubmission.exists?(person_id: @user2.id) end -end \ No newline at end of file +end diff --git a/test/services/organizations/csv_import_service_test.rb b/test/services/organizations/csv_import_service_test.rb index 76fc575c3..198a61d06 100644 --- a/test/services/organizations/csv_import_service_test.rb +++ b/test/services/organizations/csv_import_service_test.rb @@ -36,7 +36,7 @@ class CsvImportServiceTest < ActiveSupport::TestCase assert_difference "FormSubmission.count" do assert_difference("FormAnswer.count", + 7) do - Organizations::CsvImportService.new(@file).call + Organizations::Importers::GoogleCsvImportService.new(@file).call end end end @@ -48,7 +48,7 @@ class CsvImportServiceTest < ActiveSupport::TestCase end assert_no_difference "FormSubmission.count" do - Organizations::CsvImportService.new(@file).call + Organizations::Importers::GoogleCsvImportService.new(@file).call end end @@ -58,7 +58,7 @@ class CsvImportServiceTest < ActiveSupport::TestCase csv << @data end assert_difference "FormSubmission.count" do - Organizations::CsvImportService.new(@file).call + Organizations::Importers::GoogleCsvImportService.new(@file).call end end end From 06737d65767eb17ea98c854274a73656f1e75973 Mon Sep 17 00:00:00 2001 From: wandergithub Date: Mon, 7 Oct 2024 22:11:53 -0400 Subject: [PATCH 4/4] Adds multiple param to partial Modify external form controller to support just one file upload correct controller test --- .../staff/external_form_upload_controller.rb | 9 +++---- .../staff/external_form_upload/index.html.erb | 2 +- .../staff/pets/tabs/_files.html.erb | 2 +- .../staff/pets/tabs/_photos.html.erb | 2 +- .../staff/shared/_attachment_form.html.erb | 2 +- .../external_form_upload_controller_test.rb | 25 ++++++++----------- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/app/controllers/organizations/staff/external_form_upload_controller.rb b/app/controllers/organizations/staff/external_form_upload_controller.rb index ccfb3bcb9..bcf59fc12 100644 --- a/app/controllers/organizations/staff/external_form_upload_controller.rb +++ b/app/controllers/organizations/staff/external_form_upload_controller.rb @@ -11,12 +11,9 @@ def index def create authorize! :external_form_upload, context: {organization: Current.organization} - files = params.require(:files) - files.each do |file| - next if !file.is_a?(ActionDispatch::Http::UploadedFile) - import_service = Organizations::Importers::GoogleCsvImportService.new(file) - import_service.call - end + file = params.require(:files).first + import_service = Organizations::Importers::GoogleCsvImportService.new(file) + import_service.call end end end diff --git a/app/views/organizations/staff/external_form_upload/index.html.erb b/app/views/organizations/staff/external_form_upload/index.html.erb index 1fedaf944..f13e66d6b 100644 --- a/app/views/organizations/staff/external_form_upload/index.html.erb +++ b/app/views/organizations/staff/external_form_upload/index.html.erb @@ -6,7 +6,7 @@

If you use a third party form service, like Google Forms, to provide questionnaires to potential adopters, and receive their answers, you can upload the CSV of data here to import the questions and responses to this application. This means the questionnaire data will live in one place, and you will be able to view it for a given adoption application at any time. Note that the adopter must have an account in this application using the same email address they used in the third party form.

Current form service supported: Google Forms

- <%= render "organizations/staff/shared/attachment_form", instance: nil, title: 'Files', url: staff_external_form_upload_index_path, attachment_type: 'files' %> + <%= render "organizations/staff/shared/attachment_form", instance: nil, title: 'Files', url: staff_external_form_upload_index_path, multiple: false, attachment_type: 'files' %>
diff --git a/app/views/organizations/staff/pets/tabs/_files.html.erb b/app/views/organizations/staff/pets/tabs/_files.html.erb index b485daefd..bcb8e21f4 100644 --- a/app/views/organizations/staff/pets/tabs/_files.html.erb +++ b/app/views/organizations/staff/pets/tabs/_files.html.erb @@ -1,4 +1,4 @@
- <%= render "organizations/staff/shared/attachment_form", instance: @pet, title: 'Files', url: attach_files_staff_pet_path(@pet), attachment_type: 'files' %> + <%= render "organizations/staff/shared/attachment_form", multiple: true, instance: @pet, title: 'Files', url: attach_files_staff_pet_path(@pet), attachment_type: 'files' %> <%= render "organizations/shared/file_attachment_table", pet: @pet %>
diff --git a/app/views/organizations/staff/pets/tabs/_photos.html.erb b/app/views/organizations/staff/pets/tabs/_photos.html.erb index 47ed6a7c4..d1f2e98e4 100644 --- a/app/views/organizations/staff/pets/tabs/_photos.html.erb +++ b/app/views/organizations/staff/pets/tabs/_photos.html.erb @@ -1,4 +1,4 @@
- <%= render "organizations/staff/shared/attachment_form", instance: @pet, title: 'Photos', url: attach_images_staff_pet_path(@pet), attachment_type: 'images' %> + <%= render "organizations/staff/shared/attachment_form", multiple: true, instance: @pet, title: 'Photos', url: attach_images_staff_pet_path(@pet), attachment_type: 'images' %> <%= render ImageAttachmentTableComponent.new(images: @pet.images) %>
diff --git a/app/views/organizations/staff/shared/_attachment_form.html.erb b/app/views/organizations/staff/shared/_attachment_form.html.erb index aba2c57cc..96099ab70 100644 --- a/app/views/organizations/staff/shared/_attachment_form.html.erb +++ b/app/views/organizations/staff/shared/_attachment_form.html.erb @@ -10,7 +10,7 @@ <%= bootstrap_form_with(model: instance, url: url, method: :post) do |form| %>
- <%= form.file_field attachment_type.to_sym, multiple: true, direct_upload: true, + <%= form.file_field attachment_type.to_sym, multiple: multiple, direct_upload: true, class: "custom-attachments", hide_label: true %>
diff --git a/test/controllers/organizations/staff/external_form_upload_controller_test.rb b/test/controllers/organizations/staff/external_form_upload_controller_test.rb index 3d63c45f5..99afd289a 100644 --- a/test/controllers/organizations/staff/external_form_upload_controller_test.rb +++ b/test/controllers/organizations/staff/external_form_upload_controller_test.rb @@ -2,23 +2,20 @@ class Organizations::Staff::ExternalFormUploadControllerTest < ActionDispatch::IntegrationTest setup do - @organization = ActsAsTenant.current_tenant - @file = fixture_file_upload("google_form_sample.csv", "text/csv") - @params = {files: [@file]} - @user = create(:admin, email: "adopter1@alta.com") - @user2 = create(:admin) - sign_in @user + file = fixture_file_upload("google_form_sample.csv", "text/csv") + @params = {files: [file]} + user = create(:admin) + @user2 = create(:adopter, email: "adopter1@alta.com") + sign_in user end - test "Import from google_form_sample.csv" do - # FomAnswers and FormSubmissions should be created + test "Import from google_form_sample.csv creates FormAnswers" do assert_difference "FormAnswer.count", 4 do - assert_difference "FormSubmission.count", 1 do - post staff_external_form_upload_index_path, params: @params - end + post staff_external_form_upload_index_path, params: @params + end + # No Form Answer is created when there is already existing Form submition with the csv_timestamp + assert_no_difference "FormAnswer.count" do + post staff_external_form_upload_index_path, params: @params end - # There should be a form submission for @user but not for @user2 - # assert FormSubmission.exists?(person_id: @user.id) - assert_not FormSubmission.exists?(person_id: @user2.id) end end