renalware-core 2.0.110 → 2.0.111

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.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/renalware/medications_pdf.scss +6 -0
  3. data/app/assets/stylesheets/renalware/protocol_pdf.scss +4 -0
  4. data/app/controllers/renalware/admin/users_controller.rb +1 -1
  5. data/app/controllers/renalware/base_controller.rb +13 -1
  6. data/app/controllers/renalware/patients/patients_controller.rb +5 -1
  7. data/app/jobs/renalware/hd/update_rolling_patient_statistics_dj_job.rb +1 -1
  8. data/app/jobs/renalware/letters/calculate_page_count_job.rb +1 -1
  9. data/app/models/renalware/feeds.rb +2 -0
  10. data/app/models/renalware/feeds/message_processor.rb +2 -0
  11. data/app/models/renalware/feeds/persist_message.rb +6 -0
  12. data/app/policies/renalware/user_policy.rb +11 -0
  13. data/app/presenters/renalware/hd/protocol_presenter.rb +4 -2
  14. data/app/presenters/renalware/mdm_presenter.rb +3 -3
  15. data/app/presenters/renalware/transplants/mdm_presenter.rb +1 -1
  16. data/app/views/renalware/admin/users/edit.html.slim +3 -2
  17. data/app/views/renalware/api/ukrdc/patients/_clinic_visit_observation.xml.builder +4 -1
  18. data/app/views/renalware/api/ukrdc/patients/_hd_session_observations.xml.builder +4 -1
  19. data/app/views/renalware/hd/protocols/_profile.html.slim +2 -0
  20. data/app/views/renalware/hd/protocols/_recent_pathology.html.slim +3 -3
  21. data/app/views/renalware/hd/sessions/_empty_row.html.slim +6 -2
  22. data/app/views/renalware/hd/sessions/_row.html.slim +16 -16
  23. data/app/views/renalware/medications/home_delivery/prescriptions/index.pdf.slim +5 -1
  24. data/config/locales/renalware/hd/protocol.en.yml +1 -0
  25. data/lib/renalware/configuration.rb +2 -2
  26. data/lib/renalware/version.rb +1 -1
  27. metadata +8 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d299c658864be00fd2bb5e32766ca4bb07f92e4d1e3cac36536bbb351dc13132
4
- data.tar.gz: d405e456e7f324d56d2d9b10cd4643dd7d20721967d1126461d4f287150b83e1
3
+ metadata.gz: fcb6fe2a9d36fece24e482f2cf318d89af11914dab2beb7b346bf3a6cba9c8b8
4
+ data.tar.gz: ecde69203a3b74dacc87163bb1b19bf103eb79b2a7e34742fe824d6a53e62f7d
5
5
  SHA512:
6
- metadata.gz: 22d08baf9693562abd1048245c849a2015d1210594b1f7821f78820a626ac58b21442f742c702add9798b4dde1ea33e2b423703dee6e94622aa340c0ce4a689d
7
- data.tar.gz: fcb3ddb0e16d5bc177dace948ea795659523d196086967a0743b0a98843561b325bcc9b148b2f31123324a00d9817c13ca4575d4f5bc096cab95fc0469b23544
6
+ metadata.gz: 878519c049773af3fe86eb79f5a20274b470d6cbc4c132c3db7c25c67f324bc32a77bb8d6e0d9a9c1270c5090adc6187031b366be4443d4d7df66e60f2d44742
7
+ data.tar.gz: 0f3cf22ee16a7737816afd2a0245aa0676d3cb3d1ab7c10b85c5873250c90da20e91e7ca96e7b2a1d32ce51231a71b85cdb43e9f0c2ee944da1d8d9da67a3836
@@ -74,6 +74,12 @@ heading {
74
74
  .logo {
75
75
  float: right;
76
76
  }
77
+
78
+ .address {
79
+ float: right;
80
+ clear: right;
81
+ margin-top: .8rem
82
+ }
77
83
  }
78
84
 
79
85
  main {
@@ -288,3 +288,7 @@ h1 {
288
288
  }
289
289
  }
290
290
  }
291
+
292
+ .nowrap {
293
+ white-space: nowrap;
294
+ }
@@ -53,7 +53,7 @@ module Renalware
53
53
  end
54
54
 
55
55
  def update_user
56
- @service ||= System::UpdateUser.new(@user)
56
+ @update_user ||= System::UpdateUser.new(@user)
57
57
  end
58
58
  end
59
59
  end
@@ -22,6 +22,9 @@ module Renalware
22
22
 
23
23
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
24
24
 
25
+ class PatientNotFoundError < StandardError; end
26
+ rescue_from PatientNotFoundError, with: :patient_not_found
27
+
25
28
  # Expose a few attributes for use in the patient layout and its partials.
26
29
  # As a rule, we should be passing variables explicitly to all view using `locals`;
27
30
  # however, while not a fan of `helper_method`, exposing e.g. current_patient this (akin to
@@ -31,7 +34,11 @@ module Renalware
31
34
  alias_attribute :current_patient, :patient
32
35
 
33
36
  def patient
34
- @patient ||= Renalware::Patient.find_by!(secure_id: params[:patient_id])
37
+ @patient ||= begin
38
+ Renalware::Patient.find_by(secure_id: params[:patient_id]).tap do |patient_|
39
+ raise PatientNotFoundError unless patient_
40
+ end
41
+ end
35
42
  end
36
43
 
37
44
  protected
@@ -73,5 +80,10 @@ module Renalware
73
80
  flash[:error] = "You are not authorized to perform this action."
74
81
  redirect_to dashboard_path
75
82
  end
83
+
84
+ def patient_not_found
85
+ flash[:error] = "Patient not found"
86
+ redirect_to dashboard_path
87
+ end
76
88
  end
77
89
  end
@@ -66,7 +66,11 @@ module Renalware
66
66
  end
67
67
 
68
68
  def patient
69
- @patient ||= Renalware::Patient.find_by!(secure_id: params[:id])
69
+ @patient ||= begin
70
+ Renalware::Patient.find_by(secure_id: params[:id]).tap do |patient_|
71
+ raise PatientNotFoundError unless patient_
72
+ end
73
+ end
70
74
  end
71
75
 
72
76
  private
@@ -14,7 +14,7 @@ module Renalware
14
14
  end
15
15
 
16
16
  def max_attempts
17
- 3
17
+ 2
18
18
  end
19
19
 
20
20
  def queue_name
@@ -6,7 +6,7 @@ require "attr_extras"
6
6
 
7
7
  module Renalware
8
8
  module Letters
9
- # This class is both a Wispler listener (subscribing to ApproveLetter events) and an ActiveJob.
9
+ # This class is both a Wisper listener (subscribing to ApproveLetter events) and an ActiveJob.
10
10
  # Should be configured in the broadcast_subscription_map to listen to events from ApproveLetter
11
11
  # and be invoked aysnchronously via a background queue ie delayed_job.
12
12
  #
@@ -7,6 +7,8 @@ module Renalware
7
7
  module Feeds
8
8
  module_function
9
9
 
10
+ class DuplicateMessageReceivedError < StandardError; end
11
+
10
12
  def table_name_prefix
11
13
  "feed_"
12
14
  end
@@ -46,6 +46,8 @@ module Renalware
46
46
  # - so that any error in the listener has its own try mechansim and does not cause the
47
47
  # current job to retry,
48
48
  broadcast(:message_processed, feed_message: feed_message)
49
+ rescue Feeds::DuplicateMessageReceivedError => e
50
+ Rails.logger.warn("Rejected duplicate HL7 message: #{e.message}")
49
51
  rescue StandardError => e
50
52
  notify_exception(e)
51
53
  raise e
@@ -16,6 +16,12 @@ module Renalware
16
16
  body_hash: Digest::MD5.hexdigest(hl7_message.to_s),
17
17
  patient_identifier: hl7_message.patient_identification&.internal_id
18
18
  )
19
+ rescue ActiveRecord::RecordNotUnique => e
20
+ # If a duplicate messages comes in (we have calculated the body_hash for the message and it
21
+ # turns out that body_hash is not unique in the database, meaning the message is already
22
+ # stored) then raise a custom error so it can be handled upstream - ie we can choose to
23
+ # ignore it.
24
+ raise Feeds::DuplicateMessageReceivedError, e.message
19
25
  end
20
26
  end
21
27
  end
@@ -6,6 +6,17 @@ module Renalware
6
6
  super && !user_update_self?
7
7
  end
8
8
 
9
+ def assign_role?(role)
10
+ return false if role.hidden
11
+
12
+ can_assign_role = case role.name
13
+ when "devops", "super_admin" then false
14
+ when "admin" then user_is_super_admin?
15
+ else true
16
+ end
17
+ can_assign_role && update?
18
+ end
19
+
9
20
  private
10
21
 
11
22
  def user_update_self?
@@ -43,13 +43,15 @@ module Renalware
43
43
 
44
44
  def sessions
45
45
  @sessions ||= begin
46
- hd_sessions = Sessions::ProtocolSessionsQuery.new(patient: patient).call
46
+ hd_sessions =
47
+ Sessions::ProtocolSessionsQuery.new(patient: patient)
48
+ .call.includes(:patient, :signed_on_by, :signed_off_by)
47
49
  ::CollectionPresenter.new(hd_sessions, Protocol::SessionPresenter, view_context)
48
50
  end
49
51
  end
50
52
 
51
53
  def prescriptions
52
- prescriptions = patient.prescriptions.to_be_administered_on_hd
54
+ prescriptions = patient.prescriptions.includes(:drug).to_be_administered_on_hd
53
55
  ::CollectionPresenter.new(prescriptions, ::Renalware::Medications::PrescriptionPresenter)
54
56
  end
55
57
 
@@ -23,12 +23,12 @@ module Renalware
23
23
  @pathology ||= pathology_for_codes
24
24
  end
25
25
 
26
- def pathology_for_codes(codes = nil)
26
+ def pathology_for_codes(codes = nil, per_page: 10, page: 1)
27
27
  Pathology::CreateObservationsGroupedByDateTable.new(
28
28
  patient: patient,
29
29
  observation_descriptions: pathology_descriptions_for_codes(codes),
30
- page: 1,
31
- per_page: 10
30
+ page: page,
31
+ per_page: per_page
32
32
  ).call
33
33
  end
34
34
 
@@ -10,7 +10,7 @@ module Renalware
10
10
  end
11
11
 
12
12
  def cmvdna_pathology
13
- @cmvdna_pathology ||= pathology_for_codes("CMVD")
13
+ @cmvdna_pathology ||= pathology_for_codes("CMVD", per_page: 6)
14
14
  end
15
15
  end
16
16
  end
@@ -24,12 +24,13 @@
24
24
 
25
25
  br
26
26
  br
27
- - Renalware::Role.all.each_with_index do |role, index|
27
+ - Renalware::Role.all.order(id: :asc).each_with_index do |role, index|
28
28
  - element_id = "user_role_ids_#{index}"
29
+ - policy = Pundit.policy(current_user, @user)
29
30
  = check_box_tag "user[role_ids][]",
30
31
  role.id,
31
32
  @user.has_role?(role.name),
32
- disabled: role.hidden,
33
+ disabled: !(policy.assign_role?(role)),
33
34
  id: element_id,
34
35
  class: "inline"
35
36
  = label_tag element_id, role.name.humanize
@@ -15,7 +15,10 @@ if measurement.present? && measurement.to_f.nonzero?
15
15
  xml.Description I18n.t("loinc.#{i18n_key}.description")
16
16
  end
17
17
 
18
- xml.ObservationValue measurement
18
+ # Only take the first 20 characters, and then strip leading and trailing space.
19
+ # This is to allow for rogue values like
20
+ # '615 615'
21
+ xml.ObservationValue measurement.to_s[0, 19].strip
19
22
  xml.ObservationUnits I18n.t("loinc.#{i18n_key}.units")
20
23
 
21
24
  xml.Clinician do
@@ -27,7 +27,10 @@ observation_times = {
27
27
  xml.Description I18n.t("loinc.#{i18n_key}.description")
28
28
  end
29
29
 
30
- xml.ObservationValue value
30
+ # Only take the first 20 characters, and then strip leading and trailing space.
31
+ # This is to allow for rogue values like
32
+ # '615 615'
33
+ xml.ObservationValue value.to_s[0, 19].strip
31
34
  xml.ObservationUnits I18n.t("loinc.#{i18n_key}.units")
32
35
  xml.PrePost pre_post.to_s.upcase # eg PRE or POST
33
36
  end
@@ -46,3 +46,5 @@ dl
46
46
  dd= profile.transport_type&.text
47
47
  dt= t(".transport.decided_on")
48
48
  dd= profile.transport_decided_on
49
+ dt= t(".on_worryboard")
50
+ dd= yes_no(profile.patient.worry.present?)
@@ -3,12 +3,12 @@ table.recent-pathology
3
3
  tr
4
4
  th.code HGB
5
5
  td.result= recent_pathology.hgb_result
6
- td.observed_at= (l(recent_pathology.hgb_observed_at))
6
+ td.nowrap.observed_at= (l(recent_pathology.hgb_observed_at))
7
7
  tr
8
8
  th.code PLT
9
9
  td.result= recent_pathology.plt_result
10
- td.observed_at= (l(recent_pathology.plt_observed_at))
10
+ td.nowrap.observed_at= (l(recent_pathology.plt_observed_at))
11
11
  tr
12
12
  th.code CRP
13
13
  td.result= recent_pathology.crp_result
14
- td.observed_at= (l(recent_pathology.crp_observed_at))
14
+ td.nowrap.observed_at= (l(recent_pathology.crp_observed_at))
@@ -1,6 +1,5 @@
1
1
  / An empty session table row for use in the printed protocol (data-entry form)
2
2
  tr.empty-session-row.first
3
- td(rowspan=2)
4
3
  td(rowspan=2)
5
4
  td
6
5
  td
@@ -9,7 +8,8 @@ tr.empty-session-row.first
9
8
  td
10
9
  td
11
10
  td
12
- td(rowspan=2)
11
+ td
12
+ td
13
13
  td(rowspan=2)
14
14
  td(rowspan=2)
15
15
  td(rowspan=2)
@@ -20,6 +20,7 @@ tr.empty-session-row.first
20
20
  td(rowspan=2)
21
21
  td(rowspan=2)
22
22
  td
23
+ td
23
24
  tr.empty-session-row
24
25
  td
25
26
  td
@@ -29,3 +30,6 @@ tr.empty-session-row
29
30
  td
30
31
  td
31
32
  td
33
+ td
34
+ td
35
+ td
@@ -14,16 +14,16 @@ tr.hd-session-row(class=[session.state, stripe_class])
14
14
  td= session.before_measurement_for(:pulse)
15
15
  td= session.before_measurement_for(:temperature)
16
16
  td= session.before_measurement_for(:respiratory_rate)
17
- td= session.before_measurement_for(:bm_stix)
18
- td= session.before_measurement_for(:blood_pressure)
19
- td(rowspan=2)= session.arterial_pressure
20
- td(rowspan=2)= session.venous_pressure
21
- td(rowspan=2)= session.blood_flow
22
- td(rowspan=2)= session.litres_processed
23
- td(rowspan=2)= session.fluid_removed
24
- td(rowspan=2)= session.machine_no
25
- td(rowspan=2)= session.machine_ktv
26
- td(rowspan=2)= session.machine_urr
17
+ td.nowrap= session.before_measurement_for(:bm_stix)
18
+ td.nowrap= session.before_measurement_for(:blood_pressure)
19
+ td.nowrap(rowspan=2)= session.arterial_pressure
20
+ td.nowrap(rowspan=2)= session.venous_pressure
21
+ td.nowrap(rowspan=2)= session.blood_flow
22
+ td.nowrap(rowspan=2)= session.litres_processed
23
+ td.nowrap(rowspan=2)= session.fluid_removed
24
+ td.nowrap(rowspan=2)= session.machine_no
25
+ td.nowrap(rowspan=2)= session.machine_ktv
26
+ td.nowrap(rowspan=2)= session.machine_urr
27
27
  td.print-only(rowspan=2)
28
28
  td.print-only(rowspan=2)= session.notes
29
29
  td.print-only= session.signed_on_by
@@ -31,15 +31,15 @@ tr.hd-session-row(class=[session.state, stripe_class])
31
31
  tr(class=[session.state, stripe_class])
32
32
  td= tooltip(label: session.summarised_access_used, content: session.access_used)
33
33
  td= session.end_time
34
- td
34
+ td.nowrap
35
35
  = session.after_measurement_for(:weight)
36
36
  - if (change = session.change_in(:weight))
37
37
  i=" (#{change})"
38
- td= session.after_measurement_for(:pulse)
39
- td= session.after_measurement_for(:temperature)
40
- td= session.after_measurement_for(:respiratory_rate)
41
- td= session.after_measurement_for(:bm_stix)
42
- td= session.after_measurement_for(:blood_pressure)
38
+ td.nowrap= session.after_measurement_for(:pulse)
39
+ td.nowrap= session.after_measurement_for(:temperature)
40
+ td.nowrap= session.after_measurement_for(:respiratory_rate)
41
+ td.nowrap= session.after_measurement_for(:bm_stix)
42
+ td.nowrap= session.after_measurement_for(:blood_pressure)
43
43
  td.print-only= session.signed_off_by
44
44
  = content_tag(:tr, id: "hd-session-#{session.id}", style: "display: none")
45
45
  td(colspan=19)
@@ -8,7 +8,11 @@ html
8
8
  = wicked_pdf_stylesheet_link_tag "renalware/medications_pdf"
9
9
  heading
10
10
  .our-ref= "RW ref: #{patient.hospital_identifier}"
11
- span.logo= wicked_pdf_image_tag "renalware/NHS-Black.jpg", width: 50
11
+ span.logo
12
+ = wicked_pdf_image_tag "renalware/NHS-Black.jpg", width: 50
13
+ .address
14
+ = Renalware.config.hospital_address.split(",").join("<br>").html_safe
15
+
12
16
  main
13
17
  h1 Home Delivery Medication List
14
18
  = "As at #{l(Time.zone.now)}"
@@ -3,6 +3,7 @@ en:
3
3
  hd:
4
4
  protocols:
5
5
  profile:
6
+ on_worryboard: "On Worryboard"
6
7
  site: "Site:"
7
8
  schedule: "Schedule:"
8
9
  prescribed_time: "Prescribed time:"
@@ -22,7 +22,8 @@ module Renalware
22
22
  Dotenv::Railtie.load
23
23
 
24
24
  config_accessor(:site_name) { "Renalware" }
25
- config_accessor(:hospital_name) { "KINGS COLLEGE HOSPITAL" }
25
+ config_accessor(:hospital_name) { ENV.fetch("HOSPITAL_NAME", "KINGS COLLEGE HOSPITAL") }
26
+ config_accessor(:hospital_address) { ENV.fetch("HOSPITAL_ADDRESS", "") } # comma-delimited
26
27
  config_accessor(:delay_after_which_a_finished_session_becomes_immutable) { 6.hours }
27
28
  config_accessor(:new_clinic_visit_deletion_window) { 24.hours }
28
29
  config_accessor(:new_clinic_visit_edit_window) { 7.days }
@@ -84,7 +85,6 @@ module Renalware
84
85
  config_accessor(:render_pdf_as_html_for_debugging) { false }
85
86
 
86
87
  config_accessor(:hd_session_prescriptions_require_signoff) { true }
87
-
88
88
  config_accessor(:batch_printing_enabled) { true }
89
89
 
90
90
  # A host app can override this to add/remove/re-order the clinical summary display
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Renalware
4
- VERSION = "2.0.110"
4
+ VERSION = "2.0.111"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: renalware-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.110
4
+ version: 2.0.111
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airslie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-29 00:00:00.000000000 Z
11
+ date: 2019-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_type
@@ -86,28 +86,28 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 14.1.0
89
+ version: '16.0'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 14.1.0
96
+ version: '16.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: client_side_validations-simple_form
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 6.10.0
103
+ version: '9.0'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 6.10.0
110
+ version: '9.0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: clipboard-rails
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -772,14 +772,14 @@ dependencies:
772
772
  requirements:
773
773
  - - "~>"
774
774
  - !ruby/object:Gem::Version
775
- version: 4.1.0
775
+ version: '5.0'
776
776
  type: :runtime
777
777
  prerelease: false
778
778
  version_requirements: !ruby/object:Gem::Requirement
779
779
  requirements:
780
780
  - - "~>"
781
781
  - !ruby/object:Gem::Version
782
- version: 4.1.0
782
+ version: '5.0'
783
783
  - !ruby/object:Gem::Dependency
784
784
  name: sinatra
785
785
  requirement: !ruby/object:Gem::Requirement