uc3-dmp-id 0.1.24 → 0.1.26

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: 91c01361a882520a281db0da8aa029bcb480b4873b684a4a526f2542abda9ea0
4
- data.tar.gz: b9ca235a0c706469b4568e74a6ecbec2b39e0b44f9298d781ea11a2dd9cde749
3
+ metadata.gz: f692820577dff088fec1d1df2e4975a00a6b30952fa66979b1fa8449523d092e
4
+ data.tar.gz: 8bf2326f3a6fddf9454e915c596eb40231c939337e517de73f6ea998f04a9d6f
5
5
  SHA512:
6
- metadata.gz: d4a5092b448ce3f1a1ad71ed53c5d81304780732a5766d35a2875d0ed244d07b660d25f156a8facd1c9a794d89e59f17915bf640692ba1266556892245b5cab9
7
- data.tar.gz: 4e3d0e476d2479f075abd8bc0cb45028766a766e60a1fae4441a1ef68c451e58757c5cd33c4b3033ccce5784c23112b1f9cb9782b49fd17c8dd2b80f538a4d24
6
+ metadata.gz: fcc0689438e54715882ed7c30aa7d608e9c3abaa1e61fec7589a213c7665f257be933f93a482dc4d589756517c94a7f9b4803930f0a2a1bd51d877cd890b8a90
7
+ data.tar.gz: df18ed7a17053c9527e6a111150b81c2a6d285bda1263b854b0cc3fae5b703e3ef9dd3213b62230431c898c948cf80f6e821da3515089e6cfc156622223bdf7e
@@ -9,203 +9,102 @@ module Uc3DmpId
9
9
  # Class that compares incoming data from an external source to the DMP
10
10
  # It determines if they are likely related and applies a confidence rating
11
11
  class Comparator
12
- MSG_MISSING_DMP = 'No DMP or the DMP did not contain enough information to use.'
12
+ MSG_MISSING_DMPS = 'No DMPs were defined. Expected an Array of OpenSearch documents!'
13
13
 
14
14
  STOP_WORDS = %w[a an and if of or the then they].freeze
15
15
 
16
16
  # See the bottom of this file for a hard-coded crosswalk between Crossref funder ids and ROR ids
17
17
  # Some APIs do not support ROR fully for funder ids, so we need to be able to reference both
18
18
 
19
- attr_accessor :dmp, :details_hash, :logger
19
+ attr_accessor :dmps, :logger
20
20
 
21
+ # Expecting an Array of OpenSearch documents as :dmps in the :args
21
22
  def initialize(**args)
22
23
  @logger = args[:logger]
23
24
  @details_hash = {}
24
25
 
25
- @dmp = args.fetch(:dmp, {})['dmp'].nil? ? args[:dmp] : args.fetch(:dmp, {})['dmp']
26
- _extract_dmp_details(dmp:)
27
- raise ComparatorError, MSG_MISSING_DMP if @details_hash.empty?
26
+ @dmps = args.fetch(:dmps, [])
27
+ raise ComparatorError, MSG_MISSING_DMPS if @dmps.empty?
28
28
  end
29
29
 
30
30
  # Compare the incoming hash with the DMP details that were gathered during initialization.
31
31
  #
32
- # The Hash should contain:
33
- # {
34
- # title: "Example research project",
35
- # abstract: "Lorem ipsum psuedo abstract",
36
- # keywords: ["foo", "bar"],z
37
- # people: [
38
- # {
39
- # id: "https://orcid.org/blah",
40
- # last_name: "doe",
41
- # affiliation: { id: "https://ror.org/blah", name: "Foo" }
42
- # }
43
- # ],
44
- # fundings: [
45
- # { id: "https://doi.org/crossref123", name: "Bar", grant: ["1234", "http://foo.bar/543"] }
46
- # ],
47
- # repositories: [
48
- # { id: ["http://some.repo.org", "https://doi.org/re3data123"], name: "Repo" }
49
- # ]
50
- # }
32
+ # The incoming Hash should match the documents found in OpenSearch. For example:
33
+ # {
34
+ # "people": ["john doe", "jdoe@example.com"],
35
+ # "people_ids": ["https://orcid.org/0000-0000-0000-ZZZZ"],
36
+ # "affiliations": ["example college"],
37
+ # "affiliation_ids": ["https://ror.org/00000zzzz"],
38
+ # "funder_ids": ["https://doi.org/10.13039/00000000000"],
39
+ # "funders": ["example funder (example.gov)"],
40
+ # "funder_opportunity_ids": ["485yt8325ty"],
41
+ # "grant_ids": [],
42
+ # "funding_status": "planned",
43
+ # "dmp_id": "doi.org/11.22222/A1B2c3po",
44
+ # "title": "example data management plan",
45
+ # "visibility": "private",
46
+ # "featured": 0,
47
+ # "description": "the example project abstract",
48
+ # "project_start": "2022-01-03",
49
+ # "project_end": "2024-12-23",
50
+ # "created": "2023-08-07",
51
+ # "modified": "2023-08-07",
52
+ # "registered": "2023-08-07"
53
+ # }
51
54
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
52
55
  def compare(hash:)
53
- response = { confidence: 'None', score: 0, notes: [] }
54
- return response unless hash.is_a?(Hash) && !hash['title'].nil?
56
+ return [] unless hash.is_a?(Hash) && !hash['title'].nil?
55
57
 
56
58
  # Compare the grant ids. If we have a match return the response immediately since that is
57
59
  # a very positive match!
58
- response = _grants_match?(array: hash['fundings'], response:)
59
- return response if response[:confidence] != 'None'
60
-
61
- response = _opportunities_match?(array: hash['fundings'], response:)
62
- response = _orcids_match?(array: hash['people'], response:)
63
- response = _last_name_and_affiliation_match?(array: hash['people'], response:)
64
-
65
- # Only process the following if we had some matching contributors, affiliations or opportuniy nbrs
66
- response = _repository_match?(array: hash['repositories'], response:) if response[:score].positive?
67
- response = _keyword_match?(array: hash['keywords'], response:) if response[:score].positive?
68
- response = _text_match?(type: 'title', text: hash['title'], response:) if response[:score].positive?
69
- response = _text_match?(type: 'abstract', text: hash['abstract'], response:) if response[:score].positive?
70
- # If the score is less than 3 then we have no confidence that it is a match
71
- return response if response[:score] <= 2
72
-
73
- # Set the confidence level based on the score
74
- response[:confidence] = if response[:score] > 10
75
- 'High'
76
- else
77
- (response[:score] > 5 ? 'Medium' : 'Low')
78
- end
79
- response
80
- end
81
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
82
-
83
- private
84
-
85
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
86
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
87
- def _extract_dmp_details(dmp:)
88
- return nil unless dmp.is_a?(Hash) && !dmp['title'].nil? && !dmp['contact'].nil?
89
-
90
- projects = dmp.fetch('project', [{}])
91
- fundings = projects.map { |proj| proj.fetch('funding', []) }.flatten.compact.uniq
92
- hosts = dmp.fetch('dataset', []).map { |dset| dset.fetch('distribution', []).map { |d| d['host'] } }
93
- people = [dmp['contact']]
94
- people << dmp.fetch('contributor', [])
95
-
96
- # Extract all of the important bits about the DMP
97
- @details_hash = {
98
- created: dmp.fetch('created', Time.now.iso8601),
99
- title: _cleanse_text(text: projects&.first&.fetch('title', dmp['title'])),
100
- abstract: _cleanse_text(text: projects&.first&.fetch('description', dmp['description'])),
101
- keywords: dmp.fetch('dataset', []).map { |ds| ds.fetch('keyword', []) }.flatten.compact.uniq,
102
- identifiers: [dmp.fetch('dmp_id', {})['identifier']],
103
- last_names: [],
104
- orcids: [],
105
- affiliation_ids: [],
106
- affiliations: [],
107
- funder_names: [],
108
- funder_ids: [],
109
- opportunity_ids: [],
110
- grant_ids: [],
111
- repositories: []
112
- }
113
- _extract_people(array: people&.flatten&.compact&.uniq)
114
- _extract_funding(array: fundings)
115
- _extract_repositories(repos: hosts.flatten.compact.uniq)
116
-
117
- # Clean up the results by flattening and removing duplicates from the Arrays
118
- @details_hash.each_key do |key|
119
- @details_hash[key] = @details_hash[key].flatten.compact.uniq if @details_hash[key].is_a?(Array)
120
- end
121
- @logger&.debug(message: 'Extracted the following from the DMP', details: @details_hash)
122
- end
123
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
124
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
125
-
126
- # Extract all of the funding information
127
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
128
- def _extract_funding(array:)
129
- return [] unless array.is_a?(Array)
130
-
131
- array.each do |funding|
132
- next unless funding.is_a?(Hash)
133
-
134
- funder_id = funding.fetch('funder_id', {})
135
- ror = funder_id['identifier'] if funder_id['type']&.downcase&.strip == 'ror'
136
- fundref = ror.nil? ? funder_id['identifier']&.downcase&.strip : ROR_FUNDREF_ID_CROSSWALK[:"#{ror}"]
137
- opportunity = funding.fetch('dmproadmap_funding_opportunity_id', {})['identifier']
138
- grant = funding.fetch('grant_id', {})['identifier']
139
-
140
- @details_hash[:identifiers] << ror&.downcase&.strip
141
- @details_hash[:identifiers] << fundref&.downcase&.strip
142
- @details_hash[:identifiers] << grant&.downcase&.strip
143
- @details_hash[:identifiers] << grant&.split('/')&.last&.downcase&.strip
144
- @details_hash[:identifiers] << opportunity&.downcase&.strip
145
-
146
- @details_hash[:funder_names] << funding['name']&.downcase&.split(' (').first&.strip
147
- @details_hash[:funder_ids] << fundref
148
- @details_hash[:opportunity_ids] << opportunity&.downcase&.strip
149
- @details_hash[:grant_ids] << [grant&.downcase&.strip, grant&.split('/')&.last&.downcase&.strip]
60
+ scoring = @dmps.map do |entry|
61
+ dmp = entry.fetch('_source', {})
62
+ response = { dmp_id: dmp['_id'], confidence: 'None', score: 0, notes: [] }
63
+ response = _grants_match?(array: hash.fetch('grant_ids', []), dmp:, response:)
64
+ return response if response[:confidence] != 'None'
65
+
66
+ response = _opportunities_match?(array: hash.fetch('funder_opportunity_ids', []), dmp:, response:)
67
+ response = _orcids_match?(array: hash.fetch('people_ids', []), dmp:, response:)
68
+ response = _last_name_and_affiliation_match?(hash:, dmp:, response:)
69
+
70
+ # Only process the following if we had some matching contributors, affiliations or opportuniy nbrs
71
+ response = _repository_match?(hash:, dmp:, response:) if response[:score].positive?
72
+ # response = _keyword_match?(array: hash['keywords'], response:) if response[:score].positive?
73
+ response = _text_match?(type: 'title', text: hash['title'], dmp:, response:) if response[:score].positive?
74
+ response = _text_match?(type: 'abstract', text: hash['description'], dmp:, response:) if response[:score].positive?
75
+ # If the score is less than 3 then we have no confidence that it is a match
76
+ return nil if response[:score] <= 2
77
+
78
+ # Set the confidence level based on the score
79
+ response[:confidence] = if response[:score] > 10
80
+ 'High'
81
+ else
82
+ (response[:score] > 5 ? 'Medium' : 'Low')
83
+ end
84
+ response
150
85
  end
151
- array
152
- end
153
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
154
86
 
155
- # Extract all of the ORCIDs, last names, and affiliation ids and names
156
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
157
- def _extract_people(array:)
158
- return [] unless array.is_a?(Array)
159
-
160
- array.each do |entry|
161
- next unless entry.is_a?(Hash)
162
-
163
- id = entry.fetch('contributor_id', entry.fetch('contact_id', {}))['identifier']&.downcase&.strip
164
- affil = entry.fetch('dmproadmap_affiliation', {})
165
- ror = affil.fetch('affiliation_id', {})['identifier']&.downcase&.strip
166
- name = entry.fetch('name', '')&.downcase&.strip
167
- last_name = name.include?(', ') ? name.split(', ').first : name.split.last
168
-
169
- @details_hash[:orcids] << id unless id.nil?
170
- @details_hash[:identifiers] << [id, ror&.downcase&.strip]
171
- @details_hash[:last_names] << last_name
172
- @details_hash[:affiliation_ids] << ror
173
- @details_hash[:affiliations] << affil.fetch('name', '')&.split(' (')&.first&.downcase&.strip
174
- end
175
- array
87
+ # TODO: introduce a tie-breaker here (maybe the closes to the project_end date)
88
+ scoring.compact.sort { |a, b| b[:score] <=> a[:score] }&.first
176
89
  end
177
90
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
178
91
 
179
- # Extract all of the re3data ids, URLs and names
180
- # rubocop:disable Metrics/AbcSize
181
- def _extract_repositories(repos:)
182
- return [] unless repos.is_a?(Array)
183
-
184
- repos.each do |repo|
185
- next unless repo.is_a?(Hash)
186
-
187
- @details_hash[:identifiers] << [
188
- repo['url']&.downcase&.strip, repo.fetch('dmproadmap_host_id', {})['identifier']&.downcase&.strip
189
- ]
190
- @details_hash[:repositories] << repo.fetch('name', '')&.downcase&.strip
191
- end
192
- repos
193
- end
194
- # rubocop:enable Metrics/AbcSize
92
+ private
195
93
 
196
94
  # Returns whether or not the incoming grant id(s) match the DMPs grant id. Expecting:
197
95
  # [
198
96
  # { id: "https://doi.org/crossref123", name: "Bar", grant: ["1234", "http://foo.bar/543"] }
199
97
  # ]
200
98
  # rubocop:disable Metrics/AbcSize
201
- def _grants_match?(array:, response:)
202
- return response unless array.is_a?(Array) && response.is_a?(Hash)
99
+ def _grants_match?(array:, dmp:, response:)
100
+ return response unless array.is_a?(Array) && dmp.is_a?(Hash) && response.is_a?(Hash)
101
+ return response unless dmp['grant_ids'].is_a?(Array) && !dmp['grant_ids'].empty?
203
102
 
204
103
  ids = array.select { |funding| funding.is_a?(Hash) && funding['grant'].is_a?(Array) }
205
104
  .map { |funding| funding['grant'].map { |id| id&.downcase&.strip } }
206
105
  .flatten.compact.uniq
207
106
 
208
- matched = _compare_arrays(array_a: @details_hash.fetch(:grant_ids, []), array_b: ids)
107
+ matched = _compare_arrays(array_a: dmp['grant_ids'], array_b: ids)
209
108
  return response if matched <= 0
210
109
 
211
110
  response[:confidence] = 'Absolute'
@@ -220,14 +119,15 @@ module Uc3DmpId
220
119
  # { id: "https://doi.org/crossref123", name: "Bar", grant: ["1234", "http://foo.bar/543"] }
221
120
  # ]
222
121
  # rubocop:disable Metrics/AbcSize
223
- def _opportunities_match?(array:, response:)
224
- return response unless array.is_a?(Array) && response.is_a?(Hash)
122
+ def _opportunities_match?(array:, dmp:, response:)
123
+ return response unless array.is_a?(Array) && dmp.is_a?(Hash) && response.is_a?(Hash)
124
+ return response unless dmp['funder_opportunity_ids'].is_a?(Array) && !dmp['funder_opportunity_ids'].empty?
225
125
 
226
126
  ids = array.select { |funding| funding.is_a?(Hash) && funding['grant'].is_a?(Array) }
227
127
  .map { |funding| funding['grant'].map { |id| id&.downcase&.strip } }
228
128
  .flatten.compact.uniq
229
129
 
230
- matched = _compare_arrays(array_a: @details_hash.fetch(:opportunity_ids, []), array_b: ids)
130
+ matched = _compare_arrays(array_a: dmp['funder_opportunity_ids'], array_b: ids)
231
131
  return response if matched <= 0
232
132
 
233
133
  response[:score] += 5
@@ -245,14 +145,15 @@ module Uc3DmpId
245
145
  # }
246
146
  # ]
247
147
  # rubocop:disable Metrics/AbcSize
248
- def _orcids_match?(array:, response:)
249
- return response unless array.is_a?(Array) && response.is_a?(Hash)
148
+ def _orcids_match?(array:, dmp:, response:)
149
+ return response unless array.is_a?(Array) && dmp.is_a?(Hash) && response.is_a?(Hash)
150
+ return response unless dmp['people_ids'].is_a?(Array) && !dmp['people_ids'].empty?
250
151
 
251
152
  ids = array.select { |repo| repo.is_a?(Hash) }
252
153
  .map { |person| person['id']&.downcase&.strip }
253
154
  .flatten.compact.uniq
254
155
 
255
- matched = _compare_arrays(array_a: @details_hash.fetch(:identifiers, []), array_b: ids)
156
+ matched = _compare_arrays(array_a: dmp['people_ids'], array_b: ids)
256
157
  return response if matched <= 0
257
158
 
258
159
  response[:score] += (matched * 2)
@@ -262,28 +163,21 @@ module Uc3DmpId
262
163
  # rubocop:enable Metrics/AbcSize
263
164
 
264
165
  # Returns whether or not the inciming list of creators/contributors match those on the DMP. Expecting:
265
- # [
266
- # {
267
- # id: "https://orcid.org/blah",
268
- # last_name: "doe",
269
- # affiliation: { id: "https://ror.org/blah", name: "Foo" }
270
- # }
271
- # ]
166
+ # {
167
+ # people: ["john doe", "jdoe@example.com"],
168
+ # affiliations: ["example college"],
169
+ # affiliation_ids: ["https://ror.org/blah"]
170
+ # }
272
171
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
273
- def _last_name_and_affiliation_match?(array:, response:)
274
- return response unless array.is_a?(Array) && response.is_a?(Hash)
275
-
276
- array = array.select { |repo| repo.is_a?(Hash) }
277
- affiliations = array.map { |person| person['affiliation'] }&.flatten&.compact&.uniq
278
- last_names = array.map { |person| person['last_name']&.downcase&.strip }&.flatten&.compact&.uniq
279
- rors = affiliations.map { |affil| affil['id']&.downcase&.strip }&.flatten&.compact&.uniq
280
- affil_names = affiliations.map { |affil| affil['name']&.downcase&.strip }&.flatten&.compact&.uniq
172
+ def _last_name_and_affiliation_match?(hash:, dmp:, response:)
173
+ return response unless hash.is_a?(Hash) && dmp.is_a?(Hash) && response.is_a?(Hash)
174
+ return response unless hash['people'].is_a?(Array) && !dmp['people'].empty?
281
175
 
282
176
  # Check the person last names and affiliation name and RORs
283
- last_names_matched = _compare_arrays(array_a: @details_hash.fetch(:last_names, []), array_b: last_names)
284
- rors_matched = _compare_arrays(array_a: @details_hash.fetch(:affiliation_ids, []), array_b: rors)
285
- affil_names_matched = _compare_arrays(array_a: @details_hash.fetch(:affiliations, []), array_b: affil_names)
286
- return response if last_names_matched <= 0
177
+ last_names_matched = _compare_arrays(array_a: dmp['people'], array_b: hash['people'])
178
+ rors_matched = _compare_arrays(array_a: dmp.fetch('affiliation_ids', []), array_b: hash['affiliation_ids'])
179
+ affil_names_matched = _compare_arrays(array_a: dmp.fetch('affiliations', []), array_b: hash['affiliations'])
180
+ return response if last_names_matched <= 0 && rors_matched <= 0 && affil_names_matched <= 0
287
181
 
288
182
  response[:score] += last_names_matched + rors_matched + affil_names_matched
289
183
  response[:notes] << 'contributor names and affiliations matched'
@@ -292,19 +186,16 @@ module Uc3DmpId
292
186
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
293
187
 
294
188
  # Returns whether or not the incoming list of repositories match those defined in the DMP. Expecting:
295
- # [
296
- # { id: ["http://some.repo.org", "https://doi.org/re3data123"], name: "Repo" }
297
- # ]
189
+ # {
190
+ # repo_ids: ["http://some.repo.org", "https://doi.org/re3data123"],
191
+ # repos: ["repo"]
192
+ # }
298
193
  # rubocop:disable Metrics/AbcSize
299
- def _repository_match?(array:, response:)
300
- return response unless array.is_a?(Array) && response.is_a?(Hash)
301
-
302
- # We only care about repositories with ids/urls
303
- ids = array.select { |repo| repo.is_a?(Hash) }
304
- .map { |repo| repo['id'].map { |id| id&.downcase&.strip } }
305
- .flatten.compact.uniq
194
+ def _repository_match?(hash:, dmp:, response:)
195
+ return response unless hash.is_a?(Hash) && dmp.is_a?(Hash) && response.is_a?(Hash)
196
+ return response unless hash['repo_ids'].is_a?(Array) && !dmp['repo_ids'].empty?
306
197
 
307
- matched = _compare_arrays(array_a: @details_hash.fetch(:identifiers, []), array_b: ids)
198
+ matched = _compare_arrays(array_a: dmp['repo_ids'], array_b: hash['repo_ids'])
308
199
  return response if matched <= 0
309
200
 
310
201
  response[:score] += matched
@@ -313,33 +204,19 @@ module Uc3DmpId
313
204
  end
314
205
  # rubocop:enable Metrics/AbcSize
315
206
 
316
- # Returns whether or not the list of keywords exist in the DMP. Expecting:
317
- # keywords: ["foo", "bar"]
318
- def _keyword_match?(array:, response:)
319
- return response unless array.is_a?(Array) && response.is_a?(Hash)
320
-
321
- keywords = array.map { |word| word&.downcase&.strip }&.flatten&.compact&.uniq
322
- matched = _compare_arrays(array_a: @details_hash.fetch(:keywords, []), array_b: keywords)
323
- return response if matched <= 0
324
-
325
- response[:score] += 1
326
- response[:notes] << 'keywords matched'
327
- response
328
- end
329
-
330
207
  # Uses an NLP library to determine if the :text matches the DMP/Project :title or :description
331
208
  # rubocop:disable Metrics/AbcSize
332
- def _text_match?(text:, response:, type: 'title')
333
- return response unless response.is_a?(Hash) && text.is_a?(String) && !text.strip.empty? &&
334
- !@details_hash[type.to_sym].nil?
209
+ def _text_match?(text:, dmp:, response:, type: 'title')
210
+ return response unless response.is_a?(Hash) && text.is_a?(String) && !text.strip.empty? && dmp.is_a?(Hash)
335
211
 
336
212
  nlp_processor = Text::WhiteSimilarity.new
337
213
  cleansed = _cleanse_text(text:)
338
214
 
215
+ dmp_val = type == 'title' ? dmp['title'] : dmp['description']
339
216
  details = {
340
- "dmp_#{type}": @details_hash[type.to_sym],
217
+ "dmp_#{type}": dmp_val,
341
218
  "incoming_#{type}": cleansed,
342
- nlp_score: nlp_processor.similarity(@details_hash[type.to_sym], cleansed)
219
+ nlp_score: nlp_processor.similarity(dmp_val, cleansed)
343
220
  }
344
221
  @logger&.debug(message: 'Text::WhiteSimilarity score', details:)
345
222
  return response if details[:nlp_score] < 0.5
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uc3DmpId
4
- VERSION = '0.1.24'
4
+ VERSION = '0.1.26'
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.24
4
+ version: 0.1.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Riley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-08 00:00:00.000000000 Z
11
+ date: 2024-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json