Skip to content
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

Calvin/appeals 43853 #21723

Merged
merged 53 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
6aad694
initial updates
calvincostaBAH May 22, 2024
ba18d18
removing unnecessary variable
calvincostaBAH May 22, 2024
91d0dd7
focused in on priority
calvincostaBAH May 22, 2024
ecce138
removing non priority stuff
calvincostaBAH May 23, 2024
efd102d
added general comments
calvincostaBAH May 23, 2024
6d3b556
added BFAC and AOD to cavc aod lever query
calvincostaBAH May 23, 2024
217ea76
adding judge vacols id to query
calvincostaBAH May 24, 2024
272d01e
aod affinity_start_date filter initial changes
calvincostaBAH May 28, 2024
a66f998
fixed sorting
calvincostaBAH May 28, 2024
31e3a38
fixed rubocop issues
calvincostaBAH May 29, 2024
a75ba50
updated filter method
calvincostaBAH May 29, 2024
fa90809
error handling
calvincostaBAH May 29, 2024
e246925
Merge branch 'feature/APPEALS-43179' into Calvin/APPEALS-43853
calvincostaBAH May 31, 2024
f14d3c5
Merge branch 'feature/APPEALS-43179' into Calvin/APPEALS-43853
calvincostaBAH Jun 3, 2024
d1fb211
added ineligibility to queries for PREV_DECIDING_JUDGE
calvincostaBAH Jun 3, 2024
8891f34
fixed SQL query + added comments
calvincostaBAH Jun 3, 2024
0a3edc8
added exclude from affinity check into the case docket queries
calvincostaBAH Jun 3, 2024
14a9d63
error handling + fixing sql queries
calvincostaBAH Jun 5, 2024
ab4483a
rejects appeals without affinity_start_dates and nonmatching judges
calvincostaBAH Jun 5, 2024
b6feb0e
fixing rubocop offenses
calvincostaBAH Jun 5, 2024
b4bac6c
fixed inconsistencies between methods
calvincostaBAH Jun 6, 2024
8c46d91
fixed conditions for rejecting appeals
calvincostaBAH Jun 7, 2024
284ad9a
refactored cavc aod affinity filter to make it much easier to read
calvincostaBAH Jun 7, 2024
bb8089d
refactored code to account for AC6
calvincostaBAH Jun 10, 2024
13234da
error handling for empty exclude from affinity
calvincostaBAH Jun 10, 2024
76accda
reverted next if block to old logic to ensure it works
calvincostaBAH Jun 11, 2024
fb1f96b
added PREV_DEC_JUDGE is not null
calvincostaBAH Jun 11, 2024
caa7591
case.rb factory changes
calvincostaBAH Jun 11, 2024
544f20d
added more options to legacy_cavc_appeal creation
calvincostaBAH Jun 11, 2024
17668d6
cleaned up code for simpler reading
calvincostaBAH Jun 11, 2024
fc14574
fix for aod legacy cavc creation
calvincostaBAH Jun 11, 2024
2248bda
added tied to option to legacy cavc appeal factory
calvincostaBAH Jun 11, 2024
8c2802e
limits are now handled correctly in query
calvincostaBAH Jun 11, 2024
1e30034
replaced return false to next if, as return false was causing unexpec…
calvincostaBAH Jun 11, 2024
c3b6057
fix rspecs + one edge case
calvincostaBAH Jun 12, 2024
552423f
added cavc aod lever creation to rspecs
calvincostaBAH Jun 12, 2024
848a2e4
removed bfac and aod from nonpriority query
calvincostaBAH Jun 12, 2024
d84f33d
cavc aod appeals w/excluded judges are now properly being filtered
calvincostaBAH Jun 12, 2024
84826c8
refactored excluded judges check
calvincostaBAH Jun 12, 2024
63dd834
added to old query to fix rspec errors
calvincostaBAH Jun 13, 2024
a6f8397
modified case factory bot
calvincostaBAH Jun 13, 2024
a6eabb8
query now handles when prev_deciding_judge is nil
calvincostaBAH Jun 13, 2024
b1cf9f1
removed unnecessary condition
calvincostaBAH Jun 13, 2024
197dc50
fixed case factory to now have tied_to attatched to orig appeal
calvincostaBAH Jun 13, 2024
9230db1
fixed next if block within filter
calvincostaBAH Jun 13, 2024
072f511
handles omit scenarios + correctly rejects with next
calvincostaBAH Jun 14, 2024
dd200de
working on rspec (still failing)
calvincostaBAH Jun 14, 2024
cb049eb
fix for ineligble VLJ when infinite
calvincostaBAH Jun 17, 2024
ae9d32f
fixed rspec suite for cavc aod filters
calvincostaBAH Jun 17, 2024
e61bcec
fixed omit scenario in cavc aod affinity filter
calvincostaBAH Jun 17, 2024
897a3e0
consolidation & readability refactor
calvincostaBAH Jun 18, 2024
e4f19e8
rubocop fixes
calvincostaBAH Jun 18, 2024
7a61e22
fixed spec error
calvincostaBAH Jun 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 164 additions & 39 deletions app/models/vacols/case_docket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,23 +122,26 @@ class DocketNumberCentennialLoop < StandardError; end
"

SELECT_PRIORITY_APPEALS = "
select BFKEY, BFDLOOUT, VLJ
select BFKEY, BFDLOOUT, BFAC, AOD, VLJ, PREV_TYPE_ACTION, PREV_DECIDING_JUDGE
from (
select BFKEY, BFDLOOUT,
case when BFHINES is null or BFHINES <> 'GP' then VLJ_HEARINGS.VLJ end VLJ
select BFKEY, BFDLOOUT, BFAC, AOD,
case when BFHINES is null or BFHINES <> 'GP' then VLJ_HEARINGS.VLJ end VLJ,
PREV_APPEAL.PREV_TYPE_ACTION PREV_TYPE_ACTION,
PREV_APPEAL.PREV_DECIDING_JUDGE PREV_DECIDING_JUDGE
from (
#{SELECT_READY_APPEALS}
and (BFAC = '7' or AOD = '1')
order by BFDLOOUT
) BRIEFF
#{JOIN_ASSOCIATED_VLJS_BY_HEARINGS}
#{JOIN_PREVIOUS_APPEALS}
)
"

SELECT_PRIORITY_APPEALS_ORDER_BY_BFD19 = "
select BFKEY, BFD19, BFDLOOUT, VLJ, PREV_TYPE_ACTION, PREV_DECIDING_JUDGE
select BFKEY, BFD19, BFDLOOUT, BFAC, AOD, VLJ, PREV_TYPE_ACTION, PREV_DECIDING_JUDGE
from (
select BFKEY, BFD19, BFDLOOUT,
select BFKEY, BFD19, BFDLOOUT, BFAC, AOD,
case when BFHINES is null or BFHINES <> 'GP' then VLJ_HEARINGS.VLJ end VLJ,
PREV_APPEAL.PREV_TYPE_ACTION PREV_TYPE_ACTION,
PREV_APPEAL.PREV_DECIDING_JUDGE PREV_DECIDING_JUDGE
Expand Down Expand Up @@ -206,7 +209,6 @@ class DocketNumberCentennialLoop < StandardError; end
left join STAFF on APPEALS.VLJ = STAFF.STAFKEY
order by BFD19
"

# rubocop:disable Metrics/MethodLength
def self.counts_by_priority_and_readiness
query = <<-SQL
Expand Down Expand Up @@ -341,27 +343,34 @@ def self.age_of_n_oldest_genpop_priority_appeals(num)
appeals.map { |appeal| appeal["bfdloout"] }
end

# {UPDATE}
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
def self.age_of_n_oldest_priority_appeals_available_to_judge(judge, num)
priority_cdl_aod_query = generate_priority_case_distribution_lever_aod_query
conn = connection

query = <<-SQL
#{SELECT_PRIORITY_APPEALS_ORDER_BY_BFD19}
where (VLJ = ? or #{ineligible_judges_sattyid_cache} or VLJ is null)
and rownum <= ?
where (VLJ = ? or #{ineligible_judges_sattyid_cache} or VLJ is null or #{priority_cdl_aod_query})
SQL

fmtd_query = sanitize_sql_array([
query,
judge.vacols_attorney_id,
num
judge.vacols_attorney_id
])

appeals = conn.exec_query(fmtd_query).to_a

cavc_aod_affinity_filter(appeals, judge)

appeals.sort_by { |appeal| appeal[:bfd19] } if use_by_docket_date?

appeals = appeals.first(num) unless num.nil? # {Reestablishes the limit}

appeals.map { |appeal| appeal["bfd19"] }
end
# rubocop:enable

# {UPDATE}
def self.age_of_n_oldest_nonpriority_appeals_available_to_judge(judge, num)
conn = connection

Expand Down Expand Up @@ -451,8 +460,7 @@ def self.ready_to_distribute_appeals
connection.exec_query(fmtd_query).to_a
end

# {UPDATE}
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/ParameterLists, Metrics/MethodLength
# rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
def self.distribute_nonpriority_appeals(judge, genpop, range, limit, bust_backlog, dry_run = false)
fail(DocketNumberCentennialLoop, COPY::MAX_LEGACY_DOCKET_NUMBER_ERROR_MESSAGE) if Time.zone.now.year >= 2030

Expand All @@ -461,7 +469,6 @@ def self.distribute_nonpriority_appeals(judge, genpop, range, limit, bust_backlo
#{SELECT_NONPRIORITY_APPEALS_ORDER_BY_BFD19}
where (((VLJ = ? or #{ineligible_judges_sattyid_cache}) and 1 = ?) or (VLJ is null and 1 = ?))
and (DOCKET_INDEX <= ? or 1 = ?)
and rownum <= ?
SQL
else
# Docket numbers begin with the two digit year. The Board of Veterans Appeals was created in 1930.
Expand All @@ -481,7 +488,6 @@ def self.distribute_nonpriority_appeals(judge, genpop, range, limit, bust_backlo
#{SELECT_NONPRIORITY_APPEALS}
where (((VLJ = ? or #{ineligible_judges_sattyid_cache}) and 1 = ?) or (VLJ is null and 1 = ?))
and (DOCKET_INDEX <= ? or 1 = ?)
and rownum <= ?
SQL
end

Expand All @@ -491,27 +497,23 @@ def self.distribute_nonpriority_appeals(judge, genpop, range, limit, bust_backlo
(genpop == "any" || genpop == "not_genpop") ? 1 : 0,
(genpop == "any" || genpop == "only_genpop") ? 1 : 0,
range,
range.nil? ? 1 : 0,
limit
range.nil? ? 1 : 0
])

distribute_appeals(fmtd_query, judge, dry_run)
distribute_appeals(fmtd_query, judge, limit, dry_run)
end
# rubocop:enable Metrics/ParameterLists, Metrics/MethodLength

# {UPDATE}
def self.distribute_priority_appeals(judge, genpop, limit, dry_run = false)
priority_cdl_aod_query = generate_priority_case_distribution_lever_aod_query
query = if use_by_docket_date?
<<-SQL
#{SELECT_PRIORITY_APPEALS_ORDER_BY_BFD19}
where (((VLJ = ? or #{ineligible_judges_sattyid_cache}) and 1 = ?) or (VLJ is null and 1 = ?))
and (rownum <= ? or 1 = ?)
where (((VLJ = ? or #{ineligible_judges_sattyid_cache}) and 1 = ?) or (VLJ is null and 1 = ?) or #{priority_cdl_aod_query})
SQL
else
<<-SQL
#{SELECT_PRIORITY_APPEALS}
where (((VLJ = ? or #{ineligible_judges_sattyid_cache}) and 1 = ?) or (VLJ is null and 1 = ?))
and (rownum <= ? or 1 = ?)
where (((VLJ = ? or #{ineligible_judges_sattyid_cache}) and 1 = ?) or (VLJ is null and 1 = ?) or #{priority_cdl_aod_query})
SQL
end

Expand All @@ -520,27 +522,36 @@ def self.distribute_priority_appeals(judge, genpop, limit, dry_run = false)
judge.vacols_attorney_id,
(genpop == "any" || genpop == "not_genpop") ? 1 : 0,
(genpop == "any" || genpop == "only_genpop") ? 1 : 0,
limit,
limit.nil? ? 1 : 0
judge.vacols_attorney_id
])

distribute_appeals(fmtd_query, judge, dry_run)
distribute_appeals(fmtd_query, judge, limit, dry_run)
end
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# :nocov:

def self.distribute_appeals(query, judge, dry_run)
# :nocov:
# rubocop:disable Metrics/AbcSize
def self.distribute_appeals(query, judge, limit, dry_run)
conn = connection

conn.transaction do
if dry_run
conn.exec_query(query).to_a
dry_appeals = conn.exec_query(query).to_a

cavc_aod_affinity_filter(dry_appeals, judge)

dry_appeals
else
conn.execute(LOCK_READY_APPEALS) unless FeatureToggle.enabled?(:acd_disable_legacy_lock_ready_appeals)

appeals = conn.exec_query(query).to_a
return appeals if appeals.empty?

cavc_aod_affinity_filter(appeals, judge)

appeals.sort_by { |appeal| appeal[:bfd19] } if use_by_docket_date?

appeals = appeals.first(limit) unless limit.nil? # {Reestablishes the limit}

vacols_ids = appeals.map { |appeal| appeal["bfkey"] }
location = if FeatureToggle.enabled?(:legacy_das_deprecation, user: RequestStore.store[:current_user])
LegacyAppeal::LOCATION_CODES[:caseflow]
Expand All @@ -553,15 +564,99 @@ def self.distribute_appeals(query, judge, dry_run)
end
end

# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/ParameterLists

def self.generate_priority_case_distribution_lever_aod_query
if case_affinity_days_lever_value_is_selected?(CaseDistributionLever.cavc_aod_affinity_days) ||
CaseDistributionLever.cavc_aod_affinity_days == Constants.ACD_LEVERS.omit
"((PREV_DECIDING_JUDGE = ? or PREV_DECIDING_JUDGE is null or PREV_DECIDING_JUDGE is not null)
and AOD = '1' and BFAC = '7' )"
elsif CaseDistributionLever.cavc_aod_affinity_days == Constants.ACD_LEVERS.infinite
"((PREV_DECIDING_JUDGE = ? or #{ineligible_judges_sattyid_cache(true)} or
#{vacols_judges_with_exclude_appeals_from_affinity}) and AOD = '1' and BFAC = '7' )"
else
"VLJ = ?"
end
end

def self.use_by_docket_date?
FeatureToggle.enabled?(:acd_distribute_by_docket_date, user: RequestStore.store[:current_user])
end

# rubocop:disable Metrics/MethodLength
def self.ineligible_judges_sattyid_cache
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
def self.cavc_aod_affinity_filter(appeals, judge)
appeals.reject! do |appeal|
# {will skip if not CAVC AOD || if CAVC AOD being distributed to tied_to judge || if not tied to any judge}
next if tied_to_or_not_cavc_aod?(appeal, judge)

if not_distributing_to_tied_judge?(appeal, judge)
next if ineligible_judges_sattyids&.include?(appeal["vlj"])

next (appeal["vlj"] != judge.vacols_attorney_id)
end

next if ineligible_or_excluded_deciding_judge?(appeal)

if case_affinity_days_lever_value_is_selected?(CaseDistributionLever.cavc_aod_affinity_days)
next if appeal["prev_deciding_judge"] == judge.vacols_attorney_id

reject_due_to_affinity?(appeal)
elsif CaseDistributionLever.cavc_aod_affinity_days == Constants.ACD_LEVERS.infinite
next if ineligible_judges_sattyids&.include?(appeal["vlj"])

appeal["prev_deciding_judge"] != judge.vacols_attorney_id
elsif CaseDistributionLever.cavc_aod_affinity_days == Constants.ACD_LEVERS.omit
appeal["prev_deciding_judge"] == appeal["vlj"]
end
end
end

def self.tied_to_or_not_cavc_aod?(appeal, judge)
(appeal["bfac"] != "7" || appeal["aod"] != 1) ||
(appeal["bfac"] == "7" && appeal["aod"] == 1 &&
!appeal["vlj"].blank? &&
(appeal["vlj"] == appeal["prev_deciding_judge"] || appeal["prev_deciding_judge"].nil?) &&
appeal["vlj"] == judge.vacols_attorney_id) ||
(appeal["vlj"].nil? && appeal["prev_deciding_judge"].nil?)
end

# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

def self.not_distributing_to_tied_judge?(appeal, judge)
!appeal["vlj"].blank? &&
(appeal["vlj"] == appeal["prev_deciding_judge"]) &&
(appeal["vlj"] != judge.vacols_attorney_id)
end

def self.ineligible_or_excluded_deciding_judge?(appeal)
# {if deciding_judge is ineligible or excluded, we will skip, unless excluded deciding_judge = VLJ}
ineligible_judges_sattyids&.include?(appeal["prev_deciding_judge"]) ||
(appeal["vlj"] != appeal["prev_deciding_judge"] &&
excluded_judges_sattyids&.include?(appeal["prev_deciding_judge"]))
end

def self.reject_due_to_affinity?(appeal)
VACOLS::Case.find_by(bfkey: appeal["bfkey"])&.appeal_affinity&.affinity_start_date.nil? ||
(VACOLS::Case.find_by(bfkey: appeal["bfkey"])
.appeal_affinity
.affinity_start_date > CaseDistributionLever.cavc_aod_affinity_days.to_i.days.ago)
end

def self.ineligible_judges_sattyids
Rails.cache.fetch("case_distribution_ineligible_judges")&.pluck(:sattyid)&.reject(&:blank?) || []
end

def self.excluded_judges_sattyids
VACOLS::Staff.where(sdomainid: JudgeTeam.active
.where(exclude_appeals_from_affinity: true)
.flat_map(&:judge).compact.pluck(:css_id))&.pluck(:sattyid)
end

# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
def self.ineligible_judges_sattyid_cache(prev_deciding_judge = false)
if FeatureToggle.enabled?(:acd_cases_tied_to_judges_no_longer_with_board) &&
!Rails.cache.fetch("case_distribution_ineligible_judges")&.pluck(:sattyid)&.reject(&:blank?).blank?
list = Rails.cache.fetch("case_distribution_ineligible_judges")&.pluck(:sattyid)&.reject(&:blank?)
!ineligible_judges_sattyids.blank?
list = ineligible_judges_sattyids
split_lists = {}
num_of_lists = (list.size.to_f / 999).ceil

Expand All @@ -575,15 +670,45 @@ def self.ineligible_judges_sattyid_cache

vljs_strings = split_lists.flat_map do |k, v|
base = "(#{v.join(', ')})"
base += " or VLJ in " unless k == split_lists.keys.last
if prev_deciding_judge
base += " or PREV_DECIDING_JUDGE in " unless k == split_lists.keys.last
else
base += " or VLJ in " unless k == split_lists.keys.last
end
base
end

"VLJ in #{vljs_strings.join}"
if prev_deciding_judge
"PREV_DECIDING_JUDGE in #{vljs_strings.join}"
else
"VLJ in #{vljs_strings.join}"
end

elsif prev_deciding_judge
"PREV_DECIDING_JUDGE = 'false'"
else
"VLJ = 'false'"
end
end
# rubocop:enable Metrics/MethodLength

# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength

def self.vacols_judges_with_exclude_appeals_from_affinity
return "PREV_DECIDING_JUDGE = 'false'" unless FeatureToggle.enabled?(:acd_exclude_from_affinity)

satty_ids = excluded_judges_sattyids

if satty_ids.blank?
"PREV_DECIDING_JUDGE = 'false'"
else
"PREV_DECIDING_JUDGE in (#{satty_ids.join(', ')})"
end
end

def self.case_affinity_days_lever_value_is_selected?(lever_value)
return false if lever_value == "omit" || lever_value == "infinite"

true
end
end
# rubocop:enable Metrics/ClassLength
# rubocop:enable Metrics/ClassLength, Metrics/AbcSize
12 changes: 12 additions & 0 deletions spec/factories/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
roles { ["Hearing Prep"] }
end

trait :judge_with_appeals_excluded_from_affinity do
with_appeals_excluded_from_affinity_judge_team
roles { ["Hearing Prep"] }
end

trait :ama_only_judge do
after(:create) do |judge|
JudgeTeam.for_judge(judge)&.update(ama_only_push: true, ama_only_request: true) ||
Expand Down Expand Up @@ -95,6 +100,13 @@
end
end

trait :with_appeals_excluded_from_affinity_judge_team do
after(:create) do |judge|
judge_team = JudgeTeam.for_judge(judge) || JudgeTeam.create_for_judge(judge)
judge_team.update!(exclude_appeals_from_affinity: true)
end
end

trait :with_vacols_attorney_record do
after(:create) do |user|
create(:staff, :attorney_role, user: user)
Expand Down
Loading
Loading