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,47 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenArray')
|
6
|
+
|
7
|
+
class SpecimenArray
|
8
|
+
include Resource
|
9
|
+
|
10
|
+
# caTissue alert - the superclass Container occupied_positions does not apply to SpecimenArray.
|
11
|
+
remove_attribute(:occupied_positions)
|
12
|
+
|
13
|
+
add_attribute_aliases(:container_type => :specimen_array_type, :contents => :specimen_array_contents)
|
14
|
+
|
15
|
+
set_attribute_type(:new_specimen_array_order_items, CaTissue::SpecimenArrayOrderItem)
|
16
|
+
|
17
|
+
add_dependent_attribute(:new_specimen_array_order_items)
|
18
|
+
|
19
|
+
add_dependent_attribute(:specimen_array_contents)
|
20
|
+
|
21
|
+
# Raises NotImplementedError since caTissue SpecimenArray is broken in important ways
|
22
|
+
# described below.
|
23
|
+
#
|
24
|
+
# caTissue alert - SpecimenArray is a subclass of Container but is a mismatch for Container
|
25
|
+
# in two critical ways:
|
26
|
+
# * SpecimenArray does not hold subcontainers in Container occupied_positions
|
27
|
+
# * the Specimen position cannot be a SpecimenArrayContent
|
28
|
+
# * SpecimenArrayContent is not an AbstractPosition, although it should be
|
29
|
+
#
|
30
|
+
# Thus, SpecimenArray extends Container although it shouldn't and SpecimenArrayContent
|
31
|
+
# doesn't extend AbstractPosition although it should.
|
32
|
+
def add(storable, coordinate=nil, attribute=nil)
|
33
|
+
raise NotImplementedError.new("Adding specimens to a SpecimenArray is not yet supported")
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :<< :add
|
37
|
+
|
38
|
+
alias :all_occupied_positions :specimen_array_contents
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Returns the contents if storable is a Specimen, nil otherwise.
|
43
|
+
def content_collection_for(storable)
|
44
|
+
contents if CaTissue::Specimen === storable
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'catissue/util/position'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenArrayContent')
|
6
|
+
|
7
|
+
# caTissue alert - #{CaTissue::SpecimenArrayContent} should be derived from
|
8
|
+
# {CaTissue::AbstractPosition} but isn't (cf. {CaTissue::ContainerType}).
|
9
|
+
# Partially rectify this by including the {Position} mix-in in common with
|
10
|
+
# {CaTissue::AbstractPosition}.
|
11
|
+
class SpecimenArrayContent
|
12
|
+
include Position, Resource
|
13
|
+
|
14
|
+
add_attribute_aliases(:holder => :specimen_array, :occupant => :specimen)
|
15
|
+
|
16
|
+
# Each SpecimenArrayContent has a specimen and there is only one such slot per specimen.
|
17
|
+
set_secondary_key_attributes(:specimen)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CaTissue
|
2
|
+
# import the Java class
|
3
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenArrayType')
|
4
|
+
|
5
|
+
class SpecimenArrayType
|
6
|
+
include Resource
|
7
|
+
|
8
|
+
add_mandatory_attributes(:specimen_class, :specimen_types)
|
9
|
+
|
10
|
+
# Returns SpecimenArray.
|
11
|
+
def container_class
|
12
|
+
CaTissue::SpecimenArray
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns true if Storable is a Specimen and supported by this SpecimenArrayType.
|
16
|
+
def can_hold_child?(storable)
|
17
|
+
Specimen === storable and storable.specimen_class == specimen_class and specimen_types.include?(storable.specimen_type)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenCharacteristics')
|
6
|
+
|
7
|
+
class SpecimenCharacteristics
|
8
|
+
include Resource
|
9
|
+
|
10
|
+
add_attribute_defaults(:tissue_side => 'Not Specified', :tissue_site => 'Not Specified')
|
11
|
+
|
12
|
+
add_mandatory_attributes(:tissue_site, :tissue_side)
|
13
|
+
|
14
|
+
# The tissue_side constants.
|
15
|
+
class TissueSide
|
16
|
+
LEFT = 'Left'
|
17
|
+
RIGHT = 'Right'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,412 @@
|
|
1
|
+
require 'caruby/util/transitive_closure'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
# import the Java class
|
5
|
+
java_import('edu.wustl.catissuecore.domain.SpecimenCollectionGroup')
|
6
|
+
|
7
|
+
# The SpecimenCollectionGroup domain class.
|
8
|
+
#
|
9
|
+
# _Note_: the SpecimenCollectionGroup name attribute is auto-generated on create in caTissue 1.1 API and should not
|
10
|
+
# be set by API clients when creating a new SpecimenCollectionGroup in the database.
|
11
|
+
class SpecimenCollectionGroup
|
12
|
+
include Resource
|
13
|
+
|
14
|
+
# caTissue alert - Bug #64: Some domain collection properties not initialized.
|
15
|
+
# Initialize consent_tier_statuses if necessary.
|
16
|
+
#
|
17
|
+
# @return [Java::JavaUtil::Set] the statuses
|
18
|
+
def consent_tier_statuses
|
19
|
+
getConsentTierStatusCollection or (self.consent_tier_statuses = Java::JavaUtil::LinkedHashSet.new)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Sets the collection status for this SCG.
|
23
|
+
# If the SCG status is set to +Complete+, then the status of each of the SCG Specimens with
|
24
|
+
# status +Pending+ is reset to +Collected+.
|
25
|
+
#
|
26
|
+
# @param [String] value a permissible SCG status
|
27
|
+
def collection_status=(value)
|
28
|
+
if value == 'Complete' then
|
29
|
+
specimens.each { |spc| spc.collection_status = 'Collected' if spc.pending? }
|
30
|
+
end
|
31
|
+
setCollectionStatus(value)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Converts an Integer SPN value to a String.
|
35
|
+
#
|
36
|
+
# @param [Numeric, String] value the SPN value
|
37
|
+
def surgical_pathology_number=(value)
|
38
|
+
value = value.to_s if Numeric === value
|
39
|
+
setSurgicalPathologyNumber(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
add_attribute_aliases(:collection_event => :collection_protocol_event,
|
43
|
+
:event_parameters => :specimen_event_parameters,
|
44
|
+
:events => :specimen_event_parameters,
|
45
|
+
:registration => :collection_protocol_registration)
|
46
|
+
|
47
|
+
add_mandatory_attributes(:specimen_event_parameters, :specimen_collection_site, :clinical_diagnosis, :collection_status)
|
48
|
+
|
49
|
+
set_secondary_key_attributes(:name)
|
50
|
+
|
51
|
+
set_alternate_key_attributes(:surgical_pathology_number, :collection_protocol_registration)
|
52
|
+
|
53
|
+
# caTissue alert - An auto-generated SCG in turn auto-generates a ConsentTierStatus
|
54
|
+
# for each ConsentTierResponse defined in the SCG owner CPR.
|
55
|
+
#
|
56
|
+
# caTissue alert - SCG consent_tier_statuses is cascaded but not fetched.
|
57
|
+
add_dependent_attribute(:consent_tier_statuses, :autogenerated, :unfetched)
|
58
|
+
|
59
|
+
# SCG event parameters are disjoint, since they are owned by either a SCG or a Specimen.
|
60
|
+
# An auto-generated SCG also auto-generates the Collection and Received SEPs.
|
61
|
+
# A SCG SEP is only created as a dependent when the SCG is created. A SCG SEP
|
62
|
+
# cannot be created for an existing SCG. By contrast, a Specimen SEP can only be
|
63
|
+
# created, not updated.
|
64
|
+
#
|
65
|
+
# caTissue alert - SpecimenCollectionGroup auto-generated update ignores the referenced
|
66
|
+
# specimen_event_parameters and instead creates new parameters. This occurs only on the
|
67
|
+
# first update, and an SEP cannot be added to an existing, updated SCG. Work-around is
|
68
|
+
# to update the SCG template without the parameters, then update the parameters separately.
|
69
|
+
add_dependent_attribute(:specimen_event_parameters, :autogenerated, :disjoint)
|
70
|
+
|
71
|
+
# SCG Specimens are auto-generated from SpecimenRequirement templates when the SCG is created.
|
72
|
+
# The Specimens are not cascaded.
|
73
|
+
add_dependent_attribute(:specimens, :logical, :autogenerated)
|
74
|
+
|
75
|
+
# The CPE-SCG association is bi-directional.
|
76
|
+
set_attribute_inverse(:collection_protocol_event, :specimen_collection_groups)
|
77
|
+
|
78
|
+
# CPE is fetched but not cascaded.
|
79
|
+
qualify_attribute(:collection_protocol_event, :fetched)
|
80
|
+
|
81
|
+
# caTissue alert - Augment the cascaded reference attributes to work around a CaTissue bug that requires that a
|
82
|
+
# SpecimenCollectionGroup update object references a CollectionProtocolRegistration with a Participant reference.
|
83
|
+
# The CPR identifier should be sufficient for a SCG update, but caTissue bizlogic requires an extraneous
|
84
|
+
# CPR -> Participant reference.
|
85
|
+
# TODO - file a bug and reference it here
|
86
|
+
qualify_attribute(:collection_protocol_registration, :cascaded)
|
87
|
+
|
88
|
+
# caTissue alert - Bug #65: Although SCG name uniquely identifies a SCG, the SCG name is auto-generated on create
|
89
|
+
# and cannnot be set by the client. Therefore, name is marked as update_only.
|
90
|
+
qualify_attribute(:name, :autogenerated, :update_only)
|
91
|
+
|
92
|
+
# caTissue alert - Bug #64: Initialize the SCG consent_tier_statuses to an empty set.
|
93
|
+
def initialize(params=nil)
|
94
|
+
super
|
95
|
+
# work around caTissue Bug #64
|
96
|
+
self.consent_tier_statuses ||= Java::JavaUtil::LinkedHashSet.new
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [CollectionProtocol] the SCG CPE CP
|
100
|
+
def collection_protocol
|
101
|
+
collection_protocol_event.collection_protocol if collection_protocol_event
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [Double, nil] the SCG CPE event point
|
105
|
+
def event_point
|
106
|
+
collection_protocol_event and collection_protocol_event.study_calendar_event_point
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [<SpecimenRequirement>] the SCG CPE requirements
|
110
|
+
def requirements
|
111
|
+
collection_protocol_event.nil? ? Array::EMPTY_ARRAY : collection_protocol_event.requirements
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [Boolean] whether this SCG collection status is one of the +Pending+ statuses.
|
115
|
+
def pending?
|
116
|
+
collection_status =~ /^Pending/
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns the number of specimens in this SpecimenCollectionGroup.
|
120
|
+
def size
|
121
|
+
specimens.size
|
122
|
+
end
|
123
|
+
|
124
|
+
# Removes associations to this registration.
|
125
|
+
def delete
|
126
|
+
registration.specimen_collection_groups.delete(self) if registration
|
127
|
+
end
|
128
|
+
|
129
|
+
# Merges the other object into this SpecimenCollectionGroup. This method overrides the
|
130
|
+
# standard {CaRuby::Resource#merge_attributes} method as follows:
|
131
|
+
# * Builds the SpecimenEventParameter objects from atomic parameters, e.g.:
|
132
|
+
# SpecimenCollectionGroup.create(:name = > name, ..., :collector => collector, :receiver => receiver)
|
133
|
+
# The supported event parameters are described in {#collect}.
|
134
|
+
# * Adds the transitive closure of each non-derived Specimen in other.
|
135
|
+
def merge_attributes(other, attributes=nil)
|
136
|
+
if Hash === other then
|
137
|
+
# extract the event parameters
|
138
|
+
other[:specimen_event_parameters] = extract_event_params(other)
|
139
|
+
# take the transitive closure of the specimens
|
140
|
+
spcs = other.delete(:specimens)
|
141
|
+
if spcs then
|
142
|
+
spcs = [spcs] if CaTissue::Specimen === spcs
|
143
|
+
# take the transitive closure of the root specimens in the hierarchy
|
144
|
+
other[:specimens] = spcs.select { |spc| spc.parent.nil? }.transitive_closure(:children)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
# delegate to super for standard attribute value merge
|
148
|
+
super
|
149
|
+
end
|
150
|
+
|
151
|
+
# Collects and receives this SCG with the given params option => value hash.
|
152
|
+
# The SCG collection status is set to 'Completed'.
|
153
|
+
#
|
154
|
+
# Supported parameters are as follows:
|
155
|
+
# * receiver - the required User who received the specimens
|
156
|
+
# * received_date - the optional receival date (default is now)
|
157
|
+
# * collector - the optional User who collected the specimens (default is receiver)
|
158
|
+
# * collection_date - the optional collection date (default is received_date)
|
159
|
+
#
|
160
|
+
# If there is at least a receiver, then no additional event parameters are added.
|
161
|
+
#
|
162
|
+
# Raises ValidationError if this SCG has already been received.
|
163
|
+
def collect(params)
|
164
|
+
raise ValidationError.new("SCG is already collected") if received?
|
165
|
+
specimen_event_parameters.merge!(extract_event_params(params))
|
166
|
+
end
|
167
|
+
|
168
|
+
def protocol
|
169
|
+
collection_event.protocol if collection_event
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns whether this SCG has a collected event.
|
173
|
+
def collected?
|
174
|
+
collection_event_parameters
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns the User who collected this SCG.
|
178
|
+
def collector
|
179
|
+
ep = collection_event_parameters
|
180
|
+
ep.user if ep
|
181
|
+
end
|
182
|
+
|
183
|
+
# @return [Date] the date this SCG was donated by the participant.
|
184
|
+
def collection_timestamp
|
185
|
+
ep = collection_event_parameters
|
186
|
+
ep.timestamp if ep
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns the CollectionEventParameters for this specimen group.
|
190
|
+
def collection_event_parameters
|
191
|
+
event_parameters.detect { |ep| CaTissue::CollectionEventParameters === ep }
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns whether this SCG has a received event.
|
195
|
+
def received?
|
196
|
+
received_event_parameters
|
197
|
+
end
|
198
|
+
|
199
|
+
# Returns the User who received this specimen group.
|
200
|
+
def receiver
|
201
|
+
ep = received_event_parameters
|
202
|
+
ep.user if ep
|
203
|
+
end
|
204
|
+
|
205
|
+
# Returns the ReceivedEventParameters for this specimen group.
|
206
|
+
def received_event_parameters
|
207
|
+
event_parameters.detect { |ep| CaTissue::ReceivedEventParameters === ep }
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns whether this SCG is the same as the other SCG in the scope of an existing parent CPR.
|
211
|
+
# This method returns whether the other SCG status is Pending and the event point is the
|
212
|
+
# same as the other event point.
|
213
|
+
def minimal_match?(other)
|
214
|
+
super and event_point == other.event_point
|
215
|
+
end
|
216
|
+
|
217
|
+
# Overrides {CaRuby::Resource#direct_dependents} in the case of the _specimens_ attribute to select
|
218
|
+
# only top-level Specimens not derived from another Specimen.
|
219
|
+
def direct_dependents(attribute)
|
220
|
+
if attribute == :specimens then
|
221
|
+
super.reject { |spc| spc.parent }
|
222
|
+
else
|
223
|
+
super
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def validate
|
228
|
+
super
|
229
|
+
validate_consent
|
230
|
+
validate_event_parameters
|
231
|
+
end
|
232
|
+
|
233
|
+
# Restricts the {CaRuby::Persistable#fetch_saved?} condition for a SCG as follows:
|
234
|
+
#
|
235
|
+
# If the SCG status was created or was updated from +Pending+ to +Collected+,
|
236
|
+
# then fetch the saved SCG. Otherwise, forego fetching the saved SCG.
|
237
|
+
#
|
238
|
+
# @return (see CaRuby::Persistable#fetch_saved)
|
239
|
+
def fetch_saved?
|
240
|
+
super and status_changed_to_complete?
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
SAVED_FETCH_MSG = "Saved #{qp} %s be fetched from the database to reflect the current database state, since it was %s."
|
246
|
+
|
247
|
+
# @see #fetch_saved
|
248
|
+
def status_changed_to_complete?
|
249
|
+
if identifier.nil? then
|
250
|
+
logger.debug { format_saved_fetch_message(true, "created") }
|
251
|
+
true
|
252
|
+
elsif collected? and (snapshot.nil? or snapshot[:collection_status] == 'Pending') then
|
253
|
+
logger.debug { format_saved_fetch_message(true, "updated to status Complete") }
|
254
|
+
true
|
255
|
+
else
|
256
|
+
logger.debug { format_saved_fetch_message(false, "not updated from pending to complete") }
|
257
|
+
false
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def format_saved_fetch_message(fetch, reason)
|
262
|
+
args = fetch ? [qp, 'must', reason] : [qp, 'does not need to', reason]
|
263
|
+
SAVED_FETCH_MSG % args
|
264
|
+
end
|
265
|
+
|
266
|
+
def each_defaults_dependent
|
267
|
+
# visit ReceivedEventParameters first
|
268
|
+
rep = received_event_parameters
|
269
|
+
yield rep if rep
|
270
|
+
# add other dependent defaults
|
271
|
+
each_dependent { |dep| yield dep unless ReceivedEventParameters === dep }
|
272
|
+
end
|
273
|
+
|
274
|
+
# The default In Transit collection site.
|
275
|
+
DEF_SITE = Site.new(:name => 'In Transit')
|
276
|
+
|
277
|
+
# Adds defaults as follows:
|
278
|
+
# * the default collection event is the first event in the protocol registered with this SCG.
|
279
|
+
# * the default collection status is 'Complete' if there is a received event, 'Pending' otherwise.
|
280
|
+
# * the default collection site is the CP site, if this SCG is {#received?} and there is only CP one,
|
281
|
+
# otherwise the 'In Transit' site.
|
282
|
+
# * the default conset tier status is 'Complete' if there is a received event, 'Pending' otherwise.
|
283
|
+
# * a default ReceivedEventParameters is added to this SCG if the collection status is
|
284
|
+
# 'Complete' and there is no other ReceivedEventParameters. The receiver is an arbitrary
|
285
|
+
# protocol coordinator.
|
286
|
+
#
|
287
|
+
# Raises ValidationError if the default ReceivedEventParameters could not be created because
|
288
|
+
# there is no protocol or protocol coordinator.
|
289
|
+
#
|
290
|
+
# @see CollectionProtocol#first_event
|
291
|
+
def add_defaults_local
|
292
|
+
super
|
293
|
+
# the default event
|
294
|
+
self.collection_protocol_event ||= default_collection_event
|
295
|
+
|
296
|
+
# the default collection status and received parameters
|
297
|
+
if received? then
|
298
|
+
self.collection_status ||= 'Complete'
|
299
|
+
elsif collection_status == 'Complete' then
|
300
|
+
create_default_received_event_parameters
|
301
|
+
else
|
302
|
+
self.collection_status ||= 'Pending'
|
303
|
+
end
|
304
|
+
# the default collection event
|
305
|
+
if received? and not collected? then
|
306
|
+
create_default_collection_event_parameters
|
307
|
+
end
|
308
|
+
|
309
|
+
# the default site
|
310
|
+
self.collection_site ||= default_site
|
311
|
+
|
312
|
+
# the default CT statuses
|
313
|
+
make_default_consent_tier_statuses
|
314
|
+
end
|
315
|
+
|
316
|
+
# Makes a consent status for each registration consent.
|
317
|
+
#
|
318
|
+
# caTissue alert - Bug #156: SCG without consent status displays error.
|
319
|
+
# A SCG consent tier status is required for each consent tier in the SCG registration.
|
320
|
+
def make_default_consent_tier_statuses
|
321
|
+
return if registration.nil? or registration.consent_tier_responses.empty?
|
322
|
+
|
323
|
+
# the consent tiers
|
324
|
+
ctses = consent_tier_statuses.map { |cts| cts.consent_tier }
|
325
|
+
# ensure that there is a CT status for each consent tier
|
326
|
+
registration.consent_tier_responses.each do |ctr|
|
327
|
+
ct = ctr.consent_tier
|
328
|
+
next if ctses.include?(ct)
|
329
|
+
cts = CaTissue::ConsentTierStatus.new(:consent_tier => ct).add_defaults
|
330
|
+
consent_tier_statuses << cts
|
331
|
+
logger.debug { "Made default #{qp} #{cts.qp} for consent tier #{ct.qp}." }
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def default_site
|
336
|
+
cp = collection_event.protocol if collection_event
|
337
|
+
cp && cp.sites.size == 1 ? cp.sites.first : DEF_SITE
|
338
|
+
end
|
339
|
+
|
340
|
+
# Returns the first event in the protocol registered with this SCG.
|
341
|
+
def default_collection_event
|
342
|
+
return if registration.nil?
|
343
|
+
pcl = registration.protocol || return
|
344
|
+
# if no protocol event, then add the default event
|
345
|
+
pcl.add_defaults if pcl.events.empty?
|
346
|
+
pcl.first_event
|
347
|
+
end
|
348
|
+
|
349
|
+
def create_default_received_event_parameters
|
350
|
+
cp = collection_protocol
|
351
|
+
if cp.nil? then
|
352
|
+
raise ValidationError.new("SCG with status Complete default CollectionEventParameters could not be created since there is no collection protocol: #{self}")
|
353
|
+
end
|
354
|
+
rcvr = cp.coordinators.first
|
355
|
+
if rcvr.nil? then
|
356
|
+
raise ValidationError.new("SCG with status Complete default CollectionEventParameters could not be created since there is no collection protocol coordinator: #{self}")
|
357
|
+
end
|
358
|
+
# make the REP
|
359
|
+
CaTissue::SpecimenEventParameters.create_parameters(:received, self, :user => rcvr)
|
360
|
+
end
|
361
|
+
|
362
|
+
def create_default_collection_event_parameters
|
363
|
+
rep = received_event_parameters || return
|
364
|
+
# make the CEP from the REP
|
365
|
+
CaTissue::SpecimenEventParameters.create_parameters(:collection, self, :user => rep.user, :timestamp => rep.timestamp)
|
366
|
+
end
|
367
|
+
|
368
|
+
# Verifies that each registration consent tier response has a corresponding SCG consent tier status.
|
369
|
+
def validate_consent
|
370
|
+
return unless registration
|
371
|
+
# the default consent statuses
|
372
|
+
ctses = consent_tier_statuses.map { |cts| cts.consent_tier }
|
373
|
+
registration.consent_tier_responses.each do |ctr|
|
374
|
+
ct = ctr.consent_tier
|
375
|
+
unless ctses.include?(ct) then
|
376
|
+
raise ValidationError.new("#{self} is missing a ConsentTierStatus for consent statement #{ct.statement}")
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# Verifies that a SCG create with collection status 'Pending' does not have event parameters.
|
382
|
+
def validate_event_parameters
|
383
|
+
if identifier.nil? and collection_status == 'Pending' and not event_parameters.empty? then
|
384
|
+
raise ValidationError.new("#{self} create with status Pending cannot include event parameters #{event_parameters.to_series}")
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
# Returns SpecimenEventParameters built from the params associations.
|
389
|
+
# The params used to build the SpecimenEventParameters are removed, since they are
|
390
|
+
# redundant.
|
391
|
+
#
|
392
|
+
# Raises ValidationError if the required collector or receiver features are missing.
|
393
|
+
def extract_event_params(params)
|
394
|
+
# Check if there is an attribute association
|
395
|
+
event_params = params.delete(:specimen_event_parameters) || []
|
396
|
+
# collect additional parameter associations
|
397
|
+
scg_receiver = params.delete(:receiver)
|
398
|
+
scg_collector = params.delete(:collector)
|
399
|
+
scg_collector ||= scg_receiver
|
400
|
+
# if there is not at least a collector, then don't continue parsing
|
401
|
+
return event_params if scg_collector.nil?
|
402
|
+
received_date = params.delete(:received_date)
|
403
|
+
received_date ||= DateTime.now
|
404
|
+
event_params << CaTissue::SpecimenEventParameters.create_parameters(:received, self, :user => scg_receiver, :timestamp => received_date)
|
405
|
+
collection_date = params.delete(:collection_date)
|
406
|
+
collection_date ||= received_date
|
407
|
+
event_params << CaTissue::SpecimenEventParameters.create_parameters(:collection, self, :user => scg_collector, :timestamp => collection_date)
|
408
|
+
logger.debug { "SCG #{self} event parameters: #{event_params.pp_s(:single_line)}" }
|
409
|
+
event_params
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|