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.
- data/README.rdoc +182 -0
- data/app/models/abstract.rb +181 -0
- data/app/models/abstract_search.rb +50 -0
- data/app/models/abstract_validations.rb +324 -0
- data/app/models/address.rb +70 -0
- data/app/models/address_type.rb +15 -0
- data/app/models/addressing.rb +147 -0
- data/app/models/aliquot.rb +44 -0
- data/app/models/aliquot_sample_format.rb +13 -0
- data/app/models/analysis.rb +14 -0
- data/app/models/bc_request.rb +20 -0
- data/app/models/candidate_control.rb +101 -0
- data/app/models/context.rb +23 -0
- data/app/models/context_data_source.rb +4 -0
- data/app/models/county.rb +16 -0
- data/app/models/data_source.rb +24 -0
- data/app/models/diagnosis.rb +23 -0
- data/app/models/document_type.rb +16 -0
- data/app/models/document_version.rb +27 -0
- data/app/models/enrollment.rb +78 -0
- data/app/models/enrollment_validations.rb +167 -0
- data/app/models/follow_up.rb +16 -0
- data/app/models/follow_up_type.rb +18 -0
- data/app/models/gift_card.rb +22 -0
- data/app/models/gift_card_search.rb +137 -0
- data/app/models/home_exposure_response.rb +24 -0
- data/app/models/homex_outcome.rb +75 -0
- data/app/models/hospital.rb +22 -0
- data/app/models/icf_master_id.rb +30 -0
- data/app/models/icf_master_tracker.rb +217 -0
- data/app/models/icf_master_tracker_change.rb +9 -0
- data/app/models/icf_master_tracker_update.rb +50 -0
- data/app/models/ineligible_reason.rb +26 -0
- data/app/models/instrument.rb +26 -0
- data/app/models/instrument_type.rb +17 -0
- data/app/models/instrument_version.rb +28 -0
- data/app/models/interview.rb +122 -0
- data/app/models/interview_method.rb +17 -0
- data/app/models/interview_outcome.rb +16 -0
- data/app/models/language.rb +28 -0
- data/app/models/live_birth_data_update.rb +142 -0
- data/app/models/operational_event.rb +99 -0
- data/app/models/operational_event_type.rb +31 -0
- data/app/models/organization.rb +28 -0
- data/app/models/patient.rb +63 -0
- data/app/models/patient_validations.rb +118 -0
- data/app/models/person.rb +28 -0
- data/app/models/phone_number.rb +105 -0
- data/app/models/phone_type.rb +15 -0
- data/app/models/project.rb +39 -0
- data/app/models/project_outcome.rb +19 -0
- data/app/models/race.rb +31 -0
- data/app/models/refusal_reason.rb +23 -0
- data/app/models/sample.rb +168 -0
- data/app/models/sample_kit.rb +14 -0
- data/app/models/sample_outcome.rb +16 -0
- data/app/models/sample_temperature.rb +14 -0
- data/app/models/sample_type.rb +37 -0
- data/app/models/search.rb +195 -0
- data/app/models/section.rb +18 -0
- data/app/models/state.rb +25 -0
- data/app/models/study_subject.rb +237 -0
- data/app/models/study_subject_abstracts.rb +47 -0
- data/app/models/study_subject_addresses.rb +34 -0
- data/app/models/study_subject_associations.rb +38 -0
- data/app/models/study_subject_duplicates.rb +111 -0
- data/app/models/study_subject_enrollments.rb +17 -0
- data/app/models/study_subject_homex_outcome.rb +22 -0
- data/app/models/study_subject_identifier.rb +153 -0
- data/app/models/study_subject_interviews.rb +25 -0
- data/app/models/study_subject_languages.rb +21 -0
- data/app/models/study_subject_operational_events.rb +66 -0
- data/app/models/study_subject_patient.rb +177 -0
- data/app/models/study_subject_pii.rb +74 -0
- data/app/models/study_subject_races.rb +25 -0
- data/app/models/study_subject_search.rb +260 -0
- data/app/models/study_subject_validations.rb +116 -0
- data/app/models/subject_language.rb +11 -0
- data/app/models/subject_race.rb +11 -0
- data/app/models/subject_relationship.rb +21 -0
- data/app/models/subject_type.rb +22 -0
- data/app/models/tracing_status.rb +20 -0
- data/app/models/transfer.rb +40 -0
- data/app/models/unit.rb +14 -0
- data/app/models/vital_status.rb +19 -0
- data/app/models/zip_code.rb +36 -0
- data/config/abstract_fields.yml +1038 -0
- data/config/abstract_sections.yml +77 -0
- data/config/home_exposure_response_fields.yml +583 -0
- data/config/icf_master_tracker_update.yml +56 -0
- data/config/live_birth_data_update.yml +56 -0
- data/config/shared_use_db.yml +4 -0
- data/generators/ccls_engine/USAGE +2 -0
- data/generators/ccls_engine/ccls_engine_generator.rb +123 -0
- data/generators/ccls_engine/templates/autotest_ccls_engine.rb +3 -0
- data/generators/ccls_engine/templates/ccls_engine.rake +12 -0
- data/generators/ccls_engine/templates/fixtures/address_types.yml +30 -0
- data/generators/ccls_engine/templates/fixtures/context_data_sources.yml +54 -0
- data/generators/ccls_engine/templates/fixtures/contexts.yml +19 -0
- data/generators/ccls_engine/templates/fixtures/data_sources.yml +40 -0
- data/generators/ccls_engine/templates/fixtures/diagnoses.yml +40 -0
- data/generators/ccls_engine/templates/fixtures/document_types.yml +65 -0
- data/generators/ccls_engine/templates/fixtures/document_versions.csv +155 -0
- data/generators/ccls_engine/templates/fixtures/follow_up_types.yml +16 -0
- data/generators/ccls_engine/templates/fixtures/hospitals.yml +114 -0
- data/generators/ccls_engine/templates/fixtures/ineligible_reasons.yml +35 -0
- data/generators/ccls_engine/templates/fixtures/instrument_types.yml +26 -0
- data/generators/ccls_engine/templates/fixtures/instrument_versions.yml +22 -0
- data/generators/ccls_engine/templates/fixtures/instruments.yml +22 -0
- data/generators/ccls_engine/templates/fixtures/interview_methods.yml +30 -0
- data/generators/ccls_engine/templates/fixtures/interview_outcomes.yml +31 -0
- data/generators/ccls_engine/templates/fixtures/languages.yml +34 -0
- data/generators/ccls_engine/templates/fixtures/operational_event_types.yml +141 -0
- data/generators/ccls_engine/templates/fixtures/organizations.yml +198 -0
- data/generators/ccls_engine/templates/fixtures/people.yml +130 -0
- data/generators/ccls_engine/templates/fixtures/phone_types.yml +30 -0
- data/generators/ccls_engine/templates/fixtures/project_outcomes.yml +25 -0
- data/generators/ccls_engine/templates/fixtures/projects.yml +59 -0
- data/generators/ccls_engine/templates/fixtures/races.yml +52 -0
- data/generators/ccls_engine/templates/fixtures/refusal_reasons.yml +55 -0
- data/generators/ccls_engine/templates/fixtures/sample_outcomes.yml +36 -0
- data/generators/ccls_engine/templates/fixtures/sample_temperatures.yml +16 -0
- data/generators/ccls_engine/templates/fixtures/sample_types.yml +147 -0
- data/generators/ccls_engine/templates/fixtures/sections.yml +31 -0
- data/generators/ccls_engine/templates/fixtures/states.yml +363 -0
- data/generators/ccls_engine/templates/fixtures/subject_relationships.yml +46 -0
- data/generators/ccls_engine/templates/fixtures/subject_types.yml +30 -0
- data/generators/ccls_engine/templates/fixtures/tracing_statuses.yml +30 -0
- data/generators/ccls_engine/templates/fixtures/units.yml +13 -0
- data/generators/ccls_engine/templates/fixtures/vital_statuses.yml +28 -0
- data/generators/ccls_engine/templates/functional/roles_controller_test.rb +142 -0
- data/generators/ccls_engine/templates/functional/sessions_controller_test.rb +19 -0
- data/generators/ccls_engine/templates/functional/users_controller_test.rb +94 -0
- data/generators/ccls_engine/templates/images/sort_down.png +0 -0
- data/generators/ccls_engine/templates/images/sort_up.png +0 -0
- data/generators/ccls_engine/templates/initializer.rb +28 -0
- data/generators/ccls_engine/templates/javascripts/ccls_engine.js +24 -0
- data/generators/ccls_engine/templates/javascripts/jquery-ui.js +763 -0
- data/generators/ccls_engine/templates/javascripts/jquery.js +154 -0
- data/generators/ccls_engine/templates/javascripts/jrails.js +1 -0
- data/generators/ccls_engine/templates/migrations/create_user_invitations.rb +18 -0
- data/generators/ccls_engine/templates/migrations/create_users.rb +33 -0
- data/generators/ccls_engine/templates/migrations/drop_user_invitations.rb +18 -0
- data/generators/ccls_engine/templates/stylesheets/ccls_engine.css +180 -0
- data/generators/ccls_engine/templates/stylesheets/user.css +35 -0
- data/generators/ccls_engine/templates/stylesheets/users.css +23 -0
- data/generators/ccls_engine/templates/unit/core_extension_test.rb +18 -0
- data/generators/ccls_engine/templates/unit/role_test.rb +30 -0
- data/generators/ccls_engine/templates/unit/user_test.rb +321 -0
- data/lib/ccls-ccls_engine.rb +1 -0
- data/lib/ccls_engine.rb +135 -0
- data/lib/ccls_engine/action_view_extension.rb +3 -0
- data/lib/ccls_engine/action_view_extension/base.rb +53 -0
- data/lib/ccls_engine/action_view_extension/form_builder.rb +39 -0
- data/lib/ccls_engine/active_record_extension.rb +2 -0
- data/lib/ccls_engine/active_record_extension/base.rb +70 -0
- data/lib/ccls_engine/active_record_shared.rb +8 -0
- data/lib/ccls_engine/assertions.rb +69 -0
- data/lib/ccls_engine/autotest.rb +54 -0
- data/lib/ccls_engine/ccls_user.rb +117 -0
- data/lib/ccls_engine/core_extension.rb +14 -0
- data/lib/ccls_engine/date_and_time_formats.rb +30 -0
- data/lib/ccls_engine/factories.rb +880 -0
- data/lib/ccls_engine/factory_test_helper.rb +276 -0
- data/lib/ccls_engine/helper.rb +112 -0
- data/lib/ccls_engine/icf_master_tracker_update_test_helper.rb +121 -0
- data/lib/ccls_engine/live_birth_data_update_test_helper.rb +110 -0
- data/lib/ccls_engine/package_test_helper.rb +49 -0
- data/lib/ccls_engine/shared_database.rb +20 -0
- data/lib/ccls_engine/tasks.rb +1 -0
- data/lib/ccls_engine/test_tasks.rb +52 -0
- data/lib/ccls_engine/translation_table.rb +86 -0
- data/lib/shared_migration.rb +5 -0
- data/lib/surveyor/survey_extensions.rb +125 -0
- data/lib/tasks/application.rake +286 -0
- data/lib/tasks/calnet_authenticated.rake +6 -0
- data/lib/tasks/common_lib.rake +7 -0
- data/lib/tasks/database.rake +288 -0
- data/lib/tasks/documentation.rake +71 -0
- data/lib/tasks/homex_import.rake +723 -0
- data/lib/tasks/odms_import.rake +1116 -0
- data/lib/tasks/simply_authorized.rake +6 -0
- data/lib/tasks/ucb_ccls_engine_tasks.rake +4 -0
- data/lib/tasks/use_db.rake +4 -0
- data/rails/init.rb +4 -0
- data/test/unit/ccls/abstract_search_test.rb +150 -0
- data/test/unit/ccls/abstract_test.rb +674 -0
- data/test/unit/ccls/address_test.rb +155 -0
- data/test/unit/ccls/address_type_test.rb +25 -0
- data/test/unit/ccls/addressing_test.rb +466 -0
- data/test/unit/ccls/aliquot_sample_format_test.rb +20 -0
- data/test/unit/ccls/aliquot_test.rb +156 -0
- data/test/unit/ccls/analysis_test.rb +31 -0
- data/test/unit/ccls/bc_request_test.rb +43 -0
- data/test/unit/ccls/candidate_control_test.rb +712 -0
- data/test/unit/ccls/context_data_source_test.rb +26 -0
- data/test/unit/ccls/context_test.rb +40 -0
- data/test/unit/ccls/core_extension_test.rb +17 -0
- data/test/unit/ccls/county_test.rb +34 -0
- data/test/unit/ccls/data_source_test.rb +41 -0
- data/test/unit/ccls/diagnosis_test.rb +51 -0
- data/test/unit/ccls/document_type_test.rb +35 -0
- data/test/unit/ccls/document_version_test.rb +68 -0
- data/test/unit/ccls/enrollment_test.rb +575 -0
- data/test/unit/ccls/follow_up_test.rb +23 -0
- data/test/unit/ccls/follow_up_type_test.rb +34 -0
- data/test/unit/ccls/gift_card_search_test.rb +153 -0
- data/test/unit/ccls/gift_card_test.rb +40 -0
- data/test/unit/ccls/home_exposure_response_test.rb +83 -0
- data/test/unit/ccls/homex_outcome_test.rb +199 -0
- data/test/unit/ccls/hospital_test.rb +102 -0
- data/test/unit/ccls/icf_master_id_test.rb +30 -0
- data/test/unit/ccls/icf_master_tracker_change_test.rb +14 -0
- data/test/unit/ccls/icf_master_tracker_test.rb +132 -0
- data/test/unit/ccls/icf_master_tracker_update_test.rb +176 -0
- data/test/unit/ccls/ineligible_reason_test.rb +48 -0
- data/test/unit/ccls/instrument_test.rb +62 -0
- data/test/unit/ccls/instrument_type_test.rb +39 -0
- data/test/unit/ccls/instrument_version_test.rb +71 -0
- data/test/unit/ccls/interview_method_test.rb +44 -0
- data/test/unit/ccls/interview_outcome_test.rb +34 -0
- data/test/unit/ccls/interview_test.rb +298 -0
- data/test/unit/ccls/language_test.rb +47 -0
- data/test/unit/ccls/live_birth_data_update_test.rb +358 -0
- data/test/unit/ccls/operational_event_test.rb +187 -0
- data/test/unit/ccls/operational_event_type_test.rb +51 -0
- data/test/unit/ccls/organization_test.rb +64 -0
- data/test/unit/ccls/patient_test.rb +538 -0
- data/test/unit/ccls/person_test.rb +55 -0
- data/test/unit/ccls/phone_number_test.rb +244 -0
- data/test/unit/ccls/phone_type_test.rb +32 -0
- data/test/unit/ccls/project_outcome_test.rb +34 -0
- data/test/unit/ccls/project_test.rb +60 -0
- data/test/unit/ccls/race_test.rb +37 -0
- data/test/unit/ccls/refusal_reason_test.rb +52 -0
- data/test/unit/ccls/role_test.rb +26 -0
- data/test/unit/ccls/sample_kit_test.rb +35 -0
- data/test/unit/ccls/sample_outcome_test.rb +34 -0
- data/test/unit/ccls/sample_temperature_test.rb +25 -0
- data/test/unit/ccls/sample_test.rb +363 -0
- data/test/unit/ccls/sample_type_test.rb +58 -0
- data/test/unit/ccls/section_test.rb +34 -0
- data/test/unit/ccls/state_test.rb +31 -0
- data/test/unit/ccls/study_subject_abstracts_test.rb +115 -0
- data/test/unit/ccls/study_subject_addresses_test.rb +93 -0
- data/test/unit/ccls/study_subject_duplicates_test.rb +407 -0
- data/test/unit/ccls/study_subject_enrollments_test.rb +65 -0
- data/test/unit/ccls/study_subject_homex_outcome_test.rb +64 -0
- data/test/unit/ccls/study_subject_identifier_test.rb +439 -0
- data/test/unit/ccls/study_subject_interviews_test.rb +26 -0
- data/test/unit/ccls/study_subject_languages_test.rb +142 -0
- data/test/unit/ccls/study_subject_operational_events_test.rb +53 -0
- data/test/unit/ccls/study_subject_patient_test.rb +249 -0
- data/test/unit/ccls/study_subject_pii_test.rb +278 -0
- data/test/unit/ccls/study_subject_races_test.rb +203 -0
- data/test/unit/ccls/study_subject_search_test.rb +704 -0
- data/test/unit/ccls/study_subject_test.rb +770 -0
- data/test/unit/ccls/subject_language_test.rb +43 -0
- data/test/unit/ccls/subject_race_test.rb +35 -0
- data/test/unit/ccls/subject_relationship_test.rb +43 -0
- data/test/unit/ccls/subject_type_test.rb +40 -0
- data/test/unit/ccls/tracing_status_test.rb +32 -0
- data/test/unit/ccls/transfer_test.rb +81 -0
- data/test/unit/ccls/translation_table_test.rb +40 -0
- data/test/unit/ccls/unit_test.rb +21 -0
- data/test/unit/ccls/user_test.rb +156 -0
- data/test/unit/ccls/vital_status_test.rb +36 -0
- data/test/unit/ccls/zip_code_test.rb +55 -0
- 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
|
data/app/models/state.rb
ADDED
|
@@ -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__
|