caruby-tissue 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. data/History.txt +4 -0
  2. data/README.md +6 -0
  3. data/lib/catissue/annotation/annotation_class.rb +1 -1
  4. data/lib/catissue/cli/migrate.rb +1 -0
  5. data/lib/catissue/domain/container.rb +13 -8
  6. data/lib/catissue/domain/specimen.rb +9 -3
  7. data/lib/catissue/domain/specimen_event_parameters.rb +1 -8
  8. data/lib/catissue/domain/specimen_requirement.rb +1 -1
  9. data/lib/catissue/domain/storage_container.rb +4 -3
  10. data/lib/catissue/domain/uniquify.rb +82 -0
  11. data/lib/catissue/migration/migrator.rb +15 -7
  12. data/lib/catissue/migration/uniquify.rb +2 -111
  13. data/lib/catissue/util/position.rb +14 -2
  14. data/lib/catissue/version.rb +1 -1
  15. data/test/fixtures/catissue/domain/conf/catissue_override.yaml +9 -0
  16. data/test/fixtures/catissue/extract/conf/scg_extract.yaml +3 -0
  17. data/test/fixtures/catissue/extract/conf/scg_fields.yaml +3 -0
  18. data/test/fixtures/catissue/extract/conf/spc_extract.yaml +3 -0
  19. data/test/fixtures/catissue/extract/conf/spc_fields.yaml +4 -0
  20. data/test/fixtures/lib/catissue/defaults_test_fixture.rb +202 -0
  21. data/test/lib/catissue/database/controlled_values_test.rb +47 -0
  22. data/test/lib/catissue/database/database_test.rb +28 -0
  23. data/test/lib/catissue/domain/address_test.rb +53 -0
  24. data/test/lib/catissue/domain/base_haemotology_pathology_test.rb +25 -0
  25. data/test/lib/catissue/domain/ca_tissue_test_defaults_test.rb +27 -0
  26. data/test/lib/catissue/domain/capacity_test.rb +12 -0
  27. data/test/lib/catissue/domain/collection_event_parameters_test.rb +24 -0
  28. data/test/lib/catissue/domain/collection_protocol_event_test.rb +25 -0
  29. data/test/lib/catissue/domain/collection_protocol_registration_test.rb +71 -0
  30. data/test/lib/catissue/domain/collection_protocol_test.rb +69 -0
  31. data/test/lib/catissue/domain/container_position_test.rb +29 -0
  32. data/test/lib/catissue/domain/department_test.rb +21 -0
  33. data/test/lib/catissue/domain/disposal_event_parameters_test.rb +16 -0
  34. data/test/lib/catissue/domain/location_test.rb +38 -0
  35. data/test/lib/catissue/domain/metadata_test.rb +62 -0
  36. data/test/lib/catissue/domain/participant_medical_identifier_test.rb +26 -0
  37. data/test/lib/catissue/domain/participant_test.rb +96 -0
  38. data/test/lib/catissue/domain/site_test.rb +30 -0
  39. data/test/lib/catissue/domain/specimen_array_test.rb +38 -0
  40. data/test/lib/catissue/domain/specimen_array_type_test.rb +27 -0
  41. data/test/lib/catissue/domain/specimen_characteristics_test.rb +15 -0
  42. data/test/lib/catissue/domain/specimen_collection_group_test.rb +216 -0
  43. data/test/lib/catissue/domain/specimen_event_parameters_test.rb +61 -0
  44. data/test/lib/catissue/domain/specimen_position_test.rb +62 -0
  45. data/test/lib/catissue/domain/specimen_requirement_test.rb +61 -0
  46. data/test/lib/catissue/domain/specimen_test.rb +272 -0
  47. data/test/lib/catissue/domain/storage_container_test.rb +150 -0
  48. data/test/lib/catissue/domain/storage_type_test.rb +70 -0
  49. data/test/lib/catissue/domain/transfer_event_parameters_test.rb +38 -0
  50. data/test/lib/catissue/domain/user_test.rb +50 -0
  51. data/test/lib/catissue/extract/delta_test.rb +25 -0
  52. data/test/lib/catissue/extract/extractor_test.rb +43 -0
  53. data/test/lib/catissue/import/importable_module_test.rb +14 -0
  54. data/test/lib/catissue/migration/test_case.rb +103 -0
  55. data/test/lib/catissue/test_case.rb +225 -0
  56. data/test/lib/examples/galena/domain/examples_test.rb +70 -0
  57. data/test/lib/examples/galena/migration/catissue.log +0 -0
  58. data/test/lib/examples/galena/migration/filter_test.rb +26 -0
  59. data/test/lib/examples/galena/migration/frozen_test.rb +28 -0
  60. data/test/lib/examples/galena/migration/general_test.rb +44 -0
  61. data/test/lib/examples/galena/migration/simple_test.rb +29 -0
  62. data/test/lib/examples/galena/migration/test_case.rb +52 -0
  63. data/test/lib/examples/galena/migration/uniquify.rb +93 -0
  64. metadata +223 -184
@@ -14,3 +14,7 @@
14
14
 
15
15
  * Full PCBIN DE support
16
16
 
17
+ === 1.3.2 / 2011-02-25
18
+
19
+ * Minor migration bug fixes
20
+
data/README.md CHANGED
@@ -91,3 +91,9 @@ Common fields are as follows:
91
91
  * Box - Tissue storage container
92
92
  * X - the tissue box column
93
93
  * Y - the tissue box row
94
+
95
+ Copyright
96
+ ---------
97
+
98
+ caRuby © 2010, 2011 by [Oregon Health & Science University](http://www.ohsu.edu/xd/health/services/cancer/index.cfm).
99
+ caRuby is licensed under the MIT license. Please see the LICENSE and LEGAL files for more information.
@@ -118,7 +118,7 @@ module CaTissue
118
118
  # Recurses the dependency hierarchy to this annotation class's dependents in a
119
119
  # breadth-first manner.
120
120
  #
121
- # @param [<CaRuby::AttributeMetadata>] the visited attributes
121
+ # @param [<CaRuby::AttributeMetadata>] path the visited attributes
122
122
  def add_dependent_attribute_closure(path=[])
123
123
  return if path.include?(self)
124
124
  attrs = dependent_attributes(false)
@@ -16,6 +16,7 @@ module CaTissue
16
16
  [:input, "-i", "input", "Source file to migrate"],
17
17
  [:target, "-t", "--target CLASS", "Migration target class"],
18
18
  [:mapping, "-m", "--mapping FILE", "The input field => caTissue attribute mapping file"],
19
+ [:defaults, "-d", "--defaults FILE", "The caTissue attribute => default value mapping file"],
19
20
  [:shims, "-s", "--shims FILE[,FILE...]", Array, "Migration customization shim files to load"],
20
21
  [:bad, "-b", "--bad FILE", "Write each invalid record to the given file and continue migration"],
21
22
  [:unique, "-u", "--unique", "Make the migrated objects unique for testing"],
@@ -137,6 +137,7 @@ module CaTissue
137
137
 
138
138
  # @return the occupant at the given zero-based row and column, or nil if none
139
139
  def [](column, row)
140
+ return if column.nil? or row.nil?
140
141
  all_occupied_positions.detect_value do |pos|
141
142
  return if row < pos.row
142
143
  next unless row == pos.row
@@ -149,10 +150,10 @@ module CaTissue
149
150
  # The storable Storable position is updated to reflect the new location. Returns self.
150
151
  #
151
152
  # @param [Storable] storable the item to add
152
- # @param [CaRuby::Coordinate, nil] coordinate the x-y coordinate to place the item
153
+ # @param [CaRuby::Coordinate, <Integer>] coordinate the x-y coordinate to place the item
153
154
  # @raise [IndexError] if this Container is full
154
155
  # @raise [IndexError] if the row and column are given but exceed the Container bounds
155
- def add(storable, coordinate=nil)
156
+ def add(storable, *coordinate)
156
157
  validate_type(storable)
157
158
  loc = create_location(coordinate)
158
159
  pos = storable.position || storable.position_class.new
@@ -185,18 +186,22 @@ module CaTissue
185
186
  # @param [Storable] the item to store
186
187
  # @raise [TypeError] if this container cannot hold the storable
187
188
  def validate_type(storable)
189
+ unless container_type then
190
+ raise TypeError.new("Container #{self} is missing a type")
191
+ end
188
192
  unless container_type.can_hold_child?(storable) then
189
- raise TypeError.new("Container #{self} cannot hold an item of the #{storable} type")
193
+ raise TypeError.new("Container #{self} cannot hold an item of the #{storable} type #{storable.container_type}")
190
194
  end
191
195
  end
192
196
 
193
- # @param [Coordinate] coordinate the optional location to create
197
+ # @param coordinate (see #add)
194
198
  # @return [Location] the created location
195
- def create_location(coordinate=nil)
196
- if coordinate then
197
- Location.new(:in => self, :at => coordinate)
198
- else
199
+ def create_location(coordinate)
200
+ if coordinate.empty? then
199
201
  first_available_location or raise IndexError.new("Container #{qp} does not have an available location")
202
+ else
203
+ if coordinate.size == 1 then coordinate = coordinate.first end
204
+ Location.new(:in => self, :at => coordinate)
200
205
  end
201
206
  end
202
207
 
@@ -217,9 +217,7 @@ module CaTissue
217
217
  # one external identifier.
218
218
  def match_in_owner_scope(others)
219
219
  super or others.detect do |other|
220
- external_identifiers.any? do |eid|
221
- other.external_identifiers.detect { |oeid| eid.name == oeid.name and eid.value == oeid.value }
222
- end
220
+ other.class == self.class and external_identifier_match?(other)
223
221
  end
224
222
  end
225
223
 
@@ -368,6 +366,14 @@ module CaTissue
368
366
  MERGEABLE_RQMT_ATTRS = nondomain_java_attributes - primary_key_attributes
369
367
 
370
368
  MERGEABLE_SPC_CHR_ATTRS = SpecimenCharacteristics.nondomain_java_attributes - SpecimenCharacteristics.primary_key_attributes
369
+
370
+ # @param [Resource] other the object to match
371
+ # @return [Boolean] whether this specimen matches the other specimen on at least one external identifier
372
+ def external_identifier_match?(other)
373
+ external_identifiers.any? do |eid|
374
+ other.external_identifiers.detect { |oeid| eid.name == oeid.name and eid.value == oeid.value }
375
+ end
376
+ end
371
377
 
372
378
  # @see #fetch_saved
373
379
  def available_quantity_changed?
@@ -93,15 +93,8 @@ module CaTissue
93
93
  self.user ||= default_user
94
94
  end
95
95
 
96
- # Returns whether the given value is either nil, empty or equals other.
97
- def missing_or_match?(attribute, other)
98
- value = send(attr)
99
- value.nil_or_empty? or value == other.send(attr)
100
- end
101
-
102
96
  def default_user
103
- scg = specimen_collection_group
104
- scg ||= specimen.specimen_collection_group if specimen
97
+ scg = specimen_collection_group || (specimen.specimen_collection_group if specimen)
105
98
  scg.receiver if scg
106
99
  end
107
100
  end
@@ -46,7 +46,7 @@ module CaTissue
46
46
  # in others with the same class, specimen type, pathological_status and characteristics.
47
47
  def match_in_owner_scope(others)
48
48
  others.detect do |other|
49
- self.class === other and specimen_type == other.specimen_type and pathological_status == other.pathological_status and
49
+ self.class == other.class and specimen_type == other.specimen_type and pathological_status == other.pathological_status and
50
50
  characteristics and characteristics.match?(other.characteristics)
51
51
  end
52
52
  end
@@ -61,6 +61,7 @@ module CaTissue
61
61
  end
62
62
 
63
63
  alias :add_local :add
64
+ private :add_local
64
65
 
65
66
  # Adds the given storable to this container. If the storable has a current position, then
66
67
  # the storable is moved from that position to this container. The new position is given
@@ -76,12 +77,12 @@ module CaTissue
76
77
  # freezer << specimen #=> places the specimen in the first available box in the freezer
77
78
  #
78
79
  # @param [Storable] the item to add
79
- # @param [Coordinate] the storage location (default is first available location)
80
+ # @param [Coordinate, <Integer>] the storage location (default is first available location)
80
81
  # @return [StorageContainer] self
81
82
  # @raise [IndexError] if this Container is full
82
83
  # @raise [IndexError] if the row and column are given but exceed the Container bounds
83
- def add(storable, coordinate=nil)
84
- return add_local(storable, coordinate) if coordinate
84
+ def add(storable, *coordinate)
85
+ return add_local(storable, *coordinate) if coordinate
85
86
  add_to_existing_container(storable) or add_to_new_subcontainer(storable) or out_of_bounds(storable)
86
87
  self
87
88
  end
@@ -0,0 +1,82 @@
1
+ require 'caruby/domain/uniquify'
2
+
3
+ module CaTissue
4
+ class CollectionProtocol
5
+ include CaRuby::Resource::Unique
6
+
7
+ # Makes this CP's short and long title unique.
8
+ def uniquify
9
+ super
10
+ self.title = short_title
11
+ end
12
+ end
13
+
14
+ class Container
15
+ include CaRuby::Resource::Unique
16
+
17
+ # Makes this Container and all of its subcontainers unique.
18
+ def uniquify
19
+ super
20
+ subcontainers.each { |ctr| ctr.uniquify }
21
+ end
22
+ end
23
+
24
+ class ParticipantMedicalIdentifier
25
+ include CaRuby::Resource::Unique
26
+ end
27
+
28
+ class CollectionProtocolRegistration
29
+ include CaRuby::Resource::Unique
30
+
31
+ # Makes this CPR's PPI unique.
32
+ def uniquify
33
+ oldval = protocol_participant_identifier || return
34
+ newval = uniquify_value(oldval)
35
+ self.protocol_participant_identifier = newval
36
+ logger.debug { "Reset #{qp} PPI from #{oldval} to unique value #{newval}." }
37
+ end
38
+ end
39
+
40
+ class SpecimenCollectionGroup
41
+ include CaRuby::Resource::Unique
42
+
43
+ # Makes this SCG's SPN unique.
44
+ def uniquify
45
+ super
46
+ oldval = surgical_pathology_number || return
47
+ newval = uniquify_value(oldval)
48
+ self.surgical_pathology_number = newval
49
+ logger.debug { "Reset #{qp} SPN from #{oldval} to unique value #{newval}." }
50
+ end
51
+ end
52
+
53
+ class Specimen
54
+ include CaRuby::Resource::Unique
55
+ end
56
+
57
+ class ExternalIdentifier
58
+ include CaRuby::Resource::Unique
59
+
60
+ # Makes this ExternalIdentifier's value unique.
61
+ def uniquify
62
+ oldval = value || return
63
+ newval = uniquify_value(oldval)
64
+ self.value = newval
65
+ logger.debug { "Reset #{qp} value from #{oldval} to unique value #{newval}." }
66
+ end
67
+ end
68
+
69
+ class User
70
+ include CaRuby::Resource::Unique
71
+
72
+ # Makes this User's login id and email address unique.
73
+ # The result is in the form _name___suffix_+@test.com+
74
+ # where:
75
+ # * _name_ is the name prefix portion of the original email address
76
+ # * _suffix_ is a unique number
77
+ def uniquify
78
+ email = email_address ||= self.login_name || return
79
+ self.login_name = self.email_address = uniquify_value(email[/[^@]+/]) + '@test.com'
80
+ end
81
+ end
82
+ end
@@ -30,6 +30,8 @@ module CaTissue
30
30
  # @option opts [String] :target required target domain class
31
31
  # @option opts [String] :input required source file to migrate
32
32
  # @option opts [String] :shims optional array of shim files to load
33
+ # @option opts [String] :unique makes migrated objects unique object for testing
34
+ # mix-in do not conflict with existing or future objects
33
35
  # @option opts [String] :bad write each invalid record to the given file and continue migration
34
36
  # @option opts [String] :offset zero-based starting source record number to process (default 0)
35
37
  def initialize(opts={})
@@ -40,12 +42,6 @@ module CaTissue
40
42
  # add config options but don't override the parameter options
41
43
  opts.merge!(conf, :deep) { |key, oldval, newval| oldval }
42
44
  end
43
-
44
-
45
- # TODO - move opt parsing to CaTissue::CLI::Migrate and call that from test cases
46
- # Migrate then calls this Migrator with parsed options
47
-
48
-
49
45
  # open the log file before building structure
50
46
  log_file = opts[:log]
51
47
  CaRuby::Log.instance.open(log_file, :debug => opts[:debug]) if log_file
@@ -60,12 +56,22 @@ module CaTissue
60
56
  # call the CaRuby::Migrator initializer with the augmented options
61
57
  super
62
58
 
63
- # the options specific to this CaTissue::Migrator subclass
59
+ # The remaining options are specific to this CaTissue::Migrator subclass.
60
+
61
+ # If the unique option is set, then append the CaTissue-specific uniquifier shim.
62
+ if opts[:unique] then
63
+ # add the uniquify shim
64
+ @shims << UNIQUIFY_SHIM
65
+ end
66
+
67
+ # The tissue site CV look-up option.
64
68
  tissue_sites = opts[:tissue_sites]
65
69
  if tissue_sites then
66
70
  CaTissue::SpecimenCharacteristics.tissue_site_cv_finder = ControlledValueFinder.new(:tissue_site, tissue_sites)
67
71
  logger.info("Migrator enabled controlled value lookup.")
68
72
  end
73
+
74
+ # The clinical diagnosis CV look-up option.
69
75
  diagnoses = opts[:diagnoses]
70
76
  if diagnoses then
71
77
  CaTissue::SpecimenCollectionGroup.diagnosis_cv_finder = ControlledValueFinder.new(:clinical_diagnosis, diagnoses)
@@ -75,6 +81,8 @@ module CaTissue
75
81
 
76
82
  private
77
83
 
84
+ UNIQUIFY_SHIM = File.join(File.dirname(__FILE__), 'uniquify')
85
+
78
86
  # Clears the migration protocol CPR and SCG references.
79
87
  # This action frees up memory for the next iteration, thereby ensuring that migration is an
80
88
  # O(1) rather than O(n) operation.
@@ -1,111 +1,2 @@
1
- require 'caruby/migration/uniquify'
2
-
3
- module CaTissue
4
- shims CollectionProtocol, Site, User, Container, ExternalIdentifier, ParticipantMedicalIdentifier,
5
- CollectionProtocolRegistration, SpecimenCollectionGroup, Specimen
6
-
7
- class CollectionProtocol
8
- include CaRuby::Migratable::Unique
9
-
10
- # Makes this CP's title unique.
11
- def uniquify
12
- self.title = self.short_title = uniquify_value(short_title)
13
- end
14
- end
15
-
16
- class Site
17
- include CaRuby::Migratable::Unique
18
-
19
- # Makes this Site's name unique.
20
- def uniquify
21
- self.name = uniquify_value(name)
22
- end
23
- end
24
-
25
- class CollectionProtocolRegistration
26
- include CaRuby::Migratable::Unique
27
-
28
- # Makes this CPR's PPI unique.
29
- def uniquify
30
- self.protocol_participant_identifier = uniquify_value(protocol_participant_identifier)
31
- end
32
- end
33
-
34
- class Container
35
- include CaRuby::Migratable::Unique
36
-
37
- # Makes this Container's name unique.
38
- def uniquify
39
- self.name = uniquify_value(name)
40
- end
41
- end
42
-
43
- class ContainerType
44
- include CaRuby::Migratable::Unique
45
-
46
- # Makes this ContainerType's name unique.
47
- def uniquify
48
- self.name = uniquify_value(name)
49
- end
50
- end
51
-
52
- class StorageType
53
- include CaRuby::Migratable::Unique
54
-
55
- # Makes this StorageType and the ContainerTypes which it holds unique.
56
- def uniquify
57
- super
58
- child_types.each { |ct| ct.uniquify if ContainerType === ct }
59
- end
60
- end
61
-
62
- class SpecimenCollectionGroup
63
- include CaRuby::Migratable::Unique
64
-
65
- # Makes this SCG's SPN unique.
66
- def uniquify
67
- self.surgical_pathology_number = uniquify_value(surgical_pathology_number)
68
- end
69
- end
70
-
71
- class Specimen
72
- include CaRuby::Migratable::Unique
73
-
74
- # Makes this Specimen's label unique.
75
- def uniquify
76
- self.label = uniquify_value(label.uniquify) if label
77
- end
78
- end
79
-
80
- class ExternalIdentifier
81
- include CaRuby::Migratable::Unique
82
-
83
- # Makes this ExternalIdentifier's value unique.
84
- def uniquify
85
- self.value = uniquify_value(value)
86
- end
87
- end
88
-
89
- class ParticipantMedicalIdentifier
90
- include CaRuby::Migratable::Unique
91
-
92
- # Makes this PMI's MRN unique.
93
- def uniquify
94
- self.medical_record_number = uniquify_value(medical_record_number)
95
- end
96
- end
97
-
98
- class User
99
- include CaRuby::Migratable::Unique
100
-
101
- # Makes this User's login id and email address unique.
102
- # The result is in the form _name___suffix_+@test.com+
103
- # where:
104
- # * _name_ is the name prefix portion of the original email address
105
- # * _suffix_ is a unique number
106
- def uniquify
107
- email = email_address ||= self.login_name || return
108
- self.login_name = self.email_address = uniquify_value(email[/[^@]+/]) + '@test.com'
109
- end
110
- end
111
- end
1
+ # This include file is a convenience CaTissue migrator shim which enables CaTissue class uniquification.
2
+ require 'catissue/domain/uniquify'
@@ -47,6 +47,11 @@ module CaTissue
47
47
  self.row = @location.row
48
48
  self.column = @location.column
49
49
  end
50
+
51
+ # @return whether either the column or the row is nil
52
+ def unspecified?
53
+ column.nil? or row.nil?
54
+ end
50
55
 
51
56
  # @return [(Integer, Integer)] this Position's zero-based ({#column}, {#row}) tuple.
52
57
  def to_a
@@ -56,8 +61,15 @@ module CaTissue
56
61
  # @raise [ValidationError] if the holder cannot hold the occupant type
57
62
  def validate
58
63
  super
59
- unless holder.can_hold_child?(occupant) then
60
- raise ValidationError.new("#{self} cannot be occupied by #{occupant}")
64
+ logger.debug { "Validating that #{holder} can hold #{occupant}..." }
65
+ curr_occ = holder[column, row]
66
+ if curr_occ.nil? then
67
+ unless holder.can_hold_child?(occupant) then
68
+ reason = holder.full? ? "it is full" : "the occupant type is not among the supported types #{holder.container_type.child_types.qp}"
69
+ raise ValidationError.new("#{holder} cannot hold #{occupant} since #{reason}")
70
+ end
71
+ elsif curr_occ != occupant
72
+ raise ValidationError.new("#{holder} cannot hold #{occupant} since the location #{[colum, row]} is already occupied by #{curr_occ}")
61
73
  end
62
74
  end
63
75
  end
@@ -1,6 +1,6 @@
1
1
  module CaTissue
2
2
  # The version of this caRuby Tissue release.
3
- VERSION = "1.3.1"
3
+ VERSION = "1.3.2"
4
4
 
5
5
  # The supported caTissue release versions.
6
6
  CATISSUE_VERSIONS = "1.1.2"