labimotion 2.0.0.rc2 → 2.0.0.rc6
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/lib/labimotion/apis/standard_api.rb +1 -2
- data/lib/labimotion/apis/standard_layer_api.rb +4 -17
- data/lib/labimotion/apis/vocabulary_api.rb +6 -20
- data/lib/labimotion/constants.rb +21 -0
- data/lib/labimotion/entities/dataset_entity.rb +0 -1
- data/lib/labimotion/entities/element_entity.rb +1 -0
- data/lib/labimotion/entities/properties_entity.rb +102 -41
- data/lib/labimotion/entities/segment_entity.rb +0 -2
- data/lib/labimotion/entities/vocabulary_entity.rb +2 -13
- data/lib/labimotion/helpers/element_helpers.rb +2 -2
- data/lib/labimotion/helpers/generic_helpers.rb +6 -1
- data/lib/labimotion/helpers/param_helpers.rb +0 -16
- data/lib/labimotion/libs/converter.rb +0 -1
- data/lib/labimotion/libs/data/mapper/Chemwiki.json +236 -0
- data/lib/labimotion/libs/data/mapper/Source.json +78 -0
- data/lib/labimotion/libs/data/vocab/System.json +1 -1
- data/lib/labimotion/libs/dataset_builder.rb +70 -0
- data/lib/labimotion/libs/export_dataset.rb +165 -66
- data/lib/labimotion/libs/nmr_mapper.rb +204 -247
- data/lib/labimotion/models/element.rb +3 -0
- data/lib/labimotion/utils/mapper_utils.rb +169 -0
- data/lib/labimotion/utils/utils.rb +22 -0
- data/lib/labimotion/version.rb +1 -1
- data/lib/labimotion.rb +3 -3
- metadata +7 -2
@@ -1,301 +1,258 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'labimotion/libs/dataset_builder'
|
3
4
|
require 'labimotion/version'
|
5
|
+
require 'labimotion/utils/mapper_utils'
|
4
6
|
require 'labimotion/utils/utils'
|
5
7
|
|
6
8
|
module Labimotion
|
7
9
|
## NmrMapper
|
8
10
|
class NmrMapper
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
generate_ds(id, att.attachable_id, data, current_user)
|
26
|
-
Labimotion::ConState::COMPLETED
|
11
|
+
# Constants specific to NMR mapping
|
12
|
+
module Constants
|
13
|
+
# Duplicate fields that need special handling
|
14
|
+
DUP_FIELDS = %w[time version].freeze
|
15
|
+
|
16
|
+
# Field groups for different processing stages
|
17
|
+
FG_FINALIZE = %w[set.temperature general.date general.time software.Name software.Version].freeze
|
18
|
+
FG_SYSTEM = %w[general.creator sample_details.label sample_details.id].freeze
|
19
|
+
|
20
|
+
# Valid NMR nuclei types
|
21
|
+
OBSERVED = %w[1H 13C].freeze
|
22
|
+
|
23
|
+
# OLS terms for different NMR types
|
24
|
+
module OlsTerms
|
25
|
+
NMR_1H = 'CHMO:0000593'
|
26
|
+
NMR_13C = 'CHMO:0000595'
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
if att&.attachment_attacher&.file&.url
|
35
|
-
Zip::File.open(att.attachment_attacher.file.url) do |zip_file|
|
36
|
-
zip_file.each do |entry|
|
37
|
-
if entry.name.include?('/pdata/') && entry.name.include?('parm.txt')
|
38
|
-
metadata = entry.get_input_stream.read.force_encoding('UTF-8')
|
39
|
-
return { is_bagit: false, metadata: metadata }
|
40
|
-
elsif entry.name.include?('metadata/') && entry.name.include?('converter.json')
|
41
|
-
return { is_bagit: true, metadata: nil }
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
{ is_bagit: false, metadata: nil }
|
47
|
-
end
|
30
|
+
class << self
|
31
|
+
def process_ds(id, current_user = {})
|
32
|
+
att = find_attachment(id)
|
33
|
+
return Labimotion::ConState::NONE if att.nil?
|
48
34
|
|
49
|
-
|
50
|
-
|
35
|
+
result = process(att)
|
36
|
+
return Labimotion::ConState::NONE if result.nil?
|
51
37
|
|
52
|
-
|
53
|
-
metadata = {}
|
54
|
-
lines.map do |ln|
|
55
|
-
arr = ln.split(/\s+/)
|
56
|
-
metadata[arr[0]] = arr[1..-1].join(' ') if arr.length > 1
|
38
|
+
handle_process_result(result, att, id, current_user)
|
57
39
|
end
|
58
|
-
ols = 'CHMO:0000593' if metadata['NUC1'] == '1H'
|
59
|
-
ols = 'CHMO:0000595' if metadata['NUC1'] == '13C'
|
60
|
-
|
61
|
-
{ content: { metadata: metadata, ols: ols } }
|
62
|
-
# if content.present? && att.present?
|
63
|
-
# Labimotion::NmrMapper.ts('write', att.attachable_id,
|
64
|
-
# content: { metadata: metadata, ols: ols })
|
65
|
-
# end
|
66
|
-
end
|
67
40
|
|
68
|
-
|
69
|
-
|
70
|
-
|
41
|
+
def process(att)
|
42
|
+
config = Labimotion::MapperUtils.load_brucker_config
|
43
|
+
return if config.nil?
|
71
44
|
|
72
|
-
|
73
|
-
|
74
|
-
return
|
45
|
+
attacher = att&.attachment_attacher
|
46
|
+
extracted_data = Labimotion::MapperUtils.extract_data_from_zip(attacher&.file&.url, config['sourceMap'])
|
47
|
+
return if extracted_data.nil?
|
48
|
+
|
49
|
+
extracted_data[:parameters] = config['sourceMap']['parameters']
|
50
|
+
extracted_data
|
75
51
|
end
|
76
|
-
end
|
77
52
|
|
53
|
+
def generate_ds(_id, cid, data, current_user = {}, element = nil)
|
54
|
+
return if data.nil? || cid.nil?
|
78
55
|
|
79
|
-
|
80
|
-
|
56
|
+
obj = Labimotion::NmrMapper.build_ds(cid, data[:content])
|
57
|
+
return if obj.nil? || obj[:ols].nil?
|
81
58
|
|
82
|
-
|
83
|
-
|
59
|
+
Labimotion::NmrMapper.update_ds(cid, obj, current_user, element)
|
60
|
+
end
|
84
61
|
|
85
|
-
|
86
|
-
|
87
|
-
|
62
|
+
def update_ds(_cid, obj, current_user, element)
|
63
|
+
dataset = obj[:dataset]
|
64
|
+
dataset.properties = process_prop(obj, current_user, element)
|
65
|
+
dataset.save!
|
66
|
+
end
|
88
67
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
# new_prop = dataset.properties
|
68
|
+
def build_ds(id, content)
|
69
|
+
ds = find_container(id)
|
70
|
+
return if ds.nil? || content.nil?
|
93
71
|
|
94
|
-
|
95
|
-
|
96
|
-
end
|
72
|
+
Labimotion::DatasetBuilder.build(ds, content)
|
73
|
+
end
|
97
74
|
|
98
|
-
|
99
|
-
return if field['field'] != field_name || value&.empty?
|
75
|
+
private
|
100
76
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
77
|
+
def finalize_ds(new_prop)
|
78
|
+
Constants::FG_FINALIZE.each do |field_path|
|
79
|
+
field = Labimotion::Utils.find_field(new_prop, field_path)
|
80
|
+
next unless field
|
105
81
|
|
106
|
-
|
107
|
-
dataset = obj[:dataset]
|
108
|
-
metadata = obj[:metadata]
|
109
|
-
new_prop = dataset.properties
|
110
|
-
new_prop.dig(Labimotion::Prop::LAYERS, 'general', Labimotion::Prop::FIELDS)&.each_with_index do |fi, idx|
|
111
|
-
# new_prop = set_data(new_prop, fi, idx, 'general', 'title', metadata['NAME'])
|
112
|
-
if fi['field'] == 'title' && metadata['NAME'].present?
|
113
|
-
## fi['label'] = fi['label']
|
114
|
-
fi['value'] = metadata['NAME']
|
115
|
-
fi['device'] = metadata['NAME']
|
116
|
-
fi['dkey'] = 'NAME'
|
117
|
-
new_prop[Labimotion::Prop::LAYERS]['general'][Labimotion::Prop::FIELDS][idx] = fi
|
82
|
+
update_finalized_field(field)
|
118
83
|
end
|
84
|
+
new_prop
|
85
|
+
end
|
119
86
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
fi['dkey'] = 'Date_'
|
125
|
-
new_prop[Labimotion::Prop::LAYERS]['general'][Labimotion::Prop::FIELDS][idx] = fi
|
126
|
-
end
|
87
|
+
def sys_to_ds(new_prop, element, current_user)
|
88
|
+
Constants::FG_SYSTEM.each do |field_path|
|
89
|
+
field = Labimotion::Utils.find_field(new_prop, field_path)
|
90
|
+
next unless field
|
127
91
|
|
128
|
-
|
129
|
-
## fi['label'] = fi['label']
|
130
|
-
fi['value'] = metadata['Time']
|
131
|
-
fi['device'] = metadata['Time']
|
132
|
-
fi['dkey'] = 'Time'
|
133
|
-
new_prop[Labimotion::Prop::LAYERS]['general'][Labimotion::Prop::FIELDS][idx] = fi
|
92
|
+
update_system_field(field, current_user, element)
|
134
93
|
end
|
94
|
+
new_prop
|
95
|
+
end
|
96
|
+
|
97
|
+
def params_to_ds(obj, new_prop)
|
98
|
+
metadata = obj[:metadata]
|
99
|
+
parameters = obj[:parameters]
|
135
100
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
new_prop
|
101
|
+
parameters.each do |param_key, field_path|
|
102
|
+
next if skip_parameter?(metadata, param_key)
|
103
|
+
|
104
|
+
update_param_field(new_prop, field_path, param_key, metadata)
|
140
105
|
end
|
106
|
+
new_prop
|
141
107
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
fi['value'] = element.id
|
151
|
-
new_prop[Labimotion::Prop::LAYERS]['sample_details'][Labimotion::Prop::FIELDS][idx] = fi
|
152
|
-
end
|
108
|
+
|
109
|
+
def process_prop(obj, current_user, element)
|
110
|
+
new_prop = obj[:dataset].properties
|
111
|
+
new_prop
|
112
|
+
.then { |prop| params_to_ds(obj, prop) }
|
113
|
+
.then { |prop| sys_to_ds(prop, element, current_user) }
|
114
|
+
.then { |prop| finalize_ds(prop) }
|
115
|
+
.then { |prop| Labimotion::VocabularyHandler.update_vocabularies(prop, current_user, element) }
|
153
116
|
end
|
154
117
|
|
155
|
-
|
156
|
-
|
157
|
-
## fi['label'] = fi['label']
|
158
|
-
fi['value'] = metadata['INSTRUM']
|
159
|
-
fi['device'] = metadata['INSTRUM']
|
160
|
-
fi['dkey'] = 'INSTRUM'
|
161
|
-
new_prop[Labimotion::Prop::LAYERS]['instrument'][Labimotion::Prop::FIELDS][idx] = fi
|
162
|
-
end
|
118
|
+
def find_attachment(id)
|
119
|
+
Attachment.find_by(id: id, con_state: Labimotion::ConState::NMR)
|
163
120
|
end
|
164
121
|
|
165
|
-
|
166
|
-
if
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
122
|
+
def handle_process_result(result, att, id, current_user)
|
123
|
+
if result[:is_bagit]
|
124
|
+
handle_bagit_result(att, id, current_user)
|
125
|
+
elsif invalid_metadata?(result)
|
126
|
+
Labimotion::ConState::NONE
|
127
|
+
else
|
128
|
+
handle_nmr_result(result, att, current_user)
|
172
129
|
end
|
173
130
|
end
|
174
131
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
fi['device'] = metadata['SOLVENT']
|
180
|
-
fi['dkey'] = 'SOLVENT'
|
181
|
-
fi['value'] = 'chloroform-D1 (CDCl3)' if metadata['SOLVENT'] == 'CDCl3'
|
182
|
-
new_prop[Labimotion::Prop::LAYERS]['sample_preparation'][Labimotion::Prop::FIELDS][idx] = fi
|
183
|
-
end
|
132
|
+
def handle_bagit_result(att, id, current_user)
|
133
|
+
att.update_column(:con_state, Labimotion::ConState::CONVERTED)
|
134
|
+
Labimotion::Converter.metadata(id, current_user)
|
135
|
+
Labimotion::ConState::COMPLETED
|
184
136
|
end
|
185
137
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
new_prop[Labimotion::Prop::LAYERS]['set'][Labimotion::Prop::FIELDS][idx] = fi
|
138
|
+
def invalid_metadata?(result)
|
139
|
+
result[:metadata].nil? ||
|
140
|
+
Constants::OBSERVED.exclude?(result[:metadata]['NUC1'])
|
141
|
+
end
|
142
|
+
|
143
|
+
def handle_nmr_result(result, att, current_user)
|
144
|
+
ds = find_container(att.attachable_id)
|
145
|
+
return Labimotion::ConState::NONE unless valid_container?(ds)
|
146
|
+
|
147
|
+
prepare_mapper_result(ds, result, current_user)
|
148
|
+
Labimotion::ConState::COMPLETED
|
149
|
+
end
|
150
|
+
|
151
|
+
def find_container(cid)
|
152
|
+
Container.find_by(id: cid)
|
153
|
+
end
|
154
|
+
|
155
|
+
def valid_container?(container)
|
156
|
+
container.present? &&
|
157
|
+
container.parent&.container_type == 'analysis' &&
|
158
|
+
container.root_element.present?
|
159
|
+
end
|
160
|
+
|
161
|
+
def prepare_mapper_result(container, result, current_user)
|
162
|
+
metadata = result[:metadata]
|
163
|
+
ols = determine_ols_term(metadata['NUC1'])
|
164
|
+
|
165
|
+
data = {
|
166
|
+
content: {
|
167
|
+
metadata: metadata,
|
168
|
+
ols: ols,
|
169
|
+
parameters: result[:parameters]
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
generate_ds(nil, container.id, data, current_user, container.root_element)
|
174
|
+
end
|
175
|
+
|
176
|
+
def determine_ols_term(nuc1)
|
177
|
+
case nuc1
|
178
|
+
when '1H' then Constants::OlsTerms::NMR_1H
|
179
|
+
when '13C' then Constants::OlsTerms::NMR_13C
|
229
180
|
end
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
181
|
+
end
|
182
|
+
|
183
|
+
def skip_parameter?(metadata, param_key)
|
184
|
+
metadata[param_key.to_s].blank? &&
|
185
|
+
Constants::DUP_FIELDS.exclude?(param_key.to_s.downcase)
|
186
|
+
end
|
187
|
+
|
188
|
+
def update_param_field(properties, field_path, param_key, metadata)
|
189
|
+
field = Labimotion::Utils.find_field(properties, field_path)
|
190
|
+
return unless field
|
191
|
+
|
192
|
+
update_field_value(field, param_key, metadata)
|
193
|
+
update_field_extend(field, param_key, metadata)
|
194
|
+
end
|
195
|
+
|
196
|
+
def update_duplicate_field(field_name, metadata)
|
197
|
+
case field_name
|
198
|
+
when 'time' then metadata['DATE']
|
199
|
+
when 'Version' then metadata['TITLE']
|
236
200
|
end
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
201
|
+
end
|
202
|
+
|
203
|
+
def update_field_value(field, param_key, metadata)
|
204
|
+
field['value'] = format_field_value(field, param_key, metadata)
|
205
|
+
rescue StandardError => e
|
206
|
+
Rails.logger.error "Error converting value for #{param_key}: #{e.message}"
|
207
|
+
field['value'] = ''
|
208
|
+
end
|
209
|
+
|
210
|
+
def format_field_value(field, param_key, metadata)
|
211
|
+
if field['type'] == 'integer'
|
212
|
+
Integer(metadata[param_key.to_s])
|
213
|
+
elsif Constants::DUP_FIELDS.include?(param_key.to_s.downcase)
|
214
|
+
update_duplicate_field(field['field'], metadata)
|
215
|
+
else
|
216
|
+
metadata[param_key.to_s]
|
243
217
|
end
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
218
|
+
end
|
219
|
+
|
220
|
+
def update_finalized_field(field)
|
221
|
+
case field['field']
|
222
|
+
when 'temperature'
|
223
|
+
field['value_system'] = 'K'
|
224
|
+
when 'date', 'time'
|
225
|
+
field['value'] = Labimotion::MapperUtils.format_timestamp(field['value'], field['field'])
|
226
|
+
when 'Name'
|
227
|
+
field['value'] = 'TopSpin' if field['value'].to_s.include?('TopSpin')
|
228
|
+
when 'Version'
|
229
|
+
field['value'] = field['value'].to_s.split('TopSpin').last.strip if field['value'].to_s.include?('TopSpin')
|
250
230
|
end
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
231
|
+
end
|
232
|
+
|
233
|
+
def update_system_field(field, current_user, element)
|
234
|
+
field['value'] = determine_field_value(field['field'], current_user, element)
|
235
|
+
end
|
236
|
+
|
237
|
+
def determine_field_value(field_name, current_user, element)
|
238
|
+
case field_name
|
239
|
+
when 'creator'
|
240
|
+
current_user&.name if current_user.present?
|
241
|
+
when 'label', 'id'
|
242
|
+
get_sample_value(field_name, element)
|
257
243
|
end
|
258
244
|
end
|
259
|
-
new_prop = Labimotion::VocabularyHandler.update_vocabularies(new_prop, current_user, element)
|
260
|
-
dataset.properties = new_prop
|
261
|
-
dataset.save!
|
262
|
-
end
|
263
245
|
|
264
|
-
|
265
|
-
|
266
|
-
end
|
246
|
+
def get_sample_value(field_name, element)
|
247
|
+
return unless element.present? && element.is_a?(Sample)
|
267
248
|
|
268
|
-
|
269
|
-
|
270
|
-
end
|
249
|
+
field_name == 'label' ? element.short_label : element.id
|
250
|
+
end
|
271
251
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
ols = content[:ols]
|
277
|
-
metadata = content[:metadata]
|
278
|
-
|
279
|
-
return if ols.nil? || metadata.nil?
|
280
|
-
|
281
|
-
klass = Labimotion::DatasetKlass.find_by(ols_term_id: ols)
|
282
|
-
return if klass.nil?
|
283
|
-
|
284
|
-
uuid = SecureRandom.uuid
|
285
|
-
props = klass.properties_release
|
286
|
-
props['uuid'] = uuid
|
287
|
-
props['pkg'] = Labimotion::Utils.pkg(props['pkg'])
|
288
|
-
props['klass'] = 'Dataset'
|
289
|
-
dataset = Labimotion::Dataset.create!(
|
290
|
-
uuid: uuid,
|
291
|
-
dataset_klass_id: klass.id,
|
292
|
-
element_type: 'Container',
|
293
|
-
element_id: ds.id,
|
294
|
-
properties: props,
|
295
|
-
properties_release: klass.properties_release,
|
296
|
-
klass_uuid: klass.uuid,
|
297
|
-
)
|
298
|
-
{ dataset: dataset, metadata: metadata, ols: ols }
|
252
|
+
def update_field_extend(field, param_key, metadata)
|
253
|
+
field['device'] = metadata[param_key.to_s]
|
254
|
+
field['dkey'] = param_key.to_s
|
255
|
+
end
|
299
256
|
end
|
300
257
|
end
|
301
258
|
end
|
@@ -58,6 +58,9 @@ module Labimotion
|
|
58
58
|
after_create :update_counter
|
59
59
|
before_destroy :delete_attachment
|
60
60
|
|
61
|
+
def user_labels
|
62
|
+
tag&.taggable_data&.fetch('user_labels', nil)
|
63
|
+
end
|
61
64
|
|
62
65
|
def attachments
|
63
66
|
Attachment.where(attachable_id: self.id, attachable_type: self.class.name)
|