labimotion 2.2.0.rc8 → 2.2.0.rc10

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.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'date'
2
3
  require 'ostruct'
3
4
  require 'export_table'
4
5
  require 'labimotion/version'
@@ -125,25 +126,13 @@ module Labimotion
125
126
  val = files&.map { |file| "#{file['filename']} #{file['label']}" }&.join('\n')
126
127
  field_obj[:value] = val
127
128
  when Labimotion::FieldType::TABLE
128
- field_obj[:is_table] = true
129
- field_obj[:not_table] = false
130
- tbl = []
131
- ## tbl_idx = []
132
- header = {}
133
- sub_fields = field.fetch('sub_fields', [])
134
- sub_fields.each_with_index do |sub_field, idx|
135
- header["col#{idx}"] = sub_field['col_name']
136
- end
137
- tbl.push(header)
138
- field.fetch('sub_values', []).each do |sub_val|
139
- data = {}
140
- sub_fields.each_with_index do |sub_field, idx|
141
- data["col#{idx}"] = build_table_field(sub_val, sub_field)
142
- end
143
- tbl.push(data)
144
- end
145
- field_obj[:data] = tbl
146
- # field_obj[:value] = 'this is a table'
129
+ field_obj[:is_table] = false
130
+ field_obj[:not_table] = true
131
+ field_obj[:value] = build_table_wordml(field)
132
+ when Labimotion::FieldType::DATETIME_RANGE
133
+ field_obj[:is_table] = false
134
+ field_obj[:not_table] = true
135
+ field_obj[:value] = build_datetime_range_wordml(field)
147
136
  when Labimotion::FieldType::INPUT_GROUP
148
137
  val = []
149
138
  field.fetch('sub_fields', [])&.each do |sub_field|
@@ -210,6 +199,243 @@ module Labimotion
210
199
  end
211
200
  end
212
201
 
202
+ TABLE_BLANK_WORDML = '<w:p/>'
203
+ TABLE_BORDER = '<w:tblBorders>' \
204
+ '<w:top w:val="single" w:sz="4" w:color="auto"/>' \
205
+ '<w:left w:val="single" w:sz="4" w:color="auto"/>' \
206
+ '<w:bottom w:val="single" w:sz="4" w:color="auto"/>' \
207
+ '<w:right w:val="single" w:sz="4" w:color="auto"/>' \
208
+ '<w:insideH w:val="single" w:sz="4" w:color="auto"/>' \
209
+ '<w:insideV w:val="single" w:sz="4" w:color="auto"/>' \
210
+ '</w:tblBorders>'
211
+
212
+ def build_table_wordml(field)
213
+ sub_fields = field.fetch('sub_fields', [])
214
+ return Sablon.content(:word_ml, TABLE_BLANK_WORDML) if sub_fields.empty?
215
+
216
+ width = (9000.0 / sub_fields.length).round
217
+ font_size = sub_fields.length > 6 ? 16 : 20
218
+ grid = sub_fields.map { %(<w:gridCol w:w="#{width}"/>) }.join
219
+ header = build_table_header_row(sub_fields, width, font_size)
220
+ rows = build_table_body_rows(field.fetch('sub_values', []), sub_fields, width, font_size)
221
+ tbl = '<w:tbl><w:tblPr><w:tblW w:w="5000" w:type="pct"/>' \
222
+ "#{TABLE_BORDER}</w:tblPr><w:tblGrid>#{grid}</w:tblGrid>" \
223
+ "#{header}#{rows}</w:tbl>"
224
+ Sablon.content(:word_ml, tbl)
225
+ rescue StandardError => e
226
+ Labimotion.log_exception(e)
227
+ Sablon.content(:word_ml, TABLE_BLANK_WORDML)
228
+ end
229
+
230
+ DATETIME_RANGE_HEADERS = ['Start', 'Stop', 'Duration (calc)', 'Duration'].freeze
231
+ DATETIME_RANGE_PRECISE_LABELS = %w[year month day hour minute second].freeze
232
+ DURATION_UNIT_LABELS = {
233
+ 'd' => 'day',
234
+ 'h' => 'hour',
235
+ 'min' => 'minute',
236
+ 's' => 'second'
237
+ }.freeze
238
+
239
+ def build_datetime_range_wordml(field)
240
+ sub_fields = field.fetch('sub_fields', []) || []
241
+ by_col = sub_fields.each_with_object({}) { |sf, h| h[sf['col_name']] = sf if sf.is_a?(Hash) }
242
+ values = datetime_range_values(by_col)
243
+ datetime_range_wordml_table(values)
244
+ rescue StandardError => e
245
+ Labimotion.log_exception(e)
246
+ Sablon.content(:word_ml, TABLE_BLANK_WORDML)
247
+ end
248
+
249
+ def datetime_range_values(by_col)
250
+ time_start = by_col['timeStart']&.dig('value').to_s
251
+ time_stop = by_col['timeStop']&.dig('value').to_s
252
+ [
253
+ time_start,
254
+ time_stop,
255
+ format_duration_calc(by_col['durationCalc'], time_start, time_stop),
256
+ format_duration_value(by_col['duration'])
257
+ ]
258
+ end
259
+
260
+ def datetime_range_wordml_table(values)
261
+ width = (9000.0 / DATETIME_RANGE_HEADERS.length).round
262
+ font_size = 20
263
+ grid = DATETIME_RANGE_HEADERS.map { %(<w:gridCol w:w="#{width}"/>) }.join
264
+ header_cells = DATETIME_RANGE_HEADERS.map { |h| build_wordml_cell(h, width, font_size, true) }.join
265
+ body_cells = values.map { |v| build_wordml_cell(v, width, font_size, false) }.join
266
+ tbl = '<w:tbl><w:tblPr><w:tblW w:w="5000" w:type="pct"/>' \
267
+ "#{TABLE_BORDER}</w:tblPr><w:tblGrid>#{grid}</w:tblGrid>" \
268
+ "<w:tr>#{header_cells}</w:tr><w:tr>#{body_cells}</w:tr></w:tbl>"
269
+ Sablon.content(:word_ml, tbl)
270
+ end
271
+
272
+ def format_duration_value(duration_sf)
273
+ val = duration_sf&.dig('value')
274
+ return '' if val.nil? || val.to_s.empty?
275
+
276
+ "#{val} #{duration_unit_label(duration_sf['value_system'], val)}".strip
277
+ end
278
+
279
+ def duration_unit_label(value_system, value)
280
+ base = DURATION_UNIT_LABELS[value_system.to_s]
281
+ return value_system.to_s if base.nil?
282
+
283
+ pluralize_unit?(value) ? "#{base}s" : base
284
+ end
285
+
286
+ def pluralize_unit?(value)
287
+ numeric = Float(value.to_s)
288
+ (numeric - 1.0).abs > Float::EPSILON
289
+ rescue ArgumentError, TypeError
290
+ true
291
+ end
292
+
293
+ def format_duration_calc(_duration_calc_sf, time_start, time_stop)
294
+ compute_duration_calc(time_start, time_stop)
295
+ end
296
+
297
+ def compute_duration_calc(time_start, time_stop)
298
+ start_t = parse_datetime_range_value(time_start)
299
+ stop_t = parse_datetime_range_value(time_stop)
300
+ return '' unless start_t && stop_t && stop_t > start_t
301
+
302
+ precise_diff_humanize(start_t, stop_t)
303
+ end
304
+
305
+ def parse_datetime_range_value(str)
306
+ cleaned = str.to_s.strip
307
+ return nil if cleaned.empty?
308
+
309
+ parts = Date._parse(cleaned)
310
+ return nil unless datetime_range_parts_complete?(parts)
311
+
312
+ build_utc_from_parts(parts)
313
+ rescue ArgumentError
314
+ nil
315
+ end
316
+
317
+ def datetime_range_parts_complete?(parts)
318
+ %i[year mon mday].all? { |k| parts[k] }
319
+ end
320
+
321
+ def build_utc_from_parts(parts)
322
+ Time.utc(parts[:year], parts[:mon], parts[:mday],
323
+ parts[:hour] || 0, parts[:min] || 0, parts[:sec] || 0)
324
+ end
325
+
326
+ def precise_diff_humanize(start_t, stop_t)
327
+ components = precise_diff_components(start_t, stop_t)
328
+ parts = components.zip(DATETIME_RANGE_PRECISE_LABELS).reject { |n, _| n <= 0 }
329
+ return '0 seconds' if parts.empty?
330
+
331
+ parts.map { |n, label| format_precise_part(n, label) }.join(' ')
332
+ end
333
+
334
+ def format_precise_part(count, label)
335
+ suffix = count == 1 ? '' : 's'
336
+ "#{count} #{label}#{suffix}"
337
+ end
338
+
339
+ def precise_diff_components(start_t, stop_t)
340
+ y, m, d, h, mi, s = precise_diff_raw(start_t, stop_t)
341
+ mi, s = borrow_unit(mi, s, 60)
342
+ h, mi = borrow_unit(h, mi, 60)
343
+ d, h = borrow_unit(d, h, 24)
344
+ if d.negative?
345
+ m -= 1
346
+ d += (Date.new(stop_t.year, stop_t.month, 1) - 1).day
347
+ end
348
+ y, m = borrow_unit(y, m, 12)
349
+ [y, m, d, h, mi, s]
350
+ end
351
+
352
+ def borrow_unit(higher, lower, base)
353
+ return [higher, lower] unless lower.negative?
354
+
355
+ [higher - 1, lower + base]
356
+ end
357
+
358
+ def precise_diff_raw(start_t, stop_t)
359
+ [
360
+ stop_t.year - start_t.year,
361
+ stop_t.month - start_t.month,
362
+ stop_t.day - start_t.day,
363
+ stop_t.hour - start_t.hour,
364
+ stop_t.min - start_t.min,
365
+ stop_t.sec - start_t.sec
366
+ ]
367
+ end
368
+
369
+ def build_table_header_row(sub_fields, width, font_size)
370
+ cells = sub_fields.map { |sf| build_wordml_cell(sf['col_name'].to_s, width, font_size, true) }.join
371
+ "<w:tr>#{cells}</w:tr>"
372
+ end
373
+
374
+ def build_table_body_rows(sub_values, sub_fields, width, font_size)
375
+ sub_values.map do |sv|
376
+ cells = sub_fields.map { |sf| build_wordml_cell(table_cell_text(sv, sf), width, font_size, false) }.join
377
+ "<w:tr>#{cells}</w:tr>"
378
+ end.join
379
+ end
380
+
381
+ def build_wordml_cell(text, width, font_size, bold)
382
+ bold_xml = bold ? '<w:b/>' : ''
383
+ rpr = "<w:rPr>#{bold_xml}<w:sz w:val=\"#{font_size}\"/></w:rPr>"
384
+ lines = text.to_s.split(/\r?\n/)
385
+ lines = [''] if lines.empty?
386
+ paragraphs = lines.map do |line|
387
+ "<w:p><w:r>#{rpr}<w:t xml:space=\"preserve\">#{xml_escape(line)}</w:t></w:r></w:p>"
388
+ end.join
389
+ "<w:tc><w:tcPr><w:tcW w:w=\"#{width}\" w:type=\"dxa\"/></w:tcPr>#{paragraphs}</w:tc>"
390
+ end
391
+
392
+ def xml_escape(str)
393
+ str.to_s.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
394
+ end
395
+
396
+ TABLE_CELL_TEXT_RENDERERS = {
397
+ Labimotion::FieldType::DRAG_SAMPLE => ->(_sf, c, ctx) { ctx.table_cell_sample_text(c['value'] || {}) },
398
+ Labimotion::FieldType::DRAG_MOLECULE => ->(_sf, c, ctx) { ctx.table_cell_molecule_text(c['value'] || {}) },
399
+ Labimotion::FieldType::SELECT => ->(_sf, c, _ctx) { c['value'].to_s },
400
+ Labimotion::FieldType::SYSTEM_DEFINED => ->(sf, c, ctx) { ctx.table_cell_sysdef_text(sf, c) }
401
+ }.freeze
402
+
403
+ def table_cell_text(sub_val, sub_field)
404
+ return '' if sub_field.fetch('id', nil).nil? || sub_val[sub_field['id']].nil?
405
+
406
+ cell = sub_val[sub_field['id']]
407
+ renderer = TABLE_CELL_TEXT_RENDERERS[sub_field['type']]
408
+ return renderer.call(sub_field, cell, self) if renderer
409
+
410
+ raw = cell.is_a?(Hash) ? cell['value'] : cell
411
+ raw.to_s
412
+ end
413
+
414
+ def table_cell_sample_text(val)
415
+ parts = []
416
+ parts << "Short Label: [#{val['el_label']}]" if val['el_label'].present?
417
+ parts << "Name: [#{val['el_name']}]" if val['el_name'].present?
418
+ parts << "Ext. Label: [#{val['el_external_label']}]" if val['el_external_label'].present?
419
+ parts << "Mass: [#{val['el_molecular_weight']}]" if val['el_molecular_weight'].present?
420
+ parts << "#{sample_url}/#{val['el_id']}" if val['el_id'].present?
421
+ parts.join("\n")
422
+ end
423
+
424
+ def table_cell_molecule_text(val)
425
+ parts = []
426
+ parts << "SMILES: [#{val['el_smiles']}]" if val['el_smiles'].present?
427
+ parts << "InChiKey: [#{val['el_inchikey']}]" if val['el_inchikey'].present?
428
+ parts << "IUPAC: [#{val['el_iupac']}]" if val['el_iupac'].present?
429
+ parts << "MASS: [#{val['el_molecular_weight']}]" if val['el_molecular_weight'].present?
430
+ parts.join("\n")
431
+ end
432
+
433
+ def table_cell_sysdef_text(sub_field, cell)
434
+ fdef = Labimotion::Units::FIELDS.find { |o| o[:field] == sub_field['option_layers'] }
435
+ unit = fdef&.fetch(:units, [])&.find { |u| u[:key] == cell['value_system'] }&.fetch(:label, '')
436
+ "#{cell['value']} #{unit}".strip
437
+ end
438
+
213
439
  def build_fields(layer)
214
440
  fields = layer[Labimotion::Prop::FIELDS] || []
215
441
  field_objs = []
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labimotion
4
+ class DoseRespOutput < ApplicationRecord
5
+ acts_as_paranoid
6
+ self.table_name = :dose_resp_outputs
7
+
8
+ # Associations
9
+ belongs_to :dose_resp_request, class_name: 'Labimotion::DoseRespRequest'
10
+
11
+ # Validations
12
+ validates :dose_resp_request, presence: true
13
+ validates :output_data, presence: true
14
+ validate :output_data_is_hash
15
+
16
+ # Scopes
17
+ scope :recent, -> { order(created_at: :desc) }
18
+
19
+ private
20
+
21
+ def output_data_is_hash
22
+ return if output_data.is_a?(Hash)
23
+
24
+ errors.add(:output_data, 'must be a Hash')
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labimotion
4
+ class DoseRespRequest < ApplicationRecord
5
+ acts_as_paranoid
6
+ self.table_name = :dose_resp_requests
7
+
8
+ # Token generation
9
+ has_secure_token :access_token
10
+
11
+ # Callbacks
12
+ before_create :generate_request_id
13
+
14
+ # Associations
15
+ belongs_to :element, class_name: 'Labimotion::Element'
16
+ belongs_to :creator, foreign_key: :created_by, class_name: 'User'
17
+ has_many :dose_resp_outputs, class_name: 'Labimotion::DoseRespOutput', dependent: :destroy
18
+
19
+ # Validations
20
+ validates :element, presence: true
21
+ validates :creator, presence: true
22
+ validates :expires_at, presence: true
23
+ validates :state, inclusion: { in: [-1, 0, 1, 2] }
24
+ validate :metadata_is_hash
25
+ validate :input_metadata_is_hash
26
+
27
+ # State constants
28
+ STATE_ERROR = -1
29
+ STATE_INITIAL = 0
30
+ STATE_PROCESSING = 1
31
+ STATE_COMPLETED = 2
32
+
33
+ # Scopes
34
+ scope :active, -> { where('expires_at > ?', Time.current).where(revoked_at: nil) }
35
+ scope :expired, -> { where('expires_at <= ?', Time.current) }
36
+ scope :revoked, -> { where.not(revoked_at: nil) }
37
+
38
+ # Instance methods
39
+ def expired?
40
+ expires_at.present? && expires_at < Time.current
41
+ end
42
+
43
+ def revoked?
44
+ revoked_at.present?
45
+ end
46
+
47
+ def active?
48
+ !expired? && !revoked?
49
+ end
50
+
51
+ def revoke!
52
+ update!(revoked_at: Time.current, state: STATE_ERROR)
53
+ end
54
+
55
+ def mark_processing!
56
+ update!(state: STATE_PROCESSING) if state == STATE_INITIAL
57
+ end
58
+
59
+ def mark_completed!
60
+ update!(state: STATE_COMPLETED)
61
+ end
62
+
63
+ def mark_error!(message = nil)
64
+ update!(state: STATE_ERROR, resp_message: message)
65
+ end
66
+
67
+ def track_access!
68
+ increment!(:access_count)
69
+ update_columns(
70
+ first_accessed_at: first_accessed_at || Time.current,
71
+ last_accessed_at: Time.current
72
+ )
73
+ end
74
+
75
+ private
76
+
77
+ def metadata_is_hash
78
+ return if wellplates_metadata.nil? || wellplates_metadata.is_a?(Hash)
79
+
80
+ errors.add(:wellplates_metadata, 'must be a hash')
81
+ end
82
+
83
+ def input_metadata_is_hash
84
+ return if input_metadata.nil? || input_metadata.is_a?(Hash)
85
+
86
+ errors.add(:input_metadata, 'must be a hash')
87
+ end
88
+
89
+ def generate_request_id
90
+ self.request_id = "MTT-#{Time.current.strftime('%Y%m%d-%H%M%S')}-#{SecureRandom.hex(3)}"
91
+ end
92
+ end
93
+ end
@@ -41,6 +41,12 @@ module Labimotion
41
41
  has_many :samples, through: :elements_samples, source: :sample
42
42
  has_one :container, :as => :containable
43
43
  has_many :elements_revisions, dependent: :destroy, class_name: 'Labimotion::ElementsRevision'
44
+ has_one :element_variation, dependent: :destroy, class_name: 'Labimotion::ElementVariation', foreign_key: :element_id
45
+
46
+ def variations_count
47
+ rows = element_variation&.variations
48
+ rows.is_a?(Hash) ? rows.size : 0
49
+ end
44
50
 
45
51
  accepts_nested_attributes_for :collections_elements
46
52
 
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labimotion
4
+ class ElementVariation < ApplicationRecord
5
+ self.table_name = :element_variations
6
+
7
+ belongs_to :element, class_name: 'Labimotion::Element'
8
+
9
+ validates :element_id, uniqueness: true
10
+
11
+ def variations_hash
12
+ variations.is_a?(Hash) ? variations : {}
13
+ end
14
+
15
+ def layout_hash
16
+ return {} unless self.class.column_names.include?('layout')
17
+
18
+ layout.is_a?(Hash) ? layout : {}
19
+ end
20
+ end
21
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  ## Labimotion Version
4
4
  module Labimotion
5
- VERSION = '2.2.0.rc8'
5
+ VERSION = '2.2.0.rc10'
6
6
  end
data/lib/labimotion.rb CHANGED
@@ -27,6 +27,10 @@ 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'
31
+ autoload :MttAPI, 'labimotion/apis/mtt_api'
32
+ autoload :DoseRespRequestAPI, 'labimotion/apis/dose_resp_request_api'
33
+ autoload :ElementVariationAPI, 'labimotion/apis/element_variation_api'
30
34
 
31
35
  ######## Entities
32
36
  autoload :PropertiesEntity, 'labimotion/entities/properties_entity'
@@ -49,6 +53,8 @@ module Labimotion
49
53
  autoload :SegmentRevisionEntity, 'labimotion/entities/segment_revision_entity'
50
54
  ## autoload :DatasetRevisionEntity, 'labimotion/entities/dataset_revision_entity'
51
55
  autoload :VocabularyEntity, 'labimotion/entities/vocabulary_entity'
56
+ autoload :UserEntity, 'labimotion/entities/user_entity'
57
+ autoload :ElementVariationEntity, 'labimotion/entities/element_variation_entity'
52
58
 
53
59
  ######## Helpers
54
60
  autoload :GenericHelpers, 'labimotion/helpers/generic_helpers'
@@ -114,11 +120,14 @@ module Labimotion
114
120
  autoload :StdLayersRevision, 'labimotion/models/std_layers_revision'
115
121
 
116
122
  autoload :DeviceDescription, 'labimotion/models/device_description'
123
+ autoload :DoseRespRequest, 'labimotion/models/dose_resp_request'
124
+ autoload :DoseRespOutput, 'labimotion/models/dose_resp_output'
117
125
  autoload :Reaction, 'labimotion/models/reaction'
118
126
  autoload :ResearchPlan, 'labimotion/models/research_plan'
119
127
  autoload :Sample, 'labimotion/models/sample'
120
128
  autoload :Screen, 'labimotion/models/screen'
121
129
  autoload :Wellplate, 'labimotion/models/wellplate'
130
+ autoload :ElementVariation, 'labimotion/models/element_variation'
122
131
 
123
132
  ######## Models/Concerns
124
133
  autoload :GenericKlassRevisions, 'labimotion/models/concerns/generic_klass_revisions'
@@ -126,6 +135,6 @@ module Labimotion
126
135
  autoload :ElementFetchable, 'labimotion/models/concerns/element_fetchable'
127
136
  autoload :Segmentable, 'labimotion/models/concerns/segmentable'
128
137
  autoload :Datasetable, 'labimotion/models/concerns/datasetable'
129
- autoload :AttachmentConverter, 'labimotion/models/concerns/attachment_converter.rb'
138
+ autoload :AttachmentConverter, 'labimotion/models/concerns/attachment_converter'
130
139
  autoload :LinkedProperties, 'labimotion/models/concerns/linked_properties'
131
140
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: labimotion
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0.rc8
4
+ version: 2.2.0.rc10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chia-Lin Lin
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2026-05-05 00:00:00.000000000 Z
12
+ date: 2026-06-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: caxlsx
@@ -53,17 +53,24 @@ executables: []
53
53
  extensions: []
54
54
  extra_rdoc_files: []
55
55
  files:
56
+ - CHANGELOG.md
57
+ - LICENSE
58
+ - README.md
56
59
  - lib/labimotion.rb
57
60
  - lib/labimotion/apis/converter_api.rb
61
+ - lib/labimotion/apis/dose_resp_request_api.rb
62
+ - lib/labimotion/apis/element_variation_api.rb
58
63
  - lib/labimotion/apis/exporter_api.rb
59
64
  - lib/labimotion/apis/generic_dataset_api.rb
60
65
  - lib/labimotion/apis/generic_element_api.rb
61
66
  - lib/labimotion/apis/generic_klass_api.rb
62
67
  - lib/labimotion/apis/labimotion_api.rb
63
68
  - lib/labimotion/apis/labimotion_hub_api.rb
69
+ - lib/labimotion/apis/mtt_api.rb
64
70
  - lib/labimotion/apis/segment_api.rb
65
71
  - lib/labimotion/apis/standard_api.rb
66
72
  - lib/labimotion/apis/standard_layer_api.rb
73
+ - lib/labimotion/apis/user_api.rb
67
74
  - lib/labimotion/apis/vocabulary_api.rb
68
75
  - lib/labimotion/collection/export.rb
69
76
  - lib/labimotion/collection/import.rb
@@ -75,6 +82,7 @@ files:
75
82
  - lib/labimotion/entities/element_entity.rb
76
83
  - lib/labimotion/entities/element_klass_entity.rb
77
84
  - lib/labimotion/entities/element_revision_entity.rb
85
+ - lib/labimotion/entities/element_variation_entity.rb
78
86
  - lib/labimotion/entities/eln_element_entity.rb
79
87
  - lib/labimotion/entities/generic_entity.rb
80
88
  - lib/labimotion/entities/generic_klass_entity.rb
@@ -84,12 +92,14 @@ files:
84
92
  - lib/labimotion/entities/segment_entity.rb
85
93
  - lib/labimotion/entities/segment_klass_entity.rb
86
94
  - lib/labimotion/entities/segment_revision_entity.rb
95
+ - lib/labimotion/entities/user_entity.rb
87
96
  - lib/labimotion/entities/vocabulary_entity.rb
88
97
  - lib/labimotion/helpers/converter_helpers.rb
89
98
  - lib/labimotion/helpers/dataset_helpers.rb
90
99
  - lib/labimotion/helpers/element_helpers.rb
91
100
  - lib/labimotion/helpers/exporter_helpers.rb
92
101
  - lib/labimotion/helpers/generic_helpers.rb
102
+ - lib/labimotion/helpers/mtt_helpers.rb
93
103
  - lib/labimotion/helpers/param_helpers.rb
94
104
  - lib/labimotion/helpers/repository_helpers.rb
95
105
  - lib/labimotion/helpers/sample_association_helpers.rb
@@ -129,9 +139,12 @@ files:
129
139
  - lib/labimotion/models/dataset_klasses_revision.rb
130
140
  - lib/labimotion/models/datasets_revision.rb
131
141
  - lib/labimotion/models/device_description.rb
142
+ - lib/labimotion/models/dose_resp_output.rb
143
+ - lib/labimotion/models/dose_resp_request.rb
132
144
  - lib/labimotion/models/element.rb
133
145
  - lib/labimotion/models/element_klass.rb
134
146
  - lib/labimotion/models/element_klasses_revision.rb
147
+ - lib/labimotion/models/element_variation.rb
135
148
  - lib/labimotion/models/elements_element.rb
136
149
  - lib/labimotion/models/elements_revision.rb
137
150
  - lib/labimotion/models/elements_sample.rb