uc3-dmp-id 0.1.1 → 0.1.2

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: 2d78c65551789e0b3e96488bb2c3689999a301c76a8434f945f7b855dd62d57d
4
- data.tar.gz: 73fe134b92ebe24bf9595610f3ef9e7f8dc744a3494152d9e7a6e7e73675b075
3
+ metadata.gz: 51832c144e5663dc01c805f92e81e50e63f2ce00200a8cad5525b7a34c9d7eb9
4
+ data.tar.gz: e197deb7f608ef478716a8aea113e853ab0d3903f1496f970a73d31d30b7e892
5
5
  SHA512:
6
- metadata.gz: 9fb32754f0e36c6292860e35327a9af48b566bae061eb0d08a8e4f94b9976144acae3b948837f7b1835e918d20d06971085091db81d5483ba494c51fd5279d68
7
- data.tar.gz: 889bd0b3cbe91b95a534930b267625b38fe988381cf7b76e6fd73e53d4d73796b5abe3af8bd81ae322b1c7f1e17c9b9f3ea0f59ba00f3b9deeaecb3a15a131ed
6
+ metadata.gz: 31bc5d1bb73176c2afff25715590c50d0612f558880f2bde3a750ac5ea8d49674c5dd9b00eff36960454c972a1a31aca6b4f853a98e995dd65977e1f82903ad2
7
+ data.tar.gz: 86153dfeebc52570ecb83a012f4e7242e9bec607627c2dcefc5beb101a711199bc346c1388045127df89f13e65c83aa473c0fc77e37810d436ea939101b74f2b
@@ -168,7 +168,8 @@ module Uc3DmpId
168
168
  # "id": "ABCD1234",
169
169
  # "provenance": "dmphub",
170
170
  # "timestamp": "2023-07-07T14:50:23+00:00",
171
- # "note": "data received from the NIH API",
171
+ # "note": "Data received from OpenAlex, matched by PI names and title keywords.",
172
+ # "confiedence": "Med",
172
173
  # "dmproadmap_related_identifiers": {
173
174
  # "work_type": "article",
174
175
  # "descriptor": "is_cited_by",
@@ -183,7 +184,8 @@ module Uc3DmpId
183
184
  # "id": "ABCD1234",
184
185
  # "provenance": "dmphub",
185
186
  # "timestamp": "2023-07-07T14:50:23+00:00",
186
- # "note": "data received from the NIH API",
187
+ # "note": "Data received from the NIH API, matched by the opportunity number.",
188
+ # "confidence": "High",
187
189
  # "funding": {
188
190
  # "funding_status": "granted",
189
191
  # "grant_id": {
@@ -206,5 +208,13 @@ module Uc3DmpId
206
208
  JSON.parse(assertion.to_json)
207
209
  end
208
210
  end
211
+
212
+ def _score_related_work(latest_version:, work:)
213
+
214
+ end
215
+
216
+ def _score_funding(latest_version:, funding:)
217
+
218
+ end
209
219
  end
210
220
  end
@@ -0,0 +1,500 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'text'
4
+
5
+ module Uc3DmpId
6
+ class ComparatorError < StandardError; end
7
+
8
+ # Class that compares incoming data from an external source to the DMP
9
+ # It determines if they are likely related and applies a confidence rating
10
+ class Comparator
11
+
12
+ MSG_MISSING_AUGMENTER = 'No Augmenter specified!'
13
+ MSG_MISSING_DMP = 'No DMP or the DMP did not contain enough information to use.'
14
+
15
+ STOP_WORDS = %w[a an and if of or the then they]
16
+
17
+ # See the bottom of this file for a hard-coded crosswalk between Crossref funder ids and ROR ids
18
+ # Some APIs do not support ROR fully for funder ids, so we need to be able to reference both
19
+
20
+ attr_accessor :augmenter, :dmp, :details_hash, :logger
21
+
22
+ def initialize(**args)
23
+ @logger = args[:logger]
24
+ @details_hash = {}
25
+
26
+ @augmenter = args[:augmenter]
27
+ raise ComparatorError, MSG_MISSING_AUGMENTER if @augmenter.nil? ||
28
+ !@augmenter['PK']&.start_with?('AUGMENTERS#')
29
+
30
+ @dmp = args.fetch(:dmp, {})['dmp'].nil? ? args[:dmp] : args.fetch(:dmp, {})['dmp']
31
+ _extract_dmp_details(dmp:)
32
+ raise ComparatorError, MSG_MISSING_DMP if @details_hash.empty?
33
+ end
34
+
35
+ # Compare the incoming hash with the DMP details that were gathered during initialization.
36
+ #
37
+ # The Hash should contain:
38
+ # {
39
+ # title: "Example research project",
40
+ # abstract: "Lorem ipsum psuedo abstract",
41
+ # keywords: ["foo", "bar"],z
42
+ # people: [
43
+ # {
44
+ # id: "https://orcid.org/blah",
45
+ # last_name: "doe",
46
+ # affiliation: { id: "https://ror.org/blah", name: "Foo" }
47
+ # }
48
+ # ],
49
+ # fundings: [
50
+ # { id: "https://doi.org/crossref123", name: "Bar", grant: ["1234", "http://foo.bar/543"] }
51
+ # ],
52
+ # repositories: [
53
+ # { id: ["http://some.repo.org", "https://doi.org/re3data123"], name: "Repo" }
54
+ # ]
55
+ # }
56
+ def compare(hash:)
57
+ response = { confidence: 'None', score: 0, notes: [], source: @augmenter['name'] }
58
+ return response unless hash.is_a?(Hash) && !hash['title'].nil?
59
+
60
+ # Compare the grant ids. If we have a match return the response immediately since that is
61
+ # a very positive match!
62
+ response = _grants_match?(array: hash['fundings'], response:)
63
+ return response if response[:confidence] != 'None'
64
+
65
+ response = _opportunities_match?(array: hash['fundings'], response:)
66
+ response = _orcids_match?(array: hash['people'], response:)
67
+ response = _last_name_and_affiliation_match?(array: hash['people'], response:)
68
+
69
+ # Only process the following if we had some matching contributors, affiliations or opportuniy nbrs
70
+ response = _repository_match?(array: hash['repositories'], response:) if response[:score] > 0
71
+ response = _keyword_match?(array: hash['repositories'], response:) if response[:score] > 0
72
+ response = _text_match?(type: 'title', text: hash['title'], response:) if response[:score] > 0
73
+ response = _text_match?(type: 'abstract', text: hash['abstract'], response:) if response[:score] > 0
74
+ # If the score is less than 3 then we have no confidence that it is a match
75
+ return response if response[:score] <= 2
76
+
77
+ # Set the confidence level based on the score
78
+ response[:confidence] = response[:score] > 15 ? 'High' : (response[:score] > 10 ? 'Medium' : 'Low')
79
+ response
80
+ end
81
+
82
+ private
83
+
84
+ def _extract_dmp_details(dmp:)
85
+ return nil unless dmp.is_a?(Hash) && !dmp['title'].nil? && !dmp['contact'].nil?
86
+
87
+ projects = dmp.fetch('project', [{}])
88
+ fundings = projects.map { |proj| proj.fetch('funding', []) }.flatten.compact.uniq
89
+ hosts = dmp.fetch('dataset', []).map { |dset| dset.fetch('distribution', []).map { |d| d['host'] } }
90
+ people = [dmp['contact']]
91
+ people << dmp.fetch('contributor', [])
92
+
93
+ # Extract all of the important bits about the DMP
94
+ @details_hash = {
95
+ created: dmp.fetch('created', Time.now.iso8601),
96
+ title: _cleanse_text(text: projects&.first&.fetch('title', dmp['title'])),
97
+ abstract: _cleanse_text(text: projects&.first&.fetch('description', dmp['description'])),
98
+ keywords: dmp.fetch('dataset', []).map { |ds| ds.fetch('keyword', []) }.flatten.compact.uniq,
99
+ identifiers: [dmp.fetch('dmp_id', {})['identifier']],
100
+ last_names: [],
101
+ affiliation_ids: [],
102
+ affiliations: [],
103
+ funder_names: [],
104
+ funder_ids: [],
105
+ opportunity_ids: [],
106
+ grant_ids: [],
107
+ repositories: []
108
+ }
109
+ _extract_people(array: people&.flatten&.compact&.uniq)
110
+ _extract_funding(array: fundings)
111
+ _extract_repositories(repos: hosts.flatten.compact.uniq)
112
+
113
+ # Clean up the results by flattening and removing duplicates from the Arrays
114
+ @details_hash.keys.each do |key|
115
+ @details_hash[key] = @details_hash[key].flatten.compact.uniq if @details_hash[key].is_a?(Array)
116
+ end
117
+ @logger&.debug(message: "Extracted the following from the DMP", details: @details_hash)
118
+ end
119
+
120
+ # Extract all of the funding information
121
+ def _extract_funding(array:)
122
+ return [] unless array.is_a?(Array)
123
+
124
+ array.each do |funding|
125
+ next unless funding.is_a?(Hash)
126
+
127
+ funder_id = funding.fetch('funder_id', {})
128
+ ror = funder_id['identifier'] if funder_id['type']&.downcase&.strip == 'ror'
129
+ fundref = ror.nil? ? funder_id['identifier']&.downcase&.strip : ROR_FUNDREF_ID_CROSSWALK[:"#{ror}"]
130
+ opportunity = funding.fetch('dmproadmap_funding_opportunity_id', {})['identifier']
131
+ grant = funding.fetch('grant_id', {})['identifier']
132
+
133
+ @details_hash[:identifiers] << ror&.downcase&.strip
134
+ @details_hash[:identifiers] << fundref&.downcase&.strip
135
+ @details_hash[:identifiers] << grant&.downcase&.strip
136
+ @details_hash[:identifiers] << grant&.split('/')&.last&.downcase&.strip
137
+ @details_hash[:identifiers] << opportunity&.downcase&.strip
138
+
139
+ @details_hash[:funder_names] << funding['name']&.downcase&.strip
140
+ @details_hash[:funder_ids] << fundref
141
+ @details_hash[:opportunity_ids] << opportunity&.downcase&.strip
142
+ @details_hash[:grant_ids] << [grant&.downcase&.strip, grant&.split('/')&.last&.downcase&.strip]
143
+ end
144
+ array
145
+ end
146
+
147
+ # Extract all of the ORCIDs, last names, and affiliation ids and names
148
+ def _extract_people(array:)
149
+ return [] unless array.is_a?(Array)
150
+
151
+ array.each do |entry|
152
+ next unless entry.is_a?(Hash)
153
+
154
+ id = entry.fetch('contributor_id', entry.fetch('contact_id', {}))['identifier']&.downcase&.strip
155
+ affil = entry.fetch('dmproadmap_affiliation', {})
156
+ ror = affil.fetch('affiliation_id', {})['identifier']&.downcase&.strip
157
+ name = entry.fetch('name', '')&.downcase&.strip
158
+ last_name = name.include?(', ') ? name.split(', ').first : name.split.last
159
+
160
+ @details_hash[:identifiers] << [id, ror&.downcase&.strip]
161
+ @details_hash[:last_names] << last_name
162
+ @details_hash[:affiliation_ids] << ror
163
+ @details_hash[:affiliations] << affil.fetch('name', '')&.split(' (')&.first&.downcase&.strip
164
+ end
165
+ array
166
+ end
167
+
168
+ # Extract all of the re3data ids, URLs and names
169
+ def _extract_repositories(repos:)
170
+ return [] unless repos.is_a?(Array)
171
+
172
+ repos.each do |repo|
173
+ next unless repo.is_a?(Hash)
174
+
175
+ @details_hash[:identifiers] << [
176
+ repo['url']&.downcase&.strip, repo.fetch('dmproadmap_host_id', {})['identifier']&.downcase&.strip
177
+ ]
178
+ @details_hash[:repositories] << repo.fetch('name', '')&.downcase&.strip
179
+ end
180
+ repos
181
+ end
182
+
183
+ # Returns whether or not the incoming grant id(s) match the DMPs grant id. Expecting:
184
+ # [
185
+ # { id: "https://doi.org/crossref123", name: "Bar", grant: ["1234", "http://foo.bar/543"] }
186
+ # ]
187
+ def _grants_match?(array:, response:)
188
+ return response unless array.is_a?(Array) && response.is_a?(Hash)
189
+
190
+ ids = array.select { |funding| funding.is_a?(Hash) && funding['grant'].is_a?(Array) }
191
+ .map { |funding| funding['grant'].map { |id| id&.downcase&.strip } }
192
+ .flatten.compact.uniq
193
+
194
+ matched = _compare_arrays(array_a: @details_hash.fetch(:grant_ids, []), array_b: ids)
195
+ return response if matched <= 0
196
+
197
+ response[:confidence] = 'Absolute'
198
+ response[:score] = 100
199
+ response[:notes] << 'the grant ID matched'
200
+ response
201
+ end
202
+
203
+ # Returns whether or not the incoming grant id(s) match the DMPs opportunity id. Expecting:
204
+ # [
205
+ # { id: "https://doi.org/crossref123", name: "Bar", grant: ["1234", "http://foo.bar/543"] }
206
+ # ]
207
+ def _opportunities_match?(array:, response:)
208
+ return response unless array.is_a?(Array) && response.is_a?(Hash)
209
+
210
+ ids = array.select { |funding| funding.is_a?(Hash) && funding['grant'].is_a?(Array) }
211
+ .map { |funding| funding['grant'].map { |id| id&.downcase&.strip } }
212
+ .flatten.compact.uniq
213
+
214
+ matched = _compare_arrays(array_a: @details_hash.fetch(:opportunity_ids, []), array_b: ids)
215
+ return response if matched <= 0
216
+
217
+ response[:score] += 5
218
+ response[:notes] << 'the funding opportunity number matched'
219
+ response
220
+ end
221
+
222
+ # Returns whether or not the inciming list of creators/contributors match those on the DMP. Expecting:
223
+ # [
224
+ # {
225
+ # id: "https://orcid.org/blah",
226
+ # last_name: "doe",
227
+ # affiliation: { id: "https://ror.org/blah", name: "Foo" }
228
+ # }
229
+ # ]
230
+ def _orcids_match?(array:, response:)
231
+ return response unless array.is_a?(Array) && response.is_a?(Hash)
232
+
233
+ ids = array.select { |repo| repo.is_a?(Hash) }
234
+ .map { |person| person['id']&.downcase&.strip }
235
+ .flatten.compact.uniq
236
+
237
+ matched = _compare_arrays(array_a: @details_hash.fetch(:identifiers, []), array_b: ids)
238
+ return response if matched <= 0
239
+
240
+ response[:score] += (matched * 2)
241
+ response[:notes] << 'contributor ORCIDs matched'
242
+ response
243
+ end
244
+
245
+ # Returns whether or not the inciming list of creators/contributors match those on the DMP. Expecting:
246
+ # [
247
+ # {
248
+ # id: "https://orcid.org/blah",
249
+ # last_name: "doe",
250
+ # affiliation: { id: "https://ror.org/blah", name: "Foo" }
251
+ # }
252
+ # ]
253
+ def _last_name_and_affiliation_match?(array:, response:)
254
+ return response unless array.is_a?(Array) && response.is_a?(Hash)
255
+
256
+ array = array.select { |repo| repo.is_a?(Hash) }
257
+ affiliations = array.map { |person| person['affiliation'] }&.flatten&.compact&.uniq
258
+ last_names = array.map { |person| person['last_name']&.downcase&.strip }&.flatten&.compact&.uniq
259
+ rors = affiliations.map { |affil| affil['id']&.downcase&.strip }&.flatten&.compact&.uniq
260
+ affil_names = affiliations.map { |affil| affil['name']&.downcase&.strip }&.flatten&.compact&.uniq
261
+
262
+ # Check the person last names and affiliation name and RORs
263
+ last_names_matched = _compare_arrays(array_a: @details_hash.fetch(:last_names, []), array_b: last_names)
264
+ rors_matched = _compare_arrays(array_a: @details_hash.fetch(:affiliation_ids, []), array_b: rors)
265
+ affil_names_matched = _compare_arrays(array_a: @details_hash.fetch(:affiliations, []), array_b: affil_names)
266
+ return response if last_names_matched <= 0 && rors_matched <= 0 && affil_names_matched <= 0
267
+
268
+ response[:score] += last_names_matched + rors_matched + affil_names_matched
269
+ response[:notes] << 'contributor names and affiliations matched'
270
+ response
271
+ end
272
+
273
+ # Returns whether or not the incoming list of repositories match those defined in the DMP. Expecting:
274
+ # [
275
+ # { id: ["http://some.repo.org", "https://doi.org/re3data123"], name: "Repo" }
276
+ # ]
277
+ def _repository_match?(array:, response:)
278
+ return response unless array.is_a?(Array) && response.is_a?(Hash)
279
+
280
+ # We only care about repositories with ids/urls
281
+ ids = array.select { |repo| repo.is_a?(Hash) }
282
+ .map { |repo| repo['id'].map { |id| id&.downcase&.strip } }
283
+ .flatten.compact.uniq
284
+
285
+ matched = _compare_arrays(array_a: @details_hash.fetch(:identifiers, []), array_b: ids)
286
+ return response if matched <= 0
287
+
288
+ response[:score] += matched
289
+ response[:notes] << 'repositories matched'
290
+ response
291
+ end
292
+
293
+ # Returns whether or not the list of keywords exist in the DMP. Expecting:
294
+ # keywords: ["foo", "bar"]
295
+ def _keyword_match?(array:, response:)
296
+ return response unless array.is_a?(Array) && response.is_a?(Hash)
297
+
298
+ keywords = array.map { |word| word&.downcase&.strip }&.flatten&.compact&.uniq
299
+ matched = _compare_arrays(array_a: @details_hash.fetch(:keywords, []), array_b: keywords)
300
+ return response if matched <= 0
301
+
302
+ response[:score] += 1
303
+ response[:notes] << 'keywords matched'
304
+ response
305
+ end
306
+
307
+ # Uses an NLP library to determine if the :text matches the DMP/Project :title or :description
308
+ def _text_match?(type: 'title', text:, response:, logger: nil)
309
+ return response unless response.is_a?(Hash) && text.is_a?(String) && !text.strip.empty? &&
310
+ !@details_hash[type.to_sym].nil?
311
+
312
+ nlp_processor = Text::WhiteSimilarity.new
313
+ cleansed = _cleanse_text(text:)
314
+
315
+ details = {
316
+ "dmp_#{type}": @details_hash[type.to_sym],
317
+ "incoming_#{type}": cleansed,
318
+ nlp_score: nlp_processor.similarity(@details_hash[type.to_sym], cleansed)
319
+ }
320
+ @logger&.debug(message: "Text::WhiteSimilarity score", details:)
321
+ return response if details[:nlp_score] < 0.5
322
+
323
+ response[:score] += details[:nlp_score] >= 0.75 ? 5 : 2
324
+ response[:notes] << "#{type}s are similar"
325
+ response
326
+ end
327
+
328
+ # Change the incoming text to lower case, remove spaces and STOP_WORDS
329
+ def _cleanse_text(text:)
330
+ return nil unless text.is_a?(String)
331
+
332
+ text.downcase.split.reject { |word| STOP_WORDS.include?(word) }.join(' ').strip
333
+ end
334
+
335
+ # Do an introspection of the 2 arrays and return the number of matches
336
+ def _compare_arrays(array_a: [], array_b: [])
337
+ return 0 unless array_a.is_a?(Array) && array_b.is_a?(Array)
338
+
339
+ intersection = array_a & array_b
340
+ intersection.nil? || intersection.size <= 0 ? 0 : intersection.size
341
+ end
342
+
343
+ # TODO: Remove this hard-coded crosswalk once the community has broader support for using ROR for funder ids
344
+ ROR_FUNDREF_ID_CROSSWALK = {
345
+ # NIH ID Crosswalk
346
+ "https://ror.org/01cwqze88": "https://doi.org/10.13039/100000002",
347
+ "https://ror.org/04mhx6838": "https://doi.org/10.13039/100000055",
348
+ "https://ror.org/012pb6c26": "https://doi.org/10.13039/100000050",
349
+ "https://ror.org/03wkg3b53": "https://doi.org/10.13039/100000053",
350
+ "https://ror.org/0060t0j89": "https://doi.org/10.13039/100000092",
351
+ "https://ror.org/00372qc85": "https://doi.org/10.13039/100000070",
352
+ "https://ror.org/00190t495": "https://doi.org/10.13039/100008460",
353
+ "https://ror.org/00j4k1h63": "https://doi.org/10.13039/100000066",
354
+ "https://ror.org/01y3zfr79": "https://doi.org/10.13039/100000056",
355
+ "https://ror.org/04q48ey07": "https://doi.org/10.13039/100000057",
356
+ "https://ror.org/0493hgw16": "https://doi.org/10.13039/100006545",
357
+ "https://ror.org/04vfsmv21": "https://doi.org/10.13039/100000098",
358
+ "https://ror.org/03jh5a977": "https://doi.org/10.13039/100000093",
359
+ "https://ror.org/04xeg9z08": "https://doi.org/10.13039/100000025",
360
+ "https://ror.org/01s5ya894": "https://doi.org/10.13039/100000065",
361
+ "https://ror.org/02meqm098": "https://doi.org/10.13039/100000002",
362
+ "https://ror.org/049v75w11": "https://doi.org/10.13039/100000049",
363
+ "https://ror.org/004a2wv92": "https://doi.org/10.13039/100000072",
364
+ "https://ror.org/00adh9b73": "https://doi.org/10.13039/100000062",
365
+ "https://ror.org/043z4tv69": "https://doi.org/10.13039/100000060",
366
+ "https://ror.org/00x19de83": "https://doi.org/10.13039/100000002",
367
+ "https://ror.org/02jzrsm59": "https://doi.org/10.13039/100000027",
368
+ "https://ror.org/006zn3t30": "https://doi.org/10.13039/100000069",
369
+ "https://ror.org/04byxyr05": "https://doi.org/10.13039/100000071",
370
+ "https://ror.org/04pw6fb54": "https://doi.org/10.13039/100006108",
371
+ "https://ror.org/05aq6yn88": "https://doi.org/10.13039/100006955",
372
+ "https://ror.org/02xey9a22": "https://doi.org/10.13039/100000061",
373
+ "https://ror.org/00fj8a872": "https://doi.org/10.13039/100000052",
374
+ "https://ror.org/01wtjyf13": "https://doi.org/10.13039/100000063",
375
+ "https://ror.org/04r5s4b52": "https://doi.org/10.13039/100005440",
376
+ "https://ror.org/046zezr58": "https://doi.org/10.13039/100006085",
377
+ "https://ror.org/02e3wq066": "https://doi.org/10.13039/100006086",
378
+ "https://ror.org/031gy6182": "https://doi.org/10.13039/100000002",
379
+ "https://ror.org/054j5yq82": "https://doi.org/10.13039/100000002",
380
+ "https://ror.org/02yrzyf97": "https://doi.org/10.13039/100000002",
381
+
382
+ # NSF ID Crosswalk
383
+ "https://.org/021nxhr62": "https://doi.org/10.13039/100000001",
384
+ "https://.org/04aqat463": "https://doi.org/10.13039/100000001",
385
+ "https://.org/01rcfpa16": "https://doi.org/10.13039/100005441",
386
+ "https://.org/014eweh95": "https://doi.org/10.13039/100005445",
387
+ "https://.org/001xhss06": "https://doi.org/10.13039/100000076",
388
+ "https://.org/04qn9mx93": "https://doi.org/10.13039/100000153",
389
+ "https://.org/03g87he71": "https://doi.org/10.13039/100000155",
390
+ "https://.org/01tnvpc68": "https://doi.org/10.13039/100000156",
391
+ "https://.org/01rvays47": "https://doi.org/10.13039/100000154",
392
+ "https://.org/002jdaq33": "https://doi.org/10.13039/100000152",
393
+ "https://.org/025kzpk63": "https://doi.org/10.13039/100000083",
394
+ "https://.org/04nh1dc89": "https://doi.org/10.13039/100007523",
395
+ "https://.org/01mng8331": "https://doi.org/10.13039/100000143",
396
+ "https://.org/02rdzmk74": "https://doi.org/10.13039/100000144",
397
+ "https://.org/053a2cp42": "https://doi.org/10.13039/100000145",
398
+ "https://.org/014bj5w56": "https://doi.org/10.13039/100000081",
399
+ "https://.org/00whkrf32": "https://doi.org/10.13039/100000082",
400
+ "https://.org/05s7cqk18": "https://doi.org/10.13039/100000173",
401
+ "https://.org/02kd4km72": "https://doi.org/10.13039/100000172",
402
+ "https://.org/03mamvh39": "https://doi.org/10.13039/100000171",
403
+ "https://.org/00b6sbb32": "https://doi.org/10.13039/100000084",
404
+ "https://.org/0471zv972": "https://doi.org/10.13039/100000146",
405
+ "https://.org/028yd4c30": "https://doi.org/10.13039/100000147",
406
+ "https://.org/01krpsy48": "https://doi.org/10.13039/100000148",
407
+ "https://.org/050rnw378": "https://doi.org/10.13039/100000149",
408
+ "https://.org/0388pet74": "https://doi.org/10.13039/100000150",
409
+ "https://.org/03xyg3m20": "https://doi.org/10.13039/100000151",
410
+ "https://.org/05p847d66": "https://doi.org/10.13039/100000085",
411
+ "https://.org/037gd6g64": "https://doi.org/10.13039/100000159",
412
+ "https://.org/05v01mk25": "https://doi.org/10.13039/100000160",
413
+ "https://.org/05wqqhv83": "https://doi.org/10.13039/100000141",
414
+ "https://.org/05nwjp114": "https://doi.org/10.13039/100007352",
415
+ "https://.org/05fnzca26": "https://doi.org/10.13039/100000162",
416
+ "https://.org/02trddg58": "https://doi.org/10.13039/100000163",
417
+ "https://.org/029b7h395": "https://doi.org/10.13039/100000086",
418
+ "https://.org/04mg8wm74": "https://doi.org/10.13039/100000164",
419
+ "https://.org/01ar8dr59": "https://doi.org/10.13039/100000165",
420
+ "https://.org/01pc7k308": "https://doi.org/10.13039/100000078",
421
+ "https://.org/051fftw81": "https://doi.org/10.13039/100000121",
422
+ "https://.org/04ap5x931": "https://doi.org/10.13039/100000166",
423
+ "https://.org/00apvva27": "https://doi.org/10.13039/100005716",
424
+ "https://.org/04nseet23": "https://doi.org/10.13039/100000179",
425
+ "https://.org/04k9mqs78": "https://doi.org/10.13039/100000106",
426
+ "https://.org/01k638r21": "https://doi.org/10.13039/100000089",
427
+ "https://.org/01gmp5538": "https://doi.org/10.13039/100005447",
428
+ "https://.org/01vnjbg30": "https://doi.org/10.13039/100005449",
429
+ "https://.org/03h7mcc28": "https://doi.org/10.13039/100000088",
430
+ "https://.org/05wgkzg12": "https://doi.org/10.13039/100000169",
431
+ "https://.org/0445wmv88": "https://doi.org/10.13039/100000170",
432
+ "https://.org/02dz2hb46": "https://doi.org/10.13039/100000077",
433
+ "https://.org/034m1ez10": "https://doi.org/10.13039/100000107",
434
+ "https://.org/02a65dj82": "https://doi.org/10.13039/100005717",
435
+ "https://.org/020fhsn68": "https://doi.org/10.13039/100000001",
436
+ "https://.org/03z9hh605": "https://doi.org/10.13039/100000174",
437
+ "https://.org/04ya3kq71": "https://doi.org/10.13039/100007521",
438
+ "https://.org/04evh7y43": "https://doi.org/10.13039/100005443",
439
+ "https://.org/04h67aa53": "https://doi.org/10.13039/100000177",
440
+ "https://.org/025dabr11": "https://doi.org/10.13039/100005446",
441
+ "https://.org/04vw0kz07": "https://doi.org/10.13039/100005448",
442
+ "https://.org/054ydxh33": "https://doi.org/10.13039/100005554",
443
+ "https://.org/01sharn77": "https://doi.org/10.13039/100006091",
444
+ "https://.org/02ch5q898": "https://doi.org/10.13039/100000001",
445
+
446
+ # NASA ID Crosswalk
447
+ "https://.org/0171mag52": "https://doi.org/10.13039/100006198",
448
+ "https://.org/027k65916": "https://doi.org/10.13039/100006196",
449
+ "https://.org/027ka1x80": "https://doi.org/10.13039/100000104",
450
+ "https://.org/02acart68": "https://doi.org/10.13039/100006195",
451
+ "https://.org/059fqnc42": "https://doi.org/10.13039/100006193",
452
+ "https://.org/01cyfxe35": "https://doi.org/10.13039/100016595",
453
+ "https://.org/04xx4z452": "https://doi.org/10.13039/100006203",
454
+ "https://.org/0399mhs52": "https://doi.org/10.13039/100006199",
455
+ "https://.org/02epydz83": "https://doi.org/10.13039/100006197",
456
+ "https://.org/03j9e2j92": "https://doi.org/10.13039/100006205",
457
+ "https://.org/02s42x260": "https://doi.org/10.13039/100000104",
458
+ "https://.org/01p7gwa14": "https://doi.org/10.13039/100000104",
459
+ "https://.org/01qxmdg18": "https://doi.org/10.13039/100000104",
460
+ "https://.org/006ndaj41": "https://doi.org/10.13039/100000104",
461
+ "https://.org/03em45j53": "https://doi.org/10.13039/100007346",
462
+ "https://.org/045t78n53": "https://doi.org/10.13039/100000104",
463
+ "https://.org/00r57r863": "https://doi.org/10.13039/100000104",
464
+ "https://.org/0401vze59": "https://doi.org/10.13039/100007726",
465
+ "https://.org/04hccab49": "https://doi.org/10.13039/100000104",
466
+ "https://.org/04437j066": "https://doi.org/10.13039/100000104",
467
+ "https://.org/028b18z22": "https://doi.org/10.13039/100000104",
468
+ "https://.org/00ryjtt64": "https://doi.org/10.13039/100000104",
469
+
470
+ # DOE ID Crosswalk
471
+ "https://ror.org/01bj3aw27": "https://doi.org/10.13039/100000015",
472
+ "https://ror.org/03q1rgc19": "https://doi.org/10.13039/100006133",
473
+ "https://ror.org/02xznz413": "https://doi.org/10.13039/100006134",
474
+ "https://ror.org/03sk1we31": "https://doi.org/10.13039/100006168",
475
+ "https://ror.org/00f93gc02": "https://doi.org/10.13039/100006177",
476
+ "https://ror.org/05tj7dm33": "https://doi.org/10.13039/100006147",
477
+ "https://ror.org/0012c7r22": "https://doi.org/10.13039/100006192",
478
+ "https://ror.org/00mmn6b08": "https://doi.org/10.13039/100006132",
479
+ "https://ror.org/03ery9d53": "https://doi.org/10.13039/100006120",
480
+ "https://ror.org/033jmdj81": "https://doi.org/10.13039/100000015",
481
+ "https://ror.org/03rd4h240": "https://doi.org/10.13039/100006130",
482
+ "https://ror.org/0054t4769": "https://doi.org/10.13039/100006200",
483
+ "https://ror.org/03eecgp81": "https://doi.org/10.13039/100006174",
484
+ "https://ror.org/00heb4d89": "https://doi.org/10.13039/100006135",
485
+ "https://ror.org/05ek3m339": "https://doi.org/10.13039/100006150",
486
+ "https://ror.org/00km40770": "https://doi.org/10.13039/100006138",
487
+ "https://ror.org/02ah1da87": "https://doi.org/10.13039/100006137",
488
+ "https://ror.org/05hsv7e61": "https://doi.org/10.13039/100000015",
489
+ "https://ror.org/01c9ay627": "https://doi.org/10.13039/100006165",
490
+ "https://ror.org/04z2gev20": "https://doi.org/10.13039/100006183",
491
+ "https://ror.org/02z1qvq09": "https://doi.org/10.13039/100006144",
492
+ "https://ror.org/03jf3w726": "https://doi.org/10.13039/100006186",
493
+ "https://ror.org/04848jz84": "https://doi.org/10.13039/100006142",
494
+ "https://ror.org/04s778r16": "https://doi.org/10.13039/100006171",
495
+ "https://ror.org/04nnxen11": "https://doi.org/10.13039/100000015",
496
+ "https://ror.org/05csy5p27": "https://doi.org/10.13039/100010268",
497
+ "https://ror.org/05efnac71": "https://doi.org/10.13039/100000015"
498
+ }
499
+ end
500
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uc3DmpId
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uc3-dmp-id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Riley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-04 00:00:00.000000000 Z
11
+ date: 2023-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: text
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: uc3-dmp-dynamo
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -76,6 +90,7 @@ files:
76
90
  - README.md
77
91
  - lib/uc3-dmp-id.rb
78
92
  - lib/uc3-dmp-id/asserter.rb
93
+ - lib/uc3-dmp-id/comparator.rb
79
94
  - lib/uc3-dmp-id/creator.rb
80
95
  - lib/uc3-dmp-id/deleter.rb
81
96
  - lib/uc3-dmp-id/finder.rb