Class: Collection

Inherits:
Item
  • Object
show all
Defined in:
app/models/collection.rb

Overview

A subclass of Item that has associated parts via the PartAssociation model. Stripwells, 96 well plates, and gels are examples. Note that you may in some cases be working with an item that is also a Collection, which you can tell by checking that item.collection? In this case you promote the item using the ruby method becomes.

Examples:

Cast an item as a collection

collection = item.becomes Collection

Constant Summary collapse

EMPTY =
-1

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Item

#all_attributes, #annotate, #collection?, #containing_collection, #datum, #datum=, #deleted?, #export, #features, #get_data, #inuse_less_than_quantity, #is_part, items_for, #location, #location=, make, #mark_as_deleted, #move, #move_to, new_object, new_sample, #non_wizard_location?, #num_posts, #object_type, #primitive_location, #put_at, #quantity_nonneg, #sample, #set_data, #set_primitive_location, #store, #to_s, #upgrade, #week, with_sample, with_type

Methods included from DataAssociator

#append_notes, #associate, #associations, #data_associations, #get, #get_association, #lazy_associate, #modify, #notes, #notes=, #upload

Class Method Details

.containing(s, ot = nil) ⇒ ActiveRecord::Relation

Return a list of collections containing the given sample, and optionally of the given object type.

Parameters:

Returns:

  • (ActiveRecord::Relation)


314
315
316
317
318
319
# File 'app/models/collection.rb', line 314

def self.containing(s, ot = nil)
  return [] unless s

  cids = PartAssociation.joins(:part).where('sample_id = ?', to_sample_id(s)).map(&:collection_id)
  Collection.where(id: cids).select { |c| !ot || c.object_type_id == ot.id }
end

.new_collection(ctype) ⇒ Collection

Make an entirely new collection.

Parameters:

  • ctype (String)

    the name of the valid collection object type to make a collection with

Returns:

  • (Collection)

    new empty collection of type name



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'app/models/collection.rb', line 381

def self.new_collection(ctype)

  if ctype.is_a?(String)
    name = ctype
    o = ObjectType.find_by(name: name)
  else
    o = ctype
  end

  raise "Could not find object type named '#{name}'." unless o

  i = Collection.new
  i.object_type_id = o.id
  i.quantity = 1
  i.inuse = 0

  if o
    i.object_type_id = o.id
    wiz = Wizard.find_by(name: o.prefix)
    locator = wiz.next if wiz
    i.set_primitive_location locator.to_s if wiz
  end

  if locator
    ActiveRecord::Base.transaction do
      i.save
      locator.item_id = i.id
      locator.save
      i.locator_id = locator.id
      i.save
      locator.save
    end
  else
    i.location = 'Bench'
    i.save
  end

  i
end

.parts(sample, object_type = nil) ⇒ Array

Get a list of the of the form {row: r, column: c, collection: col}, ... containing the specified sample.

Parameters:

Returns:

  • (Array)


344
345
346
347
348
349
350
# File 'app/models/collection.rb', line 344

def self.parts(sample, object_type = nil)
  plist = []
  Collection.containing(sample, object_type).reject(&:deleted?).each do |c|
    plist << Collection.find(c.id).position_as_hash(sample).merge(collection: c)
  end
  plist
end

.spread(samples, name, options = {}) ⇒ Array<Collection>

Creates as many new collections of type name as will be necessary to hold every sample in the samples list.

Parameters:

  • samples (Array<Sample>)

    list of samples to initiate collections with

  • name (String)

    the name of a valid collection object type that will be created and populated with samples

Returns:

  • (Array<Collection>)

    list of newly created collections of type name that hold all given samples. No more collections will be created than are needed to hold all the samples



362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'app/models/collection.rb', line 362

def self.spread(samples, name, options = {})
  opts = { reverse: false }.merge(options)
  remaining = samples
  collections = []
  while remaining.any?
    c = new_collection name
    old_size = remaining.size
    remaining = c.add_samples(remaining, opts)
    raise "There was an error adding samples #{samples.map { |s| to_sample_id(s) }} to collection of type #{name}" if old_size <= remaining.size

    collections << c
  end
  collections
end

.to_sample(x) ⇒ Object

Changes Item, String, or Sample to a sample

class method?



507
508
509
510
511
512
513
514
515
516
517
518
519
520
# File 'app/models/collection.rb', line 507

def self.to_sample(x)
  return Sample.find(x) if x.is_a?(Integer) && x >= 0

  if x.is_a?(Item)
    raise 'When the third argument to Collection.set is an item, it should be associated with a sample.' unless x.sample

    return x.sample
  end
  return x if x.is_a?(Sample)
  return Sample.find(x.split(':')[0].to_i) if x.is_a?(String)
  return nil if !x || x == -1

  raise "Expecting item, a sample, or a sample id, but got '#{x}' which is a #{x.class}"
end

.to_sample_id(x) ⇒ Object

Changes Item, String, or Sample to a sample.id for storing into a collection matrix.



485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'app/models/collection.rb', line 485

def self.to_sample_id(x)
  return x if x.is_a?(Integer)

  if x.is_a?(Item)
    raise 'When the third argument to Collection.set is an item, it should be associated with a sample.' unless x.sample

    return x.sample.id
  end
  return x.id if x.is_a?(Sample)
  return x.split(':')[0].to_i if x.is_a?(String)
  return EMPTY unless x

  raise "The third argument to Collection.set should be an item, a sample, or a sample id, but it was '#{x}' which is a #{x.class}"
end

Instance Method Details

#add_one(x, options = {}) ⇒ Object

Adds sample, item, or number to collection

Examples:

c = Collection.find_by_id(1)
c.matrix # [[-1, -1, 3], [4, -1, -1]]
c.add_one(777)
c.matrix
  [ [777, -1, 3],
    [4, -1, -1] ]
c.add_one(888)
  [ [777, 888, 3],
    [4, -1, -1] ]
c.add_one(999, reverse: true)
  [ [777, 888, 3],
    [4, -1, 999] ]

Parameters:

  • x (Fixnum, Sample, Item)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :reverse (Bool)

    start from end of matrix



540
541
542
543
544
545
546
547
548
549
550
551
552
553
# File 'app/models/collection.rb', line 540

def add_one(x, options = {})
  opts = { reverse: false }.merge(options)
  r = nil
  c = nil
  if opts[:reverse]
    r, c = get_empty.last
  else
    r, c = get_empty.first
  end
  return nil if r.nil? || c.nil?

  set(r, c, x)
  [r, c, x]
end

#add_samples(samples, options = {}) ⇒ Array<Sample>

Fill collecion with samples.

Parameters:

  • samples (Array<Sample>)

    to put in collection

Returns:

  • (Array<Sample>)

    samples that were not filled



631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
# File 'app/models/collection.rb', line 631

def add_samples(samples, options = {})
  opts = { reverse: false }.merge(options)
  empties = get_empty
  empties.reverse! if opts[:reverse]
  remaining = []
  samples.zip(empties).each do |s, rc|
    if rc.nil?
      remaining << s
    else
      r, c = rc
      set(r, c, s)
    end
  end
  remaining
end

#apportion(r, c) ⇒ Object

Sets the matrix for the collection to an empty rxc matrix and saves the collection to the database, whatever matrix was associated with the collection is lost.

Parameters:

  • r (Integer)

    row

  • c (Integer)

    column



426
427
428
# File 'app/models/collection.rb', line 426

def apportion(r, c)
  ### self.matrix = Array.new(r, Array.new(c, EMPTY))
end

#assign_sample_to_pairs(sample, pairs) ⇒ Collection

Assign samples to the parts at positions specified by pairs

Parameters:

  • sample (Sample)
  • pairs (Array)

    of the form [ [r1,c1], [r2, c2] ... ]

Returns:



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'app/models/collection.rb', line 256

def assign_sample_to_pairs(sample, pairs)

  pm = part_matrix

  pairs.each do |r, c|
    if pm[r][c]
      old_sample_id = pm[r][c].sample_id
      pm[r][c].sample_id = sample.id
      pm[r][c].save
      associate(
        :"Sample Reassigned",
        "The sample at #{r}, #{c} was changed from #{old_sample_id} to #{sample.id}.",
        nil,
        duplicates: true
      )
    else
      set(r, c, sample)
    end
  end

  self

end

#associate_matrix(sample_matrix) ⇒ Object

Sets the matrix associated with the collection to the matrix m where m can be either a matrix of Samples or a matrix of sample ids. Only sample ids are saved to the matrix. Whatever matrix was associated with the collection is lost.

Parameters:

  • sample_matrix (Array<Array<Sample>>, Array<Array<Fixnum>>)


677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
# File 'app/models/collection.rb', line 677

def associate_matrix(sample_matrix)

  dr = sample_matrix.length
  dc = sample_matrix[0].length
  sample_matrix_aux = to_sample_id_matrix sample_matrix

  ActiveRecord::Base.transaction do

    clear
    @matrix_cache = nil

    # create parts
    parts = []
    collection_id_string = SecureRandom.hex # random string used to identify saved parts
    (0...dr).each do |r|
      (0...dc).each do |c|
        parts << Item.new(quantity: 1, inuse: 0, sample_id: sample_matrix_aux[r][c], object_type_id: part_type.id, data: collection_id_string) unless sample_matrix_aux[r][c].nil?
      end
    end
    Item.import parts
    parts = Item.where(data: collection_id_string) # get the parts just made so we have the ids
    parts.each do |p|                              # erase temporary id
      p.data = nil
      p.save
    end
    index = 0

    # create part associations
    pas = []
    (0...dr).each do |r|
      (0...dc).each do |c|
        unless sample_matrix_aux[r][c].nil?
          pas << PartAssociation.new(collection_id: id, part_id: parts[index].id, row: r, column: c)
          index += 1
        end
      end
    end
    PartAssociation.import pas

  end

end

#capacityObject



585
586
587
588
# File 'app/models/collection.rb', line 585

def capacity
  d = dimensions
  d[0] * d[1]
end

#clearObject



720
721
722
# File 'app/models/collection.rb', line 720

def clear
  part_association_list.map(&:destroy)
end

#data_matrix(key) ⇒ Array

Return the matrix of data associations associated with the given key

Parameters:

  • key (String)

Returns:



200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'app/models/collection.rb', line 200

def data_matrix(key)
  pas = part_association_list

  part_ids = pas.collect { |p| p.part_id }.uniq
  das = DataAssociation.associations_for(parent_class: 'Item', parent_id: part_ids, key: key)

  r, c = dimensions
  m = Array.new(r) { Array.new(c) }
  pas.each do |pa|
    m[pa.row][pa.column] = das.find { |da| da.parent_id == pa.part_id }
  end

  m
end

#data_matrix_values(key) ⇒ Array

Return the matrix of data association values associated with the given key

Parameters:

  • key (String)

Returns:



218
219
220
# File 'app/models/collection.rb', line 218

def data_matrix_values(key)
  (data_matrix(key).map { |row| row.map { |da| da ? da.value : nil } })
end

#delete_selection(pairs) ⇒ Collection

Unassign any existing sample associated with the parts at positions specified by pairs

Parameters:

  • pairs (Array)

    of the form [ [r1,c1], [r2, c2] ... ]

Returns:



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'app/models/collection.rb', line 283

def delete_selection(pairs)
  pairs.each do |r, c|
    pas = PartAssociation.includes(:part).where(collection_id: id, row: r, column: c)
    next if pas.empty?

    pas[0].part.mark_as_deleted
    pas[0].destroy
    associate(
      :"Part Deleted",
      "The sample at #{r}, #{c} was deleted. " \
      "It used to be sample #{pas[0].part.sample_id} via deleted part #{pas[0].part.id}.",
      nil,
      duplicates: true
    )
  end

end

#dimensionsArray<Fixnum>

Returns the dimensions of the matrix associated with the collection.

Returns:

  • (Array<Fixnum>)


783
784
785
786
787
788
789
# File 'app/models/collection.rb', line 783

def dimensions
  # Should look up object type dims instead
  dims = [object_type.rows, object_type.columns]
  dims[0] = 12 if dims[0].nil?
  dims[1] = 1 if dims[1].nil?
  dims
end

#drop_data_matrix(key) ⇒ Collection

Remove all part data associations with the matching key

Parameters:

  • key (String)

Returns:



24
25
26
27
28
# File 'app/models/collection.rb', line 24

def drop_data_matrix(key)
  ids = data_matrix(key).flatten.compact.collect { |da| da.id }
  DataAssociation.where(id: ids).destroy_all
  self
end

#each_row_col(matrix, offset: [0, 0]) ⇒ Object

Iterate over all rows and columns of the given matrix, adding the offset

Examples:

Iterate over a sub-matrix

collection.each_row_column([[1,2],[3,4]], offset: [1,1]) do |r,c,x,y|
  # r, c will be the row and column of the matrix argument
  # x, y will be the row and column of the collection's part matrix
}


149
150
151
152
153
154
155
156
157
158
159
# File 'app/models/collection.rb', line 149

def each_row_col(matrix, offset: [0, 0])
  dr, dc = dimensions
  (0...matrix.length).each do |r|
    (0...matrix[r].length).each do |c|
      x = r + offset[0]
      y = c + offset[1]
      yield r, c, x, y if x < dr && y < dc
    end
  end

end

#empty?Bool

Whether the matrix is empty.

Returns:

  • (Bool)


600
# File 'app/models/collection.rb', line 600

delegate :empty?, to: :get_non_empty

#find(val) ⇒ Array<Array<Fixnum>>

Finds parts with sample id corresponding to val.

Parameters:

Returns:

  • (Array<Array<Fixnum>>)

    selected parts in the form [[r1, c1], [r2, c2]]



455
456
457
458
459
460
# File 'app/models/collection.rb', line 455

def find(val)
  PartAssociation
    .joins(:part)
    .where('sample_id = ? AND collection_id = ?', to_sample_id(val), id)
    .collect { |pa| [pa.row, pa.column] }
end

#full?Bool

Whether the matrix has no EMPTY slots.

Returns:

  • (Bool)


593
594
595
# File 'app/models/collection.rb', line 593

def full?
  get_empty.empty?
end

#get_emptyArray<Array<Fixnum>>

Gets all empty rows, cols.

Returns:

  • (Array<Array<Fixnum>>)

    empty parts in the form [[r1, c1], [r2, c2]]



465
466
467
# File 'app/models/collection.rb', line 465

def get_empty
  select { |x| x == EMPTY }
end

#get_matrixObject



729
730
731
# File 'app/models/collection.rb', line 729

def get_matrix
  matrix
end

#get_non_emptyArray<Array<Fixnum>>

Gets all non-empty rows, cols.

Returns:

  • (Array<Array<Fixnum>>)

    non-empty parts in the form [[r1, c1], [r2, c2]]



472
473
474
# File 'app/models/collection.rb', line 472

def get_non_empty
  select { |x| x != EMPTY }
end

#get_part_data(key, r, c) ⇒ Array, String|Float

Retrieve data at the specified row and column for the given key

param c [Fixnum] the column

Parameters:

  • key (String)
  • r (Fixnum)

    the row

Returns:

  • (Array)

    the part matrix, with new data associations inserted if required

  • (String|Float)

    The resulting data



137
138
139
140
# File 'app/models/collection.rb', line 137

def get_part_data(key, r, c)
  pa = part_association(r, c)
  pa.part.get(key) if pa && pa.part
end

#include?(x) ⇒ Boolean

Informs whether the matrix includes x.

Parameters:

Returns:

  • (Boolean)


434
435
436
437
# File 'app/models/collection.rb', line 434

def include?(x)
  sel = find x
  sel.any?
end

#matrixArray<Array<Integer>>

Get matrix of Sample ids.

Returns:

  • (Array<Array<Integer>>)


736
737
738
739
740
741
742
743
744
745
746
747
748
# File 'app/models/collection.rb', line 736

def matrix
  if @matrix_cache
    @matrix_cache
  else
    r, c = dimensions
    m = Array.new(r) { Array.new(c, EMPTY) }
    PartAssociation.includes(:part).where(collection_id: id).each do |pa|
      m[pa.row][pa.column] = pa.part.sample_id if pa.row < r && pa.column < c && pa.part.sample_id
    end
    @matrix_cache = m
    m
  end
end

#matrix=(m) ⇒ Object

Set the matrix associated with the collection to the matrix of Sample ids m, whatever matrix was associated with the collection is lost.



751
752
753
# File 'app/models/collection.rb', line 751

def matrix=(m)
  associate_matrix m
end

#new_data_matrix(key) ⇒ Array

Create or assign zeros to all part data associations for the given key

Parameters:

  • key (String)

Returns:

  • (Array)

    the part matrix, with new data associations inserted if required



94
95
96
97
# File 'app/models/collection.rb', line 94

def new_data_matrix(key)
  r, c = dimensions
  set_data_matrix(key, Array.new(r) { Array.new(c, 0.0) })
end

#next(r, c, options = {}) ⇒ Object

With no options, returns the indices of the next element of the collection, skipping to the next column or row if necessary. With the option skip_non_empty: true, returns the next non empty indices. Returns nil if [r,c] is the last element of the collection

Parameters:

  • r (Integer)

    row

  • c (Integer)

    column

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :skip_non_empty (Bool)

    next non-empty indices



762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
# File 'app/models/collection.rb', line 762

def next(r, c, options = {})

  opts = { skip_non_empty: false }.merge options

  m = matrix
  nr, nc = dimensions

  (r..nr - 1).each do |row|
    (0..nc - 1).each do |col|
      next unless row > r || col > c
      return [row, col] if !opts[:skip_non_empty] || m[row][col] == EMPTY
    end
  end

  [nil, nil]

end

#non_empty_stringString

Returns a string describing the indices of the non empty elements in the collection. For example, the method might return the string "1,1 - 5,9" to indicate that collection contains samples in those indices. Note that the string is adjusted for viewing by the user, so starts with 1 instead of 0 for rows and columns.

Returns:

  • (String)


796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
# File 'app/models/collection.rb', line 796

def non_empty_string

  m = matrix
  max = [0, 0]

  (0..m.length - 1).each do |r|
    (0..m[r].length - 1).each do |c|
      max = [r, c] if m[r][c] != EMPTY
    end
  end

  if m.length > 1
    "1,1 - #{max[0] + 1}, #{max[1] + 1}"
  else
    "1 - #{max[1] + 1}"
  end

end

#num_samplesFixnum

Returns the number of non empty slots in the matrix.

Returns:

  • (Fixnum)


479
480
481
# File 'app/models/collection.rb', line 479

def num_samples
  get_non_empty.size
end

#part(r, c) ⇒ Item

Retrive the part at position r, c

Parameters:

  • r (Fixnum)

    the row

  • c (Fixnum)

    the column

Returns:



226
227
228
229
# File 'app/models/collection.rb', line 226

def part(r, c)
  pas = PartAssociation.includes(:part).where(collection_id: id, row: r, column: c)
  pas[0].part if pas.length == 1
end

#part_association_listObject



15
16
17
18
# File 'app/models/collection.rb', line 15

def part_association_list
  # this works but rails generated part_associations seems not to
  PartAssociation.where(collection_id: id)
end

#part_matrixArray

Retrive a matrix of all parts. If no part is present for a given row and column, that entry will be nil

Returns:

  • (Array)

    an array of arrays of Items -- dimensions match collection's dimensions



233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'app/models/collection.rb', line 233

def part_matrix
  r, c = dimensions
  m = Array.new(r) { Array.new(c) }

  PartAssociation
    .includes(part: [{ sample: [:sample_type] }, :object_type])
    .where(collection_id: id)
    .each do |pa|
      m[pa.row][pa.column] = pa.part
    end

  m
end

#partsArray

Return all parts for this collection

Returns:

  • (Array)


191
192
193
194
195
# File 'app/models/collection.rb', line 191

def parts
  PartAssociation.where(collection_id: id).collect do |association|
    association.part
  end
end

#position(s) ⇒ Object

Returns first Array element from #find

See Also:



329
330
331
# File 'app/models/collection.rb', line 329

def position(s)
  find(s).first
end

#position_as_hash(s) ⇒ Object



333
334
335
336
# File 'app/models/collection.rb', line 333

def position_as_hash(s)
  pos = find to_sample_id(s)
  { row: pos.first[0], column: pos.first[1] }
end

#remove_one(x = nil, options = {}) ⇒ Object

See Also:



556
557
558
# File 'app/models/collection.rb', line 556

def remove_one(x = nil, options = {})
  subtract_one(x, options)
end

#selectArray<Array<Fixnum>>

Finds parts of collection in which block is true.

Returns:

  • (Array<Array<Fixnum>>)

    selected parts in the form [[r1, c1], [r2, c2]]



442
443
444
445
446
447
448
449
# File 'app/models/collection.rb', line 442

def select
  raise 'need selection block' unless block_given?

  matrix.map.with_index do |row, r|
    cols_where = row.each_index.select { |i| Proc.new.call(row[i]) }
    cols_where.map { |c| [r, c] }
  end.select(&:any?).flatten(1)
end

#set(r, c, x) ⇒ Object

Set the [r,c] entry of the matrix to id of the Sample s. If s=nil, then the [r,c] entry is cleared.

Parameters:

  • r (Integer)

    row

  • c (Integer)

    column

  • x (Fixnum, Sample, Item)

    new sample for that row



607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
# File 'app/models/collection.rb', line 607

def set(r, c, x)
  # TODO: Check dimensions
  @matrix_cache = nil
  if x == EMPTY
    pas = PartAssociation.where(collection_id: id, row: r, column: c)
    pas[0].destroy if pas.length == 1
  else
    s = Collection.to_sample(x)
    part = Item.make({ quantity: 1, inuse: 0 }, sample: s, object_type: part_type)
    pas = PartAssociation.where(collection_id: id, row: r, column: c)
    if pas.length == 1
      pa = pas[0]
      pa.part_id = part.id
    else
      pa = PartAssociation.new(collection_id: id, part_id: part.id, row: r, column: c)
    end
    pa.save
  end
end

#set_data_matrix(key, matrix, offset: [0, 0]) ⇒ Array

Create or assign data to parts according to the given key and matrix.

Parameters:

  • key (String)
  • matrix (Array)

    an array of arrays of either numbers or strings whose dimensions are either equal to or small than the collection's dimensions

  • offset (Array) (defaults to: [0, 0])

    the offset used to compute which sub-matrix of parts to which the data should be assigned

Returns:

  • (Array)

    the part matrix, with new data associations inserted if required



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/models/collection.rb', line 36

def set_data_matrix(key, matrix, offset: [0, 0])

  pm = part_matrix
  dm = data_matrix(key)
  parts = []
  pas = []
  das = []

  # REDO
  # 1. make new pas
  # 2. make new parts and bulk save to get ids
  # 3. associate parts with pas
  # 4. bulk save pas
  # 5. make new das
  # 6. bulk save pas

  collection_id_string = SecureRandom.hex # random string used to identify saved parts

  each_row_col(matrix, offset: offset) do |x, y, ox, oy|
    if pm[ox][oy]
      if dm[ox][oy]
        dm[ox][oy].object = { key => matrix[x][y] }.to_json
        das << dm[ox][oy]
      else
        das << pm[ox][oy].lazy_associate(key, matrix[x][y])
      end
    else
      parts << Item.new(quantity: 1, inuse: 0, object_type_id: part_type.id, data: collection_id_string)
    end
  end

  Item.import parts unless parts.empty?
  parts = Item.where(data: collection_id_string) # get the parts just made so we have the ids
  parts.each do |p|                              # erase temporary id
    p.data = nil
    p.save
  end
  index = 0

  each_row_col(matrix, offset: offset) do |x, y, ox, oy|
    next if pm[ox][oy]

    pas << PartAssociation.new(collection_id: id, part_id: parts[index].id, row: ox, column: oy)
    das << parts[index].lazy_associate(key, matrix[x][y])
    index += 1
  end

  PartAssociation.import pas unless pas.empty?
  DataAssociation.import das, on_duplicate_key_update: [:object] unless das.empty?

  pm

end

#set_matrix(m) ⇒ Object



725
726
727
# File 'app/models/collection.rb', line 725

def set_matrix(m)
  associate_matrix m
end

#set_part_data(key, r, c, value) ⇒ Array, Collection

Create or assign data for the given key at the specific row and column

Parameters:

  • key (String)
  • r (Fixnum)

    the row

  • c (Fixnum)

    the column

  • value (Float|Fixnum|String)

Returns:

  • (Array)

    the part matrix, with new data associations inserted if required

  • (Collection)

    the collection, for chaining



116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'app/models/collection.rb', line 116

def set_part_data(key, r, c, value)

  pm = part_association(r, c)
  if pm
    pm.part.associate key, value
  else
    pa = initialize_part(r, c)
    pa.part.associate key, value
  end

  self

end

#subtract_one(x = nil, options = {}) ⇒ Object

Find last [r,c] that equals x and sets to EMPTY. If x.nil? then it finds the last non_empty slot. If reverse: false then finds the first [r,c] equal to x. Returns [r,c,sample_at_rc] if x is in collection, or nil if x is not found or the collection is empty.

Parameters:

  • x (Fixnum, Sample, Item) (defaults to: nil)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :reverse (Bool)

    begin from the end of the matrix



567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
# File 'app/models/collection.rb', line 567

def subtract_one(x = nil, options = {})
  opts = { reverse: true }.merge(options)
  r = nil
  c = nil
  sel = get_non_empty
  sel = find x unless x.nil?
  return nil if sel.empty?

  if opts[:reverse]
    r, c = sel.last
  else
    r, c = sel.first
  end
  s = matrix[r][c]
  set(r, c, EMPTY)
  [r, c, s]
end

#to_sample_id(x) ⇒ Object



500
501
502
# File 'app/models/collection.rb', line 500

def to_sample_id(x)
  Collection.to_sample_id(x)
end

#to_sample_id_matrix(sample_matrix) ⇒ Object

Takes a matrix of sample ids, samples or items and returns a matrix of only sample ids



648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
# File 'app/models/collection.rb', line 648

def to_sample_id_matrix(sample_matrix)

  dr = sample_matrix.length
  dc = sample_matrix[0].length

  sample_matrix_aux = Array.new(dr) { Array.new(dc) }

  # convert sample matrix into ids
  (0...dr).each do |r|
    (0...dc).each do |c|
      sample_matrix_aux[r][c] = if sample_matrix[r][c].is_a?(Sample)
                                  sample_matrix[r][c].id
                                elsif sample_matrix[r][c].is_a?(Item)
                                  Item.sample_id
                                elsif sample_matrix[r][c].is_a?(Integer) && sample_matrix[r][c] > 0
                                  sample_matrix[r][c]
                                end
    end
  end

  sample_matrix_aux

end