diff --git a/app/models/bundle_context.rb b/app/models/bundle_context.rb index 647ba625..d7015fff 100644 --- a/app/models/bundle_context.rb +++ b/app/models/bundle_context.rb @@ -7,6 +7,11 @@ class BundleContext < ApplicationRecord validates :staging_style_symlink, inclusion: { in: [ true, false ] } validates :content_metadata_creation, presence: true, null: false + validate :verify_bundle_directory + validate :verify_content_metadata_creation + + after_initialize :normalize_bundle_dir + enum content_structure: { "simple_image_structure" => 0, "simple_book_structure" => 1, @@ -21,4 +26,82 @@ class BundleContext < ApplicationRecord "smpl_style" => 2 } + def staging_dir + '/dor/assembly' + end + + def normalize_bundle_dir + self[:bundle_dir].chomp("/") if bundle_dir + end + + def progress_log_file + Tempfile.new.path(id) #FIXME: (#78) + end + + def content_exclusion #FIXME: Delete everywhere in code (#227) + nil + end + + def file_attr + nil # FIXME can get rid of this (#228) + end + + def validate_files? + false #FIXME delete everwhere in code (#230) + end + + def content_tag_override? #TODO: find where this is used as a conditional and delete code that won't be executed (#231) + true + end + + def smpl_manifest + 'smpl_manifest.csv' + end + + def manifest + 'manifest.csv' + end + + def path_in_bundle(rel_path) + File.join(bundle_dir, rel_path) + end + + # TODO: BundleContext is not really a logical home for this util method + # load CSV allowing UTF-8 to pass through, deleting blank columns + # @param [String] filename + # @return [Array] + # @raise if file missing/unreadable + def self.import_csv(filename) + raise BundleUsageError, "CSV filename required" unless filename.present? + raise BundleUsageError, "Required file not found: #{filename}." unless File.readable?(filename) + file_contents = IO.read(filename).encode("utf-8", replace: nil) + csv = CSV.parse(file_contents, :headers => true) + csv.map { |row| row.to_hash.with_indifferent_access } + end + + # On first call, loads the manifest data, caches results + # @return [Array] + def manifest_rows + @manifest_rows ||= self.class.import_csv(path_in_bundle(manifest)) + end + + def manifest_cols + { + label: 'label', + source_id: 'sourceid', + object_container: 'object', #object referring to filename or foldername + druid: 'druid' + } + end + + private + + def verify_bundle_directory + return if errors.key?(:bundle_dir) + errors.add(:bundle_dir, "Bundle directory: #{bundle_dir} not found.") unless File.directory?(bundle_dir) + end + + def verify_content_metadata_creation + errors.add(:content_metadata_creation, "The SMPL manifest #{smpl_manifest} was not found in #{bundle_dir}.") if smpl_style? && !File.exist?(File.join(bundle_dir, smpl_manifest)) + end end diff --git a/config/projects/TEMPLATE.yaml b/config/projects/TEMPLATE.yaml index 5fe598aa..88a4412a 100644 --- a/config/projects/TEMPLATE.yaml +++ b/config/projects/TEMPLATE.yaml @@ -93,7 +93,6 @@ stageable_discovery: # 00/ # 1.tif # 2.tif - # 01/ # 1.jp2 # 2.jp2 # diff --git a/spec/models/bundle_context_spec.rb b/spec/models/bundle_context_spec.rb index 89849212..f2a1bb9c 100644 --- a/spec/models/bundle_context_spec.rb +++ b/spec/models/bundle_context_spec.rb @@ -1,15 +1,18 @@ -require 'rails_helper' - RSpec.describe BundleContext, type: :model do - context "validation" do - let(:user) { User.new(sunet_id: "Jdoe")} - subject(:bc) { BundleContext.new(project_name: "SmokeTest", - content_structure: 1, - bundle_dir: "spec/test_data/bundle_input_g", - staging_style_symlink: false, - content_metadata_creation: 1, - user: user) } + subject(:bc) do + BundleContext.new( + project_name: "SmokeTest", + content_structure: 1, + bundle_dir: "spec/test_data/bundle_input_g/", + staging_style_symlink: false, + content_metadata_creation: 1, + user: user + ) + end + let(:user) { User.new(sunet_id: "Jdoe") } + + context "validation" do it "is not valid unless it has all required attributes" do expect(BundleContext.new).not_to be_valid expect(bc).to be_valid @@ -61,10 +64,106 @@ end end + context "bundle_dir path does not exist" do + it "object does not pass validation" do + expect { bc.bundle_dir = 'does/not/exist' }.to change { bc.valid? }.to(false) + end + end + it { is_expected.to validate_presence_of(:project_name) } it { is_expected.to validate_presence_of(:content_structure) } it { is_expected.to validate_presence_of(:bundle_dir) } it { is_expected.to validate_presence_of(:content_metadata_creation) } it { is_expected.to belong_to(:user) } end + + describe "#staging_dir" do + it 'is hardcoded to the correct path' do + expect(described_class.new.staging_dir).to eq '/dor/assembly' + end + end + + describe "#normalize_bundle_dir" do + it "removes the trailing forward slash" do + expect(bc.normalize_bundle_dir).to eq "spec/test_data/bundle_input_g" + end + end + + describe "#content_tag_override?" do + it "is set to true" do + expect(described_class.new.content_tag_override?).to be true + end + end + + describe "#smpl_manifest" do + it "returns the file name" do + expect(described_class.new.smpl_manifest).to eq 'smpl_manifest.csv' + end + end + + describe "#manifest" do + it "returns the file name" do + expect(described_class.new.manifest).to eq 'manifest.csv' + end + end + + describe "#path_in_bundle" do + it "creates a relative path" do + expect(bc.path_in_bundle("manifest.csv")).to eq "spec/test_data/bundle_input_g/manifest.csv" + end + end + + describe "#progress_log_file" do + skip("Need to figure out where to set this path via planning meeting 9/10/18") + end + + describe '#import_csv' do + let(:manifest) do + described_class.import_csv("#{Rails.root}/spec/test_data/bundle_input_a/manifest.csv") + end + + it "loads a CSV as a hash with indifferent access" do + expect(manifest).to be_an(Array) + expect(manifest.size).to eq(3) + headers = %w{format sourceid filename label year inst_notes prod_notes has_more_metadata description} + expect(manifest).to all(be_an(ActiveSupport::HashWithIndifferentAccess)) # accessible w/ string and symbols + expect(manifest).to all(include(*headers)) + expect(manifest[0][:description]).to be_nil + expect(manifest[1][:description]).to eq('') + expect(manifest[2][:description]).to eq('yo, this is a description') + end + end + + describe "manifest_rows" do + it "loads the manifest CSV" do + expect(described_class).to receive(:import_csv).with("spec/test_data/bundle_input_g/manifest.csv") + bc.manifest_rows + end + + it "memoizes the manifest rows" do + expect(described_class).to receive(:import_csv).once.with("spec/test_data/bundle_input_g/manifest.csv").and_call_original + 2.times { bc.manifest_rows } + end + + it "expect the content of manifest rows" do + expect(bc.manifest_rows).to eq( + [ + {"druid"=>"druid:jy812bp9403", "sourceid"=>"bar-1.0", "folder"=>"jy812bp9403", "label"=>"Label 1", "description"=>"This is a description for label 1"}, + {"druid"=>"druid:tz250tk7584", "sourceid"=>"bar-2.1", "folder"=>"tz250tk7584", "label"=>"Label 2", "description"=>"This is a description for label 2"}, + {"druid"=>"druid:gn330dv6119", "sourceid"=>"bar-3.1", "folder"=>"gn330dv6119", "label"=>"Label 3", "description"=>"This is a description for label 3"} + ] + ) + end + end + + describe "manifest_cols" do + it "sets the column names" do + expect(bc.manifest_cols).to eq( + label: 'label', + source_id: 'sourceid', + object_container: 'object', # object referring to filename or foldername + druid: 'druid' + ) + end + end end