caruby-tissue 1.2.1
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.
- data/History.txt +4 -0
- data/LEGAL +5 -0
- data/LICENSE +22 -0
- data/README.md +44 -0
- data/bin/crtdump +31 -0
- data/bin/crtexample +18 -0
- data/bin/crtextract +47 -0
- data/bin/crtmigrate +17 -0
- data/bin/crtsmoke +27 -0
- data/examples/galena/README.md +53 -0
- data/examples/galena/bin/migrate.rb +42 -0
- data/examples/galena/bin/seed.rb +43 -0
- data/examples/galena/conf/extract/simple_fields.yaml +4 -0
- data/examples/galena/conf/migration/filter_fields.yaml +7 -0
- data/examples/galena/conf/migration/filter_migration.yaml +9 -0
- data/examples/galena/conf/migration/frozen_fields.yaml +11 -0
- data/examples/galena/conf/migration/frozen_migration.yaml +9 -0
- data/examples/galena/conf/migration/general_fields.yaml +42 -0
- data/examples/galena/conf/migration/general_migration.yaml +9 -0
- data/examples/galena/conf/migration/simple_fields.yaml +30 -0
- data/examples/galena/conf/migration/simple_migration.yaml +7 -0
- data/examples/galena/conf/migration/small_fields.yaml +24 -0
- data/examples/galena/conf/migration/small_migration.yaml +9 -0
- data/examples/galena/data/filter.csv +1 -0
- data/examples/galena/data/frozen.csv +1 -0
- data/examples/galena/data/general.csv +1 -0
- data/examples/galena/data/minimal.csv +1 -0
- data/examples/galena/data/simple.csv +1 -0
- data/examples/galena/data/small.csv +1 -0
- data/examples/galena/doc/CaTissue.html +93 -0
- data/examples/galena/doc/CaTissue/CollectionProtocolRegistration.html +181 -0
- data/examples/galena/doc/CaTissue/Participant.html +241 -0
- data/examples/galena/doc/CaTissue/SpecimenCollectionGroup.html +190 -0
- data/examples/galena/doc/CaTissue/StorageContainer.html +179 -0
- data/examples/galena/doc/CaTissue/TissueSpecimen.html +320 -0
- data/examples/galena/doc/Galena.html +290 -0
- data/examples/galena/doc/Galena/Seed.html +203 -0
- data/examples/galena/doc/Galena/Seed/Defaults.html +646 -0
- data/examples/galena/doc/_index.html +188 -0
- data/examples/galena/doc/class_list.html +36 -0
- data/examples/galena/doc/css/common.css +1 -0
- data/examples/galena/doc/css/full_list.css +53 -0
- data/examples/galena/doc/css/style.css +307 -0
- data/examples/galena/doc/file.README.html +108 -0
- data/examples/galena/doc/file_list.html +38 -0
- data/examples/galena/doc/frames.html +13 -0
- data/examples/galena/doc/index.html +108 -0
- data/examples/galena/doc/js/app.js +202 -0
- data/examples/galena/doc/js/full_list.js +149 -0
- data/examples/galena/doc/js/jquery.js +154 -0
- data/examples/galena/doc/method_list.html +179 -0
- data/examples/galena/doc/top-level-namespace.html +112 -0
- data/examples/galena/lib/README.html +33 -0
- data/examples/galena/lib/galena.rb +8 -0
- data/examples/galena/lib/galena/cli/seed.rb +43 -0
- data/examples/galena/lib/galena/migration/filter_shims.rb +43 -0
- data/examples/galena/lib/galena/migration/frozen_shims.rb +54 -0
- data/examples/galena/lib/galena/seed/defaults.rb +97 -0
- data/lib/catissue.rb +26 -0
- data/lib/catissue/cli/command.rb +51 -0
- data/lib/catissue/cli/example.rb +31 -0
- data/lib/catissue/cli/migrate.rb +60 -0
- data/lib/catissue/cli/smoke.rb +45 -0
- data/lib/catissue/database.rb +451 -0
- data/lib/catissue/database/annotation/annotatable_service.rb +25 -0
- data/lib/catissue/database/annotation/annotation_service.rb +79 -0
- data/lib/catissue/database/annotation/annotator.rb +84 -0
- data/lib/catissue/database/annotation/entity_manager.rb +10 -0
- data/lib/catissue/database/annotation/integration_service.rb +87 -0
- data/lib/catissue/database/controlled_value_finder.rb +43 -0
- data/lib/catissue/database/controlled_values.rb +162 -0
- data/lib/catissue/domain/abstract_domain_object.rb +8 -0
- data/lib/catissue/domain/abstract_position.rb +22 -0
- data/lib/catissue/domain/abstract_specimen.rb +288 -0
- data/lib/catissue/domain/abstract_specimen_collection_group.rb +25 -0
- data/lib/catissue/domain/address.rb +13 -0
- data/lib/catissue/domain/cancer_research_group.rb +11 -0
- data/lib/catissue/domain/capacity.rb +34 -0
- data/lib/catissue/domain/check_in_check_out_event_parameter.rb +19 -0
- data/lib/catissue/domain/collection_event_parameters.rb +13 -0
- data/lib/catissue/domain/collection_protocol.rb +177 -0
- data/lib/catissue/domain/collection_protocol_event.rb +108 -0
- data/lib/catissue/domain/collection_protocol_registration.rb +108 -0
- data/lib/catissue/domain/consent_tier_response.rb +13 -0
- data/lib/catissue/domain/consent_tier_status.rb +29 -0
- data/lib/catissue/domain/container.rb +234 -0
- data/lib/catissue/domain/container_position.rb +21 -0
- data/lib/catissue/domain/container_type.rb +131 -0
- data/lib/catissue/domain/department.rb +13 -0
- data/lib/catissue/domain/disposal_event_parameters.rb +13 -0
- data/lib/catissue/domain/embedded_event_parameters.rb +10 -0
- data/lib/catissue/domain/external_identifier.rb +22 -0
- data/lib/catissue/domain/frozen_event_parameters.rb +10 -0
- data/lib/catissue/domain/institution.rb +13 -0
- data/lib/catissue/domain/new_specimen_array_order_item.rb +35 -0
- data/lib/catissue/domain/order_details.rb +25 -0
- data/lib/catissue/domain/participant.rb +138 -0
- data/lib/catissue/domain/participant_medical_identifier.rb +38 -0
- data/lib/catissue/domain/password.rb +11 -0
- data/lib/catissue/domain/race.rb +11 -0
- data/lib/catissue/domain/received_event_parameters.rb +25 -0
- data/lib/catissue/domain/scg_event_parameters.rb +11 -0
- data/lib/catissue/domain/site.rb +30 -0
- data/lib/catissue/domain/specimen.rb +456 -0
- data/lib/catissue/domain/specimen_array.rb +47 -0
- data/lib/catissue/domain/specimen_array_content.rb +19 -0
- data/lib/catissue/domain/specimen_array_type.rb +20 -0
- data/lib/catissue/domain/specimen_characteristics.rb +20 -0
- data/lib/catissue/domain/specimen_collection_group.rb +412 -0
- data/lib/catissue/domain/specimen_event_parameters.rb +111 -0
- data/lib/catissue/domain/specimen_position.rb +38 -0
- data/lib/catissue/domain/specimen_protocol.rb +34 -0
- data/lib/catissue/domain/specimen_requirement.rb +143 -0
- data/lib/catissue/domain/storage_container.rb +204 -0
- data/lib/catissue/domain/storage_type.rb +82 -0
- data/lib/catissue/domain/transfer_event_parameters.rb +53 -0
- data/lib/catissue/domain/user.rb +100 -0
- data/lib/catissue/extract/command.rb +31 -0
- data/lib/catissue/extract/delta.rb +62 -0
- data/lib/catissue/extract/extractor.rb +99 -0
- data/lib/catissue/migration/migrator.rb +101 -0
- data/lib/catissue/migration/shims.rb +108 -0
- data/lib/catissue/migration/uniquify.rb +111 -0
- data/lib/catissue/resource.rb +84 -0
- data/lib/catissue/util/controlled_value.rb +29 -0
- data/lib/catissue/util/location.rb +116 -0
- data/lib/catissue/util/log.rb +30 -0
- data/lib/catissue/util/person.rb +31 -0
- data/lib/catissue/util/position.rb +54 -0
- data/lib/catissue/util/storable.rb +34 -0
- data/lib/catissue/util/storage_type_holder.rb +30 -0
- data/lib/catissue/version.rb +7 -0
- metadata +212 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'caruby/util/validation'
|
2
|
+
require 'caruby/util/inflector'
|
3
|
+
|
4
|
+
module CaTissue
|
5
|
+
# import the Java class
|
6
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenEventParameters')
|
7
|
+
|
8
|
+
class SpecimenEventParameters
|
9
|
+
include Resource
|
10
|
+
|
11
|
+
# date is a synonym for the more accurately titled timestamp attribute.
|
12
|
+
add_attribute_aliases(:date => :timestamp)
|
13
|
+
|
14
|
+
add_mandatory_attributes(:timestamp, :user)
|
15
|
+
|
16
|
+
# specimen is abstract but unfetched.
|
17
|
+
qualify_attribute(:specimen, :unfetched)
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def self.allocate
|
22
|
+
raise NotImplementedError.new("SpecimenEventParameters is abstract; use the create method to make a new instance")
|
23
|
+
end
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
# Creates a SpecimenEventParameters of the specified subclass type. The type is a
|
28
|
+
# SpecimenEventParameters subclass name without the +EventParameters+ suffix, e.g.
|
29
|
+
# +Collection+. Lower-case, underscore symbols are supported and preferred, e.g. the
|
30
|
+
# :collection type creates a CollectionEventParameters.
|
31
|
+
#
|
32
|
+
# The required scg_or_specimen argument is either a SpecimenCollectionGroup or
|
33
|
+
# a Specimen.
|
34
|
+
#
|
35
|
+
# The optional params argument are attribute => value associations, e.g.
|
36
|
+
# SpecimenEventParameters.create_parameters(:collection, scg, :user => collector, :timestamp => DateTime.now)
|
37
|
+
def self.create_parameters(type, scg_or_specimen, params=Hash::EMPTY_HASH)
|
38
|
+
# make the class name by joining the camel-cased type prefix to the subclass suffix.
|
39
|
+
# classify converts a lower_case, underscore type to a valid class name, e.g. :check_in_check_out
|
40
|
+
# becomes CheckInCheckOut.
|
41
|
+
class_name = type.to_s.classify + SUBCLASS_SUFFIX
|
42
|
+
begin
|
43
|
+
klass = CaTissue.const_get(class_name.to_sym)
|
44
|
+
rescue
|
45
|
+
raise ArgumentError.new("Unsupported event parameters type: #{type}; #{class_name} must be a subtype of #{self}")
|
46
|
+
end
|
47
|
+
event_params = klass.new(params)
|
48
|
+
case scg_or_specimen
|
49
|
+
when SpecimenCollectionGroup then
|
50
|
+
event_params.specimen_collection_group = scg_or_specimen
|
51
|
+
when Specimen then
|
52
|
+
event_params.specimen = scg_or_specimen
|
53
|
+
when nil then
|
54
|
+
raise ArgumentError.new("Missing SpecimenEventParameters scg_or_specimen factory argument")
|
55
|
+
else
|
56
|
+
raise ArgumentError.new("Unsupported SpecimenEventParameters factory argument - expected SpecimenCollectionGroup or Specimen, found #{scg_or_specimen.class}")
|
57
|
+
end
|
58
|
+
event_params
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the Specimen or SpecimenCollectionGroup to which this event is attached.
|
62
|
+
def subject
|
63
|
+
specimen.nil? ? specimen_collection_group : specimen
|
64
|
+
end
|
65
|
+
|
66
|
+
# Sets the scg_or_specimen subject to which this event is attached.
|
67
|
+
def subject=(scg_or_specimen)
|
68
|
+
spc_subject = scg_or_specimen if Specimen === scg_or_specimen
|
69
|
+
scg_subject = scg_or_specimen if SpecimenCollectionGroup === scg_or_specimen
|
70
|
+
specimen = spc_subject
|
71
|
+
specimen_collection_group = scg_subject
|
72
|
+
scg_or_specimen
|
73
|
+
end
|
74
|
+
|
75
|
+
def collection_protocol
|
76
|
+
specimen_collection_group.collection_protocol if specimen_collection_group
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate
|
80
|
+
super
|
81
|
+
if subject.nil? then
|
82
|
+
raise ValidationError.new("Both specimen_collection_group and specimen are missing in SpecimenEventParameters #{self}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
SUBCLASS_SUFFIX = 'EventParameters'
|
89
|
+
|
90
|
+
# Sets each missing value to a default as follows:
|
91
|
+
# * default user is the SCG receiver
|
92
|
+
# * default timestamp is now
|
93
|
+
def add_defaults_local
|
94
|
+
super
|
95
|
+
self.timestamp ||= Java.now
|
96
|
+
self.user ||= default_user
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns whether the given value is either nil, empty or equals other.
|
100
|
+
def missing_or_match?(attribute, other)
|
101
|
+
value = send(attr)
|
102
|
+
value.nil_or_empty? or value == other.send(attr)
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_user
|
106
|
+
scg = specimen_collection_group
|
107
|
+
scg ||= specimen.specimen_collection_group if specimen
|
108
|
+
scg.receiver if scg
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenPosition')
|
6
|
+
|
7
|
+
class SpecimenPosition
|
8
|
+
include Resource
|
9
|
+
|
10
|
+
add_mandatory_attributes(:storage_container)
|
11
|
+
|
12
|
+
add_attribute_aliases(:holder => :storage_container, :container => :storage_container, :occupant => :specimen)
|
13
|
+
|
14
|
+
# Each SpecimenPosition has a specimen and there is only one position per specimen.
|
15
|
+
set_secondary_key_attributes(:specimen)
|
16
|
+
|
17
|
+
set_attribute_inverse(:storage_container, :specimen_positions)
|
18
|
+
|
19
|
+
set_attribute_inverse(:specimen, :specimen_position)
|
20
|
+
|
21
|
+
qualify_attribute(:storage_container, :fetched)
|
22
|
+
|
23
|
+
# Returns a TransferEventParameters which serves as a proxy for saving this SpecimenPosition.
|
24
|
+
#
|
25
|
+
# caTissue alert - caTissue does not allow saving a SpecimenPosition directly in the database.
|
26
|
+
# Creating a TransferEventParameters sets the SpecimenPosition as a side-effect. Therefore,
|
27
|
+
# SpecimenPosition save is accomplished by creating a proxy TransferEventParameters instead.
|
28
|
+
def saver_proxy
|
29
|
+
xfr = CaTissue::TransferEventParameters.new(:specimen => specimen, :to => location)
|
30
|
+
if snapshot and changed? then
|
31
|
+
xfr.from_storage_container = snapshot[:storage_container]
|
32
|
+
xfr.from_position_dimension_one = snapshot[:position_dimension_one]
|
33
|
+
xfr.from_position_dimension_two = snapshot[:position_dimension_two]
|
34
|
+
end
|
35
|
+
xfr
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenProtocol')
|
6
|
+
|
7
|
+
# The SpecimenProtocol domain class.
|
8
|
+
class SpecimenProtocol
|
9
|
+
include Resource
|
10
|
+
|
11
|
+
set_secondary_key_attributes(:short_title)
|
12
|
+
|
13
|
+
# caTissue alert - Bug #155: enrollment is incorrectly defined in SpecimenProtocol rather
|
14
|
+
# than CollectionProtocol. It is defaulted here until this defect is fixed.
|
15
|
+
add_attribute_defaults(:activity_status => 'Active', :enrollment => 0)
|
16
|
+
|
17
|
+
add_mandatory_attributes(:principal_investigator, :activity_status, :start_date, :title)
|
18
|
+
|
19
|
+
# caTissue alert - Augment the standard metadata storable reference attributes to work around caTissue Bug #150:
|
20
|
+
# Create CollectionProtocol in API ignores startDate.
|
21
|
+
qualify_attribute(:start_date, :update_only)
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Sets the defaults if necessary. The start date is set to now. The title is
|
26
|
+
# set to the short title.
|
27
|
+
def add_defaults_local
|
28
|
+
super
|
29
|
+
self.title ||= short_title
|
30
|
+
self.short_title ||= title
|
31
|
+
self.start_date ||= Java::JavaUtil::Date.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module CaTissue
|
2
|
+
# import the Java class
|
3
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenRequirement')
|
4
|
+
|
5
|
+
# The SpecimenRequirement domain class.
|
6
|
+
class SpecimenRequirement
|
7
|
+
include Resource
|
8
|
+
|
9
|
+
# caTissue alert - Bug #64: Some domain collection properties not initialized.
|
10
|
+
# Initialize specimens if necessary.
|
11
|
+
#
|
12
|
+
# @return [Java::JavaUtil::Set] the specimens
|
13
|
+
def specimens
|
14
|
+
getSpecimenCollection or (self.specimens = Java::JavaUtil::LinkedHashSet.new)
|
15
|
+
end
|
16
|
+
|
17
|
+
add_attribute_aliases(:collection_event => :collection_protocol_event)
|
18
|
+
|
19
|
+
add_attribute_defaults(:initial_quantity => 0.0, :pathological_status => 'Not Specified', :specimen_type => 'Not Specified', :storage_type => 'Not Specified')
|
20
|
+
|
21
|
+
add_mandatory_attributes(:collection_protocol_event, :storage_type)
|
22
|
+
|
23
|
+
# SpecimenRequirement children are constrained to SpecimenRequirement.
|
24
|
+
set_attribute_type(:child_specimens, SpecimenRequirement)
|
25
|
+
|
26
|
+
# SpecimenRequirement parent is constrained to SpecimenRequirement.
|
27
|
+
set_attribute_type(:parent_specimen, SpecimenRequirement)
|
28
|
+
|
29
|
+
# As with Specimen, even though the inverse is declared in AbstractSpecimen, do so
|
30
|
+
# again here to ensure that there is no order dependency of the dependent declaration
|
31
|
+
# below on AbstractSpecimen metadata initialization.
|
32
|
+
set_attribute_inverse(:parent_specimen, :child_specimens)
|
33
|
+
|
34
|
+
# Unlike Specimen, a child SpecimenRequirement is not cascaded by caTissue.
|
35
|
+
# It is not auto-generated, i.e. it is not created from a template when the
|
36
|
+
# parent CPE is created.
|
37
|
+
add_dependent_attribute(:child_specimens, :logical)
|
38
|
+
|
39
|
+
# Overrides {Resource#owner} to return the parent_specimen, if it exists, or the collection_protocol_event otherwise.
|
40
|
+
def owner
|
41
|
+
parent_specimen or collection_protocol_event
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the SpecimenRequirement in others which matches this SpecimenRequirement in the scope of an owner CollectionProtocolEvent.
|
45
|
+
# This method relaxes {CaRuby::Resource#match_in_owner_scope} for a SpecimenRequirement that matches any SpecimenRequirement
|
46
|
+
# in others with the same class, specimen type, pathological_status and characteristics.
|
47
|
+
def match_in_owner_scope(others)
|
48
|
+
others.detect do |other|
|
49
|
+
self.class === other and specimen_type == other.specimen_type and pathological_status == other.pathological_status and
|
50
|
+
characteristics and characteristics.match?(other.characteristics)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Returns whether this SpecimenRequirement characteristics matches the other SpecimenRequirement characteristics
|
57
|
+
# on the tissue site and tissue side.
|
58
|
+
def match_characteristics(other)
|
59
|
+
chr = characteristics
|
60
|
+
ochr = other.characteristics
|
61
|
+
chr and ochr and chr.tissue_side == ochr.tissue_side and chr.tissue_site == ochr.tissue_site
|
62
|
+
end
|
63
|
+
|
64
|
+
# Raises NotImplementedError, since SpecimenRequirement is abstract.
|
65
|
+
def self.allocate
|
66
|
+
raise NotImplementedError.new("SpecimenRequirement is abstract; use the create method to make a new instance")
|
67
|
+
end
|
68
|
+
|
69
|
+
public
|
70
|
+
|
71
|
+
def initialize(params=nil)
|
72
|
+
super
|
73
|
+
respond_to?(:specimens)
|
74
|
+
# work around caTissue Bug #64
|
75
|
+
self.specimens ||= Java::JavaUtil::LinkedHashSet.new
|
76
|
+
end
|
77
|
+
|
78
|
+
# Augments {CaRuby::Resource#validate} to verify that this SpecimenRequirement does not have multiple non-aliquot
|
79
|
+
# derivatives, which is disallowed by caTissue.
|
80
|
+
#
|
81
|
+
# caTissue alert - multiple SpecimenRequirement non-aliquot derivatives is accepted by caTissue but results
|
82
|
+
# in obscure downstream errors (cf. Bug #151).
|
83
|
+
#
|
84
|
+
# Raises ValidationError if this SpecimenRequirement has multiple non-aliquot derivatives.
|
85
|
+
def validate
|
86
|
+
super
|
87
|
+
if multiple_derivatives? then raise ValidationError.new("Multiple derivatives not supported by caTissue") end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Creates a SpecimenRequirement of the given subclass type for the given CollectionProtocolEvent event.
|
91
|
+
# The type is a SpecimenRequirement subclass name without the +SpecimenRequirement+ suffix, e.g.
|
92
|
+
# +Tissue+. Lower-case, underscore symbols are supported and preferred, e.g. the
|
93
|
+
# :tissue type creates a TissueSpecimenRequirement.
|
94
|
+
#
|
95
|
+
# The optional params argument are attribute => value associations, e.g.
|
96
|
+
# SpecimenRequirement.create_requirement(:tissue, event, :specimen_type => 'RNA')
|
97
|
+
def self.create_requirement(type, event, params=Hash::EMPTY_HASH)
|
98
|
+
# make the class name by joining the camel-cased type prefix to the subclass suffix
|
99
|
+
class_name = type.to_s.classify + self.qp
|
100
|
+
begin
|
101
|
+
klass = CaTissue.const_get(class_name)
|
102
|
+
rescue
|
103
|
+
raise ArgumentError.new("Unsupported requirement type: #{type}; #{class_name} must be a subtype of #{self}")
|
104
|
+
end
|
105
|
+
klass.new(params.merge(:collection_protocol_event => event))
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the {#collection_event} protocol, if any.
|
109
|
+
def collection_protocol
|
110
|
+
collection_event.protocol if collection_event
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
# Returns the required attributes which are nil for this domain object.
|
116
|
+
# Overrides the CaRuby::Resource method to handle caTissue Bug #67 - SpecimenRequirement activityStatus cannot be set.
|
117
|
+
def missing_mandatory_attributes
|
118
|
+
invalid = super
|
119
|
+
# caTissue alert - Special case: work around caTissue Bug #67
|
120
|
+
if invalid.include?(:activity_status) then
|
121
|
+
invalid.delete(:activity_status)
|
122
|
+
end
|
123
|
+
# end of workaround
|
124
|
+
invalid
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Returns whether this SpecimenRequirement has multiple non-aliquot derivatives.
|
130
|
+
def multiple_derivatives?
|
131
|
+
children.size > 1 and children.any? { |drv| not drv.aliquot? }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Adds the following default values, if necessary:
|
135
|
+
# * a generic SpecimenCharacteristics
|
136
|
+
# * the parent collection_event, if this SpecimenRequirement is derived
|
137
|
+
def add_defaults_local
|
138
|
+
super
|
139
|
+
self.collection_event ||= parent.collection_event if parent
|
140
|
+
self.specimen_characteristics ||= CaTissue::SpecimenCharacteristics.new
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
require 'caruby/util/partial_order'
|
3
|
+
require 'catissue/util/storage_type_holder'
|
4
|
+
|
5
|
+
module CaTissue
|
6
|
+
# import the Java class
|
7
|
+
java_import('edu.wustl.catissuecore.domain.StorageContainer')
|
8
|
+
|
9
|
+
# The +caTissue+ +StorageContainer+ domain class wrapper.
|
10
|
+
class StorageContainer
|
11
|
+
include StorageTypeHolder, Resource
|
12
|
+
|
13
|
+
# caTissue alert - Bug #64: Some domain collection properties not initialized.
|
14
|
+
# Initialize specimen_positions if necessary.
|
15
|
+
#
|
16
|
+
# @return [Java::JavaUtil::Set] the positions
|
17
|
+
def specimen_positions
|
18
|
+
getSpecimenPositionCollection or (self.specimen_positions = Java::JavaUtil::LinkedHashSet.new)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sets the storage type to the given value. Each empty holds collection is initialized
|
22
|
+
# from the corresponding StorageType holds collection.
|
23
|
+
def storage_type=(value)
|
24
|
+
if value and holds_storage_types and holds_storage_types.empty? then
|
25
|
+
holds_storage_types.merge!(value.holds_storage_types)
|
26
|
+
end
|
27
|
+
if value and holds_specimen_array_types and holds_specimen_array_types.empty? then
|
28
|
+
holds_specimen_array_types.merge!(value.holds_specimen_array_types)
|
29
|
+
end
|
30
|
+
if value and holds_specimen_classes and holds_specimen_classes.empty? then
|
31
|
+
holds_specimen_classes.merge!(value.holds_specimen_classes)
|
32
|
+
end
|
33
|
+
setStorageType(value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def located_at_position=(value)
|
37
|
+
setLocatedAtPosition(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
add_attribute_aliases(:container_type => :storage_type)
|
41
|
+
|
42
|
+
# Aternative to the inherited secondary key +name+.
|
43
|
+
set_alternate_key_attributes(:site, :barcode)
|
44
|
+
|
45
|
+
add_mandatory_attributes(:site, :storage_type)
|
46
|
+
|
47
|
+
qualify_attribute(:collection_protocols, :fetched)
|
48
|
+
|
49
|
+
set_attribute_type(:holds_specimen_array_types, CaTissue::SpecimenArrayType)
|
50
|
+
|
51
|
+
set_attribute_type(:holds_specimen_classes, String)
|
52
|
+
|
53
|
+
set_attribute_type(:holds_storage_types, CaTissue::StorageType)
|
54
|
+
|
55
|
+
def initialize(params=nil)
|
56
|
+
super(params)
|
57
|
+
# JRuby alert - specimen_positions is sometimes unrecognized unless primed with respond_to? call
|
58
|
+
respond_to?(:specimen_positions)
|
59
|
+
# work around caTissue Bug #64
|
60
|
+
self.specimen_positions ||= Java::JavaUtil::LinkedHashSet.new
|
61
|
+
end
|
62
|
+
|
63
|
+
# Corrects the +caTissue+ +occupied_positions+ method to include +specimen_positions+.
|
64
|
+
def all_occupied_positions
|
65
|
+
subcontainer_positions.union(specimen_positions)
|
66
|
+
end
|
67
|
+
|
68
|
+
alias :add_local :add
|
69
|
+
|
70
|
+
# Moves the given Storable from its current Position, if any, to this Container at the optional
|
71
|
+
# coordinate. The default coordinate is the first available slot within this Container.
|
72
|
+
# The storable Storable position is updated to reflect the new location. Returns self.
|
73
|
+
#
|
74
|
+
# If there is no coordinate and this container cannot hold the storable type, then the
|
75
|
+
# storable is added to a subcontainer which can hold the storable type.
|
76
|
+
#
|
77
|
+
# @param [Storable] the item to add
|
78
|
+
# @param [Coordinate] the storage location (default is first available location)
|
79
|
+
# @return [StorageContainer] self
|
80
|
+
# @raise [IndexError] if this Container is full
|
81
|
+
# @raise [IndexError] if the row and column are given but exceed the Container bounds
|
82
|
+
def add(storable, coordinate=nil)
|
83
|
+
return add_local(storable, coordinate) if coordinate
|
84
|
+
add_to_existing_container(storable) or add_to_new_subcontainer(storable) or out_of_bounds(storable)
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
alias :<< :add
|
89
|
+
|
90
|
+
# Finds the container with the given name, or creates a new container
|
91
|
+
# of the given type if necessary.
|
92
|
+
#
|
93
|
+
# @param [String] the container search name
|
94
|
+
# @param [CaTissue::StorageContainer] the container type
|
95
|
+
# @return a container with the given name
|
96
|
+
def find_subcontainer(name, type)
|
97
|
+
logger.debug { "Finding box with name #{name}..." }
|
98
|
+
ctr = CaTissue::StorageContainer.new(:name => name)
|
99
|
+
if ctr.find then
|
100
|
+
logger.debug { "Container found: #{ctr}." }
|
101
|
+
else
|
102
|
+
logger.debug { "Container not found: #{name}." }
|
103
|
+
create_subcontainer(name, type)
|
104
|
+
end
|
105
|
+
box
|
106
|
+
end
|
107
|
+
|
108
|
+
# @return a new Container with the given name and type in this Container
|
109
|
+
def create_subcontainer(name, type)
|
110
|
+
logger.debug { "Creating #{qp} subcontainer of type #{type} with name #{name}..." }
|
111
|
+
ctr = type.create_container(:name => name, :site => site)
|
112
|
+
self << ctr
|
113
|
+
ctr.create
|
114
|
+
logger.debug { "Made #{self} subcontainer #{ctr}." }
|
115
|
+
ctr
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
# Returns the the content collection to which the storable is added, specimen_positions
|
121
|
+
# if storable is a Specimen, container_positions otherwise.
|
122
|
+
def content_collection_for(storable)
|
123
|
+
CaTissue::Specimen === storable ? specimen_positions : super
|
124
|
+
end
|
125
|
+
|
126
|
+
# Adds the given storable to a container within this StorageContainer's hierarchy.
|
127
|
+
#
|
128
|
+
# @param @storable (see #add)
|
129
|
+
# @return [StorageContainer, nil] self if added, nil otherwise
|
130
|
+
def add_to_existing_container(storable)
|
131
|
+
# the subcontainers in column, row sort order
|
132
|
+
scs = subcontainers.sort { |sc1, sc2| sc1.position.location <=> sc2.position.location }
|
133
|
+
# the first subcontainer that can hold the storable is preferred
|
134
|
+
if scs.detect { |ctr| ctr.add_to_existing_container(storable) if StorageContainer === ctr } then
|
135
|
+
self
|
136
|
+
elsif can_hold_child?(storable) then
|
137
|
+
add_local(storable)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Creates a subcontainer which holds the given storable. Creates nested subcontainers as necessary.
|
142
|
+
#
|
143
|
+
# @param @storable (see #add)
|
144
|
+
# @return [StorageContainer, nil] self if a subcontainer was created, nil otherwise
|
145
|
+
def add_to_new_subcontainer(storable)
|
146
|
+
# the subcontainers in column, row sort order
|
147
|
+
scs = subcontainers.sort { |sc1, sc2| sc1.position.location <=> sc2.position.location }
|
148
|
+
# the first subcontainer that can hold the new subcontainer is preferred
|
149
|
+
if scs.detect { |ctr| ctr.add_to_new_subcontainer(storable) if StorageContainer === ctr } then
|
150
|
+
self
|
151
|
+
elsif not full? then
|
152
|
+
create_subcontainer_for(storable)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# @param [Storable] (see #add)
|
157
|
+
# @return whether this StorageContainer is not full and can hold the given item's StorableType
|
158
|
+
def can_hold_child?(storable)
|
159
|
+
st = storable.storable_type
|
160
|
+
not full? and child_types.any? { |ct| CaRuby::Resource.value_equal?(ct, st) }
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# Adds the follwing defaults:
|
166
|
+
# * the default child_types are this container's CaTissue::ContainerType child_types.
|
167
|
+
# * the default site is the parent container site, if any.
|
168
|
+
def add_defaults_local
|
169
|
+
super
|
170
|
+
if child_types.empty? and container_type and not container_type.child_types.empty? then
|
171
|
+
container_type.child_types.each { |type| add_child_type(type) }
|
172
|
+
end
|
173
|
+
# Although this default is set by the caTissue app, it is good practice to do so here
|
174
|
+
# for clarity.
|
175
|
+
self.site ||= parent.site if parent
|
176
|
+
end
|
177
|
+
|
178
|
+
# @see #add_to_new_subcontainer
|
179
|
+
def create_subcontainer_for(storable)
|
180
|
+
# the StorageType path to storable
|
181
|
+
type_path = type_path_to(storable) || return
|
182
|
+
# create a container for each type leading to storable and add it to the parent container
|
183
|
+
ctr = type_path.reverse.inject(storable) do |occ, type|
|
184
|
+
subctr = type.create_container
|
185
|
+
subctr.site = site
|
186
|
+
logger.debug { "Created #{qp} #{subctr.container_type.name} subcontainer #{subctr} to hold #{occ}." }
|
187
|
+
subctr << occ
|
188
|
+
end
|
189
|
+
add_local(ctr)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Returns a StorageType array from a child StorageType to a descendant StorageType which can
|
193
|
+
# hold the given storable, or nil if no such path exists.
|
194
|
+
def type_path_to(storable)
|
195
|
+
holds_storage_types.detect_value { |type| type.path_to(storable) }
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def out_of_bounds(storable)
|
201
|
+
raise IndexError.new("Container #{name} does not have an available position for #{storable}")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|