-
Notifications
You must be signed in to change notification settings - Fork 95
Create unified slots service for CC Hybrid scheduling #27602
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
30d8775
9bbc0cd
762942d
836c310
7a6c052
4c2a17f
df3ed6c
7b6d00d
eff8e60
77defde
aa5a76a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,9 +16,9 @@ def initialize(current_user) | |
| end | ||
|
|
||
| ## | ||
| # Searches for both VA facilities and EPS CC providers near the user's address, | ||
| # filtered by the referral's category of care. Pins the referral's matched CC | ||
| # provider at the top, then sorts remaining results by distance. | ||
| # Searches for VA clinics (via Lighthouse facilities + VAOS clinics) and EPS CC providers | ||
| # near the user's address, filtered by the referral's category of care. Pins the referral's | ||
| # matched CC provider at the top, then sorts remaining results by distance. | ||
| # | ||
| # @param referral [Object] A CCRA referral object with category_of_care, provider NPI, etc. | ||
| # @param radius [Integer] Search radius in miles (default: 25) | ||
|
|
@@ -69,9 +69,7 @@ def fetch_va_providers(user_address, referral, radius, lh_client:) | |
| per_page: 50 | ||
| ) | ||
|
|
||
| providers = facilities.map { |f| VAOS::V2::Unified::VAProvider.from_lighthouse_facility(f) } | ||
| providers.each { |p| assign_distance(p, user_address) } | ||
| filter_va_by_category_of_care(providers, referral.category_of_care) | ||
| fetch_providers_for_facilities(facilities, referral, user_address) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at the UX flow and guessing at how this service is to be used, we'll want to produce a list of VA providers which are clinics not facilities. There really isn't any use for raw VA facilities so I'm updating the logic here to:
|
||
| rescue => e | ||
| Rails.logger.error("#{log_prefix}: VA facility search failed", | ||
| { | ||
|
|
@@ -84,6 +82,19 @@ def fetch_va_providers(user_address, referral, radius, lh_client:) | |
| [] | ||
| end | ||
|
|
||
| def fetch_providers_for_facilities(facilities, referral, user_address) | ||
| matching_facilities = filter_supported_facilities(facilities, referral.category_of_care) | ||
| clinical_service = ServiceTypeMapper.to_vaos(referral.category_of_care) | ||
|
|
||
| matching_facilities.flat_map do |facility| | ||
| fetch_clinics_for_facility(facility, clinical_service).map do |clinic| | ||
| provider = VAProvider.from_facility_and_clinic(facility, clinic) | ||
| assign_distance(provider, user_address) | ||
| provider | ||
| end | ||
| end | ||
|
JunTaoLuo marked this conversation as resolved.
|
||
| end | ||
|
|
||
| def fetch_eps_providers(user_address, referral, radius, eps_client:) | ||
| providers = eps_client.search_by_location( | ||
| latitude: user_address.latitude, | ||
|
|
@@ -127,18 +138,45 @@ def assign_distance(provider, user_address) | |
| ) | ||
| end | ||
|
|
||
| def filter_va_by_category_of_care(providers, category_of_care) | ||
| return providers if category_of_care.blank? | ||
| def filter_supported_facilities(facilities, category_of_care) | ||
| return facilities if category_of_care.blank? | ||
|
|
||
| normalized = category_of_care.to_s.downcase.gsub(/[\s_-]+/, '') | ||
| vaos_service_type = ServiceTypeMapper.to_vaos(category_of_care) | ||
| return facilities if vaos_service_type.nil? | ||
|
|
||
| providers.select do |provider| | ||
| provider.schedulable_services.any? do |svc| | ||
| svc.to_s.downcase.gsub(/[\s_-]+/, '') == normalized | ||
| end | ||
| facilities.select do |facility| | ||
| eligibility_service.check_eligibility( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI I updated this section to use the eligibility checks instead since this is closer to the production behavior. |
||
| facility_id: facility.unique_id, | ||
| category_of_care: | ||
| )[:direct_eligible] | ||
| end | ||
|
JunTaoLuo marked this conversation as resolved.
|
||
| end | ||
|
|
||
| def fetch_clinics_for_facility(facility, clinical_service) | ||
| systems_service.get_facility_clinics( | ||
| location_id: facility.unique_id, | ||
| clinical_service: | ||
| ) | ||
| rescue => e | ||
| Rails.logger.warn( | ||
| "#{log_prefix}: Clinic fetch failed for facility #{facility.unique_id}", | ||
| { | ||
| error_class: e.class.name, | ||
| clinical_service:, | ||
| user_uuid: @cached_user_uuid | ||
| }.compact | ||
| ) | ||
| [] | ||
| end | ||
|
|
||
| def systems_service | ||
| @systems_service ||= VAOS::V2::SystemsService.new(current_user) | ||
| end | ||
|
|
||
| def eligibility_service | ||
| @eligibility_service ||= EligibilityService.new(current_user) | ||
| end | ||
|
|
||
| def combine_and_sort(va_providers, eps_providers, referral) | ||
| referral_provider, other_eps = partition_referral_provider(eps_providers, referral) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module VAOS | ||
| module V2 | ||
| module Unified | ||
| # Fetches appointment availability (slots) for unified scheduling: VA clinic providers via | ||
| # VAOS SystemsService, community care via EPS ProviderService. VA providers must include | ||
| # +location_id+ and +id+ (clinic IEN; see VAProvider). | ||
| class SlotsService | ||
| def initialize(user) | ||
| @user = user | ||
| end | ||
|
|
||
| ## | ||
| # @param provider [BaseProvider] VAProvider or EpsProvider | ||
| # @param start_dt [String] ISO8601 start of search window (VA and EPS) | ||
| # @param end_dt [String] ISO8601 end of search window (VA and EPS) | ||
| # @param clinical_service [String, nil] VAOS clinical service (required for VA) | ||
| # @param appointment_id [String, nil] EPS draft appointment id (required for EPS) | ||
| # @return [Array<BaseSlot>] | ||
| # | ||
| # EPS +appointmentTypeId+ comes from +EpsProvider#first_self_schedulable_appointment_type_id!+ | ||
| # (EPS +appointment_types+ on the provider). | ||
| # | ||
| def slots_for(provider:, start_dt:, end_dt:, clinical_service: nil, appointment_id: nil) | ||
| case provider | ||
| when VAProvider | ||
|
PhilipDeFraties marked this conversation as resolved.
|
||
| fetch_va_slots(provider, start_dt, end_dt, clinical_service) | ||
| when EpsProvider | ||
| fetch_eps_slots(provider, start_dt, end_dt, appointment_id) | ||
| else | ||
| raise ArgumentError, "Unsupported provider type: #{provider.class.name}" | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| attr_reader :user | ||
|
|
||
| def fetch_va_slots(provider, start_dt, end_dt, clinical_service) | ||
| raise Common::Exceptions::ParameterMissing, 'clinical_service' if clinical_service.blank? | ||
|
|
||
| if provider.location_id.blank? || provider.id.blank? | ||
| raise Common::Exceptions::UnprocessableEntity.new( | ||
| detail: 'VA provider requires location_id and id (clinic IEN) for slot lookup' | ||
| ) | ||
| end | ||
|
|
||
| raw = systems_service.get_available_slots( | ||
| location_id: provider.location_id, | ||
| clinic_id: provider.id, | ||
| clinical_service:, | ||
| provider_id: nil, | ||
| start_dt:, | ||
| end_dt: | ||
| ) | ||
| Array(raw).map { |slot| VASlot.from_vaos_slot(slot, location_id: provider.location_id) } | ||
| end | ||
|
|
||
| def fetch_eps_slots(provider, start_dt, end_dt, appointment_id) | ||
|
JunTaoLuo marked this conversation as resolved.
|
||
| raise Common::Exceptions::ParameterMissing, 'start_dt' if start_dt.blank? | ||
| raise Common::Exceptions::ParameterMissing, 'end_dt' if end_dt.blank? | ||
| raise Common::Exceptions::ParameterMissing, 'appointment_id' if appointment_id.blank? | ||
|
|
||
| appointment_type_id = provider.first_self_schedulable_appointment_type_id! | ||
| provider_id = provider.provider_service_id.presence || provider.id | ||
| opts = { | ||
| appointmentTypeId: appointment_type_id, | ||
| startOnOrAfter: start_dt, | ||
| startBefore: end_dt, | ||
| appointmentId: appointment_id | ||
| } | ||
| response = eps_provider_service.get_provider_slots(provider_id, opts) | ||
| slots = response&.slots | ||
| Array(slots).map { |slot| EpsSlot.from_eps_slot(slot) } | ||
| end | ||
|
|
||
| def systems_service | ||
| @systems_service ||= VAOS::V2::SystemsService.new(user) | ||
| end | ||
|
|
||
| def eps_provider_service | ||
| @eps_provider_service ||= Eps::ProviderService.new(user) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
Uh oh!
There was an error while loading. Please reload this page.