caruby-tissue 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. data/History.txt +4 -0
  2. data/bin/crtdump +11 -3
  3. data/bin/{seed → crtseed} +0 -0
  4. data/conf/annotation/pathology_scg/AdditionalFinding.hbm.xml +19 -0
  5. data/conf/annotation/pathology_scg/BasePathologyAnnotation.hbm.xml +260 -0
  6. data/conf/annotation/pathology_scg/BreastMargin.hbm.xml +21 -0
  7. data/conf/annotation/pathology_scg/BreastMarginInvolved.hbm.xml +15 -0
  8. data/conf/annotation/pathology_scg/BreastMarginUninvolved.hbm.xml +14 -0
  9. data/conf/annotation/pathology_scg/CNSMargin.hbm.xml +19 -0
  10. data/conf/annotation/pathology_scg/CNSMarginLocation.hbm.xml +14 -0
  11. data/conf/annotation/pathology_scg/CarcinomaInSituStatus.hbm.xml +14 -0
  12. data/conf/annotation/pathology_scg/ColorectalLocalExcisionMarginUninvolved.hbm.xml +15 -0
  13. data/conf/annotation/pathology_scg/ColorectalResectedMarginUninvolved.hbm.xml +22 -0
  14. data/conf/annotation/pathology_scg/Cytogenetics.hbm.xml +15 -0
  15. data/conf/annotation/pathology_scg/DeepMelanomaMargin.hbm.xml +16 -0
  16. data/conf/annotation/pathology_scg/Details.hbm.xml +14 -0
  17. data/conf/annotation/pathology_scg/DirectExtensionOfTumor.hbm.xml +14 -0
  18. data/conf/annotation/pathology_scg/DistalMargin.hbm.xml +15 -0
  19. data/conf/annotation/pathology_scg/DistanceFromAnalVerge.hbm.xml +23 -0
  20. data/conf/annotation/pathology_scg/DistantMetastasis.hbm.xml +19 -0
  21. data/conf/annotation/pathology_scg/ExcionalBiopsyMarginUninvolved.hbm.xml +15 -0
  22. data/conf/annotation/pathology_scg/ExcisionalBiopsyColorectalDeepMargin.hbm.xml +15 -0
  23. data/conf/annotation/pathology_scg/ExcisionalBiopsyColorectalLateralOrMucosalMargin.hbm.xml +15 -0
  24. data/conf/annotation/pathology_scg/ExtraprostaticExtension.hbm.xml +20 -0
  25. data/conf/annotation/pathology_scg/ExtraprostaticExtensionTissueSites.hbm.xml +14 -0
  26. data/conf/annotation/pathology_scg/GleasonScore.hbm.xml +16 -0
  27. data/conf/annotation/pathology_scg/HistologicGrade.hbm.xml +16 -0
  28. data/conf/annotation/pathology_scg/HistologicType.hbm.xml +19 -0
  29. data/conf/annotation/pathology_scg/HistologicVariantType.hbm.xml +14 -0
  30. data/conf/annotation/pathology_scg/ImmunoPhenotyping.hbm.xml +16 -0
  31. data/conf/annotation/pathology_scg/Invasion.hbm.xml +16 -0
  32. data/conf/annotation/pathology_scg/KidneyMarginLocation.hbm.xml +15 -0
  33. data/conf/annotation/pathology_scg/KidneyNephrectomyMargin.hbm.xml +19 -0
  34. data/conf/annotation/pathology_scg/LateralMelanomaMargin.hbm.xml +16 -0
  35. data/conf/annotation/pathology_scg/LocalExcisionColorectalDeepMargin.hbm.xml +15 -0
  36. data/conf/annotation/pathology_scg/LocalExcisionColorectalLateralMargin.hbm.xml +20 -0
  37. data/conf/annotation/pathology_scg/LungResectionMargin.hbm.xml +17 -0
  38. data/conf/annotation/pathology_scg/LungResectionMarginsUninvolved.hbm.xml +14 -0
  39. data/conf/annotation/pathology_scg/MacroscopicExtentOfTumor.hbm.xml +14 -0
  40. data/conf/annotation/pathology_scg/MesentricMargin.hbm.xml +15 -0
  41. data/conf/annotation/pathology_scg/MetastasisTissueSite.hbm.xml +15 -0
  42. data/conf/annotation/pathology_scg/Microcalcification.hbm.xml +14 -0
  43. data/conf/annotation/pathology_scg/NottinghamHistologicScore.hbm.xml +17 -0
  44. data/conf/annotation/pathology_scg/OtherResectedOrgans.hbm.xml +15 -0
  45. data/conf/annotation/pathology_scg/PancreasMargin.hbm.xml +20 -0
  46. data/conf/annotation/pathology_scg/PancreasMarginInvolvedByInvasiveCarcinoma.hbm.xml +15 -0
  47. data/conf/annotation/pathology_scg/PancreasMarginUninvolvedByInvasiveCarcinoma.hbm.xml +20 -0
  48. data/conf/annotation/pathology_scg/PathologicalStaging.hbm.xml +16 -0
  49. data/conf/annotation/pathology_scg/PolypConfiguration.hbm.xml +15 -0
  50. data/conf/annotation/pathology_scg/PrimaryTumorStage.hbm.xml +14 -0
  51. data/conf/annotation/pathology_scg/ProstateMarginLocation.hbm.xml +15 -0
  52. data/conf/annotation/pathology_scg/ProximalMargin.hbm.xml +15 -0
  53. data/conf/annotation/pathology_scg/RadialMargin.hbm.xml +15 -0
  54. data/conf/annotation/pathology_scg/RadicalProstatectomyMargin.hbm.xml +20 -0
  55. data/conf/annotation/pathology_scg/RegionalLymphNode.hbm.xml +19 -0
  56. data/conf/annotation/pathology_scg/SatelliteNodule.hbm.xml +14 -0
  57. data/conf/annotation/pathology_scg/SpecimenCollectionGroup.hbm.xml +87 -0
  58. data/conf/annotation/pathology_scg/SpecimenIntegrity.hbm.xml +15 -0
  59. data/conf/annotation/pathology_scg/SpecimenSize.hbm.xml +17 -0
  60. data/conf/annotation/pathology_scg/TissueSide.hbm.xml +14 -0
  61. data/conf/annotation/pathology_scg/TumorSize.hbm.xml +29 -0
  62. data/conf/annotation/pathology_scg/TumorTissueSite.hbm.xml +20 -0
  63. data/conf/annotation/pathology_scg/UninvolvedMelanomaMargin.hbm.xml +15 -0
  64. data/conf/annotation/pathology_specimen/AdditionalFinding.hbm.xml +19 -0
  65. data/conf/annotation/pathology_specimen/AdditionalPathologicFinding.hbm.xml +18 -0
  66. data/conf/annotation/pathology_specimen/Details.hbm.xml +15 -0
  67. data/conf/annotation/pathology_specimen/GleasonScore.hbm.xml +16 -0
  68. data/conf/annotation/pathology_specimen/HistologicGrade.hbm.xml +16 -0
  69. data/conf/annotation/pathology_specimen/HistologicType.hbm.xml +19 -0
  70. data/conf/annotation/pathology_specimen/HistologicVariantType.hbm.xml +14 -0
  71. data/conf/annotation/pathology_specimen/Invasion.hbm.xml +16 -0
  72. data/conf/annotation/pathology_specimen/NottinghamHistologicScore.hbm.xml +17 -0
  73. data/conf/annotation/pathology_specimen/Specimen.hbm.xml +52 -0
  74. data/conf/annotation/pathology_specimen/SpecimenBaseSolidTissuePathologyAnnotation.hbm.xml +73 -0
  75. data/examples/galena/lib/galena/cli/seed.rb +0 -21
  76. data/examples/galena/lib/galena/migration/frozen_shims.rb +6 -5
  77. data/examples/galena/lib/galena/seed/defaults.rb +0 -5
  78. data/{lib → examples/galena/lib}/galena.rb +0 -0
  79. data/lib/catissue/annotation/annotatable.rb +37 -0
  80. data/lib/catissue/annotation/annotatable_class.rb +255 -0
  81. data/lib/catissue/annotation/annotation.rb +49 -0
  82. data/lib/catissue/annotation/annotation_class.rb +277 -0
  83. data/lib/catissue/annotation/annotation_module.rb +77 -0
  84. data/lib/catissue/annotation/hibernate_mapping.rb +46 -0
  85. data/lib/catissue/annotation/proxy.rb +28 -0
  86. data/lib/catissue/annotation/proxy_class.rb +68 -0
  87. data/lib/catissue/cli/migrate.rb +2 -2
  88. data/lib/catissue/cli/smoke.rb +6 -4
  89. data/lib/catissue/database/annotation/annotation_service.rb +75 -61
  90. data/lib/catissue/database/annotation/annotator.rb +17 -76
  91. data/lib/catissue/database/annotation/entity_facade.rb +265 -0
  92. data/lib/catissue/database/annotation/id_generator.rb +62 -0
  93. data/lib/catissue/database/annotation/integration_service.rb +105 -59
  94. data/lib/catissue/database/annotation/reference_writer.rb +150 -0
  95. data/lib/catissue/database/controlled_values.rb +12 -12
  96. data/lib/catissue/database.rb +148 -58
  97. data/lib/catissue/domain/abstract_specimen.rb +40 -14
  98. data/lib/catissue/domain/abstract_specimen_collection_group.rb +1 -3
  99. data/lib/catissue/domain/collection_protocol.rb +13 -5
  100. data/lib/catissue/domain/collection_protocol_event.rb +1 -14
  101. data/lib/catissue/domain/consent_tier_response.rb +2 -0
  102. data/lib/catissue/domain/consent_tier_status.rb +5 -3
  103. data/lib/catissue/domain/container.rb +14 -10
  104. data/lib/catissue/domain/container_position.rb +8 -0
  105. data/lib/catissue/domain/container_type.rb +13 -6
  106. data/lib/catissue/domain/participant.rb +15 -10
  107. data/lib/catissue/domain/site.rb +9 -3
  108. data/lib/catissue/domain/specimen.rb +79 -40
  109. data/lib/catissue/domain/specimen_array.rb +11 -1
  110. data/lib/catissue/domain/specimen_collection_group.rb +79 -41
  111. data/lib/catissue/domain/specimen_event_parameters.rb +5 -8
  112. data/lib/catissue/domain/specimen_position.rb +0 -2
  113. data/lib/catissue/domain/specimen_requirement.rb +1 -1
  114. data/lib/catissue/domain/storage_container.rb +109 -48
  115. data/lib/catissue/domain/storage_type.rb +1 -1
  116. data/lib/catissue/migration/migrator.rb +6 -14
  117. data/lib/catissue/resource.rb +18 -8
  118. data/lib/catissue/util/position.rb +11 -1
  119. data/lib/catissue/util/storable.rb +18 -11
  120. data/lib/catissue/util/storage_type_holder.rb +44 -6
  121. data/lib/catissue/version.rb +1 -1
  122. metadata +86 -35
  123. data/bin/migrate.rb +0 -42
  124. data/bin/seed.rb +0 -43
  125. data/examples/galena/doc/CaTissue/Participant.html +0 -241
  126. data/examples/galena/doc/CaTissue/SpecimenCollectionGroup.html +0 -190
  127. data/examples/galena/doc/CaTissue/StorageContainer.html +0 -179
  128. data/examples/galena/doc/CaTissue/TissueSpecimen.html +0 -320
  129. data/examples/galena/doc/CaTissue.html +0 -93
  130. data/examples/galena/doc/Galena/Seed/Defaults.html +0 -650
  131. data/examples/galena/doc/Galena/Seed.html +0 -203
  132. data/examples/galena/doc/Galena.html +0 -172
  133. data/examples/galena/doc/_index.html +0 -181
  134. data/examples/galena/doc/class_list.html +0 -36
  135. data/examples/galena/doc/css/common.css +0 -1
  136. data/examples/galena/doc/css/full_list.css +0 -53
  137. data/examples/galena/doc/css/style.css +0 -307
  138. data/examples/galena/doc/file.README.html +0 -153
  139. data/examples/galena/doc/file_list.html +0 -38
  140. data/examples/galena/doc/frames.html +0 -13
  141. data/examples/galena/doc/index.html +0 -153
  142. data/examples/galena/doc/js/app.js +0 -202
  143. data/examples/galena/doc/js/full_list.js +0 -149
  144. data/examples/galena/doc/js/jquery.js +0 -154
  145. data/examples/galena/doc/method_list.html +0 -163
  146. data/examples/galena/doc/top-level-namespace.html +0 -112
  147. data/lib/README.html +0 -33
  148. data/lib/catissue/database/annotation/annotatable_service.rb +0 -25
  149. data/lib/catissue/database/annotation/entity_manager.rb +0 -10
  150. data/lib/galena/cli/seed.rb +0 -43
  151. data/lib/galena/migration/filter_shims.rb +0 -43
  152. data/lib/galena/migration/frozen_shims.rb +0 -53
  153. data/lib/galena/seed/defaults.rb +0 -109
@@ -22,8 +22,8 @@ module CaTissue
22
22
  # located_at_position is cascaded but not fetched.
23
23
  add_dependent_attribute(:located_at_position, :unfetched)
24
24
 
25
- # Like SCG, Container name is ignored and assigned by caTissue. Therefore, this
26
- # attribute is marked auto-generated.
25
+ # caTissue alert - Like SCG, the Container save argument name value is ignored and assigned by caTissue
26
+ # to a different value instead. Therefore, this attribute is marked auto-generated.
27
27
  qualify_attribute(:name, :autogenerated)
28
28
 
29
29
  # Returns the ContainerType which constrains a Container in its roles as a Storable
@@ -53,9 +53,6 @@ module CaTissue
53
53
 
54
54
  # Lazy-initializes this Container's capacity to a copy of the {#storable_type} capacity.
55
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
56
  getCapacity or copy_container_type_capacity
60
57
  end
61
58
 
@@ -151,9 +148,11 @@ module CaTissue
151
148
  # coordinate. The default coordinate is the first available slot within this Container.
152
149
  # The storable Storable position is updated to reflect the new location. Returns self.
153
150
  #
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)
151
+ # @param [Storable] storable the item to add
152
+ # @param [CaRuby::Coordinate, nil] coordinate the x-y coordinate to place the item
153
+ # @raise [IndexError] if this Container is full
154
+ # @raise [IndexError] if the row and column are given but exceed the Container bounds
155
+ def add(storable, coordinate=nil)
157
156
  validate_type(storable)
158
157
  loc = create_location(coordinate)
159
158
  pos = storable.position || storable.position_class.new
@@ -219,12 +218,17 @@ module CaTissue
219
218
 
220
219
  # Copies this Container's ContainerType capacity, if it exists, to the Container capacity.
221
220
  #
221
+ # caTissue alert - this method must be called by subclass initializers. The caTissue API
222
+ # does not make the reasonable assumption that the default Container capacity is the
223
+ # ContainerType capacity.
224
+ #
222
225
  # @return [Capacity, nil] the initialized capacity, if any
223
226
  def copy_container_type_capacity
224
227
  return unless container_type and container_type.capacity
225
- self.capacity = container_type.capacity.copy(:rows, :columns)
228
+ self.capacity = cpc = container_type.capacity.copy(:rows, :columns)
229
+ logger.debug { "Initialized #{qp} capacity from #{container_type.qp} capacity #{cpc}." }
226
230
  update_full_flag
227
- capacity
231
+ cpc
228
232
  end
229
233
 
230
234
  def update_full_flag
@@ -17,5 +17,13 @@ module CaTissue
17
17
  set_attribute_inverse(:occupied_container, :located_at_position)
18
18
 
19
19
  qualify_attribute(:parent_container, :fetched)
20
+
21
+ # @raise [ValidationError] if the parent is the same as the occupant
22
+ def validate
23
+ super
24
+ if parent == occupant or (parent.identifier and parent.identifier == occupant.identifier) then
25
+ raise ValidationError.new("#{self} has a circular containment reference to subcontainer #{occupant}")
26
+ end
27
+ end
20
28
  end
21
29
  end
@@ -8,9 +8,10 @@ module CaTissue
8
8
  # Each {ContainerType} subclass is required to implement the container_class method.
9
9
  #
10
10
  # caTissue alert - the ContainerType and Container class hierarchy is a confusing
11
- # olio of entangled relationships. Conceptually, specimens are contained in boxes,
12
- # vials and specimen arrays of various types. When specimens are frozen, these
13
- # containers are placed on a rack in a freezer.
11
+ # olio of entangled relationships. The canonical use case is as follows:
12
+ # * A Specimen is contained in a box, vial or specimen array.
13
+ # * A vial or specimen array can also be placed in a box.
14
+ # * A frozen specimen container is placed on a rack in a freezer.
14
15
  #
15
16
  # This conceptual model is implemented in caTissue as follows:
16
17
  # * The specimen collection container type, e.g. +Citrate Vacutainer+, is captured
@@ -43,18 +44,24 @@ module CaTissue
43
44
  # a specimen position in a box, whereas {CaTissue::SpecimenArrayContent} is functionally
44
45
  # a specimen position in a specimen array.
45
46
  #
46
- # The ContainerType/Container mish-mash is partially alleviated in caRuby as follows:
47
+ # The ContainerType/Container mish-mash is partially rationalized in caRuby as follows:
47
48
  # * {CaTissue::StorageType} and {CaTissue::StorageContainer} include the
48
49
  # {CaTissue::StorageTypeHolder} module, which unifies treatment of contained
49
50
  # types.
50
51
  # * Similarly, {CaTissue::AbstractPosition} and {CaTissue::SpecimenArrayContent} include
51
52
  # the {CaTissue::Position} module, which unifies treatment of positions.
52
- # * Contained child types are consolidated into {CaTissue::StorageTypeHolder#child_types}
53
+ # * Contained child types are consolidated into {CaTissue::StorageTypeHolder#child_types}.
53
54
  # * Similarly, {CaTissue::StorageContainer} child items are consolidated into
54
- # {CaTissue::StorageContainer#child_types}
55
+ # {CaTissue::StorageContainer#child_types}.
55
56
  # * The various container and position classes are augmented with helper methods to
56
57
  # add, move and find specimens and subcontainers. These methods hide the mind-numbing
57
58
  # eccentricity of caTissue specimen storage interaction.
59
+ # The entire amalgamation is further simplifying by introducing the standard Ruby
60
+ # container add method {CaTissue::Container::<<}. The only call the caRuby client
61
+ # needs to make to add a specimen box to a freezer is:
62
+ # freezer << box
63
+ # which places the box in the first available rack slot in the freezer, or:
64
+ # freezer.add box :at =>
58
65
  class ContainerType
59
66
  include Resource
60
67
 
@@ -34,24 +34,29 @@ module CaTissue
34
34
  # it appears as Female in the GUI even though it is null in the database.
35
35
  add_mandatory_attributes(:activity_status, :gender)
36
36
 
37
- add_dependent_attribute(:collection_protocol_registrations, :logical)
37
+ # caTissue alert - Participant CPR cascade is simulated in the bizlogic.
38
+ # See the PMI comment below.
39
+ add_dependent_attribute(:collection_protocol_registrations)
38
40
 
39
41
  add_dependent_attribute(:races)
40
42
 
41
- # PMI is not cascaded, but insert is done in the bizlogic.
43
+ # caTissue alert - Participant PMI is fetched but not cascaded. However, the Participant bizlogic
44
+ # simulates PMI cascade. The bizlogic doesn't document why this is done, but it appears that the
45
+ # reason is to inject an empty PMI if necessary in order to work around a caTissue query bug
46
+ # (see merge_attribute comment). At any rate, mark PMI as cascaded in the caRuby metadata
47
+ # to reflect the bizlogic simulation. However, this designation should be revisited with each
48
+ # release, since if the bizlogic hack is removed then caRuby Participant PMI save will break.
42
49
  add_dependent_attribute(:participant_medical_identifiers)
43
50
 
44
51
  # SSN is a key, if present, but is not required.
45
52
  qualify_attribute(:social_security_number, :optional)
53
+
54
+ # The clinicial annotation.
55
+ add_annotation('Clinical', :package => 'clinical_annotation', :service => 'CA')
46
56
 
47
- qualify_attribute(:collection_protocol_registrations, :fetched)
48
-
49
- qualify_attribute(:participant_medical_identifiers, :fetched)
50
-
51
- def merge_attribute_value(attribute, oldval, newval)
52
- # caTissue alert - remove the autogenerated blank PMI.
53
- # TODO - file bug
54
- # @see CaTissue::Database#query_safe
57
+ # caTissue alert - remove the autogenerated blank PMI.
58
+ # cf. https://cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=436&sid=ef98f502fc0ab242781b7759a0eaff36
59
+ def merge_attribute(attribute, newval, matches=nil)
55
60
  if attribute == :participant_medical_identifiers and newval then
56
61
  CaTissue::Participant.remove_empty_medical_identifier(newval)
57
62
  end
@@ -5,9 +5,6 @@ module CaTissue
5
5
  # The Site domain class.
6
6
  class Site
7
7
  include Resource
8
-
9
- # The default pre-defined caTissue site name.
10
- DEF_SITE_NAME = 'In Transit'
11
8
 
12
9
  # caTissue alert - the Site SCG collection is ignored, since it is not fetched with the Site,
13
10
  # the caCORE query builder doesn't support abstract types, and even if it worked it would
@@ -29,5 +26,14 @@ module CaTissue
29
26
  LABORATORY = 'Laboratory'
30
27
  REPOSITORY = 'Repository'
31
28
  end
29
+
30
+ def self.default_site
31
+ @@def_site ||= new(:name => DEF_SITE_NAME)
32
+ end
33
+
34
+ private
35
+
36
+ # The default pre-defined caTissue site name.
37
+ DEF_SITE_NAME = 'In Transit'
32
38
  end
33
39
  end
@@ -12,7 +12,7 @@ module CaTissue
12
12
  include Validation, Storable, Resource
13
13
 
14
14
  # caTissue alert - Bug #64: Some domain collection properties not initialized.
15
- # Initialize consent_tier_statuses if necessary.
15
+ # Initialize consent_tier_statuses if necessary.
16
16
  #
17
17
  # @return [Java::JavaUtil::Set] the statuses
18
18
  def consent_tier_statuses
@@ -50,9 +50,18 @@ module CaTissue
50
50
  # A child Specimen is auto-generated from a SpecimenRequirement template if it
51
51
  # is part of a hierarchy built by SCG create. Unlike SpecimenRequirement,
52
52
  # Specimen children are cascaded.
53
- add_dependent_attribute(:child_specimens, :autogenerated, :no_cascade_update_to_create)
53
+ qualify_attribute(:child_specimens, :autogenerated)
54
54
 
55
55
  # caTissue alert - Specimen consent_tier_statuses is cascaded but not fetched.
56
+ #
57
+ # caTissue alert - When a Specimen is auto-generated or created, a consent tier
58
+ # status is created for each registration consent. A created Specimen has a
59
+ # a well-formed status record. An auto-generated status record is malformed,
60
+ # missing the Specimen id reference. This differs from the auto-generated
61
+ # SCG status record, which is malformed in a different way which results in
62
+ # a severe display error, as described in Bug #156. The malformed Specimen
63
+ # status record results in displaying null field values in the Specimen Consent
64
+ # page, but otherwise no adverse effects.
56
65
  add_dependent_attribute(:consent_tier_statuses, :unfetched)
57
66
 
58
67
  # caTissue alert - Bug #163: ExternalIdentifer not fetched with Specimen in API.
@@ -87,10 +96,19 @@ module CaTissue
87
96
  # available_quantity is not reflected in the caCORE create or update result.
88
97
  # This is true even though the caTissue GUI supports available_quantity update.
89
98
  # Work-around is to set the :autogenerated flag, which will refetch a saved Specimen
90
- # and reupdate the Specimen if the stored available_quantity differs from the save
91
- # argument.
99
+ # if there is a change to the available_quantity value and will reupdate the Specimen
100
+ # if the fetched available_quantity differs from the save argument value.
92
101
  qualify_attribute(:available_quantity, :autogenerated)
93
102
 
103
+ # caTissue alert - An auto-generated Specimen auto-generates the collection and received events.
104
+ # Therefore, CPR create must match the complete auto-generated SCG-Specimen-Event hierarchy.
105
+ # However, SpecimenRequirement events are not auto-generated. Therefore, the AbstractSpecimen
106
+ # events behavior must be specialized for Specimen but not SpecimenRequirement.
107
+ qualify_attribute(:specimen_events, :autogenerated)
108
+
109
+ # The Specimen pathology annotation.
110
+ add_annotation('Pathology', :package => 'pathology_specimen', :service => 'pathologySpecimen')
111
+
94
112
  # Specimen storage is constrained on the basis of the +specimen_class+.
95
113
  alias :storable_type :specimen_class
96
114
 
@@ -114,11 +132,17 @@ module CaTissue
114
132
  def collected?
115
133
  collection_status == 'Collected'
116
134
  end
117
-
118
- def merge_attribute_value(attribute, oldval, newval)
119
- # caTissue alert - remove the autogenerated blank ExternalIdentifier.
120
- # @see https://cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=436&sid=ef98f502fc0ab242781b7759a0eaff36
121
- # @see CaTissue::Database#query_safe
135
+
136
+ # Relaxes the {CaRuby::Persistable#fetch_saved?} condition for a Specimen as follows:
137
+ # * If the Specimen available_quantity was updated, then fetch the saved Specimen.
138
+ #
139
+ # @return (see CaRuby::Persistable#fetch_saved)
140
+ def fetch_saved?
141
+ super and available_quantity_changed?
142
+ end
143
+ # caTissue alert - remove the autogenerated blank ExternalIdentifier.
144
+ # cf. https://cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=436&sid=ef98f502fc0ab242781b7759a0eaff36
145
+ def merge_attribute(attribute, newval, matches=nil)
122
146
  if attribute == :external_identifiers and newval then
123
147
  CaTissue::Specimen.remove_empty_external_identifier(newval)
124
148
  end
@@ -140,22 +164,21 @@ module CaTissue
140
164
  # Override default {CaRuby::Resource#merge_attributes} to ignore a source SpecimenRequirement parent_specimen.
141
165
  def merge_attributes(other, attributes=nil)
142
166
  case other
143
- when SpecimenRequirement then
144
- # merge with the default requirement merge attributes if necessary
145
- attributes ||= MERGEABLE_RQMT_ATTRS
146
- super(other, attributes)
147
- # copy the requirement characteristics
148
- sc = other.specimen_characteristics
149
- self.specimen_characteristics ||= sc.copy(MERGEABLE_SPC_CHR_ATTRS) if sc
150
- when Hash then
151
- # the requirement template
152
- rqmt = other[:specimen_requirement] || other[:requirement]
153
- # merge the attribute => value hash
154
- super
155
- # merge the SpecimenRequirement after the hash
156
- merge_attributes(rqmt) if rqmt
157
- else
158
- super
167
+ when SpecimenRequirement then
168
+ # merge with the default requirement merge attributes if necessary
169
+ attributes ||= MERGEABLE_RQMT_ATTRS
170
+ super(other, attributes)
171
+ # copy the requirement characteristics
172
+ sc = other.specimen_characteristics
173
+ self.specimen_characteristics ||= sc.copy(MERGEABLE_SPC_CHR_ATTRS) if sc
174
+ when Hashable then
175
+ # the requirement template
176
+ rqmt = other[:specimen_requirement] || other[:requirement]
177
+ # merge the attribute => value hash
178
+ super
179
+ # merge the SpecimenRequirement after the hash
180
+ merge_attributes(rqmt) if rqmt
181
+ else super
159
182
  end
160
183
  self
161
184
  end
@@ -200,6 +223,17 @@ module CaTissue
200
223
  end
201
224
  end
202
225
 
226
+ # Augments {AbstractSpecimen#minimal_match?} with an additional restriction that the other
227
+ # specimen is in pending state. This ensures that a specimen submitted for create matches
228
+ # its auto-generated counterpart but a new specimen can be created even if it matches an
229
+ # existing specimen on the features described in {AbstractSpecimen#minimal_match?}.
230
+ #
231
+ # @param (see CaRuby::Resource#minimal_match?)
232
+ # @return (see CaRuby::Resource#minimal_match?)
233
+ def minimal_match?(other)
234
+ super and other.collection_status == 'Pending'
235
+ end
236
+
203
237
  # @return the SpecimenPosition class which this Specimen's Storable can occupy
204
238
  def position_class
205
239
  CaTissue::SpecimenPosition
@@ -334,7 +368,24 @@ module CaTissue
334
368
  MERGEABLE_RQMT_ATTRS = nondomain_java_attributes - primary_key_attributes
335
369
 
336
370
  MERGEABLE_SPC_CHR_ATTRS = SpecimenCharacteristics.nondomain_java_attributes - SpecimenCharacteristics.primary_key_attributes
337
-
371
+
372
+ # @see #fetch_saved
373
+ def available_quantity_changed?
374
+ oldval = snapshot && snapshot[:available_quantity]
375
+ newval = available_quantity
376
+ if oldval and oldval != newval then
377
+ logger.debug { "Saved #{qp} must be fetched from the database to reflect the current database state, since the available quantity was changed from #{oldval} to #{newval}." }
378
+ true
379
+ else
380
+ false
381
+ end
382
+ end
383
+
384
+ def format_saved_fetch_message(fetch, reason)
385
+ args = fetch ? [qp, 'must', reason] : [qp, 'does not need to', reason]
386
+ SAVED_FETCH_MSG % args
387
+ end
388
+
338
389
  # Adds this Specimen's defaults, as follows:
339
390
  # * The default specimen_collection_group is the parent specimen_collection_group.
340
391
  # * Add default collection and received event parameters if this Specimen is collected.
@@ -353,7 +404,7 @@ module CaTissue
353
404
  # is set to false (0 database value) when the record is inserted.
354
405
  #
355
406
  # caTissue alert - a collected Specimen without a collection and received event parameters
356
- # results in the dreaded 'Severe Error' server message. TODO - isolate and report.
407
+ # results in the dreaded 'Severe Error' caTissue server message. Create default SEPs if necessary.
357
408
  def add_defaults_local
358
409
  super
359
410
  self.specimen_collection_group ||= parent.specimen_collection_group if parent
@@ -373,7 +424,7 @@ module CaTissue
373
424
  # Adds the default collection and received event parameters if the collection status
374
425
  # is +Collected+.
375
426
  def add_default_event_parameters
376
- if collection_status == 'Collected' and specimen_collection_group then
427
+ if collected? and specimen_collection_group then
377
428
  unless event_parameters.detect { |ep| CaTissue::CollectionEventParameters === ep } then
378
429
  CaTissue::CollectionEventParameters.new(:specimen => self, :user => specimen_collection_group.collector)
379
430
  end
@@ -428,18 +479,6 @@ module CaTissue
428
479
  CaTissue.domain_type_with_name(class_name) or raise ArgumentError.new("Specimen class #{class_name} is not recognized for specimen type parameter #{symbol}")
429
480
  end
430
481
 
431
- # def default_specimen_characteristics
432
- # return if specimen_collection_group.nil?
433
- # rqmts = specimen_collection_group.requirements
434
- # # match the requirement specimen type
435
- # rqmt = specimen_collection_group.requirements.detect { |rqmt| specimen_type == rqmt.specimen_type }
436
- # # fallback is match on requirement class and generic requirment specimen type
437
- # rqmt ||= specimen_collection_group.requirements.detect do |rqmt|
438
- # specimen_class === rqmt.specimen_class and rqmt.specimen_type.nil? or rqmt.specimen_type == 'Not Specified'
439
- # end
440
- # rqmt.specimen_characteristics.copy if rqmt
441
- # end
442
-
443
482
  # Decrements this parent's available quantity by the given child's initial quantity, if the specimen types are the same and there
444
483
  # are the relevant quantities.
445
484
  def decrement_derived_quantity(child)
@@ -9,7 +9,17 @@ module CaTissue
9
9
 
10
10
  # caTissue alert - the superclass Container occupied_positions does not apply to SpecimenArray.
11
11
  remove_attribute(:occupied_positions)
12
-
12
+
13
+ # Initializes this instance's child storage types from the given type.
14
+ #
15
+ # @param [<StorageType>] the type to set
16
+ def specimen_array_type=(type)
17
+ setSpecimenArrayType(type)
18
+ unless type.nil? then
19
+ copy_container_type_capacity
20
+ end
21
+ type
22
+ end
13
23
  add_attribute_aliases(:container_type => :specimen_array_type, :contents => :specimen_array_contents)
14
24
 
15
25
  set_attribute_type(:new_specimen_array_order_items, CaTissue::SpecimenArrayOrderItem)
@@ -39,19 +39,28 @@ module CaTissue
39
39
  setSurgicalPathologyNumber(value)
40
40
  end
41
41
 
42
- add_attribute_aliases(:collection_event => :collection_protocol_event,
42
+ add_attribute_aliases(:site => :specimen_collection_site,
43
+ :collection_site => :specimen_collection_site,
44
+ :collection_event => :collection_protocol_event,
43
45
  :event_parameters => :specimen_event_parameters,
44
46
  :events => :specimen_event_parameters,
45
47
  :registration => :collection_protocol_registration)
46
48
 
49
+ # caTissue alert - Bug #116: specimen_collection_site is incorrectly attached in the caTissue class
50
+ # model to AbstractSpecimenCollectionGroup rather than SpecimenCollectionGroup. CollectionProtocolEvent
51
+ # is a subclass of AbstractSpecimenCollectionGroup but does not have a collection site. Therfore, the
52
+ # specimen_collection_site is ignored in the caRuby AbstractSpecimenCollectionGroup class declaration
53
+ # and declared as an aliased, mandatory, fetched attribute for the SpecimenCollectionGroup subclass only.
47
54
  add_mandatory_attributes(:specimen_event_parameters, :specimen_collection_site, :clinical_diagnosis, :collection_status)
55
+
56
+ qualify_attribute(:specimen_collection_site, :fetched)
48
57
 
49
58
  set_secondary_key_attributes(:name)
50
59
 
51
60
  set_alternate_key_attributes(:surgical_pathology_number, :collection_protocol_registration)
52
61
 
53
- # caTissue alert - An auto-generated SCG in turn auto-generates a ConsentTierStatus
54
- # for each ConsentTierResponse defined in the SCG owner CPR.
62
+ # caTissue alert - An auto-generated or created SCG auto-generates a ConsentTierStatus for each
63
+ # ConsentTierResponse defined in the SCG owner CPR.
55
64
  #
56
65
  # caTissue alert - SCG consent_tier_statuses is cascaded but not fetched.
57
66
  add_dependent_attribute(:consent_tier_statuses, :autogenerated, :unfetched)
@@ -70,6 +79,12 @@ module CaTissue
70
79
 
71
80
  # SCG Specimens are auto-generated from SpecimenRequirement templates when the SCG is created.
72
81
  # The Specimens are not cascaded.
82
+ #
83
+ # caTissue alert - SCG specimens query result does not set the Specimen children and parent, even
84
+ # though they are guaranteed to be in the SCG specimens result set. The children and parent must be
85
+ # fetched separately, resulting in redundant copies of the same Specimen and additional fetches.
86
+ # caRuby partially rectifies this lapse by reconciling the SCG specimens parent-child relationships
87
+ # within the SCG scope.
73
88
  add_dependent_attribute(:specimens, :logical, :autogenerated)
74
89
 
75
90
  # The CPE-SCG association is bi-directional.
@@ -82,12 +97,14 @@ module CaTissue
82
97
  # SpecimenCollectionGroup update object references a CollectionProtocolRegistration with a Participant reference.
83
98
  # The CPR identifier should be sufficient for a SCG update, but caTissue bizlogic requires an extraneous
84
99
  # CPR -> Participant reference.
85
- # TODO - file a bug and reference it here
86
100
  qualify_attribute(:collection_protocol_registration, :cascaded)
87
101
 
88
102
  # caTissue alert - Bug #65: Although SCG name uniquely identifies a SCG, the SCG name is auto-generated on create
89
103
  # and cannnot be set by the client. Therefore, name is marked as update_only.
90
104
  qualify_attribute(:name, :autogenerated, :update_only)
105
+
106
+ # The SCG pathology annotation.
107
+ add_annotation('Pathology', :package => 'pathology_scg', :service => 'pathologySCG')
91
108
 
92
109
  # caTissue alert - Bug #64: Initialize the SCG consent_tier_statuses to an empty set.
93
110
  def initialize(params=nil)
@@ -223,6 +240,17 @@ module CaTissue
223
240
  super
224
241
  end
225
242
  end
243
+
244
+ CONSENT_TIER_STATUS_ATTRS = [:consent_tier_statuses]
245
+
246
+ def mandatory_attributes
247
+ attrs = super
248
+ if registration and not registration.consent_tier_responses.empty? then
249
+ attrs + CONSENT_TIER_STATUS_ATTRS
250
+ else
251
+ attrs
252
+ end
253
+ end
226
254
 
227
255
  def validate
228
256
  super
@@ -230,60 +258,58 @@ module CaTissue
230
258
  validate_event_parameters
231
259
  end
232
260
 
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.
261
+ # Relaxes the {CaRuby::Persistable#saved_fetch_attributes} condition for a SCG as follows:
262
+ # * If the SCG status was updated from +Pending+ to +Collected+, then fetch the saved SCG event parameters.
237
263
  #
238
- # @return (see CaRuby::Persistable#fetch_saved)
239
- def fetch_saved?
240
- super and status_changed_to_complete?
264
+ # @param (see CaRuby::Persistable#saved_fetch_attributes)
265
+ # @return (see CaRuby::Persistable#saved_fetch_attributes)
266
+ def saved_fetch_attributes(operation)
267
+ operation == :update && status_changed_to_complete? ? EVENT_PARAM_ATTRS : super
268
+ end
269
+
270
+ # Relaxes the {CaRuby::Persistable#saved_fetch_attributes} condition for a SCG as follows:
271
+ # * If the SCG status was updated from +Pending+ to +Collected+, then fetch the saved SCG event parameters.
272
+ #
273
+ # @param (see CaRuby::Persistable#saved_fetch_attributes)
274
+ # @return (see CaRuby::Persistable#saved_fetch_attributes)
275
+ def autogenerated?(operation)
276
+ operation == :update && status_changed_to_complete? ? EVENT_PARAM_ATTRS : super
241
277
  end
242
278
 
243
279
  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
-
280
+
281
+ EVENT_PARAM_ATTRS = [:specimen_event_parameters]
282
+
247
283
  # @see #fetch_saved
248
284
  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") }
285
+ if collected? and snapshot and snapshot[:collection_status] == 'Pending' then
286
+ logger.debug { "Saved #{qp} event parameters must be fetched from the database to reflect the current database state, since the status was changed from Pending to Complete." }
254
287
  true
255
288
  else
256
- logger.debug { format_saved_fetch_message(false, "not updated from pending to complete") }
257
289
  false
258
290
  end
259
291
  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
292
 
266
293
  def each_defaults_dependent
267
294
  # visit ReceivedEventParameters first
268
295
  rep = received_event_parameters
269
296
  yield rep if rep
270
297
  # add other dependent defaults
271
- each_dependent { |dep| yield dep unless ReceivedEventParameters === dep }
298
+ each_dependent { |dep| yield dep unless ReceivedEventParameters === dep }
272
299
  end
273
300
 
274
301
  # Adds defaults as follows:
275
- # * the default collection event is the first event in the protocol registered with this SCG.
276
- # * the default collection status is 'Complete' if there is a received event, 'Pending' otherwise.
277
- # * the default collection site is the CP site, if this SCG is {#received?} and there is only CP one,
278
- # otherwise the {CaTissue::Site::DEF_SITE_NAME} site.
279
- # * the default conset tier status is 'Complete' if there is a received event, 'Pending' otherwise.
280
- # * a default ReceivedEventParameters is added to this SCG if the collection status is
302
+ # * The default collection event is the first event in the protocol registered with this SCG.
303
+ # * The default collection status is 'Complete' if there is a received event, 'Pending' otherwise.
304
+ # * The default collection site is the CP site, if this SCG is {#received?} and there is only CP one,
305
+ # otherwise the {CaTissue::Site.default_site}.
306
+ # * The default conset tier status is 'Complete' if there is a received event, 'Pending' otherwise.
307
+ # * A default ReceivedEventParameters is added to this SCG if the collection status is
281
308
  # 'Complete' and there is no other ReceivedEventParameters. The receiver is an arbitrary
282
309
  # protocol coordinator.
283
310
  #
284
- # Raises ValidationError if the default ReceivedEventParameters could not be created because
285
- # there is no protocol or protocol coordinator.
286
- #
311
+ # @raise [ValidationError] if the default ReceivedEventParameters could not be created because
312
+ # there is no protocol or protocol coordinator
287
313
  # @see CollectionProtocol#first_event
288
314
  def add_defaults_local
289
315
  super
@@ -322,16 +348,22 @@ module CaTissue
322
348
  # ensure that there is a CT status for each consent tier
323
349
  registration.consent_tier_responses.each do |ctr|
324
350
  ct = ctr.consent_tier
351
+ # skip if there is a status for the response tier
325
352
  next if ctses.include?(ct)
326
- cts = CaTissue::ConsentTierStatus.new(:consent_tier => ct).add_defaults
353
+ # make a new status
354
+ cts = CaTissue::ConsentTierStatus.new(:consent_tier => ct)
355
+ cts.add_defaults
327
356
  consent_tier_statuses << cts
328
357
  logger.debug { "Made default #{qp} #{cts.qp} for consent tier #{ct.qp}." }
329
358
  end
330
359
  end
331
360
 
332
361
  def default_site
333
- cp = collection_event.protocol if collection_event
334
- cp.default_site if cp
362
+ return if collection_event.nil?
363
+ cp = collection_event.protocol || return
364
+ site = cp.default_site || return
365
+ logger.debug { "Default #{qp} site is #{site}." }
366
+ site
335
367
  end
336
368
 
337
369
  # Returns the first event in the protocol registered with this SCG.
@@ -340,7 +372,9 @@ module CaTissue
340
372
  pcl = registration.protocol || return
341
373
  # if no protocol event, then add the default event
342
374
  pcl.add_defaults if pcl.events.empty?
343
- pcl.first_event
375
+ ev = pcl.first_event || return
376
+ logger.debug { "Default #{qp} collection event is the registration protocol #{pcl.qp} first event #{ev.qp}." }
377
+ ev
344
378
  end
345
379
 
346
380
  def create_default_received_event_parameters
@@ -353,13 +387,17 @@ module CaTissue
353
387
  raise ValidationError.new("SCG with status Complete default CollectionEventParameters could not be created since there is no collection protocol coordinator: #{self}")
354
388
  end
355
389
  # make the REP
356
- CaTissue::SpecimenEventParameters.create_parameters(:received, self, :user => rcvr)
390
+ ev = CaTissue::SpecimenEventParameters.create_parameters(:received, self, :user => rcvr)
391
+ logger.debug { "Made default #{qp} received event parameter #{ev.qp}." }
392
+ ev
357
393
  end
358
394
 
359
395
  def create_default_collection_event_parameters
360
396
  rep = received_event_parameters || return
361
397
  # make the CEP from the REP
362
- CaTissue::SpecimenEventParameters.create_parameters(:collection, self, :user => rep.user, :timestamp => rep.timestamp)
398
+ ev = CaTissue::SpecimenEventParameters.create_parameters(:collection, self, :user => rep.user, :timestamp => rep.timestamp)
399
+ logger.debug { "Made default #{qp} collected event parameter #{ev.qp}." }
400
+ ev
363
401
  end
364
402
 
365
403
  # Verifies that each registration consent tier response has a corresponding SCG consent tier status.
@@ -46,14 +46,11 @@ module CaTissue
46
46
  end
47
47
  event_params = klass.new(params)
48
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}")
49
+ when SpecimenCollectionGroup then event_params.specimen_collection_group = scg_or_specimen
50
+ when Specimen then event_params.specimen = scg_or_specimen
51
+ when nil then raise ArgumentError.new("Missing SpecimenEventParameters scg_or_specimen factory argument")
52
+ else
53
+ raise ArgumentError.new("Unsupported SpecimenEventParameters factory argument - expected SpecimenCollectionGroup or Specimen, found #{scg_or_specimen.class}")
57
54
  end
58
55
  event_params
59
56
  end
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  module CaTissue
4
2
  # import the Java class
5
3
  java_import('edu.wustl.catissuecore.domain.SpecimenPosition')
@@ -34,7 +34,7 @@ module CaTissue
34
34
  # Unlike Specimen, a child SpecimenRequirement is not cascaded by caTissue.
35
35
  # It is not auto-generated, i.e. it is not created from a template when the
36
36
  # parent CPE is created.
37
- add_dependent_attribute(:child_specimens, :logical)
37
+ qualify_attribute(:child_specimens, :logical)
38
38
 
39
39
  # Overrides {Resource#owner} to return the parent_specimen, if it exists, or the collection_protocol_event otherwise.
40
40
  def owner