Class: CaTissue::Specimen

Inherits:
Object
  • Object
show all
Includes:
CaRuby::Resource::Unique, Resource, Storable, Validation
Defined in:
lib/catissue/domain/specimen.rb,
lib/catissue/domain/uniquify.rb

Overview

The Specimen domain class.

Constant Summary

Constants included from Storable

CaTissue::Storable::Position

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Storable

#container, #move_to

Methods included from Resource

#database, included, #tolerant_match?

Methods included from Annotatable

#annotation_proxy, #create_proxy, #method_missing

Constructor Details

#initialize(params = nil) ⇒ Specimen

Returns a new instance of Specimen.



115
116
117
118
119
# File 'lib/catissue/domain/specimen.rb', line 115

def initialize(params=nil)
  super
  # work around caTissue Bug #64
  self.consent_tier_statuses ||= Java::JavaUtil::LinkedHashSet.new
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class CaTissue::Annotatable

Class Method Details

.create_specimen(params) ⇒ Object

Creates a new Specimen or CaTissue::SpecimenRequirement from the given symbol => value params hash.

The default class is inferred from the class parameter, if given, or inherited from this parent specimen otherwise. The inferred class is the camel-case parameter value with Specimen appended, e.g. :tissue => TissueSpecimen. This class name is resolved to a class in the CaTissue module context.

The supported :type parameter value includes the permissible caTissue specimen type String values as well as the shortcut tissue type symbols :fresh, :fixed and :frozen.

If a SpecimenRequirement parameter is provided, then that SpecimenRequirement’s attribute values are merged into the new Specimen after the other parameters are merged. Thus, params takes precedence over the SpecimenRequirement.

If the :count parameter is set to a number greater than one, then the specimen is aliquoted into the specified number of samples.

This method is a convenience method to create either a Specimen or CaTissue::SpecimenRequirement. Although CaTissue::SpecimenRequirement is a direct CaTissue::AbstractSpecimen subclass rather than a Specimen subclass, the create functionality overlaps and Specimen is the friendlier class to define this utility method as opposed to the more obscure CaTissue::AbstractSpecimen.

Parameters:

  • params (<{Symbol => Object}>)

    the create parameter hash. Besides the listed params options, this hash can include additional target Specimen attribute => value entries for any supported Specimen Java property attribute

Options Hash (params):

  • :class (Symbol, String, Class)

    the required target specimen class, e.g. :molecular, TissueSpecimen or CaTissue::TissueSpecimen

  • :type (Symbol, String, Class)

    the optional target specimen type symbol or string, e.g. :frozen or Frozen Tissue Block

  • :quantity (Numeric)

    the optional target specimen intial quantity

  • :requirement (CaTissue::SpecimenRequirement)

    the optional requirement with additional target attribute values

Raises:

  • (ArgumentError)

    if the specimen class option is not recognized



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/catissue/domain/specimen.rb', line 278

def self.create_specimen(params)
  raise ArgumentError.new("Specimen create params argument type unsupported: #{params.class}") unless Hash === params
  # standardize the class, type and quantity params
  spc_cls = params.delete(:class)
  params[:specimen_class] ||= spc_cls if spc_cls
  spc_type = params.delete(:type)
  params[:specimen_type] ||= spc_type if spc_type
  qty = params.delete(:quantity)
  params[:initial_quantity] ||= qty if qty

  # the specimen_class as a Class, Symbol or String
  cls_opt = params[:specimen_class]
  # standardize the specimen_class parameter as a permissible caTissue value
  standardize_class_parameter(params)
  # if the specimen_class was not specified as a Class, then infer the specimen domain class from the
  # parameter prefix and Specimen suffix
  if Class === cls_opt then
    klass = cls_opt
  else
    class_name = params[:specimen_class] + 'Specimen'
    klass = CaTissue.domain_type_with_name(class_name)
    raise ArgumentError.new("Specimen class #{class_name} is not recognized for parameter #{cls_opt}") if klass.nil?
  end
  
  # add a default available quantity to a Specimen but not a SpecimenRequirement
  params[:available_quantity] ||= params[:initial_quantity] if klass <= self

  # make the specimen
  klass.new(params)
end

Instance Method Details

#barcode=(value) ⇒ Object

Sets the barcode to the given value. This method converts an Integer to a String.



23
24
25
26
# File 'lib/catissue/domain/specimen.rb', line 23

def barcode=(value)
  value = value.to_s if Integer === value
  setBarcode(value)
end

#collected?Boolean

Returns whether this Specimen collection status is Collected.

Returns:

  • (Boolean)

    whether this Specimen collection status is Collected



132
133
134
# File 'lib/catissue/domain/specimen.rb', line 132

def collected?
  collection_status == 'Collected'
end

#collection_protocolCaTissue::CollectionProtocol

Convenience method which returns the SCG collection protocol.

Returns:



312
313
314
# File 'lib/catissue/domain/specimen.rb', line 312

def collection_protocol
  specimen_collection_group.collection_protocol
end

caTissue alert - Bug #64: Some domain collection properties not initialized. Initialize consent_tier_statuses if necessary.

Returns:

  • (Java::JavaUtil::Set)

    the statuses



18
19
20
# File 'lib/catissue/domain/specimen.rb', line 18

def consent_tier_statuses
  getConsentTierStatusCollection or (self.consent_tier_statuses = Java::JavaUtil::LinkedHashSet.new)
end

#dispose(reason = nil) ⇒ Object

Permanently dispose of this specimen by creating a CaTissue::DisposalEventParameters with status ‘Closed’ and the optional reason.



346
347
348
# File 'lib/catissue/domain/specimen.rb', line 346

def dispose(reason=nil)
  CaTissue::DisposalEventParameters.new(:specimen => self, :reason => reason)
end

#fetch_saved?Boolean

Relaxes the CaRuby::Persistable#fetch_saved? condition for a Specimen as follows:

  • If the Specimen available_quantity was updated, then fetch the saved Specimen.

Returns:

  • (Boolean)


140
141
142
# File 'lib/catissue/domain/specimen.rb', line 140

def fetch_saved?
  super and available_quantity_changed?
end

#locationObject

Returns this Specimen position Location.

Returns:

  • this Specimen position Location



241
242
243
# File 'lib/catissue/domain/specimen.rb', line 241

def location
  position.location if position
end

#match_in_owner_scope(others) ⇒ Object

Returns the Specimen in others which matches this Specimen in the scope of an owner SCG. This method relaxes CaRuby::Resource#match_in_owner_scope to include a match on at least one external identifier.



218
219
220
221
222
# File 'lib/catissue/domain/specimen.rb', line 218

def match_in_owner_scope(others)
  super or others.detect do |other|
    other.class == self.class and external_identifier_match?(other)
  end
end

#merge_attribute(attribute, newval, matches = nil) ⇒ Object

caTissue alert - remove the autogenerated blank ExternalIdentifier. cf. cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=436&sid=ef98f502fc0ab242781b7759a0eaff36



145
146
147
148
149
150
# File 'lib/catissue/domain/specimen.rb', line 145

def merge_attribute(attribute, newval, matches=nil)
  if attribute == :external_identifiers and newval then
    CaTissue::Specimen.remove_empty_external_identifier(newval)
  end
  super
end

#merge_attributes(other, attributes = nil) ⇒ Object

Override default CaRuby::Resource#merge_attributes to ignore a source SpecimenRequirement parent_specimen.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/catissue/domain/specimen.rb', line 165

def merge_attributes(other, attributes=nil)
  case other
    when SpecimenRequirement then
      # merge with the default requirement merge attributes if necessary
      attributes ||= MERGEABLE_RQMT_ATTRS
      super(other, attributes)
      # copy the requirement characteristics
      sc = other.specimen_characteristics
      self.specimen_characteristics ||= sc.copy(MERGEABLE_SPC_CHR_ATTRS) if sc
    when Hashable then
      # the requirement template
      rqmt = other[:specimen_requirement] || other[:requirement]
      # merge the attribute => value hash
      super
      # merge the SpecimenRequirement after the hash
      merge_attributes(rqmt) if rqmt
    else super
  end
  self
end

#minimal_match?(other) ⇒ Boolean

Augments AbstractSpecimen#minimal_match? with an additional restriction that the other specimen is in pending state. This ensures that a specimen submitted for create matches its auto-generated counterpart but a new specimen can be created even if it matches an existing specimen on the features described in AbstractSpecimen#minimal_match?.

Returns:

  • (Boolean)


231
232
233
# File 'lib/catissue/domain/specimen.rb', line 231

def minimal_match?(other)
  super and other.collection_status == 'Pending'
end

#ownerObject

Overrides Resource#owner to return the parent_specimen, if it exists, or the specimen_collection_group otherwise.



122
123
124
# File 'lib/catissue/domain/specimen.rb', line 122

def owner
  parent_specimen or specimen_collection_group
end

#pending?Boolean

Returns whether this Specimen collection status is Pending.

Returns:

  • (Boolean)

    whether this Specimen collection status is Pending



127
128
129
# File 'lib/catissue/domain/specimen.rb', line 127

def pending?
  collection_status == 'Pending'
end

#position_classObject

Returns the SpecimenPosition class which this Specimen’s Storable can occupy.

Returns:

  • the SpecimenPosition class which this Specimen’s Storable can occupy



236
237
238
# File 'lib/catissue/domain/specimen.rb', line 236

def position_class
  CaTissue::SpecimenPosition
end

#validateObject

Raises a ValidationError when one the following conditions holds:

  • a top-level Specimen does not have a SGC

  • the available_quantity exceeds the initial_quantity

  • the availability flag is set and the available_quantity is zero

caTissue alert - Bug #160: Missing Is Available? validation. Updating Specimen with the availablity flag set and available_quantity zero silently leaves the availablity flag unset.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/catissue/domain/specimen.rb', line 194

def validate
  super
  if parent.nil? and specimen_collection_group.nil? then
    raise ValidationError.new("Top-level specimen #{self} is missing specimen collection group")
  end
  if available_quantity and initial_quantity and available_quantity > initial_quantity then
    raise ValidationError.new("#{self} available quantity #{available_quantity} cannot exceed initial quantity #{initial_quantity}")
  end
  if available? and available_quantity.zero? then
    raise ValidationError.new("#{self} availablility flag cannot be set when the avaialble quantity is zero")
  end
  if collected? then
    unless event_parameters.detect { |ep| CaTissue::CollectionEventParameters === ep } then
      raise ValidationError.new("#{self} is missing CollectionEventParameters")
    end
    unless event_parameters.detect { |ep| CaTissue::ReceivedEventParameters === ep } then
      raise ValidationError.new("#{self} is missing ReceivedEventParameters")
    end
  end
end

Withdraws consent for this Specimen.

Experimental. TODO - test this method.

If a consent_tier is provided, then the SCG CaTissue::ConsentTierStatus with this consent tier is withdrawn. Otherwise, if there is a single SCG CaTissue::ConsentTierStatus, then that consent tier is withdrawn. Otherwise an exception is thrown.

Parameters:

  • optional (CaTissue::ConsentTier, nil)

    consent tier of the SCG CaTissue::ConsentTierStatus to withdraw

Raises:

  • (ValidationError)

    if an unambiguous SCG CaTissue::ConsentTierStatus to withdraw could not be determined



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/catissue/domain/specimen.rb', line 326

def withdraw_consent(consent_tier=nil)
  statuses = specimen_collection_group.consent_tier_statuses
  status = if consent_tier then
    statuses.detect { |cts| cts.consent_tier.identifier == consent_tier.identifier } or
    raise ValidationError.new("SCG #{specimen_collection_group} consent status not found for consent '#{consent_tier.statement}'")
  elsif specimen_collection_group.consent_tier_statuses.size == 1 then
    statuses.first
  elsif specimen_collection_group.consent_tier_statuses.size == 0 then
    raise ValidationError.new("Specimen #{self} SCG does not have a consent tier status")
  else
    raise ValidationError.new("Specimen #{self} SCG consent tier is ambiguous:#{consent_tier_statuses.select { |cts| "\n  #{cts.statement}" }.to_series('or')}")
  end
  ct = status.consent_tier
  cts = consent_tier_statuses.detect { |item| item.consent_tier == ct }
  consent_tier_statuses << cts = ConsentTierStatus.new(:consent_tier => ct) if cts.nil?
  cts.status = 'Withdrawn'
end