ccls-ccls_engine 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
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__