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,177 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.CollectionProtocol')
|
6
|
+
|
7
|
+
# The CollectionProtocol domain class.
|
8
|
+
class CollectionProtocol
|
9
|
+
include Resource
|
10
|
+
|
11
|
+
# caTissue alert - Bug #64: Some domain collection properties not initialized.
|
12
|
+
# Initialize consent_tiers if necessary.
|
13
|
+
#
|
14
|
+
# @return [Java::JavaUtil::Set] the tiers
|
15
|
+
def consent_tiers
|
16
|
+
getConsentTierCollection or (self.consent_tiers = Java::JavaUtil::LinkedHashSet.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
add_attribute_aliases(:events => :collection_protocol_events, :registrations => :collection_protocol_registrations)
|
20
|
+
|
21
|
+
set_secondary_key_attributes(:short_title)
|
22
|
+
|
23
|
+
add_attribute_defaults(:consents_waived => false, :aliquot_in_same_container => false)
|
24
|
+
|
25
|
+
add_mandatory_attributes(:aliquot_in_same_container, :collection_protocol_events, :consents_waived, :enrollment, :start_date, :title)
|
26
|
+
|
27
|
+
add_dependent_attribute(:collection_protocol_events)
|
28
|
+
|
29
|
+
add_dependent_attribute(:consent_tiers)
|
30
|
+
|
31
|
+
# caTissue alert - Augment the standard metadata storable reference attributes to work around caTissue Bug #150:
|
32
|
+
# Create CollectionProtocol in API ignores startDate.
|
33
|
+
qualify_attribute(:start_date, :update_only)
|
34
|
+
|
35
|
+
# caTissue alert - Augment the standard metadata storable reference attributes to work around caTissue Bug #150:
|
36
|
+
# Create CollectionProtocol in API ignores startDate.
|
37
|
+
set_attribute_type(:coordinators, CaTissue::User)
|
38
|
+
|
39
|
+
# caTissue alert - Augment the standard metadata storable reference attributes to work around caTissue Bug #150:
|
40
|
+
# Create CollectionProtocol in API ignores startDate.
|
41
|
+
qualify_attribute(:coordinators, :fetched)
|
42
|
+
|
43
|
+
def initialize(params=nil)
|
44
|
+
super
|
45
|
+
respond_to?(:consent_tiers)
|
46
|
+
# work around caTissue Bug #64 - consent tiers is nil rather than an empty set
|
47
|
+
self.consent_tiers ||= Java::JavaUtil::LinkedHashSet.new
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns all participants registered in this protocol.
|
51
|
+
def participants
|
52
|
+
registrations.nil? ? [] : registrations.map { |reg| reg.participant }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Overrides the Java CollectionProtocol hashCode to make the hash insensitive to identifier assignment.
|
56
|
+
#
|
57
|
+
# @see #==
|
58
|
+
def hash
|
59
|
+
# caTissue alert - bad caTissue API hashCode leads to ugly cascading errors when using a CP in a Set
|
60
|
+
(object_id * 31) + 17
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns whether other is {#equal?} to CollectionProtocol.
|
64
|
+
#
|
65
|
+
# This method is a work-around for caTissue Bug #70: CollectionProtocol and non-CollectionProtocol are equal in caTissue 1.1.
|
66
|
+
def ==(other)
|
67
|
+
object_id == other.object_id
|
68
|
+
end
|
69
|
+
|
70
|
+
alias :eql? :==
|
71
|
+
|
72
|
+
# Returns a new CollectionProtocolRegistration for the specified participant in this CollectionProtocol with
|
73
|
+
# optional +protocol_participant_identifier+ ppi.
|
74
|
+
def register(participant, ppi=nil)
|
75
|
+
CollectionProtocolRegistration.new(:participant => participant, :protocol => self, :protocol_participant_identifier => ppi)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the CollectionProtocolRegistration for the specified participant in this CollectionProtocol,
|
79
|
+
def registration(participant)
|
80
|
+
registrations.detect { |registration| registration.participant == participant }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the event in this protocol with the earliest study calendar event point.
|
84
|
+
def first_event
|
85
|
+
events.sort_by { |event| event.event_point or CollectionProtocolEvent::DEFAULT_EVENT_POINT }.first
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the specimens collected from the given participant for this CollectionProtocol,
|
89
|
+
# or all specimens in this protocol if participant is nil.
|
90
|
+
def specimens(participant=nil)
|
91
|
+
if participant.nil? then return registrations.map { |reg| reg.specimens }.flatten end
|
92
|
+
reg = registration(participant)
|
93
|
+
reg.nil? ? Array::EMPTY_ARRAY : reg.specimens
|
94
|
+
end
|
95
|
+
|
96
|
+
# Adds specimens to this protocol. Arguments:
|
97
|
+
# * participant - the Participant from whom the specimen is collected
|
98
|
+
# * biospecimens - the collected top-level underived specimens
|
99
|
+
# * params - additional SCG parameters as described in {SpecimenCollectionGroup#merge} method
|
100
|
+
# If params does not include a :collectionProtocolEvent parameter, then the SCG is assigned
|
101
|
+
# to the first collection event in this protocol.
|
102
|
+
# If params does not include a :specimen_collection_site parameter, then the SCG is assigned
|
103
|
+
# to the participant's collection site as determined by {Participant#collection_site}, if that
|
104
|
+
# can be uniquely determined.
|
105
|
+
#
|
106
|
+
# This add_specimens method adds the following association to params before calling the
|
107
|
+
# SpecimenCollectionGroup constructor:
|
108
|
+
# * :registration => a new CollectionProtocolRegistration for this protocol and the specified participant
|
109
|
+
# If there is no :name parameter, then this method builds a new unique SCG name as this
|
110
|
+
# CollectionProtocol's name followed by a unique suffix.
|
111
|
+
#
|
112
|
+
# Returns a new SpecimenCollectionGroup for the given participant containing the specimens.
|
113
|
+
#
|
114
|
+
# Raises ArgumentError if the SpecimenCollectionGroup does not include all required attributes.
|
115
|
+
def add_specimens(*specimens_and_params)
|
116
|
+
params = specimens_and_params.pop
|
117
|
+
spcs = specimens_and_params
|
118
|
+
# validate arguments
|
119
|
+
unless params then
|
120
|
+
raise ArgumentError.new("Collection parameters are missing when adding specimens to protocol #{self}")
|
121
|
+
end
|
122
|
+
# there must be a participant
|
123
|
+
pnt = params.delete(:participant)
|
124
|
+
unless pnt then
|
125
|
+
raise ArgumentError.new("Participant missing from collection parameters: #{params.qp}")
|
126
|
+
end
|
127
|
+
# there must be a receiver
|
128
|
+
unless params[:receiver] then
|
129
|
+
raise ArgumentError.new("Receiver missing from collection parameters: #{params.qp}")
|
130
|
+
end
|
131
|
+
# the required registration
|
132
|
+
params[:registration] ||= registration(pnt) || make_cpr(pnt)
|
133
|
+
# the new SCG
|
134
|
+
scg = SpecimenCollectionGroup.new(params)
|
135
|
+
# set each Specimen SCG
|
136
|
+
spcs.each { |spc| spc.specimen_collection_group = scg }
|
137
|
+
scg
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# Sets the defaults as follows:
|
143
|
+
# * The start date is set to now.
|
144
|
+
# * The title is set to the short title.
|
145
|
+
# * If there is no CP coordinator and there is exactly one site with a coordinator, then the
|
146
|
+
# default CP coordinator is the site coordinator.
|
147
|
+
# * If there is no CP site and there is exactly one coordinator site, then the default CP site
|
148
|
+
# is the coordinator site.
|
149
|
+
def add_defaults_local
|
150
|
+
super
|
151
|
+
self.title ||= short_title
|
152
|
+
self.short_title ||= title
|
153
|
+
self.start_date ||= Java::JavaUtil::Date.new
|
154
|
+
if coordinators.empty? and sites.size == 1 then
|
155
|
+
coord = sites.first.coordinator
|
156
|
+
coordinators << coord if coord
|
157
|
+
elsif sites.empty? and coordinators.size == 1 then
|
158
|
+
site = coordinators.first.sites.first
|
159
|
+
sites << site if site
|
160
|
+
end
|
161
|
+
make_default_collection_event unless events.detect { |evt| CollectionProtocolEvent === evt }
|
162
|
+
end
|
163
|
+
|
164
|
+
def make_default_collection_event
|
165
|
+
# make this protocol's CPE
|
166
|
+
cpe = CollectionProtocolEvent.new(:protocol => self)
|
167
|
+
# make a tissue requirement
|
168
|
+
CaTissue::TissueSpecimenRequirement.new(
|
169
|
+
:collection_event => cpe,
|
170
|
+
:specimen_characteristics => CaTissue::SpecimenCharacteristics.new)
|
171
|
+
end
|
172
|
+
|
173
|
+
def make_cpr(participant)
|
174
|
+
CollectionProtocolRegistration.new(:participant => participant, :protocol => self)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'caruby/util/collection'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.CollectionProtocolEvent')
|
6
|
+
|
7
|
+
# The CollectionProtocolRegistration domain class.
|
8
|
+
class CollectionProtocolEvent
|
9
|
+
include Resource
|
10
|
+
|
11
|
+
# caTissue alert - Bug #64: Some domain collection properties not initialized.
|
12
|
+
# Initialize specimen_collection_groups if necessary.
|
13
|
+
#
|
14
|
+
# @return [Java::JavaUtil::Set] the SCGs
|
15
|
+
def specimen_collection_groups
|
16
|
+
getSpecimenCollectionGroupCollection or (self.specimen_collection_groups = Java::JavaUtil::LinkedHashSet.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
add_attribute_aliases(:label => :collection_point_label,
|
20
|
+
:protocol => :collection_protocol,
|
21
|
+
:requirements => :specimen_requirements,
|
22
|
+
:event_point => :study_calendar_event_point)
|
23
|
+
|
24
|
+
# CPE secondary key is the CP and collection point.
|
25
|
+
set_secondary_key_attributes(:collection_protocol, :collection_point_label)
|
26
|
+
|
27
|
+
# CPE alternate key is the CP and event point.
|
28
|
+
set_alternate_key_attributes(:collection_protocol, :study_calendar_event_point)
|
29
|
+
|
30
|
+
# Default event point is day one.
|
31
|
+
add_attribute_defaults(:study_calendar_event_point => 1.0)
|
32
|
+
|
33
|
+
add_mandatory_attributes(:collection_protocol, :clinical_diagnosis, :specimen_requirements)
|
34
|
+
|
35
|
+
# caTissue alert - specimen_requirements is a cascaded dependent, but it is not fetched.
|
36
|
+
# It is marked as auto-generated, since it must be refetched and matched on CPE create.
|
37
|
+
# CollectionProtocol create cascades through each dependent CPE to each SpecimenRequirement.
|
38
|
+
# TODO - confirm that the :autogenerated flag is necessary.
|
39
|
+
add_dependent_attribute(:specimen_requirements, :autogenerated, :unfetched)
|
40
|
+
|
41
|
+
# The event point used for saving this CollectionProtocolEvent if none other is set.
|
42
|
+
DEFAULT_EVENT_POINT = 1.0
|
43
|
+
|
44
|
+
def initialize(params=nil)
|
45
|
+
super
|
46
|
+
respond_to?(:specimen_collection_groups)
|
47
|
+
# work around caTissue Bug #64
|
48
|
+
self.specimen_collection_groups ||= Java::JavaUtil::LinkedHashSet.new
|
49
|
+
end
|
50
|
+
|
51
|
+
# Overrides {CaRuby::ResourceAttributes#mandatory_attributes} to correct the following caTissue bug:
|
52
|
+
#
|
53
|
+
# caTissue alert - specimen_collection_site is incorrectly attached in the caTissue class model
|
54
|
+
# to AbstractSpecimenCollectionGroup rather than SpecimenCollectionGroup. Since CollectionProtocolEvent
|
55
|
+
# is a subclass of AbstractSpecimenCollectionGroup and mandatory attributes are inherited,
|
56
|
+
# specimen_collection_site must be listed as optional here to counteract the effect of marking it
|
57
|
+
# as mandatory for a SCG (cf. caTissue Bug #116).
|
58
|
+
def mandatory_attributes
|
59
|
+
@mdtry_attrs_filter ||= @mandatory_attributes.filter { |attr| attr != :specimen_collection_site }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Overrides the Java CollectionProtocolEvent hashCode to make the hash insensitive to identifier assignment.
|
63
|
+
def hash
|
64
|
+
# caTissue alert - caTissue determines the hashCode from the identifier. Consequently, a CollectionProtocolEvent
|
65
|
+
# added to a HashSet without an identifier can no longer find the CPE when it is assigned an identifier.
|
66
|
+
# This bug results in obscure delayed cascade errors. Work-around is to override the hash method in the
|
67
|
+
# Ruby CPE wrapper class.
|
68
|
+
(object_id * 31) + 17
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns whether other is a CollectionProtocolEvent with the same identifier as this CollectionProtocolEvent,
|
72
|
+
# or the same object_id if this CollectionProtocolEvent's identifier is nil.
|
73
|
+
#
|
74
|
+
# This method is a work-around for caTissue bug: CollectionProtocolEvent and non-CollectionProtocolEvent are equal in caTissue 1.1.
|
75
|
+
def ==(other)
|
76
|
+
object_id == other.object_id
|
77
|
+
end
|
78
|
+
|
79
|
+
alias :eql? :==
|
80
|
+
|
81
|
+
# Removes associations to this registration
|
82
|
+
def delete
|
83
|
+
protocol.events.delete(self) if protocol
|
84
|
+
end
|
85
|
+
|
86
|
+
# Overrides {CaRuby::Resource#references} in the case of the _specimen_requirements_ attribute to select
|
87
|
+
# only top-level SpecimenRequirements not derived from another SpecimenRequirement.
|
88
|
+
def direct_dependents(attribute)
|
89
|
+
if attribute == :specimen_requirements then
|
90
|
+
super.reject { |spc| spc.parent }
|
91
|
+
else
|
92
|
+
super
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Sets the default label to the protcol name followed by the event point.
|
99
|
+
def add_defaults_local
|
100
|
+
super
|
101
|
+
self.label ||= default_label
|
102
|
+
end
|
103
|
+
|
104
|
+
def default_label
|
105
|
+
"#{protocol.short_title.sub(' ', '_')}_#{event_point}" if protocol and protocol.short_title and event_point
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'caruby/util/collection'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.CollectionProtocolRegistration')
|
6
|
+
|
7
|
+
# The CollectionProtocolRegistration domain class.
|
8
|
+
class CollectionProtocolRegistration
|
9
|
+
include Resource
|
10
|
+
|
11
|
+
# caTissue alert - Bug #64: Some domain collection properties not initialized.
|
12
|
+
# Initialize consent_tier_responses if necessary.
|
13
|
+
#
|
14
|
+
# @return [Java::JavaUtil::Set] the responses
|
15
|
+
def consent_tier_responses
|
16
|
+
getConsentTierResponseCollection or (self.consent_tier_responses = Java::JavaUtil::LinkedHashSet.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns whether the consent available flag is equal to the String 'true'. This method converts
|
20
|
+
# the caTissue String to a Boolean.
|
21
|
+
def consent_available
|
22
|
+
getIsConsentAvailable == 'true'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sets the consent available flag to the specified value. A Boolean value is converted to a String.
|
26
|
+
def consent_available=(value)
|
27
|
+
value = value.to_s if value
|
28
|
+
setIsConsentAvailable(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sets the consent available flag to the specified value. An Integer value is converted to a String.
|
32
|
+
def protocol_participant_identifier=(value)
|
33
|
+
value = value.to_s if value
|
34
|
+
setProtocolParticipantIdentifier(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
add_attribute_aliases("consented?".to_sym => :is_consent_available, :protocol => :collection_protocol,
|
38
|
+
:participant_identifier => :protocol_participant_identifier, :consent_responses => :consent_tier_responses)
|
39
|
+
|
40
|
+
set_secondary_key_attributes(:collection_protocol, :participant)
|
41
|
+
|
42
|
+
set_alternate_key_attributes(:collection_protocol, :protocol_participant_identifier)
|
43
|
+
|
44
|
+
add_attribute_defaults(:activity_status => 'Active')
|
45
|
+
|
46
|
+
add_mandatory_attributes(:registration_date)
|
47
|
+
|
48
|
+
# consent_tier_responses is a cascaded dependent but is not fetched
|
49
|
+
add_dependent_attribute(:consent_tier_responses, :unfetched)
|
50
|
+
|
51
|
+
# The CPR-CP association is bi-directional.
|
52
|
+
set_attribute_inverse(:collection_protocol, :collection_protocol_registrations)
|
53
|
+
|
54
|
+
# The CPR-Participant association is bi-directional.
|
55
|
+
set_attribute_inverse(:participant, :collection_protocol_registrations)
|
56
|
+
|
57
|
+
add_dependent_attribute(:specimen_collection_groups, :logical, :autogenerated)
|
58
|
+
|
59
|
+
# CPR PPI is part of a key if it exists, but is optional.
|
60
|
+
qualify_attribute(:protocol_participant_identifier, :optional)
|
61
|
+
|
62
|
+
# caTissue alert - Augment the standard metadata storable reference attributes to work around caTissue Bug #150:
|
63
|
+
# Create CollectionProtocol in API ignores startDate.
|
64
|
+
qualify_attribute(:registration_date, :update_only)
|
65
|
+
|
66
|
+
# caTissue alert - Augment the standard metadata storable reference attributes to work around caTissue Bug #63
|
67
|
+
# that requires a SpecimenCollectionGroup with an identifier which references a CollectionProtocolRegistration
|
68
|
+
# with an identifier to nevertheless hold extraneous CollectionProtocolRegistration content, including the CPR
|
69
|
+
# collection protocol. The referenced CP should not itself cascade to its dependents. This is enforced by a
|
70
|
+
# Catissue::Database work-around.
|
71
|
+
#
|
72
|
+
# caTissue alert - CPR fetches the associated CP. This is unnecessary for the predominant caRuby use case,
|
73
|
+
# where the CP is known when the CPR is fetched. Don't mark the CP as fetched in the CPR metadata, since that
|
74
|
+
# precipitates an unnecessary CP copy/match/merge into a fetched CPR.
|
75
|
+
qualify_attribute(:collection_protocol)
|
76
|
+
|
77
|
+
def initialize(params=nil)
|
78
|
+
super
|
79
|
+
# following line works around an obscure problem whereby CPR init could not call consent_tier_responses below;
|
80
|
+
# TODO - verify that it is still necessary
|
81
|
+
respond_to?(:consent_tier_responses)
|
82
|
+
# work around caTissue Bug #64
|
83
|
+
self.consent_tier_responses ||= Java::JavaUtil::LinkedHashSet.new
|
84
|
+
end
|
85
|
+
|
86
|
+
# Removes associations to this registration
|
87
|
+
def delete
|
88
|
+
participant.collection_registrations.delete(self) if participant
|
89
|
+
protocol.registrations.delete(self) if protocol
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return all specimens collected for this CollectionProtocolRegistration
|
93
|
+
def specimens
|
94
|
+
Flattener.new(specimen_collection_groups.map { |group| group.specimens })
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Adds defaults as follows:
|
100
|
+
# * The default registration date is the current time.
|
101
|
+
# * The default PPI is a unique number.
|
102
|
+
def add_defaults_local
|
103
|
+
super
|
104
|
+
self.registration_date ||= Java.now
|
105
|
+
self.protocol_participant_identifier ||= Uniquifier.qualifier.to_s
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module CaTissue
|
2
|
+
# import the Java class
|
3
|
+
java_import('edu.wustl.catissuecore.domain.ConsentTierResponse')
|
4
|
+
|
5
|
+
class ConsentTierResponse
|
6
|
+
include Resource
|
7
|
+
|
8
|
+
add_mandatory_attributes(:consent_tier, :response)
|
9
|
+
|
10
|
+
add_attribute_defaults(:response => 'Not Specified')
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CaTissue
|
2
|
+
# import the Java class
|
3
|
+
java_import('edu.wustl.catissuecore.domain.ConsentTierStatus')
|
4
|
+
|
5
|
+
class ConsentTierStatus
|
6
|
+
include Resource
|
7
|
+
|
8
|
+
add_mandatory_attributes(:consent_tier, :status)
|
9
|
+
|
10
|
+
add_attribute_defaults(:status => 'Not Specified')
|
11
|
+
|
12
|
+
# Returns whether this ConsentTierStatus is minimally consistent with the other ConsentTierStatus.
|
13
|
+
# This method returns whether the referenced ConsentTier has the same identifer or statement text
|
14
|
+
# as the other referenced ConsentTier.
|
15
|
+
def minimal_match?(other)
|
16
|
+
super and statement_match?(other)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Returns true if this ConsentTierStatus ConsentTier is nil, the other ConsentTierStatus ConsentTier is nil,
|
22
|
+
# both ConsentTier identifiers are equal, or both ConsentTier statements are equal.
|
23
|
+
def statement_match?(other)
|
24
|
+
ct = resume_lazy_loader { consent_tier }
|
25
|
+
oct = other.resume_lazy_loader { other.consent_tier }
|
26
|
+
ct and oct and (ct.identifier == oct.identifier or ct.statement == oct.statement)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'caruby/util/coordinate'
|
2
|
+
require 'catissue/util/storable'
|
3
|
+
require 'catissue/util/location'
|
4
|
+
|
5
|
+
module CaTissue
|
6
|
+
# import the Java class
|
7
|
+
java_import('edu.wustl.catissuecore.domain.Container')
|
8
|
+
|
9
|
+
# The +caTissue+ +Container+ domain class wrapper.
|
10
|
+
# Each Container subclass is required to implement the {#container_type} method.
|
11
|
+
class Container
|
12
|
+
include Storable, Resource
|
13
|
+
|
14
|
+
add_attribute_aliases(:position => :located_at_position, :subcontainer_positions => :occupied_positions)
|
15
|
+
|
16
|
+
set_secondary_key_attributes(:name)
|
17
|
+
|
18
|
+
add_attribute_defaults(:activity_status => 'Active', :full => false)
|
19
|
+
|
20
|
+
add_dependent_attribute(:capacity, :logical, :autogenerated)
|
21
|
+
|
22
|
+
# located_at_position is cascaded but not fetched.
|
23
|
+
add_dependent_attribute(:located_at_position, :unfetched)
|
24
|
+
|
25
|
+
# Like SCG, Container name is ignored and assigned by caTissue. Therefore, this
|
26
|
+
# attribute is marked auto-generated.
|
27
|
+
qualify_attribute(:name, :autogenerated)
|
28
|
+
|
29
|
+
# Returns the ContainerType which constrains a Container in its roles as a Storable
|
30
|
+
# occupant rather than a Storable holder. {#storable_type} aliases the _container_type_
|
31
|
+
# defined by every Container subclass.
|
32
|
+
def storable_type
|
33
|
+
# can't alias because container_type is defined by subclasses
|
34
|
+
container_type
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return this Container's ContainerType
|
38
|
+
def container_type
|
39
|
+
if self.class < Container then raise NotImplementedError.new("Container subclass does not implement the container_type method") end
|
40
|
+
end
|
41
|
+
|
42
|
+
def copy(*attributes)
|
43
|
+
ctr = super
|
44
|
+
ctr.container_type = self.container_type if attributes.empty?
|
45
|
+
ctr
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the ContainerPosition class which this Container can occupy in its role as
|
49
|
+
# a Storable.
|
50
|
+
def position_class
|
51
|
+
CaTissue::ContainerPosition
|
52
|
+
end
|
53
|
+
|
54
|
+
# Lazy-initializes this Container's capacity to a copy of the {#storable_type} capacity.
|
55
|
+
def capacity
|
56
|
+
# caTissue alert - this override is necessary because the +caTissue+ API does
|
57
|
+
# not make the reasonable assumption that the default Container capacity is the
|
58
|
+
# ContainerType capacity.
|
59
|
+
getCapacity or copy_container_type_capacity
|
60
|
+
end
|
61
|
+
|
62
|
+
def bounds
|
63
|
+
capacity.bounds if capacity
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return the number of rows in this Container
|
67
|
+
def rows
|
68
|
+
capacity.rows
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return the number of columns in this Container
|
72
|
+
def columns
|
73
|
+
capacity.columns
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return this Container's parent container
|
77
|
+
def parent
|
78
|
+
position and position.parent
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return the occupants in this Container's positions.
|
82
|
+
# @see
|
83
|
+
def occupants
|
84
|
+
all_occupied_positions.wrap { |pos| pos.occupant }
|
85
|
+
end
|
86
|
+
|
87
|
+
alias :contents :occupants
|
88
|
+
|
89
|
+
# Returns whether this Container holds the given item or this Container holds
|
90
|
+
# a subcontainer which holds the item.
|
91
|
+
def include?(item)
|
92
|
+
occupants.detect { |occ| occ == item or occ.include?(item) }
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return the Specimen occupants
|
96
|
+
def specimens
|
97
|
+
occupants.filter { |occ| Specimen === occ }
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return the Container occupants
|
101
|
+
def subcontainers
|
102
|
+
occupants.filter { |occ| Container === occ }
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return the Containers in this StorageContainer hierarchy
|
106
|
+
def subcontainers_in_hierarchy
|
107
|
+
@ctr_enum ||= SUBCTR_VISITOR.to_enum(self)
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return whether this container or a subcontainer in the hierarchy holds the given object
|
111
|
+
def holds?(storable)
|
112
|
+
contents.include?(storable) or subcontainers.any? { |ctr| ctr.holds?(storable) }
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return true if this Container or a subcontainer in the hierarchy can hold the given storable
|
116
|
+
#
|
117
|
+
# @see #can_hold_child?
|
118
|
+
def can_hold?(storable)
|
119
|
+
can_hold_child?(storable) or subcontainers.detect { |ctr| ctr.can_hold?(storable) }
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return true if this Container is not full and the {#container_type} can hold the storable as a child
|
123
|
+
def can_hold_child?(storable)
|
124
|
+
not full? and container_type.can_hold_child?(storable)
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return whether this Container and every subcontainer in the hierarchy are full
|
128
|
+
def completely_full?
|
129
|
+
full? and subcontainers.all? { |ctr| ctr.completely_full? }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns -1, 0, or 1 if self is contained in, contains or the same as the other
|
133
|
+
# Container, resp.
|
134
|
+
def <=>(other)
|
135
|
+
raise TypeError.new("Can't compare #{qp} to #{other}") unless StorageContainer === self
|
136
|
+
return 0 if equal?(other) or (name and name == other.name)
|
137
|
+
return 1 if subcontainers.detect { |child| child >= other if StorageContainer === child }
|
138
|
+
-1 if other > self
|
139
|
+
end
|
140
|
+
|
141
|
+
# @return the occupant at the given zero-based row and column, or nil if none
|
142
|
+
def [](column, row)
|
143
|
+
all_occupied_positions.detect_value do |pos|
|
144
|
+
return if row < pos.row
|
145
|
+
next unless row == pos.row
|
146
|
+
pos.occupant if pos.column == column
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Moves the given Storable from its current Position, if any, to this Container at the optional
|
151
|
+
# coordinate. The default coordinate is the first available slot within this Container.
|
152
|
+
# The storable Storable position is updated to reflect the new location. Returns self.
|
153
|
+
#
|
154
|
+
# Raises IndexError if this Container is full.
|
155
|
+
# Raises IndexError if the row and column are given but exceed the Container bounds.
|
156
|
+
def add(storable, coordinate=nil, attribute=nil)
|
157
|
+
validate_type(storable)
|
158
|
+
loc = create_location(coordinate)
|
159
|
+
pos = storable.position || storable.position_class.new
|
160
|
+
pos.location = loc
|
161
|
+
pos.occupant = storable
|
162
|
+
pos.holder = self
|
163
|
+
logger.debug { "Added #{storable.qp} to #{qp} at #{loc.coordinate}." }
|
164
|
+
update_full_flag
|
165
|
+
self
|
166
|
+
end
|
167
|
+
|
168
|
+
alias :<< :add
|
169
|
+
|
170
|
+
protected
|
171
|
+
|
172
|
+
# Returns the the content collection to which the storable is added. This default returns
|
173
|
+
# occupied_positions if storable is a Container, nil otherwise. Subclasses can override.
|
174
|
+
#
|
175
|
+
# @param [Storable] the item to store
|
176
|
+
# @return [<Position>] the occupied positions
|
177
|
+
def content_collection_for(storable)
|
178
|
+
subcontainer_positions if Container === storable
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
# Subcontainer visitor.
|
184
|
+
SUBCTR_VISITOR = CaRuby::ReferenceVisitor.new { [:subcontainers] }
|
185
|
+
|
186
|
+
# @param [Storable] the item to store
|
187
|
+
# @raise [TypeError] if this container cannot hold the storable
|
188
|
+
def validate_type(storable)
|
189
|
+
unless container_type.can_hold_child?(storable) then
|
190
|
+
raise TypeError.new("Container #{self} cannot hold an item of the #{storable} type")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# @param [Coordinate] coordinate the optional location to create
|
195
|
+
# @return [Location] the created location
|
196
|
+
def create_location(coordinate=nil)
|
197
|
+
if coordinate then
|
198
|
+
Location.new(:in => self, :at => coordinate)
|
199
|
+
else
|
200
|
+
first_available_location or raise IndexError.new("Container #{qp} does not have an available location")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# @return [Location] the next available Location in this container, or nil if no unoccupied
|
205
|
+
# location is available
|
206
|
+
def first_available_location
|
207
|
+
return if full?
|
208
|
+
# look for the first unoccupied location
|
209
|
+
occupied = all_occupied_positions.map { |pos| pos.location }.sort
|
210
|
+
# find a gap, if one exists, otherwise return the next location
|
211
|
+
# after the last occupied location
|
212
|
+
current = Location.new(:in => self, :at => Coordinate.new(0, 0))
|
213
|
+
occupied.each do |loc|
|
214
|
+
break if current < loc
|
215
|
+
current.succ!
|
216
|
+
end
|
217
|
+
current
|
218
|
+
end
|
219
|
+
|
220
|
+
# Copies this Container's ContainerType capacity, if it exists, to the Container capacity.
|
221
|
+
#
|
222
|
+
# @return [Capacity, nil] the initialized capacity, if any
|
223
|
+
def copy_container_type_capacity
|
224
|
+
return unless container_type and container_type.capacity
|
225
|
+
self.capacity = container_type.capacity.copy(:rows, :columns)
|
226
|
+
update_full_flag
|
227
|
+
capacity
|
228
|
+
end
|
229
|
+
|
230
|
+
def update_full_flag
|
231
|
+
self.full = all_occupied_positions.size == rows * columns
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|