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
@@ -28,18 +28,6 @@ module CaTissue
28
28
  class ControlledValues
29
29
  include Singleton
30
30
 
31
- PUBLIC_ID_ROOTS_STMT = "select identifier, value from catissue_permissible_value where public_id = ? and parent_identifier is null or parent_identifier = 0"
32
-
33
- CHILDREN_STMT = "select identifier, value from catissue_permissible_value where parent_identifier = ?"
34
-
35
- INSERT_STMT = "insert into catissue_permissible_value (identifier, parent_identifier, public_id, value) values (?, ?, ?, ?)"
36
-
37
- DELETE_STMT = "delete from catissue_permissible_value where identifier = ?"
38
-
39
- MAX_ID_STMT = "select max(identifier) from catissue_permissible_value"
40
-
41
- SEARCH_STMT = "select identifier from catissue_permissible_value where value collate latin1_bin = ?"
42
-
43
31
  def initialize
44
32
  # the default database name, used for direct database access
45
33
  CaTissue.access_properties[:database] ||= CaTissue::Database::DEF_DATABASE_NAME
@@ -99,6 +87,18 @@ module CaTissue
99
87
 
100
88
  private
101
89
 
90
+ PUBLIC_ID_ROOTS_STMT = "select identifier, value from catissue_permissible_value where public_id = ? and parent_identifier is null or parent_identifier = 0"
91
+
92
+ CHILDREN_STMT = "select identifier, value from catissue_permissible_value where parent_identifier = ?"
93
+
94
+ INSERT_STMT = "insert into catissue_permissible_value (identifier, parent_identifier, public_id, value) values (?, ?, ?, ?)"
95
+
96
+ DELETE_STMT = "delete from catissue_permissible_value where identifier = ?"
97
+
98
+ MAX_ID_STMT = "select max(identifier) from catissue_permissible_value"
99
+
100
+ SEARCH_STMT = "select identifier from catissue_permissible_value where value collate latin1_bin = ?"
101
+
102
102
  def load_pid_cvs(pid)
103
103
  fetch_cvs_with_public_id(pid, @pid_value_cv_hash[pid])
104
104
  end
@@ -1,8 +1,7 @@
1
1
  require 'singleton'
2
2
  require 'caruby/util/topological_sync_enumerator'
3
3
  require 'caruby/database'
4
- # TODO - enable DE annotator
5
- #require 'catissue/database/annotation/annotator'
4
+ require 'catissue/database/annotation/annotator'
6
5
  require 'catissue/domain/scg_event_parameters'
7
6
 
8
7
  module CaTissue
@@ -12,20 +11,58 @@ module CaTissue
12
11
  # around caTissue and caCORE bugs.
13
12
  class Database < CaRuby::Database
14
13
  include Singleton
15
-
16
- # The application service name
17
- SERVICE_NAME = 'catissuecore'
18
-
19
- # The default database name
20
- DEF_DATABASE_NAME = 'catissue'
14
+
15
+ # return [CaRuby::SQLExecutor] a utility SQL executor
16
+ attr_reader :executor
21
17
 
22
18
  # Creates a new Database with the {SERVICE_NAME} service and {CaTissue.access_properties}.
23
19
  def initialize
24
20
  super(SERVICE_NAME, CaTissue.access_properties)
21
+ @executor = CaRuby::SQLExecutor.new(CaTissue.access_properties)
25
22
  end
26
23
 
24
+ # @return [Annotator] the annotator utility
25
+ def annotator
26
+ @annotator ||= Annotator.new(self)
27
+ end
28
+
29
+ # If the given domain object is an {Annotation}, then this method returns the {AnnotationService}
30
+ # for the object {AnnotationModule}, otherwise this method returns the standard {CaTissue::Database}
31
+ # service.
32
+ #
33
+ # @return (see CaRuby::Database#persistence_service)
34
+ def persistence_service(obj)
35
+ Annotation === obj ? obj.class.domain_module.persistence_service : super
36
+ end
37
+
38
+ # Augments {CaRuby::Database#ensure_exists} to ensure that an {Annotation::Proxy} reference identifier
39
+ # reflects the hook identifier.
40
+ #
41
+ # @param (see CaRuby::Database#ensure_exists)
42
+ def ensure_exists(ref)
43
+ if Annotation::Proxy === ref then
44
+ ref.ensure_identifier_reflects_hook
45
+ end
46
+ super
47
+ end
48
+
27
49
  private
28
50
 
51
+ # The application service name
52
+ SERVICE_NAME = 'catissuecore'
53
+
54
+ # The default database name
55
+ DEF_DATABASE_NAME = 'catissue'
56
+
57
+ # Returns whether the given specimens are compatible. The target is compatible with the source
58
+ # if each of the following conditions hold:
59
+ # * The specimen types are equal or the source specimen type is +Not Specified+
60
+ # * The specimen pathological statuses are equal or the source pathological status is +Not Specified+
61
+ # * The parent specimens are compatible, if they exist
62
+ #
63
+ # @param [Resource] target the mnerge target specimen
64
+ # @param [Resource] source the mnerge source specimen
65
+ # @return [Boolean] whether the specimens are compatible
29
66
  def specimen_compatible?(target, source)
30
67
  target.class === source and
31
68
  specimen_parent_compatible?(target, source) and
@@ -33,6 +70,7 @@ module CaTissue
33
70
  (target.pathological_status == source.pathological_status or source.pathological_status == 'Not Specified')
34
71
  end
35
72
 
73
+ # @see #specimen_compatible?
36
74
  def specimen_parent_compatible?(target, source)
37
75
  if target.parent then
38
76
  source.parent and source.parent.identifier == target.parent.identifier
@@ -50,6 +88,11 @@ module CaTissue
50
88
  # s1.identifier == s3.identifier. Thus, enforcing reference consistency requires a post-fetch step
51
89
  # that matches the fetched objects to the original query result on identifier and resets the
52
90
  # references.
91
+ #
92
+ # Not yet enabled. TODO - plug this into fetch_object.
93
+ #
94
+ # @param [<Resource>] refs the fetched domain objects
95
+ # @param [Symbol] attribute the owner attribute
53
96
  def resolve_parent(refs, attribute)
54
97
  id_ref_hash = refs.to_compact_hash { |ref| ref.identifier }.invert
55
98
  refs.each do |ref|
@@ -68,7 +111,7 @@ module CaTissue
68
111
  # @return (see CaRuby::Database#update_object)
69
112
  def update_object(obj)
70
113
  if scg_event_parameters?(obj) then
71
- update_scg_event_parameters(obj)
114
+ save_scg_event_parameters(obj)
72
115
  else
73
116
  if CaTissue::Specimen === obj
74
117
  # Specimen activity status is not always set to default; don't know why.
@@ -94,12 +137,16 @@ module CaTissue
94
137
 
95
138
  # Returns whether operation is the second SCG Update described in {#recursive_save?}.
96
139
  def scg_event_update_workaround?(obj, operation)
97
- return false unless CaTissue::SpecimenCollectionGroup == obj and operation == :update
140
+ # Is this an SCG update?
141
+ return false unless CaTissue::SpecimenCollectionGroup === obj and operation == :update
98
142
  last = @operations.last
143
+ # Is the nesting operation on an SEP?
99
144
  return false unless last and scg_event_parameters?(last.subject)
100
145
  ev = last.subject
146
+ # Is the SEP SCG the same as the target SCG?
101
147
  return false unless ev.specimen_collection_group == obj
102
148
  penultimate = @operations[-2]
149
+ # Is the operation which nests the SEP operation a SCG update on the same SCG?
103
150
  penultimate and penultimate.subject == obj
104
151
  end
105
152
 
@@ -178,9 +225,9 @@ module CaTissue
178
225
  SCGEventParameters === obj and obj.specimen_collection_group
179
226
  end
180
227
 
181
- # @param [SCGEventParameters] ep the SCG event parameters to update
228
+ # @param [SCGEventParameters] ep the SCG event parameters to save
182
229
  # @return (see CaRuby::Database#update_object)
183
- def update_scg_event_parameters(ep)
230
+ def save_scg_event_parameters(ep)
184
231
  scg = ep.specimen_collection_group
185
232
  logger.debug { "Work around #{ep.qp} caTissue SCG SpecimenEventParameters update bug by updating the owner #{scg.qp} instead..." }
186
233
  ensure_exists(scg)
@@ -238,10 +285,19 @@ module CaTissue
238
285
  save_dependent(dsp)
239
286
  end
240
287
  end
288
+
289
+ # Overrides {CaRuby::Database#build_save_template} to return obj itself if
290
+ # obj is an {Annotation}, since annotations do not employ a separate template.
291
+ #
292
+ # @param (see CaRuby::Database#build_save_template)
293
+ # @return (see CaRuby::Database#build_save_template)
294
+ def build_save_template(obj, builder)
295
+ Annotation === obj ? obj : super
296
+ end
241
297
 
242
298
  # Overrides {CaRuby::Database::Writer#save_with_template} to work around the following
243
299
  # caTissue bugs:
244
- # * caTissue alert - Bug TODO: CollectionProtocolRegistration must cascade through
300
+ # * caTissue alert - Bug: CollectionProtocolRegistration must cascade through
245
301
  # CP, but the CP events cannot cascade to SpecimenRequirement without raising an
246
302
  # Exception. Work-around is to clear the template CP events.
247
303
  # * caTissue alert - Bug #164: Update Specimen with unchanged ExternalIdentifier fails.
@@ -257,27 +313,43 @@ module CaTissue
257
313
  end
258
314
  logger.debug { "Work around caTissue Bug #164 by setting the #{obj.qp} update template #{template.qp} external_identifiers to nil." }
259
315
  template.external_identifiers = nil
316
+ elsif Annotation === obj and obj.class.proxy_attribute then
317
+ copy_annotation_proxy_owner_to_template(obj, template)
260
318
  end
261
319
  super
262
320
  end
321
+
322
+ # The annotation proxy is not copied because the attribute redirects to the hook rather
323
+ # than the proxy. Set the template copy source proxy to the target object proxy using
324
+ # the low-level Java property methods instead.
325
+ #
326
+ # @param [Annotation] obj the copy source
327
+ # @param [Annotation] template the copy target
328
+ def copy_annotation_proxy_owner_to_template(obj, template)
329
+ pxy_attr = obj.class.proxy_attribute
330
+ attr_md = obj.class.attribute_metadata(pxy_attr)
331
+ # Ignore the proxy attribute if it is defined by caRuby rather than caTissue.
332
+ return unless attr_md.java_property?
333
+ rdr, wtr = obj.class.attribute_metadata(pxy_attr).property_accessors
334
+ pxy = obj.send(rdr)
335
+ logger.debug { "Setting #{obj.qp} template #{template.qp} proxy owner to #{pxy}..." }
336
+ template.send(wtr, pxy)
337
+ end
263
338
 
264
339
  # Augment {CaRuby::Database::Writer#create_object} for the following work-arounds:
265
340
  # * caTissue alert - Bug #124: SpecimenEventParameters with SCG rather than Specimen fails validation.
266
- # Work-around is to create the SEP with the specimen set to a SCG specimen, then unset the
267
- # specimen reference.
268
- # * if obj is a CaTissue::Specimen with the is_available flag set to false, then work around the bug
341
+ # Work-around is to create the SEP by updating the SCG.
342
+ # * If obj is a CaTissue::Specimen with the is_available flag set to false, then work around the bug
269
343
  # described in {#create_unavailable_specimen}.
270
344
  # * caTissue alert - Bug #161: Specimen API disposal not reflected in result activity status.
271
345
  # DisposalEventParameters create sets the owner Specimen activity_status to +Closed+ as a side-effect.
272
346
  # Reflect this side-effect in the submitted DisposalEventParameters owner Specimen object.
273
- # * Ensure that a {CaTissue::Participant} obj registrations reference an existing protocol.
274
- # This compensates for a hole in the storable template logic which does not recognize the
275
- # CPR CP references as a pre-requisite. TODO - isolate, report and fix.
347
+ #
348
+ # @param [Resource] obj the dependent domain object to save
276
349
  def create_object(obj)
277
- # if CaTissue::SCGEventParameters === obj and obj.specimen_collection_group then
278
- # return create_scg_event_parameters(obj) { super }
279
- # elsif CaTissue::Specimen === obj and obj.is_available == false then
280
- if CaTissue::Specimen === obj then
350
+ if scg_event_parameters?(obj) then
351
+ save_scg_event_parameters(obj)
352
+ elsif CaTissue::Specimen === obj then
281
353
  obj.add_defaults
282
354
  if obj.is_available == false or obj.available_quantity.zero? then
283
355
  # Note that the obj.is_available == false test is required as opposed to obj.is_available?,
@@ -363,10 +435,8 @@ module CaTissue
363
435
 
364
436
  def fetch_alternative(obj)
365
437
  case obj
366
- when CaTissue::Specimen then
367
- fetch_specimen_alternative(obj)
368
- when CaTissue::Participant then
369
- fetch_participant_alternative(obj)
438
+ when CaTissue::Specimen then fetch_specimen_alternative(obj)
439
+ when CaTissue::Participant then fetch_participant_alternative(obj)
370
440
  end
371
441
  end
372
442
 
@@ -386,28 +456,66 @@ module CaTissue
386
456
 
387
457
  # caTissue alert - Bug #147: SpecimenRequirement query ignores CPE.
388
458
  # Work around this bug by an inverted query on the referenced CPE.
459
+ #
460
+ # caTissue alert - Accessing an annotation hook DE proxy attribute uses a separate mechanism.
461
+ # Redirect the query to the annotation integration service in that case.
462
+ #
463
+ # caTissue alert - Bug #169: ContainerPosition occupied container query returns parent
464
+ # container instead. Substitute a hard-coded HQL search for this case.
465
+ #
466
+ # @see CaRuby::Database#query_object
389
467
  def query_object(obj, attribute=nil)
390
- if CaTissue::SpecimenRequirement === obj and not obj.identifier and obj.collection_protocol_event then
468
+ if hook_proxy_attribute?(obj, attribute) then
469
+ query_hook_proxy(obj, attribute)
470
+ elsif CaTissue::SpecimenRequirement === obj and not obj.identifier and obj.collection_protocol_event then
391
471
  query_requirement_using_cpe_inversion(obj, attribute)
472
+ elsif CaTissue::ContainerPosition === obj and obj.identifier and attribute == :occupied_container then
473
+ query_container_position_occupied_container(obj, attribute)
392
474
  else
393
475
  super
394
476
  end
395
477
  end
396
-
478
+
479
+ def query_container_position_occupied_container(obj, attribute)
480
+ logger.debug { "Work around caTissue bug by querying #{obj} #{attribute} using HQL..." }
481
+ src = obj.class.java_class.name
482
+ query("select pos.occupiedContainer from #{src} pos where pos.id = #{obj.identifier}")
483
+ end
484
+
485
+ # @param (see #query_object)
486
+ # @return [Boolean] whether the given attribute is a reference from an {Annotatable} to a #{Annotation}
487
+ def hook_proxy_attribute?(obj, attribute)
488
+ return false if attribute.nil?
489
+ attr_md = obj.class.attribute_metadata(attribute)
490
+ attr_md.declarer < Annotatable and attr_md.type < Annotation
491
+ end
492
+
493
+ # Queries on the given object attribute using the {Annotation::IntegationService}.
494
+ #
495
+ # @param (see #query_object)
496
+ # @result (see #query_object)
497
+ def query_hook_proxy(hook, attribute)
498
+ unless hook.identifier then
499
+ logger.debug { "Querying annotation hook #{hook.qp} proxy reference #{attribute} by collecting the matching #{hook.class.qp} proxy references..." }
500
+ return query(hook).map { |ref| query_hook_proxy(ref, attribute) }.flatten
501
+ end
502
+ proxy = hook.annotation_proxy(attribute)
503
+ # update the proxy identifier if necessary
504
+ proxy.identifier ||= hook.identifier
505
+ # delegate to the integration service to find the referenced hook annotation proxies
506
+ logger.debug { "Delegating #{hook.qp} annotation #{attribute} query to proxy #{proxy} integration service query..." }
507
+ annotator.integration_service.query(proxy, attribute)
508
+ end
509
+
397
510
  # caCORE alert - Override {CaRuby::Database::Reader#invertible_query?} to enable the Bug #147 work
398
511
  # around in {#query_object}. Invertible queries are performed to work around Bug #79. However, this
399
512
  # work-around induces Bug #147, so we disable the Bug #79 work-around here for the special case of
400
513
  # a CPE in order to enable the Bug #147 work-around. And so it goes....
514
+ #
515
+ # @see CaRuby::Database#invertible_query?
401
516
  def invertible_query?(obj, attribute)
402
- super and not (CaTissue::CollectionProtocolEvent === obj and attribute == :specimen_requirements)
403
- end
404
-
405
- # Returns the Annotation::Annotator service for klass, or nil if klass is neither annotatable nor an annotation.
406
- def annotation_service(klass)
407
- # create the single annotator on demand
408
- @annotator ||= CaTissue::Annotation::Annotator.new(self)
409
- # find the service or return nil if kla
410
- @annotator.service(klass)
517
+ super and not (hook_proxy_attribute?(obj, attribute) or
518
+ (CaTissue::CollectionProtocolEvent === obj and attribute == :specimen_requirements))
411
519
  end
412
520
 
413
521
  def fetch_participant_alternative(pnt)
@@ -427,23 +535,6 @@ module CaTissue
427
535
  pmi = pnt.medical_identifiers.first
428
536
  return if pmi.nil?
429
537
  logger.debug { "Using alternative Participant fetch strategy to find Participant by medical record number..." }
430
-
431
- #
432
- # TODO - hack to work around intricate add_default logic; ties in with the create participant hack in this class.
433
- # FIX THIS!!
434
- #
435
- if pmi.site.nil? then
436
- # fetch Participant CPR protocols if necessary
437
- pnt.registrations.each do |reg|
438
- pcl = reg.protocol || next
439
- pcl.find unless pcl.identifier
440
- reg.add_defaults
441
- end
442
- pnt.add_defaults
443
- end
444
-
445
- # END OF HACK
446
-
447
538
  return unless exists?(pmi)
448
539
  candidates = query(pmi.copy, :participant)
449
540
  candidates.first if candidates.size == 1
@@ -463,11 +554,10 @@ module CaTissue
463
554
  query(spc)
464
555
  end
465
556
 
466
- def query_requirement_using_cpe_inversion(rqmt, attribute)
557
+ def query_requirement_using_cpe_inversion(rqmt, *path)
467
558
  cpe = rqmt.collection_protocol_event
468
559
  logger.debug { "Using alternative SpecimenRequirement query strategy to find SpecimenRequirements by inverted CPE reference #{cpe}..." }
469
- path = [:specimen_requirements]
470
- path << attribute if attribute
560
+ path.unshift(:specimen_requirements)
471
561
  query(cpe, *path)
472
562
  end
473
563
  end
@@ -9,6 +9,8 @@ module CaTissue
9
9
 
10
10
  # Sets the specimen type to the specified value. The value can be a permissible caTissue String value or
11
11
  # the shortcut symbols :fresh, :fixed and :frozen.
12
+ #
13
+ # @param [String, Symbol, nil] value the value to set
12
14
  def specimen_type=(value)
13
15
  value = value.to_s.capitalize_first + ' Tissue' if Symbol === value
14
16
  setSpecimenType(value)
@@ -24,7 +26,7 @@ module CaTissue
24
26
  # caTissue alert - initial_quantity must be set (cf. Bug #160)
25
27
  add_attribute_defaults(:initial_quantity => 0.0, :pathological_status => 'Not Specified', :specimen_type => 'Not Specified')
26
28
 
27
- add_mandatory_attributes(:lineage, :pathological_status, :specimen_class, :specimen_type)
29
+ add_mandatory_attributes(:lineage, :pathological_status, :specimen_class, :specimen_type, :specimen_characteristics)
28
30
 
29
31
  # Specimen characteristics are auto-generated but SpecimenRequirement characteristics are not.
30
32
  # It is safe to set the :autogenerated flag for both AbstractSpecimen subclasses. This results
@@ -35,18 +37,21 @@ module CaTissue
35
37
  # Guard against updating a Specimen with a SpecimenCharacteristics whose id differs from
36
38
  # the existing id.
37
39
  #
40
+ # caTissue alert - Unlike most dependents, AbstractSpecimen characteristics, events and child
41
+ # specimens have cascade style 'all'. This implies that an AbstractSpecimen update does not
42
+ # create a referenced dependent. AbstractSpecimen create cascades to create and AbstractSpecimen
43
+ # update cascades to update, but AbstractSpecimen update does not cascade to create.
44
+ #
38
45
  # The :no_cascade_update_to_create flag is set since characteristics have cascade style 'all'.
39
46
  add_dependent_attribute(:specimen_characteristics, :autogenerated, :no_cascade_update_to_create)
40
47
 
48
+ # The :no_cascade_update_to_create flag is set since events has cascade style 'all'.
41
49
  add_dependent_attribute(:specimen_events, :create_only, :disjoint, :no_cascade_update_to_create)
50
+
51
+ # The :no_cascade_update_to_create flag is set since child specimens has cascade style 'all'.
52
+ add_dependent_attribute(:child_specimens, :unfetched, :no_cascade_update_to_create)
42
53
 
43
54
  set_attribute_inverse(:parent_specimen, :child_specimens)
44
-
45
- # caTissue alert - SCG specimens query result does not set the Specimen children and parent, even though they are
46
- # guaranteed to be in the SCG specimens result set. The children and parent must be fetched separately, resulting
47
- # in multiple copies of the same Specimen and additional fetches.
48
- qualify_attribute(:parent_specimen)
49
- qualify_attribute(:child_specimens, :no_cascade_update_to_create)
50
55
 
51
56
  class SpecimenClass
52
57
  TISSUE = 'Tissue'
@@ -64,6 +69,10 @@ module CaTissue
64
69
  end
65
70
  end
66
71
 
72
+ # Initializes this AbstractSpecimen. The default +specimen_class+ is inferred from this
73
+ # AbstractSpecimen instance's subclass.
74
+ #
75
+ # @param [{Symbol => Object}] params the specimen attribute => value hash
67
76
  def initialize(params=nil)
68
77
  super
69
78
  self.specimen_class ||= infer_specimen_class(self.class)
@@ -140,9 +149,8 @@ module CaTissue
140
149
  # e.g. 3090_3 for the third child in parent specimen with label 3090.
141
150
  #
142
151
  # @return the new derived specimen if _count_ is one, otherwise an Array of _count_ derived specimens
143
- #
144
- # Raises ValidationError if an aliquoted parent _available_quantity_ is not greater than zero
145
- # or the derived specimen quantities exceed the parent _available_quantity_.
152
+ # @raise [ValidationError] if an aliquoted parent available quantity is not greater than zero
153
+ # or the derived specimen quantities exceed the parent available quantity
146
154
  def derive(params={})
147
155
  # add defaults
148
156
  add_defaults if specimen_class.nil?
@@ -195,6 +203,9 @@ module CaTissue
195
203
 
196
204
  # Sets special aliquot parameters for the given count of aliquots.
197
205
  # This default implementation is a no-op. Subclasses can override.
206
+ #
207
+ # @param [{Symbol => Object}] params the specimen attribute => value hash
208
+ # @param [Integer] count the number of aliquots
198
209
  def set_aliquot_parameters(params, count); end
199
210
 
200
211
  def add_defaults_local
@@ -215,6 +226,9 @@ module CaTissue
215
226
  # 'SpecimenRequirement' depending on this AbstractSpecimen's subclass. If the
216
227
  # :specimen_class parameter is missing and there is a :specimen_requirement parameter,
217
228
  # then the specimen requirement specimen_class attribute value is used.
229
+ #
230
+ # @param [{Symbol => Object}] params the specimen attribute => value hash
231
+ # @return [Class] the AbstactSpecimen subclass to use
218
232
  def infer_class(params)
219
233
  opt = params[:specimen_class]
220
234
  if opt.nil? then
@@ -229,6 +243,10 @@ module CaTissue
229
243
  CaTissue.domain_type_with_name(cls_nm)
230
244
  end
231
245
 
246
+ # Creates a derived specimen.
247
+ #
248
+ # @param [{Symbol => Object}] params the derived specimen attribute => value hash
249
+ # @return [Specimen] the derived specimen
232
250
  def create_derived(params)
233
251
  # Merge the non-domain attribute values from this specimen, unless there is a requirement.
234
252
  # Precedence order is given params highest, requirements next, this specimen lowest
@@ -242,9 +260,11 @@ module CaTissue
242
260
  spc
243
261
  end
244
262
 
245
- # @return a copy of this Specimen's specimen_characteristics, or nil if none
246
- # The new SpecimenCharacteristics does not have an identifier.
247
- def default_derived_characteristics
263
+ # Returns characteristics to use for a derived specimen. The new characteristics is copied from this
264
+ # parent specimen's characteristics, without the identifier.
265
+ #
266
+ # @return [CaTissue::SpecimenCharacteristics, nil] a copy of this Specimen's specimen_characteristics, or nil if none
267
+ def default_derived_characteristics
248
268
  chrs = specimen_characteristics || return
249
269
  attrs = chrs.class.nondomain_attributes.reject { |attr| attr == :identifier }
250
270
  chrs.copy(attrs)
@@ -268,6 +288,8 @@ module CaTissue
268
288
  # not just the same content. This odd condition is a caTissue artifact. A more sensible
269
289
  # criterion is whether this Specimen and its parent share the same tissue_class and
270
290
  # tissue_type, but so it goes.
291
+ #
292
+ # @return ['New', 'Aliqout', 'Derived'] the lineage to use
271
293
  def default_lineage
272
294
  if parent.nil? then
273
295
  'New'
@@ -279,8 +301,12 @@ module CaTissue
279
301
  end
280
302
 
281
303
  # Infers the specimen class from the first word on the specified klass Ruby class name.
282
- # For example:
304
+ #
305
+ # @example
283
306
  # infer_specimen_class(CaTissue::TissueRequirement) #=> "Tissue"
307
+ #
308
+ # @param [Class, nil] the AbstractSpecimen domain object class (default this specimen's class)
309
+ # @return [String] the +specimen_class+ value
284
310
  def infer_specimen_class(klass=self.class)
285
311
  klass.to_s[/(\w+?)(Specimen(Requirement)?)$/, 1] if klass
286
312
  end
@@ -4,13 +4,11 @@ module CaTissue
4
4
  class AbstractSpecimenCollectionGroup
5
5
  include Resource
6
6
 
7
- add_attribute_aliases(:site => :specimen_collection_site, :collection_site => :specimen_collection_site, :diagnosis => :clinical_diagnosis)
7
+ add_attribute_aliases(:diagnosis => :clinical_diagnosis)
8
8
 
9
9
  add_attribute_defaults(:activity_status => 'Active', :clinical_status => 'Not Specified', :clinical_diagnosis => 'Not Specified')
10
10
 
11
11
  add_mandatory_attributes(:activity_status)
12
-
13
- qualify_attribute(:collection_site, :fetched)
14
12
 
15
13
  # Overrides {CaRuby::Resource#each_dependent} to exclude Specimens or SpecimenRequirements with a parent,
16
14
  # since parent is the immediate Specimen or SpecimenRequirement owner.
@@ -142,14 +142,14 @@ module CaTissue
142
142
  # Returns the default protocol site, determined as follows:
143
143
  # * If there is exactly one authorized site for this protocol, then that is the default site.
144
144
  # * If there is exactly two authorized sites for this protocol, then the site other than the
145
- # {CaTissue::Site::DEF_SITE_NAME} is returned.
145
+ # {CaTissue::Site.default_site} is returned.
146
146
  # * Otherwise, this method returns nil.
147
147
  #
148
148
  # @return [CaTissue::Site, nil] the default site
149
149
  def default_site
150
150
  case sites.size
151
151
  when 1 then sites.first
152
- when 2 then sites.select { |site| site.name != CaTissue::Site::DEF_SITE_NAME }
152
+ when 2 then sites.select { |site| site.name != CaTissue::Site.default_site.name }
153
153
  end
154
154
  end
155
155
 
@@ -167,15 +167,23 @@ module CaTissue
167
167
  self.title ||= short_title
168
168
  self.short_title ||= title
169
169
  self.start_date ||= Java::JavaUtil::Date.new
170
+ if sites.empty? then add_default_site end
170
171
  if coordinators.empty? and sites.size == 1 then
171
172
  coord = sites.first.coordinator
172
173
  coordinators << coord if coord
173
- elsif sites.empty? and coordinators.size == 1 then
174
- site = coordinators.first.sites.first
175
- sites << site if site
176
174
  end
177
175
  make_default_collection_event unless events.detect { |evt| CollectionProtocolEvent === evt }
178
176
  end
177
+
178
+ def add_default_site
179
+ if coordinators.size == 1 then
180
+ site = coordinators.first.sites.first
181
+ else
182
+ site = CaTissue::Site.default_site
183
+ site.find unless site.identifier
184
+ end
185
+ sites << site
186
+ end
179
187
 
180
188
  def make_default_collection_event
181
189
  # make this protocol's CPE
@@ -33,10 +33,8 @@ module CaTissue
33
33
  add_mandatory_attributes(:collection_protocol, :clinical_diagnosis, :specimen_requirements)
34
34
 
35
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
36
  # 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)
37
+ add_dependent_attribute(:specimen_requirements, :unfetched)
40
38
 
41
39
  # The event point used for saving this CollectionProtocolEvent if none other is set.
42
40
  DEFAULT_EVENT_POINT = 1.0
@@ -48,17 +46,6 @@ module CaTissue
48
46
  self.specimen_collection_groups ||= Java::JavaUtil::LinkedHashSet.new
49
47
  end
50
48
 
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
49
  # Overrides the Java CollectionProtocolEvent hashCode to make the hash insensitive to identifier assignment.
63
50
  def hash
64
51
  # caTissue alert - caTissue determines the hashCode from the identifier. Consequently, a CollectionProtocolEvent
@@ -8,6 +8,8 @@ module CaTissue
8
8
  add_mandatory_attributes(:consent_tier, :response)
9
9
 
10
10
  add_attribute_defaults(:response => 'Not Specified')
11
+
12
+ qualify_attribute(:consent_tier, :fetched)
11
13
 
12
14
  end
13
15
  end
@@ -8,6 +8,8 @@ module CaTissue
8
8
  add_mandatory_attributes(:consent_tier, :status)
9
9
 
10
10
  add_attribute_defaults(:status => 'Not Specified')
11
+
12
+ qualify_attribute(:consent_tier, :fetched)
11
13
 
12
14
  # Returns whether this ConsentTierStatus is minimally consistent with the other ConsentTierStatus.
13
15
  # This method returns whether the referenced ConsentTier has the same identifer or statement text
@@ -21,9 +23,9 @@ module CaTissue
21
23
  # Returns true if this ConsentTierStatus ConsentTier is nil, the other ConsentTierStatus ConsentTier is nil,
22
24
  # both ConsentTier identifiers are equal, or both ConsentTier statements are equal.
23
25
  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)
26
+ ct = consent_tier
27
+ oct = other.consent_tier
28
+ (ct.nil? or oct.nil?) or (ct.identifier == oct.identifier or ct.statement == oct.statement)
27
29
  end
28
30
  end
29
31
  end