ccls-ccls_engine 3.11.0

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 (269) hide show
  1. data/README.rdoc +182 -0
  2. data/app/models/abstract.rb +181 -0
  3. data/app/models/abstract_search.rb +50 -0
  4. data/app/models/abstract_validations.rb +324 -0
  5. data/app/models/address.rb +70 -0
  6. data/app/models/address_type.rb +15 -0
  7. data/app/models/addressing.rb +147 -0
  8. data/app/models/aliquot.rb +44 -0
  9. data/app/models/aliquot_sample_format.rb +13 -0
  10. data/app/models/analysis.rb +14 -0
  11. data/app/models/bc_request.rb +20 -0
  12. data/app/models/candidate_control.rb +101 -0
  13. data/app/models/context.rb +23 -0
  14. data/app/models/context_data_source.rb +4 -0
  15. data/app/models/county.rb +16 -0
  16. data/app/models/data_source.rb +24 -0
  17. data/app/models/diagnosis.rb +23 -0
  18. data/app/models/document_type.rb +16 -0
  19. data/app/models/document_version.rb +27 -0
  20. data/app/models/enrollment.rb +78 -0
  21. data/app/models/enrollment_validations.rb +167 -0
  22. data/app/models/follow_up.rb +16 -0
  23. data/app/models/follow_up_type.rb +18 -0
  24. data/app/models/gift_card.rb +22 -0
  25. data/app/models/gift_card_search.rb +137 -0
  26. data/app/models/home_exposure_response.rb +24 -0
  27. data/app/models/homex_outcome.rb +75 -0
  28. data/app/models/hospital.rb +22 -0
  29. data/app/models/icf_master_id.rb +30 -0
  30. data/app/models/icf_master_tracker.rb +217 -0
  31. data/app/models/icf_master_tracker_change.rb +9 -0
  32. data/app/models/icf_master_tracker_update.rb +50 -0
  33. data/app/models/ineligible_reason.rb +26 -0
  34. data/app/models/instrument.rb +26 -0
  35. data/app/models/instrument_type.rb +17 -0
  36. data/app/models/instrument_version.rb +28 -0
  37. data/app/models/interview.rb +122 -0
  38. data/app/models/interview_method.rb +17 -0
  39. data/app/models/interview_outcome.rb +16 -0
  40. data/app/models/language.rb +28 -0
  41. data/app/models/live_birth_data_update.rb +142 -0
  42. data/app/models/operational_event.rb +99 -0
  43. data/app/models/operational_event_type.rb +31 -0
  44. data/app/models/organization.rb +28 -0
  45. data/app/models/patient.rb +63 -0
  46. data/app/models/patient_validations.rb +118 -0
  47. data/app/models/person.rb +28 -0
  48. data/app/models/phone_number.rb +105 -0
  49. data/app/models/phone_type.rb +15 -0
  50. data/app/models/project.rb +39 -0
  51. data/app/models/project_outcome.rb +19 -0
  52. data/app/models/race.rb +31 -0
  53. data/app/models/refusal_reason.rb +23 -0
  54. data/app/models/sample.rb +168 -0
  55. data/app/models/sample_kit.rb +14 -0
  56. data/app/models/sample_outcome.rb +16 -0
  57. data/app/models/sample_temperature.rb +14 -0
  58. data/app/models/sample_type.rb +37 -0
  59. data/app/models/search.rb +195 -0
  60. data/app/models/section.rb +18 -0
  61. data/app/models/state.rb +25 -0
  62. data/app/models/study_subject.rb +237 -0
  63. data/app/models/study_subject_abstracts.rb +47 -0
  64. data/app/models/study_subject_addresses.rb +34 -0
  65. data/app/models/study_subject_associations.rb +38 -0
  66. data/app/models/study_subject_duplicates.rb +111 -0
  67. data/app/models/study_subject_enrollments.rb +17 -0
  68. data/app/models/study_subject_homex_outcome.rb +22 -0
  69. data/app/models/study_subject_identifier.rb +153 -0
  70. data/app/models/study_subject_interviews.rb +25 -0
  71. data/app/models/study_subject_languages.rb +21 -0
  72. data/app/models/study_subject_operational_events.rb +66 -0
  73. data/app/models/study_subject_patient.rb +177 -0
  74. data/app/models/study_subject_pii.rb +74 -0
  75. data/app/models/study_subject_races.rb +25 -0
  76. data/app/models/study_subject_search.rb +260 -0
  77. data/app/models/study_subject_validations.rb +116 -0
  78. data/app/models/subject_language.rb +11 -0
  79. data/app/models/subject_race.rb +11 -0
  80. data/app/models/subject_relationship.rb +21 -0
  81. data/app/models/subject_type.rb +22 -0
  82. data/app/models/tracing_status.rb +20 -0
  83. data/app/models/transfer.rb +40 -0
  84. data/app/models/unit.rb +14 -0
  85. data/app/models/vital_status.rb +19 -0
  86. data/app/models/zip_code.rb +36 -0
  87. data/config/abstract_fields.yml +1038 -0
  88. data/config/abstract_sections.yml +77 -0
  89. data/config/home_exposure_response_fields.yml +583 -0
  90. data/config/icf_master_tracker_update.yml +56 -0
  91. data/config/live_birth_data_update.yml +56 -0
  92. data/config/shared_use_db.yml +4 -0
  93. data/generators/ccls_engine/USAGE +2 -0
  94. data/generators/ccls_engine/ccls_engine_generator.rb +123 -0
  95. data/generators/ccls_engine/templates/autotest_ccls_engine.rb +3 -0
  96. data/generators/ccls_engine/templates/ccls_engine.rake +12 -0
  97. data/generators/ccls_engine/templates/fixtures/address_types.yml +30 -0
  98. data/generators/ccls_engine/templates/fixtures/context_data_sources.yml +54 -0
  99. data/generators/ccls_engine/templates/fixtures/contexts.yml +19 -0
  100. data/generators/ccls_engine/templates/fixtures/data_sources.yml +40 -0
  101. data/generators/ccls_engine/templates/fixtures/diagnoses.yml +40 -0
  102. data/generators/ccls_engine/templates/fixtures/document_types.yml +65 -0
  103. data/generators/ccls_engine/templates/fixtures/document_versions.csv +155 -0
  104. data/generators/ccls_engine/templates/fixtures/follow_up_types.yml +16 -0
  105. data/generators/ccls_engine/templates/fixtures/hospitals.yml +114 -0
  106. data/generators/ccls_engine/templates/fixtures/ineligible_reasons.yml +35 -0
  107. data/generators/ccls_engine/templates/fixtures/instrument_types.yml +26 -0
  108. data/generators/ccls_engine/templates/fixtures/instrument_versions.yml +22 -0
  109. data/generators/ccls_engine/templates/fixtures/instruments.yml +22 -0
  110. data/generators/ccls_engine/templates/fixtures/interview_methods.yml +30 -0
  111. data/generators/ccls_engine/templates/fixtures/interview_outcomes.yml +31 -0
  112. data/generators/ccls_engine/templates/fixtures/languages.yml +34 -0
  113. data/generators/ccls_engine/templates/fixtures/operational_event_types.yml +141 -0
  114. data/generators/ccls_engine/templates/fixtures/organizations.yml +198 -0
  115. data/generators/ccls_engine/templates/fixtures/people.yml +130 -0
  116. data/generators/ccls_engine/templates/fixtures/phone_types.yml +30 -0
  117. data/generators/ccls_engine/templates/fixtures/project_outcomes.yml +25 -0
  118. data/generators/ccls_engine/templates/fixtures/projects.yml +59 -0
  119. data/generators/ccls_engine/templates/fixtures/races.yml +52 -0
  120. data/generators/ccls_engine/templates/fixtures/refusal_reasons.yml +55 -0
  121. data/generators/ccls_engine/templates/fixtures/sample_outcomes.yml +36 -0
  122. data/generators/ccls_engine/templates/fixtures/sample_temperatures.yml +16 -0
  123. data/generators/ccls_engine/templates/fixtures/sample_types.yml +147 -0
  124. data/generators/ccls_engine/templates/fixtures/sections.yml +31 -0
  125. data/generators/ccls_engine/templates/fixtures/states.yml +363 -0
  126. data/generators/ccls_engine/templates/fixtures/subject_relationships.yml +46 -0
  127. data/generators/ccls_engine/templates/fixtures/subject_types.yml +30 -0
  128. data/generators/ccls_engine/templates/fixtures/tracing_statuses.yml +30 -0
  129. data/generators/ccls_engine/templates/fixtures/units.yml +13 -0
  130. data/generators/ccls_engine/templates/fixtures/vital_statuses.yml +28 -0
  131. data/generators/ccls_engine/templates/functional/roles_controller_test.rb +142 -0
  132. data/generators/ccls_engine/templates/functional/sessions_controller_test.rb +19 -0
  133. data/generators/ccls_engine/templates/functional/users_controller_test.rb +94 -0
  134. data/generators/ccls_engine/templates/images/sort_down.png +0 -0
  135. data/generators/ccls_engine/templates/images/sort_up.png +0 -0
  136. data/generators/ccls_engine/templates/initializer.rb +28 -0
  137. data/generators/ccls_engine/templates/javascripts/ccls_engine.js +24 -0
  138. data/generators/ccls_engine/templates/javascripts/jquery-ui.js +763 -0
  139. data/generators/ccls_engine/templates/javascripts/jquery.js +154 -0
  140. data/generators/ccls_engine/templates/javascripts/jrails.js +1 -0
  141. data/generators/ccls_engine/templates/migrations/create_user_invitations.rb +18 -0
  142. data/generators/ccls_engine/templates/migrations/create_users.rb +33 -0
  143. data/generators/ccls_engine/templates/migrations/drop_user_invitations.rb +18 -0
  144. data/generators/ccls_engine/templates/stylesheets/ccls_engine.css +180 -0
  145. data/generators/ccls_engine/templates/stylesheets/user.css +35 -0
  146. data/generators/ccls_engine/templates/stylesheets/users.css +23 -0
  147. data/generators/ccls_engine/templates/unit/core_extension_test.rb +18 -0
  148. data/generators/ccls_engine/templates/unit/role_test.rb +30 -0
  149. data/generators/ccls_engine/templates/unit/user_test.rb +321 -0
  150. data/lib/ccls-ccls_engine.rb +1 -0
  151. data/lib/ccls_engine.rb +135 -0
  152. data/lib/ccls_engine/action_view_extension.rb +3 -0
  153. data/lib/ccls_engine/action_view_extension/base.rb +53 -0
  154. data/lib/ccls_engine/action_view_extension/form_builder.rb +39 -0
  155. data/lib/ccls_engine/active_record_extension.rb +2 -0
  156. data/lib/ccls_engine/active_record_extension/base.rb +70 -0
  157. data/lib/ccls_engine/active_record_shared.rb +8 -0
  158. data/lib/ccls_engine/assertions.rb +69 -0
  159. data/lib/ccls_engine/autotest.rb +54 -0
  160. data/lib/ccls_engine/ccls_user.rb +117 -0
  161. data/lib/ccls_engine/core_extension.rb +14 -0
  162. data/lib/ccls_engine/date_and_time_formats.rb +30 -0
  163. data/lib/ccls_engine/factories.rb +880 -0
  164. data/lib/ccls_engine/factory_test_helper.rb +276 -0
  165. data/lib/ccls_engine/helper.rb +112 -0
  166. data/lib/ccls_engine/icf_master_tracker_update_test_helper.rb +121 -0
  167. data/lib/ccls_engine/live_birth_data_update_test_helper.rb +110 -0
  168. data/lib/ccls_engine/package_test_helper.rb +49 -0
  169. data/lib/ccls_engine/shared_database.rb +20 -0
  170. data/lib/ccls_engine/tasks.rb +1 -0
  171. data/lib/ccls_engine/test_tasks.rb +52 -0
  172. data/lib/ccls_engine/translation_table.rb +86 -0
  173. data/lib/shared_migration.rb +5 -0
  174. data/lib/surveyor/survey_extensions.rb +125 -0
  175. data/lib/tasks/application.rake +286 -0
  176. data/lib/tasks/calnet_authenticated.rake +6 -0
  177. data/lib/tasks/common_lib.rake +7 -0
  178. data/lib/tasks/database.rake +288 -0
  179. data/lib/tasks/documentation.rake +71 -0
  180. data/lib/tasks/homex_import.rake +723 -0
  181. data/lib/tasks/odms_import.rake +1116 -0
  182. data/lib/tasks/simply_authorized.rake +6 -0
  183. data/lib/tasks/ucb_ccls_engine_tasks.rake +4 -0
  184. data/lib/tasks/use_db.rake +4 -0
  185. data/rails/init.rb +4 -0
  186. data/test/unit/ccls/abstract_search_test.rb +150 -0
  187. data/test/unit/ccls/abstract_test.rb +674 -0
  188. data/test/unit/ccls/address_test.rb +155 -0
  189. data/test/unit/ccls/address_type_test.rb +25 -0
  190. data/test/unit/ccls/addressing_test.rb +466 -0
  191. data/test/unit/ccls/aliquot_sample_format_test.rb +20 -0
  192. data/test/unit/ccls/aliquot_test.rb +156 -0
  193. data/test/unit/ccls/analysis_test.rb +31 -0
  194. data/test/unit/ccls/bc_request_test.rb +43 -0
  195. data/test/unit/ccls/candidate_control_test.rb +712 -0
  196. data/test/unit/ccls/context_data_source_test.rb +26 -0
  197. data/test/unit/ccls/context_test.rb +40 -0
  198. data/test/unit/ccls/core_extension_test.rb +17 -0
  199. data/test/unit/ccls/county_test.rb +34 -0
  200. data/test/unit/ccls/data_source_test.rb +41 -0
  201. data/test/unit/ccls/diagnosis_test.rb +51 -0
  202. data/test/unit/ccls/document_type_test.rb +35 -0
  203. data/test/unit/ccls/document_version_test.rb +68 -0
  204. data/test/unit/ccls/enrollment_test.rb +575 -0
  205. data/test/unit/ccls/follow_up_test.rb +23 -0
  206. data/test/unit/ccls/follow_up_type_test.rb +34 -0
  207. data/test/unit/ccls/gift_card_search_test.rb +153 -0
  208. data/test/unit/ccls/gift_card_test.rb +40 -0
  209. data/test/unit/ccls/home_exposure_response_test.rb +83 -0
  210. data/test/unit/ccls/homex_outcome_test.rb +199 -0
  211. data/test/unit/ccls/hospital_test.rb +102 -0
  212. data/test/unit/ccls/icf_master_id_test.rb +30 -0
  213. data/test/unit/ccls/icf_master_tracker_change_test.rb +14 -0
  214. data/test/unit/ccls/icf_master_tracker_test.rb +132 -0
  215. data/test/unit/ccls/icf_master_tracker_update_test.rb +176 -0
  216. data/test/unit/ccls/ineligible_reason_test.rb +48 -0
  217. data/test/unit/ccls/instrument_test.rb +62 -0
  218. data/test/unit/ccls/instrument_type_test.rb +39 -0
  219. data/test/unit/ccls/instrument_version_test.rb +71 -0
  220. data/test/unit/ccls/interview_method_test.rb +44 -0
  221. data/test/unit/ccls/interview_outcome_test.rb +34 -0
  222. data/test/unit/ccls/interview_test.rb +298 -0
  223. data/test/unit/ccls/language_test.rb +47 -0
  224. data/test/unit/ccls/live_birth_data_update_test.rb +358 -0
  225. data/test/unit/ccls/operational_event_test.rb +187 -0
  226. data/test/unit/ccls/operational_event_type_test.rb +51 -0
  227. data/test/unit/ccls/organization_test.rb +64 -0
  228. data/test/unit/ccls/patient_test.rb +538 -0
  229. data/test/unit/ccls/person_test.rb +55 -0
  230. data/test/unit/ccls/phone_number_test.rb +244 -0
  231. data/test/unit/ccls/phone_type_test.rb +32 -0
  232. data/test/unit/ccls/project_outcome_test.rb +34 -0
  233. data/test/unit/ccls/project_test.rb +60 -0
  234. data/test/unit/ccls/race_test.rb +37 -0
  235. data/test/unit/ccls/refusal_reason_test.rb +52 -0
  236. data/test/unit/ccls/role_test.rb +26 -0
  237. data/test/unit/ccls/sample_kit_test.rb +35 -0
  238. data/test/unit/ccls/sample_outcome_test.rb +34 -0
  239. data/test/unit/ccls/sample_temperature_test.rb +25 -0
  240. data/test/unit/ccls/sample_test.rb +363 -0
  241. data/test/unit/ccls/sample_type_test.rb +58 -0
  242. data/test/unit/ccls/section_test.rb +34 -0
  243. data/test/unit/ccls/state_test.rb +31 -0
  244. data/test/unit/ccls/study_subject_abstracts_test.rb +115 -0
  245. data/test/unit/ccls/study_subject_addresses_test.rb +93 -0
  246. data/test/unit/ccls/study_subject_duplicates_test.rb +407 -0
  247. data/test/unit/ccls/study_subject_enrollments_test.rb +65 -0
  248. data/test/unit/ccls/study_subject_homex_outcome_test.rb +64 -0
  249. data/test/unit/ccls/study_subject_identifier_test.rb +439 -0
  250. data/test/unit/ccls/study_subject_interviews_test.rb +26 -0
  251. data/test/unit/ccls/study_subject_languages_test.rb +142 -0
  252. data/test/unit/ccls/study_subject_operational_events_test.rb +53 -0
  253. data/test/unit/ccls/study_subject_patient_test.rb +249 -0
  254. data/test/unit/ccls/study_subject_pii_test.rb +278 -0
  255. data/test/unit/ccls/study_subject_races_test.rb +203 -0
  256. data/test/unit/ccls/study_subject_search_test.rb +704 -0
  257. data/test/unit/ccls/study_subject_test.rb +770 -0
  258. data/test/unit/ccls/subject_language_test.rb +43 -0
  259. data/test/unit/ccls/subject_race_test.rb +35 -0
  260. data/test/unit/ccls/subject_relationship_test.rb +43 -0
  261. data/test/unit/ccls/subject_type_test.rb +40 -0
  262. data/test/unit/ccls/tracing_status_test.rb +32 -0
  263. data/test/unit/ccls/transfer_test.rb +81 -0
  264. data/test/unit/ccls/translation_table_test.rb +40 -0
  265. data/test/unit/ccls/unit_test.rb +21 -0
  266. data/test/unit/ccls/user_test.rb +156 -0
  267. data/test/unit/ccls/vital_status_test.rb +36 -0
  268. data/test/unit/ccls/zip_code_test.rb +55 -0
  269. metadata +633 -0
@@ -0,0 +1,17 @@
1
+ #
2
+ # Simply extracted some code to clean up model.
3
+ # I'd like to do this to all of the really big classes
4
+ # but let's see how this goes first.
5
+ #
6
+ module StudySubjectEnrollments
7
+ def self.included(base)
8
+ # Must delay the calls to these ActiveRecord methods
9
+ # or it will raise many "undefined method"s.
10
+ base.class_eval do
11
+
12
+ has_many :enrollments
13
+ accepts_nested_attributes_for :enrollments
14
+
15
+ end # class_eval
16
+ end # included
17
+ end # StudySubjectEnrollments
@@ -0,0 +1,22 @@
1
+ #
2
+ # Simply extracted some code to clean up model.
3
+ # I'd like to do this to all of the really big classes
4
+ # but let's see how this goes first.
5
+ #
6
+ module StudySubjectHomexOutcome
7
+ def self.included(base)
8
+ # Must delay the calls to these ActiveRecord methods
9
+ # or it will raise many "undefined method"s.
10
+ base.class_eval do
11
+
12
+ has_one :homex_outcome
13
+
14
+ delegate :interview_outcome, :interview_outcome_on,
15
+ :sample_outcome, :sample_outcome_on,
16
+ :to => :homex_outcome, :allow_nil => true
17
+
18
+ accepts_nested_attributes_for :homex_outcome
19
+
20
+ end # class_eval
21
+ end # included
22
+ end # StudySubjectHomexOutcome
@@ -0,0 +1,153 @@
1
+ #
2
+ # Simply extracted some code to clean up model.
3
+ # I'd like to do this to all of the really big classes
4
+ # but let's see how this goes first.
5
+ #
6
+ module StudySubjectIdentifier
7
+ def self.included(base)
8
+ # Must delay the calls to these ActiveRecord methods
9
+ # or it will raise many "undefined method"s.
10
+ base.class_eval do
11
+
12
+ # Very cool that this doesn't stop factory girl from using them.
13
+ # it will stop the study_subject nested_attribute tests though
14
+ attr_protected :studyid, :studyid_nohyphen, :studyid_intonly_nohyphen,
15
+ :familyid, :childid, :subjectid, :patid, :orderno
16
+
17
+ before_validation :prepare_fields_for_validation
18
+ before_create :prepare_fields_for_creation
19
+
20
+ def self.find_all_by_studyid_or_icf_master_id(studyid,icf_master_id)
21
+ # if decide to use LIKE, will need to NOT include nils so
22
+ # will need to add some conditions to the conditions.
23
+ self.find( :all,
24
+ :conditions => [
25
+ "studyid = :studyid OR icf_master_id = :icf_master_id",
26
+ { :studyid => studyid, :icf_master_id => icf_master_id }
27
+ ]
28
+ )
29
+ end
30
+
31
+ protected
32
+
33
+ def prepare_fields_for_validation
34
+ # NOTE ANY field that has a unique index in the database NEEDS
35
+ # to NOT be blank. Multiple nils are acceptable in index,
36
+ # but multiple blanks are NOT. Nilify ALL fields with
37
+ # unique indexes in the database.
38
+ self.email = nil if email.blank?
39
+ self.ssn = nil if ssn.blank?
40
+ self.state_id_no = nil if state_id_no.blank?
41
+ self.state_registrar_no = nil if state_registrar_no.blank?
42
+ self.local_registrar_no = nil if local_registrar_no.blank?
43
+ self.gbid = nil if gbid.blank?
44
+ self.lab_no_wiemels = nil if lab_no_wiemels.blank?
45
+ self.accession_no = nil if accession_no.blank?
46
+ self.idno_wiemels = nil if idno_wiemels.blank?
47
+
48
+ self.case_control_type = ( ( case_control_type.blank?
49
+ ) ? nil : case_control_type.to_s.upcase )
50
+
51
+ patid.try(:gsub!,/\D/,'')
52
+ self.patid = sprintf("%04d",patid.to_i) unless patid.blank?
53
+
54
+ matchingid.try(:gsub!,/\D/,'')
55
+ # TODO add more tests for this (try with valid? method)
56
+ #puts "Matchingid before before validation:#{matchingid}"
57
+ self.matchingid = sprintf("%06d",matchingid.to_i) unless matchingid.blank?
58
+ end
59
+
60
+ # made separate method so can be stubbed
61
+ def get_next_childid
62
+ self.class.maximum(:childid).to_i + 1
63
+ end
64
+
65
+ # made separate method so can be stubbed
66
+ def get_next_patid
67
+ self.class.maximum(:patid).to_i + 1
68
+ #
69
+ # What happens if/when this goes over 4 digits?
70
+ # The database field is only 4 chars.
71
+ #
72
+ end
73
+
74
+ # fields made from fields that WON'T change go here
75
+ def prepare_fields_for_creation
76
+ # don't assign if given or is mother (childid is currently protected)
77
+ self.childid = get_next_childid if !is_mother? and childid.blank?
78
+
79
+ # don't assign if given or is not case (patid is currently protected)
80
+ self.patid = sprintf("%04d",get_next_patid.to_i) if is_case? and patid.blank?
81
+
82
+ # should move this from pre validation to here for ALL subjects.
83
+ # patid.try(:gsub!,/\D/,'')
84
+ # self.patid = sprintf("%04d",patid.to_i) unless patid.blank?
85
+
86
+ # don't assign if given or is not case (orderno is currently protected)
87
+ self.orderno = 0 if is_case? and orderno.blank?
88
+
89
+ # don't assign if given or is mother (studyid is currently protected)
90
+ # or if can't make complete studyid
91
+ if !is_mother? and studyid.blank? and
92
+ !patid.blank? and !case_control_type.blank? and !orderno.blank?
93
+ self.studyid = "#{patid}-#{case_control_type}-#{orderno}"
94
+ end
95
+
96
+ # perhaps put in an after_save with an update_attribute(s)
97
+ # and simply generate a new one until all is well
98
+ # don't assign if given (subjectid is currently protected)
99
+ self.subjectid = generate_subjectid if subjectid.blank?
100
+
101
+ # cases and controls: their own subjectID is also their familyID.
102
+ # mothers: their child's subjectID is their familyID. That is,
103
+ # a mother and her child have identical familyIDs.
104
+ # don't assign if given (familyid is currently protected)
105
+ self.familyid = subjectid if !is_mother? and familyid.blank?
106
+
107
+ # cases (patients): matchingID is the study_subject's own subjectID
108
+ # controls: matchingID is subjectID of the associated case
109
+ # (like PatID in this respect).
110
+ # mothers: matchingID is subjectID of their own child's associated case.
111
+ # That is, a mother's matchingID is the same as their child's. This
112
+ # will become clearer when I provide specs for mother study_subject creation.
113
+ # matchingid is manually set in some tests. will need to setup for stubbing this.
114
+ # don't assign if given (matchingid is currently NOT protected)
115
+ self.matchingid = subjectid if is_case? and matchingid.blank?
116
+ end
117
+
118
+ # made separate method so can stub it in testing
119
+ # This only guarantees uniqueness before creation,
120
+ # but not at creation. This is NOT scalable.
121
+ # Fortunately, we won't be creating tons of study_subjects
122
+ # at the same time so this should not be an issue,
123
+ # however, when it fails, it will be confusing. # TODO
124
+ # How to rescue from ActiveRecord::RecordInvalid here?
125
+ # or would it be RecordNotSaved?
126
+ #
127
+ # Perhaps treat subjectid like icf_master_id?
128
+ # Create a table with all of the possible
129
+ # subjectid ... (1..999999)
130
+ # study_subject_id
131
+ # assigned_on
132
+ # Then select a random unassigned one?
133
+ # Would this be faster?
134
+ #
135
+ def generate_subjectid
136
+ subjectids = ( (1..999999).to_a - self.class.find(:all,:select => 'subjectid'
137
+ ).collect(&:subjectid).collect(&:to_i) )
138
+ # CANNOT have leading 0' as it thinks its octal and converts
139
+ #>> sprintf("%06d","0001234")
140
+ #=> "000668"
141
+ #
142
+ # CANNOT have leading 0's and include and 8 or 9 as it thinks its octal
143
+ # so convert back to Integer first
144
+ #>> sprintf("%06d","0001280")
145
+ #ArgumentError: invalid value for Integer: "0001280"
146
+ # from (irb):24:in `sprintf'
147
+ # from (irb):24
148
+ sprintf("%06d",subjectids[rand(subjectids.length)].to_i)
149
+ end
150
+
151
+ end # class_eval
152
+ end # included
153
+ end # StudySubjectIdentifier
@@ -0,0 +1,25 @@
1
+ #
2
+ # Simply extracted some code to clean up model.
3
+ # I'd like to do this to all of the really big classes
4
+ # but let's see how this goes first.
5
+ #
6
+ module StudySubjectInterviews
7
+ def self.included(base)
8
+ # Must delay the calls to these ActiveRecord methods
9
+ # or it will raise many "undefined method"s.
10
+ base.class_eval do
11
+
12
+ has_many :interviews
13
+
14
+ # Returns home exposures interview
15
+ def hx_interview
16
+ interviews.find(:first,
17
+ # :conditions => "projects.key = 'HomeExposures'",
18
+ :conditions => { 'projects.id' => Project['HomeExposures'].id },
19
+ :joins => [:instrument_version => [:instrument => :project]]
20
+ )
21
+ end
22
+
23
+ end # class_eval
24
+ end # included
25
+ end # StudySubjectInterviews
@@ -0,0 +1,21 @@
1
+ #
2
+ # Simply extracted some code to clean up model.
3
+ # I'd like to do this to all of the really big classes
4
+ # but let's see how this goes first.
5
+ #
6
+ module StudySubjectLanguages
7
+ def self.included(base)
8
+ # Must delay the calls to these ActiveRecord methods
9
+ # or it will raise many "undefined method"s.
10
+ base.class_eval do
11
+
12
+ has_many :subject_languages
13
+ has_many :languages, :through => :subject_languages
14
+
15
+ accepts_nested_attributes_for :subject_languages,
16
+ :allow_destroy => true,
17
+ :reject_if => proc{|attributes| attributes['language_id'].blank? }
18
+
19
+ end # class_eval
20
+ end # included
21
+ end # StudySubjectLanguages
@@ -0,0 +1,66 @@
1
+ #
2
+ # Simply extracted some code to clean up model.
3
+ # I'd like to do this to all of the really big classes
4
+ # but let's see how this goes first.
5
+ #
6
+ module StudySubjectOperationalEvents
7
+ def self.included(base)
8
+ # Must delay the calls to these ActiveRecord methods
9
+ # or it will raise many "undefined method"s.
10
+ base.class_eval do
11
+
12
+ has_many :operational_events, :through => :enrollments
13
+
14
+ after_create :add_new_subject_operational_event
15
+ after_save :add_subject_died_operational_event
16
+
17
+ # All subjects are to have a CCLS project enrollment, so create after create.
18
+ # All subjects are to have this operational event, so create after create.
19
+ # I suspect that this'll be attached to the CCLS project enrollment.
20
+ def add_new_subject_operational_event
21
+ ccls_enrollment = enrollments.find_or_create_by_project_id(Project['ccls'].id)
22
+ OperationalEvent.create!(
23
+ :enrollment => ccls_enrollment,
24
+ :operational_event_type => OperationalEventType['newSubject'],
25
+ :occurred_on => Date.today
26
+ )
27
+ end
28
+
29
+ # Add this if the vital status changes to deceased.
30
+ # I suspect that this'll be attached to the CCLS project enrollment.
31
+ def add_subject_died_operational_event
32
+ if( ( vital_status_id == VitalStatus['deceased'].id ) &&
33
+ ( vital_status_id_was != VitalStatus['deceased'].id ) )
34
+ ccls_enrollment = enrollments.find_or_create_by_project_id(Project['ccls'].id)
35
+ OperationalEvent.create!(
36
+ :enrollment => ccls_enrollment,
37
+ :operational_event_type => OperationalEventType['subjectDied'],
38
+ :occurred_on => Date.today
39
+ )
40
+ end
41
+ end
42
+
43
+ # operational_events.occurred_on where operational_event_type_id = 26 and enrollment_id is for any open project (where projects.ended_on is null) for study_subject_id
44
+
45
+ def screener_complete_date_for_open_project
46
+ OperationalEvent.find(:first,
47
+ :joins => [
48
+ 'LEFT JOIN enrollments ON operational_events.enrollment_id = enrollments.id',
49
+ 'LEFT JOIN projects ON enrollments.project_id = projects.id'
50
+ ],
51
+ :conditions => [
52
+ "study_subject_id = :subject_id AND " <<
53
+ "operational_event_type_id = :screener_complete AND " <<
54
+ 'projects.ended_on IS NULL',
55
+ {
56
+ :subject_id => self.id,
57
+ :screener_complete => OperationalEventType['screener_complete'].id
58
+ }
59
+ ]
60
+ ).try(:occurred_on)
61
+ end
62
+
63
+ end # class_eval
64
+ end # included
65
+ end # StudySubjectOperationalEvents
66
+ __END__
@@ -0,0 +1,177 @@
1
+ #
2
+ # Simply extracted some code to clean up model.
3
+ # I'd like to do this to all of the really big classes
4
+ # but let's see how this goes first.
5
+ #
6
+ module StudySubjectPatient
7
+ def self.included(base)
8
+ # Must delay the calls to these ActiveRecord methods
9
+ # or it will raise many "undefined method"s.
10
+ base.class_eval do
11
+
12
+ has_one :patient
13
+
14
+ delegate :admit_date, :hospital_no,
15
+ :organization, :organization_id,
16
+ :to => :patient, :allow_nil => true
17
+
18
+ accepts_nested_attributes_for :patient
19
+
20
+ validate :must_be_case_if_patient
21
+ validate :patient_admit_date_is_after_dob
22
+ validate :patient_diagnosis_date_is_after_dob
23
+
24
+ after_save :trigger_setting_was_under_15_at_dx,
25
+ :if => :dob_changed?
26
+ after_save :trigger_update_matching_study_subjects_reference_date,
27
+ :if => :matchingid_changed?
28
+
29
+ def admitting_oncologist
30
+ # can be blank so need more than try unless I nilify admitting_oncologist if blank
31
+ #patient.try(:admitting_oncologist) || "[no oncologist specified]"
32
+ if patient and !patient.admitting_oncologist.blank?
33
+ patient.admitting_oncologist
34
+ else
35
+ "[no oncologist specified]"
36
+ end
37
+ end
38
+
39
+ ##
40
+ # triggered from patient and self
41
+ def update_patient_was_under_15_at_dx
42
+ # due to the high probability that self and patient will not
43
+ # yet be resolved, we have to get the associations manually.
44
+ my_patient = Patient.find_by_study_subject_id(self.attributes['id'])
45
+ if dob && my_patient && my_patient.admit_date &&
46
+ dob.to_date != Date.parse('1/1/1900') &&
47
+ my_patient.admit_date.to_date != Date.parse('1/1/1900')
48
+ #
49
+ # update_all(updates, conditions = nil, options = {})
50
+ #
51
+ # Updates all records with details given if they match a set of
52
+ # conditions supplied, limits and order can also be supplied.
53
+ # This method constructs a single SQL UPDATE statement and sends
54
+ # it straight to the database. It does not instantiate the involved
55
+ # models and it does not trigger Active Record callbacks.
56
+ #
57
+ # Patient.update_all({
58
+ # :was_under_15_at_dx => (((
59
+ # my_patient.admit_date.to_date - my_pii.dob.to_date
60
+ # ) / 365 ) < 15 )}, { :id => my_patient.id })
61
+
62
+ # crude and probably off by a couple days
63
+ # would be better to compare year, month then day
64
+ was_under_15 = (((
65
+ my_patient.admit_date.to_date - dob.to_date
66
+ ) / 365 ) < 15 ) ? YNDK[:yes] : YNDK[:no]
67
+ Patient.update_all({ :was_under_15_at_dx => was_under_15 },
68
+ { :id => my_patient.id })
69
+ end
70
+ # make sure we return true as is a callback
71
+ true
72
+ end
73
+
74
+ ##
75
+ #
76
+ def update_study_subjects_reference_date_matching(*matchingids)
77
+ logger.debug "DEBUG: In update_study_subjects_reference_date_matching(*matchingids)"
78
+ logger.debug "DEBUG: update_study_subjects_reference_date_matching(#{matchingids.join(',')})"
79
+ # if matchingids ~ [nil,12345]
80
+ # identifier was either just created or matchingid added (compact as nil not needed)
81
+ # if matchingids ~ [12345,nil]
82
+ # matchingid was removed (compact as nil not needed)
83
+ # if matchingids ~ [12345,54321]
84
+ # matchingid was just changed
85
+ # if matchingids ~ []
86
+ # trigger came from Patient so need to find matchingid
87
+
88
+ # due to the high probability that self and identifier will not
89
+ # yet be resolved, we have to get the associations manually.
90
+ # my_identifier = Identifier.find_by_study_subject_id(self.attributes['id'])
91
+ # matchingids.compact.push(my_identifier.try(:matchingid)).uniq.each do |matchingid|
92
+ matchingids.compact.push(matchingid).uniq.each do |mid|
93
+ study_subject_ids = if( !mid.nil? )
94
+ # Identifier.find_all_by_matchingid(mid).collect(&:study_subject_id)
95
+ # StudySubject.find_all_by_matchingid(mid).collect(&:id)
96
+ self.class.find_all_by_matchingid(mid).collect(&:id)
97
+ else
98
+ [id]
99
+ end
100
+
101
+ # SHOULD only ever be 1 patient found amongst the study_subject_ids although there is
102
+ # currently no validation applied to the uniqueness of matchingid
103
+ # If there is more than one patient for a given matchingid, this'll just be wrong.
104
+
105
+ matching_patient = Patient.find_by_study_subject_id(study_subject_ids)
106
+ admit_date = matching_patient.try(:admit_date)
107
+
108
+ logger.debug "DEBUG: calling StudySubject.update_study_subjects_reference_date(#{study_subject_ids.join(',')},#{admit_date})"
109
+ # StudySubject.update_study_subjects_reference_date( study_subject_ids, admit_date )
110
+ self.class.update_study_subjects_reference_date( study_subject_ids, admit_date )
111
+ end
112
+ true
113
+ end
114
+
115
+ protected
116
+
117
+ # This is a duplication of a patient validation that won't
118
+ # work if using nested attributes. Don't like doing this.
119
+ def patient_admit_date_is_after_dob
120
+ # if !patient.nil? && !patient.admit_date.blank? &&
121
+ # !pii.nil? && !pii.dob.blank? && patient.admit_date < pii.dob &&
122
+ # pii.dob.to_date != Date.parse('1/1/1900') &&
123
+ if !patient.nil? && !patient.admit_date.blank? &&
124
+ !dob.blank? && patient.admit_date < dob &&
125
+ dob.to_date != Date.parse('1/1/1900') &&
126
+ patient.admit_date.to_date != Date.parse('1/1/1900')
127
+ errors.add('patient:admit_date', "is before study_subject's dob.")
128
+ end
129
+ end
130
+
131
+ # This is a duplication of a patient validation that won't
132
+ # work if using nested attributes. Don't like doing this.
133
+ def patient_diagnosis_date_is_after_dob
134
+ if !patient.nil? && !patient.diagnosis_date.blank? &&
135
+ !dob.blank? && patient.diagnosis_date < dob
136
+ # !pii.nil? && !pii.dob.blank? && patient.diagnosis_date < pii.dob
137
+ errors.add('patient:diagnosis_date', "is before study_subject's dob.")
138
+ end
139
+ end
140
+
141
+ def must_be_case_if_patient
142
+ if !patient.nil? and !is_case?
143
+ errors.add(:patient ,"must be case to have patient info")
144
+ end
145
+ end
146
+
147
+ #
148
+ # logger levels are ... debug, info, warn, error, and fatal.
149
+ #
150
+ def trigger_setting_was_under_15_at_dx
151
+ logger.debug "DEBUG: calling update_patient_was_under_15_at_dx from StudySubject:#{self.attributes['id']}"
152
+ logger.debug "DEBUG: DOB changed from:#{dob_was}:to:#{dob}"
153
+ update_patient_was_under_15_at_dx
154
+ end
155
+
156
+ def trigger_update_matching_study_subjects_reference_date
157
+ logger.debug "DEBUG: triggering_update_matching_study_subjects_reference_date from StudySubject:#{self.attributes['id']}"
158
+ logger.debug "DEBUG: matchingid changed from:#{matchingid_was}:to:#{matchingid}"
159
+ self.update_study_subjects_reference_date_matching(matchingid_was,matchingid)
160
+ end
161
+
162
+ def self.update_study_subjects_reference_date(study_subject_ids,new_reference_date)
163
+ logger.debug "DEBUG: In StudySubject.update_study_subjects_reference_date"
164
+ logger.debug "DEBUG: update_study_subjects_reference_date(#{study_subject_ids.join(',')},#{new_reference_date})"
165
+ # UPDATE `study_subjects` SET `reference_date` = '2011-06-02' WHERE (`subjects`.`id` IN (1,2))
166
+ # UPDATE `study_subjects` SET `reference_date` = '2011-06-02' WHERE (`subjects`.`id` IN (NULL))
167
+ unless study_subject_ids.empty?
168
+ # StudySubject.update_all(
169
+ self.update_all(
170
+ {:reference_date => new_reference_date },
171
+ { :id => study_subject_ids })
172
+ end
173
+ end
174
+
175
+ end # class_eval
176
+ end # included
177
+ end # StudySubjectPatient