renalware-core 2.0.110 → 2.0.111

Sign up to get free protection for your applications and to get access to all the features.
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