labimotion 0.1.6

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 (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