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,18 @@
1
+ # == requires
2
+ # * key ( unique )
3
+ # * description ( > 3 chars )
4
+ class Section < ActiveRecordShared
5
+
6
+ acts_as_list
7
+ default_scope :order => :position
8
+
9
+ acts_like_a_hash
10
+
11
+ has_many :follow_ups
12
+
13
+ # Returns description
14
+ def to_s
15
+ description
16
+ end
17
+
18
+ end
@@ -0,0 +1,25 @@
1
+ # Currently just US states + DC
2
+ class State < ActiveRecordShared
3
+
4
+ acts_as_list
5
+ default_scope :order => :position
6
+
7
+
8
+ validates_presence_of :code
9
+ validates_presence_of :name
10
+ validates_presence_of :fips_state_code
11
+ validates_presence_of :fips_country_code
12
+ validates_uniqueness_of :code
13
+ validates_uniqueness_of :name
14
+ validates_uniqueness_of :fips_state_code
15
+ validates_length_of :code, :maximum => 250, :allow_blank => true
16
+ validates_length_of :name, :maximum => 250, :allow_blank => true
17
+ validates_length_of :fips_state_code, :maximum => 250, :allow_blank => true
18
+ validates_length_of :fips_country_code, :maximum => 250, :allow_blank => true
19
+
20
+ # Returns an array of state abbreviations.
21
+ def self.abbreviations
22
+ @@abbreviations ||= all.collect(&:code)
23
+ end
24
+
25
+ end
@@ -0,0 +1,237 @@
1
+ # == requires
2
+ # * subject_type_id
3
+ class StudySubject < ActiveRecordShared
4
+
5
+ class NotTwoAbstracts < StandardError; end
6
+ class DuplicatesFound < StandardError; end
7
+
8
+ # This is purely an attempt at organization.
9
+ # Initially, this was by purpose, but now modifying to
10
+ # be by association or topic as this seems more logical.
11
+ # Also trying to map this same organization in the tests
12
+ # so that autotest triggers the more likely effected tests.
13
+ include StudySubjectAssociations
14
+ include StudySubjectValidations
15
+
16
+ include StudySubjectPatient
17
+ include StudySubjectPii
18
+ include StudySubjectIdentifier
19
+ include StudySubjectDuplicates
20
+ include StudySubjectAbstracts
21
+ include StudySubjectRaces
22
+ include StudySubjectLanguages
23
+ include StudySubjectAddresses
24
+
25
+ # Declaration order matters.
26
+ # OperationalEvents are through Enrollments.
27
+ include StudySubjectEnrollments
28
+ include StudySubjectOperationalEvents
29
+
30
+ include StudySubjectHomexOutcome
31
+ include StudySubjectInterviews
32
+
33
+ delegate :is_other?, :to => :guardian_relationship,
34
+ :allow_nil => true, :prefix => true
35
+
36
+ # can lead to multiple piis in db for study_subject
37
+ # if not done correctly
38
+ # s.update_attributes({"pii_attributes" => { "ssn" => "123456789", 'state_id_no' => 'x'}})
39
+ # s.update_attributes({"pii_attributes" => { "ssn" => "987654321", 'state_id_no' => 'a'}})
40
+ # Pii.find(:all, :conditions => {:study_subject_id => s.id }).count
41
+ # => 2
42
+ # without the :id attribute, it will create, but NOT destroy
43
+ # s.reload.pii will return the first one (sorts by id)
44
+ # s.pii.destroy will destroy the last one !?!?!?
45
+ # Make all these require a unique study_subject_id
46
+ # Newer versions of rails actually nullify the old record
47
+
48
+ accepts_nested_attributes_for :phone_numbers,
49
+ :reject_if => proc { |attrs| attrs[:phone_number].blank? }
50
+ accepts_nested_attributes_for :gift_cards
51
+
52
+ # Find the case or control subject with matching familyid except self.
53
+ def child
54
+ if (subject_type_id == self.class.subject_type_mother_id) && !familyid.blank?
55
+ self.class.find(:first,
56
+ :include => [:subject_type],
57
+ :conditions => [
58
+ "study_subjects.id != ? AND subjectid = ? AND subject_type_id IN (?)",
59
+ id, familyid,
60
+ [self.class.subject_type_case_id,self.class.subject_type_control_id] ]
61
+ )
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
67
+ # Find the subject with matching familyid and subject_type of Mother.
68
+ def mother
69
+ return nil if familyid.blank?
70
+ self.class.find(:first,
71
+ :include => [:subject_type],
72
+ :conditions => {
73
+ :familyid => familyid,
74
+ :subject_type_id => self.class.subject_type_mother_id
75
+ }
76
+ )
77
+ end
78
+
79
+ # Find all the subjects with matching familyid except self.
80
+ def family
81
+ return [] if familyid.blank?
82
+ self.class.find(:all,
83
+ :include => [:subject_type],
84
+ :conditions => [
85
+ "study_subjects.id != ? AND familyid = ?", id, familyid ]
86
+ )
87
+ end
88
+
89
+ # Find all the subjects with matching matchingid except self.
90
+ def matching
91
+ return [] if matchingid.blank?
92
+ self.class.find(:all,
93
+ :include => [:subject_type],
94
+ :conditions => [
95
+ "study_subjects.id != ? AND matchingid = ?",
96
+ id, matchingid ]
97
+ )
98
+ end
99
+
100
+ # Find all the subjects with matching patid with subject_type Control except self.
101
+ # If patid is nil, this sql doesn't work.
102
+ # TODO Could fix, but this situation is unlikely.
103
+ def controls
104
+ return [] unless is_case?
105
+ self.class.find(:all,
106
+ :include => [:subject_type],
107
+ :conditions => [
108
+ "study_subjects.id != ? AND patid = ? AND subject_type_id = ?",
109
+ id, patid, self.class.subject_type_control_id ]
110
+ )
111
+ end
112
+
113
+ def rejected_controls
114
+ return [] unless is_case?
115
+ CandidateControl.find(:all,
116
+ :conditions => {
117
+ :related_patid => patid,
118
+ :reject_candidate => true
119
+ }
120
+ )
121
+ end
122
+
123
+ def to_s
124
+ [childid,'(',studyid,full_name,')'].compact.join(' ')
125
+ end
126
+
127
+ # Returns boolean of comparison
128
+ # true only if type is Case
129
+ def is_case?
130
+ subject_type_id == self.class.subject_type_case_id
131
+ end
132
+
133
+ # Returns boolean of comparison
134
+ # true only if type is Control
135
+ def is_control?
136
+ subject_type_id == self.class.subject_type_control_id
137
+ end
138
+
139
+ # Returns boolean of comparison
140
+ # true only if type is Mother
141
+ def is_mother?
142
+ subject_type_id == self.class.subject_type_mother_id
143
+ end
144
+
145
+ def self.search(params={})
146
+ StudySubjectSearch.new(params).study_subjects
147
+ end
148
+
149
+ # Create (or just return mother) a mother subject based on subject's own data.
150
+ def create_mother
151
+ return self if is_mother?
152
+ # The mother method will effectively find and itself.
153
+ existing_mother = mother
154
+ if existing_mother
155
+ existing_mother
156
+ else
157
+ new_mother = self.class.new do |s|
158
+ s.subject_type_id = self.class.subject_type_mother_id
159
+ s.vital_status_id = VitalStatus['living'].id
160
+ s.sex = 'F'
161
+ s.hispanicity_id = mother_hispanicity_id
162
+ s.first_name = mother_first_name
163
+ s.middle_name = mother_middle_name
164
+ s.last_name = mother_last_name
165
+ s.maiden_name = mother_maiden_name
166
+
167
+ # protected attributes!
168
+ s.matchingid = matchingid
169
+ s.familyid = familyid
170
+ end
171
+ new_mother.save!
172
+ new_mother.assign_icf_master_id
173
+ new_mother
174
+ end
175
+ end
176
+
177
+ def assign_icf_master_id
178
+ if icf_master_id.blank?
179
+ next_icf_master_id = IcfMasterId.next_unused
180
+ if next_icf_master_id
181
+ self.update_attribute(:icf_master_id, next_icf_master_id.to_s)
182
+ next_icf_master_id.study_subject = self
183
+ next_icf_master_id.assigned_on = Date.today
184
+ next_icf_master_id.save!
185
+ end
186
+ end
187
+ self
188
+ end
189
+
190
+ def next_control_orderno(grouping='6')
191
+ return nil unless is_case?
192
+ last_control = self.class.find(:first,
193
+ :order => 'orderno DESC',
194
+ :conditions => {
195
+ :subject_type_id => self.class.subject_type_control_id,
196
+ :case_control_type => grouping,
197
+ :matchingid => self.subjectid
198
+ }
199
+ )
200
+ ( last_control.try(:orderno) || 0 ) + 1
201
+ end
202
+
203
+ def self.find_case_by_patid(patid)
204
+ self.find(:first, # patid is unique so better only be 1
205
+ :conditions => [
206
+ 'subject_type_id = ? AND patid = ?',
207
+ subject_type_case_id, patid
208
+ ]
209
+ )
210
+ end
211
+
212
+ def icf_master_id_to_s
213
+ ( icf_master_id.blank? ) ? "[no ID assigned]" : icf_master_id
214
+ end
215
+
216
+ def childid_to_s
217
+ ( is_mother? ) ? "#{child.try(:childid)} (mother)" : childid
218
+ end
219
+
220
+ def studyid_to_s
221
+ ( is_mother? ) ? "n/a" : studyid
222
+ end
223
+
224
+ protected
225
+
226
+ # Use these to stop the constant checking.
227
+ def self.subject_type_mother_id
228
+ @@subject_type_mother_id ||= SubjectType['Mother'].id
229
+ end
230
+ def self.subject_type_control_id
231
+ @@subject_type_control_id ||= SubjectType['Control'].id
232
+ end
233
+ def self.subject_type_case_id
234
+ @@subject_type_case_id ||= SubjectType['Case'].id
235
+ end
236
+
237
+ end
@@ -0,0 +1,47 @@
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 StudySubjectAbstracts
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
+ # Hmm? How to do this here?
13
+ # class NotTwoAbstracts < StandardError; end
14
+
15
+ has_many :abstracts
16
+ has_one :first_abstract, :class_name => 'Abstract',
17
+ :conditions => [
18
+ "entry_1_by_uid IS NOT NULL AND " <<
19
+ "entry_2_by_uid IS NULL AND " <<
20
+ "merged_by_uid IS NULL" ]
21
+ has_one :second_abstract, :class_name => 'Abstract',
22
+ :conditions => [
23
+ "entry_2_by_uid IS NOT NULL AND " <<
24
+ "merged_by_uid IS NULL" ]
25
+ has_one :merged_abstract, :class_name => 'Abstract',
26
+ :conditions => [ "merged_by_uid IS NOT NULL" ]
27
+ has_many :unmerged_abstracts, :class_name => 'Abstract',
28
+ :conditions => [ "merged_by_uid IS NULL" ]
29
+
30
+ def abstracts_the_same?
31
+ raise StudySubject::NotTwoAbstracts unless abstracts_count == 2
32
+ # abstracts.inject(:is_the_same_as?) was nice
33
+ # but using inject is ruby >= 1.8.7
34
+ return abstracts[0].is_the_same_as?(abstracts[1])
35
+ end
36
+
37
+ def abstract_diffs
38
+ raise StudySubject::NotTwoAbstracts unless abstracts_count == 2
39
+ # abstracts.inject(:diff) was nice
40
+ # but using inject is ruby >= 1.8.7
41
+ return abstracts[0].diff(abstracts[1])
42
+ end
43
+
44
+ end # class_eval
45
+ end # included
46
+ end # StudySubjectAbstracts
47
+ __END__
@@ -0,0 +1,34 @@
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 StudySubjectAddresses
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 :addressings
13
+ has_many :addresses, :through => :addressings
14
+
15
+ accepts_nested_attributes_for :addressings,
16
+ :reject_if => proc { |attrs|
17
+ !attrs[:address_required] &&
18
+ attrs[:address_attributes][:line_1].blank? &&
19
+ attrs[:address_attributes][:line_2].blank? &&
20
+ attrs[:address_attributes][:unit].blank? &&
21
+ attrs[:address_attributes][:city].blank? &&
22
+ attrs[:address_attributes][:zip].blank? &&
23
+ attrs[:address_attributes][:county].blank?
24
+ }
25
+
26
+ # Returns number of addresses with
27
+ # address_type.key == 'residence'
28
+ def residence_addresses_count
29
+ addresses.count(:conditions => { :address_type_id => AddressType['residence'].id })
30
+ end
31
+
32
+ end # class_eval
33
+ end # included
34
+ end # StudySubjectAddresses
@@ -0,0 +1,38 @@
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 StudySubjectAssociations
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
+ belongs_to :subject_type
13
+ belongs_to :vital_status
14
+
15
+ has_and_belongs_to_many :analyses
16
+ has_many :gift_cards
17
+ has_many :phone_numbers
18
+ has_many :samples
19
+ has_one :home_exposure_response
20
+ has_many :bc_requests
21
+ belongs_to :guardian_relationship, :class_name => 'SubjectRelationship'
22
+
23
+ ##########
24
+ #
25
+ # Declaration order does matter. Because of a patient callback that
26
+ # references the study_subject's dob when using nested attributes,
27
+ # pii NEEDS to be BEFORE patient.
28
+ #
29
+ # identifier should also be before patient
30
+ #
31
+ # has_one :identifier
32
+ # has_one :pii
33
+ #
34
+ ##########
35
+
36
+ end # class_eval
37
+ end # included
38
+ end # StudySubjectAssociations
@@ -0,0 +1,111 @@
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 StudySubjectDuplicates
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
+ #
13
+ # Basically this is just a custom search expecting only the
14
+ # following possible params for search / comparison ...
15
+ #
16
+ # mother_maiden_name
17
+ # hospital_no
18
+ # dob
19
+ # sex
20
+ # admit_date
21
+ # organization_id
22
+ #
23
+ # Would want to explicitly exclude self, but this check is
24
+ # to be done BEFORE subject creation so won't actually
25
+ # have an id to use to exclude itself anyway.
26
+ #
27
+ # For adding controls, will need to be able to exclude case
28
+ # so adding :exclude_id option somehow
29
+ #
30
+ def self.duplicates(params={})
31
+ conditions = [[],{}]
32
+
33
+ if params.has_key?(:hospital_no) and !params[:hospital_no].blank?
34
+ conditions[0] << '(hospital_no = :hospital_no)'
35
+ conditions[1][:hospital_no] = params[:hospital_no]
36
+ end
37
+
38
+ # This is effectively the only test for adding controls
39
+ # as the other attributes are from the patient model
40
+ # which is only for cases.
41
+ if params.has_key?(:dob) and !params[:dob].blank? and
42
+ params.has_key?(:sex) and !params[:sex].blank? and
43
+ params.has_key?(:mother_maiden_name)
44
+ # since remove nullify of name fields, added comparison to ""
45
+ conditions[0] << '(dob = :dob AND sex = :sex AND ( mother_maiden_name IS NULL OR mother_maiden_name = "" OR mother_maiden_name = :mother_maiden_name ))'
46
+ conditions[1][:dob] = params[:dob]
47
+ conditions[1][:sex] = params[:sex]
48
+ # added to_s as may be null so sql is valid and has '' rather than a blank
49
+ conditions[1][:mother_maiden_name] = params[:mother_maiden_name].to_s
50
+ end
51
+ if params.has_key?(:admit_date) and !params[:admit_date].blank? and
52
+ params.has_key?(:organization_id) and !params[:organization_id].blank?
53
+ conditions[0] << '(admit_date = :admit AND organization_id = :org)'
54
+ conditions[1][:admit] = params[:admit_date]
55
+ conditions[1][:org] = params[:organization_id]
56
+ end
57
+
58
+ unless conditions[0].blank?
59
+ conditions_array = [ "(#{conditions[0].join(' OR ')})" ]
60
+ if params.has_key?(:exclude_id)
61
+ conditions_array[0] << " AND study_subjects.id != :exclude_id"
62
+ conditions[1][:exclude_id] = params[:exclude_id]
63
+ end
64
+ conditions_array << conditions[1]
65
+ #puts conditions_array.inspect
66
+ #["((hospital_no = :hospital_no) OR (dob = :dob AND sex = :sex AND ( mother_maiden_name IS NULL OR mother_maiden_name = :mother_maiden_name )) OR (admit_date = :admit AND organization_id = :org)) AND study_subjects.id != :exclude_id", {:hospital_no=>"matchthis", :org=>31, :admit=>Wed, 16 Nov 2011, :sex=>"F", :exclude_id=>3, :mother_maiden_name=>"", :dob=>Wed, 16 Nov 2011}]
67
+
68
+ find(:all,
69
+ # have to do a LEFT JOIN, not the default INNER JOIN, here
70
+ # :joins => [:pii,:patient,:identifier]
71
+ # otherwise would only include subjects with pii, patient and identifier,
72
+ # which would effectively exclude controls. (maybe that's ok?. NOT OK.)
73
+ :joins => [
74
+ 'LEFT JOIN patients ON study_subjects.id = patients.study_subject_id'
75
+ ],
76
+ :conditions => conditions_array
77
+ )
78
+ else
79
+ []
80
+ end
81
+ end
82
+
83
+ def duplicates(options={})
84
+ self.class.duplicates({
85
+ :mother_maiden_name => self.mother_maiden_name,
86
+ :hospital_no => self.hospital_no,
87
+ :dob => self.dob,
88
+ :sex => self.sex,
89
+ :admit_date => self.admit_date,
90
+ :organization_id => self.organization_id
91
+ }.merge(options))
92
+ end
93
+
94
+ def raf_duplicate_creation_attempted(attempted_subject)
95
+ ccls_enrollment = enrollments.find_or_create_by_project_id(Project['ccls'].id)
96
+ OperationalEvent.create!(
97
+ :enrollment => ccls_enrollment,
98
+ :operational_event_type => OperationalEventType['DuplicateCase'],
99
+ :occurred_on => Date.today,
100
+ :description => "a new RAF for this subject was submitted by " <<
101
+ "#{attempted_subject.admitting_oncologist} of " <<
102
+ "#{attempted_subject.organization} " <<
103
+ "with hospital number: " <<
104
+ "#{attempted_subject.hospital_no}."
105
+ )
106
+ end
107
+
108
+ end # class_eval
109
+ end # included
110
+ end # StudySubjectDuplicates
111
+ __END__