From fd07e57087d4e8c0ec49d2d60e18ca9d9d354afb Mon Sep 17 00:00:00 2001 From: David Cliff Date: Tue, 1 Oct 2024 13:50:34 +0000 Subject: [PATCH 01/11] Patch bump --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index dbe590065..1817afea4 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.1 +2.8.2 From 789fbdf34d0047c81765f1e0b270ce847c8fdb3a Mon Sep 17 00:00:00 2001 From: David Cliff Date: Tue, 1 Oct 2024 14:01:09 +0000 Subject: [PATCH 02/11] Add multi-file xlsx zip, renamed existing to singular, and added pids to spec stub --- spec/controllers/loads_controller_spec.rb | 8 ++++++++ spec/fixtures/files/.version | 1 + .../fixtures/files/metadata_existing_file.zip | Bin 0 -> 18858 bytes .../files/metadata_existing_files.zip | Bin 18858 -> 14441 bytes 4 files changed, 9 insertions(+) create mode 100644 spec/fixtures/files/.version create mode 100644 spec/fixtures/files/metadata_existing_file.zip diff --git a/spec/controllers/loads_controller_spec.rb b/spec/controllers/loads_controller_spec.rb index 5b0554ba6..9c41792dd 100644 --- a/spec/controllers/loads_controller_spec.rb +++ b/spec/controllers/loads_controller_spec.rb @@ -13,6 +13,14 @@ # and patch them in to test objects for the xml update testing AtlasRb::Community.metadata(work['id'], { 'noid' => '123' }) expect(AtlasRb::Work.find('123')).to be_present + + # PIDs in metadata_existing_files.zip + + # 8j2RtvbFW + # 2fCuGC0E5 + # 4GaFRGnrr + # hHtP31089 + # 089wXVbXf end end end diff --git a/spec/fixtures/files/.version b/spec/fixtures/files/.version new file mode 100644 index 000000000..9f8d8a916 --- /dev/null +++ b/spec/fixtures/files/.version @@ -0,0 +1 @@ +2.8.3 diff --git a/spec/fixtures/files/metadata_existing_file.zip b/spec/fixtures/files/metadata_existing_file.zip new file mode 100644 index 0000000000000000000000000000000000000000..07fe50b545321ea87fc19efe805a6d3d241ee39d GIT binary patch literal 18858 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 RA%4GWe*eBXEPs6ZKLEL(=KBBu literal 18858 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 b--@O5wf(KwF1bHClHSpO?!2!Y{_y? Date: Thu, 3 Oct 2024 11:25:57 -0400 Subject: [PATCH 03/11] Added load_report, modified load_controller and ingests to accommodate. Issue with rspec testing in loads_controller_spec.rb --- app/controllers/loads_controller.rb | 51 ++++-- app/models/ingest.rb | 7 +- app/models/load_report.rb | 26 +++ app/views/loads/index.html.erb | 170 ++++++++++++++++---- db/migrate/20240926172306_create_ingests.rb | 1 + spec/controllers/loads_controller_spec.rb | 18 +-- 6 files changed, 217 insertions(+), 56 deletions(-) create mode 100644 app/models/load_report.rb diff --git a/app/controllers/loads_controller.rb b/app/controllers/loads_controller.rb index 193209bfc..8fc061651 100644 --- a/app/controllers/loads_controller.rb +++ b/app/controllers/loads_controller.rb @@ -3,9 +3,13 @@ require 'zip' require 'roo' +# There needs to be work done on ingests and whether or not they go into load_report +# as of right now as long as the files seem valid they make it into ingest and then attempt to load +# but that makes the success rate of load_report 100% pretty much always (not always correct). +# Also with some things it will look like it failed but all valid jobs haven't class LoadsController < ApplicationController def index - @ingests = Ingest.order(created_at: :desc) + @load_reports = LoadReport.order(created_at: :desc).includes(:ingests) end def create @@ -25,21 +29,36 @@ def create private def process_zip(zip) - Zip::File.open(zip) do |zip_file| - manifest_file = zip_file.find_entry('manifest.xlsx') - if manifest_file - process_spreadsheet(manifest_file, zip_file) - else - redirect_to loads_path, alert: 'Manifest file not found in ZIP.' + failures = [] + load_report = nil + + begin + Zip::File.open(zip) do |zip_file| + manifest_file = zip_file.find_entry('manifest.xlsx') + if manifest_file + load_report = process_spreadsheet(manifest_file, zip_file, failures) + else + failures << 'Manifest file not found in ZIP.' + end end + rescue Zip::Error => e + failures << "Error processing ZIP file: #{e.message}" + end + + if failures.empty? + load_report&.finish_load + redirect_to loads_path, notice: 'ZIP file processed successfully.' + else + load_report&.fail_load + redirect_to loads_path, alert: "Errors occurred during processing: #{failures.join(', ')}" end - rescue Zip::Error => e - redirect_to loads_path, alert: "Error processing ZIP file: #{e.message}" end - def process_spreadsheet(xlsx_file, zip_file) + def process_spreadsheet(xlsx_file, zip_file, failures) spreadsheet_content = xlsx_file.get_input_stream.read spreadsheet = Roo::Spreadsheet.open(StringIO.new(spreadsheet_content), extension: :xlsx) + load_report = LoadReport.create!(status: :in_progress) + load_report.start_load spreadsheet.each_with_index do |row, index| next if index.zero? @@ -48,16 +67,20 @@ def process_spreadsheet(xlsx_file, zip_file) if pid && file_name xml_entry = zip_file.find_entry(file_name) if xml_entry - ingest = Ingest.create_from_spreadsheet_row(row) + ingest = Ingest.create_from_spreadsheet_row(row, load_report.id) xml_content = xml_entry.get_input_stream.read UpdateMetadataJob.perform_later(pid, xml_content, ingest.id) else - redirect_to loads_path, alert: "#{file_name} file not found in ZIP: "; return + failures << "#{file_name} file not found in ZIP" end + else + failures << "Missing PID or filename in row #{index + 1}" end end - redirect_to loads_path, notice: 'ZIP file processed successfully.' + + load_report rescue StandardError => e - redirect_to loads_path, alert: "Error processing spreadsheet: #{e.message}" + failures << "Error processing spreadsheet: #{e.message}" + load_report end end diff --git a/app/models/ingest.rb b/app/models/ingest.rb index 3919cc5d7..6eddaa5f4 100644 --- a/app/models/ingest.rb +++ b/app/models/ingest.rb @@ -1,17 +1,20 @@ # frozen_string_literal: true class Ingest < ApplicationRecord + belongs_to :load_report + enum status: { pending: 0, completed: 1, failed: 2 } validates :pid, presence: true validates :xml_filename, presence: true validates :status, presence: true - def self.create_from_spreadsheet_row(row) + def self.create_from_spreadsheet_row(row, load_report_id) create!( pid: row[0], xml_filename: row[1], - status: :pending + status: :pending, + load_report: LoadReport.find(load_report_id), ) end end diff --git a/app/models/load_report.rb b/app/models/load_report.rb new file mode 100644 index 000000000..f5de41c95 --- /dev/null +++ b/app/models/load_report.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class LoadReport < ApplicationRecord + has_many :ingests + + enum status: { in_progress: 0, completed: 1, failed: 2 } + + validates :status, presence: true + + def start_load + update(status: :in_progress, started_at: Time.now) + end + + def finish_load + update(status: :completed, finished_at: Time.now) + end + + def fail_load + update(status: :failed, finished_at: Time.now) + end + + def success_rate + return 0 if ingests.empty? + ((ingests.completed.count.to_f / ingests.count.to_f) * 100).round(2) + end +end diff --git a/app/views/loads/index.html.erb b/app/views/loads/index.html.erb index 7fcb06247..eda6bc27f 100644 --- a/app/views/loads/index.html.erb +++ b/app/views/loads/index.html.erb @@ -1,35 +1,143 @@ -

Ingest Management

+ + Ingest Management + + + +
+

Ingest Management

+
-

Upload ZIP File

+
+
+

Upload ZIP File

+ <%= form_tag loads_path, multipart: true do %> +
+ <%= file_field_tag :file, accept: 'application/zip,application/x-zip-compressed' %> +
+
+ <%= submit_tag "Upload and Process" %> +
+ <% end %> +
-<%= form_tag loads_path, multipart: true do %> -
- <%= file_field_tag :file, accept: 'application/zip,application/x-zip-compressed' %> -
-
- <%= submit_tag "Upload and Process" %> -
-<% end %> +
+

Load Reports

+ + + + + + + + + + + <% @load_reports.each do |load_report| %> + + + + + + + + + + <% end %> + +
StatusStarted AtFinished AtSuccess Rate
<%= load_report.status %><%= load_report.started_at %><%= load_report.finished_at %><%= load_report.success_rate %>%
+ + + + + + + + + + + <% load_report.ingests.each do |ingest| %> + + + + + + + <% end %> + +
PIDXML FilenameStatusCreated At
<%= ingest.pid %><%= ingest.xml_filename %><%= ingest.status %><%= ingest.created_at %>
+
+
+
-

Ingests

+ + - - - - - - - - - - - <% @ingests.each do |ingest| %> - - - - - - - <% end %> - -
PIDXML FilenameStatusCreated At
<%= ingest.pid %><%= ingest.xml_filename %><%= ingest.status %><%= ingest.created_at %>
diff --git a/db/migrate/20240926172306_create_ingests.rb b/db/migrate/20240926172306_create_ingests.rb index c0f275124..3096989ef 100644 --- a/db/migrate/20240926172306_create_ingests.rb +++ b/db/migrate/20240926172306_create_ingests.rb @@ -4,6 +4,7 @@ def change t.string :pid, null: false t.string :xml_filename, null: false t.integer :status, null: false, default: 0 + t.references :load_report, foreign_key: true, null: false t.timestamps end diff --git a/spec/controllers/loads_controller_spec.rb b/spec/controllers/loads_controller_spec.rb index 9c41792dd..7f40fa783 100644 --- a/spec/controllers/loads_controller_spec.rb +++ b/spec/controllers/loads_controller_spec.rb @@ -6,21 +6,21 @@ let(:community) { AtlasRb::Community.create(nil, '/home/cerberus/web/spec/fixtures/files/community-mods.xml') } let(:collection) { AtlasRb::Collection.create(community['id'], '/home/cerberus/web/spec/fixtures/files/collection-mods.xml') } let(:work) { AtlasRb::Work.create(collection['id'], '/home/cerberus/web/spec/fixtures/files/work-mods.xml') } + let(:zip) { fixture_file_upload('/home/cerberus/web/spec/fixtures/files/metadata_existing_files.zip', 'application/zip') } describe 'noid test' do it 'lets spec set the noid' do - # not for keeping, just iterating - this lets us hard code pids in the xml zip fixture file - # and patch them in to test objects for the xml update testing AtlasRb::Community.metadata(work['id'], { 'noid' => '123' }) expect(AtlasRb::Work.find('123')).to be_present + end + end - # PIDs in metadata_existing_files.zip - - # 8j2RtvbFW - # 2fCuGC0E5 - # 4GaFRGnrr - # hHtP31089 - # 089wXVbXf + describe 'create popups' do + it 'processes the zip file successfully' do + post :create, params: { file: zip } + expect(response).to redirect_to(loads_path) + # expect(flash[:notice]).to eq("ZIP file processed successfully.") + expect(flash[:alert]).to be_nil end end end From 7592bb4d49f7594338000e18b431bbecb7a47704 Mon Sep 17 00:00:00 2001 From: George Lawton Date: Thu, 3 Oct 2024 11:46:46 -0400 Subject: [PATCH 04/11] Added load report migration --- db/migrate/20240926172305_create_load_reports.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 db/migrate/20240926172305_create_load_reports.rb diff --git a/db/migrate/20240926172305_create_load_reports.rb b/db/migrate/20240926172305_create_load_reports.rb new file mode 100644 index 000000000..230df48c2 --- /dev/null +++ b/db/migrate/20240926172305_create_load_reports.rb @@ -0,0 +1,11 @@ +class CreateLoadReports < ActiveRecord::Migration[7.2] + def change + create_table :load_reports do |t| + t.integer :status, null: false, default: 0 + t.datetime :started_at + t.datetime :finished_at + + t.timestamps + end + end +end From 0ce7e62b043b2f887bad83d9bb7d646649a50671 Mon Sep 17 00:00:00 2001 From: David Cliff Date: Thu, 3 Oct 2024 19:58:57 +0000 Subject: [PATCH 05/11] Update atlas and atlas rb to have a reset function, fix broken rails helper for rspec (db cleaner invoked incorrectly), and remove trailing comma --- .atlas | 2 +- .version | 2 +- Gemfile.lock | 6 +++++- app/models/ingest.rb | 2 +- spec/rails_helper.rb | 7 +------ 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.atlas b/.atlas index caf3c7c74..9014c1825 100644 --- a/.atlas +++ b/.atlas @@ -1 +1 @@ -0.0.156 +0.0.157 diff --git a/.version b/.version index 1817afea4..9f8d8a916 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.2 +2.8.3 diff --git a/Gemfile.lock b/Gemfile.lock index 9b9c46c14..0e2f15a13 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -85,7 +85,7 @@ GEM addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) ast (2.4.2) - atlas_rb (0.0.61) + atlas_rb (0.0.64) faraday (~> 2.7) faraday-follow_redirects (~> 0.3.0) faraday-multipart (~> 1) @@ -341,6 +341,9 @@ GEM actionpack (>= 5.2) railties (>= 5.2) rexml (3.3.7) + roo (2.10.1) + nokogiri (~> 1) + rubyzip (>= 1.3.0, < 3.0.0) rsolr (2.6.0) builder (>= 2.1.2) faraday (>= 0.9, < 3, != 2.0.0) @@ -469,6 +472,7 @@ DEPENDENCIES puma rails rails-controller-testing + roo rsolr (>= 1.0, < 3) rspec rspec-rails diff --git a/app/models/ingest.rb b/app/models/ingest.rb index 6eddaa5f4..fe3af5cc6 100644 --- a/app/models/ingest.rb +++ b/app/models/ingest.rb @@ -14,7 +14,7 @@ def self.create_from_spreadsheet_row(row, load_report_id) pid: row[0], xml_filename: row[1], status: :pending, - load_report: LoadReport.find(load_report_id), + load_report: LoadReport.find(load_report_id) ) end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 347adbb92..aacba4158 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -49,12 +49,7 @@ end RSpec.configure do |config| config.before(:suite) do - # TODO: Atlas wipe <- fixed! with below - DatabaseCleaner[:active_record, db: :atlas_test].strategy = :deletion - DatabaseCleaner[:active_record, db: :atlas_test].clean - c = RSolr.connect(:url => 'http://solr:8983/solr/blacklight-test') - c.delete_by_query '*:*' - c.commit + AtlasRb::Reset.clean end config.include Devise::Test::ControllerHelpers, type: :controller From acefcaa12137b7b0561790f1a8fac7547d8a161d Mon Sep 17 00:00:00 2001 From: George Lawton Date: Fri, 4 Oct 2024 15:48:19 -0400 Subject: [PATCH 06/11] Added loads_controller testing, and corresponding test zip files. --- .version | 2 +- spec/controllers/loads_controller_spec.rb | 110 +++++++++++++++++- spec/fixtures/files/zip_with_missing_pids.zip | Bin 0 -> 5953 bytes spec/fixtures/files/zip_with_missing_xml.zip | Bin 0 -> 6039 bytes spec/fixtures/files/zip_without_manifest.zip | Bin 0 -> 1622 bytes 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 spec/fixtures/files/zip_with_missing_pids.zip create mode 100644 spec/fixtures/files/zip_with_missing_xml.zip create mode 100644 spec/fixtures/files/zip_without_manifest.zip diff --git a/.version b/.version index 9f8d8a916..2701a226a 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.3 +2.8.4 diff --git a/spec/controllers/loads_controller_spec.rb b/spec/controllers/loads_controller_spec.rb index 7f40fa783..44d0981ff 100644 --- a/spec/controllers/loads_controller_spec.rb +++ b/spec/controllers/loads_controller_spec.rb @@ -7,9 +7,12 @@ let(:collection) { AtlasRb::Collection.create(community['id'], '/home/cerberus/web/spec/fixtures/files/collection-mods.xml') } let(:work) { AtlasRb::Work.create(collection['id'], '/home/cerberus/web/spec/fixtures/files/work-mods.xml') } let(:zip) { fixture_file_upload('/home/cerberus/web/spec/fixtures/files/metadata_existing_files.zip', 'application/zip') } + let(:zip_s) { fixture_file_upload('/home/cerberus/web/spec/fixtures/files/metadata_existing_file.zip', 'application/zip') } describe 'noid test' do it 'lets spec set the noid' do + # not for keeping, just iterating - this lets us hard code pids in the xml zip fixture file + # and patch them in to test objects for the xml update testing AtlasRb::Community.metadata(work['id'], { 'noid' => '123' }) expect(AtlasRb::Work.find('123')).to be_present end @@ -19,8 +22,113 @@ it 'processes the zip file successfully' do post :create, params: { file: zip } expect(response).to redirect_to(loads_path) - # expect(flash[:notice]).to eq("ZIP file processed successfully.") + expect(flash[:notice]).to eq("ZIP file processed successfully.") expect(flash[:alert]).to be_nil end end + + describe 'creates LoadReports and Ingests' do + it 'creates a LoadReport' do + expect { + post :create, params: { file: zip } + }.to change(LoadReport, :count).by(1) + end + + it 'creates Ingests' do + expect { + post :create, params: { file: zip } + }.to change(Ingest, :count).by(5) + end + end + + describe 'updates metadata' do + it 'updates existing work with new metadata' do + AtlasRb::Work.metadata(work['id'], { 'noid' => 'neu:test123' }) + found_work = AtlasRb::Work.find('neu:test123') + expect(found_work).to be_present + initial_xml_content = AtlasRb::Work.mods(found_work['id'], 'xml') + expect(initial_xml_content).to be_present + post :create, params: { file: zip_s } + + updated_work = AtlasRb::Work.find('neu:test123') + expect(updated_work).to be_present + updated_xml_content = AtlasRb::Work.mods(updated_work['id'], 'xml') + expect(updated_xml_content).to be_present + + expect(updated_xml_content).not_to eq(initial_xml_content) + end + end + + describe 'error handling' do + it 'handles missing file' do + post :create + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to eq('No file uploaded. Please select a ZIP file.') + end + + it 'handles non-zip file' do + non_zip = fixture_file_upload('/home/cerberus/web/spec/fixtures/files/spongebob.png', 'text/plain') + post :create, params: { file: non_zip } + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to include('Invalid file type') + end + + it 'handles zip without manifest' do + zip_without_manifest = fixture_file_upload('/home/cerberus/web/spec/fixtures/files/zip_without_manifest.zip', 'application/zip') + post :create, params: { file: zip_without_manifest } + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to include('Manifest file not found in ZIP') + end + + it 'handles missing XML file' do + zip_with_missing_xml = fixture_file_upload('/home/cerberus/web/spec/fixtures/files/zip_with_missing_xml.zip', 'application/zip') + post :create, params: { file: zip_with_missing_xml } + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to include('file not found in ZIP') + end + + it 'handles missing PIDs in spreadsheet' do + zip_with_missing_pids = fixture_file_upload('/home/cerberus/web/spec/fixtures/files/zip_with_missing_pids.zip', 'application/zip') + post :create, params: { file: zip_with_missing_pids } + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to include('Missing PID or filename in row') + end + + it 'handles Zip::Error' do + allow(Zip::File).to receive(:open).and_raise(Zip::Error.new("Corrupted zip file")) + + post :create, params: { file: zip } + + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to include("Error processing ZIP file: Corrupted zip file") + end + + it 'handles StandardError during spreadsheet processing' do + allow(Roo::Spreadsheet).to receive(:open).and_raise(StandardError.new("Invalid spreadsheet format")) + + post :create, params: { file: zip } + + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to include("Error processing spreadsheet: Invalid spreadsheet format") + end + end + + describe "index" do + it "orders LoadReports" do + oldest_report = LoadReport.create!(status: :completed, created_at: 2.days.ago) + older_report = LoadReport.create!(status: :completed, created_at: 1.day.ago) + newer_report = LoadReport.create!(status: :completed, created_at: 1.hour.ago) + newest_report = LoadReport.create!(status: :completed, created_at: 1.minute.ago) + + get :index + + expect(assigns(:load_reports).to_a).to eq([newest_report, newer_report, older_report, oldest_report]) + end + end + + it "renders html template" do + get :index + expect(response).to render_template("loads/index") + end end + diff --git a/spec/fixtures/files/zip_with_missing_pids.zip b/spec/fixtures/files/zip_with_missing_pids.zip new file mode 100644 index 0000000000000000000000000000000000000000..11dcf99ac6fd2d969b609ffb88e91e3f5f1c9f25 GIT binary patch literal 5953 zcmV-H7ry9FO9KQH0000801|gZSvzsqam5w@09YXa01W^D0BvDzX=Y_}bS`*ob9m)h z2UL^YmZby;5a|M;H$iDeidYb&h)8cDy@Wsjfk^KlU6GFT4oWXlM39zH1vG%6_l|S~ z=>h{fZ$^Fp^S%GgdNXgW`Lb@3Z)csoa`t!bx#!%gB99B8!U6(;SmcJ%+E_naw5P+x z>N3pE*xAAcZvU4rSNL75tx~%6Z5kmUf(^P8+R`ks>}x?t(f=51i2dvjG9 z=kyW^HxYMc>sWEXzfzcJS0aVs;2NtxHS_>)*KD#dkDyrfmdaP`R&QUyV4R?wusFc&7KH5~qdCljIIWD75=ew$F zdoyFD-0fFiU*xeWH5e~?U(WW%P(_{qNC36)!^6hHBEZGMQu%vQIwR~J+MAo2I@n+S z_VHcHF@0r|Hb#O;b;$rrKy!8=o>?u{X$tm_J<9b9!%IeO zoC@}*_8go=#t_cW_CMrdmW=7(G#I&P>mzS@sSwe;8$4QUfYge`b)d2Mf0e{PGg9=$yN{t9CQ z_^3_gxIxbN>ba=~xw-w$-Nqo{2Cw3ZGJ&qAqMp*l{7M(RhxPTX7M!1@(F7S*imzrr zpusH6{09xObr)fyjT z4r?HZE>CJ%{W{h(rJNMx&5t12Z8Mn?ZC#l09`SIo(L*cum57iYVg_=cX@`nv&Cczd z^{w!nb>>uqgp|00-mTrk<{Yul;-15<%Z97bV!L^lm4%LkS~20rM_(ZgZwIZ#l2?^d z(!8E)a$X(}nHgfDKl(gq>22vQ`c|CD(_6SEzTTVnuy?v9j<3=F+2UutF0K%}#`WoE zcAs}UUzWrsiM{C@EH{a6SHS%_8nMER2YE0<-`al6t_DlW@XBO4^f@slvR0#v~F`M?6918Djot;hz?;WSAYT?Z-< z_^h%(m;n8LNDs0>QXE`KBMG1a(B~X0gbkqSj|?FjB*&4GG`a^=AoSs5g>VA;{gKIJ zgOoUal18dP1tK3sRtPshGXPmgHh3PVNzzCgs6gyv%?jZM^amho$p&d~aHWilfeIj> zP*#WlKr;~eo@@}zc2f!lf3gCKOkph(ly(Yyhb9~PJ=jwszClc8V2g(XpB-S?y94w( z?`s%1Il#IX0Q@sN|6U*zF1rGHn!;Kt$l(;&jE)!tQ~cB5th4AIAE{SY6OWDCWXRV_ zf`)Yz!eiqm^VfcYKg08X0KQuRC8V&H3&NZN-=iajzydd=%>D)7jxe=HB%YxuDo)#_ z|@w z;+qE@VI^9_k%F5ojGm5n4aXz@4A1`%EECjl3hY3u41u|CN}0lQS3oiUE}$nf;e>3X zbJRu=hsR~Y35DtNHTNFL)Tj9)E~5Sn&p!eRIR&<&y9dE^H>FJA<}0Akl#J4Uf3V*E zB|?AE{lUR}OuhR{gkd|n+o06v0(@3i>z{N94*#?QN=eBm7j$q6M5DWhz&HLMf*_|r z6dE!J#=qHQ1gBjA`K1`Y`u_(?#UCNe^`(ho~RK*hCfN^5(IIN?hkk( zv?}9o^ODI(Hk;IniQ{w6NK$C#WaHfXE{`TdVN$_z`haWKc`|14AY~nk?IaZFG21#^ zYD%5Z@^TL244`6NzLJnhhlp*m2Mk=Q+87@-K}_B!i_h+yYNg~x1IiW+lyK3YVUV>7 zhVnF?HZ^hWI()1ZZvp*$smG=I6UxCPO8V!9zRU!4W3N|hab5vE=Xi8HT-Ou@r8?y_R}sfXOyzte#HbifcMDt3rR_Lof%cBW@;TSty5HL=hJmLT0(lHWW> z3lUq?;C=|x6S95QDk$0D)VxV~yJoDeVkAG|reAH>;Rg2HQo7rhb7=W29t8}Dy}MCJ z&H4Pe^*}oXJ9Cwd3qnX^56D!qRHLTF8RAC+=dEHvWnKK3N~k;K35+G5ZegO@wcv07 z>CRA*+AiOKR`1O+Mu>J2WwLAp5-74uUpUgugB!bwzsnCY^_ak1h3XZ? zHg4AX^G22a%#AMdNLZ&owQgeh*VMT$SsV|JR9$rFA0S>lYE8PDzEBy1(~b6{=Mhwm z@;*^N!8()Ge2t=)_Egg4-^xn)J+vImO|4D8zkiFT4-a$^!w{-tyhGC1iGfidHb8@q zYSkF?qST^`)0UznjB+Sj|Kx~)i)^7^lP89Vbg*sW`Z2mi%*^#jBx@4eUc#=@7aemB z+sv4WJhpJMB(0`P5a=(>D=7qb!ApDfcwkvsz9Qlh8ltkKz}roftwv4UNs&RpZW}`r zpZEIO0yxUDgOX?ZlWM|;+_~YM-VQS3SN#hmJzX>L{w_&DZ6HBy@KiioUhcE!R5&<+ z0xh;_P+mN}G}&=gJn@M z(m;$^@&T5m5RBQ@n_Y2OsKd=@m1|6UyNe$LFjY|TQ-9GuB4Ig6uS%0 z^I2kfX)6N>pU{|3c8!6mF`ujOWx{{TX;;9N7p~{`Vrw>H;@azH)X<_ zx<;-wL!A_xCFUvC9e$1R$y{s3M3Wu9aPGXBWOTaP?Xt*`W9?#`OFWB0=E3_`23-`! z6dSL52BINXA4YHB%L2xiWv}hL)Ot^#^*(t)OG`$xHv>C&h2{7K6d?WNVpxah{ezqp z15u0#%Bp6Sq)H@Bj~By7AyfVO9<6D7CeXQs2EFYemTc)9Ap>y_*V8?!@hRH@iRKF~ z>~|&f+$HnZ63l(X(WO22!In|FKPM;#x+ zCYhQACbyw-rffkw-e+#{8A$bd-_3T%@$Qw&TSUce@Ed{5i53Gq4=?%$!JMBKAC{>t zd}Od-L4D%e@lqT&nmbp%NwEHQ|Gihed~F>oZLe8VZ>_PVN!^SuDUM0aqU`8sk5zps zy+c92z<7*X|30pBRN)Es*;K0%#BSln#los4`Q22b`aad{&5i9$Vd@TcXIqMAb4@>d zOs*M17LbN?rkx+p=6)g@Rrg4nOixb*RYNgsODW6wT5x+|HS`J4z-xg3U78dgH5E#I zFNQJI@fOZ!t(@k&LCWDZ7#=_mQvF>_`xFrRn6~H2dO&NUzKTYWc?ChWK9_Tqq59s(3g5I z^}uW3{cd?Mp+J>Nk67zeSSgE}h3g=ah0%@5rIS7J7}+>^VO{bD?A(a;oAvm);hy)8 zM5qzfRnw(9F2M9@na8H-P7h}f+_%%7*6}OTccz46wrtmEYm>dg2!!XI`i{$b6 z@!2LnLGL&dtHe!lxlZFBRy4_!zNIokMn9n#Ok`sX(@iiDv&+&gp}fXbiqyQhK?q9_ z@IJxqsReNKw(4bv-PVVOs=5uNOT->|(W!ixsxwQxzIM4shWTKxeQR;$U2VD@aedQ0 zo!7)~B`-PHLi0Yx2MOJp=)3bNS2k>a^gdM^B(hky&}!&ymi3C|hA{*~pwO$zp&U6}b?y;U-X;sfE z-~EEg_5L;+4)N*;x=y3O74W-AAjSHxng)tMJp8r~9Ic8-f{;D-MjhjBK8&Hw?0#|a z#mC_yOOZReBi6&?3?@k89o7+*GQ$QxES)5p3K}c>y)EZ9SNpNt#)5!S3I_$>YGu)k z>l|wEEBiZNZ7Vrb9Tm6=vG|~NRIz%932Or+!wwb!>Fz`N=+SC{%-&7Ci)ITdM4^#X zAyASy&a6ry-wRi18uNVlJx-#yIfbv2vk3-ZTX0ub;Tc@Y7$byAqZo!q^kSXT^X5T0 z93n=nj`k$^Tp8FonNyjRIt>(R9>I~&d6gJJ=~(m9xB!PX^uy-fD|sPdZmm|m=DX4^ zUK$0^y(+?3Ol?=N>zxvVArXl^?-$4%i4FYMu!Yz0#VvcXY8c22^P(>7X5|Qt)SIL* z#9`^GQc$S2JkM?bOR5=BoU%19a@@M@5TKjn!Wnh#%5HlUO8`l{^b56Tu%JWU`h~*D zu`8LjX+_I2B?1DR&RE*4H_Gbr3UM7f84kVPiV|16XDLHudd`DG61Mp9ANyngwF%a^ zH^NPy*ylP-hw$q5D)R54G_{(>W?ImsYmMThqF*yK_cOh;&##B4iTO8Zl787-z$6}L z&rcV4Hzy5c2VY7~)t7Y0o!_~976Xanu<^Fj7&!Uup#a_Y82Dby{ul}0%GshQMWv?G zyr_(1~}N zb0+ACbX^tUsi54y6_ogNd{^_Ef-fT!El(AcQSQF^WTI$0W8#KhwT1`;IPKZJHf=mD0TKe)xvze&i>wQkg!{*%Jjmqw|bx8pb zdLuf%d_l#=wMfLvil5j^HgQ6F9zmn$S$haLlS7M**0lfB+3Me}HpJiK%i7ez7-sBX zJOjPjD;PmB4;7-C7J4HDfryktN;O$44hcJgr4Tj_rZx_S8m^B_?e)(l%4?00vs1_;-y^A*fxL4o#)+Inq&1>8vL?(%bW-fY z9Kjz}*B7YcggPitJ7I?xQ~qM7H)YyZZt`?nQYQOYu8AZgahBZllu*v(3k)5{%ERUv#i>oMOzH zc}`E#w|;9HFn^wlH0k7=V=;?=2Q_k9o;X1+RTIa?>MGn*a=bC&mf&K)EOn`+Np6Kt z@h%?VrOed*8Jwm~oOE%4)#6Oy7(ytBY#+~(wXZRyGZh%2t!E!pdtU&{Kv|e@+b2dvOjD*e_ ztfcyhK#A&)%jsVkek@1M{luVl{+D9(SJNL?G18w*^=SU8dimA($Ba$)ld%!_cgx3+)gzZ(9SSbjD{(*Kkfe>MCu*nfU#&lvtN3#rKC;h+5sI(<=`x(i`C zv-%HEO928D02BZK00;mQcSKn`aoBOi761TPApigk000000000003ZMW000000BvDz jX=Y_}bS`*ob9hio1qJ{B000310RTGy008h700000f$@kU literal 0 HcmV?d00001 diff --git a/spec/fixtures/files/zip_with_missing_xml.zip b/spec/fixtures/files/zip_with_missing_xml.zip new file mode 100644 index 0000000000000000000000000000000000000000..291f6a43610a1e96d338fff1c2888cf81bcb9c59 GIT binary patch literal 6039 zcmV;I7ij2EO9KQH0000803&NbS&fcv$Qu^`0Jb3j01W^D0BvDzX=Y_}bS`*ob9m)h z2UJsCx}_5#^cs2-kQzuRih>jor56#Thu#uO1f+ukilP*eD!qz;bPxyxqy(uVA|-%S zkq*+^fWD&6KhFH`t$Aspc`{1%Uzm?lv>4GCGca&f>zT96*sR_O5%GXS|$yX3%vHa4(gXVYJl#L@>;QQqu ziNc<`T&^V_)7(-jzemltuIpdAFd+{7++R*M&5;v*A^%MGH4pvnf-hIrF9Q;N-c+aZ zPJTh-C*n_S9H|f9tCS$wmVeH&C(U_}9=1ogZ8PyQpSVc-lGXukYd|n094}1rnLzcz zxgiyW!;5F6kC-;G?K-HA0kd{2Z5uADX0uR@qIY{(39l(C##-5PIjG7GpEc-hd$sh= z6t+YFBU!;O=u~1lR@hm_Rb{57N(>-|+219^#la!Q$HCG1ds2EL+#KEQtgVsmqTfEg zONo7}li3C$nhw)(1M6r&I(^; zZ;zuM)~9(GusvVJep8BXg<%BiRwkQt?`9bW1M@4XF)k|Z;gvgH*hlP5TUDhNi_S?U zZB9C^FTkXBw+=LO*(FD+YNZ~w98LG4y*(xGl#P$0QZzgn5cCDR%LB6>C9-vH6+=a{ z`_0Wun@G=ElTOhm5&Ba7(n$mJ4k%+y+09jNK|iOWqGt>bS0ou$^dD{>>GF-VbomCv zA1!ySdu-QK4YcuL3!gEJbiv�&bTWVz-g;=oaMK8q76W>lmg`K$_6!fb!d4-bzx8 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 R0RRC2I{*LxMi&48008cnwmAR* literal 0 HcmV?d00001 diff --git a/spec/fixtures/files/zip_without_manifest.zip b/spec/fixtures/files/zip_without_manifest.zip new file mode 100644 index 0000000000000000000000000000000000000000..ed95ea4dd25fd61dc369758ecc977cab85434367 GIT binary patch literal 1622 zcmV-c2C4Z_O9KQH000080KKjyR@2GFAif0v0D=$z02=@R0CQn&aBO8?ZEs|AUw3JA zXkTbyZe(m_E_iKhomWk7+c*%t@2?S3hb?*sgcBuL<%Go z$NTH|k)mQ*vE3p)q}F_K`1m+8ymUv_{_g5ETf9ADZ;ltIY{ov^+}+=NJ7Qno&+&CFfe`y3jau@qIbt8BA%Qbt zx?rv%_ALOnBU%7+Jihb3&)=H;)LPTtzVH`oLcti1|S{VDCCNF5SF}>x+Me`ETRg%RN;u3?+ zZkK{LvPQg^?qQ!aej5$iZGPU9X<|4>PcF$uNCjMQGHpdB1|M9q(RVglcn=mrn^qg{ zC`rc_+Q?F>4LvuSw&sF1!imCXkRH@-ZKgy)wABl(Pp~f5eZ= zWKDKlTURF{cSVwU9?g!=W{bBm;>h{M*1BaaEg;Pf(`#eb`w88yfmh{xCcl%zmcy{s zDdc_Znyaz{NvF9E@)6)M&bCK9aP6KCT)J@4x6j*l%YSLOFdMPctrVFWp)pjipcpSZ zM7Y@T)mwK{9DDI^`!BV1TD{I!@TTqT>wIZb=3W<|3PtgCzO^s)uXA0-{;)daK5l5> z$4&8_tnLgh9Xdx{GZe;>h5*|JBUiK^ZSq~^qSst$)PCjef@8Yd<|gp}g)%2AjBGt+ z@8V?e!e*fSO>xE1iZtSsyNb|`OB0moFQN=3b+XG?HLA6hg4Z86iB9t?H0mDjT!lOC z)|UyIjqtK0qZ)?*a!C=_@ZuK`02n0H1B7dUniNGtP1`c)olswac(!2ebFYZjQ8N^? zed+gMWDZ+dNObPLepEU7W-cu%^ZJ@EslH5Z0Vh|!UgG_>LzE6xsysvsMAK2Q;T-%@9|4bX^5s$0lHfHDYs}n z)vB&pl1cX)jAy@RUAi;&MY?-udPAC_Xa>8; zEiN!IT3qT}dcH(vbQW<`&hSJqI`2g2G8mi8135o7dfk<78uWqwWnla2Vi`vUAU^XE zw88|qlCFx=qwuBU$@u|xPgkC^_bpb$5a-)3Vj!*(b(homK9`xHI*bo1TH-s5zk4i~ z+`xLRvVSBT+|idF7+dMC z&e;+$7y{UopR;5<{9$TuXx}tEPlh-iV9Wr_57Pmnm^RhL4A3@#;mr~`hg)dPEX*h$q32o)HBcY3yjbkBJdnBowVeyCXT|# zus#Tq=l@CNu>>KI6I+3!ATh%T-$)zEMuGgdlRQMZIs5DbYXIW!9txKiAF;NEch5E) zSzsPcN_vnSFN#qWB{Mv1bWunVFgi$bym(F*XY-S@llke{=~=u_lRGqwk~PP!is&21 z7B-u)vO_ubKY0I|65%y9%#+(*QR@F>{{v7<0Rj{N6aWAK2mrmVC05hP#UQ=~004pz z000{R0000000031AOHXW00000b75_8Y-L| Date: Mon, 7 Oct 2024 15:50:56 +0000 Subject: [PATCH 07/11] Require not neccessary due to rails automagic, and running some remediation steps to hopefully cause schema to stop flip-flopping --- .version | 2 +- app/controllers/loads_controller.rb | 3 --- db/schema.rb | 22 +++++++++++++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.version b/.version index 2701a226a..766d70806 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.4 +2.8.5 diff --git a/app/controllers/loads_controller.rb b/app/controllers/loads_controller.rb index 8fc061651..47fd1456b 100644 --- a/app/controllers/loads_controller.rb +++ b/app/controllers/loads_controller.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require 'zip' -require 'roo' - # There needs to be work done on ingests and whether or not they go into load_report # as of right now as long as the files seem valid they make it into ingest and then attempt to load # but that makes the success rate of load_report 100% pretty much always (not always correct). diff --git a/db/schema.rb b/db/schema.rb index f673b5d87..3f63450bb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_09_09_175347) do +ActiveRecord::Schema[7.2].define(version: 2024_10_03_164742) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" enable_extension "uuid-ossp" @@ -115,6 +115,24 @@ t.index ["scheduled_at"], name: "index_good_jobs_on_scheduled_at", where: "(finished_at IS NULL)" end + create_table "ingests", force: :cascade do |t| + t.string "pid", null: false + t.string "xml_filename", null: false + t.integer "status", default: 0, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.bigint "load_report_id", null: false + t.index ["load_report_id"], name: "index_ingests_on_load_report_id" + end + + create_table "load_reports", force: :cascade do |t| + t.integer "status", default: 0, null: false + t.datetime "started_at" + t.datetime "finished_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "metadata_mods", force: :cascade do |t| t.jsonb "json_attributes" t.datetime "created_at", null: false @@ -165,4 +183,6 @@ t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + + add_foreign_key "ingests", "load_reports" end From 42dbbed47e1029e420dfe62ebdc36af0980830c7 Mon Sep 17 00:00:00 2001 From: David Cliff Date: Mon, 7 Oct 2024 15:57:49 +0000 Subject: [PATCH 08/11] Deprecation fixes --- .version | 2 +- app/models/ingest.rb | 2 +- app/models/load_report.rb | 2 +- spec/rails_helper.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.version b/.version index 766d70806..e43686acc 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.5 +2.8.6 diff --git a/app/models/ingest.rb b/app/models/ingest.rb index fe3af5cc6..ed3c12970 100644 --- a/app/models/ingest.rb +++ b/app/models/ingest.rb @@ -3,7 +3,7 @@ class Ingest < ApplicationRecord belongs_to :load_report - enum status: { pending: 0, completed: 1, failed: 2 } + enum :status, { pending: 0, completed: 1, failed: 2 } validates :pid, presence: true validates :xml_filename, presence: true diff --git a/app/models/load_report.rb b/app/models/load_report.rb index f5de41c95..7faa536c2 100644 --- a/app/models/load_report.rb +++ b/app/models/load_report.rb @@ -3,7 +3,7 @@ class LoadReport < ApplicationRecord has_many :ingests - enum status: { in_progress: 0, completed: 1, failed: 2 } + enum :status, { in_progress: 0, completed: 1, failed: 2 } validates :status, presence: true diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index aacba4158..a9a6b6fc2 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -54,7 +54,7 @@ config.include Devise::Test::ControllerHelpers, type: :controller # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures - config.fixture_path = Rails.root.join('spec/fixtures').to_s + config.fixture_paths = [Rails.root.join('spec/fixtures').to_s] # Adding config includes for ViewComponent Test Helpers config.include ViewComponent::TestHelpers, type: :component From e0de9f6f393a36a242d2af57690f50207f2a294a Mon Sep 17 00:00:00 2001 From: David Cliff Date: Mon, 7 Oct 2024 16:14:22 +0000 Subject: [PATCH 09/11] Adding fix-db-schema-conflicts gem which presorts cosmetic schema db changes to prevent flip flopping on commits from different devs --- .version | 2 +- Gemfile | 1 + Gemfile.lock | 3 ++ db/schema.rb | 133 ++++++++++++++++++++------------------------------- 4 files changed, 56 insertions(+), 83 deletions(-) diff --git a/.version b/.version index e43686acc..bcd0f91fe 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.6 +2.8.7 diff --git a/Gemfile b/Gemfile index 1641e341d..ab3edd2ee 100644 --- a/Gemfile +++ b/Gemfile @@ -79,6 +79,7 @@ group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem 'debug', platforms: %i[mri mingw x64_mingw] gem 'faker' + gem 'fix-db-schema-conflicts' gem 'rspec' gem 'rspec-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 0e2f15a13..49d81a848 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -178,6 +178,8 @@ GEM faraday-net_http (3.3.0) net-http ffi (1.17.0-x86_64-linux-gnu) + fix-db-schema-conflicts (3.1.1) + rubocop (>= 0.38.0) fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) @@ -460,6 +462,7 @@ DEPENDENCIES enumerations factory_bot_rails faker + fix-db-schema-conflicts good_job hamlit importmap-rails diff --git a/db/schema.rb b/db/schema.rb index 3f63450bb..5d407e3be 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,95 +10,94 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_10_03_164742) do +ActiveRecord::Schema[7.2].define(version: 2024_09_26_172306) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - enable_extension "uuid-ossp" create_table "bookmarks", id: :serial, force: :cascade do |t| - t.integer "user_id", null: false - t.string "user_type" + t.datetime "created_at", precision: nil, null: false t.string "document_id" t.string "document_type" t.binary "title" - t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.integer "user_id", null: false + t.string "user_type" t.index ["document_id"], name: "index_bookmarks_on_document_id" t.index ["user_id"], name: "index_bookmarks_on_user_id" end create_table "good_job_batches", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.integer "callback_priority" + t.text "callback_queue_name" t.datetime "created_at", null: false - t.datetime "updated_at", null: false t.text "description" - t.jsonb "serialized_properties" - t.text "on_finish" - t.text "on_success" - t.text "on_discard" - t.text "callback_queue_name" - t.integer "callback_priority" - t.datetime "enqueued_at" t.datetime "discarded_at" + t.datetime "enqueued_at" t.datetime "finished_at" + t.text "on_discard" + t.text "on_finish" + t.text "on_success" + t.jsonb "serialized_properties" + t.datetime "updated_at", null: false end create_table "good_job_executions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.datetime "created_at", null: false - t.datetime "updated_at", null: false t.uuid "active_job_id", null: false - t.text "job_class" - t.text "queue_name" - t.jsonb "serialized_params" - t.datetime "scheduled_at" - t.datetime "finished_at" + t.datetime "created_at", null: false + t.interval "duration" t.text "error" - t.integer "error_event", limit: 2 t.text "error_backtrace", array: true + t.integer "error_event", limit: 2 + t.datetime "finished_at" + t.text "job_class" t.uuid "process_id" - t.interval "duration" + t.text "queue_name" + t.datetime "scheduled_at" + t.jsonb "serialized_params" + t.datetime "updated_at", null: false t.index ["active_job_id", "created_at"], name: "index_good_job_executions_on_active_job_id_and_created_at" t.index ["process_id", "created_at"], name: "index_good_job_executions_on_process_id_and_created_at" end create_table "good_job_processes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.jsonb "state" t.integer "lock_type", limit: 2 + t.jsonb "state" + t.datetime "updated_at", null: false end create_table "good_job_settings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.datetime "created_at", null: false - t.datetime "updated_at", null: false t.text "key" + t.datetime "updated_at", null: false t.jsonb "value" t.index ["key"], name: "index_good_job_settings_on_key", unique: true end create_table "good_jobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.text "queue_name" - t.integer "priority" - t.jsonb "serialized_params" - t.datetime "scheduled_at" - t.datetime "performed_at" - t.datetime "finished_at" - t.text "error" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false t.uuid "active_job_id" + t.uuid "batch_callback_id" + t.uuid "batch_id" t.text "concurrency_key" - t.text "cron_key" - t.uuid "retried_good_job_id" + t.datetime "created_at", null: false t.datetime "cron_at" - t.uuid "batch_id" - t.uuid "batch_callback_id" - t.boolean "is_discrete" + t.text "cron_key" + t.text "error" + t.integer "error_event", limit: 2 t.integer "executions_count" + t.datetime "finished_at" + t.boolean "is_discrete" t.text "job_class" - t.integer "error_event", limit: 2 t.text "labels", array: true - t.uuid "locked_by_id" t.datetime "locked_at" + t.uuid "locked_by_id" + t.datetime "performed_at" + t.integer "priority" + t.text "queue_name" + t.uuid "retried_good_job_id" + t.datetime "scheduled_at" + t.jsonb "serialized_params" + t.datetime "updated_at", null: false t.index ["active_job_id", "created_at"], name: "index_good_jobs_on_active_job_id_and_created_at" t.index ["batch_callback_id"], name: "index_good_jobs_on_batch_callback_id", where: "(batch_callback_id IS NOT NULL)" t.index ["batch_id"], name: "index_good_jobs_on_batch_id", where: "(batch_id IS NOT NULL)" @@ -116,69 +115,39 @@ end create_table "ingests", force: :cascade do |t| + t.datetime "created_at", null: false + t.bigint "load_report_id", null: false t.string "pid", null: false - t.string "xml_filename", null: false t.integer "status", default: 0, null: false - t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.bigint "load_report_id", null: false + t.string "xml_filename", null: false t.index ["load_report_id"], name: "index_ingests_on_load_report_id" end create_table "load_reports", force: :cascade do |t| - t.integer "status", default: 0, null: false - t.datetime "started_at" - t.datetime "finished_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - create_table "metadata_mods", force: :cascade do |t| - t.jsonb "json_attributes" t.datetime "created_at", null: false + t.datetime "finished_at" + t.datetime "started_at" + t.integer "status", default: 0, null: false t.datetime "updated_at", null: false - t.string "valkyrie_id" - end - - create_table "minter_states", id: :serial, force: :cascade do |t| - t.string "namespace", default: "default", null: false - t.string "template", null: false - t.text "counters" - t.bigint "seq", default: 0 - t.binary "rand" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["namespace"], name: "index_minter_states_on_namespace", unique: true - end - - create_table "orm_resources", id: :uuid, default: -> { "uuid_generate_v4()" }, force: :cascade do |t| - t.jsonb "metadata", default: {}, null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.string "internal_resource" - t.integer "lock_version" - t.index ["internal_resource"], name: "index_orm_resources_on_internal_resource" - t.index ["metadata"], name: "index_orm_resources_on_metadata", using: :gin - t.index ["metadata"], name: "index_orm_resources_on_metadata_jsonb_path_ops", opclass: :jsonb_path_ops, using: :gin - t.index ["updated_at"], name: "index_orm_resources_on_updated_at" end create_table "searches", id: :serial, force: :cascade do |t| + t.datetime "created_at", precision: nil, null: false t.binary "query_params" + t.datetime "updated_at", precision: nil, null: false t.integer "user_id" t.string "user_type" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false t.index ["user_id"], name: "index_searches_on_user_id" end create_table "users", force: :cascade do |t| + t.datetime "created_at", null: false t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.datetime "created_at", null: false + t.datetime "reset_password_sent_at" + t.string "reset_password_token" t.datetime "updated_at", null: false t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true From e09bd720a689fe1db699b234fe67e9aa5574b9d9 Mon Sep 17 00:00:00 2001 From: George Lawton Date: Tue, 8 Oct 2024 13:55:46 -0400 Subject: [PATCH 10/11] Changed xlsx processing to look for headers. Added corresponding test and testing files. --- .version | 2 +- app/controllers/loads_controller.rb | 42 ++++++++++++++-------- app/models/ingest.rb | 6 ++-- spec/controllers/loads_controller_spec.rb | 8 +++++ spec/fixtures/files/no_header.zip | Bin 0 -> 5926 bytes 5 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 spec/fixtures/files/no_header.zip diff --git a/.version b/.version index bcd0f91fe..80803faf1 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.7 +2.8.8 diff --git a/app/controllers/loads_controller.rb b/app/controllers/loads_controller.rb index 47fd1456b..04cab22b4 100644 --- a/app/controllers/loads_controller.rb +++ b/app/controllers/loads_controller.rb @@ -57,25 +57,37 @@ def process_spreadsheet(xlsx_file, zip_file, failures) load_report = LoadReport.create!(status: :in_progress) load_report.start_load - spreadsheet.each_with_index do |row, index| - next if index.zero? - pid = row[0] - file_name = row[1] - if pid && file_name - xml_entry = zip_file.find_entry(file_name) - if xml_entry - ingest = Ingest.create_from_spreadsheet_row(row, load_report.id) - xml_content = xml_entry.get_input_stream.read - UpdateMetadataJob.perform_later(pid, xml_content, ingest.id) + header_row = spreadsheet.row(1) + header_hash = {} + header_row.each_with_index do |cell, index| + header_hash[cell] = index + end + if header_hash.key?("PIDs") && header_hash.key?("MODS XML File Path") + pid_column = header_hash["PIDs"] + file_path_column = header_hash["MODS XML File Path"] + spreadsheet.each_with_index do |row, index| + next if index.zero? + pid = row[pid_column] + file_name = row[file_path_column] + if pid && file_name + xml_entry = zip_file.find_entry(file_name) + if xml_entry + ingest = Ingest.create_from_spreadsheet_row(pid, file_name, load_report.id) + xml_content = xml_entry.get_input_stream.read + UpdateMetadataJob.perform_later(pid, xml_content, ingest.id) + else + failures << "#{file_name} file not found in ZIP" + end else - failures << "#{file_name} file not found in ZIP" + failures << "Missing PID or filename in row #{index + 1}" end - else - failures << "Missing PID or filename in row #{index + 1}" end - end - load_report + load_report + else + failures << "Cannot find header labels" + load_report + end rescue StandardError => e failures << "Error processing spreadsheet: #{e.message}" load_report diff --git a/app/models/ingest.rb b/app/models/ingest.rb index ed3c12970..0cffaaec8 100644 --- a/app/models/ingest.rb +++ b/app/models/ingest.rb @@ -9,10 +9,10 @@ class Ingest < ApplicationRecord validates :xml_filename, presence: true validates :status, presence: true - def self.create_from_spreadsheet_row(row, load_report_id) + def self.create_from_spreadsheet_row(pid, file_name, load_report_id) create!( - pid: row[0], - xml_filename: row[1], + pid: pid, + xml_filename: file_name, status: :pending, load_report: LoadReport.find(load_report_id) ) diff --git a/spec/controllers/loads_controller_spec.rb b/spec/controllers/loads_controller_spec.rb index 44d0981ff..f33c4a687 100644 --- a/spec/controllers/loads_controller_spec.rb +++ b/spec/controllers/loads_controller_spec.rb @@ -111,6 +111,14 @@ expect(response).to redirect_to(loads_path) expect(flash[:alert]).to include("Error processing spreadsheet: Invalid spreadsheet format") end + + it 'handles missing headers in spreadsheet' do + zip_with_missing_headers = fixture_file_upload('/home/cerberus/web/spec/fixtures/files/no_header.zip', 'application/zip') + + post :create, params: { file: zip_with_missing_headers } + expect(response).to redirect_to(loads_path) + expect(flash[:alert]).to include("Cannot find header labels") + end end describe "index" do diff --git a/spec/fixtures/files/no_header.zip b/spec/fixtures/files/no_header.zip new file mode 100644 index 0000000000000000000000000000000000000000..03d2093371847f7de441d5c1d17b245239b6d800 GIT binary patch literal 5926 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 zI3WN44FCWD00000001BW00000003=aZfRy^b963vY;$-}O9ci1000010096y0002h I761SM0OvA|2mk;8 literal 0 HcmV?d00001 From c6111c55ed8e397a07e96bce4df2bd38d579488c Mon Sep 17 00:00:00 2001 From: David Cliff Date: Thu, 10 Oct 2024 18:26:56 +0000 Subject: [PATCH 11/11] Minor refactor - add show for show report. Use bootstrap theming. Stay inside the layout, to avoid affecting header + footer. --- .version | 2 +- app/controllers/loads_controller.rb | 6 +- app/views/loads/index.html.erb | 143 ---------------------------- app/views/loads/index.html.haml | 30 ++++++ app/views/loads/show.html.haml | 17 ++++ config/routes.rb | 3 +- 6 files changed, 54 insertions(+), 147 deletions(-) delete mode 100644 app/views/loads/index.html.erb create mode 100644 app/views/loads/index.html.haml create mode 100644 app/views/loads/show.html.haml diff --git a/.version b/.version index bcd0f91fe..80803faf1 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.7 +2.8.8 diff --git a/app/controllers/loads_controller.rb b/app/controllers/loads_controller.rb index 47fd1456b..39b6062fd 100644 --- a/app/controllers/loads_controller.rb +++ b/app/controllers/loads_controller.rb @@ -6,7 +6,11 @@ # Also with some things it will look like it failed but all valid jobs haven't class LoadsController < ApplicationController def index - @load_reports = LoadReport.order(created_at: :desc).includes(:ingests) + @load_reports = LoadReport.order(created_at: :desc) + end + + def show + @load_report = LoadReport.find(params[:id]) end def create diff --git a/app/views/loads/index.html.erb b/app/views/loads/index.html.erb deleted file mode 100644 index eda6bc27f..000000000 --- a/app/views/loads/index.html.erb +++ /dev/null @@ -1,143 +0,0 @@ - - Ingest Management - - - -
-

Ingest Management

-
- -
-
-

Upload ZIP File

- <%= form_tag loads_path, multipart: true do %> -
- <%= file_field_tag :file, accept: 'application/zip,application/x-zip-compressed' %> -
-
- <%= submit_tag "Upload and Process" %> -
- <% end %> -
- -
-

Load Reports

- - - - - - - - - - - <% @load_reports.each do |load_report| %> - - - - - - - - - - <% end %> - -
StatusStarted AtFinished AtSuccess Rate
<%= load_report.status %><%= load_report.started_at %><%= load_report.finished_at %><%= load_report.success_rate %>%
- - - - - - - - - - - <% load_report.ingests.each do |ingest| %> - - - - - - - <% end %> - -
PIDXML FilenameStatusCreated At
<%= ingest.pid %><%= ingest.xml_filename %><%= ingest.status %><%= ingest.created_at %>
-
-
-
- - - - diff --git a/app/views/loads/index.html.haml b/app/views/loads/index.html.haml new file mode 100644 index 000000000..3f05d71c2 --- /dev/null +++ b/app/views/loads/index.html.haml @@ -0,0 +1,30 @@ +%h2.py-3 + Ingest Management + +.upload-form + %h3.py-3 + Upload ZIP File + = form_tag loads_path, multipart: true do + .input-group.mb-3 + %input#file.form-control{name: "file", type: "file", accept: 'application/zip,application/x-zip-compressed'}/ + = submit_tag "Upload", class: 'btn btn-primary' +%div + %h2.py-3 + Load Reports + %table.table + %thead + %tr + %th Status + %th Started At + %th Finished At + %th Success Rate + %th View + %tbody + - @load_reports.each do |load_report| + %tr + %td= load_report.status + %td= load_report.started_at + %td= load_report.finished_at + %td= load_report.success_rate + %td + %a.btn.btn-sm.btn-primary{:href => load_path(load_report.id)} View diff --git a/app/views/loads/show.html.haml b/app/views/loads/show.html.haml new file mode 100644 index 000000000..390577319 --- /dev/null +++ b/app/views/loads/show.html.haml @@ -0,0 +1,17 @@ +%h2.py-3 + Load Report +%table.table + %thead + %tr + %th PID + %th XML Filename + %th Status + %th Created At + %tbody + - @load_report.ingests.each do |ingest| + %tr + %td= ingest.pid + %td= ingest.xml_filename + %td= ingest.status + %td= ingest.created_at + diff --git a/config/routes.rb b/config/routes.rb index a56cfa92d..d18bf1051 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,12 +27,11 @@ resources :communities resources :collections + resources :loads resources :works # xml get '/xml/editor/:id' => 'xml#editor', as: 'xml_editor' put '/xml/validate' => 'xml#validate' put '/xml/update' => 'xml#update' - - resources :loads, only: [:index, :create] end