renalware-core 2.0.46 → 2.0.47
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -11
- data/app/assets/javascripts/renalware/clipboard_setup.js +37 -0
- data/app/assets/javascripts/renalware/navigation.js +3 -3
- data/app/assets/stylesheets/renalware/base/_variables.scss +2 -0
- data/app/assets/stylesheets/renalware/lib/_foundation_and_overrides.scss +1 -0
- data/app/assets/stylesheets/renalware/modules/_clipboard.scss +12 -0
- data/app/assets/stylesheets/renalware/modules/_patients.scss +9 -0
- data/app/assets/stylesheets/renalware/partials/_button.scss +2 -1
- data/app/assets/stylesheets/renalware/partials/_layout.scss +30 -2
- data/app/assets/stylesheets/renalware/partials/_navigation.scss +30 -21
- data/app/assets/stylesheets/renalware/partials/_tables.scss +18 -3
- data/app/controllers/renalware/accesses/plans_controller.rb +1 -0
- data/app/controllers/renalware/admissions/requests_controller.rb +1 -0
- data/app/controllers/renalware/clinical/dry_weights_controller.rb +1 -0
- data/app/controllers/renalware/concerns/cache_busting.rb +1 -1
- data/app/controllers/renalware/events/events_controller.rb +3 -0
- data/app/controllers/renalware/hd/diary_slots_controller.rb +1 -0
- data/app/controllers/renalware/hospitals/wards_controller.rb +1 -1
- data/app/controllers/renalware/letters/base_controller.rb +4 -0
- data/app/controllers/renalware/letters/formatted_letters_controller.rb +9 -4
- data/app/controllers/renalware/letters/letters_controller.rb +1 -0
- data/app/controllers/renalware/letters/printable_letters_controller.rb +47 -0
- data/app/controllers/renalware/medications/prescriptions_controller.rb +1 -1
- data/app/controllers/renalware/messaging/internal/messages_controller.rb +1 -0
- data/app/controllers/renalware/patients/primary_care_physician_controller.rb +2 -0
- data/app/controllers/renalware/session_timeout_controller.rb +2 -0
- data/app/documents/renalware/blood_pressure.rb +1 -0
- data/app/documents/renalware/dated_boolean_diagnosis.rb +1 -0
- data/app/helpers/renalware/application_helper.rb +1 -0
- data/app/helpers/renalware/drugs_helper.rb +1 -0
- data/app/helpers/renalware/form_helper.rb +1 -0
- data/app/helpers/renalware/hospitals_helper.rb +25 -0
- data/app/helpers/renalware/mdm_helper.rb +1 -0
- data/app/inputs/date_picker_input.rb +1 -0
- data/app/jobs/renalware/letters/calculate_page_count_job.rb +46 -0
- data/app/models/concerns/renalware/letters/letter_pathology.rb +1 -0
- data/app/models/concerns/renalware/patient_pathology_scopes.rb +1 -1
- data/app/models/concerns/renalware/patient_transplant_scopes.rb +17 -0
- data/app/models/concerns/renalware/patients_ransack_helper.rb +13 -11
- data/app/models/renalware/accesses/assessment.rb +1 -1
- data/app/models/renalware/accesses/procedure.rb +1 -1
- data/app/models/renalware/accesses/profile.rb +3 -3
- data/app/models/renalware/admissions/admission_search_form.rb +1 -0
- data/app/models/renalware/clinical/allergy_status_form.rb +1 -0
- data/app/models/renalware/clinical/body_composition.rb +1 -1
- data/app/models/renalware/clinical/dry_weight.rb +1 -1
- data/app/models/renalware/clinics/clinic_visit.rb +4 -1
- data/app/models/renalware/clinics/create_clinic_visit.rb +1 -0
- data/app/models/renalware/events/line_change_event_query.rb +1 -0
- data/app/models/renalware/feeds/files/practices/convert_xml_to_csv.rb +1 -0
- data/app/models/renalware/feeds/files/practices/country_map.rb +1 -0
- data/app/models/renalware/feeds/files/practices/xml_parser.rb +2 -0
- data/app/models/renalware/hd/generate_monthly_statistics_for_patient.rb +2 -0
- data/app/models/renalware/hd/mdm_patients_form.rb +1 -0
- data/app/models/renalware/hd/mdm_patients_query.rb +2 -5
- data/app/models/renalware/hd/modality_description.rb +1 -0
- data/app/models/renalware/hd/patient_listener.rb +1 -0
- data/app/models/renalware/hd/patient_statistics.rb +1 -0
- data/app/models/renalware/hd/patients_dialysing_by_schedule_query.rb +1 -0
- data/app/models/renalware/hd/preference_set.rb +1 -1
- data/app/models/renalware/hd/profile.rb +1 -1
- data/app/models/renalware/hd/revise_hd_profile.rb +2 -1
- data/app/models/renalware/hd/session.rb +1 -1
- data/app/models/renalware/hd/session/closed.rb +1 -0
- data/app/models/renalware/hd/session/dna.rb +1 -0
- data/app/models/renalware/hd/session_factory.rb +2 -0
- data/app/models/renalware/hd/sessions/auditable_session.rb +3 -0
- data/app/models/renalware/hd/sessions/save_session.rb +3 -0
- data/app/models/renalware/hd/update_rolling_patient_statistics.rb +1 -0
- data/app/models/renalware/hospitals/ward.rb +1 -0
- data/app/models/renalware/letters/contact.rb +1 -1
- data/app/models/renalware/letters/delivery/email_letter_to_practice.rb +1 -0
- data/app/models/renalware/letters/event.rb +1 -0
- data/app/models/renalware/letters/html_renderer.rb +3 -0
- data/app/models/renalware/letters/letter.rb +1 -1
- data/app/models/renalware/letters/letter_factory.rb +2 -0
- data/app/models/renalware/letters/part/clinical_observations.rb +1 -0
- data/app/models/renalware/letters/part/recent_pathology_results.rb +3 -0
- data/app/models/renalware/letters/pdf_letter_cache.rb +5 -4
- data/app/models/renalware/letters/pdf_renderer.rb +3 -3
- data/app/models/renalware/letters/primary_care_physician.rb +1 -0
- data/app/models/renalware/letters/printing/create_pdf_by_interleaving_address_sheet_and_letter_for_each_recipient.rb +139 -0
- data/app/models/renalware/letters/printing/duplex_interleaved_pdf_renderer.rb +39 -0
- data/app/models/renalware/letters/printing/pdf_combining.rb +64 -0
- data/app/models/renalware/letters/printing/printable_recipients.rb +20 -0
- data/app/models/renalware/letters/recipient.rb +7 -2
- data/app/models/renalware/letters/recipient_address_pdf_renderer.rb +26 -0
- data/app/models/renalware/letters/recipient_params_processor.rb +1 -0
- data/app/models/renalware/low_clearance/mdm_patients_query.rb +2 -0
- data/app/models/renalware/low_clearance/profile.rb +2 -1
- data/app/models/renalware/medications/prescription.rb +3 -1
- data/app/models/renalware/messaging/internal/message_form_builder.rb +1 -0
- data/app/models/renalware/modalities/change_patient_modality.rb +1 -0
- data/app/models/renalware/pathology/message_listener.rb +1 -0
- data/app/models/renalware/pathology/observation_requests_attributes_builder.rb +2 -0
- data/app/models/renalware/pathology/observations_grouped_by_date_query.rb +1 -0
- data/app/models/renalware/pathology/observations_jsonb_serializer.rb +1 -0
- data/app/models/renalware/pathology/requests/global_rule/observation_result.rb +1 -0
- data/app/models/renalware/pathology/requests/global_rule/prescription_drug.rb +1 -0
- data/app/models/renalware/pathology/requests/global_rule/prescription_drug_category.rb +1 -0
- data/app/models/renalware/pathology/requests/global_rule/prescription_drug_type.rb +1 -0
- data/app/models/renalware/pathology/requests/global_rule/request_result.rb +3 -0
- data/app/models/renalware/pathology/requests/global_rule_set.rb +2 -0
- data/app/models/renalware/pathology/requests/high_risk_rule_set.rb +5 -0
- data/app/models/renalware/patient.rb +14 -11
- data/app/models/renalware/patients/calculate_age.rb +47 -0
- data/app/models/renalware/patients/idempotent_create_patient.rb +1 -0
- data/app/models/renalware/patients/mdm_patients_query.rb +0 -2
- data/app/models/renalware/patients/patient_hospital_identifiers.rb +2 -0
- data/app/models/renalware/patients/worry.rb +1 -1
- data/app/models/renalware/pd/apd/available_overnight_volume.rb +2 -0
- data/app/models/renalware/pd/apd/calculate_volumes.rb +1 -0
- data/app/models/renalware/pd/apd/non_tidal_regime_calculations.rb +1 -0
- data/app/models/renalware/pd/apd/regime_calculations.rb +1 -0
- data/app/models/renalware/pd/apd/tidal_regime_calculations.rb +2 -0
- data/app/models/renalware/pd/mdm_patients_query.rb +2 -0
- data/app/models/renalware/problems/problem.rb +1 -1
- data/app/models/renalware/renal/profile.rb +1 -1
- data/app/models/renalware/renal/registry/preflight_checks/patients_query.rb +15 -2
- data/app/models/renalware/research/study_participant.rb +1 -0
- data/app/models/renalware/system/message.rb +2 -0
- data/app/models/renalware/system/update_user.rb +1 -2
- data/app/models/renalware/transplants/donation.rb +2 -1
- data/app/models/renalware/transplants/donor_followup.rb +2 -1
- data/app/models/renalware/transplants/donor_operation.rb +2 -1
- data/app/models/renalware/transplants/donor_workup.rb +2 -1
- data/app/models/renalware/transplants/recipient_followup.rb +2 -1
- data/app/models/renalware/transplants/recipient_operation.rb +2 -1
- data/app/models/renalware/transplants/recipient_workup.rb +2 -1
- data/app/models/renalware/transplants/registration.rb +2 -1
- data/app/models/renalware/ukrdc/send_patient.rb +1 -0
- data/app/policies/renalware/base_policy.rb +2 -0
- data/app/policies/renalware/clinics/clinic_visit_policy.rb +1 -0
- data/app/presenters/renalware/admissions/admission_presenter.rb +1 -0
- data/app/presenters/renalware/clinical/header_presenter.rb +1 -1
- data/app/presenters/renalware/dashboard/dashboard_presenter.rb +1 -1
- data/app/presenters/renalware/hd/profile_document_presenter.rb +1 -0
- data/app/presenters/renalware/hd/profile_presenter.rb +4 -0
- data/app/presenters/renalware/hd/session_access_presenter.rb +3 -0
- data/app/presenters/renalware/hd/session_presenter.rb +2 -0
- data/app/presenters/renalware/hd/station_presenter.rb +1 -0
- data/app/presenters/renalware/hd/unmet_preferences_presenter.rb +2 -0
- data/app/presenters/renalware/hospitals/ward_presenter.rb +21 -0
- data/app/presenters/renalware/letters/letter_presenter.rb +16 -5
- data/app/presenters/renalware/medications/prescription_presenter.rb +1 -0
- data/app/presenters/renalware/pathology/observation_set_presenter.rb +2 -0
- data/app/presenters/renalware/patient_presenter.rb +1 -0
- data/app/presenters/renalware/pd/peritonitis_episode_presenter.rb +1 -0
- data/app/presenters/renalware/reporting/audit_presenter.rb +1 -0
- data/app/presenters/renalware/system/users_presenter.rb +1 -0
- data/app/presenters/renalware/transplants/donor_dashboard_presenter.rb +1 -1
- data/app/presenters/renalware/transplants/wait_list_registration_presenter.rb +2 -0
- data/app/presenters/renalware/ukrdc/patient_presenter.rb +2 -0
- data/app/validators/renalware/letters/author_signature_validator.rb +1 -0
- data/app/validators/renalware/patients/blood_pressure_validator.rb +1 -0
- data/app/validators/renalware/patients/bm_stix_validator.rb +1 -0
- data/app/validators/renalware/patients/height_validator.rb +1 -0
- data/app/validators/renalware/patients/pulse_validator.rb +1 -0
- data/app/validators/renalware/patients/temperature_validator.rb +1 -0
- data/app/validators/renalware/patients/weight_validator.rb +1 -0
- data/app/values/renalware/address.rb +1 -0
- data/app/values/renalware/bmi.rb +1 -0
- data/app/views/renalware/admin/users/_filters.html.slim +0 -8
- data/app/views/renalware/admin/users/_tabs.html.slim +7 -0
- data/app/views/renalware/admin/users/index.html.slim +6 -2
- data/app/views/renalware/admissions/admissions/_filters.html.slim +1 -2
- data/app/views/renalware/admissions/admissions/_form.html.slim +1 -9
- data/app/views/renalware/admissions/admissions/index.html.slim +3 -1
- data/app/views/renalware/admissions/consults/_form.html.slim +3 -7
- data/app/views/renalware/admissions/consults/index.html.slim +3 -1
- data/app/views/renalware/api/ukrdc/patients/_patient.xml.builder +1 -0
- data/app/views/renalware/clinics/appointments/index.html.slim +16 -15
- data/app/views/renalware/clinics/clinic_visits/_table.html.slim +1 -1
- data/app/views/renalware/clinics/clinic_visits/_table_row.html.slim +10 -0
- data/app/views/renalware/clinics/visits/_table.html.slim +2 -2
- data/app/views/renalware/clinics/visits/_table_row.html.slim +10 -0
- data/app/views/renalware/clinics/visits/index.html.slim +2 -2
- data/app/views/renalware/deaths/index.html.slim +6 -1
- data/app/views/renalware/directory/people/index.html.slim +2 -1
- data/app/views/renalware/drugs/drugs/index.html.slim +2 -2
- data/app/views/renalware/events/types/{_header.html.slim → _header.html.slim.dead} +0 -0
- data/app/views/renalware/hd/cannulation_types/index.html.slim +3 -1
- data/app/views/renalware/hd/dialysers/index.html.slim +2 -1
- data/app/views/renalware/hd/mdm_patients/index.html.slim +3 -2
- data/app/views/renalware/hd/ongoing_sessions/show.html.slim +3 -1
- data/app/views/renalware/hd/sessions/_row.html.slim +11 -1
- data/app/views/renalware/hd/sessions/_thead.html.slim +1 -1
- data/app/views/renalware/hd/sessions/dna/_row.html.slim +11 -1
- data/app/views/renalware/hospitals/wards/_form.html.slim +1 -0
- data/app/views/renalware/hospitals/wards/index.html.slim +9 -6
- data/app/views/renalware/layouts/_non_patient.html.slim +16 -9
- data/app/views/renalware/letters/electronic_receipts/read.html.slim +6 -2
- data/app/views/renalware/letters/electronic_receipts/sent.html.slim +6 -2
- data/app/views/renalware/letters/electronic_receipts/unread.html.slim +6 -2
- data/app/views/renalware/letters/formatted_letters/_letter.html.slim +3 -2
- data/app/views/renalware/letters/letters/_additional_ccs.html.slim +1 -1
- data/app/views/renalware/letters/letters/_form.html.slim +2 -5
- data/app/views/renalware/letters/letters/_main_recipient.html.slim +2 -3
- data/app/views/renalware/letters/letters/edit.html.slim +1 -0
- data/app/views/renalware/letters/letters/new.html.slim +1 -1
- data/app/views/renalware/letters/lists/_letter.html.slim +14 -3
- data/app/views/renalware/letters/lists/_table.html.slim +2 -1
- data/app/views/renalware/letters/lists/show.html.slim +27 -25
- data/app/views/renalware/letters/printable_letters/_recipient_address_cover_sheet.html.slim +40 -0
- data/app/views/renalware/letters/printable_letters/show.pdf.slim +10 -0
- data/app/views/renalware/letters/shared/_electronic_cc_type_tabs.html.slim +6 -4
- data/app/views/renalware/low_clearance/mdm_patients/{_filters.html.slim → _tabs.html.slim} +2 -2
- data/app/views/renalware/mdm_patients/_table.html.slim +1 -1
- data/app/views/renalware/mdm_patients/index.html.slim +5 -5
- data/app/views/renalware/medications/drug_types/prescriptions/_filters.html.slim +1 -1
- data/app/views/renalware/medications/drug_types/prescriptions/index.html.slim +3 -1
- data/app/views/renalware/medications/prescriptions/_tables.html.slim +2 -2
- data/app/views/renalware/messaging/internal/receipts/read.html.slim +6 -2
- data/app/views/renalware/messaging/internal/receipts/sent.html.slim +6 -2
- data/app/views/renalware/messaging/internal/receipts/unread.html.slim +6 -2
- data/app/views/renalware/messaging/shared/_messages_type_tabs.html.slim +6 -4
- data/app/views/renalware/modalities/descriptions/{_header.html.slim → _header.html.slim.dead} +0 -0
- data/app/views/renalware/pathology/_navigation.html.slim +8 -7
- data/app/views/renalware/patients/_header.html.slim +3 -3
- data/app/views/renalware/patients/_index_header.html.slim +1 -1
- data/app/views/renalware/patients/patients/edit.html.slim +2 -1
- data/app/views/renalware/patients/patients/index.html.slim +3 -1
- data/app/views/renalware/patients/patients/new.html.slim +1 -2
- data/app/views/renalware/patients/primary_care_physicians/index.html.slim +2 -1
- data/app/views/renalware/patients/worryboard/show.html.slim +3 -1
- data/app/views/renalware/problems/problems/_archived_table.html.slim +1 -1
- data/app/views/renalware/problems/problems/index.html.slim +1 -1
- data/app/views/renalware/renal/aki_alerts/_filters.html.slim +8 -8
- data/app/views/renalware/renal/aki_alerts/edit.html.slim +1 -6
- data/app/views/renalware/renal/aki_alerts/index.html.slim +3 -1
- data/app/views/renalware/renal/registry_preflight_checks/_filters.html.slim +5 -0
- data/app/views/renalware/renal/registry_preflight_checks/_table.html.slim +4 -1
- data/app/views/renalware/renal/registry_preflight_checks/deaths.html.slim +5 -1
- data/app/views/renalware/renal/registry_preflight_checks/patients.html.slim +5 -1
- data/app/views/renalware/transplants/mdm_patients/{_filters.html.slim → _filters.html.slim.dead} +0 -0
- data/app/views/renalware/transplants/mdm_patients/_tabs.html.slim +8 -0
- data/app/views/renalware/transplants/wait_lists/show.html.slim +2 -1
- data/config/initializers/ransack.rb +11 -0
- data/config/initializers/renalware.rb +6 -1
- data/config/locales/renalware/low_clearance/low_clearance.en.yml +2 -2
- data/config/locales/renalware/transplants/mdm.en.yml +2 -2
- data/config/routes.rb +5 -0
- data/db/migrate/20180831134926_create_daily_reports.rb +5 -0
- data/db/migrate/20180907100545_add_page_count_to_letters.rb +5 -0
- data/db/migrate/20181001162513_add_active_to_hospital_wards.rb +5 -0
- data/lib/collection_presenter.rb +1 -0
- data/lib/core_extensions/active_record/sort.rb +1 -0
- data/lib/document/base.rb +1 -0
- data/lib/document/embedded.rb +1 -0
- data/lib/dropdown_button_item.rb +1 -0
- data/lib/renalware/remembered_preferences.rb +2 -0
- data/lib/renalware/version.rb +1 -1
- data/lib/sql/index_case_stmt.rb +1 -0
- data/lib/tasks/reporting.rake +1 -1
- data/spec/support/letters_spec_helper.rb +74 -2
- data/spec/support/matchers/have_pdf_page_text.rb +15 -0
- metadata +51 -32
@@ -76,6 +76,7 @@ module Renalware
|
|
76
76
|
|
77
77
|
def for_element(name, &block)
|
78
78
|
return unless self.name == name and is_start?
|
79
|
+
|
79
80
|
self.instance_eval(&block)
|
80
81
|
end
|
81
82
|
|
@@ -88,6 +89,7 @@ module Renalware
|
|
88
89
|
|
89
90
|
@node.each do
|
90
91
|
return if self.name == name and is_end? and @node.depth == depth
|
92
|
+
|
91
93
|
self.instance_eval(&block)
|
92
94
|
end
|
93
95
|
end
|
@@ -20,6 +20,7 @@ module Renalware
|
|
20
20
|
|
21
21
|
def create_patient_statistics(sessions)
|
22
22
|
return unless sessions.any?
|
23
|
+
|
23
24
|
stats = build_patient_statistics
|
24
25
|
stats.hospital_unit = most_recently_used_hospital_unit(sessions)
|
25
26
|
stats.assign_attributes(auditable_sessions(sessions).to_h)
|
@@ -73,6 +74,7 @@ module Renalware
|
|
73
74
|
# }
|
74
75
|
def to_h
|
75
76
|
return {} if observation_set.blank?
|
77
|
+
|
76
78
|
observation_set.values.select { |code, _| CODES.include?(code.to_s) }
|
77
79
|
end
|
78
80
|
|
@@ -25,13 +25,14 @@ module Renalware
|
|
25
25
|
@search ||= begin
|
26
26
|
HD::Patient
|
27
27
|
.include(QueryablePatient)
|
28
|
+
.extending(PatientTransplantScopes)
|
28
29
|
.merge(Accesses::Patient.with_current_plan)
|
29
30
|
.merge(Accesses::Patient.with_profile)
|
30
|
-
.merge(Transplants::Patient.with_registration_statuses)
|
31
31
|
.eager_load(hd_profile: [:hospital_unit])
|
32
32
|
.extending(ModalityScopes)
|
33
33
|
.extending(PatientPathologyScopes)
|
34
34
|
.with_current_pathology
|
35
|
+
.with_registration_statuses
|
35
36
|
.with_current_modality_matching(MODALITY_NAMES)
|
36
37
|
.search(params)
|
37
38
|
end
|
@@ -59,10 +60,6 @@ module Renalware
|
|
59
60
|
ransacker :current_access, type: :string do
|
60
61
|
Arel.sql("access_types.name")
|
61
62
|
end
|
62
|
-
|
63
|
-
ransacker :transplant_registration_status do
|
64
|
-
Arel.sql("transplant_registration_status_descriptions.name")
|
65
|
-
end
|
66
63
|
end
|
67
64
|
end
|
68
65
|
end
|
@@ -12,6 +12,7 @@ module Renalware
|
|
12
12
|
def nullify_significant_hd_profile_attributes(patient, by:)
|
13
13
|
hd_profile = HD.cast_patient(patient).hd_profile
|
14
14
|
return if hd_profile.nil?
|
15
|
+
|
15
16
|
hd_profile.hospital_unit = nil
|
16
17
|
hd_profile.schedule_definition = nil
|
17
18
|
hd_profile.save_by!(by)
|
@@ -12,7 +12,7 @@ module Renalware
|
|
12
12
|
belongs_to :hospital_unit, class_name: "Hospitals::Unit"
|
13
13
|
belongs_to :schedule_definition, foreign_key: "schedule_definition_id"
|
14
14
|
|
15
|
-
has_paper_trail class_name: "Renalware::HD::Version"
|
15
|
+
has_paper_trail class_name: "Renalware::HD::Version", on: [:create, :update, :destroy]
|
16
16
|
|
17
17
|
validates :patient, presence: true
|
18
18
|
validates :entered_on, timeliness: { type: :date, allow_blank: true }
|
@@ -20,7 +20,7 @@ module Renalware
|
|
20
20
|
belongs_to :schedule_definition, foreign_key: "schedule_definition_id"
|
21
21
|
|
22
22
|
has_document class_name: "Renalware::HD::ProfileDocument"
|
23
|
-
has_paper_trail class_name: "Renalware::HD::Version"
|
23
|
+
has_paper_trail class_name: "Renalware::HD::Version", on: [:create, :update, :destroy]
|
24
24
|
|
25
25
|
validates :patient, presence: true
|
26
26
|
validates :prescriber, presence: true
|
@@ -5,6 +5,7 @@ module Renalware
|
|
5
5
|
class ReviseHDProfile
|
6
6
|
def initialize(profile)
|
7
7
|
raise(ArgumentError, "Cannot revise a new Profile") unless profile.persisted?
|
8
|
+
|
8
9
|
@profile = profile
|
9
10
|
end
|
10
11
|
|
@@ -12,8 +13,8 @@ module Renalware
|
|
12
13
|
profile.assign_attributes(params)
|
13
14
|
return true unless profile.changed?
|
14
15
|
return false unless profile.valid?
|
15
|
-
profile.restore_attributes
|
16
16
|
|
17
|
+
profile.restore_attributes
|
17
18
|
profile.supersede!(params)
|
18
19
|
end
|
19
20
|
|
@@ -29,7 +29,7 @@ module Renalware
|
|
29
29
|
dependent: :destroy
|
30
30
|
accepts_nested_attributes_for :prescription_administrations
|
31
31
|
|
32
|
-
has_paper_trail class_name: "Renalware::HD::Version"
|
32
|
+
has_paper_trail class_name: "Renalware::HD::Version", on: [:create, :update, :destroy]
|
33
33
|
|
34
34
|
before_create :assign_modality
|
35
35
|
before_save :compute_duration
|
@@ -54,6 +54,7 @@ module Renalware
|
|
54
54
|
|
55
55
|
def set_default_access(session) # rubocop:disable Naming/AccessorMethodName
|
56
56
|
return if dna_session?
|
57
|
+
|
57
58
|
accesses_patient = Renalware::Accesses.cast_patient(patient)
|
58
59
|
if (profile = accesses_patient.current_profile)
|
59
60
|
session.document.info.access_type = profile.type.name
|
@@ -63,6 +64,7 @@ module Renalware
|
|
63
64
|
|
64
65
|
def build_prescription_administrations(session)
|
65
66
|
return unless session.new_record?
|
67
|
+
|
66
68
|
patient.prescriptions.to_be_administered_on_hd.map do |prescription|
|
67
69
|
session.prescription_administrations.build(prescription: prescription)
|
68
70
|
end
|
@@ -30,6 +30,7 @@ module Renalware
|
|
30
30
|
|
31
31
|
def document
|
32
32
|
return SessionDocument.new if dna?
|
33
|
+
|
33
34
|
super || SessionDocument.new
|
34
35
|
end
|
35
36
|
|
@@ -42,6 +43,7 @@ module Renalware
|
|
42
43
|
|
43
44
|
def dialysis_minutes_shortfall_percentage
|
44
45
|
return 0.0 if dialysis_minutes_shortfall == 0
|
46
|
+
|
45
47
|
(dialysis_minutes_shortfall.to_f / prescribed_time.to_f) * 100.0
|
46
48
|
end
|
47
49
|
|
@@ -55,6 +57,7 @@ module Renalware
|
|
55
57
|
|
56
58
|
def weight_loss_as_percentage_of_body_weight
|
57
59
|
return unless measured_dry_weight > 0
|
60
|
+
|
58
61
|
(weight_loss / measured_dry_weight) * 100.0
|
59
62
|
end
|
60
63
|
|
@@ -41,6 +41,7 @@ module Renalware
|
|
41
41
|
def parse_params(params)
|
42
42
|
@session_type = params.delete(:type)
|
43
43
|
raise(ArgumentError, "Missing type in session params") if session_type.blank?
|
44
|
+
|
44
45
|
params
|
45
46
|
end
|
46
47
|
|
@@ -92,8 +93,10 @@ module Renalware
|
|
92
93
|
|
93
94
|
def lookup_access_type_abbreviation(session)
|
94
95
|
return unless session.document&.respond_to?(:info)
|
96
|
+
|
95
97
|
access_type = Accesses::Type.find_by(name: session.document.info.access_type)
|
96
98
|
return unless access_type
|
99
|
+
|
97
100
|
session.document.info.access_type_abbreviation = access_type.abbreviation
|
98
101
|
end
|
99
102
|
|
@@ -14,7 +14,7 @@ module Renalware
|
|
14
14
|
validates :other_description, presence: true, if: -> { unspecified_description? }
|
15
15
|
|
16
16
|
delegate :salutation, to: :person
|
17
|
-
delegate :address, :to_s, :family_name, to: :person
|
17
|
+
delegate :address, :to_s, :family_name, :full_name, to: :person
|
18
18
|
delegate :name, to: :address, prefix: true
|
19
19
|
|
20
20
|
accepts_nested_attributes_for :person
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
module Renalware
|
4
4
|
module Letters
|
5
|
+
# Note we cannot use a partial layout with render_to_string as it will always expect
|
6
|
+
# the location to be in views/layouts and its not possible in Rails 5.1 to specify
|
7
|
+
# another or absolute path
|
5
8
|
class HTMLRenderer
|
6
9
|
def call(letter)
|
7
10
|
context = LettersController.new
|
@@ -56,7 +56,7 @@ module Renalware
|
|
56
56
|
}
|
57
57
|
singleton_class.send(:alias_method, :in_progress, :pending)
|
58
58
|
|
59
|
-
scope :
|
59
|
+
scope :reversed, -> { order("#{effective_date_sort} asc") }
|
60
60
|
scope :ordered, -> { order("#{effective_date_sort} desc") }
|
61
61
|
scope :with_letterhead, -> { includes(:letterhead) }
|
62
62
|
scope :with_main_recipient, -> { includes(main_recipient: [:address, :addressee]) }
|
@@ -44,6 +44,7 @@ module Renalware
|
|
44
44
|
# practice address but the GP's name as the salutation.
|
45
45
|
def include_primary_care_physician_as_default_main_recipient
|
46
46
|
return if letter.main_recipient.present?
|
47
|
+
|
47
48
|
if patient.primary_care_physician.present? && patient.practice_id.present?
|
48
49
|
letter.build_main_recipient(person_role: :primary_care_physician)
|
49
50
|
else
|
@@ -68,6 +69,7 @@ module Renalware
|
|
68
69
|
# the parent letter is saved.
|
69
70
|
def build_electronic_ccs
|
70
71
|
return if electronic_cc_recipient_ids.blank?
|
72
|
+
|
71
73
|
electronic_cc_recipient_ids.reject(&:blank?).map do |user_id|
|
72
74
|
letter.electronic_receipts.build(recipient_id: user_id)
|
73
75
|
end
|
@@ -18,6 +18,7 @@ module Renalware
|
|
18
18
|
@results ||= begin
|
19
19
|
snapshot = letter.pathology_snapshot.dup
|
20
20
|
return if snapshot.blank?
|
21
|
+
|
21
22
|
groups = group_snapshot_by_code_and_date(snapshot)
|
22
23
|
format_groups_into_string(groups)
|
23
24
|
end
|
@@ -47,6 +48,7 @@ module Renalware
|
|
47
48
|
match = snapshot[obs_desc.code.to_sym]
|
48
49
|
next if match.nil?
|
49
50
|
next if match[:observed_at].nil?
|
51
|
+
|
50
52
|
date = I18n.l(Date.parse(match[:observed_at]))
|
51
53
|
dates[date] ||= {}
|
52
54
|
dates[date][obs_desc.code] = match[:result]
|
@@ -75,6 +77,7 @@ module Renalware
|
|
75
77
|
|
76
78
|
def format_code_and_result_string(code, result)
|
77
79
|
return "(#{code} #{result})" if code.casecmp?("EGFR")
|
80
|
+
|
78
81
|
"#{code} #{result}"
|
79
82
|
end
|
80
83
|
end
|
@@ -39,8 +39,8 @@ module Renalware
|
|
39
39
|
class << self
|
40
40
|
delegate :clear, to: :store
|
41
41
|
|
42
|
-
def fetch(letter)
|
43
|
-
store.fetch(cache_key_for(letter)) { yield }
|
42
|
+
def fetch(letter, **options)
|
43
|
+
store.fetch(cache_key_for(letter, **options)) { yield }
|
44
44
|
end
|
45
45
|
|
46
46
|
# Note the letter must be a LetterPresenter which has a #to_html method
|
@@ -48,10 +48,11 @@ module Renalware
|
|
48
48
|
# html including surrounding layout with inline css and images. This way if the
|
49
49
|
# layout changes or the image is changed for example, the cache for the pdf is no longer
|
50
50
|
# valid and a new key and cache entry will be created.
|
51
|
-
def cache_key_for(letter)
|
51
|
+
def cache_key_for(letter, **options)
|
52
52
|
timestamp = letter&.updated_at&.strftime("%Y%m%d%H%M%S")
|
53
53
|
pat_id = letter.patient.id
|
54
|
-
"letter-pdf-#{letter.id}-#{pat_id}-#{timestamp}
|
54
|
+
"letter-pdf-#{letter.id}-#{pat_id}-#{timestamp}-" \
|
55
|
+
"#{Digest::MD5.hexdigest(letter.to_html(**options))}"
|
55
56
|
end
|
56
57
|
|
57
58
|
def cache_path
|
@@ -17,12 +17,12 @@ module Renalware
|
|
17
17
|
encoding: "UTF-8"
|
18
18
|
}.freeze
|
19
19
|
|
20
|
-
def self.call(letter)
|
20
|
+
def self.call(letter, **options)
|
21
21
|
unless letter.respond_to?(:to_html)
|
22
22
|
letter = LetterPresenterFactory.new(letter)
|
23
23
|
end
|
24
|
-
PdfLetterCache.fetch(letter) do
|
25
|
-
WickedPdf.new.pdf_from_string(letter.to_html, OPTIONS)
|
24
|
+
PdfLetterCache.fetch(letter, **options) do
|
25
|
+
WickedPdf.new.pdf_from_string(letter.to_html(**options), OPTIONS)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/letters"
|
4
|
+
require "attr_extras"
|
5
|
+
|
6
|
+
module Renalware
|
7
|
+
module Letters
|
8
|
+
module Printing
|
9
|
+
# Build PDF for printing. We are targetting an envelope stuffer so there will be an address
|
10
|
+
# cover sheet in front of each letter.
|
11
|
+
# So for a letter where the patient is the main recipient, and we CC to the GP (no practice
|
12
|
+
# email address so snail mailing) and 1 extra CC (a contact) the output will look like
|
13
|
+
# this:
|
14
|
+
#
|
15
|
+
# 1. Patient address cover sheet
|
16
|
+
# 2. Letter
|
17
|
+
# 3. GP address cover sheet
|
18
|
+
# 2. Letter
|
19
|
+
# 5. Contact address cover sheet
|
20
|
+
# 2. Letter
|
21
|
+
#
|
22
|
+
# Note however the we always pad each item (address sheet, letter) with blank pages to make
|
23
|
+
# sure duplex printing does not cause the next page recipient's content to be rendered on the
|
24
|
+
# back of the ast page of the previous letter for example/
|
25
|
+
#
|
26
|
+
# Compiles print content for all input letters and outputs the merged PDF to output_file.
|
27
|
+
# Allows batch printing if you want to print say all 2 page letters together.
|
28
|
+
# The caller may choose for example to send the output_file could be sent back to the browser
|
29
|
+
# for manual printing using (eg using send_file) or copy the merged PDF to a folder for
|
30
|
+
# automated printing.
|
31
|
+
class CreatePdfByInterleavingAddressSheetAndLetterForEachRecipient
|
32
|
+
pattr_initialize [:letters!, :output_file!]
|
33
|
+
|
34
|
+
def call
|
35
|
+
# There is a choice of two methods of building the merged PDF. See pros and cons.
|
36
|
+
# The other is UsingSeparatePdfForEachLetterSection.
|
37
|
+
UsingOnePdfForAllLetterSections.new(
|
38
|
+
letters: letters,
|
39
|
+
output_file: output_file
|
40
|
+
).call
|
41
|
+
end
|
42
|
+
|
43
|
+
# For each letter, uses whkhtmltopdf to build the entire print content, ie all address
|
44
|
+
# sheets and required instance of the letter are created as one fresh pdf (no cache will be
|
45
|
+
# used).
|
46
|
+
#
|
47
|
+
# Pros:
|
48
|
+
# - Quicker: For each letter only one PDF is generated, and although the generation is slow
|
49
|
+
# as it does not use the previously cached letter PDF, and it has to render all the letter
|
50
|
+
# sections, including the letter itself, for every recipient, its stil faster then
|
51
|
+
# rendering separate PDFs fo each section.
|
52
|
+
# Cons:
|
53
|
+
# - wastes some resources because it does not use the cache and renders same letter
|
54
|
+
# multiple times in the views
|
55
|
+
# - cannot be 100% sure the number of pages in the final PDF will be extactly what we
|
56
|
+
# predict, so requires a check to load the pdf into a reader and validate the page_count
|
57
|
+
class UsingOnePdfForAllLetterSections
|
58
|
+
include PdfCombining
|
59
|
+
pattr_initialize [:letters!, :output_file!]
|
60
|
+
|
61
|
+
def call
|
62
|
+
in_a_temporary_folder do |dir|
|
63
|
+
Array(letters).each do |letter|
|
64
|
+
if letter.page_count.to_i < 1
|
65
|
+
raise(ArgumentError, "letter.page_count not set on letter.id=#{letter.id}")
|
66
|
+
end
|
67
|
+
letter_filename = create_letter_pdf_in(dir, letter)
|
68
|
+
files << letter_filename
|
69
|
+
end
|
70
|
+
combine_multiple_pdfs_into_one(dir, output_file)
|
71
|
+
# For debuging:
|
72
|
+
# FileUtils.cp output_file, "/Users/tim/Desktop/x.pdf"
|
73
|
+
# `open /Users/tim/Desktop/x.pdf`
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def create_letter_pdf_in(dir, letter)
|
80
|
+
filename = "letter_#{letter.id}.pdf"
|
81
|
+
path = dir.join(filename)
|
82
|
+
File.open(path, "wb") { |file| file.write(DuplexInterleavedPdfRenderer.call(letter)) }
|
83
|
+
filename
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# This approach generates a PDF for each address cover sheet and each
|
88
|
+
# letter. It shells to ghostscript to merge the PDFs together in
|
89
|
+
# the right order.
|
90
|
+
#
|
91
|
+
# Pros:
|
92
|
+
# - uses the letter PDF already in the cache from when we rendered it to deduce the page
|
93
|
+
# count after the letter was archived
|
94
|
+
# - uses very little memory as PDF concatenation done on disk
|
95
|
+
# - concatentation itslef reasonable fast
|
96
|
+
#
|
97
|
+
# Cons:
|
98
|
+
# - slow because it shells to wkhtmltopdf for each cover sheet, and these are unklikely to
|
99
|
+
# be found in tha cache. So for main recip and 2 CCs it wil call wkhtmltopdf 3 times to
|
100
|
+
# render the address cover sheets, and the letter PDF should be pulled from cache so that
|
101
|
+
# is quick at least.
|
102
|
+
#
|
103
|
+
class UsingSeparatePdfForEachLetterSection
|
104
|
+
include PdfCombining
|
105
|
+
pattr_initialize [:letters!, :output_file!]
|
106
|
+
|
107
|
+
def call
|
108
|
+
in_a_temporary_folder do |dir|
|
109
|
+
Array(letters).each do |letter|
|
110
|
+
letter_filename = create_letter_pdf_in(dir, letter)
|
111
|
+
PrintableRecipients.for(letter).each do |recipient|
|
112
|
+
files << create_cover_sheet_for(recipient, letter, dir)
|
113
|
+
files << letter_filename
|
114
|
+
end
|
115
|
+
end
|
116
|
+
combine_multiple_pdfs_into_one(dir, output_file)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def create_letter_pdf_in(dir, letter)
|
123
|
+
filename = "letter_#{letter.id}.pdf"
|
124
|
+
path = dir.join(filename)
|
125
|
+
File.open(path, "wb") { |file| file.write(PdfRenderer.call(letter)) }
|
126
|
+
filename
|
127
|
+
end
|
128
|
+
|
129
|
+
def create_cover_sheet_for(recipient, _letter, dir)
|
130
|
+
filename = "recipient_#{recipient.id}.pdf"
|
131
|
+
path = dir.join(filename)
|
132
|
+
File.open(path, "wb") { |file| file.write(RecipientAddressPdfRenderer.call(recipient)) }
|
133
|
+
filename
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|