diff --git a/Gemfile b/Gemfile
index d7cc2f36..d04880af 100644
--- a/Gemfile
+++ b/Gemfile
@@ -20,7 +20,7 @@ gem 'jbuilder', '~> 2.5' # Build JSON APIs with ease. Read more: https://
# gem 'mini_magick', '~> 4.8' # Use ActiveStorage variant
gem 'bootsnap', '>= 1.1.0', require: false # Reduces boot times through caching; required in config/boot.rb
-gem 'bootstrap' # bootstrap
+gem 'bootstrap', '~> 4.4' # bootstrap
gem 'jquery-rails' # jQuery
gem 'bootstrap-toggle-rails' # bootstrap toggle
gem 'fhir_client', git: 'https://github.com/paciowg/fhir_client.git' # FHIR client that supports fullUrl for transaction entries
diff --git a/Gemfile.lock b/Gemfile.lock
index f413bf6e..2bda7c9c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -17,43 +17,43 @@ GIT
GEM
remote: https://rubygems.org/
specs:
- actioncable (5.2.4.1)
- actionpack (= 5.2.4.1)
+ actioncable (5.2.4.6)
+ actionpack (= 5.2.4.6)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailer (5.2.4.1)
- actionpack (= 5.2.4.1)
- actionview (= 5.2.4.1)
- activejob (= 5.2.4.1)
+ actionmailer (5.2.4.6)
+ actionpack (= 5.2.4.6)
+ actionview (= 5.2.4.6)
+ activejob (= 5.2.4.6)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (5.2.4.1)
- actionview (= 5.2.4.1)
- activesupport (= 5.2.4.1)
+ actionpack (5.2.4.6)
+ actionview (= 5.2.4.6)
+ activesupport (= 5.2.4.6)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.2.4.1)
- activesupport (= 5.2.4.1)
+ actionview (5.2.4.6)
+ activesupport (= 5.2.4.6)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (5.2.4.1)
- activesupport (= 5.2.4.1)
+ activejob (5.2.4.6)
+ activesupport (= 5.2.4.6)
globalid (>= 0.3.6)
- activemodel (5.2.4.1)
- activesupport (= 5.2.4.1)
- activerecord (5.2.4.1)
- activemodel (= 5.2.4.1)
- activesupport (= 5.2.4.1)
+ activemodel (5.2.4.6)
+ activesupport (= 5.2.4.6)
+ activerecord (5.2.4.6)
+ activemodel (= 5.2.4.6)
+ activesupport (= 5.2.4.6)
arel (>= 9.0)
- activestorage (5.2.4.1)
- actionpack (= 5.2.4.1)
- activerecord (= 5.2.4.1)
+ activestorage (5.2.4.6)
+ actionpack (= 5.2.4.6)
+ activerecord (= 5.2.4.6)
marcel (~> 0.3.1)
- activesupport (5.2.4.1)
+ activesupport (5.2.4.6)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@@ -63,34 +63,34 @@ GEM
archive-zip (0.12.0)
io-like (~> 0.3.0)
arel (9.0.0)
- autoprefixer-rails (9.7.3)
- execjs
+ autoprefixer-rails (10.4.0.0)
+ execjs (~> 2)
bcp47 (0.3.3)
i18n
bindex (0.8.1)
- bootsnap (1.4.5)
+ bootsnap (1.7.5)
msgpack (~> 1.0)
- bootstrap (4.4.1)
+ bootstrap (4.6.1)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.14.3, < 2)
sassc-rails (>= 2.0.0)
bootstrap-toggle-rails (2.2.1.0)
builder (3.2.4)
- byebug (11.0.1)
- capybara (3.30.0)
+ byebug (11.1.3)
+ capybara (3.35.3)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
- regexp_parser (~> 1.5)
+ regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
- chartkick (3.3.1)
+ chartkick (4.0.4)
childprocess (3.0.0)
chromedriver-helper (2.1.1)
archive-zip (~> 0.10)
nokogiri (~> 1.8)
- coderay (1.1.2)
+ coderay (1.1.3)
coffee-rails (4.2.2)
coffee-script (>= 2.2.0)
railties (>= 4.0.0)
@@ -98,27 +98,34 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
- concurrent-ruby (1.1.5)
- crass (1.0.5)
+ concurrent-ruby (1.1.8)
+ crass (1.0.6)
date_time_precision (0.8.1)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
- erubi (1.9.0)
+ erubi (1.10.0)
execjs (2.7.0)
- faraday (1.0.1)
+ faraday (1.4.1)
+ faraday-excon (~> 1.1)
+ faraday-net_http (~> 1.0)
+ faraday-net_http_persistent (~> 1.1)
multipart-post (>= 1.2, < 3)
- ffi (1.11.3)
- fhir_dstu2_models (1.0.10)
+ ruby2_keywords (>= 0.0.4)
+ faraday-excon (1.1.0)
+ faraday-net_http (1.0.1)
+ faraday-net_http_persistent (1.1.0)
+ ffi (1.15.0)
+ fhir_dstu2_models (1.0.11)
bcp47 (>= 0.3)
date_time_precision (>= 0.8)
mime-types (>= 3.0)
nokogiri (>= 1.10.4)
- fhir_models (4.1.0)
+ fhir_models (4.1.2)
bcp47 (>= 0.3)
date_time_precision (>= 0.8)
mime-types (>= 3.0)
nokogiri (>= 1.10.4)
- fhir_stu3_models (3.0.1)
+ fhir_stu3_models (3.0.2)
bcp47 (>= 0.3)
date_time_precision (>= 0.8)
mime-types (>= 3.0)
@@ -128,21 +135,21 @@ GEM
http-accept (1.7.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
- i18n (1.7.0)
+ i18n (1.8.10)
concurrent-ruby (~> 1.0)
- io-like (0.3.0)
- jbuilder (2.9.1)
- activesupport (>= 4.2.0)
- jquery-rails (4.3.5)
+ io-like (0.3.1)
+ jbuilder (2.11.2)
+ activesupport (>= 5.0.0)
+ jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- jwt (2.2.2)
+ jwt (2.2.3)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
- loofah (2.4.0)
+ loofah (2.9.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -151,69 +158,75 @@ GEM
mimemagic (~> 0.3.2)
masonry-rails (0.2.4)
railties
- method_source (0.9.2)
+ method_source (1.0.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
- mime-types-data (3.2020.0512)
- mimemagic (0.3.3)
- mini_mime (1.0.2)
- mini_portile2 (2.4.0)
- minitest (5.13.0)
- msgpack (1.3.1)
+ mime-types-data (3.2021.0225)
+ mimemagic (0.3.10)
+ nokogiri (~> 1)
+ rake
+ mini_mime (1.1.0)
+ mini_portile2 (2.5.3)
+ minitest (5.14.4)
+ msgpack (1.4.2)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
netrc (0.11.0)
- nio4r (2.5.2)
- nokogiri (1.10.7)
- mini_portile2 (~> 2.4.0)
- oauth2 (1.4.4)
+ nio4r (2.5.7)
+ nokogiri (1.11.3)
+ mini_portile2 (~> 2.5.0)
+ racc (~> 1.4)
+ nokogiri (1.11.3-x86_64-linux)
+ racc (~> 1.4)
+ oauth2 (1.4.7)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
- pg (1.2.1)
- popper_js (1.14.5)
- pry (0.12.2)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- public_suffix (4.0.2)
- puma (3.12.2)
- rack (2.0.8)
+ pg (1.2.3)
+ popper_js (1.16.0)
+ pry (0.14.1)
+ coderay (~> 1.1)
+ method_source (~> 1.0)
+ public_suffix (4.0.6)
+ puma (3.12.6)
+ racc (1.5.2)
+ rack (2.2.3)
rack-test (1.1.0)
rack (>= 1.0, < 3)
- rails (5.2.4.1)
- actioncable (= 5.2.4.1)
- actionmailer (= 5.2.4.1)
- actionpack (= 5.2.4.1)
- actionview (= 5.2.4.1)
- activejob (= 5.2.4.1)
- activemodel (= 5.2.4.1)
- activerecord (= 5.2.4.1)
- activestorage (= 5.2.4.1)
- activesupport (= 5.2.4.1)
+ rails (5.2.4.6)
+ actioncable (= 5.2.4.6)
+ actionmailer (= 5.2.4.6)
+ actionpack (= 5.2.4.6)
+ actionview (= 5.2.4.6)
+ activejob (= 5.2.4.6)
+ activemodel (= 5.2.4.6)
+ activerecord (= 5.2.4.6)
+ activestorage (= 5.2.4.6)
+ activesupport (= 5.2.4.6)
bundler (>= 1.3.0)
- railties (= 5.2.4.1)
+ railties (= 5.2.4.6)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
- railties (5.2.4.1)
- actionpack (= 5.2.4.1)
- activesupport (= 5.2.4.1)
+ railties (5.2.4.6)
+ actionpack (= 5.2.4.6)
+ activesupport (= 5.2.4.6)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
- rake (13.0.1)
- rb-fsevent (0.10.3)
+ rake (13.0.3)
+ rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
- rdoc (6.2.1)
- regexp_parser (1.6.0)
- responders (3.0.0)
+ rdoc (6.3.1)
+ regexp_parser (2.1.1)
+ responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rest-client (2.1.0)
@@ -221,8 +234,9 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
+ ruby2_keywords (0.0.4)
ruby_dep (1.5.0)
- rubyzip (2.0.0)
+ rubyzip (2.3.0)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
@@ -234,7 +248,7 @@ GEM
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
- sassc (2.2.1)
+ sassc (2.4.0)
ffi (~> 1.9)
sassc-rails (2.1.2)
railties (>= 4.0.0)
@@ -245,24 +259,24 @@ GEM
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
- spring (2.1.0)
+ spring (2.1.1)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
- sprockets-rails (3.2.1)
+ sprockets-rails (3.2.2)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
- thor (1.0.1)
+ thor (1.1.0)
thread_safe (0.3.6)
tilt (2.0.10)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
- tzinfo (1.2.6)
+ tzinfo (1.2.9)
thread_safe (~> 0.1)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
@@ -274,18 +288,19 @@ GEM
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
- websocket-driver (0.7.1)
+ websocket-driver (0.7.3)
websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.4)
+ websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
PLATFORMS
ruby
+ x86_64-linux
DEPENDENCIES
bootsnap (>= 1.1.0)
- bootstrap
+ bootstrap (~> 4.4)
bootstrap-toggle-rails
byebug
capybara (>= 2.15)
@@ -316,4 +331,4 @@ RUBY VERSION
ruby 2.6.3p62
BUNDLED WITH
- 2.1.4
+ 2.2.17
diff --git a/app/assets/javascripts/splasch_observation.js b/app/assets/javascripts/splasch_observation.js
new file mode 100644
index 00000000..5dc5db78
--- /dev/null
+++ b/app/assets/javascripts/splasch_observation.js
@@ -0,0 +1,17 @@
+$(() => {
+ update_splasch_code_selection_by_category();
+});
+
+const update_splasch_code_selection_by_category = function () {
+ codes = $('#splasch_observation_code').html();
+
+ $('#splasch_observation_category').change(function() {
+ category = $('#splasch_observation_category :selected').val();
+ options = $(codes).filter("optgroup[label='" + category + "']").html();
+ if (options) {
+ $('#splasch_observation_code').html(options);
+ } else {
+ $('#splasch_observation_code').empty();
+ }
+ });
+}
diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss
index bc7cfe8c..e02285a0 100644
--- a/app/assets/stylesheets/style.scss
+++ b/app/assets/stylesheets/style.scss
@@ -140,6 +140,10 @@ pre {
color: #888;
}
+#add-splash-observation {
+ float: right;
+}
+
@media (min-width: 200px) and (max-width: 767px) {
.welcome-form {
display: block;
diff --git a/app/controllers/advance_directives_controller.rb b/app/controllers/advance_directives_controller.rb
index 9c757860..797ec081 100644
--- a/app/controllers/advance_directives_controller.rb
+++ b/app/controllers/advance_directives_controller.rb
@@ -23,21 +23,21 @@ def index
birthdate: '1950-11-01'
}
}
- ).resource.entry.first.resource
+ ).resource&.entry&.first&.resource
- @patient = Patient.new(fhir_patient, @fhir_client)
+ @patient = fhir_patient ? Patient.new(fhir_patient, @fhir_client) : nil
fhir_document_refs = @fhir_client.search(FHIR::DocumentReference,
search: {
parameters: {
- patient: @patient.id,
+ patient: @patient&.id,
status: 'current'
}
}
- ).resource.entry
+ ).resource&.entry
@documents = []
- fhir_document_refs.each do |fhir_document_ref|
+ fhir_document_refs&.each do |fhir_document_ref|
@documents << DocumentReference.new(fhir_document_ref.resource)
end
end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index dd807451..dd289a72 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -16,9 +16,14 @@ def index
read(FHIR::Patient, patient_id).resource
@patient = Patient.new(fhir_patient, SessionHandler.fhir_client(session.id))
- @medications = @patient.medications
- @functional_statuses = @patient.bundled_functional_statuses
- @cognitive_statuses = @patient.bundled_cognitive_statuses
+ #@medications = @patient.medications
+ #@functional_statuses = @patient.bundled_functional_statuses
+ #@cognitive_statuses = @patient.bundled_cognitive_statuses
+ @splasch_observations = @patient.splasch_observations
+ # @spoken_language_comprehension_observations = @patient.spoken_language_comprehension_observations
+ # @spoken_language_expression_observations = @patient.spoken_language_expression_observations
+ # @swallowing_observations = @patient.swallowing_observations
+ #@splasch_collections = @patient.splasch_collections
else
redirect_to :root
end
diff --git a/app/controllers/patients_controller.rb b/app/controllers/patients_controller.rb
index 391c6424..251cb3fb 100644
--- a/app/controllers/patients_controller.rb
+++ b/app/controllers/patients_controller.rb
@@ -10,7 +10,10 @@ def index
# GET /patients/1
# GET /patients/1.json
def show
- redirect_to :controller => 'dashboard', :action => 'index'
+ fhir_client = SessionHandler.fhir_client(session.id)
+ fhir_patient = fhir_client.read(FHIR::Patient, params[:id]).resource
+
+ @patient = Patient.new(fhir_patient, fhir_client) unless fhir_patient.nil?
end
# GET /patients/new
diff --git a/app/controllers/splasch_collections_controller.rb b/app/controllers/splasch_collections_controller.rb
new file mode 100644
index 00000000..dd82377b
--- /dev/null
+++ b/app/controllers/splasch_collections_controller.rb
@@ -0,0 +1,33 @@
+################################################################################
+#
+# SPLASCH Collections Controller
+#
+# SPLASCH = SPeech, LAnguage, Swallowing, cognitive Communication, and Hearing
+#
+# Copyright (c) 2022 The MITRE Corporation. All rights reserved.
+#
+################################################################################
+
+class SplaschCollectionsController < ApplicationController
+
+ def index
+ fhir_client = SessionHandler.fhir_client(session.id)
+ fhir_patient = SessionHandler.fhir_client(session.id).read(FHIR::Patient,
+ params['patient']).resource
+
+ @patient = Patient.new(fhir_patient, fhir_client)
+ @splasch_collections = @patient.all_splasch_collections
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def show
+ fhir_client = SessionHandler.fhir_client(session.id)
+ fhir_splasch_collection = fhir_client.read(
+ FHIR::Observation, params[:id]).resource
+ @splasch_collection = SplaschCollection.new(
+ fhir_splasch_collection, fhir_client)
+ @splasch_observations = @splasch_collection.splasch_observations
+ end
+
+end
diff --git a/app/controllers/splasch_observations_controller.rb b/app/controllers/splasch_observations_controller.rb
new file mode 100644
index 00000000..1a2b9429
--- /dev/null
+++ b/app/controllers/splasch_observations_controller.rb
@@ -0,0 +1,222 @@
+################################################################################
+#
+# SPLASCH Observations Controller
+#
+# SPLASCH = SPeech, LAnguage, Swallowing, cognitive Communication, and Hearing
+#
+# Copyright (c) 2022 The MITRE Corporation. All rights reserved.
+#
+################################################################################
+
+class SplaschObservationsController < ApplicationController
+
+ before_action :get_fhir_client
+
+ SPEECH_COMPREHENSION = {
+ "understand-simple-routine-low-demand": {
+ display: "Understand simple messages in routine low demand situations",
+ definition: "How often does the individual understand simple messages/conversations related to routine daily activities in LOW demand situations (e.g., following simple directions)?"
+ },
+ "understand-complex-low-demand": {
+ display: "Understand complex messages in low demand situations",
+ definition: "How often does the individual understand complex messages, as expected for chronological age, in LOW demand situations (e.g., story/lecture, sequencing of events, drawing appropriate conclusions, inferences, humor, subtle references)?"
+ },
+ "understand-simple-routine-high-demand": {
+ display: "Understand simple messages in routine high demand situations",
+ definition: "How often does the individual understand simple messages/conversations related to routine daily activities in HIGH demand situations (e.g., following simple directions)?"
+ },
+ "understand-complex-high-demand": {
+ display: "Understand complex messages in high demand situations",
+ definition: "How often does the individual understand complex messages, as expected for chronological age, in HIGH demand situations (e.g., story/lecture, sequencing of events, drawing appropriate conclusions, inferences, humor, subtle references)?"
+ },
+ "function-comprehension-without-assistance": {
+ display: "Function without assistance due to comprehension deficit",
+ definition: "How often does the individual function safely WITHOUT additional supervision/assistance (in excess of chronological age expectations) due to comprehension deficits?",
+ },
+ "communicate-without-assistance": {
+ display: "Participate in communication without assistance",
+ definition: "How often does the individual participate in communication exchanges WITHOUT additional assistance from communication partner (no more than would be expected for chronological age)?",
+ }
+ }
+
+ SPEECH_EXPRESSION = {
+ "exhibit-distracting-expression-difficulties": {
+ display: "Expression of distracting spoken language expression difficulties",
+ definition: "How often does the individual exhibit spoken language expression difficulties that are noticeable or distracting to the listener? Note: consider message content, form, pauses, extra time."
+ },
+ "simple-meaningful-word-expression": {
+ display: "Expression of simple meaningful word or phrases",
+ definition: "How often does the individual produce simple spoken word and phrases that are meaningful?"
+ },
+ "verbal-form-low-demand": {
+ display: "Produce verbal messages with appropriate form in low demand situations",
+ definition: "How often does the individual produce verbal messages with appropriate FORM in LOW demand situations? Note: consider phonology, morphology and syntax when assessing functional level."
+ },
+ "verbal-content-low-demand": {
+ display: "Produce verbal messages with appropriate content in low demand situations",
+ definition: "How often does the individual produce verbal messages with appropriate CONTENT in LOW demand situations? Note: consider semantic meaning when assessing functional level."
+ },
+ "verbal-form-high-demand": {
+ display: "Produce verbal messages with appropriate form in high demand situations",
+ definition: "How often does the individual produce verbal messages with appropriate FORM in HIGH demand situations (e.g., academic or work-related tasks)? Note: consider phonology, morphology and syntax when assessing functional level."
+ },
+ "verbal-content-high-demand": {
+ display: "Produce verbal messages with appropriate content in high demand situations",
+ definition: "How often does the individual produce verbal messages with appropriate CONTENT in HIGH demand situations (e.g., academic or work-related tasks)? Note: consider semantic meaning when assessing functional level."
+ },
+ "communicate-without-assistance": {
+ display: "Participate in communication without assistance",
+ definition: "How often does the individual participate in communication exchanges WITHOUT additional assistance from communication partner (no more than would be expected for chronological age)?"
+ }
+ }
+
+ SWALLOWING = {
+ "modified-solid-diet": {
+ display: "Recommended modified solids diet",
+ definition: "What modified diet is recommended for the individual to swallow solids safely?"
+ },
+ "modified-liquid-diet": {
+ display: "Recommended modified liquids diet",
+ definition: "What modified liquid is recommended for the individual to swallow safely?"
+ },
+ "non-oral-diet-sustenance": {
+ display: "Nutrition and hydration diet delivered through non-oral means",
+ definition: "What percentage of the individual's daily diet is delivered through non-oral means in order to maintain adequate nutrition and hydration?"
+ },
+ "oral-containment-secretion-difficulty": {
+ display: "Difficulties with oral containment or secretion management",
+ definition: "How often does the individual exhibit difficulties with oral containment or secretion management?"
+ },
+ "assistance-swallowing-safety": {
+ display: "Require assistance or supervision for swallowing safety",
+ definition: "How often does the individual require supervision/assistance at meal time due to swallowing safety (e.g., risk of aspiration, pocketing)? Note: If individual is NPO, rate supervision/assistance as 91-100% of the time."
+ }
+ }
+
+ CATEGORIES = {
+ "spoken-language-comprehension": {
+ text: "Spoken language comprehension",
+ system: "http://hl7.org/fhir/us/pacio-splasch/CodeSystem/SPLASCHSpeechComprehensionObservationCS",
+ codes: SPEECH_COMPREHENSION
+ },
+ "spoken-language-expression": {
+ text: "Spoken language expression",
+ system: "http://hl7.org/fhir/us/pacio-splasch/CodeSystem/SPLASCHSpeechExpressionObservationCS",
+ codes: SPEECH_EXPRESSION
+ },
+ "swallowing": {
+ text: "Swallowing",
+ system: "http://hl7.org/fhir/us/pacio-splasch/CodeSystem/SPLASCHSwallowingObservationCS",
+ codes: SWALLOWING
+ }
+ }
+
+ VALUES = {
+ "LA6270-8": "Never",
+ "LA10066-1": "Rarely",
+ "LA10082-8": "Sometimes",
+ "LA10044-8": "Often",
+ "LA9933-8": "Always"
+ }
+
+ #-----------------------------------------------------------------------------
+
+ def show
+ fhir_observation = @fhir_client.read(FHIR::Observation, params[:id]).resource
+ @splasch_observation = SplaschObservation.new(fhir_observation)
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def new
+ @patient = params[:patient]
+ @categories = CATEGORIES.map { |key, value| [ value[:text], key.to_s ] }
+
+ # Build list of codes grouped by category
+ @codes = CATEGORIES.map do |key, value|
+ [
+ key.to_s,
+ value[:codes].map do |code_key, code_value|
+ [ code_value[:display], code_key.to_s ]
+ end
+ ]
+ end
+
+ @values = VALUES.map { |key, value| [ value, key.to_s ] }
+
+ bundle = @fhir_client.search(FHIR::Practitioner).resource
+ @practitioners = bundle.entry.map {|p| [ p.resource.name.first.text, p.resource.id ] }
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def create
+ new_observation = params[:splasch_observation]
+
+ fhir_observation = FHIR::Observation.new
+ fhir_observation.status = "final"
+
+ category = new_observation[:category].to_sym
+
+ coding = FHIR::Coding.new
+ coding.system = "http://hl7.org/fhir/us/pacio-splasch/CodeSystem/SPLASCHObservationCategoryCS"
+ coding.code = new_observation[:category]
+ coding.display = CATEGORIES[category][:text]
+ codeable_concept = FHIR::CodeableConcept.new
+ codeable_concept.coding = [ coding ]
+ fhir_observation.category = [ codeable_concept ]
+
+ code = new_observation[:code].to_sym
+
+ coding = FHIR::Coding.new
+ coding.system = CATEGORIES[category][:system]
+ coding.code = new_observation[:code]
+ coding.display = CATEGORIES[category][:codes][code][:display]
+ fhir_observation.code = FHIR::CodeableConcept.new
+ fhir_observation.code.coding = [ coding ]
+
+ value = new_observation[:value].to_sym
+
+ observation_value = FHIR::Coding.new
+ observation_value.system = "http://hl7.org/fhir/us/pacio-splasch/CodeSystem/SPLASCHFrequencyCS"
+ observation_value.code = new_observation[:value]
+ observation_value.display = VALUES[value]
+ fhir_observation.valueCodeableConcept = FHIR::CodeableConcept.new
+ fhir_observation.valueCodeableConcept.coding = [ observation_value ]
+
+ fhir_observation.effectiveDateTime = Time.now.strftime("%FT%T%:z")
+
+ fhir_observation.subject = FHIR::Reference.new
+ fhir_observation.subject.reference = [ "Patient", Rails::Html::LinkSanitizer.new.sanitize(params[:patient]) ].join('/')
+
+ fhir_observation.performer = FHIR::Reference.new
+ fhir_observation.performer.reference = [ "Practitioner", Rails::Html::LinkSanitizer.new.sanitize(new_observation[:performer]) ].join('/')
+
+ result = @fhir_client.create(fhir_observation)
+
+ respond_to do |format|
+ if result.response[:code] == "201"
+ format.html { redirect_to dashboard_path(patient: params[:patient]),
+ success: "Observation was successfully created (ID=#{result.id})" }
+ format.json { render :show, status: :created,
+ location: "Observation/#{observation_id}" }
+ else
+ format.html { redirect_to dashboard_path(patient: params[:patient]),
+ alert: 'Creating new observation failed' }
+ # format.html { redirect_to new_splasch_observation_path(patient: params[:patient]),
+ # alert: 'Creating new observation failed' }
+ format.json { render json: 'Creating new observation failed',
+ status: :unprocessable_entity }
+ end
+ end
+ end
+
+ #-----------------------------------------------------------------------------
+ private
+ #-----------------------------------------------------------------------------
+
+ def get_fhir_client
+ @fhir_client ||= SessionHandler.fhir_client(session.id)
+ end
+
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 6d6179fc..233cae03 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -36,12 +36,46 @@ def display_human_name(name)
#-----------------------------------------------------------------------------
+ def display_photo(photo, gender, options)
+ options[:class] = 'img-fluid'
+ if photo.present?
+ result = image_tag(photo, options)
+ else
+ result = image_tag(gender == "female" ? "woman.svg" : "man-user.svg", options)
+ end
+
+ return result
+ end
+
+ #-----------------------------------------------------------------------------
+
def display_telecom(telecom)
sanitize(telecom.system + ': ' + number_to_phone(telecom.value, area_code: true))
end
#-----------------------------------------------------------------------------
+ def display_marital_status(marital_status)
+ marital_status_values = {
+ A: 'Annulled Marriage',
+ D: 'Divorced',
+ I: 'Interlocutory Subject',
+ L: 'Legally Separated',
+ M: 'Married',
+ C: 'Common Law',
+ P: 'Polygamous',
+ T: 'Domestic Partner',
+ U: 'Unmarried',
+ S: 'Never Married',
+ W: 'Widowed'
+ }
+
+ marital_status_values[marital_status.coding.first.code.to_sym] ||
+ sanitize(marital_status.coding.first.code)
+ end
+
+ #-----------------------------------------------------------------------------
+
def display_identifier(identifier)
sanitize("#{identifier.assigner.display}: ( #{identifier.type.text}, #{identifier.value})")
# sanitize([identifier.type.text, identifier.value, identifier.assigner.display].join(', '))
@@ -57,8 +91,19 @@ def display_list(list)
#-----------------------------------------------------------------------------
+ def display_categories(categories)
+ result = []
+ categories.each do |category|
+ result << display_coding_list(category.coding)
+ end
+
+ result = sanitize(result.join('
'))
+ end
+
+ #-----------------------------------------------------------------------------
+
def display_code(code)
- sanitize(code.coding[0].display)
+ sanitize(code.coding[0].display) unless code.nil?
end
#-----------------------------------------------------------------------------
@@ -143,7 +188,7 @@ def display_date(datetime)
#-----------------------------------------------------------------------------
def display_datetime(datetime)
- datetime.present? ? sanitize(datetime.strftime('%m/%d/%Y')) : "No date/time"
+ datetime.present? ? sanitize(datetime.strftime('%m/%d/%Y %I:%M%p')) : "No date/time"
end
#-----------------------------------------------------------------------------
@@ -188,6 +233,12 @@ def display_array(array)
#-----------------------------------------------------------------------------
+ def display_subject(subject)
+ display_reference(subject)
+ end
+
+ #-----------------------------------------------------------------------------
+
def display_performers(performers)
list = []
@@ -195,7 +246,13 @@ def display_performers(performers)
list << display_reference(performer)
end
- raw(list.join(', '))
+ raw(list.join('
'))
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def display_div(div)
+ sanitize(div)
end
#-----------------------------------------------------------------------------
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index 2ebced74..03e34d7a 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -7,16 +7,4 @@
################################################################################
module DashboardHelper
-
- def display_photo(photo, gender, options)
- options[:class] = 'img-fluid'
- if photo.present?
- result = image_tag(photo, options)
- else
- result = image_tag(gender == "female" ? "woman.svg" : "man-user.svg", options)
- end
-
- return result
- end
-
end
diff --git a/app/models/patient.rb b/app/models/patient.rb
index d7065c27..edaafc47 100644
--- a/app/models/patient.rb
+++ b/app/models/patient.rb
@@ -43,10 +43,12 @@ def medications
}
fhir_bundle = @fhir_client.search(FHIR::Medication, search_param).resource
- fhir_medications = filter(fhir_bundle.entry.map(&:resource), 'Medication')
+ unless fhir_bundle.nil?
+ fhir_medications = filter(fhir_bundle.entry.map(&:resource), 'Medication')
- fhir_medications.each do |fhir_medication|
- medications << Medication.new(fhir_medication) unless fhir_medication.nil?
+ fhir_medications.each do |fhir_medication|
+ medications << Medication.new(fhir_medication) unless fhir_medication.nil?
+ end
end
return medications
@@ -66,13 +68,15 @@ def medication_statements
}
fhir_bundle = @fhir_client.search(FHIR::MedicationStatement, search_param).resource
- fhir_medication_statements = filter(fhir_bundle.entry.map(&:resource),
- 'MedicationStatement')
-
- fhir_medication_statements.each do |fhir_medication_statement|
- medication_statements <<
- MedicationStatement.new(fhir_medication_statement, @fhir_client) unless
- fhir_medication_statement.nil?
+ unless fhir_bundle.nil?
+ fhir_medication_statements = filter(fhir_bundle.entry.map(&:resource),
+ 'MedicationStatement')
+
+ fhir_medication_statements.each do |fhir_medication_statement|
+ medication_statements <<
+ MedicationStatement.new(fhir_medication_statement, @fhir_client) unless
+ fhir_medication_statement.nil?
+ end
end
return medication_statements
@@ -93,11 +97,13 @@ def bundled_functional_statuses
}
fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource
- fhir_functional_statuses = filter(fhir_bundle.entry.map(&:resource), 'Observation')
- # puts fhir_functional_statuses
- fhir_functional_statuses.each do |fhir_functional_status|
- bundled_functional_statuses << BundledFunctionalStatus.new(fhir_functional_status, @fhir_client) unless
- fhir_functional_status.nil?
+ unless fhir_bundle.nil?
+ fhir_functional_statuses = filter(fhir_bundle.entry.map(&:resource), 'Observation')
+ # puts fhir_functional_statuses
+ fhir_functional_statuses.each do |fhir_functional_status|
+ bundled_functional_statuses << BundledFunctionalStatus.new(fhir_functional_status, @fhir_client) unless
+ fhir_functional_status.nil?
+ end
end
return bundled_functional_statuses
@@ -112,17 +118,19 @@ def bundled_cognitive_statuses
{ parameters:
{
subject: ["Patient", @id].join('/'),
- _profile: 'http://paciowg.github.io/functional-status-ig/StructureDefinition/pacio-bcs'
+ _profile: 'http://paciowg.github.io/cognitive-status-ig/StructureDefinition/pacio-bcs'
}
}
}
fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource
- fhir_cognitive_statuses = filter(fhir_bundle.entry.map(&:resource), 'Observation')
+ unless fhir_bundle.nil?
+ fhir_cognitive_statuses = filter(fhir_bundle.entry.map(&:resource), 'Observation')
- fhir_cognitive_statuses.each do |fhir_cognitive_status|
- bundled_cognitive_statuses << BundledCognitiveStatus.new(fhir_cognitive_status, @fhir_client) unless
- fhir_cognitive_status.nil?
+ fhir_cognitive_statuses.each do |fhir_cognitive_status|
+ bundled_cognitive_statuses << BundledCognitiveStatus.new(fhir_cognitive_status, @fhir_client) unless
+ fhir_cognitive_status.nil?
+ end
end
return bundled_cognitive_statuses
@@ -130,6 +138,140 @@ def bundled_cognitive_statuses
#-----------------------------------------------------------------------------
+ def splasch_collections
+ splasch_collections = []
+
+ search_param = { search:
+ { parameters:
+ {
+ subject: ["Patient", @id].join('/'),
+ _profile: '???'
+ }
+ }
+ }
+
+ fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource
+ unless fhir_bundle.nil?
+ fhir_splasch_collections = filter(fhir_bundle.entry.map(&:resource), 'Observation')
+
+ fhir_splasch_collections.each do |fhir_splasch_collection|
+ splasch_collections << SplaschCollection.new(fhir_splasch_collection, @fhir_client) unless
+ fhir_splasch_collection.nil?
+ end
+ end
+
+ return splasch_collections
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def splasch_observations
+ splasch_observations = []
+
+ search_param = { search:
+ { parameters:
+ {
+ _count: 200
+ }
+ }
+ }
+
+ fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource
+ unless fhir_bundle.nil?
+ fhir_splasch_observations = filter(fhir_bundle.entry.map(&:resource), 'Observation')
+
+ fhir_splasch_observations.each do |fhir_splasch_observation|
+ splasch_observations << SplaschObservation.new(fhir_splasch_observation) unless
+ fhir_splasch_observation.nil?
+ end
+ end
+
+ return splasch_observations.sort_by(&:effective_datetime).reverse
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def spoken_language_comprehension_observations
+ spoken_language_comprehension_observations = []
+
+ search_param = { search:
+ { parameters:
+ {
+ subject: ["Patient", @id].join('/'),
+ _profile: 'http://paciowg.github.io/splasch-ig/StructureDefinition/splasch-SpokenLanguageComprehensionObservation'
+ }
+ }
+ }
+
+ fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource
+ unless fhir_bundle.nil?
+ fhir_observations = filter(fhir_bundle.entry.map(&:resource), 'Observation')
+
+ fhir_observations.each do |fhir_observation|
+ spoken_language_comprehension_observations << SplaschObservation.new(fhir_observation) unless
+ fhir_observation.nil?
+ end
+ end
+
+ return spoken_language_comprehension_observations
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def spoken_language_expression_observations
+ spoken_language_expression_observations = []
+
+ search_param = { search:
+ { parameters:
+ {
+ subject: ["Patient", @id].join('/'),
+ _profile: 'http://paciowg.github.io/splasch-ig/StructureDefinition/splasch-SpokenLanguageExpressionObservation'
+ }
+ }
+ }
+
+ fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource
+ unless fhir_bundle.nil?
+ fhir_observations = filter(fhir_bundle.entry.map(&:resource), 'Observation')
+
+ fhir_observations.each do |fhir_observation|
+ spoken_language_expression_observations << SplaschObservation.new(fhir_observation) unless
+ fhir_observation.nil?
+ end
+ end
+
+ return spoken_language_expression_observations
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def swallowing_observations
+ swallowing_observations = []
+
+ search_param = { search:
+ { parameters:
+ {
+ subject: ["Patient", @id].join('/'),
+ _profile: 'http://paciowg.github.io/splasch-ig/StructureDefinition/splasch-SwallowingObservation'
+ }
+ }
+ }
+
+ fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource
+ unless fhir_bundle.nil?
+ fhir_observations = filter(fhir_bundle.entry.map(&:resource), 'Observation')
+
+ fhir_observations.each do |fhir_observation|
+ swallowing_observations << SplaschObservation.new(fhir_observation) unless
+ fhir_observation.nil?
+ end
+ end
+
+ return swallowing_observations
+ end
+
+ #-----------------------------------------------------------------------------
+
def all_functional_statuses
all_functional_statuses = []
@@ -168,6 +310,25 @@ def all_cognitive_statuses
#-----------------------------------------------------------------------------
+ def all_splasch_collections
+ all_splasch_collections = []
+
+ fhir_splasch_collections = get_fhir_statuses_with_profile(
+ 'http://paciowg.github.io/functional-status-ig/StructureDefinition/pacio-bcs')
+ fhir_splasch_collections.each do |fhir_splasch_collection|
+ splasch_collections = {}
+ splasch_collections[:bundle] =
+ SplaschCollection.new(fhir_splasch_collection, @fhir_client) unless
+ fhir_splasch_collection.nil?
+ splasch_collections[:assessments] = splasch_collections[:bundle].splasch_observations
+ all_splasch_collections << splasch_collections
+ end
+
+ return all_splasch_collections
+ end
+
+ #-----------------------------------------------------------------------------
+
def age
now = Time.now.to_date
age = now.year - @birth_date.year
diff --git a/app/models/splasch_collection.rb b/app/models/splasch_collection.rb
new file mode 100644
index 00000000..334dba1d
--- /dev/null
+++ b/app/models/splasch_collection.rb
@@ -0,0 +1,65 @@
+################################################################################
+#
+# SPLASCH Collection Model
+#
+# Copyright (c) 2022 The MITRE Corporation. All rights reserved.
+#
+################################################################################
+
+class SplaschCollection < Resource
+
+ include ActiveModel::Model
+
+ attr_reader :id, :text, :based_on, :part_of, :status, :category, :code,
+ :subject, :focus, :encounter, :effective, :performer,
+ :value_string, :data_absent_reason, :interpretation, :note,
+ :body_site, :method, :specimen, :device, :reference_range,
+ :has_member, :derived_from, :component
+
+ #-----------------------------------------------------------------------------
+
+ def initialize(fhir_splasch_collection, fhir_client)
+ @id = fhir_splasch_collection.id
+ @text = fhir_splasch_collection.text
+ @based_on = fhir_splasch_collection.basedOn
+ @part_of = fhir_splasch_collection.partOf
+ @status = fhir_splasch_collection.status
+ @category = fhir_splasch_collection.category
+ @code = fhir_splasch_collection.code
+ @subject = fhir_splasch_collection.subject
+ @focus = fhir_splasch_collection.focus
+ @encounter = fhir_splasch_collection.encounter
+ @effective = DateTime.parse(fhir_splasch_collection.effective)
+ @performer = fhir_splasch_collection.performer
+ @value_string = fhir_splasch_collection.valueString
+ @data_absent_reason = fhir_splasch_collection.dataAbsentReason
+ @interpretation = fhir_splasch_collection.interpretation
+ @note = fhir_splasch_collection.note
+ @body_site = fhir_splasch_collection.bodySite
+ #@method = fhir_splasch_collection.method
+ @specimen = fhir_splasch_collection.specimen
+ @device = fhir_splasch_collection.device
+ @reference_range = fhir_splasch_collection.referenceRange
+ @has_member = fhir_splasch_collection.hasMember
+ @derived_from = fhir_splasch_collection.derivedFrom
+ @component = fhir_splasch_collection.component
+
+ @fhir_client = fhir_client
+ end
+
+ #-----------------------------------------------------------------------------
+
+ def splasch_observations
+ splasch_observations = []
+ self.has_member.each do |member|
+ member_id = member.reference.split('/').last
+ fhir_splasch_observation = @fhir_client.read(FHIR::Observation, member_id).resource
+
+ splasch_observations << SplaschObservation.new(fhir_splasch_observation) unless
+ fhir_splasch_observation.nil?
+ end
+
+ return splasch_observations
+ end
+
+end
diff --git a/app/models/splasch_observation.rb b/app/models/splasch_observation.rb
new file mode 100644
index 00000000..5c4faba8
--- /dev/null
+++ b/app/models/splasch_observation.rb
@@ -0,0 +1,36 @@
+################################################################################
+#
+# SPLASCH Observation Model
+#
+# SPLASCH = SPeech, LAnguage, Swallowing, cognitive Communication, and Hearing
+#
+# Copyright (c) 2022 The MITRE Corporation. All rights reserved.
+#
+################################################################################
+
+class SplaschObservation < Resource
+
+ include ActiveModel::Model
+
+ attr_reader :id, :div
+ attr_accessor :categories, :text, :status, :code, :subject, :performer,
+ :effective_datetime, :value
+
+ #-----------------------------------------------------------------------------
+
+ def initialize(fhir_splasch_observation)
+ @id = fhir_splasch_observation.id
+ @text = fhir_splasch_observation.text
+ if @text.present?
+ @div = fhir_splasch_observation.text.div
+ end
+ @status = fhir_splasch_observation.status
+ @categories = fhir_splasch_observation.category
+ @code = fhir_splasch_observation.code
+ @subject = fhir_splasch_observation.subject
+ @performer = fhir_splasch_observation.performer
+ @effective_datetime = DateTime.parse(fhir_splasch_observation.effectiveDateTime)
+ @value = fhir_splasch_observation.valueCodeableConcept
+ end
+
+end
diff --git a/app/views/dashboard/_patient-card.html.erb b/app/views/dashboard/_patient-card.html.erb
index 0cb8337f..c6e16a24 100644
--- a/app/views/dashboard/_patient-card.html.erb
+++ b/app/views/dashboard/_patient-card.html.erb
@@ -26,7 +26,7 @@
| <%= display_date(splasch_collection.effective) %> | +<%= link_to display_code(splasch_collection.code), + splasch_collection_path(id: splasch_collection.id) %> | + +
| <%= display_datetime(observation.effective_datetime) %> | +<%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %> | +
| <%= display_date(observation.effective) %> | +<%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %> | + +
| <%= display_date(observation.effective) %> | +<%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %> | + +
| <%= display_date(observation.effective) %> | +<%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %> | + +
<%= notice %>
- -<%= link_to 'Edit', edit_patient_path(@patient) %> | -<%= link_to 'Back', patients_path %> +| Phone/Fax: | -
- <% @practitioner.telecoms.each do |telecom| %>
- <%= display_telecom(telecom) %> - <% end %> - |
-
| Locations: | -- <% @practitioner.addresses.each do |address| %> - <%= render partial: 'partials/address', - locals: { address: address } %> - <% end %> - | -
| Gender: | -<%= @practitioner.gender %> | -
| Licenses: | -
- <% @practitioner.qualifications.each do |qualification| %>
- <%= render partial: 'qualification',
- locals: { qualification: qualification } %>
- Period: <%= display_period(qualification.period) %> - Issuer: <%= display_issuer(qualification.issuer) %> - - <% end %> - |
-
| Phone/Fax: | +
+ <% @practitioner.telecoms.each do |telecom| %>
+ <%= display_telecom(telecom) %> + <% end %> + |
+
| Locations: | ++ <% @practitioner.addresses.each do |address| %> + <%= render partial: 'partials/address', + locals: { address: address } %> + <% end %> + | +
| Gender: | +<%= @practitioner.gender %> | +
| Licenses: | +
+ <% @practitioner.qualifications.each do |qualification| %>
+ <%= render partial: 'qualification',
+ locals: { qualification: qualification } %>
+ Period: <%= display_period(qualification.period) %> + Issuer: <%= display_issuer(qualification.issuer) %> + + <% end %> + |
+
| Assessment | + <% @splasch_collections.each do |splasch_collection| %> +<%= display_date(splasch_collection[:bundle].effective) %> | + <% end %> + <% assessment_count = @splasch_collections.first[:assessments].count %> + <% i = 0 %> + <% while i < assessment_count %> +
|---|---|
| <%= @splasch_collections.first[:assessments][i].code.text %> | + <% @splasch_collections.each do |splasch_collection| %> ++ <%= splasch_collection[:assessments][i].value.text %> + | + <% end %> +
| Assessment | +Observation | +
|---|---|
| <%= @splasch_observation.code.coding[0].display %> | +<%= @splasch_observation.value.coding[0].display %> | +