darlingtonia 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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