labimotion 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/lib/labimotion/api.rb +19 -0
  3. data/lib/labimotion/apis/converter_api.rb +68 -0
  4. data/lib/labimotion/apis/generic_dataset_api.rb +50 -0
  5. data/lib/labimotion/apis/generic_element_api.rb +339 -0
  6. data/lib/labimotion/apis/labimotion_hub_api.rb +53 -0
  7. data/lib/labimotion/apis/segment_api.rb +144 -0
  8. data/lib/labimotion/entities/application_entity.rb +88 -0
  9. data/lib/labimotion/entities/dataset_entity.rb +16 -0
  10. data/lib/labimotion/entities/dataset_klass_entity.rb +9 -0
  11. data/lib/labimotion/entities/element_entity.rb +108 -0
  12. data/lib/labimotion/entities/element_klass_entity.rb +10 -0
  13. data/lib/labimotion/entities/element_revision_entity.rb +57 -0
  14. data/lib/labimotion/entities/eln_element_entity.rb +110 -0
  15. data/lib/labimotion/entities/generic_entity.rb +54 -0
  16. data/lib/labimotion/entities/generic_klass_entity.rb +14 -0
  17. data/lib/labimotion/entities/generic_public_entity.rb +25 -0
  18. data/lib/labimotion/entities/klass_revision_entity.rb +20 -0
  19. data/lib/labimotion/entities/segment_entity.rb +62 -0
  20. data/lib/labimotion/entities/segment_klass_entity.rb +8 -0
  21. data/lib/labimotion/entities/segment_revision_entity.rb +55 -0
  22. data/lib/labimotion/helpers/converter_helpers.rb +13 -0
  23. data/lib/labimotion/helpers/dataset_helpers.rb +38 -0
  24. data/lib/labimotion/helpers/element_helpers.rb +268 -0
  25. data/lib/labimotion/helpers/generic_helpers.rb +252 -0
  26. data/lib/labimotion/helpers/repository_helpers.rb +14 -0
  27. data/lib/labimotion/helpers/sample_association_helpers.rb +126 -0
  28. data/lib/labimotion/helpers/search_helpers.rb +62 -0
  29. data/lib/labimotion/helpers/segment_helpers.rb +97 -0
  30. data/lib/labimotion/libs/converter.rb +325 -0
  31. data/lib/labimotion/libs/export_dataset.rb +121 -0
  32. data/lib/labimotion/libs/nmr_mapper.rb +265 -0
  33. data/lib/labimotion/libs/nmr_mapper_repo.rb +263 -0
  34. data/lib/labimotion/libs/template_hub.rb +55 -0
  35. data/lib/labimotion/models/collections_element.rb +42 -0
  36. data/lib/labimotion/models/concerns/attachment_converter.rb +42 -0
  37. data/lib/labimotion/models/concerns/datasetable.rb +50 -0
  38. data/lib/labimotion/models/concerns/generic_klass_revisions.rb +39 -0
  39. data/lib/labimotion/models/concerns/generic_revisions.rb +43 -0
  40. data/lib/labimotion/models/concerns/segmentable.rb +74 -0
  41. data/lib/labimotion/models/dataset.rb +14 -0
  42. data/lib/labimotion/models/dataset_klass.rb +25 -0
  43. data/lib/labimotion/models/dataset_klasses_revision.rb +9 -0
  44. data/lib/labimotion/models/datasets_revision.rb +9 -0
  45. data/lib/labimotion/models/element.rb +121 -0
  46. data/lib/labimotion/models/element_klass.rb +25 -0
  47. data/lib/labimotion/models/element_klasses_revision.rb +9 -0
  48. data/lib/labimotion/models/elements_element.rb +11 -0
  49. data/lib/labimotion/models/elements_revision.rb +8 -0
  50. data/lib/labimotion/models/elements_sample.rb +11 -0
  51. data/lib/labimotion/models/segment.rb +14 -0
  52. data/lib/labimotion/models/segment_klass.rb +24 -0
  53. data/lib/labimotion/models/segment_klasses_revision.rb +9 -0
  54. data/lib/labimotion/models/segments_revision.rb +9 -0
  55. data/lib/labimotion/utils/con_state.rb +13 -0
  56. data/lib/labimotion/utils/export.rb +112 -0
  57. data/lib/labimotion/utils/import.rb +186 -0
  58. data/lib/labimotion/utils/search.rb +112 -0
  59. data/lib/labimotion/utils/serializer.rb +78 -0
  60. data/lib/labimotion/version.rb +6 -0
  61. data/lib/labimotion.rb +95 -0
  62. metadata +119 -0
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require 'grape'
3
+ # require 'labimotion/models/segment_klass'
4
+ module Labimotion
5
+ ## ElementHelpers
6
+ module SearchHelpers
7
+ extend Grape::API::Helpers
8
+
9
+ def serialization_elements(result, page, page_size, element_ids, params)
10
+ # klasses = Labimotion::ElementKlass.where(is_active: true, is_generic: true)
11
+ # klasses.each do |klass|
12
+ # element_ids_for_klass = Element.where(id: element_ids, element_klass_id: klass.id).pluck(:id)
13
+ # paginated_element_ids = Kaminari.paginate_array(element_ids_for_klass).page(page).per(page_size)
14
+ # serialized_elements = Element.find(paginated_element_ids).map do |element|
15
+ # Labimotion::ElementEntity.represent(element, displayed_in_list: true).serializable_hash
16
+ # end
17
+
18
+ # result["#{klass.name}s"] = {
19
+ # elements: serialized_elements,
20
+ # totalElements: element_ids_for_klass.size,
21
+ # page: page,
22
+ # pages: pages(element_ids_for_klass.size),
23
+ # perPage: page_size,
24
+ # ids: element_ids_for_klass,
25
+ # }
26
+ # end
27
+ # result
28
+ end
29
+
30
+
31
+ def gl_elements_search(col, params)
32
+ # element_scope = Element.joins(:collections_elements).where(
33
+ # 'collections_elements.collection_id = ? and collections_elements.element_type = (?)', collection.id, params[:selection][:genericElName]
34
+ # )
35
+ # if params[:selection][:searchName].present?
36
+ # element_scope = element_scope.where('name like (?)',
37
+ # "%#{params[:selection][:searchName]}%")
38
+ # end
39
+ # if params[:selection][:searchShowLabel].present?
40
+ # element_scope = element_scope.where('short_label like (?)', "%#{params[:selection][:searchShowLabel]}%")
41
+ # end
42
+ # if params[:selection][:searchProperties].present?
43
+ # params[:selection][:searchProperties] && params[:selection][:searchProperties][:layers] && params[:selection][:searchProperties][:layers].keys.each do |lk|
44
+ # layer = params[:selection][:searchProperties][:layers][lk]
45
+ # qs = layer[:fields].select { |f| f[:value].present? || f[:type] == 'input-group' }
46
+ # qs.each do |f|
47
+ # if f[:type] == 'input-group'
48
+ # sfs = f[:sub_fields].map { |e| { id: e[:id], value: e[:value] } }
49
+ # query = { "#{lk}": { fields: [{ field: f[:field].to_s, sub_fields: sfs }] } } if sfs.length > 0
50
+ # elsif f[:type] == 'checkbox' || f[:type] == 'integer' || f[:type] == 'system-defined'
51
+ # query = { "#{lk}": { fields: [{ field: f[:field].to_s, value: f[:value] }] } }
52
+ # else
53
+ # query = { "#{lk}": { fields: [{ field: f[:field].to_s, value: f[:value].to_s }] } }
54
+ # end
55
+ # element_scope = element_scope.where('properties @> ?', query.to_json)
56
+ # end
57
+ # end
58
+ # end
59
+ # element_scope
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+ require 'grape'
3
+ require 'labimotion/models/segment_klass'
4
+ module Labimotion
5
+ ## ElementHelpers
6
+ module SegmentHelpers
7
+ extend Grape::API::Helpers
8
+
9
+ def klass_list(el_klass, is_active=false)
10
+ scope = SegmentKlass.all
11
+ scope = scope.where(is_active: is_active) if is_active.present? && is_active == true
12
+ scope = scope.joins(:element_klass).where(klass_element: params[:element], is_active: true) if el_klass.present?
13
+ scope.order('place') || []
14
+ end
15
+
16
+ def create_segment_klass(current_user, params)
17
+ place = params[:place]
18
+ begin
19
+ place = place.to_i if place.present? && place.to_i == place.to_f
20
+ rescue StandardError
21
+ place = 100
22
+ end
23
+
24
+ uuid = SecureRandom.uuid
25
+ template = { uuid: uuid, layers: {}, select_options: {} }
26
+ attributes = declared(params, include_missing: false)
27
+ attributes[:properties_template]['uuid'] = uuid if attributes[:properties_template].present?
28
+ template = (attributes[:properties_template].presence || template)
29
+ template['eln'] = Chemotion::Application.config.version
30
+ template['labimotion'] = Labimotion::VERSION
31
+ template['klass'] = 'SegmentKlass'
32
+ attributes.merge!(properties_template: template, element_klass: @klass, created_by: current_user.id,
33
+ place: place)
34
+ attributes[:is_active] = false
35
+ attributes[:uuid] = uuid
36
+ attributes[:released_at] = DateTime.now
37
+ attributes[:properties_release] = attributes[:properties_template]
38
+ klass = Labimotion::SegmentKlass.create!(attributes)
39
+ klass.reload
40
+ klass.create_klasses_revision(current_user.id)
41
+ klass
42
+ rescue => e
43
+ Labimotion.log_exception(e, current_user)
44
+ raise e
45
+ end
46
+
47
+ def update_segment_klass(current_user, params)
48
+ segment = fetch_klass('SegmentKlass', params[:id])
49
+ place = params[:place]
50
+ begin
51
+ place = place.to_i if place.present? && place.to_i == place.to_f
52
+ rescue StandardError
53
+ place = 100
54
+ end
55
+ attributes = declared(params, include_missing: false)
56
+ attributes.delete(:id)
57
+ attributes[:place] = place
58
+ segment&.update!(attributes)
59
+ segment
60
+ rescue => e
61
+ Labimotion.log_exception(e, current_user)
62
+ raise e
63
+ end
64
+
65
+ def create_repo_klass(params, current_user)
66
+ response = Labimotion::TemplateHub.fetch_identifier('SegmentKlass', params[:identifier])
67
+ byebug
68
+ attributes = response.slice('label', 'desc', 'uuid', 'identifier', 'released_at') # .except(:id, :is_active, :place, :created_by, :created_at, :updated_at)
69
+ attributes['properties_template'] = response['properties_release']
70
+ attributes['place'] = ((Labimotion::SegmentKlass.all.length * 10) || 0) + 10
71
+ attributes['is_active'] = false
72
+ attributes['updated_by'] = current_user.id
73
+ attributes['sync_by'] = current_user.id
74
+ attributes['sync_time'] = DateTime.now
75
+
76
+ element_klass = Labimotion::ElementKlass.find_by(identifier: response['element_klass']['identifier'])
77
+ element_klass = Labimotion::ElementKlass.find_by(name: response['element_klass']['name']) if element_klass.nil?
78
+ if element_klass.nil?
79
+ el_attributes = response['element_klass'].slice('name', 'label', 'desc', 'uuid', 'identifier', 'icon_name', 'klass_prefix', 'is_generic', 'released_at')
80
+ el_attributes['properties_template'] = response['element_klass']['properties_release']
81
+ Labimotion::ElementKlass.create!(el_attributes)
82
+ end
83
+
84
+
85
+ if Labimotion::SegmentKlass.find_by(ols_term_id: attributes['ols_term_id']).present?
86
+ ds = Labimotion::SegmentKlass.find_by(ols_term_id: attributes['ols_term_id'])
87
+ ds.update!(attributes)
88
+ else
89
+ attributes['created_by'] = current_user.id
90
+ Labimotion::SegmentKlass.create!(attributes)
91
+ end
92
+ rescue => e
93
+ Labimotion.log_exception(e, current_user)
94
+ raise e
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,325 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'date'
7
+ require 'labimotion/version'
8
+
9
+ # rubocop: disable Metrics/AbcSize
10
+ # rubocop: disable Metrics/MethodLength
11
+ # rubocop: disable Metrics/ClassLength
12
+ # rubocop: disable Metrics/CyclomaticComplexity
13
+
14
+ module Labimotion
15
+ class Converter
16
+
17
+ def self.logger
18
+ @@converter_logger ||= Logger.new(Rails.root.join('log/converter.log')) # rubocop:disable Style/ClassVars
19
+ end
20
+
21
+ def self.uri(api_name)
22
+ url = Rails.configuration.converter.url
23
+ "#{url}#{api_name}"
24
+ end
25
+
26
+ def self.timeout
27
+ Rails.configuration.try(:converter).try(:timeout) || 30
28
+ end
29
+
30
+ def self.extname
31
+ '.jdx'
32
+ end
33
+
34
+ def self.client_id
35
+ Rails.configuration.converter.profile || ''
36
+ end
37
+
38
+ def self.secret_key
39
+ Rails.configuration.converter.secret_key || ''
40
+ end
41
+
42
+ def self.auth
43
+ { username: client_id, password: secret_key }
44
+ end
45
+
46
+ def self.date_time
47
+ DateTime.now.strftime('%Q')
48
+ end
49
+
50
+ def self.signature(jbody)
51
+ md5 = Digest::MD5.new
52
+ md5.update jbody
53
+ mdhex = md5.hexdigest
54
+ mdall = mdhex << secret_key
55
+ @signature = Digest::SHA1.hexdigest mdall
56
+ end
57
+
58
+ def self.header(opt = {})
59
+ opt || {}
60
+ end
61
+
62
+ def self.vor_conv(id)
63
+ conf = Rails.configuration.try(:converter).try(:url)
64
+ oa = Attachment.find(id)
65
+ folder = Rails.root.join('tmp/uploads/converter')
66
+ FileUtils.mkdir_p(folder)
67
+ return nil if conf.nil? || oa.nil?
68
+
69
+ { a: oa, f: folder }
70
+ end
71
+
72
+ def self.collect_metadata(zip_file) # rubocop: disable Metrics/PerceivedComplexity
73
+ dsr = []
74
+ ols = nil
75
+ zip_file.each do |entry|
76
+ next unless entry.name == 'metadata/converter.json'
77
+
78
+ metadata = entry.get_input_stream.read.force_encoding('UTF-8')
79
+ jdata = JSON.parse(metadata)
80
+
81
+ ols = jdata['ols']
82
+ matches = jdata['matches']
83
+ matches&.each do |match|
84
+ idf = match['identifier']
85
+ idr = match['result']
86
+ if idf&.class == Hash && idr&.class == Hash && !idf['outputLayer'].nil? && !idf['outputKey'].nil? && !idr['value'].nil? # rubocop:disable Layout/LineLength
87
+ dsr.push(layer: idf['outputLayer'], field: idf['outputKey'], value: idr['value'])
88
+ end
89
+ end
90
+ end
91
+ { d: dsr, o: ols }
92
+ end
93
+
94
+ def self.handle_response(oat, response) # rubocop: disable Metrics/PerceivedComplexity
95
+ dsr = []
96
+ ols = nil
97
+
98
+ begin
99
+ tmp_file = Tempfile.new(encoding: 'ascii-8bit')
100
+ tmp_file.write(response.parsed_response)
101
+ tmp_file.rewind
102
+
103
+ name = response&.headers && response&.headers['content-disposition']&.split('=')&.last
104
+ filename = oat.filename
105
+ name = "#{File.basename(filename, '.*')}.zip" if name.nil?
106
+
107
+ att = Attachment.new(
108
+ filename: name,
109
+ file_path: tmp_file.path,
110
+ ## content_type: file[:type],
111
+ attachable_id: oat.attachable_id,
112
+ attachable_type: 'Container',
113
+ con_state: Labimotion::ConState::CONVERTED,
114
+ created_by: oat.created_by,
115
+ created_for: oat.created_for,
116
+ )
117
+ # att.attachment_attacher.attach(tmp_file)
118
+ if att.valid? && Labimotion::IS_RAILS5 == false
119
+ att.attachment_attacher.create_derivatives
120
+ att.save!
121
+ end
122
+ if att.valid? && Labimotion::IS_RAILS5 == true
123
+ primary_store = Rails.configuration.storage.primary_store
124
+ att.update!(storage: primary_store)
125
+ end
126
+
127
+ Zip::File.open(tmp_file.path) do |zip_file|
128
+ res = Labimotion::Converter.collect_metadata(zip_file) if name.split('.')&.last == 'zip'
129
+ ols = res[:o] unless res&.dig(:o).nil?
130
+ dsr.push(res[:d]) unless res&.dig(:d).nil?
131
+ end
132
+
133
+ dsr.flatten!
134
+ if dsr.length.positive? && name.split('.')&.last == 'zip'
135
+ Labimotion::Converter.ts('write', att.attachable_id, ols: ols, info: dsr)
136
+ end
137
+ rescue StandardError => e
138
+ raise e
139
+ ensure
140
+ tmp_file&.close
141
+ end
142
+ end
143
+
144
+ def self.process(data)
145
+ return if data[:a]&.attachable_type != 'Container'
146
+
147
+ response = nil
148
+ begin
149
+ ofile = Rails.root.join(data[:f], data[:a].filename)
150
+ FileUtils.cp(data[:a].store.path, ofile)
151
+ File.open(ofile, 'r') do |f|
152
+ body = { file: f }
153
+ response = HTTParty.post(
154
+ uri('conversions'),
155
+ basic_auth: auth,
156
+ body: body,
157
+ timeout: timeout,
158
+ )
159
+ end
160
+ if response.ok?
161
+ Labimotion::Converter.handle_response(data[:a], response)
162
+ else
163
+ Labimotion::Converter.logger.error ["Converter Response Error: id: [#{data[:a]&.id}], filename: [#{data[:a]&.filename}], response: #{response}"].join($INPUT_RECORD_SEPARATOR)
164
+ end
165
+ response
166
+ rescue StandardError => e
167
+ raise e
168
+ ensure
169
+ FileUtils.rm_f(ofile)
170
+ end
171
+ end
172
+
173
+ def self.jcamp_converter(id)
174
+ resp = nil
175
+ begin
176
+ data = Labimotion::Converter.vor_conv(id)
177
+ return if data.nil?
178
+
179
+ resp = Labimotion::Converter.process(data)
180
+ resp&.success? ? Labimotion::ConState::PROCESSED : Labimotion::ConState::ERROR
181
+ rescue StandardError => e
182
+ Labimotion::ConState::ERROR
183
+ Labimotion::Converter.logger.error ["jcamp_converter fail: #{id}", e.message, *e.backtrace].join($INPUT_RECORD_SEPARATOR)
184
+ end
185
+ end
186
+
187
+ def self.generate_ds(att_id, current_user = {})
188
+ dsr_info = Labimotion::Converter.fetch_dsr(att_id)
189
+ begin
190
+ return unless dsr_info && dsr_info[:info]&.length.positive?
191
+ dataset = Labimotion::Converter.build_ds(att_id, dsr_info[:ols])
192
+ Labimotion::Converter.update_ds(dataset, dsr_info[:info], current_user)
193
+ rescue StandardError => e
194
+ Labimotion::Converter.logger.error ["Att ID: #{att_id}, OLS: #{dsr_info[:ols]}", "DSR: #{dsr_info[:info]}", e.message, *e.backtrace].join($INPUT_RECORD_SEPARATOR)
195
+ ensure
196
+ Labimotion::Converter.clean_dsr(att_id)
197
+ end
198
+ end
199
+
200
+ def self.build_ds(att_id, ols)
201
+ cds = Container.find_by(id: att_id)
202
+ dataset = Labimotion::Dataset.find_by(element_type: 'Container', element_id: cds.id)
203
+ return dataset unless dataset.nil?
204
+
205
+ klass = Labimotion::DatasetKlass.find_by(ols_term_id: ols)
206
+ return if klass.nil?
207
+
208
+ uuid = SecureRandom.uuid
209
+ props = klass.properties_release
210
+ props['uuid'] = uuid
211
+ props['eln'] = Chemotion::Application.config.version
212
+ props['labimotion'] = Labimotion::VERSION
213
+ props['klass'] = 'Dataset'
214
+ Labimotion::Dataset.create!(
215
+ uuid: uuid,
216
+ dataset_klass_id: klass.id,
217
+ element_type: 'Container',
218
+ element_id: cds.id,
219
+ properties: props,
220
+ properties_release: klass.properties_release,
221
+ klass_uuid: klass.uuid
222
+ )
223
+ end
224
+
225
+ def self.update_ds(dataset, dsr, current_user = nil) # rubocop: disable Metrics/PerceivedComplexity
226
+ layers = dataset.properties['layers'] || {}
227
+ new_prop = dataset.properties
228
+ dsr.each do |ds|
229
+ layer = layers[ds[:layer]]
230
+ fields = layer['fields'].select{ |f| f['field'] == ds[:field] }
231
+ fi = fields&.first
232
+ idx = layer['fields'].find_index(fi)
233
+ fi['value'] = ds[:value]
234
+ fi['device'] = ds[:device] || ds[:value]
235
+ new_prop['layers'][ds[:layer]]['fields'][idx] = fi
236
+ end
237
+ new_prop.dig('layers', 'general', 'fields')&.each_with_index do |fi, idx|
238
+ if fi['field'] == 'creator' && current_user.present?
239
+ fi['label'] = fi['label']
240
+ fi['value'] = current_user.name
241
+ fi['system'] = current_user.name
242
+ new_prop['layers']['general']['fields'][idx] = fi
243
+ end
244
+ end
245
+ element = Container.find(dataset.element_id)&.root_element
246
+ element.present? && element&.class&.name == 'Sample' && new_prop.dig('layers', 'sample_details', 'fields')&.each_with_index do |fi, idx|
247
+ if fi['field'] == 'id'
248
+ fi['value'] = element.id
249
+ fi['system'] = element.id
250
+ new_prop['layers']['general']['fields'][idx] = fi
251
+ end
252
+
253
+ if fi['field'] == 'label'
254
+ fi['value'] = element.short_label
255
+ fi['system'] = element.short_label
256
+ new_prop['layers']['general']['fields'][idx] = fi
257
+ end
258
+ end
259
+ dataset.properties = new_prop
260
+ dataset.save!
261
+ end
262
+
263
+ def self.ts(method, identifier, params = nil)
264
+ Rails.cache.send(method, "#{Labimotion::Converter.new.class.name}#{identifier}", params)
265
+ end
266
+
267
+ def self.fetch_dsr(att_id)
268
+ Labimotion::Converter.ts('read', att_id)
269
+ end
270
+
271
+ def self.clean_dsr(att_id)
272
+ Labimotion::Converter.ts('delete', att_id)
273
+ end
274
+
275
+ def self.fetch_options
276
+ options = { basic_auth: auth, timeout: timeout }
277
+ response = HTTParty.get(uri('options'), options)
278
+ response.parsed_response if response.code == 200
279
+ end
280
+
281
+ def self.delete_profile(id)
282
+ options = { basic_auth: auth, timeout: timeout }
283
+ response = HTTParty.delete("#{uri('profiles')}/#{id}", options)
284
+ response.parsed_response if response.code == 200
285
+ end
286
+
287
+ def self.create_profile(data)
288
+ options = { basic_auth: auth, timeout: timeout, body: data.to_json, headers: { 'Content-Type' => 'application/json' } }
289
+ response = HTTParty.post(uri('profiles'), options)
290
+ response.parsed_response if response.code == 201
291
+ end
292
+
293
+ def self.update_profile(data)
294
+ options = { basic_auth: auth, timeout: timeout, body: data.to_json, headers: { 'Content-Type' => 'application/json' } }
295
+ response = HTTParty.put("#{uri('profiles')}/#{data[:id]}", options)
296
+ response.parsed_response if response.code == 200
297
+ end
298
+
299
+ def self.fetch_profiles
300
+ options = { basic_auth: auth, timeout: timeout }
301
+ response = HTTParty.get(uri('profiles'), options)
302
+ response.parsed_response if response.code == 200
303
+ end
304
+
305
+ def self.create_tables(tmpfile)
306
+ res = {}
307
+ File.open(tmpfile.path, 'r') do |file|
308
+ body = { file: file }
309
+ response = HTTParty.post(
310
+ uri('tables'),
311
+ basic_auth: auth,
312
+ body: body,
313
+ timeout: timeout,
314
+ )
315
+ res = response.parsed_response
316
+ end
317
+ res
318
+ end
319
+ end
320
+ end
321
+
322
+ # rubocop: enable Metrics/AbcSize
323
+ # rubocop: enable Metrics/MethodLength
324
+ # rubocop: enable Metrics/ClassLength
325
+ # rubocop: enable Metrics/CyclomaticComplexity
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+ require 'export_table'
3
+
4
+ module Labimotion
5
+ ## ExportDataset
6
+ class ExportDataset
7
+ DEFAULT_ROW_WIDTH = 100
8
+ DEFAULT_ROW_HEIGHT = 20
9
+
10
+ def initialize(**args)
11
+ @xfile = Axlsx::Package.new
12
+ @file_extension = 'xlsx'
13
+ @xfile.workbook.styles.fonts.first.name = 'Calibri'
14
+ end
15
+
16
+ def res_name(id)
17
+ element_name = Container.find(id)&.root_element&.short_label
18
+ ols = ols_name(id)
19
+ "#{element_name}_#{ols.gsub(' ', '_')}.xlsx"
20
+ end
21
+
22
+ def ols_name(id)
23
+ ds = Labimotion::Dataset.find_by(element_id: id, element_type: 'Container')
24
+ return nil if ds.nil?
25
+
26
+ name = ds.dataset_klass.label
27
+
28
+ match = name.match(/\((.*?)\)/)
29
+ name = match && match.length > 2 ? match[1] : name
30
+
31
+ name = '1H NMR' if ds.dataset_klass.ols_term_id == 'CHMO:0000593'
32
+ name = '13C NMR' if ds.dataset_klass.ols_term_id == 'CHMO:0000595'
33
+ name.slice(0, 26)
34
+ end
35
+
36
+ def description(ds, id)
37
+ wb = @xfile.workbook
38
+ sheet = @xfile.workbook.add_worksheet(name: 'Description')
39
+ header_style = sheet.styles.add_style(sz: 12, fg_color: 'FFFFFF', bg_color: '00008B', border: { style: :thick, color: 'FF777777', edges: [:bottom] })
40
+ sheet.add_row(['File name', res_name(id)])
41
+ sheet.add_row(['Time', Time.now.strftime("%Y-%m-%d %H:%M:%S %Z")] )
42
+ sheet.add_row([''])
43
+ sheet.add_row(['Fields description of sheet:' + ds.dataset_klass.label])
44
+ sheet.add_row(['Fields', 'Field description'], style: header_style)
45
+ sheet.add_row(['Layer Label', 'The label of the layer'])
46
+ sheet.add_row(['Field Label', 'The label of the field'])
47
+ sheet.add_row(['Value', 'The current value of the field'])
48
+ sheet.add_row(['Unit', 'The unit of the field'])
49
+ sheet.add_row(['Name', 'The key of the field, can be used to identify the field'])
50
+ sheet.add_row(['Type', 'The type of the field'])
51
+ sheet.add_row(['From device?', '[Yes] if the field is from the device, [No] if the field is manually entered by the user, [SYS] if the field is automatically generated by the system'])
52
+ sheet.add_row(['Device source', 'The source tag of the device file, available only if the ontology term is 1H NMR or 13C NMR'])
53
+ sheet.add_row(['Device data', 'The original device data, can not be changed after data uploaded'])
54
+ sheet.add_row([''])
55
+ sheet.add_row([''])
56
+ sheet.add_row([''])
57
+ sheet.add_row(['', '(This file is automatically generated by the system.)'])
58
+ end
59
+
60
+ def export(id)
61
+ ds = Labimotion::Dataset.find_by(element_id: id, element_type: 'Container')
62
+ return if ds.nil?
63
+
64
+ description(ds, id)
65
+
66
+ wb = @xfile.workbook
67
+ name = ols_name(id)
68
+ return if name.nil?
69
+
70
+ sheet = @xfile.workbook.add_worksheet(name: name)
71
+ sheet.add_row([ds.dataset_klass.label])
72
+ header_style = sheet.styles.add_style(sz: 12, fg_color: 'FFFFFF', bg_color: '00008B', border: { style: :thick, color: 'FF777777', edges: [:bottom] })
73
+ layer_style = sheet.styles.add_style(b: true, bg_color: 'CEECF5')
74
+ sheet.add_row(header, style: header_style)
75
+
76
+ layers = ds.properties['layers'] || {}
77
+ layer_keys = layers.keys.sort_by { |key| layers[key]['position'] }
78
+ layer_keys.each do |key|
79
+ layer = layers[key]
80
+ sheet.add_row([layer['label'], ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], style: layer_style)
81
+ sorted_fields = layer['fields'].sort_by { |obj| obj['position'] }
82
+ sorted_fields.each do |field|
83
+ next if field['type'] == 'dummy'
84
+
85
+ type = field['type']
86
+ from_device = field['device'].present? ? 'Yes' : 'No'
87
+ from_device = field['system'].present? ? 'SYS' : from_device
88
+ type = "#{field['type']}-#{field['option_layers']}" if field['type'] == 'select' || field['type'] == 'system-defined'
89
+
90
+ show_value = field['value'] =~ /\A\d+,\d+\z/ ? field['value']&.gsub(',', '.') : field['value']
91
+ sheet.add_row([' ', field['label'], show_value, field['value_system'], field['field'], type, from_device, field['dkey'], field['system'] || field['device']].freeze)
92
+ end
93
+ # sheet.column_widths nil, nil, nil, nil, 0, 0, 0, 0, 0
94
+ end
95
+ end
96
+
97
+ def spectra(id)
98
+ wb = @xfile.workbook
99
+ gds = Labimotion::Dataset.find_by(element_id: id, element_type: 'Container')
100
+ cds = Container.find(id)
101
+ cds.attachments.where(aasm_state: 'csv').each do |att|
102
+ name = File.basename(att.filename, '.csv')
103
+ sheet = @xfile.workbook.add_worksheet(name: name)
104
+ File.open(att.store.path) do |fi|
105
+ fi.each_line do |line|
106
+ sheet.add_row(line.split(','))
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ def header
113
+ ['Layer Label', 'Field Label', 'Value', 'Unit', 'Name', 'Type', 'from device?', 'Device source', 'Device data'].freeze
114
+ end
115
+
116
+ def read
117
+ @xfile.to_stream.read
118
+ end
119
+
120
+ end
121
+ end