From e24aba80f3a4f15bcc1fc5839c33f15317752a6b Mon Sep 17 00:00:00 2001 From: vanessuniq Date: Thu, 13 May 2021 16:17:05 -0400 Subject: [PATCH 1/7] Avoided an undefined method for nil object when no resource is retrieved --- Gemfile.lock | 200 +++++++++--------- .../advance_directives_controller.rb | 10 +- 2 files changed, 110 insertions(+), 100 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f413bf6e..c73807c4 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.2.5.0) + execjs (< 2.8.0) 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 (5.0.0) autoprefixer-rails (>= 9.1.0) - popper_js (>= 1.14.3, < 2) + popper_js (>= 2.9.2, < 3) 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,71 @@ 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) + 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-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 (2.9.2) + 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 +230,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 +244,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 +255,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,14 +284,14 @@ 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) @@ -316,4 +326,4 @@ RUBY VERSION ruby 2.6.3p62 BUNDLED WITH - 2.1.4 + 2.2.17 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 From a162225184f30a695ec188aeccdcd77c511cafab Mon Sep 17 00:00:00 2001 From: "Hill, Dave" Date: Sun, 9 Jan 2022 12:27:29 -0500 Subject: [PATCH 2/7] Added retrieval and display of SPLASCH observations. --- Gemfile.lock | 5 + app/controllers/dashboard_controller.rb | 5 + .../splasch_collections_controller.rb | 33 +++ .../splasch_observations_controller.rb | 19 ++ app/helpers/application_helper.rb | 23 +- app/models/patient.rb | 202 ++++++++++++++++-- app/models/splasch_collection.rb | 65 ++++++ app/models/splasch_observation.rb | 30 +++ app/views/dashboard/_patient-card.html.erb | 2 +- .../_splasch-collections-card.html.erb | 17 ++ .../_splasch-observations-card.html.erb | 16 ++ ...poken-language-comprehension-card.html.erb | 17 ++ .../_spoken-language-expression-card.html.erb | 17 ++ app/views/dashboard/_swallowing-card.html.erb | 17 ++ app/views/dashboard/index.html.erb | 3 + app/views/partials/_address.html.erb | 16 +- app/views/splasch_observations/index.html.erb | 23 ++ app/views/splasch_observations/show.html.erb | 19 ++ config/routes.rb | 2 + 19 files changed, 503 insertions(+), 28 deletions(-) create mode 100644 app/controllers/splasch_collections_controller.rb create mode 100644 app/controllers/splasch_observations_controller.rb create mode 100644 app/models/splasch_collection.rb create mode 100644 app/models/splasch_observation.rb create mode 100644 app/views/dashboard/_splasch-collections-card.html.erb create mode 100644 app/views/dashboard/_splasch-observations-card.html.erb create mode 100644 app/views/dashboard/_spoken-language-comprehension-card.html.erb create mode 100644 app/views/dashboard/_spoken-language-expression-card.html.erb create mode 100644 app/views/dashboard/_swallowing-card.html.erb create mode 100644 app/views/splasch_observations/index.html.erb create mode 100644 app/views/splasch_observations/show.html.erb diff --git a/Gemfile.lock b/Gemfile.lock index c73807c4..7de7b251 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -166,6 +166,7 @@ GEM 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) @@ -173,6 +174,9 @@ GEM multipart-post (2.1.1) netrc (0.11.0) 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) @@ -291,6 +295,7 @@ GEM nokogiri (~> 1.8) PLATFORMS + ruby x86_64-linux DEPENDENCIES diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index dd807451..a5691f28 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -19,6 +19,11 @@ def index @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/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..5852dab1 --- /dev/null +++ b/app/controllers/splasch_observations_controller.rb @@ -0,0 +1,19 @@ +################################################################################ +# +# SPLASCH Observations Controller +# +# SPLASCH = SPeech, LAnguage, Swallowing, cognitive Communication, and Hearing +# +# Copyright (c) 2022 The MITRE Corporation. All rights reserved. +# +################################################################################ + +class SplaschObservationsController < ApplicationController + + def show + fhir_client = SessionHandler.fhir_client(session.id) + fhir_observation = fhir_client.read(FHIR::Observation, params[:id]).resource + @splasch_observation = SplaschObservation.new(fhir_observation) + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6d6179fc..2465f92b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -42,6 +42,27 @@ def display_telecom(telecom) #----------------------------------------------------------------------------- + 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(', ')) @@ -143,7 +164,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 #----------------------------------------------------------------------------- diff --git a/app/models/patient.rb b/app/models/patient.rb index d7065c27..1dddfffc 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,141 @@ 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: + # { + # subject: ["Patient", @id].join('/'), + # _profile: '???' + # } + # } + } + + 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) + 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 +311,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..36cad225 --- /dev/null +++ b/app/models/splasch_observation.rb @@ -0,0 +1,30 @@ +################################################################################ +# +# 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, :text, :div, :status, :code, :subject, :effective_datetime, :value + + #----------------------------------------------------------------------------- + + def initialize(fhir_splasch_observation) + @id = fhir_splasch_observation.id + @text = fhir_splasch_observation.text + @div = fhir_splasch_observation.text.div + @status = fhir_splasch_observation.status + @code = fhir_splasch_observation.code + @subject = fhir_splasch_observation.subject + @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 @@ Marital Status - <%= sanitize(@patient.marital_status.text) %> + <%= display_marital_status(@patient.marital_status) %> Phone diff --git a/app/views/dashboard/_splasch-collections-card.html.erb b/app/views/dashboard/_splasch-collections-card.html.erb new file mode 100644 index 00000000..c26b7d85 --- /dev/null +++ b/app/views/dashboard/_splasch-collections-card.html.erb @@ -0,0 +1,17 @@ +
+
+ <%= link_to 'SPLASCH Data', splasch_collections_path(patient: @patient.id) %> +
+
+ + <% @splasch_collections.each do |splasch_collection| %> + + + + + + <% end %> +
<%= display_date(splasch_collection.effective) %><%= link_to display_code(splasch_collection.code), + splasch_collection_path(id: splasch_collection.id) %>
+
+
diff --git a/app/views/dashboard/_splasch-observations-card.html.erb b/app/views/dashboard/_splasch-observations-card.html.erb new file mode 100644 index 00000000..a0b4c112 --- /dev/null +++ b/app/views/dashboard/_splasch-observations-card.html.erb @@ -0,0 +1,16 @@ +
+
+ SPLASCH Observations +
+
+ + <% @splasch_observations.each do |observation| %> + + + + + <% end %> +
<%= display_datetime(observation.effective_datetime) %><%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %>
+
+
diff --git a/app/views/dashboard/_spoken-language-comprehension-card.html.erb b/app/views/dashboard/_spoken-language-comprehension-card.html.erb new file mode 100644 index 00000000..f16184df --- /dev/null +++ b/app/views/dashboard/_spoken-language-comprehension-card.html.erb @@ -0,0 +1,17 @@ +
+
+ Spoken Language Comprehension +
+
+ + <% @spoken_language_comprehension_observations.each do |observation| %> + + + + + + <% end %> +
<%= display_date(observation.effective) %><%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %>
+
+
diff --git a/app/views/dashboard/_spoken-language-expression-card.html.erb b/app/views/dashboard/_spoken-language-expression-card.html.erb new file mode 100644 index 00000000..3bafd732 --- /dev/null +++ b/app/views/dashboard/_spoken-language-expression-card.html.erb @@ -0,0 +1,17 @@ +
+
+ Spoken Language Expression +
+
+ + <% @spoken_language_expression_observations.each do |observation| %> + + + + + + <% end %> +
<%= display_date(observation.effective) %><%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %>
+
+
diff --git a/app/views/dashboard/_swallowing-card.html.erb b/app/views/dashboard/_swallowing-card.html.erb new file mode 100644 index 00000000..9e0848d4 --- /dev/null +++ b/app/views/dashboard/_swallowing-card.html.erb @@ -0,0 +1,17 @@ +
+
+ Swallowing +
+
+ + <% @swallowing_observations.each do |observation| %> + + + + + + <% end %> +
<%= display_date(observation.effective) %><%= link_to display_code(observation.code), + splasch_observation_path(id: observation.id) %>
+
+
diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb index a3039f9d..50a23fc6 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -13,6 +13,9 @@
<%= render 'cognitive-status-card' %>
+
+ <%= render 'splasch-observations-card' %> +
<% else %> Not available <% end %> diff --git a/app/views/partials/_address.html.erb b/app/views/partials/_address.html.erb index 1730f0e9..c360a765 100644 --- a/app/views/partials/_address.html.erb +++ b/app/views/partials/_address.html.erb @@ -5,12 +5,16 @@ <%= link_to google_maps(address), target: '_blank' do %> - <% address.line.each do |line| %> - <%= sanitize(line) %>
- <% end %> - <%= sanitize(address.city) %>, - <%= sanitize(address.state) %> - <%= display_postal_code(address.postalCode) %> + <% if address.line.present? %> + <% address.line.each do |line| %> + <%= sanitize(line) %>
+ <% end %> + <%= sanitize(address.city) %>, + <%= sanitize(address.state) %> + <%= display_postal_code(address.postalCode) %> + <% else %> + <%= address.text %> + <% end %> <% end %> diff --git a/app/views/splasch_observations/index.html.erb b/app/views/splasch_observations/index.html.erb new file mode 100644 index 00000000..b61fc82c --- /dev/null +++ b/app/views/splasch_observations/index.html.erb @@ -0,0 +1,23 @@ +
+
+ + + <% @splasch_collections.each do |splasch_collection| %> + + <% end %> + <% assessment_count = @splasch_collections.first[:assessments].count %> + <% i = 0 %> + <% while i < assessment_count %> + + + <% @splasch_collections.each do |splasch_collection| %> + + <% end %> + + <% i += 1 %> + <% end %> +
Assessment<%= display_date(splasch_collection[:bundle].effective) %>
<%= @splasch_collections.first[:assessments][i].code.text %> + <%= splasch_collection[:assessments][i].value.text %> +
+
+
\ No newline at end of file diff --git a/app/views/splasch_observations/show.html.erb b/app/views/splasch_observations/show.html.erb new file mode 100644 index 00000000..98c8df03 --- /dev/null +++ b/app/views/splasch_observations/show.html.erb @@ -0,0 +1,19 @@ +
+
+

+ Observation: <%= display_code(@splasch_observation.code) %>
+ Date/Time: <%= display_datetime(@splasch_observation.effective_datetime) %> +

+ + + + + + + + + +
AssessmentObservation
<%= @splasch_observation.code.coding[0].display %><%= @splasch_observation.value.coding[0].display %>
+ <%= display_div(@splasch_observation.div) %> +
+
diff --git a/config/routes.rb b/config/routes.rb index 55560d16..d41baada 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,6 +17,8 @@ resources :advance_directives, only: [:index, :show] resources :functional_status, only: [:index, :show] resources :cognitive_status, only: [:index, :show] + resources :splasch_collections, only: [:index, :show] + resources :splasch_observations, only: [:show] resources :practitioners, only: [:show] resources :patients resources :observations From eb9e287b01f4be51676ddad808fae538f01d4c93 Mon Sep 17 00:00:00 2001 From: "Hill, Dave" Date: Mon, 10 Jan 2022 00:06:20 -0500 Subject: [PATCH 3/7] Added write functionality, cleaned up some displays. --- app/assets/javascripts/splasch_observation.js | 17 ++ app/controllers/dashboard_controller.rb | 6 +- app/controllers/patients_controller.rb | 5 +- .../splasch_observations_controller.rb | 207 +++++++++++++++++- app/helpers/application_helper.rb | 38 +++- app/helpers/dashboard_helper.rb | 12 - app/models/patient.rb | 2 +- app/models/splasch_observation.rb | 10 +- .../_splasch-observations-card.html.erb | 1 + app/views/dashboard/index.html.erb | 32 ++- app/views/partials/_messages.html.erb | 5 +- app/views/patients/show.html.erb | 35 ++- app/views/practitioners/show.html.erb | 92 ++++---- app/views/splasch_observations/new.html.erb | 20 ++ app/views/splasch_observations/show.html.erb | 15 +- config/routes.rb | 2 +- 16 files changed, 409 insertions(+), 90 deletions(-) create mode 100644 app/assets/javascripts/splasch_observation.js create mode 100644 app/views/splasch_observations/new.html.erb 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/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index a5691f28..dd289a72 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -16,9 +16,9 @@ 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 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_observations_controller.rb b/app/controllers/splasch_observations_controller.rb index 5852dab1..1a2b9429 100644 --- a/app/controllers/splasch_observations_controller.rb +++ b/app/controllers/splasch_observations_controller.rb @@ -10,10 +10,213 @@ 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_client = SessionHandler.fhir_client(session.id) - fhir_observation = fhir_client.read(FHIR::Observation, params[:id]).resource + 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 2465f92b..a2ce5188 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -36,6 +36,19 @@ 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 @@ -78,6 +91,17 @@ 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) end @@ -209,6 +233,12 @@ def display_array(array) #----------------------------------------------------------------------------- + def display_subject(subject) + display_reference(subject) + end + + #----------------------------------------------------------------------------- + def display_performers(performers) list = [] @@ -216,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 1dddfffc..3a62ef54 100644 --- a/app/models/patient.rb +++ b/app/models/patient.rb @@ -187,7 +187,7 @@ def splasch_observations end end - return splasch_observations.sort_by(&:effective_datetime) + return splasch_observations.sort_by(&:effective_datetime).reverse end #----------------------------------------------------------------------------- diff --git a/app/models/splasch_observation.rb b/app/models/splasch_observation.rb index 36cad225..5c4faba8 100644 --- a/app/models/splasch_observation.rb +++ b/app/models/splasch_observation.rb @@ -12,17 +12,23 @@ class SplaschObservation < Resource include ActiveModel::Model - attr_reader :id, :text, :div, :status, :code, :subject, :effective_datetime, :value + 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 - @div = fhir_splasch_observation.text.div + 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 diff --git a/app/views/dashboard/_splasch-observations-card.html.erb b/app/views/dashboard/_splasch-observations-card.html.erb index a0b4c112..cfaa65e7 100644 --- a/app/views/dashboard/_splasch-observations-card.html.erb +++ b/app/views/dashboard/_splasch-observations-card.html.erb @@ -1,6 +1,7 @@
SPLASCH Observations + <%= link_to "New", new_splasch_observation_path(patient: params[:patient]) %>
diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb index 50a23fc6..f413e2b2 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -4,18 +4,26 @@
<%= render 'patient-card' %>
-
- <%= render 'medications-card' %> -
-
- <%= render 'functional-status-card' %> -
-
- <%= render 'cognitive-status-card' %> -
-
- <%= render 'splasch-observations-card' %> -
+ <% if @medications.present? %> +
+ <%= render 'medications-card' %> +
+ <% end %> + <% if @functional_statuses.present? %> +
+ <%= render 'functional-status-card' %> +
+ <% end %> + <% if @cognitive_statuses.present? %> +
+ <%= render 'cognitive-status-card' %> +
+ <% end %> + <% if @splasch_observations.present? %> +
+ <%= render 'splasch-observations-card' %> +
+ <% end %> <% else %> Not available <% end %> diff --git a/app/views/partials/_messages.html.erb b/app/views/partials/_messages.html.erb index 36ea0c86..5cd0677a 100644 --- a/app/views/partials/_messages.html.erb +++ b/app/views/partials/_messages.html.erb @@ -3,11 +3,8 @@
<% flash.each do |key, value| %> <% if key.is_a?(String) %> -
- - - - - - - - - - - - - - - - -
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 %> +
+
+
<% else %> Not available diff --git a/app/views/splasch_observations/new.html.erb b/app/views/splasch_observations/new.html.erb new file mode 100644 index 00000000..d606753d --- /dev/null +++ b/app/views/splasch_observations/new.html.erb @@ -0,0 +1,20 @@ +
+
+

New SPLASCH Observation

+ <%= form_for :splasch_observation, url: splasch_observations_path(patient: params[:patient]) do |f| %> + <%= f.label :performer %>
+ <%= f.select :performer, @practitioners, { include_blank: true }, + { class: 'form-select bg-dark text-white' } %> + <%= f.label :category %>
+ <%= f.select :category, @categories, { include_blank: true }, + { class: 'form-select bg-dark text-white' } %> + <%= f.label :code %>
+ <%= f.select :code, grouped_options_for_select(@codes), { include_blank: true }, + { class: 'form-select bg-dark text-white' } %> + <%= f.label :value %>
+ <%= f.select :value, @values, { include_blank: true }, + { class: 'form-select bg-dark text-white' } %> + <%= f.submit 'Submit', class: 'btn btn-outline-success', id: 'healthcare-service-search-button' %> + <% end %> +
+
diff --git a/app/views/splasch_observations/show.html.erb b/app/views/splasch_observations/show.html.erb index 98c8df03..ea8cf818 100644 --- a/app/views/splasch_observations/show.html.erb +++ b/app/views/splasch_observations/show.html.erb @@ -1,9 +1,15 @@
-

- Observation: <%= display_code(@splasch_observation.code) %>
- Date/Time: <%= display_datetime(@splasch_observation.effective_datetime) %> -

+
+
Category
+
<%= display_categories(@splasch_observation.categories) %>
+
Date/Time:
+
<%= display_datetime(@splasch_observation.effective_datetime) %>
+
Patient:
+
<%= display_subject(@splasch_observation.subject) %>
+
Performer:
+
<%= display_performers(@splasch_observation.performer) %>
+
@@ -14,6 +20,5 @@
Assessment<%= @splasch_observation.value.coding[0].display %>
- <%= display_div(@splasch_observation.div) %>
diff --git a/config/routes.rb b/config/routes.rb index d41baada..bfe7ee75 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,7 +18,7 @@ resources :functional_status, only: [:index, :show] resources :cognitive_status, only: [:index, :show] resources :splasch_collections, only: [:index, :show] - resources :splasch_observations, only: [:show] + resources :splasch_observations, only: [:show, :new, :create] resources :practitioners, only: [:show] resources :patients resources :observations From 6f2a7e9f6f4cb9c6518bf43cc622aa08c1d9eeb4 Mon Sep 17 00:00:00 2001 From: "Hill, Dave" Date: Mon, 10 Jan 2022 00:23:30 -0500 Subject: [PATCH 4/7] More display cleanup. --- app/assets/stylesheets/style.scss | 4 ++++ app/views/dashboard/_splasch-observations-card.html.erb | 4 +++- app/views/splasch_observations/show.html.erb | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) 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/views/dashboard/_splasch-observations-card.html.erb b/app/views/dashboard/_splasch-observations-card.html.erb index cfaa65e7..590cf328 100644 --- a/app/views/dashboard/_splasch-observations-card.html.erb +++ b/app/views/dashboard/_splasch-observations-card.html.erb @@ -1,7 +1,9 @@
SPLASCH Observations - <%= link_to "New", new_splasch_observation_path(patient: params[:patient]) %> + <%= link_to("Add", new_splasch_observation_path(patient: params[:patient]), + method: :get, id: "add-splash-observation", + class: "btn btn-primary", role: "button") %>
diff --git a/app/views/splasch_observations/show.html.erb b/app/views/splasch_observations/show.html.erb index ea8cf818..df87feb9 100644 --- a/app/views/splasch_observations/show.html.erb +++ b/app/views/splasch_observations/show.html.erb @@ -1,5 +1,6 @@
+

SPLASCH Observation (<%= @splasch_observation.id %>)

Category
<%= display_categories(@splasch_observation.categories) %>
From 35e3a7161b09c2c9634ffd69a1858bebc89b0330 Mon Sep 17 00:00:00 2001 From: "Hill, Dave" Date: Mon, 10 Jan 2022 14:03:50 -0500 Subject: [PATCH 5/7] More display cleanup. --- app/models/patient.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/patient.rb b/app/models/patient.rb index 3a62ef54..aad8499a 100644 --- a/app/models/patient.rb +++ b/app/models/patient.rb @@ -168,9 +168,9 @@ def splasch_collections def splasch_observations splasch_observations = [] - search_param = { #search: + search_param = { search: _count=200 # { parameters: - # { + # { # subject: ["Patient", @id].join('/'), # _profile: '???' # } From 83e1aaec06c214c0936cfc811ddf688140cc2fe2 Mon Sep 17 00:00:00 2001 From: "Hill, Dave" Date: Mon, 10 Jan 2022 14:21:17 -0500 Subject: [PATCH 6/7] Added count parameter to largely eliminate paging for new when retrieving SPLASCH observations. --- app/helpers/application_helper.rb | 2 +- app/models/patient.rb | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a2ce5188..233cae03 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -103,7 +103,7 @@ def display_categories(categories) #----------------------------------------------------------------------------- def display_code(code) - sanitize(code.coding[0].display) + sanitize(code.coding[0].display) unless code.nil? end #----------------------------------------------------------------------------- diff --git a/app/models/patient.rb b/app/models/patient.rb index aad8499a..edaafc47 100644 --- a/app/models/patient.rb +++ b/app/models/patient.rb @@ -168,13 +168,12 @@ def splasch_collections def splasch_observations splasch_observations = [] - search_param = { search: _count=200 - # { parameters: - # { - # subject: ["Patient", @id].join('/'), - # _profile: '???' - # } - # } + search_param = { search: + { parameters: + { + _count: 200 + } + } } fhir_bundle = @fhir_client.search(FHIR::Observation, search_param).resource From 014b843bfb4ee70cb4be615259d8d1b0430bb5c6 Mon Sep 17 00:00:00 2001 From: "Hill, Dave" Date: Mon, 10 Jan 2022 15:10:12 -0500 Subject: [PATCH 7/7] Reverted to older Bootstrap for now. Bootstrap 5.0 was causing some weird layout issues. --- Gemfile | 2 +- Gemfile.lock | 12 ++++++------ app/views/splasch_observations/new.html.erb | 10 ++++++---- 3 files changed, 13 insertions(+), 11 deletions(-) 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 7de7b251..2bda7c9c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -63,16 +63,16 @@ GEM archive-zip (0.12.0) io-like (~> 0.3.0) arel (9.0.0) - autoprefixer-rails (10.2.5.0) - execjs (< 2.8.0) + autoprefixer-rails (10.4.0.0) + execjs (~> 2) bcp47 (0.3.3) i18n bindex (0.8.1) bootsnap (1.7.5) msgpack (~> 1.0) - bootstrap (5.0.0) + bootstrap (4.6.1) autoprefixer-rails (>= 9.1.0) - popper_js (>= 2.9.2, < 3) + popper_js (>= 1.14.3, < 2) sassc-rails (>= 2.0.0) bootstrap-toggle-rails (2.2.1.0) builder (3.2.4) @@ -186,7 +186,7 @@ GEM multi_xml (~> 0.5) rack (>= 1.2, < 3) pg (1.2.3) - popper_js (2.9.2) + popper_js (1.16.0) pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) @@ -300,7 +300,7 @@ PLATFORMS DEPENDENCIES bootsnap (>= 1.1.0) - bootstrap + bootstrap (~> 4.4) bootstrap-toggle-rails byebug capybara (>= 2.15) diff --git a/app/views/splasch_observations/new.html.erb b/app/views/splasch_observations/new.html.erb index d606753d..2d1831a8 100644 --- a/app/views/splasch_observations/new.html.erb +++ b/app/views/splasch_observations/new.html.erb @@ -1,19 +1,21 @@

New SPLASCH Observation

+
+
<%= form_for :splasch_observation, url: splasch_observations_path(patient: params[:patient]) do |f| %> <%= f.label :performer %>
<%= f.select :performer, @practitioners, { include_blank: true }, - { class: 'form-select bg-dark text-white' } %> + { class: 'custom-select bg-dark text-white' } %> <%= f.label :category %>
<%= f.select :category, @categories, { include_blank: true }, - { class: 'form-select bg-dark text-white' } %> + { class: 'custom-select bg-dark text-white' } %> <%= f.label :code %>
<%= f.select :code, grouped_options_for_select(@codes), { include_blank: true }, - { class: 'form-select bg-dark text-white' } %> + { class: 'custom-select bg-dark text-white' } %> <%= f.label :value %>
<%= f.select :value, @values, { include_blank: true }, - { class: 'form-select bg-dark text-white' } %> + { class: 'custom-select bg-dark text-white' } %> <%= f.submit 'Submit', class: 'btn btn-outline-success', id: 'healthcare-service-search-button' %> <% end %>