renalware-core 2.0.25 → 2.0.26

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/renalware/modules/_pathology.scss +28 -1
  3. data/app/assets/stylesheets/renalware/modules/_system.scss +27 -0
  4. data/app/assets/stylesheets/renalware/partials/_forms.scss +2 -1
  5. data/app/assets/stylesheets/renalware/partials/_tooltip.scss +347 -0
  6. data/app/controllers/renalware/hospitals/wards_controller.rb +61 -3
  7. data/app/controllers/renalware/session_timeout_controller.rb +1 -0
  8. data/app/controllers/renalware/system/messages_controller.rb +69 -0
  9. data/app/models/concerns/renalware/patients_ransack_helper.rb +2 -2
  10. data/app/models/renalware/admissions/consult_query.rb +2 -3
  11. data/app/models/renalware/pathology/adjust_observation.rb +72 -0
  12. data/app/models/renalware/pathology/create_observation_requests.rb +5 -1
  13. data/app/models/renalware/pathology/message_listener.rb +1 -0
  14. data/app/models/renalware/pathology/observation_requests_attributes_builder.rb +5 -1
  15. data/app/models/renalware/pathology/observations_grouped_by_date_query.rb +1 -1
  16. data/app/models/renalware/pathology/observations_grouped_by_date_table.rb +17 -3
  17. data/app/models/renalware/pathology.rb +3 -0
  18. data/app/models/renalware/system/message.rb +41 -0
  19. data/app/policies/renalware/system/message_policy.rb +21 -0
  20. data/app/views/renalware/admin/cache/show.html.slim +2 -2
  21. data/app/views/renalware/devise/sessions/new.html.slim +1 -0
  22. data/app/views/renalware/hospitals/units/index.html.slim +3 -1
  23. data/app/views/renalware/hospitals/wards/_form.html.slim +3 -0
  24. data/app/views/renalware/hospitals/wards/edit.html.slim +12 -0
  25. data/app/views/renalware/hospitals/wards/index.html.slim +27 -0
  26. data/app/views/renalware/hospitals/wards/new.html.slim +12 -0
  27. data/app/views/renalware/letters/formatted_letters/_letter.html.slim +4 -0
  28. data/app/views/renalware/navigation/_super_admin.html.slim +1 -0
  29. data/app/views/renalware/pathology/historical_observation_results/_table.html.slim +7 -2
  30. data/app/views/renalware/pathology/historical_observation_results/index.html.slim +1 -3
  31. data/app/views/renalware/pathology/recent_observation_results/_table.html.slim +20 -12
  32. data/app/views/renalware/system/messages/_form.html.slim +18 -0
  33. data/app/views/renalware/system/messages/_list.html.slim +4 -0
  34. data/app/views/renalware/system/messages/_message.html.slim +4 -0
  35. data/app/views/renalware/system/messages/edit.html.slim +3 -0
  36. data/app/views/renalware/system/messages/index.html.slim +30 -0
  37. data/app/views/renalware/system/messages/new.html.slim +3 -0
  38. data/app/views/renalware/transplants/mdm/_bottom.html.slim +2 -2
  39. data/config/initializers/renalware.rb +2 -1
  40. data/config/permissions.yml +1 -0
  41. data/config/routes.rb +2 -0
  42. data/db/migrate/20180514151627_create_system_messages.rb +13 -0
  43. data/db/migrate/20180516111411_create_view_patient_current_modalities.rb +5 -0
  44. data/db/views/patient_current_modalities_v01.sql +16 -0
  45. data/lib/renalware/version.rb +1 -1
  46. data/spec/factories/system/messages.rb +9 -0
  47. metadata +21 -2
@@ -0,0 +1,72 @@
1
+ require_dependency "renalware/pathology"
2
+ require "attr_extras"
3
+
4
+ module Renalware
5
+ module Pathology
6
+ # A utility class used for example by the host app to provide a generic way of adjusting
7
+ # an observation, usually in a Wisper listener on receipt of a broadcast pathology message
8
+ # probably 'after_observation_request_persisted'.
9
+ #
10
+ # Example usage:
11
+ #
12
+ # # Subscribe our class to pathology messages.
13
+ # Renalware.configure do |config|
14
+ # map = config.broadcast_subscription_map
15
+ # map["Renalware::Pathology::CreateObservationRequests"] << "MyListener"
16
+ # end
17
+ #
18
+ # class MyListener
19
+ # def after_observation_request_persisted(observation_request)
20
+ # service = Renalware::Pathology::AdjustObservation.new(
21
+ # observation_request: observation_request,
22
+ # code: "EGFR",
23
+ # policy: ->(patient, observation){
24
+ # .. some logic returning true or false to indicate to adjust or not
25
+ # }
26
+ # )
27
+ # service.call do |egfr_observation_requiring_adjustment|
28
+ # egfr_observation_requiring_adjustment.result += 1 or whatever
29
+ # egfr_observation_requiring_adjustment.comment = "adjusted (original = 123)"
30
+ # egfr_observation_requiring_adjustment.save!
31
+ # end
32
+ # end
33
+ # end
34
+ class AdjustObservation
35
+ pattr_initialize [:observation_request!, :code!, :policy!]
36
+ delegate :patient, to: :observation_request
37
+
38
+ # Will yield the requested observation if one found - the caller is responsible for
39
+ # making and saving the adjustment.
40
+ def call
41
+ return unless request_has_the_relevant_observation?
42
+ return if observation_result_is_zero?
43
+ return unless adjustment_required?
44
+
45
+ yield observation
46
+ end
47
+
48
+ def request_has_the_relevant_observation?
49
+ observation.present?
50
+ end
51
+
52
+ def observation_result_is_zero?
53
+ observation.result.to_f.zero?
54
+ end
55
+
56
+ def adjustment_required?
57
+ policy.call(patient, observation)
58
+ end
59
+
60
+ def observation
61
+ @observation ||= begin
62
+ observation_request
63
+ .observations
64
+ .joins(:description)
65
+ .find_by(
66
+ pathology_observation_descriptions: { code: code }
67
+ ) || Renalware::NullObject.instance
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -10,11 +10,15 @@ require_dependency "renalware/pathology"
10
10
  module Renalware
11
11
  module Pathology
12
12
  class CreateObservationRequests
13
+ include Wisper::Publisher
14
+
13
15
  def call(params)
14
16
  Array(params).each do |request|
15
17
  patient = find_patient(request.fetch(:patient_id))
16
18
  observation_params = request.fetch(:observation_request)
17
- patient.observation_requests.create!(observation_params)
19
+ broadcast :before_observation_request_persisted, observation_params
20
+ obr = patient.observation_requests.create!(observation_params)
21
+ broadcast :after_observation_request_persisted, obr
18
22
  end
19
23
  end
20
24
 
@@ -9,6 +9,7 @@ require_dependency "renalware/pathology"
9
9
  module Renalware
10
10
  module Pathology
11
11
  class MessageListener
12
+ # Note: We are already inside a transaction here
12
13
  def message_processed(message_payload)
13
14
  pathology_params = parse_pathology_params(message_payload)
14
15
  create_observation_requests_and_their_child_observations_from(pathology_params)
@@ -9,7 +9,7 @@ module Renalware
9
9
  # Note:
10
10
  # - A message can have multiple observation_requests, each with its own observations.
11
11
  # - This class could be removed and a Builder class used to create the database models
12
- # directly - this would remove the extra level of indirection that this class introduces.
12
+ # directly - this would remove the extra level of indirection that this class introduces.
13
13
  class ObservationRequestsAttributesBuilder
14
14
  DEFAULT_REQUESTOR_NAME = "UNKNOWN"
15
15
  delegate :patient_identification, :observation_requests, to: :message_payload
@@ -87,10 +87,14 @@ module Renalware
87
87
 
88
88
  def find_request_description(code)
89
89
  RequestDescription.find_by!(code: code)
90
+ rescue ActiveRecord::RecordNotFound
91
+ raise MissingRequestDescriptionError, code
90
92
  end
91
93
 
92
94
  def find_observation_description(code)
93
95
  ObservationDescription.find_by!(code: code)
96
+ rescue ActiveRecord::RecordNotFound
97
+ raise MissingObservationDescriptionError, code
94
98
  end
95
99
 
96
100
  def validate_observation(observation, observation_description)
@@ -68,7 +68,7 @@ module Renalware
68
68
  def to_sql
69
69
  <<-SQL.squish
70
70
  select obs_req.patient_id, cast(observed_at as date) as observed_on,
71
- jsonb_object_agg(obs_desc.code, obs.result) results
71
+ jsonb_object_agg(obs_desc.code, ARRAY[obs.result, obs.comment]) results
72
72
  from pathology_observations obs
73
73
  inner join pathology_observation_requests obs_req on obs.request_id = obs_req.id
74
74
  inner join pathology_observation_descriptions obs_desc on obs.description_id = obs_desc.id
@@ -41,6 +41,10 @@ module Renalware
41
41
  @rows ||= relation.all.map(&:with_indifferent_access).map { |row| Row.new(row) }
42
42
  end
43
43
 
44
+ # Each row wraps a PGResult (database row).
45
+ # Each row is all the pathology results for this patient on a particular day.
46
+ # Hence there is one observed_on date but multiple results which are the columns going across
47
+ # the table.
44
48
  class Row
45
49
  pattr_initialize :row
46
50
 
@@ -49,11 +53,21 @@ module Renalware
49
53
  end
50
54
 
51
55
  def result_for(code)
52
- results[code&.to_sym]
56
+ row_hash[code&.to_sym]&.first
53
57
  end
54
58
 
55
- def results
56
- @results = JSON.parse(row[:results]).with_indifferent_access
59
+ def comment_for(code)
60
+ row_hash[code&.to_sym]&.last
61
+ end
62
+
63
+ # A hash keyed by OBX code, containing an array of [result, comment] arrays, e.g.
64
+ #
65
+ # { "HGB": [[123,nil],[124,'Some comment about the result',[..]], "PLT": [..] }
66
+ #
67
+ # This is a compressed format (as opposed to returning more verbose json) to
68
+ # improve performance. Just remember that for any result, its an array of [result, comment]
69
+ def row_hash
70
+ @row_hash = JSON.parse(row[:results]).with_indifferent_access
57
71
  end
58
72
  end
59
73
  end
@@ -19,5 +19,8 @@ module Renalware
19
19
  def configure
20
20
  SubscriptionRegistry.instance.register(Feeds::MessageProcessor, MessageListener)
21
21
  end
22
+
23
+ class MissingRequestDescriptionError < StandardError; end
24
+ class MissingObservationDescriptionError < StandardError; end
22
25
  end
23
26
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/system"
4
+
5
+ module Renalware
6
+ module System
7
+ class Message < ApplicationRecord
8
+ extend Enumerize
9
+ validates :body, presence: true
10
+ validates :display_from, timeliness: { type: :datetime }, presence: true
11
+ validates :display_until,
12
+ timeliness: { type: :datetime, allow_blank: true, after: :display_from }
13
+ enumerize :severity, in: %i(default warning info success)
14
+
15
+ scope :active, lambda{
16
+ where(
17
+ "display_from <= ? and (display_until is null or display_until >= ?)",
18
+ Time.zone.now,
19
+ Time.zone.now
20
+ )
21
+ }
22
+
23
+ # Return true if the message should be visible.
24
+ # A message is visible if display_from is in the past and display_until is nil
25
+ # or in the future
26
+ def active?
27
+ return false if display_from.nil?
28
+ now = Time.zone.now
29
+ self.display_until ||= far_future_date
30
+ return true if display_from < now && display_until > now
31
+ false
32
+ end
33
+
34
+ private
35
+
36
+ def far_future_date
37
+ Time.zone.parse("2999-01-01 00:00:01")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/snippets"
4
+
5
+ module Renalware
6
+ module System
7
+ class MessagePolicy < BasePolicy
8
+ def new?
9
+ user_is_super_admin?
10
+ end
11
+
12
+ def create?
13
+ user_is_super_admin?
14
+ end
15
+
16
+ def destroy?
17
+ user_is_super_admin?
18
+ end
19
+ end
20
+ end
21
+ end
@@ -34,8 +34,8 @@
34
34
  | source for PDF letters, rather a volatile cache to aid performance. EPR should be the place
35
35
  | to look for letter content - or just view the PDF through the UI.
36
36
  p
37
- | You may wish to clear the PDF Letter Cache if you notice any anomalies in the PDFs. Please
38
- | raise a GitHub issue after clearing the cache so we can look into it.
37
+ | You may wish to clear the PDF Letter Cache if you notice any anomalies in the PDFs.
38
+ | Please raise a GitHub issue after clearing the cache so we can look into it.
39
39
 
40
40
  = link_to "Clear the PDF Letter Cache",
41
41
  letters_pdf_letter_cache_path,
@@ -2,6 +2,7 @@
2
2
  .row.small-8.large-12
3
3
  h3 Log in to Renalware
4
4
 
5
+ = render "renalware/system/messages/list"
5
6
  = render "warning"
6
7
 
7
8
  .row.small-6.large-3
@@ -14,7 +14,7 @@
14
14
  th.col-width-small NRD Code
15
15
  th.col-width-small HD Site?
16
16
  th.col-width-tiny Stations
17
- th.col-width-medium
17
+ th.col-width-largish
18
18
  tbody
19
19
  - hospital_units.each do |unit|
20
20
  tr
@@ -36,3 +36,5 @@
36
36
  = link_to("HD Stations", hd_unit_stations_path(unit))
37
37
  = pipe_separator
38
38
  = link_to("HD Diaries", hd_unit_diaries_path(unit))
39
+ = pipe_separator
40
+ = link_to("Wards", hospitals_unit_wards_path(unit))
@@ -0,0 +1,3 @@
1
+ = f.input :code, wrapper: :horizontal_small
2
+ = f.input :name, wrapper: :horizontal_medium
3
+ = f.submit class: :button
@@ -0,0 +1,12 @@
1
+ = within_admin_layout(title: "Edit", breadcrumbs: [ \
2
+ breadcrumb_for(ward.hospital_unit.name, hospitals_units_path),
3
+ breadcrumb_for("Wards", hospitals_unit_wards_path(ward.hospital_unit)) \
4
+ ]) do
5
+
6
+ = simple_form_for ward,
7
+ url: hospitals_unit_ward_path(ward.hospital_unit, ward),
8
+ html: { autocomplete: "off" },
9
+ as: :ward,
10
+ wrapper: "horizontal_form" do |f|
11
+
12
+ = render "form", f: f
@@ -0,0 +1,27 @@
1
+ = content_for(:actions) do
2
+ = link_to "Add",
3
+ new_hospitals_unit_ward_path(unit),
4
+ class: "button"
5
+
6
+ = within_admin_layout(title: "Wards", breadcrumbs: \
7
+ breadcrumb_for(unit.name, hospitals_units_path)) do
8
+
9
+ table.hospital-wards
10
+ thead
11
+ th.col-width-tiny
12
+ th.col-width-medium Code
13
+ th Name
14
+ tbody
15
+ - wards.each do |ward|
16
+ tr
17
+ td
18
+ - if policy(ward).edit?
19
+ = link_to "Edit", edit_hospitals_unit_ward_path(unit, ward)
20
+ - if policy(ward).destroy?
21
+ = pipe_separator
22
+ = link_to "Delete",
23
+ hospitals_unit_ward_path(unit, ward),
24
+ method: :delete,
25
+ data: { confirm: "Are you sure?" }
26
+ td= ward.code
27
+ td= ward.name
@@ -0,0 +1,12 @@
1
+ = within_admin_layout(title: "New", breadcrumbs: [ \
2
+ breadcrumb_for(ward.hospital_unit.name, hospitals_units_path),
3
+ breadcrumb_for("Wards", hospitals_unit_wards_path(ward.hospital_unit)) \
4
+ ]) do
5
+
6
+ = simple_form_for ward,
7
+ url: hospitals_unit_wards_path(unit_id: ward.hospital_unit.id),
8
+ html: { autocomplete: "off" },
9
+ as: :ward,
10
+ wrapper: "horizontal_form" do |f|
11
+
12
+ = render "form", f: f
@@ -154,6 +154,9 @@ scss:
154
154
 
155
155
  .allergies {
156
156
  margin-bottom: 1rem;
157
+ float: left;
158
+ clear: left;
159
+ width: 50%;
157
160
 
158
161
  h3 {
159
162
  padding-right: 1rem;
@@ -220,6 +223,7 @@ scss:
220
223
  .message {
221
224
  margin-top: 30px;
222
225
  margin-bottom: 20px;
226
+ clear: both;
223
227
 
224
228
  p {
225
229
  margin-bottom: 15px;
@@ -9,3 +9,4 @@ li.has-dropdown
9
9
  li= link_to "Cache", admin_cache_path
10
10
  li= link_to "File Imports", admin_feeds_files_path
11
11
  li= link_to "User Feedback", system_user_feedback_index_path
12
+ li= link_to "System Messages", system_messages_path
@@ -7,7 +7,12 @@ table#observations
7
7
  tbody
8
8
  - table.rows.each do |row|
9
9
  tr
10
- td.date= l(row.observed_on)
10
+ th.date= l(row.observed_on)
11
11
  - table.observation_descriptions.each do |desc|
12
12
  - code = desc.code
13
- td(class=code.downcase)= row.result_for(code)
13
+ - if row.comment_for(code).present?
14
+ td(class=code.downcase data-balloon=row.comment_for(code) data-balloon-pos="up")
15
+ .triangle-top-right
16
+ = row.result_for(code)
17
+ - else
18
+ td(class=code.downcase)= row.result_for(code)
@@ -2,11 +2,9 @@
2
2
  navigation_partial: "renalware/pathology/navigation" do
3
3
  - if table.rows.empty?
4
4
  p No results available for this patient.
5
-
6
5
  - else
7
- p.hint Hover over table then scroll horizontally to view more results.
8
-
9
6
  .embedded-scroll
7
+ p.hint Hover over table then scroll horizontally to view more results.
10
8
  = render "table", table: table
11
9
 
12
10
  = paginate(table)
@@ -2,17 +2,25 @@
2
2
  / historical results, we transpose the cols and rows so we have
3
3
  / dates along the top and OBX codes down the left.
4
4
  table#observations
5
- tr
6
- th Year
7
- - table.rows.each do |row|
8
- th.date= row.observed_on.year
9
- tr
10
- th Date
11
- - table.rows.each do |row|
12
- th= row.observed_on.strftime("%d/%m")
13
- - table.observation_descriptions.each do |desc|
14
- - code = desc.code
5
+ thead
15
6
  tr
16
- th(title=desc.name)= code
7
+ th Year
17
8
  - table.rows.each do |row|
18
- td(class=code.downcase)= row.results[code]
9
+ th.date= row.observed_on.year
10
+ thead
11
+ tr
12
+ th Date
13
+ - table.rows.each do |row|
14
+ th= row.observed_on.strftime("%d/%m")
15
+ tbody
16
+ - table.observation_descriptions.each do |desc|
17
+ - code = desc.code
18
+ tr
19
+ th(title=desc.name)= code
20
+ - table.rows.each do |row|
21
+ - if row.comment_for(code).present?
22
+ td(class=code.downcase data-balloon=row.comment_for(code) data-balloon-pos="up")
23
+ .triangle-top-right
24
+ = row.result_for(code)
25
+ - else
26
+ td(class=code.downcase)= row.result_for(code)
@@ -0,0 +1,18 @@
1
+ - severities = Renalware::System::Message.severity.values.map{|val| [val&.humanize, val] }
2
+ = simple_form_for(message,
3
+ as: :message,
4
+ html: { autocomplete: "off" }) do |f|
5
+
6
+ = render "renalware/shared/errors", model: f.object
7
+
8
+ = f.input :title, placeholder: "An optional title"
9
+ = f.trix_editor :body, wrapper: :horizontal_large
10
+ br
11
+ = f.input :severity,
12
+ collection: severities,
13
+ input_html: { style: "width:100px" },
14
+ include_blank: nil
15
+ = f.input :display_from, as: :datetime_picker
16
+ = f.input :display_until, as: :datetime_picker
17
+ br
18
+ = f.submit "Save", class: :button
@@ -0,0 +1,4 @@
1
+ - messages = Renalware::System::Message.active
2
+ - return if messages.empty?
3
+ .system-messages
4
+ = render messages
@@ -0,0 +1,4 @@
1
+ div.system-message.alert-box.radius(class=message.severity)
2
+ - if message.title.present?
3
+ h1= message.title
4
+ = simple_format(message.body)
@@ -0,0 +1,3 @@
1
+ = within_admin_layout(title: "Edit",
2
+ breadcrumbs: breadcrumb_for("System Messages", system_messages_path)) do
3
+ = render "form", message: message
@@ -0,0 +1,30 @@
1
+ = content_for(:actions) do
2
+ = link_to "Add", new_system_message_path, class: :button
3
+
4
+ = within_admin_layout(title: "System Messages") do
5
+ .system-messages
6
+ table
7
+ thead
8
+ th
9
+ th
10
+ th.col-width-tiny Severity
11
+ th.col-width-date-time Display from
12
+ th.col-width-date-time Display until
13
+ th.col-width-tiny Active now
14
+ tbody
15
+ - messages.each do |message|
16
+ tr
17
+ td
18
+ = link_to "Edit", edit_system_message_path(message)
19
+ = pipe_separator
20
+ = link_to "Delete",
21
+ system_message_path(message),
22
+ method: :delete,
23
+ data: { confirm: I18n.t("prompts.confirm_delete") }
24
+ td
25
+ = render message
26
+ /td= simple_format(message.body)
27
+ td= message.severity&.humanize
28
+ td= l(message.display_from)
29
+ td= l(message.display_until)
30
+ td= yes_no(message.active?)
@@ -0,0 +1,3 @@
1
+ = within_admin_layout(title: "New",
2
+ breadcrumbs: breadcrumb_for("System Messages", system_messages_path)) do
3
+ = render "form", message: message
@@ -8,10 +8,10 @@
8
8
  .columns.small-12.medium-4
9
9
  = render "measurements", mdm: mdm
10
10
  .row
11
- .columns.medium-12.large-6
11
+ .columns.medium-12
12
12
  = render "clinic_visits", mdm: mdm
13
13
 
14
- .columns.medium-12.large-6
14
+ .columns.medium-12.large-4
15
15
  = render "biopsies", mdm: mdm
16
16
  .row
17
17
  .columns.medium-12.large-12
@@ -11,7 +11,8 @@ Renalware.configure do |config|
11
11
  "Renalware::Letters::PatientListener",
12
12
  "Renalware::HD::PatientListener"
13
13
  ],
14
- "Renalware::Letters::ApproveLetter" => []
14
+ "Renalware::Letters::ApproveLetter" => [],
15
+ "Renalware::Pathology::CreateObservationRequests" => []
15
16
  }
16
17
  end
17
18
 
@@ -1,5 +1,6 @@
1
1
  super_admin:
2
2
  - Renalware::Role
3
+ - Renalware::Hospitals::Ward
3
4
  admin:
4
5
  - Renalware::User
5
6
  - Renalware::BagType
data/config/routes.rb CHANGED
@@ -178,6 +178,7 @@ Renalware::Engine.routes.draw do
178
178
 
179
179
  namespace :hospitals do
180
180
  resources :units, except: :show do
181
+ resources :wards
181
182
  scope format: true, constraints: { format: :json } do
182
183
  resources :wards, only: :index
183
184
  end
@@ -267,6 +268,7 @@ Renalware::Engine.routes.draw do
267
268
  namespace :system do
268
269
  resources :email_templates, only: :index
269
270
  resources :user_feedback, except: :destroy, controller: "user_feedback"
271
+ resources :messages
270
272
  end
271
273
 
272
274
  namespace :transplants do
@@ -0,0 +1,13 @@
1
+ class CreateSystemMessages < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :system_messages do |t|
4
+ t.string :title, null: true
5
+ t.text :body, null: false
6
+ t.integer :message_type, null: false, default: 0
7
+ t.string :severity
8
+ t.datetime :display_from, null: false
9
+ t.datetime :display_until
10
+ t.timestamps null: false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class CreateViewPatientCurrentModalities < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_view :patient_current_modalities
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ select
2
+ patients.id as patient_id,
3
+ patients.secure_id as patient_secure_id,
4
+ current_modality.id as modality_id,
5
+ modality_descriptions.id as modality_description_id,
6
+ modality_descriptions.name as modality_name,
7
+ current_modality.started_on as started_on
8
+ from patients
9
+ -- this join only required as currently there can be > 1 current modality for a patient
10
+ -- here we select the current modality with the most recent started_on
11
+ left outer join (
12
+ select distinct on (patient_id) * from modality_modalities
13
+ order by patient_id, started_on desc
14
+ ) as current_modality
15
+ on patients.id = current_modality.patient_id
16
+ left outer join modality_descriptions on modality_descriptions.id = current_modality.description_id;
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Renalware
4
- VERSION = "2.0.25"
4
+ VERSION = "2.0.26"
5
5
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :system_message, class: "Renalware::System::Message" do
5
+ title "Test title"
6
+ body "Test body"
7
+ display_from { Time.zone.now }
8
+ end
9
+ end