renalware-core 2.0.146 → 2.0.147

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/renalware/charting.js +1 -1
  3. data/app/assets/javascripts/renalware/rollup_compiled.js +87 -0
  4. data/app/assets/stylesheets/renalware/partials/_forms.scss +5 -0
  5. data/app/components/renalware/system/admin_menu_component.html.slim +1 -0
  6. data/app/controllers/renalware/admin/playgrounds_controller.rb +65 -0
  7. data/app/controllers/renalware/deaths_controller.rb +6 -5
  8. data/app/controllers/renalware/medications/home_delivery/events_controller.rb +0 -3
  9. data/app/controllers/renalware/medications/home_delivery/prescriptions_controller.rb +43 -0
  10. data/app/controllers/renalware/patients/primary_care_physician_controller.rb +6 -0
  11. data/app/javascript/renalware/controllers/charts_controller.js +52 -0
  12. data/app/javascript/renalware/controllers/medications/prescriptions_controller.js +24 -0
  13. data/app/javascript/renalware/index.js +6 -0
  14. data/app/models/renalware/medications.rb +2 -0
  15. data/app/models/renalware/medications/delivery/prescriptions_due_for_delivery_query.rb +53 -0
  16. data/app/models/renalware/patient.rb +4 -0
  17. data/app/models/renalware/patients/deceased_patients_query.rb +42 -0
  18. data/app/models/renalware/ukrdc/create_encrypted_patient_xml_files.rb +44 -16
  19. data/app/models/renalware/ukrdc/create_patient_xml_file.rb +1 -1
  20. data/app/views/renalware/accesses/assessments/_form.html.slim +2 -2
  21. data/app/views/renalware/accesses/plans/new.html.slim +1 -1
  22. data/app/views/renalware/accesses/procedures/_form.html.slim +2 -2
  23. data/app/views/renalware/accesses/profiles/_form.html.slim +2 -2
  24. data/app/views/renalware/admin/playgrounds/pathology_chart_data.js.erb +1 -0
  25. data/app/views/renalware/admin/playgrounds/show.html.slim +59 -0
  26. data/app/views/renalware/admin/users/_filters.html.slim +1 -1
  27. data/app/views/renalware/admissions/admissions/index.html.slim +2 -2
  28. data/app/views/renalware/admissions/consults/_form.html.slim +1 -1
  29. data/app/views/renalware/admissions/consults/index.html.slim +2 -2
  30. data/app/views/renalware/deaths/index.html.slim +18 -10
  31. data/app/views/renalware/events/events/toggled_cell/_biopsy.html.slim +1 -1
  32. data/app/views/renalware/events/events/toggled_cell/_simple.html.slim +1 -1
  33. data/app/views/renalware/events/events/toggled_cell/_swab.html.slim +1 -1
  34. data/app/views/renalware/hd/transmission_logs/index.html.slim +1 -2
  35. data/app/views/renalware/letters/mailshots/mailshots/_form.html.slim +2 -1
  36. data/app/views/renalware/mdm_patients/_patient.html.slim +1 -2
  37. data/app/views/renalware/medications/drug_types/prescriptions/_filters.html.slim +1 -3
  38. data/app/views/renalware/medications/home_delivery/prescriptions/index.html.slim +51 -0
  39. data/app/views/renalware/medications/prescriptions/_filter_form.html.slim +1 -1
  40. data/app/views/renalware/medications/prescriptions/_form.html.slim +20 -12
  41. data/app/views/renalware/navigation/_renal.html.slim +2 -0
  42. data/app/views/renalware/patients/_header.html.slim +1 -1
  43. data/app/views/renalware/patients/alerts/_alert.html.slim +1 -1
  44. data/app/views/renalware/patients/alerts/_form.html.slim +1 -1
  45. data/app/views/renalware/patients/patients/_form.html.slim +1 -2
  46. data/app/views/renalware/patients/patients/new.html.slim +6 -5
  47. data/app/views/renalware/patients/patients/show/_primary_care_physician.html.slim +9 -1
  48. data/app/views/renalware/patients/primary_care_physician/destroy.js.erb +1 -0
  49. data/app/views/renalware/pd/dashboards/show/_page_actions.html.slim +2 -2
  50. data/app/views/renalware/pd/exit_site_infections/show.html.slim +1 -1
  51. data/app/views/renalware/pd/peritonitis_episodes/show.html.slim +1 -1
  52. data/app/views/renalware/reporting/audits/edit.html.slim +1 -3
  53. data/app/views/renalware/research/_alert.html.slim +1 -1
  54. data/app/views/renalware/snippets/snippets/_row.html.slim +3 -4
  55. data/app/views/renalware/system/downloads/index.html.slim +2 -3
  56. data/app/views/renalware/transplants/live_donors/_filters.html.slim +2 -2
  57. data/config/initializers/groupdate.rb +3 -0
  58. data/config/locales/renalware/medications/prescription.yml +2 -1
  59. data/config/locales/renalware/navigation/renal.en.yml +1 -0
  60. data/config/routes/admin.rb +5 -0
  61. data/config/routes/medications.rb +5 -0
  62. data/config/routes/patients.rb +1 -1
  63. data/db/functions/pathology_chart_data_v01.sql +13 -0
  64. data/db/migrate/20200313120655_create_pathology_chart_data_function.rb +13 -0
  65. data/lib/renalware/engine.rb +1 -0
  66. data/lib/renalware/version.rb +1 -1
  67. data/spec/support/pages/medications/prescription_fom.rb +2 -2
  68. data/vendor/assets/javascripts/renalware/highcharts.js +506 -0
  69. metadata +32 -5
  70. data/app/views/renalware/medications/home_delivery/prescriptions/index.pdf.slim +0 -103
@@ -73,6 +73,10 @@ module Renalware
73
73
  -> { order(started_on: :desc).where(state: "current") },
74
74
  class_name: "Modalities::Modality"
75
75
 
76
+ has_one :previous_modality,
77
+ -> { order(ended_on: :desc, created_at: :desc).where.not(ended_on: nil) },
78
+ class_name: "Modalities::Modality"
79
+
76
80
  has_one :modality_description,
77
81
  through: :current_modality,
78
82
  class_name: "Modalities::Description",
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module Patients
5
+ # Finds patients with a death modality or who have a died_on date
6
+ class DeceasedPatientsQuery
7
+ attr_reader :query_params
8
+
9
+ def initialize(query_params)
10
+ @query_params = query_params || {}
11
+ @query_params[:s] = "family_name ASC" if @query_params[:s].blank?
12
+ end
13
+
14
+ def call
15
+ search.result.where(id: ids_of_deceased_patients)
16
+ end
17
+
18
+ def search
19
+ Patient
20
+ .includes(:first_cause, current_modality: :description)
21
+ .ransack(query_params)
22
+ end
23
+
24
+ private
25
+
26
+ def ids_of_deceased_patients
27
+ (ids_of_patients_with_death_modality + ids_of_patients_with_died_on_date).uniq
28
+ end
29
+
30
+ def ids_of_patients_with_death_modality
31
+ Patient
32
+ .extending(ModalityScopes)
33
+ .with_current_modality_of_class(Renalware::Deaths::ModalityDescription)
34
+ .pluck(:id)
35
+ end
36
+
37
+ def ids_of_patients_with_died_on_date
38
+ Patient.where.not(died_on: nil).pluck(:id)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -66,31 +66,59 @@ module Renalware
66
66
  end
67
67
  end
68
68
 
69
- # rubocop:disable Metrics/MethodLength
69
+ # If UKRDC_THREADS is set to a nonzero value (eg 4 but depending on CPUs/cores),
70
+ # a threadpool containing that number of threads is created and XML creation
71
+ # (including PDF letter rendering) for that patient happens in a separate thread.
72
+ # The performance increase in doing it this way is marginal unless there are
73
+ # PDFs to be rendered, which can introduce significant IO wait. As PDFs are cached
74
+ # by the PDFLetterCache, using threads may have limited benefit but
75
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
70
76
  def create_patient_xml_files
71
77
  count = 0
72
78
  patients = ukrdc_patients_who_have_changed_since_last_send
73
79
  summary.num_changed_patients = patients.count
74
80
  schema = UKRDC::XsdSchema.new
75
- patients.find_each do |patient|
81
+ thread_count = ENV["UKRDC_THREADS"].to_i
82
+ use_threads = thread_count.nonzero?
83
+
84
+ if use_threads
85
+ thread_pool = Concurrent::ThreadPoolExecutor.new(max_threads: thread_count)
86
+ end
87
+
88
+ patients.map do |patient|
76
89
  count += 1
77
90
  Rails.logger.info count
78
- CreatePatientXMLFile.new(
79
- patient: patient,
80
- dir: paths.timestamped_xml_folder,
81
- changes_since: changed_since,
82
- request_uuid: request_uuid,
83
- batch_number: batch_number,
84
- logger: logger,
85
- force_send: force_send,
86
- schema: schema
87
- ).call
88
-
89
- # Every n patients, force the garbage collector to kick in
90
- # GC.start if (count % 10).zero?
91
+ if use_threads
92
+ thread_pool.post do
93
+ Rails.logger.info "Thread #{Thread.current.object_id}"
94
+ ActiveRecord::Base.connection_pool.with_connection do
95
+ create_xml_file(patient: patient, schema: schema)
96
+ end
97
+ end
98
+ else
99
+ create_xml_file(patient: patient, schema: schema)
100
+ end
101
+ end
102
+ ensure
103
+ if use_threads
104
+ thread_pool.shutdown
105
+ thread_pool.wait_for_termination
91
106
  end
92
107
  end
93
- # rubocop:enable Metrics/MethodLength
108
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
109
+
110
+ def create_xml_file(patient:, schema:)
111
+ CreatePatientXMLFile.new(
112
+ patient: patient,
113
+ dir: paths.timestamped_xml_folder,
114
+ changes_since: changed_since,
115
+ request_uuid: request_uuid,
116
+ batch_number: batch_number,
117
+ logger: logger,
118
+ force_send: force_send,
119
+ schema: schema
120
+ ).call
121
+ end
94
122
 
95
123
  def build_summary
96
124
  summary.count_of_files_in_outgoing_folder = count_of_files_in_outgoing_folder
@@ -86,7 +86,7 @@ module Renalware
86
86
  nil
87
87
  else
88
88
  Payload.new(result.xml).tap do |payload|
89
- log.payload = payload.to_s
89
+ # log.payload = payload.to_s
90
90
  log.payload_hash = payload.to_md5_hash
91
91
  end
92
92
  end
@@ -4,7 +4,7 @@ ruby:
4
4
  .row.top
5
5
  .medium-6.columns
6
6
  = f.button :submit
7
- span= " or "
7
+ ' or
8
8
  = link_to "cancel", back_path
9
9
 
10
10
  .form-content
@@ -39,5 +39,5 @@ ruby:
39
39
  .row
40
40
  .large-12.columns
41
41
  = f.button :submit
42
- span= " or "
42
+ ' or
43
43
  = link_to "cancel", back_path
@@ -18,5 +18,5 @@
18
18
  wrapper: :horizontal_medium
19
19
 
20
20
  = f.submit "Save", class: "button"
21
- span= " or "
21
+ ' or
22
22
  = link_to "cancel", patient_accesses_dashboard_path(patient)
@@ -4,7 +4,7 @@ ruby:
4
4
  .row.top
5
5
  .medium-6.columns
6
6
  = f.button :submit
7
- span= " or "
7
+ ' or
8
8
  = link_to "cancel", back_path
9
9
 
10
10
  .form-content
@@ -41,5 +41,5 @@ ruby:
41
41
  .row
42
42
  .large-12.columns
43
43
  = f.button :submit
44
- span= " or "
44
+ ' or
45
45
  = link_to "cancel", back_path
@@ -4,7 +4,7 @@ ruby:
4
4
  .row.top
5
5
  .medium-6.columns
6
6
  = f.button :submit
7
- span= " or "
7
+ ' or
8
8
  = link_to "cancel", back_path
9
9
 
10
10
  .form-content
@@ -31,5 +31,5 @@ ruby:
31
31
  .row
32
32
  .large-12.columns
33
33
  = f.button :submit
34
- span= " or "
34
+ ' or
35
35
  = link_to "cancel", back_path
@@ -0,0 +1,59 @@
1
+ css:
2
+ .chart {
3
+ width: 500px;
4
+ height: 200px;
5
+ }
6
+ #patient_id {
7
+ width:300px;
8
+ display: block
9
+ }
10
+ #patient_id input {
11
+ width:200px;
12
+ display:inline;
13
+ }
14
+
15
+ #patient_id a {
16
+ width: 90px;
17
+ display:inline;
18
+ padding: 5px;
19
+ margin-left: 5px;
20
+ }
21
+
22
+ = within_new_admin_layout(title: "Admin Playground") do
23
+
24
+ article
25
+ header
26
+ h2 Pathology Charts
27
+ span A testbed for trying out highchart linecharts
28
+ br
29
+ br
30
+
31
+ .chart-container(data-controller="charts"
32
+ data-charts-url=pathology_chart_data_admin_playground_path)
33
+ .row
34
+ .columns.small-12.medium-6.large-4
35
+ = simple_form_for(form,
36
+ remote: true,
37
+ as: :chart,
38
+ url: pathology_chart_data_admin_playground_path,
39
+ wrapper: nil,
40
+ method: :get,
41
+ data: { target: "charts.form", action: "ajax:success->charts#redisplay" }) do |f|
42
+
43
+ = f.input :patient_id,
44
+ collection: [[form.patient_id, form.patient_id]],
45
+ selected: 1,
46
+ input_html: { \
47
+ class: "patient-id-select2 patient-ajax-search",
48
+ data: { "ajax--url" => search_patients_path(format: :json),
49
+ placeholder: "Search by patient name or hospital/NHS no." } \
50
+ }
51
+ = f.input :obx_code,
52
+ collection: Renalware::Pathology::ObservationDescription.order(:code).pluck(:code),
53
+ input_html: { class: "searchable_select" }
54
+ = f.input :period,
55
+ collection: %w(all yr 6m 3m 1m wk),
56
+ include_blank: false
57
+ = f.submit "Refresh", class: :button
58
+
59
+ #chart1 style="height: 300px; width: 500px" data-target="charts.chart"
@@ -7,5 +7,5 @@
7
7
  = f.search_field :family_name_or_given_name_or_username_or_email_cont
8
8
  .small-3.large-4.columns.actions.end
9
9
  = f.submit t("helpers.submit.filter"), class: "button"
10
- span= " or "
10
+ ' or
11
11
  = link_to t("helpers.reset"), admin_users_path
@@ -6,9 +6,9 @@
6
6
 
7
7
  = within_admin_layout(title: "Inpatient Admissions") do
8
8
  .panel.radius.compact
9
- | If the patient is not on Renalware yet, please 
9
+ ' If the patient is not on Renalware yet, please
10
10
  = link_to("add them", new_patient_path)
11
- |  first
11
+ | first
12
12
 
13
13
  = render "table", admissions: admissions
14
14
  = paginate admissions
@@ -53,5 +53,5 @@
53
53
  = f.input :description, wrapper: :horizontal_large, input_html: { rows: 10 }
54
54
 
55
55
  = f.submit class: :button
56
- span= " or "
56
+ ' or
57
57
  = link_to "Cancel", admissions_consults_path
@@ -15,9 +15,9 @@
15
15
  = render "renalware/admissions/requests/modal_dialog_placeholder"
16
16
 
17
17
  .panel.radius.compact
18
- | If the patient is not on Renalware yet, please 
18
+ ' If the patient is not on Renalware yet, please
19
19
  = link_to("add them", new_patient_path)
20
- |  first
20
+ | first
21
21
 
22
22
  = render "table", consults: consults, query: query
23
23
  = paginate consults
@@ -9,28 +9,36 @@
9
9
  table#patients-deceased
10
10
  thead
11
11
  tr
12
- th Name
12
+ th
13
+ th.col-width-mediumish= sort_link([:renalware, q], :family_name, "Name")
13
14
  th.col-width-nhs-no NHS No.
14
15
  th.col-width-reference-no Hosp No.
15
16
  th.col-width-tiny Sex
16
- th.col-width-date= sort_link([:renalware, q], :born_on, "Date of Birth")
17
- th.col-width-date-time= sort_link([:renalware, q], :died_on, "Date of Death")
18
17
  th.col-width-tiny Age
19
- th.col-width-medium
18
+ th.col-width-date= sort_link([:renalware, q], :born_on, "Date of Birth")
19
+ th.col-width-date= sort_link([:renalware, q], :died_on, "Date of Death")
20
+ th First cause
21
+ th Current Modality
22
+ th Previous Modality
20
23
 
21
24
  tbody
22
25
  - patients.each do |patient|
23
26
  tr
27
+ td.actions
28
+ = link_to edit_patient_death_path(patient),
29
+ title: "Edit death details" do
30
+ i.fas.fa-edit
31
+ span.show-for-large-up
32
+ |  Death details
24
33
  td= default_patient_link(patient)
25
34
  td= patient.nhs_number
26
35
  td= patient.hospital_identifier
27
36
  td= patient.sex
37
+ td= patient.age
28
38
  td= l patient.born_on
29
39
  td= l patient.died_on
30
- td= patient.age
31
- td.actions
32
- = link_to "Edit death details",
33
- edit_patient_death_path(patient),
34
- title: "Edit death details"
40
+ td= patient.first_cause&.description
41
+ td= patient.current_modality
42
+ td= patient.previous_modality
35
43
 
36
- = paginate patients
44
+ == pagy_foundation_nav pagy
@@ -6,4 +6,4 @@ dl.dl-horizontal
6
6
  dt= attr_name(document, :result2, suffix: ":")
7
7
  dd= document.result2&.text || t("unspecified")
8
8
  dt= attr_name(event, :notes, suffix: ":")
9
- dd= event.notes&.html_safe
9
+ dd== event.notes
@@ -2,4 +2,4 @@ dl.dl-horizontal
2
2
  dt= attr_name(event, :description, suffix: ":")
3
3
  dd= event.description
4
4
  dt= attr_name(event, :notes, suffix: ":")
5
- dd= event.notes&.html_safe
5
+ dd== event.notes
@@ -3,4 +3,4 @@ dl.dl-horizontal
3
3
  dt= attr_name(document, :location, suffix: ":")
4
4
  dd= document.location.present? ? document.location : t("unspecified")
5
5
  dt= attr_name(event, :notes, suffix: ":")
6
- dd= event.notes&.html_safe
6
+ dd== event.notes
@@ -36,8 +36,7 @@
36
36
  - filename = log.filepath && Pathname(log.filepath).basename
37
37
  - if filename.present?
38
38
  i.fas.fa-file
39
- |  
40
- = filename
39
+ => filename
41
40
  td= log.patient && link_to(log.patient, patient_hd_sessions_path(log.patient))
42
41
  td
43
42
  - if log.parent_id.present?
@@ -4,7 +4,8 @@
4
4
  url: letters_mailshots_path,
5
5
  html: { id: "mailshot-form" }) do |form|
6
6
  .columns.small-12
7
- = form.input :description, placeholder: "A description of the mailshot purpose."
7
+ = form.input(:description,
8
+ placeholder: "The patient will see this at the top of letter. A description of the letter's purpose.")
8
9
  .columns.small-12
9
10
  = form.input(:sql_view_name,
10
11
  collection: Renalware::Letters::Mailshots::DataSource.all.map(&:viewname),
@@ -4,8 +4,7 @@ tr
4
4
  td.actions
5
5
  = link_to view_proc.call(patient), target: "_blank" do
6
6
  i.fa.fa-external-link-square-alt
7
- |   
8
- = t(".view")
7
+ =< t(".view")
9
8
  td.full-name= link_to patient.to_s(:default), view_proc.call(patient)
10
9
  td.nowrap= patient.nhs_number
11
10
  td.nowrap= patient.hospital_identifier
@@ -18,8 +18,6 @@
18
18
  label: "Provider"
19
19
  .small-3.columns.actions.end
20
20
  = f.submit "Search", class: "button"
21
- | &nbsp;
22
- span or
23
- | &nbsp;
21
+ ' or
24
22
  = link_to "Reset", medications_esa_prescriptions_path
25
23
  /class: "cancel-link #{'disabled' unless searching}"
@@ -0,0 +1,51 @@
1
+ - content_for(:filters) do
2
+ .panel
3
+ | Prescriptions with a next home prescription date within the last 4 weeks or the next 2 weeks.
4
+
5
+ .row
6
+ .columns.small-12.medium-6
7
+ = simple_form_for(form,
8
+ as: :search,
9
+ url: renalware.medications_home_delivery_prescriptions_path(named_filter: params[:named_filter]),
10
+ method: :get) do |f|
11
+ = f.input :modality_description_id,
12
+ collection: Renalware::Modalities::Description.order(:name),
13
+ input_html: { onchange: "this.form.submit();" }
14
+ br
15
+
16
+ - content_for(:tabs) do
17
+ dl.sub-nav
18
+ dd
19
+ = link_to "ESA",
20
+ renalware.medications_home_delivery_prescriptions_path(named_filter: "esa")
21
+ dd
22
+ = link_to "Immunosuppressant",
23
+ renalware.medications_home_delivery_prescriptions_path(named_filter: "immunosuppressant")
24
+
25
+ = within_admin_layout(title: "Home Delivery Prescriptions") do
26
+ table
27
+ thead
28
+ tr
29
+ th.col-width-large= sort_link(query, :patient_family_name, "Patient")
30
+ th.col-width-nhs-no NHS number
31
+ th.col-width-medium Drug Type
32
+ th.col-width-large= sort_link(query, :drug_name, "Drug")
33
+ th.col-width-date= sort_link(query, :prescribed_on, "Prescribed on")
34
+ th.col-width-date-time= sort_link(query, :last_delivery_date, "Last home prescription date")
35
+ th.col-width-date-time= sort_link(query, :next_delivery_date, "Next home prescription date")
36
+ th.col-width-medium Modality
37
+ th
38
+ tbody
39
+ - prescriptions.each do |prescription|
40
+ tr
41
+ td= default_patient_link(prescription.patient)
42
+ td= prescription.patient.nhs_number
43
+ td= prescription.drug.drug_types.first&.name
44
+ td= prescription.drug.name
45
+ td= l(prescription.prescribed_on)
46
+ td= l(prescription.last_delivery_date)
47
+ td= l(prescription.next_delivery_date)
48
+ td= prescription.patient.current_modality
49
+ td
50
+
51
+ == pagy_foundation_nav pagy