darlingtonia 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad3721dc07a3ded3cdd80bd95b0068f098e930a002b5f6e29eeb03a855d62731
4
- data.tar.gz: c5b3895c77363da927d7952ff786fe6c63048054db96400cf809511ce5cba144
3
+ metadata.gz: 145bf822e40f94c82cf2a0c61213e964a8f5e780209703e9506fa6ce35466612
4
+ data.tar.gz: ed804197a3ccad1047da9b8c8127603e38814c03d799325bb9c317938de71c88
5
5
  SHA512:
6
- metadata.gz: 0d02ffe72075c23e20aa4d9d222074aa50b669ef3cb7485739e5b9b5cb473b0ff94f9981d3896e2ce3c64b10218222fcd2b65083ec3fb6dafb823806f46492c0
7
- data.tar.gz: 27451965e8f4d3493bb7083dc8e6f7f4ad0b189186812d50374b8a7627c4006551bf56c3586b70e62621d470a7b692bc824c2eacb7f6726acf4c3b7a951d3f49
6
+ metadata.gz: dbaec8b01d54cb94859e1630fec14b84082c981e25143646eb1aa58ab4dbb5e0bbebcb4954457799dccdac0d8cc20f78b315486f2f2d38bfa709bf209df70980
7
+ data.tar.gz: d5ce22c9e01ba293513d7398fa5af18bb5cfcfe1df67dc6313ebb70cce13c655dd8c1b0998042a1b823014337b2e2ae7980b288f5168a0d4d4caa5077d32701e
data/.gitignore CHANGED
@@ -4,3 +4,12 @@ Gemfile.lock
4
4
  .byebug_history
5
5
  .fcrepo_wrapper_test.yml
6
6
  .solr_wrapper_test.yml
7
+ darlingtonia_import.log
8
+ *~
9
+ \#*\#
10
+ /.emacs.desktop
11
+ /.emacs.desktop.lock
12
+ *.elc
13
+ auto-save-list
14
+ tramp
15
+ .\#*
@@ -19,6 +19,10 @@ Metrics/BlockLength:
19
19
  - 'spec/**/*'
20
20
  - 'lib/darlingtonia/spec/**/*'
21
21
 
22
+ Metrics/CyclomaticComplexity:
23
+ Exclude:
24
+ - lib/darlingtonia/hyrax_basic_metadata_mapper.rb
25
+
22
26
  Metrics/LineLength:
23
27
  Exclude:
24
28
  - 'lib/darlingtonia/hyrax_basic_metadata_mapper.rb'
@@ -47,3 +51,6 @@ RSpec/MultipleExpectations:
47
51
  Exclude:
48
52
  - 'spec/darlingtonia/hyrax_basic_metadata_mapper_spec.rb'
49
53
  - 'spec/integration/import_hyrax_csv.rb'
54
+
55
+ Style/StructInheritance:
56
+ Enabled: false
@@ -1,9 +1,18 @@
1
+ 2.1.0 - Tue Feb 12, 2019
2
+
3
+ * Map a variety of values for 'visibility' to their Hyrax approved equivalents
4
+ * Remove date_uploaded from HyraxBasicMetadataMapper so it doesn't over-write the Hyrax provided timestamp
5
+ * Import headers are now case and whitespace insensitive
6
+ * Raise a more meaningful error when an expected file isn't found
7
+ * Transform location URIs into a based_near hash so geonames URIs import properly into Hyrax
8
+ * Add a default LogStream class so that Darlingtonia has logging by default instead of only sending output to STDOUT
9
+
1
10
  2.0.0 - Mon Feb 4, 2019
2
11
 
3
12
  Assume our base use case is to import into Hyrax.
4
13
  * Use HyraxRecordImporter as default
5
14
  * Use HyraxMapper as default
6
- * Add a getting started guide to the docs directory
15
+ * Add a getting started guide to the docs directory
7
16
 
8
17
  1.2.3 - Thu Jan 24, 2019
9
18
  ------------------------
@@ -20,13 +20,14 @@ Gem::Specification.new do |gem|
20
20
  gem.add_dependency 'active-fedora', '>= 11.5.2'
21
21
 
22
22
  gem.add_development_dependency 'yard', '~> 0.9'
23
- gem.add_development_dependency 'bixby', '~> 0.3'
23
+ gem.add_development_dependency 'bixby', '~> 1.0'
24
24
  gem.add_development_dependency 'hyrax-spec', '~> 0.2'
25
25
  gem.add_development_dependency 'rspec', '~> 3.6'
26
26
  gem.add_development_dependency 'coveralls', '~> 0.8'
27
27
  gem.add_development_dependency 'solr_wrapper', '~> 2.1'
28
28
  gem.add_development_dependency 'fcrepo_wrapper', '~> 0.9'
29
29
  gem.add_development_dependency 'byebug'
30
+ gem.add_development_dependency 'rake'
30
31
 
31
32
  gem.files = `git ls-files`.split("\n")
32
33
  gem.test_files = `git ls-files -- {spec}/*`.split("\n")
@@ -33,6 +33,7 @@ module Darlingtonia
33
33
  end
34
34
  module_function :config
35
35
 
36
+ require 'darlingtonia/log_stream'
36
37
  ##
37
38
  # Module-wide options for `Darlingtonia`.
38
39
  class Configuration
@@ -44,8 +45,8 @@ module Darlingtonia
44
45
  attr_accessor :default_error_stream, :default_info_stream
45
46
 
46
47
  def initialize
47
- self.default_error_stream = STDOUT
48
- self.default_info_stream = STDOUT
48
+ self.default_error_stream = Darlingtonia::LogStream.new
49
+ self.default_info_stream = Darlingtonia::LogStream.new
49
50
  end
50
51
  end
51
52
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'uri'
2
3
 
3
4
  module Darlingtonia
4
5
  ##
@@ -37,31 +38,52 @@ module Darlingtonia
37
38
  # Hyrax should return a single value instead of
38
39
  # an Array of values.
39
40
  def depositor
40
- metadata['depositor']
41
- end
42
-
43
- def date_uploaded
44
- metadata['date_uploaded']
41
+ single_value('depositor')
45
42
  end
46
43
 
47
44
  def date_modified
48
- metadata['date_modified']
45
+ single_value('date_modified')
49
46
  end
50
47
 
51
48
  def label
52
- metadata['label']
49
+ single_value('label')
53
50
  end
54
51
 
55
52
  def relative_path
56
- metadata['relative_path']
53
+ single_value('relative_path')
57
54
  end
58
55
 
59
56
  def import_url
60
- metadata['import_url']
57
+ single_value('import_url')
61
58
  end
62
59
 
60
+ # We should accept visibility values that match the UI and transform them into
61
+ # the controlled vocabulary term expected by Hyrax
63
62
  def visibility
64
- metadata['visibility']
63
+ case metadata[matching_header('visibility')]&.downcase&.gsub(/\s+/, "")
64
+ when 'public'
65
+ 'open'
66
+ when 'open'
67
+ 'open'
68
+ when 'registered'
69
+ 'authenticated'
70
+ when "authenticated"
71
+ 'authenticated'
72
+ when ::Hyrax::Institution.name&.downcase&.gsub(/\s+/, "")
73
+ 'authenticated'
74
+ when ::Hyrax::Institution.name_full&.downcase&.gsub(/\s+/, "")
75
+ 'authenticated'
76
+ when 'private'
77
+ 'restricted'
78
+ when 'restricted'
79
+ 'restricted'
80
+ else
81
+ 'restricted' # This is the default if nothing else matches
82
+ end
83
+ end
84
+
85
+ def files
86
+ map_field('files')
65
87
  end
66
88
 
67
89
  ##
@@ -79,16 +101,38 @@ module Darlingtonia
79
101
  ##
80
102
  # @see MetadataMapper#map_field
81
103
  def map_field(name)
82
- method_name = name
104
+ method_name = name.to_s
83
105
  method_name = CSV_HEADERS[name] if CSV_HEADERS.keys.include?(name)
84
- Array(metadata[method_name.to_s]&.split(delimiter))
106
+ key = matching_header(method_name)
107
+ Array(metadata[key]&.split(delimiter))
85
108
  end
86
109
 
87
110
  protected
88
111
 
112
+ # Some fields should have single values instead
113
+ # of array values.
114
+ def single_value(field_name)
115
+ metadata[matching_header(field_name)]
116
+ end
117
+
118
+ # Lenient matching for headers.
119
+ # If the user has headers like:
120
+ # 'Title' or 'TITLE' or 'Title '
121
+ # it should match the :title field.
122
+ def matching_header(field_name)
123
+ metadata.keys.find do |key|
124
+ next unless key
125
+ key.downcase.strip == field_name
126
+ end
127
+ end
128
+
89
129
  # Properties defined in Hyrax::CoreMetadata
130
+ # Note that date_uploaded is NOT set here, even though it is defined in
131
+ # Hyrax::CoreMetadata. Hyrax expects to set date_uploaded itself, and
132
+ # sending a metadata value for that field interferes with Hyrax expected
133
+ # behavior.
90
134
  def core_fields
91
- [:depositor, :title, :date_uploaded, :date_modified]
135
+ [:depositor, :title, :date_modified]
92
136
  end
93
137
 
94
138
  # Properties defined in Hyrax::BasicMetadata
@@ -1,26 +1,42 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Darlingtonia
3
4
  class HyraxRecordImporter < RecordImporter
4
5
  # TODO: Get this from Hyrax config
5
6
  DEFAULT_CREATOR_KEY = 'batchuser@example.com'
6
7
 
7
- ##
8
- # @!attribute [rw] creator
9
- # @return [User]
10
- attr_accessor :creator
8
+ # @!attribute [rw] depositor
9
+ # @return [User]
10
+ attr_accessor :depositor
11
+
12
+ # @!attribute [rw] collection_id
13
+ # @return [String] The fedora ID for a Collection.
11
14
  attr_accessor :collection_id
12
15
 
13
- ##
14
- # @param creator [User]
16
+ # @param attributes [Hash] Attributes that come
17
+ # from the UI or importer rather than from
18
+ # the CSV/mapper.
19
+ # @example
20
+ # attributes: { collection_id: '123',
21
+ # depositor_id: '456' }
15
22
  def initialize(error_stream: Darlingtonia.config.default_error_stream,
16
23
  info_stream: Darlingtonia.config.default_info_stream,
17
- collection_id: nil)
18
- self.creator = ::User.find_or_create_system_user(DEFAULT_CREATOR_KEY)
19
- self.collection_id = collection_id
24
+ attributes: {})
25
+ self.collection_id = attributes[:collection_id]
26
+ set_depositor(attributes[:depositor_id])
20
27
 
21
28
  super(error_stream: error_stream, info_stream: info_stream)
22
29
  end
23
30
 
31
+ # "depositor" is a required field for Hyrax. If
32
+ # it hasn't been set, set it to the Hyrax default
33
+ # batch user.
34
+ def set_depositor(user_id)
35
+ user = User.find(user_id) if user_id
36
+ user ||= ::User.find_or_create_system_user(DEFAULT_CREATOR_KEY)
37
+ self.depositor = user
38
+ end
39
+
24
40
  ##
25
41
  # @param record [ImportRecord]
26
42
  #
@@ -42,14 +58,6 @@ module Darlingtonia
42
58
  Hyrax.config.curation_concerns.first
43
59
  end
44
60
 
45
- # Depositor is a required field for Hyrax
46
- # If it hasn't been set in the metadata, set it to the Hyrax default batch user
47
- # Even if it has been set, for now, override that with the batch user.
48
- # @param Darlingtonia::InputRecord
49
- def set_depositor(record)
50
- record.mapper.metadata["depositor"] = @creator.user_key
51
- end
52
-
53
61
  # The path on disk where file attachments can be found
54
62
  def file_attachments_path
55
63
  ENV['IMPORT_PATH'] || '/opt/data'
@@ -61,15 +69,13 @@ module Darlingtonia
61
69
  # @param [Darlingtonia::InputRecord]
62
70
  # @return [Array] an array of Hyrax::UploadedFile ids
63
71
  def create_upload_files(record)
64
- file_attachment_filenames = record.mapper.metadata["files"]
65
- return [] if file_attachment_filenames.nil? || file_attachment_filenames.empty?
66
-
67
- files_to_attach = file_attachment_filenames.split(record.mapper.delimiter)
72
+ files_to_attach = record.mapper.files
73
+ return [] if files_to_attach.nil? || files_to_attach.empty?
68
74
 
69
75
  uploaded_file_ids = []
70
76
  files_to_attach.each do |filename|
71
77
  file = File.open(find_file_path(filename))
72
- uploaded_file = Hyrax::UploadedFile.create(user: @creator, file: file)
78
+ uploaded_file = Hyrax::UploadedFile.create(user: @depositor, file: file)
73
79
  uploaded_file_ids << uploaded_file.id
74
80
  file.close
75
81
  end
@@ -79,10 +85,51 @@ module Darlingtonia
79
85
  ##
80
86
  # Within the directory specified by ENV['IMPORT_PATH'], find the first
81
87
  # instance of a file matching the given filename.
88
+ # If there is no matching file, raise an exception.
82
89
  # @param [String] filename
83
90
  # @return [String] a full pathname to the found file
84
91
  def find_file_path(filename)
85
- Dir.glob("#{ENV['IMPORT_PATH']}/**/#{filename}").first
92
+ filepath = Dir.glob("#{ENV['IMPORT_PATH']}/**/#{filename}").first
93
+ raise "Cannot find file #{filename}... Are you sure it has been uploaded and that the filename matches?" if filepath.nil?
94
+ filepath
95
+ end
96
+
97
+ ##
98
+ # When submitting location data (a.k.a. the "based near" attribute) via the UI,
99
+ # Hyrax expects to receive a `based_near_attributes` hash in a specific format.
100
+ # We need to take geonames urls as provided by the customer and transform them to
101
+ # mimic what the Hyrax UI would ordinarily produce. These will get turned into
102
+ # Hyrax::ControlledVocabularies::Location objects upon ingest.
103
+ # The expected hash looks like this:
104
+ # "based_near_attributes"=>
105
+ # {
106
+ # "0"=> {
107
+ # "id"=>"http://sws.geonames.org/5667009/", "_destroy"=>""
108
+ # },
109
+ # "1"=> {
110
+ # "id"=>"http://sws.geonames.org/6252001/", "_destroy"=>""
111
+ # },
112
+ # }
113
+ # @return [Hash] a "based_near_attributes" hash as
114
+ def based_near_attributes(based_near)
115
+ original_geonames_uris = based_near
116
+ return if original_geonames_uris.empty?
117
+ based_near_attributes = {}
118
+ original_geonames_uris.each_with_index do |uri, i|
119
+ based_near_attributes[i.to_s] = { 'id' => uri_to_sws(uri), "_destroy" => "" }
120
+ end
121
+ based_near_attributes
122
+ end
123
+
124
+ #
125
+ # Take a user-facing geonames URI and return an sws URI, of the form Hyrax expects
126
+ # (e.g., "http://sws.geonames.org/6252001/")
127
+ # @param [String] uri
128
+ # @return [String] an sws style geonames uri
129
+ def uri_to_sws(uri)
130
+ uri = URI(uri)
131
+ geonames_number = uri.path.split('/')[1]
132
+ "http://sws.geonames.org/#{geonames_number}/"
86
133
  end
87
134
 
88
135
  private
@@ -92,16 +139,21 @@ module Darlingtonia
92
139
  def create_for(record:)
93
140
  info_stream << 'Creating record: ' \
94
141
  "#{record.respond_to?(:title) ? record.title : record}."
95
- set_depositor(record)
96
- uploaded_files = { uploaded_files: create_upload_files(record) }
97
- created = import_type.new
142
+ additional_attrs = {
143
+ uploaded_files: create_upload_files(record),
144
+ depositor: @depositor.user_key
145
+ }
146
+ created = import_type.new
147
+
148
+ attrs = record.attributes.merge(additional_attrs)
149
+ attrs = attrs.merge(member_of_collections_attributes: { '0' => { id: collection_id } }) if collection_id
98
150
 
99
- attributes = record.attributes.merge(uploaded_files)
100
- attributes = attributes.merge(member_of_collection_ids: [collection_id]) if collection_id
151
+ based_near = attrs.delete(:based_near)
152
+ attrs = attrs.merge(based_near_attributes: based_near_attributes(based_near)) unless based_near.nil? || based_near.empty?
101
153
 
102
- actor_env = Hyrax::Actors::Environment.new(created,
103
- ::Ability.new(@creator),
104
- attributes)
154
+ actor_env = Hyrax::Actors::Environment.new(created,
155
+ ::Ability.new(@depositor),
156
+ attrs)
105
157
 
106
158
  if Hyrax::CurationConcern.actor.create(actor_env)
107
159
  info_stream << "Record created at: #{created.id}"
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Darlingtonia
4
+ class LogStream
5
+ ##
6
+ # @!attribute [rw] logger
7
+ # @return [Logger]
8
+ # @!attribute [rw] severity
9
+ # @return [Logger::Serverity]
10
+ attr_accessor :logger, :severity
11
+
12
+ def initialize(logger: nil, severity: nil)
13
+ self.logger = logger || Logger.new(build_filename)
14
+ self.severity = severity || Logger::INFO
15
+ end
16
+
17
+ def <<(msg)
18
+ logger.add(severity, msg)
19
+ STDOUT << msg
20
+ end
21
+
22
+ private
23
+
24
+ def build_filename
25
+ return ENV['IMPORT_LOG'] if ENV['IMPORT_LOG']
26
+ return rails_log_name if rails_log_name
27
+ return './darlingtonia_import.log' unless rails_log_name
28
+ end
29
+
30
+ def rails_log_name
31
+ case Rails.env
32
+ when 'production'
33
+ Rails.root.join('log', "csv_import.log").to_s
34
+ when 'development'
35
+ Rails.root.join('log', "dev_csv_import.log").to_s
36
+ when 'test'
37
+ Rails.root.join('log', "test_csv_import.log").to_s
38
+ end
39
+ rescue NameError
40
+ false
41
+ end
42
+ end
43
+ end
@@ -14,11 +14,9 @@ class FakeParser < Darlingtonia::Parser
14
14
  end
15
15
  end
16
16
 
17
- # rubocop:disable RSpec/FilePath
18
17
  describe FakeParser do
19
18
  it_behaves_like 'a Darlingtonia::Parser' do
20
19
  subject(:parser) { described_class.new }
21
20
  let(:record_count) { 3 }
22
21
  end
23
22
  end
24
- # rubocop:enable RSpec/FilePath
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Darlingtonia
4
- VERSION = '2.0.0'
4
+ VERSION = '2.1.0'
5
5
  end
@@ -6,7 +6,7 @@ describe Darlingtonia::HyraxBasicMetadataMapper do
6
6
 
7
7
  # Properties defined in Hyrax::CoreMetadata
8
8
  let(:core_fields) do
9
- [:depositor, :title, :date_uploaded, :date_modified]
9
+ [:depositor, :title, :date_modified]
10
10
  end
11
11
 
12
12
  # Properties defined in Hyrax::BasicMetadata
@@ -49,7 +49,6 @@ describe Darlingtonia::HyraxBasicMetadataMapper do
49
49
 
50
50
  it 'returns single values for single-value fields' do
51
51
  expect(mapper.depositor).to eq 'someone@example.org'
52
- expect(mapper.date_uploaded).to eq nil
53
52
  expect(mapper.date_modified).to eq nil
54
53
  expect(mapper.label).to eq nil
55
54
  expect(mapper.relative_path).to eq nil
@@ -83,4 +82,109 @@ describe Darlingtonia::HyraxBasicMetadataMapper do
83
82
  expect(mapper.delimiter).to eq 'ಠ_ಠ'
84
83
  end
85
84
  end
85
+
86
+ describe 'lenient headers' do
87
+ context 'headers with capital letters' do
88
+ before { mapper.metadata = metadata }
89
+ let(:metadata) do
90
+ { 'Title' => 'A Title',
91
+ 'Related URL' => 'http://example.com',
92
+ 'Abstract or Summary' => 'desc1|~|desc2',
93
+ 'visiBILITY' => 'open',
94
+ 'Depositor' => 'someone@example.org',
95
+ 'DATE_modified' => 'mod date',
96
+ 'laBel' => 'label',
97
+ 'relative_PATH' => 'rel path',
98
+ 'import_URL' => 'imp url' }
99
+ end
100
+
101
+ it 'matches the correct fields' do
102
+ expect(mapper.title).to eq ['A Title']
103
+ expect(mapper.related_url).to eq ['http://example.com']
104
+ expect(mapper.description).to eq ['desc1', 'desc2']
105
+ expect(mapper.creator).to eq []
106
+ expect(mapper.visibility).to eq 'open'
107
+ expect(mapper.depositor).to eq 'someone@example.org'
108
+ expect(mapper.date_modified).to eq 'mod date'
109
+ expect(mapper.label).to eq 'label'
110
+ expect(mapper.relative_path).to eq 'rel path'
111
+ expect(mapper.import_url).to eq 'imp url'
112
+ end
113
+ end
114
+
115
+ context 'headers with sloppy whitespace' do
116
+ before { mapper.metadata = metadata }
117
+ let(:metadata) do
118
+ { ' Title ' => 'A Title',
119
+ " Related URL \n " => 'http://example.com',
120
+ ' visiBILITY ' => 'open' }
121
+ end
122
+
123
+ it 'matches the correct fields' do
124
+ expect(mapper.title).to eq ['A Title']
125
+ expect(mapper.related_url).to eq ['http://example.com']
126
+ expect(mapper.visibility).to eq 'open'
127
+ end
128
+ end
129
+
130
+ context 'Visibility values in the CSV should match the Edit UI' do
131
+ load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
132
+ include_context 'with a work type'
133
+ context 'public is a synonym for open' do
134
+ before { mapper.metadata = metadata }
135
+ let(:metadata) do
136
+ { ' Title ' => 'A Title',
137
+ " Related URL \n " => 'http://example.com',
138
+ ' visiBILITY ' => 'PubLIC' }
139
+ end
140
+
141
+ it 'transforms public to open regardless of capitalization' do
142
+ expect(mapper.title).to eq ['A Title']
143
+ expect(mapper.related_url).to eq ['http://example.com']
144
+ expect(mapper.visibility).to eq 'open'
145
+ end
146
+ end
147
+ context 'institution name is a synonym for authenticated' do
148
+ before { mapper.metadata = metadata }
149
+ let(:metadata) do
150
+ { ' Title ' => 'A Title',
151
+ " Related URL \n " => 'http://example.com',
152
+ ' visiBILITY ' => 'my_institution' }
153
+ end
154
+
155
+ it 'transforms institution name to authenticated regardless of capitalization' do
156
+ expect(mapper.title).to eq ['A Title']
157
+ expect(mapper.related_url).to eq ['http://example.com']
158
+ expect(mapper.visibility).to eq 'authenticated'
159
+ end
160
+ end
161
+ context 'full institution name is a synonym for authenticated' do
162
+ before { mapper.metadata = metadata }
163
+ let(:metadata) do
164
+ { ' Title ' => 'A Title',
165
+ " Related URL \n " => 'http://example.com',
166
+ ' visiBILITY ' => 'my full institution name' }
167
+ end
168
+
169
+ it 'transforms full institution name to authenticated regardless of capitalization' do
170
+ expect(mapper.title).to eq ['A Title']
171
+ expect(mapper.related_url).to eq ['http://example.com']
172
+ expect(mapper.visibility).to eq 'authenticated'
173
+ end
174
+ end
175
+ end
176
+
177
+ # When someone accidentally has too many commas in the CSV rows
178
+ context 'headers with a nil' do
179
+ before { mapper.metadata = metadata }
180
+ let(:metadata) do
181
+ { ' Title ' => 'A Title',
182
+ nil => nil }
183
+ end
184
+
185
+ it 'doesn\'t raise an error for missing fields' do
186
+ expect(mapper.depositor).to eq nil
187
+ end
188
+ end
189
+ end
86
190
  end
@@ -6,19 +6,19 @@ describe Darlingtonia::HyraxRecordImporter, :clean do
6
6
  described_class.new(error_stream: error_stream, info_stream: info_stream)
7
7
  end
8
8
 
9
+ load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
10
+ include_context 'with a work type'
11
+
9
12
  let(:error_stream) { [] }
10
13
  let(:info_stream) { [] }
11
14
  let(:record) { Darlingtonia::InputRecord.from(metadata: metadata) }
12
15
 
13
16
  context 'collection id' do
14
17
  subject(:importer) do
15
- described_class.new(collection_id: collection_id)
18
+ described_class.new(attributes: { collection_id: collection_id })
16
19
  end
17
20
  let(:collection_id) { '123' }
18
21
 
19
- load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
20
- include_context 'with a work type'
21
-
22
22
  it 'can have a collection id' do
23
23
  expect(importer.collection_id).to eq collection_id
24
24
  end
@@ -32,8 +32,6 @@ describe Darlingtonia::HyraxRecordImporter, :clean do
32
32
  'visibility' => 'open'
33
33
  }
34
34
  end
35
- load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
36
- include_context 'with a work type'
37
35
 
38
36
  it 'creates a work for record' do
39
37
  expect { importer.import(record: record) }
@@ -54,8 +52,6 @@ describe Darlingtonia::HyraxRecordImporter, :clean do
54
52
  'files' => 'darlingtonia.png|~|cat.png'
55
53
  }
56
54
  end
57
- load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
58
- include_context 'with a work type'
59
55
  it 'finds a file even if it is in a subdirectory' do
60
56
  expect(importer.find_file_path('cat.png')).to eq "#{ENV['IMPORT_PATH']}/animals/cat.png"
61
57
  end
@@ -71,33 +67,89 @@ describe Darlingtonia::HyraxRecordImporter, :clean do
71
67
  end
72
68
  end
73
69
 
74
- context 'with and without a depositor value' do
75
- context 'when there is no depositor set' do
76
- let(:metadata) do
77
- {
78
- 'title' => 'A Title',
79
- 'language' => 'English',
80
- 'visibility' => 'open'
81
- }
70
+ context 'with attached files, alternate capitalization and whitespace in "files" header' do
71
+ before do
72
+ ENV['IMPORT_PATH'] = File.expand_path('../fixtures/images', File.dirname(__FILE__))
73
+ end
74
+ let(:metadata) do
75
+ {
76
+ 'title' => 'A Title',
77
+ 'visibility' => 'open',
78
+ ' Files' => 'darlingtonia.png|~|cat.png'
79
+ }
80
+ end
81
+
82
+ it 'makes an uploaded file object for each file attachment' do
83
+ expect { importer.import(record: record) }
84
+ .to change { Hyrax::UploadedFile.count }
85
+ .by 2
86
+ end
87
+ end
88
+
89
+ context 'with missing files' do
90
+ before do
91
+ ENV['IMPORT_PATH'] = File.expand_path('../fixtures/images', File.dirname(__FILE__))
92
+ end
93
+ it 'raises an exception' do
94
+ expect { importer.find_file_path('foo.png') }.to raise_exception(RuntimeError)
95
+ end
96
+ end
97
+
98
+ describe '#set_depositor' do
99
+ let(:metadata) { { 'title' => 'A Title' } }
100
+
101
+ context 'when no depositor is set' do
102
+ it 'sets the Hyrax default batch user' do
103
+ expect(importer.depositor.user_key).to eq 'batchuser@example.com'
82
104
  end
83
- it 'adds the batch user as the depositor' do
84
- importer.set_depositor(record)
85
- expect(record.mapper.metadata["depositor"]).to eq "batchuser@example.com"
105
+ end
106
+
107
+ context 'when depositor is passed to initializer' do
108
+ subject(:importer) { described_class.new(error_stream: error_stream, info_stream: info_stream, attributes: { depositor_id: user.id }) }
109
+
110
+ let(:user) { ::User.new(id: '123', user_key: 'special_user@example.com') }
111
+ before { allow(::User).to receive(:find).and_return(user) }
112
+
113
+ it 'sets it to the passed-in depositor' do
114
+ expect(importer.depositor.user_key).to eq 'special_user@example.com'
86
115
  end
87
116
  end
88
- context 'when there is a depositor set' do
117
+
118
+ context 'when depositor is set in metadata' do
89
119
  let(:metadata) do
90
- {
91
- 'title' => 'A Title',
92
- 'language' => 'English',
93
- 'visibility' => 'open',
94
- 'depositor' => 'my@custom.depositor'
95
- }
120
+ { 'title' => 'A Title',
121
+ 'Depositor' => 'metadata_user@example.com' }
96
122
  end
97
- it 'resets the batch user as the depositor' do
98
- importer.set_depositor(record)
99
- expect(record.mapper.metadata["depositor"]).to eq "batchuser@example.com"
123
+
124
+ it 'sets the Hyrax default batch user' do
125
+ expect(importer.depositor.user_key).to eq 'batchuser@example.com'
126
+ # TODO: expect(importer.depositor.user_key).to eq 'metadata_user@example.com'
127
+ # The metadata depositor should probably override any passed-in or default depositor.
100
128
  end
101
129
  end
102
130
  end
131
+ # When submitting location data (a.k.a., the "based near" attribute) via the UI,
132
+ # Hyrax expects to receive a `based_near_attributes` hash in a specific format.
133
+ # We need to take geonames urls as provided by the customer and transform them to
134
+ # mimic what the Hyrax UI would ordinarily produce. These will get turned into
135
+ # Hyrax::ControlledVocabularies::Location objects upon ingest.
136
+ context 'with location uris' do
137
+ let(:based_near) { ['http://www.geonames.org/5667009/montana.html', 'http://www.geonames.org/6252001/united-states.html'] }
138
+ let(:expected_bn_hash) do
139
+ {
140
+ "0" => {
141
+ "id" => "http://sws.geonames.org/5667009/", "_destroy" => ""
142
+ },
143
+ "1" => {
144
+ "id" => "http://sws.geonames.org/6252001/", "_destroy" => ""
145
+ }
146
+ }
147
+ end
148
+ it "gets a sws uri from a geonames uri" do
149
+ expect(importer.uri_to_sws("http://www.geonames.org/6252001/united-states.html")).to eq "http://sws.geonames.org/6252001/"
150
+ end
151
+ it 'transforms an array of geonames uris into the expected based_near_attributes hash' do
152
+ expect(importer.based_near_attributes(based_near)).to eq expected_bn_hash
153
+ end
154
+ end
103
155
  end
@@ -3,6 +3,9 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Darlingtonia::Importer do
6
+ load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
7
+ include_context 'with a work type'
8
+
6
9
  subject(:importer) { described_class.new(parser: parser) }
7
10
  let(:parser) { FakeParser.new(file: input) }
8
11
  let(:input) { [{ 'title' => '1' }, { 'title' => '2' }, { 'title' => '3' }] }
@@ -3,6 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Darlingtonia::InputRecord do
6
+ load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
7
+ include_context 'with a work type'
6
8
  subject(:record) { described_class.from(metadata: metadata) }
7
9
 
8
10
  let(:metadata) do
@@ -5,21 +5,15 @@ require 'spec_helper'
5
5
  describe Darlingtonia do
6
6
  describe '#config' do
7
7
  it 'can set a default error stream' do
8
- stream = []
9
-
10
- expect { described_class.config { |c| c.default_error_stream = stream } }
8
+ expect { described_class.config { |c| c.default_error_stream = STDOUT } }
11
9
  .to change { described_class.config.default_error_stream }
12
- .from(STDOUT)
13
- .to(stream)
10
+ .to(STDOUT)
14
11
  end
15
12
 
16
13
  it 'can set a default info stream' do
17
- stream = []
18
-
19
- expect { described_class.config { |c| c.default_info_stream = stream } }
14
+ expect { described_class.config { |c| c.default_info_stream = STDOUT } }
20
15
  .to change { described_class.config.default_info_stream }
21
- .from(STDOUT)
22
- .to(stream)
16
+ .to(STDOUT)
23
17
  end
24
18
  end
25
19
  end
@@ -1,3 +1,3 @@
1
- title,depositor,date_uploaded,date_modified,label,relative_path,import_url,resource type,creator,contributor,abstract or summary,keyword,license,rights statement,publisher,date created,subject,language,identifier,location,related url,bibliographic_citation,source
2
- Work 1 Title,user@example.com,2018-12-21,2018-01-01,Work 1 Label,tmp/files,https://example.com,Work 1 Type,Work 1 creator,Work 1 contrib,Desc 1,Key 1,Lic 1,RS 1,Pub 1,2018-06-06,Subj 1,English|~|Japanese,Ident 1,Based 1,https://example.com/related,Bib 1,Source 1
3
- Work 2 Title,,1970-12-21,,Work 2 Label,,,Work 2 Type,,,Desc 2,,,,Pub 2,,Subj 2
1
+ title,depositor,date_modified,label,relative_path,import_url,resource type,creator,contributor,abstract or summary,keyword,license,rights statement,publisher,date created,subject,language,identifier,location,related url,bibliographic_citation,source
2
+ Work 1 Title,user@example.com,2018-01-01,Work 1 Label,tmp/files,https://example.com,Work 1 Type,Work 1 creator,Work 1 contrib,Desc 1,Key 1,Lic 1,RS 1,Pub 1,2018-06-06,Subj 1,English|~|Japanese,Ident 1,Based 1,https://example.com/related,Bib 1,Source 1
3
+ Work 2 Title,1970-12-21,,Work 2 Label,,,Work 2 Type,,,Desc 2,,,,Pub 2,,Subj 2
@@ -2,17 +2,13 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe 'importing a CSV with Hyrax defaults', :clean do
5
- subject(:importer) { Darlingtonia::Importer.new(parser: parser) }
5
+ subject(:importer) { Darlingtonia::Importer.new(parser: parser, record_importer: record_importer) }
6
6
  let(:parser) { Darlingtonia::CsvParser.new(file: csv_file) }
7
+ let(:record_importer) { Darlingtonia::HyraxRecordImporter.new }
7
8
 
8
9
  let(:csv_file) { File.open('spec/fixtures/hyrax/example.csv') }
9
10
  after { csv_file.close }
10
11
 
11
- before do
12
- # Force it to use the Hyrax mapper instead of the default mapper
13
- allow(Darlingtonia::HashMapper).to receive(:new).and_return(Darlingtonia::HyraxBasicMetadataMapper.new)
14
- end
15
-
16
12
  load File.expand_path("../../support/shared_contexts/with_work_type.rb", __FILE__)
17
13
  include_context 'with a work type'
18
14
 
@@ -24,8 +20,7 @@ describe 'importing a CSV with Hyrax defaults', :clean do
24
20
  work2 = works.find { |w| w.title == ['Work 2 Title'] }
25
21
 
26
22
  # First Record
27
- expect(work1.depositor).to eq 'user@example.com'
28
- expect(work1.date_uploaded).to eq '2018-12-21'
23
+ expect(work1.depositor).to eq 'batchuser@example.com'
29
24
  expect(work1.date_modified).to eq '2018-01-01'
30
25
  expect(work1.label).to eq 'Work 1 Label'
31
26
  expect(work1.relative_path).to eq 'tmp/files'
@@ -51,8 +46,7 @@ describe 'importing a CSV with Hyrax defaults', :clean do
51
46
  expect(work1.source).to eq ['Source 1']
52
47
 
53
48
  # Second Record
54
- expect(work2.depositor).to be_nil
55
- expect(work2.date_uploaded).to eq '1970-12-21'
49
+ expect(work2.depositor).to eq 'batchuser@example.com'
56
50
  expect(work2.date_modified).to be_nil
57
51
  expect(work2.label).to eq 'Work 2 Label'
58
52
  expect(work2.relative_path).to be_nil
@@ -8,16 +8,22 @@ shared_context 'with a work type' do
8
8
 
9
9
  class Work < ActiveFedora::Base
10
10
  attr_accessor :visibility
11
+ attr_accessor :based_near_attributes
11
12
  include ::Hyrax::CoreMetadata
12
13
  include ::Hyrax::BasicMetadata
13
14
  end
14
15
 
15
- class User
16
+ class User < Struct.new(:id, :user_key)
17
+ def initialize(inputs = {})
18
+ self.id = inputs[:id]
19
+ self.user_key = inputs[:user_key] || batch_user_key
20
+ end
21
+
16
22
  def self.find_or_create_system_user(_email)
17
23
  User.new
18
24
  end
19
25
 
20
- def user_key
26
+ def batch_user_key
21
27
  'batchuser@example.com'
22
28
  end
23
29
  end
@@ -37,6 +43,16 @@ shared_context 'with a work type' do
37
43
  end
38
44
  end
39
45
 
46
+ class Institution
47
+ def self.name
48
+ 'my_institution'
49
+ end
50
+
51
+ def self.name_full
52
+ 'my full institution name'
53
+ end
54
+ end
55
+
40
56
  class UploadedFile < ActiveFedora::Base
41
57
  def self.create(*)
42
58
  h = Hyrax::UploadedFile.new
@@ -47,14 +63,20 @@ shared_context 'with a work type' do
47
63
 
48
64
  module Actors
49
65
  class Environment
50
- def initialize(new_object, ability, attributes); end
66
+ attr_reader :new_object, :attributes
67
+ def initialize(new_object, _ability, attributes)
68
+ @new_object = new_object
69
+ @attributes = attributes
70
+ end
51
71
  end
52
72
  end
53
73
 
54
74
  class Actor
55
- def create(_actor_env)
56
- Work.create
57
- true
75
+ def create(actor_env)
76
+ attrs = actor_env.attributes
77
+ attrs.delete(:uploaded_files)
78
+ actor_env.new_object.attributes = attrs
79
+ actor_env.new_object.save
58
80
  end
59
81
  end
60
82
 
@@ -68,6 +90,8 @@ shared_context 'with a work type' do
68
90
 
69
91
  after do
70
92
  Object.send(:remove_const, :Hyrax) if defined?(Hyrax)
71
- Object.send(:remove_const, :Work) if defined?(Work)
93
+ Object.send(:remove_const, :Work) if defined?(Work)
94
+ Object.send(:remove_const, :User) if defined?(User)
95
+ Object.send(:remove_const, :Ability) if defined?(Ability)
72
96
  end
73
97
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: darlingtonia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Data Curation Experts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-04 00:00:00.000000000 Z
11
+ date: 2019-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active-fedora
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.3'
47
+ version: '1.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.3'
54
+ version: '1.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: hyrax-spec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rake
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
139
153
  description:
140
154
  email:
141
155
  - administrator@curationexperts.com
@@ -160,6 +174,7 @@ files:
160
174
  - lib/darlingtonia/hyrax_record_importer.rb
161
175
  - lib/darlingtonia/importer.rb
162
176
  - lib/darlingtonia/input_record.rb
177
+ - lib/darlingtonia/log_stream.rb
163
178
  - lib/darlingtonia/metadata_mapper.rb
164
179
  - lib/darlingtonia/parser.rb
165
180
  - lib/darlingtonia/parsers/csv_parser.rb