datacite 0.7.0 → 0.8.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 +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +118 -17
- data/README.md +20 -0
- data/datacite.gemspec +2 -0
- data/lib/datacite/mapping/from_cocina/alternate_identifiers.rb +30 -0
- data/lib/datacite/mapping/from_cocina/attributes.rb +74 -0
- data/lib/datacite/mapping/from_cocina/contributor_attributes.rb +209 -0
- data/lib/datacite/mapping/from_cocina/date.rb +198 -0
- data/lib/datacite/mapping/from_cocina/descriptions.rb +41 -0
- data/lib/datacite/mapping/from_cocina/identifiers.rb +28 -0
- data/lib/datacite/mapping/from_cocina/related_resource.rb +168 -0
- data/lib/datacite/mapping/from_cocina/rights_list.rb +36 -0
- data/lib/datacite/mapping/from_cocina/subject.rb +68 -0
- data/lib/datacite/mapping/from_cocina/titles.rb +32 -0
- data/lib/datacite/mapping/from_cocina/types.rb +81 -0
- data/lib/datacite/schema/datacite-v4.6.json +643 -0
- data/lib/datacite/validators/attributes_validator.rb +43 -0
- data/lib/datacite/validators/cocina_validator.rb +26 -0
- data/lib/datacite/version.rb +1 -1
- metadata +45 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70e80d4615ecf31ae69938f5008ee396db504bd2e4ce7637816bb5df5c4038cd
|
|
4
|
+
data.tar.gz: ef72c4e1491a8567f087e1fa889e68c5cfba68a96070f324cbe4f670dda70967
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 447c41b1b2d1cc567fb9f9aa854e249b56d31f9f8646f8e859af461dad8931e381b611eb5f346b129868677d1773b3d84f356fa5ed3b1cc1895df88021c8e1f7
|
|
7
|
+
data.tar.gz: '085d1ddcb4ed62ab36480fb1875e73f71b29245e16fd8e8d7746df321d6a89df6a01156902ed639962f79e9a8bec0ce2b10a51e3137001404118d7633eade4fc'
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,59 +1,148 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
datacite (0.
|
|
4
|
+
datacite (0.8.0)
|
|
5
|
+
activesupport
|
|
5
6
|
dry-monads (~> 1.3)
|
|
6
7
|
faraday (~> 2.0)
|
|
8
|
+
json_schemer
|
|
7
9
|
zeitwerk (~> 2.4)
|
|
8
10
|
|
|
9
11
|
GEM
|
|
10
12
|
remote: https://rubygems.org/
|
|
11
13
|
specs:
|
|
14
|
+
activesupport (8.1.1)
|
|
15
|
+
base64
|
|
16
|
+
bigdecimal
|
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
18
|
+
connection_pool (>= 2.2.5)
|
|
19
|
+
drb
|
|
20
|
+
i18n (>= 1.6, < 2)
|
|
21
|
+
json
|
|
22
|
+
logger (>= 1.4.2)
|
|
23
|
+
minitest (>= 5.1)
|
|
24
|
+
securerandom (>= 0.3)
|
|
25
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
26
|
+
uri (>= 0.13.1)
|
|
12
27
|
addressable (2.8.7)
|
|
13
28
|
public_suffix (>= 2.0.2, < 7.0)
|
|
14
29
|
ast (2.4.3)
|
|
30
|
+
attr_extras (7.1.0)
|
|
15
31
|
base64 (0.3.0)
|
|
16
32
|
bigdecimal (3.3.1)
|
|
17
33
|
byebug (12.0.0)
|
|
34
|
+
cocina-models (0.108.2)
|
|
35
|
+
activesupport
|
|
36
|
+
deprecation
|
|
37
|
+
dry-struct (~> 1.0)
|
|
38
|
+
dry-types (~> 1.1)
|
|
39
|
+
edtf
|
|
40
|
+
equivalent-xml
|
|
41
|
+
i18n
|
|
42
|
+
jsonpath
|
|
43
|
+
nokogiri
|
|
44
|
+
openapi_parser (~> 1.0)
|
|
45
|
+
super_diff
|
|
46
|
+
thor
|
|
47
|
+
zeitwerk (~> 2.1)
|
|
18
48
|
concurrent-ruby (1.3.5)
|
|
19
|
-
|
|
49
|
+
connection_pool (2.5.4)
|
|
50
|
+
crack (1.0.1)
|
|
20
51
|
bigdecimal
|
|
21
52
|
rexml
|
|
53
|
+
deprecation (1.1.0)
|
|
54
|
+
activesupport
|
|
22
55
|
diff-lcs (1.6.2)
|
|
23
56
|
docile (1.4.1)
|
|
57
|
+
drb (2.2.3)
|
|
24
58
|
dry-core (1.1.0)
|
|
25
59
|
concurrent-ruby (~> 1.0)
|
|
26
60
|
logger
|
|
27
61
|
zeitwerk (~> 2.6)
|
|
62
|
+
dry-inflector (1.2.0)
|
|
63
|
+
dry-logic (1.6.0)
|
|
64
|
+
bigdecimal
|
|
65
|
+
concurrent-ruby (~> 1.0)
|
|
66
|
+
dry-core (~> 1.1)
|
|
67
|
+
zeitwerk (~> 2.6)
|
|
28
68
|
dry-monads (1.9.0)
|
|
29
69
|
concurrent-ruby (~> 1.0)
|
|
30
70
|
dry-core (~> 1.1)
|
|
31
71
|
zeitwerk (~> 2.6)
|
|
72
|
+
dry-struct (1.8.0)
|
|
73
|
+
dry-core (~> 1.1)
|
|
74
|
+
dry-types (~> 1.8, >= 1.8.2)
|
|
75
|
+
ice_nine (~> 0.11)
|
|
76
|
+
zeitwerk (~> 2.6)
|
|
77
|
+
dry-types (1.8.3)
|
|
78
|
+
bigdecimal (~> 3.0)
|
|
79
|
+
concurrent-ruby (~> 1.0)
|
|
80
|
+
dry-core (~> 1.0)
|
|
81
|
+
dry-inflector (~> 1.0)
|
|
82
|
+
dry-logic (~> 1.4)
|
|
83
|
+
zeitwerk (~> 2.6)
|
|
84
|
+
edtf (3.2.0)
|
|
85
|
+
activesupport (>= 3.0, < 9.0)
|
|
86
|
+
equivalent-xml (0.6.0)
|
|
87
|
+
nokogiri (>= 1.4.3)
|
|
32
88
|
faraday (2.14.0)
|
|
33
89
|
faraday-net_http (>= 2.0, < 3.5)
|
|
34
90
|
json
|
|
35
91
|
logger
|
|
36
92
|
faraday-net_http (3.4.1)
|
|
37
93
|
net-http (>= 0.5.0)
|
|
94
|
+
hana (1.3.7)
|
|
38
95
|
hashdiff (1.2.1)
|
|
39
|
-
|
|
96
|
+
i18n (1.14.7)
|
|
97
|
+
concurrent-ruby (~> 1.0)
|
|
98
|
+
ice_nine (0.11.2)
|
|
99
|
+
json (2.15.2)
|
|
100
|
+
json_schemer (2.4.0)
|
|
101
|
+
bigdecimal
|
|
102
|
+
hana (~> 1.3)
|
|
103
|
+
regexp_parser (~> 2.0)
|
|
104
|
+
simpleidn (~> 0.2)
|
|
105
|
+
jsonpath (1.1.5)
|
|
106
|
+
multi_json
|
|
40
107
|
language_server-protocol (3.17.0.5)
|
|
41
108
|
lint_roller (1.1.0)
|
|
42
109
|
logger (1.7.0)
|
|
43
|
-
|
|
110
|
+
minitest (5.26.0)
|
|
111
|
+
multi_json (1.17.0)
|
|
112
|
+
net-http (0.7.0)
|
|
44
113
|
uri
|
|
114
|
+
nokogiri (1.18.10-aarch64-linux-gnu)
|
|
115
|
+
racc (~> 1.4)
|
|
116
|
+
nokogiri (1.18.10-aarch64-linux-musl)
|
|
117
|
+
racc (~> 1.4)
|
|
118
|
+
nokogiri (1.18.10-arm-linux-gnu)
|
|
119
|
+
racc (~> 1.4)
|
|
120
|
+
nokogiri (1.18.10-arm-linux-musl)
|
|
121
|
+
racc (~> 1.4)
|
|
122
|
+
nokogiri (1.18.10-arm64-darwin)
|
|
123
|
+
racc (~> 1.4)
|
|
124
|
+
nokogiri (1.18.10-x86_64-darwin)
|
|
125
|
+
racc (~> 1.4)
|
|
126
|
+
nokogiri (1.18.10-x86_64-linux-gnu)
|
|
127
|
+
racc (~> 1.4)
|
|
128
|
+
nokogiri (1.18.10-x86_64-linux-musl)
|
|
129
|
+
racc (~> 1.4)
|
|
130
|
+
openapi_parser (1.0.0)
|
|
131
|
+
optimist (3.2.1)
|
|
45
132
|
parallel (1.27.0)
|
|
46
|
-
parser (3.3.
|
|
133
|
+
parser (3.3.10.0)
|
|
47
134
|
ast (~> 2.4.1)
|
|
48
135
|
racc
|
|
136
|
+
patience_diff (1.2.0)
|
|
137
|
+
optimist (~> 3.0)
|
|
49
138
|
prism (1.6.0)
|
|
50
139
|
public_suffix (6.0.2)
|
|
51
140
|
racc (1.8.1)
|
|
52
141
|
rainbow (3.1.1)
|
|
53
|
-
rake (13.3.
|
|
142
|
+
rake (13.3.1)
|
|
54
143
|
regexp_parser (2.11.3)
|
|
55
144
|
rexml (3.4.4)
|
|
56
|
-
rspec (3.13.
|
|
145
|
+
rspec (3.13.2)
|
|
57
146
|
rspec-core (~> 3.13.0)
|
|
58
147
|
rspec-expectations (~> 3.13.0)
|
|
59
148
|
rspec-mocks (~> 3.13.0)
|
|
@@ -62,11 +151,11 @@ GEM
|
|
|
62
151
|
rspec-expectations (3.13.5)
|
|
63
152
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
64
153
|
rspec-support (~> 3.13.0)
|
|
65
|
-
rspec-mocks (3.13.
|
|
154
|
+
rspec-mocks (3.13.7)
|
|
66
155
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
67
156
|
rspec-support (~> 3.13.0)
|
|
68
157
|
rspec-support (3.13.6)
|
|
69
|
-
rubocop (1.81.
|
|
158
|
+
rubocop (1.81.7)
|
|
70
159
|
json (~> 2.3)
|
|
71
160
|
language_server-protocol (~> 3.17.0.2)
|
|
72
161
|
lint_roller (~> 1.1.0)
|
|
@@ -87,33 +176,45 @@ GEM
|
|
|
87
176
|
lint_roller (~> 1.1)
|
|
88
177
|
rubocop (~> 1.72, >= 1.72.1)
|
|
89
178
|
ruby-progressbar (1.13.0)
|
|
179
|
+
securerandom (0.4.1)
|
|
90
180
|
simplecov (0.22.0)
|
|
91
181
|
docile (~> 1.1)
|
|
92
182
|
simplecov-html (~> 0.11)
|
|
93
183
|
simplecov_json_formatter (~> 0.1)
|
|
94
184
|
simplecov-html (0.13.2)
|
|
95
185
|
simplecov_json_formatter (0.1.4)
|
|
186
|
+
simpleidn (0.2.3)
|
|
187
|
+
super_diff (0.17.0)
|
|
188
|
+
attr_extras (>= 6.2.4)
|
|
189
|
+
diff-lcs
|
|
190
|
+
patience_diff
|
|
191
|
+
thor (1.4.0)
|
|
192
|
+
tzinfo (2.0.6)
|
|
193
|
+
concurrent-ruby (~> 1.0)
|
|
96
194
|
unicode-display_width (3.2.0)
|
|
97
195
|
unicode-emoji (~> 4.1)
|
|
98
196
|
unicode-emoji (4.1.0)
|
|
99
|
-
uri (1.
|
|
100
|
-
webmock (3.
|
|
197
|
+
uri (1.1.1)
|
|
198
|
+
webmock (3.26.1)
|
|
101
199
|
addressable (>= 2.8.0)
|
|
102
200
|
crack (>= 0.3.2)
|
|
103
201
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
104
202
|
zeitwerk (2.7.3)
|
|
105
203
|
|
|
106
204
|
PLATFORMS
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
x86_64-
|
|
205
|
+
aarch64-linux-gnu
|
|
206
|
+
aarch64-linux-musl
|
|
207
|
+
arm-linux-gnu
|
|
208
|
+
arm-linux-musl
|
|
209
|
+
arm64-darwin
|
|
210
|
+
x86_64-darwin
|
|
211
|
+
x86_64-linux-gnu
|
|
212
|
+
x86_64-linux-musl
|
|
113
213
|
|
|
114
214
|
DEPENDENCIES
|
|
115
215
|
base64
|
|
116
216
|
byebug
|
|
217
|
+
cocina-models
|
|
117
218
|
datacite!
|
|
118
219
|
rake (~> 13.0)
|
|
119
220
|
rspec (~> 3.0)
|
data/README.md
CHANGED
|
@@ -97,6 +97,26 @@ result.either(
|
|
|
97
97
|
)
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
## Working with Cocina
|
|
101
|
+
|
|
102
|
+
### Validating
|
|
103
|
+
|
|
104
|
+
This gem provides a method for mapping and validating a `Cocina::Models::DRO` object to a DataCite request based on the DataCite JSON Schema (v4.6), without sending any data to the DataCite API:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
cocina_object = Cocina::Models::DRO.new(...)
|
|
108
|
+
validator = Datacite::Validators::CocinaValidator.new(cocina_object:)
|
|
109
|
+
puts validator.errors.join(', ') unless validator.valid?
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
NOTE: A Datacite request payload can be validated without first translating from Cocina if you build the request manually or use your own metadata mapping library, e.g.:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
datacite_attributes = { ... }
|
|
116
|
+
validator = Datacite::Validators::AttributesValidator.new(attributes: datacite_attributes)
|
|
117
|
+
puts validator.errors.join(', ') unless validator.valid?
|
|
118
|
+
```
|
|
119
|
+
|
|
100
120
|
## Development
|
|
101
121
|
|
|
102
122
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/datacite.gemspec
CHANGED
|
@@ -28,8 +28,10 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
29
29
|
spec.require_paths = ['lib']
|
|
30
30
|
|
|
31
|
+
spec.add_dependency 'activesupport'
|
|
31
32
|
spec.add_dependency 'dry-monads', '~> 1.3'
|
|
32
33
|
spec.add_dependency 'faraday', '~> 2.0'
|
|
34
|
+
spec.add_dependency 'json_schemer'
|
|
33
35
|
spec.add_dependency 'zeitwerk', '~> 2.4'
|
|
34
36
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
35
37
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datacite
|
|
4
|
+
module Mapping
|
|
5
|
+
module FromCocina
|
|
6
|
+
# Maps alternative identifiers from cocina description to DataCite JSON
|
|
7
|
+
class AlternateIdentifiers
|
|
8
|
+
# @param [Cocina::Models::Description] description
|
|
9
|
+
def self.build(...)
|
|
10
|
+
new(...).call
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(description:)
|
|
14
|
+
@description = description
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :description
|
|
18
|
+
|
|
19
|
+
delegate :purl, to: :description
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
[{
|
|
23
|
+
alternateIdentifier: purl,
|
|
24
|
+
alternateIdentifierType: 'PURL'
|
|
25
|
+
}]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/all'
|
|
4
|
+
require 'cocina/models'
|
|
5
|
+
|
|
6
|
+
module Datacite
|
|
7
|
+
module Mapping
|
|
8
|
+
module FromCocina
|
|
9
|
+
# Transform the Cocina::Models::DRO to a DataCite request attributes payload
|
|
10
|
+
class Attributes
|
|
11
|
+
# @param [Cocina::Models::DRO] cocina_object
|
|
12
|
+
# @return [Hash] Hash of DataCite attributes, conforming to the DataCite API Schema
|
|
13
|
+
def self.build(...)
|
|
14
|
+
new(...).call
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(cocina_object:)
|
|
18
|
+
@cocina_object = cocina_object
|
|
19
|
+
|
|
20
|
+
# Set the time zone
|
|
21
|
+
Time.zone = 'Pacific Time (US & Canada)'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :cocina_object
|
|
25
|
+
|
|
26
|
+
delegate :access, :description, :identification, to: :cocina_object
|
|
27
|
+
|
|
28
|
+
def call # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
29
|
+
{
|
|
30
|
+
event: 'publish',
|
|
31
|
+
url: description.purl,
|
|
32
|
+
identifiers: Identifiers.build(identification:),
|
|
33
|
+
titles: Titles.build(description:),
|
|
34
|
+
publisher: { name: 'Stanford Digital Repository' }, # per DataCite schema
|
|
35
|
+
publicationYear: publication_year,
|
|
36
|
+
subjects: Subject.build(description:),
|
|
37
|
+
dates: Date.build(cocina_object:),
|
|
38
|
+
language: 'en',
|
|
39
|
+
types: Types.build(description:),
|
|
40
|
+
alternateIdentifiers: AlternateIdentifiers.build(description:),
|
|
41
|
+
relatedIdentifiers: related_identifiers,
|
|
42
|
+
rightsList: RightsList.build(access:),
|
|
43
|
+
descriptions: Descriptions.build(description:),
|
|
44
|
+
relatedItems: related_items,
|
|
45
|
+
schemaVersion: 'http://datacite.org/schema/kernel-4'
|
|
46
|
+
}.merge(ContributorAttributes.build(description:)).compact
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def publication_year
|
|
52
|
+
date = if access.embargo
|
|
53
|
+
access.embargo.releaseDate.to_datetime
|
|
54
|
+
else
|
|
55
|
+
Time.zone.today
|
|
56
|
+
end
|
|
57
|
+
date.year.to_s
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def related_identifiers
|
|
61
|
+
Array(description&.relatedResource).filter_map do |related_resource|
|
|
62
|
+
RelatedResource.related_identifier_attributes(related_resource:)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def related_items
|
|
67
|
+
Array(description&.relatedResource).filter_map do |related_resource|
|
|
68
|
+
RelatedResource.related_item_attributes(related_resource:)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datacite
|
|
4
|
+
module Mapping
|
|
5
|
+
module FromCocina
|
|
6
|
+
# Transform the Cocina::Models::Description to contributor attributes
|
|
7
|
+
# see https://support.datacite.org/reference/dois-2#put_dois-id
|
|
8
|
+
class ContributorAttributes # rubocop:disable Metrics/ClassLength
|
|
9
|
+
DATACITE_PERSON_CONTRIBUTOR_TYPES = {
|
|
10
|
+
'copyright holder' => 'RightsHolder',
|
|
11
|
+
'compiler' => 'DataCollector',
|
|
12
|
+
'editor' => 'Editor',
|
|
13
|
+
'organizer' => 'Supervisor',
|
|
14
|
+
'research team head' => 'ProjectLeader',
|
|
15
|
+
'researcher' => 'Researcher'
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
DATACITE_ORGANIZATION_CONTRIBUTOR_TYPES = {
|
|
19
|
+
'copyright holder' => 'RightsHolder',
|
|
20
|
+
'compiler' => 'DataCollector',
|
|
21
|
+
'distributor' => 'Distributor',
|
|
22
|
+
'host institution' => 'HostingInstitution',
|
|
23
|
+
'issuing body' => 'Distributor',
|
|
24
|
+
'publisher' => 'Distributor',
|
|
25
|
+
'researcher' => 'ResearchGroup',
|
|
26
|
+
'sponsor' => 'Sponsor'
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# @param [Cocina::Models::Description] description
|
|
30
|
+
# @return [Hash] Hash of DataCite attributes containing creators, contributors, and fundingReferences keys
|
|
31
|
+
def self.build(...)
|
|
32
|
+
new(...).call
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialize(description:)
|
|
36
|
+
@description = description
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Hash] Hash of DataCite attributes containing creators, contributors, and fundingReferences keys
|
|
40
|
+
def call
|
|
41
|
+
{
|
|
42
|
+
creators: datacite_creators,
|
|
43
|
+
contributors: datacite_contributors,
|
|
44
|
+
fundingReferences: datacite_funders
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
attr_reader :description
|
|
51
|
+
|
|
52
|
+
def cocina_creators
|
|
53
|
+
@cocina_creators ||= Array(description.contributor).select do |cocina_contributor|
|
|
54
|
+
datacite_creator?(cocina_contributor)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def cocina_contributors
|
|
59
|
+
@cocina_contributors ||= Array(description.contributor).select do |cocina_contributor|
|
|
60
|
+
datacite_contributor?(cocina_contributor)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def cocina_funders
|
|
65
|
+
@cocina_funders ||= Array(description.contributor).select do |cocina_contributor|
|
|
66
|
+
datacite_funder?(cocina_contributor)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def datacite_creator?(cocina_contributor)
|
|
71
|
+
!datacite_funder?(cocina_contributor) && !datacite_contributor?(cocina_contributor)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def datacite_funder?(cocina_contributor)
|
|
75
|
+
marc_relator(cocina_contributor) == 'funder'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def datacite_contributor?(cocina_contributor)
|
|
79
|
+
marc_relator(cocina_contributor) == 'publisher' || degree_committee_member?(cocina_contributor)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def datacite_creators
|
|
83
|
+
@datacite_creators ||= cocina_creators.map { |cocina_creator| datacite_creator(cocina_creator) }.uniq
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def datacite_contributors
|
|
87
|
+
@datacite_contributors ||= cocina_contributors.map do |cocina_contributor|
|
|
88
|
+
datacite_contributor(cocina_contributor)
|
|
89
|
+
end.uniq
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def datacite_funders
|
|
93
|
+
@datacite_funders ||= cocina_funders.map { |cocina_funder| { funderName: cocina_funder.name.first.value } }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def datacite_creator(cocina_contributor)
|
|
97
|
+
return personal_name(cocina_contributor) if person?(cocina_contributor)
|
|
98
|
+
|
|
99
|
+
organizational_name(cocina_contributor)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def person?(cocina_contributor)
|
|
103
|
+
cocina_contributor.type == 'person'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def datacite_contributor(cocina_contributor)
|
|
107
|
+
datacite_creator(cocina_contributor).merge({ contributorType: contributor_type(cocina_contributor) })
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def personal_name(cocina_contributor) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
111
|
+
{
|
|
112
|
+
nameType: 'Personal',
|
|
113
|
+
nameIdentifiers: name_identifiers(cocina_contributor).presence,
|
|
114
|
+
affiliation: affiliations(cocina_contributor).presence
|
|
115
|
+
}.tap do |name_hash|
|
|
116
|
+
# NOTE: This is needed for ETDs, for which we do not receive structured
|
|
117
|
+
# contributor names from Axess for ETD readers
|
|
118
|
+
if cocina_contributor.name.first.structuredValue.empty?
|
|
119
|
+
name_hash[:name] = cocina_contributor.name.first.value
|
|
120
|
+
elsif (name = cocina_contributor.name.first.structuredValue.find { |part| part.type == 'name' }).present?
|
|
121
|
+
name_hash[:name] = name.value
|
|
122
|
+
else
|
|
123
|
+
forename = cocina_contributor.name.first.structuredValue.find { |part| part.type == 'forename' }
|
|
124
|
+
surname = cocina_contributor.name.first.structuredValue.find { |part| part.type == 'surname' }
|
|
125
|
+
|
|
126
|
+
name_hash[:name] = "#{surname.value}, #{forename.value}"
|
|
127
|
+
name_hash[:givenName] = forename.value
|
|
128
|
+
name_hash[:familyName] = surname.value
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
.compact
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def organizational_name(cocina_contributor)
|
|
135
|
+
name = cocina_contributor.name.first.structuredValue.first || cocina_contributor.name.first
|
|
136
|
+
{
|
|
137
|
+
name: name.value,
|
|
138
|
+
nameType: 'Organizational',
|
|
139
|
+
nameIdentifiers: name_identifiers(name).presence
|
|
140
|
+
}.compact
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def name_identifiers(cocina_contributor)
|
|
144
|
+
Array(cocina_contributor.identifier).map do |identifier|
|
|
145
|
+
{
|
|
146
|
+
nameIdentifier: identifier.value || identifier.uri,
|
|
147
|
+
nameIdentifierScheme: identifier.type,
|
|
148
|
+
schemeURI: identifier.source.uri
|
|
149
|
+
}.compact
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def affiliations(cocina_contributor) # rubocop:disable Metrics/MethodLength
|
|
154
|
+
Array(cocina_contributor.affiliation).map do |affiliation|
|
|
155
|
+
institution = affiliation.structuredValue.find { |descriptive_value| descriptive_value.identifier.present? }
|
|
156
|
+
institution ||= affiliation # if no structured value with identifier, use the affiliation itself
|
|
157
|
+
identifier = institution.identifier.find { |id| id.type == 'ROR' }
|
|
158
|
+
next unless identifier&.uri
|
|
159
|
+
|
|
160
|
+
{
|
|
161
|
+
affiliationIdentifier: identifier.uri,
|
|
162
|
+
affiliationIdentifierScheme: 'ROR',
|
|
163
|
+
name: institution.value,
|
|
164
|
+
schemeUri: 'https://ror.org/'
|
|
165
|
+
}.compact
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def contributor_type(cocina_contributor)
|
|
170
|
+
if person?(cocina_contributor)
|
|
171
|
+
return DATACITE_PERSON_CONTRIBUTOR_TYPES.fetch(marc_relator(cocina_contributor),
|
|
172
|
+
'Other')
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
DATACITE_ORGANIZATION_CONTRIBUTOR_TYPES.fetch(marc_relator(cocina_contributor), 'Other')
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# NOTE: This is how ETDs map to Cocina by way of MARC21 to MODS, where the
|
|
179
|
+
# 700$e and 700$4 subfields cannot be interpreted as pertaining to
|
|
180
|
+
# the same role, so the Cocina winds up expressing this as two
|
|
181
|
+
# roles, e.g.:
|
|
182
|
+
#
|
|
183
|
+
# role: [
|
|
184
|
+
# {
|
|
185
|
+
# value: "degree committee member",
|
|
186
|
+
# },
|
|
187
|
+
# {
|
|
188
|
+
# code: "ths",
|
|
189
|
+
# source: {
|
|
190
|
+
# code: "marcrelator",
|
|
191
|
+
# },
|
|
192
|
+
# }
|
|
193
|
+
# ]
|
|
194
|
+
def degree_committee_member?(cocina_contributor)
|
|
195
|
+
cocina_contributor.role.any? { |contrib_role| contrib_role.value == 'degree committee member' } &&
|
|
196
|
+
cocina_contributor.role.any? do |contrib_role|
|
|
197
|
+
contrib_role.code == 'ths' && contrib_role.source.code == 'marcrelator'
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def marc_relator(cocina_contributor)
|
|
202
|
+
Array(cocina_contributor.role).find do |role|
|
|
203
|
+
role&.source&.code == 'marcrelator'
|
|
204
|
+
end&.value
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|