Skip to content

Commit

Permalink
Merge pull request #21852 from department-of-veterans-affairs/release…
Browse files Browse the repository at this point in the history
…/FY24Q3.4.0

Release FY24Q3.4.0
  • Loading branch information
raymond-hughes authored Jun 18, 2024
2 parents 2e6dd41 + a9f2584 commit 6dc18ec
Show file tree
Hide file tree
Showing 48 changed files with 977 additions and 719 deletions.
1 change: 1 addition & 0 deletions .reek.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ detectors:
- UpdateCachedAppealsAttributesJob
- Veteran
- LegacyDocket
- Test::UsersController
TooManyConstants:
exclude:
- Fakes::BGSServicePOA
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/metrics/v2/logs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def create
private

def metrics_not_saved
render json: { error_code: "Metrics not saved for user" }, status: :unprocessable_entity
render json: { error_code: "Metrics not saved for user" }, status: :accepted
end

def allowed_params
Expand Down
9 changes: 8 additions & 1 deletion app/controllers/test/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
CaseflowCertification::Application.load_tasks

class Test::UsersController < ApplicationController
before_action :require_demo, only: [:set_user, :set_end_products, :reseed, :toggle_feature]
before_action :require_demo, only: [:set_user, :set_end_products, :reseed, :optional_seed, :toggle_feature]
before_action :require_global_admin, only: :log_in_as_user
skip_before_action :deny_vso_access, only: [:index, :set_user, :show]

Expand Down Expand Up @@ -110,6 +110,13 @@ def reseed
head :ok
end

def optional_seed
return unless Rails.deploy_env?(:demo)

system "bundle exec rake db:seed:optional"
head :ok
end

def toggle_feature
params[:enable]&.each do |f|
FeatureToggle.enable!(f[:value])
Expand Down
5 changes: 4 additions & 1 deletion app/models/appeal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,6 @@ def clone_task_tree(parent_appeal, user_css_id)
parent_ordered_tasks = parent_appeal.tasks.order(:created_at)
# define hash to store parent/child relationship values
task_parent_to_child_hash = {}

while parent_appeal.tasks.count != tasks.count && !parent_appeal.tasks.nil?
# cycle each task in the parent
parent_ordered_tasks.each do |task|
Expand Down Expand Up @@ -698,6 +697,10 @@ def cavc
court_remand?
end

def predocketed?
tasks.select { |task| task.class.name == "PreDocketTask" && task.open? }
end

def vha_predocket_needed?
request_issues.active.any?(&:vha_predocket?)
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/bgs_power_of_attorney.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def save_with_updated_bgs_record!

def update_ihp_task
related_appeals.each do |appeal|
InformalHearingPresentationTask.update_to_new_poa(appeal) if appeal.active?
InformalHearingPresentationTask.update_to_new_poa(appeal) if appeal.active? && !appeal.predocketed?
end
end

Expand Down
2 changes: 0 additions & 2 deletions app/models/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -957,8 +957,6 @@ def status_is_valid_on_create
if status != Constants.TASK_STATUSES.assigned
fail Caseflow::Error::InvalidStatusOnTaskCreate, task_type: type
end

true
end

def assignee_status_is_valid_on_create
Expand Down
10 changes: 9 additions & 1 deletion app/models/tasks/schedule_hearing_colocated_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,18 @@ def just_completed_ama_organization_task?
assigned_to.is_a?(Organization)
end

# Selects all judge tasks that are NOT QualityReviewJudgeTasks
def handle_judge_tasks!
judge_tasks = JudgeTask.open.where(appeal: appeal)
non_quality_review_judge_tasks = judge_tasks.to_a.reject { |task| task.type == "JudgeQualityReviewTask" }
# Converts array to active record association and runs cancel_task_and_child_subtasks
JudgeTask.where(id: non_quality_review_judge_tasks.map(&:id)).find_each(&:cancel_task_and_child_subtasks)
end

def send_to_hearings_branch
parent = DistributionTask.create!(appeal: appeal, parent: appeal.root_task)
ScheduleHearingTask.create!(appeal: appeal, parent: parent)
JudgeTask.open.where(appeal: appeal).find_each(&:cancel_task_and_child_subtasks)
handle_judge_tasks!
DistributedCase.find_by(case_id: appeal.uuid)&.rename_for_redistribution!
end
end
4 changes: 4 additions & 0 deletions app/models/tasks/schedule_hearing_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ def default_instructions
end

def create_parent_hearing_task
return true unless parent

if parent.type != HearingTask.name
self.parent = HearingTask.create(appeal: appeal, parent: parent)
end

true
end

def verify_vso_can_change_hearing_to_virtual!(params)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def cancel_tasks_blocking_distribution

def verify_appeal_distributable
if DistributionTask.open.where(appeal: appeal).empty?
return true if appeal.appeal_split_process == true

fail(Caseflow::Error::IneligibleForBlockedSpecialCaseMovement, appeal_id: appeal.id)
end
end
Expand Down
27 changes: 15 additions & 12 deletions app/models/tasks/special_case_movement_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,25 @@ def self.label
private

def distribute_to_judge
Task.transaction do
JudgeAssignTask.create!(appeal: appeal,
parent: appeal.root_task,
assigned_to: assigned_to,
assigned_by: assigned_by,
instructions: instructions)
# We don't want the judge to have to worry about the SpecialCaseMovementTask,
# so we assign it to the SCM user that assigned this.
update!(status: Constants.TASK_STATUSES.completed, assigned_to: assigned_by)
# For now, we expect the parent to always be the distribution task
# so we don't worry about distribution task explicitly
parent.update!(status: Constants.TASK_STATUSES.completed)
unless appeal.appeal_split_process
Task.transaction do
JudgeAssignTask.create!(appeal: appeal,
parent: appeal.root_task,
assigned_to: assigned_to,
assigned_by: assigned_by,
instructions: instructions)
# We don't want the judge to have to worry about the SpecialCaseMovementTask,
# so we assign it to the SCM user that assigned this.
update!(status: Constants.TASK_STATUSES.completed, assigned_to: assigned_by)
# For now, we expect the parent to always be the distribution task
# so we don't worry about distribution task explicitly
parent.update!(status: Constants.TASK_STATUSES.completed)
end
end
end

def verify_appeal_distributable
return true if appeal.appeal_split_process
if !appeal.ready_for_distribution?
fail(Caseflow::Error::IneligibleForSpecialCaseMovement, appeal_id: appeal.id)
end
Expand Down
2 changes: 1 addition & 1 deletion app/services/external_api/va_dot_gov_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ def send_facilities_requests(ids:, query:)
response = nil

until remaining_ids.empty? || response.try(:next?) == false || response.try(:success?) == false
response = send_facilities_request(query: query.merge(page: page, perPage: 200)).merge(response)
response = send_facilities_request(query: query.merge(page: page, per_page: 200)).merge(response)

remaining_ids -= response.data.pluck(:facility_id)

Expand Down
3 changes: 1 addition & 2 deletions app/services/metrics_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ def self.record(description, service: nil, name: "unknown", caller: nil)
app_name: app,
attrs: {
service: service,
endpoint: name,
uuid: uuid
endpoint: name
}
}
MetricsService.emit_gauge(sent_to_info)
Expand Down
18 changes: 18 additions & 0 deletions app/workflows/ihp_tasks_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ def initialize(parent)

def create_ihp_tasks!
appeal = @parent.appeal

if appeal.status.send(:open_pre_docket_task?)
cancel_any_existing_ihp_tasks(appeal)
return []
end

appeal.representatives.select { |org| org.should_write_ihp?(appeal) }.map do |vso_organization|
# For some RAMP appeals, this method may run twice.
existing_task = InformalHearingPresentationTask.find_by(
Expand All @@ -22,4 +28,16 @@ def create_ihp_tasks!
)
end
end

private

def cancel_any_existing_ihp_tasks(appeal)
appeal.representatives.select { |org| org.should_write_ihp?(appeal) }.each do |vso_organization|
existing_task = InformalHearingPresentationTask.find_by(
appeal: appeal,
assigned_to: vso_organization
)
existing_task&.update!(status: Constants.TASK_STATUSES.cancelled)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def create_send_final_notification_letter_tasks
appeal: @task.appeal,
parent: @task.parent,
assigned_to: Organization.find_by_url("clerk-of-the-board"),
assigned_by: current_user
assigned_by: @task.assigned_by
)
# sfnlt.instructions.push(instructions)
sfnlt.update!(status: Constants.TASK_STATUSES.assigned)
Expand Down
26 changes: 24 additions & 2 deletions client/app/components/LoadingDataDisplay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,38 @@ import LoadingScreen from './LoadingScreen';
import StatusMessage from './StatusMessage';
import COPY from '../../COPY';
import { recordAsyncMetrics } from '../util/Metrics';
import { ExternalLinkIcon } from './icons';
import { css } from 'glamor';
import Link from './Link';

const ICON_POSITION_FIX = css({ position: 'relative', top: 3 });

const PROMISE_RESULTS = {
SUCCESS: 'SUCCESS',
FAILURE: 'FAILURE'
};

const ESCALATION_FORM_URL = 'https://leaf.va.gov/VBA/335/sensitive_level_access_request/';

const accessDeniedTitle = { title: COPY.ACCESS_DENIED_TITLE };
const accessDeniedMsg = <div>
It looks like you do not have the necessary level of access to view this information.<br />
Please check with your application administrator before trying again.</div>;
VBA employs a sensitive access system and to access records at any designated level requires approval for the same or
higher-level access.<br />
You are receiving this message because you do not have an authorized access level required to view this page.<br />
<br />
To request access, please click the button below
<div>
<Link href={ESCALATION_FORM_URL}>
<button className="btn btn-default">Request Access &nbsp;
<span {...ICON_POSITION_FIX}><ExternalLinkIcon /></span>
</button>
</Link>
</div>
<br />
If you have any questions or need assistance with the request form linked above,
please contact the Restricted Portfolio Management team at
<a href="mailto:VBA.RPM@va.gov">VBA.RPM@va.gov</a>.
</div>;

const duplicateNumberTitle = { title: COPY.DUPLICATE_PHONE_NUMBER_TITLE };
const duplicateNumberMsg = <div>
Expand Down
32 changes: 14 additions & 18 deletions client/app/reader/PdfFile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ export class PdfFile extends React.PureComponent {
file: this.props.file,
documentType: this.props.documentType,
prefetchDisabled: this.props.featureToggles.prefetchDisabled,
overscan: this.props.windowingOverscan
overscan: this.props.windowingOverscan,
isPageVisible: this.props.isVisible,
name: null
};
}

Expand Down Expand Up @@ -467,32 +469,26 @@ export class PdfFile extends React.PureComponent {
clearTimeout(this.scrollTimer);
}

this.scrollTimer = setTimeout(() => {
const scrollStart = performance.now();
const scrollStart = performance.now();

const data = {
overscan: this.props.windowingOverscan,
documentType: this.props.documentType,
pageCount: this.props.pdfDocument.numPages,
pageIndex: this.pageIndex,
prefetchDisabled: this.props.featureToggles.prefetchDisabled,
start: scrollStart,
end: performance.now()
};
this.scrollTimer = setTimeout(() => {
const scrollEnd = performance.now();
const scrollMessage = `Scroll to page ${this.currentPage + 1}
(${(Math.round(this.scrollLeft * 100) / 100).toFixed(2)},
${(Math.round(this.scrollTop * 100) / 100).toFixed(2)})`;

const posx = (Math.round(this.scrollLeft * 100) / 100).toFixed(2);
const posy = (Math.round(this.scrollTop * 100) / 100).toFixed(2);
this.metricsAttributes.name = scrollMessage;

storeMetrics(
this.props.documentId,
this.metricsAttributes,
{
message: `Scroll to position ${posx}, ${posy}`,
message: scrollMessage,
type: 'performance',
product: 'reader',
start: new Date(performance.timeOrigin + data.start),
end: new Date(performance.timeOrigin + data.end),
duration: data.start ? data.end - data.start : 0
start: new Date(performance.timeOrigin + scrollStart),
end: new Date(performance.timeOrigin + scrollEnd),
duration: scrollStart ? scrollEnd - scrollStart : 0
},
this.metricsIdentifier,
);
Expand Down
12 changes: 12 additions & 0 deletions client/app/reader/PdfPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ export class PdfPage extends React.PureComponent {
this.isDrawing = false;
this.renderTask = null;
this.marks = [];

this.metricsAttributes = {
documentId: this.props.documentId,
numPagesInDoc: null,
pageIndex: this.props.pageIndex,
file: this.props.file,
documentType: this.props.documentType,
prefetchDisabled: this.props.featureToggles.prefetchDisabled,
overscan: this.props.windowingOverscan,
isPageVisible: this.props.isVisible,
name: null
};
}

getPageContainerRef = (pageContainer) => (this.pageContainer = pageContainer);
Expand Down
3 changes: 1 addition & 2 deletions client/app/reader/ReaderLoadingScreen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ export class ReaderLoadingScreen extends React.Component {
this.props.onReceiveAnnotations(annotations);
}).
catch((err) => {
// allow HTTP errors to fall on the floor via the console.
console.error(new Error(`Problem with GET /reader/appeal/${this.props.vacolsId}/documents?json ${err}`));
throw err;
});
}

Expand Down
24 changes: 22 additions & 2 deletions client/app/test/TestUsers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export default function TestUsers(props) {
const [isLoggingIn, setIsLoggingIn] = useState(false);
const [reseedingError, setReseedingError] = useState(null);
const [isReseeding, setIsReseeding] = useState(false);
const [optionalSeedingError, setOptionalSeedingError] = useState(null);
const [isOptionalSeeding, setIsOptionalSeeding] = useState(false);
const [inputValue, setInputValue] = useState('');

const handleEpSeed = (type) => ApiUtil.post(`/test/set_end_products?type=${type}`).
Expand Down Expand Up @@ -80,6 +82,18 @@ export default function TestUsers(props) {
});
};

const optionalSeed = () => {
setIsOptionalSeeding(true);
ApiUtil.post('/test/optional_seed').then(() => {
setOptionalSeedingError(null);
setIsOptionalSeeding(false);
}, (err) => {
console.warn(err);
setOptionalSeedingError(err);
setIsOptionalSeeding(false);
});
};

const filteredUserOptions = useMemo(() => {
const userOptions = props.testUsersList.map((user) => ({
value: user.id,
Expand Down Expand Up @@ -215,9 +229,9 @@ export default function TestUsers(props) {
Not all applications are available to every user. Additionally,
some users have access to different parts of the same application.
<br />This button reseeds the database with default values.</p>
{reseedingError &&
{(reseedingError || optionalSeedingError) &&
<Alert
message={reseedingError.toString()}
message={reseedingError ? reseedingError.toString() : optionalSeedingError.toString()}
type="error"
/>
}
Expand All @@ -226,6 +240,12 @@ export default function TestUsers(props) {
name="Reseed the DB"
loading={isReseeding}
loadingText="Reseeding the DB" />
<br />
<Button
onClick={optionalSeed}
name="Run optional seeds"
loading={isOptionalSeeding}
loadingText="Running optional seed" />
<br /> <br />
<h3>Global Feature Toggles Enabled:</h3>
<SearchableDropdown
Expand Down
Loading

0 comments on commit 6dc18ec

Please sign in to comment.