caruby-tissue 1.4.2 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.md +53 -0
- data/bin/crtdump +1 -6
- data/bin/crtexample +1 -6
- data/bin/crtmigrate +1 -6
- data/bin/crtsmoke +1 -6
- data/conf/migration/filter_fields.yaml +1 -1
- data/conf/migration/general_fields.yaml +1 -1
- data/conf/migration/simple_fields.yaml +1 -1
- data/conf/migration/small_fields.yaml +1 -1
- data/examples/galena/README.md +34 -25
- data/examples/galena/bin/seed +2 -7
- data/examples/galena/conf/migration/annotation_fields.yaml +1 -1
- data/examples/galena/conf/migration/filter_fields.yaml +1 -1
- data/examples/galena/conf/migration/frozen_fields.yaml +2 -2
- data/examples/galena/conf/migration/general_fields.yaml +1 -1
- data/examples/galena/conf/migration/registration_fields.yaml +1 -0
- data/examples/galena/conf/migration/simple_fields.yaml +1 -1
- data/examples/galena/data/frozen.csv +1 -1
- data/examples/galena/data/general.csv +1 -1
- data/examples/galena/data/registration.csv +1 -1
- data/examples/galena/lib/galena/tissue/migration/frozen_shims.rb +8 -4
- data/examples/galena/lib/galena/tissue/seed/defaults.rb +16 -9
- data/examples/pcbin/README.md +8 -8
- data/examples/pcbin/conf/biopsy_fields.yaml +1 -1
- data/examples/pcbin/conf/patient_fields.yaml +1 -1
- data/examples/pcbin/conf/surgery_fields.yaml +1 -1
- data/examples/pcbin/conf/t_stage_fields.yaml +1 -1
- data/examples/pcbin/conf/therapy_fields.yaml +1 -1
- data/lib/catissue.rb +1 -1
- data/lib/catissue/annotation/annotatable.rb +12 -22
- data/lib/catissue/annotation/annotatable_class.rb +87 -114
- data/lib/catissue/annotation/annotation.rb +9 -44
- data/lib/catissue/annotation/annotation_class.rb +238 -145
- data/lib/catissue/annotation/annotation_module.rb +47 -59
- data/lib/catissue/annotation/de_integration.rb +12 -2
- data/lib/catissue/annotation/proxy.rb +11 -17
- data/lib/catissue/annotation/proxy_1_1.rb +47 -0
- data/lib/catissue/annotation/proxy_class.rb +55 -37
- data/lib/catissue/annotation/record_entry_proxy.rb +20 -0
- data/lib/catissue/database.rb +164 -112
- data/lib/catissue/database/annotation/annotation_service.rb +5 -5
- data/lib/catissue/database/annotation/annotator.rb +2 -2
- data/lib/catissue/database/annotation/entity_facade.rb +24 -21
- data/lib/catissue/database/annotation/id_generator.rb +26 -26
- data/lib/catissue/database/annotation/integration_service.rb +8 -8
- data/lib/catissue/database/annotation/record_entry_integrator.rb +24 -6
- data/lib/catissue/database/annotation/reference_writer.rb +33 -6
- data/lib/catissue/domain.rb +26 -0
- data/lib/catissue/domain/abstract_position.rb +1 -1
- data/lib/catissue/domain/abstract_specimen.rb +16 -16
- data/lib/catissue/domain/check_in_check_out_event_parameter.rb +1 -1
- data/lib/catissue/domain/collection_event_parameters.rb +1 -1
- data/lib/catissue/domain/collection_protocol.rb +9 -13
- data/lib/catissue/domain/collection_protocol_event.rb +4 -4
- data/lib/catissue/domain/collection_protocol_registration.rb +4 -4
- data/lib/catissue/domain/container.rb +12 -21
- data/lib/catissue/domain/container_type.rb +65 -64
- data/lib/catissue/domain/disposal_event_parameters.rb +1 -1
- data/lib/catissue/domain/hash_code.rb +3 -6
- data/lib/catissue/domain/new_specimen_array_order_item.rb +4 -4
- data/lib/catissue/domain/order_details.rb +3 -3
- data/lib/catissue/domain/participant.rb +28 -20
- data/lib/catissue/domain/participant/clinical/chemotherapy.rb +21 -0
- data/lib/catissue/domain/participant/clinical/duration.rb +11 -0
- data/lib/catissue/domain/participant/clinical/radiation_therapy.rb +21 -0
- data/lib/catissue/domain/participant_medical_identifier.rb +2 -2
- data/lib/catissue/domain/site.rb +6 -6
- data/lib/catissue/domain/specimen.rb +41 -34
- data/lib/catissue/domain/specimen/pathology/prostate_specimen_gleason_score.rb +18 -0
- data/lib/catissue/domain/specimen/pathology/prostate_specimen_pathology_annotation.rb +13 -8
- data/lib/catissue/domain/specimen/pathology/specimen_additional_finding.rb +21 -0
- data/lib/catissue/domain/specimen/pathology/specimen_base_solid_tissue_pathology_annotation.rb +30 -7
- data/lib/catissue/domain/specimen/pathology/specimen_details.rb +18 -0
- data/lib/catissue/domain/specimen/pathology/specimen_histologic_grade.rb +18 -0
- data/lib/catissue/domain/specimen/pathology/specimen_histologic_type.rb +22 -6
- data/lib/catissue/domain/specimen/pathology/specimen_histologic_variant_type.rb +18 -0
- data/lib/catissue/domain/specimen/pathology/specimen_invasion.rb +18 -0
- data/lib/catissue/domain/specimen_array.rb +8 -8
- data/lib/catissue/domain/specimen_array_content.rb +4 -4
- data/lib/catissue/domain/specimen_collection_group.rb +43 -41
- data/lib/catissue/domain/specimen_collection_group/pathology/base_pathology_annotation.rb +16 -0
- data/lib/catissue/domain/specimen_collection_group/pathology/base_solid_tissue_pathology_annotation.rb +16 -0
- data/lib/catissue/domain/specimen_position.rb +3 -3
- data/lib/catissue/domain/specimen_protocol.rb +7 -7
- data/lib/catissue/domain/specimen_requirement.rb +9 -9
- data/lib/catissue/domain/storage_container.rb +13 -13
- data/lib/catissue/domain/storage_type.rb +12 -7
- data/lib/catissue/domain/transfer_event_parameters.rb +2 -2
- data/lib/catissue/domain/user.rb +15 -15
- data/lib/catissue/extract/extractor.rb +1 -1
- data/lib/catissue/resource.rb +8 -36
- data/lib/catissue/util/location.rb +0 -13
- data/lib/catissue/util/person.rb +2 -1
- data/lib/catissue/util/position.rb +1 -11
- data/lib/catissue/util/uniquify.rb +1 -1
- data/lib/catissue/version.rb +1 -1
- data/lib/catissue/wustl/logger.rb +17 -16
- data/test/fixtures/lib/catissue/defaults_test_fixture.rb +6 -4
- data/test/lib/catissue/domain/collection_protocol_test.rb +0 -1
- data/test/lib/catissue/domain/participant_test.rb +74 -19
- data/test/lib/catissue/domain/specimen_collection_group_test.rb +14 -10
- data/test/lib/catissue/domain/specimen_test.rb +36 -60
- data/test/lib/catissue/domain/storage_type_test.rb +5 -5
- data/test/lib/catissue/domain/transfer_event_parameters_test.rb +14 -12
- data/test/lib/catissue/migration/test_case.rb +0 -1
- data/test/lib/catissue/test_case.rb +12 -12
- data/test/lib/examples/galena/tissue/domain/examples_test.rb +1 -1
- data/test/lib/examples/galena/tissue/migration/annotation_test.rb +9 -7
- data/test/lib/examples/galena/tissue/migration/frozen_test.rb +1 -1
- data/test/lib/examples/galena/tissue/migration/general_test.rb +1 -1
- data/test/lib/examples/galena/tissue/migration/registration_test.rb +3 -9
- data/test/lib/examples/galena/tissue/migration/seedify.rb +3 -3
- data/test/lib/examples/pcbin/migration_test.rb +37 -16
- metadata +24 -10
- data/History.txt +0 -50
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'caruby/domain
|
1
|
+
require 'caruby/domain'
|
2
2
|
require 'catissue/annotation/annotation'
|
3
3
|
require 'catissue/annotation/annotation_class'
|
4
4
|
require 'catissue/annotation/proxy'
|
@@ -7,15 +7,15 @@ require 'catissue/annotation/de_integration'
|
|
7
7
|
|
8
8
|
module CaTissue
|
9
9
|
module AnnotationModule
|
10
|
-
include CaRuby::
|
10
|
+
include CaRuby::Domain
|
11
11
|
|
12
|
-
# @return [
|
13
|
-
|
12
|
+
# @return [ProxyClass] the annotation proxy class
|
13
|
+
attr_reader :proxy
|
14
14
|
|
15
15
|
# @return [Class] the hook-annotation association class, or nil for 1.1.x caTissue
|
16
16
|
attr_reader :record_entry_class
|
17
17
|
|
18
|
-
# @return [Symbol] the {#
|
18
|
+
# @return [Symbol] the {#de_integration_proxy_class} hook writer method, or nil for 1.1.x caTissue
|
19
19
|
attr_reader :record_entry_hook_writer
|
20
20
|
|
21
21
|
# @param [AnnotationModule] mod the annotation module to build
|
@@ -29,43 +29,33 @@ module CaTissue
|
|
29
29
|
mod.initialize_annotation(hook, opts)
|
30
30
|
end
|
31
31
|
|
32
|
+
# Builds the annotation module.
|
33
|
+
# This method intended to be called only by {AnnotationModule.extend_module}.
|
34
|
+
#
|
35
|
+
# @param (see AnnotationModule.extend_module)
|
32
36
|
def initialize_annotation(hook, opts)
|
33
37
|
logger.debug { "Building #{hook.qp} annotation #{qp}..." }
|
34
|
-
|
38
|
+
pkg = opts[:package]
|
35
39
|
@svc_nm = opts[:service]
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
if Annotation::DEIntegration.const_defined?(rec_entry) then
|
44
|
-
@record_entry_class = Annotation::DEIntegration.const_get(rec_entry)
|
45
|
-
@record_entry_hook_writer = "#{hook.name.demodulize.underscore}=".to_sym
|
46
|
-
else
|
47
|
-
logger.warn("Ignored missing annotation #{name} record entry class #{rec_entry}.")
|
48
|
-
rec_entry = nil
|
49
|
-
end
|
40
|
+
# Enable the resource metadata aspect.
|
41
|
+
md_proc = Proc.new { |klass| AnnotationClass.extend_class(klass, self) }
|
42
|
+
CaRuby::Domain::Importer.extend_module(self, :mixin => Annotation, :metadata => md_proc, :package => pkg)
|
43
|
+
dei = hook.de_integration_proxy_class
|
44
|
+
if dei then
|
45
|
+
import_record_entry_class(dei, hook)
|
46
|
+
pxy_nm = dei.name.demodulize
|
50
47
|
end
|
51
|
-
@proxy = import_proxy(hook,
|
52
|
-
|
53
|
-
@proxy.extend(Annotation::ProxyClass)
|
48
|
+
@proxy = import_proxy(hook, pxy_nm)
|
49
|
+
load_annotation_class_definitions(hook)
|
54
50
|
logger.debug { "Built #{hook.qp} annotation #{qp}." }
|
55
51
|
end
|
56
52
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
logger.debug { "Ensuring that #{qp} primary annotations reference the proxy #{@proxy.qp}..." }
|
61
|
-
@rsc_classes.each { |klass| klass.ensure_primary_has_proxy(@proxy) }
|
62
|
-
end
|
63
|
-
|
64
|
-
# Builds an annotation dependency hierarchy starting at the proxy.
|
65
|
-
def add_annotation_dependents
|
66
|
-
@proxy.add_annotation_dependents
|
53
|
+
# @return (ProxyClass#hook)
|
54
|
+
def hook
|
55
|
+
@proxy.hook
|
67
56
|
end
|
68
57
|
|
58
|
+
# @return [CaRuby::PersistenceService] this module's application service
|
69
59
|
def persistence_service
|
70
60
|
@ann_svc ||= Database.instance.annotator.create_annotation_service(self, @svc_nm)
|
71
61
|
end
|
@@ -73,41 +63,39 @@ module CaTissue
|
|
73
63
|
private
|
74
64
|
|
75
65
|
# The location of the domain class definitions.
|
76
|
-
DOMAIN_DIR = File.join(File.dirname(__FILE__), '
|
66
|
+
DOMAIN_DIR = File.join(File.dirname(File.dirname(__FILE__)), 'domain')
|
77
67
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
68
|
+
def load_annotation_class_definitions(hook)
|
69
|
+
dir = File.join(DOMAIN_DIR, hook.name.demodulize.underscore, name.demodulize.underscore)
|
70
|
+
load_dir(dir)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets the record entry instance variables for the given class name, if it exists
|
74
|
+
# as a {Annotation::DEIntegration} proxy class. caTissue v1.1.x does not have
|
75
|
+
# a record entry class.
|
76
|
+
#
|
77
|
+
# @param [String] the record entry class name specified in the
|
78
|
+
# {CaTissue::AnntatableClass#add_annotation_attribute} +:record_entry+ option
|
79
|
+
def import_record_entry_class(klass, hook)
|
80
|
+
@record_entry_class = const_get(klass.name.demodulize.to_sym)
|
81
|
+
@record_entry_hook_writer = "#{hook.name.demodulize.underscore}=".to_sym
|
91
82
|
end
|
92
83
|
|
93
84
|
# @param hook (see #initialize_annotation)
|
94
|
-
# @param [String] name the demodulized name of the proxy class
|
85
|
+
# @param [String] name the demodulized name of the proxy class
|
86
|
+
# (default is the demodulized hook class name)
|
95
87
|
def import_proxy(hook, name=nil)
|
96
|
-
logger.debug { "Importing #{qp} #{hook.qp} proxy#{' ' + name if name}..." }
|
97
88
|
name ||= hook.name.demodulize
|
89
|
+
logger.debug { "Importing #{qp} #{hook.qp} annotation proxy..." }
|
98
90
|
begin
|
99
|
-
const_get(name.to_sym)
|
100
|
-
rescue CaRuby::
|
101
|
-
raise
|
91
|
+
klass = const_get(name.to_sym)
|
92
|
+
rescue CaRuby::JavaImportError
|
102
93
|
raise AnnotationError.new("#{hook.qp} annotation #{qp} does not have a hook proxy class - #{$!}")
|
103
94
|
end
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
# @param (see ResourceModule#imported)
|
109
|
-
def imported(klass)
|
110
|
-
klass.infer_inverses
|
95
|
+
klass.extend(Annotation::ProxyClass)
|
96
|
+
klass.hook = hook
|
97
|
+
logger.debug { "Built #{name} #{hook.qp} annotation proxy #{klass}." }
|
98
|
+
klass
|
111
99
|
end
|
112
100
|
end
|
113
101
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'caruby/domain'
|
2
|
+
|
1
3
|
module CaTissue
|
2
4
|
module Annotation
|
3
5
|
# DEIntegration encapsulates the +edu.wustl.catissuecore.domain.deintegration+ package in caTissue 1.2 and higher.
|
@@ -6,13 +8,21 @@ module CaTissue
|
|
6
8
|
# @return [Class] yet another undocumented special-purpose association record entry class
|
7
9
|
# which associates the given hook proxy class symbol to an annotation
|
8
10
|
def self.const_missing(symbol)
|
11
|
+
name = [PKG, symbol].join('.')
|
12
|
+
logger.debug { "Importing DE integration proxy Java class #{name}..." }
|
9
13
|
begin
|
10
|
-
java_import
|
11
|
-
rescue
|
14
|
+
java_import name
|
15
|
+
rescue NameError
|
12
16
|
super
|
13
17
|
end
|
14
18
|
end
|
15
19
|
|
20
|
+
# @param [String] name the annotated hook class name
|
21
|
+
# @return [Class, nil] the hook proxy class, or nil if none defined
|
22
|
+
def self.proxy(name)
|
23
|
+
const_get(name.to_sym) rescue nil
|
24
|
+
end
|
25
|
+
|
16
26
|
private
|
17
27
|
|
18
28
|
# The auxiliary record entry class Java package name.
|
@@ -2,26 +2,20 @@ module CaTissue
|
|
2
2
|
module Annotation
|
3
3
|
# {CaTissue::Resource} annotation hook proxy mix-in.
|
4
4
|
module Proxy
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
# @return [Integer] the hook identifier
|
9
|
-
def identifier
|
10
|
-
hook.identifier if hook
|
5
|
+
# @return [Annotatable] the annotated domain object
|
6
|
+
def hook
|
7
|
+
send(self.class.owner_attribute_metadata.reader)
|
11
8
|
end
|
12
9
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
# @param [Annotatable] obj the domain object to annotate
|
11
|
+
def hook=(obj)
|
12
|
+
send(self.class.owner_attribute_metadata.writer, obj)
|
13
|
+
end
|
17
14
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
setId(hook.identifier)
|
23
|
-
logger.debug { "Set annotation proxy #{self} identifier to that of the hook entity #{hook.qp}." }
|
24
|
-
end
|
15
|
+
# Ensures that this proxy's hook exists in the database.
|
16
|
+
def ensure_hook_exists
|
17
|
+
if hook.nil? then raise AnnotationError.new("Annotation proxy #{self} is missing the hook domain object") end
|
18
|
+
hook.ensure_exists
|
25
19
|
end
|
26
20
|
end
|
27
21
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'catissue/annotation/proxy'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
module Annotation
|
5
|
+
# caTissue 1.1.x {CaTissue::Resource} annotation hook proxy mix-in.
|
6
|
+
module Proxy_1_1
|
7
|
+
include Proxy
|
8
|
+
|
9
|
+
# The hook proxy identifier is the hook identifier.
|
10
|
+
# This method delegates to the hook.
|
11
|
+
#
|
12
|
+
# @return [Integer] the hook identifier
|
13
|
+
def identifier
|
14
|
+
hook.identifier if hook
|
15
|
+
end
|
16
|
+
|
17
|
+
# The proxy identifier cannot be set directly. Assignment is a no-op.
|
18
|
+
#
|
19
|
+
# @param [Integer] value the (ignored) identifier value
|
20
|
+
def identifier=(value); end
|
21
|
+
|
22
|
+
# @param [Annotatable] obj the domain object to annotate
|
23
|
+
def hook=(obj)
|
24
|
+
super
|
25
|
+
ensure_identifier_reflects_hook
|
26
|
+
end
|
27
|
+
|
28
|
+
# Ensures that this proxy's hook exists in the database. This proxy's identifier is set to
|
29
|
+
# the hook identifier.
|
30
|
+
def ensure_hook_exists
|
31
|
+
super
|
32
|
+
ensure_identifier_reflects_hook
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Sets the +id+ Java property to the hook identifier.
|
38
|
+
# This method must be called before saving a caTissue 1.1.x annotation that references this proxy.
|
39
|
+
def ensure_identifier_reflects_hook
|
40
|
+
if getId.nil? and hook and hook.identifier then
|
41
|
+
setId(hook.identifier)
|
42
|
+
logger.debug { "Set annotation proxy #{self} identifier to that of the hook entity #{hook.qp}." }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,57 +1,61 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'caruby/util/collection'
|
3
|
+
require 'caruby/util/partial_order'
|
2
4
|
require 'catissue/annotation/annotation'
|
3
|
-
require 'catissue/annotation/
|
5
|
+
require 'catissue/annotation/proxy_1_1'
|
6
|
+
require 'catissue/annotation/record_entry_proxy'
|
4
7
|
|
5
8
|
module CaTissue
|
6
9
|
module Annotation
|
7
10
|
# Annotation hook proxy class mix-in.
|
8
11
|
module ProxyClass
|
9
|
-
# @return [AnnotatableClass] the hook class for this proxy
|
10
|
-
attr_reader :hook
|
11
|
-
|
12
12
|
# @param [Class] klass the proxy class
|
13
13
|
def self.extended(klass)
|
14
14
|
super
|
15
|
-
|
15
|
+
# distinguish the 1.1 from the 1.2 proxy class
|
16
|
+
mixin = klass.name =~ /RecordEntry$/ ? RecordEntryProxy : Proxy_1_1
|
17
|
+
klass.class_eval { include mixin }
|
16
18
|
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
+
# @return [AnnotatableClass] the hook class for this proxy
|
21
|
+
def hook
|
22
|
+
owner_type
|
20
23
|
end
|
21
24
|
|
22
|
-
# Sets this proxy's hook to the given class
|
23
|
-
#
|
25
|
+
# Sets this proxy's hook to the given class and creates the
|
26
|
+
# proxy => hook attribute with the given hook => proxy inverse.
|
24
27
|
#
|
25
|
-
# @param [
|
26
|
-
|
27
|
-
def set_hook(klass, inverse)
|
28
|
-
@hook = klass
|
28
|
+
# @param [AnnotatableClass] klass the annotated domain object class
|
29
|
+
def hook=(klass)
|
29
30
|
# Make a new hook reference attribute.
|
30
|
-
|
31
|
+
attr = klass.name.demodulize.underscore
|
32
|
+
attr_accessor(attr)
|
31
33
|
# The attribute type is the given hook class.
|
32
|
-
add_attribute(
|
33
|
-
|
34
|
-
set_attribute_inverse(:hook, inverse)
|
35
|
-
logger.debug { "Added #{klass.qp} annotation proxy => hook attribute with inverse #{klass.qp}.#{inverse}." }
|
34
|
+
add_attribute(attr, klass)
|
35
|
+
logger.debug { "Added #{klass.qp} annotation proxy => hook attribute #{attr}." }
|
36
36
|
end
|
37
37
|
|
38
38
|
# Adds each proxy => annotation reference as a dependent attribute.
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
# Recursively adds dependents of all referenced annotations.
|
40
|
+
def build_annotation_dependency_hierarchy
|
41
|
+
logger.debug { "Building annotation dependency hierarchy..." }
|
42
|
+
non_proxy_annotation_classes.each do |klass|
|
43
|
+
klass.annotation_hierarchy.each do |anc|
|
44
|
+
if anc.primary? and anc.proxy_attribute.nil? then
|
45
|
+
anc.define_proxy_attribute(self)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
logger.info(klass.pp_s)
|
47
49
|
end
|
50
|
+
set_inverses
|
51
|
+
add_dependent_attributes
|
52
|
+
add_dependent_attribute_closure
|
48
53
|
end
|
49
54
|
|
50
55
|
# Creates a reference attribute from this proxy to the given primary {Annotation} class.
|
51
56
|
#
|
52
57
|
# @param [Class] klass the annotation class
|
53
|
-
|
54
|
-
def create_annotation_attribute(klass, inverse)
|
58
|
+
def create_annotation_attribute(klass)
|
55
59
|
# the new attribute symbol
|
56
60
|
attr = klass.name.demodulize.underscore.pluralize.to_sym
|
57
61
|
logger.debug { "Creating annotation proxy #{qp} attribute #{attr} to hold primary annotation #{klass.qp} instances..." }
|
@@ -59,20 +63,34 @@ module CaTissue
|
|
59
63
|
attr_create_on_demand_accessor(attr) { Set.new }
|
60
64
|
# add the annotation collection attribute
|
61
65
|
add_attribute(attr, klass, :collection)
|
62
|
-
#
|
63
|
-
|
64
|
-
# set the attribute inverse
|
65
|
-
set_attribute_inverse(attr, inverse)
|
66
|
+
# The annotation is dependent.
|
67
|
+
add_dependent_attribute(attr, :logical)
|
66
68
|
attr
|
67
69
|
end
|
68
70
|
|
69
71
|
private
|
72
|
+
|
73
|
+
# Sets each annotation reference attribute inverse to the direct, unwrapped proxy
|
74
|
+
# reference named by the annotation module. E.g. the caTissue +Participant+
|
75
|
+
# +clinical+ proxy +ParticipantRecordEntry+ -> +NewDiagnosisAnnotation+ attribute
|
76
|
+
# inverse is set to the +NewDiagnosisAnnotation+ -> +ParticipantRecordEntry+
|
77
|
+
# +clinical+ reference attribute.
|
78
|
+
def set_inverses
|
79
|
+
# The inverse is the direct, unwrapped proxy reference named by the annotation module.
|
80
|
+
inv = annotation_module.name.demodulize.underscore.to_sym
|
81
|
+
# The attributes in class hierarchy general-to-specific order
|
82
|
+
attr_mds = annotation_attributes.enum_metadata.partial_sort_by { |attr_md| attr_md.type }.reverse
|
83
|
+
logger.debug { "Setting #{self} inverses for annotation attributes #{attr_mds.to_series}." }
|
84
|
+
attr_mds.each do |attr_md|
|
85
|
+
attr_md.type.define_proxy_attribute(self)
|
86
|
+
set_attribute_inverse(attr_md.to_sym, inv)
|
87
|
+
end
|
88
|
+
end
|
70
89
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
domain_attributes.compose { |attr_md| attr_md.type < Annotation }
|
90
|
+
# @return <AnnotationClass> the non-proxy annotation classes
|
91
|
+
def non_proxy_annotation_classes
|
92
|
+
consts = annotation_module.constants.map { |s| annotation_module.const_get(s) }
|
93
|
+
consts.select { |c| Class === c and c < Annotation and not c < Proxy }
|
76
94
|
end
|
77
95
|
end
|
78
96
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'catissue/annotation/proxy'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
module Annotation
|
5
|
+
# caTissue 1.2 {CaTissue::Resource} annotation hook proxy mix-in.
|
6
|
+
module RecordEntryProxy
|
7
|
+
include Proxy
|
8
|
+
|
9
|
+
# @return [Annotatable] the annotated domain object
|
10
|
+
def hook
|
11
|
+
send(self.class.owner_attribute_metadata.reader)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [Annotatable] obj the domain object to annotate
|
15
|
+
def hook=(obj)
|
16
|
+
send(self.class.owner_attribute_metadata.writer, obj)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/catissue/database.rb
CHANGED
@@ -17,11 +17,10 @@ module CaTissue
|
|
17
17
|
# return [CaRuby::SQLExecutor] a utility SQL executor
|
18
18
|
attr_reader :executor, :access_properties
|
19
19
|
|
20
|
-
# Creates a new Database with the
|
20
|
+
# Creates a new Database with the +catissuecore+ service and {CaTissue.access_properties}.
|
21
21
|
def initialize
|
22
22
|
@access_properties = CaTissue.access_properties
|
23
|
-
@access_properties
|
24
|
-
super(SERVICE_NAME, @access_properties)
|
23
|
+
super(SVC_NAME, @access_properties)
|
25
24
|
@executor = CaRuby::SQLExecutor.new(@access_properties)
|
26
25
|
end
|
27
26
|
|
@@ -37,16 +36,16 @@ module CaTissue
|
|
37
36
|
# @param (see CaRuby::Database#persistence_service)
|
38
37
|
# @return (see CaRuby::Database#persistence_service)
|
39
38
|
def persistence_service(klass)
|
40
|
-
klass < Annotation ? klass.
|
39
|
+
klass < Annotation ? klass.annotation_module.persistence_service : super
|
41
40
|
end
|
42
41
|
|
43
42
|
# Augments {CaRuby::Database#ensure_exists} to ensure that an {Annotation::Proxy} reference identifier
|
44
43
|
# reflects the hook identifier.
|
45
44
|
#
|
46
|
-
# @param (see CaRuby::Database#ensure_exists)
|
47
|
-
def ensure_exists(
|
48
|
-
if Annotation::Proxy ===
|
49
|
-
|
45
|
+
# @param (see CaRuby::Database::Writer#ensure_exists)
|
46
|
+
def ensure_exists(obj)
|
47
|
+
if Annotation::Proxy === obj then
|
48
|
+
obj.ensure_hook_exists
|
50
49
|
end
|
51
50
|
super
|
52
51
|
end
|
@@ -54,10 +53,7 @@ module CaTissue
|
|
54
53
|
private
|
55
54
|
|
56
55
|
# The application service name
|
57
|
-
|
58
|
-
|
59
|
-
# The default database name
|
60
|
-
DEF_DATABASE_NAME = 'catissue'
|
56
|
+
SVC_NAME = 'catissuecore'
|
61
57
|
|
62
58
|
UPD_EID_SQL = 'update catissue_external_identifier set name = ?, value = ?, specimen_id = ? where identifier = ?'
|
63
59
|
|
@@ -103,13 +99,13 @@ module CaTissue
|
|
103
99
|
|
104
100
|
# This method patches up fetched sources to correct the following anomaly:
|
105
101
|
#
|
106
|
-
# caCORE
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
102
|
+
# @quirk caCORE fetched references are not reconciled within an existing query result, e.g.
|
103
|
+
# given a query result with two Specimens s1 and s2, the parent reference is not fetched.
|
104
|
+
# Subsequently fetching the parent is independent of the query result. Thus if s1 is the parent
|
105
|
+
# of s2 in the database, the fetched s2 parent s3 is distinct from s1, even though
|
106
|
+
# s1.identifier == s3.identifier. Thus, enforcing reference consistency requires a post-fetch step
|
107
|
+
# that matches the fetched objects to the original query result on identifier and resets the
|
108
|
+
# references.
|
113
109
|
#
|
114
110
|
# Not yet enabled. TODO - plug this into fetch_object.
|
115
111
|
#
|
@@ -125,10 +121,9 @@ module CaTissue
|
|
125
121
|
end
|
126
122
|
end
|
127
123
|
|
128
|
-
#
|
129
|
-
# * caTissue alert - Bug #135: Update SCG SpecimenEventParameters raises AuditException.
|
124
|
+
# @quirk caTissue Bug #135: Update SCG SpecimenEventParameters raises AuditException.
|
130
125
|
# Work around is to update the SCG instead.
|
131
|
-
#
|
126
|
+
# @quirk caTissue CPR consent tier response update results in Access denied error.
|
132
127
|
# Work-around is to update the response using a direct SQL call.
|
133
128
|
#
|
134
129
|
# @param (see CaRuby::Database#update_object)
|
@@ -138,6 +133,8 @@ module CaTissue
|
|
138
133
|
save_collectible_event_parameters(obj)
|
139
134
|
elsif CaTissue::ConsentTierResponse === obj then
|
140
135
|
update_consent_tier_response(obj)
|
136
|
+
elsif Annotation === obj then
|
137
|
+
raise CaRuby::DatabaseError.new("Annotation update is not supported on #{obj}")
|
141
138
|
else
|
142
139
|
case obj
|
143
140
|
when CaTissue::Specimen then
|
@@ -154,8 +151,8 @@ module CaTissue
|
|
154
151
|
|
155
152
|
# Updates the given dependent.
|
156
153
|
#
|
157
|
-
# caTissue
|
158
|
-
#
|
154
|
+
# @quirk caTissue 1.2 user address update results in authorization error. Work-around is to
|
155
|
+
# create a new address record.
|
159
156
|
#
|
160
157
|
# @param (see CaRuby::Writer#update_changed_dependent)
|
161
158
|
def update_changed_dependent(owner, attribute, dependent, autogenerated)
|
@@ -197,44 +194,44 @@ module CaTissue
|
|
197
194
|
|
198
195
|
# Augments {CaRuby::Database#save_with_template} to work around the following caTissue anomalies:
|
199
196
|
#
|
200
|
-
# caTissue
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
# caTissue
|
206
|
-
#
|
207
|
-
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
197
|
+
# @quirk caTissue Bug #149: API update TissueSpecimen position validation incorrect.
|
198
|
+
# The Specimen update argument must reference the old position, even though the position is not
|
199
|
+
# updatable, unless old status is Pending. The validation defect described in Bug #149 requires
|
200
|
+
# a work-around that is also used for a different reason described in the following paragraph.
|
201
|
+
#
|
202
|
+
# @quirk caTissue Update of a {CaTissue::Specimen} which references a position must include the former
|
203
|
+
# position in the caTissue service update argument. A Specimen position is altered as a side-effect
|
204
|
+
# by creating a proxy save {CaTissue::TransferEventParameters}. The changed position is not reflected
|
205
|
+
# in the Specimen position, which must be refetched to reflect the database state. This fetch is
|
206
|
+
# done automatically by {CaRuby::Database} as part of the save proxy mechanism. The Specimen update
|
207
|
+
# template must include a reference to the former position but not the changed position.
|
208
|
+
#
|
209
|
+
# However, the Specimen {CaRuby::Writer#update} argument will include the changed position, not the
|
210
|
+
# former position. The template built {CaRuby::Writer#update} for submission to the caTissue app
|
211
|
+
# does not include a position reference, since the position has a save proxy which handles position
|
212
|
+
# change as part of the {CaRuby::Writer} update dependent propagation.
|
213
|
+
#
|
214
|
+
# Thus, updating a Specimen which includes a position change is performed as follows:
|
215
|
+
# * reconstitute the former position from the Position snapshot taken as part of the
|
216
|
+
# {CaRuby::Persistable} change tracker.
|
217
|
+
# * add the former position to the template (which will now differ from the {CaRuby::Writer#update}
|
218
|
+
# argument).
|
219
|
+
# * submit the adjusted Specimen template to the caTissue app updateObject.
|
220
|
+
# * {CaRuby::Writer#update} will propagate the Specimen update to the changed position dependent,
|
221
|
+
# which in turn saves via the {CaTissue::TransferEventParameters} proxy.
|
222
|
+
# * The proxy save will in turn refetch the proxied Specimen position to obtain the identifier
|
223
|
+
# and merge this into the Specimen position.
|
224
|
+
# * The Specimen update template is used solely to satisfy the often arcane caTissue interaction
|
225
|
+
# requirements like this work-around, and is thrown away along with its aberrant state.
|
226
|
+
#
|
227
|
+
# This work-around is the only case of a save template modification to handle a caTissue special
|
228
|
+
# case. Note that the {CaTissue::SpecimenPosition} logic does not apply to a
|
229
|
+
# {CaTissue::ContainerPosition}, which can be updated directly.
|
230
|
+
#
|
231
|
+
# The additional complexity of this work-around is necessitated by the caTissue policy of update
|
232
|
+
# by indirect server-side side-effects that are not reflected back to the client. The caRuby
|
233
|
+
# policy of a declarative API that persists the save argument as given and reflects the
|
234
|
+
# changed database state requires this work-around.
|
238
235
|
#
|
239
236
|
# @param obj (see #store)
|
240
237
|
# @param [Resource] template the obj template to submit to caCORE
|
@@ -294,18 +291,21 @@ module CaTissue
|
|
294
291
|
logger.debug { "caTissue #{ctr} update work-around completed." }
|
295
292
|
end
|
296
293
|
|
297
|
-
# Overrides {CaRuby::Database::Writer#
|
298
|
-
# in {#
|
294
|
+
# Overrides {CaRuby::Database::Writer#save_changed_dependents} to handle the following anomalies:
|
295
|
+
# * create Specimen disposal event last, as described in {#save_changed_specimen_dependents}
|
299
296
|
#
|
300
297
|
# @param (see CaRuby::Writer#save_dependents)
|
301
|
-
def
|
302
|
-
|
298
|
+
def save_changed_dependents(obj)
|
299
|
+
case obj
|
300
|
+
when Specimen then save_changed_specimen_dependents(obj) { super }
|
301
|
+
else super
|
302
|
+
end
|
303
303
|
end
|
304
304
|
|
305
|
-
# Overrides {CaRuby::Database::Writer#
|
305
|
+
# Overrides {CaRuby::Database::Writer#save_changed_dependents} on a Specimen to correct the
|
306
306
|
# following problem:
|
307
307
|
#
|
308
|
-
# caTissue
|
308
|
+
# @quirk caTissue DisposalEventParameters must be created after all other Specimen SEPs.
|
309
309
|
#
|
310
310
|
# The process for migrating a discarded Specimen is as follows:
|
311
311
|
# * Create the Specimen with status Active.
|
@@ -321,9 +321,9 @@ module CaTissue
|
|
321
321
|
# for the subtle interaction required between these two work-arounds.
|
322
322
|
#
|
323
323
|
# @param [CaTissue::Specimen] the specimen whose dependents are to be saved
|
324
|
-
# @yield [dependent] calls the base {CaRuby::Writer#
|
324
|
+
# @yield [dependent] calls the base {CaRuby::Writer#save_changed_dependents}
|
325
325
|
# @yieldparam [Resource] dependent the dependent to save
|
326
|
-
def
|
326
|
+
def save_changed_specimen_dependents(specimen)
|
327
327
|
dsp = specimen.specimen_events.detect { |ep| CaTissue::DisposalEventParameters === ep }
|
328
328
|
if dsp then
|
329
329
|
logger.debug { "Work around caTissue #{specimen.qp} event parameters save order dependency by deferring #{dsp.qp} save..." }
|
@@ -350,7 +350,7 @@ module CaTissue
|
|
350
350
|
# @return (see CaRuby::Database#build_save_template)
|
351
351
|
def build_save_template(obj, builder)
|
352
352
|
Annotation === obj ? prepare_annotation_for_save(obj) : super
|
353
|
-
end
|
353
|
+
end
|
354
354
|
|
355
355
|
# Ensures that a primary annotation hook exists.
|
356
356
|
#
|
@@ -358,8 +358,10 @@ module CaTissue
|
|
358
358
|
# @return [Annotation] the annotation object
|
359
359
|
# @raise [DatabaseError] if the annotation does not reference a hook entity
|
360
360
|
def prepare_annotation_for_save(annotation)
|
361
|
-
hook = annotation.
|
362
|
-
if hook.nil? then
|
361
|
+
hook = annotation.hook
|
362
|
+
if hook.nil? then
|
363
|
+
raise CaRuby::DatabaseError.new("Cannot save annotation #{annotation} since it does not reference a hook entity")
|
364
|
+
end
|
363
365
|
if hook.identifier.nil? then
|
364
366
|
logger.debug { "Ensuring that the annotation #{annotation.qp} hook entity #{hook.qp} exists in the database..." }
|
365
367
|
ensure_exists(hook)
|
@@ -367,15 +369,16 @@ module CaTissue
|
|
367
369
|
annotation
|
368
370
|
end
|
369
371
|
|
370
|
-
# Overrides {CaRuby::Database::Writer#save_with_template} to work around
|
371
|
-
# caTissue
|
372
|
-
# * caTissue alert - Bug #63: a SpecimenCollectionGroup update requires the referenced CollectionProtocolRegistration
|
372
|
+
# Overrides {CaRuby::Database::Writer#save_with_template} to work around caTissue bugs.
|
373
|
+
# @quirk caTissue Bug #63: a SpecimenCollectionGroup update requires the referenced CollectionProtocolRegistration
|
373
374
|
# with an identifier to hold extraneous CollectionProtocolRegistration content, including the CPR
|
374
375
|
# collection protocol and PPI.
|
375
|
-
#
|
376
|
+
# @quirk caTissue Bug: CollectionProtocolRegistration must cascade throughCP, but the CP events
|
376
377
|
# cannot cascade to SpecimenRequirement without raising an Exception. Work-around is to clear the template CP events.
|
377
|
-
#
|
378
|
+
# @quirk caTissue Create Specimen with nil label does not auto-generate the label.
|
378
379
|
# Work-around is to set the label to a unique value.
|
380
|
+
#
|
381
|
+
# @raise DatabaseError if the object to save is an {Annotation::Proxy}, which is not supported
|
379
382
|
def save_with_template(obj, template)
|
380
383
|
# special cases to work around caTissue bugs
|
381
384
|
if CaTissue::CollectionProtocolRegistration === obj and template.collection_protocol then
|
@@ -450,10 +453,12 @@ module CaTissue
|
|
450
453
|
if cep.nil? then raise CaRuby::DatabaseError.new("Default collection event parameters were not added to #{obj}.") end
|
451
454
|
cep.copy.merge_attributes(:user => cep.user, :specimen_collection_group => template)
|
452
455
|
end
|
453
|
-
elsif Annotation === obj
|
456
|
+
elsif Annotation::Proxy === obj then
|
457
|
+
raise CaRuby::DatabaseError.new("Annotation proxy direct database save is not supported: #{obj}")
|
458
|
+
elsif Annotation === obj and obj.class.primary? then
|
454
459
|
copy_annotation_proxy_owner_to_template(obj, template)
|
455
460
|
end
|
456
|
-
|
461
|
+
|
457
462
|
# delegate to standard save
|
458
463
|
super
|
459
464
|
end
|
@@ -474,18 +479,19 @@ module CaTissue
|
|
474
479
|
template.send(wtr, pxy)
|
475
480
|
end
|
476
481
|
|
477
|
-
# Augment {CaRuby::Database::Writer#create_object}
|
478
|
-
#
|
482
|
+
# Augment {CaRuby::Database::Writer#create_object} to work around caTissue bugs.
|
483
|
+
# @quirk caTissue Bug #124: SCG SpecimenEventParameters save fails validation.
|
479
484
|
# Work-around is to create the SEP by updating the SCG.
|
480
|
-
#
|
485
|
+
# @quirk If obj is a CaTissue::Specimen with the is_available flag set to false, then work around the bug
|
481
486
|
# described in {#create_unavailable_specimen}.
|
482
|
-
#
|
487
|
+
# @quirk caTissue Bug #161: Specimen API disposal not reflected in result activity status.
|
483
488
|
# DisposalEventParameters create sets the owner Specimen activity_status to +Closed+ as a side-effect.
|
484
489
|
# Reflect this side-effect in the submitted DisposalEventParameters owner Specimen object.
|
490
|
+
# Pass through an {Annotation::Proxy} to the referenced annotations.
|
485
491
|
#
|
486
492
|
# @param [Resource] obj the dependent domain object to save
|
487
493
|
def create_object(obj)
|
488
|
-
|
494
|
+
if collectible_event_parameters?(obj) then
|
489
495
|
save_collectible_event_parameters(obj)
|
490
496
|
elsif CaTissue::Specimen === obj then
|
491
497
|
obj.add_defaults
|
@@ -507,10 +513,33 @@ module CaTissue
|
|
507
513
|
|
508
514
|
obj
|
509
515
|
end
|
510
|
-
|
516
|
+
|
517
|
+
# Overrides {CaRuby::Database#create_from_template} as follows:
|
518
|
+
# * Surrogate {Annotation::Proxy} is "created" by setting the identifier to its hook owner.
|
519
|
+
# The create operation then creates referenced uncreated dependents.
|
520
|
+
#
|
521
|
+
# @param (CaRuby::Database#create_from_template)
|
522
|
+
def create_from_template(obj)
|
523
|
+
if Annotation::Proxy === obj then
|
524
|
+
hook = obj.hook
|
525
|
+
if hook.identifier.nil? then
|
526
|
+
raise CaRuby::DatabaseError.new("Annotation proxy #{obj.qp} hook owner #{hook.qp} does not have an identifier")
|
527
|
+
end
|
528
|
+
obj.identifier = hook.identifier
|
529
|
+
obj.take_snapshot
|
530
|
+
logger.debug { "Marked annotation proxy #{obj} as created by setting the identifier to that of the hook owner #{hook}." }
|
531
|
+
logger.debug { "Creating annotation proxy #{obj} dependent primary annotations..." }
|
532
|
+
save_changed_dependents(obj)
|
533
|
+
persistify(obj)
|
534
|
+
obj
|
535
|
+
else
|
536
|
+
super
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
511
540
|
# Creates the given specimen by working around the following bug:
|
512
541
|
#
|
513
|
-
# caTissue
|
542
|
+
# @quirk caTissue Bug #160: Missing Is Available? validation.
|
514
543
|
# Cannot create a Specimen with any of the following conditions:
|
515
544
|
# * zero available_quantity
|
516
545
|
# * is_available flag set to false
|
@@ -567,6 +596,8 @@ module CaTissue
|
|
567
596
|
specimen
|
568
597
|
end
|
569
598
|
|
599
|
+
# Overrides {CaRuby::Database::Reader#fetch_object} to circumvent {Annotation} fetch, since an annotation
|
600
|
+
# does not have a key.
|
570
601
|
def fetch_object(obj)
|
571
602
|
super or fetch_alternative(obj)
|
572
603
|
end
|
@@ -579,9 +610,9 @@ module CaTissue
|
|
579
610
|
end
|
580
611
|
|
581
612
|
# Override {CaRuby::Database#query_safe} to work around the following +caTissue+ bugs:
|
582
|
-
# * caTissue
|
583
|
-
#
|
584
|
-
# * caTissue
|
613
|
+
# * @quirk caTissue Specimen auto-generates blank ExternalIdentifier.
|
614
|
+
# cf. https://cabig-kc.nci.nih.gov/Biospecimen/forums/viewtopic.php?f=19&t=436&sid=ef98f502fc0ab242781b7759a0eaff36
|
615
|
+
# * @quirk caTissue Specimen auto-generates blank PMI.
|
585
616
|
def query_safe(obj_or_hql, *path)
|
586
617
|
if path.last == :external_identifiers then
|
587
618
|
CaTissue::Specimen.remove_empty_external_identifier(super)
|
@@ -592,19 +623,19 @@ module CaTissue
|
|
592
623
|
end
|
593
624
|
end
|
594
625
|
|
595
|
-
# caTissue
|
596
|
-
#
|
626
|
+
# @quirk caTissue Bug #147: SpecimenRequirement query ignores CPE.
|
627
|
+
# Work around this bug by an inverted query on the referenced CPE.
|
597
628
|
#
|
598
|
-
# caTissue
|
599
|
-
#
|
629
|
+
# @quirk caTissue Accessing an annotation hook DE proxy attribute uses a separate mechanism.
|
630
|
+
# Redirect the query to the annotation integration service in that case.
|
600
631
|
#
|
601
|
-
# caTissue
|
602
|
-
#
|
632
|
+
# @quirk caTissue Bug #169: ContainerPosition occupied container query returns parent
|
633
|
+
# container instead. Substitute a hard-coded HQL search for this case.
|
603
634
|
#
|
604
635
|
# @see CaRuby::Database#query_object
|
605
636
|
def query_object(obj, attribute=nil)
|
606
637
|
if hook_proxy_attribute?(obj, attribute) then
|
607
|
-
|
638
|
+
query_hook_proxies(obj, attribute)
|
608
639
|
elsif CaTissue::SpecimenRequirement === obj and not obj.identifier and obj.collection_protocol_event then
|
609
640
|
query_requirement_using_cpe_inversion(obj, attribute)
|
610
641
|
elsif CaTissue::ContainerPosition === obj and obj.identifier and attribute == :occupied_container then
|
@@ -621,34 +652,48 @@ module CaTissue
|
|
621
652
|
end
|
622
653
|
|
623
654
|
# @param (see #query_object)
|
624
|
-
# @return [Boolean] whether the given attribute is a reference from an {Annotatable} to a #{Annotation}
|
655
|
+
# @return [Boolean] whether the given attribute is a reference from an {Annotatable} to a #{Annotation::Proxy}
|
625
656
|
def hook_proxy_attribute?(obj, attribute)
|
626
657
|
return false if attribute.nil?
|
627
658
|
attr_md = obj.class.attribute_metadata(attribute)
|
628
|
-
attr_md.declarer < Annotatable and attr_md.type < Annotation
|
659
|
+
attr_md.declarer < Annotatable and attr_md.type < Annotation::Proxy
|
629
660
|
end
|
630
661
|
|
631
662
|
# Queries on the given object attribute using the {Annotation::IntegationService}.
|
632
663
|
#
|
633
|
-
# @param
|
664
|
+
# @param [Annotatable] hook the annotated domain object
|
665
|
+
# @param [Symbol] attribute the proxy attribute
|
634
666
|
# @result (see #query_object)
|
635
|
-
def
|
667
|
+
def query_hook_proxies(hook, attribute)
|
636
668
|
unless hook.identifier then
|
637
669
|
logger.debug { "Querying annotation hook #{hook.qp} proxy reference #{attribute} by collecting the matching #{hook.class.qp} proxy references..." }
|
638
|
-
return query(hook).map { |ref|
|
670
|
+
return query(hook).map { |ref| query_hook_proxies(ref, attribute) }.flatten
|
639
671
|
end
|
640
|
-
|
672
|
+
# the hook proxies
|
673
|
+
proxies = hook.send(attribute)
|
674
|
+
# catenate the query results for each proxy
|
675
|
+
proxies.each { |pxy| find_hook_proxy(pxy, hook) }
|
676
|
+
proxies
|
677
|
+
end
|
678
|
+
|
679
|
+
# Queries on the given proxy using the {Annotation::IntegationService}.
|
680
|
+
#
|
681
|
+
# @param [Annotation::Proxy] proxy the proxy object
|
682
|
+
# @param hook (see #query_hook_proxies)
|
683
|
+
# @param [Symbol] attribute the proxy attribute
|
684
|
+
# @result (see #query_object)
|
685
|
+
def find_hook_proxy(proxy, hook)
|
641
686
|
# update the proxy identifier if necessary
|
642
687
|
proxy.identifier ||= hook.identifier
|
643
688
|
# delegate to the integration service to find the referenced hook annotation proxies
|
644
|
-
logger.debug { "Delegating #{hook.qp}
|
645
|
-
annotator.integrator.
|
689
|
+
logger.debug { "Delegating #{hook.qp} proxy #{proxy} query to the integration service..." }
|
690
|
+
annotator.integrator.find(proxy)
|
646
691
|
end
|
647
692
|
|
648
|
-
# caCORE
|
649
|
-
#
|
650
|
-
#
|
651
|
-
#
|
693
|
+
# @quirk caCORE Override {CaRuby::Database::Reader#invertible_query?} to enable the Bug #147 work
|
694
|
+
# around in {#query_object}. Invertible queries are performed to work around Bug #79. However, this
|
695
|
+
# work-around induces Bug #147, so we disable the Bug #79 work-around here for the special case of
|
696
|
+
# a CPE in order to enable the Bug #147 work-around. And so it goes....
|
652
697
|
#
|
653
698
|
# @see CaRuby::Database#invertible_query?
|
654
699
|
def invertible_query?(obj, attribute)
|
@@ -673,9 +718,16 @@ module CaTissue
|
|
673
718
|
pmi = pnt.medical_identifiers.first
|
674
719
|
return if pmi.nil?
|
675
720
|
logger.debug { "Using alternative Participant fetch strategy to find Participant by medical record number..." }
|
676
|
-
|
677
|
-
|
678
|
-
|
721
|
+
# If the PMI has an identifier (unlikely) then find the PMI participant.
|
722
|
+
if pmi.identifier then return query(pmi.copy, :participant).first end
|
723
|
+
# Add the default site. If no default site, then bail.
|
724
|
+
if pmi.site.nil? then
|
725
|
+
pmi.add_defaults
|
726
|
+
return unless pmi.site
|
727
|
+
end
|
728
|
+
return unless exists?(pmi.site)
|
729
|
+
# Find the PMI based on the site and MRN.
|
730
|
+
return query(pmi.copy(:site, :medical_record_number), :participant).first
|
679
731
|
end
|
680
732
|
|
681
733
|
# @param [CaTissue::Specimen] spc the specimen to fetch
|