diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 386e92d..c361adc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -27,14 +27,18 @@ def get_patient def coverage_plans # Read all of the insurance drug plans from the server cp_type = "http://terminology.hl7.org/CodeSystem/v3-ActCode|DRUGPOL" - reply = @client.search(FHIR::InsurancePlan, search: { parameters: { type: cp_type}}).resource - @plansbyid = build_coverage_plans(reply) + @plansbyid = JSON.parse(build_coverage_plans(reply).to_json).deep_symbolize_keys + @formulary_items_and_drugs = formulary_items_and_drugs + @formulary_items = @formulary_items_and_drugs[:formulary_items] + @formulary_drugs = @formulary_items_and_drugs[:formulary_drugs] @locationsbyid = locations @cp_options = build_coverage_plan_options(reply) session[:plansbyid] = compress_hash(@plansbyid.to_json) session[:locationsbyid] = compress_hash(@locationsbyid.to_json) session[:cp_options] = compress_hash(@cp_options) + session[:formularyitemsbyid] = compress_hash(@formulary_items.to_json) + session[:formularydrugsbyid] = compress_hash(@formulary_drugs.to_json) # Prepare the query string for display on the page @search = URI.decode(reply.link.select { |l| l.relation === "self"}.first.url) if reply.link.first @@ -65,6 +69,37 @@ def get_plansbyid #----------------------------------------------------------------------------- + def get_formularyItemsById + if session[:formularyitemsbyid] + @formularyitemsbyid = JSON.parse(decompress_hash(session[:formularyitemsbyid])) + @cp_options = decompress_hash(session[:cp_options]) + @search = session[:query] + else + puts "get_formularyItemsById: session[:formularyitemsbyid] is nil, calling formulary_items " + @formularyitemsbyid = nil + @cp_options = [["N/A (Must connect first)", "-"]] + formulary_items_and_drugs + end + end + + #----------------------------------------------------------------------------- + + + def get_formularyDrugsById + if session[:formularydrugsbyid] + @formularydrugsbyid = JSON.parse(decompress_hash(session[:formularydrugsbyid])) + @cp_options = decompress_hash(session[:cp_options]) + @search = session[:query] + else + puts "get_formularyDrugsById: session[:formularydrugsbyid] is nil, calling formulary_drugs " + @formularydrugsbyid = nil + @cp_options = [["N/A (Must connect first)", "-"]] + formulary_items_and_drugs + end + end + + #----------------------------------------------------------------------------- + def locations profile = "http://hl7.org/fhir/us/davinci-drug-formulary/StructureDefinition/usdf-InsurancePlanLocation" bundle = @client.search(FHIR::Location, search: { parameters: { _profile: profile}}).resource&.entry || [] @@ -76,7 +111,34 @@ def locations end #----------------------------------------------------------------------------- + + def formulary_items_and_drugs + profile = "http://hl7.org/fhir/us/davinci-drug-formulary/StructureDefinition/usdf-FormularyItem" + bundle = @client.search(FHIR::Basic, search: { parameters: {_profile: profile, _include: "Basic:subject", _count: 500 }}).resource&.entry || [] + formulary_drugs = {} + formulary_items = bundle.each_with_object({}) do | entry, formularyItemById | + if entry.search.mode == "match" + formularyItemById[entry.resource.id] = FormularyItem.new(entry.resource, @plansbyid) + else + formulary_drugs[entry.resource.id] = MedicationKnowledge.new(entry.resource) + end + end + @formulary_items_and_drugs = {formulary_items: formulary_items.deep_symbolize_keys, formulary_drugs: formulary_drugs.deep_symbolize_keys} + end + #----------------------------------------------------------------------------- + + def formulary_drugs + profile = "http://hl7.org/fhir/us/davinci-drug-formulary/StructureDefinition/usdf-FormularyDrug" + bundle = @client.search(FHIR::MedicationKnowledge, search: { parameters: {_profile: profile}}).resource&.entry || [] + formulary_drugs = bundle.each_with_object({}) do | entry, formularyDrugById | + formularyDrugById[entry.resource.id] = MedicationKnowledge.new(entry.resource) + end + formulary_drugs.deep_symbolize_keys + end + + #----------------------------------------------------------------------------- + def payer_plans # Read all payer insurance plans from the server payerplan_type = "http://hl7.org/fhir/us/davinci-pdex-plan-net/CodeSystem/InsuranceProductTypeCS|" diff --git a/app/controllers/compare_controller.rb b/app/controllers/compare_controller.rb index ded93d5..b09fa9c 100644 --- a/app/controllers/compare_controller.rb +++ b/app/controllers/compare_controller.rb @@ -19,7 +19,6 @@ class CompareController < ApplicationController def index @codes = nil @params = nil - get_plansbyid if params[:search].length>0 or params[:code].length>0 @drugname = params[:search].split(' ').first @@ -109,12 +108,14 @@ def set_table chosen = @cache[:fis] @drugsbyid = build_formulary_drugs(@cache[:fds]) @table_rows = Hash.new - - chosen.collect!{ |fi| FormularyItem.new(fi, @plansbyid, @drugsbyid) } + chosen.collect!{ |fi| FormularyItem.new(fi, @plansbyid)} chosen.each do |fi| - code = fi.rxnorm_code - plan = fi.plan_id + fd = FormularyDrug.new(@cache[:fds].find { |drug| drug.id == fi.subject.reference.split('/')[1] }) + code = fi.subject.reference.split('/')[1] + plan = fi.formulary.reference.split('/')[1] @table_rows.has_key?(code) ? @table_rows[code][plan] = fi : @table_rows[code] = { plan => fi } + @table_rows[code][code] = fd + end end diff --git a/app/controllers/formularies_controller.rb b/app/controllers/formularies_controller.rb index e22827c..7b4d4dc 100644 --- a/app/controllers/formularies_controller.rb +++ b/app/controllers/formularies_controller.rb @@ -43,7 +43,7 @@ def index @drugsbyid = build_formulary_drugs(fhir_formularydrugs) @formularydrugs = [] fhir_formularyitems.each do |fhir_formularyitem| - @formularydrugs << FormularyItem.new(fhir_formularyitem,@plansbyid, @drugsbyid) + @formularydrugs << FormularyItem.new(fhir_formularyitem, @plansbyid) end # Prepare the query string for display on the page @@ -68,7 +68,7 @@ def show # fhir_formularydrug = @@bundle.entry.map(&:resource).first get_plansbyid @drugsbyid = build_formulary_drugs(fhir_formularydrugs) - @formulary_drug = FormularyItem.new(fhir_formularyitem, @plansbyid, @drugsbyid) + @formulary_drug = FormularyItem.new(fhir_formularyitem, @plansbyid) # Prepare the query string for display on the page @search = "" diff --git a/app/controllers/formularyitems_controller.rb b/app/controllers/formularyitems_controller.rb new file mode 100644 index 0000000..5b20c8a --- /dev/null +++ b/app/controllers/formularyitems_controller.rb @@ -0,0 +1,42 @@ +################################################################################ +# +# FormularItems Controller => Formulary Item (Basic) profile +# +# Copyright (c) 2021 The MITRE Corporation. All rights reserved. +# +################################################################################ + +require 'json' + +class FormularyitemsController < ApplicationController + + before_action :check_formulary_server_connection, only: [ :index] + + #----------------------------------------------------------------------------- + + # GET /formularyitems + + def index + get_plansbyid + get_payers_byid + get_formularyItemsById + get_formularyDrugsById + @formulary_items = @formularyitemsbyid.values.select do |formulary_item| + formulary_drug = @formularydrugsbyid.values.find {|drug| formulary_item["subject"]["reference"] == "MedicationKnowledge/" + drug["id"]} + include_in_results = true + if params[:coverage].present? + include_in_results = include_in_results && formulary_item["formulary"]["reference"] == "InsurancePlan/" + params[:coverage] + end + if params[:drug_tier].present? + include_in_results = include_in_results && formulary_item["drug_tier"]["code"] == params[:drug_tier] + end + if params[:name].present? + include_in_results = include_in_results && params[:name].in?(formulary_drug["code"]["display"]) + end + include_in_results + end + end + + #----------------------------------------------------------------------------- + +end diff --git a/app/models/formulary_item.rb b/app/models/formulary_item.rb index 5cf5f39..02d47be 100644 --- a/app/models/formulary_item.rb +++ b/app/models/formulary_item.rb @@ -1,125 +1,52 @@ ################################################################################ # -# Formulary Item Model +# FormularyItem Model => FormularyItem profile # # Copyright (c) 2021 The MITRE Corporation. All rights reserved. # ################################################################################ -class FormularyItem < Resource +class FormularyItem include ActiveModel::Model - attr_accessor :drug_tier, :drug_class, :id, :plan_id, :drug_name, :rxnorm_code, - :prior_auth, :step_therapy, :quantity_limit, :errors, :step_therapy_newstart, - :warnings, :plan_id_path, :plan_id_name, :rxnorm_path, :prior_auth_newstart, - :copay, :coinsurancerate, :formulary_id_path, :plan, :plansbyid, :mailorder - - #----------------------------------------------------------------------------- - - def initialize(fhir_formulary, plansbyid, drugsbyid) - @id = parse_id(fhir_formulary) - drug = drugsbyid[parse_reference_id(fhir_formulary.subject.reference).to_sym] - @drug_name = drug[:drug_name] - @rxnorm_code = drug[:rxnorm_code] - @plansbyid = plansbyid - #@drug_class = parse_drug_class(fhir_formulary) - @rxnorm_path = "https://mor.nlm.nih.gov/RxNav/search?searchBy=RXCUI&searchTerm=" + @rxnorm_code - @formulary_id_path = "/formularies/#{@id}" - parse_extensions(fhir_formulary) - @plan = plansbyid[@plan_id.to_sym] - # Test inclusion of drug tier info in formulary drug for display - @tier = @plan[:tiers][@drug_tier.to_sym] - if @tier + attr_accessor :id, :subject, :formulary, :drug_tier, :prior_authorization, :quantity_limit, :step_therapy_limit, + :status, :period, :pharmacy_type, :copay, :coinsurancerate + + def initialize(fhir_formularyitem, plansbyid) + @id = fhir_formularyitem.id + @subject = fhir_formularyitem.subject + + @extension = fhir_formularyitem.extension + @formulary = find_element_in_extension(@extension, "DrugPlanReference").valueReference + @drug_tier = find_element_in_extension(@extension, "DrugTierID").valueCodeableConcept.coding.first + @prior_authorization = find_element_in_extension(@extension, "PriorAuthorization").valueBoolean + @quantity_limit = find_element_in_extension(@extension, "QuantityLimit").valueBoolean + @step_therapy_limit = find_element_in_extension(@extension, "StepTherapyLimit").valueBoolean + @status = find_element_in_extension(@extension, "AvailabilityStatus").valueCode + @period = find_element_in_extension(@extension, "AvailabilityPeriod").valuePeriod + @pharmacy_type = find_element_in_extension(@extension, "PharmacyType").valueCodeableConcept.coding.first + @plan = plansbyid[@formulary.reference.split('/')[1].to_sym] + @tier = @plan[:tiers][@drug_tier.code.to_sym] + if @tier @copay = @tier["1 month in network retail".to_sym][:copay] @coinsurancerate = @tier["1 month in network retail".to_sym][:coinsurancerate] else @copay = "missing" @coinsurancerate = "missing" end + end - - #----------------------------------------------------------------------------- - private - #----------------------------------------------------------------------------- - - # Isolates the ID from the formulary drug resource. - - def parse_id(fhir_formulary) - return fhir_formulary.id - end - - #----------------------------------------------------------------------------- - - def parse_reference_id(reference) - return reference.split("/").last - end - #----------------------------------------------------------------------------- - - # Parses the values within the extensions defined by the formulary drug - # resource. - - def parse_extensions(fhir_formulary) - extensions = fhir_formulary.extension - if extensions.present? - extensions.each do |extension| - if extension.url.include?("DrugTierID") - @drug_tier = parse_drug_tier(extension) - elsif extension.url.include?("PharmacyType") - if parse_drug_tier(extension).include?("mail") - @mailorder = true - end - - elsif extension.url.include?("PriorAuthorization-extension") - @prior_auth = extension.valueBoolean - elsif extension.url.include?("PriorAuthorizationNewStartsOnly") - @prior_auth_newstart = extension.valueBoolean - elsif extension.url.include?("StepTherapyLimit-extension") - @step_therapy = extension.valueBoolean - elsif extension.url.include?("StepTherapyLimitNewStartsOnly") - @step_therapy_newstart = extension.valueBoolean - elsif extension.url.include?("QuantityLimit") - @quantity_limit = extension.valueBoolean - elsif extension.url.include?("DrugPlanReference") - @plan_id = parse_reference_id(extension.valueReference.reference) - @plan = plansbyid[plan_id.to_sym] - @plan_id_path = "/coverageplans/#{plan_id}" - @plan_id_name = plan[:name] - end - end - else - @drug_tier = "Required extensions not specified" - end - end - - #----------------------------------------------------------------------------- - - def parse_drug_tier(extension) - if (concept = extension.valueCodeableConcept).present? - if (coding = concept.coding).present? - value = code_list(coding) - else - value = "Drug tier not specified" - end - else - value = "Codeable concept not present" - end - - return value - end - - #----------------------------------------------------------------------------- - def display_list(list) - list.map{ |element| element.display }.join(', ') - end - #----------------------------------------------------------------------------- - # Concatenates a list of code elements. - - def code_list(list) - list.map{ |element| element.code }.join(', ') - end + private + def find_element_in_extension(extensions, reference_key) + extensions.each do |extension| + if extension.url.include?(reference_key) + return extension + end + end + end end \ No newline at end of file diff --git a/app/models/medication_knowledge.rb b/app/models/medication_knowledge.rb new file mode 100644 index 0000000..3087931 --- /dev/null +++ b/app/models/medication_knowledge.rb @@ -0,0 +1,25 @@ +################################################################################ +# +# FormularyMedicationKnowledge Model => MedicationKnowledge profile +# +# Copyright (c) 2021 The MITRE Corporation. All rights reserved. +# +################################################################################ + +class MedicationKnowledge + + include ActiveModel::Model + + attr_accessor :id, :status, :code, :synonym, :dose_form, :related_medication_knowledge, :medicine_classification + + def initialize(fhir_medicationknowledge) + @id = fhir_medicationknowledge.id + @code = fhir_medicationknowledge.code.coding.first + @status = fhir_medicationknowledge.status + @synonym = fhir_medicationknowledge.synonym + @dose_form = fhir_medicationknowledge.doseForm + @related_medication_knowledge = fhir_medicationknowledge.relatedMedicationKnowledge + @medicine_classification = fhir_medicationknowledge.medicineClassification + + end +end \ No newline at end of file diff --git a/app/views/compare/index.html.erb b/app/views/compare/index.html.erb index 9b30bcf..a6a6490 100644 --- a/app/views/compare/index.html.erb +++ b/app/views/compare/index.html.erb @@ -17,72 +17,57 @@ <% end %>
- - - - - <% @table_header.each do |cp| %> - - <% end %> - - +
Drug<%= cp.name -%>
+ + + + <% @table_header.each do |cp| %> + + <% end %> + + <% @table_rows.each do |code, plan_hash| %> - - - <% @table_header.each do |cp| %> - <% fd = plan_hash[cp.id] %> - <% c = (fd ? "table table-dark": "drug-not-in-plan") %> - - <% end %> - + + + <% @table_header.each do |cp| %> + <% fi = plan_hash[cp.id] %> + <% c = (fi ? "table table-dark": "drug-not-in-plan") %> + <% end %> -
Drug<%= cp.name -%>
- <%= plan_hash.values.first.drug_name -%> (<%= link_to code, "https://mor.nlm.nih.gov/RxNav/search?searchBy=RXCUI&searchTerm=" + code %>) - - > - <% if fd %> - - - - - - - - - - - - - -
Tier: - <%= link_to fd.drug_tier.split('-').map(&:capitalize).join(' ') , fd.formulary_id_path%> -
Copay - $<%=fd.copay%> -
CoInsurance - <%=fd.coinsurancerate %>% -
-
- <% if fd.prior_auth %> - Prior Authorization - <% end %> - <% if fd.prior_auth_newstart %> -  Prior Auth New Starts Only - <% end %> - <% if fd.step_therapy %> - <% mtop = fd.prior_auth ? 'compare-buffer' : '' %> - Step Therapy - <% end %> - <% if fd.step_therapy_newstart %> - <% mtop = fd.prior_auth_newstart ? 'compare-buffer' : '' %> -  Step Therapy New Starts Only - <% end %> - <% if fd.quantity_limit %> - <% mtop = fd.prior_auth || fd.step_therapy ? 'compare-buffer' : '' %> - Quantity Limit - <% end %> - - <% end %> -
+ <%= plan_hash[code].drug_name -%> (<%= link_to code, "https://mor.nlm.nih.gov/RxNav/search?searchBy=RXCUI&searchTerm=" + code %>) + > + <% if fi %> + + + + + + + + + + + + + +
Tier: <%= link_to fi.drug_tier.code.split('-').map(&:capitalize).join(' ') , "/formularies/" + fi.id%>
Copay$<%= '%.2f' % fi.copay %>
CoInsurance<%= fi.coinsurancerate * 100 %>%
+
+ <% if fi.prior_authorization %> + Prior Authorization + <% end %> + <% if fi.step_therapy_limit %> + <% mtop = fd.prior_auth ? 'compare-buffer' : '' %> + Step Therapy + <% end %> + <% if fi.quantity_limit %> + <% mtop = fd.prior_auth || fd.step_therapy ? 'compare-buffer' : '' %> + Quantity Limit + <% end %> + <% end %> +
-
+ + <% end %> + + diff --git a/app/views/coverageplans/index.html.erb b/app/views/coverageplans/index.html.erb index 0d6a321..31ad241 100644 --- a/app/views/coverageplans/index.html.erb +++ b/app/views/coverageplans/index.html.erb @@ -74,7 +74,7 @@ <% end %> - >drugs + >drugs <% end %> diff --git a/app/views/formularyitems/index.html.erb b/app/views/formularyitems/index.html.erb new file mode 100644 index 0000000..1769de6 --- /dev/null +++ b/app/views/formularyitems/index.html.erb @@ -0,0 +1,53 @@ +<% if @formulary_items.empty? %> + <%= render partial: "formularies/no_formularies" %> +<% else %> +
+
+
+

Formulary Drugs

+ <%= render 'partials/query' %> +
+ <%= link_to 'Previous', formularyitems_path(page: 'previous'), class: 'btn btn-outline-primary' %> + <%= link_to 'Next', formularyitems_path(page: 'next'), class: 'btn btn-outline-primary' %> +
+
+ + + + + + + + <% @formulary_items.each do |formulary_item| %> + <% formulary_drug = @formularydrugsbyid.values.find {|drug| formulary_item["subject"]["reference"] == "MedicationKnowledge/" + drug["id"]}%> + + + + + + + <% end %> +
DrugTierCopayID
+
+ <%= formulary_drug["code"]["display"] %> +
+ <% if formulary_item["prior_authorization"] %> + Prior Authorization + <% end %> + <% if formulary_item["step_therapy"] %> + Step Therapy + <% end %> + <% if formulary_item["quantity_limit"] %> + Quantity Limit + <% end %> +
<%= formulary_item["drug_tier"]["display"].split('-').map(&:capitalize).join(' ') %> <%= link_to @plansbyid.values.find { |plan| formulary_item["formulary"]["reference"] == "InsurancePlan/" + plan[:id]}[:name] , formulary_item["formulary"] %><%= formulary_item["id"] %>
+
+
+ <%= link_to 'Previous', formularyitems_path(page: 'previous'), class: 'btn btn-outline-primary' %> + <%= link_to 'Next', formularyitems_path(page: 'next'), class: 'btn btn-outline-primary' %> +
+
+
+
+ +<% end %> diff --git a/app/views/welcome/_drug_lookup.html.erb b/app/views/welcome/_drug_lookup.html.erb index 37b73f6..a67f85a 100644 --- a/app/views/welcome/_drug_lookup.html.erb +++ b/app/views/welcome/_drug_lookup.html.erb @@ -1,6 +1,6 @@

Lookup Drugs by Plan, Tier, Name, and/or RxNorm

- <%= form_tag formularies_path, method: 'get', class: 'form-inline welcome-form', id: 'formulary-form' do %> + <%= form_tag formularyitems_path, method: 'get', class: 'form-inline welcome-form', id: 'formulary-form' do %>
<%= select_tag(:coverage, options_for_select(@cp_options), prompt: 'Insurance Drug Plan', class: 'custom-select coverage-select') %> diff --git a/app/views/welcome/index.html.erb b/app/views/welcome/index.html.erb index d8ec3ad..fb84869 100644 --- a/app/views/welcome/index.html.erb +++ b/app/views/welcome/index.html.erb @@ -19,7 +19,7 @@
- + diff --git a/config/routes.rb b/config/routes.rb index 0fd475d..f02d745 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,6 +7,7 @@ resources :coverageplans, only: [:index, :show] resources :payerplans, only: [:index] resources :compare, only: [:index] + resources :formularyitems, only: [:index] get '/home', to: 'welcome#index' get '/dashboard', to: 'dashboard#index'
<%= @count %> <%= link_to "Formulary Drugs", formularies_path%><%= @count %> <%= link_to "Formulary Drugs", formularyitems_path %> <%=@cp_count %> <%= link_to "Insurance Drug Plans", coverageplans_path%> <%=@payers_count %> <%= link_to "Payer Insurance Plans", payerplans_path%>