labimotion 2.4.0.rc2 → 2.4.0.rc4
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/CHANGELOG.md +9 -0
- data/lib/labimotion/apis/generic_dataset_api.rb +41 -3
- data/lib/labimotion/apis/generic_element_api.rb +60 -8
- data/lib/labimotion/apis/generic_klass_api.rb +50 -6
- data/lib/labimotion/apis/klass_share_api.rb +646 -0
- data/lib/labimotion/apis/labimotion_api.rb +1 -0
- data/lib/labimotion/apis/labimotion_doi_api.rb +24 -10
- data/lib/labimotion/apis/labimotion_template_browse_api.rb +13 -1
- data/lib/labimotion/apis/segment_api.rb +61 -10
- data/lib/labimotion/constants.rb +14 -0
- data/lib/labimotion/entities/application_entity.rb +13 -1
- data/lib/labimotion/entities/generic_klass_entity.rb +125 -0
- data/lib/labimotion/entities/klass_share_entity.rb +48 -0
- data/lib/labimotion/helpers/dataset_helpers.rb +123 -1
- data/lib/labimotion/helpers/element_helpers.rb +206 -4
- data/lib/labimotion/helpers/generic_helpers.rb +276 -4
- data/lib/labimotion/helpers/param_helpers.rb +68 -0
- data/lib/labimotion/helpers/segment_helpers.rb +87 -4
- data/lib/labimotion/libs/ai_egress_guard.rb +84 -0
- data/lib/labimotion/libs/ai_template.rb +1482 -0
- data/lib/labimotion/libs/data/datacite/labimotion_template.html.erb +67 -0
- data/lib/labimotion/libs/file_extractor.rb +210 -0
- data/lib/labimotion/libs/owner_resolver.rb +50 -0
- data/lib/labimotion/libs/ownership_audit.rb +73 -0
- data/lib/labimotion/libs/share_notifier.rb +114 -0
- data/lib/labimotion/libs/share_resolver.rb +369 -0
- data/lib/labimotion/models/concerns/template_doi.rb +133 -0
- data/lib/labimotion/models/dataset_klass.rb +1 -1
- data/lib/labimotion/models/element_klass.rb +1 -1
- data/lib/labimotion/models/klass_share.rb +126 -0
- data/lib/labimotion/models/segment_klass.rb +1 -1
- data/lib/labimotion/usecases/build_template_doi_xml.rb +69 -23
- data/lib/labimotion/usecases/release_template_doi.rb +42 -17
- data/lib/labimotion/usecases/template_doi_helpers.rb +28 -6
- data/lib/labimotion/usecases/update_template_publication_metadata.rb +71 -2
- data/lib/labimotion/utils/prop.rb +1 -0
- data/lib/labimotion/version.rb +1 -1
- data/lib/labimotion.rb +11 -0
- metadata +28 -2
|
@@ -11,7 +11,11 @@ module Labimotion
|
|
|
11
11
|
class BuildTemplateDoiXml
|
|
12
12
|
include ::DataCitePublisher
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
# The template ships with the gem. A host app may still override it by
|
|
15
|
+
# placing its own copy at HOST_TEMPLATE_PATH, which is how it used to be
|
|
16
|
+
# sourced — hence the lookup order below.
|
|
17
|
+
GEM_TEMPLATE_PATH = File.expand_path('../libs/data/datacite/labimotion_template.html.erb', __dir__)
|
|
18
|
+
HOST_TEMPLATE_PATH = 'app/publish/datacite_metadata_labimotion_template.html.erb'
|
|
15
19
|
|
|
16
20
|
RESOURCE_LABEL = {
|
|
17
21
|
'element' => 'LabIMotion Element Template',
|
|
@@ -19,21 +23,28 @@ module Labimotion
|
|
|
19
23
|
'dataset' => 'LabIMotion Dataset Template'
|
|
20
24
|
}.freeze
|
|
21
25
|
|
|
22
|
-
def initialize(record, doi = nil
|
|
26
|
+
def initialize(record, doi = nil)
|
|
23
27
|
@record = record
|
|
24
28
|
@doi = doi || ::Doi.labimotion_latest_doi(record)
|
|
25
|
-
@current_user = current_user
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
# Returns the XML string, or nil when no DOI has been reserved yet.
|
|
29
32
|
def call
|
|
30
33
|
return nil if @doi.nil?
|
|
31
34
|
|
|
32
|
-
ERB.new(File.read(
|
|
35
|
+
ERB.new(File.read(template_path), trim_mode: '-').result(binding)
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
private
|
|
36
39
|
|
|
40
|
+
# Resolved per call rather than at load time, so the class no longer needs
|
|
41
|
+
# Rails to be booted just to be autoloaded.
|
|
42
|
+
def template_path
|
|
43
|
+
# `Rails.root` is still nil if Rails is loaded but the app has not booted.
|
|
44
|
+
host = defined?(Rails) && Rails.respond_to?(:root) ? Rails.root&.join(HOST_TEMPLATE_PATH) : nil
|
|
45
|
+
host && File.exist?(host) ? host : GEM_TEMPLATE_PATH
|
|
46
|
+
end
|
|
47
|
+
|
|
37
48
|
def full_doi
|
|
38
49
|
@doi.full_doi
|
|
39
50
|
end
|
|
@@ -65,35 +76,70 @@ module Labimotion
|
|
|
65
76
|
end
|
|
66
77
|
|
|
67
78
|
def creators
|
|
68
|
-
Array(publication['authors']).filter_map
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
given = author['givenName'].to_s.strip
|
|
72
|
-
family = author['familyName'].to_s.strip
|
|
73
|
-
next if given.empty? && family.empty?
|
|
79
|
+
Array(publication['authors']).filter_map { |author| person(author) }
|
|
80
|
+
end
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
# The people credited as DataCite contributors, each in their own role.
|
|
83
|
+
# Held apart from the creators — a contributor need not be one of the
|
|
84
|
+
# authors — and empty (no contributors block) until one is named; they are
|
|
85
|
+
# never inferred from whoever happens to be publishing. DOIs released
|
|
86
|
+
# before contributors became a list carry a single `contributor`.
|
|
87
|
+
def contributors
|
|
88
|
+
list = publication['contributors']
|
|
89
|
+
list = [publication['contributor']].compact unless list.is_a?(Array)
|
|
90
|
+
list.filter_map do |entry|
|
|
91
|
+
person(entry)&.merge('contributorType' => h(contributor_type(entry)))
|
|
77
92
|
end
|
|
78
93
|
end
|
|
79
94
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
95
|
+
def contributor_type(entry)
|
|
96
|
+
type = entry['contributorType'].to_s.strip
|
|
97
|
+
types = TemplateDoiHelpers::CONTRIBUTOR_TYPES
|
|
98
|
+
types.include?(type) ? type : TemplateDoiHelpers::DEFAULT_CONTRIBUTOR_TYPE
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# A creator or contributor, escaped for the template. Nameless entries are
|
|
102
|
+
# dropped: DataCite has nowhere to put an ORCID or affiliation on its own.
|
|
103
|
+
# `ror` is the resolver URI DataCite expects in affiliationIdentifier,
|
|
104
|
+
# rebuilt from the bare id that is stored.
|
|
105
|
+
def person(entry)
|
|
106
|
+
return nil unless entry.is_a?(Hash)
|
|
85
107
|
|
|
86
|
-
given =
|
|
87
|
-
family =
|
|
108
|
+
given = entry['givenName'].to_s.strip
|
|
109
|
+
family = entry['familyName'].to_s.strip
|
|
88
110
|
return nil if given.empty? && family.empty?
|
|
89
111
|
|
|
90
|
-
|
|
91
|
-
{ 'givenName' => h(given), 'familyName' => h(family), 'orcid' => h(orcid)
|
|
112
|
+
ror = entry['ror_id'].to_s.strip
|
|
113
|
+
{ 'givenName' => h(given), 'familyName' => h(family), 'orcid' => h(entry['orcid'].to_s.strip),
|
|
114
|
+
'affiliation' => h(entry['affiliation'].to_s.strip),
|
|
115
|
+
'ror' => ror.empty? ? '' : h("https://ror.org/#{ror}") }
|
|
92
116
|
end
|
|
93
117
|
|
|
118
|
+
# Works this template cites, published as DataCite relatedIdentifiers of
|
|
119
|
+
# relationType "References". Only the DOI travels — a relatedIdentifier
|
|
120
|
+
# carries no title — so entries without one are skipped.
|
|
121
|
+
def references
|
|
122
|
+
Array(publication['references']).filter_map do |reference|
|
|
123
|
+
next unless reference.is_a?(Hash)
|
|
124
|
+
|
|
125
|
+
doi = reference['doi'].to_s.strip
|
|
126
|
+
next if doi.empty?
|
|
127
|
+
|
|
128
|
+
{ 'doi' => h(doi) }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# What this DOI renders: the snapshot taken when it was released, so every
|
|
133
|
+
# released version keeps showing the metadata it was published with rather
|
|
134
|
+
# than the template's latest. A DOI that is still reserved has no snapshot
|
|
135
|
+
# and renders the template's current publication, which is what is still
|
|
136
|
+
# being edited for it.
|
|
94
137
|
def publication
|
|
95
|
-
@publication ||=
|
|
96
|
-
|
|
138
|
+
@publication ||= ::Doi.labimotion_doi_publication(@doi).presence || record_publication
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def record_publication
|
|
142
|
+
(@record.properties_template.is_a?(Hash) ? @record.properties_template['publication'] : nil) || {}
|
|
97
143
|
end
|
|
98
144
|
|
|
99
145
|
def h(value)
|
|
@@ -24,7 +24,8 @@ module Labimotion
|
|
|
24
24
|
return failure('doi not reserved') if @doi.nil?
|
|
25
25
|
return self if @doi.minted
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
missing = missing_metadata
|
|
28
|
+
return failure("publication metadata is incomplete: #{missing.join(', ')}") if missing.any?
|
|
28
29
|
|
|
29
30
|
publish_at_datacite
|
|
30
31
|
self
|
|
@@ -37,22 +38,33 @@ module Labimotion
|
|
|
37
38
|
private
|
|
38
39
|
|
|
39
40
|
def publish_at_datacite
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# DataCite requires the metadata to exist before the DOI can be minted.
|
|
43
|
-
metadata = mds.upload_metadata(BuildTemplateDoiXml.new(record, doi, current_user).call)
|
|
44
|
-
return failure(datacite_error('metadata upload', metadata)) unless metadata.is_a?(Net::HTTPSuccess)
|
|
45
|
-
|
|
46
|
-
response = mds.mint(doi.full_doi, TemplateDoiHelpers.template_url(record))
|
|
47
|
-
return failure(datacite_error('mint', response)) unless response.is_a?(Net::HTTPSuccess)
|
|
48
|
-
end
|
|
41
|
+
datacite_failure = send_to_datacite? ? mint_at_datacite : nil
|
|
42
|
+
return failure(datacite_failure) if datacite_failure
|
|
49
43
|
|
|
50
44
|
ActiveRecord::Base.transaction do
|
|
51
|
-
|
|
45
|
+
# The publication is snapshotted onto the DOI as it is minted, so this
|
|
46
|
+
# version keeps rendering what it was published with once the template's
|
|
47
|
+
# metadata moves on to the next version.
|
|
48
|
+
doi.update!(minted: true, minted_at: Time.current,
|
|
49
|
+
metadata: ::Doi.labimotion_metadata_with_publication(doi, publication))
|
|
52
50
|
stamp_release_on_record
|
|
53
51
|
end
|
|
54
52
|
end
|
|
55
53
|
|
|
54
|
+
# Uploads the metadata and mints the DOI at DataCite. Returns an error
|
|
55
|
+
# message, or nil when both calls succeeded.
|
|
56
|
+
def mint_at_datacite
|
|
57
|
+
mds = ::Repo::Datacite::Mds.new
|
|
58
|
+
# DataCite requires the metadata to exist before the DOI can be minted.
|
|
59
|
+
metadata = mds.upload_metadata(BuildTemplateDoiXml.new(record, doi).call)
|
|
60
|
+
return datacite_error('metadata upload', metadata) unless metadata.is_a?(Net::HTTPSuccess)
|
|
61
|
+
|
|
62
|
+
response = mds.mint(doi.full_doi, TemplateDoiHelpers.template_url(record))
|
|
63
|
+
return datacite_error('mint', response) unless response.is_a?(Net::HTTPSuccess)
|
|
64
|
+
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
|
|
56
68
|
def datacite_error(step, response)
|
|
57
69
|
status = response.respond_to?(:code) ? response.code : 'unknown'
|
|
58
70
|
"datacite #{step} failed (status #{status})"
|
|
@@ -73,13 +85,26 @@ module Labimotion
|
|
|
73
85
|
record.update!(attrs) if attrs.any?
|
|
74
86
|
end
|
|
75
87
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
# Everything the publication form owns that DataCite needs, named so the
|
|
89
|
+
# modal can say what is still missing. Contributors and references are
|
|
90
|
+
# optional: not every template credits a role beyond its authors, and not
|
|
91
|
+
# every template cites a work.
|
|
92
|
+
def missing_metadata
|
|
93
|
+
missing = %w[title description].reject { |key| publication[key].to_s.strip.present? }
|
|
94
|
+
missing << 'authors' unless Array(publication['authors']).any? { |author| named?(author) }
|
|
95
|
+
missing
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def named?(person)
|
|
99
|
+
person.is_a?(Hash) && person['familyName'].to_s.strip.present?
|
|
100
|
+
end
|
|
79
101
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
# The template's current publication metadata — what this release publishes.
|
|
103
|
+
def publication
|
|
104
|
+
@publication ||= begin
|
|
105
|
+
pub = record.properties_template.is_a?(Hash) ? record.properties_template['publication'] : nil
|
|
106
|
+
pub.is_a?(Hash) ? pub : {}
|
|
107
|
+
end
|
|
83
108
|
end
|
|
84
109
|
|
|
85
110
|
def failure(message)
|
|
@@ -15,11 +15,29 @@ module Labimotion
|
|
|
15
15
|
# admin rights over it. Keys are plural, matching the LabIMotion gem's
|
|
16
16
|
# `authenticate_admin!` and `User#generic_admin`.
|
|
17
17
|
GENERIC_ADMIN_KEY = {
|
|
18
|
-
::Labimotion::ElementKlass =>
|
|
19
|
-
::Labimotion::SegmentKlass =>
|
|
20
|
-
::Labimotion::DatasetKlass =>
|
|
18
|
+
::Labimotion::ElementKlass => ::Labimotion::Constants::Family::ELEMENT,
|
|
19
|
+
::Labimotion::SegmentKlass => ::Labimotion::Constants::Family::SEGMENT,
|
|
20
|
+
::Labimotion::DatasetKlass => ::Labimotion::Constants::Family::DATASET
|
|
21
21
|
}.freeze
|
|
22
22
|
|
|
23
|
+
# Where the hub lives, most specific first. PUBLIC_URL is the instance's own
|
|
24
|
+
# public address (the host app's config/application.rb insists on it), so the
|
|
25
|
+
# link follows whichever instance is serving it rather than a hardcoded
|
|
26
|
+
# chemotion-repository.net. The DataCite-facing domains stay as fallbacks for
|
|
27
|
+
# deployments that only set those.
|
|
28
|
+
HOST_ENV_KEYS = %w[PUBLIC_URL REPO_HOST DOI_DOMAIN_TEST DOI_DOMAIN].freeze
|
|
29
|
+
|
|
30
|
+
# The DataCite kernel-4 contributorType vocabulary. A template DOI may name
|
|
31
|
+
# any number of contributors — including none — each credited in one of
|
|
32
|
+
# these roles. The list is closed: DataCite rejects anything else.
|
|
33
|
+
CONTRIBUTOR_TYPES = %w[
|
|
34
|
+
ContactPerson DataCollector DataCurator DataManager Distributor Editor
|
|
35
|
+
HostingInstitution Producer ProjectLeader ProjectManager ProjectMember
|
|
36
|
+
RegistrationAgency RegistrationAuthority RelatedPerson Researcher
|
|
37
|
+
ResearchGroup RightsHolder Sponsor Supervisor WorkPackageLeader Other
|
|
38
|
+
].freeze
|
|
39
|
+
DEFAULT_CONTRIBUTOR_TYPE = 'Researcher'
|
|
40
|
+
|
|
23
41
|
module_function
|
|
24
42
|
|
|
25
43
|
# True once the template's latest DOI has been released (minted) and the
|
|
@@ -49,13 +67,17 @@ module Labimotion
|
|
|
49
67
|
# Used as the DOI's DataCite landing page and surfaced in the UI.
|
|
50
68
|
def template_url(record)
|
|
51
69
|
type = klass_type_for(record)
|
|
52
|
-
host = ENV['REPO_HOST'].presence || ENV['DOI_DOMAIN_TEST'].presence ||
|
|
53
|
-
ENV['DOI_DOMAIN'].presence || 'https://www.chemotion-repository.net'
|
|
54
70
|
record_id = record.try(:identifier).presence || record.try(:uuid).presence || record.id
|
|
55
71
|
query = "type=finder&kind=#{type}&id=#{record_id}"
|
|
56
72
|
version = record.try(:version)
|
|
57
73
|
query += "&version=#{version}" if version.present?
|
|
58
|
-
"#{
|
|
74
|
+
"#{public_host}/home/genericHub?#{query}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# The instance serving the hub, without its trailing slash.
|
|
78
|
+
def public_host
|
|
79
|
+
host = HOST_ENV_KEYS.lazy.filter_map { |key| ENV[key].presence }.first
|
|
80
|
+
(host || 'http://localhost:3000').chomp('/')
|
|
59
81
|
end
|
|
60
82
|
|
|
61
83
|
# A user may manage a template's DOI iff they hold the LabIMotion
|
|
@@ -7,7 +7,12 @@ module Labimotion
|
|
|
7
7
|
class UpdateTemplatePublicationMetadata
|
|
8
8
|
include TemplateDoiHelpers
|
|
9
9
|
|
|
10
|
-
ALLOWED_KEYS = %w[title description authors license].freeze
|
|
10
|
+
ALLOWED_KEYS = %w[title description authors contributors references license].freeze
|
|
11
|
+
REFERENCE_KEYS = %w[doi title url].freeze
|
|
12
|
+
# Authors and contributors are the same shape: a DataCite personal name plus
|
|
13
|
+
# the identifiers that make it resolvable — ORCID for the person, ROR for
|
|
14
|
+
# their organisation. Contributors carry a contributorType on top.
|
|
15
|
+
PERSON_KEYS = %w[givenName familyName orcid affiliation affiliation_id ror_id].freeze
|
|
11
16
|
|
|
12
17
|
attr_reader :record, :current_user, :publication, :error
|
|
13
18
|
|
|
@@ -39,18 +44,82 @@ module Labimotion
|
|
|
39
44
|
def normalized_publication
|
|
40
45
|
slice = publication.stringify_keys.slice(*ALLOWED_KEYS)
|
|
41
46
|
slice['authors'] = Array(slice['authors']).filter_map { |a| normalize_author(a) }
|
|
47
|
+
# Contributors are people in their own right — none of them need be one of
|
|
48
|
+
# the authors — so they are stored as given rather than matched back
|
|
49
|
+
# against the author list. There may be any number, including none.
|
|
50
|
+
slice['contributors'] = submitted_contributors.filter_map { |c| normalize_contributor(c) }
|
|
51
|
+
slice['references'] = Array(slice['references']).filter_map { |r| normalize_reference(r) }
|
|
42
52
|
slice
|
|
43
53
|
end
|
|
44
54
|
|
|
55
|
+
# A single `contributor` is what the first version of the form sent; a
|
|
56
|
+
# template saved by it is read back into the list rather than dropped.
|
|
57
|
+
def submitted_contributors
|
|
58
|
+
given = publication.stringify_keys
|
|
59
|
+
given['contributors'].is_a?(Array) ? given['contributors'] : [given['contributor']].compact
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A person plus the DataCite role they are credited in. An unknown or
|
|
63
|
+
# missing role falls back to "Researcher" rather than failing the save —
|
|
64
|
+
# the vocabulary is closed, so anything else would be rejected at minting.
|
|
65
|
+
def normalize_contributor(contributor)
|
|
66
|
+
h = normalize_author(contributor)
|
|
67
|
+
return nil unless h
|
|
68
|
+
|
|
69
|
+
type = contributor.stringify_keys['contributorType'].to_s.strip
|
|
70
|
+
known = TemplateDoiHelpers::CONTRIBUTOR_TYPES.include?(type)
|
|
71
|
+
h.merge('contributorType' => known ? type : TemplateDoiHelpers::DEFAULT_CONTRIBUTOR_TYPE)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# A cited work, identified by its DOI. Anything without one is dropped: the
|
|
75
|
+
# DOI is what goes to DataCite as a relatedIdentifier, and a reference
|
|
76
|
+
# without it would be published as an empty relation.
|
|
77
|
+
def normalize_reference(reference)
|
|
78
|
+
return nil unless reference.is_a?(Hash)
|
|
79
|
+
|
|
80
|
+
h = reference.stringify_keys.slice(*REFERENCE_KEYS)
|
|
81
|
+
h['doi'] = sanitize_doi(h['doi'])
|
|
82
|
+
return nil if h['doi'].nil?
|
|
83
|
+
|
|
84
|
+
h.transform_values { |v| v.to_s.strip }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Accepts a bare DOI, a doi: prefix or a resolver URL, and keeps the bare
|
|
88
|
+
# "10.x/y" form. Nil for anything that is not a DOI.
|
|
89
|
+
def sanitize_doi(value)
|
|
90
|
+
match = value.to_s.strip.match(%r{(10\.\d{4,9}/\S+)\z})
|
|
91
|
+
match && match[1]
|
|
92
|
+
end
|
|
93
|
+
|
|
45
94
|
def normalize_author(author)
|
|
46
95
|
return nil unless author.is_a?(Hash)
|
|
47
96
|
|
|
48
|
-
h = author.stringify_keys.slice(
|
|
97
|
+
h = author.stringify_keys.slice(*PERSON_KEYS)
|
|
49
98
|
return nil if h.values_at('givenName', 'familyName').all? { |v| v.to_s.strip.empty? }
|
|
50
99
|
|
|
100
|
+
ror = normalize_ror(h['ror_id']) || ror_for(h['affiliation_id'])
|
|
101
|
+
ror ? h['ror_id'] = ror : h.delete('ror_id')
|
|
51
102
|
h
|
|
52
103
|
end
|
|
53
104
|
|
|
105
|
+
# ROR IDs travel either bare ("05f950310") or as a resolver URL; DataCite
|
|
106
|
+
# wants the URI, so the bare form is what gets stored and the URI is built
|
|
107
|
+
# when the XML is rendered.
|
|
108
|
+
def normalize_ror(value)
|
|
109
|
+
value.to_s.strip.sub(%r{\Ahttps?://ror\.org/}i, '').presence
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# The authoritative ROR for a picked affiliation. Whoever the editor picked
|
|
113
|
+
# came from an Affiliation record, so its id is the better source than
|
|
114
|
+
# anything retyped — and the current-user payload carries no ROR at all.
|
|
115
|
+
def ror_for(affiliation_id)
|
|
116
|
+
return nil if affiliation_id.blank? || !defined?(::Affiliation)
|
|
117
|
+
|
|
118
|
+
normalize_ror(::Affiliation.where(id: affiliation_id).pick(:ror_id))
|
|
119
|
+
rescue StandardError
|
|
120
|
+
nil
|
|
121
|
+
end
|
|
122
|
+
|
|
54
123
|
def failure(message)
|
|
55
124
|
@error = message
|
|
56
125
|
self
|
data/lib/labimotion/version.rb
CHANGED
data/lib/labimotion.rb
CHANGED
|
@@ -29,6 +29,7 @@ module Labimotion
|
|
|
29
29
|
autoload :VocabularyAPI, 'labimotion/apis/vocabulary_api'
|
|
30
30
|
autoload :UserAPI, 'labimotion/apis/user_api'
|
|
31
31
|
autoload :UserKlassSettingsAPI, 'labimotion/apis/user_klass_settings_api'
|
|
32
|
+
autoload :KlassShareAPI, 'labimotion/apis/klass_share_api'
|
|
32
33
|
autoload :MttAPI, 'labimotion/apis/mtt_api'
|
|
33
34
|
autoload :ElementVariationAPI, 'labimotion/apis/element_variation_api'
|
|
34
35
|
autoload :LabimotionDoiAPI, 'labimotion/apis/labimotion_doi_api'
|
|
@@ -58,6 +59,7 @@ module Labimotion
|
|
|
58
59
|
autoload :VocabularyEntity, 'labimotion/entities/vocabulary_entity'
|
|
59
60
|
autoload :UserEntity, 'labimotion/entities/user_entity'
|
|
60
61
|
autoload :UserKlassSettingEntity, 'labimotion/entities/user_klass_setting_entity'
|
|
62
|
+
autoload :KlassShareEntity, 'labimotion/entities/klass_share_entity'
|
|
61
63
|
autoload :ElementVariationEntity, 'labimotion/entities/element_variation_entity'
|
|
62
64
|
autoload :LabimotionTemplateDoiEntity, 'labimotion/entities/labimotion_template_doi_entity'
|
|
63
65
|
|
|
@@ -82,6 +84,9 @@ module Labimotion
|
|
|
82
84
|
autoload :UpdateTemplatePublicationMetadata, 'labimotion/usecases/update_template_publication_metadata'
|
|
83
85
|
|
|
84
86
|
######## Libs
|
|
87
|
+
autoload :AiTemplate, 'labimotion/libs/ai_template'
|
|
88
|
+
autoload :AiEgressGuard, 'labimotion/libs/ai_egress_guard'
|
|
89
|
+
autoload :FileExtractor, 'labimotion/libs/file_extractor'
|
|
85
90
|
autoload :Converter, 'labimotion/libs/converter'
|
|
86
91
|
autoload :DatasetBuilder, 'labimotion/libs/dataset_builder'
|
|
87
92
|
autoload :NmrMapper, 'labimotion/libs/nmr_mapper'
|
|
@@ -99,6 +104,10 @@ module Labimotion
|
|
|
99
104
|
autoload :ElementVariationHeader, 'labimotion/libs/element_variation_header'
|
|
100
105
|
autoload :ExportElementVariations, 'labimotion/libs/export_element_variations'
|
|
101
106
|
autoload :ImportElementVariations, 'labimotion/libs/import_element_variations'
|
|
107
|
+
autoload :OwnerResolver, 'labimotion/libs/owner_resolver'
|
|
108
|
+
autoload :OwnershipAudit, 'labimotion/libs/ownership_audit'
|
|
109
|
+
autoload :ShareNotifier, 'labimotion/libs/share_notifier'
|
|
110
|
+
autoload :ShareResolver, 'labimotion/libs/share_resolver'
|
|
102
111
|
|
|
103
112
|
######## Utils
|
|
104
113
|
autoload :Prop, 'labimotion/utils/prop'
|
|
@@ -137,6 +146,7 @@ module Labimotion
|
|
|
137
146
|
autoload :StdLayer, 'labimotion/models/std_layer'
|
|
138
147
|
autoload :StdLayersRevision, 'labimotion/models/std_layers_revision'
|
|
139
148
|
autoload :UserKlassSetting, 'labimotion/models/user_klass_setting'
|
|
149
|
+
autoload :KlassShare, 'labimotion/models/klass_share'
|
|
140
150
|
|
|
141
151
|
autoload :CellLine, 'labimotion/models/cellline'
|
|
142
152
|
autoload :DeviceDescription, 'labimotion/models/device_description'
|
|
@@ -157,4 +167,5 @@ module Labimotion
|
|
|
157
167
|
autoload :Datasetable, 'labimotion/models/concerns/datasetable'
|
|
158
168
|
autoload :AttachmentConverter, 'labimotion/models/concerns/attachment_converter'
|
|
159
169
|
autoload :LinkedProperties, 'labimotion/models/concerns/linked_properties'
|
|
170
|
+
autoload :TemplateDoi, 'labimotion/models/concerns/template_doi'
|
|
160
171
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: labimotion
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.4.0.
|
|
4
|
+
version: 2.4.0.rc4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chia-Lin Lin
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-08-05 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: caxlsx
|
|
@@ -25,6 +25,20 @@ dependencies:
|
|
|
25
25
|
- - "~>"
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
27
|
version: '4.0'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: pdf-reader
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '2.11'
|
|
35
|
+
type: :runtime
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '2.11'
|
|
28
42
|
- !ruby/object:Gem::Dependency
|
|
29
43
|
name: rails
|
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -77,6 +91,7 @@ files:
|
|
|
77
91
|
- lib/labimotion/apis/generic_dataset_api.rb
|
|
78
92
|
- lib/labimotion/apis/generic_element_api.rb
|
|
79
93
|
- lib/labimotion/apis/generic_klass_api.rb
|
|
94
|
+
- lib/labimotion/apis/klass_share_api.rb
|
|
80
95
|
- lib/labimotion/apis/labimotion_api.rb
|
|
81
96
|
- lib/labimotion/apis/labimotion_doi_api.rb
|
|
82
97
|
- lib/labimotion/apis/labimotion_hub_api.rb
|
|
@@ -105,6 +120,7 @@ files:
|
|
|
105
120
|
- lib/labimotion/entities/generic_klass_entity.rb
|
|
106
121
|
- lib/labimotion/entities/generic_public_entity.rb
|
|
107
122
|
- lib/labimotion/entities/klass_revision_entity.rb
|
|
123
|
+
- lib/labimotion/entities/klass_share_entity.rb
|
|
108
124
|
- lib/labimotion/entities/labimotion_template_doi_entity.rb
|
|
109
125
|
- lib/labimotion/entities/properties_entity.rb
|
|
110
126
|
- lib/labimotion/entities/segment_entity.rb
|
|
@@ -125,8 +141,11 @@ files:
|
|
|
125
141
|
- lib/labimotion/helpers/search_helpers.rb
|
|
126
142
|
- lib/labimotion/helpers/segment_helpers.rb
|
|
127
143
|
- lib/labimotion/helpers/vocabulary_helpers.rb
|
|
144
|
+
- lib/labimotion/libs/ai_egress_guard.rb
|
|
145
|
+
- lib/labimotion/libs/ai_template.rb
|
|
128
146
|
- lib/labimotion/libs/attachment_handler.rb
|
|
129
147
|
- lib/labimotion/libs/converter.rb
|
|
148
|
+
- lib/labimotion/libs/data/datacite/labimotion_template.html.erb
|
|
130
149
|
- lib/labimotion/libs/data/mapper/Chemwiki.json
|
|
131
150
|
- lib/labimotion/libs/data/mapper/Source.json
|
|
132
151
|
- lib/labimotion/libs/data/vocab/Standard.json
|
|
@@ -137,11 +156,16 @@ files:
|
|
|
137
156
|
- lib/labimotion/libs/export_dataset.rb
|
|
138
157
|
- lib/labimotion/libs/export_element.rb
|
|
139
158
|
- lib/labimotion/libs/export_element_variations.rb
|
|
159
|
+
- lib/labimotion/libs/file_extractor.rb
|
|
140
160
|
- lib/labimotion/libs/import_element_variations.rb
|
|
141
161
|
- lib/labimotion/libs/linked_element.rb
|
|
142
162
|
- lib/labimotion/libs/nmr_mapper.rb
|
|
163
|
+
- lib/labimotion/libs/owner_resolver.rb
|
|
164
|
+
- lib/labimotion/libs/ownership_audit.rb
|
|
143
165
|
- lib/labimotion/libs/properties_handler.rb
|
|
144
166
|
- lib/labimotion/libs/sample_association.rb
|
|
167
|
+
- lib/labimotion/libs/share_notifier.rb
|
|
168
|
+
- lib/labimotion/libs/share_resolver.rb
|
|
145
169
|
- lib/labimotion/libs/template_hub.rb
|
|
146
170
|
- lib/labimotion/libs/template_matcher.rb
|
|
147
171
|
- lib/labimotion/libs/vocabulary_handler.rb
|
|
@@ -158,6 +182,7 @@ files:
|
|
|
158
182
|
- lib/labimotion/models/concerns/linked_properties.rb
|
|
159
183
|
- lib/labimotion/models/concerns/metadata_validation.rb
|
|
160
184
|
- lib/labimotion/models/concerns/segmentable.rb
|
|
185
|
+
- lib/labimotion/models/concerns/template_doi.rb
|
|
161
186
|
- lib/labimotion/models/concerns/workflow.rb
|
|
162
187
|
- lib/labimotion/models/dataset.rb
|
|
163
188
|
- lib/labimotion/models/dataset_klass.rb
|
|
@@ -175,6 +200,7 @@ files:
|
|
|
175
200
|
- lib/labimotion/models/elements_sample.rb
|
|
176
201
|
- lib/labimotion/models/elements_wellplate.rb
|
|
177
202
|
- lib/labimotion/models/hub_log.rb
|
|
203
|
+
- lib/labimotion/models/klass_share.rb
|
|
178
204
|
- lib/labimotion/models/reaction.rb
|
|
179
205
|
- lib/labimotion/models/research_plan.rb
|
|
180
206
|
- lib/labimotion/models/sample.rb
|