uc3-dmp-id 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/uc3-dmp-id/augmenter.rb +153 -0
- data/lib/uc3-dmp-id/comparator.rb +0 -3
- data/lib/uc3-dmp-id/schemas/author.rb +211 -158
- data/lib/uc3-dmp-id/updater.rb +9 -27
- data/lib/uc3-dmp-id/version.rb +1 -1
- data/lib/uc3-dmp-id.rb +1 -0
- metadata +31 -3
- data/lib/uc3-dmp-id/asserter.rb +0 -216
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d3c26185a12403a3c3dfa1b9ab0dec6b798b1b127905a22d04f6f929d8cbf73
|
4
|
+
data.tar.gz: de6b7a8f6c3395516ed10adc6eb55d0d84a9f52ae785dc36418f408f097c5900
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e790c4ee74028bc85b924ec1f82b4301be23014f1e0543c69103e285993002be0c00642b0a58a3e2d7418956708142f562f5306af69eeae3377609e5a3f3cb9
|
7
|
+
data.tar.gz: 06bd91810cde2da48258c1a22bc699788a131c1e4aaae3b1d12f86edbfb55ac85fd560803dcc84df4632fbafa9257650f56529d0475b03f291a4b9e0b76e829d
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bibtex'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module Uc3DmpId
|
7
|
+
class AugmenterError < StandardError; end
|
8
|
+
|
9
|
+
# Class that adds items to the :dmphub_modifications array or directly to the
|
10
|
+
# :dmpraodmap_related_identifiers array if the confidence level was 'Absolute'
|
11
|
+
class Augmenter
|
12
|
+
attr_accessor :augmenter, :dmp, :known_mods, :known_works, :known_awards, :logger
|
13
|
+
|
14
|
+
MSG_MISSING_ANNOTATIONS = 'DMP must have its DMPHub specific annotations!'
|
15
|
+
MSG_MISSING_AUGMENTER = 'No Augmenter specified!'
|
16
|
+
MSG_MISSING_DMP = 'No DMP or the DMP did not contain enough information to use.'
|
17
|
+
|
18
|
+
# rubocop:disable Metrics/AbcSize
|
19
|
+
def initialize(**args)
|
20
|
+
@logger = args[:logger]
|
21
|
+
@augmenter = args[:augmenter]
|
22
|
+
raise AugmenterError, MSG_MISSING_AUGMENTER unless @augmenter.is_a?(Hash) && !@augmenter['PK'].nil?
|
23
|
+
|
24
|
+
@dmp = args.fetch(:dmp, {})['dmp'].nil? ? args[:dmp] : args.fetch(:dmp, {})['dmp']
|
25
|
+
raise AugmenterError, MSG_MISSING_DMP if @dmp.nil? || @dmp['dmp_id'].nil?
|
26
|
+
raise AugmenterError, MSG_MISSING_ANNOTATIONS if @dmp['PK'].nil?
|
27
|
+
|
28
|
+
_extract_known
|
29
|
+
end
|
30
|
+
# rubocop:enable Metrics/AbcSize
|
31
|
+
|
32
|
+
# rubocop:disable Metrics/AbcSize
|
33
|
+
def add_modifications(works:)
|
34
|
+
mod_hash = _generate_mod_header
|
35
|
+
|
36
|
+
works.fetch('publications', []).each do |publication|
|
37
|
+
# Skip the entry if we already know about this identifier
|
38
|
+
next if @known_works.include?(publication['id'])
|
39
|
+
|
40
|
+
work_hash = _work_to_mod_entry(type: 'publication', work: publication)
|
41
|
+
mod_hash.fetch('dmproadmap_related_identifiers', []) << work_hash unless work_hash.nil?
|
42
|
+
fundings = publication.fetch('fundingReferences', [])
|
43
|
+
next unless fundings.any?
|
44
|
+
|
45
|
+
award_hash = fundings.map { |funding| _funding_to_mod_entry(funding:) }
|
46
|
+
mod_hash.fetch('funding', []) << award_hash unless award_hash.nil?
|
47
|
+
end
|
48
|
+
return 0 unless mod_hash['dmproadmap_related_identifiers'].any? || mod_hash.fetch('funding', []).any?
|
49
|
+
|
50
|
+
# Save the DMP
|
51
|
+
mods = (@known_mods.nil? ? [] : @known_mods)
|
52
|
+
@dmp['dmphub_modifications'] = (@known_mods.nil? ? [] : @known_mods) << mod_hash
|
53
|
+
|
54
|
+
client = Uc3DmpDynamo::Client.new
|
55
|
+
resp = client.put_item(json: @dmp, logger:)
|
56
|
+
raise AugmenterError, Helper::MSG_DMP_NO_DMP_ID if resp.nil?
|
57
|
+
|
58
|
+
# Return the number of modifications added to the DMP
|
59
|
+
mod_hash.fetch('dmproadmap_related_identifiers', []).length + mod_hash.fetch('funding', []).length
|
60
|
+
end
|
61
|
+
# rubocop:enable Metrics/AbcSize
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def _generate_mod_header
|
66
|
+
JSON.parse({
|
67
|
+
id: "#{Time.now.utc.strftime('%Y-%m-%d')}-#{SecureRandom.hex(4)}",
|
68
|
+
provenance: @augmenter['name'],
|
69
|
+
timestamp: Time.now.utc.iso8601,
|
70
|
+
dmproadmap_related_identifiers: [],
|
71
|
+
fundings: []
|
72
|
+
}.to_json)
|
73
|
+
end
|
74
|
+
|
75
|
+
# rubocop:disable Metrics/AbcSize
|
76
|
+
def _work_to_mod_entry(type:, work:)
|
77
|
+
return nil if work['id'].nil?
|
78
|
+
|
79
|
+
ret = {
|
80
|
+
type: 'doi',
|
81
|
+
identifier: work['id'],
|
82
|
+
descriptor: 'references',
|
83
|
+
status: 'pending'
|
84
|
+
}
|
85
|
+
work_type = work.fetch('type', 'Text')&.downcase&.strip
|
86
|
+
ret[:work_type] = work_type == 'text' ? type : work_type
|
87
|
+
return JSON.parse(ret.to_json) if work['bibtex'].nil?
|
88
|
+
|
89
|
+
ret[:citation] = Uc3DmpCitation::Citer.bibtex_to_citation(bibtex_as_string: work['bibtex'])
|
90
|
+
JSON.parse(ret.to_json)
|
91
|
+
end
|
92
|
+
# rubocop:enable Metrics/AbcSize
|
93
|
+
|
94
|
+
# Convert a funding entry for the dmphub_modification
|
95
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
96
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
97
|
+
def _funding_to_mod_entry(funding:)
|
98
|
+
return nil unless funding.is_a?(Hash) && (!funding['awardUri'] || !funding['awardNumber'])
|
99
|
+
return nil if @known_awards.include?(funding['awardUri']) || @known_awards.include?(funding['awardNumber'])
|
100
|
+
|
101
|
+
id = funding['awardUri'] if funding.fetch('awardUri', '').start_with?('http')
|
102
|
+
id = funding['awardNumber'] if id.nil?
|
103
|
+
|
104
|
+
ret = {
|
105
|
+
status: 'pending',
|
106
|
+
name: funding['funderName'],
|
107
|
+
funding_status: 'granted',
|
108
|
+
grant_id: {
|
109
|
+
type: if id.start_with?('http')
|
110
|
+
id.include?('doi') ? 'doi' : 'url'
|
111
|
+
else
|
112
|
+
'other'
|
113
|
+
end,
|
114
|
+
identifier: id
|
115
|
+
}
|
116
|
+
}
|
117
|
+
funder_id = funding['funderIdentifier']
|
118
|
+
return JSON.parse(ret.to_json) if funder_id.nil?
|
119
|
+
|
120
|
+
ret[:funder_id] = {
|
121
|
+
type: if funder_id.include?('ror')
|
122
|
+
'ror'
|
123
|
+
else
|
124
|
+
(funder_id.start_with?('http') ? 'url' : 'other')
|
125
|
+
end,
|
126
|
+
identifier: funder_id
|
127
|
+
}
|
128
|
+
JSON.parse(ret.to_json)
|
129
|
+
end
|
130
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
131
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
132
|
+
|
133
|
+
# Retrieve all of the known modifications, related identifiers and awards from the DMP
|
134
|
+
# rubocop:disable Metrics/AbcSize
|
135
|
+
def _extract_known
|
136
|
+
@known_mods = @dmp.fetch('dmphub_modifications', [])
|
137
|
+
|
138
|
+
ids = @dmp.fetch('dmproadmap_related_identifiers', []).map { |id| id['identifier'] }
|
139
|
+
ids += @known_mods.map { |m| m.fetch('dmproadmap_related_identifiers', []).map { |i| i['identifier'] } }
|
140
|
+
@known_works = ids.flatten.compact.uniq
|
141
|
+
|
142
|
+
fundings = @dmp.fetch('project', []).map { |proj| proj.fetch('funding', []) }.flatten.compact.uniq
|
143
|
+
awards = fundings.map { |fund| [fund['dmproadmap_funding_opportunity_id'], fund['grant_id']] }
|
144
|
+
.flatten.compact.uniq
|
145
|
+
|
146
|
+
awards += @known_mods.map do |mod|
|
147
|
+
mod.fetch('funding', []).map { |fund| [fund['dmproadmap_funding_opportunity_id'], fund['grant_id']] }
|
148
|
+
end
|
149
|
+
@known_awards = awards.flatten.compact.uniq.map { |award| award['identifier'] }.flatten.compact.uniq
|
150
|
+
end
|
151
|
+
# rubocop:enable Metrics/AbcSize
|
152
|
+
end
|
153
|
+
end
|
@@ -9,7 +9,6 @@ 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_AUGMENTER = 'No Augmenter specified!'
|
13
12
|
MSG_MISSING_DMP = 'No DMP or the DMP did not contain enough information to use.'
|
14
13
|
|
15
14
|
STOP_WORDS = %w[a an and if of or the then they].freeze
|
@@ -19,7 +18,6 @@ module Uc3DmpId
|
|
19
18
|
|
20
19
|
attr_accessor :dmp, :details_hash, :logger
|
21
20
|
|
22
|
-
# rubocop:disable Metrics/AbcSize
|
23
21
|
def initialize(**args)
|
24
22
|
@logger = args[:logger]
|
25
23
|
@details_hash = {}
|
@@ -28,7 +26,6 @@ module Uc3DmpId
|
|
28
26
|
_extract_dmp_details(dmp:)
|
29
27
|
raise ComparatorError, MSG_MISSING_DMP if @details_hash.empty?
|
30
28
|
end
|
31
|
-
# rubocop:enable Metrics/AbcSize
|
32
29
|
|
33
30
|
# Compare the incoming hash with the DMP details that were gathered during initialization.
|
34
31
|
#
|
@@ -989,173 +989,226 @@ module Uc3DmpId
|
|
989
989
|
title: 'Descriptive note',
|
990
990
|
examples: ['data received from event data']
|
991
991
|
},
|
992
|
-
|
993
|
-
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/
|
994
|
-
type: '
|
995
|
-
title: '
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
992
|
+
dmproadmap_related_identifiers: {
|
993
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/dmproadmap_related_identifiers',
|
994
|
+
type: 'array',
|
995
|
+
title: 'Related identifier modifications',
|
996
|
+
description: 'Identifiers discovered by an external API',
|
997
|
+
items: {
|
998
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/dmproadmap_related_identifiers/items',
|
999
|
+
type: 'object',
|
1000
|
+
title: 'A related identifier',
|
1001
|
+
properties: {
|
1002
|
+
descriptor: {
|
1003
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/dmproadmap_related_identifiers/items/properties/descriptor',
|
1004
|
+
type: 'string',
|
1005
|
+
enum: %w[
|
1006
|
+
is_cited_by
|
1007
|
+
cites
|
1008
|
+
is_supplement_to
|
1009
|
+
is_supplemented_by
|
1010
|
+
is_described_by
|
1011
|
+
describes
|
1012
|
+
has_metadata
|
1013
|
+
is_metadata_for
|
1014
|
+
is_part_of
|
1015
|
+
has_part
|
1016
|
+
is_referenced_by
|
1017
|
+
references
|
1018
|
+
is_documented_by
|
1019
|
+
documents
|
1020
|
+
is_new_version_of
|
1021
|
+
is_previous_version_of
|
1022
|
+
]
|
1023
|
+
},
|
1024
|
+
identifier: {
|
1025
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/dmproadmap_related_identifiers/items/properties/identifier',
|
1026
|
+
type: 'string',
|
1027
|
+
title: 'A unique identifier for the item',
|
1028
|
+
description: 'Identifier for a DMP',
|
1029
|
+
examples: ['https://doi.org/10.1371/journal.pcbi.1006750']
|
1030
|
+
},
|
1031
|
+
status: {
|
1032
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/dmproadmap_related_identifiers/items/properties/status',
|
1033
|
+
type: 'string',
|
1034
|
+
title: 'Modification status',
|
1035
|
+
enum: %w[
|
1036
|
+
accepted
|
1037
|
+
pending
|
1038
|
+
rejected
|
1039
|
+
]
|
1040
|
+
},
|
1041
|
+
type: {
|
1042
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/dmproadmap_related_identifiers/items/properties/type',
|
1043
|
+
type: 'string',
|
1044
|
+
enum: %w[
|
1045
|
+
handle
|
1046
|
+
doi
|
1047
|
+
ark
|
1048
|
+
url
|
1049
|
+
other
|
1050
|
+
]
|
1051
|
+
},
|
1052
|
+
work_type: {
|
1053
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/dmproadmap_related_identifiers/items/properties/work_type',
|
1054
|
+
type: 'string'
|
1055
|
+
}
|
1046
1056
|
},
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
identifier
|
1055
|
-
type
|
1056
|
-
work_type
|
1057
|
-
]
|
1057
|
+
required: %w[
|
1058
|
+
descriptor
|
1059
|
+
identifier
|
1060
|
+
type
|
1061
|
+
work_type
|
1062
|
+
]
|
1063
|
+
}
|
1058
1064
|
},
|
1059
1065
|
funding: {
|
1060
1066
|
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding',
|
1061
|
-
type: '
|
1062
|
-
title: '
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1067
|
+
type: 'array',
|
1068
|
+
title: 'Funding modifications',
|
1069
|
+
description: 'Funding information discovered by an external API',
|
1070
|
+
items: {
|
1071
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items',
|
1072
|
+
type: 'object',
|
1073
|
+
title: 'A funding',
|
1074
|
+
properties: {
|
1075
|
+
dmproadmap_project_number: {
|
1076
|
+
'$id': '#/properties/dmp/properties/project/items/properties/funding/items/properties/dmproadmap_project_number',
|
1077
|
+
type: 'string',
|
1078
|
+
title: "The funder's identifier for the research project",
|
1079
|
+
description: "The funder's identifier used to identify the research project",
|
1080
|
+
examples: ['prj-XYZ987-UCB']
|
1081
|
+
},
|
1082
|
+
funder_id: {
|
1083
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/funder_id',
|
1084
|
+
type: 'object',
|
1085
|
+
title: 'The Funder ID Schema',
|
1086
|
+
description: 'Funder ID of the associated project',
|
1087
|
+
properties: {
|
1088
|
+
identifier: {
|
1089
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/funder_id/properties/identifier',
|
1090
|
+
type: 'string',
|
1091
|
+
title: 'The Funder ID Value Schema',
|
1092
|
+
description: 'Funder ID, recommended to use CrossRef Funder Registry. See: https://www.crossref.org/services/funder-registry/',
|
1093
|
+
examples: ['501100002428']
|
1094
|
+
},
|
1095
|
+
type: {
|
1096
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/funder_id/properties/type',
|
1097
|
+
type: 'string',
|
1098
|
+
enum: %w[
|
1099
|
+
fundref
|
1100
|
+
ror
|
1101
|
+
url
|
1102
|
+
other
|
1103
|
+
],
|
1104
|
+
title: 'The Funder ID Type Schema',
|
1105
|
+
description: 'Identifier type. Allowed values: fundref, url, other',
|
1106
|
+
examples: ['fundref']
|
1107
|
+
}
|
1083
1108
|
},
|
1084
|
-
|
1085
|
-
|
1086
|
-
type
|
1087
|
-
|
1088
|
-
fundref
|
1089
|
-
ror
|
1090
|
-
url
|
1091
|
-
other
|
1092
|
-
],
|
1093
|
-
title: 'The Funder ID Type Schema',
|
1094
|
-
description: 'Identifier type. Allowed values: fundref, url, other',
|
1095
|
-
examples: ['fundref']
|
1096
|
-
}
|
1109
|
+
required: %w[
|
1110
|
+
identifier
|
1111
|
+
type
|
1112
|
+
]
|
1097
1113
|
},
|
1098
|
-
|
1099
|
-
|
1100
|
-
type
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
applied
|
1109
|
-
granted
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
type:
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1114
|
+
funding_status: {
|
1115
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/funding_status',
|
1116
|
+
type: 'string',
|
1117
|
+
enum: %w[
|
1118
|
+
planned
|
1119
|
+
applied
|
1120
|
+
granted
|
1121
|
+
rejected
|
1122
|
+
],
|
1123
|
+
title: 'The Funding Status Schema',
|
1124
|
+
description: 'To express different phases of project lifecycle. Allowed values: planned, applied, granted, rejected',
|
1125
|
+
examples: ['granted']
|
1126
|
+
},
|
1127
|
+
dmproadmap_funding_opportunity_id: {
|
1128
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/dmproadmap_funding_opportunity_id',
|
1129
|
+
type: 'object',
|
1130
|
+
title: 'The Funding Opportunity ID Schema',
|
1131
|
+
description: 'Opportunity ID of the associated project',
|
1132
|
+
properties: {
|
1133
|
+
identifier: {
|
1134
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/dmproadmap_funding_opportunity_id/properties/identifier',
|
1135
|
+
type: 'string',
|
1136
|
+
title: 'The Funding Opportunity ID Value Schema',
|
1137
|
+
description: 'Opportunity ID',
|
1138
|
+
examples: ['ABC-12345-03']
|
1139
|
+
},
|
1140
|
+
type: {
|
1141
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/dmproadmap_funding_opportunity_id/properties/type',
|
1142
|
+
type: 'string',
|
1143
|
+
title: 'The Funding Opportunity ID Type Schema',
|
1144
|
+
enum: %w[
|
1145
|
+
doi
|
1146
|
+
url
|
1147
|
+
other
|
1148
|
+
],
|
1149
|
+
description: 'Identifier type. Allowed values: url, other',
|
1150
|
+
examples: ['other']
|
1151
|
+
}
|
1128
1152
|
},
|
1129
|
-
|
1130
|
-
|
1131
|
-
type
|
1132
|
-
|
1133
|
-
enum: %w[
|
1134
|
-
doi
|
1135
|
-
url
|
1136
|
-
other
|
1137
|
-
],
|
1138
|
-
description: 'Identifier type. Allowed values: url, other',
|
1139
|
-
examples: ['other']
|
1140
|
-
}
|
1153
|
+
required: %w[
|
1154
|
+
identifier
|
1155
|
+
type
|
1156
|
+
]
|
1141
1157
|
},
|
1142
|
-
|
1143
|
-
|
1144
|
-
type
|
1145
|
-
|
1158
|
+
grant_id: {
|
1159
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/grant_id',
|
1160
|
+
type: 'object',
|
1161
|
+
title: 'The Funding Grant ID Schema',
|
1162
|
+
description: 'Grant ID of the associated project',
|
1163
|
+
properties: {
|
1164
|
+
identifier: {
|
1165
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/grant_id/properties/identifier',
|
1166
|
+
type: 'string',
|
1167
|
+
title: 'The Funding Grant ID Value Schema',
|
1168
|
+
description: 'Grant ID',
|
1169
|
+
examples: ['776242']
|
1170
|
+
},
|
1171
|
+
type: {
|
1172
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/grant_id/properties/type',
|
1173
|
+
type: 'string',
|
1174
|
+
title: 'The Funding Grant ID Type Schema',
|
1175
|
+
enum: %w[
|
1176
|
+
doi
|
1177
|
+
url
|
1178
|
+
other
|
1179
|
+
],
|
1180
|
+
description: 'Identifier type. Allowed values: url, other',
|
1181
|
+
examples: ['other']
|
1182
|
+
}
|
1183
|
+
},
|
1184
|
+
required: %w[
|
1185
|
+
identifier
|
1186
|
+
type
|
1187
|
+
]
|
1188
|
+
},
|
1189
|
+
status: {
|
1190
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/status',
|
1191
|
+
type: 'string',
|
1192
|
+
title: 'Modification status',
|
1193
|
+
enum: %w[
|
1194
|
+
accepted
|
1195
|
+
pending
|
1196
|
+
rejected
|
1197
|
+
]
|
1198
|
+
},
|
1199
|
+
name: {
|
1200
|
+
'$id': '#/properties/dmp/properties/dmphub_modifications/items/properties/funding/items/properties/name',
|
1201
|
+
type: 'string',
|
1202
|
+
title: 'The name of the funding instituion / organization',
|
1203
|
+
description: 'Name',
|
1204
|
+
examples: ['National Science Foundation']
|
1205
|
+
}
|
1146
1206
|
},
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
examples: ['National Science Foundation']
|
1153
|
-
}
|
1154
|
-
},
|
1155
|
-
required: %w[
|
1156
|
-
funding_status
|
1157
|
-
name
|
1158
|
-
]
|
1207
|
+
required: %w[
|
1208
|
+
funding_status
|
1209
|
+
name
|
1210
|
+
]
|
1211
|
+
}
|
1159
1212
|
},
|
1160
1213
|
project: {
|
1161
1214
|
'$id': '#/properties/dmp/properties/dmphub_modifications/project',
|
data/lib/uc3-dmp-id/updater.rb
CHANGED
@@ -13,7 +13,7 @@ module Uc3DmpId
|
|
13
13
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
14
14
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
15
15
|
# -------------------------------------------------------------------------
|
16
|
-
def update(provenance:, p_key:, json: {},
|
16
|
+
def update(provenance:, p_key:, json: {}, logger: nil)
|
17
17
|
raise UpdaterError, Helper::MSG_DMP_INVALID_DMP_ID unless p_key.is_a?(String) && !p_key.strip.empty?
|
18
18
|
|
19
19
|
mods = Helper.parse_json(json:).fetch('dmp', {})
|
@@ -40,13 +40,14 @@ module Uc3DmpId
|
|
40
40
|
version = Versioner.generate_version(client:, latest_version:, owner:,
|
41
41
|
updater:, logger:)
|
42
42
|
raise UpdaterError, Helper::MSG_DMP_UNABLE_TO_VERSION if version.nil?
|
43
|
+
# Bail if the system trying to make the update is not the creator of the DMP ID
|
44
|
+
raise UpdaterError, Helper::MSG_DMP_FORBIDDEN if owner != updater
|
43
45
|
|
44
46
|
# Remove the version info because we don't want to save it on the record
|
45
47
|
version.delete('dmphub_versions')
|
46
48
|
|
47
49
|
# Splice the assertions
|
48
|
-
version = _process_modifications(owner:, updater:, version:, mods:,
|
49
|
-
logger:)
|
50
|
+
version = _process_modifications(owner:, updater:, version:, mods:, logger:)
|
50
51
|
# Set the :modified timestamps
|
51
52
|
now = Time.now.utc
|
52
53
|
version['modified'] = now.iso8601
|
@@ -117,42 +118,23 @@ module Uc3DmpId
|
|
117
118
|
end
|
118
119
|
# rubocop:enable Metrics/AbcSize
|
119
120
|
|
120
|
-
# rubocop:disable Metrics/
|
121
|
-
def _process_modifications(owner:, updater:, version:, mods:,
|
121
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
122
|
+
def _process_modifications(owner:, updater:, version:, mods:, logger: nil)
|
122
123
|
return version unless mods.is_a?(Hash) && !updater.nil?
|
123
124
|
return mods unless version.is_a?(Hash) && !owner.nil?
|
124
125
|
|
125
|
-
updated = if owner == updater
|
126
|
-
# Splice together any assertions that may have been made while the user was editing the DMP ID
|
127
|
-
Asserter.splice(latest_version: version, modified_version: mods, logger:)
|
128
|
-
else
|
129
|
-
# Attach the incoming changes as an assertion to the DMP ID since the updater is NOT the owner
|
130
|
-
Asserter.add(updater:, latest_version: version, modified_version: mods, note:,
|
131
|
-
logger:)
|
132
|
-
end
|
133
|
-
|
134
|
-
_merge_versions(latest_version: version, mods: updated, logger:)
|
135
|
-
end
|
136
|
-
# rubocop:enable Metrics/ParameterLists
|
137
|
-
|
138
|
-
# We are replacing the latest version with the modifcations but want to retain the PK, SK and any dmphub_ prefixed
|
139
|
-
# entries in the metadata so that we do not lose creation timestamps, provenance ids, etc.
|
140
|
-
# rubocop:disable Metrics/AbcSize
|
141
|
-
def _merge_versions(latest_version:, mods:, logger: nil)
|
142
|
-
return mods unless latest_version.is_a?(Hash)
|
143
|
-
|
144
126
|
logger.debug(message: 'Modifications before merge.', details: mods) if logger.respond_to?(:debug)
|
145
|
-
keys_to_retain =
|
127
|
+
keys_to_retain = version.keys.select do |key|
|
146
128
|
(key.start_with?('dmphub_') && !%w[dmphub_modifications dmphub_versions].include?(key)) ||
|
147
129
|
key.start_with?('PK') || key.start_with?('SK')
|
148
130
|
end
|
149
131
|
keys_to_retain.each do |key|
|
150
|
-
mods[key] =
|
132
|
+
mods[key] = version[key]
|
151
133
|
end
|
152
134
|
logger.debug(message: 'Modifications after merge.', details: mods) if logger.respond_to?(:debug)
|
153
135
|
mods
|
154
136
|
end
|
155
|
-
# rubocop:enable Metrics/AbcSize
|
137
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
156
138
|
|
157
139
|
# Once the DMP has been updated, we need to update it's DOI metadata
|
158
140
|
# -------------------------------------------------------------------------
|
data/lib/uc3-dmp-id/version.rb
CHANGED
data/lib/uc3-dmp-id.rb
CHANGED
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.
|
4
|
+
version: 0.1.7
|
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-
|
11
|
+
date: 2023-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: uc3-dmp-citation
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: uc3-dmp-dynamo
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: uc3-dmp-external-api
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.0'
|
83
111
|
description: Helpers for working with JSON that represents a DMP ID
|
84
112
|
email:
|
85
113
|
- brian.riley@ucop.edu
|
@@ -89,7 +117,7 @@ extra_rdoc_files: []
|
|
89
117
|
files:
|
90
118
|
- README.md
|
91
119
|
- lib/uc3-dmp-id.rb
|
92
|
-
- lib/uc3-dmp-id/
|
120
|
+
- lib/uc3-dmp-id/augmenter.rb
|
93
121
|
- lib/uc3-dmp-id/comparator.rb
|
94
122
|
- lib/uc3-dmp-id/creator.rb
|
95
123
|
- lib/uc3-dmp-id/deleter.rb
|
data/lib/uc3-dmp-id/asserter.rb
DELETED
@@ -1,216 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'time'
|
4
|
-
|
5
|
-
module Uc3DmpId
|
6
|
-
class AsserterError < StandardError; end
|
7
|
-
|
8
|
-
# Class that handles changes to a DMP ID's :dmphub_modifications section
|
9
|
-
class Asserter
|
10
|
-
DEFAULT_DESCRIPTOR = 'references'
|
11
|
-
DEFAULT_WORK_TYPE = 'other'
|
12
|
-
|
13
|
-
class << self
|
14
|
-
# Add assertions to a DMP ID - this is performed by non-provenance systems
|
15
|
-
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
16
|
-
def add(updater:, latest_version:, modified_version:, note: nil, logger: nil)
|
17
|
-
return latest_version unless latest_version.is_a?(Hash)
|
18
|
-
|
19
|
-
owner = latest_version['dmphub_provenance_id']&.gsub('PROVENANCE#', '')
|
20
|
-
# If the updater and provenance are the same just return the :dmp as-is
|
21
|
-
return latest_version if updater.nil? || !latest_version.is_a?(Hash) || !modified_version.is_a?(Hash) ||
|
22
|
-
updater&.gsub('PROVENANCE#', '') == owner
|
23
|
-
|
24
|
-
# contact = modified_version['contact']
|
25
|
-
# contributor = modified_version.fetch('contributor', [])
|
26
|
-
# project = modified_version.fetch('project', [])
|
27
|
-
funding = modified_version.fetch('project', []).first&.fetch('funding', [])
|
28
|
-
related_works = modified_version.fetch('dmproadmap_related_identifiers', [])
|
29
|
-
|
30
|
-
if related_works.any?
|
31
|
-
latest_version = _add_related_identifier(updater:, latest_version:,
|
32
|
-
identifiers: related_works, note:, logger:)
|
33
|
-
end
|
34
|
-
return latest_version unless !funding.nil? && funding.any?
|
35
|
-
|
36
|
-
_add_funding_mod(updater:, latest_version:, funding:,
|
37
|
-
note:, logger:)
|
38
|
-
end
|
39
|
-
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
40
|
-
|
41
|
-
# Splice together assertions made by the owner of the DMP ID so that any :dmphub_modifications made to
|
42
|
-
# the record while it was being updated are not lost
|
43
|
-
# rubocop:disable Metrics/AbcSize
|
44
|
-
def splice(latest_version:, modified_version:, logger: nil)
|
45
|
-
# Return the modified_version if the timestamps are the same OR neither version has :dmphub_modifications
|
46
|
-
return modified_version if latest_version['modified'] == modified_version['modified'] ||
|
47
|
-
(latest_version.fetch('dmphub_modifications', []).empty? &&
|
48
|
-
modified_version.fetch('dmphub_modifications', []).empty?)
|
49
|
-
|
50
|
-
# Clone any existing :dmphub_modifications on the current DMP ID so we can retain them
|
51
|
-
existing_assertions = Helper.deep_copy_dmp(obj: latest_version.fetch('dmphub_modifications', []))
|
52
|
-
incoming_assertions = Helper.deep_copy_dmp(obj: modified_version.fetch('dmphub_modifications', []))
|
53
|
-
if logger.respond_to?(:debug)
|
54
|
-
logger.debug(message: 'Existing dmphub_modifications',
|
55
|
-
details: existing_assertions)
|
56
|
-
end
|
57
|
-
if logger.respond_to?(:debug)
|
58
|
-
logger.debug(message: 'Incoming dmphub_modifications',
|
59
|
-
details: incoming_assertions)
|
60
|
-
end
|
61
|
-
|
62
|
-
# Keep any :dmphub_modifications and then add the incoming to the Array
|
63
|
-
modified_version['dmphub_modifications'] = existing_assertions
|
64
|
-
return modified_version unless incoming_assertions.any?
|
65
|
-
|
66
|
-
# Add any of the assertions still on the incoming record back to the latest record
|
67
|
-
incoming_assertions.each { |entry| modified_version['dmphub_modifications'] << entry }
|
68
|
-
modified_version
|
69
|
-
end
|
70
|
-
# rubocop:enable Metrics/AbcSize
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
# Verify that the DMP ID record does not already have the specified identifiers and then add them
|
75
|
-
# to the :latest_version in the :dmphub_modifications Array
|
76
|
-
#
|
77
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
78
|
-
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
79
|
-
def _add_related_identifier(updater:, latest_version:, identifiers:, note: '', logger: nil)
|
80
|
-
return latest_version unless updater.is_a?(String) && latest_version.is_a?(Hash) && identifiers.is_a?(Array)
|
81
|
-
|
82
|
-
latest_version['dmphub_modifications'] = [] if latest_version['dmphub_modifications'].nil?
|
83
|
-
known_mods = latest_version['dmphub_modifications'].map do |mod|
|
84
|
-
mod.fetch('dmproadmap_related_identifiers', [])
|
85
|
-
end
|
86
|
-
known_mods = known_mods.flatten.compact.map { |mod| mod['identifier'].downcase.strip }.compact.uniq
|
87
|
-
|
88
|
-
asserted = latest_version.fetch('dmproadmap_related_identifiers', [])
|
89
|
-
asserted = asserted.flatten.compact.map { |mod| mod['identifier'].downcase.strip }.compact.uniq
|
90
|
-
|
91
|
-
additions = []
|
92
|
-
identifiers.each do |related_identifier|
|
93
|
-
# Skip if there is no :type or :identifier value
|
94
|
-
if !related_identifier.is_a?(Hash) || related_identifier['type'].nil? || related_identifier['identifier'].nil?
|
95
|
-
next
|
96
|
-
end
|
97
|
-
|
98
|
-
id = related_identifier['identifier'].downcase.strip
|
99
|
-
# Skip if the :identifier is already listed in :dmphub_modifications or the
|
100
|
-
# :dmproadmap_related_identifiers Arrays
|
101
|
-
next if known_mods.include?(id) || asserted.include?(id)
|
102
|
-
|
103
|
-
related_identifier['work_type'] = DEFAULT_WORK_TYPE if related_identifier['work_type'].nil?
|
104
|
-
related_identifier['descriptor'] = DEFAULT_DESCRIPTOR if related_identifier['descriptor'].nil?
|
105
|
-
additions << related_identifier
|
106
|
-
end
|
107
|
-
|
108
|
-
latest_version['dmproadmap_related_identifiers'] = [] if latest_version['dmproadmap_related_identifiers'].nil?
|
109
|
-
assertion = _generate_assertion(updater:, note:,
|
110
|
-
mods: JSON.parse({ dmproadmap_related_identifiers: additions }.to_json))
|
111
|
-
if logger.respond_to?(:debug)
|
112
|
-
logger.debug(message: 'Adding change to :dmphub_modifications.',
|
113
|
-
details: assertion)
|
114
|
-
end
|
115
|
-
latest_version['dmphub_modifications'] << assertion
|
116
|
-
latest_version
|
117
|
-
end
|
118
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
119
|
-
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
120
|
-
|
121
|
-
# Verify that the DMP ID record does not already have the specified funding change and then add it
|
122
|
-
# to the :latest_version in the :dmphub_modifications Array
|
123
|
-
#
|
124
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
125
|
-
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
126
|
-
def _add_funding_mod(updater:, latest_version:, funding:, note: '', logger: nil)
|
127
|
-
return latest_version unless updater.is_a?(String) && latest_version.is_a?(Hash) && funding.is_a?(Array)
|
128
|
-
|
129
|
-
known_mods = latest_version['dmphub_modifications'].map do |mod|
|
130
|
-
next if mod.nil?
|
131
|
-
|
132
|
-
mod.fetch('funding', {}).fetch('grant_id', {})['identifier']&.downcase&.strip
|
133
|
-
end
|
134
|
-
known_mods = known_mods.flatten.compact.uniq
|
135
|
-
|
136
|
-
asserted = latest_version.fetch('project', [])&.map do |project|
|
137
|
-
next if project.nil?
|
138
|
-
|
139
|
-
project&.fetch('funding', [])&.first&.fetch('grant_id', {})&.[]('identifier')&.downcase&.strip
|
140
|
-
end
|
141
|
-
asserted = asserted.flatten.compact.uniq
|
142
|
-
|
143
|
-
fund = funding.reject { |entry| entry['grant_id'].nil? }.first
|
144
|
-
# Skip if there is no :grant_id
|
145
|
-
return latest_version if !fund.is_a?(Hash) || fund.fetch('grant_id', {})['identifier'].nil?
|
146
|
-
|
147
|
-
grant_id = fund.fetch('grant_id', {})['identifier'].downcase.strip
|
148
|
-
# Skip if the :grant_id is already listed as a :dmphub_modifications or project: :funding
|
149
|
-
return latest_version if known_mods.include?(grant_id) || asserted.include?(grant_id)
|
150
|
-
|
151
|
-
latest_version['dmphub_modifications'] = [] if latest_version['dmphub_modifications'].nil?
|
152
|
-
mod = JSON.parse({ funding: fund }.to_json)
|
153
|
-
mod['funding']['funding_status'] = 'granted'
|
154
|
-
assertion = _generate_assertion(updater:, mods: mod, note:)
|
155
|
-
if logger.respond_to?(:debug)
|
156
|
-
logger.debug(message: 'Adding change to :dmphub_modifications.',
|
157
|
-
details: assertion)
|
158
|
-
end
|
159
|
-
latest_version['dmphub_modifications'] << assertion
|
160
|
-
latest_version
|
161
|
-
end
|
162
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
163
|
-
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
164
|
-
|
165
|
-
# Generate an assertion entry. For example:
|
166
|
-
#
|
167
|
-
# {
|
168
|
-
# "id": "ABCD1234",
|
169
|
-
# "provenance": "dmphub",
|
170
|
-
# "timestamp": "2023-07-07T14:50:23+00:00",
|
171
|
-
# "note": "Data received from OpenAlex, matched by PI names and title keywords.",
|
172
|
-
# "confiedence": "Med",
|
173
|
-
# "dmproadmap_related_identifiers": {
|
174
|
-
# "work_type": "article",
|
175
|
-
# "descriptor": "is_cited_by",
|
176
|
-
# "type": "doi",
|
177
|
-
# "identifier": "https://dx.doi.org/99.9876/ZYX987.V6"
|
178
|
-
# }
|
179
|
-
# }
|
180
|
-
#
|
181
|
-
# OR:
|
182
|
-
#
|
183
|
-
# {
|
184
|
-
# "id": "ABCD1234",
|
185
|
-
# "provenance": "dmphub",
|
186
|
-
# "timestamp": "2023-07-07T14:50:23+00:00",
|
187
|
-
# "note": "Data received from the NIH API, matched by the opportunity number.",
|
188
|
-
# "confidence": "High",
|
189
|
-
# "funding": {
|
190
|
-
# "funding_status": "granted",
|
191
|
-
# "grant_id": {
|
192
|
-
# "identifier": "2019/22702-3",
|
193
|
-
# "type": "other"
|
194
|
-
# }
|
195
|
-
# }
|
196
|
-
# }
|
197
|
-
def _generate_assertion(updater:, mods:, note: '')
|
198
|
-
return nil if updater.nil? || !mods.is_a?(Hash)
|
199
|
-
|
200
|
-
assertion = {
|
201
|
-
id: SecureRandom.hex(4).upcase,
|
202
|
-
provenance: updater.gsub('PROVENANCE#', ''),
|
203
|
-
timestamp: Time.now.utc.iso8601,
|
204
|
-
status: 'pending',
|
205
|
-
note:
|
206
|
-
}
|
207
|
-
mods.each_pair { |key, val| assertion[key] = val }
|
208
|
-
JSON.parse(assertion.to_json)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def _score_related_work(latest_version:, work:); end
|
213
|
-
|
214
|
-
def _score_funding(latest_version:, funding:); end
|
215
|
-
end
|
216
|
-
end
|