caruby-tissue 1.5.6 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (255) hide show
  1. data/Gemfile +17 -0
  2. data/History.md +5 -1
  3. data/README.md +2 -2
  4. data/bin/crtdump +2 -8
  5. data/bin/crtexample +2 -5
  6. data/bin/crtmigrate +3 -6
  7. data/bin/crtsmoke +3 -8
  8. data/conf/wustl/{log4j.properties → linux/log4j.properties} +3 -3
  9. data/conf/wustl/windows/log4j.properties +40 -0
  10. data/examples/galena/Gemfile +16 -0
  11. data/examples/galena/Gemfile.lock +88 -0
  12. data/examples/galena/README.md +16 -16
  13. data/examples/galena/Rakefile +30 -0
  14. data/examples/galena/bin/seed +5 -11
  15. data/examples/galena/conf/annotation/defaults.yaml +2 -0
  16. data/examples/galena/conf/{migration/annotation_fields.yaml → annotation/fields.yaml} +2 -4
  17. data/examples/galena/conf/defaults.yaml +9 -0
  18. data/examples/galena/conf/{migration/filter_fields.yaml → filter/fields.yaml} +0 -1
  19. data/examples/galena/conf/filter/values.yaml +8 -0
  20. data/examples/galena/conf/{migration/frozen_defaults.yaml → frozen/defaults.yaml} +0 -0
  21. data/examples/galena/conf/{migration/frozen_fields.yaml → frozen/fields.yaml} +0 -2
  22. data/examples/galena/conf/{migration/general_fields.yaml → general/fields.yaml} +0 -24
  23. data/examples/galena/conf/registration/fields.yaml +6 -0
  24. data/examples/galena/conf/{migration/simple_fields.yaml → simple/fields.yaml} +1 -6
  25. data/examples/galena/data/annotation.csv +1 -1
  26. data/examples/galena/data/filter.csv +1 -1
  27. data/examples/galena/data/frozen.csv +1 -1
  28. data/examples/galena/data/general.csv +1 -1
  29. data/examples/galena/data/registration.csv +1 -1
  30. data/examples/galena/data/simple.csv +1 -1
  31. data/examples/galena/galena.gemspec +24 -0
  32. data/examples/galena/lib/galena/filter.rb +25 -0
  33. data/examples/galena/lib/galena/{tissue/migration/frozen_shims.rb → frozen.rb} +6 -10
  34. data/examples/galena/lib/galena/seed.rb +126 -0
  35. data/examples/galena/lib/galena/version.rb +3 -0
  36. data/examples/galena/lib/galena.rb +18 -7
  37. data/examples/galena/log/galena.log +37351 -0
  38. data/examples/galena/log/galena.log.0 +147830 -0
  39. data/examples/galena/spec/annotation_spec.rb +46 -0
  40. data/examples/galena/spec/filter_spec.rb +94 -0
  41. data/examples/galena/spec/frozen_spec.rb +39 -0
  42. data/examples/galena/spec/general_spec.rb +62 -0
  43. data/examples/galena/spec/registration_spec.rb +37 -0
  44. data/examples/galena/spec/seed.rb +107 -0
  45. data/examples/galena/spec/simple_spec.rb +58 -0
  46. data/examples/galena/spec/spec_helper.rb +11 -0
  47. data/examples/galena/spec/support/migration.rb +70 -0
  48. data/lib/catissue/annotation/annotatable.rb +10 -8
  49. data/lib/catissue/annotation/annotation.rb +7 -7
  50. data/lib/catissue/annotation/de_integration.rb +9 -20
  51. data/lib/catissue/annotation/importer.rb +148 -0
  52. data/lib/catissue/annotation/introspector.rb +32 -0
  53. data/lib/catissue/annotation/metadata.rb +422 -0
  54. data/lib/catissue/annotation/proxy.rb +2 -2
  55. data/lib/catissue/annotation/proxy_class.rb +45 -30
  56. data/lib/catissue/annotation/record_entry_proxy.rb +2 -2
  57. data/lib/catissue/cli/command.rb +14 -24
  58. data/lib/catissue/cli/example.rb +5 -3
  59. data/lib/catissue/cli/migrate.rb +45 -37
  60. data/lib/catissue/cli/smoke.rb +2 -3
  61. data/lib/catissue/database/annotation/annotation_service.rb +8 -17
  62. data/lib/catissue/database/annotation/entity_facade.rb +33 -30
  63. data/lib/catissue/database/annotation/id_generator.rb +1 -1
  64. data/lib/catissue/database/annotation/integration_service.rb +11 -4
  65. data/lib/catissue/database/annotation/reference_writer.rb +38 -38
  66. data/lib/catissue/database/controlled_value_finder.rb +13 -28
  67. data/lib/catissue/database/controlled_values.rb +73 -45
  68. data/lib/catissue/database.rb +637 -277
  69. data/lib/catissue/domain/abstract_domain_object.rb +5 -0
  70. data/lib/catissue/domain/abstract_position.rb +3 -5
  71. data/lib/catissue/domain/abstract_specimen.rb +79 -65
  72. data/lib/catissue/domain/abstract_specimen_collection_group.rb +3 -6
  73. data/lib/catissue/domain/address.rb +0 -2
  74. data/lib/catissue/domain/cancer_research_group.rb +0 -3
  75. data/lib/catissue/domain/capacity.rb +2 -4
  76. data/lib/catissue/domain/check_in_check_out_event_parameter.rb +0 -3
  77. data/lib/catissue/domain/collection_event_parameters.rb +2 -7
  78. data/lib/catissue/domain/collection_protocol.rb +11 -16
  79. data/lib/catissue/domain/collection_protocol_event.rb +19 -12
  80. data/lib/catissue/domain/collection_protocol_registration.rb +8 -12
  81. data/lib/catissue/domain/consent_tier_response.rb +0 -4
  82. data/lib/catissue/domain/consent_tier_status.rb +1 -4
  83. data/lib/catissue/domain/container.rb +10 -10
  84. data/lib/catissue/domain/container_position.rb +4 -7
  85. data/lib/catissue/domain/container_type.rb +4 -7
  86. data/lib/catissue/domain/department.rb +0 -3
  87. data/lib/catissue/domain/disposal_event_parameters.rb +5 -5
  88. data/lib/catissue/domain/embedded_event_parameters.rb +1 -4
  89. data/lib/catissue/domain/external_identifier.rb +0 -12
  90. data/lib/catissue/domain/frozen_event_parameters.rb +1 -4
  91. data/lib/catissue/domain/institution.rb +0 -3
  92. data/lib/catissue/domain/new_specimen_array_order_item.rb +0 -5
  93. data/lib/catissue/domain/order_details.rb +0 -2
  94. data/lib/catissue/domain/participant/clinical/chemotherapy.rb +1 -3
  95. data/lib/catissue/domain/participant/clinical/duration.rb +2 -4
  96. data/lib/catissue/domain/participant/clinical/radiation_therapy.rb +2 -4
  97. data/lib/catissue/domain/participant.rb +22 -24
  98. data/lib/catissue/domain/participant_medical_identifier.rb +0 -4
  99. data/lib/catissue/domain/password.rb +0 -4
  100. data/lib/catissue/domain/race.rb +0 -3
  101. data/lib/catissue/domain/received_event_parameters.rb +3 -6
  102. data/lib/catissue/domain/site.rb +1 -4
  103. data/lib/catissue/domain/specimen/pathology/additional_finding.rb +12 -0
  104. data/lib/catissue/domain/specimen/pathology/details.rb +12 -0
  105. data/lib/catissue/domain/specimen/pathology/gleason_score.rb +12 -0
  106. data/lib/catissue/domain/specimen/pathology/histologic_grade.rb +12 -0
  107. data/lib/catissue/domain/specimen/pathology/histologic_type.rb +19 -0
  108. data/lib/catissue/domain/specimen/pathology/histologic_variant_type.rb +12 -0
  109. data/lib/catissue/domain/specimen/pathology/invasion.rb +12 -0
  110. data/lib/catissue/domain/specimen/pathology/prostate_specimen_gleason_score.rb +5 -11
  111. data/lib/catissue/domain/specimen/pathology/prostate_specimen_pathology_annotation.rb +12 -12
  112. data/lib/catissue/domain/specimen/pathology/specimen_additional_finding.rb +5 -14
  113. data/lib/catissue/domain/specimen/pathology/specimen_base_solid_tissue_pathology_annotation.rb +6 -21
  114. data/lib/catissue/domain/specimen/pathology/specimen_details.rb +4 -10
  115. data/lib/catissue/domain/specimen/pathology/specimen_histologic_grade.rb +4 -10
  116. data/lib/catissue/domain/specimen/pathology/specimen_histologic_type.rb +6 -14
  117. data/lib/catissue/domain/specimen/pathology/specimen_histologic_variant_type.rb +5 -11
  118. data/lib/catissue/domain/specimen/pathology/specimen_invasion.rb +5 -11
  119. data/lib/catissue/domain/specimen.rb +113 -76
  120. data/lib/catissue/domain/specimen_array.rb +0 -3
  121. data/lib/catissue/domain/specimen_array_content.rb +1 -4
  122. data/lib/catissue/domain/specimen_array_type.rb +1 -4
  123. data/lib/catissue/domain/specimen_characteristics.rb +0 -3
  124. data/lib/catissue/domain/specimen_collection_group/pathology/base_pathology_annotation.rb +2 -4
  125. data/lib/catissue/domain/specimen_collection_group/pathology/base_solid_tissue_pathology_annotation.rb +2 -4
  126. data/lib/catissue/domain/specimen_collection_group.rb +43 -53
  127. data/lib/catissue/domain/specimen_event_parameters.rb +24 -32
  128. data/lib/catissue/domain/specimen_position.rb +8 -5
  129. data/lib/catissue/domain/specimen_protocol.rb +3 -6
  130. data/lib/catissue/domain/specimen_requirement.rb +22 -20
  131. data/lib/catissue/domain/storage_container.rb +9 -12
  132. data/lib/catissue/domain/storage_type.rb +6 -10
  133. data/lib/catissue/domain/transfer_event_parameters.rb +3 -6
  134. data/lib/catissue/domain/user.rb +22 -29
  135. data/lib/catissue/{util → helpers}/collectible.rb +23 -18
  136. data/lib/catissue/helpers/collectible_event_parameters.rb +68 -0
  137. data/lib/catissue/helpers/controlled_value.rb +35 -0
  138. data/lib/catissue/{domain → helpers}/hash_code.rb +0 -0
  139. data/lib/catissue/{util → helpers}/location.rb +6 -5
  140. data/lib/catissue/helpers/log.rb +4 -0
  141. data/lib/catissue/{util → helpers}/person.rb +1 -1
  142. data/lib/catissue/{util → helpers}/position.rb +10 -8
  143. data/lib/catissue/helpers/properties_loader.rb +143 -0
  144. data/lib/catissue/{util → helpers}/storable.rb +2 -1
  145. data/lib/catissue/{util → helpers}/storage_type_holder.rb +9 -3
  146. data/lib/catissue/{annotation/annotatable_class.rb → metadata.rb} +73 -95
  147. data/lib/catissue/migration/migratable.rb +93 -44
  148. data/lib/catissue/migration/migrator.rb +26 -42
  149. data/lib/catissue/migration/shims.rb +1 -1
  150. data/lib/catissue/migration/unique.rb +76 -0
  151. data/lib/catissue/resource.rb +16 -20
  152. data/lib/catissue/version.rb +1 -1
  153. data/lib/catissue/wustl/logger.rb +52 -32
  154. data/lib/catissue.rb +38 -20
  155. data/test/lib/catissue/database/controlled_values_test.rb +22 -27
  156. data/test/lib/catissue/database/database_test.rb +18 -0
  157. data/test/lib/catissue/domain/address_test.rb +9 -11
  158. data/test/lib/catissue/domain/ca_tissue_test_defaults_test.rb +5 -16
  159. data/test/lib/catissue/domain/capacity_test.rb +2 -2
  160. data/test/lib/catissue/domain/collection_event_parameters_test.rb +16 -8
  161. data/test/lib/catissue/domain/collection_protocol_event_test.rb +1 -1
  162. data/test/lib/catissue/domain/collection_protocol_registration_test.rb +26 -16
  163. data/test/lib/catissue/domain/collection_protocol_test.rb +2 -2
  164. data/test/lib/catissue/domain/container_position_test.rb +7 -4
  165. data/test/lib/catissue/domain/department_test.rb +3 -3
  166. data/test/lib/catissue/domain/disposal_event_parameters_test.rb +1 -1
  167. data/test/lib/catissue/domain/external_identifier_test.rb +5 -1
  168. data/test/lib/catissue/domain/location_test.rb +4 -4
  169. data/test/lib/catissue/domain/participant_medical_identifier_test.rb +3 -3
  170. data/test/lib/catissue/domain/participant_test.rb +33 -20
  171. data/test/lib/catissue/domain/received_event_parameters_test.rb +19 -0
  172. data/test/lib/catissue/domain/site_test.rb +2 -2
  173. data/test/lib/catissue/domain/specimen_array_test.rb +3 -3
  174. data/test/lib/catissue/domain/specimen_array_type_test.rb +6 -6
  175. data/test/lib/catissue/domain/specimen_characteristics_test.rb +1 -1
  176. data/test/lib/catissue/domain/specimen_collection_group_test.rb +49 -13
  177. data/test/lib/catissue/domain/specimen_event_parameters_test.rb +4 -4
  178. data/test/lib/catissue/domain/specimen_position_test.rb +1 -1
  179. data/test/lib/catissue/domain/specimen_requirement_test.rb +2 -2
  180. data/test/lib/catissue/domain/specimen_test.rb +58 -24
  181. data/test/lib/catissue/domain/storage_container_test.rb +3 -16
  182. data/test/lib/catissue/domain/storage_type_test.rb +3 -3
  183. data/test/lib/catissue/domain/transfer_event_parameters_test.rb +17 -17
  184. data/test/lib/catissue/domain/user_test.rb +32 -34
  185. data/test/lib/catissue/helpers/properties_loader_test.rb +19 -0
  186. data/test/lib/catissue/migration/{test_case.rb → helpers/test_case.rb} +30 -20
  187. data/test/lib/examples/galena/tissue/domain/examples_test.rb +28 -38
  188. data/test/lib/examples/galena/tissue/helpers/test_case.rb +24 -0
  189. metadata +175 -99
  190. data/bin/crtextract +0 -47
  191. data/examples/galena/conf/extract/simple_fields.yaml +0 -4
  192. data/examples/galena/conf/migration/annotation_defaults.yaml +0 -2
  193. data/examples/galena/conf/migration/filter_defaults.yaml +0 -1
  194. data/examples/galena/conf/migration/filter_values.yaml +0 -13
  195. data/examples/galena/conf/migration/participant_fields.yaml +0 -4
  196. data/examples/galena/conf/migration/registration_fields.yaml +0 -5
  197. data/examples/galena/data/participant.csv +0 -1
  198. data/examples/galena/lib/galena/tissue/migration/filter_shims.rb +0 -41
  199. data/examples/galena/lib/galena/tissue/seed/defaults.rb +0 -127
  200. data/examples/psbin/README.md +0 -45
  201. data/examples/psbin/conf/adjuvant_hormone_defaults.yaml +0 -2
  202. data/examples/psbin/conf/adjuvant_radiation_defaults.yaml +0 -3
  203. data/examples/psbin/conf/biopsy_defaults.yaml +0 -3
  204. data/examples/psbin/conf/biopsy_fields.yaml +0 -9
  205. data/examples/psbin/conf/neoadjuvant_hormone_defaults.yaml +0 -2
  206. data/examples/psbin/conf/neoadjuvant_radiation_defaults.yaml +0 -3
  207. data/examples/psbin/conf/patient_defaults.yaml +0 -3
  208. data/examples/psbin/conf/patient_fields.yaml +0 -5
  209. data/examples/psbin/conf/surgery_defaults.yaml +0 -4
  210. data/examples/psbin/conf/surgery_fields.yaml +0 -15
  211. data/examples/psbin/conf/t_stage_defaults.yaml +0 -1
  212. data/examples/psbin/conf/t_stage_fields.yaml +0 -4
  213. data/examples/psbin/conf/therapy_fields.yaml +0 -5
  214. data/examples/psbin/data/adjuvant_hormone.csv +0 -1
  215. data/examples/psbin/data/adjuvant_radiation.csv +0 -1
  216. data/examples/psbin/data/biopsy.csv +0 -1
  217. data/examples/psbin/data/neoadjuvant_hormone.csv +0 -1
  218. data/examples/psbin/data/neoadjuvant_radiation.csv +0 -1
  219. data/examples/psbin/data/patient.csv +0 -1
  220. data/examples/psbin/data/surgery.csv +0 -1
  221. data/examples/psbin/data/t_stage.csv +0 -1
  222. data/examples/psbin/lib/psbin/biopsy_shims.rb +0 -15
  223. data/examples/psbin/lib/psbin/surgery_shims.rb +0 -15
  224. data/lib/catissue/annotation/annotation_class.rb +0 -406
  225. data/lib/catissue/annotation/annotation_module.rb +0 -106
  226. data/lib/catissue/domain.rb +0 -26
  227. data/lib/catissue/extract/command.rb +0 -31
  228. data/lib/catissue/extract/delta.rb +0 -58
  229. data/lib/catissue/extract/extractor.rb +0 -99
  230. data/lib/catissue/migration/uniquify.rb +0 -2
  231. data/lib/catissue/util/collectible_event_parameters.rb +0 -71
  232. data/lib/catissue/util/controlled_value.rb +0 -29
  233. data/lib/catissue/util/uniquify.rb +0 -86
  234. data/test/fixtures/catissue/domain/conf/catissue_override.yaml +0 -9
  235. data/test/fixtures/catissue/extract/conf/scg_extract.yaml +0 -3
  236. data/test/fixtures/catissue/extract/conf/scg_fields.yaml +0 -3
  237. data/test/fixtures/catissue/extract/conf/spc_extract.yaml +0 -3
  238. data/test/fixtures/catissue/extract/conf/spc_fields.yaml +0 -4
  239. data/test/fixtures/lib/catissue/defaults_test_fixture.rb +0 -206
  240. data/test/fixtures/lib/examples/galena/migration/alt_key_shims.rb +0 -7
  241. data/test/lib/catissue/domain/base_haemotology_pathology_test.rb +0 -24
  242. data/test/lib/catissue/extract/delta_test.rb +0 -25
  243. data/test/lib/catissue/extract/extractor_test.rb +0 -43
  244. data/test/lib/catissue/import/importable_module_test.rb +0 -14
  245. data/test/lib/catissue/test_case.rb +0 -247
  246. data/test/lib/examples/galena/tissue/migration/annotation_test.rb +0 -29
  247. data/test/lib/examples/galena/tissue/migration/filter_test.rb +0 -29
  248. data/test/lib/examples/galena/tissue/migration/frozen_test.rb +0 -36
  249. data/test/lib/examples/galena/tissue/migration/general_test.rb +0 -56
  250. data/test/lib/examples/galena/tissue/migration/participant_test.rb +0 -61
  251. data/test/lib/examples/galena/tissue/migration/registration_test.rb +0 -17
  252. data/test/lib/examples/galena/tissue/migration/seedify.rb +0 -119
  253. data/test/lib/examples/galena/tissue/migration/simple_test.rb +0 -30
  254. data/test/lib/examples/galena/tissue/migration/test_case.rb +0 -72
  255. data/test/lib/examples/psbin/migration_test.rb +0 -153
@@ -45,7 +45,7 @@ module CaTissue
45
45
 
46
46
  logger.debug { "Work around caTissue DE EntityManagerUtil bug by fetching the maximum #{table} identifier directly from the database..." }
47
47
  sql = TABLE_MAX_ID_SQL_TMPL % table
48
- result = @executor.execute { |dbh| dbh.select_one(sql) }
48
+ result = @executor.query(sql).first
49
49
  max = result ? result[0].to_i : 0
50
50
  next_id = max + 1
51
51
  # End of work-around
@@ -35,6 +35,17 @@ module CaTissue
35
35
  # Creates an entity map record with content (annotation entity id, annotation id, form context id).
36
36
  # This record associates the static hook record to the annotation record qualified by the context.
37
37
  #
38
+ # @quirk caTissue 1.1.2 The caTissue 1.1.2 ClientDemo*.java examples do not set the entity map record
39
+ # createdDate. This results in a caTissue 1.1.2 -> 1.2 migration error.
40
+ # The entity map record in question is +edu.common.dynamicextensions.domain.integration.EntityMapRecord+.
41
+ # However, that class is not referenced in the examples. Rather, the
42
+ # referenced class is +deintegration.EntityMapRecord+, which does not have a createdDate property.
43
+ # Therefore, there is no caRuby work-around for this caTissue bug. The only recourse is to set
44
+ # the created_date SQL column directly to today() prior to performing a 1.1.2-to-1.2 migration.
45
+ # 1.2 uses a different mechanism, so this bug is specific to 1.1.2, although, of course, this does
46
+ # not preclude the possibility of other obscure 1.2-to-2.0 caTissue migration bugs in a similar
47
+ # vein.
48
+ #
38
49
  # @param (see #associate)
39
50
  # @return [EntityMapRecord] the new entity map record
40
51
  def create_entity_map_record(hook, annotation)
@@ -42,14 +53,12 @@ module CaTissue
42
53
  emr = EntityMapRecord.new
43
54
  emr.static_entity_record_id = hook.identifier
44
55
  emr.dynamic_entity_record_id = annotation.identifier
45
-
46
56
  # the form context
47
57
  ctxt = form_context(hook, annotation)
48
58
  if ctxt then
49
59
  emr.form_context = ctxt
50
60
  emr.form_context_id = ctxt.id
51
61
  end
52
-
53
62
  emr
54
63
  end
55
64
 
@@ -57,7 +66,6 @@ module CaTissue
57
66
  # @return [FormContent] an undocumented bit of caTissue presentation flotsam polluting the data layer
58
67
  def form_context(hook, annotation)
59
68
  map = entity_map(hook, annotation)
60
-
61
69
  # the fetched form context
62
70
  ctxts = map.form_context_collection
63
71
  if ctxts.empty? then
@@ -69,7 +77,6 @@ module CaTissue
69
77
  ctxt = ctxts.first
70
78
  logger.debug { "#{hook} has form context id #{ctxt.id}." }
71
79
  end
72
-
73
80
  ctxt
74
81
  end
75
82
 
@@ -5,20 +5,20 @@ module CaTissue
5
5
  # DYEXT tables of the introspected Java annotation class properties.
6
6
  class ReferenceWriter
7
7
  # @param [Integer] eid the referencing annotation entity id
8
- # @param [CaRuby::Domain::Attribute] attr_md the annotation attribute metadata of the attribute to save
8
+ # @param [Jinx::Property] prop the annotation attribute metadata of the attribute to save
9
9
  # @param [Integer, nil] assn_eid the referenced annotation entity id
10
- def initialize(eid, attr_md, assn_eid=nil)
11
- logger.debug { "Mapping annotation #{attr_md.declarer.qp}.#{attr_md} role attributes to database columns..." }
10
+ def initialize(eid, prop, assn_eid=nil)
11
+ logger.debug { "Mapping annotation #{prop.declarer.qp}.#{prop} role attributes to database columns..." }
12
12
  efcd = EntityFacade.instance
13
13
  # the referenced annotation entity id
14
- assn_eid ||= associated_entity_id(eid, attr_md)
14
+ assn_eid ||= associated_entity_id(eid, prop)
15
15
  # the referenced entity database table
16
16
  @table = efcd.annotation_table_for_entity_id(assn_eid)
17
17
  # map the attribute => column
18
- attr_col_hash = map_attributes(attr_md.type, assn_eid)
19
- logger.debug { "Annotation #{attr_md.declarer.qp} #{attr_md} maps to #{@table} as #{attr_col_hash.qp}" }
18
+ attr_col_hash = map_attributes(prop.type, assn_eid)
19
+ logger.debug { "Annotation #{prop.declarer.qp} #{prop} reference type #{prop.type.qp} maps to #{@table} as #{attr_col_hash.qp}" }
20
20
  # the mapped attributes and columns
21
- @attrs, cols = attr_col_hash.to_a.transpose
21
+ @attributes, cols = attr_col_hash.to_a.transpose
22
22
  # the SQL parameters clause
23
23
  params = Array.new(cols.size, '?').join(', ')
24
24
  # the create SQL
@@ -26,7 +26,7 @@ module CaTissue
26
26
  # the update SQL
27
27
  @upd_sql = UPDATE_SQL % [@table, cols.map { |col| "#{col} = ?" }.join(', ')]
28
28
  # the superclass writer for annotations with superclass DE forms
29
- @parent = obtain_parent_writer(eid, attr_md)
29
+ @parent = obtain_parent_writer(eid, prop)
30
30
  end
31
31
 
32
32
  # @param [Annotation] annotation the referenced annotation value
@@ -37,11 +37,10 @@ module CaTissue
37
37
  annotation.identifier ||= next_identifier
38
38
  # the values to bind to the SQL parameters
39
39
  values = database_parameters(annotation)
40
- logger.debug { "Saving #{annotation} to #{@table}..." }
40
+ logger.debug { "Saving annotation #{annotation} to #{@table}..." }
41
41
  # dispatch the SQL update or create statement
42
- CaTissue::Database.instance.executor.execute do |dbh|
43
- dbh.prepare(sql) { |sth| sth.execute(*values) }
44
- end
42
+ Database.instance.executor.transact(sql, *values)
43
+ # Save the superclass attributes.
45
44
  if @parent then
46
45
  logger.debug { "Saving #{annotation} parent entity attributes..." }
47
46
  @parent.save(annotation)
@@ -60,53 +59,54 @@ module CaTissue
60
59
  # @param (see #initialize)
61
60
  # @return [Integer] the entity id for the given attribute role
62
61
  # @raise [AnnotationError] if the associated entity was not found
63
- def associated_entity_id(eid, attr_md)
64
- EntityFacade.instance.associated_entity_id(eid, attr_md.property_descriptor.name) or
65
- raise AnnotationError.new("Associated entity not found for entity #{eid} attribute #{attr_md}")
62
+ def associated_entity_id(eid, prop)
63
+ EntityFacade.instance.associated_entity_id(eid, prop.property_descriptor.name) or
64
+ raise AnnotationError.new("Associated entity not found for entity #{eid} attribute #{prop}")
66
65
  end
67
66
 
68
67
  # @param (see #initialize)
69
68
  # @return [Integer, nil] the superclass associated entity id for the given attribute role, or nil if none
70
- def obtain_parent_writer(eid, attr_md)
69
+ def obtain_parent_writer(eid, prop)
71
70
  # the superclass entity id for annotations with superclass DE forms
72
71
  peid = EntityFacade.instance.parent_entity_id(eid) || return
73
72
  # the associated entity id
74
- aeid = EntityFacade.instance.associated_entity_id(peid, attr_md.property_descriptor.name)
75
- ReferenceWriter.new(peid, attr_md, aeid) if aeid
73
+ aeid = EntityFacade.instance.associated_entity_id(peid, prop.property_descriptor.name)
74
+ ReferenceWriter.new(peid, prop, aeid) if aeid
76
75
  end
77
76
 
78
77
  # @param annotation (see #save)
79
78
  # @return [Array] the save SQL call parameters
80
79
  def database_parameters(annotation)
81
- @attrs.map do |attr|
82
- value = annotation.send(attr)
80
+ @attributes.map do |pa|
81
+ value = annotation.send(pa)
83
82
  Annotation === value ? value.identifier : value
84
83
  end
85
84
  end
86
85
 
87
86
  def map_attributes(klass, eid)
88
- # the non-domain columns
89
- hash = klass.nondomain_attributes.to_compact_hash do |attr|
90
- nondomain_attribute_column(klass, attr, eid)
87
+ # Fill in the attribute => column hash for non-domain attributes.
88
+ ach = klass.nondomain_attributes.to_compact_hash do |pas|
89
+ nondomain_attribute_column(klass, pas, eid)
91
90
  end
92
- # the owner attribute columns
91
+ # Add the owner attribute => column entries.
93
92
  klass.owner_attributes.each do |oattr|
94
- hash[oattr] = owner_attribute_column(klass, oattr, eid)
93
+ ach[oattr] = owner_attribute_column(klass, oattr, eid)
95
94
  end
96
- hash
95
+ ach
97
96
  end
98
97
 
99
98
  def nondomain_attribute_column(klass, attribute, eid)
100
- if attribute == :identifier then return IDENTIFIER_COL end
101
- attr_md = klass.attribute_metadata(attribute)
99
+ return IDENTIFIER_COL if attribute == :identifier
100
+ prop = klass.property(attribute)
102
101
  # skip an attribute declared by the superclass
103
- return unless attr_md.declarer == klass
104
- xctr = CaTissue::Database.instance.executor
105
- prop = attr_md.property_descriptor.name
102
+ return unless prop.declarer == klass
103
+ prop = prop.property_descriptor.name
106
104
  logger.debug { "Finding #{klass.qp} #{attribute} column for entity id #{eid} and property #{prop}..." }
107
- result = xctr.execute { |dbh| dbh.select_one(NONDOMAIN_COLUMN_SQL, prop, eid) }
105
+ result = Database.instance.executor.query(NONDOMAIN_COLUMN_SQL, prop, eid).first
108
106
  col = result[0] if result
109
- if col.nil? then raise AnnotationError.new("Column not found for #{klass.qp} #{attribute}") end
107
+ if col.nil? then
108
+ raise AnnotationError.new("Column not found for #{klass.qp} #{attribute}")
109
+ end
110
110
  col
111
111
  end
112
112
 
@@ -116,22 +116,22 @@ module CaTissue
116
116
  # @quirk caTissue The role can be a mutation of the demodulized class name as follows:
117
117
  # * decapitalization, e.g. role +specimenCollectionGroup+ for class +SpecimenCollectionGroup+
118
118
  #
119
- # @param [AnnotationClass] klass the annotation class
119
+ # @param [Metadata] klass the annotation class
120
120
  # @param [Symbol] attribute the owner attribute
121
121
  # @param [Integer] eid the annotation entity id
122
122
  # @return [String] the owner reference SQL column name
123
123
  def owner_attribute_column(klass, attribute, eid)
124
124
  logger.debug { "Finding #{klass.qp} #{attribute} column in the context of entity id #{eid}..." }
125
125
  # The referenced class name (confusingly called a source role in the caTissue schema).
126
- tgt_nm = klass.attribute_metadata(attribute).type.name.demodulize
127
- result = CaTissue::Database.instance.executor.execute { |dbh| dbh.select_one(OWNER_COLUMN_SQL, eid, tgt_nm) }
126
+ tgt_nm = klass.property(attribute).type.name.demodulize
127
+ result = Database.instance.executor.query(OWNER_COLUMN_SQL, eid, tgt_nm).first
128
128
  col = result[0] if result
129
129
  if col.nil? then
130
- result = CaTissue::Database.instance.executor.execute { |dbh| dbh.select_one(OWNER_COLUMN_SQL, eid, tgt_nm.decapitalize) }
130
+ result = Database.instance.executor.query(OWNER_COLUMN_SQL, eid, tgt_nm.decapitalize).first
131
131
  col = result[0] if result
132
132
  end
133
133
  if col.nil? then
134
- result = CaTissue::Database.instance.executor.execute { |dbh| dbh.select_one(ALT_1_1_OWNER_COLUMN_SQL, eid) }
134
+ result = Database.instance.executor.query(ALT_1_1_OWNER_COLUMN_SQL, eid).first
135
135
  col = result[0] if result
136
136
  end
137
137
  if col.nil? then raise AnnotationError.new("Column not found for #{klass.qp} owner attribute #{attribute}") end
@@ -1,43 +1,28 @@
1
- require 'caruby/util/log'
2
1
  require 'catissue/database/controlled_values'
3
2
 
4
3
  module CaTissue
4
+ class ControlledValueError < RuntimeError; end
5
+
5
6
  # Finds attribute controlled values.
6
7
  class ControlledValueFinder
7
8
  # Creates a new ControlledValueFinder for the given attribute.
8
9
  # The optional YAML properties file name maps input values to controlled values.
9
- def initialize(attribute, file=nil)
10
+ #
11
+ # @param [Symbol] attribute the CV attribute
12
+ def initialize(attribute)
10
13
  @attribute = attribute
11
- @remap_hash = load_controlled_value_hash(file)
12
14
  end
13
15
 
14
- # Returns the CV value for the given source value.
15
- # If the value is remapped, then that value is returned.
16
- # Otherwise, if the value is a standard CV, then the CV value is returned.
17
- # Otherwise, a warning message is printed to the log and this method returns nil.
16
+ # Returns the CV value for the given source value. A case-insensitive lookup
17
+ # is performed on the CV.
18
+ #
19
+ # @param [String, nil] value the CV string value to find
20
+ # @raise [ControlledValueError] if the CV was not found
21
+ # @see ControlledValues#find
18
22
  def controlled_value(value)
19
23
  return if value.blank?
20
- remapped = remapped_controlled_value(value)
21
- return remapped if remapped
22
- cv = supported_controlled_value(value)
23
- logger.warn("#{@attribute} value '#{value}' ignored since it is not a recognized controlled value.") if cv.nil?
24
- cv.value if cv
25
- end
26
-
27
- private
28
-
29
- def remapped_controlled_value(value)
30
- @remap_hash[value]
31
- end
32
-
33
- def supported_controlled_value(value)
34
- ControlledValues.instance.find(@attribute, value)
35
- end
36
-
37
- def load_controlled_value_hash(file)
38
- return {} unless file and File.exists?(file)
39
- logger.debug { "Loading controlled value map for #{@attribute} from #{file}..." }
40
- YAML::load_file(file)
24
+ ControlledValues.instance.find(@attribute, value) or
25
+ raise ControlledValueError.new("#{@attribute} value '#{value}' is not a recognized controlled value.")
41
26
  end
42
27
  end
43
28
  end
@@ -1,25 +1,26 @@
1
1
  require 'singleton'
2
- require 'caruby/util/log'
3
- require 'caruby/util/collection'
4
- require 'caruby/util/options'
5
- require 'caruby/util/visitor'
6
- require 'catissue/resource'
2
+ require 'jinx/helpers/collections'
3
+ require 'jinx/helpers/lazy_hash'
4
+ require 'jinx/helpers/case_insensitive_hash'
5
+ require 'jinx/helpers/options'
6
+ require 'jinx/helpers/visitor'
7
7
  require 'catissue/database'
8
- require 'catissue/util/controlled_value'
9
- require 'caruby/domain/properties'
8
+ require 'catissue/helpers/controlled_value'
10
9
 
11
10
  module CaTissue
12
11
  # This ControlledValues class loads caTissue permissible values from the database.
13
- # Use of this class requires the +dbi+ gem. See {CaRuby::SQLExecutor#initialize}
12
+ # Use of this class requires the +dbi+ gem. See +CaRuby::SQLExecutor.initialize+
14
13
  # for a description of the database access properties.
15
14
  class ControlledValues
16
15
  include Singleton
17
16
 
18
17
  def initialize
19
- @executor = CaTissue::Database.instance.executor
20
- @pid_loaded_hash = LazyHash.new { |pid| load_pid_cvs(pid) }
21
- @pid_value_cv_hash = LazyHash.new do |pid|
22
- CaseInsensitiveHash.new { |hash, value| hash[value] = load_cv(pid, value) unless value.nil? }
18
+ @executor = Database.instance.executor
19
+ # The pid => { value => CV } that associates each loaded parent CV to its children.
20
+ @pid_loaded_hash = Jinx::LazyHash.new { |pid| load_pid_cvs(pid) }
21
+ # The pid => { value => CV } that associates each fetched parent CV to its children.
22
+ @pid_value_cv_hash = Jinx::LazyHash.new do |pid|
23
+ Jinx::CaseInsensitiveHash.new { |hash, value| hash[value] = load_cv(pid, value) unless value.nil? }
23
24
  end
24
25
  end
25
26
 
@@ -27,37 +28,48 @@ module CaTissue
27
28
  # The CVs are loaded from the database if necessary.
28
29
  #
29
30
  # The following public id aliases are supported:
30
- # * :tissue_site
31
- # * :clinical_diagnosis
31
+ # * +:tissue_site+
32
+ # * +:clinical_diagnosis+
32
33
  #
33
- #@param [String,Symbol] public_id_or_alias the caTissue public id or an alias defined above
34
- # @return [<ControlledValue>] instances for the given public_id_or_alias
34
+ # @param [String, Symbol] public_id_or_alias the caTissue public id or an alias defined above
35
+ # @return [<CaRuby::ControlledValue>] instances for the given public_id_or_alias
35
36
  def for_public_id(public_id_or_alias)
36
37
  pid = ControlledValue.standard_public_id(public_id_or_alias)
37
38
  @pid_loaded_hash[pid].values
38
39
  end
39
40
 
40
- # Returns the ControlledValue with the given public_id_or_alias and value.
41
- # Loads the CV if necessary from the database. The loaded CV does not have a parent or children.
41
+ # Returns the CV with the given public_id_or_alias and value. Loads the CV if necessary
42
+ # from the database. The loaded CV does not have a parent or children.
42
43
  #
43
- #@param [String,Symbol] public_id_or_alias the supported for_public_id alias
44
- # @see #for_public_id load the CV hierarchy with supported aliases
45
- def find(public_id_or_alias, value)
44
+ # @param [String, Symbol] public_id_or_alias the caTissue public id or alias
45
+ # @param [String] value the CV value
46
+ # @param [Boolean] recursive whether to load the CV children as well.
47
+ # @return [CaRuby::ControlledValue, nil] the matching CV, or nil if no match
48
+ def find(public_id_or_alias, value, recursive=false)
46
49
  pid = ControlledValue.standard_public_id(public_id_or_alias)
47
- @pid_value_cv_hash[pid][value]
50
+ value_cv_hash = @pid_value_cv_hash[pid]
51
+ cv = value_cv_hash[value]
52
+ if recursive then
53
+ fetch_descendants(cv, value_cv_hash)
54
+ end
55
+ cv
48
56
  end
49
57
 
50
58
  # Creates a new controlled value record in the database from the given ControlledValue cv.
51
59
  # The default identifier is the next identifier in the permissible values table.
52
60
  #
53
61
  # @param [ControlledValue] cv the controlled value to create
54
- # @return cv
62
+ # @return [ControlledValue] the created CV
55
63
  def create(cv)
64
+ if cv.public_id.nil? then
65
+ raise ArgumentError.new("Controlled value create is missing a public id")
66
+ end
67
+ if cv.value.nil? then
68
+ raise ArgumentError.new("Controlled value create is missing a value")
69
+ end
56
70
  cv.identifier ||= next_id
57
- raise ArgumentError.new("Controlled value create is missing a public id") if cv.public_id.nil?
58
- raise ArgumentError.new("Controlled value create is missing a value") if cv.value.nil?
59
71
  logger.debug { "Creating controlled value #{cv} in the database..." }
60
- @executor.execute { |dbh| dbh.prepare(INSERT_STMT).execute(cv.identifier, cv.parent_identifier, cv.public_id, cv.value) }
72
+ @executor.transact(INSERT_STMT, cv.identifier, cv.parent_identifier, cv.public_id, cv.value)
61
73
  logger.debug { "Controlled value #{cv.public_id} #{cv.value} created with identifier #{cv.identifier}" }
62
74
  @pid_value_cv_hash[cv.public_id][cv.value] = cv
63
75
  end
@@ -67,12 +79,16 @@ module CaTissue
67
79
  #
68
80
  # @param [ControlledValue] cv the controlled value to delete
69
81
  def delete(cv)
70
- @executor.execute { |dbh| delete_recursive(cv, dbh.prepare(DELETE_STMT)) }
82
+ @executor.transact do |dbh|
83
+ sth = dbh.prepare(DELETE_STMT)
84
+ delete_recursive(cv, sth)
85
+ sth.finish
86
+ end
71
87
  end
72
88
 
73
89
  private
74
90
 
75
- PUBLIC_ID_ROOTS_STMT = "select identifier, value from catissue_permissible_value where public_id = ? and parent_identifier is null or parent_identifier = 0"
91
+ PID_ROOTS_STMT = "select identifier, value from catissue_permissible_value where public_id = ? and (parent_identifier is null or parent_identifier = 0)"
76
92
 
77
93
  CHILDREN_STMT = "select identifier, value from catissue_permissible_value where parent_identifier = ?"
78
94
 
@@ -90,7 +106,7 @@ module CaTissue
90
106
 
91
107
  def load_cv(public_id, value)
92
108
  logger.debug { "Loading controlled value #{public_id} #{value} from the database..." }
93
- row = @executor.execute { |dbh| dbh.select_one(SEARCH_STMT, value) }
109
+ row = @executor.query(SEARCH_STMT, value).first
94
110
  logger.debug { "Controlled value #{public_id} #{value} not found." } and return if row.nil?
95
111
  identifier = row[0]
96
112
  logger.debug { "Controlled value #{public_id} #{value} loaded with identifier #{identifier}." }
@@ -98,7 +114,7 @@ module CaTissue
98
114
  end
99
115
 
100
116
  def next_id
101
- @executor.execute { |dbh| dbh.select_one(MAX_ID_STMT)[0].to_i } + 1
117
+ @executor.query(MAX_ID_STMT).first[0].to_i + 1
102
118
  end
103
119
 
104
120
  def delete_recursive(cv, sth)
@@ -111,29 +127,41 @@ module CaTissue
111
127
  logger.debug { "Controlled value #{cv} deleted." }
112
128
  end
113
129
 
130
+ # @param [String] pid the public id to find
131
+ # @param [String => CaRuby::ControlledValue] the pid children value => CV hash
114
132
  def fetch_cvs_with_public_id(pid, value_cv_hash)
115
- id_cv_hash = {}
116
133
  logger.debug { "Loading #{pid} controlled values from the database..." }
117
- cvs = []
118
- @executor.execute do |dbh|
119
- dbh.select_all(PUBLIC_ID_ROOTS_STMT, pid) do |row|
120
- identifier, value = row
121
- cvs << value_cv_hash[value] ||= make_controlled_value(:identifier => identifier, :public_id => pid, :value => value)
122
- end
123
- # load the root CVs children
124
- cvs.each { |cv| fetch_descendants(cv, dbh, value_cv_hash) }
134
+ cvs = @executor.query(PID_ROOTS_STMT, pid).map do |row|
135
+ identifier, value = row
136
+ cv = make_controlled_value(:identifier => identifier, :public_id => pid, :value => value)
137
+ value_cv_hash[value] ||= cv
138
+ cv
125
139
  end
140
+ cvs.each { |cv| fetch_descendants(cv, value_cv_hash) }
141
+ logger.debug { "Loaded #{value_cv_hash.size} #{pid} controlled values from the database." }
126
142
  value_cv_hash
127
143
  end
128
144
 
129
- def fetch_descendants(cv, dbh, value_cv_hash)
130
- children = []
131
- dbh.select_all(CHILDREN_STMT, cv.identifier) do |row|
132
- identifier, value = row
133
- children << value_cv_hash[value] = make_controlled_value(:identifier => identifier, :public_id => pid, :parent => cv, :value => value)
145
+ def fetch_descendants(cv, value_cv_hash)
146
+ logger.debug { "Fetching #{cv} descendants..." }
147
+ # load the root CVs children
148
+ @executor.execute do |dbh|
149
+ sth = dbh.prepare(CHILDREN_STMT)
150
+ fetch_descendants_recursive(cv, value_cv_hash, sth)
151
+ sth.finish
152
+ end
153
+ end
154
+
155
+ def fetch_descendants_recursive(cv, value_cv_hash, sth)
156
+ pid = cv.public_id
157
+ # Collect the child CVs.
158
+ children = sth.execute(cv.identifier).map do |row|
159
+ identifier, value = row
160
+ child = make_controlled_value(:identifier => identifier, :public_id => pid, :parent => cv, :value => value)
161
+ value_cv_hash[value] = child
134
162
  end
135
163
  # recurse to chidren
136
- children.each { |cv| fetch_descendants(cv, dbh, value_cv_hash) }
164
+ children.each { |cv| fetch_descendants_recursive(cv, value_cv_hash, sth) }
137
165
  end
138
166
 
139
167
  # Returns a new ControlledValue with attributes set by the given attribute => value hash.