renalware-core 2.0.160 → 2.0.165
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/renalware/rollup_compiled.js +768 -201
- data/app/assets/stylesheets/renalware/modules/_clinical.scss +28 -0
- data/app/assets/stylesheets/renalware/partials/_tables.scss +6 -0
- data/app/components/renalware/medications/tabbed_prescriptions_list_component.html.slim +2 -2
- data/app/components/renalware/pathology/sparkline_component.html.slim +1 -1
- data/app/components/renalware/pd/pet_results_component.html.slim +4 -4
- data/app/components/renalware/system/admin_menu_component.html.slim +1 -0
- data/app/controllers/renalware/virology/vaccination_types_controller.rb +69 -0
- data/app/javascript/renalware/controllers/modal_controller.js +1 -1
- data/app/models/renalware/events/alertable_events_query.rb +27 -0
- data/app/models/renalware/events/event.rb +4 -0
- data/app/models/renalware/events/event_type_alert_trigger.rb +15 -0
- data/app/models/renalware/events/type.rb +7 -0
- data/app/models/renalware/letters/pdf_letter_cache.rb +1 -1
- data/app/models/renalware/virology/vaccination.rb +10 -2
- data/app/models/renalware/virology/vaccination_type.rb +38 -0
- data/app/views/renalware/admin/cache/show.html.slim +0 -36
- data/app/views/renalware/admin/playgrounds/show.html.slim +2 -2
- data/app/views/renalware/events/events/_alerts.html.slim +3 -0
- data/app/views/renalware/events/events/alert/_simple.html.slim +5 -0
- data/app/views/renalware/events/events/toggled_cell/_swab.html.slim +1 -1
- data/app/views/renalware/hd/dashboards/_page_actions.html.slim +4 -1
- data/app/views/renalware/hd/mdm_patients/_patient.html.slim +1 -1
- data/app/views/renalware/hd/prescription_administrations/_form.html.slim +2 -2
- data/app/views/renalware/hd/transmission_logs/index.html.slim +2 -1
- data/app/views/renalware/layouts/_patient.html.slim +1 -0
- data/app/views/renalware/letters/batches/_create.html.slim +2 -2
- data/app/views/renalware/medications/home_delivery/events/_edit.html.slim +4 -4
- data/app/views/renalware/medications/prescriptions/_form.html.slim +3 -3
- data/app/views/renalware/system/downloads/index.html.slim +1 -1
- data/app/views/renalware/system/messages/_form.html.slim +1 -1
- data/app/views/renalware/system/view_metadata/edit.html.slim +6 -6
- data/app/views/renalware/virology/vaccination_types/_form.html.slim +10 -0
- data/app/views/renalware/virology/vaccination_types/edit.html.slim +3 -0
- data/app/views/renalware/virology/vaccination_types/index.html.slim +42 -0
- data/app/views/renalware/virology/vaccination_types/new.html.slim +3 -0
- data/app/views/renalware/virology/vaccinations/_alert.html.slim +5 -0
- data/app/views/renalware/virology/vaccinations/_inputs.html.slim +2 -1
- data/app/views/renalware/virology/vaccinations/_toggled_cell.html.slim +1 -1
- data/config/permissions.yml +1 -0
- data/config/routes/virology.rb +6 -0
- data/db/migrate/20210105163944_create_virology_vaccination_types.rb +13 -0
- data/db/migrate/20210115181817_create_event_type_alert_triggers.rb +15 -0
- data/db/seeds/default/seeds.rb +1 -0
- data/db/seeds/default/virology/seeds.rb +3 -0
- data/db/seeds/default/virology/vaccination_types.rb +25 -0
- data/lib/renalware/version_number.rb +1 -1
- data/spec/factories/virology/vaccination_types.rb +8 -0
- metadata +23 -7
@@ -141,6 +141,7 @@ article.clinical-allergies {
|
|
141
141
|
.patient-alerts {
|
142
142
|
color: $white;
|
143
143
|
background-color: transparent;
|
144
|
+
padding-top: .1rem;
|
144
145
|
|
145
146
|
@media print {
|
146
147
|
display: none;
|
@@ -199,6 +200,33 @@ article.clinical-allergies {
|
|
199
200
|
}
|
200
201
|
}
|
201
202
|
|
203
|
+
&.vaccination {
|
204
|
+
background-color: $nhs-aqua-green;
|
205
|
+
|
206
|
+
&:hover {
|
207
|
+
background-color: darken($nhs-aqua-green, 4);
|
208
|
+
}
|
209
|
+
|
210
|
+
.title a,
|
211
|
+
i:before {
|
212
|
+
color: $white;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
&.event {
|
217
|
+
background-color: $nhs-yellow;
|
218
|
+
|
219
|
+
&:hover {
|
220
|
+
background-color: darken($nhs-yellow, 7);
|
221
|
+
}
|
222
|
+
|
223
|
+
.title a,
|
224
|
+
.date,
|
225
|
+
i:before {
|
226
|
+
color: $dark-grey;
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
202
230
|
&.research-study-participant {
|
203
231
|
background-color: darken($nhs-green, 3);
|
204
232
|
color: $white;
|
@@ -2,11 +2,11 @@ div(data-controller="tabs" data-tabs-active-tab="active")
|
|
2
2
|
.tabs-container
|
3
3
|
ul.list-reset.sub-nav
|
4
4
|
- groups.each do |group|
|
5
|
-
li(data-target="
|
5
|
+
li(data-tabs-target="tab" data-action="click->tabs#change")
|
6
6
|
a(class="" href="#" data-trigger-masonry-refresh="true")= group.title
|
7
7
|
|
8
8
|
- groups.each do |group|
|
9
|
-
.hidden(data-target="
|
9
|
+
.hidden(data-tabs-target="panel")
|
10
10
|
table.prescriptions-list
|
11
11
|
thead
|
12
12
|
tr
|
@@ -6,7 +6,7 @@
|
|
6
6
|
/ This is the stimulusjs version. See sparklines_controller.js
|
7
7
|
div(data-controller="pathology-sparklines"
|
8
8
|
data-pathology-sparklines-chart-data='[["2010-06-03T16:37:00.000+01:00","106"], ["2011-06-03T16:37:00.000+01:00","110"]]')
|
9
|
-
div(data-target="
|
9
|
+
div(data-pathology-sparklines-target="chart")
|
10
10
|
- else
|
11
11
|
/ This implementation uses ChartKick to insert the chart javascript into the
|
12
12
|
/ page. It works fine, but when there are 50 path results on screen, we are
|
@@ -7,12 +7,12 @@
|
|
7
7
|
.flex.justify-end
|
8
8
|
|
9
9
|
.inline-flex
|
10
|
-
a.reset.opacity-50.bg-white.m-05.pt-px.pb-0.px-1(href="#" data-target="
|
10
|
+
a.reset.opacity-50.bg-white.m-05.pt-px.pb-0.px-1(href="#" data-tabs-target="tab"
|
11
11
|
data-action="click->tabs#change")
|
12
12
|
svg.h-6.w-6.fill-current.text-black xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
13
13
|
path d="M1 4h2v2H1V4zm4 0h14v2H5V4zM1 9h2v2H1V9zm4 0h14v2H5V9zm-4 5h2v2H1v-2zm4 0h14v2H5v-2z"
|
14
14
|
|
15
|
-
a.reset.opacity-50.bg-white.m-0.pt-px.px-2(data-target="
|
15
|
+
a.reset.opacity-50.bg-white.m-0.pt-px.px-2(data-tabs-target="tab" href="#"
|
16
16
|
data-action="click->tabs#change")
|
17
17
|
svg.h-6.w-6.stroke-current.text-black viewBox="0 0 24 26" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"
|
18
18
|
path d="M12 20V10M18 20V4M6 20v-4"
|
@@ -20,7 +20,7 @@
|
|
20
20
|
= link_to "Show all", renalware.patient_pd_pet_results_path(patient), remote: true, class: "float-none button flex-initial secondary"
|
21
21
|
= link_to "Add", renalware.new_patient_pd_pet_result_path(patient), class: "float-none button flex-initial"
|
22
22
|
|
23
|
-
div#pet-results-table data-target="
|
23
|
+
div#pet-results-table data-tabs-target="panel"
|
24
24
|
|
25
25
|
= render "renalware/pd/pet_results/table",
|
26
26
|
patient: patient,
|
@@ -28,7 +28,7 @@
|
|
28
28
|
pagination: pagination,
|
29
29
|
current_user: current_user
|
30
30
|
|
31
|
-
.chart.hidden.mt-5 data-target="
|
31
|
+
.chart.hidden.mt-5 data-tabs-target="panel"
|
32
32
|
div(style="display:block; position:relative; height: 400px; width: 100%; text-align: center; margin-bottom: 20px")
|
33
33
|
#containerx(data-controller="pd-pet-chart"
|
34
34
|
data-pd-pet-chart-url=renalware.patient_pd_pet_results_path(patient, format: :json)
|
@@ -24,6 +24,7 @@ ul.side-nav.side-nav--admin
|
|
24
24
|
= super_admin_menu_item "Print batches", renalware.letters_batches_path, %r{letters/batches}
|
25
25
|
= super_admin_menu_item "OBX", renalware.pathology_observation_descriptions_path, %r{admin/pathology_observation_descriptions}
|
26
26
|
= super_admin_menu_item "Pathology Code Groups", renalware.pathology_code_groups_path, %r{pathology/code_groups}
|
27
|
+
= super_admin_menu_item "Virology Vaccination Types", renalware.virology_vaccination_types_path, %r{virology/vaccination_types}
|
27
28
|
= admin_menu_item "Mailshots", renalware.letters_mailshots_path, %r{letters/mailshots}
|
28
29
|
= super_admin_menu_item "Playground", renalware.admin_playground_path, %r{admin/playground}
|
29
30
|
= developer_menu_item "HL7 Test", renalware.new_feeds_hl7_test_message_path, %r{admin/123}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/virology"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module Virology
|
7
|
+
class VaccinationTypesController < BaseController
|
8
|
+
def index
|
9
|
+
types = VaccinationType.with_deleted.ordered
|
10
|
+
authorize types
|
11
|
+
render locals: { types: types }
|
12
|
+
end
|
13
|
+
|
14
|
+
def edit
|
15
|
+
type = find_and_authorise_type
|
16
|
+
render locals: { type: type }
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
type = find_and_authorise_type
|
21
|
+
if type.update(type_params)
|
22
|
+
redirect_to virology_vaccination_types_path
|
23
|
+
else
|
24
|
+
render :edit, locals: { type: type }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def new
|
29
|
+
type = VaccinationType.new
|
30
|
+
authorize type
|
31
|
+
render locals: { type: type }
|
32
|
+
end
|
33
|
+
|
34
|
+
def create
|
35
|
+
type = VaccinationType.new(type_params)
|
36
|
+
authorize type
|
37
|
+
if type.save
|
38
|
+
redirect_to virology_vaccination_types_path
|
39
|
+
else
|
40
|
+
render :new, locals: { type: type }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy
|
45
|
+
find_and_authorise_type.destroy!
|
46
|
+
redirect_to virology_vaccination_types_path
|
47
|
+
end
|
48
|
+
|
49
|
+
def sort
|
50
|
+
authorize VaccinationType, :sort?
|
51
|
+
ids = params[:virology_vaccination_type]
|
52
|
+
VaccinationType.sort(ids)
|
53
|
+
render json: ids
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def find_and_authorise_type
|
59
|
+
VaccinationType.find(params[:id]).tap { |type| authorize type }
|
60
|
+
end
|
61
|
+
|
62
|
+
def type_params
|
63
|
+
params
|
64
|
+
.require("virology_vaccination_type")
|
65
|
+
.permit(:name, :code, :position)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -11,7 +11,7 @@
|
|
11
11
|
// <span>Open Modal</span>
|
12
12
|
// </a>
|
13
13
|
// <!-- Modal Container -->
|
14
|
-
// <div data-target="
|
14
|
+
// <div data-modal-target="container" data-action="click->modal#closeBackground keyup@window->modal#closeWithKeyboard" class="hidden animated fadeIn fixed inset-0 overflow-y-auto flex items-center justify-center" style="z-index: 9999;">
|
15
15
|
// <!-- Modal Inner Container -->
|
16
16
|
// <div class="max-h-screen w-full max-w-lg relative">
|
17
17
|
// <!-- Modal Card -->
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/events"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module Events
|
7
|
+
# Query object that returns, for a patient, the most recent matching event (if any) for each
|
8
|
+
# event_type_alert_trigger row in the databse. The results are used to display
|
9
|
+
# alerts in the UI.
|
10
|
+
# For example given a vaccination Event::Type and an EventTypeAlertTrigger
|
11
|
+
# which is configured to find any vaccination event with the word "covid" in
|
12
|
+
# anywhere in the document, this query will return the most matching event.
|
13
|
+
# It is possible to have mltiple triggers rows for the same event type, so for eample one
|
14
|
+
# could display triggers for the most recent covid vaccination, and the most recent HBV
|
15
|
+
# vaccination.
|
16
|
+
class AlertableEventsQuery
|
17
|
+
def self.call(patient:)
|
18
|
+
Event
|
19
|
+
.for_patient(patient)
|
20
|
+
.joins(event_type: :alert_triggers)
|
21
|
+
.select("DISTINCT ON (events.patient_id, event_type_alert_triggers.id) events.*")
|
22
|
+
.where("(events.document::text ilike '%' || when_event_document_contains || '%') or (events.description ilike '%' || when_event_description_contains || '%')")
|
23
|
+
.order("events.patient_id, event_type_alert_triggers.id, events.created_at desc")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/events"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module Events
|
7
|
+
# Used to defines conditions where, for a particular event type, if the there is e.g. a case-
|
8
|
+
# insensitive text match against the contents of the event document (eg 'COVID')
|
9
|
+
# then an alert will be displayed in the UI (for the most recent match is there are > 1).
|
10
|
+
class EventTypeAlertTrigger < ApplicationRecord
|
11
|
+
belongs_to :event_type, class_name: "Events::Type"
|
12
|
+
validates :event_type, presence: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -6,6 +6,13 @@ module Renalware
|
|
6
6
|
module Events
|
7
7
|
class Type < ApplicationRecord
|
8
8
|
self.table_name = "event_types"
|
9
|
+
has_many(
|
10
|
+
:alert_triggers,
|
11
|
+
class_name: "EventTypeAlertTrigger",
|
12
|
+
foreign_key: :event_type_id,
|
13
|
+
dependent: :destroy
|
14
|
+
)
|
15
|
+
|
9
16
|
DEFAULT_EVENT_CLASS_NAME = "Renalware::Events::Simple"
|
10
17
|
|
11
18
|
acts_as_paranoid
|
@@ -40,7 +40,7 @@ module Renalware
|
|
40
40
|
delegate :clear, to: :store
|
41
41
|
|
42
42
|
def fetch(letter, **options)
|
43
|
-
store.fetch(cache_key_for(letter, **options)) { yield }
|
43
|
+
store.fetch(cache_key_for(letter, **options), expires_in: 4.weeks) { yield }
|
44
44
|
end
|
45
45
|
|
46
46
|
# Note the letter must be a LetterPresenter which has a #to_html method
|
@@ -17,12 +17,20 @@ module Renalware
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class Document < Document::Embedded
|
20
|
-
attribute :type
|
20
|
+
attribute :type # This used to be a ::Document::Enum but now we load options from the db
|
21
21
|
attribute :drug, String
|
22
22
|
validates :type, presence: true
|
23
23
|
|
24
24
|
def to_s
|
25
|
-
[
|
25
|
+
[type_name, drug].reject(&:blank?).join(" - ")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Try and find the proper name for the vaccination type - we only store the vaccination
|
29
|
+
# type code in the jsonb document. If no match found just display the code eg 'hbv_booster'.
|
30
|
+
# The Type mighjt have been deleted (has a deleted_at date) but we still want to display the
|
31
|
+
# actual name depite this, we include deleted rows when searching.
|
32
|
+
def type_name
|
33
|
+
VaccinationType.with_deleted.find_by(code: type)&.name || type
|
26
34
|
end
|
27
35
|
end
|
28
36
|
has_document
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/virology"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module Virology
|
7
|
+
class VaccinationType < ApplicationRecord
|
8
|
+
include Sortable
|
9
|
+
|
10
|
+
class UniquenessIncludingDeletedValidator < ActiveModel::EachValidator
|
11
|
+
def validate_each(record, attribute, value)
|
12
|
+
return unless record.send(:"#{attribute}_changed?")
|
13
|
+
|
14
|
+
if record.class.with_deleted.exists?(attribute => value)
|
15
|
+
record.errors.add attribute, (options[:message] || "already used")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
validates :name, presence: true, uniqueness_including_deleted: true
|
20
|
+
validates :code, presence: true, uniqueness_including_deleted: true
|
21
|
+
before_create :underscore_code
|
22
|
+
|
23
|
+
acts_as_paranoid
|
24
|
+
|
25
|
+
scope :ordered, -> { order(deleted_at: :desc, position: :asc, name: :asc) }
|
26
|
+
|
27
|
+
def self.policy_class
|
28
|
+
BasePolicy
|
29
|
+
end
|
30
|
+
|
31
|
+
def underscore_code
|
32
|
+
return if code.blank?
|
33
|
+
|
34
|
+
self.code = code.downcase.tr(" ", " ").tr(" ", "_")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -42,39 +42,3 @@
|
|
42
42
|
method: :delete,
|
43
43
|
data: { confirm: "Are you sure you want to clear the PDF letter cache?\n" },
|
44
44
|
class: "button alert"
|
45
|
-
|
46
|
-
<label class="block">
|
47
|
-
<span class="text-gray-700">Name</span>
|
48
|
-
<input class="form-input mt-1 block w-full" placeholder="Jane Doe">
|
49
|
-
</label>
|
50
|
-
|
51
|
-
<div class="mt-4">
|
52
|
-
<span class="text-gray-700">Account Type</span>
|
53
|
-
<div class="mt-2">
|
54
|
-
<label class="inline-flex items-center">
|
55
|
-
<input type="radio" class="form-radio" name="accountType" value="personal">
|
56
|
-
<span class="ml-2">Personal</span>
|
57
|
-
</label>
|
58
|
-
<label class="inline-flex items-center ml-6">
|
59
|
-
<input type="radio" class="form-radio" name="accountType" value="busines">
|
60
|
-
<span class="ml-2">Business</span>
|
61
|
-
</label>
|
62
|
-
</div>
|
63
|
-
</div>
|
64
|
-
|
65
|
-
<label class="block mt-4">
|
66
|
-
<span class="text-gray-700">Requested Limit</span>
|
67
|
-
<select class="form-select mt-1 block w-full">
|
68
|
-
<option>$1,000</option>
|
69
|
-
<option>$5,000</option>
|
70
|
-
<option>$10,000</option>
|
71
|
-
<option>$25,000</option>
|
72
|
-
</select>
|
73
|
-
</label>
|
74
|
-
|
75
|
-
<div class="flex mt-6">
|
76
|
-
<label class="flex items-center">
|
77
|
-
<input type="checkbox" class="form-checkbox">
|
78
|
-
<span class="ml-2">I agree to the <span class="underline">privacy policy</span></span>
|
79
|
-
</label>
|
80
|
-
</div>
|
@@ -34,7 +34,7 @@ css:
|
|
34
34
|
url: pathology_chart_data_admin_playground_path,
|
35
35
|
wrapper: nil,
|
36
36
|
method: :get,
|
37
|
-
data: { target
|
37
|
+
data: { "charts-target" => "form", action: "ajax:success->charts#redisplay" }) do |f|
|
38
38
|
|
39
39
|
= f.input :patient_id,
|
40
40
|
collection: [[form.patient_id, form.patient_id]],
|
@@ -52,7 +52,7 @@ css:
|
|
52
52
|
include_blank: false
|
53
53
|
= f.submit "Refresh", class: :button
|
54
54
|
|
55
|
-
#chart1 style="height: 300px; width: 500px" data-target="
|
55
|
+
#chart1 style="height: 300px; width: 500px" data-charts-target="chart"
|
56
56
|
|
57
57
|
/ To test tailwindcss
|
58
58
|
.max-w-sm.rounded.overflow-hidden.shadow-lg
|
@@ -1,6 +1,6 @@
|
|
1
1
|
- document = event.document
|
2
2
|
dl.dl-horizontal
|
3
3
|
dt= attr_name(document, :location, suffix: ":")
|
4
|
-
dd= document.location.
|
4
|
+
dd= document.location.presence || t("unspecified")
|
5
5
|
dt= attr_name(event, :notes, suffix: ":")
|
6
6
|
dd== event.notes
|
@@ -1,7 +1,10 @@
|
|
1
1
|
= link_to(patient_hd_mdm_path(patient), class: "button with-icon secondary") do
|
2
2
|
i.fas.fa-users
|
3
3
|
= t(".mdm")
|
4
|
-
= link_to(patient_hd_protocol_path(patient),
|
4
|
+
= link_to(patient_hd_protocol_path(patient),
|
5
|
+
class: "button with-icon secondary",
|
6
|
+
target: "_blank",
|
7
|
+
rel: "noopener") do
|
5
8
|
i.fas.fa-print
|
6
9
|
= t(".protocol")
|
7
10
|
|