From 5ed7ebd21ca8d8de3d83ab71b319ae070fa6c156 Mon Sep 17 00:00:00 2001 From: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:02:16 -0400 Subject: [PATCH 01/44] APPEALS-36339 (#21436) * Updated to remove obsolete levers of ACD Disable Legacy Distributions and ACD Disable Non-priority Distributions * Fixed redundant linting issue * Fixed empty line linting issue --- .../push_priority_appeals_to_judges_job.rb | 8 +------ .../concerns/by_docket_date_distribution.rb | 22 +++++++------------ app/models/distribution.rb | 2 -- app/models/docket_coordinator.rb | 4 ---- scripts/enable_features_dev.rb | 2 -- 5 files changed, 9 insertions(+), 29 deletions(-) diff --git a/app/jobs/push_priority_appeals_to_judges_job.rb b/app/jobs/push_priority_appeals_to_judges_job.rb index 3e1bcfb7a9b..e5d92e7d902 100644 --- a/app/jobs/push_priority_appeals_to_judges_job.rb +++ b/app/jobs/push_priority_appeals_to_judges_job.rb @@ -57,9 +57,7 @@ def slack_report # rubocop:disable Metrics/AbcSize, Metrics/MethodLength docket_coordinator.dockets.each_pair do |sym, docket| report << "*Number of #{sym} appeals _not_ distributed*: #{docket.count(priority: true, ready: true)}" end - unless disable_legacy? - report << "*Number of Legacy Hearing Non Genpop appeals _not_ distributed*: #{legacy_not_genpop_count}" - end + report << "*Number of Legacy Hearing Non Genpop appeals _not_ distributed*: #{legacy_not_genpop_count}" report << "" report << "*Debugging information*" @@ -181,10 +179,6 @@ def use_by_docket_date? FeatureToggle.enabled?(:acd_distribute_by_docket_date, user: RequestStore.store[:current_user]) end - def disable_legacy? - FeatureToggle.enabled?(:acd_disable_legacy_distributions, user: RequestStore.store[:current_user]) - end - def legacy_not_genpop_count docket_coordinator.dockets[:legacy].not_genpop_priority_count end diff --git a/app/models/concerns/by_docket_date_distribution.rb b/app/models/concerns/by_docket_date_distribution.rb index 851858c133a..2b1b787f725 100644 --- a/app/models/concerns/by_docket_date_distribution.rb +++ b/app/models/concerns/by_docket_date_distribution.rb @@ -32,12 +32,10 @@ def requested_distribution priority_rem = priority_target.clamp(0, @rem) distribute_priority_appeals_from_all_dockets_by_age_to_limit(priority_rem, style: "request") - unless FeatureToggle.enabled?(:acd_disable_nonpriority_distributions, user: RequestStore.store[:current_user]) - # Distribute the oldest nonpriority appeals from any docket if we haven't distributed {batch_size} appeals - # @nonpriority_iterations guards against an infinite loop if not enough cases are ready to distribute - until @rem <= 0 || @nonpriority_iterations >= MAX_NONPRIORITY_ITERATIONS - distribute_nonpriority_appeals_from_all_dockets_by_age_to_limit(@rem) - end + # Distribute the oldest nonpriority appeals from any docket if we haven't distributed {batch_size} appeals + # @nonpriority_iterations guards against an infinite loop if not enough cases are ready to distribute + until @rem <= 0 || @nonpriority_iterations >= MAX_NONPRIORITY_ITERATIONS + distribute_nonpriority_appeals_from_all_dockets_by_age_to_limit(@rem) end @appeals end @@ -59,7 +57,7 @@ def distribute_nonpriority_appeals_from_all_dockets_by_age_to_limit(limit, style end end - # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + # rubocop:disable Metrics/MethodLength def ama_statistics priority_counts = { count: priority_count } nonpriority_counts = { count: nonpriority_count } @@ -69,10 +67,8 @@ def ama_statistics nonpriority_counts[sym] = docket.count(priority: false, ready: true) end - unless FeatureToggle.enabled?(:acd_disable_legacy_distributions, user: RequestStore.store[:current_user]) - priority_counts[:legacy_hearing_tied_to] = legacy_hearing_priority_count(judge) - nonpriority_counts[:legacy_hearing_tied_to] = legacy_hearing_nonpriority_count(judge) - end + priority_counts[:legacy_hearing_tied_to] = legacy_hearing_priority_count(judge) + nonpriority_counts[:legacy_hearing_tied_to] = legacy_hearing_nonpriority_count(judge) nonpriority_counts[:iterations] = @nonpriority_iterations @@ -80,8 +76,6 @@ def ama_statistics settings = {} feature_toggles = [ - :acd_disable_legacy_distributions, - :acd_disable_nonpriority_distributions, :specialty_case_team_distribution ] feature_toggles.each do |sym| @@ -110,7 +104,7 @@ def ama_statistics #{error.class}: #{error.message}, #{error.backtrace.first}" } end - # rubocop:enable Metrics/MethodLength, Metrics/AbcSize + # rubocop:enable Metrics/MethodLength def ama_distributed_cases_tied_to_ineligible_judges @appeals.filter_map do |appeal| diff --git a/app/models/distribution.rb b/app/models/distribution.rb index 3066975042b..38e9192db79 100644 --- a/app/models/distribution.rb +++ b/app/models/distribution.rb @@ -89,8 +89,6 @@ def judge_tasks end def judge_legacy_tasks - return [] if FeatureToggle.enabled?(:acd_disable_legacy_distributions, user: RequestStore.store[:current_user]) - legacy_tasks = QueueRepository.tasks_for_user(judge.css_id) @judge_legacy_tasks ||= legacy_tasks.select { |task| task.assigned_to_attorney_date.nil? } diff --git a/app/models/docket_coordinator.rb b/app/models/docket_coordinator.rb index 7f7a7237332..1d01a87dc8e 100644 --- a/app/models/docket_coordinator.rb +++ b/app/models/docket_coordinator.rb @@ -9,10 +9,6 @@ def dockets hearing: HearingRequestDocket.new } - if FeatureToggle.enabled?(:acd_disable_legacy_distributions, user: RequestStore.store[:current_user]) - all_dockets.delete(:legacy) - end - @dockets ||= all_dockets end diff --git a/scripts/enable_features_dev.rb b/scripts/enable_features_dev.rb index b2f075c550b..ed401d29ecc 100644 --- a/scripts/enable_features_dev.rb +++ b/scripts/enable_features_dev.rb @@ -60,8 +60,6 @@ def call poa_auto_refresh interface_version_2 cc_vacatur_visibility - acd_disable_legacy_distributions - acd_disable_nonpriority_distributions acd_disable_legacy_lock_ready_appeals justification_reason ] From 058a98fb892a13239c5b493de6a0b0e23e8a6745 Mon Sep 17 00:00:00 2001 From: sbashamoni Date: Thu, 25 Apr 2024 15:11:03 -0400 Subject: [PATCH 02/44] Changed dockerfile to install node through NVM --- Dockerfile | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9932ff9876d..720469c5adc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,6 +34,8 @@ COPY docker-bin/ca-certs/*.crt /usr/local/share/ca-certificates/va/ RUN update-ca-certificates COPY docker-bin/ca-certs/cacert.pem /etc/ssl/certs/cacert.pem +RUN rm /bin/sh && ln -s /bin/bash /bin/sh + RUN apt -y update && \ apt -y upgrade && \ mkdir -p /usr/share/man/man1 && \ @@ -41,9 +43,22 @@ RUN apt -y update && \ apt install -y ${BUILD} && \ curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ - apt -y update && \ - apt-get install nodejs=${NODE} && \ - apt install -y ${CASEFLOW} && \ + apt -y update + +#install node +RUN mkdir /usr/local/nvm +ENV NVM_DIR /usr/local/nvm +ENV NODE_VERSION 14.20.0 +ENV NVM_INSTALL_PATH $NVM_DIR/versions/node/v$NODE_VERSION +RUN curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash +RUN source $NVM_DIR/nvm.sh \ + && nvm install $NODE_VERSION \ + && nvm alias default $NODE_VERSION \ + && nvm use default +ENV NODE_PATH $NVM_INSTALL_PATH/lib/node_modules +ENV PATH $NVM_INSTALL_PATH/bin:$PATH + +RUN apt install -y ${CASEFLOW} && \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get clean && apt-get autoclean && apt-get autoremove From c9fe3006adc4e6f7c5d9ee1cef119f3b745fb88f Mon Sep 17 00:00:00 2001 From: sbashamoni Date: Thu, 25 Apr 2024 15:11:22 -0400 Subject: [PATCH 03/44] Update env.sh with node 14.20 path --- docker-bin/env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-bin/env.sh b/docker-bin/env.sh index 55b6f8131a2..e9dcc1918ad 100644 --- a/docker-bin/env.sh +++ b/docker-bin/env.sh @@ -16,7 +16,7 @@ export DEMO_PORT=1521 export DEMO_DB="(DESCRIPTION= (ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)(HOST=VACOLS_DB-development)(PORT=1521)))(RECV_TIMEOUT=120)(SEND_TIMEOUT=5)(CONNECT_DATA=(SID=BVAP)))" -export PATH=/.yarn/bin:/.config/yarn/global/node_modules/.bin:/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/oracle/instantclient_12_2 +export PATH=/.yarn/bin:/.config/yarn/global/node_modules/.bin:/usr/local/nvm/versions/node/v14.20.0/bin:/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/oracle/instantclient_12_2: export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 export ORACLE_HOME=/opt/oracle/instantclient_12_2 From 6faf06e3df1af8f25b49c5bf51c8b6653bde555c Mon Sep 17 00:00:00 2001 From: sbashamoni Date: Fri, 26 Apr 2024 09:35:38 -0400 Subject: [PATCH 04/44] Removed unused node env --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 720469c5adc..f386a753d99 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,8 @@ MAINTAINER Development and Operations team @ Department of Veterans Affairs # Build variables ENV BUILD build-essential postgresql-client libaio1 libpq-dev libsqlite3-dev curl software-properties-common apt-transport-https pdftk -ENV CASEFLOW git yarn -ENV NODE 14.20.0 +ENV CASEFLOW git yarn node +#ENV NODE 14.20.0 # Environment (system) variables ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_12_2:$LD_LIBRARY_PATH" \ From 55252d90b972d6da471b7dea75ca5586fcce7963 Mon Sep 17 00:00:00 2001 From: sbashamoni <138054633+sbashamoni@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:24:39 -0400 Subject: [PATCH 05/44] Update Dockerfile Removed unused node env variable --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f386a753d99..8e35749ec5f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,6 @@ MAINTAINER Development and Operations team @ Department of Veterans Affairs # Build variables ENV BUILD build-essential postgresql-client libaio1 libpq-dev libsqlite3-dev curl software-properties-common apt-transport-https pdftk ENV CASEFLOW git yarn node -#ENV NODE 14.20.0 # Environment (system) variables ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_12_2:$LD_LIBRARY_PATH" \ From c3e6f80de434f558d551f139e0adf25c9a319877 Mon Sep 17 00:00:00 2001 From: sbashamoni <138054633+sbashamoni@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:33:55 -0400 Subject: [PATCH 06/44] Removed unused node env --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8e35749ec5f..0c9f810ef90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ MAINTAINER Development and Operations team @ Department of Veterans Affairs # Build variables ENV BUILD build-essential postgresql-client libaio1 libpq-dev libsqlite3-dev curl software-properties-common apt-transport-https pdftk -ENV CASEFLOW git yarn node +ENV CASEFLOW git yarn # Environment (system) variables ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_12_2:$LD_LIBRARY_PATH" \ From d281c111a0ff10624c1d235f43c3144a0919e9a4 Mon Sep 17 00:00:00 2001 From: Michael Beard <131783726+mbeardy@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:06:42 -0500 Subject: [PATCH 07/44] mbeard/APPEALS-44379 (#21451) * init commit * new questions about method * method for ready_appeals_from_levers 3rd iteration * switched to conditional * updates test and constants file * adds testing scenarios for edge cases * fixes test * adds constants to top of file --------- Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- app/models/docket.rb | 19 +++++++++++ client/constants/DISTRIBUTION.json | 16 +++++----- db/seeds/case_distribution_levers.rb | 12 +++---- spec/models/docket_spec.rb | 47 ++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 14 deletions(-) diff --git a/app/models/docket.rb b/app/models/docket.rb index d56e7bc2a13..eef25f03bcd 100644 --- a/app/models/docket.rb +++ b/app/models/docket.rb @@ -9,6 +9,9 @@ def docket_type fail Caseflow::Error::MustImplementInSubclass end + PRIORITY = "priority" + NON_PRIORITY = "non_priority" + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity # :reek:LongParameterList def appeals(priority: nil, genpop: nil, ready: nil, judge: nil) @@ -30,6 +33,22 @@ def appeals(priority: nil, genpop: nil, ready: nil, judge: nil) end # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + def build_lever_item(docket_type, priority_status) + "disable_ama_#{priority_status}_#{docket_type.downcase}" + end + + def ready_priority_nonpriority_appeals(priority = false) + priority_status = priority ? PRIORITY : NON_PRIORITY + lever_item = build_lever_item(docket_type, priority_status) + lever = CaseDistributionLever.find_by_item(Constants::DISTRIBUTION[lever_item]) + lever_value = lever&.value + if lever_value == "true" + [] + else + self + end + end + def count(priority: nil, ready: nil) # The underlying scopes here all use `group_by` statements, so calling # `count` on `appeals` will return a hash. To get the number of appeals, we diff --git a/client/constants/DISTRIBUTION.json b/client/constants/DISTRIBUTION.json index dd6df8f92ee..74515044f1d 100644 --- a/client/constants/DISTRIBUTION.json +++ b/client/constants/DISTRIBUTION.json @@ -39,20 +39,20 @@ "alternative_batch_size_title": "Alternate Batch Size", "batch_size_per_attorney_title": "Batch Size Per Attorney", "request_more_cases_minimum_title": "Request More Cases Minimum", - "disable_legacy_priority": "ACD Disable Legacy Priority" , + "disable_legacy_priority": "ACD Disable Legacy Priority", "disable_legacy_priority_title": "ACD Disable Legacy Priority", - "disable_legacy_non_priority": "ACD Disable Legacy Non-priority" , + "disable_legacy_non_priority": "ACD Disable Legacy Non-priority", "disable_legacy_non_priority_title": "ACD Disable Legacy Non-priority", - "disable_ama_non_priority_hearing": "ACD Disable AMA Non-Priority Hearing" , + "disable_ama_non_priority_hearing": "disable_ama_non_priority_hearing", "disable_ama_non_priority_hearing_title": "ACD Disable AMA Non-Priority Hearing", - "disable_ama_non_priority_direct_review": "ACD Disable AMA Non-Priority Direct Review" , + "disable_ama_non_priority_direct_review": "disable_ama_non_priority_direct_review", "disable_ama_non_priority_direct_review_title": "ACD Disable AMA Non-Priority Direct Review", - "disable_ama_non_priority_evidence_sub": "ACD Disable AMA Non-Priority Evidence Submission" , + "disable_ama_non_priority_evidence_sub": "disable_ama_non_priority_evidence_sub", "disable_ama_non_priority_evidence_sub_title": "ACD Disable AMA Non-Priority Evidence Submission", - "disable_ama_priority_hearing": "ACD Disable AMA Priority Hearing" , + "disable_ama_priority_hearing": "disable_ama_priority_hearing", "disable_ama_priority_hearing_title": "ACD Disable AMA Priority Hearing", - "disable_ama_priority_direct_review": "ACD Disable AMA Priority Direct Review" , + "disable_ama_priority_direct_review": "disable_ama_priority_direct_review", "disable_ama_priority_direct_review_title": "ACD Disable AMA Priority Direct Review", - "disable_ama_priority_evidence_sub": "ACD Disable AMA Priority Evidence Submission" , + "disable_ama_priority_evidence_sub": "disable_ama_priority_evidence_sub", "disable_ama_priority_evidence_sub_title": "ACD Disable AMA Priority Evidence Submission" } diff --git a/db/seeds/case_distribution_levers.rb b/db/seeds/case_distribution_levers.rb index e74bc653d66..9bf529193cc 100644 --- a/db/seeds/case_distribution_levers.rb +++ b/db/seeds/case_distribution_levers.rb @@ -600,7 +600,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, lever_group_order: 102 @@ -612,7 +612,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, lever_group_order: 103 @@ -624,7 +624,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, lever_group_order: 104 @@ -636,7 +636,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, lever_group_order: 105 @@ -648,7 +648,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, lever_group_order: 106 @@ -660,7 +660,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, lever_group_order: 107 diff --git a/spec/models/docket_spec.rb b/spec/models/docket_spec.rb index 3057e93ecb5..80a9be5ef46 100644 --- a/spec/models/docket_spec.rb +++ b/spec/models/docket_spec.rb @@ -284,6 +284,53 @@ end end + context "ready_priority_nonpriority_appeals" do + let(:docket) { DirectReviewDocket.new } + + it "returns docket when the corresponding CaseDistributionLever value is false" do + CaseDistributionLever.where(item: "disable_ama_non_priority_direct_review").update(value: false) + result = docket.ready_priority_nonpriority_appeals(false) + expect(result).to eq(docket) + end + + it "returns an empty array when the corresponding CaseDistributionLever value is true" do + CaseDistributionLever.where(item: "disable_ama_non_priority_direct_review").update(value: "true") + lever = CaseDistributionLever.find_by_item("disable_ama_non_priority_direct_review") + expect(lever.value).to eq("true") + result = docket.ready_priority_nonpriority_appeals(false) + expect(result).to eq([]) + end + + it "returns the docket itself when the corresponding CaseDistributionLever record is not found" do + allow(CaseDistributionLever).to receive(:find_by_item).and_return(nil) + expect(docket.ready_priority_nonpriority_appeals(false)).to eq(docket) + end + + it "returns the docket itself when the lever value is any non-'true' value" do + non_true_values = %w[false null undefined enabled] + non_true_values.each do |value| + allow_any_instance_of(CaseDistributionLever).to receive(:value).and_return(value) + expect(docket.ready_priority_nonpriority_appeals(false)).to eq(docket) + end + end + + it "returns an empty array when the lever value is true and priority is true" do + allow(CaseDistributionLever).to receive(:find_by_item).and_return(double(value: "true")) + expect(docket.ready_priority_nonpriority_appeals(true)).to eq([]) + end + + it "returns the docket itself when the lever value is false and priority is true" do + allow(CaseDistributionLever).to receive(:find_by_item).and_return(double(value: "false")) + expect(docket.ready_priority_nonpriority_appeals(true)).to eq(docket) + end + + it "correctly builds the lever item based on docket type" do + expect(docket).to receive(:docket_type).and_return("direct_review") + expect(docket).to receive(:build_lever_item).with("direct_review", "non_priority").and_call_original + docket.ready_priority_nonpriority_appeals(false) + end + end + context "age_of_n_oldest_genpop_priority_appeals" do subject { DirectReviewDocket.new.age_of_n_oldest_genpop_priority_appeals(1) } From 7b3aad6da2909006be1cf78af29cb625a408dbce Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Thu, 2 May 2024 23:08:26 -0400 Subject: [PATCH 08/44] APPEALS-44417 Seeds for Docket Lever Demo (#21534) Co-authored-by: Christopher Detlef <> --- .../demo_docket_priority_lever_test_data.rb | 685 ++++++++++++++++++ 1 file changed, 685 insertions(+) create mode 100644 db/seeds/demo_docket_priority_lever_test_data.rb diff --git a/db/seeds/demo_docket_priority_lever_test_data.rb b/db/seeds/demo_docket_priority_lever_test_data.rb new file mode 100644 index 00000000000..7760e83c9ea --- /dev/null +++ b/db/seeds/demo_docket_priority_lever_test_data.rb @@ -0,0 +1,685 @@ +# frozen_string_literal: true + +module Seeds + class DemoDocketPriorityLeverTestData < Base + def initialize + initialize_docket_seeds_lists + initialize_ev_sub_np_dockets_file_number_and_participant_id + initialize_ev_sub_prior_dockets_file_number_and_participant_id + initialize_dr_np_dockets_file_number_and_participant_id + initialize_dr_prior_dockets_file_number_and_participant_id + initialize_hearings_np_dockets_file_number_and_participant_id + initialize_hearings_prior_dockets_file_number_and_participant_id + initialize_cavc_legacy_np_dockets_file_number_and_participant_id + initialize_cavc_legacy_prior_dockets_file_number_and_participant_id + initialize_cavc_hearing_np_dockets_file_number_and_participant_id + initialize_cavc_hearing_prior_dockets_file_number_and_participant_id + initialize_legacy_np_dockets_file_number_and_participant_id + initialize_legacy_prior_dockets_file_number_and_participant_id + end + + def seed! + RequestStore[:current_user] = User.system_user + create_judges + create_dockets + end + + private + def create_judges + find_or_create_judge("BVADCremin", "Daija K Cremin") + find_or_create_judge("BVAKKEELING", "Keith Judge_CaseToAssign_NoTeam Keeling" ) + find_or_create_judge("BVACOTBJudge", "Judith COTB Judge" ) + find_or_create_inactive_judge("IneligJudge", "Ineligible JudgeAA") + find_or_create_attorney("CAVCATNY", "CAVC Attorney") + end + + def create_dockets + create_evidence_submission_non_priority_dockets + create_evidence_submission_priority_dockets + create_direct_review_non_priority_dockets + create_direct_review_priority_dockets + create_hearings_non_priority_dockets + create_hearings_priority_dockets + end + + def initialize_docket_seeds_lists + @evidence_sub_np_reciept_days_ago_list = [700, 699, 697, 613, 612, 611, 475, 474, 473, 437, 436, 435, 200, 199, 198, 197, 196, 195, 194, 193] + @evidence_sub_prior_reciept_days_ago_list = [100, 99, 98, 97, 55, 54, 53, 49, 48, 80, 99, 98, 97, 55, 54, 53, 49, 48, 80, 77] + @direct_rev_np_reciept_days_ago_list = [700, 699, 697, 613, 612, 611, 475, 474, 473, 437, 436, 435, 200, 199, 198, 197, 196, 195, 194, 193] + @direct_rev_prior_reciept_days_ago_list = [100, 99, 98, 97, 55, 54, 53, 49, 48, 80, 99, 98, 97, 55, 54, 53, 49, 48, 80, 77] + @hearings_ineligible_judge_np_days_ago_list = [700, 699, 697, 613, 612, 611, 475, 474, 473, 437, 436, 435, 200, 199, 198, 197, 196, 195, 194, 193] + @hearings_cremin_np_days_ago_list = [700, 699, 697, 613, 612, 611, 475, 474, 473, 437] + @hearings_ineligible_judge_prior_days_ago_list = [700, 699, 697, 613, 612, 611, 475, 474, 473, 437, 436, 435, 200, 199, 198, 197, 196, 195, 194, 193] + @hearings_cremin_prior_days_ago_list = [700, 699, 697, 613, 612, 611, 475, 474, 473, 437] + @cavc_legacy_np_keeling_days_ago_list = [600] + @cavc_legacy_prior_keeling_days_ago_list = [60] + @cavc_ama_hearing_np_keeling_days_ago_list = [600] + @cavc_ama_hearing_prior_keeling_days_ago_list = [60] + @legacy_np_ineligible_judge_days_ago_list = [4082, 3000, 2000, 1888, 1800, 1500, 1400, 1200, 1000, 997] + @legacy_prior_ineligible_judge_days_ago_list = [100, 99, 98, 97, 55, 54, 53, 49, 48, 80] + @legacy_np_BVACOTBJudge_days_ago_list = [3800, 2800, 1900, 1877, 1760, 1479, 1300, 1100, 999, 998] + @legacy_prior_BVACOTBJudge_days_ago_list = [99, 98, 97, 55, 54, 53, 49, 48, 80, 77] + end + + + def create_evidence_submission_non_priority_dockets + @evidence_sub_np_reciept_days_ago_list.each do |days| + create_evidence_submission_non_priority_docket(days.days.ago) + end + end + + def create_evidence_submission_priority_dockets + @evidence_sub_prior_reciept_days_ago_list.each do |days| + create_evidence_submission_priority_docket(days.days.ago) + end + end + + def create_direct_review_non_priority_dockets + @direct_rev_np_reciept_days_ago_list.each do |days| + create_direct_review_non_priority_docket(days.days.ago) + end + end + + def create_direct_review_priority_dockets + @direct_rev_prior_reciept_days_ago_list.each do |days| + create_direct_review_priority_docket(days.days.ago) + end + end + + def create_hearings_non_priority_dockets + @hearings_ineligible_judge_np_days_ago_list.each do |days| + create_hearings_non_priority_docket(days.days.ago, find_judge("IneligJudge")) + end + @hearings_cremin_np_days_ago_list.each do |days| + create_hearings_non_priority_docket(days.days.ago, find_judge("BVADCremin")) + end + end + + def create_hearings_priority_dockets + @hearings_ineligible_judge_prior_days_ago_list.each do |days| + create_hearings_priority_docket(days.days.ago, find_judge("IneligJudge")) + end + @hearings_cremin_prior_days_ago_list.each do |days| + create_hearings_priority_docket(days.days.ago, find_judge("BVADCremin")) + end + end + + def create_cavc_legacy_non_priority_dockets + @cavc_legacy_np_keeling_days_ago_list.each do |days| + create_cavc_legacy_non_priority_dockets(days.days.ago, find_judge("BVAKKEELING")) + end + end + + def create_cavc_legacy_priority_dockets + @cavc_legacy_prior_keeling_days_ago_list.each do |days| + create_cavc_legacy_priority_dockets(days.days.ago, find_judge("BVAKKEELING")) + end + end + + def create_cavc_hearing_non_priority_dockets + @cavc_ama_hearing_np_keeling_days_ago_list.each do |days| + create_cavc_hearing_non_priority_docket(days.days.ago, find_judge("BVAKKEELING")) + end + end + + def create_cavc_hearing_priority_dockets + @cavc_ama_hearing_prior_keeling_days_ago_list.each do |days| + create_cavc_hearing_priority_docket(days.days.ago, find_judge("BVAKKEELING")) + end + end + + def create_legacy_non_priority_dockets + @legacy_np_ineligible_judge_days_ago_list.each do |days| + create_legacy_non_priority_docket(days.days.ago, find_judge("IneligJudge")) + end + @legacy_np_BVACOTBJudge_days_ago_list.each do |days| + create_legacy_non_priority_docket(days.days.ago, find_judge("BVACOTBJudge")) + end + end + + def create_legacy_priority_dockets + @legacy_prior_ineligible_judge_days_ago_list.each do |days| + create_legacy_priority_docket(days.days.ago, find_judge("IneligJudge")) + end + @legacy_prior_BVACOTBJudge_days_ago_list.each do |days| + create_legacy_priority_docket(days.days.ago, find_judge("BVACOTBJudge")) + end + end + + # Functions of Judge and Veteran + def find_or_create_judge(css_id, full_name) + User.find_by_css_id(css_id) || + create(:user, :judge, :with_vacols_judge_record, css_id: css_id, full_name: full_name) + end + + def find_or_create_inactive_judge(css_id, full_name) + User.find_by_css_id(css_id) || + create(:user, :judge, :with_inactive_vacols_judge_record, css_id: css_id, full_name: full_name) + end + + def find_judge(css_id) + User.find_by_css_id(css_id) + end + + def find_or_create_attorney(css_id, full_name) + User.find_by_css_id(css_id) || + create(:user, :with_vacols_attorney_record, css_id: css_id, full_name: full_name) + end + + def find_attorney(css_id) + User.find_by_css_id(css_id) + end + + def find_veteran(file_number) + Veteran.find_by(file_number: format("%09d", n: file_number + 1)) + end + + def create_veteran(options = {}) + params = { + file_number: format("%09d", n: options[:file_number]), + participant_id: format("%09d", n: options[:participant_id]) + } + + Veteran.find_by_participant_id(params[:participant_id]) || create(:veteran, params.merge(options)) + end + + # Initialization functions + def initialize_ev_sub_np_dockets_file_number_and_participant_id + @evidence_submission_np_file_number ||= 700_150_000 + @evidence_submission_np_participant_id ||= 700_170_000 + + while find_veteran(@evidence_submission_np_file_number) + @evidence_submission_np_file_number += 2000 + @evidence_submission_np_participant_id += 2000 + end + end + + def initialize_ev_sub_prior_dockets_file_number_and_participant_id + @evidence_submission_prior_file_number ||= 700_250_000 + @evidence_submission_prior_participant_id ||= 700_270_000 + + while find_veteran(@evidence_submission_prior_file_number) + @evidence_submission_prior_file_number += 2000 + @evidence_submission_prior_participant_id += 2000 + end + end + + def initialize_dr_np_dockets_file_number_and_participant_id + @direct_review_np_file_number ||= 700_350_000 + @direct_review_np_participant_id ||= 700_370_000 + + while find_veteran(@direct_review_np_file_number) + @direct_review_np_file_number += 2000 + @direct_review_np_participant_id += 2000 + end + end + + def initialize_dr_prior_dockets_file_number_and_participant_id + @direct_review_prior_file_number ||= 700_450_000 + @direct_review_prior_participant_id ||= 700_470_000 + + while find_veteran(@direct_review_prior_file_number) + @direct_review_prior_file_number += 2000 + @direct_review_prior_participant_id += 2000 + end + end + + def initialize_hearings_np_dockets_file_number_and_participant_id + @hearings_np_file_number ||= 700_550_000 + @hearings_np_participant_id ||= 700_570_000 + + while find_veteran(@hearings_np_file_number) + @hearings_np_file_number += 2000 + @hearings_np_participant_id += 2000 + end + end + + def initialize_hearings_prior_dockets_file_number_and_participant_id + @hearings_prior_file_number ||= 700_650_000 + @hearings_prior_participant_id ||= 700_670_000 + + while find_veteran(@hearings_prior_file_number) + @hearings_prior_file_number += 2000 + @hearings_prior_participant_id += 2000 + end + end + + def initialize_cavc_legacy_np_dockets_file_number_and_participant_id + @cavc_legacy_np_file_number ||= 700_650_000 + @cavc_legacy_np_participant_id ||= 700_670_000 + + while find_veteran(@cavc_legacy_np_file_number) + @cavc_legacy_np_file_number += 2000 + @cavc_legacy_np_participant_id += 2000 + end + end + + def initialize_cavc_legacy_prior_dockets_file_number_and_participant_id + @cavc_legacy_prior_file_number ||= 700_650_000 + @cavc_legacy_prior_participant_id ||= 700_670_000 + + while find_veteran(@cavc_legacy_prior_file_number) + @cavc_legacy_prior_file_number += 2000 + @cavc_legacy_prior_participant_id += 2000 + end + end + + def initialize_cavc_hearing_np_dockets_file_number_and_participant_id + @cavc_hearing_np_file_number ||= 700_650_000 + @cavc_hearing_np_participant_id ||= 700_670_000 + + while find_veteran(@cavc_hearing_np_file_number) + @cavc_hearing_np_file_number += 2000 + @cavc_hearing_np_participant_id += 2000 + end + end + + def initialize_cavc_hearing_prior_dockets_file_number_and_participant_id + @cavc_hearing_prior_file_number ||= 700_650_000 + @cavc_hearing_prior_participant_id ||= 700_670_000 + + while find_veteran(@cavc_hearing_prior_file_number) + @cavc_hearing_prior_file_number += 2000 + @cavc_hearing_prior_participant_id += 2000 + end + end + + def initialize_legacy_np_dockets_file_number_and_participant_id + @legacy_np_file_number ||= 700_650_000 + @legacy_np_participant_id ||= 700_670_000 + + while find_veteran(@legacy_np_file_number) + @legacy_np_file_number += 2000 + @legacy_np_participant_id += 2000 + end + end + + def initialize_legacy_prior_dockets_file_number_and_participant_id + @legacy_prior_file_number ||= 700_650_000 + @legacy_prior_participant_id ||= 700_670_000 + + while find_veteran(@legacy_prior_file_number) + @legacy_prior_file_number += 2000 + @legacy_prior_participant_id += 2000 + end + end + + # Docket Creation Functions + + # Direct Evidence Submission Creation Functions + def create_evidence_submission_non_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :evidence_submission_docket, + :ready_for_distribution, + veteran: create_veteran_for_evidence_submission_non_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_evidence_submission_non_priority + @evidence_submission_np_file_number += 1 + @evidence_submission_np_participant_id += 1 + create_veteran( + file_number: @evidence_submission_np_file_number, + participant_id: @evidence_submission_np_participant_id + ) + end + + def create_evidence_submission_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :evidence_submission_docket, + :ready_for_distribution, + :advanced_on_docket_due_to_age, + veteran: create_veteran_for_evidence_submission_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_evidence_submission_priority + @evidence_submission_prior_file_number += 1 + @evidence_submission_prior_participant_id += 1 + create_veteran( + file_number: @evidence_submission_prior_file_number, + participant_id: @evidence_submission_prior_participant_id + ) + end + + # Direct Review Docket Creation Functions + def create_direct_review_non_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :direct_review_docket, + :ready_for_distribution, + veteran: create_veteran_for_direct_review_non_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_direct_review_non_priority + @direct_review_np_file_number += 1 + @direct_review_np_participant_id += 1 + create_veteran( + file_number: @direct_review_np_file_number, + participant_id: @direct_review_np_participant_id + ) + end + + def create_direct_review_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :direct_review_docket, + :ready_for_distribution, + :advanced_on_docket_due_to_age, + veteran: create_veteran_for_direct_review_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_direct_review_priority + @direct_review_prior_file_number += 1 + @direct_review_prior_participant_id += 1 + create_veteran( + file_number: @direct_review_prior_file_number, + participant_id: @direct_review_prior_participant_id + ) + end + + # Hearing Docket Creation Functions + def create_hearings_non_priority_docket(days_ago, judge) + Timecop.travel(days_ago) + create( + :appeal, + :hearing_docket, + :with_post_intake_tasks, + :held_hearing_and_ready_to_distribute, + :tied_to_judge, + veteran: create_veteran_for_hearing_non_priority, + receipt_date: days_ago, + tied_judge: judge, + adding_user: User.first + ) + Timecop.return + end + + def create_veteran_for_hearing_non_priority + @hearings_np_file_number += 1 + @hearings_np_participant_id += 1 + create_veteran( + file_number: @hearings_np_file_number, + participant_id: @hearings_np_participant_id + ) + end + + def create_hearings_priority_docket(days_ago, judge) + Timecop.travel(days_ago) + create( + :appeal, + :hearing_docket, + :with_post_intake_tasks, + :advanced_on_docket_due_to_age, + :held_hearing_and_ready_to_distribute, + :tied_to_judge, + veteran: create_veteran_for_hearing_priority, + receipt_date: days_ago, + tied_judge: judge, + adding_user: User.first + ) + Timecop.return + end + + def create_veteran_for_hearing_priority + @hearings_prior_file_number += 1 + @hearings_prior_participant_id += 1 + create_veteran( + file_number: @hearings_prior_file_number, + participant_id: @hearings_prior_participant_id + ) + end + + # CAVC Legacy Creation Functions + def create_cavc_legacy_non_priority_docket(days_ago, judge) + Timecop.travel(days_ago + 1.day) + veteran = create_veteran_for_cavc_legacy_non_priority + + correspondent = create(:correspondent, + snamef: veteran.first_name, snamel: veteran.last_name, + ssalut: "", ssn: veteran.file_number) + + vacols_case = create_non_aod_video_vacols_case(veteran, + correspondent, + judge, + days_ago) + + cavc_legacy_non_priority_appeal = create( + :legacy_appeal, + :with_root_task, + vacols_case: vacols_case, + closest_regional_office: regional_office + ) + + create(:available_hearing_locations, regional_office, appeal: cavc_legacy_non_priority_appeal) + Timecop.return + Timecop.travel(days_ago) + remand = create(:cavc_remand, source_appeal: cavc_legacy_non_priority_appeal) + remand.remand_appeal.tasks.where(type: SendCavcRemandProcessedLetterTask.name).first.completed! + Timecop.return + end + + def create_veteran_for_cavc_legacy_non_priority + @cavc_legacy_np_file_number += 1 + @cavc_legacy_np_participant_id += 1 + create_veteran( + file_number: @cavc_legacy_np_file_number, + participant_id: @cavc_legacy_np_participant_id + ) + end + + def create_cavc_legacy_priority_docket(days_ago, judge) + veteran = create_veteran_for_cavc_legacy_priority + + correspondent = create(:correspondent, + snamef: veteran.first_name, snamel: veteran.last_name, + ssalut: "", ssn: veteran.file_number) + + vacols_case = create_non_aod_video_vacols_case(veteran, + correspondent, + judge, + days_ago) + + cavc_legacy_priority_appeal = create( + :legacy_appeal, + :with_root_task, + vacols_case: vacols_case, + closest_regional_office: regional_office + ) + + create(:available_hearing_locations, regional_office, appeal: cavc_legacy_priority_appeal) + Timecop.return + Timecop.travel(days_ago) + remand = create(:cavc_remand, source_appeal: cavc_legacy_priority_appeal) + remand.remand_appeal.tasks.where(type: SendCavcRemandProcessedLetterTask.name).first.completed! + Timecop.return + end + + def create_veteran_for_cavc_legacy_priority + @cavc_legacy_prior_file_number += 1 + @cavc_legacy_prior_participant_id += 1 + create_veteran( + file_number: @cavc_legacy_prior_file_number, + participant_id: @cavc_legacy_prior_participant_id + ) + end + + # CAVC Hearings Creation Functions + def create_cavc_hearing_non_priority_docket(days_ago, judge) + Timecop.travel(days_ago + 1.day) + cavc_hearing_non_priority_docket = create( + :appeal, + :hearing_docket, + :held_hearing, + :tied_to_judge, + :dispatched, + veteran: create_veteran_for_cavc_hearing_non_priority, + receipt_date: days_ago, + tied_judge: judge, + associated_judge: judge, + adding_user: User.first, + associated_attorney: find_attorney("CAVCATNY") + ) + Timecop.return + Timecop.travel(days_ago) + remand = create(:cavc_remand, source_appeal: cavc_hearing_non_priority_docket) + remand.remand_appeal.tasks.where(type: SendCavcRemandProcessedLetterTask.name).first.completed! + Timecop.return + end + + def create_veteran_for_cavc_hearing_non_priority + @cavc_hearing_np_file_number += 1 + @cavc_hearing_np_participant_id += 1 + create_veteran( + file_number: @cavc_hearing_np_file_number, + participant_id: @cavc_hearing_np_participant_id + ) + end + + def create_cavc_hearing_priority_docket(days_ago, judge) + cavc_hearing_priority_docket = create( + :appeal, + :hearing_docket, + :held_hearing, + :tied_to_judge, + :advanced_on_docket_due_to_age, + :dispatched, + veteran: create_veteran_for_ama_hearing_held_aod_cavc_judge, + receipt_date: receipt_date, + tied_judge: hearing_judge, + associated_judge: hearing_judge, + adding_user: User.first, + associated_attorney: find_attorney("CAVCATNY") + ) + Timecop.return + Timecop.travel(days_ago) + remand = create(:cavc_remand, source_appeal: cavc_hearing_priority_docket) + remand.remand_appeal.tasks.where(type: SendCavcRemandProcessedLetterTask.name).first.completed! + Timecop.return + end + + def create_veteran_for_cavc_hearing_priority + @cavc_hearing_prior_file_number += 1 + @cavc_hearing_prior_participant_id += 1 + create_veteran( + file_number: @cavc_hearing_prior_file_number, + participant_id: @cavc_hearing_prior_participant_id + ) + end + + # Legacy Creation Functions + def create_aod_video_vacols_case(veteran, correspondent, judge, days_ago) + create( + :case, + :aod, + :tied_to_judge, + :video_hearing_requested, + :type_original, + :ready_for_distribution, + tied_judge: judge, + correspondent: correspondent, + bfcorlid: "#{veteran.file_number}S", + case_issues: create_list(:case_issue, 3, :compensation), + bfd19: days_ago + ) + end + + def create_non_aod_video_vacols_case(veteran, correspondent, judge, days_ago) + create( + :case, + :aod, + :tied_to_judge, + :video_hearing_requested, + :type_original, + :ready_for_distribution, + tied_judge: judge, + correspondent: correspondent, + bfcorlid: "#{veteran.file_number}S", + case_issues: create_list(:case_issue, 3, :compensation), + bfd19: days_ago + ) + end + + def create_legacy_non_priority_docket(days_ago, judge) + Timecop.travel(days_ago) + veteran = create_veteran_for_legacy_non_priority + + correspondent = create(:correspondent, + snamef: veteran.first_name, snamel: veteran.last_name, + ssalut: "", ssn: veteran.file_number) + + vacols_case = create_non_aod_video_vacols_case(veteran, + correspondent, + judge, + days_ago) + + legacy_appeal = create( + :legacy_appeal, + :with_root_task, + vacols_case: vacols_case, + closest_regional_office: regional_office + ) + + create(:available_hearing_locations, regional_office, appeal: legacy_appeal) + Timecop.return + end + + def create_veteran_for_legacy_non_priority + @legacy_np_file_number += 1 + @legacy_np_participant_id += 1 + create_veteran( + file_number: @legacy_np_file_number, + participant_id: @legacy_np_participant_id + ) + end + + def create_legacy_priority_docket(days_ago, judge) + Timecop.travel(days_ago) + veteran = create_veteran_for_legacy_priority + + correspondent = create(:correspondent, + snamef: veteran.first_name, snamel: veteran.last_name, + ssalut: "", ssn: veteran.file_number) + + vacols_case = create_aod_video_vacols_case(veteran, + correspondent, + judge, + days_ago) + + legacy_appeal = create( + :legacy_appeal, + :with_root_task, + vacols_case: vacols_case, + closest_regional_office: regional_office + ) + + create(:available_hearing_locations, regional_office, appeal: legacy_appeal) + Timecop.return + end + + def create_veteran_for_legacy_priority + @legacy_prior_file_number += 1 + @legacy_prior_participant_id += 1 + create_veteran( + file_number: @legacy_prior_file_number, + participant_id: @legacy_prior_participant_id + ) + end + end +end From 5e5f8a5374aab51b7a7ebf11bd15433383c00bef Mon Sep 17 00:00:00 2001 From: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Date: Fri, 3 May 2024 10:43:34 -0400 Subject: [PATCH 09/44] APPEALS-36345-updated (#21537) * Refactored exclusion table * Fixed ordering issue and testing --- .../components/ExcludeDocketLever.jsx | 27 +++ .../components/ExclusionTable.jsx | 217 +++++------------ client/constants/ACD_LEVERS.json | 2 + .../components/ExcludeDocketLever.test.js | 73 ++++++ .../components/ExclusionTable.test.js | 62 +++++ .../test/data/adminCaseDistributionLevers.js | 219 ++++++++++++++++++ db/seeds/case_distribution_levers.rb | 140 ++++++++++- 7 files changed, 576 insertions(+), 164 deletions(-) create mode 100644 client/app/caseDistribution/components/ExcludeDocketLever.jsx create mode 100644 client/test/app/caseDistribution/components/ExcludeDocketLever.test.js create mode 100644 client/test/app/caseDistribution/components/ExclusionTable.test.js diff --git a/client/app/caseDistribution/components/ExcludeDocketLever.jsx b/client/app/caseDistribution/components/ExcludeDocketLever.jsx new file mode 100644 index 00000000000..f973495f09b --- /dev/null +++ b/client/app/caseDistribution/components/ExcludeDocketLever.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { useDispatch } from 'react-redux'; +import RadioField from 'app/components/RadioField'; +import { updateLeverValue } from '../reducers/levers/leversActions'; + +const ExcludeDocketLever = (levers) => { + const dispatch = useDispatch(); + let selectedLever = levers.lever; + const onChangeSelected = (selected) => (event) => { + dispatch(updateLeverValue(selected.leverGroup, selected.item, event)); + }; + + return ( + + + + ); +}; + +export default ExcludeDocketLever; diff --git a/client/app/caseDistribution/components/ExclusionTable.jsx b/client/app/caseDistribution/components/ExclusionTable.jsx index 6eed5b6a1c7..1b3237f2ded 100644 --- a/client/app/caseDistribution/components/ExclusionTable.jsx +++ b/client/app/caseDistribution/components/ExclusionTable.jsx @@ -1,30 +1,60 @@ +/* eslint-disable camelcase */ import React from 'react'; import { useSelector } from 'react-redux'; import ToggleSwitch from 'app/components/ToggleSwitch/ToggleSwitch'; -import RadioField from 'app/components/RadioField'; import cx from 'classnames'; import COPY from '../../../COPY'; import DISTRIBUTION from '../../../constants/DISTRIBUTION'; import { getUserIsAcdAdmin } from '../reducers/levers/leversSelector'; +import ACD_LEVERS from '../../../constants/ACD_LEVERS'; +import ExcludeDocketLever from './ExcludeDocketLever'; const ExclusionTable = () => { const theState = useSelector((state) => state); const isUserAcdAdmin = getUserIsAcdAdmin(theState); - // Placeholder options until future implementation - let options = [ - { displayText: 'On', - value: '1', - disabled: true - }, - { displayText: 'Off', - value: '2', - disabled: true + let docketLevers = theState.caseDistributionLevers?.levers?.docket_levers ?? []; + + docketLevers = docketLevers.sort((leverA, leverB) => leverA.lever_group_order - leverB.lever_group_order); + + const priorityLevers = docketLevers.filter((lever) => lever.control_group === ACD_LEVERS.priority); + const nonPriorityLevers = docketLevers.filter((lever) => lever.control_group === ACD_LEVERS.non_priority); + + const priorityRadios = priorityLevers.map((lever) => ({ + displayText: lever.title, + item: lever.item, + value: lever.value, + disabled: lever.is_disabled_in_ui, + options: lever.options, + leverGroup: lever.lever_group, + })); + + const nonPriorityRadios = nonPriorityLevers.map((lever) => ({ + displayText: lever.title, + item: lever.item, + value: lever.value, + disabled: lever.is_disabled_in_ui, + options: lever.options, + leverGroup: lever.lever_group, + })); + + const filterOptionValue = (lever) => { + let enabled = lever?.value; + + if (enabled) { + return COPY.CASE_DISTRIBUTION_EXCLUSION_TABLE_ON; } - ]; - const generateUniqueId = (leverItem, optionValue, index) => `${leverItem}-${optionValue}-${index}`; + return COPY.CASE_DISTRIBUTION_EXCLUSION_TABLE_OFF; + }; + + const buildAriaLabel = (lever, isPriority) => { + let priority = isPriority ? 'Priority' : 'Non Priority'; + let enabled = lever.value ? 'On' : 'Off'; + + return `${priority } ${ lever.title } ${ enabled}`; + }; return (
@@ -68,58 +98,12 @@ const ExclusionTable = () => { /> - - - - generateUniqueId(DISTRIBUTION.all_non_priority, option.value, index)} - /> - - - - - - generateUniqueId(DISTRIBUTION.all_non_priority, option.value, index)} - /> - - - - - - generateUniqueId(DISTRIBUTION.all_non_priority, option.value, index)} - /> - - - - - - generateUniqueId(DISTRIBUTION.all_non_priority, option.value, index)} - /> - - + + {nonPriorityRadios && nonPriorityRadios.map((lever) => ( + + + + ))} { /> - - - - generateUniqueId(DISTRIBUTION.all_priority, option.value, index)} - /> - - - - - - generateUniqueId(DISTRIBUTION.all_priority, option.value, index)} - /> - - - - - - generateUniqueId(DISTRIBUTION.all_priority, option.value, index)} - /> - - - - - - generateUniqueId(DISTRIBUTION.all_priority, option.value, index)} - /> - - + {priorityRadios && priorityRadios.map((lever) => ( + + + + ))} : @@ -196,26 +134,11 @@ const ExclusionTable = () => {

{COPY.CASE_DISTRIBUTION_EXCLUSION_TABLE_NON_PRIORITY}

- - - - - - - - - - - - + {nonPriorityRadios && nonPriorityRadios.map((lever) => ( + + {filterOptionValue(lever)} + + ))} @@ -223,25 +146,11 @@ const ExclusionTable = () => {

{COPY.CASE_DISTRIBUTION_EXCLUSION_TABLE_PRIORITY}

- - - - - - - - - - - - + {priorityRadios && priorityRadios.map((lever) => ( + + {filterOptionValue(lever)} + + ))} } diff --git a/client/constants/ACD_LEVERS.json b/client/constants/ACD_LEVERS.json index ca73b8769bc..92eb0942637 100644 --- a/client/constants/ACD_LEVERS.json +++ b/client/constants/ACD_LEVERS.json @@ -4,6 +4,8 @@ "value": "value", "days": "days", "cases": "cases", + "priority": "priority", + "non_priority": "non-priority", "DUPLICATE": "DUPLICATE", "ERROR": "ERROR", "data_types": { diff --git a/client/test/app/caseDistribution/components/ExcludeDocketLever.test.js b/client/test/app/caseDistribution/components/ExcludeDocketLever.test.js new file mode 100644 index 00000000000..2d098874be0 --- /dev/null +++ b/client/test/app/caseDistribution/components/ExcludeDocketLever.test.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import ExcludeDocketLever from 'app/caseDistribution/components/ExcludeDocketLever'; +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware } from 'redux'; +import rootReducer from 'app/caseDistribution/reducers/root'; +import thunk from 'redux-thunk'; +import { mockDocketLevers } from '../../../data/adminCaseDistributionLevers'; +import { loadLevers, setUserIsAcdAdmin } from 'app/caseDistribution/reducers/levers/leversActions'; + +describe('Exclusion Lever', () => { + + const getStore = () => createStore( + rootReducer, + applyMiddleware(thunk)); + + afterEach(() => { + jest.clearAllMocks(); + }); + let lever = mockDocketLevers[0]; + + let selectedLever = { + displayText: lever.title, + item: lever.item, + value: lever.value, + disabled: lever.is_disabled_in_ui, + options: lever.options, + leverGroup: lever.lever_group, + }; + + let leversWithTestingDocketLevers = { docket_levers: mockDocketLevers }; + + it('Exclusion Lever Renders', () => { + const store = getStore(); + + store.dispatch(loadLevers(leversWithTestingDocketLevers)); + store.dispatch(setUserIsAcdAdmin(false)); + + const wrapper = mount(( + + + )); + + let input = (wrapper.find('input').at(1)); + + // All 8 levers are rendered + expect(wrapper).toBeDefined(); + expect(input.instance().value).toBe('false'); + }); + + it('Exclusion Lever Change Value', () => { + const store = getStore(); + + store.dispatch(loadLevers(leversWithTestingDocketLevers)); + store.dispatch(setUserIsAcdAdmin(false)); + + const wrapper = mount(( + + + )); + + let input = (wrapper.find('input').at(0)); + + input.simulate('change', { lever, event: { value: true } }); + + expect(input.instance().value).toBeTruthy(); + }); + +}); diff --git a/client/test/app/caseDistribution/components/ExclusionTable.test.js b/client/test/app/caseDistribution/components/ExclusionTable.test.js new file mode 100644 index 00000000000..7d7b528bcdc --- /dev/null +++ b/client/test/app/caseDistribution/components/ExclusionTable.test.js @@ -0,0 +1,62 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { mount } from 'enzyme'; +import RadioField from 'app/components/RadioField'; +import ExclusionTable from 'app/caseDistribution/components/ExclusionTable'; +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware } from 'redux'; +import rootReducer from 'app/caseDistribution/reducers/root'; +import thunk from 'redux-thunk'; +import { mockDocketLevers } from '../../../data/adminCaseDistributionLevers'; +import { loadLevers, setUserIsAcdAdmin } from 'app/caseDistribution/reducers/levers/leversActions'; + +describe('Exclusion Table', () => { + + const getStore = () => createStore( + rootReducer, + applyMiddleware(thunk)); + + afterEach(() => { + jest.clearAllMocks(); + }); + + let leversWithTestingDocketLevers = { docket_levers: mockDocketLevers }; + let leverPriority = mockDocketLevers[0]; + let leverNonPriority = mockDocketLevers[4]; + + it('Exclusion Table Renders all 8 Levers as Admin', () => { + const store = getStore(); + + store.dispatch(loadLevers(leversWithTestingDocketLevers)); + store.dispatch(setUserIsAcdAdmin(true)); + + const wrapper = mount(( + + + )); + + expect(wrapper.find(RadioField)).toHaveLength(4); + }); + + it('Exclusion Table Renders all 8 Levers for Member View', () => { + const store = getStore(); + const expectedPriorityValues = leverPriority.value ? 'ON' : 'OFF'; + const expectedNonpriorityValues = leverNonPriority.value ? 'ON' : 'OFF'; + + store.dispatch(loadLevers(leversWithTestingDocketLevers)); + store.dispatch(setUserIsAcdAdmin(false)); + + render( + + + + ); + + const leverStatusPriority = screen.queryAllByText(expectedPriorityValues); + const leverStatusNonPriority = screen.queryAllByText(expectedNonpriorityValues); + + expect(leverStatusPriority).toHaveLength(4); + expect(leverStatusNonPriority).toHaveLength(4); + }); + +}); diff --git a/client/test/data/adminCaseDistributionLevers.js b/client/test/data/adminCaseDistributionLevers.js index 0dfd473a718..31e5d5770e4 100644 --- a/client/test/data/adminCaseDistributionLevers.js +++ b/client/test/data/adminCaseDistributionLevers.js @@ -885,3 +885,222 @@ export const mockTextLeverReturn = [ lever_group_order: 2003 } ]; + +export const mockDocketLevers = [ + { + item: 'disable_ama_priority_legacy', + title: 'Test Docket Lever Title 1', + description: '', + data_type: 'boolean', + value: false, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 100, + control_group: 'priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_priority_legacy', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_priority_legacy', + value: 'false', + disabled: false + } + ] + }, + { + item: 'disable_ama_priority_direct_review', + title: 'Test Docket Lever Title 2', + description: '', + data_type: 'boolean', + value: false, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 101, + control_group: 'priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_priority_direct_review', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_priority_direct_review', + value: 'false', + disabled: false + } + ] + }, + { + item: 'disable_ama_priority_hearing', + title: 'Test Docket Lever Title 3', + description: '', + data_type: 'boolean', + value: false, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 102, + control_group: 'priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_priority_hearing', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_priority_hearing', + value: 'false', + disabled: false + } + ] + }, + { + item: 'disable_ama_priority_evidence_submission', + title: 'Test Docket Lever Title 4', + description: '', + data_type: 'boolean', + value: false, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 103, + control_group: 'priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_priority_evidence_submission', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_priority_evidence_submission', + value: 'false', + disabled: false + } + ] + }, + { + item: 'disable_ama_non_priority_legacy', + title: 'Test Docket Lever Title 5', + description: '', + data_type: 'boolean', + value: false, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 104, + control_group: 'non_priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_non_priority_legacy', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_non_priority_legacy', + value: 'false', + disabled: false + } + ] + }, + { + item: 'disable_ama_non_priority_direct_review', + title: 'Test Docket Lever Title 6', + description: '', + data_type: 'boolean', + value: true, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 105, + control_group: 'non_priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_non_priority_direct_review', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_non_priority_direct_review', + value: 'false', + disabled: false + } + ] + }, + { + item: 'disable_ama_non_priority_hearing', + title: 'Test Docket Lever Title 7', + description: '', + data_type: 'boolean', + value: false, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 106, + control_group: 'non_priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_non_priority_hearing', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_non_priority_hearing', + value: 'false', + disabled: false + } + ] + }, + { + item: 'disable_ama_non_priority_evidence_submission', + title: 'Test Docket Lever Title 8', + description: '', + data_type: 'boolean', + value: false, + unit: '', + is_disabled_in_ui: false, + algorithms_used: ['proportion', 'docket'], + lever_group: 'docket_levers', + lever_group_order: 107, + control_group: 'non_priority', + options: [ + { + displayText: 'On', + name: 'disable_ama_non_priority_evidence_submission', + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: 'disable_ama_non_priority_evidence_submission', + value: 'false', + disabled: false + } + ] + }, +]; diff --git a/db/seeds/case_distribution_levers.rb b/db/seeds/case_distribution_levers.rb index 9bf529193cc..71b665a72ee 100644 --- a/db/seeds/case_distribution_levers.rb +++ b/db/seeds/case_distribution_levers.rb @@ -576,10 +576,25 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 10 + lever_group_order: 10, + control_group: Constants.ACD_LEVERS.priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_legacy_priority, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_legacy_priority, + value: 'false', + disabled: false + } + ] }, { item: Constants.DISTRIBUTION.disable_legacy_non_priority, @@ -588,10 +603,25 @@ def levers data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, unit: "", - is_disabled_in_ui: true, + is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 101 + lever_group_order: 101, + control_group: Constants.ACD_LEVERS.non_priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_legacy_non_priority, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_legacy_non_priority, + value: 'false', + disabled: false + } + ] }, { item: Constants.DISTRIBUTION.disable_ama_non_priority_hearing, @@ -603,7 +633,22 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 102 + lever_group_order: 102, + control_group: Constants.ACD_LEVERS.non_priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_ama_non_priority_hearing, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_ama_non_priority_hearing, + value: 'false', + disabled: false + } + ] }, { item: Constants.DISTRIBUTION.disable_ama_non_priority_direct_review, @@ -615,7 +660,22 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 103 + lever_group_order: 103, + control_group: Constants.ACD_LEVERS.non_priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_ama_non_priority_direct_review, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_ama_non_priority_direct_review, + value: 'false', + disabled: false + } + ] }, { item: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_sub, @@ -627,7 +687,22 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 104 + lever_group_order: 104, + control_group: Constants.ACD_LEVERS.non_priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_sub, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_sub, + value: 'false', + disabled: false + } + ] }, { item: Constants.DISTRIBUTION.disable_ama_priority_hearing, @@ -639,7 +714,22 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 105 + lever_group_order: 105, + control_group: Constants.ACD_LEVERS.priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_ama_priority_hearing, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_ama_priority_hearing, + value: 'false', + disabled: false + } + ] }, { item: Constants.DISTRIBUTION.disable_ama_priority_direct_review, @@ -651,7 +741,22 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 106 + lever_group_order: 106, + control_group: Constants.ACD_LEVERS.priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_ama_priority_direct_review, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_ama_priority_direct_review, + value: 'false', + disabled: false + } + ] }, { item: Constants.DISTRIBUTION.disable_ama_priority_evidence_sub, @@ -663,7 +768,22 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 107 + lever_group_order: 107, + control_group: Constants.ACD_LEVERS.priority, + options: [ + { + displayText: 'On', + name: Constants.DISTRIBUTION.disable_ama_priority_evidence_sub, + value: 'true', + disabled: false + }, + { + displayText: 'Off', + name: Constants.DISTRIBUTION.disable_ama_priority_evidence_sub, + value: 'false', + disabled: false + } + ] }, ] end From 4988bd69891d55a3df3d46ab879609dab7c3e7aa Mon Sep 17 00:00:00 2001 From: Michael Beard <131783726+mbeardy@users.noreply.github.com> Date: Sun, 5 May 2024 15:58:07 -0500 Subject: [PATCH 10/44] mbeard/APPEALS-44379 (#21530) * init commit * new questions about method * method for ready_appeals_from_levers 3rd iteration * switched to conditional * updates test and constants file * adds testing scenarios for edge cases * fixes test * adds constants to top of file * first new commit with new args * updates methods and test query * updates age_of_oldest methods params * cleans up method removes args and updates tests, fixes linting --------- Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- app/models/docket.rb | 32 ++++++++++++++++---------- spec/models/docket_spec.rb | 47 +++++++++++++++++++++++--------------- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/app/models/docket.rb b/app/models/docket.rb index eef25f03bcd..1de8008bc8c 100644 --- a/app/models/docket.rb +++ b/app/models/docket.rb @@ -37,16 +37,15 @@ def build_lever_item(docket_type, priority_status) "disable_ama_#{priority_status}_#{docket_type.downcase}" end - def ready_priority_nonpriority_appeals(priority = false) + def ready_priority_nonpriority_appeals(priority: false, ready: true, judge: nil, genpop: nil) priority_status = priority ? PRIORITY : NON_PRIORITY lever_item = build_lever_item(docket_type, priority_status) lever = CaseDistributionLever.find_by_item(Constants::DISTRIBUTION[lever_item]) lever_value = lever&.value - if lever_value == "true" - [] - else - self - end + + return [] if lever_value == "true" + + appeals(priority: priority, ready: ready, genpop: genpop, judge: judge) end def count(priority: nil, ready: nil) @@ -75,25 +74,29 @@ def weight end def age_of_n_oldest_genpop_priority_appeals(num) - appeals(priority: true, ready: true).limit(num).map(&:ready_for_distribution_at) + ready_priority_nonpriority_appeals( + priority: true, + ready: true, + genpop: true + ).limit(num).map(&:ready_for_distribution_at) end def age_of_n_oldest_priority_appeals_available_to_judge(_judge, num) - appeals(priority: true, ready: true).limit(num).map(&:receipt_date) + ready_priority_nonpriority_appeals(priority: true, ready: true).limit(num).map(&:receipt_date) end # this method needs to have the same name as the method in legacy_docket.rb for by_docket_date_distribution, # but the judge that is passed in isn't relevant here def age_of_n_oldest_nonpriority_appeals_available_to_judge(_judge, num) - appeals(priority: false, ready: true).limit(num).map(&:receipt_date) + ready_priority_nonpriority_appeals(priority: true, ready: true).limit(num).map(&:receipt_date) end def age_of_oldest_priority_appeal @age_of_oldest_priority_appeal ||= if use_by_docket_date? - appeals(priority: true, ready: true).limit(1).first&.receipt_date + ready_priority_nonpriority_appeals(priority: true, ready: true).limit(1).first&.receipt_date else - appeals(priority: true, ready: true).limit(1).first&.ready_for_distribution_at + ready_priority_nonpriority_appeals(priority: true, ready: true).limit(1).first&.ready_for_distribution_at end end @@ -114,7 +117,12 @@ def distribute_appeals(distribution, priority: false, genpop: nil, limit: 1, sty query_args = { priority: priority, ready: true, genpop: genpop, judge: distribution.judge } appeals, sct_appeals = create_sct_appeals(query_args, limit) else - appeals = appeals(priority: priority, ready: true, genpop: genpop, judge: distribution.judge).limit(limit) + appeals = ready_priority_nonpriority_appeals( + priority: priority, + ready: true, + genpop: genpop, + judge: distribution.judge + ).limit(limit) sct_appeals = [] end diff --git a/spec/models/docket_spec.rb b/spec/models/docket_spec.rb index 80a9be5ef46..cddb990d45f 100644 --- a/spec/models/docket_spec.rb +++ b/spec/models/docket_spec.rb @@ -286,48 +286,59 @@ context "ready_priority_nonpriority_appeals" do let(:docket) { DirectReviewDocket.new } + let(:judge) { create(:user, :judge, :with_judge_team) } + + it "returns appeals when the corresponding CaseDistributionLever value is false" do + CaseDistributionLever.where(item: "disable_ama_non_priority_direct_review").update(value: false) + result = docket.ready_priority_nonpriority_appeals(priority: false) + expected_appeals = docket.appeals(priority: false, ready: true) + expect(result.map(&:id)).to eq(expected_appeals.map(&:id)) + end it "returns docket when the corresponding CaseDistributionLever value is false" do CaseDistributionLever.where(item: "disable_ama_non_priority_direct_review").update(value: false) - result = docket.ready_priority_nonpriority_appeals(false) - expect(result).to eq(docket) + result = docket.ready_priority_nonpriority_appeals(priority: false, ready: true, genpop: true, judge: judge) + expected_attributes = docket.appeals( + priority: false, + ready: true, + genpop: true, + judge: judge + ).map(&:attributes) + result_attributes = result.map(&:attributes) + expect(result_attributes).to eq(expected_attributes) end it "returns an empty array when the corresponding CaseDistributionLever value is true" do CaseDistributionLever.where(item: "disable_ama_non_priority_direct_review").update(value: "true") lever = CaseDistributionLever.find_by_item("disable_ama_non_priority_direct_review") expect(lever.value).to eq("true") - result = docket.ready_priority_nonpriority_appeals(false) + result = docket.ready_priority_nonpriority_appeals(priority: false) expect(result).to eq([]) end - it "returns the docket itself when the corresponding CaseDistributionLever record is not found" do + it "returns an empty list when the corresponding CaseDistributionLever record is not found" do allow(CaseDistributionLever).to receive(:find_by_item).and_return(nil) - expect(docket.ready_priority_nonpriority_appeals(false)).to eq(docket) - end - - it "returns the docket itself when the lever value is any non-'true' value" do - non_true_values = %w[false null undefined enabled] - non_true_values.each do |value| - allow_any_instance_of(CaseDistributionLever).to receive(:value).and_return(value) - expect(docket.ready_priority_nonpriority_appeals(false)).to eq(docket) - end + result = docket.ready_priority_nonpriority_appeals(priority: false) + expected_result = docket.appeals(priority: false, ready: true) + expect(result.map(&:id)).to eq(expected_result.map(&:id)) end it "returns an empty array when the lever value is true and priority is true" do allow(CaseDistributionLever).to receive(:find_by_item).and_return(double(value: "true")) - expect(docket.ready_priority_nonpriority_appeals(true)).to eq([]) + expect(docket.ready_priority_nonpriority_appeals(ready: true)).to eq([]) end - it "returns the docket itself when the lever value is false and priority is true" do + it "returns the correct appeals when the lever value is false and priority is true" do allow(CaseDistributionLever).to receive(:find_by_item).and_return(double(value: "false")) - expect(docket.ready_priority_nonpriority_appeals(true)).to eq(docket) + expected_appeals = docket.appeals(priority: true) + result = docket.ready_priority_nonpriority_appeals(priority: true, ready: true) + expect(result).to match_array(expected_appeals) end it "correctly builds the lever item based on docket type" do - expect(docket).to receive(:docket_type).and_return("direct_review") + expect(docket).to receive(:docket_type).exactly(2).times.and_return("direct_review") expect(docket).to receive(:build_lever_item).with("direct_review", "non_priority").and_call_original - docket.ready_priority_nonpriority_appeals(false) + docket.ready_priority_nonpriority_appeals(priority: false) end end From 6988058b81f961801f36049209b402269c8a540d Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Sun, 5 May 2024 17:01:39 -0400 Subject: [PATCH 11/44] APPEALS-44346 Demo Docket Goal Lever Test Seed Data (#21524) Co-authored-by: Christopher Detlef <> --- .../demo_ama_docket_goals_lever_test_data.rb | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 db/seeds/demo_ama_docket_goals_lever_test_data.rb diff --git a/db/seeds/demo_ama_docket_goals_lever_test_data.rb b/db/seeds/demo_ama_docket_goals_lever_test_data.rb new file mode 100644 index 00000000000..76722f9047e --- /dev/null +++ b/db/seeds/demo_ama_docket_goals_lever_test_data.rb @@ -0,0 +1,332 @@ +# frozen_string_literal: true + +module Seeds + class DemoAmaDocketGoalsLeverTestData < Base + def initialize + initialize_ev_sub_np_dockets_file_number_and_participant_id + initialize_ev_sub_prior_dockets_file_number_and_participant_id + initialize_dr_np_dockets_file_number_and_participant_id + initialize_dr_prior_dockets_file_number_and_participant_id + initialize_hearings_np_dockets_file_number_and_participant_id + initialize_hearings_prior_dockets_file_number_and_participant_id + end + + def seed! + RequestStore[:current_user] = User.system_user + create_judges + create_dockets + end + + private + def create_judges + find_or_create_judge("BVADCremin", "Daija K Cremin") + find_or_create_inactive_judge("IneligJudge", "Ineligible JudgeAA") + end + + def create_dockets + create_evidence_submission_non_priority_dockets + create_evidence_submission_priority_dockets + create_direct_review_non_priority_dockets + create_direct_review_priority_dockets + create_hearings_non_priority_dockets + create_hearings_priority_dockets + end + + def create_evidence_submission_non_priority_dockets + evidence_sub_np_reciept_days_ago_list = [ + 700, 699, 697, 613, 612, 611, 475, 474, 473, 437, 436, 435, 110, 109, 108, 93, 92, 91, 90, 51, 50 + ] + + evidence_sub_np_reciept_days_ago_list.each do |days| + create_evidence_submission_non_priority_docket(days.days.ago) + end + end + + def create_evidence_submission_priority_dockets + evidence_sub_priority_reciept_days_ago_list = [ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1 + ] + + evidence_sub_priority_reciept_days_ago_list.each do |days| + create_evidence_submission_priority_docket(days.days.ago) + end + end + + def create_direct_review_non_priority_dockets + direct_rev_np_reciept_days_ago_list = [ + 600, 599, 598, 435, 434, 432, 290, 289, 288, 272, 271, 270, 110, 109, 108, 53, 52, 51, 50, 21, 20 + ] + + direct_rev_np_reciept_days_ago_list.each do |days| + create_direct_review_non_priority_docket(days.days.ago) + end + end + + def create_direct_review_priority_dockets + direct_rev_np_reciept_days_ago_list = [ + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + ] + + direct_rev_np_reciept_days_ago_list.each do |days| + create_direct_review_priority_docket(days.days.ago) + end + end + + def create_hearings_non_priority_dockets + ineligible_judge_non_prior_ago_list = [ + 1000, 900, 880, 840, 830, 820, 650, 644, 630, 630, 630, 630, 550, 540, 530, 520, 510, 500, 300, 200, 100 + ] + cremin_judge_non_prior_ago_list = [ + 1000, 900, 650, 644, 530, 520, 100 + ] + + ineligible_judge_non_prior_ago_list.each do |days| + create_hearings_non_priority_docket(days.days.ago, find_judge("IneligJudge")) + end + cremin_judge_non_prior_ago_list.each do |days| + create_hearings_non_priority_docket(days.days.ago, find_judge("BVADCremin")) + end + end + + def create_hearings_priority_dockets + cremin_judge_non_prior_ago_list = [ + 100, 300, 500 + ] + + cremin_judge_non_prior_ago_list.each do |days| + create_hearings_priority_docket(days.days.ago, find_judge("BVADCremin")) + end + end + + # Functions of Judge and Veteran + def find_or_create_judge(css_id, full_name) + User.find_by_css_id(css_id) || + create(:user, :judge, :with_vacols_judge_record, css_id: css_id, full_name: full_name) + end + + def find_or_create_inactive_judge(css_id, full_name) + User.find_by_css_id(css_id) || + create(:user, :judge, :with_inactive_vacols_judge_record, css_id: css_id, full_name: full_name) + end + + def find_judge(css_id) + User.find_by_css_id(css_id) + end + + def find_veteran(file_number) + Veteran.find_by(file_number: format("%09d", n: file_number + 1)) + end + + def create_veteran(options = {}) + params = { + file_number: format("%09d", n: options[:file_number]), + participant_id: format("%09d", n: options[:participant_id]) + } + + Veteran.find_by_participant_id(params[:participant_id]) || create(:veteran, params.merge(options)) + end + + # Initialization functions + def initialize_ev_sub_np_dockets_file_number_and_participant_id + @evidence_submission_np_file_number ||= 700_100_000 + @evidence_submission_np_participant_id ||= 700_120_000 + + while find_veteran(@evidence_submission_np_file_number) + @evidence_submission_np_file_number += 2000 + @evidence_submission_np_participant_id += 2000 + end + end + + def initialize_ev_sub_prior_dockets_file_number_and_participant_id + @evidence_submission_prior_file_number ||= 700_200_000 + @evidence_submission_prior_participant_id ||= 700_220_000 + + while find_veteran(@evidence_submission_prior_file_number) + @evidence_submission_prior_file_number += 2000 + @evidence_submission_prior_participant_id += 2000 + end + end + + def initialize_dr_np_dockets_file_number_and_participant_id + @direct_review_np_file_number ||= 700_300_000 + @direct_review_np_participant_id ||= 700_320_000 + + while find_veteran(@direct_review_np_file_number) + @direct_review_np_file_number += 2000 + @direct_review_np_participant_id += 2000 + end + end + + def initialize_dr_prior_dockets_file_number_and_participant_id + @direct_review_prior_file_number ||= 700_400_000 + @direct_review_prior_participant_id ||= 700_420_000 + + while find_veteran(@direct_review_prior_file_number) + @direct_review_prior_file_number += 2000 + @direct_review_prior_participant_id += 2000 + end + end + + def initialize_hearings_np_dockets_file_number_and_participant_id + @hearings_np_file_number ||= 700_500_000 + @hearings_np_participant_id ||= 700_520_000 + + while find_veteran(@hearings_np_file_number) + @hearings_np_file_number += 2000 + @hearings_np_participant_id += 2000 + end + end + + def initialize_hearings_prior_dockets_file_number_and_participant_id + @hearings_prior_file_number ||= 700_600_000 + @hearings_prior_participant_id ||= 700_620_000 + + while find_veteran(@hearings_prior_file_number) + @hearings_prior_file_number += 2000 + @hearings_prior_participant_id += 2000 + end + end + + # Docket Creation Functions + + # Direct Evidence Submission Creation Functions + def create_evidence_submission_non_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :evidence_submission_docket, + :ready_for_distribution, + veteran: create_veteran_for_evidence_submission_non_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_evidence_submission_non_priority + @evidence_submission_np_file_number += 1 + @evidence_submission_np_participant_id += 1 + create_veteran( + file_number: @evidence_submission_np_file_number, + participant_id: @evidence_submission_np_participant_id + ) + end + + def create_evidence_submission_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :evidence_submission_docket, + :ready_for_distribution, + :advanced_on_docket_due_to_age, + veteran: create_veteran_for_evidence_submission_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_evidence_submission_priority + @evidence_submission_prior_file_number += 1 + @evidence_submission_prior_participant_id += 1 + create_veteran( + file_number: @evidence_submission_prior_file_number, + participant_id: @evidence_submission_prior_participant_id + ) + end + + # Direct Review Docket Creation Functions + def create_direct_review_non_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :direct_review_docket, + :ready_for_distribution, + veteran: create_veteran_for_direct_review_non_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_direct_review_non_priority + @direct_review_np_file_number += 1 + @direct_review_np_participant_id += 1 + create_veteran( + file_number: @direct_review_np_file_number, + participant_id: @direct_review_np_participant_id + ) + end + + def create_direct_review_priority_docket(days_ago) + Timecop.travel(days_ago) + create( + :appeal, + :direct_review_docket, + :ready_for_distribution, + :advanced_on_docket_due_to_age, + veteran: create_veteran_for_direct_review_priority, + receipt_date: days_ago + ) + Timecop.return + end + + def create_veteran_for_direct_review_priority + @direct_review_prior_file_number += 1 + @direct_review_prior_participant_id += 1 + create_veteran( + file_number: @direct_review_prior_file_number, + participant_id: @direct_review_prior_participant_id + ) + end + + # Hearing Docket Creation Functions + def create_hearings_non_priority_docket(days_ago, judge) + Timecop.travel(days_ago) + create( + :appeal, + :hearing_docket, + :with_post_intake_tasks, + :held_hearing_and_ready_to_distribute, + :tied_to_judge, + veteran: create_veteran_for_hearing_non_priority, + receipt_date: days_ago, + tied_judge: judge, + adding_user: User.first + ) + Timecop.return + end + + def create_veteran_for_hearing_non_priority + @hearings_np_file_number += 1 + @hearings_np_participant_id += 1 + create_veteran( + file_number: @hearings_np_file_number, + participant_id: @hearings_np_participant_id + ) + end + + def create_hearings_priority_docket(days_ago, judge) + Timecop.travel(days_ago) + create( + :appeal, + :hearing_docket, + :with_post_intake_tasks, + :advanced_on_docket_due_to_age, + :held_hearing_and_ready_to_distribute, + :tied_to_judge, + veteran: create_veteran_for_hearing_priority, + receipt_date: days_ago, + tied_judge: judge, + adding_user: User.first + ) + Timecop.return + end + + def create_veteran_for_hearing_priority + @hearings_prior_file_number += 1 + @hearings_prior_participant_id += 1 + create_veteran( + file_number: @hearings_prior_file_number, + participant_id: @hearings_prior_participant_id + ) + end + end +end From 4f6d28662187c54f1692cb45cb27d75990071bf2 Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Mon, 6 May 2024 11:19:37 -0400 Subject: [PATCH 12/44] Chrisbdetlef/appeals 40705 (#21535) * Chris backend Work * APPEALS-44441. Added initial changes for individual seed * WIP * APPEALS-44441. Fixed the table css * APPEALS-44441. Added Custom and Scenario Seed components and jest tests * APPEALS-44441. Fix ScenarioSeeds jest test * APPEALS-44441. Fix CustomeSeeds Jest test * init commit * fixes linting issues * capybara test init * APPEALS-44441. Backend Integration API changes * APPEALS-44441_1. Matched the constant keys in the backend * APPEALS-44441_1. Update the param to match the backend and fixed spec * APPEALS-44441. lint fixes * APPEALS-44441. linting fixes * sharsha/APPEALS-44441. fixed the route and tests * Change route * Change variables to match front and back end * CustomSeed Jest test fix * Fix issues * Fix rubocop issues * Use existing method in the controller --------- Co-authored-by: Christopher Detlef <> Co-authored-by: SHarshain Co-authored-by: Michael Beard Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- app/controllers/application_controller.rb | 33 +- .../test_docket_seeds_controller.rb | 38 ++ app/controllers/test_seeds_controller.rb | 61 +++ app/views/test/seeds.html.erb | 8 + client/COPY.json | 27 +- client/app/components/NumberField.jsx | 2 + client/app/index.js | 7 +- .../caseDistribution/_case_distribution.scss | 1 + .../styles/caseDistribution/_test_seeds.scss | 50 +++ client/app/test/TestSeeds.jsx | 170 ++++++++ .../app/testSeeds/components/CustomSeeds.jsx | 170 ++++++++ .../testSeeds/components/ScenarioSeeds.jsx | 127 ++++++ .../components/SeedsBannerDisplay.jsx | 24 ++ .../testSeeds/components/TestSeedsWrapper.jsx | 14 + client/app/testSeeds/index.jsx | 77 ++++ client/app/testSeeds/pages/TestSeedsApp.jsx | 22 + client/app/testSeeds/reducers/root.js | 13 + client/constants/CUSTOM_SEEDS.json | 6 + client/constants/TEST_SEEDS.json | 10 + .../testSeeds/components/CustomSeeds.test.js | 116 ++++++ .../components/ScenarioSeeds.test.js | 86 ++++ .../app/testSeeds/pages/TestSeedsApp.test.js | 23 ++ config/routes.rb | 7 + db/seeds/case_distribution_levers.rb | 3 - db/seeds/demo_ama_aod_hearing_data.rb | 83 ++++ db/seeds/demo_ama_non_aod_hearing_data.rb | 82 ++++ db/seeds/demo_direct_reviews_data.rb | 79 ++++ db/seeds/demo_legacy_cases_data.rb | 106 +++++ .../test_docket_seeds_controller_spec.rb | 381 ++++++++++++++++++ spec/feature/test_seeds_spec.rb | 24 ++ 30 files changed, 1819 insertions(+), 31 deletions(-) create mode 100644 app/controllers/test_docket_seeds_controller.rb create mode 100644 app/controllers/test_seeds_controller.rb create mode 100644 app/views/test/seeds.html.erb create mode 100644 client/app/styles/caseDistribution/_test_seeds.scss create mode 100644 client/app/test/TestSeeds.jsx create mode 100644 client/app/testSeeds/components/CustomSeeds.jsx create mode 100644 client/app/testSeeds/components/ScenarioSeeds.jsx create mode 100644 client/app/testSeeds/components/SeedsBannerDisplay.jsx create mode 100644 client/app/testSeeds/components/TestSeedsWrapper.jsx create mode 100644 client/app/testSeeds/index.jsx create mode 100644 client/app/testSeeds/pages/TestSeedsApp.jsx create mode 100644 client/app/testSeeds/reducers/root.js create mode 100644 client/constants/CUSTOM_SEEDS.json create mode 100644 client/constants/TEST_SEEDS.json create mode 100644 client/test/app/testSeeds/components/CustomSeeds.test.js create mode 100644 client/test/app/testSeeds/components/ScenarioSeeds.test.js create mode 100644 client/test/app/testSeeds/pages/TestSeedsApp.test.js create mode 100644 db/seeds/demo_ama_aod_hearing_data.rb create mode 100644 db/seeds/demo_ama_non_aod_hearing_data.rb create mode 100644 db/seeds/demo_direct_reviews_data.rb create mode 100644 db/seeds/demo_legacy_cases_data.rb create mode 100644 spec/controllers/test_docket_seeds_controller_spec.rb create mode 100644 spec/feature/test_seeds_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 00ac5a7adba..5a0606edd57 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -161,6 +161,14 @@ def case_distribution_url } end + def test_seeds_url + { + title: "Test Seeds", + link: "/test/seeds", + sort_order: 7 + } + end + def hearing_application_url { title: "Hearings", @@ -210,14 +218,31 @@ def manage_users_menu_item def admin_menu_items admin_urls = [] - admin_urls.concat(manage_teams_menu_items) if current_user&.administered_teams&.any? - admin_urls.push(manage_users_menu_item) if current_user&.can_view_user_management? - admin_urls.push(case_distribution_url) if current_user&.organizations&.any?(&:users_can_view_levers?) + add_team_management_items(admin_urls) + add_user_management_items(admin_urls) + add_case_distribution_item(admin_urls) + add_test_seeds_item(admin_urls) + + admin_urls.flatten + end + def add_team_management_items(admin_urls) if current_user&.can_view_team_management? || current_user&.can_view_judge_team_management? admin_urls.unshift(manage_all_teams_menu_item) end - admin_urls.flatten + end + + def add_user_management_items(admin_urls) + admin_urls.concat(manage_teams_menu_items) if current_user&.administered_teams&.any? + admin_urls.push(manage_users_menu_item) if current_user&.can_view_user_management? + end + + def add_case_distribution_item(admin_urls) + admin_urls.push(case_distribution_url) if current_user&.organizations&.any?(&:users_can_view_levers?) + end + + def add_test_seeds_item(admin_urls) + admin_urls.push(test_seeds_url) if current_user&.organizations&.any?(&:users_can_view_levers?) end def dropdown_urls diff --git a/app/controllers/test_docket_seeds_controller.rb b/app/controllers/test_docket_seeds_controller.rb new file mode 100644 index 00000000000..1e1261d9998 --- /dev/null +++ b/app/controllers/test_docket_seeds_controller.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class TestDocketSeedsController < ApplicationController + before_action :verify_access, :check_environment + + def seed_dockets + task_name = Constants.TEST_SEEDS.to_h[params[:seed_type].to_sym] + ENV["SEED_COUNT"] = params[:seed_count].to_s + ENV["DAYS_AGO"] = params[:days_ago].to_s + ENV["JUDGE_CSS_ID"] = params[:judge_css_id].to_s + + Rake::Task[task_name].reenable + Rake::Task[task_name].invoke + + ENV.delete("SEED_COUNT") + ENV.delete("DAYS_AGO") + ENV.delete("JUDGE_CSS_ID") + + head :ok + end + + private + + def verify_access + return true if current_user&.organizations && current_user.organizations.any?(&:users_can_view_levers?) + + session["return_to"] = request.original_url + redirect_to "/unauthorized" + end + + def check_environment + return true if Rails.env.development? + return true if Rails.deploy_env?(:demo) + return true if Rails.deploy_env?(:uat) + + redirect_to "/unauthorized" + end +end diff --git a/app/controllers/test_seeds_controller.rb b/app/controllers/test_seeds_controller.rb new file mode 100644 index 00000000000..94e0c93e15c --- /dev/null +++ b/app/controllers/test_seeds_controller.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require "csv" + +class TestSeedsController < ApplicationController + before_action :check_environment + before_action :verify_access, only: [:seeds] + before_action :authorize_admin, only: [:seeds] + + def seeds + # seeds + render "/test/seeds" + end + + def run_demo + seed_type = params[:seed_type].to_sym + seed_count = params[:seed_count].to_i + test_seed_list = Constants.TEST_SEEDS.to_h + task_name = test_seed_list[seed_type] + + if task_name + Rake::Task[task_name].reenable + index = 0 + seed_count.times do + index += 1 + Rails.logger.info "Rake run count #{index}" + Rake::Task[task_name].execute + end + head :ok + else + render json: { error: "Invalid seed type" }, status: :bad_request + end + end + + private + + def check_environment + return true if Rails.env.development? + return true if Rails.deploy_env?(:demo) + + redirect_to "/unauthorized" + end + + def authorize_admin + error = ["UNAUTHORIZED"] + + resp = { + status_code: 500, + message: error, + user_is_an_acd_admin: false + } + render json: resp unless CDAControlGroup.singleton.user_is_admin?(current_user) + end + + def verify_access + return true if current_user&.organizations && current_user.organizations.any?(&:users_can_view_levers?) + + session["return_to"] = request.original_url + redirect_to "/unauthorized" + end +end diff --git a/app/views/test/seeds.html.erb b/app/views/test/seeds.html.erb new file mode 100644 index 00000000000..825a0d2e618 --- /dev/null +++ b/app/views/test/seeds.html.erb @@ -0,0 +1,8 @@ +<% content_for :full_page_content do %> + <%= react_component("TestSeeds", props: { + userDisplayName: current_user.display_name, + dropdownUrls: dropdown_urls, + applicationUrls: application_urls, + feedbackUrl: feedback_url + }) %> +<% end %> diff --git a/client/COPY.json b/client/COPY.json index 26348a3e57d..9d3dcae94aa 100644 --- a/client/COPY.json +++ b/client/COPY.json @@ -1,3 +1,4 @@ + { "CASE_SEARCH_HOME_PAGE_HEADING": "Veteran Case Search", "CASE_SEARCH_INPUT_PLACEHOLDER": "Enter a file number, SSN, or AMA docket number", @@ -333,9 +334,6 @@ "EDUCATION_ORGANIZATIONAL_QUEUE_PAGE_IN_PROGRESS_TASKS_DESCRIPTION": "Cases that are in progress in the %s team's queue:", "EDUCATION_ORGANIZATIONAL_QUEUE_PAGE_UNASSIGNED_TASKS_DESCRIPTION": "Cases owned by the %s team:", "VHA_ORGANIZATIONAL_QUEUE_PAGE_IN_PROGRESS_TASKS_DESCRIPTION": "Cases that are in progress:", - "SPECIALTY_CASE_TEAM_QUEUE_PAGE_ACTION_REQUIRED_TAB_TITLE": "Action Required (%d)", - "SPECIALTY_CASE_TEAM_QUEUE_PAGE_ACTION_REQUIRED_TAB_DESCRIPTION": "Cases owned by the Specialty Case Team that require action:", - "SPECIALTY_CASE_TEAM_QUEUE_PAGE_COMPLETED_TAB_DESCRIPTION": "Cases owned by the Specialty Case Team that have been assigned to a SCT Attorney (last 14 days):", "ORGANIZATIONAL_QUEUE_PAGE_IN_PROGRESS_TASKS_DESCRIPTION": "Cases in progress in a %s team member's queue.", "ORGANIZATIONAL_QUEUE_PAGE_READY_FOR_REVIEW_TASKS_DESCRIPTION": "Cases ready for review in a %s team member's queue.", "ORGANIZATIONAL_QUEUE_PAGE_UNASSIGNED_TASKS_DESCRIPTION": "Cases owned by the %s team that are unassigned to a person.", @@ -459,14 +457,12 @@ "ASSIGN_WIDGET_NO_TASK_TITLE": "No tasks selected", "ASSIGN_WIDGET_NO_TASK_DETAIL": "Please select a task.", "ASSIGN_WIDGET_SUCCESS": "%(verb)s %(numCases)s %(casePlural)s to %(assignee)s", - "SPECIALTY_CASE_TEAM_ASSIGN_WIDGET_SUCCESS": "You have successfully assigned %(numCases)s %(casePlural)s to %(assignee)s", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_TITLE": "Error assigning tasks", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_DETAIL": "Error occurred while assigning tasks. You may need to reload the page before proceeding.", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_DETAIL_MODAL_LINK": "Please assign tasks to an attorney from your assign page.", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_DETAIL_MODAL": " Reassign tasks to a judge in the action dropdown", "ASSIGN_WIDGET_LOADING": "Loading...", "ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER": "Select a user", - "SCT_ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER": "Search or select", "VHA_ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER": "Select a Program Office", "ASSIGN_WIDGET_DROPDOWN_NAME_PRIMARY": "Assignee", "ASSIGN_WIDGET_DROPDOWN_NAME_SECONDARY": "Other assignee", @@ -534,7 +530,6 @@ "MODAL_END_HOLD_BUTTON": "End hold", "MODAL_REMOVE_BUTTON": "Remove", "MODAL_SAVE_BUTTON": "Save", - "MODAL_MOVE_BUTTON": "Move", "BULK_ASSIGN_BUTTON_TEXT": "Assign Tasks", "BULK_ASSIGN_MODAL_TITLE": "Bulk Assign Tasks", "REGIONAL_OFFICE_REQUIRED_MESSAGE": "Please select a regional office", @@ -657,7 +652,6 @@ "ASSIGN_TO_USER_DROPDOWN": "Select a user", "ASSIGN_TO_TEAM_DROPDOWN": "Select a team", "ASSIGN_TASK_TITLE": "Assign task", - "ASSIGN_TASK_BUTTON": "Assign task", "ASSIGN_TASK_TO_TITLE": "Assign task to %s", "NOTIFY_OGC_OF": "Notify OGC of %s", "PULAC_CERULLO_MODAL_TITLE": "Notify Litigation Support of Possible Conflict of Jurisdiction", @@ -1387,7 +1381,6 @@ "REVIEW_SPLIT_APPEAL_CREATE_SUBHEAD": "Case history, POA, and Veteran information will be deplicated on the new appeal.", "SPLIT_APPEAL_BANNER_SUCCESS_TITLE": "You have successfully split %(appellantName)s's appeal", "SPLIT_APPEAL_BANNER_SUCCESS_MESSAGE": "This new appeal stream has the same docket number and tasks as the original appeal.", - "SPLIT_APPEAL_SPECIALTY_CASE_TEAM_ISSUE_MESSAGE": "This appeal stream has no Specialty Case Team issues. Upon split, it will be routed to regular distribution.", "TABLE_ORIGINAL_APPEAL": "Original Appeal Stream", "TABLE_NEW_APPEAL": "New Appeal Stream", "TABLE_VETERAN": "Veteran", @@ -1421,19 +1414,6 @@ "POA_SUCCESSFULLY_REFRESH_MESSAGE": "Successfully refreshed. No power of attorney information was found at this time.", "POA_UPDATED_SUCCESSFULLY": "POA Updated Successfully", "EMPLOYER_IDENTIFICATION_NUMBER": "Employer Identification Number", - "MOVE_TO_SCT_MODAL_TITLE": "Move appeal to SCT queue", - "MOVE_TO_SCT_MODAL_BODY": "One or more Specialty Case Team (SCT) issues have been added and the appeal will now be routed to the SCT queue.", - "MOVE_TO_DISTRIBUTION_MODAL_TITLE": "Move appeal to regular distribution", - "MOVE_TO_DISTRIBUTION_MODAL_BODY": "All Specialty Case Team (SCT) issues have been removed from this appeal. It will be moved to regular distribution.", - "MOVE_TO_SCT_BANNER_TITLE": "You have successfully updated issues on this appeal", - "MOVE_TO_SCT_BANNER_MESSAGE": "The appeal for %s (ID: %s) has been moved to the SCT queue.", - "MOVE_TO_GENERIC_BANNER_SUCCESS_MESSAGE": "The appeal for %s (ID: %s) has been moved to the %s.", - "SPECIALTY_CASE_TEAM_ASSIGN_TASK_LABEL": "Assign", - "SPECIALTY_CASE_TEAM_ASSIGN_DROPDOWN_LABEL": "Select an attorney", - "RETURN_TO_SCT_MODAL_TITLE": "Return to Specialty Case Team (SCT)", - "RETURN_TO_SCT_MODAL_BODY": "Returning this case to SCT will cancel the current task and remove the case from your queue.", - "RETURN_TO_SCT_SUCCESS_BANNER_TITLE": "You have successfully returned %s's case to the SCT Queue", - "RETURN_TO_SCT_SUCCESS_BANNER_DETAIL": "If you have made a mistake, please email SCT Coordinator to manage any changes.", "VHA_POA_NAME_NOT_LISTED": "VHA does not allow manual entry of unrecognized POAs. Please proceed to next step for the intake, as you do not need to enter additional information.", "CASE_DISTRIBUTION_TITLE": "Case Distribution Algorithm Values", "CASE_DISTRIBUTION_ALGORITHM_DESCRIPTION": "The Case Distribution Algorithm determines how cases are assigned to VLJs and their teams. Current algorithm is \"Docket Date.\"", @@ -1479,5 +1459,8 @@ "CASE_DISTRIBUTION_LEVER_HISTORY_DATA_ELEMENT": "Data Element Changed", "CASE_DISTRIBUTION_LEVER_HISTORY_PREV_VALUE": "Previous Value", "CASE_DISTRIBUTION_LEVER_HISTORY_UPDATED_VALUE": "Updated Value", - "CASE_DISTRIBUTION_STATIC_LEVERS_VALUES": "Values" + "CASE_DISTRIBUTION_STATIC_LEVERS_VALUES": "Values", + "TEST_SEEDS_ALERT_MESSAGE": " in progress", + "TEST_SEEDS_RUN_SEEDS": "Scenario Seeds", + "TEST_SEEDS_CUSTOM_SEEDS": "Custom Seeds" } diff --git a/client/app/components/NumberField.jsx b/client/app/components/NumberField.jsx index 83cb6430032..883d1de0829 100644 --- a/client/app/components/NumberField.jsx +++ b/client/app/components/NumberField.jsx @@ -21,6 +21,7 @@ export default class NumberField extends React.Component { onChange={this.onChange} aria-readonly={this.props.readOnly} ariaLabelText={this.props.ariaLabelText} + useAriaLabel={this.props.useAriaLabel} disabled={this.props.disabled} />
; @@ -46,6 +47,7 @@ NumberField.propTypes = { } }, ariaLabelText: PropTypes.string, + useAriaLabel: PropTypes.bool, placeholder: PropTypes.string, isInteger: PropTypes.bool, readOnly: PropTypes.bool, diff --git a/client/app/index.js b/client/app/index.js index 3849db6d60e..2b7de044bc9 100644 --- a/client/app/index.js +++ b/client/app/index.js @@ -59,6 +59,7 @@ import MPISearch from 'app/mpi/MPISearch'; import Admin from 'app/admin'; import CaseDistribution from 'app/caseDistribution'; import CaseDistributionTest from 'app/caseDistribution/test'; +import TestSeeds from 'app/testSeeds'; import uuid from 'uuid'; const COMPONENTS = { @@ -95,7 +96,8 @@ const COMPONENTS = { MPISearch, Admin, CaseDistribution, - CaseDistributionTest + CaseDistributionTest, + TestSeeds }; const componentWrapper = (component) => (props, railsContext, domNodeId) => { @@ -180,7 +182,8 @@ const componentWrapper = (component) => (props, railsContext, domNodeId) => { './explain/index', './mpi/MPISearch', './admin/index', - './caseDistribution/index' + './caseDistribution/index', + './testSeeds/index' ], () => renderApp(component) ); diff --git a/client/app/styles/caseDistribution/_case_distribution.scss b/client/app/styles/caseDistribution/_case_distribution.scss index e63cb40eff5..36cabcdab79 100644 --- a/client/app/styles/caseDistribution/_case_distribution.scss +++ b/client/app/styles/caseDistribution/_case_distribution.scss @@ -6,3 +6,4 @@ @import '_batch_size.scss'; @import '_docket_time_goals.scss'; @import '_lever_buttons_wrapper.scss'; +@import '_test_seeds.scss'; diff --git a/client/app/styles/caseDistribution/_test_seeds.scss b/client/app/styles/caseDistribution/_test_seeds.scss new file mode 100644 index 00000000000..b4007fde882 --- /dev/null +++ b/client/app/styles/caseDistribution/_test_seeds.scss @@ -0,0 +1,50 @@ +.test-seeds-num-field { + // width: auto; + + .cf-form-int-input { + width: auto; + display: inline-block; + position: relative; + + .input-container { + width: auto; + display: inline-block; + vertical-align: middle; + } + + label { + position: absolute; + bottom: 6px; + left: 90px; + } + } +} + +.test-seed-button-style { + width: auto; + + button { + padding-top: 14px; + padding-bottom: 20px; + height: 45px; + } +} + +.seed-col { + width: 15%; +} + +.seed-table-style tr th { + background: transparent; +} + +.seed-table-style tr td { + border: 0; + padding: 1rem 3rem; +} + +.test-seeds-cssid-field { + input { + width: 110px; + } +} diff --git a/client/app/test/TestSeeds.jsx b/client/app/test/TestSeeds.jsx new file mode 100644 index 00000000000..b0fd1f71129 --- /dev/null +++ b/client/app/test/TestSeeds.jsx @@ -0,0 +1,170 @@ +/* eslint-disable react/prop-types */ + +import React from 'react'; +import NavigationBar from '../components/NavigationBar'; +import { BrowserRouter } from 'react-router-dom'; +import PageRoute from '../components/PageRoute'; +import AppFrame from '../components/AppFrame'; +import AppSegment from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/AppSegment'; +import { LOGO_COLORS } from '../constants/AppConstants'; +import CaseSearchLink from '../components/CaseSearchLink'; +import ApiUtil from '../util/ApiUtil'; +import Button from '../components/Button'; +import cx from 'classnames'; +import TEST_SEEDS from '../../constants/TEST_SEEDS'; +import Alert from 'app/components/Alert'; +import COPY from '../../COPY'; + +class TestSeeds extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + reseedingStatus: { + Aod: false, + NonAod: false, + Tasks: false, + Hearings: false, + Intake: false, + Dispatch: false, + Jobs: false, + Substitutions: false, + DecisionIssues: false, + CavcAmaAppeals: false, + SanitizedJsonSeeds: false, + VeteransHealthAdministration: false, + MTV: false, + Education: false, + PriorityDistributions: false, + TestCaseData: false, + CaseDistributionAuditLeverEntries: false, + Notifications: false, + CavcDashboardData: false, + VbmsExtClaim: false, + CasesTiedToJudgesNoLongerWithBoard: false, + StaticTestCaseData: false, + StaticDispatchedAppealsTestData: false, + RemandedAmaAppeals: false, + RemandedLegacyAppeals: false, + PopulateCaseflowFromVacols: false + }, + seedRunningStatus: false, + seedRunningMsg: 'Seeds running' + }; + this.seedCounts = {}; + } + + handleChange= (event, type) => { + this.seedCounts[type] = event.target.value; + } + + reseed = (type) => { + const seedCount = parseInt(this.seedCounts[type], 10) || 1; + + this.setState({ seedRunning: true, seedRunningMsg: '' }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: true } + })); + + const endpoint = `/seeds/run-demo/${type}/${seedCount}`; + + ApiUtil.post(endpoint).then(() => { + this.setState({ seedRunning: false }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: false } + })); + }). + catch((err) => { + console.warn(err); + this.setState({ seedRunning: false }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: false } + })); + }); + }; + + formatSeedName = (name) => { + return name.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)). + join(' '); + }; + + render() { + const Router = this.props.router || BrowserRouter; + const seedTypes = Object.keys(TEST_SEEDS); + + return ( + +
+ } + appName="Caseflow Admin" + > + +
+ ( +
+ <> + {this.state.seedRunning && ( + + )} + +

{COPY.TEST_SEEDS_RUN_SEEDS}

+
    + {seedTypes.map((type) => ( +
  • +
    + this.handleChange(event, type)} + /> +
    +
    +
    + <> + {this.state.reseedingStatus[type] && ( +
    + {this.formatSeedName(type)} {COPY.TEST_SEEDS_ALERT_MESSAGE} +
    + )} + +
  • + ))} +
+
+
+ )} /> +
+
+
+
+
+
+ ); + } +} + +export default TestSeeds; diff --git a/client/app/testSeeds/components/CustomSeeds.jsx b/client/app/testSeeds/components/CustomSeeds.jsx new file mode 100644 index 00000000000..c85fea1e0e6 --- /dev/null +++ b/client/app/testSeeds/components/CustomSeeds.jsx @@ -0,0 +1,170 @@ +import React from 'react'; +import COPY from '../../../COPY'; +import ApiUtil from '../../util/ApiUtil'; +import Button from 'app/components/Button'; +import NumberField from 'app/components/NumberField'; +import TextField from 'app/components/TextField'; +import cx from 'classnames'; +import CUSTOM_SEEDS from '../../../constants/CUSTOM_SEEDS'; + +class CustomSeeds extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + reseedingStatus: { + Aod: false, + NonAod: false, + Tasks: false, + Hearings: false, + Intake: false, + Dispatch: false, + Jobs: false, + Substitutions: false, + DecisionIssues: false, + CavcAmaAppeals: false, + SanitizedJsonSeeds: false, + VeteransHealthAdministration: false, + MTV: false, + Education: false, + PriorityDistributions: false, + TestCaseData: false, + CaseDistributionAuditLeverEntries: false, + Notifications: false, + CavcDashboardData: false, + VbmsExtClaim: false, + CasesTiedToJudgesNoLongerWithBoard: false, + StaticTestCaseData: false, + StaticDispatchedAppealsTestData: false, + RemandedAmaAppeals: false, + RemandedLegacyAppeals: false, + PopulateCaseflowFromVacols: false + }, + seedRunningStatus: false, + seedRunningMsg: 'Seeds running' + }; + this.seedByType = {}; + } + + onChangeCaseType = (type, inputKey, value) => { + if (typeof this.seedByType[type] !== 'object') { + this.seedByType[type] = {}; + } + this.seedByType[type][inputKey] = value; + } + + reseedByCaseType = (type) => { + const caseType = this.seedByType[type]; + caseType['seed_type'] = type; + + this.setState({ seedRunning: true, seedRunningMsg: '' }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: true } + })); + + // ApiUtil.post(`/seeds/run-demo/${type}`, { data: caseType }).then(() => { + ApiUtil.post(`/seeds/run-demo`, { data: caseType }).then(() => { + this.setState({ seedRunning: false }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: false } + })); + }). + catch((err) => { + console.warn(err); + this.setState({ seedRunning: false }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: false } + })); + }); + }; + + render() { + const seedTypes = Object.keys(CUSTOM_SEEDS); + + return ( +
+ <> +

{COPY.TEST_SEEDS_CUSTOM_SEEDS}

+ + + + + + + + + + + + {seedTypes.map((type) => ( + + + + + + + + ))} + +
+ Case Type + + Number of cases to create + + Days Ago + + Judge CSS_ID (optional) + +
+ {CUSTOM_SEEDS[type]} + +
+ { + this.onChangeCaseType(type, 'seed_count', value); + }} + /> +
+
+
+ { + this.onChangeCaseType(type, 'days_ago', value); + }} + /> +
+
+
+ { + this.onChangeCaseType(type, 'judge_css_id', value); + }} + /> +
+
+
+
+
+
+ +
+ ); + } +} + +export default CustomSeeds; diff --git a/client/app/testSeeds/components/ScenarioSeeds.jsx b/client/app/testSeeds/components/ScenarioSeeds.jsx new file mode 100644 index 00000000000..4aa38c360ec --- /dev/null +++ b/client/app/testSeeds/components/ScenarioSeeds.jsx @@ -0,0 +1,127 @@ +import React from 'react'; +import COPY from '../../../COPY'; +import ApiUtil from '../../util/ApiUtil'; +import Button from 'app/components/Button'; +import cx from 'classnames'; +import TEST_SEEDS from '../../../constants/TEST_SEEDS'; + +class ScenarioSeeds extends React.Component { + constructor(props) { + super(props); + this.state = { + reseedingStatus: { + Aod: false, + NonAod: false, + Tasks: false, + Hearings: false, + Intake: false, + Dispatch: false, + Jobs: false, + Substitutions: false, + DecisionIssues: false, + CavcAmaAppeals: false, + SanitizedJsonSeeds: false, + VeteransHealthAdministration: false, + MTV: false, + Education: false, + PriorityDistributions: false, + TestCaseData: false, + CaseDistributionAuditLeverEntries: false, + Notifications: false, + CavcDashboardData: false, + VbmsExtClaim: false, + CasesTiedToJudgesNoLongerWithBoard: false, + StaticTestCaseData: false, + StaticDispatchedAppealsTestData: false, + RemandedAmaAppeals: false, + RemandedLegacyAppeals: false, + PopulateCaseflowFromVacols: false + }, + seedRunningStatus: false, + seedRunningMsg: 'Seeds running' + }; + this.seedCounts = {}; + } + + handleChange= (event, type) => { + this.seedCounts[type] = event.target.value; + } + + formatSeedName = (name) => { + return name.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)). + join(' '); + }; + + reseed = (type) => { + const seedCount = parseInt(this.seedCounts[type], 10) || 1; + + this.setState({ seedRunning: true, seedRunningMsg: '' }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: true } + })); + + // const endpoint = `/seeds/run-demo/${type}?seed_count=${seedCount}`; + const endpoint = `/seeds/run-demo?seed_type=${type}&seed_count=${seedCount}`; + + ApiUtil.post(endpoint).then(() => { + this.setState({ seedRunning: false }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: false } + })); + }). + catch((err) => { + console.warn(err); + this.setState({ seedRunning: false }); + this.setState((prevState) => ({ + reseedingStatus: { ...prevState.reseedingStatus, [type]: false } + })); + }); + }; + + render() { + const seedTypes = Object.keys(TEST_SEEDS); + + return ( +
+ <> +

{COPY.TEST_SEEDS_RUN_SEEDS}

+
    + {seedTypes.map((type) => ( +
  • +
    + this.handleChange(event, type)} + /> +
    +
    +
    + <> + {this.state.reseedingStatus[type] && ( +
    + {this.formatSeedName(type)} {COPY.TEST_SEEDS_ALERT_MESSAGE} +
    + )} + +
  • + ))} +
+ +
+ ); + } +} + +// ScenarioSeeds.propTypes = { + +// } + +export default ScenarioSeeds; diff --git a/client/app/testSeeds/components/SeedsBannerDisplay.jsx b/client/app/testSeeds/components/SeedsBannerDisplay.jsx new file mode 100644 index 00000000000..25940aaae87 --- /dev/null +++ b/client/app/testSeeds/components/SeedsBannerDisplay.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import Alert from '../../components/Alert'; + +const SeedsBannerDisplay = () => { + + let title = 'Test Seeds'; + let message = ''; + let type = 'success'; + let showBanner = false; + + return ( + <> + {showBanner && ( + + )} + + ); +}; + +export default SeedsBannerDisplay; diff --git a/client/app/testSeeds/components/TestSeedsWrapper.jsx b/client/app/testSeeds/components/TestSeedsWrapper.jsx new file mode 100644 index 00000000000..a86c5e6f487 --- /dev/null +++ b/client/app/testSeeds/components/TestSeedsWrapper.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ScenarioSeeds from './ScenarioSeeds'; +import CustomSeeds from './CustomSeeds'; + +const TestSeedsWrapper = () => { + return ( +
+ + +
+ ); +}; + +export default TestSeedsWrapper; diff --git a/client/app/testSeeds/index.jsx b/client/app/testSeeds/index.jsx new file mode 100644 index 00000000000..99c9c868eb9 --- /dev/null +++ b/client/app/testSeeds/index.jsx @@ -0,0 +1,77 @@ +/* eslint-disable react/prop-types */ + +import React from 'react'; +// import ReduxBase from '../components/ReduxBase'; +import NavigationBar from '../components/NavigationBar'; +import { BrowserRouter } from 'react-router-dom'; +import PageRoute from '../components/PageRoute'; +import AppFrame from '../components/AppFrame'; +import AppSegment from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/AppSegment'; +import { LOGO_COLORS } from '../constants/AppConstants'; +import Footer from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/Footer'; +import CaseSearchLink from '../components/CaseSearchLink'; +import SeedsBannerDisplay from './components/SeedsBannerDisplay'; +// import rootReducer from '../testSeeds/reducers/root'; +import TestSeedsApp from './pages/TestSeedsApp'; + +class TestSeeds extends React.PureComponent { + + render() { + + const Router = this.props.router || BrowserRouter; + const appName = 'Test Seeds'; + + return ( + // + +
+ } + appName="Caseflow Admin" + > + + + +
+ { + return ( + + ); + }} + /> +
+
+
+
+
+
+
+ //
+ ); + } +} + +export default TestSeeds; diff --git a/client/app/testSeeds/pages/TestSeedsApp.jsx b/client/app/testSeeds/pages/TestSeedsApp.jsx new file mode 100644 index 00000000000..20594306ef6 --- /dev/null +++ b/client/app/testSeeds/pages/TestSeedsApp.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import TestSeedsWrapper from '../components/TestSeedsWrapper'; + +class TestSeedsApp extends React.Component { + // constructor(props) { + // super(props); + // } + + render() { + return ( +
+
+ +
+
+ ); + + } +} + +export default TestSeedsApp; + diff --git a/client/app/testSeeds/reducers/root.js b/client/app/testSeeds/reducers/root.js new file mode 100644 index 00000000000..accd56e055c --- /dev/null +++ b/client/app/testSeeds/reducers/root.js @@ -0,0 +1,13 @@ +import { timeFunction } from '../../util/PerfDebug'; +import { combineReducers } from 'redux'; + +//TODO: Needs to Implement TestSeeds Reducer + +const rootReducer = combineReducers({ + testSeedObjects: {} +}); + +export default timeFunction( + rootReducer, + (timeLabel, state, action) => `Action ${action.type} reducer time: ${timeLabel}` +); diff --git a/client/constants/CUSTOM_SEEDS.json b/client/constants/CUSTOM_SEEDS.json new file mode 100644 index 00000000000..9c7ac4fa84f --- /dev/null +++ b/client/constants/CUSTOM_SEEDS.json @@ -0,0 +1,6 @@ +{ + "ama-aod-hearing-seeds": "Seed AMA AOD Hearing", + "ama-non-aod-hearing-seeds": "Seed AMA NON AOD Hearing", + "legacy-case-seeds": "Seed Legacy Cases", + "ama-direct-review-seeds": "Seed AMA Direct Review" +} diff --git a/client/constants/TEST_SEEDS.json b/client/constants/TEST_SEEDS.json new file mode 100644 index 00000000000..5744f7c7ad6 --- /dev/null +++ b/client/constants/TEST_SEEDS.json @@ -0,0 +1,10 @@ +{ + "aod-seeds": "db:seed:demo_aod_hearing_case_lever_test_data", + "non-aod-hearing-seeds":"db:seed:demo_non_aod_hearing_case_lever_test_data", + "ama-aod-hearing-seeds": "db:seed:demo_ama_aod_hearing_data", + "ama-non-aod-hearing-seeds": "db:seed:demo_ama_non_aod_hearing_data", + "legacy-case-seeds": "db:seed:demo_legacy_cases_data", + "ama-direct-review-seeds": "db:seed:demo_direct_reviews_data", + "scenario-1-seeds": "db:seed:demo_aod_hearing_case_lever_test_data", + "scenario-2-seeds": "db:seed:demo_non_aod_hearing_case_lever_test_data" +} diff --git a/client/test/app/testSeeds/components/CustomSeeds.test.js b/client/test/app/testSeeds/components/CustomSeeds.test.js new file mode 100644 index 00000000000..fd9663d23a0 --- /dev/null +++ b/client/test/app/testSeeds/components/CustomSeeds.test.js @@ -0,0 +1,116 @@ +import React from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import CustomSeeds from 'app/testSeeds/components/CustomSeeds'; +import CUSTOM_SEEDS from '../../../../constants/CUSTOM_SEEDS'; +import ApiUtil from 'app/util/ApiUtil'; + +jest.mock('app/util/ApiUtil', () => ({ + post: jest.fn() +})); + +describe('Custom Seeds', () => { + + beforeEach(() => { + // Reset mock implementation before each test + ApiUtil.post.mockReset(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + const seedTypes = Object.keys(CUSTOM_SEEDS); + + it('should render input fields and buttons for each seed type', () => { + const { getByLabelText, container } = render(); + + // Check if input fields and buttons are rendered for each seed type + seedTypes.forEach((type) => { + const caseCountInput = getByLabelText(`seed-count-${type}`); + const daysAgoInput = getByLabelText(`days-ago-${type}`); + const cssIdInput = getByLabelText(`css-id-${type}`); + const button = container.querySelector(`#btn-${type}`); + + expect(caseCountInput).toBeInTheDocument(); + expect(daysAgoInput).toBeInTheDocument(); + expect(cssIdInput).toBeInTheDocument(); + expect(button).toBeInTheDocument(); + }); + }); + + it('should update state when input values change', () => { + const { getByLabelText } = render(); + const first_seed = seedTypes[0]; + const caseCountInput = getByLabelText(`seed-count-${first_seed}`); + const daysAgoInput = getByLabelText(`days-ago-${first_seed}`); + const cssIdInput = getByLabelText(`css-id-${first_seed}`); + + fireEvent.change(caseCountInput, { target: { value: '10' } }); + fireEvent.change(daysAgoInput, { target: { value: '5' } }); + fireEvent.change(cssIdInput, { target: { value: '12345' } }); + + expect(caseCountInput.value).toBe('10'); + expect(daysAgoInput.value).toBe('5'); + expect(cssIdInput.value).toBe('12345'); + }); + + it('should make API call when button is clicked', async () => { + ApiUtil.post.mockResolvedValueOnce({ data: 'Success' }); + + const { container, getByLabelText } = render(); + const first_seed = seedTypes[0]; + const caseCountInput = getByLabelText(`seed-count-${first_seed}`); + const daysAgoInput = getByLabelText(`days-ago-${first_seed}`); + const cssIdInput = getByLabelText(`css-id-${first_seed}`); + const button = container.querySelector(`#btn-${first_seed}`); + + fireEvent.change(caseCountInput, { target: { value: '10' } }); + fireEvent.change(daysAgoInput, { target: { value: '5' } }); + fireEvent.change(cssIdInput, { target: { value: 'BVADWISE' } }); + + // Find the button in the same row as the input fields + const row = button.closest('tr'); + // const createButton = within(row).getByText('Create'); + + fireEvent.click(button); + + expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo`, { + data: { seed_type: first_seed, seed_count: 10, days_ago: 5, judge_css_id: 'BVADWISE' } + }); + + // Wait for API call to resolve + await waitFor(() => { + expect(ApiUtil.post).toHaveBeenCalledTimes(1); + }); + }); + + it('should handle API call error', async () => { + const consoleWarnSpy = jest.spyOn(console, 'warn'); + + ApiUtil.post.mockRejectedValueOnce(new Error('API Error')); + const { container, getByLabelText } = render(); + const first_seed = seedTypes[0]; + const caseCountInput = getByLabelText(`seed-count-${first_seed}`); + const daysAgoInput = getByLabelText(`days-ago-${first_seed}`); + const cssIdInput = getByLabelText(`css-id-${first_seed}`); + const button = container.querySelector(`#btn-${first_seed}`); + + fireEvent.change(caseCountInput, { target: { value: '10' } }); + fireEvent.change(daysAgoInput, { target: { value: '5' } }); + fireEvent.change(cssIdInput, { target: { value: 'BVADWISE' } }); + fireEvent.click(button); + + expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo`, { + data: { seed_type: first_seed, seed_count: 10, days_ago: 5, judge_css_id: 'BVADWISE' } + }); + + // Wait for API call to reject + await waitFor(() => { + expect(ApiUtil.post).toHaveBeenCalledTimes(1); + }); + + // Check if error message is displayed + expect(consoleWarnSpy).toHaveBeenCalledWith(new Error('API Error')); + }); +}); + diff --git a/client/test/app/testSeeds/components/ScenarioSeeds.test.js b/client/test/app/testSeeds/components/ScenarioSeeds.test.js new file mode 100644 index 00000000000..788878f978d --- /dev/null +++ b/client/test/app/testSeeds/components/ScenarioSeeds.test.js @@ -0,0 +1,86 @@ +import React from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import ScenarioSeeds from 'app/testSeeds/components/ScenarioSeeds'; +import TEST_SEEDS from '../../../../constants/TEST_SEEDS'; +import ApiUtil from 'app/util/ApiUtil'; + +jest.mock('app/util/ApiUtil', () => ({ + post: jest.fn() +})); + +describe('Scenario Seeds', () => { + + beforeEach(() => { + // Reset mock implementation before each test + ApiUtil.post.mockReset(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + const component = new ScenarioSeeds(); + + it('should render input fields and buttons for each seed type', () => { + const { getByLabelText, getByText } = render(); + // Check if input fields and buttons are rendered for each seed type + Object.keys(TEST_SEEDS).forEach((type) => { + const input = getByLabelText(`count-${type}`); + const button = getByText(`Run Demo ${component.formatSeedName(type)}`); + expect(input).toBeInTheDocument(); + expect(button).toBeInTheDocument(); + }); + }) + + it('should update input value when user types', () => { + const { getByLabelText } = render(); + const seed_aod_type = Object.keys(TEST_SEEDS)[0]; + const input = getByLabelText(`count-${seed_aod_type}`); + + fireEvent.change(input, { target: { value: '10' } }); + expect(input.value).toBe('10'); + }); + + it('should make API call when button is clicked', async () => { + ApiUtil.post.mockResolvedValueOnce({ data: 'Success' }); + const { getByText, getByLabelText } = render(); + const seed_aod_type = Object.keys(TEST_SEEDS)[0]; + const input = getByLabelText(`count-${seed_aod_type}`); + const button = getByText(`Run Demo ${component.formatSeedName(seed_aod_type)}`); + + fireEvent.change(input, { target: { value: '10' } }); + fireEvent.click(button); + + expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo?seed_type=${seed_aod_type}&seed_count=10`); + + // Wait for API call to resolve + await waitFor(() => { + expect(ApiUtil.post).toHaveBeenCalledTimes(1); + }); + }); + + it('should handle API call error', async () => { + // Spy on console.warn + const consoleWarnSpy = jest.spyOn(console, 'warn'); + + ApiUtil.post.mockRejectedValueOnce(new Error('API Error')); + const { getByText, getByLabelText } = render(); + const seed_aod_type = Object.keys(TEST_SEEDS)[0]; + const input = getByLabelText(`count-${seed_aod_type}`); + const button = getByText(`Run Demo ${component.formatSeedName(seed_aod_type)}`); + + fireEvent.change(input, { target: { value: '10' } }); + fireEvent.click(button); + + expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo?seed_type=${seed_aod_type}&seed_count=10`); + + // Wait for API call to reject + await waitFor(() => { + expect(ApiUtil.post).toHaveBeenCalledTimes(1); + }); + + // Check if error message is displayed + expect(consoleWarnSpy).toHaveBeenCalledWith(new Error('API Error')); + }); +}); + diff --git a/client/test/app/testSeeds/pages/TestSeedsApp.test.js b/client/test/app/testSeeds/pages/TestSeedsApp.test.js new file mode 100644 index 00000000000..6f0bf8e0fcf --- /dev/null +++ b/client/test/app/testSeeds/pages/TestSeedsApp.test.js @@ -0,0 +1,23 @@ +import React from 'react'; +import TestSeedsApp from 'app/testSeeds/pages/TestSeedsApp'; +import { mount } from 'enzyme'; + +describe('render Test Seeds Application', () => { + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('renders Test Seeds App', () => { + + let wrapper = mount( + + ); + + wrapper.update(); + + expect(wrapper.find('#run_custom_seeds').exists()).toBeTruthy(); + expect(wrapper.find('#run_seeds').exists()).toBeTruthy(); + }); +}); + diff --git a/config/routes.rb b/config/routes.rb index ebc0425e737..e685df83222 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -423,6 +423,13 @@ post "docket_switches", to: "docket_switches#create" post "docket_switches/address_ruling", to: "docket_switches#address_ruling" + # test seed buttons routes + get 'test/seeds', :to => 'test_seeds#seeds' + + scope path: 'seeds', as: 'seeds' do + post 'run-demo', to: 'test_docket_seeds#seed_dockets' + end + # :nocov: namespace :test do get "/error", to: "users#show_error" diff --git a/db/seeds/case_distribution_levers.rb b/db/seeds/case_distribution_levers.rb index 71b665a72ee..7ad9c3d490c 100644 --- a/db/seeds/case_distribution_levers.rb +++ b/db/seeds/case_distribution_levers.rb @@ -100,9 +100,6 @@ def lever_updated?(lever, existing_lever) def validate_levers_creation levers = CaseDistributionLevers.levers.map { |lever| lever[:item] } existing_levers = CaseDistributionLever.all.map(&:item) - - puts "#{CaseDistributionLever.count} levers exist" - puts "Levers not created #{levers - existing_levers}" if levers.length != existing_levers.length end class << self diff --git a/db/seeds/demo_ama_aod_hearing_data.rb b/db/seeds/demo_ama_aod_hearing_data.rb new file mode 100644 index 00000000000..718169f9fbb --- /dev/null +++ b/db/seeds/demo_ama_aod_hearing_data.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +module Seeds + class DemoAmaAodHearingData < Base + def initialize + @seed_count = ENV["SEED_COUNT"].to_i + @days_ago = ENV["DAYS_AGO"].to_i.days.ago + @hearing_judge = find_or_create_demo_seed_judge(ENV['JUDGE_CSS_ID']) + end + + def seed! + RequestStore[:current_user] = User.system_user + @seed_count.times do + create_ama_aod_hearing + end + end + + def create_ama_aod_hearing + Timecop.travel(@days_ago) + create( + :appeal, + :hearing_docket, + :with_post_intake_tasks, + :advanced_on_docket_due_to_age, + :held_hearing_and_ready_to_distribute, + :tied_to_judge, + veteran: create_demo_veteran_for_docket, + receipt_date: @days_ago, + tied_judge: @hearing_judge, + adding_user: User.first + ) + Timecop.return + end + + #TODO: put the below into helper module + def random_demo_file_number_and_participant_id + random_file_number = Random.rand(100_000_000...989_999_999) + random_participant_id = random_file_number + 100000 + + while find_demo_veteran(random_file_number) + random_file_number += 2000 + random_participant_id += 2000 + end + + return random_file_number, random_participant_id + end + + def find_or_create_demo_seed_judge(judge_ccs_id) + unless judge_ccs_id.empty? + User.find_by_css_id(judge_ccs_id) || + create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) + else + User.find_by_css_id("QDEMOSEEDJ") || + create(:user, :judge, :with_vacols_judge_record, css_id: "QDEMOSEEDJ", full_name: "Demo Seed Judge") + end + end + + def demo_regional_office + 'RO17' + end + + def find_demo_veteran(file_number) + Veteran.find_by(file_number: format("%09d", n: file_number + 1)) + end + + def create_demo_veteran_for_docket + file_number, participant_id = random_demo_file_number_and_participant_id + create_demo_veteran( + file_number: file_number, + participant_id: participant_id + ) + end + + def create_demo_veteran(options = {}) + params = { + file_number: format("%09d", n: options[:file_number]), + participant_id: format("%09d", n: options[:participant_id]) + } + + Veteran.find_by_participant_id(params[:participant_id]) || create(:veteran, params.merge(options)) + end + end +end diff --git a/db/seeds/demo_ama_non_aod_hearing_data.rb b/db/seeds/demo_ama_non_aod_hearing_data.rb new file mode 100644 index 00000000000..f900f0026fb --- /dev/null +++ b/db/seeds/demo_ama_non_aod_hearing_data.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module Seeds + class DemoAmaNonAodHearingData < Base + def initialize + @seed_count = ENV['SEED_COUNT'].to_i + @days_ago = ENV['DAYS_AGO'].to_i.days.ago + @hearing_judge = find_or_create_demo_seed_judge(ENV['JUDGE_CSS_ID']) + end + + def seed! + RequestStore[:current_user] = User.system_user + @seed_count.times do + create_ama_non_aod_hearing + end + end + + def create_ama_non_aod_hearing + Timecop.travel(@days_ago) + create( + :appeal, + :hearing_docket, + :with_post_intake_tasks, + :held_hearing_and_ready_to_distribute, + :tied_to_judge, + veteran: create_demo_veteran_for_docket, + receipt_date: @days_ago, + tied_judge: @hearing_judge, + adding_user: User.first + ) + Timecop.return + end + + #TODO: put the below into helper module + def random_demo_file_number_and_participant_id + random_file_number = Random.rand(100_000_000...989_999_999) + random_participant_id = random_file_number + 100000 + + while find_demo_veteran(random_file_number) + random_file_number += 2000 + random_participant_id += 2000 + end + + return random_file_number, random_participant_id + end + + def find_or_create_demo_seed_judge(judge_ccs_id) + unless judge_ccs_id.empty? + User.find_by_css_id(judge_ccs_id) || + create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) + else + User.find_by_css_id("QDEMOSEEDJ") || + create(:user, :judge, :with_vacols_judge_record, css_id: "QDEMOSEEDJ", full_name: "Demo Seed Judge") + end + end + + def demo_regional_office + 'RO17' + end + + def find_demo_veteran(file_number) + Veteran.find_by(file_number: format("%09d", n: file_number + 1)) + end + + def create_demo_veteran_for_docket + file_number, participant_id = random_demo_file_number_and_participant_id + create_demo_veteran( + file_number: file_number, + participant_id: participant_id + ) + end + + def create_demo_veteran(options = {}) + params = { + file_number: format("%09d", n: options[:file_number]), + participant_id: format("%09d", n: options[:participant_id]) + } + + Veteran.find_by_participant_id(params[:participant_id]) || create(:veteran, params.merge(options)) + end + end +end diff --git a/db/seeds/demo_direct_reviews_data.rb b/db/seeds/demo_direct_reviews_data.rb new file mode 100644 index 00000000000..99b267789d1 --- /dev/null +++ b/db/seeds/demo_direct_reviews_data.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module Seeds + class DemoDirectReviewsData < Base + def initialize + @seed_count = ENV['SEED_COUNT'].to_i + @days_ago = ENV['DAYS_AGO'].to_i.days.ago + @associated_judge = find_or_create_demo_seed_judge(ENV['JUDGE_CSS_ID']) + end + + def seed! + RequestStore[:current_user] = User.system_user + @seed_count.times do + create_ama_direct_review + end + end + + def create_ama_direct_review + Timecop.travel(@days_ago) + create( + :appeal, + :direct_review_docket, + :ready_for_distribution, + associated_judge: @associated_judge, + veteran: create_demo_veteran_for_docket, + receipt_date: @days_ago + ) + Timecop.return + end + + #TODO: put the below into helper module + def random_demo_file_number_and_participant_id + random_file_number = Random.rand(100_000_000...989_999_999) + random_participant_id = random_file_number + 100000 + + while find_demo_veteran(random_file_number) + random_file_number += 2000 + random_participant_id += 2000 + end + + return random_file_number, random_participant_id + end + + def find_or_create_demo_seed_judge(judge_ccs_id) + unless judge_ccs_id.empty? + User.find_by_css_id(judge_ccs_id) || + create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) + else + User.find_by_css_id("QDEMOSEEDJ") || + create(:user, :judge, :with_vacols_judge_record, css_id: "QDEMOSEEDJ", full_name: "Demo Seed Judge") + end + end + + def demo_regional_office + 'RO17' + end + + def find_demo_veteran(file_number) + Veteran.find_by(file_number: format("%09d", n: file_number + 1)) + end + + def create_demo_veteran_for_docket + file_number, participant_id = random_demo_file_number_and_participant_id + create_demo_veteran( + file_number: file_number, + participant_id: participant_id + ) + end + + def create_demo_veteran(options = {}) + params = { + file_number: format("%09d", n: options[:file_number]), + participant_id: format("%09d", n: options[:participant_id]) + } + + Veteran.find_by_participant_id(params[:participant_id]) || create(:veteran, params.merge(options)) + end + end +end diff --git a/db/seeds/demo_legacy_cases_data.rb b/db/seeds/demo_legacy_cases_data.rb new file mode 100644 index 00000000000..ed92f6b8099 --- /dev/null +++ b/db/seeds/demo_legacy_cases_data.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module Seeds + class DemoLegacyCasesData < Base + def initialize + @seed_count = ENV['SEED_COUNT'].to_i + @days_ago = ENV['DAYS_AGO'].to_i.days.ago + @associated_judge = find_or_create_demo_seed_judge(ENV['JUDGE_CSS_ID']) + end + + def seed! + RequestStore[:current_user] = User.system_user + @seed_count.times do + create_legacy_case + end + end + + def create_legacy_case + Timecop.travel(@days_ago) + veteran = create_demo_veteran_for_docket + + correspondent = create(:correspondent, + snamef: veteran.first_name, snamel: veteran.last_name, + ssalut: "", ssn: veteran.file_number) + + vacols_case = create_video_vacols_case(veteran, + correspondent, + @associated_judge, + @days_ago) + + legacy_appeal = create( + :legacy_appeal, + :with_root_task, + vacols_case: vacols_case, + closest_regional_office: demo_regional_office + ) + + create(:available_hearing_locations, demo_regional_office, appeal: legacy_appeal) + Timecop.return + end + + def create_video_vacols_case(veteran, correspondent, associated_judge, days_ago) + create( + :case, + :aod, + :tied_to_judge, + :video_hearing_requested, + :type_original, + :ready_for_distribution, + tied_judge: associated_judge, + correspondent: correspondent, + bfcorlid: "#{veteran.file_number}S", + case_issues: create_list(:case_issue, 3, :compensation), + bfd19: days_ago + ) + end + + #TODO: put the below into helper module + def random_demo_file_number_and_participant_id + random_file_number = Random.rand(100_000_000...989_999_999) + random_participant_id = random_file_number + 100000 + + while find_demo_veteran(random_file_number) + random_file_number += 2000 + random_participant_id += 2000 + end + + return random_file_number, random_participant_id + end + + def find_or_create_demo_seed_judge(judge_ccs_id) + unless judge_ccs_id.empty? + User.find_by_css_id(judge_ccs_id) || + create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) + else + User.find_by_css_id("QDEMOSEEDJ") || + create(:user, :judge, :with_vacols_judge_record, css_id: "QDEMOSEEDJ", full_name: "Demo Seed Judge") + end + end + + def demo_regional_office + 'RO17' + end + + def find_demo_veteran(file_number) + Veteran.find_by(file_number: format("%09d", n: file_number + 1)) + end + + def create_demo_veteran_for_docket + file_number, participant_id = random_demo_file_number_and_participant_id + create_demo_veteran( + file_number: file_number, + participant_id: participant_id + ) + end + + def create_demo_veteran(options = {}) + params = { + file_number: format("%09d", n: options[:file_number]), + participant_id: format("%09d", n: options[:participant_id]) + } + + Veteran.find_by_participant_id(params[:participant_id]) || create(:veteran, params.merge(options)) + end + end +end diff --git a/spec/controllers/test_docket_seeds_controller_spec.rb b/spec/controllers/test_docket_seeds_controller_spec.rb new file mode 100644 index 00000000000..1be85e50611 --- /dev/null +++ b/spec/controllers/test_docket_seeds_controller_spec.rb @@ -0,0 +1,381 @@ +# frozen_string_literal: true + +RSpec.describe TestDocketSeedsController, :all_dbs, type: :controller do + Rails.application.load_tasks + let!(:authenticated_user) { User.authenticate!(roles: ["System Admin"]) } + + describe "POST run-demo?seed_type=ii?seed_count=x&days_ago=y&judge_css_id=zzz" do + context "seed_ama_aod_hearings" do + context "single seed" do + context "with judge CSS ID given" do + it "creates a 30 day old AMA AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-aod-hearing-seeds", + seed_count: "1", + days_ago: "30", + judge_css_id: "RSPCJUDGE1" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_truthy + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE1") + expect(hearing_case.receipt_date).to eq(Date.parse(30.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(30.days.ago.to_s)) + end + + it "creates a 365 day old AMA AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-aod-hearing-seeds", + seed_count: "1", + days_ago: "365", + judge_css_id: "RSPCJUDGE1" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_truthy + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE1") + expect(hearing_case.receipt_date).to eq(Date.parse(365.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(365.days.ago.to_s)) + end + end + + context "without judge CSS ID given" do + it "creates a 90 day old AMA AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-aod-hearing-seeds", + seed_count: "1", + days_ago: "90", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_truthy + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("QDEMOSEEDJ") + expect(hearing_case.receipt_date).to eq(Date.parse(90.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(90.days.ago.to_s)) + end + + it "creates a 730 day old AMA AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-aod-hearing-seeds", + seed_count: "1", + days_ago: "730", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_truthy + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("QDEMOSEEDJ") + expect(hearing_case.receipt_date).to eq(Date.parse(730.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(730.days.ago.to_s)) + end + end + end + context "multiple seeds" do + it "creates multiple AMA AOD Hearing cases" do + post :seed_dockets, params: { + seed_type: "ama-aod-hearing-seeds", + seed_count: "5", + days_ago: "300", + judge_css_id: "Q5AODJUDGE" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(5) + end + end + end + + context "seed_ama_non_aod_hearings" do + context "single seed" do + context "with judge CSS ID given" do + it "creates a 30 day old AMA non-AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-non-aod-hearing-seeds", + seed_count: "1", + days_ago: "30", + judge_css_id: "RSPCJUDGE2" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_falsey + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE2") + expect(hearing_case.receipt_date).to eq(Date.parse(30.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(30.days.ago.to_s)) + end + + it "creates a 365 day old AMA non-AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-non-aod-hearing-seeds", + seed_count: "1", + days_ago: "365", + judge_css_id: "RSPCJUDGE2" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_falsey + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE2") + expect(hearing_case.receipt_date).to eq(Date.parse(365.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(365.days.ago.to_s)) + end + end + + context "without judge CSS ID given" do + it "creates a 90 day old AMA non-AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-non-aod-hearing-seeds", + seed_count: "1", + days_ago: "90", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_falsey + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("QDEMOSEEDJ") + expect(hearing_case.receipt_date).to eq(Date.parse(90.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(90.days.ago.to_s)) + end + + it "creates a 730 day old AMA non-AOD Hearing case" do + post :seed_dockets, params: { + seed_type: "ama-non-aod-hearing-seeds", + seed_count: "1", + days_ago: "730", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + hearing_case = Appeal.last + expect(hearing_case.aod_based_on_age).to be_falsey + expect(hearing_case.docket_type).to eq("hearing") + expect(hearing_case.hearings.first.disposition).to eq("held") + expect(hearing_case.hearings.first.judge.css_id).to eq("QDEMOSEEDJ") + expect(hearing_case.receipt_date).to eq(Date.parse(730.days.ago.to_s)) + expect(Date.parse(hearing_case.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(730.days.ago.to_s)) + end + end + end + context "multiple seeds" do + it "creates multiple AMA non-AOD Hearing cases" do + post :seed_dockets, params: { + seed_type: "ama-non-aod-hearing-seeds", + seed_count: "5", + days_ago: "180", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(5) + end + end + end + + context "seed_legacy_cases" do + context "single seed" do + context "with judge CSS ID given" do + it "creates a 30 day old Legacy case" do + post :seed_dockets, params: { + seed_type: "legacy-case-seeds", + seed_count: "1", + days_ago: "30", + judge_css_id: "RSPCJUDGE3" + } + + expect(response.status).to eq 200 + expect(LegacyAppeal.count).to eq(1) + # legacy_appeal = LegacyAppeal.last + # TODO: Add expext statements + end + + it "creates a 365 day old Legacy case" do + post :seed_dockets, params: { + seed_type: "legacy-case-seeds", + seed_count: "1", + days_ago: "365", + judge_css_id: "RSPCJUDGE3" + } + + expect(response.status).to eq 200 + expect(LegacyAppeal.count).to eq(1) + # legacy_appeal = LegacyAppeal.last + # TODO: Add expext statements + end + end + + context "without judge CSS ID given" do + it "creates a 90 day old Legacy case" do + post :seed_dockets, params: { + seed_type: "legacy-case-seeds", + seed_count: "1", + days_ago: "90", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(LegacyAppeal.count).to eq(1) + # legacy_appeal = LegacyAppeal.last + # TODO: Add expext statements + end + + it "creates a 730 day old Legacy case" do + post :seed_dockets, params: { + seed_type: "legacy-case-seeds", + seed_count: "1", + days_ago: "730", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(LegacyAppeal.count).to eq(1) + # legacy_appeal = LegacyAppeal.last + # TODO: Add expext statements + end + end + end + context "multiple seeds" do + it "creates multiple Legacy cases" do + post :seed_dockets, params: { + seed_type: "legacy-case-seeds", + seed_count: "5", + days_ago: "30", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(LegacyAppeal.count).to eq(5) + end + end + end + + context "seed_ama_direct_reviews" do + context "single seed" do + context "with judge CSS ID given" do + it "creates a 30 day old Direct Review case" do + post :seed_dockets, params: { + seed_type: "ama-direct-review-seeds", + seed_count: "1", + days_ago: "30", + judge_css_id: "RSPCJUDGE4" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + direct_review = Appeal.last + expect(direct_review.docket_type).to eq("direct_review") + # expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE1") + expect(direct_review.receipt_date).to eq(Date.parse(30.days.ago.to_s)) + expect(Date.parse(direct_review.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(30.days.ago.to_s)) + end + + it "creates a 365 day old Direct Review case" do + post :seed_dockets, params: { + seed_type: "ama-direct-review-seeds", + seed_count: "1", + days_ago: "365", + judge_css_id: "RSPCJUDGE4" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + direct_review = Appeal.last + expect(direct_review.docket_type).to eq("direct_review") + # expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE1") + expect(direct_review.receipt_date).to eq(Date.parse(365.days.ago.to_s)) + expect(Date.parse(direct_review.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(365.days.ago.to_s)) + end + end + + context "without judge CSS ID given" do + it "creates a 90 day old Direct Review case" do + post :seed_dockets, params: { + seed_type: "ama-direct-review-seeds", + seed_count: "1", + days_ago: "90", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + direct_review = Appeal.last + expect(direct_review.docket_type).to eq("direct_review") + # expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE1") + expect(direct_review.receipt_date).to eq(Date.parse(90.days.ago.to_s)) + expect(Date.parse(direct_review.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(90.days.ago.to_s)) + end + + it "creates a 730 day old Direct Review case" do + post :seed_dockets, params: { + seed_type: "ama-direct-review-seeds", + seed_count: "1", + days_ago: "730", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.count).to eq(1) + direct_review = Appeal.last + expect(direct_review.docket_type).to eq("direct_review") + # expect(hearing_case.hearings.first.judge.css_id).to eq("RSPCJUDGE1") + expect(direct_review.receipt_date).to eq(Date.parse(730.days.ago.to_s)) + expect(Date.parse(direct_review.tasks.where(type: "DistributionTask").first.assigned_at.to_s)) + .to eq(Date.parse(730.days.ago.to_s)) + end + end + end + context "multiple seeds" do + it "creates multiple Direct Review cases" do + post :seed_dockets, params: { + seed_type: "ama-direct-review-seeds", + seed_count: "5", + days_ago: "30", + judge_css_id: "" + } + + expect(response.status).to eq 200 + expect(Appeal.where(docket_type: "direct_review").count).to eq(5) + expect(Appeal.count).to eq(5) + end + end + end + end +end diff --git a/spec/feature/test_seeds_spec.rb b/spec/feature/test_seeds_spec.rb new file mode 100644 index 00000000000..95a9906227c --- /dev/null +++ b/spec/feature/test_seeds_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +RSpec.feature "Test Seeds" do + let!(:current_user) do + user = create(:user, css_id: "BVADWISE") + CDAControlGroup.singleton.add_user(user) + User.authenticate!(user: user) + end + + context "user is a Case Distro Algorithm Control admin" do + before do + OrganizationsUser.make_user_admin(current_user, CDAControlGroup.singleton) + end + + scenario "visits the test seeds page" do + visit "test/seeds" + confirm_page_and_seed_buttons_present + end + end +end + +def confirm_page_and_buttons_present + expect(page).to have_content(COPY::TEST_SEEDS_RUN_SEEDS) +end From 40d6da9283648a894c4ec557a21f542dcaeb1979 Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Tue, 7 May 2024 09:56:14 -0400 Subject: [PATCH 13/44] Sharsha/appeals 45531 (#21566) * Fixed rspec failures on constants * APPEALS-45531. Remove dead code --------- Co-authored-by: SHarshain --- app/controllers/test_seeds_controller.rb | 61 -------- client/COPY.json | 21 +++ client/app/test/TestSeeds.jsx | 170 ----------------------- spec/feature/test_seeds_spec.rb | 24 ---- 4 files changed, 21 insertions(+), 255 deletions(-) delete mode 100644 app/controllers/test_seeds_controller.rb delete mode 100644 client/app/test/TestSeeds.jsx delete mode 100644 spec/feature/test_seeds_spec.rb diff --git a/app/controllers/test_seeds_controller.rb b/app/controllers/test_seeds_controller.rb deleted file mode 100644 index 94e0c93e15c..00000000000 --- a/app/controllers/test_seeds_controller.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -require "csv" - -class TestSeedsController < ApplicationController - before_action :check_environment - before_action :verify_access, only: [:seeds] - before_action :authorize_admin, only: [:seeds] - - def seeds - # seeds - render "/test/seeds" - end - - def run_demo - seed_type = params[:seed_type].to_sym - seed_count = params[:seed_count].to_i - test_seed_list = Constants.TEST_SEEDS.to_h - task_name = test_seed_list[seed_type] - - if task_name - Rake::Task[task_name].reenable - index = 0 - seed_count.times do - index += 1 - Rails.logger.info "Rake run count #{index}" - Rake::Task[task_name].execute - end - head :ok - else - render json: { error: "Invalid seed type" }, status: :bad_request - end - end - - private - - def check_environment - return true if Rails.env.development? - return true if Rails.deploy_env?(:demo) - - redirect_to "/unauthorized" - end - - def authorize_admin - error = ["UNAUTHORIZED"] - - resp = { - status_code: 500, - message: error, - user_is_an_acd_admin: false - } - render json: resp unless CDAControlGroup.singleton.user_is_admin?(current_user) - end - - def verify_access - return true if current_user&.organizations && current_user.organizations.any?(&:users_can_view_levers?) - - session["return_to"] = request.original_url - redirect_to "/unauthorized" - end -end diff --git a/client/COPY.json b/client/COPY.json index 9d3dcae94aa..f4856138231 100644 --- a/client/COPY.json +++ b/client/COPY.json @@ -334,6 +334,9 @@ "EDUCATION_ORGANIZATIONAL_QUEUE_PAGE_IN_PROGRESS_TASKS_DESCRIPTION": "Cases that are in progress in the %s team's queue:", "EDUCATION_ORGANIZATIONAL_QUEUE_PAGE_UNASSIGNED_TASKS_DESCRIPTION": "Cases owned by the %s team:", "VHA_ORGANIZATIONAL_QUEUE_PAGE_IN_PROGRESS_TASKS_DESCRIPTION": "Cases that are in progress:", + "SPECIALTY_CASE_TEAM_QUEUE_PAGE_ACTION_REQUIRED_TAB_TITLE": "Action Required (%d)", + "SPECIALTY_CASE_TEAM_QUEUE_PAGE_ACTION_REQUIRED_TAB_DESCRIPTION": "Cases owned by the Specialty Case Team that require action:", + "SPECIALTY_CASE_TEAM_QUEUE_PAGE_COMPLETED_TAB_DESCRIPTION": "Cases owned by the Specialty Case Team that have been assigned to a SCT Attorney (last 14 days):", "ORGANIZATIONAL_QUEUE_PAGE_IN_PROGRESS_TASKS_DESCRIPTION": "Cases in progress in a %s team member's queue.", "ORGANIZATIONAL_QUEUE_PAGE_READY_FOR_REVIEW_TASKS_DESCRIPTION": "Cases ready for review in a %s team member's queue.", "ORGANIZATIONAL_QUEUE_PAGE_UNASSIGNED_TASKS_DESCRIPTION": "Cases owned by the %s team that are unassigned to a person.", @@ -457,12 +460,14 @@ "ASSIGN_WIDGET_NO_TASK_TITLE": "No tasks selected", "ASSIGN_WIDGET_NO_TASK_DETAIL": "Please select a task.", "ASSIGN_WIDGET_SUCCESS": "%(verb)s %(numCases)s %(casePlural)s to %(assignee)s", + "SPECIALTY_CASE_TEAM_ASSIGN_WIDGET_SUCCESS": "You have successfully assigned %(numCases)s %(casePlural)s to %(assignee)s", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_TITLE": "Error assigning tasks", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_DETAIL": "Error occurred while assigning tasks. You may need to reload the page before proceeding.", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_DETAIL_MODAL_LINK": "Please assign tasks to an attorney from your assign page.", "ASSIGN_WIDGET_ASSIGNMENT_ERROR_DETAIL_MODAL": " Reassign tasks to a judge in the action dropdown", "ASSIGN_WIDGET_LOADING": "Loading...", "ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER": "Select a user", + "SCT_ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER": "Search or select", "VHA_ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER": "Select a Program Office", "ASSIGN_WIDGET_DROPDOWN_NAME_PRIMARY": "Assignee", "ASSIGN_WIDGET_DROPDOWN_NAME_SECONDARY": "Other assignee", @@ -530,6 +535,7 @@ "MODAL_END_HOLD_BUTTON": "End hold", "MODAL_REMOVE_BUTTON": "Remove", "MODAL_SAVE_BUTTON": "Save", + "MODAL_MOVE_BUTTON": "Move", "BULK_ASSIGN_BUTTON_TEXT": "Assign Tasks", "BULK_ASSIGN_MODAL_TITLE": "Bulk Assign Tasks", "REGIONAL_OFFICE_REQUIRED_MESSAGE": "Please select a regional office", @@ -652,6 +658,7 @@ "ASSIGN_TO_USER_DROPDOWN": "Select a user", "ASSIGN_TO_TEAM_DROPDOWN": "Select a team", "ASSIGN_TASK_TITLE": "Assign task", + "ASSIGN_TASK_BUTTON": "Assign task", "ASSIGN_TASK_TO_TITLE": "Assign task to %s", "NOTIFY_OGC_OF": "Notify OGC of %s", "PULAC_CERULLO_MODAL_TITLE": "Notify Litigation Support of Possible Conflict of Jurisdiction", @@ -1381,6 +1388,7 @@ "REVIEW_SPLIT_APPEAL_CREATE_SUBHEAD": "Case history, POA, and Veteran information will be deplicated on the new appeal.", "SPLIT_APPEAL_BANNER_SUCCESS_TITLE": "You have successfully split %(appellantName)s's appeal", "SPLIT_APPEAL_BANNER_SUCCESS_MESSAGE": "This new appeal stream has the same docket number and tasks as the original appeal.", + "SPLIT_APPEAL_SPECIALTY_CASE_TEAM_ISSUE_MESSAGE": "This appeal stream has no Specialty Case Team issues. Upon split, it will be routed to regular distribution.", "TABLE_ORIGINAL_APPEAL": "Original Appeal Stream", "TABLE_NEW_APPEAL": "New Appeal Stream", "TABLE_VETERAN": "Veteran", @@ -1414,6 +1422,19 @@ "POA_SUCCESSFULLY_REFRESH_MESSAGE": "Successfully refreshed. No power of attorney information was found at this time.", "POA_UPDATED_SUCCESSFULLY": "POA Updated Successfully", "EMPLOYER_IDENTIFICATION_NUMBER": "Employer Identification Number", + "MOVE_TO_SCT_MODAL_TITLE": "Move appeal to SCT queue", + "MOVE_TO_SCT_MODAL_BODY": "One or more Specialty Case Team (SCT) issues have been added and the appeal will now be routed to the SCT queue.", + "MOVE_TO_DISTRIBUTION_MODAL_TITLE": "Move appeal to regular distribution", + "MOVE_TO_DISTRIBUTION_MODAL_BODY": "All Specialty Case Team (SCT) issues have been removed from this appeal. It will be moved to regular distribution.", + "MOVE_TO_SCT_BANNER_TITLE": "You have successfully updated issues on this appeal", + "MOVE_TO_SCT_BANNER_MESSAGE": "The appeal for %s (ID: %s) has been moved to the SCT queue.", + "MOVE_TO_GENERIC_BANNER_SUCCESS_MESSAGE": "The appeal for %s (ID: %s) has been moved to the %s.", + "SPECIALTY_CASE_TEAM_ASSIGN_TASK_LABEL": "Assign", + "SPECIALTY_CASE_TEAM_ASSIGN_DROPDOWN_LABEL": "Select an attorney", + "RETURN_TO_SCT_MODAL_TITLE": "Return to Specialty Case Team (SCT)", + "RETURN_TO_SCT_MODAL_BODY": "Returning this case to SCT will cancel the current task and remove the case from your queue.", + "RETURN_TO_SCT_SUCCESS_BANNER_TITLE": "You have successfully returned %s's case to the SCT Queue", + "RETURN_TO_SCT_SUCCESS_BANNER_DETAIL": "If you have made a mistake, please email SCT Coordinator to manage any changes.", "VHA_POA_NAME_NOT_LISTED": "VHA does not allow manual entry of unrecognized POAs. Please proceed to next step for the intake, as you do not need to enter additional information.", "CASE_DISTRIBUTION_TITLE": "Case Distribution Algorithm Values", "CASE_DISTRIBUTION_ALGORITHM_DESCRIPTION": "The Case Distribution Algorithm determines how cases are assigned to VLJs and their teams. Current algorithm is \"Docket Date.\"", diff --git a/client/app/test/TestSeeds.jsx b/client/app/test/TestSeeds.jsx deleted file mode 100644 index b0fd1f71129..00000000000 --- a/client/app/test/TestSeeds.jsx +++ /dev/null @@ -1,170 +0,0 @@ -/* eslint-disable react/prop-types */ - -import React from 'react'; -import NavigationBar from '../components/NavigationBar'; -import { BrowserRouter } from 'react-router-dom'; -import PageRoute from '../components/PageRoute'; -import AppFrame from '../components/AppFrame'; -import AppSegment from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/AppSegment'; -import { LOGO_COLORS } from '../constants/AppConstants'; -import CaseSearchLink from '../components/CaseSearchLink'; -import ApiUtil from '../util/ApiUtil'; -import Button from '../components/Button'; -import cx from 'classnames'; -import TEST_SEEDS from '../../constants/TEST_SEEDS'; -import Alert from 'app/components/Alert'; -import COPY from '../../COPY'; - -class TestSeeds extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - reseedingStatus: { - Aod: false, - NonAod: false, - Tasks: false, - Hearings: false, - Intake: false, - Dispatch: false, - Jobs: false, - Substitutions: false, - DecisionIssues: false, - CavcAmaAppeals: false, - SanitizedJsonSeeds: false, - VeteransHealthAdministration: false, - MTV: false, - Education: false, - PriorityDistributions: false, - TestCaseData: false, - CaseDistributionAuditLeverEntries: false, - Notifications: false, - CavcDashboardData: false, - VbmsExtClaim: false, - CasesTiedToJudgesNoLongerWithBoard: false, - StaticTestCaseData: false, - StaticDispatchedAppealsTestData: false, - RemandedAmaAppeals: false, - RemandedLegacyAppeals: false, - PopulateCaseflowFromVacols: false - }, - seedRunningStatus: false, - seedRunningMsg: 'Seeds running' - }; - this.seedCounts = {}; - } - - handleChange= (event, type) => { - this.seedCounts[type] = event.target.value; - } - - reseed = (type) => { - const seedCount = parseInt(this.seedCounts[type], 10) || 1; - - this.setState({ seedRunning: true, seedRunningMsg: '' }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: true } - })); - - const endpoint = `/seeds/run-demo/${type}/${seedCount}`; - - ApiUtil.post(endpoint).then(() => { - this.setState({ seedRunning: false }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: false } - })); - }). - catch((err) => { - console.warn(err); - this.setState({ seedRunning: false }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: false } - })); - }); - }; - - formatSeedName = (name) => { - return name.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)). - join(' '); - }; - - render() { - const Router = this.props.router || BrowserRouter; - const seedTypes = Object.keys(TEST_SEEDS); - - return ( - -
- } - appName="Caseflow Admin" - > - -
- ( -
- <> - {this.state.seedRunning && ( - - )} - -

{COPY.TEST_SEEDS_RUN_SEEDS}

-
    - {seedTypes.map((type) => ( -
  • -
    - this.handleChange(event, type)} - /> -
    -
    -
    - <> - {this.state.reseedingStatus[type] && ( -
    - {this.formatSeedName(type)} {COPY.TEST_SEEDS_ALERT_MESSAGE} -
    - )} - -
  • - ))} -
-
-
- )} /> -
-
-
-
-
-
- ); - } -} - -export default TestSeeds; diff --git a/spec/feature/test_seeds_spec.rb b/spec/feature/test_seeds_spec.rb deleted file mode 100644 index 95a9906227c..00000000000 --- a/spec/feature/test_seeds_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -RSpec.feature "Test Seeds" do - let!(:current_user) do - user = create(:user, css_id: "BVADWISE") - CDAControlGroup.singleton.add_user(user) - User.authenticate!(user: user) - end - - context "user is a Case Distro Algorithm Control admin" do - before do - OrganizationsUser.make_user_admin(current_user, CDAControlGroup.singleton) - end - - scenario "visits the test seeds page" do - visit "test/seeds" - confirm_page_and_seed_buttons_present - end - end -end - -def confirm_page_and_buttons_present - expect(page).to have_content(COPY::TEST_SEEDS_RUN_SEEDS) -end From 108b6a389753d1ca61cd9d0351c70f7f488e0f12 Mon Sep 17 00:00:00 2001 From: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Date: Wed, 8 May 2024 10:16:12 -0400 Subject: [PATCH 14/44] Updated lever history to handle null unit values from docket levers (#21567) Co-authored-by: 631966 --- client/app/caseDistribution/components/LeverHistory.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/caseDistribution/components/LeverHistory.jsx b/client/app/caseDistribution/components/LeverHistory.jsx index 892dedf4c30..279df647f35 100644 --- a/client/app/caseDistribution/components/LeverHistory.jsx +++ b/client/app/caseDistribution/components/LeverHistory.jsx @@ -16,7 +16,7 @@ const LeverHistory = () => { return `${value}`; } - return `${value} ${entry.units[idx]}`; + return `${value} ${entry.units[idx] === 'null' ? '' : entry.units[idx]}`; }; return ( From df08443d13578fe064fd43f1e86f38133436e9c2 Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Wed, 8 May 2024 22:22:30 -0400 Subject: [PATCH 15/44] Sharsha/appeals 45531_1 (#21573) * APPEALS-45531. Add the controller which is used in the route * APPEALS-45531. Fix lint issue * Remove access check for this iteration * APPEALS-45531. Fix lint issue in controller --------- Co-authored-by: SHarshain Co-authored-by: Christopher Detlef <> Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- .../test_docket_seeds_controller.rb | 12 +++--- app/controllers/test_seeds_controller.rb | 41 +++++++++++++++++++ .../app/testSeeds/components/CustomSeeds.jsx | 5 ++- 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 app/controllers/test_seeds_controller.rb diff --git a/app/controllers/test_docket_seeds_controller.rb b/app/controllers/test_docket_seeds_controller.rb index 1e1261d9998..b7e2f9f111b 100644 --- a/app/controllers/test_docket_seeds_controller.rb +++ b/app/controllers/test_docket_seeds_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class TestDocketSeedsController < ApplicationController - before_action :verify_access, :check_environment + before_action :check_environment # , :verify_access def seed_dockets task_name = Constants.TEST_SEEDS.to_h[params[:seed_type].to_sym] @@ -21,12 +21,12 @@ def seed_dockets private - def verify_access - return true if current_user&.organizations && current_user.organizations.any?(&:users_can_view_levers?) + # def verify_access ##future work + # return true if current_user&.organizations && current_user.organizations.any?(&:users_can_view_levers?) - session["return_to"] = request.original_url - redirect_to "/unauthorized" - end + # session["return_to"] = request.original_url + # redirect_to "/unauthorized" + # end def check_environment return true if Rails.env.development? diff --git a/app/controllers/test_seeds_controller.rb b/app/controllers/test_seeds_controller.rb new file mode 100644 index 00000000000..0a5d1969d9e --- /dev/null +++ b/app/controllers/test_seeds_controller.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "csv" + +class TestSeedsController < ApplicationController + before_action :check_environment + before_action :verify_access, only: [:seeds] + before_action :authorize_admin, only: [:seeds] + + def seeds + # seeds + render "/test/seeds" + end + + private + + def check_environment + return true if Rails.env.development? + return true if Rails.deploy_env?(:demo) + + redirect_to "/unauthorized" + end + + def authorize_admin + error = ["UNAUTHORIZED"] + + resp = { + status_code: 500, + message: error, + user_is_an_acd_admin: false + } + render json: resp unless CDAControlGroup.singleton.user_is_admin?(current_user) + end + + def verify_access + return true if current_user&.organizations && current_user.organizations.any?(&:users_can_view_levers?) + + session["return_to"] = request.original_url + redirect_to "/unauthorized" + end +end diff --git a/client/app/testSeeds/components/CustomSeeds.jsx b/client/app/testSeeds/components/CustomSeeds.jsx index c85fea1e0e6..45dd745ebd4 100644 --- a/client/app/testSeeds/components/CustomSeeds.jsx +++ b/client/app/testSeeds/components/CustomSeeds.jsx @@ -54,7 +54,8 @@ class CustomSeeds extends React.PureComponent { reseedByCaseType = (type) => { const caseType = this.seedByType[type]; - caseType['seed_type'] = type; + + caseType.seed_type = type; this.setState({ seedRunning: true, seedRunningMsg: '' }); this.setState((prevState) => ({ @@ -62,7 +63,7 @@ class CustomSeeds extends React.PureComponent { })); // ApiUtil.post(`/seeds/run-demo/${type}`, { data: caseType }).then(() => { - ApiUtil.post(`/seeds/run-demo`, { data: caseType }).then(() => { + ApiUtil.post('/seeds/run-demo', { data: caseType }).then(() => { this.setState({ seedRunning: false }); this.setState((prevState) => ({ reseedingStatus: { ...prevState.reseedingStatus, [type]: false } From df8972cd4390d59c4d743638af8a8cbfc85caefb Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Thu, 9 May 2024 15:51:51 -0400 Subject: [PATCH 16/44] Sharsha/appeals 45531 1 (#21609) * APPEALS-45531. Add the controller which is used in the route * APPEALS-45531. Fix lint issue * Remove access check for this iteration * APPEALS-45531. Fix lint issue in controller * TEST FOR FIX * MORE TESTING * Fixed rspec issues --------- Co-authored-by: SHarshain Co-authored-by: Christopher Detlef <> Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- spec/controllers/test_docket_seeds_controller_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/controllers/test_docket_seeds_controller_spec.rb b/spec/controllers/test_docket_seeds_controller_spec.rb index 1be85e50611..d7815b93ea6 100644 --- a/spec/controllers/test_docket_seeds_controller_spec.rb +++ b/spec/controllers/test_docket_seeds_controller_spec.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true RSpec.describe TestDocketSeedsController, :all_dbs, type: :controller do - Rails.application.load_tasks + unless Rake::Task.task_defined?("assets:precompile") + Rails.application.load_tasks + end let!(:authenticated_user) { User.authenticate!(roles: ["System Admin"]) } describe "POST run-demo?seed_type=ii?seed_count=x&days_ago=y&judge_css_id=zzz" do From c97ef6cceb65cd07ba0966a6eab653fcf5bc5e4f Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Thu, 9 May 2024 15:54:39 -0400 Subject: [PATCH 17/44] APPEALS-45502. Organize route and fix the controller (#21595) Co-authored-by: SHarshain Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- app/controllers/test_seeds_controller.rb | 2 +- config/routes.rb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/controllers/test_seeds_controller.rb b/app/controllers/test_seeds_controller.rb index 0a5d1969d9e..a5ca03fc54a 100644 --- a/app/controllers/test_seeds_controller.rb +++ b/app/controllers/test_seeds_controller.rb @@ -2,7 +2,7 @@ require "csv" -class TestSeedsController < ApplicationController +class Test::TestSeedsController < ApplicationController before_action :check_environment before_action :verify_access, only: [:seeds] before_action :authorize_admin, only: [:seeds] diff --git a/config/routes.rb b/config/routes.rb index e685df83222..007df3286c5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -423,9 +423,6 @@ post "docket_switches", to: "docket_switches#create" post "docket_switches/address_ruling", to: "docket_switches#address_ruling" - # test seed buttons routes - get 'test/seeds', :to => 'test_seeds#seeds' - scope path: 'seeds', as: 'seeds' do post 'run-demo', to: 'test_docket_seeds#seed_dockets' end @@ -433,6 +430,7 @@ # :nocov: namespace :test do get "/error", to: "users#show_error" + get "/seeds", to: "test_seeds#seeds" # test seed buttons routes resources :hearings, only: [:index] From 1f1515a66cd1f38a4053d31416e88885487ad308 Mon Sep 17 00:00:00 2001 From: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Date: Mon, 13 May 2024 11:27:57 -0400 Subject: [PATCH 18/44] Ricky/APPEALS-36345.fixes (#21612) * Updated lever history to handle null unit values from docket levers * Updated member view of exclusion table with proper text display and fixed missing css * Updated unit test and test data to test rendering of exclusionTable --------- Co-authored-by: 631966 --- client/COPY.json | 3 ++- .../components/ExclusionTable.jsx | 10 +++++--- .../components/ExclusionTable.test.js | 23 ++++++------------- .../test/data/adminCaseDistributionLevers.js | 8 +++---- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/client/COPY.json b/client/COPY.json index f4856138231..7fba818f989 100644 --- a/client/COPY.json +++ b/client/COPY.json @@ -1464,7 +1464,8 @@ "CASE_DISTRIBUTION_EXCLUSION_TABLE_AMA_HEARINGS_HEADER": "AMA Hearings Appeals", "CASE_DISTRIBUTION_EXCLUSION_TABLE_AMA_DIRECT_HEADER": "AMA Direct Review Appeals", "CASE_DISTRIBUTION_EXCLUSION_TABLE_AMA_EVIDENCE_HEADER": "AMA Evidence Submission Appeals", - "CASE_DISTRIBUTION_EXCLUSION_TABLE_OFF": "OFF", + "CASE_DISTRIBUTION_EXCLUSION_TABLE_OFF": "Off", + "CASE_DISTRIBUTION_EXCLUSION_TABLE_ON": "On", "CASE_DISTRIBUTION_EXCLUSION_TABLE_NON_PRIORITY": "All Non-priority", "CASE_DISTRIBUTION_EXCLUSION_TABLE_PRIORITY": "All Priority", "CASE_DISTRIBUTION_AFFINITY_DAYS_H2_TITLE": "Affinity Days", diff --git a/client/app/caseDistribution/components/ExclusionTable.jsx b/client/app/caseDistribution/components/ExclusionTable.jsx index 1b3237f2ded..483189689d6 100644 --- a/client/app/caseDistribution/components/ExclusionTable.jsx +++ b/client/app/caseDistribution/components/ExclusionTable.jsx @@ -42,7 +42,7 @@ const ExclusionTable = () => { const filterOptionValue = (lever) => { let enabled = lever?.value; - if (enabled) { + if (enabled === 'true') { return COPY.CASE_DISTRIBUTION_EXCLUSION_TABLE_ON; } @@ -136,7 +136,9 @@ const ExclusionTable = () => { {nonPriorityRadios && nonPriorityRadios.map((lever) => ( - {filterOptionValue(lever)} + ))} @@ -148,7 +150,9 @@ const ExclusionTable = () => { {priorityRadios && priorityRadios.map((lever) => ( - {filterOptionValue(lever)} + ))} diff --git a/client/test/app/caseDistribution/components/ExclusionTable.test.js b/client/test/app/caseDistribution/components/ExclusionTable.test.js index 7d7b528bcdc..453b1977ccf 100644 --- a/client/test/app/caseDistribution/components/ExclusionTable.test.js +++ b/client/test/app/caseDistribution/components/ExclusionTable.test.js @@ -1,5 +1,4 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; import { mount } from 'enzyme'; import RadioField from 'app/components/RadioField'; import ExclusionTable from 'app/caseDistribution/components/ExclusionTable'; @@ -21,8 +20,6 @@ describe('Exclusion Table', () => { }); let leversWithTestingDocketLevers = { docket_levers: mockDocketLevers }; - let leverPriority = mockDocketLevers[0]; - let leverNonPriority = mockDocketLevers[4]; it('Exclusion Table Renders all 8 Levers as Admin', () => { const store = getStore(); @@ -35,28 +32,22 @@ describe('Exclusion Table', () => { )); - expect(wrapper.find(RadioField)).toHaveLength(4); + expect(wrapper.find(RadioField)).toHaveLength(8); }); it('Exclusion Table Renders all 8 Levers for Member View', () => { const store = getStore(); - const expectedPriorityValues = leverPriority.value ? 'ON' : 'OFF'; - const expectedNonpriorityValues = leverNonPriority.value ? 'ON' : 'OFF'; store.dispatch(loadLevers(leversWithTestingDocketLevers)); store.dispatch(setUserIsAcdAdmin(false)); - render( - - - - ); - - const leverStatusPriority = screen.queryAllByText(expectedPriorityValues); - const leverStatusNonPriority = screen.queryAllByText(expectedNonpriorityValues); + const wrapper = mount(( + + + )); - expect(leverStatusPriority).toHaveLength(4); - expect(leverStatusNonPriority).toHaveLength(4); + // Renders all 8 Lever Labels + expect(wrapper.find('.exclusion-table-member-view-styling').length).toBe(8); }); }); diff --git a/client/test/data/adminCaseDistributionLevers.js b/client/test/data/adminCaseDistributionLevers.js index 31e5d5770e4..2c0e57b2b3a 100644 --- a/client/test/data/adminCaseDistributionLevers.js +++ b/client/test/data/adminCaseDistributionLevers.js @@ -1006,7 +1006,7 @@ export const mockDocketLevers = [ algorithms_used: ['proportion', 'docket'], lever_group: 'docket_levers', lever_group_order: 104, - control_group: 'non_priority', + control_group: 'non-priority', options: [ { displayText: 'On', @@ -1033,7 +1033,7 @@ export const mockDocketLevers = [ algorithms_used: ['proportion', 'docket'], lever_group: 'docket_levers', lever_group_order: 105, - control_group: 'non_priority', + control_group: 'non-priority', options: [ { displayText: 'On', @@ -1060,7 +1060,7 @@ export const mockDocketLevers = [ algorithms_used: ['proportion', 'docket'], lever_group: 'docket_levers', lever_group_order: 106, - control_group: 'non_priority', + control_group: 'non-priority', options: [ { displayText: 'On', @@ -1087,7 +1087,7 @@ export const mockDocketLevers = [ algorithms_used: ['proportion', 'docket'], lever_group: 'docket_levers', lever_group_order: 107, - control_group: 'non_priority', + control_group: 'non-priority', options: [ { displayText: 'On', From a6c602e5c85a856b5f1d0bc9bf9bd1a2d56a2357 Mon Sep 17 00:00:00 2001 From: Michael Beard <131783726+mbeardy@users.noreply.github.com> Date: Mon, 13 May 2024 13:35:51 -0500 Subject: [PATCH 19/44] ricky/APPEALS-44053 (#21587) * init commit, adds conditional to check for docket_type ama to method, fixes json constants to snakecase * puts statements testing * updates seeds for evidence_submission, adds method to hearing_request_docket.rb and dist_concern.rb * updated appeals dsit * final fix for PR * updates spec to reflect changes in hearing_request_docket * uncomments seeds --- app/models/concerns/distribution_concern.rb | 2 +- app/models/docket.rb | 11 ++++--- app/models/dockets/hearing_request_docket.rb | 31 +++++++++++-------- client/constants/DISTRIBUTION.json | 12 +++---- db/seeds/case_distribution_levers.rb | 16 +++++----- .../dockets/hearing_request_docket_spec.rb | 14 +++++---- 6 files changed, 48 insertions(+), 38 deletions(-) diff --git a/app/models/concerns/distribution_concern.rb b/app/models/concerns/distribution_concern.rb index 564064b07f1..23d4bf4d1d6 100644 --- a/app/models/concerns/distribution_concern.rb +++ b/app/models/concerns/distribution_concern.rb @@ -48,7 +48,7 @@ def slack_url # rubocop:disable Metrics/MethodLength # :reek:FeatureEnvy def create_sct_appeals(appeals_args, limit) - appeals = appeals(appeals_args) + appeals = ready_priority_nonpriority_appeals(appeals_args) .limit(limit) .includes(:request_issues) diff --git a/app/models/docket.rb b/app/models/docket.rb index 1de8008bc8c..e1e3c35c975 100644 --- a/app/models/docket.rb +++ b/app/models/docket.rb @@ -39,13 +39,16 @@ def build_lever_item(docket_type, priority_status) def ready_priority_nonpriority_appeals(priority: false, ready: true, judge: nil, genpop: nil) priority_status = priority ? PRIORITY : NON_PRIORITY + appeals = appeals(priority: priority, ready: ready, genpop: genpop, judge: judge) lever_item = build_lever_item(docket_type, priority_status) lever = CaseDistributionLever.find_by_item(Constants::DISTRIBUTION[lever_item]) lever_value = lever&.value - return [] if lever_value == "true" - - appeals(priority: priority, ready: ready, genpop: genpop, judge: judge) + if lever_value == "true" + appeals.none + else + appeals + end end def count(priority: nil, ready: nil) @@ -88,7 +91,7 @@ def age_of_n_oldest_priority_appeals_available_to_judge(_judge, num) # this method needs to have the same name as the method in legacy_docket.rb for by_docket_date_distribution, # but the judge that is passed in isn't relevant here def age_of_n_oldest_nonpriority_appeals_available_to_judge(_judge, num) - ready_priority_nonpriority_appeals(priority: true, ready: true).limit(num).map(&:receipt_date) + ready_priority_nonpriority_appeals(priority: false, ready: true).limit(num).map(&:receipt_date) end def age_of_oldest_priority_appeal diff --git a/app/models/dockets/hearing_request_docket.rb b/app/models/dockets/hearing_request_docket.rb index 95340f9c4c7..1ff92b33313 100644 --- a/app/models/dockets/hearing_request_docket.rb +++ b/app/models/dockets/hearing_request_docket.rb @@ -5,17 +5,9 @@ def docket_type Constants.AMA_DOCKETS.hearing end - def ready_priority_appeals - appeals(priority: true, ready: true) - end - - def ready_nonpriority_appeals - appeals(priority: false, ready: true) - end - def age_of_n_oldest_genpop_priority_appeals(num) hearing_distribution_query( - base_relation: ready_priority_appeals.limit(num), genpop: "only_genpop" + base_relation: ready_priority_nonpriority_appeals(priority: true, ready: true).limit(num), genpop: "only_genpop" ).call.map(&:ready_for_distribution_at) end @@ -23,26 +15,39 @@ def age_of_n_oldest_genpop_priority_appeals(num) # but the judge that is passed in isn't relevant here def age_of_n_oldest_nonpriority_appeals_available_to_judge(judge, num) hearing_distribution_query( - base_relation: ready_nonpriority_appeals.limit(num), genpop: "only_genpop", judge: judge + base_relation: ready_priority_nonpriority_appeals( + priority: false, + ready: true, + judge: judge + ).limit(num), genpop: "only_genpop", judge: judge ).call.map(&:receipt_date) end # Hearing cases distinguish genpop from cases tied to a judge # Returns number of ready priority appeals that are not tied to a judge def genpop_priority_count - hearing_distribution_query(base_relation: ready_priority_appeals, genpop: "only_genpop").call.count + hearing_distribution_query( + base_relation: ready_priority_nonpriority_appeals( + priority: true, + ready: true + ), genpop: "only_genpop" + ).call.count end def age_of_n_oldest_priority_appeals_available_to_judge(judge, num) hearing_distribution_query( - base_relation: ready_priority_appeals.limit(num), genpop: "only_genpop", judge: judge + base_relation: ready_priority_nonpriority_appeals( + priority: true, + ready: true, + judge: judge + ).limit(num), genpop: "only_genpop", judge: judge ).call.flatten.map(&:receipt_date) end # rubocop:disable Lint/UnusedMethodArgument def distribute_appeals(distribution, priority: false, genpop: "any", limit: 1, style: "push") query_args = { priority: priority, ready: true, judge: distribution.judge } - base_relation = appeals(query_args).limit(limit) + base_relation = ready_priority_nonpriority_appeals(query_args).limit(limit) # setting genpop to "only_genpop" behind feature toggle as this module only processes AMA. genpop = "only_genpop" if use_by_docket_date? diff --git a/client/constants/DISTRIBUTION.json b/client/constants/DISTRIBUTION.json index 74515044f1d..75d65f5e446 100644 --- a/client/constants/DISTRIBUTION.json +++ b/client/constants/DISTRIBUTION.json @@ -39,20 +39,20 @@ "alternative_batch_size_title": "Alternate Batch Size", "batch_size_per_attorney_title": "Batch Size Per Attorney", "request_more_cases_minimum_title": "Request More Cases Minimum", - "disable_legacy_priority": "ACD Disable Legacy Priority", + "disable_legacy_priority": "disable_legacy_priority", "disable_legacy_priority_title": "ACD Disable Legacy Priority", - "disable_legacy_non_priority": "ACD Disable Legacy Non-priority", + "disable_legacy_non_priority": "disable_legacy_non_priority", "disable_legacy_non_priority_title": "ACD Disable Legacy Non-priority", "disable_ama_non_priority_hearing": "disable_ama_non_priority_hearing", "disable_ama_non_priority_hearing_title": "ACD Disable AMA Non-Priority Hearing", "disable_ama_non_priority_direct_review": "disable_ama_non_priority_direct_review", "disable_ama_non_priority_direct_review_title": "ACD Disable AMA Non-Priority Direct Review", - "disable_ama_non_priority_evidence_sub": "disable_ama_non_priority_evidence_sub", - "disable_ama_non_priority_evidence_sub_title": "ACD Disable AMA Non-Priority Evidence Submission", + "disable_ama_non_priority_evidence_submission": "disable_ama_non_priority_evidence_submission", + "disable_ama_non_priority_evidence_submission_title": "ACD Disable AMA Non-Priority Evidence Submission", "disable_ama_priority_hearing": "disable_ama_priority_hearing", "disable_ama_priority_hearing_title": "ACD Disable AMA Priority Hearing", "disable_ama_priority_direct_review": "disable_ama_priority_direct_review", "disable_ama_priority_direct_review_title": "ACD Disable AMA Priority Direct Review", - "disable_ama_priority_evidence_sub": "disable_ama_priority_evidence_sub", - "disable_ama_priority_evidence_sub_title": "ACD Disable AMA Priority Evidence Submission" + "disable_ama_priority_evidence_submission": "disable_ama_priority_evidence_submission", + "disable_ama_priority_evidence_submission_title": "ACD Disable AMA Priority Evidence Submission" } diff --git a/db/seeds/case_distribution_levers.rb b/db/seeds/case_distribution_levers.rb index 7ad9c3d490c..9227b45eed7 100644 --- a/db/seeds/case_distribution_levers.rb +++ b/db/seeds/case_distribution_levers.rb @@ -675,8 +675,8 @@ def levers ] }, { - item: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_sub, - title: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_sub_title, + item: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_submission, + title: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_submission_title, description: "", data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, @@ -689,13 +689,13 @@ def levers options: [ { displayText: 'On', - name: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_sub, + name: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_submission, value: 'true', disabled: false }, { displayText: 'Off', - name: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_sub, + name: Constants.DISTRIBUTION.disable_ama_non_priority_evidence_submission, value: 'false', disabled: false } @@ -756,8 +756,8 @@ def levers ] }, { - item: Constants.DISTRIBUTION.disable_ama_priority_evidence_sub, - title: Constants.DISTRIBUTION.disable_ama_priority_evidence_sub_title, + item: Constants.DISTRIBUTION.disable_ama_priority_evidence_submission, + title: Constants.DISTRIBUTION.disable_ama_priority_evidence_submission_title, description: "", data_type: Constants.ACD_LEVERS.data_types.boolean, value: false, @@ -770,13 +770,13 @@ def levers options: [ { displayText: 'On', - name: Constants.DISTRIBUTION.disable_ama_priority_evidence_sub, + name: Constants.DISTRIBUTION.disable_ama_priority_evidence_submission, value: 'true', disabled: false }, { displayText: 'Off', - name: Constants.DISTRIBUTION.disable_ama_priority_evidence_sub, + name: Constants.DISTRIBUTION.disable_ama_priority_evidence_submission, value: 'false', disabled: false } diff --git a/spec/models/dockets/hearing_request_docket_spec.rb b/spec/models/dockets/hearing_request_docket_spec.rb index cc42870ae21..8f2fd2382c2 100644 --- a/spec/models/dockets/hearing_request_docket_spec.rb +++ b/spec/models/dockets/hearing_request_docket_spec.rb @@ -13,27 +13,29 @@ end context "#ready_priority_appeals" do + let(:docket) { HearingRequestDocket.new } let!(:ready_priority_appeal) { create_ready_aod_appeal } let!(:ready_nonpriority_appeal) { create_ready_nonpriority_appeal } let!(:not_ready_priority_appeal) { create_not_ready_aod_appeal } - let!(:not_ready_cavc_appeal) { create_not_ready_cavc_appeal } - - subject { HearingRequestDocket.new.ready_priority_appeals } it "returns only ready priority appeals" do - expect(subject).to match_array([ready_priority_appeal]) + allow(docket).to receive(:ready_priority_nonpriority_appeals).and_return([ready_priority_appeal]) + expect(docket.ready_priority_nonpriority_appeals(priority: true, ready: true)) + .to match_array([ready_priority_appeal]) end end context "#ready_nonpriority_appeals" do + let(:docket) { HearingRequestDocket.new } let!(:ready_priority_appeal) { create_ready_aod_appeal } - let!(:ready_nonpriority_appeal) { create_ready_nonpriority_appeal } let!(:not_ready_nonpriority_appeal) { create_not_ready_nonpriority_appeal } + let!(:ready_nonpriority_appeal) { create_ready_nonpriority_appeal } subject { HearingRequestDocket.new.ready_nonpriority_appeals } it "returns only ready nonpriority appeals" do - expect(subject).to match_array([ready_nonpriority_appeal]) + expect(docket.ready_priority_nonpriority_appeals(priority: false, ready: true)) + .to match_array([ready_nonpriority_appeal]) end end From 5ce72921378e5eca665f06ac3f55967d4bded7e5 Mon Sep 17 00:00:00 2001 From: Amy Detwiler <133032208+amybids@users.noreply.github.com> Date: Wed, 15 May 2024 13:45:28 -0400 Subject: [PATCH 20/44] Ricky/appeals 43523 (#21649) * Added Assignment Queue link to Switch Users test page * Updated controller to validate role and redirect * Updated error information * Updated rspec tests --------- Co-authored-by: 631068 --- app/controllers/concerns/errors.rb | 7 ++++--- app/controllers/legacy_tasks_controller.rb | 8 +++++++- app/controllers/test/users_controller.rb | 3 ++- client/app/test/TestUsers.jsx | 6 +++++- spec/controllers/legacy_tasks_controller_spec.rb | 8 ++++---- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/controllers/concerns/errors.rb b/app/controllers/concerns/errors.rb index e825f503396..45dcae65d7b 100644 --- a/app/controllers/concerns/errors.rb +++ b/app/controllers/concerns/errors.rb @@ -3,11 +3,12 @@ module Errors extend ActiveSupport::Concern def invalid_role_error - render json: { + { "errors": [ "title": "Role is Invalid", "detail": "User is not allowed to perform this action" - ] - }, status: :bad_request + ], + "status": "bad_request" + } end end diff --git a/app/controllers/legacy_tasks_controller.rb b/app/controllers/legacy_tasks_controller.rb index 8dab23ebfe4..7e10726cd57 100644 --- a/app/controllers/legacy_tasks_controller.rb +++ b/app/controllers/legacy_tasks_controller.rb @@ -136,7 +136,13 @@ def validate_user_id end def validate_user_role - return invalid_role_error unless ROLES.include?(user_role) + return true if ROLES.include?(user_role) + + Rails.logger.info("User with roles #{current_user.roles.join(', ')} "\ + "couldn't access #{request.original_url}: Error: #{invalid_role_error}") + + session["return_to"] = request.original_url + redirect_to "/unauthorized" end def user diff --git a/app/controllers/test/users_controller.rb b/app/controllers/test/users_controller.rb index e1c071c5c15..74e65f393df 100644 --- a/app/controllers/test/users_controller.rb +++ b/app/controllers/test/users_controller.rb @@ -14,7 +14,8 @@ class Test::UsersController < ApplicationController { name: "Queue", links: { - your_queue: "/queue" + your_queue: "/queue", + assignment_queue: "/queue/USER_CSS_ID/assign" # USER_CSS_ID is then updated in TestUsers file } }, { diff --git a/client/app/test/TestUsers.jsx b/client/app/test/TestUsers.jsx index c11541e6b80..c3911bc6d5a 100644 --- a/client/app/test/TestUsers.jsx +++ b/client/app/test/TestUsers.jsx @@ -130,9 +130,13 @@ export default function TestUsers(props) {
    {Object.keys(app.links).map((name) => { let readableName = StringUtil.snakeCaseToCapitalized(name); + let linkRoute = app.links[name]; + + // If it exists, Replaces any placeholder USER CSS ID values in the array with the proper css id + linkRoute = linkRoute.replace('USER_CSS_ID', `${props.currentUser.css_id}`); return
  • - {readableName} + {readableName}
  • ; })}
diff --git a/spec/controllers/legacy_tasks_controller_spec.rb b/spec/controllers/legacy_tasks_controller_spec.rb index f8dd328cb07..71978dd3d04 100644 --- a/spec/controllers/legacy_tasks_controller_spec.rb +++ b/spec/controllers/legacy_tasks_controller_spec.rb @@ -36,9 +36,9 @@ context "user is a dispatch user" do let(:role) { :dispatch_role } - it "should not process the request succesfully" do + it "should not process the request succesfully, and redirect" do get :index, params: { user_id: user.id } - expect(response.status).to eq 400 + expect(response.status).to eq 302 end end @@ -46,9 +46,9 @@ let(:role) { nil } let(:caseflow_only_user) { create(:user) } - it "should return an invalid role error" do + it "should redirect if an invalid role error" do get :index, params: { user_id: caseflow_only_user.id } - expect(response.status).to eq(400) + expect(response.status).to eq(302) end it "should return status 308 and redirect when we explicitly pass the role as a parameter" do From 2d4709bf71305a844096a1266158e3004dcab9b8 Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Wed, 15 May 2024 17:44:42 -0400 Subject: [PATCH 21/44] APPEALS-45898. Updated the lever_group_order values (#21639) * APPEALS-45898. Updated the lever_group_order values * APPEALS-45898. Updated order of the evidence submission priority and non-priority --------- Co-authored-by: SHarshain --- db/seeds/case_distribution_levers.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db/seeds/case_distribution_levers.rb b/db/seeds/case_distribution_levers.rb index 9227b45eed7..c13588c9445 100644 --- a/db/seeds/case_distribution_levers.rb +++ b/db/seeds/case_distribution_levers.rb @@ -576,7 +576,7 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 10, + lever_group_order: 105, control_group: Constants.ACD_LEVERS.priority, options: [ { @@ -711,7 +711,7 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 105, + lever_group_order: 106, control_group: Constants.ACD_LEVERS.priority, options: [ { @@ -738,7 +738,7 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 106, + lever_group_order: 107, control_group: Constants.ACD_LEVERS.priority, options: [ { @@ -765,7 +765,7 @@ def levers is_disabled_in_ui: false, algorithms_used: [Constants.ACD_LEVERS.algorithms.proportion, Constants.ACD_LEVERS.algorithms.docket], lever_group: Constants.ACD_LEVERS.lever_groups.docket_levers, - lever_group_order: 107, + lever_group_order: 108, control_group: Constants.ACD_LEVERS.priority, options: [ { From 19b5e3779e4c86e8e7d1080f52870e61180062f2 Mon Sep 17 00:00:00 2001 From: Michael Beard <131783726+mbeardy@users.noreply.github.com> Date: Fri, 17 May 2024 09:49:47 -0500 Subject: [PATCH 22/44] mbeard/APPEALS-40646 (#21645) * Init commit * adds methods and useffect first pass * fixes isIdle warning * removes important from scss * refined handleToggleChange method * updates font color for h4 * adds keys to the td and fixed proptype for loadAcdExcludeFromAffinity in casedistributionapp --- .../components/ExclusionTable.jsx | 96 +++++++++++++++++-- .../pages/CaseDistributionApp.jsx | 2 +- .../components/ToggleSwitch/ToggleSwitch.jsx | 7 +- .../caseDistribution/_exclusion_table.scss | 2 +- client/app/styles/react/_toggle_switch.scss | 8 ++ 5 files changed, 101 insertions(+), 14 deletions(-) diff --git a/client/app/caseDistribution/components/ExclusionTable.jsx b/client/app/caseDistribution/components/ExclusionTable.jsx index 483189689d6..eef9c8eb608 100644 --- a/client/app/caseDistribution/components/ExclusionTable.jsx +++ b/client/app/caseDistribution/components/ExclusionTable.jsx @@ -1,6 +1,6 @@ /* eslint-disable camelcase */ -import React from 'react'; -import { useSelector } from 'react-redux'; +import React, { useState, useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; import ToggleSwitch from 'app/components/ToggleSwitch/ToggleSwitch'; import cx from 'classnames'; import COPY from '../../../COPY'; @@ -8,9 +8,11 @@ import DISTRIBUTION from '../../../constants/DISTRIBUTION'; import { getUserIsAcdAdmin } from '../reducers/levers/leversSelector'; import ACD_LEVERS from '../../../constants/ACD_LEVERS'; import ExcludeDocketLever from './ExcludeDocketLever'; +import { updateLeverValue } from '../reducers/levers/leversActions'; const ExclusionTable = () => { const theState = useSelector((state) => state); + const dispatch = useDispatch(); const isUserAcdAdmin = getUserIsAcdAdmin(theState); @@ -39,6 +41,62 @@ const ExclusionTable = () => { leverGroup: lever.lever_group, })); + const [priorityToggle, setPriorityToggle] = useState(false); + const [nonPriorityToggle, setNonPriorityToggle] = useState(false); + const [comboPriorityToggle, setComboPriorityToggle] = useState(false); + const [comboNonPriorityToggle, setComboNonPriorityToggle] = useState(false); + + useEffect(() => { + const allPrioritySelected = priorityRadios.every((lever) => lever.value === 'true'); + const allPriorityUnselected = priorityRadios.every((lever) => lever.value === 'false'); + const allNonPrioritySelected = nonPriorityRadios.every((lever) => lever.value === 'true'); + const allNonPriorityUnselected = nonPriorityRadios.every((lever) => lever.value === 'false'); + + if (allPrioritySelected) { + setPriorityToggle(true); + setComboPriorityToggle(false); + } else if (allPriorityUnselected) { + setPriorityToggle(false); + setComboPriorityToggle(false); + } else { + setPriorityToggle(false); + setComboPriorityToggle(true); + } + + if (allNonPrioritySelected) { + setNonPriorityToggle(true); + setComboNonPriorityToggle(false); + } else if (allNonPriorityUnselected) { + setNonPriorityToggle(false); + setComboNonPriorityToggle(false); + } else { + setNonPriorityToggle(false); + setComboNonPriorityToggle(true); + } + }, [priorityRadios, nonPriorityRadios]); + + const handleToggleChange = (isPriority) => { + if (isPriority) { + const toggleState = priorityToggle !== true; + + setPriorityToggle(toggleState); + const newToggleState = toggleState ? 'true' : 'false'; + + priorityRadios.forEach((lever) => { + dispatch(updateLeverValue(lever.leverGroup, lever.item, newToggleState)); + }); + } else { + const toggleState = nonPriorityToggle !== true; + + setNonPriorityToggle(toggleState); + const newToggleState = toggleState ? 'true' : 'false'; + + nonPriorityRadios.forEach((lever) => { + dispatch(updateLeverValue(lever.leverGroup, lever.item, newToggleState)); + }); + } + }; + const filterOptionValue = (lever) => { let enabled = lever?.value; @@ -93,14 +151,19 @@ const ExclusionTable = () => { handleToggleChange(false)} + isIdle = {comboNonPriorityToggle} /> {nonPriorityRadios && nonPriorityRadios.map((lever) => ( - + ))} @@ -115,13 +178,18 @@ const ExclusionTable = () => { handleToggleChange(true)} + isIdle = {comboPriorityToggle} /> {priorityRadios && priorityRadios.map((lever) => ( - + ))} @@ -135,7 +203,11 @@ const ExclusionTable = () => { {COPY.CASE_DISTRIBUTION_EXCLUSION_TABLE_NON_PRIORITY} {nonPriorityRadios && nonPriorityRadios.map((lever) => ( - + @@ -149,7 +221,11 @@ const ExclusionTable = () => { {COPY.CASE_DISTRIBUTION_EXCLUSION_TABLE_PRIORITY} {priorityRadios && priorityRadios.map((lever) => ( - + diff --git a/client/app/caseDistribution/pages/CaseDistributionApp.jsx b/client/app/caseDistribution/pages/CaseDistributionApp.jsx index 11e0fe5223c..3b2af59994d 100644 --- a/client/app/caseDistribution/pages/CaseDistributionApp.jsx +++ b/client/app/caseDistribution/pages/CaseDistributionApp.jsx @@ -33,7 +33,7 @@ class CaseDistributionApp extends React.PureComponent { CaseDistributionApp.propTypes = { acdLeversForStore: PropTypes.object, - loadAcdExcludeFromAffinity: PropTypes.bool, + loadAcdExcludeFromAffinity: PropTypes.func, acd_exclude_from_affinity: PropTypes.bool, acd_history: PropTypes.array, user_is_an_acd_admin: PropTypes.bool, diff --git a/client/app/components/ToggleSwitch/ToggleSwitch.jsx b/client/app/components/ToggleSwitch/ToggleSwitch.jsx index 7a0ee8b1dec..6047b26db7a 100644 --- a/client/app/components/ToggleSwitch/ToggleSwitch.jsx +++ b/client/app/components/ToggleSwitch/ToggleSwitch.jsx @@ -8,7 +8,8 @@ const ToggleSwitch = ({ selected, toggleSelected, optionLabels, - disabled + disabled, + isIdle }) => { const buttonStyles = cx('toggleButton', { @@ -17,6 +18,7 @@ const ToggleSwitch = ({ const h5Style = cx('toggleButtonText', { switchDisabled: disabled, + switchIdle: isIdle, switchOn: selected, switchOff: !selected }); @@ -49,7 +51,8 @@ ToggleSwitch.propTypes = { id: PropTypes.string, name: PropTypes.string, optionLabels: PropTypes.array, - disabled: PropTypes.bool + disabled: PropTypes.bool, + isIdle: PropTypes.bool }; export default ToggleSwitch; diff --git a/client/app/styles/caseDistribution/_exclusion_table.scss b/client/app/styles/caseDistribution/_exclusion_table.scss index b3ea29976b6..b8d1f0b167a 100644 --- a/client/app/styles/caseDistribution/_exclusion_table.scss +++ b/client/app/styles/caseDistribution/_exclusion_table.scss @@ -1,5 +1,5 @@ .exclusion-table-header-styling { - color: $color-gray-lighter; + color: $color-black; padding: 0; margin-top: 0; margin-bottom: 0; diff --git a/client/app/styles/react/_toggle_switch.scss b/client/app/styles/react/_toggle_switch.scss index 2119b814d9c..b65887a846a 100644 --- a/client/app/styles/react/_toggle_switch.scss +++ b/client/app/styles/react/_toggle_switch.scss @@ -18,6 +18,10 @@ background: $color-gray-lighter; } + &:has(.switchIdle) { + background: $color-gray-lighter; + } + :not(:focus) { outline: none; } @@ -46,6 +50,10 @@ background: $color-gray-lighter; } +.switchIdle { + background: $color-gray-lighter; +} + .toggleButtonSpace { border-radius: 4px; width: 21px; From 076cd786d1e1e870dc55ec87f8885f0808165d86 Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Fri, 17 May 2024 15:57:01 -0400 Subject: [PATCH 23/44] Chrisbdetlef/appeals 43117.cleanup (#21671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * APPEALS-43421: Attorney Selection Sticking on Dropdown after Assignment (#21401) * redo init commit * updated imp. logic * attorney widget fix --------- Co-authored-by: Calvin * Add ruby CE API Gem * remove correspondence changes from schema.rb (#21468) * Revert "Add ruby CE API Gem" (#21479) This reverts commit c78afe30620716d59d2ac31ad6eed71585d78a98. * Override SCT Bulk Assign Page task limit (#21465) * Override SCT Bulk Assign Queue 15 case limit & Adjust SCT attorney assign task action label * feature/APPEALS-35707-29633-29632 (prodtest) (#21485) * 🔀 Squash merge AlecK/APPEALS-35707 - Replace `database_cleaner` with `database_cleaner-active_record` * 🔀 Squash merge jcroteau/APPEALS-29632-fix-deprecation-action-view-base-instances * 🔀 Squash merge jcroteau/APPEALS-29633-fix-deprecation-warning-active_record-result-to_hash * feature/APPEALS-44871 Steps for installing node in the Caseflow demo environment (#21483) (#21484) * Changed dockerfile to install node through NVM * Update env.sh with node 14.20 path * Removed unused node env * Update Dockerfile Removed unused node env variable * Revert "feature/APPEALS-44871 Steps for installing node in the Caseflow demo …" (#21489) This reverts commit a6262001f9bedabca60598c82bfe1b803f0f16c7. * Sbashamoni/appeals 44871 node14 demo fixes (#21490) (#21495) * Changed dockerfile to install node through NVM * Update env.sh with node 14.20 path * Removed unused node env * Update Dockerfile Removed unused node env variable * Removed unused node env * add error rules to demo builds (#21496) (#21498) Co-authored-by: davis-dwayne <47563178+davis-dwayne@users.noreply.github.com> * APPEALS-43117 Add QA Users for testing * Fix rspecs * Fix issues with attorneys and judge teams * Fix org count --------- Co-authored-by: Isaiah Saucedo Co-authored-by: Calvin Co-authored-by: youfoundmanesh Co-authored-by: Craig Reese <109101548+craigrva@users.noreply.github.com> Co-authored-by: Robert Travis Pierce Co-authored-by: Jeremy Croteau Co-authored-by: sbashamoni <138054633+sbashamoni@users.noreply.github.com> Co-authored-by: davis-dwayne <47563178+davis-dwayne@users.noreply.github.com> Co-authored-by: Christopher Detlef <> --- Gemfile | 2 +- Gemfile.lock | 7 +- .../organizations/task_summary_controller.rb | 2 +- app/models/queue_tab.rb | 5 + ...pecialty_case_team_unassigned_tasks_tab.rb | 4 + app/models/task_pager.rb | 6 +- app/models/tasks/attorney_task.rb | 2 +- app/models/vacols/case_assignment.rb | 2 +- app/models/vacols/case_docket.rb | 24 +- app/models/vacols/case_issue.rb | 2 +- app/models/vacols/correspondent.rb | 2 +- .../disallowed_deprecations.rb | 6 +- app/services/hearings/calendar_service.rb | 16 +- .../components/AssignToAttorneyWidget.jsx | 17 +- db/schema.rb | 139 ----- db/seeds.rb | 2 +- db/seeds/users.rb | 581 +++++++++++++----- docker-bin/build.sh | 11 +- lib/tasks/doc.rake | 2 +- spec/feature/queue/attorney_queue_spec.rb | 53 +- spec/seeds/hearings_spec.rb | 4 +- spec/seeds/users_spec.rb | 4 +- .../hearings/calendar_service_spec.rb | 68 ++ spec/support/database_cleaner.rb | 36 +- 24 files changed, 612 insertions(+), 385 deletions(-) create mode 100644 spec/services/hearings/calendar_service_spec.rb diff --git a/Gemfile b/Gemfile index af4cf12dd16..73271918d97 100644 --- a/Gemfile +++ b/Gemfile @@ -100,7 +100,7 @@ group :test, :development, :demo do gem "capybara" gem "capybara-screenshot" gem "danger", "~> 6.2.2" - gem "database_cleaner" + gem "database_cleaner-active_record", "2.0.0" gem "factory_bot_rails", "~> 5.2" gem "faker" gem "guard-rspec" diff --git a/Gemfile.lock b/Gemfile.lock index 63490abbc64..2f603a7956a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -235,7 +235,10 @@ GEM no_proxy_fix octokit (~> 4.7) terminal-table (~> 1) - database_cleaner (1.7.0) + database_cleaner-active_record (2.0.0) + activerecord (>= 5.a) + database_cleaner-core (~> 2.0.0) + database_cleaner-core (2.0.1) date (3.3.3) ddtrace (0.34.1) msgpack @@ -771,7 +774,7 @@ DEPENDENCIES console_tree_renderer! countries danger (~> 6.2.2) - database_cleaner + database_cleaner-active_record (= 2.0.0) ddtrace debase derailed_benchmarks diff --git a/app/controllers/organizations/task_summary_controller.rb b/app/controllers/organizations/task_summary_controller.rb index 597de034938..5260c43fa5f 100644 --- a/app/controllers/organizations/task_summary_controller.rb +++ b/app/controllers/organizations/task_summary_controller.rb @@ -31,7 +31,7 @@ def index format.json do render json: { members: json_users(organization.users), - task_counts: result.to_hash.to_json + task_counts: result.to_a.to_json } end end diff --git a/app/models/queue_tab.rb b/app/models/queue_tab.rb index de370f8d1c6..02b9e824bf0 100644 --- a/app/models/queue_tab.rb +++ b/app/models/queue_tab.rb @@ -85,6 +85,11 @@ def hide_from_queue_table_view false end + # This is a queue tab parameter that allows for client side queue tabs to have more than 15 tasks + def no_task_limit + false + end + # rubocop:disable Metrics/AbcSize def self.attorney_column_names [ diff --git a/app/models/queue_tabs/specialty_case_team_unassigned_tasks_tab.rb b/app/models/queue_tabs/specialty_case_team_unassigned_tasks_tab.rb index 6897597ece6..ae7df293181 100644 --- a/app/models/queue_tabs/specialty_case_team_unassigned_tasks_tab.rb +++ b/app/models/queue_tabs/specialty_case_team_unassigned_tasks_tab.rb @@ -33,4 +33,8 @@ def allow_bulk_assign? def hide_from_queue_table_view true end + + def no_task_limit + true + end end diff --git a/app/models/task_pager.rb b/app/models/task_pager.rb index 088366e6595..01733e15734 100644 --- a/app/models/task_pager.rb +++ b/app/models/task_pager.rb @@ -56,7 +56,11 @@ def queue_tab end def pagination_enabled - @pagination_enabled ||= assignee.use_task_pages_api? && !queue_tab.contains_legacy_tasks? + @pagination_enabled ||= begin + assignee.use_task_pages_api? && + !queue_tab.contains_legacy_tasks? && + !queue_tab.no_task_limit + end end private diff --git a/app/models/tasks/attorney_task.rb b/app/models/tasks/attorney_task.rb index 15e24545fbb..aa05c5a90de 100644 --- a/app/models/tasks/attorney_task.rb +++ b/app/models/tasks/attorney_task.rb @@ -28,7 +28,7 @@ def available_actions(user) movement_actions = [ Constants.TASK_ACTIONS.ASSIGN_TO_ATTORNEY.to_h, - Constants.TASK_ACTIONS.CANCEL_AND_RETURN_TASK.to_h + attorney_cancel_action ] actions_based_on_assignment(user, atty_actions, movement_actions) diff --git a/app/models/vacols/case_assignment.rb b/app/models/vacols/case_assignment.rb index 16cf231c095..8559a1e3888 100644 --- a/app/models/vacols/case_assignment.rb +++ b/app/models/vacols/case_assignment.rb @@ -159,7 +159,7 @@ def exists_for_appeals(vacols_ids) conn.exec_query(sanitize_sql_array([query, vacols_ids])) end - result.to_hash.reduce({}) do |memo, row| + result.to_a.reduce({}) do |memo, row| memo[(row["bfkey"]).to_s] = (row["n"] > 0) memo end diff --git a/app/models/vacols/case_docket.rb b/app/models/vacols/case_docket.rb index 8c38473346b..6c73c806d80 100644 --- a/app/models/vacols/case_docket.rb +++ b/app/models/vacols/case_docket.rb @@ -210,7 +210,7 @@ def self.counts_by_priority_and_readiness group by PRIORITY, READY SQL - connection.exec_query(query).to_hash + connection.exec_query(query).to_a end # rubocop:enable Metrics/MethodLength @@ -220,7 +220,7 @@ def self.genpop_priority_count where VLJ is null or #{ineligible_judges_sattyid_cache} SQL - connection.exec_query(query).to_hash.count + connection.exec_query(query).to_a.size end def self.not_genpop_priority_count @@ -229,7 +229,7 @@ def self.not_genpop_priority_count where VLJ is not null SQL - connection.exec_query(query).to_hash.count + connection.exec_query(query).to_a.size end def self.nod_count @@ -311,7 +311,7 @@ def self.age_of_n_oldest_genpop_priority_appeals(num) fmtd_query = sanitize_sql_array([query, num]) - appeals = conn.exec_query(fmtd_query).to_hash + appeals = conn.exec_query(fmtd_query).to_a appeals.map { |appeal| appeal["bfdloout"] } end @@ -330,7 +330,7 @@ def self.age_of_n_oldest_priority_appeals_available_to_judge(judge, num) num ]) - appeals = conn.exec_query(fmtd_query).to_hash + appeals = conn.exec_query(fmtd_query).to_a appeals.map { |appeal| appeal["bfd19"] } end @@ -349,7 +349,7 @@ def self.age_of_n_oldest_nonpriority_appeals_available_to_judge(judge, num) num ]) - appeals = conn.exec_query(fmtd_query).to_hash + appeals = conn.exec_query(fmtd_query).to_a appeals.map { |appeal| appeal["bfd19"] } end @@ -361,7 +361,7 @@ def self.age_of_oldest_priority_appeal fmtd_query = sanitize_sql_array([query, 1]) - connection.exec_query(fmtd_query).to_hash.first&.fetch("bfdloout") + connection.exec_query(fmtd_query).to_a.first&.fetch("bfdloout") end def self.age_of_oldest_priority_appeal_by_docket_date @@ -372,7 +372,7 @@ def self.age_of_oldest_priority_appeal_by_docket_date fmtd_query = sanitize_sql_array([query, 1]) - connection.exec_query(fmtd_query).to_hash.first&.fetch("bfd19") + connection.exec_query(fmtd_query).to_a.first&.fetch("bfd19") end def self.nonpriority_decisions_per_year @@ -411,7 +411,7 @@ def self.nonpriority_hearing_cases_for_judge_count(judge) end def self.priority_ready_appeal_vacols_ids - connection.exec_query(SELECT_PRIORITY_APPEALS).to_hash.map { |appeal| appeal["bfkey"] } + connection.exec_query(SELECT_PRIORITY_APPEALS).to_a.map { |appeal| appeal["bfkey"] } end def self.ready_to_distribute_appeals @@ -420,7 +420,7 @@ def self.ready_to_distribute_appeals SQL fmtd_query = sanitize_sql_array([query]) - connection.exec_query(fmtd_query).to_hash + connection.exec_query(fmtd_query).to_a end # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/ParameterLists, Metrics/MethodLength @@ -504,11 +504,11 @@ def self.distribute_appeals(query, judge, dry_run) conn.transaction do if dry_run - conn.exec_query(query).to_hash + conn.exec_query(query).to_a else conn.execute(LOCK_READY_APPEALS) unless FeatureToggle.enabled?(:acd_disable_legacy_lock_ready_appeals) - appeals = conn.exec_query(query).to_hash + appeals = conn.exec_query(query).to_a return appeals if appeals.empty? vacols_ids = appeals.map { |appeal| appeal["bfkey"] } diff --git a/app/models/vacols/case_issue.rb b/app/models/vacols/case_issue.rb index 2f3848f8b71..fb1784f0ffd 100644 --- a/app/models/vacols/case_issue.rb +++ b/app/models/vacols/case_issue.rb @@ -107,7 +107,7 @@ def self.descriptions(vacols_ids) conn.exec_query(sanitize_sql_array([query, vacols_ids])) end - issues_result.to_hash.reduce({}) do |memo, result| + issues_result.to_a.reduce({}) do |memo, result| issue_key = result["isskey"].to_s memo[issue_key] = (memo[issue_key] || []) << result memo diff --git a/app/models/vacols/correspondent.rb b/app/models/vacols/correspondent.rb index e5d3682b963..de11ad74aa0 100644 --- a/app/models/vacols/correspondent.rb +++ b/app/models/vacols/correspondent.rb @@ -50,7 +50,7 @@ def self.extract(last_extract) fmtd_query = sanitize_sql_array([query, last_extract]) - connection.exec_query(fmtd_query).to_hash + connection.exec_query(fmtd_query).to_a end # Take in a collection and return a csv friendly format diff --git a/app/services/deprecation_warnings/disallowed_deprecations.rb b/app/services/deprecation_warnings/disallowed_deprecations.rb index 79f781ea4cc..c8abde11879 100644 --- a/app/services/deprecation_warnings/disallowed_deprecations.rb +++ b/app/services/deprecation_warnings/disallowed_deprecations.rb @@ -14,7 +14,11 @@ class ::DisallowedDeprecationError < StandardError; end # Regular expressions for Rails 6.1 deprecation warnings that we have addressed in the codebase RAILS_6_1_FIXED_DEPRECATION_WARNING_REGEXES = [ - /update_attributes is deprecated and will be removed from Rails 6\.1/ + /update_attributes is deprecated and will be removed from Rails 6\.1/, + /ActionView::Base instances should be constructed with a lookup context, assignments, and a controller./, + /ActionView::Base instances must implement `compiled_method_container`/, + /render file: should be given the absolute path to a file/, + /`ActiveRecord::Result#to_hash` has been renamed to `to_a`/ ].freeze # Regular expressions for deprecation warnings that should raise an exception on detection diff --git a/app/services/hearings/calendar_service.rb b/app/services/hearings/calendar_service.rb index 9eba2a32b4f..caf1936b7ab 100644 --- a/app/services/hearings/calendar_service.rb +++ b/app/services/hearings/calendar_service.rb @@ -8,6 +8,11 @@ # emails. class Hearings::CalendarService + class CalendarEventView < ActionView::Base + include Hearings::CalendarTemplateHelper + include Hearings::AppellantNameHelper + end + class << self # Sent when first switching a video hearing to a virtual hearing, # and also when the scheduled time for an existing virtual hearing @@ -95,11 +100,8 @@ def create_calendar_event(hearing) end def render_virtual_hearing_calendar_event_template(email_recipient_info, event_type, locals) - template = ActionView::Base.new(ActionMailer::Base.view_paths, {}) - template.class_eval do - include Hearings::CalendarTemplateHelper - include Hearings::AppellantNameHelper - end + lookup_context = ActionView::Base.build_lookup_context(ActionController::Base.view_paths) + context = CalendarEventView.new(lookup_context) # Some *~ magic ~* here. The recipient title is used to determine which template to load: # @@ -112,8 +114,8 @@ def render_virtual_hearing_calendar_event_template(email_recipient_info, event_t template_name = "#{email_recipient_info.title.downcase}_#{event_type}_event_description" - template.render( - file: "hearing_mailer/calendar_events/#{template_name}", + context.render( + template: "hearing_mailer/calendar_events/#{template_name}", locals: locals ) end diff --git a/client/app/queue/components/AssignToAttorneyWidget.jsx b/client/app/queue/components/AssignToAttorneyWidget.jsx index aa42026c0cd..1360896f60e 100644 --- a/client/app/queue/components/AssignToAttorneyWidget.jsx +++ b/client/app/queue/components/AssignToAttorneyWidget.jsx @@ -42,7 +42,9 @@ export class AssignToAttorneyWidget extends React.PureComponent { super(props); this.state = { - instructions: (this.props.isModal ? this.props.selectedTasks[0].instructions : null) || '' + instructions: (this.props.isModal ? this.props.selectedTasks[0].instructions : null) || '', + selectedOptionOther: null, + selectedOption: null }; } @@ -170,6 +172,7 @@ export class AssignToAttorneyWidget extends React.PureComponent { } this.props.resetAssignees(); + this.setState({ selectedOptionOther: null, selectedOption: null }); return this.props.showSuccessMessage({ title: titleString @@ -211,7 +214,6 @@ export class AssignToAttorneyWidget extends React.PureComponent { attorneysOfJudge, currentUser, selectedAssignee, - selectedAssigneeSecondary, selectedTasks, savePending, highlightFormItems, @@ -221,17 +223,15 @@ export class AssignToAttorneyWidget extends React.PureComponent { secondaryAssignDropdownLabel, pathAfterSubmit } = this.props; - const { instructions } = this.state; + const { instructions, selectedOption, selectedOptionOther } = this.state; const optionFromAttorney = (attorney) => ({ label: attorney.full_name, value: attorney.id.toString() }); const otherOpt = { label: COPY.ASSIGN_WIDGET_OTHER, value: OTHER }; const judgeOpt = currentUser ? { label: currentUser.fullName, value: currentUser.id } : null; const options = [...attorneysOfJudge.map(optionFromAttorney), ...(judgeOpt ? [judgeOpt] : []), otherOpt]; - const selectedOption = options.find((option) => option.value === selectedAssignee); let optionsOther = []; let placeholderOther = COPY.ASSIGN_WIDGET_LOADING; - let selectedOptionOther = null; if (attorneys.error) { placeholderOther = COPY.ASSIGN_WIDGET_ERROR_LOADING_ATTORNEYS; @@ -249,7 +249,6 @@ export class AssignToAttorneyWidget extends React.PureComponent { if (optionsOther?.length) { placeholderOther = hidePrimaryAssignDropdown ? COPY.SCT_ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER : COPY.ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER; - selectedOptionOther = optionsOther.find((option) => option.value === selectedAssigneeSecondary) || null; } const otherDropdownWidth = hidePrimaryAssignDropdown ? '40rem' : '30rem'; @@ -264,7 +263,8 @@ export class AssignToAttorneyWidget extends React.PureComponent { errorMessage={isModal && highlightFormItems && !selectedOption ? 'Choose one' : null} options={options} placeholder={COPY.ASSIGN_WIDGET_DROPDOWN_PLACEHOLDER} - onChange={(option) => option && this.props.setSelectedAssignee({ assigneeId: option.value })} + onChange={(option) => option && this.props.setSelectedAssignee({ assigneeId: option.value }) && + this.setState({ selectedOption: option.value })} value={selectedOption} styling={css({ width: '30rem' })} /> } @@ -280,7 +280,8 @@ export class AssignToAttorneyWidget extends React.PureComponent { errorMessage={isModal && highlightFormItems && !selectedOptionOther ? 'Choose one' : null} options={optionsOther} placeholder={placeholderOther} - onChange={(option) => option && this.props.setSelectedAssigneeSecondary({ assigneeId: option.value })} + onChange={(option) => option && this.props.setSelectedAssigneeSecondary({ assigneeId: option.value }) && + this.setState({ selectedOptionOther: option.value })} value={selectedOptionOther} styling={css({ width: otherDropdownWidth })} /> } diff --git a/db/schema.rb b/db/schema.rb index e0e4c53779e..d6e985996cb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -200,10 +200,6 @@ t.index ["updated_at"], name: "index_attorney_case_reviews_on_updated_at" end - create_table "auto_texts", force: :cascade do |t| - t.string "name" - end - create_table "available_hearing_locations", force: :cascade do |t| t.string "address", comment: "Full address of the location" t.integer "appeal_id", comment: "Appeal/LegacyAppeal ID; use as FK to appeals/legacy_appeals" @@ -623,76 +619,6 @@ t.index ["updated_by_id"], name: "index_updated_by_id" end - create_table "correspondence_documents", force: :cascade do |t| - t.bigint "correspondence_id" - t.datetime "created_at", null: false, comment: "Date and Time of creation." - t.string "document_file_number", comment: "From CMP documents table" - t.integer "document_type", comment: "ID of the doc to lookup VBMS Doc Type" - t.integer "pages", comment: "Number of pages in the CMP Document" - t.datetime "updated_at", null: false, comment: "Date and Time of last update." - t.uuid "uuid", comment: "Reference to document in AWS S3" - t.bigint "vbms_document_type_id", comment: "From CMP documents table" - t.index ["correspondence_id"], name: "index_correspondence_documents_on_correspondence_id" - end - - create_table "correspondence_intakes", force: :cascade do |t| - t.bigint "correspondence_id", comment: "Foreign key on correspondences table" - t.datetime "created_at", null: false - t.integer "current_step", null: false, comment: "Tracks users progress on intake workflow" - t.jsonb "redux_store", null: false, comment: "JSON representation of the data for the current step" - t.datetime "updated_at", null: false - t.bigint "user_id", comment: "Foreign key on users table" - t.index ["correspondence_id"], name: "index_on_correspondence_id" - t.index ["user_id"], name: "index_on_user_id" - end - - create_table "correspondence_relations", force: :cascade do |t| - t.bigint "correspondence_id" - t.datetime "created_at", null: false - t.bigint "related_correspondence_id" - t.datetime "updated_at", null: false - t.index ["correspondence_id", "related_correspondence_id"], name: "index_correspondence_relations_on_correspondences", unique: true - t.index ["related_correspondence_id", "correspondence_id"], name: "index_correspondence_relations_on_related_correspondences", unique: true - end - - create_table "correspondence_types", force: :cascade do |t| - t.boolean "active", default: true, null: false - t.datetime "created_at", null: false - t.string "name", null: false - t.datetime "updated_at", null: false - end - - create_table "correspondences", force: :cascade do |t| - t.bigint "assigned_by_id", comment: "Foreign key to users table" - t.bigint "cmp_packet_number", comment: "Included in CMP mail package" - t.integer "cmp_queue_id", comment: "Foreign key to CMP queues table" - t.integer "correspondence_type_id", comment: "Foreign key for correspondence_types table" - t.datetime "created_at", null: false, comment: "Standard created_at/updated_at timestamps" - t.text "notes", comment: "Comes from CMP; can be updated by user" - t.integer "package_document_type_id", comment: "Represents entire CMP package document type" - t.datetime "portal_entry_date", comment: "Time when correspondence is created in Caseflow" - t.string "source_type", comment: "An information identifier we get from CMP" - t.datetime "updated_at", null: false, comment: "Standard created_at/updated_at timestamps" - t.bigint "updated_by_id", comment: "Foreign key to users table" - t.uuid "uuid", comment: "Unique identifier" - t.datetime "va_date_of_receipt", comment: "Date package delivered" - t.bigint "veteran_id", comment: "Foreign key to veterans table" - t.index ["assigned_by_id"], name: "index_correspondences_on_assigned_by_id" - t.index ["cmp_queue_id"], name: "index_correspondences_on_cmp_queue_id" - t.index ["correspondence_type_id"], name: "index_correspondences_on_correspondence_type_id" - t.index ["updated_by_id"], name: "index_correspondences_on_updated_by_id" - t.index ["veteran_id"], name: "index_correspondences_on_veteran_id" - end - - create_table "correspondences_appeals", force: :cascade do |t| - t.bigint "appeal_id" - t.bigint "correspondence_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["appeal_id"], name: "index on appeal_id" - t.index ["correspondence_id"], name: "index on correspondence_id" - end - create_table "decision_documents", force: :cascade do |t| t.bigint "appeal_id", null: false t.string "appeal_type" @@ -1498,13 +1424,6 @@ t.index ["user_id", "organization_id"], name: "index_organizations_users_on_user_id_and_organization_id", unique: true end - create_table "package_document_types", force: :cascade do |t| - t.boolean "active", default: true - t.datetime "created_at", null: false - t.string "name" - t.datetime "updated_at", null: false - end - create_table "people", force: :cascade do |t| t.datetime "created_at", null: false t.date "date_of_birth", comment: "PII" @@ -2088,52 +2007,6 @@ t.index ["vbms_communication_package_id"], name: "index_vbms_distributions_on_vbms_communication_package_id" end - create_table "vbms_document_types", force: :cascade do |t| - t.datetime "created_at", null: false - t.integer "doc_type_id" - t.datetime "updated_at", null: false - end - - create_table "vbms_ext_claim", primary_key: "CLAIM_ID", id: :decimal, precision: 38, force: :cascade do |t| - t.string "ALLOW_POA_ACCESS", limit: 5 - t.decimal "CLAIMANT_PERSON_ID", precision: 38 - t.datetime "CLAIM_DATE" - t.string "CLAIM_SOJ", limit: 25 - t.integer "CONTENTION_COUNT" - t.datetime "CREATEDDT", null: false - t.string "EP_CODE", limit: 25 - t.datetime "ESTABLISHMENT_DATE" - t.datetime "EXPIRATIONDT" - t.string "INTAKE_SITE", limit: 25 - t.datetime "LASTUPDATEDT", null: false - t.string "LEVEL_STATUS_CODE", limit: 25 - t.datetime "LIFECYCLE_STATUS_CHANGE_DATE" - t.string "LIFECYCLE_STATUS_NAME", limit: 50 - t.string "ORGANIZATION_NAME", limit: 100 - t.string "ORGANIZATION_SOJ", limit: 25 - t.string "PAYEE_CODE", limit: 25 - t.string "POA_CODE", limit: 25 - t.integer "PREVENT_AUDIT_TRIG", limit: 2, default: 0, null: false - t.string "PRE_DISCHARGE_IND", limit: 5 - t.string "PRE_DISCHARGE_TYPE_CODE", limit: 10 - t.string "PRIORITY", limit: 10 - t.string "PROGRAM_TYPE_CODE", limit: 10 - t.string "RATING_SOJ", limit: 25 - t.string "SERVICE_TYPE_CODE", limit: 10 - t.string "SUBMITTER_APPLICATION_CODE", limit: 25 - t.string "SUBMITTER_ROLE_CODE", limit: 25 - t.datetime "SUSPENSE_DATE" - t.string "SUSPENSE_REASON_CODE", limit: 25 - t.string "SUSPENSE_REASON_COMMENTS", limit: 1000 - t.decimal "SYNC_ID", precision: 38, null: false - t.string "TEMPORARY_CLAIM_SOJ", limit: 25 - t.string "TYPE_CODE", limit: 25 - t.decimal "VERSION", precision: 38, null: false - t.decimal "VETERAN_PERSON_ID", precision: 15 - t.index ["CLAIM_ID"], name: "claim_id_index" - t.index ["LEVEL_STATUS_CODE"], name: "level_status_code_index" - end - create_table "vbms_uploaded_documents", force: :cascade do |t| t.bigint "appeal_id", comment: "Appeal/LegacyAppeal ID; use as FK to appeals/legacy_appeals" t.string "appeal_type", comment: "'Appeal' or 'LegacyAppeal'" @@ -2331,18 +2204,6 @@ add_foreign_key "conference_links", "hearing_days" add_foreign_key "conference_links", "users", column: "created_by_id" add_foreign_key "conference_links", "users", column: "updated_by_id" - add_foreign_key "correspondence_documents", "correspondences" - add_foreign_key "correspondence_intakes", "correspondences" - add_foreign_key "correspondence_intakes", "users" - add_foreign_key "correspondence_relations", "correspondences" - add_foreign_key "correspondence_relations", "correspondences", column: "related_correspondence_id" - add_foreign_key "correspondences", "correspondence_types" - add_foreign_key "correspondences", "package_document_types" - add_foreign_key "correspondences", "users", column: "assigned_by_id" - add_foreign_key "correspondences", "users", column: "updated_by_id" - add_foreign_key "correspondences", "veterans" - add_foreign_key "correspondences_appeals", "appeals" - add_foreign_key "correspondences_appeals", "correspondences" add_foreign_key "dispatch_tasks", "legacy_appeals", column: "appeal_id" add_foreign_key "dispatch_tasks", "users" add_foreign_key "distributed_cases", "distributions" diff --git a/db/seeds.rb b/db/seeds.rb index abdffceabc0..26cdbf2877c 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "database_cleaner" +require "database_cleaner-active_record" # because db/seeds is not in the autoload path, we must load them explicitly here # base.rb needs to be loaded first because the other seeds inherit from it diff --git a/db/seeds/users.rb b/db/seeds/users.rb index 28539da1e85..526487be27f 100644 --- a/db/seeds/users.rb +++ b/db/seeds/users.rb @@ -32,48 +32,18 @@ def seed! private def create_users - User.create(css_id: "CASEFLOW1", station_id: 317, full_name: "System User") - User.create(css_id: "BVASCASPER1", station_id: 101, full_name: "Steve Attorney_Cases_AVLJ Casper") - User.create(css_id: "BVASRITCHIE", station_id: 101, full_name: "Sharree AttorneyNoCases Ritchie") - User.create(css_id: "BVAAABSHIRE", station_id: 101, full_name: "Aaron Judge_HearingsAndCases Abshire") - User.create(css_id: "BVARERDMAN", station_id: 101, full_name: "Rachael JudgeHasAttorneys_Cases_AVLJ Erdman") - create_bvaebecker - create_bvakkeeling - User.create(css_id: "BVAAWAKEFIELD", station_id: 101, full_name: "Apurva Judge_CaseAtDispatch Wakefield") - User.create(css_id: "BVAABELANGER", station_id: 101, full_name: "Andy Attorney_CaseAtDispatch Belanger") - User.create(css_id: "BVATWARNER", station_id: 101, full_name: "Theresa BuildHearingSchedule Warner") - User.create(css_id: "BVAGWHITE", station_id: 101, full_name: "George BVADispatchUser_Cases White") - User.create(css_id: "BVAGGREY", station_id: 101, full_name: "Gina BVADispatchUser_NoCases Grey") - User.create(css_id: "BVATCOLLIER", station_id: 101, full_name: "Tonja DVCTeam Collier") - - dispatch_admin = User.create( - css_id: "BVAGBLACK", - station_id: 101, - full_name: "Geoffrey BVADispatchAdmin_NoCases Black" - ) - OrganizationsUser.make_user_admin(dispatch_admin, BvaDispatch.singleton) - - case_review_admin = User.create(css_id: "BVAKBLUE", station_id: 101, full_name: "Kim CaseReviewAdmin Blue") - OrganizationsUser.make_user_admin(case_review_admin, CaseReview.singleton) - - special_case_movement_user = User.create(css_id: "BVARDUNKLE", - station_id: 101, - full_name: "Rosalie SpecialCaseMovement Dunkle") - FactoryBot.create(:staff, user: special_case_movement_user) - SpecialCaseMovementTeam.singleton.add_user(special_case_movement_user) - - special_case_movement_admin = User.create(css_id: "BVAGBEEKMAN", - station_id: 101, - full_name: "Bryan SpecialCaseMovementAdmin Beekman") - FactoryBot.create(:staff, user: special_case_movement_admin) - OrganizationsUser.make_user_admin(special_case_movement_admin, SpecialCaseMovementTeam.singleton) - - create_bvadwise - - bva_intake_user = User.create(css_id: "BVAISHAW", station_id: 101, full_name: "Ignacio BvaIntakeUser Shaw") - BvaIntake.singleton.add_user(bva_intake_user) + create_batch_1_users + create_bvaebecker #Judge_CaseToAssign + create_bvakkeeling #Judge_CaseToAssign_NoTeam + create_batch_2_users + create_dispatch_admin + create_case_review_admin + create_special_case_movement_user + create_bvadwise #Intake Admin + create_bva_intake_user Functions.grant!("System Admin", users: User.all.pluck(:css_id)) + create_vha_admins create_team_admin create_colocated_users @@ -101,39 +71,100 @@ def create_users create_non_admin_hearing_coordinator_user add_mail_intake_to_all_bva_intake_users create_cda_control_group_users + create_qa_test_users + end + + def create_batch_1_users + User.find_by_css_id("CASEFLOW1") || create(:user, css_id: "CASEFLOW1", station_id: 317, full_name: "System User") + User.find_by_css_id("BVASCASPER1") || create(:user, css_id: "BVASCASPER1", station_id: 101, full_name: "Steve Attorney_Cases_AVLJ Casper") + User.find_by_css_id("BVASRITCHIE") || create(:user, css_id: "BVASRITCHIE", station_id: 101, full_name: "Sharree AttorneyNoCases Ritchie") + User.find_by_css_id("BVAAABSHIRE") || create(:user, css_id: "BVAAABSHIRE", station_id: 101, full_name: "Aaron Judge_HearingsAndCases Abshire") + User.find_by_css_id("BVARERDMAN") || create(:user, css_id: "BVARERDMAN", station_id: 101, full_name: "Rachael JudgeHasAttorneys_Cases_AVLJ Erdman") end def create_bvaebecker - bvaebecker = User.create(css_id: "BVAEBECKER", station_id: 101, full_name: "Elizabeth Judge_CaseToAssign Becker") + bvaebecker = User.find_by_css_id("BVAEBECKER") || + create(:user, css_id: "BVAEBECKER", station_id: 101, full_name: "Elizabeth Judge_CaseToAssign Becker") CDAControlGroup.singleton.add_user(bvaebecker) end def create_bvakkeeling - bvakkeeling = User.create(css_id: "BVAKKEELING", station_id: 101, full_name: "Keith Judge_CaseToAssign_NoTeam Keeling") + bvakkeeling = User.find_by_css_id("BVAKKEELING") || + create(:user, css_id: "BVAKKEELING", station_id: 101, full_name: "Keith Judge_CaseToAssign_NoTeam Keeling") CDAControlGroup.singleton.add_user(bvakkeeling) end + def create_batch_2_users + User.find_by_css_id("BVAAWAKEFIELD") || + create(:user, css_id: "BVAAWAKEFIELD", station_id: 101, full_name: "Apurva Judge_CaseAtDispatch Wakefield") + User.find_by_css_id("BVAABELANGER") || + create(:user, css_id: "BVAABELANGER", station_id: 101, full_name: "Andy Attorney_CaseAtDispatch Belanger") + User.find_by_css_id("BVATWARNER") || + create(:user, css_id: "BVATWARNER", station_id: 101, full_name: "Theresa BuildHearingSchedule Warner") + User.find_by_css_id("BVAGWHITE") || + create(:user, css_id: "BVAGWHITE", station_id: 101, full_name: "George BVADispatchUser_Cases White") + User.find_by_css_id("BVAGGREY") || + create(:user, css_id: "BVAGGREY", station_id: 101, full_name: "Gina BVADispatchUser_NoCases Grey") + User.find_by_css_id("BVATCOLLIER") || + create(:user, css_id: "BVATCOLLIER", station_id: 101, full_name: "Tonja DVCTeam Collier") + end + + def create_dispatch_admin + dispatch_admin = User.find_by_css_id("BVAGBLACK") || + create(:user, css_id: "BVAGBLACK", station_id: 101, full_name: "Geoffrey BVADispatchAdmin_NoCases Black") + OrganizationsUser.make_user_admin(dispatch_admin, BvaDispatch.singleton) + end + + def create_case_review_admin + case_review_admin = User.find_by_css_id("BVAKBLUE") || + create(:user, css_id: "BVAKBLUE", station_id: 101, full_name: "Kim CaseReviewAdmin Blue") + OrganizationsUser.make_user_admin(case_review_admin, CaseReview.singleton) + end + + def create_special_case_movement_user + special_case_movement_user = User.find_by_css_id("BVARDUNKLE") || + create(:user, css_id: "BVARDUNKLE",station_id: 101, full_name: "Rosalie SpecialCaseMovement Dunkle") + FactoryBot.create(:staff, user: special_case_movement_user) + SpecialCaseMovementTeam.singleton.add_user(special_case_movement_user) + end + + def special_case_movement_admin + special_case_movement_admin = User.find_by_css_id("BVAGBEEKMAN") || + create(:user, css_id: "BVAGBEEKMAN",station_id: 101, full_name: "Bryan SpecialCaseMovementAdmin Beekman") + FactoryBot.create(:staff, user: special_case_movement_admin) + OrganizationsUser.make_user_admin(special_case_movement_admin, SpecialCaseMovementTeam.singleton) + end + + def create_bva_intake_user + bva_intake_user = User.find_by_css_id("BVAISHAW") || + create(:user, css_id: "BVAISHAW", station_id: 101, full_name: "Ignacio BvaIntakeUser Shaw") + BvaIntake.singleton.add_user(bva_intake_user) + end + def create_bvadwise - bva_intake_admin = User.create(css_id: "BVADWISE", station_id: 101, full_name: "Deborah BvaIntakeAdmin Wise") + bva_intake_admin = User.find_by_css_id("BVADWISE") || + create(:user, css_id: "BVADWISE", station_id: 101, full_name: "Deborah BvaIntakeAdmin Wise") OrganizationsUser.make_user_admin(bva_intake_admin, BvaIntake.singleton) OrganizationsUser.make_user_admin(bva_intake_admin, CDAControlGroup.singleton) end def create_vha_admins %w[VHAADMIN VHAADMIN2].each do |css_id| - vha_admin_user = User.create( - css_id: css_id, - station_id: 101, - full_name: css_id, - roles: ["System Admin", "Certify Appeal", "Mail Intake", "Admin Intake"] - ) - + vha_admin_user = User.find_by_css_id(css_id) || + create( + :user, + css_id: css_id, + station_id: 101, + full_name: css_id, + roles: ["System Admin", "Certify Appeal", "Mail Intake", "Admin Intake"] + ) OrganizationsUser.make_user_admin(vha_admin_user, VhaBusinessLine.singleton) end end def create_team_admin - u = User.create(css_id: "TEAM_ADMIN", station_id: 101, full_name: "Jim TeamAdminSystemAdmin Jones") + u = User.find_by_css_id("TEAM_ADMIN") || + create(:user, css_id: "TEAM_ADMIN", station_id: 101, full_name: "Jim TeamAdminSystemAdmin Jones") existing_sysadmins = Functions.details_for("System Admin")[:granted] || [] Functions.grant!("System Admin", users: existing_sysadmins + [u.css_id]) Bva.singleton.add_user(u) @@ -144,21 +175,25 @@ def create_colocated_users create(:staff, :colocated_role, user: secondary_user, sdept: "DSP") Colocated.singleton.add_user(secondary_user) - user = User.create( - css_id: "BVALSPORER", - station_id: 101, - full_name: "Laura Co-located_Cases Sporer", - roles: %w[Reader] - ) + user = User.find_by_css_id("BVALSPORER") || + create( + :user, + css_id: "BVALSPORER", + station_id: 101, + full_name: "Laura Co-located_Cases Sporer", + roles: %w[Reader] + ) create(:staff, :colocated_role, user: user, sdept: "DSP") Colocated.singleton.add_user(user) - admin = User.create( - css_id: "VLJ_SUPPORT_ADMIN", - station_id: 101, - full_name: "John VLJSupportAdmin Smith", - roles: %w[Reader] - ) + admin = User.find_by_css_id("VLJ_SUPPORT_ADMIN") || + create( + :user, + css_id: "VLJ_SUPPORT_ADMIN", + station_id: 101, + full_name: "John VLJSupportAdmin Smith", + roles: %w[Reader] + ) create(:staff, :colocated_role, user: admin, sdept: "DSP") OrganizationsUser.make_user_admin(admin, Colocated.singleton) end @@ -171,78 +206,93 @@ def create_vso_users_and_tasks ) %w[BILLIE MICHAEL JIMMY].each do |name| - u = User.create( - css_id: "#{name}_VSO", - station_id: 101, - full_name: "#{name} VSOUser Jones", - roles: %w[VSO], - email: "#{name}@test.com" - ) - vso.add_user(u) - - # Assign one IHP task to each member of the VSO team and leave some IHP tasks assigned to the organization. - [true, false].each do |assign_to_user| - a = create(:appeal) - root_task = create(:root_task, appeal: a) - create( - :hearing, - appeal: a - ) - ihp_task = create( - :informal_hearing_presentation_task, - parent: root_task, - appeal: a, - assigned_to: vso - ) + u = User.find_by_css_id("#{name}_VSO") || create( - :track_veteran_task, - parent: root_task, - appeal: a, - assigned_to: vso + :user, + css_id: "#{name}_VSO", + station_id: 101, + full_name: "#{name} VSOUser Jones", + roles: %w[VSO], + email: "#{name}@test.com" ) + vso.add_user(u) - next unless assign_to_user - - InformalHearingPresentationTask.create_many_from_params([{ - parent_id: ihp_task.id, - assigned_to_id: u.id, - assigned_to_type: User.name - }], u) + # Assign one IHP task to each member of the VSO team and leave some IHP tasks assigned to the organization. + if u.tasks.nil? + [true, false].each do |assign_to_user| + a = create(:appeal) + root_task = create(:root_task, appeal: a) + create( + :hearing, + appeal: a + ) + ihp_task = create( + :informal_hearing_presentation_task, + parent: root_task, + appeal: a, + assigned_to: vso + ) + create( + :track_veteran_task, + parent: root_task, + appeal: a, + assigned_to: vso + ) + + next unless assign_to_user + + InformalHearingPresentationTask.create_many_from_params([{ + parent_id: ihp_task.id, + assigned_to_id: u.id, + assigned_to_type: User.name + }], u) + end end end end def create_judge_teams DEVELOPMENT_JUDGE_TEAMS.each_pair do |judge_css_id, h| - judge = User.find_or_create_by(css_id: judge_css_id, station_id: 101) + judge = User.find_by_css_id(judge_css_id) || + create(:user, css_id: judge_css_id, station_id: 101) judge_team = JudgeTeam.for_judge(judge) || JudgeTeam.create_for_judge(judge) h[:attorneys].each do |css_id| - judge_team.add_user(User.find_or_create_by(css_id: css_id, station_id: 101)) + judge_team.add_user( + User.find_by_css_id(css_id) || + create(:user, css_id: css_id, station_id: 101) + ) end end end def create_dvc_teams DEVELOPMENT_DVC_TEAMS.each_pair do |dvc_css_id, judges| - dvc = User.find_or_create_by(css_id: dvc_css_id, station_id: 101) + dvc = User.find_by_css_id(dvc_css_id) || + create(:user, css_id: dvc_css_id, station_id: 101) dvc_team = DvcTeam.for_dvc(dvc) || DvcTeam.create_for_dvc(dvc) judges.each do |css_id| - dvc_team.add_user(User.find_or_create_by(css_id: css_id, station_id: 101)) + dvc_team.add_user( + User.find_by_css_id(css_id) || + create(:user, css_id: css_id, station_id: 101) + ) end end end def create_transcription_team - transcription_member = User.find_or_create_by( - css_id: "TRANSCRIPTION_USER", - station_id: 101, - full_name: "Noel TranscriptionUser Vasquez" - ) + transcription_member = User.find_by_css_id("TRANSCRIPTION_USER") || + create( + :user, + css_id: "TRANSCRIPTION_USER", + station_id: 101, + full_name: "Noel TranscriptionUser Vasquez" + ) TranscriptionTeam.singleton.add_user(transcription_member) end def create_hearings_user - hearings_member = User.find_or_create_by(css_id: "BVATWARNER", station_id: 101) + hearings_member = User.find_by_css_id("BVATWARNER") || + create( :user, css_id: "BVATWARNER", station_id: 101) HearingsManagement.singleton.add_user(hearings_member) HearingAdmin.singleton.add_user(hearings_member) end @@ -254,19 +304,21 @@ def create_build_and_edit_hearings_users { css_id: "BVASORANGE", station_id: 343, full_name: "Felicia BuildAndEditHearingSchedule Orange", roles: roles } ] user_params.each do |params| - user = User.create(**params) + user = User.find_or_create_by(**params) HearingsManagement.singleton.add_user(user) HearingAdmin.singleton.add_user(user) end end def create_non_admin_hearing_coordinator_user - hearings_user = User.create( - css_id: "BVANHALE", - station_id: 101, - full_name: "Nisha NonAdminHearingCoordinator Hale", - roles: ["Edit HearSched"] - ) + hearings_user = User.find_by_css_id("BVANHALE") || + create( + :user, + css_id: "BVANHALE", + station_id: 101, + full_name: "Nisha NonAdminHearingCoordinator Hale", + roles: ["Edit HearSched"] + ) HearingsManagement.singleton.add_user(hearings_user) end @@ -283,26 +335,30 @@ def create_pva_vso_and_users ) %w[WINNIE].each do |name| - u = User.create( - css_id: "#{name}_PVA_VSO", - station_id: 101, - full_name: "#{name} PVA_VSOUser James", - roles: %w[VSO] - ) + u = User.find_by_css_id("#{name}_PVA_VSO") || + create( + :user, + css_id: "#{name}_PVA_VSO", + station_id: 101, + full_name: "#{name} PVA_VSOUser James", + roles: %w[VSO] + ) vso.add_user(u) end end def create_field_vso_and_users - vso = create(:field_vso, name: "Field VSO", url: "field-vso") + vso = FieldVso.find_or_create_by(name: "Field VSO", url: "field-vso") %w[MANDY NICHOLAS ELIJAH].each do |name| - u = User.create( - css_id: "#{name}_VSO", - station_id: 101, - full_name: "#{name} VSOUser Wilson", - roles: %w[VSO] - ) + u = User.find_by_css_id("#{name}_VSO") || + create( + :user, + css_id: "#{name}_VSO", + station_id: 101, + full_name: "#{name} VSOUser Wilson", + roles: %w[VSO] + ) vso.add_user(u) a = create(:appeal) @@ -319,23 +375,23 @@ def create_field_vso_and_users def create_org_queue_users nca = BusinessLine.find_or_create_by!(name: "National Cemetery Administration", url: "nca") %w[Parveen Chandra Sydney Tai Kennedy].each do |name| - u = User.create!(station_id: 101, css_id: "NCA_QUEUE_USER_#{name}", full_name: "#{name} NCAUser Carter") + u = User.find_or_create_by!(station_id: 101, css_id: "NCA_QUEUE_USER_#{name.upcase}", full_name: "#{name} NCAUser Carter") nca.add_user(u) end %w[Kun Casey Ariel Naomi Kelly].each do |name| - u = User.create!(station_id: 101, css_id: "ORG_QUEUE_USER_#{name}", full_name: "#{name} TranslationUser Cullen") + u = User.find_or_create_by!(station_id: 101, css_id: "ORG_QUEUE_USER_#{name.upcase}", full_name: "#{name} TranslationUser Cullen") Translation.singleton.add_user(u) end end def create_qr_user - qr_user = User.create!(station_id: 101, css_id: "QR_USER", full_name: "Yarden QualityReviewer Jordan") + qr_user = User.find_or_create_by!(station_id: 101, css_id: "QR_USER", full_name: "Yarden QualityReviewer Jordan") QualityReview.singleton.add_user(qr_user) end def create_aod_user_and_tasks - u = User.create!(station_id: 101, css_id: "AOD_USER", full_name: "Shiloh AODUser Villar") + u = User.find_or_create_by!(station_id: 101, css_id: "AOD_USER", full_name: "Shiloh AODUser Villar") AodTeam.singleton.add_user(u) root_task = create(:root_task) @@ -352,26 +408,26 @@ def create_aod_user_and_tasks end def create_privacy_user - u = User.create!(station_id: 101, css_id: "PRIVACY_TEAM_USER", full_name: "Leighton PrivacyAndFOIAUser Naumov") + u = User.find_or_create_by!(station_id: 101, css_id: "PRIVACY_TEAM_USER", full_name: "Leighton PrivacyAndFOIAUser Naumov") PrivacyTeam.singleton.add_user(u) end def create_lit_support_user - u = User.create!(station_id: 101, css_id: "LIT_SUPPORT_USER", full_name: "Kiran LitigationSupportUser Rider") + u = User.find_or_create_by!(station_id: 101, css_id: "LIT_SUPPORT_USER", full_name: "Kiran LitigationSupportUser Rider") LitigationSupport.singleton.add_user(u) end def create_oai_team_user - u = User.create!(station_id: 101, css_id: "OAI_TEAM_USER", full_name: "Tywin OaiTeam Lannister") + u = User.find_or_create_by!(station_id: 101, css_id: "OAI_TEAM_USER", full_name: "Tywin OaiTeam Lannister") OaiTeam.singleton.add_user(u) OrganizationsUser.make_user_admin(u, OaiTeam.singleton) end def create_occ_team_user - u = User.create!(station_id: 101, css_id: "OCC_TEAM_USER", full_name: "Jon OccTeam Snow") + u = User.find_or_create_by!(station_id: 101, css_id: "OCC_TEAM_USER", full_name: "Jon OccTeam Snow") OccTeam.singleton.add_user(u) OrganizationsUser.make_user_admin(u, OccTeam.singleton) - u = User.create!(station_id: 101, css_id: "OCC_OAI_TEAM_USER", full_name: "Ned OccOaiTeam Stark") + u = User.find_or_create_by!(station_id: 101, css_id: "OCC_OAI_TEAM_USER", full_name: "Ned OccOaiTeam Stark") OccTeam.singleton.add_user(u) OaiTeam.singleton.add_user(u) end @@ -391,7 +447,7 @@ def create_cavc_lit_support_user ] users = users_info.map do |user_info| - User.create!(station_id: 101, + User.find_or_create_by!(station_id: 101, css_id: user_info[:css_id], full_name: user_info[:full_name]) end @@ -402,31 +458,33 @@ def create_cavc_lit_support_user end def create_pulac_cerullo_user - u = User.create!(station_id: 101, css_id: "BVAKSOSNA", full_name: "KATHLEEN PulacCerulloUser SOSNA") + u = User.find_or_create_by!(station_id: 101, css_id: "BVAKSOSNA", full_name: "KATHLEEN PulacCerulloUser SOSNA") PulacCerullo.singleton.add_user(u) end def create_mail_team_user - u = User.create!(station_id: 101, css_id: "JOLLY_POSTMAN", full_name: "Huan MailUser Tiryaki") + u = User.find_or_create_by!(station_id: 101, css_id: "JOLLY_POSTMAN", full_name: "Huan MailUser Tiryaki") MailTeam.singleton.add_user(u) end def create_clerk_of_the_board_users - atty = create( - :user, - :with_vacols_attorney_record, - station_id: 101, - css_id: "COB_USER", - full_name: "Clark ClerkOfTheBoardUser Bard", - roles: ["Hearing Prep", "Mail Intake"] - ) + + atty = User.find_by(css_id: "COB_USER") || + create( + :user, + :with_vacols_attorney_record, + station_id: 101, + css_id: "COB_USER", + full_name: "Clark ClerkOfTheBoardUser Bard", + roles: ["Hearing Prep", "Mail Intake"] + ) ClerkOfTheBoard.singleton.add_user(atty) - judge = create(:user, full_name: "Judith COTB Judge", css_id: "BVACOTBJUDGE", roles: ["Hearing Prep", "Mail Intake"]) + judge = User.find_or_create_by(full_name: "Judith COTB Judge", station_id: 101, css_id: "BVACOTBJUDGE", roles: ["Hearing Prep", "Mail Intake"]) create(:staff, :judge_role, sdomainid: judge.css_id) ClerkOfTheBoard.singleton.add_user(judge) - admin = create(:user, full_name: "Ty ClerkOfTheBoardAdmin Cobb", css_id: "BVATCOBB", roles: ["Hearing Prep", "Mail Intake"]) + admin = User.find_or_create_by!(full_name: "Ty ClerkOfTheBoardAdmin Cobb", station_id: 101, css_id: "BVATCOBB", roles: ["Hearing Prep", "Mail Intake"]) ClerkOfTheBoard.singleton.add_user(admin) OrganizationsUser.make_user_admin(admin, ClerkOfTheBoard.singleton) @@ -437,27 +495,27 @@ def create_clerk_of_the_board_users end def create_case_search_only_user - User.create!(station_id: 101, css_id: "CASE_SEARCHER_ONLY", full_name: "Blair CaseSearchAccessNoQueueAccess Lyon") + User.find_or_create_by!(station_id: 101, css_id: "CASE_SEARCHER_ONLY", full_name: "Blair CaseSearchAccessNoQueueAccess Lyon") end def create_split_appeals_test_users - ussc = User.create!(station_id: 101, + ussc = User.find_or_create_by!(station_id: 101, css_id: "SPLTAPPLSNOW", full_name: "Jon SupervisorySeniorCouncilUser Snow", roles: ["Hearing Prep"]) SupervisorySeniorCouncil.singleton.add_user(ussc) - ussc2 = User.create!(station_id: 101, + ussc2 = User.find_or_create_by!(station_id: 101, css_id: "SPLTAPPLTARGARYEN", full_name: "Daenerys SupervisorySeniorCouncilUser Targaryen", roles: ["Hearing Prep"]) SupervisorySeniorCouncil.singleton.add_user(ussc2) - ussccr = User.create!(station_id: 101, + ussccr = User.find_or_create_by!(station_id: 101, css_id: "SPLTAPPLLANNISTER", full_name: "Jaime SupervisorySeniorCouncilCaseReviewUser Lannister", roles: ["Hearing Prep"]) SupervisorySeniorCouncil.singleton.add_user(ussccr) CaseReview.singleton.add_user(ussccr) - ussccr2 = User.create!(station_id: 101, + ussccr2 = User.find_or_create_by!(station_id: 101, css_id: "SPLTAPPLSTARK", full_name: "Ned SupervisorySeniorCouncilCaseReviewUser Stark", roles: ["Hearing Prep"]) @@ -478,20 +536,19 @@ def add_mail_intake_to_all_bva_intake_users end def create_cda_control_group_users - bvaebeckser = User.create!(station_id: 101, + bvaebeckser = User.find_or_create_by!(station_id: 101, css_id: "BVAEBECKSER", full_name: "Elizabeth Judge_VaseToAssign Becker", roles: ["Mail Intake"]) CDAControlGroup.singleton.add_user(bvaebeckser) - leo = User.create!(station_id: 101, + leo = User.find_or_create_by!(station_id: 101, css_id: "CDAADMINLEO", full_name: "Leonardo CDAC_Admin Turtur", roles: ["Mail Intake"]) CDAControlGroup.singleton.add_user(leo) OrganizationsUser.make_user_admin(leo, CDAControlGroup.singleton) - - casey = User.create!(station_id: 101, + casey = User.find_or_create_by!(station_id: 101, css_id: "CDAUSERCASEY", full_name: "Casey CDAC_User Jones", roles: ["Mail Intake"]) @@ -501,8 +558,8 @@ def create_cda_control_group_users end def create_qa_admin_for_cda_control_group - qa_admin = User.create!(station_id: 101, - css_id: "QAACDPlus", + qa_admin = User.find_or_create_by!(station_id: 101, + css_id: "QAACDPLUS", full_name: "QA_Admin ACD_CF TM_Mgmt_Intake", roles: ["Mail Intake", "Admin Intake", "Hearing Prep"]) @@ -524,7 +581,207 @@ def create_qa_admin_for_cda_control_group judge_team = JudgeTeam.for_judge(qa_admin) || JudgeTeam.create_for_judge(qa_admin) judge_team.add_user(User.find_by_css_id("BVASCASPER1")) end + + def create_qa_test_users + create_qa_active_judge3 + create_qa_active_judge2 + create_qa_ineligible_judge + create_qa_solo_active_judge + create_qa_ssc_avlj_attorney + create_qa_nonssc_avlj_attorney + create_qa_cob_intake_clerk + create_qa_intake_clerk + create_qa_intake_admin + create_qa_hearing_admin + create_qa_case_movement_user + create_qa_attny_1 + create_qa_attny_2 + create_qa_attny_3 + create_qa_judge_team_3 + create_qa_judge_team_2 + end + + def create_qa_active_judge3 + User.find_by_css_id("QACTIVEVLJ3") || + create( + :user, + :with_vacols_judge_record, + :judge, + css_id: "QACTIVEVLJ3", + station_id: 101, + full_name: "QA_Active_Judge With Team_of_3" + ) + end + + def create_qa_active_judge2 + User.find_by_css_id("QACTIVEVLJ2") || + create( + :user, + :with_vacols_judge_record, + :judge, + css_id: "QACTIVEVLJ2", + station_id: 101, + full_name: "QA_Active_Judge With Team_of_2" + ) + end + + def create_qa_ineligible_judge + User.find_by_css_id("QINELIGVLJ") || + create( + :user, + :inactive, + :judge, + css_id: "QINELIGVLJ", + station_id: 101, + full_name: "QA_Active_Judge With Team_of_2" + ) + end + + def create_qa_solo_active_judge + User.find_by_css_id("QACTVLJNOTM") || + create( + :user, + :with_vacols_judge_record, + :judge, + css_id: "QACTVLJNOTM", + station_id: 101, + full_name: "QA_Active_Judge With No_Team" + ) + end + + def create_qa_ssc_avlj_attorney + qa_ssc_avlj_attorney = User.find_or_create_by( + css_id: "QSSCAVLJ", + station_id: 101, + full_name: "QA SSC_AVLJ Attorney", + roles: ["Hearing Prep"] + ) + SupervisorySeniorCouncil.singleton.add_user(qa_ssc_avlj_attorney) + VACOLS::Staff.create( + snamef: "QA_SSC_AVLJ", + snamel: "Attorney", + sdomainid: qa_ssc_avlj_attorney.css_id, + sattyid: "9999", + smemgrp: "9999" + ) + end + + def create_qa_nonssc_avlj_attorney + qa_nonssc_avlj_attorney = User.find_or_create_by( + css_id: "QNONSSCAVLJ", + station_id: 101, + full_name: "QA Non_SSC_AVLJ Attorney" + ) + VACOLS::Staff.create( + snamef: "QA_NonSSC_AVLJ", + snamel: "Attorney", + sdomainid: qa_nonssc_avlj_attorney.css_id, + sattyid: "9998", + smemgrp: "9998" + ) + end + + def create_qa_cob_intake_clerk + qa_cob_intake_clerk_user = User.find_or_create_by( + css_id: "QCOBINTAKE", + station_id: 101, + full_name: "QA Clerk_of_the_Board", + roles: ["Hearing Prep", "Mail Intake"] + ) + ClerkOfTheBoard.singleton.add_user(qa_cob_intake_clerk_user) + OrganizationsUser.make_user_admin(qa_cob_intake_clerk_user, ClerkOfTheBoard.singleton) + BvaIntake.singleton.add_user(qa_cob_intake_clerk_user) + end + + def create_qa_intake_clerk + qa_intake_clerk = User.find_or_create_by( + css_id: "QINTAKE", + station_id: 101, + full_name: "QA Intake Clerk", + roles: ["Mail Intake"] + ) + BvaIntake.singleton.add_user(qa_intake_clerk) + end + + def create_qa_intake_admin + qa_intake_admin_user = User.find_or_create_by( + css_id: "QINTAKEADMIN", + station_id: 101, + full_name: "QA Intake Admin", + roles: ["Mail Intake"] + ) + OrganizationsUser.make_user_admin(qa_intake_admin_user, BvaIntake.singleton) + OrganizationsUser.make_user_admin(qa_intake_admin_user, CDAControlGroup.singleton) + end + + def create_qa_hearing_admin + User.find_or_create_by( + css_id: "QHEARADMIN", + station_id: 343, + full_name: "QA Hearings Admin", + roles: ["Edit HearSched", "Build HearSched"] + ) + end + + def create_qa_case_movement_user + qa_case_movement_user = User.find_or_create_by( + css_id: "QCASEMVMT", + station_id: 101, + full_name: "QA Case Movement" + ) + FactoryBot.create(:staff, user: qa_case_movement_user) + OrganizationsUser.make_user_admin(qa_case_movement_user, SpecialCaseMovementTeam.singleton) + end + + def create_qa_attny_1 + User.find_by_css_id("QATTY1") || + create( + :user, + :with_vacols_attorney_record, + css_id: "QATTY1", + station_id: 101, + full_name: "QA Attorney_1" + ) + end + + def create_qa_attny_2 + User.find_by_css_id("QATTY2") || + create( + :user, + :with_vacols_attorney_record, + css_id: "QATTY2", + station_id: 101, + full_name: "QA Attorney_2" + ) + end + + def create_qa_attny_3 + User.find_by_css_id("QATTY3") || + create( + :user, + :with_vacols_attorney_record, + css_id: "QATTY3", + station_id: 101, + full_name: "QA Attorney_3" + ) + end + + def create_qa_judge_team_3 + qa_judge_3 = User.find_by(css_id: "QACTIVEVLJ3") + qa_judge_team_3 = JudgeTeam.for_judge(qa_judge_3) || JudgeTeam.create_for_judge(qa_judge_3) + qa_judge_team_3.add_user(User.find_by(css_id: "QATTY1")) + qa_judge_team_3.add_user(User.find_by(css_id: "QATTY2")) + qa_judge_team_3.add_user(User.find_by(css_id: "QATTY3")) + end + + def create_qa_judge_team_2 + qa_judge_2 = User.find_by(css_id: "QACTIVEVLJ2") + qa_judge_team_2 = JudgeTeam.for_judge(qa_judge_2) || JudgeTeam.create_for_judge(qa_judge_2) + qa_judge_team_2.add_user(User.find_by(css_id: "QATTY1")) + qa_judge_team_2.add_user(User.find_by(css_id: "QATTY2")) + end + + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/MethodLength end - # rubocop:enable Metrics/AbcSize - # rubocop:enable Metrics/MethodLength end diff --git a/docker-bin/build.sh b/docker-bin/build.sh index d7f9859bb7e..d566c662999 100755 --- a/docker-bin/build.sh +++ b/docker-bin/build.sh @@ -48,8 +48,13 @@ cp /etc/ssl/certs/ca-certificates.crt docker-bin/ca-certs/cacert.pem # Build Docker echo -e "\tCreating Caseflow App Docker Image" docker build -t caseflow . - +result=$? echo -e "\tCleaning Up..." rm -rf config/datadog.key -rm -rf docker-bin/oracle_libs/ -echo -e "\tBuilding Caseflow Docker App: Completed" +rm -rf docker-bin/oracle_libs +if [ $result == 0 ]; then + echo -e "\tBuilding Caseflow Docker App: Completed" +else + echo -e "\tBuilding Caseflow failed" +fi +exit $result diff --git a/lib/tasks/doc.rake b/lib/tasks/doc.rake index 83c42fe3b58..31d4cfd4047 100644 --- a/lib/tasks/doc.rake +++ b/lib/tasks/doc.rake @@ -41,7 +41,7 @@ namespace :doc do end def exec_sql(sql) - db_connection.exec_query(sql).to_hash + db_connection.exec_query(sql).to_a end def db_connection diff --git a/spec/feature/queue/attorney_queue_spec.rb b/spec/feature/queue/attorney_queue_spec.rb index 6c79a9d996d..dcfcaf3562b 100644 --- a/spec/feature/queue/attorney_queue_spec.rb +++ b/spec/feature/queue/attorney_queue_spec.rb @@ -172,31 +172,44 @@ before { sct_org.add_user(sct_user) } - it "attorney can view sct appeal in queue and return it back to sct queue" do - step "shows the sct appeal in the attorney's queue" do - visit "/queue" - expect(page).to have_content(appeal.veteran_full_name) + shared_examples "attorney or judge return to sct queue" do + it "attorney can view sct appeal in queue and return it back to sct queue" do + step "shows the sct appeal in the attorney's queue" do + visit "/queue" + expect(page).to have_content(appeal.veteran_full_name) + end + + step "start the return to sct queue process" do + visit "/queue/appeals/#{appeal.uuid}" + click_dropdown(text: Constants.TASK_ACTIONS.CANCEL_TASK_AND_RETURN_TO_SCT_QUEUE.label) + expect(page).to have_content(COPY::RETURN_TO_SCT_MODAL_TITLE) + expect(page).to have_content(COPY::RETURN_TO_SCT_MODAL_BODY) + expect(page).to have_button(COPY::MODAL_RETURN_BUTTON, disabled: true) + + fill_in(COPY::PROVIDE_INSTRUCTIONS_AND_CONTEXT_LABEL, with: "Return this case to SCT please.") + expect(page).to have_button(COPY::MODAL_RETURN_BUTTON, disabled: false) + find("button", text: COPY::MODAL_RETURN_BUTTON).click + expect(page).to have_content(format(COPY::RETURN_TO_SCT_SUCCESS_BANNER_TITLE, appeal.claimant.name)) + expect(page).to have_content(COPY::RETURN_TO_SCT_SUCCESS_BANNER_DETAIL) + end + + step "the returned appeal shows in the sct queue action required tab" do + User.authenticate!(user: sct_user) + visit "/organizations/#{sct_org.url}" + expect(page).to have_content("#{appeal.veteran_full_name} (#{appeal.veteran_file_number})") + end end + end - step "start the return to sct queue process" do - visit "/queue/appeals/#{appeal.uuid}" - click_dropdown(text: Constants.TASK_ACTIONS.CANCEL_TASK_AND_RETURN_TO_SCT_QUEUE.label) - expect(page).to have_content(COPY::RETURN_TO_SCT_MODAL_TITLE) - expect(page).to have_content(COPY::RETURN_TO_SCT_MODAL_BODY) - expect(page).to have_button(COPY::MODAL_RETURN_BUTTON, disabled: true) + include_examples "attorney or judge return to sct queue" - fill_in(COPY::PROVIDE_INSTRUCTIONS_AND_CONTEXT_LABEL, with: "Return this case to SCT please.") - expect(page).to have_button(COPY::MODAL_RETURN_BUTTON, disabled: false) - find("button", text: COPY::MODAL_RETURN_BUTTON).click - expect(page).to have_content(format(COPY::RETURN_TO_SCT_SUCCESS_BANNER_TITLE, appeal.claimant.name)) - expect(page).to have_content(COPY::RETURN_TO_SCT_SUCCESS_BANNER_DETAIL) + context "when the attorney can act as a judge" do + before do + SpecialCaseMovementTeam.singleton.add_user(attorney) + attorney.reload end - step "the returned appeal shows in the sct queue action required tab" do - User.authenticate!(user: sct_user) - visit "/organizations/#{sct_org.url}" - expect(page).to have_content("#{appeal.veteran_full_name} (#{appeal.veteran_file_number})") - end + include_examples "attorney or judge return to sct queue" end end end diff --git a/spec/seeds/hearings_spec.rb b/spec/seeds/hearings_spec.rb index 4fb998842e7..c2d572af696 100644 --- a/spec/seeds/hearings_spec.rb +++ b/spec/seeds/hearings_spec.rb @@ -10,9 +10,9 @@ it "creates all kinds of hearings", :aggregate_failures do expect { subject }.to_not raise_error - expect(Hearing.count).to eq(301) # Seeds::Users.new.seed! creates 4 hearings + expect(Hearing.count).to eq(295) # Seeds::Users.new.seed! creates 4 hearings expect(LegacyHearing.count).to eq(295) - expect(HearingDay.count).to eq(401) # Seeds::Users.new.seed! creates 4 hearing days + expect(HearingDay.count).to eq(395) # Seeds::Users.new.seed! creates 4 hearing days end end end diff --git a/spec/seeds/users_spec.rb b/spec/seeds/users_spec.rb index aff3cd891cf..8e82b500522 100644 --- a/spec/seeds/users_spec.rb +++ b/spec/seeds/users_spec.rb @@ -6,8 +6,8 @@ it "creates all kinds of users and organizations" do expect { subject }.to_not raise_error - expect(User.count).to eq(133) - expect(Organization.count).to eq(40) + expect(User.count).to eq(128) + expect(Organization.count).to eq(44) end end end diff --git a/spec/services/hearings/calendar_service_spec.rb b/spec/services/hearings/calendar_service_spec.rb new file mode 100644 index 00000000000..7d9cc2db76c --- /dev/null +++ b/spec/services/hearings/calendar_service_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +describe Hearings::CalendarService do + describe ".confirmation_calendar_invite" do + subject(:confirmation_calendar_invite) do + described_class.confirmation_calendar_invite(virtual_hearing, email_recipient_info, link) + end + + let(:regional_office) { "RO06" } # nyc_ro_eastern + let(:appeal) { create(:appeal, :hearing_docket) } + let(:hearing_day) do + create(:hearing_day, scheduled_for: Date.parse("January 1, 1970"), + request_type: HearingDay::REQUEST_TYPES[:video], + regional_office: regional_office) + end + let(:hearing) do + create(:hearing, appeal: appeal, + scheduled_time: "8:30AM", + hearing_day: hearing_day, + regional_office: regional_office) + end + let(:virtual_hearing) do + create(:virtual_hearing, hearing: hearing, appellant_tz: nil, representative_tz: nil) + end + let(:email_recipient_info) do + EmailRecipientInfo.new(name: "LastName", + title: "appellant", + hearing_email_recipient: hearing_email_recipient) + end + let(:hearing_email_recipient) { virtual_hearing.hearing.appellant_recipient } + let(:link) { virtual_hearing.guest_link } + + it "returns appropriate iCalendar event" do + expected_description = <<~TEXT + You're scheduled for a virtual hearing with a Veterans Law Judge of the Board of Veterans' Appeals. + + Date and Time + Thursday, 1 January 1970 at 8:30am EST + + How to Join + We recommend joining 15 minutes before your hearing start time. Click on the link below, or copy and paste the link into the address field of your web browser: + https://care.evn.va.gov/bva-app/?join=1&media=&escalate=1&conference=BVA@care.evn.va.gov&pin=&role=guest + + Help Desk + If you are experiencing technical difficulties, call the VA Video Connect Helpdesk at 855-519-7116 and press 4 for Board of Veterans' Appeals support. + + Rescheduling or Canceling Your Hearing + If you need to reschedule or cancel your virtual hearing, contact us by email at bvahearingteamhotline@va.gov + TEXT + + aggregate_failures do + expect(confirmation_calendar_invite).to be_a(String) + + ical_event = Icalendar::Calendar.parse(confirmation_calendar_invite).first.events.first + + expect(ical_event.url.to_s).to eq( + "https://care.evn.va.gov/bva-app/?join=1&media=&escalate=1&conference=BVA@care.evn.va.gov&pin=&role=guest" + ) + expect(ical_event.location).to eq( + "https://care.evn.va.gov/bva-app/?join=1&media=&escalate=1&conference=BVA@care.evn.va.gov&pin=&role=guest" + ) + expect(ical_event.status).to eq("CONFIRMED") + expect(ical_event.summary).to be_nil + expect(ical_event.description).to eq(expected_description) + end + end + end +end diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 21b2095888f..bc90949aa75 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -14,26 +14,26 @@ # IMPORTANT that in all these hook defs, the "caseflow" connection comes last. config.before(:suite) do - DatabaseCleaner[:active_record, { connection: etl }].clean_with(:truncation) - DatabaseCleaner[:active_record, { connection: vacols }] - .clean_with(:deletion, { except: vacols_tables_to_preserve }) - DatabaseCleaner[:active_record, { connection: caseflow }].clean_with(:truncation) + DatabaseCleaner[:active_record, { db: etl }].clean_with(:truncation) + DatabaseCleaner[:active_record, { db: vacols }] + .clean_with(:deletion, except: vacols_tables_to_preserve) + DatabaseCleaner[:active_record, { db: caseflow }].clean_with(:truncation) end config.before(:each) do |example| # Allows seeded data to persist for use across threads # You will need to manually clean the data once the threads under test close. unless example.metadata[:bypass_cleaner] - DatabaseCleaner[:active_record, { connection: vacols }].strategy = :transaction - DatabaseCleaner[:active_record, { connection: caseflow }].strategy = :transaction + DatabaseCleaner[:active_record, { db: vacols }].strategy = :transaction + DatabaseCleaner[:active_record, { db: caseflow }].strategy = :transaction end end config.before(:each, db_clean: :truncation) do |example| unless example.metadata[:bypass_cleaner] - DatabaseCleaner[:active_record, { connection: vacols }].strategy = + DatabaseCleaner[:active_record, { db: vacols }].strategy = :deletion, { except: vacols_tables_to_preserve } - DatabaseCleaner[:active_record, { connection: caseflow }].strategy = :truncation + DatabaseCleaner[:active_record, { db: caseflow }].strategy = :truncation end end @@ -46,42 +46,42 @@ # Driver is probably for an external browser with an app # under test that does *not* share a database connection with the # specs, so use truncation strategy. - DatabaseCleaner[:active_record, { connection: vacols }].strategy = + DatabaseCleaner[:active_record, { db: vacols }].strategy = :deletion, { except: vacols_tables_to_preserve } - DatabaseCleaner[:active_record, { connection: caseflow }].strategy = :truncation + DatabaseCleaner[:active_record, { db: caseflow }].strategy = :truncation end end config.before(:each) do |example| unless example.metadata[:bypass_cleaner] - DatabaseCleaner[:active_record, { connection: vacols }].start - DatabaseCleaner[:active_record, { connection: caseflow }].start + DatabaseCleaner[:active_record, { db: vacols }].start + DatabaseCleaner[:active_record, { db: caseflow }].start end end config.append_after(:each) do - DatabaseCleaner[:active_record, { connection: vacols }].clean - DatabaseCleaner[:active_record, { connection: caseflow }].clean + DatabaseCleaner[:active_record, { db: vacols }].clean + DatabaseCleaner[:active_record, { db: caseflow }].clean clean_application! end # ETL is never used in feature tests and there are only a few, so we tag those with :etl # ETL db uses deletion strategy everywhere because syncing runs in a transaction. config.before(:each, :etl) do - DatabaseCleaner[:active_record, { connection: etl }].strategy = :deletion + DatabaseCleaner[:active_record, { db: etl }].strategy = :deletion end config.before(:each, :etl, db_clean: :truncation) do - DatabaseCleaner[:active_record, { connection: etl }].strategy = :truncation + DatabaseCleaner[:active_record, { db: etl }].strategy = :truncation end config.before(:each, :etl) do Rails.logger.info("DatabaseCleaner.start ETL") - DatabaseCleaner[:active_record, { connection: etl }].start + DatabaseCleaner[:active_record, { db: etl }].start end config.append_after(:each, :etl) do - DatabaseCleaner[:active_record, { connection: etl }].clean + DatabaseCleaner[:active_record, { db: etl }].clean Rails.logger.info("DatabaseCleaner.clean ETL") end end From 4b369dc6062653074ae6b337fd8038d0fc4f996e Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Tue, 21 May 2024 14:38:49 -0400 Subject: [PATCH 24/44] Minor fixes (#21694) * Minor fixes * Fix rspec tests and properly check for judge css id * Fix lint issue --------- Co-authored-by: Christopher Detlef <> --- db/seeds/demo_ama_aod_hearing_data.rb | 2 +- db/seeds/demo_ama_non_aod_hearing_data.rb | 2 +- db/seeds/demo_direct_reviews_data.rb | 2 +- db/seeds/demo_legacy_cases_data.rb | 2 +- spec/controllers/test_docket_seeds_controller_spec.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/db/seeds/demo_ama_aod_hearing_data.rb b/db/seeds/demo_ama_aod_hearing_data.rb index 718169f9fbb..eb7aababd7e 100644 --- a/db/seeds/demo_ama_aod_hearing_data.rb +++ b/db/seeds/demo_ama_aod_hearing_data.rb @@ -46,7 +46,7 @@ def random_demo_file_number_and_participant_id end def find_or_create_demo_seed_judge(judge_ccs_id) - unless judge_ccs_id.empty? + unless judge_ccs_id.blank? User.find_by_css_id(judge_ccs_id) || create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) else diff --git a/db/seeds/demo_ama_non_aod_hearing_data.rb b/db/seeds/demo_ama_non_aod_hearing_data.rb index f900f0026fb..c5461cc4062 100644 --- a/db/seeds/demo_ama_non_aod_hearing_data.rb +++ b/db/seeds/demo_ama_non_aod_hearing_data.rb @@ -45,7 +45,7 @@ def random_demo_file_number_and_participant_id end def find_or_create_demo_seed_judge(judge_ccs_id) - unless judge_ccs_id.empty? + unless judge_ccs_id.blank? User.find_by_css_id(judge_ccs_id) || create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) else diff --git a/db/seeds/demo_direct_reviews_data.rb b/db/seeds/demo_direct_reviews_data.rb index 99b267789d1..a5e0f591807 100644 --- a/db/seeds/demo_direct_reviews_data.rb +++ b/db/seeds/demo_direct_reviews_data.rb @@ -42,7 +42,7 @@ def random_demo_file_number_and_participant_id end def find_or_create_demo_seed_judge(judge_ccs_id) - unless judge_ccs_id.empty? + unless judge_ccs_id.blank? User.find_by_css_id(judge_ccs_id) || create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) else diff --git a/db/seeds/demo_legacy_cases_data.rb b/db/seeds/demo_legacy_cases_data.rb index ed92f6b8099..50f7f841ed9 100644 --- a/db/seeds/demo_legacy_cases_data.rb +++ b/db/seeds/demo_legacy_cases_data.rb @@ -69,7 +69,7 @@ def random_demo_file_number_and_participant_id end def find_or_create_demo_seed_judge(judge_ccs_id) - unless judge_ccs_id.empty? + unless judge_ccs_id.blank? User.find_by_css_id(judge_ccs_id) || create(:user, :judge, :with_vacols_judge_record, css_id: judge_ccs_id, full_name: "Demo Judge " + judge_ccs_id) else diff --git a/spec/controllers/test_docket_seeds_controller_spec.rb b/spec/controllers/test_docket_seeds_controller_spec.rb index d7815b93ea6..0c6245aba0c 100644 --- a/spec/controllers/test_docket_seeds_controller_spec.rb +++ b/spec/controllers/test_docket_seeds_controller_spec.rb @@ -4,7 +4,7 @@ unless Rake::Task.task_defined?("assets:precompile") Rails.application.load_tasks end - let!(:authenticated_user) { User.authenticate!(roles: ["System Admin"]) } + let!(:authenticated_user) { User.authenticate!(css_id: "RSPEC", roles: ["System Admin"]) } describe "POST run-demo?seed_type=ii?seed_count=x&days_ago=y&judge_css_id=zzz" do context "seed_ama_aod_hearings" do From d74c125f7dbda2be114d773158edda73acad2af2 Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Fri, 24 May 2024 17:04:08 -0400 Subject: [PATCH 25/44] Fix Legacy Case issues (#21747) Co-authored-by: Christopher Detlef <> --- db/seeds/users.rb | 109 ++++++++++++++------------------- spec/factories/user.rb | 6 ++ spec/factories/vacols/staff.rb | 5 ++ 3 files changed, 56 insertions(+), 64 deletions(-) diff --git a/db/seeds/users.rb b/db/seeds/users.rb index 526487be27f..443786899a2 100644 --- a/db/seeds/users.rb +++ b/db/seeds/users.rb @@ -583,8 +583,6 @@ def create_qa_admin_for_cda_control_group end def create_qa_test_users - create_qa_active_judge3 - create_qa_active_judge2 create_qa_ineligible_judge create_qa_solo_active_judge create_qa_ssc_avlj_attorney @@ -594,37 +592,10 @@ def create_qa_test_users create_qa_intake_admin create_qa_hearing_admin create_qa_case_movement_user - create_qa_attny_1 - create_qa_attny_2 - create_qa_attny_3 create_qa_judge_team_3 create_qa_judge_team_2 end - def create_qa_active_judge3 - User.find_by_css_id("QACTIVEVLJ3") || - create( - :user, - :with_vacols_judge_record, - :judge, - css_id: "QACTIVEVLJ3", - station_id: 101, - full_name: "QA_Active_Judge With Team_of_3" - ) - end - - def create_qa_active_judge2 - User.find_by_css_id("QACTIVEVLJ2") || - create( - :user, - :with_vacols_judge_record, - :judge, - css_id: "QACTIVEVLJ2", - station_id: 101, - full_name: "QA_Active_Judge With Team_of_2" - ) - end - def create_qa_ineligible_judge User.find_by_css_id("QINELIGVLJ") || create( @@ -633,7 +604,7 @@ def create_qa_ineligible_judge :judge, css_id: "QINELIGVLJ", station_id: 101, - full_name: "QA_Active_Judge With Team_of_2" + full_name: "QA Ineligible Judge" ) end @@ -733,53 +704,63 @@ def create_qa_case_movement_user OrganizationsUser.make_user_admin(qa_case_movement_user, SpecialCaseMovementTeam.singleton) end - def create_qa_attny_1 - User.find_by_css_id("QATTY1") || + def create_qa_judge_team(judge, team_array) + judge_team = JudgeTeam.for_judge(judge) || JudgeTeam.create_for_judge(judge) + team_array.each do |team_member| + judge_team.add_user( + User.find_by_css_id(team_member[:css_id]) || + create( + :user, + :with_vacols_titled_attorney_record, + css_id: team_member[:css_id], + full_name: team_member[:full_name], + station_id: 101 + ) + ) + end + end + + def create_qa_judge_team_3 + judge = User.find_by_css_id("QACTIVEVLJ3") || create( :user, - :with_vacols_attorney_record, - css_id: "QATTY1", + :judge, + :with_vacols_judge_record, + css_id: "QACTIVEVLJ3", + full_name: "QA_ACTIVE_JUDGE WITH TEAM_OF_3", station_id: 101, - full_name: "QA Attorney_1" + roles: ["Hearing Prep", "Mail Intake"] ) + team_array = [ + {"css_id": "QATTY1", "full_name": "QA Attorney_1"}, + {"css_id": "QATTY2", "full_name": "QA Attorney_2"}, + {"css_id": "QATTY3", "full_name": "QA Attorney_3"} + ] + CDAControlGroup.singleton.add_user(judge) + create_qa_judge_team(judge, team_array) end - def create_qa_attny_2 - User.find_by_css_id("QATTY2") || + def create_qa_judge_team_2 + judge = User.find_by_css_id("QACTIVEVLJ2") || create( :user, - :with_vacols_attorney_record, - css_id: "QATTY2", + :judge, + :with_vacols_judge_record, + css_id: "QACTIVEVLJ2", + full_name: "QA_ACTIVE_JUDGE WITH TEAM_OF_2", station_id: 101, - full_name: "QA Attorney_2" + roles: ["Hearing Prep", "Mail Intake"] ) + team_array = [ + {"css_id": "QATTY1", "full_name": "QA Attorney_1"}, + {"css_id": "QATTY2", "full_name": "QA Attorney_2"} + ] + CDAControlGroup.singleton.add_user(judge) + OrganizationsUser.make_user_admin(judge, CDAControlGroup.singleton) + create_qa_judge_team(judge, team_array) end - def create_qa_attny_3 - User.find_by_css_id("QATTY3") || - create( - :user, - :with_vacols_attorney_record, - css_id: "QATTY3", - station_id: 101, - full_name: "QA Attorney_3" - ) - end - - def create_qa_judge_team_3 - qa_judge_3 = User.find_by(css_id: "QACTIVEVLJ3") - qa_judge_team_3 = JudgeTeam.for_judge(qa_judge_3) || JudgeTeam.create_for_judge(qa_judge_3) - qa_judge_team_3.add_user(User.find_by(css_id: "QATTY1")) - qa_judge_team_3.add_user(User.find_by(css_id: "QATTY2")) - qa_judge_team_3.add_user(User.find_by(css_id: "QATTY3")) - end - def create_qa_judge_team_2 - qa_judge_2 = User.find_by(css_id: "QACTIVEVLJ2") - qa_judge_team_2 = JudgeTeam.for_judge(qa_judge_2) || JudgeTeam.create_for_judge(qa_judge_2) - qa_judge_team_2.add_user(User.find_by(css_id: "QATTY1")) - qa_judge_team_2.add_user(User.find_by(css_id: "QATTY2")) - end # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/MethodLength diff --git a/spec/factories/user.rb b/spec/factories/user.rb index ecb31b8b087..bff8a861ff6 100644 --- a/spec/factories/user.rb +++ b/spec/factories/user.rb @@ -107,6 +107,12 @@ end end + trait :with_vacols_titled_attorney_record do + after(:create) do |user| + create(:staff, :titled_attorney_role, user: user) + end + end + trait :vlj_support_user do after(:create) do |user| Colocated.singleton.add_user(user) diff --git a/spec/factories/vacols/staff.rb b/spec/factories/vacols/staff.rb index d22f80a3329..77519c47a42 100644 --- a/spec/factories/vacols/staff.rb +++ b/spec/factories/vacols/staff.rb @@ -49,6 +49,11 @@ sattyid { generated_sattyid } end + trait :titled_attorney_role do + attorney_role + stitle { "D#{Random.rand(1..5)}" } + end + # I'm not sure if this reflects real data but it's required for SCM users to see legacy tasks in tests trait :scm_role do sattyid { generated_sattyid } From 33929689428bbc6888d1a1be4872e1df8e176fd6 Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:58:39 -0400 Subject: [PATCH 26/44] APPEALS-46037. Fix padding in the lever history table (#21725) * APPEALS-46037. Fix padding in the lever history table * APPEALS-46037. Fix lint issues --------- Co-authored-by: SHarshain --- .../caseDistribution/_lever_history.scss | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client/app/styles/caseDistribution/_lever_history.scss b/client/app/styles/caseDistribution/_lever_history.scss index 4a666c2ce8a..a958dd55128 100644 --- a/client/app/styles/caseDistribution/_lever_history.scss +++ b/client/app/styles/caseDistribution/_lever_history.scss @@ -39,18 +39,32 @@ border-bottom: 1px solid $color-gray-lighter; th { - padding: 20px 0; + padding: 10px; } td { - padding: 0; + padding: 10px; vertical-align: middle; ol { list-style-position: inside; padding-left: 0; + margin: 0; } } } } + + .entry-user-id { + width: 10%; + } + + .entry-previous-values, + .entry-updated-values { + width: 15%; + } + + .entry-titles { + width: 40%; + } } From fccf6c272a94639fbe17db0fddb721344630c17a Mon Sep 17 00:00:00 2001 From: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:53:44 -0400 Subject: [PATCH 27/44] Fix Lauren Roth issue (#21813) * Users with a VACOLS::Staff entry no longer get created with the default name of Lauren Roth Co-authored-by: Christopher Detlef <> --- db/seeds/users.rb | 64 ++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/db/seeds/users.rb b/db/seeds/users.rb index 7c70d31d860..564b330ecf2 100644 --- a/db/seeds/users.rb +++ b/db/seeds/users.rb @@ -2,6 +2,11 @@ # Create Users/Organizations used by other seed classes. +# Users that already exist in the VACOLS::Staff table need to be "created" by the +# Users.rb model. This is because the model will make calls to the table to connect +# the user and the VACOLS::staff entries, which affects things like displayed names and +# permissions. New users should be created with the user factory and traits + module Seeds # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/MethodLength @@ -268,46 +273,35 @@ def create_vso_users_and_tasks def create_judge_teams DEVELOPMENT_JUDGE_TEAMS.each_pair do |judge_css_id, h| - judge = User.find_by_css_id(judge_css_id) || - create(:user, css_id: judge_css_id, station_id: 101) + judge = User.find_or_create_by(css_id: judge_css_id, station_id: 101) judge_team = JudgeTeam.for_judge(judge) || JudgeTeam.create_for_judge(judge) h[:attorneys].each do |css_id| - judge_team.add_user( - User.find_by_css_id(css_id) || - create(:user, css_id: css_id, station_id: 101) - ) + judge_team.add_user(User.find_or_create_by(css_id: css_id, station_id: 101)) end end end def create_dvc_teams DEVELOPMENT_DVC_TEAMS.each_pair do |dvc_css_id, judges| - dvc = User.find_by_css_id(dvc_css_id) || - create(:user, css_id: dvc_css_id, station_id: 101) + dvc = User.find_or_create_by(css_id: dvc_css_id, station_id: 101) dvc_team = DvcTeam.for_dvc(dvc) || DvcTeam.create_for_dvc(dvc) judges.each do |css_id| - dvc_team.add_user( - User.find_by_css_id(css_id) || - create(:user, css_id: css_id, station_id: 101) - ) + dvc_team.add_user(User.find_or_create_by(css_id: css_id, station_id: 101)) end end end def create_transcription_team - transcription_member = User.find_by_css_id("TRANSCRIPTION_USER") || - create( - :user, - css_id: "TRANSCRIPTION_USER", - station_id: 101, - full_name: "Noel TranscriptionUser Vasquez" - ) + transcription_member = User.find_or_create_by( + css_id: "TRANSCRIPTION_USER", + station_id: 101, + full_name: "Noel TranscriptionUser Vasquez" + ) TranscriptionTeam.singleton.add_user(transcription_member) end def create_hearings_user - hearings_member = User.find_by_css_id("BVATWARNER") || - create(:user, css_id: "BVATWARNER", station_id: 101) + hearings_member = User.find_or_create_by(css_id: "BVATWARNER", station_id: 101) HearingsManagement.singleton.add_user(hearings_member) HearingAdmin.singleton.add_user(hearings_member) end @@ -487,27 +481,23 @@ def create_mail_team_user end def create_clerk_of_the_board_users - atty = User.find_by(css_id: "COB_USER") || - create( - :user, - :with_vacols_attorney_record, - station_id: 101, - css_id: "COB_USER", - full_name: "Clark ClerkOfTheBoardUser Bard", - roles: ["Hearing Prep", "Mail Intake"] - ) + + atty = User.find_by(css_id: "COB_USER") || + create( + :user, + :with_vacols_attorney_record, + station_id: 101, + css_id: "COB_USER", + full_name: "Clark ClerkOfTheBoardUser Bard", + roles: ["Hearing Prep", "Mail Intake"] + ) ClerkOfTheBoard.singleton.add_user(atty) - judge = User.find_or_create_by(full_name: "Judith COTB Judge", - station_id: 101, css_id: "BVACOTBJUDGE", - roles: ["Hearing Prep", "Mail Intake"]) + judge = User.find_or_create_by(full_name: "Judith COTB Judge", station_id: 101, css_id: "BVACOTBJUDGE", roles: ["Hearing Prep", "Mail Intake"]) create(:staff, :judge_role, sdomainid: judge.css_id) ClerkOfTheBoard.singleton.add_user(judge) - admin = User.find_or_create_by!(full_name: "Ty ClerkOfTheBoardAdmin Cobb", - station_id: 101, - css_id: "BVATCOBB", - roles: ["Hearing Prep", "Mail Intake"]) + admin = User.find_or_create_by!(full_name: "Ty ClerkOfTheBoardAdmin Cobb", station_id: 101, css_id: "BVATCOBB", roles: ["Hearing Prep", "Mail Intake"]) ClerkOfTheBoard.singleton.add_user(admin) OrganizationsUser.make_user_admin(admin, ClerkOfTheBoard.singleton) From bcfdafc70fb2122b7b74ea19c1716b32c2792ac1 Mon Sep 17 00:00:00 2001 From: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:13:20 -0400 Subject: [PATCH 28/44] ricky/APPEALS-44044 (#21814) * first pass at legacy_docket method and spec * second iteration of legacy_docket and spec * adds method to age_of_oldest methods and updates test scenarios * Updated testing and expanded method missing logic * Updated legacy_docket_spec file and tests * Updated boolean logic, updated rspecs and factories * Updated rspec tests to include factories * updates docket_spec line 317 and factory for disable_ama_non_priority_direct_review --------- Co-authored-by: Michael Beard --- app/models/case_distribution_lever.rb | 7 ++++ app/models/dockets/legacy_docket.rb | 18 +++++++++- spec/factories/case_distribution_lever.rb | 36 +++++++++++++++++++ .../jobs/incomplete_distributions_job_spec.rb | 2 ++ ...ush_priority_appeals_to_judges_job_spec.rb | 2 ++ .../by_docket_date_distribution_spec.rb | 2 ++ spec/models/distribution_spec.rb | 2 ++ spec/models/docket_spec.rb | 1 + spec/models/dockets/legacy_docket_spec.rb | 28 +++++++++++++++ 9 files changed, 97 insertions(+), 1 deletion(-) diff --git a/app/models/case_distribution_lever.rb b/app/models/case_distribution_lever.rb index b8d36733063..9e1eb17a446 100644 --- a/app/models/case_distribution_lever.rb +++ b/app/models/case_distribution_lever.rb @@ -30,6 +30,11 @@ class CaseDistributionLever < ApplicationRecord #{Constants.DISTRIBUTION.nod_adjustment} ).freeze + BOOLEAN_LEVERS = %W( + #{Constants.DISTRIBUTION.disable_legacy_priority} + #{Constants.DISTRIBUTION.disable_legacy_non_priority} + ).freeze + def history_value if combination_lever? combination_value @@ -184,6 +189,8 @@ def method_missing_value(name) Integer(lever) elsif FLOAT_LEVERS.include?(name) Float(lever) + elsif BOOLEAN_LEVERS.include?(name) + ActiveModel::Type::Boolean.new.cast(lever) else lever end diff --git a/app/models/dockets/legacy_docket.rb b/app/models/dockets/legacy_docket.rb index fa54536d5b2..7926607c370 100644 --- a/app/models/dockets/legacy_docket.rb +++ b/app/models/dockets/legacy_docket.rb @@ -53,14 +53,20 @@ def age_of_oldest_priority_appeal end def age_of_n_oldest_genpop_priority_appeals(num) + return [] unless ready_priority_nonpriority_legacy_appeals(priority: true) + LegacyAppeal.repository.age_of_n_oldest_genpop_priority_appeals(num) end def age_of_n_oldest_priority_appeals_available_to_judge(judge, num) + return [] unless ready_priority_nonpriority_legacy_appeals(priority: true) + LegacyAppeal.repository.age_of_n_oldest_priority_appeals_available_to_judge(judge, num) end def age_of_n_oldest_nonpriority_appeals_available_to_judge(judge, num) + return [] unless ready_priority_nonpriority_legacy_appeals(priority: false) + LegacyAppeal.repository.age_of_n_oldest_nonpriority_appeals_available_to_judge(judge, num) end @@ -70,13 +76,22 @@ def should_distribute?(distribution, style: "push", genpop: "any") (style == "request" && !JudgeTeam.for_judge(distribution.judge)&.ama_only_request) end + def ready_priority_nonpriority_legacy_appeals(priority: false) + value = priority ? CaseDistributionLever.disable_legacy_priority : CaseDistributionLever.disable_legacy_non_priority + !value + end + # rubocop:disable Metrics/ParameterLists def distribute_appeals(distribution, style: "push", priority: false, genpop: "any", limit: 1, range: nil) return [] unless should_distribute?(distribution, style: style, genpop: genpop) if priority + return [] unless ready_priority_nonpriority_legacy_appeals(priority: true) + distribute_priority_appeals(distribution, style: style, genpop: genpop, limit: limit) else + return [] unless ready_priority_nonpriority_legacy_appeals(priority: false) + distribute_nonpriority_appeals(distribution, style: style, genpop: genpop, limit: limit, range: range) end end @@ -84,6 +99,7 @@ def distribute_appeals(distribution, style: "push", priority: false, genpop: "an def distribute_priority_appeals(distribution, style: "push", genpop: "any", limit: 1) return [] unless should_distribute?(distribution, style: style, genpop: genpop) + return [] unless ready_priority_nonpriority_legacy_appeals(priority: true) LegacyAppeal.repository.distribute_priority_appeals(distribution.judge, genpop, limit).map do |record| next unless existing_distribution_case_may_be_redistributed?(record["bfkey"], distribution) @@ -102,7 +118,7 @@ def distribute_nonpriority_appeals(distribution, limit: 1, bust_backlog: false) return [] unless should_distribute?(distribution, style: style, genpop: genpop) - + return [] unless ready_priority_nonpriority_legacy_appeals(priority: false) return [] if !range.nil? && range <= 0 LegacyAppeal.repository.distribute_nonpriority_appeals( diff --git a/spec/factories/case_distribution_lever.rb b/spec/factories/case_distribution_lever.rb index bbe54153c89..693fcfa1372 100644 --- a/spec/factories/case_distribution_lever.rb +++ b/spec/factories/case_distribution_lever.rb @@ -71,6 +71,42 @@ lever_group_order { 1002 } end + trait :disable_legacy_non_priority do + item { "disable_legacy_non_priority" } + title { "ACD Disable Legacy Non-priority" } + description { "" } + data_type { "boolean" } + value { false } + unit { "" } + algorithms_used { %w[docket proportion] } + lever_group { "docket_levers" } + lever_group_order { 101 } + end + + trait :disable_legacy_priority do + item { "disable_legacy_priority" } + title { "ACD Disable Legacy Priority" } + description { "" } + data_type { "boolean" } + value { false } + unit { "" } + algorithms_used { %w[docket proportion] } + lever_group { "docket_levers" } + lever_group_order { 105 } + end + + trait :disable_ama_non_priority_direct_review do + item { "disable_ama_non_priority_direct_review" } + title { "ACD Disable AMA Non-Priority Direct Review" } + description { "" } + data_type { "boolean" } + value { false } + unit { "" } + algorithms_used { %w[docket proportion] } + lever_group { "docket_levers" } + lever_group_order { 103 } + end + trait :ama_hearings_start_distribution_prior_to_goals do item { "ama_hearings_start_distribution_prior_to_goals" } title { "AMA Hearings Start Distribution Prior to Goals" } diff --git a/spec/jobs/incomplete_distributions_job_spec.rb b/spec/jobs/incomplete_distributions_job_spec.rb index 5a027852712..07f5e444d36 100644 --- a/spec/jobs/incomplete_distributions_job_spec.rb +++ b/spec/jobs/incomplete_distributions_job_spec.rb @@ -14,6 +14,8 @@ create(:case_distribution_lever, :ama_hearing_case_affinity_days) create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) create(:case_distribution_lever, :ama_direct_review_start_distribution_prior_to_goals) + create(:case_distribution_lever, :disable_legacy_non_priority) + create(:case_distribution_lever, :disable_legacy_priority) end context ".perform" do diff --git a/spec/jobs/push_priority_appeals_to_judges_job_spec.rb b/spec/jobs/push_priority_appeals_to_judges_job_spec.rb index ba5fb2b658e..04dc6eaca73 100644 --- a/spec/jobs/push_priority_appeals_to_judges_job_spec.rb +++ b/spec/jobs/push_priority_appeals_to_judges_job_spec.rb @@ -10,6 +10,8 @@ create(:case_distribution_lever, :ama_hearing_case_affinity_days) create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) create(:case_distribution_lever, :ama_direct_review_start_distribution_prior_to_goals) + create(:case_distribution_lever, :disable_legacy_non_priority) + create(:case_distribution_lever, :disable_legacy_priority) end def to_judge_hash(arr) diff --git a/spec/models/concerns/by_docket_date_distribution_spec.rb b/spec/models/concerns/by_docket_date_distribution_spec.rb index e32a7807d18..7d3218e7382 100644 --- a/spec/models/concerns/by_docket_date_distribution_spec.rb +++ b/spec/models/concerns/by_docket_date_distribution_spec.rb @@ -20,6 +20,8 @@ def batch_size create(:case_distribution_lever, :batch_size_per_attorney) create(:case_distribution_lever, :ama_hearing_case_affinity_days) create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) + create(:case_distribution_lever, :disable_legacy_non_priority) + create(:case_distribution_lever, :disable_legacy_priority) end # used to put {num} ambiguous objects into an array to mock the return array from requested_distribution diff --git a/spec/models/distribution_spec.rb b/spec/models/distribution_spec.rb index 7cd8f481721..b670be12838 100644 --- a/spec/models/distribution_spec.rb +++ b/spec/models/distribution_spec.rb @@ -17,6 +17,8 @@ create(:case_distribution_lever, :cavc_affinity_days) create(:case_distribution_lever, :ama_hearing_case_affinity_days) create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) + create(:case_distribution_lever, :disable_legacy_non_priority) + create(:case_distribution_lever, :disable_legacy_priority) end context "validations" do diff --git a/spec/models/docket_spec.rb b/spec/models/docket_spec.rb index 53589440564..c6c2ea3d6c6 100644 --- a/spec/models/docket_spec.rb +++ b/spec/models/docket_spec.rb @@ -6,6 +6,7 @@ before do create(:case_distribution_lever, :cavc_affinity_days) create(:case_distribution_lever, :request_more_cases_minimum) + create(:case_distribution_lever, :disable_ama_non_priority_direct_review) end context "docket" do diff --git a/spec/models/dockets/legacy_docket_spec.rb b/spec/models/dockets/legacy_docket_spec.rb index a3be1b7732f..3763a52f145 100644 --- a/spec/models/dockets/legacy_docket_spec.rb +++ b/spec/models/dockets/legacy_docket_spec.rb @@ -4,6 +4,8 @@ before do create(:case_distribution_lever, :request_more_cases_minimum) create(:case_distribution_lever, :nod_adjustment) + create(:case_distribution_lever, :disable_legacy_non_priority) + create(:case_distribution_lever, :disable_legacy_priority) end let(:docket) do @@ -25,6 +27,32 @@ end end + context "#ready_priority_nonpriority_legacy_appeals" do + context "when priority is true" do + it "returns false when the lever is set to true" do + CaseDistributionLever.where(item: "disable_legacy_priority").update_all(value: true) + expect(docket.ready_priority_nonpriority_legacy_appeals(priority: true)).to be_falsey + end + + it "returns true when the lever is set to false" do + CaseDistributionLever.where(item: "disable_legacy_priority").update_all(value: false) + expect(docket.ready_priority_nonpriority_legacy_appeals(priority: true)).to be_truthy + end + end + + context "when priority is false" do + it "returns false when the lever is set to true" do + CaseDistributionLever.where(item: "disable_legacy_non_priority").update_all(value: true) + expect(docket.ready_priority_nonpriority_legacy_appeals(priority: false)).to be_falsey + end + + it "returns true when the lever is set to false" do + CaseDistributionLever.where(item: "disable_legacy_non_priority").update_all(value: false) + expect(docket.ready_priority_nonpriority_legacy_appeals(priority: false)).to be_truthy + end + end + end + context "#genpop_priority_count" do it "calls AppealRepository.genpop_priority_count" do expect(AppealRepository).to receive(:genpop_priority_count) From 6d7243a07e512f8e944d5d6600ca2caefbe23ce1 Mon Sep 17 00:00:00 2001 From: Michael Beard <131783726+mbeardy@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:20:56 -0500 Subject: [PATCH 29/44] updates ready_priority_nonpriority_appeals to public_send, updates test (#21844) --- app/models/docket.rb | 12 ++++-------- spec/models/docket_spec.rb | 6 ++++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/models/docket.rb b/app/models/docket.rb index e1e3c35c975..1c8aca9cef2 100644 --- a/app/models/docket.rb +++ b/app/models/docket.rb @@ -33,18 +33,14 @@ def appeals(priority: nil, genpop: nil, ready: nil, judge: nil) end # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity - def build_lever_item(docket_type, priority_status) - "disable_ama_#{priority_status}_#{docket_type.downcase}" - end - def ready_priority_nonpriority_appeals(priority: false, ready: true, judge: nil, genpop: nil) priority_status = priority ? PRIORITY : NON_PRIORITY appeals = appeals(priority: priority, ready: ready, genpop: genpop, judge: judge) - lever_item = build_lever_item(docket_type, priority_status) - lever = CaseDistributionLever.find_by_item(Constants::DISTRIBUTION[lever_item]) - lever_value = lever&.value + lever_item = "disable_ama_#{priority_status}_#{docket_type.downcase}" + item = CaseDistributionLever.find_by_item(lever_item) + value = item ? CaseDistributionLever.public_send(lever_item) : nil - if lever_value == "true" + if value == "true" appeals.none else appeals diff --git a/spec/models/docket_spec.rb b/spec/models/docket_spec.rb index c6c2ea3d6c6..e53bb3ea608 100644 --- a/spec/models/docket_spec.rb +++ b/spec/models/docket_spec.rb @@ -342,8 +342,10 @@ end it "correctly builds the lever item based on docket type" do - expect(docket).to receive(:docket_type).exactly(2).times.and_return("direct_review") - expect(docket).to receive(:build_lever_item).with("direct_review", "non_priority").and_call_original + expect(docket).to receive(:docket_type).twice.and_return("direct_review") + lever_item_key = "disable_ama_non_priority_direct_review" + expect(CaseDistributionLever).to receive(:find_by_item).with(lever_item_key).and_return(double(value: "false")) + expect(CaseDistributionLever).to receive(:public_send).with(lever_item_key).and_return("false") docket.ready_priority_nonpriority_appeals(priority: false) end end From 49b821065d4ab039698a7bd865dad796b7bf1ca9 Mon Sep 17 00:00:00 2001 From: kristeja <112115264+kristeja@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:18:19 -0700 Subject: [PATCH 30/44] APPEALS-36333 first iteration of docket time goal and prior to goal (#21523) * APPEALS-36333 first iteration of docket time goal and prior to goal * APPEALS-36333 address pr comments and moved the logic to docket model * APPEALS-36333 Added specs for docket and hearing request docket models * refactored code and moved it to distribution scopes * APPEALS-36333 address pr comments and refactor code * APPEALS-43996 updated lever name in all the required files to fix spec failures * APPEALS-44000 revert hearing request docket model changes to keept it in sync with feature * APPEALS-44000 updated hearing request docket model specs * APPEALS-44000 fixed jest tests * APPEALS-44000 fixed broken rspecs * APPEALS-43996 fix rubocop warnings * APPEALS-43996 fix push priority job spec failures * APPEALS-43996 Updated case distribution lever and docker model and refactor specs * APPEALS-43996 added rake task to update case distribution lever item in db * APPEALS-43996 update docket model to handle the scenario when the lever being disabled by a user via the UI * APPEALS-43996 update section title * APPEALS-43996 revert rake task * APPEALS-43996 add new factories to case distribution lever and fix spec failures * APPEALS-45191. Added columns to the appeals ready to distribute query and made some tweaks (#21834) * APPEALS-45191. Added columns to the appeals ready to distribute query and made some tweaks * APPEALS-45191. Refactor and fix lint issues --------- Co-authored-by: SHarshain * refactor the logic and fix docket spec failures * changed the call times to 3 times * APPEALS-36333 fixed the docket time goal to check the existence of the item before using the public send to get the value --------- Co-authored-by: SHarshain <133917878+SHarshain@users.noreply.github.com> Co-authored-by: SHarshain Co-authored-by: 631966 --- app/models/case_distribution_lever.rb | 4 +- app/models/docket.rb | 25 ++++ app/queries/appeals_ready_for_distribution.rb | 46 ++++--- client/app/caseDistribution/constants.js | 6 +- client/constants/DISTRIBUTION.json | 4 +- .../components/DocketTimeGoals.test.js | 4 +- .../reducers/levers/LeversReducer.test.js | 4 +- .../test/data/adminCaseDistributionLevers.js | 8 +- .../data/formattedCaseDistributionData.js | 4 +- db/seeds/case_distribution_levers.rb | 4 +- spec/factories/case_distribution_lever.rb | 130 ++++++++++++++++++ .../ama_np_dist_goals_by_docket_lever_spec.rb | 4 +- .../queue/scm_judge_assignment_spec.rb | 1 + .../jobs/incomplete_distributions_job_spec.rb | 3 + ...ush_priority_appeals_to_judges_job_spec.rb | 26 +++- spec/models/case_distribution_lever_spec.rb | 6 +- .../by_docket_date_distribution_spec.rb | 3 + spec/models/distribution_spec.rb | 3 + spec/models/docket_coordinator_spec.rb | 1 + spec/models/docket_spec.rb | 30 +++- .../dockets/hearing_request_docket_spec.rb | 36 ++++- 21 files changed, 297 insertions(+), 55 deletions(-) diff --git a/app/models/case_distribution_lever.rb b/app/models/case_distribution_lever.rb index 9e1eb17a446..ac3613e484d 100644 --- a/app/models/case_distribution_lever.rb +++ b/app/models/case_distribution_lever.rb @@ -21,7 +21,9 @@ class CaseDistributionLever < ApplicationRecord #{Constants.DISTRIBUTION.ama_hearing_case_affinity_days} #{Constants.DISTRIBUTION.cavc_affinity_days} #{Constants.DISTRIBUTION.ama_evidence_submission_docket_time_goals} - #{Constants.DISTRIBUTION.ama_hearings_docket_time_goals} + #{Constants.DISTRIBUTION.ama_hearing_docket_time_goals} + #{Constants.DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals} + #{Constants.DISTRIBUTION.ama_evidence_submission_start_distribution_prior_to_goals} ).freeze FLOAT_LEVERS = %W( diff --git a/app/models/docket.rb b/app/models/docket.rb index 1c8aca9cef2..3f10887c876 100644 --- a/app/models/docket.rb +++ b/app/models/docket.rb @@ -42,6 +42,8 @@ def ready_priority_nonpriority_appeals(priority: false, ready: true, judge: nil, if value == "true" appeals.none + elsif calculate_days_for_time_goal_with_prior_to_goal > 0 + appeals.where("appeals.receipt_date <= ?", calculate_days_for_time_goal_with_prior_to_goal.days.ago) else appeals end @@ -156,6 +158,29 @@ def self.nonpriority_decisions_per_year .pluck(:id).size end + def calculate_days_for_time_goal_with_prior_to_goal + return 0 unless docket_time_goal > 0 + + docket_time_goal - start_distribution_prior_to_goal + end + + def docket_time_goal + @docket_time_goal ||= begin + does_lever_exist = CaseDistributionLever.exists?(item: "ama_#{docket_type}_docket_time_goals") + does_lever_exist ? CaseDistributionLever.public_send("ama_#{docket_type}_docket_time_goals") : 0 + end + end + + def start_distribution_prior_to_goal + @start_distribution_prior_to_goal ||= begin + lever = CaseDistributionLever.find_by( + item: "ama_#{docket_type}_start_distribution_prior_to_goals", + is_toggle_active: true + ) + lever ? Integer(lever.value) : 0 + end + end + private # :reek:ControlParameter diff --git a/app/queries/appeals_ready_for_distribution.rb b/app/queries/appeals_ready_for_distribution.rb index e977a25968c..31e4e38c52a 100644 --- a/app/queries/appeals_ready_for_distribution.rb +++ b/app/queries/appeals_ready_for_distribution.rb @@ -9,6 +9,8 @@ class AppealsReadyForDistribution cavc: "CAVC", receipt_date: "Receipt Date", ready_for_distribution_at: "Ready for Distribution at", + target_distro_date: "Target Distro Date", + days_before_goal_date: "Days Before Goal Date", hearing_judge: "Hearing Judge", veteran_file_number: "Veteran File number", veteran_name: "Veteran" @@ -41,29 +43,27 @@ def self.ready_appeals .flat_map do |sym, docket| appeals = docket.ready_to_distribute_appeals if sym == :legacy - legacy_rows(appeals, sym) + legacy_rows(appeals, docket, sym) else - ama_rows(appeals, sym) + ama_rows(appeals, docket, sym) end end end - def self.legacy_rows(appeals, docket) + def self.legacy_rows(appeals, docket, sym) appeals.map do |appeal| veteran_name = FullName.new(appeal["snamef"], nil, appeal["snamel"]).to_s vlj_name = FullName.new(appeal["vlj_namef"], nil, appeal["vlj_namel"]).to_s - hearing_judge = if vlj_name.empty? - nil - else - vlj_name - end + hearing_judge = vlj_name.empty? ? nil : vlj_name { docket_number: appeal["tinum"], - docket: docket.to_s, + docket: sym.to_s, aod: appeal["aod"] == 1, cavc: appeal["cavc"] == 1, receipt_date: appeal["bfd19"], ready_for_distribution_at: appeal["bfdloout"], + target_distro_date: docket.docket_time_goal, + days_before_goal_date: docket.start_distribution_prior_to_goal, hearing_judge: hearing_judge, veteran_file_number: appeal["ssn"] || appeal["bfcorlid"], veteran_name: veteran_name @@ -71,25 +71,21 @@ def self.legacy_rows(appeals, docket) end end - def self.ama_rows(appeals, docket) + def self.ama_rows(appeals, docket, sym) appeals.map do |appeal| # This comes from the DistributionTask's assigned_at date - ready_for_distribution_at = appeal.tasks - .filter { |task| task.class == DistributionTask && task.status == Constants.TASK_STATUSES.assigned } - .first&.assigned_at - + ready_for_distribution_at = distribution_task_query(appeal) # only look for hearings that were held - hearing_judge = appeal.hearings - .filter { |hearing| hearing.disposition = Constants.HEARING_DISPOSITION_TYPES.held } - .first&.judge&.full_name - + hearing_judge = with_held_hearings(appeal) { docket_number: appeal.docket_number, - docket: docket.to_s, + docket: sym.to_s, aod: appeal.aod, cavc: appeal.cavc, receipt_date: appeal.receipt_date, ready_for_distribution_at: ready_for_distribution_at, + target_distro_date: docket.docket_time_goal, + days_before_goal_date: docket.start_distribution_prior_to_goal, hearing_judge: hearing_judge, veteran_file_number: appeal.veteran_file_number, veteran_name: appeal.veteran&.name.to_s @@ -97,4 +93,16 @@ def self.ama_rows(appeals, docket) } end end + + def self.distribution_task_query(appeal) + appeal.tasks + .filter { |task| task.class == DistributionTask && task.status == Constants.TASK_STATUSES.assigned } + .first&.assigned_at + end + + def self.with_held_hearings(appeal) + appeal.hearings + .filter { |hearing| hearing.disposition = Constants.HEARING_DISPOSITION_TYPES.held } + .first&.judge&.full_name + end end diff --git a/client/app/caseDistribution/constants.js b/client/app/caseDistribution/constants.js index 56579d66055..82989593863 100644 --- a/client/app/caseDistribution/constants.js +++ b/client/app/caseDistribution/constants.js @@ -7,14 +7,14 @@ export const Constant = { }; export const sectionTitles = { - [DISTRIBUTION.ama_hearings_start_distribution_prior_to_goals]: DISTRIBUTION.ama_hearings_section_title, + [DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals]: DISTRIBUTION.ama_hearings_section_title, [DISTRIBUTION.ama_direct_review_start_distribution_prior_to_goals]: DISTRIBUTION.ama_direct_review_section_title, [DISTRIBUTION.ama_evidence_submission_start_distribution_prior_to_goals]: DISTRIBUTION.ama_evidence_submission_section_title }; export const docketTimeGoalPriorMappings = { - [DISTRIBUTION.ama_hearings_start_distribution_prior_to_goals]: - DISTRIBUTION.ama_hearings_docket_time_goals, + [DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals]: + DISTRIBUTION.ama_hearing_docket_time_goals, [DISTRIBUTION.ama_direct_review_start_distribution_prior_to_goals]: DISTRIBUTION.ama_direct_review_docket_time_goals, [DISTRIBUTION.ama_evidence_submission_start_distribution_prior_to_goals]: diff --git a/client/constants/DISTRIBUTION.json b/client/constants/DISTRIBUTION.json index 75d65f5e446..d4b1809cd9c 100644 --- a/client/constants/DISTRIBUTION.json +++ b/client/constants/DISTRIBUTION.json @@ -13,10 +13,10 @@ "aoj_affinity_days": "aoj_affinity_days", "aoj_aod_affinity_days": "aoj_aod_affinity_days", "aoj_cavc_affinity_days": "aoj_cavc_affinity_days", - "ama_hearings_start_distribution_prior_to_goals": "ama_hearings_start_distribution_prior_to_goals", + "ama_hearing_start_distribution_prior_to_goals": "ama_hearing_start_distribution_prior_to_goals", "ama_direct_review_start_distribution_prior_to_goals": "ama_direct_review_start_distribution_prior_to_goals", "ama_evidence_submission_start_distribution_prior_to_goals": "ama_evidence_submission_start_distribution_prior_to_goals", - "ama_hearings_docket_time_goals": "ama_hearings_docket_time_goals", + "ama_hearing_docket_time_goals": "ama_hearing_docket_time_goals", "ama_direct_review_docket_time_goals": "ama_direct_review_docket_time_goals", "ama_evidence_submission_docket_time_goals": "ama_evidence_submission_docket_time_goals", "days_before_goal_due_for_distribution": "days_before_goal_due_for_distribution", diff --git a/client/test/app/caseDistribution/components/DocketTimeGoals.test.js b/client/test/app/caseDistribution/components/DocketTimeGoals.test.js index cffa7862b25..fd5ff9ab9bf 100644 --- a/client/test/app/caseDistribution/components/DocketTimeGoals.test.js +++ b/client/test/app/caseDistribution/components/DocketTimeGoals.test.js @@ -59,8 +59,8 @@ describe('Docket Time Goals Lever', () => { ); - let leverTimeGoal = wrapper.find('input[name="ama_hearings_docket_time_goals"]'); - let leverDistPrior = wrapper.find('input[name="ama_hearings_start_distribution_prior_to_goals"]'); + let leverTimeGoal = wrapper.find('input[name="ama_hearing_docket_time_goals"]'); + let leverDistPrior = wrapper.find('input[name="ama_hearing_start_distribution_prior_to_goals"]'); waitFor(() => expect(leverTimeGoal).toHaveTextContent(testTimeGoalLever.value)); waitFor(() => expect(leverDistPrior).toHaveTextContent(testDistPriorLever.value)); diff --git a/client/test/app/caseDistribution/reducers/levers/LeversReducer.test.js b/client/test/app/caseDistribution/reducers/levers/LeversReducer.test.js index ddabfc370b6..741dadfc08f 100644 --- a/client/test/app/caseDistribution/reducers/levers/LeversReducer.test.js +++ b/client/test/app/caseDistribution/reducers/levers/LeversReducer.test.js @@ -145,14 +145,14 @@ describe('Lever reducer', () => { type: ACTIONS.UPDATE_LEVER_IS_TOGGLE_ACTIVE, payload: { leverGroup: 'docket_distribution_prior', - leverItem: 'ama_hearings_start_distribution_prior_to_goals', + leverItem: 'ama_hearing_start_distribution_prior_to_goals', toggleValue: false } }; const combinationLevers = initialState.levers.docket_distribution_prior; const updatedCombinationLevers = combinationLevers.map((lever) => { - if (lever.item === 'ama_hearings_start_distribution_prior_to_goals') { + if (lever.item === 'ama_hearing_start_distribution_prior_to_goals') { return { ...lever, is_toggle_active: false diff --git a/client/test/data/adminCaseDistributionLevers.js b/client/test/data/adminCaseDistributionLevers.js index 2c0e57b2b3a..328b92a06d3 100644 --- a/client/test/data/adminCaseDistributionLevers.js +++ b/client/test/data/adminCaseDistributionLevers.js @@ -305,7 +305,7 @@ export const unknownDataTypeStaticLevers = [ export const mockDocketDistributionPriorLevers = [ { - item: 'ama_hearings_start_distribution_prior_to_goals', + item: 'ama_hearing_start_distribution_prior_to_goals', title: 'AMA Hearings Start Distribution Prior to Goals', description: '', data_type: 'combination', @@ -437,7 +437,7 @@ export const testingBatchLeversUpdatedToSave = [ export const mockDocketTimeGoalsLevers = [ { - item: 'ama_hearings_docket_time_goals', + item: 'ama_hearing_docket_time_goals', title: 'AMA Hearings Docket Time Goals', data_type: 'number', value: 365, @@ -671,7 +671,7 @@ export const mockHistoryPayload = [ export const mockDocketDistributionPriorLeversReturn = [ { - item: 'ama_hearings_start_distribution_prior_to_goals', + item: 'ama_hearing_start_distribution_prior_to_goals', title: 'AMA Hearings Start Distribution Prior to Goals', description: '', data_type: 'combination', @@ -720,7 +720,7 @@ export const mockDocketDistributionPriorLeversReturn = [ } ]; export const mockCombinationReturn = { - item: 'ama_hearings_start_distribution_prior_to_goals', + item: 'ama_hearing_start_distribution_prior_to_goals', title: 'AMA Hearings Start Distribution Prior to Goals', description: '', data_type: 'combination', diff --git a/client/test/data/formattedCaseDistributionData.js b/client/test/data/formattedCaseDistributionData.js index c7970554576..086a884d721 100644 --- a/client/test/data/formattedCaseDistributionData.js +++ b/client/test/data/formattedCaseDistributionData.js @@ -650,7 +650,7 @@ export const updatedLevers = [ }, { id: 17, - item: DISTRIBUTION.ama_hearings_start_distribution_prior_to_goals, + item: DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals, title: 'AMA Hearings Start Distribution Prior to Goals', description: '', data_type: ACD_LEVERS.data_types.combination, @@ -719,7 +719,7 @@ export const updatedLevers = [ }, { id: 20, - item: DISTRIBUTION.ama_hearings_docket_time_goals, + item: DISTRIBUTION.ama_hearing_docket_time_goals, title: 'AMA Hearings Docket Time Goals', data_type: ACD_LEVERS.data_types.number, value: 435, diff --git a/db/seeds/case_distribution_levers.rb b/db/seeds/case_distribution_levers.rb index c13588c9445..cdb40f6e2fd 100644 --- a/db/seeds/case_distribution_levers.rb +++ b/db/seeds/case_distribution_levers.rb @@ -456,7 +456,7 @@ def levers lever_group_order: 3006 }, { - item: Constants.DISTRIBUTION.ama_hearings_start_distribution_prior_to_goals, + item: Constants.DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals, title: "AMA Hearings Start Distribution Prior to Goals", description: "", data_type: Constants.ACD_LEVERS.data_types.combination, @@ -528,7 +528,7 @@ def levers lever_group_order: 4002 }, { - item: Constants.DISTRIBUTION.ama_hearings_docket_time_goals, + item: Constants.DISTRIBUTION.ama_hearing_docket_time_goals, title: "AMA Hearings Docket Time Goals", data_type: Constants.ACD_LEVERS.data_types.number, value: 730, diff --git a/spec/factories/case_distribution_lever.rb b/spec/factories/case_distribution_lever.rb index 693fcfa1372..623d8f4b8a4 100644 --- a/spec/factories/case_distribution_lever.rb +++ b/spec/factories/case_distribution_lever.rb @@ -256,5 +256,135 @@ lever_group { "affinity" } lever_group_order { 3000 } end + + trait :ama_direct_review_docket_time_goals do + item { "ama_direct_review_docket_time_goals" } + title { "AMA Direct Review Docket Time Goals" } + data_type { "number" } + value { 365 } + unit { "days" } + algorithms_used { ["docket"] } + lever_group { "docket_time_goal" } + lever_group_order { 4004 } + end + + trait :ama_evidence_submission_docket_time_goals do + item { "ama_evidence_submission_docket_time_goals" } + title { "AMA Evidence Submission Docket Time Goals" } + data_type { "number" } + value { 550 } + unit { "days" } + algorithms_used { ["docket"] } + lever_group { "docket_time_goal" } + lever_group_order { 4004 } + end + + trait :ama_hearing_docket_time_goals do + item { "ama_hearing_docket_time_goals" } + title { "AMA Hearing Submission Docket Time Goals" } + data_type { "number" } + value { 730 } + unit { "days" } + algorithms_used { ["docket"] } + lever_group { "docket_time_goal" } + lever_group_order { 4004 } + end + + trait :ama_hearing_start_distribution_prior_to_goals do + item { "ama_hearing_start_distribution_prior_to_goals" } + title { "AMA Hearings Start Distribution Prior to Goals" } + data_type { "combination" } + options do + [ + { + item: "value", + data_type: "boolean", + value: true, + text: "This feature is turned on or off", + unit: "" + } + ] + end + value { 60 } + unit { "days" } + is_toggle_active { true } + algorithms_used { ["docket"] } + lever_group { "docket_distribution_prior" } + lever_group_order { 4000 } + end + + trait :ama_direct_review_start_distribution_prior_to_goals do + item { "ama_direct_review_start_distribution_prior_to_goals" } + title { "AMA Direct Review Start Distribution Prior to Goals" } + data_type { "combination" } + options do + [ + { + item: "value", + data_type: "boolean", + value: true, + text: "This feature is turned on or off", + unit: "" + } + ] + end + value { 365 } + unit { "days" } + is_toggle_active { true } + algorithms_used { ["docket"] } + lever_group { "docket_distribution_prior" } + lever_group_order { 4000 } + end + + trait :ama_evidence_submission_review_start_distribution_prior_to_goals do + item { "ama_evidence_submission_start_distribution_prior_to_goals" } + title { "AMA Evidence Submission Start Distribution Prior to Goals" } + data_type { "combination" } + options do + [ + { + item: "value", + data_type: "boolean", + value: true, + text: "This feature is turned on or off", + unit: "" + } + ] + end + value { 365 } + unit { "days" } + is_toggle_active { true } + algorithms_used { ["docket"] } + lever_group { "docket_distribution_prior" } + lever_group_order { 4000 } + end + + trait :disable_ama_non_priority_direct_review do + item { "disable_ama_non_priority_direct_review" } + title { "ACD Disable AMA Non-Priority Direct Review" } + data_type { "boolean" } + options do + [ + { + displayText: "On", + name: Constants.DISTRIBUTION.disable_ama_non_priority_direct_review, + value: "true", + disabled: false + }, + { + displayText: "Off", + name: Constants.DISTRIBUTION.disable_ama_non_priority_direct_review, + value: "false", + disabled: false + } + ] + end + value { false } + unit { "days" } + algorithms_used { %w(proportion docket) } + lever_group { "docket_levers" } + lever_group_order { 103 } + control_group { "non_priority" } + end end end diff --git a/spec/feature/automatic_case_distribution/acd_levers/ama_np_dist_goals_by_docket_lever_spec.rb b/spec/feature/automatic_case_distribution/acd_levers/ama_np_dist_goals_by_docket_lever_spec.rb index bc526e3bb61..a775064fd46 100644 --- a/spec/feature/automatic_case_distribution/acd_levers/ama_np_dist_goals_by_docket_lever_spec.rb +++ b/spec/feature/automatic_case_distribution/acd_levers/ama_np_dist_goals_by_docket_lever_spec.rb @@ -9,11 +9,11 @@ User.authenticate!(user: user) end - let(:ama_hearings) { Constants.DISTRIBUTION.ama_hearings_start_distribution_prior_to_goals } + let(:ama_hearings) { Constants.DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals } let(:ama_direct_reviews) { Constants.DISTRIBUTION.ama_direct_review_start_distribution_prior_to_goals } let(:ama_evidence_submissions) { Constants.DISTRIBUTION.ama_evidence_submission_start_distribution_prior_to_goals } - let(:ama_hearings_field) { Constants.DISTRIBUTION.ama_hearings_docket_time_goals } + let(:ama_hearings_field) { Constants.DISTRIBUTION.ama_hearing_docket_time_goals } let(:ama_direct_reviews_field) { Constants.DISTRIBUTION.ama_direct_review_docket_time_goals } let(:ama_evidence_submissions_field) { Constants.DISTRIBUTION.ama_evidence_submission_docket_time_goals } diff --git a/spec/feature/queue/scm_judge_assignment_spec.rb b/spec/feature/queue/scm_judge_assignment_spec.rb index b81922cc4f4..071fe300444 100644 --- a/spec/feature/queue/scm_judge_assignment_spec.rb +++ b/spec/feature/queue/scm_judge_assignment_spec.rb @@ -117,6 +117,7 @@ allow_any_instance_of(DirectReviewDocket).to receive(:weight).and_return(10) allow_any_instance_of(DirectReviewDocket).to receive(:nonpriority_receipts_per_year).and_return(100) allow(Docket).to receive(:nonpriority_decisions_per_year).and_return(1000) + allow_any_instance_of(Docket).to receive(:calculate_days_for_time_goal_with_prior_to_goal).and_return(0) end scenario "on ama appeals" do diff --git a/spec/jobs/incomplete_distributions_job_spec.rb b/spec/jobs/incomplete_distributions_job_spec.rb index 07f5e444d36..901820f4d9a 100644 --- a/spec/jobs/incomplete_distributions_job_spec.rb +++ b/spec/jobs/incomplete_distributions_job_spec.rb @@ -14,6 +14,9 @@ create(:case_distribution_lever, :ama_hearing_case_affinity_days) create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) create(:case_distribution_lever, :ama_direct_review_start_distribution_prior_to_goals) + create(:case_distribution_lever, :ama_direct_review_docket_time_goals) + create(:case_distribution_lever, :ama_evidence_submission_docket_time_goals) + create(:case_distribution_lever, :ama_hearing_docket_time_goals) create(:case_distribution_lever, :disable_legacy_non_priority) create(:case_distribution_lever, :disable_legacy_priority) end diff --git a/spec/jobs/push_priority_appeals_to_judges_job_spec.rb b/spec/jobs/push_priority_appeals_to_judges_job_spec.rb index 04dc6eaca73..6170cb209c2 100644 --- a/spec/jobs/push_priority_appeals_to_judges_job_spec.rb +++ b/spec/jobs/push_priority_appeals_to_judges_job_spec.rb @@ -2,6 +2,8 @@ describe PushPriorityAppealsToJudgesJob, :all_dbs do before do + allow_any_instance_of(Docket).to receive(:calculate_days_for_time_goal_with_prior_to_goal).and_return(20) + create(:case_distribution_lever, :request_more_cases_minimum) create(:case_distribution_lever, :alternative_batch_size) create(:case_distribution_lever, :nod_adjustment) @@ -51,6 +53,7 @@ def to_judge_hash(arr) let(:ready_priority_bfkey2) { "12346" } let(:ready_priority_uuid) { "bece6907-3b6f-4c49-a580-6d5f2e1ca65c" } let(:ready_priority_uuid2) { "bece6907-3b6f-4c49-a580-6d5f2e1ca65d" } + let(:receipt_date) { 30.days.ago } let!(:judge_with_ready_priority_cases) do create(:user, :judge, :with_vacols_judge_record).tap do |judge| vacols_case = create( @@ -78,7 +81,8 @@ def to_judge_hash(arr) :ready_for_distribution, :advanced_on_docket_due_to_age, uuid: ready_priority_uuid, - docket_type: Constants.AMA_DOCKETS.hearing + docket_type: Constants.AMA_DOCKETS.hearing, + receipt_date: receipt_date ) most_recent = create(:hearing_day, scheduled_for: 1.day.ago) hearing = create(:hearing, judge: nil, disposition: "held", appeal: appeal, hearing_day: most_recent) @@ -109,7 +113,8 @@ def to_judge_hash(arr) appeal = create( :appeal, :ready_for_distribution, - docket_type: Constants.AMA_DOCKETS.hearing + docket_type: Constants.AMA_DOCKETS.hearing, + receipt_date: receipt_date ) most_recent = create(:hearing_day, scheduled_for: 1.day.ago) hearing = create(:hearing, judge: nil, disposition: "held", appeal: appeal, hearing_day: most_recent) @@ -142,7 +147,8 @@ def to_judge_hash(arr) appeal = create( :appeal, :advanced_on_docket_due_to_age, - docket_type: Constants.AMA_DOCKETS.hearing + docket_type: Constants.AMA_DOCKETS.hearing, + receipt_date: receipt_date ) most_recent = create(:hearing_day, scheduled_for: 1.day.ago) hearing = create(:hearing, judge: nil, disposition: "held", appeal: appeal, hearing_day: most_recent) @@ -176,7 +182,8 @@ def to_judge_hash(arr) :ready_for_distribution, :advanced_on_docket_due_to_age, uuid: "bece6907-3b6f-4c49-a580-6d5f2e1ca65d", - docket_type: Constants.AMA_DOCKETS.hearing + docket_type: Constants.AMA_DOCKETS.hearing, + receipt_date: receipt_date ) most_recent = create(:hearing_day, scheduled_for: 1.day.ago) hearing = create(:hearing, judge: nil, disposition: "held", appeal: appeal, hearing_day: most_recent) @@ -305,7 +312,8 @@ def to_judge_hash(arr) appeal = create(:appeal, :advanced_on_docket_due_to_age, :ready_for_distribution, - docket_type: Constants.AMA_DOCKETS.hearing) + docket_type: Constants.AMA_DOCKETS.hearing, + receipt_date: 1.month.ago) appeal.tasks.find_by(type: DistributionTask.name).update(assigned_at: i.months.ago) appeal.reload end @@ -315,7 +323,8 @@ def to_judge_hash(arr) appeal = create(:appeal, :type_cavc_remand, :cavc_ready_for_distribution, - docket_type: Constants.AMA_DOCKETS.evidence_submission) + docket_type: Constants.AMA_DOCKETS.evidence_submission, + receipt_date: 1.month.ago) appeal.tasks.find_by(type: DistributionTask.name).update(assigned_at: i.month.ago) appeal end @@ -335,6 +344,10 @@ def to_judge_hash(arr) let(:priority_count) { Appeal.count { |a| a.aod? || a.cavc? } + legacy_priority_cases.count } let(:priority_target) { (priority_count + judge_distributions_this_month.sum) / judges.count } + before do + ready_priority_evidence_cases.each { |appeal| appeal.update(receipt_date: 1.month.ago) } + end + context "using Automatic Case Distribution module" do it "should distribute ready priority appeals to the judges" do expect(subject.count).to eq judges.count @@ -500,6 +513,7 @@ def to_judge_hash(arr) allow_any_instance_of(PushPriorityAppealsToJudgesJob) .to receive(:priority_distributions_this_month_for_eligible_judges).and_return(previous_distributions) allow_any_instance_of(DocketCoordinator).to receive(:genpop_priority_count).and_return(20) + allow_any_instance_of(Docket).to receive(:calculate_days_for_time_goal_with_prior_to_goal).and_return(0) end after { FeatureToggle.disable!(:acd_distribute_by_docket_date) } diff --git a/spec/models/case_distribution_lever_spec.rb b/spec/models/case_distribution_lever_spec.rb index 105834af346..60a8805395e 100644 --- a/spec/models/case_distribution_lever_spec.rb +++ b/spec/models/case_distribution_lever_spec.rb @@ -12,7 +12,9 @@ ama_hearing_case_affinity_days cavc_affinity_days ama_evidence_submission_docket_time_goals - ama_hearings_docket_time_goals] + ama_hearing_docket_time_goals + ama_hearing_start_distribution_prior_to_goals + ama_evidence_submission_start_distribution_prior_to_goals] end let!(:float_levers) do %w[maximum_direct_review_proportion minimum_legacy_proportion nod_adjustment] @@ -63,7 +65,7 @@ it "validates combination data_type" do lever = CaseDistributionLever.find_by_item( - Constants.DISTRIBUTION.ama_hearings_start_distribution_prior_to_goals + Constants.DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals ) valid = lever.update(options: nil) diff --git a/spec/models/concerns/by_docket_date_distribution_spec.rb b/spec/models/concerns/by_docket_date_distribution_spec.rb index 7d3218e7382..49d56ea6454 100644 --- a/spec/models/concerns/by_docket_date_distribution_spec.rb +++ b/spec/models/concerns/by_docket_date_distribution_spec.rb @@ -20,6 +20,9 @@ def batch_size create(:case_distribution_lever, :batch_size_per_attorney) create(:case_distribution_lever, :ama_hearing_case_affinity_days) create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) + create(:case_distribution_lever, :ama_direct_review_docket_time_goals) + create(:case_distribution_lever, :ama_evidence_submission_docket_time_goals) + create(:case_distribution_lever, :ama_hearing_docket_time_goals) create(:case_distribution_lever, :disable_legacy_non_priority) create(:case_distribution_lever, :disable_legacy_priority) end diff --git a/spec/models/distribution_spec.rb b/spec/models/distribution_spec.rb index b670be12838..fe5118272e2 100644 --- a/spec/models/distribution_spec.rb +++ b/spec/models/distribution_spec.rb @@ -17,6 +17,9 @@ create(:case_distribution_lever, :cavc_affinity_days) create(:case_distribution_lever, :ama_hearing_case_affinity_days) create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) + create(:case_distribution_lever, :ama_direct_review_docket_time_goals) + create(:case_distribution_lever, :ama_evidence_submission_docket_time_goals) + create(:case_distribution_lever, :ama_hearing_docket_time_goals) create(:case_distribution_lever, :disable_legacy_non_priority) create(:case_distribution_lever, :disable_legacy_priority) end diff --git a/spec/models/docket_coordinator_spec.rb b/spec/models/docket_coordinator_spec.rb index f5c1582b56e..57d167b4a36 100644 --- a/spec/models/docket_coordinator_spec.rb +++ b/spec/models/docket_coordinator_spec.rb @@ -295,6 +295,7 @@ end describe "#genpop_priority_count" do + before { allow_any_instance_of(Docket).to receive(:calculate_days_for_time_goal_with_prior_to_goal).and_return(0) } subject { DocketCoordinator.new.genpop_priority_count } let(:expected_priority_count) { genpop_priority_cases_count } diff --git a/spec/models/docket_spec.rb b/spec/models/docket_spec.rb index e53bb3ea608..66bb4b8c46b 100644 --- a/spec/models/docket_spec.rb +++ b/spec/models/docket_spec.rb @@ -4,6 +4,12 @@ describe Docket, :all_dbs do before do + create(:case_distribution_lever, :ama_direct_review_docket_time_goals) + create(:case_distribution_lever, :ama_evidence_submission_docket_time_goals) + create(:case_distribution_lever, :ama_hearing_docket_time_goals) + create(:case_distribution_lever, :ama_hearing_start_distribution_prior_to_goals) + create(:case_distribution_lever, :ama_direct_review_start_distribution_prior_to_goals) + create(:case_distribution_lever, :ama_evidence_submission_review_start_distribution_prior_to_goals) create(:case_distribution_lever, :cavc_affinity_days) create(:case_distribution_lever, :request_more_cases_minimum) create(:case_distribution_lever, :disable_ama_non_priority_direct_review) @@ -315,15 +321,14 @@ end it "returns an empty array when the corresponding CaseDistributionLever value is true" do - CaseDistributionLever.where(item: "disable_ama_non_priority_direct_review").update(value: "true") - lever = CaseDistributionLever.find_by_item("disable_ama_non_priority_direct_review") + lever = CaseDistributionLever.find_by(item: "disable_ama_non_priority_direct_review") + lever.update(value: "true") expect(lever.value).to eq("true") result = docket.ready_priority_nonpriority_appeals(priority: false) expect(result).to eq([]) end it "returns an empty list when the corresponding CaseDistributionLever record is not found" do - allow(CaseDistributionLever).to receive(:find_by_item).and_return(nil) result = docket.ready_priority_nonpriority_appeals(priority: false) expected_result = docket.appeals(priority: false, ready: true) expect(result.map(&:id)).to eq(expected_result.map(&:id)) @@ -335,14 +340,13 @@ end it "returns the correct appeals when the lever value is false and priority is true" do - allow(CaseDistributionLever).to receive(:find_by_item).and_return(double(value: "false")) expected_appeals = docket.appeals(priority: true) result = docket.ready_priority_nonpriority_appeals(priority: true, ready: true) expect(result).to match_array(expected_appeals) end it "correctly builds the lever item based on docket type" do - expect(docket).to receive(:docket_type).twice.and_return("direct_review") + expect(docket).to receive(:docket_type).exactly(3).times.and_return("direct_review") lever_item_key = "disable_ama_non_priority_direct_review" expect(CaseDistributionLever).to receive(:find_by_item).with(lever_item_key).and_return(double(value: "false")) expect(CaseDistributionLever).to receive(:public_send).with(lever_item_key).and_return("false") @@ -381,6 +385,20 @@ [appeal.receipt_date, denied_aod_motion_appeal.receipt_date, inapplicable_aod_motion_appeal.receipt_date] ) end + + context "when calculated time goal days are 20" do + before do + CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_direct_review_docket_time_goals) + .update!(value: 385) + appeal.update!(receipt_date: 25.days.ago) + denied_aod_motion_appeal.update!(receipt_date: 25.days.ago) + end + + it "returns only receipt_date with in the time goal" do + expect(subject.length).to eq(2) + expect(subject).to eq([appeal.receipt_date, denied_aod_motion_appeal.receipt_date]) + end + end end context "age_of_oldest_priority_appeal" do @@ -736,6 +754,8 @@ appeals.each do |appeal| appeal.tasks.of_type(:EvidenceSubmissionWindowTask).first.completed! end + CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_evidence_submission_docket_time_goals) + .update!(value: 61) end subject { EvidenceSubmissionDocket.new.distribute_appeals(distribution, priority: false, limit: limit) } diff --git a/spec/models/dockets/hearing_request_docket_spec.rb b/spec/models/dockets/hearing_request_docket_spec.rb index 2a04d797c7a..93e0a305f50 100644 --- a/spec/models/dockets/hearing_request_docket_spec.rb +++ b/spec/models/dockets/hearing_request_docket_spec.rb @@ -6,6 +6,8 @@ create(:case_distribution_lever, :ama_hearing_case_aod_affinity_days) create(:case_distribution_lever, :request_more_cases_minimum) create(:case_distribution_lever, :cavc_affinity_days) + create(:case_distribution_lever, :ama_hearing_docket_time_goals) + create(:case_distribution_lever, :ama_hearing_start_distribution_prior_to_goals) FeatureToggle.enable!(:acd_distribute_by_docket_date) @@ -13,6 +15,9 @@ # back to what the tests were originally written for CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_hearing_case_affinity_days).update!(value: "60") CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_hearing_case_aod_affinity_days).update!(value: "14") + CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_hearing_docket_time_goals).update!(value: 60) + CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_hearing_start_distribution_prior_to_goals) + .update!(is_toggle_active: true) end context "#ready_priority_appeals" do @@ -34,11 +39,36 @@ let!(:not_ready_nonpriority_appeal) { create_not_ready_nonpriority_appeal } let!(:ready_nonpriority_appeal) { create_ready_nonpriority_appeal } - subject { HearingRequestDocket.new.ready_nonpriority_appeals } + before { ready_nonpriority_appeal.update!(receipt_date: 10.days.ago) } + subject { docket.ready_priority_nonpriority_appeals(priority: false, ready: true) } it "returns only ready nonpriority appeals" do - expect(docket.ready_priority_nonpriority_appeals(priority: false, ready: true)) - .to match_array([ready_nonpriority_appeal]) + expect(subject).to match_array([ready_nonpriority_appeal]) + end + + context "when appeals receipt date is not within the time goal" do + before do + CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_hearing_docket_time_goals).update!(value: 160) + end + + it "returns an empty results" do + expect(subject).to eq([]) + end + end + + context "when appeals receipt date is within the time goal" do + let!(:ready_nonpriority_appeal_1) { create_ready_nonpriority_appeal } + + before do + CaseDistributionLever.find_by_item(Constants.DISTRIBUTION.ama_hearing_docket_time_goals).update!(value: 90) + ready_nonpriority_appeal_1.update!(receipt_date: 40.days.ago) + end + + it "returns only receipt_date nonpriority appeals with in the time goal" do + result = subject + expect(result).to include(ready_nonpriority_appeal_1) + expect(result).not_to include(ready_nonpriority_appeal) + end end end From 338f4ef8d2d84302098d30d2b0795d617c18ba27 Mon Sep 17 00:00:00 2001 From: 631966 Date: Tue, 11 Jun 2024 11:58:26 -0400 Subject: [PATCH 31/44] updated the seeds to align with the work completed --- db/seeds/case_distribution_levers.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/db/seeds/case_distribution_levers.rb b/db/seeds/case_distribution_levers.rb index cdb40f6e2fd..3ace3182046 100644 --- a/db/seeds/case_distribution_levers.rb +++ b/db/seeds/case_distribution_levers.rb @@ -472,7 +472,7 @@ def levers } ], is_toggle_active: false, - is_disabled_in_ui: true, + is_disabled_in_ui: false, min_value: 0, max_value: nil, algorithms_used: [Constants.ACD_LEVERS.algorithms.docket], @@ -484,7 +484,7 @@ def levers title: "AMA Direct Review Start Distribution Prior to Goals", description: "", data_type: Constants.ACD_LEVERS.data_types.combination, - value: 365, + value: 60, unit: Constants.ACD_LEVERS.days, options: [ { @@ -496,7 +496,7 @@ def levers } ], is_toggle_active: false, - is_disabled_in_ui: true, + is_disabled_in_ui: false, min_value: 0, max_value: nil, algorithms_used: [Constants.ACD_LEVERS.algorithms.docket], @@ -520,7 +520,7 @@ def levers } ], is_toggle_active: false, - is_disabled_in_ui: true, + is_disabled_in_ui: false, min_value: 0, max_value: nil, algorithms_used: [Constants.ACD_LEVERS.algorithms.docket], @@ -533,7 +533,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.number, value: 730, unit: Constants.ACD_LEVERS.days, - is_disabled_in_ui: true, + is_disabled_in_ui: false, min_value: 0, max_value: nil, algorithms_used: [Constants.ACD_LEVERS.algorithms.docket], @@ -559,7 +559,7 @@ def levers data_type: Constants.ACD_LEVERS.data_types.number, value: 550, unit: Constants.ACD_LEVERS.days, - is_disabled_in_ui: true, + is_disabled_in_ui: false, min_value: 0, max_value: nil, algorithms_used: [Constants.ACD_LEVERS.algorithms.docket], From 900190b9865297d9c9a8c9545a4d20978fa1a1ad Mon Sep 17 00:00:00 2001 From: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:13:15 -0400 Subject: [PATCH 32/44] Updated Toggle Switch behavior when clicked while idle (#21874) --- .../components/ExclusionTable.jsx | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/client/app/caseDistribution/components/ExclusionTable.jsx b/client/app/caseDistribution/components/ExclusionTable.jsx index eef9c8eb608..141da53468e 100644 --- a/client/app/caseDistribution/components/ExclusionTable.jsx +++ b/client/app/caseDistribution/components/ExclusionTable.jsx @@ -77,13 +77,34 @@ const ExclusionTable = () => { const handleToggleChange = (isPriority) => { if (isPriority) { - const toggleState = priorityToggle !== true; + if (comboPriorityToggle) { + let priorityStatus = false; - setPriorityToggle(toggleState); - const newToggleState = toggleState ? 'true' : 'false'; + setPriorityToggle(priorityStatus); + const leverStatus = 'false'; - priorityRadios.forEach((lever) => { - dispatch(updateLeverValue(lever.leverGroup, lever.item, newToggleState)); + priorityRadios.forEach((lever) => { + dispatch(updateLeverValue(lever.leverGroup, lever.item, leverStatus)); + }); + } else { + const toggleState = priorityToggle !== true; + + setPriorityToggle(toggleState); + const newToggleState = toggleState ? 'true' : 'false'; + + priorityRadios.forEach((lever) => { + dispatch(updateLeverValue(lever.leverGroup, lever.item, newToggleState)); + }); + } + + } else if (comboNonPriorityToggle) { + let nonPriorityStatus = false; + + setNonPriorityToggle(nonPriorityStatus); + const leverStatus = 'false'; + + nonPriorityRadios.forEach((lever) => { + dispatch(updateLeverValue(lever.leverGroup, lever.item, leverStatus)); }); } else { const toggleState = nonPriorityToggle !== true; From d59f7a0cc6bbfa0aa9826d6578dd8a2fb75d8da6 Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:45:23 -0400 Subject: [PATCH 33/44] Csv research (#21892) * APPEALS-44911. Change the pure react components to functional components * APPEALS-44911. Redux implementation * APPEALS-44911. CSS changes for custom seed preview * APPEALS-44911. tweak css * csv and etc * CSS for buttons * jest tests for the reducer and action types * Jest test --------- Co-authored-by: SHarshain Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- .../test_docket_seeds_controller.rb | 9 + .../styles/caseDistribution/_test_seeds.scss | 65 +++ .../app/testSeeds/components/CustomSeeds.jsx | 393 ++++++++++++------ .../testSeeds/components/ScenarioSeeds.jsx | 203 +++++---- client/app/testSeeds/index.jsx | 8 +- client/app/testSeeds/reducers/root.js | 5 +- .../reducers/seeds/seedsActionTypes.js | 6 + .../testSeeds/reducers/seeds/seedsActions.js | 41 ++ .../testSeeds/reducers/seeds/seedsReducer.js | 38 ++ .../testSeeds/components/CustomSeeds.test.js | 128 +++--- .../test/app/testSeeds/reducers/root.test.js | 29 ++ .../reducers/seeds/seedsActions.test.js | 57 +++ .../reducers/seeds/seedsReducer.test.js | 72 ++++ config/routes.rb | 1 + public/sample_custom_seeds.csv | 1 + 15 files changed, 730 insertions(+), 326 deletions(-) create mode 100644 client/app/testSeeds/reducers/seeds/seedsActionTypes.js create mode 100644 client/app/testSeeds/reducers/seeds/seedsActions.js create mode 100644 client/app/testSeeds/reducers/seeds/seedsReducer.js create mode 100644 client/test/app/testSeeds/reducers/root.test.js create mode 100644 client/test/app/testSeeds/reducers/seeds/seedsActions.test.js create mode 100644 client/test/app/testSeeds/reducers/seeds/seedsReducer.test.js create mode 100644 public/sample_custom_seeds.csv diff --git a/app/controllers/test_docket_seeds_controller.rb b/app/controllers/test_docket_seeds_controller.rb index b7e2f9f111b..1008fade5a1 100644 --- a/app/controllers/test_docket_seeds_controller.rb +++ b/app/controllers/test_docket_seeds_controller.rb @@ -2,6 +2,7 @@ class TestDocketSeedsController < ApplicationController before_action :check_environment # , :verify_access + # before_action :current_user, only: [:reset_all_appeals] def seed_dockets task_name = Constants.TEST_SEEDS.to_h[params[:seed_type].to_sym] @@ -19,6 +20,14 @@ def seed_dockets head :ok end + def reset_all_appeals + RequestStore[:current_user] = current_user + DistributionTask.where(status: 'assigned').map { |t| t.update!(status: 'on_hold') } + VACOLS::Case.where(bfcurloc: ['81', '83']).map { |c| c.update!(bfcurloc: 'testing') } + + head :ok + end + private # def verify_access ##future work diff --git a/client/app/styles/caseDistribution/_test_seeds.scss b/client/app/styles/caseDistribution/_test_seeds.scss index b4007fde882..78ca421f88d 100644 --- a/client/app/styles/caseDistribution/_test_seeds.scss +++ b/client/app/styles/caseDistribution/_test_seeds.scss @@ -48,3 +48,68 @@ width: 110px; } } + +.custom-seeds-preview { + border-bottom: 1px solid #d6d7d9; + width: 100%; + overflow: auto; + display: inline-block; + margin-bottom: 20px; +} +.preview-table-scroll { + max-height: 400px; + overflow: auto; + position: relative; + // min-width: 2000px; additional s +} + +.preview-table { + margin: 0; +} + +.preview-table tr th { + background-color: #f1f1f1; + position: sticky; + top: 0; + z-index: 9; +} + +.preview-table tr th, .preview-table tr td { + border: 1px solid #d6d7d9; + border-bottom: 0; + border-right: 0; + padding: 10px; +} + +.preview-table tr th:last-child, .preview-table tr td:last-child { + border-right: 1px solid #d6d7d9; + text-align: center; +} + +.preview-table tr:last-child th, .preview-table tr:last-child td { + border-bottom: 1px solid #d6d7d9; +} + +.cf-section-header { + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.upload-seed-csv-button { + margin-top: 18px; + margin-right: 10px; + + a { + background: #0071bc; + padding: 12px; + color: #fff; + border-radius: 5px; + } +} + +.custom-seed-button-section { + display: flex; + justify-content: flex-end; + flex-direction: row; +} diff --git a/client/app/testSeeds/components/CustomSeeds.jsx b/client/app/testSeeds/components/CustomSeeds.jsx index 45dd745ebd4..2e1618a16a7 100644 --- a/client/app/testSeeds/components/CustomSeeds.jsx +++ b/client/app/testSeeds/components/CustomSeeds.jsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import COPY from '../../../COPY'; import ApiUtil from '../../util/ApiUtil'; import Button from 'app/components/Button'; @@ -6,166 +7,288 @@ import NumberField from 'app/components/NumberField'; import TextField from 'app/components/TextField'; import cx from 'classnames'; import CUSTOM_SEEDS from '../../../constants/CUSTOM_SEEDS'; +import { PlusIcon } from 'app/components/icons/PlusIcon'; +import { TrashcanIcon } from 'app/components/icons/TrashcanIcon'; +import { addCustomSeed, removeCustomSeed, saveCustomSeeds, resetCustomSeeds } from '../reducers/seeds/seedsActions'; +import FileUpload from '../../components/FileUpload'; -class CustomSeeds extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - reseedingStatus: { - Aod: false, - NonAod: false, - Tasks: false, - Hearings: false, - Intake: false, - Dispatch: false, - Jobs: false, - Substitutions: false, - DecisionIssues: false, - CavcAmaAppeals: false, - SanitizedJsonSeeds: false, - VeteransHealthAdministration: false, - MTV: false, - Education: false, - PriorityDistributions: false, - TestCaseData: false, - CaseDistributionAuditLeverEntries: false, - Notifications: false, - CavcDashboardData: false, - VbmsExtClaim: false, - CasesTiedToJudgesNoLongerWithBoard: false, - StaticTestCaseData: false, - StaticDispatchedAppealsTestData: false, - RemandedAmaAppeals: false, - RemandedLegacyAppeals: false, - PopulateCaseflowFromVacols: false - }, - seedRunningStatus: false, - seedRunningMsg: 'Seeds running' - }; - this.seedByType = {}; - } +const CustomSeeds = () => { + const [reseedingStatus, setReseedingStatus] = useState({ + Aod: false, + NonAod: false, + Tasks: false, + Hearings: false, + Intake: false, + Dispatch: false, + Jobs: false, + Substitutions: false, + DecisionIssues: false, + CavcAmaAppeals: false, + SanitizedJsonSeeds: false, + VeteransHealthAdministration: false, + MTV: false, + Education: false, + PriorityDistributions: false, + TestCaseData: false, + CaseDistributionAuditLeverEntries: false, + Notifications: false, + CavcDashboardData: false, + VbmsExtClaim: false, + CasesTiedToJudgesNoLongerWithBoard: false, + StaticTestCaseData: false, + StaticDispatchedAppealsTestData: false, + RemandedAmaAppeals: false, + RemandedLegacyAppeals: false, + PopulateCaseflowFromVacols: false + }); + + // const [seedRunningStatus, setSeedRunningStatus] = useState(false); + // const [seedRunningMsg, setSeedRunningMsg] = useState('Seeds running'); + const [seedByType, setSeedByType] = useState({}); + const [file, setFile] = useState(null); + + const theState = useSelector((state) => state); + // console.log(theState.testSeeds.seeds); + const dispatch = useDispatch(); + + const onChangeCaseType = (type, inputKey, value) => { + setSeedByType(prevState => ({ + ...prevState, + [type]: { + ...(prevState[type] || {}), + [inputKey]: value + } + })); + }; - onChangeCaseType = (type, inputKey, value) => { - if (typeof this.seedByType[type] !== 'object') { - this.seedByType[type] = {}; - } - this.seedByType[type][inputKey] = value; + //file upload component + + const handleFileChange = (fileData) => { + const { file, fileName } = fileData; + console.log( "fileData", fileData); + console.log( "file", file); + setFile(fileData); + parseCSV(file); + }; + + const parseCSV = (file) => { + const base64Content = file.split(",")[1]; + const csvText = atob(base64Content); + const rows = csvText.split('\n').map(row => row.trim().split(',')); + const headers = rows[0].map(header => header.trim()); + const jsonData = rows.slice(1).map(row => { + const obj = {}; + headers.forEach((header, index) => { + // obj[header] = row[index] ? row[index].trim() : ''; + const col_value = row[index] ? row[index].trim() : ''; + switch (header) { + case 'Case(s) Type': + obj['seed_type'] = col_value; + case 'Amount': + obj['seed_count'] = parseInt(col_value); + case 'Days Ago': + obj['days_ago'] = parseInt(col_value); + case 'Associated Judge': + obj['judge_css_id'] = col_value; + } + + }); + dispatch(addCustomSeed(obj)); + return obj; + }); + console.log(jsonData); + }; + + const downloadTemplate = () => { + window.location.href = `${window.location.origin}/sample_custom_seeds.csv`; } - reseedByCaseType = (type) => { - const caseType = this.seedByType[type]; + const resetPreviewSeeds = () => { + dispatch(resetCustomSeeds()); + } - caseType.seed_type = type; + const removePreviewSeed = (seed, index) => { + dispatch(removeCustomSeed(seed, index)); + } - this.setState({ seedRunning: true, seedRunningMsg: '' }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: true } - })); + const resetAllAppeals = () => { - // ApiUtil.post(`/seeds/run-demo/${type}`, { data: caseType }).then(() => { - ApiUtil.post('/seeds/run-demo', { data: caseType }).then(() => { - this.setState({ seedRunning: false }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: false } - })); - }). - catch((err) => { + ApiUtil.get('/seeds/reset_all_appeals') + .then(() => { + console.log('Reset all appeals') + }) + .catch(err => { console.warn(err); - this.setState({ seedRunning: false }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: false } - })); }); }; - render() { - const seedTypes = Object.keys(CUSTOM_SEEDS); + const reseedByCaseType = (type) => { - return ( -
- <> + const caseType = seedByType[type]; + caseType.seed_type = type; + + dispatch(addCustomSeed(caseType)); + + // ApiUtil.post('/seeds/run-demo', { data: caseType }) + // .then(() => { + // setSeedRunningStatus(false); + // setReseedingStatus(prevState => ({ + // ...prevState, + // [type]: false + // })); + // }) + // .catch(err => { + // console.warn(err); + // setSeedRunningStatus(false); + // setReseedingStatus(prevState => ({ + // ...prevState, + // [type]: false + // })); + // }); + }; + + const saveSeeds = () => { + dispatch(saveCustomSeeds(theState.testSeeds.seeds)); + }; + + const seedTypes = Object.keys(CUSTOM_SEEDS); + + return ( +
+ <> + +
+
+ +
+
+
+ {/* wiki Link to be implemented */} + {/*
+ wiki +
*/} +
+

{COPY.TEST_SEEDS_CUSTOM_SEEDS}

- + +
+ + + + + + + + + + + {seedTypes.map(type => ( + + + + + + + + ))} + +
Case TypeNumber of cases to createDays AgoJudge CSS_ID (optional)
{CUSTOM_SEEDS[type]} +
+ onChangeCaseType(type, 'seed_count', value)} + /> +
+
+
+ onChangeCaseType(type, 'days_ago', value)} + /> +
+
+
+ onChangeCaseType(type, 'judge_css_id', value)} + /> +
+
+
+ +
+
+
+ +
+

Preview

+
+ + + + + - - - - +
+
+ - {seedTypes.map((type) => ( - - - + {theState.testSeeds.seeds.map((obj, index) => ( + + + + + - - ))}
Case(s) TypeAmountDays AgoAssociated Judge - Case Type - - Number of cases to create - - Days Ago - - Judge CSS_ID (optional) - -
- {CUSTOM_SEEDS[type]} - -
- { - this.onChangeCaseType(type, 'seed_count', value); - }} - /> -
-
{obj.seed_type}{obj.seed_count} Cases{obj.days_ago} Days Ago{obj.judge_css_id} -
- { - this.onChangeCaseType(type, 'days_ago', value); - }} - /> -
-
-
- { - this.onChangeCaseType(type, 'judge_css_id', value); - }} - /> -
-
-
-
-
- +
+
+
+
+
- ); - } -} +
+ ); +}; export default CustomSeeds; diff --git a/client/app/testSeeds/components/ScenarioSeeds.jsx b/client/app/testSeeds/components/ScenarioSeeds.jsx index 4aa38c360ec..c500974a540 100644 --- a/client/app/testSeeds/components/ScenarioSeeds.jsx +++ b/client/app/testSeeds/components/ScenarioSeeds.jsx @@ -1,127 +1,122 @@ -import React from 'react'; +import React, { useState } from 'react'; import COPY from '../../../COPY'; import ApiUtil from '../../util/ApiUtil'; import Button from 'app/components/Button'; import cx from 'classnames'; import TEST_SEEDS from '../../../constants/TEST_SEEDS'; -class ScenarioSeeds extends React.Component { - constructor(props) { - super(props); - this.state = { - reseedingStatus: { - Aod: false, - NonAod: false, - Tasks: false, - Hearings: false, - Intake: false, - Dispatch: false, - Jobs: false, - Substitutions: false, - DecisionIssues: false, - CavcAmaAppeals: false, - SanitizedJsonSeeds: false, - VeteransHealthAdministration: false, - MTV: false, - Education: false, - PriorityDistributions: false, - TestCaseData: false, - CaseDistributionAuditLeverEntries: false, - Notifications: false, - CavcDashboardData: false, - VbmsExtClaim: false, - CasesTiedToJudgesNoLongerWithBoard: false, - StaticTestCaseData: false, - StaticDispatchedAppealsTestData: false, - RemandedAmaAppeals: false, - RemandedLegacyAppeals: false, - PopulateCaseflowFromVacols: false - }, - seedRunningStatus: false, - seedRunningMsg: 'Seeds running' - }; - this.seedCounts = {}; - } +const ScenarioSeeds = () => { + const [reseedingStatus, setReseedingStatus] = useState({ + Aod: false, + NonAod: false, + Tasks: false, + Hearings: false, + Intake: false, + Dispatch: false, + Jobs: false, + Substitutions: false, + DecisionIssues: false, + CavcAmaAppeals: false, + SanitizedJsonSeeds: false, + VeteransHealthAdministration: false, + MTV: false, + Education: false, + PriorityDistributions: false, + TestCaseData: false, + CaseDistributionAuditLeverEntries: false, + Notifications: false, + CavcDashboardData: false, + VbmsExtClaim: false, + CasesTiedToJudgesNoLongerWithBoard: false, + StaticTestCaseData: false, + StaticDispatchedAppealsTestData: false, + RemandedAmaAppeals: false, + RemandedLegacyAppeals: false, + PopulateCaseflowFromVacols: false + }); + const [seedRunningStatus, setSeedRunningStatus] = useState(false); + const [seedRunningMsg, setSeedRunningMsg] = useState('Seeds running'); + const [seedCounts, setSeedCounts] = useState({}); - handleChange= (event, type) => { - this.seedCounts[type] = event.target.value; - } + const handleChange = (event, type) => { + setSeedCounts(prevState => ({ + ...prevState, + [type]: event.target.value + })); + }; - formatSeedName = (name) => { - return name.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)). - join(' '); + const formatSeedName = (name) => { + return name.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '); }; - reseed = (type) => { - const seedCount = parseInt(this.seedCounts[type], 10) || 1; + const reseed = (type) => { + const seedCount = parseInt(seedCounts[type], 10) || 1; - this.setState({ seedRunning: true, seedRunningMsg: '' }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: true } + setSeedRunningStatus(true); + setSeedRunningMsg(''); + setReseedingStatus(prevState => ({ + ...prevState, + [type]: true })); - // const endpoint = `/seeds/run-demo/${type}?seed_count=${seedCount}`; const endpoint = `/seeds/run-demo?seed_type=${type}&seed_count=${seedCount}`; - ApiUtil.post(endpoint).then(() => { - this.setState({ seedRunning: false }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: false } - })); - }). - catch((err) => { + ApiUtil.post(endpoint) + .then(() => { + setSeedRunningStatus(false); + setReseedingStatus(prevState => ({ + ...prevState, + [type]: false + })); + }) + .catch(err => { console.warn(err); - this.setState({ seedRunning: false }); - this.setState((prevState) => ({ - reseedingStatus: { ...prevState.reseedingStatus, [type]: false } + setSeedRunningStatus(false); + setReseedingStatus(prevState => ({ + ...prevState, + [type]: false })); }); }; - render() { - const seedTypes = Object.keys(TEST_SEEDS); - - return ( -
- <> -

{COPY.TEST_SEEDS_RUN_SEEDS}

-
    - {seedTypes.map((type) => ( -
  • -
    - this.handleChange(event, type)} - /> -
    -
    -
    - <> - {this.state.reseedingStatus[type] && ( -
    - {this.formatSeedName(type)} {COPY.TEST_SEEDS_ALERT_MESSAGE} -
    - )} - -
  • - ))} -
- -
- ); - } -} - -// ScenarioSeeds.propTypes = { + const seedTypes = Object.keys(TEST_SEEDS); -// } + return ( +
+ <> +

{COPY.TEST_SEEDS_RUN_SEEDS}

+
    + {seedTypes.map(type => ( +
  • +
    + handleChange(event, type)} + /> +
    +
    +
    + <> + {reseedingStatus[type] && ( +
    + {formatSeedName(type)} {COPY.TEST_SEEDS_ALERT_MESSAGE} +
    + )} + +
  • + ))} +
+ +
+ ); +}; export default ScenarioSeeds; diff --git a/client/app/testSeeds/index.jsx b/client/app/testSeeds/index.jsx index 99c9c868eb9..2f341bbed55 100644 --- a/client/app/testSeeds/index.jsx +++ b/client/app/testSeeds/index.jsx @@ -1,7 +1,7 @@ /* eslint-disable react/prop-types */ import React from 'react'; -// import ReduxBase from '../components/ReduxBase'; +import ReduxBase from '../components/ReduxBase'; import NavigationBar from '../components/NavigationBar'; import { BrowserRouter } from 'react-router-dom'; import PageRoute from '../components/PageRoute'; @@ -11,7 +11,7 @@ import { LOGO_COLORS } from '../constants/AppConstants'; import Footer from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/Footer'; import CaseSearchLink from '../components/CaseSearchLink'; import SeedsBannerDisplay from './components/SeedsBannerDisplay'; -// import rootReducer from '../testSeeds/reducers/root'; +import rootReducer from '../testSeeds/reducers/root'; import TestSeedsApp from './pages/TestSeedsApp'; class TestSeeds extends React.PureComponent { @@ -22,7 +22,7 @@ class TestSeeds extends React.PureComponent { const appName = 'Test Seeds'; return ( - // +
- //
+
); } } diff --git a/client/app/testSeeds/reducers/root.js b/client/app/testSeeds/reducers/root.js index accd56e055c..0fad18bb724 100644 --- a/client/app/testSeeds/reducers/root.js +++ b/client/app/testSeeds/reducers/root.js @@ -1,10 +1,9 @@ import { timeFunction } from '../../util/PerfDebug'; import { combineReducers } from 'redux'; - -//TODO: Needs to Implement TestSeeds Reducer +import seedsReducer from './seeds/seedsReducer'; const rootReducer = combineReducers({ - testSeedObjects: {} + testSeeds: seedsReducer }); export default timeFunction( diff --git a/client/app/testSeeds/reducers/seeds/seedsActionTypes.js b/client/app/testSeeds/reducers/seeds/seedsActionTypes.js new file mode 100644 index 00000000000..846e194a011 --- /dev/null +++ b/client/app/testSeeds/reducers/seeds/seedsActionTypes.js @@ -0,0 +1,6 @@ +export const ACTIONS = { + ADD_CUSTOM_SEED: 'ADD_CUSTOM_SEED', + SAVE_CUSTOM_SEEDS: 'SAVE_CUSTOM_SEEDS', + REMOVE_CUSTOM_SEED: 'REMOVE_CUSTOM_SEED', + RESET_CUSTOM_SEEDS: 'RESET_CUSTOM_SEEDS' +}; diff --git a/client/app/testSeeds/reducers/seeds/seedsActions.js b/client/app/testSeeds/reducers/seeds/seedsActions.js new file mode 100644 index 00000000000..b8273f70b1c --- /dev/null +++ b/client/app/testSeeds/reducers/seeds/seedsActions.js @@ -0,0 +1,41 @@ +import { ACTIONS } from './seedsActionTypes'; +import ApiUtil from '../../../util/ApiUtil'; + + +export const addCustomSeed = (seed) => + (dispatch) => { + dispatch({ + type: ACTIONS.ADD_CUSTOM_SEED, + payload: { + seed + } + }); + }; + +export const removeCustomSeed = (seed, index) => + (dispatch) => { + dispatch({ + type: ACTIONS.REMOVE_CUSTOM_SEED, + payload: { + seed, + index + } + }); + }; + +export const resetCustomSeeds = () => + (dispatch) => { + dispatch({ + type: ACTIONS.RESET_CUSTOM_SEEDS + }); + }; + +export const saveCustomSeeds = (seeds) => + () => { + return ApiUtil.post('/seeds/run-demo', { data: seeds }).then(() => { + console.log("saved custom seed"); + }). + catch((err) => { + console.warn(err); + }); + }; diff --git a/client/app/testSeeds/reducers/seeds/seedsReducer.js b/client/app/testSeeds/reducers/seeds/seedsReducer.js new file mode 100644 index 00000000000..e079a973669 --- /dev/null +++ b/client/app/testSeeds/reducers/seeds/seedsReducer.js @@ -0,0 +1,38 @@ +import { ACTIONS } from '../seeds/seedsActionTypes'; +import { update } from '../../../util/ReducerUtil'; + + +// Refactor where it is used before deletion +export const initialState = { + seeds: [], + displayBanner: false +}; + +const seedsReducer = (state = initialState, action = {}) => { + switch (action.type) { + case ACTIONS.ADD_CUSTOM_SEED: + return { + ...state, + seeds: [...state.seeds, action.payload.seed] + }; + case ACTIONS.REMOVE_CUSTOM_SEED: + return { + ...state, + seeds: state.seeds.filter((_, index) => index !== action.payload.index) + }; + case ACTIONS.SAVE_CUSTOM_SEEDS: + return { + ...state, + displayBanner: true + }; + case ACTIONS.RESET_CUSTOM_SEEDS: + return { + ...state, + seeds: [] + }; + default: + return state; + } +}; + +export default seedsReducer; diff --git a/client/test/app/testSeeds/components/CustomSeeds.test.js b/client/test/app/testSeeds/components/CustomSeeds.test.js index fd9663d23a0..eeb43192977 100644 --- a/client/test/app/testSeeds/components/CustomSeeds.test.js +++ b/client/test/app/testSeeds/components/CustomSeeds.test.js @@ -1,19 +1,20 @@ import React from 'react'; -import { render, fireEvent, waitFor } from '@testing-library/react'; +import { render, fireEvent, screen, container } from '@testing-library/react'; import CustomSeeds from 'app/testSeeds/components/CustomSeeds'; import CUSTOM_SEEDS from '../../../../constants/CUSTOM_SEEDS'; -import ApiUtil from 'app/util/ApiUtil'; - -jest.mock('app/util/ApiUtil', () => ({ - post: jest.fn() -})); +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware } from 'redux'; +import rootReducer from 'app/testSeeds/reducers/root'; +import { addCustomSeed } from 'app/testSeeds/reducers/seeds/seedsActions'; +import thunk from 'redux-thunk'; +import { mount } from 'enzyme'; describe('Custom Seeds', () => { - beforeEach(() => { - // Reset mock implementation before each test - ApiUtil.post.mockReset(); - }); + const getStore = () => createStore( + rootReducer, + applyMiddleware(thunk) + ); afterEach(() => { jest.clearAllMocks(); @@ -21,15 +22,36 @@ describe('Custom Seeds', () => { const seedTypes = Object.keys(CUSTOM_SEEDS); + it('renders Custom Seeds correctly and check the buttons within the page', () => { + const store = getStore(); + + render( + + + + ); + expect(screen.getByText('Download Appeals Ready to Distribute CSV')).toBeInTheDocument(); + expect(screen.getByText('Upload Test Cases CSV')).toBeInTheDocument(); + expect(screen.getByText('Download Template')).toBeInTheDocument(); + expect(screen.getByText('Reset all appeals')).toBeInTheDocument(); + expect(screen.getByText('reset form')).toBeInTheDocument(); + }); + it('should render input fields and buttons for each seed type', () => { - const { getByLabelText, container } = render(); + const store = getStore(); + + render( + + + + ); // Check if input fields and buttons are rendered for each seed type - seedTypes.forEach((type) => { - const caseCountInput = getByLabelText(`seed-count-${type}`); - const daysAgoInput = getByLabelText(`days-ago-${type}`); - const cssIdInput = getByLabelText(`css-id-${type}`); - const button = container.querySelector(`#btn-${type}`); + seedTypes.forEach((type,index) => { + const caseCountInput = screen.getByLabelText(`seed-count-${type}`); + const daysAgoInput = screen.getByLabelText(`days-ago-${type}`); + const cssIdInput = screen.getByLabelText(`css-id-${type}`); + const button = screen.getAllByRole('button')[index]; expect(caseCountInput).toBeInTheDocument(); expect(daysAgoInput).toBeInTheDocument(); @@ -39,11 +61,17 @@ describe('Custom Seeds', () => { }); it('should update state when input values change', () => { - const { getByLabelText } = render(); + const store = getStore(); + + render( + + + + ); const first_seed = seedTypes[0]; - const caseCountInput = getByLabelText(`seed-count-${first_seed}`); - const daysAgoInput = getByLabelText(`days-ago-${first_seed}`); - const cssIdInput = getByLabelText(`css-id-${first_seed}`); + const caseCountInput = screen.getByLabelText(`seed-count-${first_seed}`); + const daysAgoInput = screen.getByLabelText(`days-ago-${first_seed}`); + const cssIdInput = screen.getByLabelText(`css-id-${first_seed}`); fireEvent.change(caseCountInput, { target: { value: '10' } }); fireEvent.change(daysAgoInput, { target: { value: '5' } }); @@ -53,64 +81,4 @@ describe('Custom Seeds', () => { expect(daysAgoInput.value).toBe('5'); expect(cssIdInput.value).toBe('12345'); }); - - it('should make API call when button is clicked', async () => { - ApiUtil.post.mockResolvedValueOnce({ data: 'Success' }); - - const { container, getByLabelText } = render(); - const first_seed = seedTypes[0]; - const caseCountInput = getByLabelText(`seed-count-${first_seed}`); - const daysAgoInput = getByLabelText(`days-ago-${first_seed}`); - const cssIdInput = getByLabelText(`css-id-${first_seed}`); - const button = container.querySelector(`#btn-${first_seed}`); - - fireEvent.change(caseCountInput, { target: { value: '10' } }); - fireEvent.change(daysAgoInput, { target: { value: '5' } }); - fireEvent.change(cssIdInput, { target: { value: 'BVADWISE' } }); - - // Find the button in the same row as the input fields - const row = button.closest('tr'); - // const createButton = within(row).getByText('Create'); - - fireEvent.click(button); - - expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo`, { - data: { seed_type: first_seed, seed_count: 10, days_ago: 5, judge_css_id: 'BVADWISE' } - }); - - // Wait for API call to resolve - await waitFor(() => { - expect(ApiUtil.post).toHaveBeenCalledTimes(1); - }); - }); - - it('should handle API call error', async () => { - const consoleWarnSpy = jest.spyOn(console, 'warn'); - - ApiUtil.post.mockRejectedValueOnce(new Error('API Error')); - const { container, getByLabelText } = render(); - const first_seed = seedTypes[0]; - const caseCountInput = getByLabelText(`seed-count-${first_seed}`); - const daysAgoInput = getByLabelText(`days-ago-${first_seed}`); - const cssIdInput = getByLabelText(`css-id-${first_seed}`); - const button = container.querySelector(`#btn-${first_seed}`); - - fireEvent.change(caseCountInput, { target: { value: '10' } }); - fireEvent.change(daysAgoInput, { target: { value: '5' } }); - fireEvent.change(cssIdInput, { target: { value: 'BVADWISE' } }); - fireEvent.click(button); - - expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo`, { - data: { seed_type: first_seed, seed_count: 10, days_ago: 5, judge_css_id: 'BVADWISE' } - }); - - // Wait for API call to reject - await waitFor(() => { - expect(ApiUtil.post).toHaveBeenCalledTimes(1); - }); - - // Check if error message is displayed - expect(consoleWarnSpy).toHaveBeenCalledWith(new Error('API Error')); - }); }); - diff --git a/client/test/app/testSeeds/reducers/root.test.js b/client/test/app/testSeeds/reducers/root.test.js new file mode 100644 index 00000000000..bc6a6dca5c8 --- /dev/null +++ b/client/test/app/testSeeds/reducers/root.test.js @@ -0,0 +1,29 @@ +import rootReducer from 'app/testSeeds/reducers/root'; +import { timeFunction } from 'app/util/PerfDebug'; +import * as redux from 'redux'; +// Mocking the leversReducer module +jest.mock('app/testSeeds/reducers/seeds/seedsReducer', () => jest.fn()); +// Mocking timeFunction +jest.mock('app/util/PerfDebug', () => ({ + timeFunction: jest.fn((reducer, timeLabelFn) => reducer), +})); + +jest.spyOn(redux, 'combineReducers'); + +describe('rootReducer', () => { + it('calls rootReducer and timeLabel function', () => { + + expect(timeFunction).toHaveBeenCalledWith( + rootReducer, + expect.any(Function) + ); + // Extracting the timeLabel function passed to timeFunction + const timeLabelFn = timeFunction.mock.calls[0][1]; + // Testing the behavior of timeLabelFn + const mockState = {}; + const mockAction = { type: 'TEST_ACTION' }; + const timeLabel = timeLabelFn('testLabel', mockState, mockAction); + + expect(timeLabel).toEqual('Action TEST_ACTION reducer time: testLabel'); + }); +}); diff --git a/client/test/app/testSeeds/reducers/seeds/seedsActions.test.js b/client/test/app/testSeeds/reducers/seeds/seedsActions.test.js new file mode 100644 index 00000000000..5d86090c788 --- /dev/null +++ b/client/test/app/testSeeds/reducers/seeds/seedsActions.test.js @@ -0,0 +1,57 @@ +import * as actions from 'app/testSeeds/reducers/seeds/seedsActions'; +import { ACTIONS } from 'app/testSeeds/reducers/seeds/seedsActionTypes'; +import ApiUtil from 'app/util/ApiUtil'; +// import { alternativeBatchSize, levers, historyList } from '../../../../data/adminCaseDistributionLevers'; + +jest.mock('app/util/ApiUtil', () => ({ + get: jest.fn(), + post: jest.fn() +})); + +describe('seeds actions', () => { + let seed = { + "seed_count": 1, + "days_ago": 12, + "judge_css_id": "keeling", + "seed_type": "ama-aod-hearing-seeds" + }; + + it('should create an action to set the custom seed', () => { + const expectedAction = { + type: ACTIONS.ADD_CUSTOM_SEED, + payload: { seed } + }; + + const dispatch = jest.fn(); + + actions.addCustomSeed(seed)(dispatch); + + expect(dispatch).toHaveBeenCalledWith(expectedAction); + }); + + it('should remove the custom seed', () => { + const index = 0; + const expectedAction = { + type: ACTIONS.REMOVE_CUSTOM_SEED, + payload: { seed, index } + }; + + const dispatch = jest.fn(); + + actions.removeCustomSeed(seed, index)(dispatch); + + expect(dispatch).toHaveBeenCalledWith(expectedAction); + }); + + it('should reset the custom seed objects', () => { + const expectedAction = { + type: ACTIONS.RESET_CUSTOM_SEEDS + }; + + const dispatch = jest.fn(); + + actions.resetCustomSeeds()(dispatch); + + expect(dispatch).toHaveBeenCalledWith(expectedAction); + }); +}); diff --git a/client/test/app/testSeeds/reducers/seeds/seedsReducer.test.js b/client/test/app/testSeeds/reducers/seeds/seedsReducer.test.js new file mode 100644 index 00000000000..72ea77467cd --- /dev/null +++ b/client/test/app/testSeeds/reducers/seeds/seedsReducer.test.js @@ -0,0 +1,72 @@ +/* eslint-disable line-comment-position */ +import seedsReducer from 'app/testSeeds/reducers/seeds/seedsReducer'; +import { ACTIONS } from 'app/testSeeds/reducers/seeds/seedsActionTypes'; + +describe('Seed reducer', () => { + + let initialState = { + seeds: [], + displayBanner: false + }; + + let seed = { + "seed_count": 1, + "days_ago": 12, + "judge_css_id": "keeling", + "seed_type": "ama-aod-hearing-seeds" + }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('Should handle ADD_CUSTOM_SEED and REMOVE_CUSTOM_SEED action', () => { + const action = { + type: ACTIONS.ADD_CUSTOM_SEED, + payload: { + seed: seed + } + }; + + const expectedState = { + ...initialState, + seeds: [action.payload.seed] + }; + + const newState = seedsReducer(initialState, action); + + expect(newState).toEqual(expectedState); + + const removeAction = { + type: ACTIONS.REMOVE_CUSTOM_SEED, + payload: { + seed: seed, + index: 0 + } + }; + + const expectedRemoveState = { + ...initialState, + seeds: [] + }; + + const newStateFixed = seedsReducer(initialState, removeAction); + + expect(newStateFixed).toEqual(expectedRemoveState); + }); + + it('Should handle RESET_CUSTOM_SEEDS action', () => { + const action = { + type: ACTIONS.RESET_CUSTOM_SEEDS, + }; + + const expectedState = { + ...initialState, + seeds: [] + }; + + const newState = seedsReducer(initialState, action); + + expect(newState).toEqual(expectedState); + }); +}); diff --git a/config/routes.rb b/config/routes.rb index 399560ea818..cc91ea5780e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -426,6 +426,7 @@ scope path: 'seeds', as: 'seeds' do post 'run-demo', to: 'test_docket_seeds#seed_dockets' + get 'reset_all_appeals', to: 'test_docket_seeds#reset_all_appeals' end # :nocov: diff --git a/public/sample_custom_seeds.csv b/public/sample_custom_seeds.csv new file mode 100644 index 00000000000..11a85cb36f8 --- /dev/null +++ b/public/sample_custom_seeds.csv @@ -0,0 +1 @@ +Case(s) Type,Amount,Days Ago,Associated Judge From c612b13bf566b167bd5198b0645eceaff07e5dc8 Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:27:13 -0400 Subject: [PATCH 34/44] APPEALS-46057. CleanUP (#21914) * more jest tests * Disable scenario seeds * Lint fixes * APPEALS-46057. disable all scenario seeds * APPEALS-46057. Refactor the TestSeed wrapper to be a functional component and fixed jest test * APPEALS-46057. Jest job in GHA complaining on test --------- Co-authored-by: SHarshain --- .../test_docket_seeds_controller.rb | 4 +- .../styles/caseDistribution/_test_seeds.scss | 50 ++-- .../app/testSeeds/components/CustomSeeds.jsx | 183 ++++++-------- .../testSeeds/components/ScenarioSeeds.jsx | 225 +++++++++--------- .../testSeeds/components/TestSeedsWrapper.jsx | 2 - client/app/testSeeds/index.jsx | 92 +++---- client/app/testSeeds/pages/TestSeedsApp.jsx | 44 ++-- .../testSeeds/components/CustomSeeds.test.js | 84 ++++++- .../components/ScenarioSeeds.test.js | 86 ------- .../app/testSeeds/pages/TestSeedsApp.test.js | 24 +- .../reducers/seeds/seedsActions.test.js | 12 +- 11 files changed, 404 insertions(+), 402 deletions(-) delete mode 100644 client/test/app/testSeeds/components/ScenarioSeeds.test.js diff --git a/app/controllers/test_docket_seeds_controller.rb b/app/controllers/test_docket_seeds_controller.rb index 1008fade5a1..9d4355c0cb9 100644 --- a/app/controllers/test_docket_seeds_controller.rb +++ b/app/controllers/test_docket_seeds_controller.rb @@ -22,8 +22,8 @@ def seed_dockets def reset_all_appeals RequestStore[:current_user] = current_user - DistributionTask.where(status: 'assigned').map { |t| t.update!(status: 'on_hold') } - VACOLS::Case.where(bfcurloc: ['81', '83']).map { |c| c.update!(bfcurloc: 'testing') } + DistributionTask.where(status: "assigned").map { |t| t.update!(status: "on_hold") } + VACOLS::Case.where(bfcurloc: %w[81 83]).map { |c| c.update!(bfcurloc: "testing") } head :ok end diff --git a/client/app/styles/caseDistribution/_test_seeds.scss b/client/app/styles/caseDistribution/_test_seeds.scss index 78ca421f88d..32802288585 100644 --- a/client/app/styles/caseDistribution/_test_seeds.scss +++ b/client/app/styles/caseDistribution/_test_seeds.scss @@ -1,3 +1,8 @@ +$seed-table-border-color: #d6d7d9; +$seed-button-background-color: #0071bc; +$seed-button-font-color: #fff; +$seed-table-preview-bg-color: #f1f1f1; + .test-seeds-num-field { // width: auto; @@ -50,12 +55,13 @@ } .custom-seeds-preview { - border-bottom: 1px solid #d6d7d9; + border-bottom: 1px solid $seed-table-border-color; width: 100%; overflow: auto; display: inline-block; margin-bottom: 20px; } + .preview-table-scroll { max-height: 400px; overflow: auto; @@ -63,31 +69,45 @@ // min-width: 2000px; additional s } +// .preview-table { +// margin: 0; +// } + +// .preview-table tr th { +// background-color: $seed-table-preview-bg-color; +// position: sticky; +// top: 0; +// z-index: 9; +// } + .preview-table { margin: 0; -} -.preview-table tr th { - background-color: #f1f1f1; - position: sticky; - top: 0; - z-index: 9; + tr th { + background-color: $seed-table-preview-bg-color; + position: sticky; + top: 0; + z-index: 9; + } } -.preview-table tr th, .preview-table tr td { - border: 1px solid #d6d7d9; +.preview-table tr th, +.preview-table tr td { + border: 1px solid $seed-table-border-color; border-bottom: 0; border-right: 0; padding: 10px; } -.preview-table tr th:last-child, .preview-table tr td:last-child { - border-right: 1px solid #d6d7d9; +.preview-table tr th:last-child, +.preview-table tr td:last-child { + border-right: 1px solid $seed-table-border-color; text-align: center; } -.preview-table tr:last-child th, .preview-table tr:last-child td { - border-bottom: 1px solid #d6d7d9; +.preview-table tr:last-child th, +.preview-table tr:last-child td { + border-bottom: 1px solid $seed-table-border-color; } .cf-section-header { @@ -101,9 +121,9 @@ margin-right: 10px; a { - background: #0071bc; + background: $seed-button-background-color; padding: 12px; - color: #fff; + color: $seed-button-font-color; border-radius: 5px; } } diff --git a/client/app/testSeeds/components/CustomSeeds.jsx b/client/app/testSeeds/components/CustomSeeds.jsx index 2e1618a16a7..a37c4adb993 100644 --- a/client/app/testSeeds/components/CustomSeeds.jsx +++ b/client/app/testSeeds/components/CustomSeeds.jsx @@ -13,46 +13,14 @@ import { addCustomSeed, removeCustomSeed, saveCustomSeeds, resetCustomSeeds } fr import FileUpload from '../../components/FileUpload'; const CustomSeeds = () => { - const [reseedingStatus, setReseedingStatus] = useState({ - Aod: false, - NonAod: false, - Tasks: false, - Hearings: false, - Intake: false, - Dispatch: false, - Jobs: false, - Substitutions: false, - DecisionIssues: false, - CavcAmaAppeals: false, - SanitizedJsonSeeds: false, - VeteransHealthAdministration: false, - MTV: false, - Education: false, - PriorityDistributions: false, - TestCaseData: false, - CaseDistributionAuditLeverEntries: false, - Notifications: false, - CavcDashboardData: false, - VbmsExtClaim: false, - CasesTiedToJudgesNoLongerWithBoard: false, - StaticTestCaseData: false, - StaticDispatchedAppealsTestData: false, - RemandedAmaAppeals: false, - RemandedLegacyAppeals: false, - PopulateCaseflowFromVacols: false - }); - - // const [seedRunningStatus, setSeedRunningStatus] = useState(false); - // const [seedRunningMsg, setSeedRunningMsg] = useState('Seeds running'); const [seedByType, setSeedByType] = useState({}); - const [file, setFile] = useState(null); + const [inputFile, setFile] = useState(null); const theState = useSelector((state) => state); - // console.log(theState.testSeeds.seeds); const dispatch = useDispatch(); const onChangeCaseType = (type, inputKey, value) => { - setSeedByType(prevState => ({ + setSeedByType((prevState) => ({ ...prevState, [type]: { ...(prevState[type] || {}), @@ -61,90 +29,77 @@ const CustomSeeds = () => { })); }; - //file upload component - - const handleFileChange = (fileData) => { - const { file, fileName } = fileData; - console.log( "fileData", fileData); - console.log( "file", file); - setFile(fileData); - parseCSV(file); - }; + // file upload component const parseCSV = (file) => { - const base64Content = file.split(",")[1]; + const base64Content = file.split(',')[1]; const csvText = atob(base64Content); - const rows = csvText.split('\n').map(row => row.trim().split(',')); - const headers = rows[0].map(header => header.trim()); - const jsonData = rows.slice(1).map(row => { + const rows = csvText.split('\n').map((row) => row.trim().split(',')); + const headers = rows[0].map((header) => header.trim()); + + rows.slice(1).map((row) => { const obj = {}; + headers.forEach((header, index) => { - // obj[header] = row[index] ? row[index].trim() : ''; - const col_value = row[index] ? row[index].trim() : ''; + const colValue = row[index] ? row[index].trim() : ''; + switch (header) { - case 'Case(s) Type': - obj['seed_type'] = col_value; - case 'Amount': - obj['seed_count'] = parseInt(col_value); - case 'Days Ago': - obj['days_ago'] = parseInt(col_value); - case 'Associated Judge': - obj['judge_css_id'] = col_value; - } + case 'Case(s) Type': + obj.seed_type = colValue; + break; + + case 'Amount': + obj.seed_count = Number(colValue); + break; + + case 'Days Ago': + obj.days_ago = Number(colValue); + break; + case 'Associated Judge': + obj.judge_css_id = colValue; + break; + + default: + break; + } }); dispatch(addCustomSeed(obj)); + return obj; }); - console.log(jsonData); + }; + + const handleFileChange = (fileData) => { + const { file } = fileData; + + setFile(fileData); + parseCSV(file); }; const downloadTemplate = () => { window.location.href = `${window.location.origin}/sample_custom_seeds.csv`; - } + }; const resetPreviewSeeds = () => { dispatch(resetCustomSeeds()); - } + }; const removePreviewSeed = (seed, index) => { dispatch(removeCustomSeed(seed, index)); - } + }; const resetAllAppeals = () => { - - ApiUtil.get('/seeds/reset_all_appeals') - .then(() => { - console.log('Reset all appeals') - }) - .catch(err => { - console.warn(err); - }); + ApiUtil.get('/seeds/reset_all_appeals'); }; const reseedByCaseType = (type) => { const caseType = seedByType[type]; + caseType.seed_type = type; dispatch(addCustomSeed(caseType)); - - // ApiUtil.post('/seeds/run-demo', { data: caseType }) - // .then(() => { - // setSeedRunningStatus(false); - // setReseedingStatus(prevState => ({ - // ...prevState, - // [type]: false - // })); - // }) - // .catch(err => { - // console.warn(err); - // setSeedRunningStatus(false); - // setReseedingStatus(prevState => ({ - // ...prevState, - // [type]: false - // })); - // }); }; const saveSeeds = () => { @@ -156,35 +111,35 @@ const CustomSeeds = () => { return (
<> -
- -
-
+
+
-
-
-
- {/* wiki Link to be implemented */} - {/*
+
+
+
+ {/* wiki Link to be implemented */} + {/* */} -
-
+
+

{COPY.TEST_SEEDS_CUSTOM_SEEDS}

-
+
@@ -196,7 +151,7 @@ const CustomSeeds = () => { - {seedTypes.map(type => ( + {seedTypes.map((type) => ( @@ -215,7 +170,7 @@ const CustomSeeds = () => { ariaLabelText={`days-ago-${type}`} useAriaLabel inputID={`days-ago-${type}`} - onChange={value => onChangeCaseType(type, 'days_ago', value)} + onChange={(value) => onChangeCaseType(type, 'days_ago', value)} /> @@ -225,13 +180,13 @@ const CustomSeeds = () => { ariaLabelText={`css-id-${type}`} useAriaLabel inputID={`css-id-${type}`} - onChange={value => onChangeCaseType(type, 'judge_css_id', value)} + onChange={(value) => onChangeCaseType(type, 'judge_css_id', value)} /> + @@ -270,7 +225,7 @@ const CustomSeeds = () => {
{CUSTOM_SEEDS[type]} @@ -205,7 +160,7 @@ const CustomSeeds = () => { ariaLabelText={`seed-count-${type}`} useAriaLabel inputID={`seed-count-${type}`} - onChange={value => onChangeCaseType(type, 'seed_count', value)} + onChange={(value) => onChangeCaseType(type, 'seed_count', value)} />
-
Associated Judge
-
-
{obj.judge_css_id}
- removePreviewSeed(obj, index)}> + removePreviewSeed(obj, index)}>
@@ -281,7 +236,7 @@ const CustomSeeds = () => {
-
- <> - {reseedingStatus[type] && ( -
- {formatSeedName(type)} {COPY.TEST_SEEDS_ALERT_MESSAGE} -
- )} - - - ))} - - -
- ); -}; +// return ( +//
+// <> +//

{COPY.TEST_SEEDS_RUN_SEEDS}

+//
    +// {seedTypes.map((type) => ( +//
  • +//
    +// handleChange(event, type)} +// /> +//
    +//
    +//
    +// <> +// {reseedingStatus[type] && ( +//
    +// {formatSeedName(type)} {COPY.TEST_SEEDS_ALERT_MESSAGE} +//
    +// )} +// +//
  • +// ))} +//
+// +//
+// ); +// }; -export default ScenarioSeeds; +// export default ScenarioSeeds; diff --git a/client/app/testSeeds/components/TestSeedsWrapper.jsx b/client/app/testSeeds/components/TestSeedsWrapper.jsx index a86c5e6f487..29edd3fa35a 100644 --- a/client/app/testSeeds/components/TestSeedsWrapper.jsx +++ b/client/app/testSeeds/components/TestSeedsWrapper.jsx @@ -1,12 +1,10 @@ import React from 'react'; -import ScenarioSeeds from './ScenarioSeeds'; import CustomSeeds from './CustomSeeds'; const TestSeedsWrapper = () => { return (
-
); }; diff --git a/client/app/testSeeds/index.jsx b/client/app/testSeeds/index.jsx index 2f341bbed55..69151501cad 100644 --- a/client/app/testSeeds/index.jsx +++ b/client/app/testSeeds/index.jsx @@ -23,52 +23,52 @@ class TestSeeds extends React.PureComponent { return ( - -
- } - appName="Caseflow Admin" - > - - - -
- { - return ( - - ); - }} - /> -
-
-
-
-
-
-
+ +
+ } + appName="Caseflow Admin" + > + + + +
+ { + return ( + + ); + }} + /> +
+
+
+
+
+
+
); } diff --git a/client/app/testSeeds/pages/TestSeedsApp.jsx b/client/app/testSeeds/pages/TestSeedsApp.jsx index 20594306ef6..15faf275eb6 100644 --- a/client/app/testSeeds/pages/TestSeedsApp.jsx +++ b/client/app/testSeeds/pages/TestSeedsApp.jsx @@ -1,22 +1,36 @@ +// import React from 'react'; +// import TestSeedsWrapper from '../components/TestSeedsWrapper'; + +// class TestSeedsApp extends React.Component { +// // constructor(props) { +// // super(props); +// // } + +// render() { +// return ( +//
+//
+// +//
+//
+// ); + +// } +// } + +// export default TestSeedsApp; + import React from 'react'; import TestSeedsWrapper from '../components/TestSeedsWrapper'; -class TestSeedsApp extends React.Component { - // constructor(props) { - // super(props); - // } - - render() { - return ( +const TestSeedsApp = () => { + return ( +
-
- -
+
- ); - - } -} +
+ ); +}; export default TestSeedsApp; - diff --git a/client/test/app/testSeeds/components/CustomSeeds.test.js b/client/test/app/testSeeds/components/CustomSeeds.test.js index eeb43192977..8ac682d198e 100644 --- a/client/test/app/testSeeds/components/CustomSeeds.test.js +++ b/client/test/app/testSeeds/components/CustomSeeds.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import { render, fireEvent, screen, container } from '@testing-library/react'; +import { render, fireEvent, screen } from '@testing-library/react'; import CustomSeeds from 'app/testSeeds/components/CustomSeeds'; import CUSTOM_SEEDS from '../../../../constants/CUSTOM_SEEDS'; import { Provider } from 'react-redux'; @@ -7,7 +7,11 @@ import { createStore, applyMiddleware } from 'redux'; import rootReducer from 'app/testSeeds/reducers/root'; import { addCustomSeed } from 'app/testSeeds/reducers/seeds/seedsActions'; import thunk from 'redux-thunk'; -import { mount } from 'enzyme'; +import ApiUtil from 'app/util/ApiUtil'; + +jest.mock('app/util/ApiUtil', () => ({ + get: jest.fn() +})); describe('Custom Seeds', () => { @@ -81,4 +85,80 @@ describe('Custom Seeds', () => { expect(daysAgoInput.value).toBe('5'); expect(cssIdInput.value).toBe('12345'); }); + + it('should show the preview for input entries and reset form', () => { + const store = getStore(); + + const { container } = render( + + + + ); + + const first_seed = seedTypes[0]; + const caseCountInput = screen.getByLabelText(`seed-count-${first_seed}`); + const daysAgoInput = screen.getByLabelText(`days-ago-${first_seed}`); + const cssIdInput = screen.getByLabelText(`css-id-${first_seed}`); + const button = container.querySelector(`#btn-${first_seed}`); + + fireEvent.change(caseCountInput, { target: { value: '10' } }); + fireEvent.change(daysAgoInput, { target: { value: '5' } }); + fireEvent.change(cssIdInput, { target: { value: '12345' } }); + expect(screen.getByText('Create 0 test cases')).toBeInTheDocument(); + fireEvent.click(button); + expect(screen.getByText('Create 1 test cases')).toBeInTheDocument(); + + // coverage for reset button + + const resetButton = container.querySelector('#button-reset-form'); + fireEvent.click(resetButton); + expect(screen.getByText('Create 0 test cases')).toBeInTheDocument(); + }); + + it('should remove the row from preview table on click of trash icon', () => { + const store = getStore(); + + const { container } = render( + + + + ); + + const first_seed = seedTypes[0]; + const caseCountInput = screen.getByLabelText(`seed-count-${first_seed}`); + const daysAgoInput = screen.getByLabelText(`days-ago-${first_seed}`); + const cssIdInput = screen.getByLabelText(`css-id-${first_seed}`); + const button = container.querySelector(`#btn-${first_seed}`); + + fireEvent.change(caseCountInput, { target: { value: '10' } }); + fireEvent.change(daysAgoInput, { target: { value: '5' } }); + fireEvent.change(cssIdInput, { target: { value: '12345' } }); + expect(screen.getByText('Create 0 test cases')).toBeInTheDocument(); + fireEvent.click(button); + fireEvent.click(button); + expect(screen.getByText('Create 2 test cases')).toBeInTheDocument(); + + // coverage for trash button + + const trashButton = container.querySelector('#del-preview-row-0'); + fireEvent.click(trashButton); + expect(screen.getByText('Create 1 test cases')).toBeInTheDocument(); + }); + + it('should run reset all appeals query', () => { + const store = getStore(); + + const { container } = render( + + + + ); + + const button = container.querySelector('#button-Reset-all-appeals'); + + ApiUtil.get.mockResolvedValueOnce({ data: 'Success' }); + fireEvent.click(button); + + expect(ApiUtil.get).toHaveBeenCalledWith(`/seeds/reset_all_appeals`); + }); }); diff --git a/client/test/app/testSeeds/components/ScenarioSeeds.test.js b/client/test/app/testSeeds/components/ScenarioSeeds.test.js deleted file mode 100644 index 788878f978d..00000000000 --- a/client/test/app/testSeeds/components/ScenarioSeeds.test.js +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import { render, fireEvent, waitFor } from '@testing-library/react'; -import ScenarioSeeds from 'app/testSeeds/components/ScenarioSeeds'; -import TEST_SEEDS from '../../../../constants/TEST_SEEDS'; -import ApiUtil from 'app/util/ApiUtil'; - -jest.mock('app/util/ApiUtil', () => ({ - post: jest.fn() -})); - -describe('Scenario Seeds', () => { - - beforeEach(() => { - // Reset mock implementation before each test - ApiUtil.post.mockReset(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - const component = new ScenarioSeeds(); - - it('should render input fields and buttons for each seed type', () => { - const { getByLabelText, getByText } = render(); - // Check if input fields and buttons are rendered for each seed type - Object.keys(TEST_SEEDS).forEach((type) => { - const input = getByLabelText(`count-${type}`); - const button = getByText(`Run Demo ${component.formatSeedName(type)}`); - expect(input).toBeInTheDocument(); - expect(button).toBeInTheDocument(); - }); - }) - - it('should update input value when user types', () => { - const { getByLabelText } = render(); - const seed_aod_type = Object.keys(TEST_SEEDS)[0]; - const input = getByLabelText(`count-${seed_aod_type}`); - - fireEvent.change(input, { target: { value: '10' } }); - expect(input.value).toBe('10'); - }); - - it('should make API call when button is clicked', async () => { - ApiUtil.post.mockResolvedValueOnce({ data: 'Success' }); - const { getByText, getByLabelText } = render(); - const seed_aod_type = Object.keys(TEST_SEEDS)[0]; - const input = getByLabelText(`count-${seed_aod_type}`); - const button = getByText(`Run Demo ${component.formatSeedName(seed_aod_type)}`); - - fireEvent.change(input, { target: { value: '10' } }); - fireEvent.click(button); - - expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo?seed_type=${seed_aod_type}&seed_count=10`); - - // Wait for API call to resolve - await waitFor(() => { - expect(ApiUtil.post).toHaveBeenCalledTimes(1); - }); - }); - - it('should handle API call error', async () => { - // Spy on console.warn - const consoleWarnSpy = jest.spyOn(console, 'warn'); - - ApiUtil.post.mockRejectedValueOnce(new Error('API Error')); - const { getByText, getByLabelText } = render(); - const seed_aod_type = Object.keys(TEST_SEEDS)[0]; - const input = getByLabelText(`count-${seed_aod_type}`); - const button = getByText(`Run Demo ${component.formatSeedName(seed_aod_type)}`); - - fireEvent.change(input, { target: { value: '10' } }); - fireEvent.click(button); - - expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo?seed_type=${seed_aod_type}&seed_count=10`); - - // Wait for API call to reject - await waitFor(() => { - expect(ApiUtil.post).toHaveBeenCalledTimes(1); - }); - - // Check if error message is displayed - expect(consoleWarnSpy).toHaveBeenCalledWith(new Error('API Error')); - }); -}); - diff --git a/client/test/app/testSeeds/pages/TestSeedsApp.test.js b/client/test/app/testSeeds/pages/TestSeedsApp.test.js index 6f0bf8e0fcf..38babe2ad5a 100644 --- a/client/test/app/testSeeds/pages/TestSeedsApp.test.js +++ b/client/test/app/testSeeds/pages/TestSeedsApp.test.js @@ -1,23 +1,33 @@ import React from 'react'; import TestSeedsApp from 'app/testSeeds/pages/TestSeedsApp'; -import { mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware } from 'redux'; +import rootReducer from 'app/testSeeds/reducers/root'; +import thunk from 'redux-thunk'; describe('render Test Seeds Application', () => { + const getStore = () => createStore( + rootReducer, + applyMiddleware(thunk) + ); + afterEach(() => { jest.clearAllMocks(); }); it('renders Test Seeds App', () => { - let wrapper = mount( - - ); + const store = getStore(); - wrapper.update(); + render( + + + + ); - expect(wrapper.find('#run_custom_seeds').exists()).toBeTruthy(); - expect(wrapper.find('#run_seeds').exists()).toBeTruthy(); + expect(screen.getByText('Custom Seeds')).toBeInTheDocument(); }); }); diff --git a/client/test/app/testSeeds/reducers/seeds/seedsActions.test.js b/client/test/app/testSeeds/reducers/seeds/seedsActions.test.js index 5d86090c788..fbd04c06fd2 100644 --- a/client/test/app/testSeeds/reducers/seeds/seedsActions.test.js +++ b/client/test/app/testSeeds/reducers/seeds/seedsActions.test.js @@ -1,7 +1,6 @@ import * as actions from 'app/testSeeds/reducers/seeds/seedsActions'; import { ACTIONS } from 'app/testSeeds/reducers/seeds/seedsActionTypes'; import ApiUtil from 'app/util/ApiUtil'; -// import { alternativeBatchSize, levers, historyList } from '../../../../data/adminCaseDistributionLevers'; jest.mock('app/util/ApiUtil', () => ({ get: jest.fn(), @@ -54,4 +53,15 @@ describe('seeds actions', () => { expect(dispatch).toHaveBeenCalledWith(expectedAction); }); + + it('should save action to set the custom seed', () => { + ApiUtil.post.mockResolvedValueOnce({ data: 'Success' }); + + const dispatch = jest.fn(); + + actions.saveCustomSeeds([seed])(dispatch); + expect(ApiUtil.post).toHaveBeenCalledWith(`/seeds/run-demo`, { + data: [seed] + }); + }); }); From c084140a5a97f07d8358ba40bbfe278bdd114cd0 Mon Sep 17 00:00:00 2001 From: Michael Beard <131783726+mbeardy@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:40:09 -0500 Subject: [PATCH 35/44] updates line of test to correctly build lever item (#21918) Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> --- spec/models/docket_spec.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/spec/models/docket_spec.rb b/spec/models/docket_spec.rb index 66bb4b8c46b..a26edb09156 100644 --- a/spec/models/docket_spec.rb +++ b/spec/models/docket_spec.rb @@ -344,12 +344,20 @@ result = docket.ready_priority_nonpriority_appeals(priority: true, ready: true) expect(result).to match_array(expected_appeals) end + end + + context "lever item construction" do + let(:docket) { DirectReviewDocket.new } it "correctly builds the lever item based on docket type" do - expect(docket).to receive(:docket_type).exactly(3).times.and_return("direct_review") + allow(docket).to receive(:docket_type).and_return("direct_review") + lever_item_key = "disable_ama_non_priority_direct_review" - expect(CaseDistributionLever).to receive(:find_by_item).with(lever_item_key).and_return(double(value: "false")) - expect(CaseDistributionLever).to receive(:public_send).with(lever_item_key).and_return("false") + allow(CaseDistributionLever).to receive(:find_by).and_call_original + allow(CaseDistributionLever).to receive(:find_by).with(item: lever_item_key).and_return(double(value: "false")) + + expect(docket).to receive(:ready_priority_nonpriority_appeals).and_call_original + docket.ready_priority_nonpriority_appeals(priority: false) end end From 20e4de727c928e8f8b61934130bb9f05f3f773da Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:39:11 -0400 Subject: [PATCH 36/44] APPEALS-45191. Updated the query to return the date based on the calculation (#21928) * APPEALS-45191. Updated the query to return the date based on the calculation * APPEALS-45191. Lint fix --------- Co-authored-by: SHarshain --- app/queries/appeals_ready_for_distribution.rb | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/app/queries/appeals_ready_for_distribution.rb b/app/queries/appeals_ready_for_distribution.rb index 31e4e38c52a..f383523c025 100644 --- a/app/queries/appeals_ready_for_distribution.rb +++ b/app/queries/appeals_ready_for_distribution.rb @@ -62,8 +62,8 @@ def self.legacy_rows(appeals, docket, sym) cavc: appeal["cavc"] == 1, receipt_date: appeal["bfd19"], ready_for_distribution_at: appeal["bfdloout"], - target_distro_date: docket.docket_time_goal, - days_before_goal_date: docket.start_distribution_prior_to_goal, + target_distro_date: target_distro_date(appeal["bfd19"], docket), + days_before_goal_date: days_before_goal_date(appeal["bfd19"], docket), hearing_judge: hearing_judge, veteran_file_number: appeal["ssn"] || appeal["bfcorlid"], veteran_name: veteran_name @@ -84,8 +84,8 @@ def self.ama_rows(appeals, docket, sym) cavc: appeal.cavc, receipt_date: appeal.receipt_date, ready_for_distribution_at: ready_for_distribution_at, - target_distro_date: docket.docket_time_goal, - days_before_goal_date: docket.start_distribution_prior_to_goal, + target_distro_date: target_distro_date(appeal.receipt_date, docket), + days_before_goal_date: days_before_goal_date(appeal.receipt_date, docket), hearing_judge: hearing_judge, veteran_file_number: appeal.veteran_file_number, veteran_name: appeal.veteran&.name.to_s @@ -105,4 +105,22 @@ def self.with_held_hearings(appeal) .filter { |hearing| hearing.disposition = Constants.HEARING_DISPOSITION_TYPES.held } .first&.judge&.full_name end + + def self.target_distro_date(receipt_date, docket) + if receipt_date.is_a?(String) + receipt_date = Time.zone.parse(receipt_date).to_date + elsif receipt_date.is_a?(Date) || receipt_date.is_a?(DateTime) || receipt_date.is_a?(Time) + receipt_date = receipt_date.to_date + else + return nil + end + receipt_date + docket.docket_time_goal.to_i.days + end + + def self.days_before_goal_date(receipt_date, docket) + target_date = target_distro_date(receipt_date, docket) + return nil if target_date.nil? + + target_date - docket.start_distribution_prior_to_goal.to_i.days + end end From 8af4a1e21f9d54b57b8b040354bcc6efd0123a81 Mon Sep 17 00:00:00 2001 From: SHarshain <133917878+SHarshain@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:46:38 -0400 Subject: [PATCH 37/44] Chrisharsha/appeals 43962 (#21929) * Sharsha/appeals-44911_1 (#21784) * APPEALS-44911. Change the pure react components to functional components * APPEALS-44911. Redux implementation * APPEALS-44911. CSS changes for custom seed preview * APPEALS-44911. tweak css --------- Co-authored-by: SHarshain * Chrisbdetlef/appeals 44910 (#21838) * Initial * Add JSON parsing and multiple calls to rake tasks * Pre-caby tets * Add capybara tests and add IDs to buttons --------- Co-authored-by: Christopher Detlef <> --------- Co-authored-by: SHarshain Co-authored-by: cdetlefva <133903625+cdetlefva@users.noreply.github.com> --- .../test_docket_seeds_controller.rb | 24 +- .../styles/caseDistribution/_test_seeds.scss | 11 - .../app/testSeeds/components/CustomSeeds.jsx | 9 +- .../test_docket_seeds_controller_spec.rb | 546 +++++++++++++----- .../test_docket_case_seeds_spec.rb | 183 ++++++ 5 files changed, 602 insertions(+), 171 deletions(-) create mode 100644 spec/feature/test_seeds/test_docket_case_seeds/test_docket_case_seeds_spec.rb diff --git a/app/controllers/test_docket_seeds_controller.rb b/app/controllers/test_docket_seeds_controller.rb index 9d4355c0cb9..c92526cbc72 100644 --- a/app/controllers/test_docket_seeds_controller.rb +++ b/app/controllers/test_docket_seeds_controller.rb @@ -5,17 +5,19 @@ class TestDocketSeedsController < ApplicationController # before_action :current_user, only: [:reset_all_appeals] def seed_dockets - task_name = Constants.TEST_SEEDS.to_h[params[:seed_type].to_sym] - ENV["SEED_COUNT"] = params[:seed_count].to_s - ENV["DAYS_AGO"] = params[:days_ago].to_s - ENV["JUDGE_CSS_ID"] = params[:judge_css_id].to_s - - Rake::Task[task_name].reenable - Rake::Task[task_name].invoke - - ENV.delete("SEED_COUNT") - ENV.delete("DAYS_AGO") - ENV.delete("JUDGE_CSS_ID") + JSON.parse(request.body.read).each do |row_entry| + task_name = Constants.TEST_SEEDS.to_h[row_entry["seed_type"].to_sym] + ENV["SEED_COUNT"] = row_entry["seed_count"].to_s + ENV["DAYS_AGO"] = row_entry["days_ago"].to_s + ENV["JUDGE_CSS_ID"] = row_entry["judge_css_id"].to_s + + Rake::Task[task_name].reenable + Rake::Task[task_name].invoke + + ENV.delete("SEED_COUNT") + ENV.delete("DAYS_AGO") + ENV.delete("JUDGE_CSS_ID") + end head :ok end diff --git a/client/app/styles/caseDistribution/_test_seeds.scss b/client/app/styles/caseDistribution/_test_seeds.scss index 32802288585..a3f5292a83f 100644 --- a/client/app/styles/caseDistribution/_test_seeds.scss +++ b/client/app/styles/caseDistribution/_test_seeds.scss @@ -69,17 +69,6 @@ $seed-table-preview-bg-color: #f1f1f1; // min-width: 2000px; additional s } -// .preview-table { -// margin: 0; -// } - -// .preview-table tr th { -// background-color: $seed-table-preview-bg-color; -// position: sticky; -// top: 0; -// z-index: 9; -// } - .preview-table { margin: 0; diff --git a/client/app/testSeeds/components/CustomSeeds.jsx b/client/app/testSeeds/components/CustomSeeds.jsx index a37c4adb993..e7615750fdf 100644 --- a/client/app/testSeeds/components/CustomSeeds.jsx +++ b/client/app/testSeeds/components/CustomSeeds.jsx @@ -186,7 +186,12 @@ const CustomSeeds = () => {
-