labimotion 2.0.0 → 2.0.1

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/labimotion/apis/labimotion_api.rb +3 -1
  3. data/lib/labimotion/apis/labimotion_hub_api.rb +83 -10
  4. data/lib/labimotion/apis/standard_api.rb +2 -1
  5. data/lib/labimotion/apis/standard_layer_api.rb +17 -4
  6. data/lib/labimotion/apis/vocabulary_api.rb +20 -6
  7. data/lib/labimotion/constants.rb +7 -0
  8. data/lib/labimotion/entities/dataset_entity.rb +1 -0
  9. data/lib/labimotion/entities/element_entity.rb +0 -1
  10. data/lib/labimotion/entities/klass_revision_entity.rb +5 -9
  11. data/lib/labimotion/entities/properties_entity.rb +41 -102
  12. data/lib/labimotion/entities/segment_entity.rb +2 -0
  13. data/lib/labimotion/entities/vocabulary_entity.rb +12 -3
  14. data/lib/labimotion/helpers/element_helpers.rb +1 -2
  15. data/lib/labimotion/helpers/generic_helpers.rb +1 -6
  16. data/lib/labimotion/helpers/param_helpers.rb +16 -0
  17. data/lib/labimotion/libs/converter.rb +1 -0
  18. data/lib/labimotion/libs/data/layer/StdDataset.json +212 -0
  19. data/lib/labimotion/libs/data/vocab/System.json +1 -1
  20. data/lib/labimotion/libs/export_dataset.rb +98 -343
  21. data/lib/labimotion/libs/nmr_mapper.rb +247 -204
  22. data/lib/labimotion/libs/template_hub.rb +28 -10
  23. data/lib/labimotion/models/concerns/generic_klass_revisions.rb +0 -1
  24. data/lib/labimotion/models/concerns/generic_revisions.rb +0 -3
  25. data/lib/labimotion/models/concerns/klass_revision.rb +23 -0
  26. data/lib/labimotion/models/dataset_klasses_revision.rb +4 -1
  27. data/lib/labimotion/models/element.rb +0 -3
  28. data/lib/labimotion/models/element_klasses_revision.rb +3 -2
  29. data/lib/labimotion/models/segment_klasses_revision.rb +4 -1
  30. data/lib/labimotion/models/template_submission.rb +52 -0
  31. data/lib/labimotion/utils/utils.rb +0 -21
  32. data/lib/labimotion/version.rb +1 -1
  33. data/lib/labimotion.rb +3 -3
  34. metadata +5 -6
  35. data/lib/labimotion/libs/data/mapper/Chemwiki.json +0 -236
  36. data/lib/labimotion/libs/data/mapper/Source.json +0 -78
  37. data/lib/labimotion/libs/dataset_builder.rb +0 -70
  38. data/lib/labimotion/utils/mapper_utils.rb +0 -169
@@ -8,174 +8,47 @@ module Labimotion
8
8
  DEFAULT_ROW_WIDTH = 100
9
9
  DEFAULT_ROW_HEIGHT = 20
10
10
 
11
- def initialize(id)
11
+ def initialize(**args)
12
12
  @xfile = Axlsx::Package.new
13
- @xfile.workbook.styles.fonts.first.name = 'Calibri'
14
13
  @file_extension = 'xlsx'
15
-
16
- @id = id
17
- @dataset = Labimotion::Dataset.find_by(element_id: @id, element_type: 'Container')
18
- return if @dataset.nil?
19
-
20
- @klass = @dataset.dataset_klass
21
- @ols_term_id = @klass.ols_term_id
22
- @label = @klass.label
23
- @analysis = @dataset&.element&.parent
24
- @element = @analysis&.root&.containable if @analysis.present?
25
- @element_type = @element.class.name if @element.present?
26
- @spectra_values = []
27
- end
28
-
29
- def read
30
- @xfile.to_stream.read
14
+ @xfile.workbook.styles.fonts.first.name = 'Calibri'
31
15
  end
32
16
 
33
- def res_name
34
- element_name = Container.find(@id)&.root_element&.short_label
35
- ols = ols_name
17
+ def res_name(id)
18
+ element_name = Container.find(id)&.root_element&.short_label
19
+ ols = ols_name(id)
36
20
  "#{element_name}_#{ols.gsub(' ', '_')}.xlsx"
37
21
  rescue StandardError => e
38
22
  Labimotion.log_exception(e)
39
23
  end
40
24
 
41
- def export
42
- return if @dataset.nil? || @analysis.nil? || @element.nil?
43
-
44
- description
45
- dataset_info
46
- spectra_info
47
- chemwiki_info
48
- rescue StandardError => e
49
- Labimotion.log_exception(e)
50
- end
51
-
52
- private
53
-
54
- def chemwiki_info
55
- @spectra_values
56
- config = Labimotion::MapperUtils.load_config(File.read(Constants::Mapper::WIKI_CONFIG))
57
- return if config.nil? || config[@ols_term_id].nil?
58
-
59
- sheet = @xfile.workbook.add_worksheet(name: 'ChemWiki')
60
- map_index = config.dig(@ols_term_id, 'index')
61
- map_mapper = config.dig(@ols_term_id, 'mapper')
62
- map_source = config.dig(@ols_term_id, 'source')
63
-
64
- return if map_index.nil? || map_mapper.nil? || map_source.nil?
65
-
66
- return unless map_index.is_a?(Array) && map_mapper.is_a?(Hash) && map_source.is_a?(Hash)
25
+ def ols_name(id)
26
+ ds = Labimotion::Dataset.find_by(element_id: id, element_type: 'Container')
27
+ return nil if ds.nil?
67
28
 
68
- # sheet.column_info.each { |col| col.width = map_width }
69
-
70
- if @spectra_values.length == 0
71
- @spectra_values.push([])
72
- end
73
-
74
- @spectra_values.each_with_index do |spdata, idx|
75
- array_header = []
76
- array_data = []
77
- map_index.each do |key|
78
- mapper = map_mapper.dig(key)
79
- next if mapper.nil? || mapper['sources'].nil? || !mapper.is_a?(Hash)
80
-
81
- col_header = ''
82
- col_value = ''
83
- mapper['sources'].each_with_index do |source_key, idx|
84
- source = map_source&.dig(source_key)
85
- next if source.nil? || source['title'].nil?
86
-
87
- col_header = source['title']
88
-
89
- if col_value.present?
90
- col_value += (mapper['separator'] || '') + source_data(source, spdata)
91
- else
92
- col_value = source_data(source, spdata)
93
- end
94
- end
95
- array_header.push(col_header || '')
96
- array_data.push(col_value || '')
97
- end
98
- if idx == 0
99
- sheet.add_row(array_header)
100
- end
101
- sheet.add_row(array_data)
102
-
103
- ## for Redox potential
104
- last_row = sheet.rows.last
105
- last_row.cells[3].type = :string
106
- end
107
-
108
-
109
- rescue StandardError => e
110
- Labimotion.log_exception(e)
111
- end
29
+ name = ds.dataset_klass.label
112
30
 
31
+ match = name.match(/\((.*?)\)/)
32
+ name = match && match.length > 1 ? match[1] : name
113
33
 
114
- def conv_value(field, properties)
115
- return '' if field.nil?
116
-
117
- case field['type']
118
- when 'select'
119
- return '' if field['value'].empty?
120
- Labimotion::Utils.find_options_val(field, properties) || field['value']
121
- else
122
- field['value']
123
- end
124
- end
125
-
126
- def source_data(source, spdata)
127
- param = source['param']
128
- return '' if param.nil? || source['type'].nil?
129
-
130
- val = case source['type']
131
- when 'string'
132
- param
133
- when 'spc'
134
- spdata.length.positive? && param.is_a?(Integer) ? spdata.pluck(param).join(';') : ''
135
- when 'sample'
136
- @element.send(param) if @element.respond_to?(param)
137
- when 'molecule'
138
- @element.molecule.send(param) if @element_type == 'Sample' && @element&.molecule&.respond_to?(param)
139
- when 'dataset'
140
- field = Labimotion::Utils.find_field(@dataset.properties, param)
141
- conv_value(field, @dataset.properties)
142
- end
143
- val || ''
144
- rescue StandardError => e
145
- Labimotion.log_exception(e)
146
- end
147
-
148
- def element_info
149
- if @element.class.name == 'Sample'
150
- sheet = @xfile.workbook.add_worksheet(name: 'Element')
151
- sheet.add_row(['inchikey', @element.molecule_inchikey])
152
- sheet.add_row(['molfile', @element.molfile])
153
- end
154
- rescue StandardError => e
155
- Labimotion.log_exception(e)
156
- end
157
-
158
- def ols_name
159
- match = @label.match(/\((.*?)\)/)
160
- name = match && match.length > 1 ? match[1] : @label
161
-
162
- name = '1H NMR' if @ols_term_id == 'CHMO:0000593'
163
- name = '13C NMR' if @ols_term_id == 'CHMO:0000595'
34
+ name = '1H NMR' if ds.dataset_klass.ols_term_id == 'CHMO:0000593'
35
+ name = '13C NMR' if ds.dataset_klass.ols_term_id == 'CHMO:0000595'
164
36
  name.slice(0, 26)
165
37
  rescue StandardError => e
166
38
  Labimotion.log_exception(e)
167
39
  'ols_name'
168
40
  end
169
41
 
170
- def description
42
+ def description(ds, id)
43
+ wb = @xfile.workbook
171
44
  sheet = @xfile.workbook.add_worksheet(name: 'Description')
172
45
  header_style = sheet.styles.add_style(sz: 12, fg_color: 'FFFFFF', bg_color: '00008B', border: { style: :thick, color: 'FF777777', edges: [:bottom] })
173
- sheet.add_row(['File name', res_name])
46
+ sheet.add_row(['File name', res_name(id)])
174
47
  sheet.add_row(['Time', Time.now.strftime("%Y-%m-%d %H:%M:%S %Z")] )
175
48
  sheet.add_row(['(This file is automatically generated by the system.)'])
176
49
  sheet.add_row([''])
177
50
  sheet.add_row([''])
178
- sheet.add_row(['Fields description of sheet:' + @dataset.dataset_klass.label])
51
+ sheet.add_row(['Fields description of sheet:' + ds.dataset_klass.label])
179
52
  sheet.add_row(['Fields', 'Field description'], style: header_style)
180
53
  sheet.add_row(['Layer Label', 'The label of the layer'])
181
54
  sheet.add_row(['Field Label', 'The label of the field'])
@@ -192,211 +65,66 @@ module Labimotion
192
65
  Labimotion.log_exception(e)
193
66
  end
194
67
 
195
- def dataset_info
196
- name = ols_name
197
- return if name.nil?
198
-
199
- sheet = create_dataset_sheet(name)
200
- process_layers(sheet)
201
- rescue StandardError => e
202
- Labimotion.log_exception(e)
203
- end
204
-
205
- def create_dataset_sheet(name)
206
- sheet = @xfile.workbook.add_worksheet(name: name)
207
- sheet.add_row([@dataset.dataset_klass.label])
208
- sheet.add_row(header, style: create_header_style(sheet))
209
- sheet
210
- end
211
-
212
- def process_layers(sheet)
213
- layers = @dataset.properties[Labimotion::Prop::LAYERS] || {}
214
- options = @dataset.properties[Labimotion::Prop::SEL_OPTIONS]
68
+ def export(id)
69
+ ds = Labimotion::Dataset.find_by(element_id: id, element_type: 'Container')
70
+ return if ds.nil?
215
71
 
216
- sort_layers(layers).each do |key|
217
- layer = layers[key]
218
- add_layer_row(sheet, layer)
219
- process_fields(sheet, layer, options)
220
- end
221
- end
72
+ description(ds, id)
222
73
 
223
- def sort_layers(layers)
224
- layers.keys.sort_by { |key| layers[key]['position'] }
225
- end
74
+ wb = @xfile.workbook
75
+ name = ols_name(id)
76
+ return if name.nil?
226
77
 
227
- def add_layer_row(sheet, layer)
78
+ sheet = @xfile.workbook.add_worksheet(name: name)
79
+ sheet.add_row([ds.dataset_klass.label])
80
+ header_style = sheet.styles.add_style(sz: 12, fg_color: 'FFFFFF', bg_color: '00008B', border: { style: :thick, color: 'FF777777', edges: [:bottom] })
228
81
  layer_style = sheet.styles.add_style(b: true, bg_color: 'CEECF5')
229
- sheet.add_row([layer['label']] + [' '] * 8, style: layer_style)
230
- end
82
+ sheet.add_row(header, style: header_style)
231
83
 
232
- def process_fields(sheet, layer, options)
233
- sorted_fields = sort_fields(layer)
234
-
235
- sorted_fields.each do |field|
236
- next if field['type'] == 'dummy'
237
-
238
- add_field_row(sheet, field)
239
- set_field_types(sheet, field, options)
240
- end
241
- end
242
-
243
- def sort_fields(layer)
244
- layer[Labimotion::Prop::FIELDS].sort_by { |obj| obj['position'] }
245
- end
246
-
247
- def add_field_row(sheet, field)
248
- type = determine_field_type(field)
249
- from_device = determine_source(field)
250
- show_value = format_value(field['value'])
251
- ontology_data = extract_ontology_data(field)
252
-
253
- row_data = [
254
- ' ',
255
- field['label'],
256
- nil,
257
- field['value_system'],
258
- field['field'],
259
- type,
260
- from_device,
261
- field['dkey'],
262
- nil
263
- ] + ontology_data
264
-
265
- sheet.add_row(row_data.freeze)
266
- end
267
-
268
- def determine_field_type(field)
269
- return "#{field['type']}-#{field['option_layers']}" if [
270
- Labimotion::FieldType::SELECT,
271
- Labimotion::FieldType::SYSTEM_DEFINED
272
- ].include?(field['type'])
273
-
274
- field['type']
275
- end
276
-
277
- def determine_source(field)
278
- return 'Chemotion' if field['system'].present?
279
- return 'Device' if field['device'].present?
280
- ''
281
- end
282
-
283
- def format_value(value)
284
- return value&.gsub(',', '.') if value =~ /\A\d+,\d+\z/
285
- value
286
- end
287
-
288
- def extract_ontology_data(field)
289
- ontology = field['ontology'] || {}
290
- [
291
- ontology['short_form'] || '',
292
- ontology['label'] || '',
293
- ontology['iri'] || ''
294
- ]
295
- end
84
+ layers = ds.properties[Labimotion::Prop::LAYERS] || {}
85
+ options = ds.properties[Labimotion::Prop::SEL_OPTIONS]
86
+ layer_keys = layers.keys.sort_by { |key| layers[key]['position'] }
87
+ layer_keys.each do |key|
88
+ layer = layers[key]
89
+ sheet.add_row([layer['label'], ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], style: layer_style)
90
+ sorted_fields = layer[Labimotion::Prop::FIELDS].sort_by { |obj| obj['position'] }
91
+ sorted_fields.each do |field|
92
+ next if field['type'] == 'dummy'
93
+
94
+ type = field['type']
95
+ from_device = field['device'].present? ? 'Device' : ''
96
+ from_device = field['system'].present? ? 'Chemotion' : from_device
97
+ type = "#{field['type']}-#{field['option_layers']}" if field['type'] == Labimotion::FieldType::SELECT || field['type'] == Labimotion::FieldType::SYSTEM_DEFINED
98
+
99
+ show_value = field['value'] =~ /\A\d+,\d+\z/ ? field['value']&.gsub(',', '.') : field['value']
100
+ ols_short_form = (field['ontology'] && field['ontology']['short_form']) || ''
101
+ ols_label = (field['ontology'] && field['ontology']['label']) || ''
102
+ ols_iri = (field['ontology'] && field['ontology']['iri']) || ''
103
+ sheet.add_row([' ', field['label'], nil, field['value_system'], field['field'], type, from_device, field['dkey'], nil, ols_short_form, ols_label, ols_iri].freeze)
104
+
105
+ case field['type']
106
+ when Labimotion::FieldType::SELECT
107
+ sheet.rows.last.cells[2].type = :string
108
+ sheet.rows.last.cells[8].type = :string
109
+ show_value = opt_value(field, options) if show_value.present?
110
+ when Labimotion::FieldType::SYSTEM_DEFINED, Labimotion::FieldType::INTEGER
111
+ sheet.rows.last.cells[2].type = :float
112
+ sheet.rows.last.cells[8].type = :float
113
+ else
114
+ sheet.rows.last.cells[2].type = :string
115
+ sheet.rows.last.cells[8].type = :string
116
+ end
117
+ sheet.rows.last.cells[2].value = show_value
118
+ sheet.rows.last.cells[8].value = field['system'] || field['device']
296
119
 
297
- def set_field_types(sheet, field, options)
298
- last_row = sheet.rows.last
299
120
 
300
- case field['type']
301
- when Labimotion::FieldType::SELECT
302
- set_select_field_type(last_row, field, options)
303
- when Labimotion::FieldType::SYSTEM_DEFINED, Labimotion::FieldType::INTEGER
304
- set_numeric_field_type(last_row)
305
- else
306
- set_string_field_type(last_row)
121
+ end
122
+ # sheet.column_widths nil, nil, nil, nil, 0, 0, 0, 0, 0
307
123
  end
308
-
309
- set_field_values(last_row, field)
310
- end
311
-
312
- def set_select_field_type(row, field, options)
313
- row.cells[2].type = :string
314
- row.cells[8].type = :string
315
- row.cells[2].value = opt_value(field, options) if field['value'].present?
316
- end
317
-
318
- def set_numeric_field_type(row)
319
- row.cells[2].type = :float
320
- row.cells[8].type = :float
321
- end
322
-
323
- def set_string_field_type(row)
324
- row.cells[2].type = :string
325
- row.cells[8].type = :string
326
- end
327
-
328
- def set_field_values(row, field)
329
- row.cells[8].value = field['system'] || field['device']
330
- end
331
-
332
- def spectra_info
333
- name_mapping = process_csv_files
334
- return if name_mapping.nil? || name_mapping.length <= 1
335
-
336
- create_mapping_sheet(name_mapping)
337
124
  rescue StandardError => e
338
125
  Labimotion.log_exception(e)
339
126
  end
340
127
 
341
- def process_csv_files
342
- cds_csv = Container.find(@id).attachments.where(aasm_state: 'csv').order(:filename)
343
- return if cds_csv.empty?
344
-
345
- cds_csv.each_with_index.map do |att, index|
346
- process_single_csv(att, index)
347
- end.compact
348
- end
349
-
350
- def process_single_csv(attachment, index)
351
- sheet_name = "Sheet#{index + 1}"
352
- sheet = @xfile.workbook.add_worksheet(name: sheet_name)
353
-
354
- File.open(attachment.attachment_url) do |file|
355
- first_line = file.readline.chomp
356
- next unless cv_spc?(first_line)
357
-
358
- process_csv_content(first_line, file, sheet)
359
- end
360
- [sheet_name, attachment.filename]
361
- end
362
-
363
- def process_csv_content(first_line, file, sheet)
364
- lines = []
365
- file.each_line.with_index do |line, index|
366
- line = first_line if index.zero?
367
- row_data = line.split(',')
368
- sheet.add_row(row_data)
369
- lines << row_data if cv_data?(row_data, index)
370
- end
371
- @spectra_values << lines if lines.any?
372
- end
373
-
374
- def create_mapping_sheet(name_mapping)
375
- first_sheet = @xfile.workbook.worksheets&.first
376
- return unless first_sheet
377
-
378
- header_style = create_header_style(first_sheet)
379
- first_sheet.add_row(['Sheet name', 'File name'], style: header_style)
380
-
381
- name_mapping.each do |sheet_name, filename|
382
- first_sheet.add_row([sheet_name.to_s, filename.to_s])
383
- end
384
- end
385
-
386
-
387
- def create_header_style(sheet)
388
- sheet.styles.add_style(
389
- sz: 12,
390
- fg_color: 'FFFFFF',
391
- bg_color: '00008B',
392
- border: {
393
- style: :thick,
394
- color: 'FF777777',
395
- edges: [:bottom]
396
- }
397
- )
398
- end
399
-
400
128
  def opt_value(field, options)
401
129
  return nil if field.nil? || options.nil? || field['value']&.empty? || field['option_layers']&.empty?
402
130
  return nil unless opts = options.fetch(field['option_layers'], nil)&.fetch('options', nil)
@@ -408,18 +136,45 @@ module Labimotion
408
136
  field['value']
409
137
  end
410
138
 
411
- def cv_spc?(first_line)
412
- data = first_line.split(',')
413
- data && data.length > 2 && data[2] == Constants::CHMO::CV
414
- end
139
+ def spectra(id)
140
+ name_mapping = []
141
+ wb = @xfile.workbook
142
+ gds = Labimotion::Dataset.find_by(element_id: id, element_type: 'Container')
143
+ cds = Container.find(id)
144
+ cds_csv = cds.attachments.where(aasm_state: 'csv').order(:filename)
145
+ csv_length = cds_csv.length
146
+ return if csv_length.zero?
147
+ cds_csv.each_with_index do |att, idx|
148
+ sheet_name = "Sheet#{idx+1}"
149
+ sheet = @xfile.workbook.add_worksheet(name: sheet_name)
150
+ name_mapping.push([sheet_name, att.filename])
151
+ File.open(att.attachment_url) do |fi|
152
+ fi.each_line do |line|
153
+ sheet.add_row(line.split(','))
154
+ end
155
+ end
156
+ end
157
+
158
+ if name_mapping.length > 1
159
+ first_sheet = @xfile.workbook.worksheets&.first
160
+ header_style = first_sheet&.styles.add_style(sz: 12, fg_color: 'FFFFFF', bg_color: '00008B', border: { style: :thick, color: 'FF777777', edges: [:bottom] })
161
+ first_sheet&.add_row(['Sheet name', 'File name'], style: header_style)
162
+ name_mapping&.each do |mapping|
163
+ next if mapping.length < 2
164
+
165
+ @xfile.workbook.worksheets&.first&.add_row([mapping[0].to_s, mapping[1].to_s])
166
+ end
167
+ end
415
168
 
416
- def cv_data?(data, idx)
417
- data.length > 7 && idx > 7
418
169
  end
419
170
 
420
171
  def header
421
172
  ['Layer Label', 'Field Label', 'Value', 'Unit', 'Name', 'Type', 'Source?', 'Source identifier', 'Source data', 'Ontology', 'Ontology Label', 'iri'].freeze
422
173
  end
423
174
 
175
+ def read
176
+ @xfile.to_stream.read
177
+ end
178
+
424
179
  end
425
180
  end