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,17 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Simply extracted some code to clean up model.
|
|
3
|
+
# I'd like to do this to all of the really big classes
|
|
4
|
+
# but let's see how this goes first.
|
|
5
|
+
#
|
|
6
|
+
module StudySubjectEnrollments
|
|
7
|
+
def self.included(base)
|
|
8
|
+
# Must delay the calls to these ActiveRecord methods
|
|
9
|
+
# or it will raise many "undefined method"s.
|
|
10
|
+
base.class_eval do
|
|
11
|
+
|
|
12
|
+
has_many :enrollments
|
|
13
|
+
accepts_nested_attributes_for :enrollments
|
|
14
|
+
|
|
15
|
+
end # class_eval
|
|
16
|
+
end # included
|
|
17
|
+
end # StudySubjectEnrollments
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Simply extracted some code to clean up model.
|
|
3
|
+
# I'd like to do this to all of the really big classes
|
|
4
|
+
# but let's see how this goes first.
|
|
5
|
+
#
|
|
6
|
+
module StudySubjectHomexOutcome
|
|
7
|
+
def self.included(base)
|
|
8
|
+
# Must delay the calls to these ActiveRecord methods
|
|
9
|
+
# or it will raise many "undefined method"s.
|
|
10
|
+
base.class_eval do
|
|
11
|
+
|
|
12
|
+
has_one :homex_outcome
|
|
13
|
+
|
|
14
|
+
delegate :interview_outcome, :interview_outcome_on,
|
|
15
|
+
:sample_outcome, :sample_outcome_on,
|
|
16
|
+
:to => :homex_outcome, :allow_nil => true
|
|
17
|
+
|
|
18
|
+
accepts_nested_attributes_for :homex_outcome
|
|
19
|
+
|
|
20
|
+
end # class_eval
|
|
21
|
+
end # included
|
|
22
|
+
end # StudySubjectHomexOutcome
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Simply extracted some code to clean up model.
|
|
3
|
+
# I'd like to do this to all of the really big classes
|
|
4
|
+
# but let's see how this goes first.
|
|
5
|
+
#
|
|
6
|
+
module StudySubjectIdentifier
|
|
7
|
+
def self.included(base)
|
|
8
|
+
# Must delay the calls to these ActiveRecord methods
|
|
9
|
+
# or it will raise many "undefined method"s.
|
|
10
|
+
base.class_eval do
|
|
11
|
+
|
|
12
|
+
# Very cool that this doesn't stop factory girl from using them.
|
|
13
|
+
# it will stop the study_subject nested_attribute tests though
|
|
14
|
+
attr_protected :studyid, :studyid_nohyphen, :studyid_intonly_nohyphen,
|
|
15
|
+
:familyid, :childid, :subjectid, :patid, :orderno
|
|
16
|
+
|
|
17
|
+
before_validation :prepare_fields_for_validation
|
|
18
|
+
before_create :prepare_fields_for_creation
|
|
19
|
+
|
|
20
|
+
def self.find_all_by_studyid_or_icf_master_id(studyid,icf_master_id)
|
|
21
|
+
# if decide to use LIKE, will need to NOT include nils so
|
|
22
|
+
# will need to add some conditions to the conditions.
|
|
23
|
+
self.find( :all,
|
|
24
|
+
:conditions => [
|
|
25
|
+
"studyid = :studyid OR icf_master_id = :icf_master_id",
|
|
26
|
+
{ :studyid => studyid, :icf_master_id => icf_master_id }
|
|
27
|
+
]
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
protected
|
|
32
|
+
|
|
33
|
+
def prepare_fields_for_validation
|
|
34
|
+
# NOTE ANY field that has a unique index in the database NEEDS
|
|
35
|
+
# to NOT be blank. Multiple nils are acceptable in index,
|
|
36
|
+
# but multiple blanks are NOT. Nilify ALL fields with
|
|
37
|
+
# unique indexes in the database.
|
|
38
|
+
self.email = nil if email.blank?
|
|
39
|
+
self.ssn = nil if ssn.blank?
|
|
40
|
+
self.state_id_no = nil if state_id_no.blank?
|
|
41
|
+
self.state_registrar_no = nil if state_registrar_no.blank?
|
|
42
|
+
self.local_registrar_no = nil if local_registrar_no.blank?
|
|
43
|
+
self.gbid = nil if gbid.blank?
|
|
44
|
+
self.lab_no_wiemels = nil if lab_no_wiemels.blank?
|
|
45
|
+
self.accession_no = nil if accession_no.blank?
|
|
46
|
+
self.idno_wiemels = nil if idno_wiemels.blank?
|
|
47
|
+
|
|
48
|
+
self.case_control_type = ( ( case_control_type.blank?
|
|
49
|
+
) ? nil : case_control_type.to_s.upcase )
|
|
50
|
+
|
|
51
|
+
patid.try(:gsub!,/\D/,'')
|
|
52
|
+
self.patid = sprintf("%04d",patid.to_i) unless patid.blank?
|
|
53
|
+
|
|
54
|
+
matchingid.try(:gsub!,/\D/,'')
|
|
55
|
+
# TODO add more tests for this (try with valid? method)
|
|
56
|
+
#puts "Matchingid before before validation:#{matchingid}"
|
|
57
|
+
self.matchingid = sprintf("%06d",matchingid.to_i) unless matchingid.blank?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# made separate method so can be stubbed
|
|
61
|
+
def get_next_childid
|
|
62
|
+
self.class.maximum(:childid).to_i + 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# made separate method so can be stubbed
|
|
66
|
+
def get_next_patid
|
|
67
|
+
self.class.maximum(:patid).to_i + 1
|
|
68
|
+
#
|
|
69
|
+
# What happens if/when this goes over 4 digits?
|
|
70
|
+
# The database field is only 4 chars.
|
|
71
|
+
#
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# fields made from fields that WON'T change go here
|
|
75
|
+
def prepare_fields_for_creation
|
|
76
|
+
# don't assign if given or is mother (childid is currently protected)
|
|
77
|
+
self.childid = get_next_childid if !is_mother? and childid.blank?
|
|
78
|
+
|
|
79
|
+
# don't assign if given or is not case (patid is currently protected)
|
|
80
|
+
self.patid = sprintf("%04d",get_next_patid.to_i) if is_case? and patid.blank?
|
|
81
|
+
|
|
82
|
+
# should move this from pre validation to here for ALL subjects.
|
|
83
|
+
# patid.try(:gsub!,/\D/,'')
|
|
84
|
+
# self.patid = sprintf("%04d",patid.to_i) unless patid.blank?
|
|
85
|
+
|
|
86
|
+
# don't assign if given or is not case (orderno is currently protected)
|
|
87
|
+
self.orderno = 0 if is_case? and orderno.blank?
|
|
88
|
+
|
|
89
|
+
# don't assign if given or is mother (studyid is currently protected)
|
|
90
|
+
# or if can't make complete studyid
|
|
91
|
+
if !is_mother? and studyid.blank? and
|
|
92
|
+
!patid.blank? and !case_control_type.blank? and !orderno.blank?
|
|
93
|
+
self.studyid = "#{patid}-#{case_control_type}-#{orderno}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# perhaps put in an after_save with an update_attribute(s)
|
|
97
|
+
# and simply generate a new one until all is well
|
|
98
|
+
# don't assign if given (subjectid is currently protected)
|
|
99
|
+
self.subjectid = generate_subjectid if subjectid.blank?
|
|
100
|
+
|
|
101
|
+
# cases and controls: their own subjectID is also their familyID.
|
|
102
|
+
# mothers: their child's subjectID is their familyID. That is,
|
|
103
|
+
# a mother and her child have identical familyIDs.
|
|
104
|
+
# don't assign if given (familyid is currently protected)
|
|
105
|
+
self.familyid = subjectid if !is_mother? and familyid.blank?
|
|
106
|
+
|
|
107
|
+
# cases (patients): matchingID is the study_subject's own subjectID
|
|
108
|
+
# controls: matchingID is subjectID of the associated case
|
|
109
|
+
# (like PatID in this respect).
|
|
110
|
+
# mothers: matchingID is subjectID of their own child's associated case.
|
|
111
|
+
# That is, a mother's matchingID is the same as their child's. This
|
|
112
|
+
# will become clearer when I provide specs for mother study_subject creation.
|
|
113
|
+
# matchingid is manually set in some tests. will need to setup for stubbing this.
|
|
114
|
+
# don't assign if given (matchingid is currently NOT protected)
|
|
115
|
+
self.matchingid = subjectid if is_case? and matchingid.blank?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# made separate method so can stub it in testing
|
|
119
|
+
# This only guarantees uniqueness before creation,
|
|
120
|
+
# but not at creation. This is NOT scalable.
|
|
121
|
+
# Fortunately, we won't be creating tons of study_subjects
|
|
122
|
+
# at the same time so this should not be an issue,
|
|
123
|
+
# however, when it fails, it will be confusing. # TODO
|
|
124
|
+
# How to rescue from ActiveRecord::RecordInvalid here?
|
|
125
|
+
# or would it be RecordNotSaved?
|
|
126
|
+
#
|
|
127
|
+
# Perhaps treat subjectid like icf_master_id?
|
|
128
|
+
# Create a table with all of the possible
|
|
129
|
+
# subjectid ... (1..999999)
|
|
130
|
+
# study_subject_id
|
|
131
|
+
# assigned_on
|
|
132
|
+
# Then select a random unassigned one?
|
|
133
|
+
# Would this be faster?
|
|
134
|
+
#
|
|
135
|
+
def generate_subjectid
|
|
136
|
+
subjectids = ( (1..999999).to_a - self.class.find(:all,:select => 'subjectid'
|
|
137
|
+
).collect(&:subjectid).collect(&:to_i) )
|
|
138
|
+
# CANNOT have leading 0' as it thinks its octal and converts
|
|
139
|
+
#>> sprintf("%06d","0001234")
|
|
140
|
+
#=> "000668"
|
|
141
|
+
#
|
|
142
|
+
# CANNOT have leading 0's and include and 8 or 9 as it thinks its octal
|
|
143
|
+
# so convert back to Integer first
|
|
144
|
+
#>> sprintf("%06d","0001280")
|
|
145
|
+
#ArgumentError: invalid value for Integer: "0001280"
|
|
146
|
+
# from (irb):24:in `sprintf'
|
|
147
|
+
# from (irb):24
|
|
148
|
+
sprintf("%06d",subjectids[rand(subjectids.length)].to_i)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
end # class_eval
|
|
152
|
+
end # included
|
|
153
|
+
end # StudySubjectIdentifier
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Simply extracted some code to clean up model.
|
|
3
|
+
# I'd like to do this to all of the really big classes
|
|
4
|
+
# but let's see how this goes first.
|
|
5
|
+
#
|
|
6
|
+
module StudySubjectInterviews
|
|
7
|
+
def self.included(base)
|
|
8
|
+
# Must delay the calls to these ActiveRecord methods
|
|
9
|
+
# or it will raise many "undefined method"s.
|
|
10
|
+
base.class_eval do
|
|
11
|
+
|
|
12
|
+
has_many :interviews
|
|
13
|
+
|
|
14
|
+
# Returns home exposures interview
|
|
15
|
+
def hx_interview
|
|
16
|
+
interviews.find(:first,
|
|
17
|
+
# :conditions => "projects.key = 'HomeExposures'",
|
|
18
|
+
:conditions => { 'projects.id' => Project['HomeExposures'].id },
|
|
19
|
+
:joins => [:instrument_version => [:instrument => :project]]
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end # class_eval
|
|
24
|
+
end # included
|
|
25
|
+
end # StudySubjectInterviews
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Simply extracted some code to clean up model.
|
|
3
|
+
# I'd like to do this to all of the really big classes
|
|
4
|
+
# but let's see how this goes first.
|
|
5
|
+
#
|
|
6
|
+
module StudySubjectLanguages
|
|
7
|
+
def self.included(base)
|
|
8
|
+
# Must delay the calls to these ActiveRecord methods
|
|
9
|
+
# or it will raise many "undefined method"s.
|
|
10
|
+
base.class_eval do
|
|
11
|
+
|
|
12
|
+
has_many :subject_languages
|
|
13
|
+
has_many :languages, :through => :subject_languages
|
|
14
|
+
|
|
15
|
+
accepts_nested_attributes_for :subject_languages,
|
|
16
|
+
:allow_destroy => true,
|
|
17
|
+
:reject_if => proc{|attributes| attributes['language_id'].blank? }
|
|
18
|
+
|
|
19
|
+
end # class_eval
|
|
20
|
+
end # included
|
|
21
|
+
end # StudySubjectLanguages
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Simply extracted some code to clean up model.
|
|
3
|
+
# I'd like to do this to all of the really big classes
|
|
4
|
+
# but let's see how this goes first.
|
|
5
|
+
#
|
|
6
|
+
module StudySubjectOperationalEvents
|
|
7
|
+
def self.included(base)
|
|
8
|
+
# Must delay the calls to these ActiveRecord methods
|
|
9
|
+
# or it will raise many "undefined method"s.
|
|
10
|
+
base.class_eval do
|
|
11
|
+
|
|
12
|
+
has_many :operational_events, :through => :enrollments
|
|
13
|
+
|
|
14
|
+
after_create :add_new_subject_operational_event
|
|
15
|
+
after_save :add_subject_died_operational_event
|
|
16
|
+
|
|
17
|
+
# All subjects are to have a CCLS project enrollment, so create after create.
|
|
18
|
+
# All subjects are to have this operational event, so create after create.
|
|
19
|
+
# I suspect that this'll be attached to the CCLS project enrollment.
|
|
20
|
+
def add_new_subject_operational_event
|
|
21
|
+
ccls_enrollment = enrollments.find_or_create_by_project_id(Project['ccls'].id)
|
|
22
|
+
OperationalEvent.create!(
|
|
23
|
+
:enrollment => ccls_enrollment,
|
|
24
|
+
:operational_event_type => OperationalEventType['newSubject'],
|
|
25
|
+
:occurred_on => Date.today
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Add this if the vital status changes to deceased.
|
|
30
|
+
# I suspect that this'll be attached to the CCLS project enrollment.
|
|
31
|
+
def add_subject_died_operational_event
|
|
32
|
+
if( ( vital_status_id == VitalStatus['deceased'].id ) &&
|
|
33
|
+
( vital_status_id_was != VitalStatus['deceased'].id ) )
|
|
34
|
+
ccls_enrollment = enrollments.find_or_create_by_project_id(Project['ccls'].id)
|
|
35
|
+
OperationalEvent.create!(
|
|
36
|
+
:enrollment => ccls_enrollment,
|
|
37
|
+
:operational_event_type => OperationalEventType['subjectDied'],
|
|
38
|
+
:occurred_on => Date.today
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# operational_events.occurred_on where operational_event_type_id = 26 and enrollment_id is for any open project (where projects.ended_on is null) for study_subject_id
|
|
44
|
+
|
|
45
|
+
def screener_complete_date_for_open_project
|
|
46
|
+
OperationalEvent.find(:first,
|
|
47
|
+
:joins => [
|
|
48
|
+
'LEFT JOIN enrollments ON operational_events.enrollment_id = enrollments.id',
|
|
49
|
+
'LEFT JOIN projects ON enrollments.project_id = projects.id'
|
|
50
|
+
],
|
|
51
|
+
:conditions => [
|
|
52
|
+
"study_subject_id = :subject_id AND " <<
|
|
53
|
+
"operational_event_type_id = :screener_complete AND " <<
|
|
54
|
+
'projects.ended_on IS NULL',
|
|
55
|
+
{
|
|
56
|
+
:subject_id => self.id,
|
|
57
|
+
:screener_complete => OperationalEventType['screener_complete'].id
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
).try(:occurred_on)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end # class_eval
|
|
64
|
+
end # included
|
|
65
|
+
end # StudySubjectOperationalEvents
|
|
66
|
+
__END__
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Simply extracted some code to clean up model.
|
|
3
|
+
# I'd like to do this to all of the really big classes
|
|
4
|
+
# but let's see how this goes first.
|
|
5
|
+
#
|
|
6
|
+
module StudySubjectPatient
|
|
7
|
+
def self.included(base)
|
|
8
|
+
# Must delay the calls to these ActiveRecord methods
|
|
9
|
+
# or it will raise many "undefined method"s.
|
|
10
|
+
base.class_eval do
|
|
11
|
+
|
|
12
|
+
has_one :patient
|
|
13
|
+
|
|
14
|
+
delegate :admit_date, :hospital_no,
|
|
15
|
+
:organization, :organization_id,
|
|
16
|
+
:to => :patient, :allow_nil => true
|
|
17
|
+
|
|
18
|
+
accepts_nested_attributes_for :patient
|
|
19
|
+
|
|
20
|
+
validate :must_be_case_if_patient
|
|
21
|
+
validate :patient_admit_date_is_after_dob
|
|
22
|
+
validate :patient_diagnosis_date_is_after_dob
|
|
23
|
+
|
|
24
|
+
after_save :trigger_setting_was_under_15_at_dx,
|
|
25
|
+
:if => :dob_changed?
|
|
26
|
+
after_save :trigger_update_matching_study_subjects_reference_date,
|
|
27
|
+
:if => :matchingid_changed?
|
|
28
|
+
|
|
29
|
+
def admitting_oncologist
|
|
30
|
+
# can be blank so need more than try unless I nilify admitting_oncologist if blank
|
|
31
|
+
#patient.try(:admitting_oncologist) || "[no oncologist specified]"
|
|
32
|
+
if patient and !patient.admitting_oncologist.blank?
|
|
33
|
+
patient.admitting_oncologist
|
|
34
|
+
else
|
|
35
|
+
"[no oncologist specified]"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# triggered from patient and self
|
|
41
|
+
def update_patient_was_under_15_at_dx
|
|
42
|
+
# due to the high probability that self and patient will not
|
|
43
|
+
# yet be resolved, we have to get the associations manually.
|
|
44
|
+
my_patient = Patient.find_by_study_subject_id(self.attributes['id'])
|
|
45
|
+
if dob && my_patient && my_patient.admit_date &&
|
|
46
|
+
dob.to_date != Date.parse('1/1/1900') &&
|
|
47
|
+
my_patient.admit_date.to_date != Date.parse('1/1/1900')
|
|
48
|
+
#
|
|
49
|
+
# update_all(updates, conditions = nil, options = {})
|
|
50
|
+
#
|
|
51
|
+
# Updates all records with details given if they match a set of
|
|
52
|
+
# conditions supplied, limits and order can also be supplied.
|
|
53
|
+
# This method constructs a single SQL UPDATE statement and sends
|
|
54
|
+
# it straight to the database. It does not instantiate the involved
|
|
55
|
+
# models and it does not trigger Active Record callbacks.
|
|
56
|
+
#
|
|
57
|
+
# Patient.update_all({
|
|
58
|
+
# :was_under_15_at_dx => (((
|
|
59
|
+
# my_patient.admit_date.to_date - my_pii.dob.to_date
|
|
60
|
+
# ) / 365 ) < 15 )}, { :id => my_patient.id })
|
|
61
|
+
|
|
62
|
+
# crude and probably off by a couple days
|
|
63
|
+
# would be better to compare year, month then day
|
|
64
|
+
was_under_15 = (((
|
|
65
|
+
my_patient.admit_date.to_date - dob.to_date
|
|
66
|
+
) / 365 ) < 15 ) ? YNDK[:yes] : YNDK[:no]
|
|
67
|
+
Patient.update_all({ :was_under_15_at_dx => was_under_15 },
|
|
68
|
+
{ :id => my_patient.id })
|
|
69
|
+
end
|
|
70
|
+
# make sure we return true as is a callback
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
#
|
|
76
|
+
def update_study_subjects_reference_date_matching(*matchingids)
|
|
77
|
+
logger.debug "DEBUG: In update_study_subjects_reference_date_matching(*matchingids)"
|
|
78
|
+
logger.debug "DEBUG: update_study_subjects_reference_date_matching(#{matchingids.join(',')})"
|
|
79
|
+
# if matchingids ~ [nil,12345]
|
|
80
|
+
# identifier was either just created or matchingid added (compact as nil not needed)
|
|
81
|
+
# if matchingids ~ [12345,nil]
|
|
82
|
+
# matchingid was removed (compact as nil not needed)
|
|
83
|
+
# if matchingids ~ [12345,54321]
|
|
84
|
+
# matchingid was just changed
|
|
85
|
+
# if matchingids ~ []
|
|
86
|
+
# trigger came from Patient so need to find matchingid
|
|
87
|
+
|
|
88
|
+
# due to the high probability that self and identifier will not
|
|
89
|
+
# yet be resolved, we have to get the associations manually.
|
|
90
|
+
# my_identifier = Identifier.find_by_study_subject_id(self.attributes['id'])
|
|
91
|
+
# matchingids.compact.push(my_identifier.try(:matchingid)).uniq.each do |matchingid|
|
|
92
|
+
matchingids.compact.push(matchingid).uniq.each do |mid|
|
|
93
|
+
study_subject_ids = if( !mid.nil? )
|
|
94
|
+
# Identifier.find_all_by_matchingid(mid).collect(&:study_subject_id)
|
|
95
|
+
# StudySubject.find_all_by_matchingid(mid).collect(&:id)
|
|
96
|
+
self.class.find_all_by_matchingid(mid).collect(&:id)
|
|
97
|
+
else
|
|
98
|
+
[id]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# SHOULD only ever be 1 patient found amongst the study_subject_ids although there is
|
|
102
|
+
# currently no validation applied to the uniqueness of matchingid
|
|
103
|
+
# If there is more than one patient for a given matchingid, this'll just be wrong.
|
|
104
|
+
|
|
105
|
+
matching_patient = Patient.find_by_study_subject_id(study_subject_ids)
|
|
106
|
+
admit_date = matching_patient.try(:admit_date)
|
|
107
|
+
|
|
108
|
+
logger.debug "DEBUG: calling StudySubject.update_study_subjects_reference_date(#{study_subject_ids.join(',')},#{admit_date})"
|
|
109
|
+
# StudySubject.update_study_subjects_reference_date( study_subject_ids, admit_date )
|
|
110
|
+
self.class.update_study_subjects_reference_date( study_subject_ids, admit_date )
|
|
111
|
+
end
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
protected
|
|
116
|
+
|
|
117
|
+
# This is a duplication of a patient validation that won't
|
|
118
|
+
# work if using nested attributes. Don't like doing this.
|
|
119
|
+
def patient_admit_date_is_after_dob
|
|
120
|
+
# if !patient.nil? && !patient.admit_date.blank? &&
|
|
121
|
+
# !pii.nil? && !pii.dob.blank? && patient.admit_date < pii.dob &&
|
|
122
|
+
# pii.dob.to_date != Date.parse('1/1/1900') &&
|
|
123
|
+
if !patient.nil? && !patient.admit_date.blank? &&
|
|
124
|
+
!dob.blank? && patient.admit_date < dob &&
|
|
125
|
+
dob.to_date != Date.parse('1/1/1900') &&
|
|
126
|
+
patient.admit_date.to_date != Date.parse('1/1/1900')
|
|
127
|
+
errors.add('patient:admit_date', "is before study_subject's dob.")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# This is a duplication of a patient validation that won't
|
|
132
|
+
# work if using nested attributes. Don't like doing this.
|
|
133
|
+
def patient_diagnosis_date_is_after_dob
|
|
134
|
+
if !patient.nil? && !patient.diagnosis_date.blank? &&
|
|
135
|
+
!dob.blank? && patient.diagnosis_date < dob
|
|
136
|
+
# !pii.nil? && !pii.dob.blank? && patient.diagnosis_date < pii.dob
|
|
137
|
+
errors.add('patient:diagnosis_date', "is before study_subject's dob.")
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def must_be_case_if_patient
|
|
142
|
+
if !patient.nil? and !is_case?
|
|
143
|
+
errors.add(:patient ,"must be case to have patient info")
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
#
|
|
148
|
+
# logger levels are ... debug, info, warn, error, and fatal.
|
|
149
|
+
#
|
|
150
|
+
def trigger_setting_was_under_15_at_dx
|
|
151
|
+
logger.debug "DEBUG: calling update_patient_was_under_15_at_dx from StudySubject:#{self.attributes['id']}"
|
|
152
|
+
logger.debug "DEBUG: DOB changed from:#{dob_was}:to:#{dob}"
|
|
153
|
+
update_patient_was_under_15_at_dx
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def trigger_update_matching_study_subjects_reference_date
|
|
157
|
+
logger.debug "DEBUG: triggering_update_matching_study_subjects_reference_date from StudySubject:#{self.attributes['id']}"
|
|
158
|
+
logger.debug "DEBUG: matchingid changed from:#{matchingid_was}:to:#{matchingid}"
|
|
159
|
+
self.update_study_subjects_reference_date_matching(matchingid_was,matchingid)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def self.update_study_subjects_reference_date(study_subject_ids,new_reference_date)
|
|
163
|
+
logger.debug "DEBUG: In StudySubject.update_study_subjects_reference_date"
|
|
164
|
+
logger.debug "DEBUG: update_study_subjects_reference_date(#{study_subject_ids.join(',')},#{new_reference_date})"
|
|
165
|
+
# UPDATE `study_subjects` SET `reference_date` = '2011-06-02' WHERE (`subjects`.`id` IN (1,2))
|
|
166
|
+
# UPDATE `study_subjects` SET `reference_date` = '2011-06-02' WHERE (`subjects`.`id` IN (NULL))
|
|
167
|
+
unless study_subject_ids.empty?
|
|
168
|
+
# StudySubject.update_all(
|
|
169
|
+
self.update_all(
|
|
170
|
+
{:reference_date => new_reference_date },
|
|
171
|
+
{ :id => study_subject_ids })
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
end # class_eval
|
|
176
|
+
end # included
|
|
177
|
+
end # StudySubjectPatient
|