labimotion 2.2.0.rc9 → 2.2.0.rc11
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 +117 -0
- data/LICENSE +661 -0
- data/README.md +68 -0
- data/lib/labimotion/apis/generic_element_api.rb +0 -1
- data/lib/labimotion/apis/labimotion_api.rb +3 -0
- data/lib/labimotion/apis/labimotion_doi_api.rb +170 -0
- data/lib/labimotion/apis/labimotion_template_browse_api.rb +120 -0
- data/lib/labimotion/apis/user_api.rb +56 -0
- data/lib/labimotion/entities/labimotion_template_doi_entity.rb +30 -0
- data/lib/labimotion/entities/user_entity.rb +9 -0
- data/lib/labimotion/helpers/element_helpers.rb +15 -21
- data/lib/labimotion/libs/converter.rb +2 -1
- data/lib/labimotion/libs/nmr_mapper.rb +2 -2
- data/lib/labimotion/models/concerns/attachment_converter.rb +1 -0
- data/lib/labimotion/models/concerns/element_fetchable.rb +2 -2
- data/lib/labimotion/models/element.rb +2 -2
- data/lib/labimotion/utils/search.rb +2 -2
- data/lib/labimotion/version.rb +1 -1
- data/lib/labimotion.rb +5 -0
- metadata +10 -2
data/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# LabIMotion [](https://doi.org/10.5281/zenodo.8305411)
|
|
2
|
+
|
|
3
|
+
LabIMotion is a software that offers the flexibility to design new modules tailored to the specific needs of the scientists.
|
|
4
|
+
Generic elements, segments, and datasets are organized using Components, which are introduced as layers and fields. Each generic element, segment, or dataset can include multiple layers, with the potential for multiple fields within each layer. This hierarchical structure enables a flexible and comprehensive organization of data and information.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Version 1.0.18 of LabIMotion, featuring:
|
|
12
|
+
|
|
13
|
+
* Generic Designer
|
|
14
|
+
* Workflow of Generic Element
|
|
15
|
+
* Repetitation of layers
|
|
16
|
+
* Drag Element to Element
|
|
17
|
+
* Dataset Metadata
|
|
18
|
+
* LabIMotion Template Hub Synchronization [**[LabIMotion Template Hub]**]
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
This repository contains a backend service for the LabIMotion. It is written in **[Ruby]**.
|
|
23
|
+
|
|
24
|
+
### Community
|
|
25
|
+
|
|
26
|
+
* [GitHub discussions](https://github.com/labimotion/labimotion/discussions)
|
|
27
|
+
|
|
28
|
+
### Code
|
|
29
|
+
|
|
30
|
+
* [GitHub code](https://github.com/labimotion/labimotion) and [bug tracker](https://github.com/labimotion/labimotion/issues)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
Documentation for users **⸢ [Documentation] ⸥**
|
|
38
|
+
|
|
39
|
+
Documentation for developers **⸢ [Technical Documentation] ⸥**
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
Code released under the [AGPL-3.0 License]([https://www.gnu.org/licenses/agpl-3.0.txt](https://www.gnu.org/licenses/agpl-3.0.txt)).
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Acknowledgments
|
|
50
|
+
|
|
51
|
+
This project has been funded by the **[DFG]**.
|
|
52
|
+
|
|
53
|
+
[![DFG Logo]][DFG]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
Funded by the [Deutsche Forschungsgemeinschaft (DFG, German Research Foundation)](https://www.dfg.de/) under the [National Research Data Infrastructure – NFDI4Chem](https://nfdi4chem.de/) – Projektnummer **441958208** since 2020.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
<!----------------------------------------------------------------------------->
|
|
61
|
+
[Documentation]: https://www.chemotion.net/docs/labimotion/
|
|
62
|
+
[Technical Documentation]: https://www.rubydoc.info/gems/labimotion
|
|
63
|
+
[DFG]: https://www.dfg.de/en/
|
|
64
|
+
[DFG Logo]: https://www.dfg.de/zentralablage/bilder/service/logos_corporate_design/logo_negativ_267.png
|
|
65
|
+
[Nicole Jung]: mailto:nicole.jung@kit.edu
|
|
66
|
+
[Karlsruhe Institute of Technology]: https://www.kit.edu/english/
|
|
67
|
+
[Ruby]: https://www.ruby-lang.org/
|
|
68
|
+
[LabIMotion Template Hub]: https://www.chemotion-repository.net/home/genericHub
|
|
@@ -432,7 +432,6 @@ module Labimotion
|
|
|
432
432
|
desc 'Return serialized elements of current user'
|
|
433
433
|
params do
|
|
434
434
|
optional :collection_id, type: Integer, desc: 'Collection id'
|
|
435
|
-
optional :sync_collection_id, type: Integer, desc: 'SyncCollectionsUser id'
|
|
436
435
|
optional :el_type, type: String, desc: 'element klass name'
|
|
437
436
|
optional :from_date, type: Integer, desc: 'created_date from in ms'
|
|
438
437
|
optional :to_date, type: Integer, desc: 'created_date to in ms'
|
|
@@ -12,8 +12,11 @@ module Labimotion
|
|
|
12
12
|
mount Labimotion::LabimotionHubAPI
|
|
13
13
|
mount Labimotion::StandardLayerAPI
|
|
14
14
|
mount Labimotion::VocabularyAPI
|
|
15
|
+
mount Labimotion::UserAPI
|
|
15
16
|
mount Labimotion::MttAPI
|
|
16
17
|
mount Labimotion::DoseRespRequestAPI
|
|
17
18
|
mount Labimotion::ElementVariationAPI
|
|
19
|
+
mount Labimotion::LabimotionDoiAPI
|
|
20
|
+
mount Labimotion::LabimotionTemplateBrowseAPI
|
|
18
21
|
end
|
|
19
22
|
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labimotion
|
|
4
|
+
# DOI lifecycle (reserve + release) and publication metadata for
|
|
5
|
+
# LabIMotion templates: ElementKlass / SegmentKlass / DatasetKlass.
|
|
6
|
+
class LabimotionDoiAPI < Grape::API
|
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
|
8
|
+
# Grape route and helper DSL blocks are necessarily large.
|
|
9
|
+
KLASS_TYPES = ::Usecases::Labimotion::TemplateDoiHelpers::KLASS_BY_TYPE.keys.freeze
|
|
10
|
+
|
|
11
|
+
helpers do
|
|
12
|
+
def resolve_klass!(type, id)
|
|
13
|
+
model = ::Usecases::Labimotion::TemplateDoiHelpers::KLASS_BY_TYPE[type]
|
|
14
|
+
error!('unknown template type', 404) unless model
|
|
15
|
+
|
|
16
|
+
record = model.find_by(id: id)
|
|
17
|
+
error!('template not found', 404) unless record
|
|
18
|
+
|
|
19
|
+
record
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render_template_doi(record)
|
|
23
|
+
type = ::Usecases::Labimotion::TemplateDoiHelpers.klass_type_for(record)
|
|
24
|
+
dois = ::Doi.labimotion_dois(record)
|
|
25
|
+
# `doi` is the current (most recently reserved) DOI; `released_doi` is the
|
|
26
|
+
# latest released (minted) one. They differ once a new version has been
|
|
27
|
+
# reserved on top of a released one, so the modal can show both at once.
|
|
28
|
+
doi = dois.last
|
|
29
|
+
released_doi = dois.reverse.find(&:minted)
|
|
30
|
+
Labimotion::LabimotionTemplateDoiEntity.represent(
|
|
31
|
+
type: type,
|
|
32
|
+
klass_id: record.id,
|
|
33
|
+
doi: doi,
|
|
34
|
+
released_doi_version: released_doi && ::Doi.labimotion_doi_version(released_doi),
|
|
35
|
+
publication: (record.properties_template || {})['publication'],
|
|
36
|
+
released_at: record.released_at,
|
|
37
|
+
released_by: record.released_by,
|
|
38
|
+
new_version_available: ::Usecases::Labimotion::TemplateDoiHelpers.new_version_available?(record),
|
|
39
|
+
next_doi_version: ::Doi.labimotion_version_segment(record),
|
|
40
|
+
template_url: ::Usecases::Labimotion::TemplateDoiHelpers.template_url(record)
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Public, read-only view of one released DOI version.
|
|
45
|
+
def released_doi_version(doi)
|
|
46
|
+
full = doi.full_doi
|
|
47
|
+
{
|
|
48
|
+
doi: full,
|
|
49
|
+
doi_url: "https://dx.doi.org/#{full}",
|
|
50
|
+
version: ::Doi.labimotion_doi_version(doi),
|
|
51
|
+
minted_at: doi.minted_at
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Public payload for a template that has at least one released DOI: the
|
|
56
|
+
# latest released DOI promoted to the top level, plus its publication
|
|
57
|
+
# metadata, hub deep-link and every released version. `record_id` matches
|
|
58
|
+
# the hub grid's row id (identifier || uuid) so the frontend can merge it.
|
|
59
|
+
def released_template_doi(record, dois)
|
|
60
|
+
versions = dois.sort_by(&:id).map { |doi| released_doi_version(doi) }
|
|
61
|
+
publication = (record.properties_template || {})['publication']
|
|
62
|
+
publication = publication.is_a?(Hash) ? publication.slice('title', 'description', 'authors', 'license') : {}
|
|
63
|
+
versions.last.merge(
|
|
64
|
+
record_id: record.try(:identifier).presence || record.try(:uuid).presence || record.id,
|
|
65
|
+
identifier: record.try(:identifier),
|
|
66
|
+
uuid: record.try(:uuid),
|
|
67
|
+
label: record.try(:label),
|
|
68
|
+
template_version: record.try(:version),
|
|
69
|
+
next_doi_version: ::Doi.labimotion_version_segment(record),
|
|
70
|
+
template_url: ::Usecases::Labimotion::TemplateDoiHelpers.template_url(record),
|
|
71
|
+
publication: publication,
|
|
72
|
+
versions: versions
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
namespace :labimotion_doi do
|
|
78
|
+
# Public, anonymous read-only DOI info for the LabIMotion Template Hub.
|
|
79
|
+
# Whitelisted in RepoAPI::PUBLIC_URLS so it bypasses authenticate!; only
|
|
80
|
+
# released (minted) DOIs and their public publication metadata are exposed.
|
|
81
|
+
namespace :public do
|
|
82
|
+
route_param :type, type: String, values: KLASS_TYPES do
|
|
83
|
+
desc 'Released DOIs + publication metadata for every released template of a type'
|
|
84
|
+
get :released do
|
|
85
|
+
model = ::Usecases::Labimotion::TemplateDoiHelpers::KLASS_BY_TYPE[params[:type]]
|
|
86
|
+
grouped = ::Doi.where(doiable_type: model.name).where.not(minted_at: nil)
|
|
87
|
+
.order(:id).group_by(&:doiable_id)
|
|
88
|
+
records = model.where(id: grouped.keys).index_by(&:id)
|
|
89
|
+
released = grouped.filter_map do |klass_id, dois|
|
|
90
|
+
record = records[klass_id]
|
|
91
|
+
record && released_template_doi(record, dois)
|
|
92
|
+
end
|
|
93
|
+
{ released_dois: released }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
route_param :type, type: String, values: KLASS_TYPES do
|
|
99
|
+
desc "DOI state (reserved / released) per template of a type, for the designer grid's DOI icon"
|
|
100
|
+
get :states do
|
|
101
|
+
model = ::Usecases::Labimotion::TemplateDoiHelpers::KLASS_BY_TYPE[params[:type]]
|
|
102
|
+
grouped = ::Doi.where(doiable_type: model.name).order(:id).group_by(&:doiable_id)
|
|
103
|
+
states = grouped.transform_values do |list|
|
|
104
|
+
released = list.last.minted == true
|
|
105
|
+
{ reserved: !released, released: released }
|
|
106
|
+
end
|
|
107
|
+
{ states: states }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
route_param :id, type: Integer do
|
|
111
|
+
desc 'Returns the current DOI/publication state for a template'
|
|
112
|
+
get do
|
|
113
|
+
record = resolve_klass!(params[:type], params[:id])
|
|
114
|
+
render_template_doi(record)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
desc "Returns the DataCite metadata XML for every one of the template's DOI versions"
|
|
118
|
+
get :metadata_xml do
|
|
119
|
+
record = resolve_klass!(params[:type], params[:id])
|
|
120
|
+
dois = ::Doi.labimotion_dois(record)
|
|
121
|
+
error!('no DOI reserved', 404) if dois.empty?
|
|
122
|
+
|
|
123
|
+
versions = dois.map do |doi|
|
|
124
|
+
{
|
|
125
|
+
version: ::Doi.labimotion_doi_version(doi),
|
|
126
|
+
full_doi: doi.full_doi,
|
|
127
|
+
minted: doi.minted == true,
|
|
128
|
+
xml: ::Usecases::Labimotion::BuildTemplateDoiXml.new(record, doi, current_user).call
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
{ versions: versions }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
desc 'Updates the publication metadata of a template'
|
|
135
|
+
params do
|
|
136
|
+
requires :publication, type: Hash
|
|
137
|
+
end
|
|
138
|
+
put :publication_metadata do
|
|
139
|
+
record = resolve_klass!(params[:type], params[:id])
|
|
140
|
+
uc = ::Usecases::Labimotion::UpdateTemplatePublicationMetadata.new(
|
|
141
|
+
record, current_user, params[:publication]
|
|
142
|
+
).call
|
|
143
|
+
error!(uc.error, 422) unless uc.success?
|
|
144
|
+
|
|
145
|
+
render_template_doi(record.reload)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
desc 'Reserves a DataCite DOI for a template'
|
|
149
|
+
post :reserve_doi do
|
|
150
|
+
record = resolve_klass!(params[:type], params[:id])
|
|
151
|
+
uc = ::Usecases::Labimotion::ReserveTemplateDoi.new(record, current_user).call
|
|
152
|
+
error!(uc.error, 422) unless uc.success?
|
|
153
|
+
|
|
154
|
+
render_template_doi(record.reload)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
desc 'Releases (mints) the reserved DOI for a template'
|
|
158
|
+
post :release_doi do
|
|
159
|
+
record = resolve_klass!(params[:type], params[:id])
|
|
160
|
+
uc = ::Usecases::Labimotion::ReleaseTemplateDoi.new(record, current_user).call
|
|
161
|
+
error!(uc.error, 422) unless uc.success?
|
|
162
|
+
|
|
163
|
+
render_template_doi(record.reload)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
# rubocop:enable Metrics/BlockLength
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labimotion
|
|
4
|
+
# Public, read-only browsing of released LabIMotion templates for the Template
|
|
5
|
+
# Hub "Find a Template" tab: pick a type, a template, then a release version.
|
|
6
|
+
# Returns every released version (revision) of one template, each with its own
|
|
7
|
+
# properties_release so the frontend can render the example inline.
|
|
8
|
+
# Whitelisted in RepoAPI::PUBLIC_URLS so anonymous hub visitors can use it.
|
|
9
|
+
class LabimotionTemplateBrowseAPI < Grape::API
|
|
10
|
+
KLASS_BY_TYPE = ::Usecases::Labimotion::TemplateDoiHelpers::KLASS_BY_TYPE
|
|
11
|
+
KLASS_TYPES = KLASS_BY_TYPE.keys.freeze
|
|
12
|
+
# type => [revision model, foreign key to the klass]
|
|
13
|
+
REVISION_BY_TYPE = {
|
|
14
|
+
'element' => [::Labimotion::ElementKlassesRevision, :element_klass_id],
|
|
15
|
+
'segment' => [::Labimotion::SegmentKlassesRevision, :segment_klass_id],
|
|
16
|
+
'dataset' => [::Labimotion::DatasetKlassesRevision, :dataset_klass_id]
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
# rubocop:disable Metrics/BlockLength
|
|
20
|
+
# The helpers block is large due to the many template/version/DOI shaping helpers.
|
|
21
|
+
helpers do
|
|
22
|
+
def find_template(model, identifier)
|
|
23
|
+
model.find_by(identifier: identifier) || model.find_by(uuid: identifier)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Numeric sort key for a "major.minor" version string.
|
|
27
|
+
def version_sort_key(version)
|
|
28
|
+
version.to_s.split('.').map(&:to_i)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def template_summary(record, type)
|
|
32
|
+
element_klass = record.try(:element_klass)
|
|
33
|
+
{
|
|
34
|
+
type: type,
|
|
35
|
+
identifier: record.try(:identifier),
|
|
36
|
+
uuid: record.try(:uuid),
|
|
37
|
+
name: record.try(:name),
|
|
38
|
+
label: record.try(:label),
|
|
39
|
+
desc: record.try(:desc),
|
|
40
|
+
klass_prefix: record.try(:klass_prefix),
|
|
41
|
+
icon_name: record.try(:icon_name),
|
|
42
|
+
current_version: record.try(:version),
|
|
43
|
+
element_klass: element_klass && { label: element_klass.label, icon_name: element_klass.icon_name }
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def version_entry(version, released_at, uuid, properties_release)
|
|
48
|
+
{ version: version, released_at: released_at, uuid: uuid, properties_release: properties_release }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The current klass's released version is appended only when no revision
|
|
52
|
+
# already captures it, so the latest is always selectable.
|
|
53
|
+
def append_current?(record, versions)
|
|
54
|
+
record.try(:released_at).present? && versions.none? { |v| v[:version] == record.try(:version) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Drops blank-version entries and orders newest-first.
|
|
58
|
+
def sort_versions(versions)
|
|
59
|
+
versions.reject { |v| v[:version].to_s.strip.empty? }
|
|
60
|
+
.sort_by { |v| version_sort_key(v[:version]) }.reverse
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# All released versions of a template, newest first. Built from the
|
|
64
|
+
# revision snapshots, plus the current klass (see append_current?).
|
|
65
|
+
def released_versions(record, type)
|
|
66
|
+
rev_model, foreign_key = REVISION_BY_TYPE[type]
|
|
67
|
+
scope = rev_model.where(foreign_key => record.id).where.not(released_at: nil)
|
|
68
|
+
versions = scope.map { |rev| version_entry(rev.version, rev.released_at, rev.uuid, rev.properties_release) }
|
|
69
|
+
if append_current?(record, versions)
|
|
70
|
+
versions << version_entry(record.try(:version), record.released_at,
|
|
71
|
+
record.try(:uuid), record.try(:properties_release))
|
|
72
|
+
end
|
|
73
|
+
sort_versions(versions)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The template's DOI publication metadata (shown alongside the DOI).
|
|
77
|
+
def template_publication(record)
|
|
78
|
+
pub = (record.properties_template || {})['publication']
|
|
79
|
+
pub.is_a?(Hash) ? pub.slice('title', 'description', 'authors', 'license') : {}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Released (minted) DOIs of the template, each tagged with its DOI version
|
|
83
|
+
# and its DataCite (kernel-4) metadata XML (the downloadable DOI metadata).
|
|
84
|
+
# The frontend maps a chosen template version to the applicable DOI version
|
|
85
|
+
# and shows/downloads the matching entry.
|
|
86
|
+
def released_dois(record)
|
|
87
|
+
::Doi.labimotion_dois(record).select(&:minted).map do |doi|
|
|
88
|
+
full = doi.full_doi
|
|
89
|
+
{ version: ::Doi.labimotion_doi_version(doi), doi: full,
|
|
90
|
+
doi_url: "https://dx.doi.org/#{full}", minted_at: doi.minted_at,
|
|
91
|
+
xml: ::Usecases::Labimotion::BuildTemplateDoiXml.new(record, doi).call }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
# rubocop:enable Metrics/BlockLength
|
|
96
|
+
|
|
97
|
+
namespace :labimotion_template_browse do
|
|
98
|
+
route_param :type, type: String, values: KLASS_TYPES do
|
|
99
|
+
desc 'Released versions (revisions) of one template, each with properties_release'
|
|
100
|
+
params do
|
|
101
|
+
requires :identifier, type: String, desc: 'template identifier or uuid'
|
|
102
|
+
end
|
|
103
|
+
get :versions do
|
|
104
|
+
model = KLASS_BY_TYPE[params[:type]]
|
|
105
|
+
error!('unknown template type', 404) unless model
|
|
106
|
+
|
|
107
|
+
record = find_template(model, params[:identifier])
|
|
108
|
+
error!('template not found', 404) unless record
|
|
109
|
+
|
|
110
|
+
{
|
|
111
|
+
template: template_summary(record, params[:type]),
|
|
112
|
+
versions: released_versions(record, params[:type]),
|
|
113
|
+
dois: released_dois(record),
|
|
114
|
+
publication: template_publication(record)
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'labimotion/version'
|
|
4
|
+
|
|
5
|
+
module Labimotion
|
|
6
|
+
# User API
|
|
7
|
+
class UserAPI < Grape::API
|
|
8
|
+
helpers Labimotion::ParamHelpers
|
|
9
|
+
helpers Labimotion::GenericHelpers
|
|
10
|
+
|
|
11
|
+
resource :limo do
|
|
12
|
+
resource :users do
|
|
13
|
+
namespace :list do
|
|
14
|
+
desc 'List users by keyword'
|
|
15
|
+
params do
|
|
16
|
+
requires :name, type: String, desc: 'Keyword'
|
|
17
|
+
optional :limit, type: Integer, default: 5, desc: 'Limit (max 10)'
|
|
18
|
+
optional :type, type: String, default: 'Person', desc: 'User type'
|
|
19
|
+
end
|
|
20
|
+
get do
|
|
21
|
+
error!('401 Unauthorized', 401) unless current_user
|
|
22
|
+
return { mc: 'ss00', msg: 'Entered name too short', data: [] } if params[:name].to_s.length < 3
|
|
23
|
+
|
|
24
|
+
query = 'first_name ILIKE :q OR last_name ILIKE :q OR name ILIKE :q OR name_abbreviation ILIKE :q'
|
|
25
|
+
users = User.where(query, q: "%#{params[:name]}%")
|
|
26
|
+
.where(type: params[:type])
|
|
27
|
+
.limit([params[:limit].to_i, 10].min)
|
|
28
|
+
data = Labimotion::UserEntity.represent(users)
|
|
29
|
+
{ mc: 'ss00', data: data }
|
|
30
|
+
rescue StandardError => e
|
|
31
|
+
Labimotion.log_exception(e, current_user)
|
|
32
|
+
{ mc: 'se00', msg: e.message, data: [] }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc 'Get user info by id'
|
|
37
|
+
params do
|
|
38
|
+
requires :id, type: Integer, desc: 'User id'
|
|
39
|
+
end
|
|
40
|
+
route_param :id do
|
|
41
|
+
get do
|
|
42
|
+
error!('401 Unauthorized', 401) unless current_user
|
|
43
|
+
user = User.find(params[:id])
|
|
44
|
+
data = Labimotion::UserEntity.represent(user)
|
|
45
|
+
{ mc: 'ss00', data: data }
|
|
46
|
+
rescue ActiveRecord::RecordNotFound
|
|
47
|
+
error!('404 Not Found', 404)
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
Labimotion.log_exception(e, current_user)
|
|
50
|
+
{ mc: 'se00', msg: e.message, data: {} }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labimotion
|
|
4
|
+
# Serializes the DOI + publication state for a LabIMotion template
|
|
5
|
+
# (ElementKlass / SegmentKlass / DatasetKlass). Built ad-hoc by
|
|
6
|
+
# Labimotion::LabimotionDoiAPI; not a direct AR record entity.
|
|
7
|
+
class LabimotionTemplateDoiEntity < Grape::Entity
|
|
8
|
+
expose :type
|
|
9
|
+
expose :klass_id
|
|
10
|
+
expose :doi do |obj|
|
|
11
|
+
next nil unless obj[:doi]
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
id: obj[:doi].id,
|
|
15
|
+
suffix: obj[:doi].suffix,
|
|
16
|
+
full_doi: obj[:doi].full_doi,
|
|
17
|
+
version: ::Doi.labimotion_doi_version(obj[:doi]),
|
|
18
|
+
minted: obj[:doi].minted == true,
|
|
19
|
+
minted_at: obj[:doi].minted_at
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
expose :released_doi_version
|
|
23
|
+
expose :publication
|
|
24
|
+
expose :released_at
|
|
25
|
+
expose :released_by
|
|
26
|
+
expose :new_version_available
|
|
27
|
+
expose :next_doi_version
|
|
28
|
+
expose :template_url
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -215,7 +215,7 @@ module Labimotion
|
|
|
215
215
|
layer, field = params[:sort_column].split('.')
|
|
216
216
|
|
|
217
217
|
element_klass = Labimotion::ElementKlass.find_by(name: params[:el_type])
|
|
218
|
-
allowed_fields = element_klass
|
|
218
|
+
allowed_fields = element_klass&.properties_release&.dig(Labimotion::Prop::LAYERS, layer, Labimotion::Prop::FIELDS)&.pluck('field') || []
|
|
219
219
|
|
|
220
220
|
if field.in?(allowed_fields)
|
|
221
221
|
query = ActiveRecord::Base.sanitize_sql(
|
|
@@ -247,27 +247,21 @@ module Labimotion
|
|
|
247
247
|
end
|
|
248
248
|
|
|
249
249
|
def list_serialized_elements(params, current_user)
|
|
250
|
-
|
|
251
|
-
if params[:collection_id]
|
|
252
|
-
Collection
|
|
253
|
-
.belongs_to_or_shared_by(current_user.id, current_user.group_ids)
|
|
254
|
-
.find_by(id: params[:collection_id])&.id
|
|
255
|
-
elsif params[:sync_collection_id]
|
|
256
|
-
current_user
|
|
257
|
-
.all_sync_in_collections_users
|
|
258
|
-
.find_by(id: params[:sync_collection_id])&.collection&.id
|
|
259
|
-
end
|
|
250
|
+
scope = Labimotion::Element.none
|
|
260
251
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
252
|
+
if params[:collection_id]
|
|
253
|
+
begin
|
|
254
|
+
collection = Collection.accessible_for(current_user).find(params[:collection_id])
|
|
255
|
+
scope = collection.elements
|
|
256
|
+
.joins(:element_klass)
|
|
257
|
+
.where(element_klasses: { name: params[:el_type] })
|
|
258
|
+
.includes(:tag)
|
|
259
|
+
rescue ActiveRecord::RecordNotFound
|
|
260
|
+
Labimotion::Element.none
|
|
261
|
+
end
|
|
269
262
|
else
|
|
270
|
-
|
|
263
|
+
# All collection of current_user
|
|
264
|
+
scope = Labimotion::Element.for_user(current_user.id)
|
|
271
265
|
end
|
|
272
266
|
|
|
273
267
|
## TO DO: refactor labimotion
|
|
@@ -278,7 +272,7 @@ module Labimotion
|
|
|
278
272
|
layer, field = params[:sort_column].split('.')
|
|
279
273
|
|
|
280
274
|
element_klass = Labimotion::ElementKlass.find_by(name: params[:el_type])
|
|
281
|
-
allowed_fields = element_klass
|
|
275
|
+
allowed_fields = element_klass&.properties_release&.dig(Labimotion::Prop::LAYERS, layer, Labimotion::Prop::FIELDS)&.pluck('field') || []
|
|
282
276
|
|
|
283
277
|
if field.in?(allowed_fields)
|
|
284
278
|
query = ActiveRecord::Base.sanitize_sql(
|
|
@@ -125,7 +125,8 @@ module Labimotion
|
|
|
125
125
|
tmp_file.rewind
|
|
126
126
|
|
|
127
127
|
filename = oat.filename
|
|
128
|
-
name = "#{File.basename(filename, '.*')}.zip"
|
|
128
|
+
# name = "#{File.basename(filename, '.*')}.zip"
|
|
129
|
+
name = "#{File.basename(filename, '.*')}#{File.extname(filename) == '.zip' ? '.bagit.zip' : '.zip'}"
|
|
129
130
|
att = Attachment.new(
|
|
130
131
|
filename: name,
|
|
131
132
|
file_path: tmp_file.path,
|
|
@@ -34,7 +34,7 @@ module Labimotion
|
|
|
34
34
|
return Labimotion::ConState::NONE if att.nil?
|
|
35
35
|
|
|
36
36
|
result = process(att)
|
|
37
|
-
return Labimotion::ConState::
|
|
37
|
+
return Labimotion::ConState::WAIT if result.nil?
|
|
38
38
|
|
|
39
39
|
handle_process_result(result, att, id, current_user)
|
|
40
40
|
end
|
|
@@ -136,7 +136,7 @@ module Labimotion
|
|
|
136
136
|
if result[:is_bagit]
|
|
137
137
|
handle_bagit_result(att, id, current_user)
|
|
138
138
|
elsif invalid_metadata?(result)
|
|
139
|
-
Labimotion::ConState::
|
|
139
|
+
Labimotion::ConState::WAIT
|
|
140
140
|
else
|
|
141
141
|
handle_nmr_result(result, att, current_user)
|
|
142
142
|
end
|
|
@@ -31,6 +31,7 @@ module Labimotion
|
|
|
31
31
|
case con_state
|
|
32
32
|
when Labimotion::ConState::NMR
|
|
33
33
|
self.con_state = Labimotion::NmrMapper.process_ds(id, current_user)
|
|
34
|
+
return exec_converter if con_state == Labimotion::ConState::WAIT
|
|
34
35
|
update_column(:con_state, con_state)
|
|
35
36
|
when Labimotion::ConState::WAIT
|
|
36
37
|
self.con_state = Labimotion::Converter.jcamp_converter(id, current_user)
|
|
@@ -26,9 +26,9 @@ module Labimotion
|
|
|
26
26
|
joins(collections: :user).where(collections: { user_id: user_id })
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
# Shared
|
|
29
|
+
# Shared records
|
|
30
30
|
shared = apply_filters.call(
|
|
31
|
-
|
|
31
|
+
left_joins(:collection_shares).where(collection_shares: { shared_with_id: user_id })
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
# Combine (remove duplicates), order, and limit
|
|
@@ -141,9 +141,9 @@ module Labimotion
|
|
|
141
141
|
joins(collections: :user).where(collections: { user_id: user_id })
|
|
142
142
|
)
|
|
143
143
|
|
|
144
|
-
# Shared
|
|
144
|
+
# Shared elements
|
|
145
145
|
shared = apply_filters.call(
|
|
146
|
-
|
|
146
|
+
left_joins(:collection_shares).where(collection_shares: { shared_with_id: user_id })
|
|
147
147
|
)
|
|
148
148
|
|
|
149
149
|
# Combine (remove duplicates), order, and limit
|
|
@@ -12,7 +12,7 @@ module Labimotion
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.elements_search(params, current_user, c_id, dl)
|
|
15
|
-
collection = Collection.
|
|
15
|
+
collection = Collection.accessible_for(current_user).find(c_id)
|
|
16
16
|
element_scope = Labimotion::Element.joins(:collections_elements).where('collections_elements.collection_id = ?', collection.id).joins(:element_klass).where('element_klasses.id = elements.element_klass_id AND element_klasses.name = ?', params[:selection][:genericElName])
|
|
17
17
|
element_scope = element_scope.where('elements.name like (?)', "%#{params[:selection][:searchName]}%") if params[:selection][:searchName].present?
|
|
18
18
|
element_scope = element_scope.where('elements.short_label like (?)', "%#{params[:selection][:searchShowLabel]}%") if params[:selection][:searchShowLabel].present?
|
|
@@ -97,7 +97,7 @@ module Labimotion
|
|
|
97
97
|
def self.samples_search(c_id = @c_id)
|
|
98
98
|
sqls = []
|
|
99
99
|
sps = params[:selection][:searchProperties]
|
|
100
|
-
collection = Collection.
|
|
100
|
+
collection = Collection.accessible_for(current_user).find(c_id)
|
|
101
101
|
element_scope = Sample.joins(:collections_samples).where('collections_samples.collection_id = ?', collection.id)
|
|
102
102
|
return element_scope if sps.empty?
|
|
103
103
|
|
data/lib/labimotion/version.rb
CHANGED
data/lib/labimotion.rb
CHANGED
|
@@ -27,9 +27,12 @@ module Labimotion
|
|
|
27
27
|
autoload :ExporterAPI, 'labimotion/apis/exporter_api'
|
|
28
28
|
autoload :StandardLayerAPI, 'labimotion/apis/standard_layer_api'
|
|
29
29
|
autoload :VocabularyAPI, 'labimotion/apis/vocabulary_api'
|
|
30
|
+
autoload :UserAPI, 'labimotion/apis/user_api'
|
|
30
31
|
autoload :MttAPI, 'labimotion/apis/mtt_api'
|
|
31
32
|
autoload :DoseRespRequestAPI, 'labimotion/apis/dose_resp_request_api'
|
|
32
33
|
autoload :ElementVariationAPI, 'labimotion/apis/element_variation_api'
|
|
34
|
+
autoload :LabimotionDoiAPI, 'labimotion/apis/labimotion_doi_api'
|
|
35
|
+
autoload :LabimotionTemplateBrowseAPI, 'labimotion/apis/labimotion_template_browse_api'
|
|
33
36
|
|
|
34
37
|
######## Entities
|
|
35
38
|
autoload :PropertiesEntity, 'labimotion/entities/properties_entity'
|
|
@@ -52,7 +55,9 @@ module Labimotion
|
|
|
52
55
|
autoload :SegmentRevisionEntity, 'labimotion/entities/segment_revision_entity'
|
|
53
56
|
## autoload :DatasetRevisionEntity, 'labimotion/entities/dataset_revision_entity'
|
|
54
57
|
autoload :VocabularyEntity, 'labimotion/entities/vocabulary_entity'
|
|
58
|
+
autoload :UserEntity, 'labimotion/entities/user_entity'
|
|
55
59
|
autoload :ElementVariationEntity, 'labimotion/entities/element_variation_entity'
|
|
60
|
+
autoload :LabimotionTemplateDoiEntity, 'labimotion/entities/labimotion_template_doi_entity'
|
|
56
61
|
|
|
57
62
|
######## Helpers
|
|
58
63
|
autoload :GenericHelpers, 'labimotion/helpers/generic_helpers'
|