From f7a4a52b58503c7cdfbd64ffda3634f59be2ae68 Mon Sep 17 00:00:00 2001 From: Adam Ducker Date: Wed, 6 Nov 2024 09:23:57 -0600 Subject: [PATCH 1/8] Merge contents of feature/APPEALS-49620-and-APPEALS-49626 in with latest from main (#23410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update to guestLink rendering * APPEALS-34673: update snapshot tests, add hearing links to test runs * APPEALS-34673: revert caseworkerindex snapshot and remove comment * APPEALS-34673: update snapshot - passes locally * APPEALS-34673: updates to snapshot * APPEALS-34673: updates to snapshot * APPEALS-34673: updates to snapshot * APPEALS-34880: added nbf and exp methods to hearing day and virtual hearing * APPEALS-34880: added nbf exp rspec to virtual hearing spec * APPEALS-34880: added hearing day spec test for nbf and exp and removed comments * APPEALS-34880: removed comments * APPEALS-34880: removed comments * APPEALS-34880: removed spacing in rspec * APPEALS-34908: added one liners * APPEALS-34908 update resp * APPEALS-34908 update json parse * APPEALS-34908 update send_webex method nil * APPEALS-34908 update webex_conference_link to string * APPEALS-34908 update create_response.rb sym to string * DRY up generate_conference_information * APPEALS-34908: service and fakes updates * APPEALS-34908: update request body * Change param passing * APPEALS-34908 rspec test fix * APPEALS-34908: updated webex service spec * APPEALS-34908 converted hash to json in fake * Change some names * APPEALS-35033: change casing for Nbf Exp to nbf exp * Correct spec file name * Remove .gitignore diff * Use .to_json * APPEALS-35195: update sub to use unique hearing day id * APPEALS-35195: update hearing day spec to have specific id for hearing day * APPEALS-35195: change date format * APPEALS-35728: initial updates to daily docket row * APPEALS-35728: update logic for type vs conferenceProvider * APPEALS-35728: updates to test, props, store, snapshot * Test: Change hearing links label (#20082) * Change hearing links label * Update snapshot * Fix tests * More snapshots --------- Co-authored-by: Matthew Thornton * Merge branch 'feature/APPEALS-26734' into feature/APPEALS-35295 Resolves [Jira Issue Title](https://jira.devops.va.gov/browse/JIRA-12345) # Description Please explain the changes you made here. ## Acceptance Criteria - [ ] Code compiles correctly ## Testing Plan 1. Go to [Jira Issue/Test Plan Link](https://jira.devops.va.gov/browse/JIRA-12345) or list them below - [ ] For feature branches merging into master: Was this deployed to UAT? # Frontend ## User Facing Changes - [ ] Screenshots of UI changes added to PR & Original Issue BEFORE|AFTER ---|--- ## Storybook Story *For Frontend (Presentation) Components* * [ ] Add a [Storybook](https://github.com/department-of-veterans-affairs/caseflow/wiki/Documenting-React-Components-with-Storybook) file alongside the component file (e.g. create `MyComponent.stories.js` alongside `MyComponent.jsx`) * [ ] Give it a title that reflects the component's location within the overall Caseflow hierarchy * [ ] Write a separate story (within the same file) for each discrete variation of the component # Backend ## Database Changes *Only for Schema Changes* * [ ] Add typical timestamps (`created_at`, `updated_at`) for new tables * [ ] Update column comments; include a "PII" prefix to indicate definite or potential [PII data content](https://github.com/department-of-veterans-affairs/appeals-team/blob/master/caseflow-team/0-how-we-work/pii-handbook.md#what-is-pii) * [ ] Have your migration classes inherit from `Caseflow::Migration`, especially when adding indexes (use `add_safe_index`) (see [Writing DB migrations](https://github.com/department-of-veterans-affairs/caseflow/wiki/Writing-DB-migrations)) * [ ] Verify that `migrate:rollback` works as desired ([`change` supported functions](https://edgeguides.rubyonrails.org/active_record_migrations.html#using-the-change-method)) * [ ] Perform query profiling (eyeball Rails log, check bullet and fasterer output) * [ ] For queries using raw sql was an explain plan run by System Team * [ ] Add appropriate indexes (especially for foreign keys, polymorphic columns, unique constraints, and Rails scopes) * [ ] Run `make check-fks`; add any missing foreign keys or add to `config/initializers/immigrant.rb` (see [Record associations and Foreign Keys](https://github.com/department-of-veterans-affairs/caseflow/wiki/Record-associations-and-Foreign-Keys)) * [ ] Add `belongs_to` for associations to enable the [schema diagrams](https://department-of-veterans-affairs.github.io/caseflow/task_trees/schema/schema_diagrams) to be automatically updated * [ ] Document any non-obvious semantics or logic useful for interpreting database data at [Caseflow Data Model and Dictionary](https://github.com/department-of-veterans-affairs/caseflow/wiki/Caseflow-Data-Model-and-Dictionary) ## Integrations: Adding endpoints for external APIs * [ ] Check that Caseflow's external API code for the endpoint matches the code in the relevant integration repo * [ ] Request: Service name, method name, input field names * [ ] Response: Check expected data structure * [ ] Check that calls are wrapped in MetricService record block * [ ] Check that all configuration is coming from ENV variables * [ ] Listed all new ENV variables in description * [ ] Worked with or notified System Team that new ENV variables need to be set * [ ] Update Fakes * [ ] For feature branches: Was this tested in Caseflow UAT # Best practices ## Code Documentation Updates - [ ] Add or update code comments at the top of the class, module, and/or component. ## Tests ### Test Coverage Did you include any test coverage for your code? Check below: - [ ] RSpec - [ ] Jest - [ ] Other ### Code Climate Your code does not add any new code climate offenses? If so why? - [ ] No new code climate issues added ## Monitoring, Logging, Auditing, Error, and Exception Handling Checklist ### Monitoring - [ ] Are performance metrics (e.g., response time, throughput) being tracked? - [ ] Are key application components monitored (e.g., database, cache, queues)? - [ ] Is there a system in place for setting up alerts based on performance thresholds? ### Logging - [ ] Are logs being produced at appropriate log levels (debug, info, warn, error, fatal)? - [ ] Are logs structured (e.g., using log tags) for easier querying and analysis? - [ ] Are sensitive data (e.g., passwords, tokens) redacted or omitted from logs? - [ ] Is log retention and rotation configured correctly? - [ ] Are logs being forwarded to a centralized logging system if needed? ### Auditing - [ ] Are user actions being logged for audit purposes? - [ ] Are changes to critical data being tracked ? - [ ] Are logs being securely stored and protected from tampering or exposing protected data? ### Error Handling - [ ] Are errors being caught and handled gracefully? - [ ] Are appropriate error messages being displayed to users? - [ ] Are critical errors being reported to an error tracking system (e.g., Sentry, ELK)? - [ ] Are unhandled exceptions being caught at the application level ? ### Exception Handling - [ ] Are custom exceptions defined and used where appropriate? - [ ] Is exception handling consistent throughout the codebase? - [ ] Are exceptions logged with relevant context and stack trace information? - [ ] Are exceptions being grouped and categorized for easier analysis and resolution? * APPEALS-31618 add migration files * APPEALS-31618 rmove migration * APPEALS-31618 0fficial add co_host_hearing_link * APPEALS-31618 Add co_host_link to conference_links * APPEALS-31618 added code for co-host * APPEALS-31618 remove attribute conferencelinks * APPEALS-31618 update tests * APPEALS-31618 update lint * APPEALS-31618 update VirtualHearingsFields snapshot * APPEALS-31618 update details.test.js snapshot * APPEALS-31618 update create_conference_job_spec * APPEALS-31618 updated Marcs sugestions rnd 1 * jefftmarks/APPEALS-31619 (#20515) * APPEALS-31619 Created migration * APPEALS-31619 Add xls column to migration * APPEALS-31619 Update index name * APPEALS-31619 Change null options on migration * APPEALS-31619 Refactor transcription files table * APPEALS-31619 Remove old migration * APPEALS-31618 updated migration inheritance * Min/APPEALS-31621 (#20523) * APPEALS-31621 added the table * APPEALS-31621 moved model under hearings folder * APPEALS-31621 reverted schema back from testing table * APPEALS-31621 added transcriptions association * APPEALS-31621 fixed typo * APPEALS-31621 removed migration file * APPEALS-31621 added additional template methods in model and comments * APPEALS-31621 fixed transcription typo * APPEALS-31621 added additional template methods * APPEALS-31621 changed name of model and controller * APPEALS-31621 removed transcript association * hotfix/APPEALS-35296-flaky-conference-job-test (#20555) * akonhilas/APPEALS-31820 (#20510) * APPEALS-31820: create transcription transactions serializer with outline until model complete * APPEALS-31820: update aws link attributes * APPEALS-31820: removed methods and has many relationship, change file name * APPEALS-31820: updated file tree * Revert "APPEALS-31820: updated file tree" This reverts commit 4b58d5a93c3922582b97447e6b40c5cb1b087083. * APPEALS-31820: updated serializer to match new db columns and updated AC * APPEALS-35177 adjust spacing * APPEALS-35177 fix lint * APPEALS-35177 made final positioning adjustments * APPEALS-35176 - Pull radio buttons closer and tidy up the CSS * jefftmarks/APPEALS-31619 Remove unique contraints on indexes (#20620) * APPEALS-31619 Created migration * APPEALS-31619 Add xls column to migration * APPEALS-31619 Update index name * APPEALS-31619 Change null options on migration * APPEALS-31619 Refactor transcription files table * APPEALS-31619 Remove old migration * APPEALS-31619 Remove unique contraints on indexes * APPEALS-31619 Change index order * APPEALS-31619 Move docket number in index * Allow NonVirtual Webex hearings to display the HC link on the Hearing Details page * APPEALS-36652 Backend changes * Display only Virtual Hearing Links for virtual hearings and only Hearing Links for nonvirtual hearings * APPEALS-36652 * APPEALS-35176 - Update html and css to better match the figma design * Refine conference provider search so that correct links show up for pexip conferences * APPEALS-35176 - Update radio button to align better with label * Add daily docket conference links to legacy appeals * Add daily docket conference link to the nonvirtual webex hearing links * Show non virtual webex HC link * APPEALS-35176 - Make margins more consistent across browsers * Update feature tests to remove old conditions * APPEALS-36652 heearing_spec update * Updated jest testing in progress * Add nonvirtual webex test - in Progress * Add test with default props back into test file * Remove unnecessary test and resolve testing errors with webex hearinglinks tests * Show hearing links titles and clickable link * Update jest snapshots * Capitalize webex hearing styling * akonhilas/APPEALS-38906 (#20664) * APPEALS-38906: updates to service, created new file for job * APPEALS-38906: response, updates to query, finalizing job, fakes * APPEALS-38906: add query to call * APPEALS-38906: add scheduled jobs, cgi formatting, exponential backoff, add get post to service, service spec updates * APPEALS-38906: added get webex recording job spec, changed timing for to and from params in job * APPEALS-38906: pushing failing tests for visibility * APPEALS-38906: pushing tests for visibility * APPEALS-38906: final test updates * APPEALS-38906: remove comments, time updates, va ops email comment * APPEALS-38906: fixed rspec and removed unnecessary error raise from job * jefftmarks/APPEALS-38907 (#20659) * APPEALS-31821 created job file and template * APPEALS-38907 Create download transcription file job * min/APPEALS-31821 made new helper method templates for use on perform * APPEALS-38907 Continue prototype * APPEALS-31821 basic webvtt to rtf conversion and tmp folder initializers * APPEALS-38907 Complete basic structure for job * APPEALS-38907 Update error handling * APPEALS-31821 gathering info for the front page * APPEALS-38907 Add error handling for invalid file name * APPEALS-38907 Add error handling for invalid file type * APPEALS-38907 Fix linting * APPEALS-38907 Open URI CodeQL fix * APPEALS-31821 generating a table for the front page info * APPEALS-31821 generating header info for first page cell * APPEALS-38907 Update initializer to create subdirectory for each file type * APPEALS-38907 Update code comments * APPEALS-31821 most info appearing on first page now * APPEALS-31821 stashing regexes for future use * APPEALS-31821 vtt fully converted to rtf * APPEALS-38907 Implement S3 upload * APPEALS-38907 Update comments * APPEALS-31821 page numbers and footers now displaying * APPEALS-31821 texts from the same speaker is now consolidated * APPEALS-31821 refactored footer creation on document * APPEALS-38907 Complete implementation of job minus va ops email * APPEALS-38907 Update S3 bucket and folder names * APPEALS-31821 S3 download and upload implemented and tmp file clean up * APPEALS-31821 S3 download and upload implemented and tmp file clean up * APPEALS-38907 Polish error handling before moving on to tests * APPEALS-31821 added error handling and logging * APPEALS-31821 repurposed class into a workflow * APPEALS-31821 removed mailer * APPEALS-38907 Connect job with TranscriptionTransformer workflow * APPEALS-38907 Small fixes after writing xray tests * APPEALS-31821 added rspec tests and refactored workflow * APPEALS-38907 Finish spec files * APPEALS-31821 removed the new datadog metrics that was made * APPEALS-31821 removed the new datadog metrics that was made * APPEALS-31821 added back old datadog metrics * APPEALS-31821 removed ext_claim schema * APPEALS-31821 moved transcription_transformer to workflow folder * APPEALS-38907 Complete spec * APPEALS-31821 adjusted rspec tests and fixed minor edgecase for empty string identifiers * APPEALS-31821 fixed minor formatting issues * APPEALS-31821 edited spacing * APPEALS-31821 added error class back * APPEALS-38907 Fix argument being passed to TranscriptionTransformer * APPEALS-38907 Fix footer on rtf layout * APPEALS-38907 Fix spec to mock TranscriptionTransformer correctly * APPEALS-38907 Change URI.open call to be more safe --------- Co-authored-by: Minhazur Rahaman Co-authored-by: msteele Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> * Refactor double returns to a single return * Update snapshots * Remove unnecessary commented out code * Update linting issues * Fix linting error * Update snapshots for jest testing * jefftmarks/APPEALS-40657 (#20851) * APPEALS-40657 Refactor DownloadTranscriptionFileJob * APPEALS-40657 Refactor TranscriptionFile * APPEALS-40657 Refactor UploadTranscriptionFileToS3 workflow * APPEALS-40657 Refactor DownloadTranscriptionFileJob spec * APPEALS-40657 Update migration and model for TranscriptionFile to associate with hearing instead of appeal * APPEALS-40657 Update DownloadTranscription job to save records with hearing instead of appeal attributes * APPEALS-40657 Update subject for conference to include hearing attributes instead of appeal * APPEALS-40657 Schema * APPEALS-40657 Fix failing spec in virtual hearing spec * APPEALS-40657 Update comment in migration * APPEALS-40657 Update schema * Min/APPEALS-39905 (#20853) * APPEALS-39905 audible text will now be displayed as [INAUDIBLE] * APPEALS-39905 csv getting made and uploaded when inaudibles are found * APPEALS-39905 turned tmp clean up back on * APPEALS-39905 adjusted rspec tests * APPEALS-39905 adjusted transformer rspec tests * APPEALS-39905 added more rspec tests to transformer workflow * APPEALS-39905 added guards for nil values in hearing info * added byebug * removed byebug * APPEALS-39905 Resolve merge conflicts in download transcription job spec * APPEALS-39905 changed inaudible display * APPEALS-39905 changed test url back * APPEALS-39905 converts input vtt to readable format if invalud utf is found * APPEALS-39905 adjusted rspec tests --------- Co-authored-by: Jeff Marks * akonhilas/APPEALS-31620 (#20788) * APPEALS-31620: initial commit * APPEALS-31620: finished all webex service/response/fakes/spec updates * APPEALS-31620: adding recording details job * APPEALS-31620: cannot get past rspecs here * APPEALS-31620: update rspecs * APPEALS-31620: add file * APPEALS-31620: finalized rspecs, removed Hearings prepend - unnecessary after rebuild * APPEALS-31620: resolve webex service rspec * APPEALS-31630: code cleanup * APPEALS-31620: remove hearings prepend, perform_later, scheduled_for, regex * APPEALS-31620: rspec * jefftmarks/APPEALS-40909 (#20951) * APPEALS-40909 Create migration and write spec tests * APPEALS-40909 Update conference_link model to associate with polymorphic hearing * APPEALS-40909 Write spec for add index to conference links * APPEALS-40909 Add spec to test conference link factory after null constraint removed * APPEALS-40909 Uncomment out code in migration to see if tests pass on GHA * APPEALS-40909 Implement database cleaner fix and write down test for index migration * APPEALS-40909 Test conference links can belong to hearings and hearing days * APPEALS-40909 Remove migration tests * APPEALS-40909 Refactor delete conference link job spec to avoid static record count * APPEALS-40909 Fix duplicate examples in download transcription file spec --------- Co-authored-by: Adam Ducker * APPEALS-39907 (#20952) * APPEALS-39907 Initial commit * APPEALS-39907 update convention * APPEALS-39907 update render call * APPEALS-39907 update to/from within .erb * APPEALS-39907 added to and from attributes * APPEALS-39907 to/from update * APPEALS-39907 direction update * APPEALS-39907 conditional added * APPEALS-39907 update conditional * b_reed/APPEALS-39907 added mailing in error handling for DownloadTranscriptionFileJob and GetWebexRecordingDetailsJob * APPEALS-39907 added more mailing code for error handling * APPEALS-39907 adjusted rspec tests * APPEALS-39907 Unit tests complete * APPEALS-39907 adjusted lint * APPEALS-39907 update class ActionMailer * APPEALS-39907 added comments * APPEALS-39907 removed redundant unit test * APPEALS-39907 update demo url * APPEALS-39907 seperated env instances * APPEALS-39907 update id fetch method ln: 209 * APPEALS-39907 remove filename parameter ln: 32 * APPEALS-39907 added filename argument back * APPEALS-39907 added the lint disabler back * APPEALS-39907 updated demo env test * b_reed/APPEALS-39907 fixed minor issue with argument names * APPEALS-39907 updated how appeal ids are found * APPEALS-39907 more minor fixes * APPEALS-39907 * APPEALS-39907 email address to send mail and cc now changes depending on rails env * APPEALS-39907 fixed rspec tests * APPEALS-39907 Update html doc layout * APPEALS-39907 adjusted email addresses and some refactors * APPEALS-39907 adjusted rspec tests * APPEALS-39907 update case statements * APPEALS-39907 email preview template * APPEALS-39907 update values * APPEALS-39907 added appeal id * APPEALS-39907 previewer lint * APPEALS-39907 remove blank space * APEALS-39907 disable actionMailer lint * APPEALS-39907 fixed rspecs * APPEALS-39907 config file update --------- Co-authored-by: Minhazur Rahaman * akonhilas/APPEALS-40915 (#20948) * APPEALS-40915: initial commit * APPEALS-40909 Create migration and write spec tests * APPEALS-40909 Update conference_link model to associate with polymorphic hearing * APPEALS-40909 Write spec for add index to conference links * APPEALS-40909 Add spec to test conference link factory after null constraint removed * APPEALS-40909 Uncomment out code in migration to see if tests pass on GHA * APPEALS-40915: updated comments to job * APPEALS-40909 Implement database cleaner fix and write down test for index migration * APPEALS-40915: added error catching, updated hearing model, removed webex hearing day link creation * APPEALS-40915: updated existing rspec tests to include/exclude new functionality * APPEALS-40909 Test conference links can belong to hearings and hearing days * APPEALS-40915: prepping branch for merge * APPEALS-40915: job creates a hearing conference link * APPEALS-40915: updating rspecs * APPEALS-40915: finished rspecs * APPEALS-40915: nullifying link values to resolve frontend errors * APPEALS-40915: added create nv conf call to correct spot, removed old code/rspecs * APPEALS-40915: final rspec addition * APPEALS-40915: remove migration specs * APPEALS-40915: add code review updates --------- Co-authored-by: Jeff Marks Co-authored-by: Adam Ducker * Missed rubocop merge conflict * akonhilas/APPEALS-40921 (#21116) * APPEALS-40921: added method to concern and reference to both hearing serializers * APPEALS-40921: change get to fetch, conference link find by hearing, fix rubocop lint errors * APPEALS-40921: change get to fetch, conference link find by hearing, fix rubocop lint errors * Min/APPEALS-37605 (#21151) * APPEALS-37605 added route and test json response * APPEALS-37605 file now gets downloaded onto local computer * APPEALS-37605 added user verification * min/APPEALS-37605 added rspec and factory for transcription_file * APPEALS-37605 finished writing rspec tests * APPEALS-37605 finished writing rspec tests * APPEALS-37605 added more error handling * APPEALS-37605 Move controller back to hearings folder * APPEALS-37605 Remove unused methods from controller * APPEALS-37605 Move transcription_file_spec to hearings folder --------- Co-authored-by: msteele * jefftmarks/APPEALS-37292-37293 (#21140) * APPEALS-37292 Create transcription files table * APPEALS-37292 Adjust table styling * APPEALS-37292 Update legacy conditional to show transcription details * APPEALS-37292 Conditionally render different sections of transcription details * APPEALS-37292 Implement useState and useEffect to reduce renders on table * APPEALS-37293 Populate table with updated transcription file serializer * APPEALS-37292-37293 * APPEALS-37293 Comment code * APPEALS-37293 Update snapshot * APPEALS-37292-37293 Update snapshot * APPEALS-37293 Revert transcription files controller * APPEALS-37293 Update Details jest test * APPEALS-37293 Add date attribute to hearings data for jest test * APPEALS-37293 Update imports on Details.jsx jest test * APPEALS-37293 Update hearing concern with transcription file by recording method * APPEALS-37292 Update LegacyHearing model and serializer * APPEALS-37293 Update feature test for details page * APPEALS-37293 Fix typo in table id * APPEALS-37293 Add aws link to factory * APPEALS-37293 Fix hearing links snapshot to pass jest test * APPEALS-37293 Fix hearing factory * APPEALS-37293-37293 * APPEALS-37293 Fix transcription files controller spec * APPEALS-37293 Update to group files on table by docket number * APPEALS-37293 Refactor route/controller without responds_to and move css * APPEALS-37293 Fix feature test to reflect route update * APPEALS-37293 Fix linting on css * APPEALS-37293 scss lint * BelongsToPolymorphicHearingConcern updates from Rails 6.0 * jefftmarks/APPEALS-40907 (#21249) * APPEALS-40907 Create polymorphic hearing concerns specific to conference links and transcription files and update models * APPEALS-40907 Remove unnecessary polymorphic concerns * APPEALS-40907 Update daily docket for one to one nonvirtual webex conference links * APPEALS-40907 Update models * APPEALS-40907 Update models * APPEALS-40907 Update and remove jest tests * Min/APPPEALS-42711 (#21285) * APPEALS-42711 adjusted query for webex recordings list * APPEALS-42711 added rspec tests for webex_conference_link * APPEALS-42711 some code climate refactors * APPEALS-42711 fixed code climate issues and webex service argument refactor * APPEALS-42711 fixed som broken rspec tests * APPEALS-42711 fixed a few code climate issues with mailer * APPEALS-42711 refactored more code climate issues with mailer and webex implementation * APPEALS-42711 fixed broken rspec tests and finishing up with clearing up viable code climate issues * APPEALS-42711 fixed some broken rspec tests and more code climate issues * APPEALS-42711 changed names of classes and methods to fix code climate issue * APPEALS-42711 changed names of classes and methods to fix code climate issue * APPEALS-42711 changed names of classes and methods to fix code climate issue * APPEALS-42711 added max value back in to webex recordings * APPEALS-42711 added max value back in to webex recordings * min/APPEALS-42711 adjusted query * APPEALS-42711 refactored some code * akonhilas/APPEALS-40906 (#21165) * APPEALS-40906: remove sub nbf exp from hearing_day * APPEALS-40906: updates to serializer, coHostHearingLink to coHostLink, proper conditionals for virtual vs non virtual * APPEALS-40906: Update so many jest tests and test data * APPEALS-40906: merge latest from feature * APPEALS-40906: Update snapshots after merge * APPEALS-40906: updates to feature test * APPEALS-40906 change serializer debug attempt * APPEALS-40906 Revert previous debugging commit and add isVirtual back to VirtualHearingLink.jsx * APPEALS-40906 Return references of conference_links to conference_link * APPEALS-40906 Duplicate changes from hearing_serializer to legacy_hearing_serializer * APPEALS-40906 Update failing spec and serializer * APPEALS-40906 Allow for null links prop in LinkContainer in HearingLinks.jsx * APPEALS-40906 Ensure correct classname and button text for hearing coordinator link * Fix hearing_day_spec to user singular conference link * Update hearing details snap shots * APPEALS-40906 Update Details.test.js snapshot * APPEALS-40906 Fix hearing details feature by adding was virtual to HearingLinks.jsx * APPEALS-40906 Remove hearing links from central and video details page * Uncomment hearing details feature spec * APPEALS-40906 Remove hearing links from details if hearing type video and hearing in past * APPEALS-40906 Add bang operator * APPEALS-40906 Update Hearing Links jest to not use redux store * APPEALS-40906 Remove console.log * APPEALS-40906 Remove console.log * APPEALS-40906 Ensure hearing day links replace virtuali nks when converted from virtual to video * APPEALS-40906 Update snapshots * APPEALS-40906 Update hearing serializers so attributes conform across virtual and nonvirtual conference links * APPEALS-40908 Update HearingLinks UI to show copy link button at all times * APPEALS-40906 Update hearing details feature test to reflect hearing links for all hearing types * APPEALS-40906 Fix jest test props for HearingLinks test * APPEALS-40906 Uncomment out jest config * APPEALS-40906: remove unused code from hearing links test * APPEALS-40906: move non virtual conference link to hearing concern --------- Co-authored-by: msteele Co-authored-by: Jeff Marks Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> * Min/APPEALS-43214 (#21344) * APPEALS-43214 added new error handling for file upload and new param to custom exception * APPEALS-43214 wrote rspec tests and made some adjustment to error handlers * rerun checks * rerun checks * APPEALS-43214 adjusted failing rspec * APPEALS-43214 Linting fix * APPEALS-43214 Indentation fixes * APPEALS-43214 Update mailer to handle different transcription file jobs * APPEALS-43214 Added beginning of hour to webex list calls * APPEALS-43214 Update mailer to show download link and fix failing test * APPEALS-43214 Refactor transcription file issues mailer * APPEALS-43214 Remove old spec file * APPEALS-43214 Refactor content section of mailer * APPEALS-43214 Add missing comma * APPEALS-43212 Refactor mailer to render nested lists * APPEALS-43214 Update prodtest email and demo vs dev distinction * APPEALS-43214 Linting and fixes to create conference job * APPEALS-43214 Call job#extra * APPEALS-43214 job#log_error * APPEALS-43214 Remove extra method * APPEALS-43214 Fix log error issue * APPEALS-43214 Fix typo * APPEALS-43214 Incorporate action direction into job action hash * APPEALS-43214 Remove byebug --------- Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> Co-authored-by: msteele Co-authored-by: Jeff Marks * b_reed/APPEALS-34071-v2 (#21422) * Add rcredstash and dynamodb * Update ports * Fix m1 docker compose * Add kms * Add initializers * Upgrade aws-sdk * Lint roll * Update table name * Add back VirtualHearings::RefreshWebexAccessTokenJob * Add refresh_access_token method to ExternalApi::WebexService * Add back AccessTokenRefreshResponse * Add back in error handling in Webex Response class * Add new job to SCHEDULED_JOBS * Add WebexInvalidTokenError error class * Add refresh_access_token to fake WebexService * Add back in specs * APPEALS-34071 bundle install additions gemfile.lock * APPEALS-34071 slim out test * Fix the 'levers exists' message overwritting test output --------- Co-authored-by: Matthew Thornton * Move methods from hearing.rb to hearing concern and add default aws region to resolve error * Match yarn.lock to master * Update ref for caseflow-commons * Update gemfile.lock * Update scheduled jobs key and move job to different folder * Update job to set user manually and pass in updated_by * msteele/APPEALS-45182 Fix transcription_files relationships on Hearings/LegacyHearings (#21492) * APPEALS-45182 Reinforce has_many relationships * APPEALS-45182 Add missing comma * Resolve lint issue --------- Co-authored-by: Matthew Thornton * Hotfix/APPEALS-45218 (#21503) * APPEALS-45218 reverted webex config arguments * APPEALS-45218 reverted webex config arguments * APPEALS-25218 Update WebexService to remove config in favor of individual arguments * APPEALS-45218 Format comment --------- Co-authored-by: msteele * B_reed/hotfix_APPEALS-45285 (#21531) * APPEALS-45285 Bug Fix * APPEALS-45285 update pexip_service_spec * APPEALS-45285 remove white space * Hotfix/APPEALS-45218-v2 (#21522) * APPEALS-45218 reverted webex config arguments * APPEALS-45218 reverted webex config arguments * APPEALS-45218 added query argument to webex conference link * APPEALS-45218: webex recordings endpoint correction * APPEALS-45218 adjusted arguments during inititalizing of refresh tokens * APPEALS-45218 fixed rspec * APPEALS-45218: added error catching on webex service response * APPEALS-45218: remove tested code * APPEALS-45218 Go back to leveraging HTTPI response inherited methods --------- Co-authored-by: Ariana Konhilas Co-authored-by: msteele * msteele/APPEALS-45349 (#21539) * APPEALS-45349 Check for error on HTTPI object, then create Webex response object and fail with specific error * APPEALS-45349 Flip conditionals * APPEALS-45349 Fix linting errors * hotfix/APPEALS-45399-45401-45472 (#21555) * APPEALS-45399-45401 Update fetch webex list and details to retrieve apikey from cred stash * APPEALS-45399-45401 Remove CGI escape from fetch webex list job * APPEALS-445399-45401 Update spec files for webex list and details job * APPEALS-45399-45401 Include error handling for transcription issue mailer failed delivery * Revert "APPEALS-45399-45401 Include error handling for transcription issue mailer failed delivery" This reverts commit d9ce585c59444f9793e681e58060d71872057e4c. * hotfix/APPEALS-45472 (#21562) * APPEALS-45472 Refactor error handling for transcription * APPEALS-45472 Include missing provider in error details for fetch recording list job and update comment for mailer * Hotfix/APPEALS-45818 (#21632) * APPEALS-45818: parsing fix, remove topic param, fix response * APPEALS-45818: revert list and details job back to original state, update tests --------- Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> * msteele/APPEALS-45285-v2 Fix delete_conference in PexipService (#21634) * APPEALS-45285 update ExternalApi::PexipService#delete_conference to accept virtual_hearing as the only argument * APPEALS-45285 Update spec to use virtual_hearing * APPEALS-45285 Linting fixes * hotfix/APPEALS-45828 (#21625) * APPEALS-45828 Include call to create webex conference links in base hearing update form * APPEALS-45828 Update details feature spec to expect webex link creation on conversion from virtual * Fix needing to reload to display co host link on details page * Fix incorrect method name * Remove temporary rendering of default pexip link for webex hearings after conversion * APPEALS-45828 Fix hearing mailer spec context issues and null check for BAD_VIRTUAL_LINK_TEXT * APPEALS-45828 Remove file name argument from fetch details job spec * APPEALS-45828 Fail on bad hearing link if link nil or legacy link * APPEALS-45828 Sanitize url string * APPEALS-45828 Undo url sanitize * Remove unused styles after merge * APPEALS-45285 added conditional to handled nil conference ids (#21766) Co-authored-by: Minhazur Rahaman * feature/APPEALS-45998 Webex Rooms API Workaround (#21810) * Min/APPEALS-46009 (#21690) * APPEALS-46005: job, job logic, env placeholder, schedule update * APPEALS-46011 Add fetch rooms list and fetch room details methods to webex service * APPEALS-46011 Update fakes * APPEALS-46005: merge 46011 logic updates * APPEALS-46009 added the new webex room details job * APPEALS-46011 Update specs * commenting out code * commenting code back in * APPEALS-46009 adjusted rspec tests * APPEALS-46009 adjusted rspec tests * APPEALS-46005: add rspec, comment out rooms details call, final filter * APPEALS-46009 adjusted rspec tests * APPEALS-46005: rough filter test added to rooms list spec commented out * APPEALS-46009 adjusted rspec tests * APPEALS-46009: filter rspec, filter in rooms list job, add fake data * APPEALS-46009: resolved comments --------- Co-authored-by: Ariana Konhilas Co-authored-by: Jeff Marks * akonhilas/APPEALS-46006 (#21734) * APPEALS-46006: initial updates to recordings list job, recordings list resp, recordings details job, room meeting details job * APPEALS-46006: webex service spec update, fake update, recordings list response update * APPEALS-46006: recordings details job and spec updates, lint fix recordings list job * APPEALS-46006: recordings list spec and job updates * APPEALS-46006: room meeting details spec updates * APPEALS-46006: final updates to fake data * APPEALS-46006: changing topic to actual webex response * APPEALS-46006 create and use self.acceses_token method in WebexService classes * APPEALS-46006 Update failing rspec for Details and List Jobs * APPEALS-46006: update mailer, preview, specs, lint error --------- Co-authored-by: msteele --------- Co-authored-by: minhazur9 <65432922+minhazur9@users.noreply.github.com> Co-authored-by: Ariana Konhilas Co-authored-by: Jeff Marks Co-authored-by: Ariana Konhilas <109693628+konhilas-ariana@users.noreply.github.com> * Hotfix/APPEALS-46083 (#21832) * Min/APPEALS-46009 (#21690) * APPEALS-46005: job, job logic, env placeholder, schedule update * APPEALS-46011 Add fetch rooms list and fetch room details methods to webex service * APPEALS-46011 Update fakes * APPEALS-46005: merge 46011 logic updates * APPEALS-46009 added the new webex room details job * APPEALS-46011 Update specs * commenting out code * commenting code back in * APPEALS-46009 adjusted rspec tests * APPEALS-46009 adjusted rspec tests * APPEALS-46005: add rspec, comment out rooms details call, final filter * APPEALS-46009 adjusted rspec tests * APPEALS-46005: rough filter test added to rooms list spec commented out * APPEALS-46009 adjusted rspec tests * APPEALS-46009: filter rspec, filter in rooms list job, add fake data * APPEALS-46009: resolved comments --------- Co-authored-by: Ariana Konhilas Co-authored-by: Jeff Marks * APPEALS-46083 hearing details page now shows NA when hearing is converted from virtual to non or if postponed * APPEALS-46083 fixed conditional for cancelled hearings and virtual hearings --------- Co-authored-by: Ariana Konhilas Co-authored-by: Jeff Marks * akonhilas/APPEALS-46130 (#21804) * APPEALS-43160: Fix linting errors in OrgUsers * APPEALS-43160: fix margins and move divider * APPEALS-43160: remove margin top on search bar styling * APPEALS-43160: fix webex lint errors * APPEALS-43160: move radio button style div inside visibility check * APPEALS-46130: move css inline style to scss * APPEALS-46130: fix lint errors, remove gray line * Hotfix/APPEALS-48161 (#21917) * APPEALS-48161: remove, refactor, fake update, specs, filename update * APPEALS-48161: kickoff gha on right branch merge * APPEALS-48161: filter title call only once * APPEALS-46385: remove s, add aws-sdk, bundle install, move rcredstash up (#21941) * Adjust spacing in gemfile * Hotfix/APPEALS-49560 (#22015) * APPEALS-49560 moved non-virtual conference link creation to hearing concern * APPEALS-49560 moved non_virtual conference creation inside of reschedule method * APPEALS-49560 added a new spec test to test link creation * APPEALS-49560 refactored rspec * APPEALS-49560 removed additional method in after_create to prevent risk of duplication * APPEALS-49560 removed additional method in after_create to prevent risk of duplication * Hotfix/APPEALS-49624 (#22016) * APPEALS-49624: update api_call in fetch jobs for error emails, change i to breaks * APPEALS-49624: add rooms and room details to mailer preview, correct api url * APPEALS-49624: cleaning up rspecs * APPEALS-49624: update query format, add mailer tests * APPEALS-49624: update mailer keys * APPEALS-49624: fix lint errors * APPEALS-49624 Remove rubocop ignores and fix query format --------- Co-authored-by: msteele * Remove extra newline for Rubocop * Hotfix/Appeals-49560-v2 (#22052) * APPEALS-49560 moved non-virtual conference link creation to hearing concern * APPEALS-49560 moved non_virtual conference creation inside of reschedule method * APPEALS-49560 added a new spec test to test link creation * APPEALS-49560 refactored rspec * APPEALS-49560 removed additional method in after_create to prevent risk of duplication * APPEALS-49560 removed additional method in after_create to prevent risk of duplication * APPEALS-49560 added extra filtering for conference link querying and same fix applied to HearingPostponementRequestMailTask * Update app/models/concerns/hearing_concern.rb Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> --------- Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> * Remove extra newline after merge conflict * Recover lost code to compress multiple lines by same speaker into one large block * Reintroduce code after bad merge conflict resolution * Min/APPEALS-50859 (#22081) * remediated code climate issues * remediated code climate issues * remediated code climate issues * fixed duplicate code issues * fixed duplicate code issues * fixed duplicate code issues * APPEALS-50859 added new concern to abstract duplicate code * APPEALS-50859 fixed new concern climate issues * APPEALS-50859 added new config method for rooms endpoints to concern * APPEALS-50859 added new config method for rooms endpoints to concern * APPEALS-50859 added new config method for instant connect to concern * APPEALS-50859 added new config method for instant connect to concern * Merge changes from feature/APPEALS-25125 into APPEALS-49626 (#22185) * Wmedders21/appeals 44873 (#21469) * add rubyzip gem * Test: Hearings::ZipAndUploadTranscriptionFilesJob * Feature: Hearings::ZipAndUploadTranscriptionFilesJob * Add zip subfolder to tmp/transcription_files * Move new gem to proper alphabetical position in list * revert gem file changes * update gem file with feature branch * refactor #perform method parameters * update parameter name * Piedram/appeals 44897 (#21519) * Create new Workflow transcription_packages: Step One * add some changes * Modified the parameters for ZipAndUploadTranscriptionFilesJob * Modified spec test * Update transcription_packages.rb * remove extra blank line --------- Co-authored-by: piedram * Wmedders21/appeals 44874 (#21512) * add rubyzip gem * Test: Hearings::ZipAndUploadTranscriptionFilesJob * Feature: Hearings::ZipAndUploadTranscriptionFilesJob * Add zip subfolder to tmp/transcription_files * Move new gem to proper alphabetical position in list * revert gem file changes * update gem file with feature branch * refactor #perform method parameters * update parameter name * add zip extension to valid file types * Test: transcription file creation and upload to s3 * Hearings::ZipAndUploadTranscriptionFilesJob updates db and uploads to s3 * resolve merge conflict * APPEALS-44876 Work order job file (#21504) * APPEALS-44876 Work order job file * Updated filename with work order name * Refactored create table method and updated spec --------- Co-authored-by: Jim Foley * Create new Workflow transcription_packages: Step Two (#21570) * Add call function to the new job, and modified spec files * call the jobs * Work order file upload to AWS s3 (#21582) * Work order file upload to AWS s3 * refactor code for upload to AWS * refactor code for instance variables * Revert "refactor code for instance variables" This reverts commit b61c72a3f2e30e4003e9d4a4dc57a01dbb105443. * code refactor for work order file * APPEALS-44877 refactor for work order * APPEALS-43190 (#21448) * APPEALS-43190 initial migration file * APPEALS-43190 add seed data * APPEALS-43190 add magic comment * APPEALS-43190 move seed command * APPEALS-43190 add bang! * APPEALS-43190 update migration file * APPEALS-43190 attribute name fix and updates * APPEALS-43190 fix seed attribute * APPEALS-43190 stubbed more seed data * APPEALS-43190 remove seed comment * APPEALS-43190 remove comment * APPEALS-43190 add magic comment * APPEALS-43190 update comments * Wmedders21/appeals 44875 (#21605) * Add retry logic for upload failure * fix indentation rubocop warning * Refactor zip job * remove blank line --------- Co-authored-by: Jim Foley * akonhilas/APPEALS-43193 (#21581) * APPEALS-43193: Add new columns and indexes to transcriptions table * APPEALS-43193: Add new columns and indexes to transcriptions table * APPEALS-43193: move index to own file * APPEALS-43193: Update transcription contractor id to bigint * APPEALS-43193: bigint * APPEALS-43193: add fk after trancription_contractors table added * APPEALS-44878 refactor for retry and upon success kickoff zip file job (#21652) * APPEALS-44878 refactor for retry and upon success kickoff zip file job * APPEALS-44878 refactor code * APPEALS-44878 refactor xls path * lint error fix * Piedram/appeals 45696 (#21655) * Create new Workflow Error handling transcription_packages: Step Three related to appeals-31793 * MOdified spec file * fix lint error * APPEALS-45696 refactor for work_order_params --------- Co-authored-by: Kamala Madamanchi Co-authored-by: Jim Foley * Wmedders21/appeals 43231 (#21670) * example bom file * wip BoM job * wip bom job * bom job uploads bom file to aws s3 * fix style - indentation * fail with custom error * Ruby style fixes * fix rubocop indentation errors --------- Co-authored-by: Jim Foley * APPEALS-45503 created table for transcription_packages (#21685) * APPEALS-45503 created table for transcription_packages * APPEALS-45503 modal and factory creation for transcription package --------- Co-authored-by: Jim Foley * APPEALS-45503 updated and add new columns to transcription_packages table (#21724) * APPEALS-45503 updated and add new columns to transcription_packages table * APPEALS-45503 updated spec and factory for new migration * APPEALS-45503 refactor code * APPEALS-45503 transcription package spec moved to spec/models/ hearing * APPEALS-45503 removed spec from the spec/workflow * Min/APPEALS-43140 (#21640) * APPEALS-43140 transcription page rendes * APPEALS-43140 tabs are rendered * APPEALS-43140 set user permissions and changed route name * APPEALS-43140 made jest tests, adjusted permissions * APPEALS-43140 added descriptions in the tabs * APPEALS-43140 made search bar and package files button * APPEALS-43140 styling changes * APPEALS-43140 changed name of main component * APPEALS-43140 added empty table * APPEALS-43140 added more css styling * APPEALS-43140 added more styling, filters and sorting * APPEALS-43140 styling the table and page * APPEALS-43140 refactors and jest tests * Update snapshots for queue table jest test * APPEALS-43140 fixed filter, added jest tests, and a new prop for pre filerting statuses before render * APPEALS-43140 adjusted where permission checks and routing should be located --------- Co-authored-by: Jeff Marks Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> * Piedram/appeals 43237 (#21751) * Create Master File * add file in s3 * APPEALS-45503 updated and add new columns to transcription_packages table * APPEALS-45503 updated spec and factory for new migration * APPEALS-45503 refactor code * Add spec file * If failed after retries sent Notification sent * Modified spec to fix the issue * refactoring code * Add test to spec file * remove comment and space from the spec file * Modified function in spec * Fix lint error --------- Co-authored-by: Kamala Madamanchi Co-authored-by: Jim Foley * Wmedders21/appeals 43185 (#21764) * WIP endpoint for transcription packages show * WIP transcription packages serializer * remove space - style * update schema with transcription_packages * add show route for transcription packages * Request test: Hearings::TranscriptionPackagesController show response * Migration: TranscriptionPackageHearings * Migration: TranscriptionPackageLegacyHearing * Models TranscriptionPackageHearing and TranscriptionPackageLegacyHearing * Add Hearings::TranscriptionPAckageSerializer * Add relationships and methods to TranscriptionPackage * Update TranscriptionPackage Factory * Add #show action for Hearings::TranscriptionPackagesController * schema migration * rubocop style fixes * fix date issue * updated TranscriptionFactory time values to be varying * add test for record not found sad path * fix test to find TranscriptionContractor id * refactor TranscriptionPackage#formate_case_details --------- Co-authored-by: Jim Foley * APPEALS-45264 - Populate Transcription File table in Unassigned Tab (#21937) * Feature/appeals 46208.rc (#21746) * Added Assignment Queue link to Switch Users test page * Updated controller to validate role and redirect * Updated error information * Updated rspec tests * Ricky/appeals-43523 (#21345) * Added Assignment Queue link to Switch Users test page * Updated controller to validate role and redirect * Updated error information * Updated rspec tests * Added Download Ineligible Judge List * APPEALS-43116 add sentry error capture to push priority job and updated specs (#21369) * Updated query to include css_id and updated id fetch * Updates button styling, fixed csv to have all values but judge name * Updated id to sattyid values, and added name data to column * fixed linting error * Updated logic to account for inactive users in both caseflow and vacols * Updated button component and cleaned up code * Updated handling of ids in situation where sattyid is unavailable in record * Updated to have individual columns for caseflow id and satty id * APPEALS-43117 Add QA Users for testing (#21580) * APPEALS-43117 Add QA Users for testing * Fix rspecs --------- Co-authored-by: Christopher Detlef <> * APPEALS-42266 Remove CaseDistributionLevers seed config in Rails helper and update required spec files * APPEALS-42266 fixing a couple of linting issues * APPEALS-43523 reverted the legacy tasks controller (#21713) * APPEALS-43523 reverted the legacy tasks controller * APPEALS-43523 reverted the erorrs concern too * Update legacy_tasks_controller_spec.rb * reverted raven call * APPEALS-46208 fixed code climate warning about a bad naming convention. * APPEALS-46208 fixed indentation --------- Co-authored-by: 631068 Co-authored-by: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Co-authored-by: kristeja <112115264+kristeja@users.noreply.github.com> Co-authored-by: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Co-authored-by: kristeja * APPEALS-44148: Add ruby CE API Gem (#21520) * hotfix/APPEALS-46182 (#21709) * Added tests and added logic to account for sct distribution * Added more tests and changed logic * Adding comments * Fixing logic for SCT * Update test and logic * Fixing tests * Update docket switch mail task to match corrected logic * Fixing logic * Cleaning up refactor * Worked with the amazing ALex and we figured it out! * Fixing linting issue * Fix linting issues * Skipping flaky tests * Skipping another flaky test --------- Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * MattT/APPEALS-46383: Upgrade aws-sdk gem to v3.2 (#21748) * Upgrade aws-sdk gem to v3.2 * Update caseflow-commons hash * Apply new hash update * Skip flaky test --------- Co-authored-by: Matthew Thornton * Update gemfile and gemfile.lock to point to new bgs version (#21749) * Update gemfile and gemfile.lock to point to new bgs version * Skipping flake specs in review_spec.rb file * Revert "APPEALS-44148: Add ruby CE API Gem (#21520)" (#21762) This reverts commit 3a393d1f3f40b35b5063c2b7d0f1508ef5a61bb7. * Update ruby-bgs gemfile and gemfile.lock to new version (#21761) * Update MAC_INTEL.md Update Oracle client link * APPEALS-45264 - Get pagenated data from the backend for transcription file dispatch * APPEALS-45264 - Some cleanup and repairing filter and sort on the front end * APPEALS-45264 - More general frontend cleanup * APPEALS-45264 - Fix filtering using better model scopes * APPEALS-45264 - Fix sorting using better scopes * APPEALS-45264 - Cleanup and testing * APPEALS-45264 - Update permissions and test * APPEALS-45264 - Change permissions back to try and fix a failing test * APPEALS-45264 - Update transcription files seed and add it to seeds --------- Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> Co-authored-by: 631068 Co-authored-by: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Co-authored-by: kristeja <112115264+kristeja@users.noreply.github.com> Co-authored-by: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Co-authored-by: kristeja Co-authored-by: youfoundmanesh <129548081+youfoundmanesh@users.noreply.github.com> Co-authored-by: Alex Ferencz VA <156860944+aferencz1987VA@users.noreply.github.com> Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> Co-authored-by: Matthew Thornton Co-authored-by: cacevesva <109166981+cacevesva@users.noreply.github.com> Co-authored-by: Alec Spottswood * B_reed/merge_master_into_25125_v3 (#22012) * Feature/appeals 46208.rc (#21746) * Added Assignment Queue link to Switch Users test page * Updated controller to validate role and redirect * Updated error information * Updated rspec tests * Ricky/appeals-43523 (#21345) * Added Assignment Queue link to Switch Users test page * Updated controller to validate role and redirect * Updated error information * Updated rspec tests * Added Download Ineligible Judge List * APPEALS-43116 add sentry error capture to push priority job and updated specs (#21369) * Updated query to include css_id and updated id fetch * Updates button styling, fixed csv to have all values but judge name * Updated id to sattyid values, and added name data to column * fixed linting error * Updated logic to account for inactive users in both caseflow and vacols * Updated button component and cleaned up code * Updated handling of ids in situation where sattyid is unavailable in record * Updated to have individual columns for caseflow id and satty id * APPEALS-43117 Add QA Users for testing (#21580) * APPEALS-43117 Add QA Users for testing * Fix rspecs --------- Co-authored-by: Christopher Detlef <> * APPEALS-42266 Remove CaseDistributionLevers seed config in Rails helper and update required spec files * APPEALS-42266 fixing a couple of linting issues * APPEALS-43523 reverted the legacy tasks controller (#21713) * APPEALS-43523 reverted the legacy tasks controller * APPEALS-43523 reverted the erorrs concern too * Update legacy_tasks_controller_spec.rb * reverted raven call * APPEALS-46208 fixed code climate warning about a bad naming convention. * APPEALS-46208 fixed indentation --------- Co-authored-by: 631068 Co-authored-by: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Co-authored-by: kristeja <112115264+kristeja@users.noreply.github.com> Co-authored-by: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Co-authored-by: kristeja * APPEALS-44148: Add ruby CE API Gem (#21520) * hotfix/APPEALS-46182 (#21709) * Added tests and added logic to account for sct distribution * Added more tests and changed logic * Adding comments * Fixing logic for SCT * Update test and logic * Fixing tests * Update docket switch mail task to match corrected logic * Fixing logic * Cleaning up refactor * Worked with the amazing ALex and we figured it out! * Fixing linting issue * Fix linting issues * Skipping flaky tests * Skipping another flaky test --------- Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * MattT/APPEALS-46383: Upgrade aws-sdk gem to v3.2 (#21748) * Upgrade aws-sdk gem to v3.2 * Update caseflow-commons hash * Apply new hash update * Skip flaky test --------- Co-authored-by: Matthew Thornton * Update gemfile and gemfile.lock to point to new bgs version (#21749) * Update gemfile and gemfile.lock to point to new bgs version * Skipping flake specs in review_spec.rb file * Revert "APPEALS-44148: Add ruby CE API Gem (#21520)" (#21762) This reverts commit 3a393d1f3f40b35b5063c2b7d0f1508ef5a61bb7. * Update ruby-bgs gemfile and gemfile.lock to new version (#21761) * Update MAC_INTEL.md Update Oracle client link * APPEALS-44496 Fix Factory (#21770) * APPEALS-44496 Fix Factory * updated falky test --------- Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * fix(va facilities): update per page param (#21754) * Hotfix/appeals 44033:Fix Judge and Attorney tasks in the QualityReview workflow are being erroneously cancelled (#21786) * Adjusts the send_to_hearings_branch method to exclude JudgeQualityReviews when cancelling all Judge tasks and subtasks rebase * adjusts spacing and adds back deleted test * Remove unnecessary schema changes and adds an exclamation mark to account for method that changes data * add exclamation to handle_judge_tasks --------- Co-authored-by: Zackary Borges-Rowe Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * Hotfix/appeals 45150 (#21627) * Added distribution task with children status change on split appeal test * Added tests and commented out pry session in appeal * Changed split appeal test to test for both defects * Added conditional logic to status_is_valid_on_create in task model * looking for defect * Reverted changes to finalize split appeal method added condtional logic to schedule hearing tasks model * Removed debugger and commented out lines * Added erroneously removed lines of code and added spacing * Fix linting errors * Added conditional logic for appeal split process --------- Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * hotfix/APPEALS-36313-V2 (#21782) * avoid IHP task creation with an open pre-docket task * Fix linting errors on RSpec * Skip flaky BGS Share Error test * comment failed test case --------- Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> Co-authored-by: raymond-hughes * APPEALS-47106 Caseflow Swagger (#21847) * Add Rswag and convert existing API documentation (#21778) * add rswag * Update Gemfile.lock * Setup yaml UI * remove and reroute old swagger files * update format and servers values * Update route_docs_controller.rb * Update cmp_controller.rb * cmp endpoint yaml * cmp endpoint to return 501 * CMP endpoint swagger flies * update swagger files * adjust server order, add auth token * cmp parameter name change * add comments * Update users_controller.rb * unit tests and linting * lint fixes * Update swagger_helper.rb * skip flakey tests * Update appeal_notifications_page_spec.rb * Hotfix/appeals 36313 v2 (#21858) * avoid IHP task creation with an open pre-docket task * Fix linting errors on RSpec * Skip flaky BGS Share Error test * comment failed test case * Add predocket coverage for IHP task creation when POA is updated * adjust predocketed from active status to open to include on_hold status * Remove commented out data staging for bgs POA rspec test --------- Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> Co-authored-by: raymond-hughes * feature/appeals-43220 (#21862) * Adding 403 and 404 functionality to Reader. * Adding icon to button. * Fixing linting issues. * Using the Link component and moving href into constant * fix linter issues --------- Co-authored-by: Brian Bommarito * feature/appeals-39842 (#21864) * anusha/appeals-45976 (#21738) * Change address format * added rspec tests to cover hotfix * testing for inclusion of expected values (addressLine1, etc.) * rspec test -- wip * still WIP * Fix address tests. * updated fakes * Update internationalPostalCode * appeals controller spec update * http request changed and test --------- Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> Co-authored-by: Anusha Palliyil Co-authored-by: Lauren Berry * lauren/APPEALS-39842 (#21739) * Add scroll metrics start time fix and update metric message * Add metrics attributes json in PdfPage constructor and add isPageVisible metric attribute * Fix scroll message spacing --------- Co-authored-by: laurenyj <44596134+laurenyj@users.noreply.github.com> * noelle/appeals-39842 (#21740) * Remove UUID from attrs sent_to metrics. * Remove UUID from spec * remove uuid from update_appellant job --------- Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> * fix failing tests --------- Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> Co-authored-by: Anusha Palliyil Co-authored-by: Lauren Berry Co-authored-by: laurenyj <44596134+laurenyj@users.noreply.github.com> * APPEALS-44916: Case Distribution Affinity Calculations (#21666) * APPEALS-44956: Add AppealAffinity model and database table (#21526) * add migration for appeal_affinities * add AppealAffinity model and associations, update migration for new column * update index to be unique * add factory, add tests * add factory traits to appeal and case for appeal affinities * add combination trait to appeal factory * add appeal_affinity to skipped associations in ETL reporting * add a validation, test * Craig/appeals 44958 (#21564) * add new job, update affinity model validation and after save hook * add update from push job * fix job extending distribution scopes * add with appeal affinities to distribution scopes * typo * add error handling, add test file * add distributed case factory, refactor naming in job * fix factories, added tests * fix migration for null affinity start date column * fixes, added tests * more test updates * add return in job if no query results, tests for no query results * add test for after_save hook adding dist task instructions * set start dist job to queue affinity job after running * fix update job and start dist job spec * queue affinity update job from push job * code clarity * fix judge in seed file * remove comment, fix hearing factory, disable some seeds for testing * add more tests * test refactor * update appeals for dist query to add affinity start, add seed file, fix hearing factory, add stat to dist factory * disable new seed on reset * update seed file with vet names, add another seed category * fix distirbuted case factory? * actually fix GHA runs * lint, test fixes * change constants in new job * APPEALS-44959: Modify affinity date checks to use appeal_affinity (#21611) * swap distribution queries from distribution_task to appeal_affinities * update seed files to use appeal affinities instead of distribution task * clean up seed file method names * add missing Timecop.return in ama affinity case seed * fix name of a method in a seed file * remove references to distribution task in distribution scopes * fix push priority job tests * fix naming of args in one of the seed files * fix user seed, fix date format in distribution task instructions * fix tests for date format update * Calvin/APPEALS-44957-rake-affinity (#21577) * grabbed receipt dates from distributed cases * refactored for functionality + added method to grab appeals that match * using receipt date, get all related appeals * added update/creation plus cleaned prior imple. * gets most recent distributed case receipt_date * skips if receipt_date is nil for performance * if appeal affinity is nil, it will now be updated * created spec file * fixed non ready appeals * updated query to match new AC * removing comment * testing for each docket * updated spec file * added new tests to rspec * updated start date to receipt date instead of Time.now * fixed date/time rspec errors * added rails logger to know when rake task has finished * added tag for rails log * removed nonpriority dockets for direct_review and evidence_submission * fixed lint issue * fixed flaky spec test * limits distributed cases query to within the last week * APPEALS-46016: Add Affinity Start Date to the Explain Page (#21660) * add affinity start date to explain page * add feature test to verify dates display * update rake task and spec (#21731) * APPEALS-46325: Add Seeds for AOD Appeals and Update Dates to Match CAVC (#21730) * add aod hearing cases to ama affinity cases seed * fix lever spec * APPEALS-45148: Hook to clear saved affinity date (#21623) * initial imp. idea * AC1: check for affinity_start_date on assignment * AC2/3: update affinity start date w/ instr. * updates to naming, instructions, and hook logic * updates after review * rspec coverage and addtional condition * removed unused identifier * removed reduntant 'self's * added update on actual AA record * updated to save aa record and addtional rspec * added change to assignment on no record test * check for assignment * addd update to 'on_hold' status * public method to handle legacy affinity appeals * added .reload to :with_affinity_appeal * added .reload to :ready_for_distribution * updates to pass explain_spec * switched boolean values * typo * readujsted order on :create for affinity appeal * removed after(:create) * testing rspec by readding after :create * reloading in assertation * addressing lint errors * fix seeds/users_spec * add case dist lever to new tests (#21776) * remove unnecessarily included module from job (#21827) * APPEALS-47211: Improve Performance of Distribution Queries (#21840) * rework ready_for_distribution scope * fix non-hearing docket distribution bug * restart tests --------- Co-authored-by: calvincostaBAH <108481161+calvincostaBAH@users.noreply.github.com> Co-authored-by: Isaiah Saucedo * APPEALS-46540: Reduce time to deploy demo environments (#21791) * APPEALS-42710: Modify local/demo seed data so that not all seed files need to be run when resetting/deploying (#21743) * refactor users to use factories and create singletons * code quality, reduce counts * refactor substitutions * refactor veterans health administration seed * refactor MTV * move education orgs to users seed * move intake users to users seed, comment non-required seed files * add veterans seed file from Sean's branch * fix errors * add hearing prep role to judges * add optional seed file * add optional seed file and button to run it in demo * lint, specs * remove priority dist from optional, test fix * fix tests * Disable repeated conditional in Test::UsersController * feature/APPEALS-43597 RC (#21860) * APPEALS-36759 Add sms_response_content and sms_response_time notifications table * APPEALS-37003 First pass at job refactor * APPEALS-37003 Add comments and update kwargs for va notify service * APPEALS-37003 Update spec files * APPEALS-37003 Update spec * APPEALS-37003 Ensure user in job and create email_enabled method * APPEALS-37003 Remove question mark * Limit max retry attempts to 5 * Add ? to boolen return methods * jcohen/APPEALS-43706 (#21444) * APPEALS-43706 macros added to each model file for the polymorphic relationship to be setup. * APPEALS-43706 migration made and yet to be run. * APPEALS-43706 migrations ran and expected columns and indexes are in table. * Fix some spec failures unrelated to this branch * Limit schema diff * Add a missing tab * Fix more unrelated failures. Also prevent ACD lever seeds from overwriting spec output * Fix last unrelated failure. * APPEALS-43706 linting issues in va_notify_service fixed. --------- Co-authored-by: Jonathan Cohen Co-authored-by: Matthew Thornton * [APPEALS-43598] Quarterly Notification Job refactoring (#21505) * Quarterly Notification Job refactoring * APPEALS-43598 Fix lint issues for QuarterlyNotificationsJob refactor * APPEALS-43598 Fix db schema.rb file * APPEALS-43598 Fix schema.rb file * APPEALS-43598 Refactoring QuarterlyNotificationsJob for readability * APPEALS-43598 Refactoring QuarterlyNotificationsJob and removing extra methods * [APPEALS-43598] Update method name --------- Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * APPEALS-44007: Eliminate Transmission of Notifications to Deceased Veterans (#21481) * APPEALS-44007: Writing tests to ensure adding the 'failure due to deceased' information is properly set and added to the db * updates to pr * pushing changes for tonight * updating spec file to handle enabling and disabling feature flags * Update format_message_status * Lint roll * fixing conflicts * Updates to send notification spec * Fix one of the legacy test cases * Remove accidental push * Fix remaining test --------- Co-authored-by: Noah Hansen Co-authored-by: Matthew Thornton Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * MattT/APPEALS-45235: Remove Temporary PDF Files From tmp Directory After All eFolder Upload Attempts (#21557) * Cleanup files after uploading to VBMS * Only delete file if it exists * Resolve linter issue * Update specs * Trigger CodeClimate --------- Co-authored-by: Matthew Thornton * Add appeal alias for notifiable * APPEALS-45346: Populate Polymorphic Association Columns When Creating New Notifications (#21575) * APPEALS-45346: Populate Polymorphic Association Columns When Creating New Notifications * fixing some errors * updating factory * updating tests * updating tests and factory * rubocop --------- Co-authored-by: Noah Hansen Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * APPEALS-43596 Adding JobMessageDeletionMiddleware to delete SQS messages for certain jobs * APPEALS-43596 Adding tests * APPEALS-43596 Fix codeclimate * APPEALS-43714: Refactor VANotify Modules to Not Rely on Class Variables (#21596) * APPEALS-43714: Refactor VANotify Modules to Not Rely on Class Variables * pushing changes * updating tests * rubocop * rubocop'n * deleting repeat constants * removing file in favor of EVENT_TYPE_FILTERS.json * removing instances of using VA_NOTIFY_TEMPLATE_NAMES and using EVENT_TYPE_FILTERS instead * replacing all instances of strings with constants * fixing bug * rubocop'n * rubocop'n * rubocop --------- Co-authored-by: Noah Hansen Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * Clean notification_events after notification report specs finish (#21662) Co-authored-by: Matthew Thornton * MattT/APPEALS-43725: Extract `notify_appellant` Call from `QuaterlyNotificationsJob` to Decrease Runtime (#21643) * Group notification types as scopes * Add new job * Fix typo * Add back batching * Batch send messages * Multithread job queueing * Reconfigure bulk enqueueing to utilize send_notifications.fifo queue * Update the comment on QuaterlyNotificationsJob#perform to adhere to the RDoc standard * Use constants for template name * Provide a more descriptive var name * Remove unused env var * Fix linting issues * Prevent error from being thrown early in init job * Add RDoc comments to appeal_state * Add rdoc comment to init job * Add another rdoc comment to the init job * Use a more specific error class whenever an appeal cannot be located within the init job * Add a more specific error to CaseflowJob#enqueue_batch_of_jobs * Add test for SendNotificationJob.queue_name_suffix * Update QuarterlyNotificationsJob tests * Update appeal_state_spec.rb to cover new scopes * Add spec file for NotificationInitializationJob * Add CaseflowJob specs * Add CaseflowJob specs * Address rubocop issue (by ignoring it) * Kicking off CodeClimate * Fix some lint issues * Freeze constant --------- Co-authored-by: Matthew Thornton * Move spec file to proper location * APPEALS-43599 (#21696) * APPEALS-43599 Refactoring SendNotificationJob * APPEALS-43599 Remove comments * APPEALS-43599 Suppress code smells from reek * APPEALS-43599 Suppress code smells from reek * jcohen/APPEALS-43727 (#21589) * APPEALS-43727 Branch created. Extracted all the 'when' conditions in the case statement within appellant_notification.rb update_appeal_state method. * APPEALS-43727 appellant_notification_service is created, logic from crowded method in appellant_notificaion.rb placed in newly created file, allowing redability. * APPEALS-43727 added logic to appeal_state.rb, appellant_notification.rb updated with less logic, readable code through well named methods. * APPEALS-43727 commit before merge from teammates work * APPEALS-43727 removed freeze method on default status hash in appeal_state.rb. removed some rubocop disabling in appellant_notificaion.rb * APPEALS-43727 extracted more logic from the appellant_notification module and placed it into the appeal_state class. Also got rid of rubocop declarations in app_notif and app_state as well. finally removed feature_toggle calls in appell_notif. * APPEALS-43727 tests passing, code works * APPEALS-43727 adding tests to appeal_state_spec to test functionality * APPEALS-43727 rspec tests in appeal_state. * APPEALS-43727 tests pass. * Fixing code climate duplicate code issue * fixing case statement * code climate things * rubocop * APPEALS-43727 work completed. * APPEALS-43727 PR comments applied. tests not in a working state. * APPEALS-43727 PR comments applied. appeal_state_spec in working state. * APPEALS-43727 PR comments applied. appellant_notification_spec.rb all green. * Adjust spacing of comments in appeal_state.rb * Utilize #external_id method over ternary * Swap or conditional for blank? * Fix another whitespace gap * APPEALS-43727 PR comments applied. again after reset. * APPEALS-43727 PR comments applied. flipped conditionals in appeal_state.rb * Update app/models/concerns/appeal_concern.rb * APPEALS-43727 code/test edits * APPEALS-43727 bulk_task_reasssignment_spec reverted to orginal state, with the exception of an expectation that was on line 98 * APPEALS-43727 lint fix --------- Co-authored-by: Jonathan Cohen Co-authored-by: Noah Hansen Co-authored-by: Matthew Thornton Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * APPEALS-46179 (#21712) * APPEALS-46179 Create JobExecutionTimes table * APPEALS-46179 Add comment * APPEALS-46179 Update schema * APPEALS-46193 (#21753) * APPEALS-46193 Add record to JobExecutionTime table when job is performed * APPEALS-46193 Updating JobExecutionTimes table with an upsert query * APPEALS-46193 Ignore JobExecutionTimes for certain jobs * APPEALS-46193 Update ignore_job_execution_time? method * APPEALS-46193 Adding tests * Fix lint issues * Update AWS dependencies * Rollback gem reduction due to UAT issue * Alter queue priority * Forward error in SendNotificationJob * Allow for notification audit records to remain in ProdTest * Fix spec * Fix typo in test fixture's name * Fix lint infraction related to re-raising exceptions * Address duplication CC error * Reenable-privacy-mail-notifications (#21859) * Add conditional for FOIA and Privacy Act mail task to receives 'Privacy Act Pending' notifications * Update spec * Adjust some specs * Add missing param --------- Co-authored-by: Matthew Thornton * Remove byebug from spec * Revert queue adjustment * Remove unused method --------- Co-authored-by: Jeff Marks Co-authored-by: Matthew Thornton Co-authored-by: Jonathan Cohen <121630615+JCohDev@users.noreply.github.com> Co-authored-by: Jonathan Cohen Co-authored-by: prernadevbah <132498915+prernadevbah@users.noreply.github.com> Co-authored-by: noahhansen-gov <166541737+noahhansen-gov@users.noreply.github.com> Co-authored-by: Noah Hansen Co-authored-by: Prerna Devulapalli * feature/appeals-39842.1 (#21871) * Fix PdfFile test * Delete client/app/readerprototype/components/ReaderDocument.jsx (#21870) Delete empty ReaderDocument file * Fix lint issue in PdfFile (#21873) --------- Co-authored-by: laurenyj <44596134+laurenyj@users.noreply.github.com> * Revert "APPEALS-44916: Case Distribution Affinity Calculations (#21666)" (#21888) * Revert "APPEALS-44916: Case Distribution Affinity Calculations (#21666)" This reverts commit 7aa04e6713759c3212820a78aa6500863dd7f91a. * fix users seed spec * Revert "feature/APPEALS-43597 RC (#21860)" (#21895) This reverts commit df60ddb567c3e02556e85ba37d555768d3a0de7e. * Revert "APPEALS-47106 Caseflow Swagger (#21847)" (#21901) This reverts commit c3bc5012618d614c3d017ae0f260a91c6d4aebec. * sbashamoni/APPEALS-43724 Node 16 version upgrade changes (#21966) (#21975) * Update node verstion to 16 in git workflow * Update node verstion to 16 in nvm and dockerfile for demo * Update node version in yarn.lock and package.json and .nvmrc file * Update node's version path to node16 in docker-bin/env.sh * Update and upgrade to node16 in client * More updates to yarn.lock and package.json * Fix tests by Adam Shaw * Upadate snapshot for ScheduleVeteran.test.js.snap * Doing some code clean up * Doiing some more clean up * Add and remove spaces to match master * One more space to take care of * One more space to take care of again * One more space to take care of again --------- Co-authored-by: ramon-chavez * Merge remote-tracking branch 'origin/master' into 25125-test-resolution * Forgot to save Gemfile.lock * uncomment seed --------- Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> Co-authored-by: 631068 Co-authored-by: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Co-authored-by: kristeja <112115264+kristeja@users.noreply.github.com> Co-authored-by: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Co-authored-by: kristeja Co-authored-by: youfoundmanesh <129548081+youfoundmanesh@users.noreply.github.com> Co-authored-by: Alex Ferencz VA <156860944+aferencz1987VA@users.noreply.github.com> Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> Co-authored-by: Matthew Thornton Co-authored-by: cacevesva <109166981+cacevesva@users.noreply.github.com> Co-authored-by: Alec Spottswood Co-authored-by: alex-guanipatin <143651918+alex-guanipatin@users.noreply.github.com> Co-authored-by: Christian Cain <149622375+christian-cain-bah@users.noreply.github.com> Co-authored-by: zborgesva <145717155+zborgesva@users.noreply.github.com> Co-authored-by: Zackary Borges-Rowe Co-authored-by: Aiman Kayad Co-authored-by: Matt Roth Co-authored-by: mikefinneran <110622959+mikefinneran@users.noreply.github.com> Co-authored-by: Brian Bommarito Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> Co-authored-by: Anusha Palliyil Co-authored-by: Lauren Berry Co-authored-by: laurenyj <44596134+laurenyj@users.noreply.github.com> Co-authored-by: Craig Reese <109101548+craigrva@users.noreply.github.com> Co-authored-by: calvincostaBAH <108481161+calvincostaBAH@users.noreply.github.com> Co-authored-by: Isaiah Saucedo Co-authored-by: Jeff Marks Co-authored-by: Jonathan Cohen <121630615+JCohDev@users.noreply.github.com> Co-authored-by: Jonathan Cohen Co-authored-by: prernadevbah <132498915+prernadevbah@users.noreply.github.com> Co-authored-by: noahhansen-gov <166541737+noahhansen-gov@users.noreply.github.com> Co-authored-by: Noah Hansen Co-authored-by: Prerna Devulapalli Co-authored-by: sbashamoni <138054633+sbashamoni@users.noreply.github.com> Co-authored-by: ramon-chavez * APPEALS-43196 - Create MVC for Unassigned Hearings (#22021) * APPEALS-43196 - First set of changes for selecting and locking files * APPEALS-43196 - Cleanup some console errors * APPEALS-43196 - Handle select all and other UI related problems * APPEALS-43196 - Cleanup and testing * APPEALS-43196 - Fix permissions and status bug * APPEALS-43196 - Bump counts from a broken test * APPEALS-43196 - Fix another permission issue, preload users, and fix an issue with external appeal id * APPEALS-49196-v2 (#22050) * Feature/appeals 46208.rc (#21746) * Added Assignment Queue link to Switch Users test page * Updated controller to validate role and redirect * Updated error information * Updated rspec tests * Ricky/appeals-43523 (#21345) * Added Assignment Queue link to Switch Users test page * Updated controller to validate role and redirect * Updated error information * Updated rspec tests * Added Download Ineligible Judge List * APPEALS-43116 add sentry error capture to push priority job and updated specs (#21369) * Updated query to include css_id and updated id fetch * Updates button styling, fixed csv to have all values but judge name * Updated id to sattyid values, and added name data to column * fixed linting error * Updated logic to account for inactive users in both caseflow and vacols * Updated button component and cleaned up code * Updated handling of ids in situation where sattyid is unavailable in record * Updated to have individual columns for caseflow id and satty id * APPEALS-43117 Add QA Users for testing (#21580) * APPEALS-43117 Add QA Users for testing * Fix rspecs --------- Co-authored-by: Christopher Detlef <> * APPEALS-42266 Remove CaseDistributionLevers seed config in Rails helper and update required spec files * APPEALS-42266 fixing a couple of linting issues * APPEALS-43523 reverted the legacy tasks controller (#21713) * APPEALS-43523 reverted the legacy tasks controller * APPEALS-43523 reverted the erorrs concern too * Update legacy_tasks_controller_spec.rb * reverted raven call * APPEALS-46208 fixed code climate warning about a bad naming convention. * APPEALS-46208 fixed indentation --------- Co-authored-by: 631068 Co-authored-by: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Co-authored-by: kristeja <112115264+kristeja@users.noreply.github.com> Co-authored-by: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Co-authored-by: kristeja * APPEALS-44148: Add ruby CE API Gem (#21520) * hotfix/APPEALS-46182 (#21709) * Added tests and added logic to account for sct distribution * Added more tests and changed logic * Adding comments * Fixing logic for SCT * Update test and logic * Fixing tests * Update docket switch mail task to match corrected logic * Fixing logic * Cleaning up refactor * Worked with the amazing ALex and we figured it out! * Fixing linting issue * Fix linting issues * Skipping flaky tests * Skipping another flaky test --------- Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * MattT/APPEALS-46383: Upgrade aws-sdk gem to v3.2 (#21748) * Upgrade aws-sdk gem to v3.2 * Update caseflow-commons hash * Apply new hash update * Skip flaky test --------- Co-authored-by: Matthew Thornton * Update gemfile and gemfile.lock to point to new bgs version (#21749) * Update gemfile and gemfile.lock to point to new bgs version * Skipping flake specs in review_spec.rb file * Revert "APPEALS-44148: Add ruby CE API Gem (#21520)" (#21762) This reverts commit 3a393d1f3f40b35b5063c2b7d0f1508ef5a61bb7. * Update ruby-bgs gemfile and gemfile.lock to new version (#21761) * Update MAC_INTEL.md Update Oracle client link * APPEALS-44496 Fix Factory (#21770) * APPEALS-44496 Fix Factory * updated falky test --------- Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * fix(va facilities): update per page param (#21754) * Hotfix/appeals 44033:Fix Judge and Attorney tasks in the QualityReview workflow are being erroneously cancelled (#21786) * Adjusts the send_to_hearings_branch method to exclude JudgeQualityReviews when cancelling all Judge tasks and subtasks rebase * adjusts spacing and adds back deleted test * Remove unnecessary schema changes and adds an exclamation mark to account for method that changes data * add exclamation to handle_judge_tasks --------- Co-authored-by: Zackary Borges-Rowe Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * Hotfix/appeals 45150 (#21627) * Added distribution task with children status change on split appeal test * Added tests and commented out pry session in appeal * Changed split appeal test to test for both defects * Added conditional logic to status_is_valid_on_create in task model * looking for defect * Reverted changes to finalize split appeal method added condtional logic to schedule hearing tasks model * Removed debugger and commented out lines * Added erroneously removed lines of code and added spacing * Fix linting errors * Added conditional logic for appeal split process --------- Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> * hotfix/APPEALS-36313-V2 (#21782) * avoid IHP task creation with an open pre-docket task * Fix linting errors on RSpec * Skip flaky BGS Share Error test * comment failed test case --------- Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> Co-authored-by: raymond-hughes * APPEALS-47106 Caseflow Swagger (#21847) * Add Rswag and convert existing API documentation (#21778) * add rswag * Update Gemfile.lock * Setup yaml UI * remove and reroute old swagger files * update format and servers values * Update route_docs_controller.rb * Update cmp_controller.rb * cmp endpoint yaml * cmp endpoint to return 501 * CMP endpoint swagger flies * update swagger files * adjust server order, add auth token * cmp parameter name change * add comments * Update users_controller.rb * unit tests and linting * lint fixes * Update swagger_helper.rb * skip flakey tests * Update appeal_notifications_page_spec.rb * Hotfix/appeals 36313 v2 (#21858) * avoid IHP task creation with an open pre-docket task * Fix linting errors on RSpec * Skip flaky BGS Share Error test * comment failed test case * Add predocket coverage for IHP task creation when POA is updated * adjust predocketed from active status to open to include on_hold status * Remove commented out data staging for bgs POA rspec test --------- Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> Co-authored-by: raymond-hughes * feature/appeals-43220 (#21862) * Adding 403 and 404 functionality to Reader. * Adding icon to button. * Fixing linting issues. * Using the Link component and moving href into constant * fix linter issues --------- Co-authored-by: Brian Bommarito * feature/appeals-39842 (#21864) * anusha/appeals-45976 (#21738) * Change address format * added rspec tests to cover hotfix * testing for inclusion of expected values (addressLine1, etc.) * rspec test -- wip * still WIP * Fix address tests. * updated fakes * Update internationalPostalCode * appeals controller spec update * http request changed and test --------- Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> Co-authored-by: Anusha Palliyil Co-authored-by: Lauren Berry * lauren/APPEALS-39842 (#21739) * Add scroll metrics start time fix and update metric message * Add metrics attributes json in PdfPage constructor and add isPageVisible metric attribute * Fix scroll message spacing --------- Co-authored-by: laurenyj <44596134+laurenyj@users.noreply.github.com> * noelle/appeals-39842 (#21740) * Remove UUID from attrs sent_to metrics. * Remove UUID from spec * remove uuid from update_appellant job --------- Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> * fix failing tests --------- Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> Co-authored-by: Anusha Palliyil Co-authored-by: Lauren Berry Co-authored-by: laurenyj <44596134+laurenyj@users.noreply.github.com> * APPEALS-44916: Case Distribution Affinity Calculations (#21666) * APPEALS-44956: Add AppealAffinity model and database table (#21526) * add migration for appeal_affinities * add AppealAffinity model and associations, update migration for new column * update index to be unique * add factory, add tests * add factory traits to appeal and case for appeal affinities * add combination trait to appeal factory * add appeal_affinity to skipped associations in ETL reporting * add a validation, test * Craig/appeals 44958 (#21564) * add new job, update affinity model validation and after save hook * add update from push job * fix job extending distribution scopes * add with appeal affinities to distribution scopes * typo * add error handling, add test file * add distributed case factory, refactor naming in job * fix factories, added tests * fix migration for null affinity start date column * fixes, added tests * more test updates * add return in job if no query results, tests for no query results * add test for after_save hook adding dist task instructions * set start dist job to queue affinity job after running * fix update job and start dist job spec * queue affinity update job from push job * code clarity * fix judge in seed file * remove comment, fix hearing factory, disable some seeds for testing * add more tests * test refactor * update appeals for dist query to add affinity start, add seed file, fix hearing factory, add stat to dist factory * disable new seed on reset * update seed file with vet names, add another seed category * fix distirbuted case factory? * actually fix GHA runs * lint, test fixes * change constants in new job * APPEALS-44959: Modify affinity date checks to use appeal_affinity (#21611) * swap distribution queries from distribution_task to appeal_affinities * update seed files to use appeal affinities instead of distribution task * clean up seed file method names * add missing Timecop.return in ama affinity case seed * fix name of a method in a seed file * remove references to distribution task in distribution scopes * fix push priority job tests * fix naming of args in one of the seed files * fix user seed, fix date format in distribution task instructions * fix tests for date format update * Calvin/APPEALS-44957-rake-affinity (#21577) * grabbed receipt dates from distributed cases * refactored for functionality + added method to grab appeals that match * using receipt date, get all related appeals * added update/creation plus cleaned prior imple. * gets most recent distributed case receipt_date * skips if receipt_date is nil for performance * if appeal affinity is nil, it will now be updated * created spec file * fixed non ready appeals * updated query to match new AC * removing comment * testing for each docket * updated spec file * added new tests to rspec * updated start date to receipt date instead of Time.now * fixed date/time rspec errors * added rails logger to know when rake task has finished * added tag for rails log * removed nonpriority dockets for direct_review and evidence_submission * fixed lint issue * fixed flaky spec test * limits distributed cases query to within the last week * APPEALS-46016: Add Affinity Start Date to the Explain Page (#21660) * add affinity start date to explain page * add feature test to verify dates display * update rake task and spec (#21731) * APPEALS-46325: Add Seeds for AOD Appeals and Update Dates to Match CAVC (#21730) * add aod hearing cases to ama affinity cases seed * fix lever spec * APPEALS-45148: Hook to clear saved affinity date (#21623) * initial imp. idea * AC1: check for affinity_start_date on assignment * AC2/3: update affinity start date w/ instr. * updates to naming, instructions, and hook logic * updates after review * rspec coverage and addtional condition * removed unused identifier * removed reduntant 'self's * added update on actual AA record * updated to save aa record and addtional rspec * added change to assignment on no record test * check for assignment * addd update to 'on_hold' status * public method to handle legacy affinity appeals * added .reload to :with_affinity_appeal * added .reload to :ready_for_distribution * updates to pass explain_spec * switched boolean values * typo * readujsted order on :create for affinity appeal * removed after(:create) * testing rspec by readding after :create * reloading in assertation * addressing lint errors * fix seeds/users_spec * add case dist lever to new tests (#21776) * remove unnecessarily included module from job (#21827) * APPEALS-47211: Improve Performance of Distribution Queries (#21840) * rework ready_for_distribution scope * fix non-hearing docket distribution bug * restart tests --------- Co-authored-by: calvincostaBAH <108481161+calvincostaBAH@users.noreply.github.com> Co-authored-by: Isaiah Saucedo * APPEALS-46540: Reduce time to deploy demo environments (#21791) * APPEALS-42710: Modify local/demo seed data so that not all seed files need to be run when resetting/deploying (#21743) * refactor users to use factories and create singletons * code quality, reduce counts * refactor substitutions * refactor veterans health administration seed * refactor MTV * move education orgs to users seed * move intake users to users seed, comment non-required seed files * add veterans seed file from Sean's branch * fix errors * add hearing prep role to judges * add optional seed file * add optional seed file and button to run it in demo * lint, specs * remove priority dist from optional, test fix * fix tests * Disable repeated conditional in Test::UsersController * feature/APPEALS-43597 RC (#21860) * APPEALS-36759 Add sms_response_content and sms_response_time notifications table * APPEALS-37003 First pass at job refactor * APPEALS-37003 Add comments and update kwargs for va notify service * APPEALS-37003 Update spec files * APPEALS-37003 Update spec * APPEALS-37003 Ensure user in job and create email_enabled method * APPEALS-37003 Remove question mark * Limit max retry attempts to 5 * Add ? to boolen return methods * jcohen/APPEALS-43706 (#21444) * APPEALS-43706 macros added to each model file for the polymorphic relationship to be setup. * APPEALS-43706 migration made and yet to be run. * APPEALS-43706 migrations ran and expected columns and indexes are in table. * Fix some spec failures unrelated to this branch * Limit schema diff * Add a missing tab * Fix more unrelated failures. Also prevent ACD lever seeds from overwriting spec output * Fix last unrelated failure. * APPEALS-43706 linting issues in va_notify_service fixed. --------- Co-authored-by: Jonathan Cohen Co-authored-by: Matthew Thornton * [APPEALS-43598] Quarterly Notification Job refactoring (#21505) * Quarterly Notification Job refactoring * APPEALS-43598 Fix lint issues for QuarterlyNotificationsJob refactor * APPEALS-43598 Fix db schema.rb file * APPEALS-43598 Fix schema.rb file * APPEALS-43598 Refactoring QuarterlyNotificationsJob for readability * APPEALS-43598 Refactoring QuarterlyNotificationsJob and removing extra methods * [APPEALS-43598] Update method name --------- Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * APPEALS-44007: Eliminate Transmission of Notifications to Deceased Veterans (#21481) * APPEALS-44007: Writing tests to ensure adding the 'failure due to deceased' information is properly set and added to the db * updates to pr * pushing changes for tonight * updating spec file to handle enabling and disabling feature flags * Update format_message_status * Lint roll * fixing conflicts * Updates to send notification spec * Fix one of the legacy test cases * Remove accidental push * Fix remaining test --------- Co-authored-by: Noah Hansen Co-authored-by: Matthew Thornton Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * MattT/APPEALS-45235: Remove Temporary PDF Files From tmp Directory After All eFolder Upload Attempts (#21557) * Cleanup files after uploading to VBMS * Only delete file if it exists * Resolve linter issue * Update specs * Trigger CodeClimate --------- Co-authored-by: Matthew Thornton * Add appeal alias for notifiable * APPEALS-45346: Populate Polymorphic Association Columns When Creating New Notifications (#21575) * APPEALS-45346: Populate Polymorphic Association Columns When Creating New Notifications * fixing some errors * updating factory * updating tests * updating tests and factory * rubocop --------- Co-authored-by: Noah Hansen Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * APPEALS-43596 Adding JobMessageDeletionMiddleware to delete SQS messages for certain jobs * APPEALS-43596 Adding tests * APPEALS-43596 Fix codeclimate * APPEALS-43714: Refactor VANotify Modules to Not Rely on Class Variables (#21596) * APPEALS-43714: Refactor VANotify Modules to Not Rely on Class Variables * pushing changes * updating tests * rubocop * rubocop'n * deleting repeat constants * removing file in favor of EVENT_TYPE_FILTERS.json * removing instances of using VA_NOTIFY_TEMPLATE_NAMES and using EVENT_TYPE_FILTERS instead * replacing all instances of strings with constants * fixing bug * rubocop'n * rubocop'n * rubocop --------- Co-authored-by: Noah Hansen Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * Clean notification_events after notification report specs finish (#21662) Co-authored-by: Matthew Thornton * MattT/APPEALS-43725: Extract `notify_appellant` Call from `QuaterlyNotificationsJob` to Decrease Runtime (#21643) * Group notification types as scopes * Add new job * Fix typo * Add back batching * Batch send messages * Multithread job queueing * Reconfigure bulk enqueueing to utilize send_notifications.fifo queue * Update the comment on QuaterlyNotificationsJob#perform to adhere to the RDoc standard * Use constants for template name * Provide a more descriptive var name * Remove unused env var * Fix linting issues * Prevent error from being thrown early in init job * Add RDoc comments to appeal_state * Add rdoc comment to init job * Add another rdoc comment to the init job * Use a more specific error class whenever an appeal cannot be located within the init job * Add a more specific error to CaseflowJob#enqueue_batch_of_jobs * Add test for SendNotificationJob.queue_name_suffix * Update QuarterlyNotificationsJob tests * Update appeal_state_spec.rb to cover new scopes * Add spec file for NotificationInitializationJob * Add CaseflowJob specs * Add CaseflowJob specs * Address rubocop issue (by ignoring it) * Kicking off CodeClimate * Fix some lint issues * Freeze constant --------- Co-authored-by: Matthew Thornton * Move spec file to proper location * APPEALS-43599 (#21696) * APPEALS-43599 Refactoring SendNotificationJob * APPEALS-43599 Remove comments * APPEALS-43599 Suppress code smells from reek * APPEALS-43599 Suppress code smells from reek * jcohen/APPEALS-43727 (#21589) * APPEALS-43727 Branch created. Extracted all the 'when' conditions in the case statement within appellant_notification.rb update_appeal_state method. * APPEALS-43727 appellant_notification_service is created, logic from crowded method in appellant_notificaion.rb placed in newly created file, allowing redability. * APPEALS-43727 added logic to appeal_state.rb, appellant_notification.rb updated with less logic, readable code through well named methods. * APPEALS-43727 commit before merge from teammates work * APPEALS-43727 removed freeze method on default status hash in appeal_state.rb. removed some rubocop disabling in appellant_notificaion.rb * APPEALS-43727 extracted more logic from the appellant_notification module and placed it into the appeal_state class. Also got rid of rubocop declarations in app_notif and app_state as well. finally removed feature_toggle calls in appell_notif. * APPEALS-43727 tests passing, code works * APPEALS-43727 adding tests to appeal_state_spec to test functionality * APPEALS-43727 rspec tests in appeal_state. * APPEALS-43727 tests pass. * Fixing code climate duplicate code issue * fixing case statement * code climate things * rubocop * APPEALS-43727 work completed. * APPEALS-43727 PR comments applied. tests not in a working state. * APPEALS-43727 PR comments applied. appeal_state_spec in working state. * APPEALS-43727 PR comments applied. appellant_notification_spec.rb all green. * Adjust spacing of comments in appeal_state.rb * Utilize #external_id method over ternary * Swap or conditional for blank? * Fix another whitespace gap * APPEALS-43727 PR comments applied. again after reset. * APPEALS-43727 PR comments applied. flipped conditionals in appeal_state.rb * Update app/models/concerns/appeal_concern.rb * APPEALS-43727 code/test edits * APPEALS-43727 bulk_task_reasssignment_spec reverted to orginal state, with the exception of an expectation that was on line 98 * APPEALS-43727 lint fix ----… * Min/APPEALS-48462-v2 (#22229) * APPEALS-48462 api patch request * APPEALS-48462 component rerenders on state change * APPEALS-48462 added jest tests for toggles * Min/APPEALS-48366 (#22257) * APPEALS-48366 index method in transcription_contractors_controller now returns new transcription count attribute for each one * APPEALS-48366 query for getting count of transcriptions now only gets count for this week * APPEALS-48366 transcriptions_count for the week now displays in the UI * APPEALS-48366 wrote rspec tests in controller spec * APPEALS-48366 added jest tests * APPEALS-48366 added a new scope for the query * APPEALS-48366 turned console.log mock back on * Create Link to Transcription File Dispatch from Transcription Settings (#22286) * APPEALS-48366 fixed undefined display when toggling work assignment (#22287) * Min/APPEALS-48366-v3 (#22299) * APPEALS-48366 fixed undefined display when toggling work assignment * APPEALS-48366 edit goal modal now validates value when attempting to submit instead of on display * APPEALS-48366 removed unused useAffect * APPEALS-49626 fixed jest test * APPEALS-49626 fixed jest test * APPEALS-48366 removed console.log * Merge latest from master (#22366) * APPEALS-44287: Excluding disposition held and select that appeal for distribution (#22277) * first run at an SQL query removing duplicate appeals from distribution * code refactor and excluding disposition held and select that appeal for distribution * automated test for the duplicate hearing bug * fix rubocop offense SpaceInsideBlockBraces --------- Co-authored-by: Sean Parker Co-authored-by: samasudhirreddy * APPEALS-51237, APPEALS-51536: Optimize feature tests in intake/appeal/edit_spec.rb and optimize automatic case distribution tests (#22280) * refactoring edit_spec * move one assertion and remove duplicate test * refactor withdraw issues context * move duplicate test assertions * move duplicate test assertions to existing tests and remove duplicate tests * move assertion and remove duplicate test * consolidate SCT appeal tests * refactor SCT tests to use step blocks * consolidate a individual test into a step * move CC appeal task tests to appeals_controller_spec intead of a feature test * remove unnecessary code comments * consolidate member not admin tests * remove commented portions of test * move tests from affinity_days_levers_spec.rb * move tests from affinity_days_levers_spec.rb, last commit was from audit_lever_history_table_spec.rb * remove ama_np_dist_goals_by_docket_lever_spec.rb * remove batch_size_levers_spec.rb * remove inactive_data_elements_levers_spec.rb * remove lever_buttons_spec.rb * refactor * move jest file for convention, add snapshot * move test for admin ui not interactable for non-admins to a jest test * remove unnecessary comments, whitespace * restore jest folder structure * modify edit spec to use case details page instead of searching again * modify assertions * remove TODO comment * add assertion to allow async save actions to be executed * add check to ensure banner is gone before trying to save * refactor assertions for lever history display * fix failing dependency report tests * APPEALS-53151: Update ECR Workflow to Utilize OIDC Flow (#22348) * Reconfigure ECR login workflow to utilize Github's OIDC flow * Add permissions * Update AWS account number to one for VAEC * Update account number for main CI workflow * Try using a separate secret in test * Try using a separate secret in test * Change name of secret --------- Co-authored-by: Matthew Thornton * Fix snapshots --------- Co-authored-by: Craig Reese <109101548+craigrva@users.noreply.github.com> Co-authored-by: Sean Parker Co-authored-by: samasudhirreddy Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> Co-authored-by: Matthew Thornton * Create Link to Transcription Settings from Transcription File Dispatch (#22328) * add link for transcription settings * fixing jest tests * added the space between icon and text * fixing jest tests * Creation of DatePicker.jsx reusable component (#22326) * APPEALS-48373 - Initial basic version of date picker without many bells and whistles * APPEALS-48373 - New version of picker that handles data going in and out better * APPEALS-48373 - Fix bug related to clearing filters and trying to re-apply dates * APPEALS-48373 - Building out the dropdown UI * APPEALS-48373 - Fix the backend to allow filter by date and fix some front end issues * APPEALS-48373 - Write jest tests for date picker and update snapshots * APPEALS-48373 - UI fixes after seeing the filter on other tables * APPEALS-48373 - Use copy JSON * APPEALS-48373 - Update how clear filter button works and write a test * APPEALS-48373 - Allow custom front end filtering on QueueTable with DatePicker * APPEALS-48373 - Test date picker wrapped in a front end sorting queue table * APPEALS-48373 - Test date picker wrapped in a back end sorting queue table * APPEALS-48373 - Move tests out of DatePicker into QueueTable where they belong and add more tests * APPEALS-48373 - Keep DateFilter icon still selected when filtering by date instead of just when open * Min/APPEALS-43157 (#22397) * APPEALS-43157 basic modal now appears when clicking on package button * APPEALS-43157 basic modal calculates return date including business days * APPEALS-43157 populating dropdown and added new controller action for the contractor names only * APPEALS-43157 added some jest tests * APPEALS-43157 work order now generating and new controller route created for task_id * APPEALS-43157 added jest tests * APPEALS-43157 added documentation * APPEALS-43157 fixed linting errors * APPEALS-43157 fixed broken tests * APPEALS-43157 seeding transcriptions * APPEALS-43157 update snapshot and scope * APPEALS-43157 updated seed file * APPEALS-43157 added triggers for task_id sequencing in transcriptions * APPEALS-43157 added triggers for task_id sequencing in transcriptions * APPEALS-43157 added triggers for task_id sequencing in transcriptions * APPEALS-43157 new transcription_sequence_id class added to services * APPEALS-43157 added modal controller action for testing sequencing * Created jest tests for Select All/Individual Files, Unassigned tab Tran… (#22441) * ading jest tests for Select All/Individual Files, Unassigned tab Transcription File Dispatch page * fix for lint errors * APPEALS-43238-v2 (#22461) * APPEALS-43238 add service * APPEALS-43238 add dependencies * APPEALS-43238 upload job spec * APPEALS-43238 alphebetize gemfile * APPEALS-43238 add insert and upsert * APPEALS-43238 add transcription_files update * APPEALS-43238 cleanup * APPEALS-43238 va box service spec * APPEALS-43238 remove test folder fix lint * APPEALS-43238 update bundler * APPEALS-43238 remove redundancy * APPEALS-43238 remove empty line * Min/APPEALS-43169 (#22535) * APPEALS-43169 added basic modal after clicking package files button * APPEALS-43169 cancel and modify buttons are now working * APPEALS-43169 added form information from modal * APPEALS-43169 added in controller action and routes for getting selected files and their info * APPEALS-43169 table now rendered with information in the modal * APPEALS-43169 added a template for a controller action for dispatching files * APPEALS-43169 added jest tests * APPEALS-43169 added rspec tests * APPEALS-43169 fixed an rspec test * APPEALS-43169 fixed up the dates to exclude holidays only * Min/APPEALS-43169-v2 (#22556) * APPEALS-43169 added basic modal after clicking package files button * APPEALS-43169 cancel and modify buttons are now working * APPEALS-43169 added form information from modal * APPEALS-43169 added in controller action and routes for getting selected files and their info * APPEALS-43169 table now rendered with information in the modal * APPEALS-43169 added a template for a controller action for dispatching files * APPEALS-43169 added jest tests * APPEALS-43169 added rspec tests * APPEALS-43169 fixed an rspec test * APPEALS-43169 fixed up the dates to exclude holidays only * APPEALS-43169 data sent to controller is now a JSON object * Create Model View Controller for package management activities (#22555) * Create Model View Controller for package management activities * removed foreign key * removed foreign key * removed empty line * removed unused code * APPEALS-55525: Disable tests for seed files and lock Chrome version to v127 in Github Actions (#22567) * add exclude pattern to github actions rspec command * add test file exclude pattern constant * lock chrome version to 127.0.6533.119 * Removed outdated transcription id field * added specs * renaming model name * change route name * removed work order package file * update snapshot --------- Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> Co-authored-by: Craig Reese <109101548+craigrva@users.noreply.github.com> * APPEALS-43197 (#22579) * APPEALS-49234 - Add new table for assigned view using mock backend data * APPEALS-49234 - Refactor testing and set up basic test for assigned tab * APPEALS-49234 - Cleanup of styles and language * APPEALS-49234 - Add consistency to styles and elements for tabs * APPEALS-55525 - Prevent table from having a scroll bar sometimes * APPEALS-55525 - Adjust spacing around icon in the table * APPEALS-55525 - Forgot to update snapshots * APPEALS-55525 - UI changes from review * APPEALS-55525 - Change spacing around switch views dropdown * APPEALS-55525 - Attempt to fix a flapping spec test * APPEALS-55525 - Stub out setinterval to hopefully prevent test timeouts * APPEALS-55525 - Maintain the correct tab on page refresh * APPEALS-55525 - Remove test code * APPEALS-55525 - Reset pagenation values when tab changes * APPEALS-55525 - Broken snapshot * APPEALS-55525 - A better fix for the tab params reset logic * APPEALS-55525 - Test fix --------- Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> * B_reed/APPEALS-43211 (#22633) * APPEALS-43211 work_order work flow * APPEALS-43211 update jest tests * APPEALS-43211 update specs * APPEALS-43211 update lint * APPEALS-43211 fix lint * APPEALS-43211 add space * APPEALS-43254 Update transcription_files Db table (#22639) * APPEALS-43254 Update transcription_files Db table * APPEALS-43254 Update change column null to false * APPEALS-43254 Update schema * APPEALS-43254 Revert changes for columns to non null constraint * APPEALS-43254 Updated Schema file * Min/APPEALS-43183 (#22661) * APPEALS-43183 solved merge conflict * APPEALS-43183 added some fake data and css styling * APPEALS-43183 populating modal and tweaked controller action for additional required information * APPEALS-43183 added comments * APPEALS-43183 added in jest testing * APPEALS-43183 fixed an rspec test * APPEALS-55495 - Seed transcription package data and start to use it * APPEALS-55495 - Do a little cleanup and get seed file ready * APPEALS-55495 - Set up a relationship between transcription package and transcription * APPEALS-55495 - Updated front end tests for URL change * APPEALS-55495 - Fix seed files to prevent class name conflicts * APPEALS-55495 - Fix contractor seed calls * APPEALS-55495 - Move seeds to end of the file so they’ll have good hearings * APPEALS-43183 applied new seed file and tweaked response for wo_contents * APPEALS-43183 fixed some tests * APPEALS-43183 removed transcription package spec --------- Co-authored-by: Adam Ducker * Display work order summary (#22724) * Display work order summary * fix date issue * Fixed spec * Added bold text for Docket number * Added close button * APPEALS-55495 (#22758) * APPEALS-55495 - Update transcription file seed to use task numbers and transcription records * APPEALS-55495 - Starting to make progress on testing * APPEALS-55495 - More cleanup and testing # Conflicts: # spec/models/hearings/transcription_package_spec.rb * APPEALS-55495 - Even more cleanup and testing * APPEALS-55495 - Snapshot update after rebase * APPEALS-55495 - Add back missing test method * Fix bad merge conflict resolution (#22784) Co-authored-by: Matthew Thornton * Wmedders21/appeals 43243 (#22669) * WIP prototype * add faraday and faraday-multipart gems * Refactor box service to use faraday and to symbolize keys from responses * Update box upload job with correct namespacing and to account for changes to box service * update box service to cache access token * fix test/lint errors * feat: Hearings::MonitorBoxJob * remove net-http gem * test comments * fix rubocop style with double quotes * update for namespacing change * set queue priority value * add error handling * Add Hearings::MonitorBoxJob to scheduled jobs list * revert files pulled from wrong branch * updated schema * revert schema * update for new column added * Update regex expression that finds webex files * checkout file from 49626 feature * revert files to feature branch versions * Update tests with properly formatted file names * rubocop fix * update snapshots * Add YARD doc * Fix typo in YARD doc * edit YARD docstring * b_reed/APPEALS-43184 (#22753) * APPEALS-43184 create WorkOrderUnassignModal * APPEALS-43184 use modal component * APPEALS-43184 backend connection * APPEALS-43184 add route * APPEALS-43184 update dispatchtablecolumn * APPEALS-43184 add unassign_by_task_number * APPEALS-43184 remove id from FileDispatchTable * APPEALS-43184 add transcription_package method * APPEALS-43184 add error logic to controller * APPEALS-43184 clean up modal * APPEALS-43184 update file and import name * APPEALS-43184 update Unassigning_work_order * APPEALS-43184 lint * APPEALS-43184 lint * APPEALS-43184 update timeout * APPEALS-43184 change tests * APPEALS-43184 test-test changes * APPEALS-43184 update tests * APPEALS-43184 update jest test * APPEALS-43184 update snapshots * APPEALS-43184 update snapshot * APPEALS-43184 fix for violations test * APPEALS-43184 snapshot updated * Removed close and adding work order in new tab (#22809) * Update solargraph * Update solargraph * APPEALS-49401 transcription file sub section showed in the UI (#22755) * APPEALS-49401 transcription file sub section showed in the UI * APPEALS-49401 Jest test and PR comments fixed * APPEALS-49401 Storybook changes * Update solargraph --------- Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> Co-authored-by: Matthew Thornton * APPEALS-49402 tests to show up word docs in the transcription files s… (#22856) * APPEALS-49402 tests to show up word docs in the transcription files sub-section * A couple of test fixes. Co-authored-by: Will Medders <93014155+wmedders21@users.noreply.github.com> --------- Co-authored-by: Matthew Thornton Co-authored-by: Will Medders <93014155+wmedders21@users.noreply.github.com> Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> * Min/APPEALS-43265-v2 (#22879) * APPEALS-43265 added new transcription subsection for webex hearings * APPEALS-43265 added a new column to transcriptions table * APPEALS-43265 validating return dates and formatted dates in the frontend * APPEALS-43265 wrote jest tests * APPEALS-43265 temporary code to easily create transcriptions for testing purposes * APPEALS-43265 added comments * APPEALS-43265 fixed some tests * APPEALS-43265 fixed feature tests * APPEALS-43265 added comment * APPEALS-43265 merged in jest test fix --------- Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> * Add va box service initializer * Create new sub section for Transcription Details for Originating Transcriber (#22873) * APPEALS-43265 added new transcription subsection for webex hearings * APPEALS-43265 added a new column to transcriptions table * APPEALS-43265 validating return dates and formatted dates in the frontend * APPEALS-43265 wrote jest tests * APPEALS-43265 temporary code to easily create transcriptions for testing purposes * APPEALS-43265 added comments * APPEALS-43265 fixed some tests * APPEALS-43265 fixed feature tests * APPEALS-43265 added comment * APPEALS-43265 merged in jest test fix * adding the transcription details * added mp3 filetype in query * fix lint error * fix lint error * Fix the specs * fix lint error * fix rspec --------- Co-authored-by: Minhazur Rahaman Co-authored-by: minhazur9 <65432922+minhazur9@users.noreply.github.com> Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> * Squashed commit of the following: commit 701ed4a7970bf77b37efa4c6f918616dcd57ac87 Merge: 608ac5ee4d e34da001d7 Author: Craig Reese Date: Mon Sep 16 11:27:31 2024 -0500 Merge branch 'master' of https://github.com/department-of-veterans-affairs/caseflow into dev-support/APPEALS-54874 commit 608ac5ee4d526b40b48a84a92fdf679abdf83df2 Author: Craig Reese Date: Mon Aug 26 08:16:13 2024 -0500 require ruby-oci8 in config/boot.rb to fix SIGV fault commit 6e7c7aa84560ccc62d33d437542527cb0b4ad103 Merge: 2a6a28e16b 6dd9870c8f Author: Craig Reese <109101548+craigrva@users.noreply.github.com> Date: Fri Aug 23 07:00:27 2024 -0500 Merge branch 'master' into dev-support/APPEALS-54874 commit 2a6a28e16b4aef24c6363f3d33d751b334ae85a1 Author: Craig Reese <109101548+craigrva@users.noreply.github.com> Date: Thu Aug 15 13:27:23 2024 -0500 update minimum version in gemfile and ran bundle install (#22503) * APPEALS-43242 (#22881) * APPEALS-43242 new PR * APPEALS-43242 push space * APPEALS-43242 update files * APPEALS-43242 New rails * APPEALS-43242 add filter changes * APPEALS-43242 lint * APPEALS-43242 lint * APPEALS-43242 disable lint * APPEALS-43242 remove lint * APPEALS-43242 lint * APPEALS-43242 lint * APPEALS-43242 update schema * APPEALS-43242 update specs * APPEALS-43242 update statuses on tabs * APPEALS-43242 update copy.json * APPEALS-43242 add to copy.json * APPEALS-43242 gemfile update * Limit fake Box service usage to our test env. * Rubocop error fix - Mmyana/appeals 43264 (#22908) * APPEALS-43265 added new transcription subsection for webex hearings * APPEALS-43265 added a new column to transcriptions table * APPEALS-43265 validating return dates and formatted dates in the frontend * APPEALS-43265 wrote jest tests * APPEALS-43265 temporary code to easily create transcriptions for testing purposes * APPEALS-43265 added comments * APPEALS-43265 fixed some tests * APPEALS-43265 fixed feature tests * APPEALS-43265 added comment * APPEALS-43265 merged in jest test fix * adding the transcription details * added mp3 filetype in query * fix lint error * fix lint error * Fix the specs * fix lint error * fix rspec * fix rubocop issues * fix for spec and trascription details --------- Co-authored-by: Minhazur Rahaman Co-authored-by: minhazur9 <65432922+minhazur9@users.noreply.github.com> Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> * APPEALS-43242 (#22961) * APPEALS-43242 new PR * APPEALS-43242 push space * APPEALS-43242 update files * APPEALS-43242 New rails * APPEALS-43242 add filter changes * APPEALS-43242 lint * APPEALS-43242 lint * APPEALS-43242 disable lint * APPEALS-43242 remove lint * APPEALS-43242 lint * APPEALS-43242 lint * APPEALS-43242 update schema * APPEALS-43242 update specs * APPEALS-43242 update statuses on tabs * APPEALS-43242 update copy.json * APPEALS-43242 add to copy.json * APPEALS-43242 gemfile update * APPEALS-43242 add conditional updates * APPEALS-43242 add interpolation updates * APPEALS-43242 update tests * APPEALS-43242 check failing tests * APPEALS-43242 fix lint * Min/APPEALS-59050 (#22971) * APPEALS-59050 added links to the switch view dropdown for hearings and transcription users to navigate * APPEALS-59050 fixed some broken tests * APPEALS-59041 (#22999) * APPEALS-59041 update titles * APPEALS-59041 update tests * APPEALS-59041 update lint * APPEALS-59044 - Add consistency between filter dropdown clear buttons (#22989) * Add transcription file dispatch queue link to admin page * APPEALS-43187 Transcription files Complete tab (#22943) * APPEALS-43187 Transcription files Complete tab * APPEALS-43187 Jest and spec fixes * APPEALS-43187 Showed up Completed tab data and test fixes * APPEALS-43187 Changed ifels to when case statement in transcription file controller * APPEALS-43187 Jest test fix * Create job for updating status field for transcription package (#23016) * Create job for updating status field for transcription package * Made use of constant * Addressed pr comment * Create new job for workflow to pull Transcription files back from specified endpoint folder (#22761) * Modified box_services and add download file * modified dowload services * change box_service * Modified type to permit in thanscription_file table * fix lint error * add spec file * change path to S3 * Modified Download_job to call from Monitor_job * Some lint fixes * Another lint fix * Modified monitor job and download job * modified spec files * change va_box_download_job * modifed test and job * Update spec files and change job * add transcription_id in Transcription_file table when create a new record * fix spec errors * fix spec issues * modified job * Modified legacy_hearing * Address some namespace issues and some lint issues * Replace log_error in controller since it isn't defined * Fix migrations * Adjust tests * Stop execution if transcription for hearing cannot be found * Revert schema --------- Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> Co-authored-by: Matthew Thornton * Add ReviewTranscriptTask, instructions, and task actions to case details task snapshot (#23131) * Add ReviewTranscriptTask and task actions * Add default instructions to ReviewTranscriptTask * Add task instructions to COPY file and fix style errors * Test: ReviewTranscriptTask creation * rubcop style fixes --------- Co-authored-by: William Medders * APPEALS-58421 Research for Legacy appeals to have Review Transcript Task (#23254) Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> * APPEALS-59937 (#23122) * APPEALS-59937 init commit * APPEALS-59937 remove space * APPEALS-59937 finish migration file * APPEALS-59937 fix hearing validation * APPEALS-59937 update transcriptions seeds * APPEALS-59937 remove self.ignore * APPEALS-59937 add space * APPEALS-59937 remove space * APPEALS-59937 remove lint * APPEALS-59937 remove lint * APPEALS-59937 update transcription_work_order_spec.rb * APPEALS-59937 check tests * APPEALS-59937 wait for alert * APPEALS-59937 update transcription_files seed file * APPEALS-59937 return to original state * APPEALS-59937 update hearing model * APPEALS-59937 add legacy association * APPEALS-59937 remove trailing space * APPEALS-59937 fix first test * APPEALS-59937 update migration file * APPEALS-59937 add polymorphic for legacy hearing * APPEALS-59937 remove comment * APPEALS-59937 update spec * APPEALS-59937 update download_job_spec * APPEALS-59937 add magic comment * APPEALS-59937 update repo * APPEALS-59937 update tests * APPEALS-59937 remove byebug * APPEALS-59937 remove space * APPEALS-59937 update test * Piedram/appeals 59933 (#23250) * Create Review Transcript task in the Hearing Admin queue * Update download job * Add change about the comments * Fix lint error * fix spec files --------- Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> * Added new tab called All Transcription (#23226) * Added new tab called All Transcription * fixing jest failure * update transcription file declaration * Update transcription_file.rb --------- Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> * APPEALS-49237 (#23270) * WIP search bar for transcription file dispatch * Fixed a bug where the filter params where not being properly updated in the get params if there was more than one filter present due to overwritten filter params during the QueueTable deep linking. * APPEALS-49237 - Fix some issues with front end searching on transcription dispatch tabs # Conflicts: # client/app/hearings/components/TranscriptionFileDispatchTabs.jsx * APPEALS-49237 - Implement working search for hearings on the backend * APPEALS-49237 - Load search string from URL params * APPEALS-49237 - Update rspecs for searches * APPEALS-49237 - Test fixes that started to fail for some random reason * APPEALS-49237 - Lint fix and snapshot update * APPEALS-49237 - Fixing a bug and removing a puts * APPEALS-49237 - Fix for broken spec * APPEALS-49237 - Apply search changes to new all files tab --------- Co-authored-by: William Medders Co-authored-by: = * Update Hearing Type filter options (#23272) * Wmedders21/appeals 59949 (#23338) * Pass task default instructions as an array * Account for polymorphic association on Transcript * WIP ErrorsFoundAndCorrectedModal * add css classes for form control file input * add routing logic for the ErrorsFoundAndCorrectedModal action to QueueApp * add logic to handle disable/enable of submit button on modal * add comments to the submit function * format request params for submit * jest test for ErrorsFoundAndCorrectedModal * reorganize modal to live in queue dir * use scss color variables instead of hex strings * fix hex value for linter * change file input background color --------- Co-authored-by: William Medders Co-authored-by: Michael Bidwell <122634362+mchbidwell@users.noreply.github.com> * No errors found upload transcript to VBMS modal (#23349) * No errors found upload transcript to VBMS modal * Add jest test * Fix error when failt and put placeholder in textarea field * remove imprt * reduce method in the controller * empty space below a return clause and indentation fix * merge with feature branch --------- Co-authored-by: Michael Bidwell <122634362+mchbidwell@users.noreply.github.com> * APPEALS-59935 Review transcript task for No error found on Case timeline (#23359) Co-authored-by: Michael Bidwell <122634362+mchbidwell@users.noreply.github.com> * APPEALS-43268 Display Returned Work Order files statuses and Returned Work Order file download (#23048) * APPEALS-43187 Transcription files Complete tab * APPEALS-43187 Jest and spec fixes * APPEALS-43187 Showed up Completed tab data and test fixes * create a bottom to download zip file and change the color when overdue * APPEALS-43268 make adjustments * APPEALS-43268 fix params * APPEALS-43268 fix params in .rb * APPEALS-43268 fix associations * APPEALS-43268 remove byebug * APPEALS-43268 firxed WO Details * APPEALS-43268 add api call for file download * APPEALS-43268 add file-saver dependency * APPEALS-43268 fix lint * APPEALS-43268 add route * APPEALS-43268 add frontend functionality * APPEALS-43268 update snapshots * APPEALS-43268 remove byebug * APPEALS-43268 update WorkOrderDetails.test.js * APPEALS-43268 lint * APPEALS-43268 lint * APPEALS-43268 add file-saver * APPEALS-43268 add file-sever via npm * APPEALS-43268 remove console.log * APPEALS-43268 fix status * APPEALS-43268 test file path * APPEALS-43268 comment file-saver * APPEALS-43268 use different method * APPEALS-43268 transcription_files_controller.rb * APPEALS-43268 update snapshot * APPEALS-43268 fix more tests * APPEALS-43268 change to get file from s3 * APPEALS-43268 remove trailing space * APPEALS-43268 update snapshot * APPEALS-43268 remove redundancy * APPEALS-43268 remove file-saver --------- Co-authored-by: Kamala Madamanchi Co-authored-by: breedbah Co-authored-by: breedbah <123968373+breedbah@users.noreply.github.com> * Remove change to factory for now * APPEALS-49620 fix lint * APPEALS-49620 fix broken test logic around new webex section * added bold text for case details column in unassugned and assigned tabs (#23365) * resolve conflicts * fix jest tests * fix jest tests --------- Co-authored-by: mounikamy Co-authored-by: Michael Bidwell <122634362+mchbidwell@users.noreply.github.com> * fix time * fix time base_hearing_update_form * return back to previous commit * fix hearing_update_form_spec * check test * Lint fixes --------- Co-authored-by: breedbah Co-authored-by: Ariana Konhilas Co-authored-by: mchbidwell <122634362+mchbidwell@users.noreply.github.com> Co-authored-by: Matthew Thornton <99351305+ThorntonMatthew@users.noreply.github.com> Co-authored-by: Matthew Thornton Co-authored-by: msteele Co-authored-by: Jeff Marks <106996298+jefftmarks@users.noreply.github.com> Co-authored-by: minhazur9 <65432922+minhazur9@users.noreply.github.com> Co-authored-by: Ariana Konhilas <109693628+konhilas-ariana@users.noreply.github.com> Co-authored-by: 631862 Co-authored-by: Minhazur Rahaman Co-authored-by: Marc Steele <71673522+msteele96@users.noreply.github.com> Co-authored-by: Jeff Marks Co-authored-by: breedbah <123968373+breedbah@users.noreply.github.com> Co-authored-by: Jeff Marks Co-authored-by: Will Medders <93014155+wmedders21@users.noreply.github.com> Co-authored-by: Jim Foley Co-authored-by: piedram Co-authored-by: Kamala Madamanchi <110078646+kamala-07@users.noreply.github.com> Co-authored-by: piedram <110848569+piedram@users.noreply.github.com> Co-authored-by: Kamala Madamanchi Co-authored-by: Amy Detwiler <133032208+amybids@users.noreply.github.com> Co-authored-by: 631068 Co-authored-by: Blake Manus <33578594+Blake-Manus@users.noreply.github.com> Co-authored-by: kristeja <112115264+kristeja@users.noreply.github.com> Co-authored-by: cdetlefva <133903625+cdetlefva@users.noreply.github.com> Co-authored-by: kristeja Co-authored-by: youfoundmanesh <129548081+youfoundmanesh@users.noreply.github.com> Co-authored-by: Alex Ferencz VA <156860944+aferencz1987VA@users.noreply.github.com> Co-authored-by: Alexandra Ferencz Co-authored-by: raymond-hughes Co-authored-by: Raymond Hughes <131811099+raymond-hughes@users.noreply.github.com> Co-authored-by: cacevesva <109166981+cacevesva@users.noreply.github.com> Co-authored-by: Alec Spottswood Co-authored-by: alex-guanipatin <143651918+alex-guanipatin@users.noreply.github.com> Co-authored-by: Christian Cain <149622375+christian-cain-bah@users.noreply.github.com> Co-authored-by: zborgesva <145717155+zborgesva@users.noreply.github.com> Co-authored-by: Zackary Borges-Rowe Co-authored-by: Aiman Kayad Co-authored-by: Matt Roth Co-authored-by: mikefinneran <110622959+mikefinneran@users.noreply.github.com> Co-authored-by: Brian Bommarito Co-authored-by: Noelle Adkin <98478937+NoelleAd@users.noreply.github.com> Co-authored-by: Anusha Palliyil Co-authored-by: Lauren Berry Co-authored-by: laurenyj <44596134+laurenyj@users.noreply.github.com> Co-authored-by: Craig Reese <109101548+craigrva@users.noreply.github.com> Co-authored-by: calvincostaBAH <108481161+calvincostaBAH@users.noreply.github.com> Co-authored-by: Isaiah Saucedo Co-authored-by: Jonathan Cohen <121630615+JCohDev@users.noreply.github.com> Co-authored-by: Jonathan Cohen Co-authored-by: prernadevbah <132498915+prernadevbah@users.noreply.github.com> Co-authored-by: noahhansen-gov <166541737+noahhansen-gov@users.noreply.github.com> Co-authored-by: Noah Hansen Co-authored-by: Prerna Devulapalli Co-authored-by: sbashamoni <138054633+sbashamoni@users.noreply.github.com> Co-authored-by: ramon-chavez Co-authored-by: Ron Wabukenda <130374706+ronwabVa@users.noreply.github.com> Co-authored-by: slwallerBAH <161883271+slwallerBAH@users.noreply.github.com> Co-authored-by: Tyler Broyles <109369527+TylerBroyles@users.noreply.github.com> Co-authored-by: Robert Travis Pierce Co-authored-by: nhansen3 Co-authored-by: Sean Parker Co-authored-by: Calvin Co-authored-by: samasudhirreddy <108430298+samasudhirreddy@users.noreply.github.com> Co-authored-by: Clay Sheppard Co-authored-by: Brandon Lee Dorner Co-authored-by: jonathanh-va <111081469+jonathanh-va@users.noreply.github.com> Co-authored-by: Jonathan Hoang Co-authored-by: Prajwal Amatya <122557351+pamatyatake2@users.noreply.github.com> Co-authored-by: almorbah Co-authored-by: Brandon Dorner Co-authored-by: Sean Craig <110493538+seancva@users.noreply.github.com> Co-authored-by: = Co-authored-by: almorbah <149511814+almorbah@users.noreply.github.com> Co-authored-by: Robert Travis Pierce Co-authored-by: Prajwal Amatya Co-authored-by: 631966 Co-authored-by: sbashamoni Co-authored-by: Michael Beard <131783726+mbeardy@users.noreply.github.com> Co-authored-by: SHarshain Co-authored-by: Michael Beard Co-authored-by: SHarshain <133917878+SHarshain@users.noreply.github.com> Co-authored-by: youfoundmanesh Co-authored-by: Jeremy Croteau Co-authored-by: davis-dwayne <47563178+davis-dwayne@users.noreply.github.com> Co-authored-by: Daniel Mage Co-authored-by: lauren-e-thompson <138795438+lauren-e-thompson@users.noreply.github.com> Co-authored-by: HunJerBAH Co-authored-by: HunJerBAH <99915461+HunJerBAH@users.noreply.github.com> Co-authored-by: Andrew Hadley Co-authored-by: IsaiahBar Co-authored-by: Mounika Myana Co-authored-by: samasudhirreddy Co-authored-by: Craig Reese Co-authored-by: William Medders Co-authored-by: Mounika Myana <173168271+mounikamy@users.noreply.github.com> Co-authored-by: William Medders Co-authored-by: mounikamy --- Gemfile | 7 +- Gemfile.lock | 12 + app/controllers/concerns/hearings_concerns.rb | 6 + .../transcription_contractors_controller.rb | 98 + .../transcription_files_controller.rb | 204 +- .../transcription_packages_controller.rb | 99 + .../transcription_work_order_controller.rb | 55 + .../hearings/transcriptions_controller.rb | 13 + .../hearings_application_controller.rb | 5 + app/controllers/tasks_controller.rb | 18 + app/controllers/test/users_controller.rb | 3 +- .../hearings/create_bill_of_materials_job.rb | 244 + .../download_transcription_file_job.rb | 2 +- app/jobs/hearings/monitor_box_job.rb | 102 + ...update_transcription_package_status_job.rb | 30 + app/jobs/hearings/va_box_download_job.rb | 207 + app/jobs/hearings/va_box_upload_job.rb | 179 + app/jobs/hearings/work_order_file_job.rb | 138 + .../zip_and_upload_transcription_files_job.rb | 102 + ...ip_and_upload_transcription_package_job.rb | 113 + app/mailers/work_order_file_issues_mailer.rb | 30 + app/models/hearing.rb | 38 +- .../hearings/forms/hearing_update_form.rb | 7 +- app/models/hearings/transcription.rb | 37 +- .../hearings/transcription_contractor.rb | 29 + app/models/hearings/transcription_file.rb | 165 +- app/models/hearings/transcription_package.rb | 92 + .../hearings/transcription_package_hearing.rb | 6 + .../transcription_package_legacy_hearing.rb | 6 + app/models/legacy_hearing.rb | 5 +- app/models/tasks/review_transcript_task.rb | 29 + app/models/transcription_work_order.rb | 165 + app/models/user.rb | 7 + app/repositories/hearing_repository.rb | 29 +- .../hearings/hearing_serializer.rb | 14 + .../hearings/transcription_file_serializer.rb | 1 + .../transcription_package_serializer.rb | 15 + app/services/external_api/va_box_service.rb | 231 + .../hearings/transcription_sequence_id.rb | 47 + app/views/hearings/index.html.erb | 1 + .../work_order_file_issues_mailer.html.erb | 16 + app/workflows/transcription_file_upload.rb | 4 +- app/workflows/transcription_packages.rb | 35 + client/COPY.json | 104 +- client/app/components/Checkbox.jsx | 4 +- client/app/components/DatePicker.jsx | 318 + client/app/components/DateSelector.jsx | 9 +- client/app/components/DropdownFilter.jsx | 1 + client/app/components/FilterOption.jsx | 4 +- client/app/components/SearchBar.jsx | 7 + client/app/components/TableFilter.jsx | 100 +- client/app/components/TextField.jsx | 1 + .../app/components/icons/ExternalLinkIcon.jsx | 4 +- client/app/hearings/HearingsApp.jsx | 55 +- .../TranscriptionFileDispatchTable.jsx | 330 + .../TranscriptionFileDispatchTableColumns.jsx | 546 ++ .../TranscriptionFileDispatchTabs.jsx | 280 + .../TranscriptionFileDispatchView.jsx | 197 + .../hearings/components/WorkOrderDetails.jsx | 150 + .../assignHearings/AssignHearingsTable.jsx | 1 + .../assignHearings/LinkToAppeal.jsx | 5 +- .../components/details/DetailsForm.jsx | 1 + .../components/details/TranscriberDetails.jsx | 53 + .../details/TranscriptionDetailsInputs.jsx | 144 +- .../details/TranscriptionDetailsWebex.jsx | 105 + .../details/TranscriptionFilesTable.jsx | 24 +- .../TranscriptionFilesTables.stories.mdx | 26 + .../details/TranscriptionFormSection.jsx | 23 +- .../AddEditContractorModal.jsx | 167 + .../ConfirmWorkOrderModal.jsx | 246 + .../EditTotalHearingsModal.jsx | 138 + .../PackageFilesModal.jsx | 154 + .../RemoveContractorModal.jsx | 66 + .../TranscriptionSettings.jsx | 404 + .../WorkOrderHighlightsModal.jsx | 116 + .../WorkOrderUnassignModal.jsx | 86 + .../TranscriptionSettingsContainer.jsx | 35 + client/app/queue/QueueApp.jsx | 27 + client/app/queue/QueueDropdownFilter.jsx | 17 +- client/app/queue/QueueTable.jsx | 46 +- client/app/queue/QueueTableBuilder.jsx | 1 + .../UploadTranscriptionVBMSNoErrorModal.jsx | 121 + .../ErrorsFoundAndCorrectedModal.jsx | 108 + client/app/queue/components/TaskRows.jsx | 45 +- client/app/queue/components/TaskTable.jsx | 1 + client/app/queue/constants.js | 1 + client/app/styles/_commons.scss | 36 + client/app/styles/_table.scss | 28 +- client/app/styles/hearings/_hearings.scss | 8 +- client/constants/TASK_ACTIONS.json | 12 + .../TRANSCRIPTION_FILE_DISPATCH_CONFIG.json | 98 + client/package.json | 1 + .../CaseDistributionApp.test.js.snap | 6 + client/test/app/components/DatePicker.test.js | 179 + .../__snapshots__/DatePicker.test.js.snap | 923 ++ .../__snapshots__/FilterOption.test.js.snap | 2 +- .../app/hearings/components/Details.test.js | 133 +- .../TranscriptionDetailsInputs.test.js | 46 + .../TranscriptionFileDispatchTable.test.js | 782 ++ .../TranscriptionFileDispatchView.test.js | 269 + .../components/WorkOrderDetails.test.js | 83 + .../__snapshots__/Details.test.js.snap | 4 - ...ranscriptionFileDispatchTable.test.js.snap | 8564 +++++++++++++++++ .../StaticVirtualHearing.test.js.snap | 10 - .../details/TranscriberDetails.test.js | 40 + .../details/TranscriptionFilesTable.test.js | 99 + .../details/VirtualHearingFields.test.js | 2 + .../__snapshots__/HearingLinks.test.js.snap | 26 - .../VirtualHearingFields.test.js.snap | 8 - .../AddEditContractorModal.test.js | 128 + .../ConfirmWorkOrderModal.test.js | 154 + .../EditTotalHearingsModal.test.js | 145 + .../PackageFilesModal.test.js | 83 + .../RemoveContractorModal.test.js | 53 + .../TranscriptionDetailsWebex.test.js | 59 + .../TranscriptionSettings.test.js | 94 + .../WorkOrderHighlights.test.js | 87 + .../WorkOrderUnassignModal.test.js | 42 + .../AddEditContractorModal.test.js.snap | 907 ++ .../ConfirmWorkOrderModal.test.js.snap | 139 + .../EditTotalHearingsModal.test.js.snap | 1173 +++ .../PackageFilesModal.test.js.snap | 242 + .../RemoveContractorModal.test.js.snap | 426 + .../TranscriptionDetailsWebex.test.js.snap | 110 + .../TranscriptionSettings.test.js.snap | 1206 +++ .../WorkOrderHighlights.test.js.snap | 98 + client/test/app/jestSetup.js | 1 + client/test/app/queue/QueueTable.test.js | 359 +- .../NotificationsView.test.js.snap | 2 - .../__snapshots__/QueueTable.test.js.snap | 1103 +++ .../ErrorsFoundAndCorrectedModal.test.js | 93 + .../app/queue/components/TaskRows.test.js | 105 + ...ploadTranscriptionVBMSNoErrorModal.test.js | 74 + .../ErrorsFoundAndCorrectedModal.test.js.snap | 146 + client/test/data/hearings.js | 1 + .../taskActionModals/taskActionModalData.js | 1800 ++++ client/test/data/transcriptionContractors.js | 50 + client/yarn.lock | 5 + .../create_transcription_files_folder.rb | 2 +- config/initializers/scheduled_jobs.rb | 4 +- config/initializers/va_box_service.rb | 1 + config/routes.rb | 17 + ...190320_create_transcription_contractors.rb | 19 + ...deleted_at_to_transcription_contractors.rb | 8 + ...507145931_add_columns_to_transcriptions.rb | 13 + ...507203310_add_indexes_to_transcriptions.rb | 6 + ...161059_add_transcription_packages_table.rb | 16 + ...7_update_transcription_packages_columns.rb | 11 + ...3_create_transcription_package_hearings.rb | 8 + ...e_transcription_package_legacy_hearings.rb | 8 + ...id_and_locked_at_to_transcription_files.rb | 17 + .../20240712140629_create_task_id_seq.rb | 19 + ...712142557_add_task_id_to_transcriptions.rb | 9 + ...transcription_id_to_transcription_files.rb | 9 + ...160514_update_transcription_files_table.rb | 11 + ...add_return_date_to_transcriptions_table.rb | 5 + ..._transcriptions_for_polymorphic_hearing.rb | 34 + db/schema.rb | 118 +- db/scripts/create_transcriptions_trigger.sql | 18 + db/scripts/update_transcriptions.sql | 9 + db/seeds.rb | 23 + db/seeds/education.rb | 30 +- db/seeds/transcription_contractors.rb | 47 + db/seeds/transcription_files.rb | 82 + db/seeds/transcription_packages.rb | 111 + db/seeds/transcriptions.rb | 16 + db/seeds/users.rb | 7 +- lib/fakes/va_box_service.rb | 71 + lib/tasks/create_transcriptions_trigger.rake | 9 + lib/tasks/update_transcriptions.rake | 9 + package-lock.json | 36 +- ...anscription_contractors_controller_spec.rb | 197 + .../transcription_files_controller_spec.rb | 519 +- .../transcription_packages_controller_spec.rb | 290 + ...ranscription_work_order_controller_spec.rb | 157 + .../hearings_application_controller_spec.rb | 19 + spec/factories/hearing.rb | 12 +- spec/factories/legacy_hearing.rb | 12 +- spec/factories/transcription.rb | 2 + spec/factories/transcription_contractor.rb | 11 + spec/factories/transcription_file.rb | 4 +- spec/factories/transcription_package.rb | 14 + spec/feature/hearings/hearing_details_spec.rb | 13 +- .../virtual_hearings/daily_docket_spec.rb | 4 +- .../non_comp/individual_claim_history_spec.rb | 2 +- .../queue/appeal_notifications_page_spec.rb | 8 +- .../create_bill_of_materials_job_spec.rb | 129 + .../download_transcription_file_job_spec.rb | 40 +- spec/jobs/hearings/monitor_box_job_spec.rb | 55 + ...e_transcription_package_status_job_spec.rb | 30 + .../jobs/hearings/va_box_download_job_spec.rb | 53 + spec/jobs/hearings/va_box_upload_job_spec.rb | 88 + .../jobs/hearings/work_order_file_job_spec.rb | 95 + ...and_upload_transcription_files_job_spec.rb | 93 + ...d_upload_transcription_package_job_spec.rb | 95 + .../add_task_id_to_transcriptions_spec.rb | 32 + .../hearings/create_task_id_seq_spec.rb | 38 + spec/models/docket_spec.rb | 5 - .../forms/hearing_update_form_spec.rb | 9 +- .../hearings/transcription_contractor_spec.rb | 42 + .../transcription_contractors_model_spec.rb | 36 + .../hearings/transcription_file_spec.rb | 103 +- .../hearings/transcription_package_spec.rb | 166 + spec/models/hearings/transcription_spec.rb | 17 + .../tasks/review_transcript_task_spec.rb | 35 + spec/models/transcription_work_order_spec.rb | 112 + spec/models/user_spec.rb | 37 + spec/seeds/hearings_spec.rb | 4 +- spec/seeds/users_spec.rb | 2 +- .../external_api/va_box_service_spec.rb | 65 + spec/spec_helper.rb | 1 - spec/workflows/transcription_packages_spec.rb | 50 + yarn.lock | 15 + 213 files changed, 30284 insertions(+), 391 deletions(-) create mode 100644 app/controllers/hearings/transcription_contractors_controller.rb create mode 100644 app/controllers/hearings/transcription_packages_controller.rb create mode 100644 app/controllers/hearings/transcription_work_order_controller.rb create mode 100644 app/controllers/hearings/transcriptions_controller.rb create mode 100644 app/jobs/hearings/create_bill_of_materials_job.rb create mode 100644 app/jobs/hearings/monitor_box_job.rb create mode 100644 app/jobs/hearings/update_transcription_package_status_job.rb create mode 100644 app/jobs/hearings/va_box_download_job.rb create mode 100644 app/jobs/hearings/va_box_upload_job.rb create mode 100644 app/jobs/hearings/work_order_file_job.rb create mode 100644 app/jobs/hearings/zip_and_upload_transcription_files_job.rb create mode 100644 app/jobs/hearings/zip_and_upload_transcription_package_job.rb create mode 100644 app/mailers/work_order_file_issues_mailer.rb create mode 100644 app/models/hearings/transcription_contractor.rb create mode 100644 app/models/hearings/transcription_package.rb create mode 100644 app/models/hearings/transcription_package_hearing.rb create mode 100644 app/models/hearings/transcription_package_legacy_hearing.rb create mode 100644 app/models/tasks/review_transcript_task.rb create mode 100644 app/models/transcription_work_order.rb create mode 100644 app/serializers/hearings/transcription_package_serializer.rb create mode 100644 app/services/external_api/va_box_service.rb create mode 100644 app/services/hearings/transcription_sequence_id.rb create mode 100644 app/views/layouts/work_order_file_issues_mailer.html.erb create mode 100644 app/workflows/transcription_packages.rb create mode 100644 client/app/components/DatePicker.jsx create mode 100644 client/app/hearings/components/TranscriptionFileDispatchTable.jsx create mode 100644 client/app/hearings/components/TranscriptionFileDispatchTableColumns.jsx create mode 100644 client/app/hearings/components/TranscriptionFileDispatchTabs.jsx create mode 100644 client/app/hearings/components/TranscriptionFileDispatchView.jsx create mode 100644 client/app/hearings/components/WorkOrderDetails.jsx create mode 100644 client/app/hearings/components/details/TranscriberDetails.jsx create mode 100644 client/app/hearings/components/details/TranscriptionDetailsWebex.jsx create mode 100644 client/app/hearings/components/details/TranscriptionFilesTables.stories.mdx create mode 100644 client/app/hearings/components/transcriptionProcessing/AddEditContractorModal.jsx create mode 100644 client/app/hearings/components/transcriptionProcessing/ConfirmWorkOrderModal.jsx create mode 100644 client/app/hearings/components/transcriptionProcessing/EditTotalHearingsModal.jsx create mode 100644 client/app/hearings/components/transcriptionProcessing/PackageFilesModal.jsx create mode 100644 client/app/hearings/components/transcriptionProcessing/RemoveContractorModal.jsx create mode 100644 client/app/hearings/components/transcriptionProcessing/TranscriptionSettings.jsx create mode 100644 client/app/hearings/components/transcriptionProcessing/WorkOrderHighlightsModal.jsx create mode 100644 client/app/hearings/components/transcriptionProcessing/WorkOrderUnassignModal.jsx create mode 100644 client/app/hearings/containers/TranscriptionSettingsContainer.jsx create mode 100644 client/app/queue/UploadTranscriptionVBMSNoErrorModal.jsx create mode 100644 client/app/queue/components/ErrorsFoundAndCorrectedModal.jsx create mode 100644 client/constants/TRANSCRIPTION_FILE_DISPATCH_CONFIG.json create mode 100644 client/test/app/components/DatePicker.test.js create mode 100644 client/test/app/components/__snapshots__/DatePicker.test.js.snap create mode 100644 client/test/app/hearings/components/TranscriptionDetailsInputs.test.js create mode 100644 client/test/app/hearings/components/TranscriptionFileDispatchTable.test.js create mode 100644 client/test/app/hearings/components/TranscriptionFileDispatchView.test.js create mode 100644 client/test/app/hearings/components/WorkOrderDetails.test.js create mode 100644 client/test/app/hearings/components/__snapshots__/TranscriptionFileDispatchTable.test.js.snap create mode 100644 client/test/app/hearings/components/details/TranscriberDetails.test.js create mode 100644 client/test/app/hearings/components/details/TranscriptionFilesTable.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/AddEditContractorModal.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/ConfirmWorkOrderModal.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/EditTotalHearingsModal.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/PackageFilesModal.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/RemoveContractorModal.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/TranscriptionDetailsWebex.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/TranscriptionSettings.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/WorkOrderHighlights.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/WorkOrderUnassignModal.test.js create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/AddEditContractorModal.test.js.snap create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/ConfirmWorkOrderModal.test.js.snap create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/EditTotalHearingsModal.test.js.snap create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/PackageFilesModal.test.js.snap create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/RemoveContractorModal.test.js.snap create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/TranscriptionDetailsWebex.test.js.snap create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/TranscriptionSettings.test.js.snap create mode 100644 client/test/app/hearings/components/transcriptionProcessing/__snapshots__/WorkOrderHighlights.test.js.snap create mode 100644 client/test/app/queue/components/ErrorsFoundAndCorrectedModal.test.js create mode 100644 client/test/app/queue/components/TaskRows.test.js create mode 100644 client/test/app/queue/components/UploadTranscriptionVBMSNoErrorModal.test.js create mode 100644 client/test/app/queue/components/__snapshots__/ErrorsFoundAndCorrectedModal.test.js.snap create mode 100644 client/test/data/transcriptionContractors.js create mode 100644 config/initializers/va_box_service.rb create mode 100644 db/migrate/20240423190320_create_transcription_contractors.rb create mode 100644 db/migrate/20240507101830_add_deleted_at_to_transcription_contractors.rb create mode 100644 db/migrate/20240507145931_add_columns_to_transcriptions.rb create mode 100644 db/migrate/20240507203310_add_indexes_to_transcriptions.rb create mode 100644 db/migrate/20240520161059_add_transcription_packages_table.rb create mode 100644 db/migrate/20240523021417_update_transcription_packages_columns.rb create mode 100644 db/migrate/20240528152913_create_transcription_package_hearings.rb create mode 100644 db/migrate/20240528153139_create_transcription_package_legacy_hearings.rb create mode 100644 db/migrate/20240621083822_add_locked_by_user_id_and_locked_at_to_transcription_files.rb create mode 100644 db/migrate/20240712140629_create_task_id_seq.rb create mode 100644 db/migrate/20240712142557_add_task_id_to_transcriptions.rb create mode 100644 db/migrate/20240825120325_add_transcription_id_to_transcription_files.rb create mode 100644 db/migrate/20240827160514_update_transcription_files_table.rb create mode 100644 db/migrate/20240910105904_add_return_date_to_transcriptions_table.rb create mode 100644 db/migrate/20241008145121_update_transcriptions_for_polymorphic_hearing.rb create mode 100644 db/scripts/create_transcriptions_trigger.sql create mode 100644 db/scripts/update_transcriptions.sql create mode 100644 db/seeds/transcription_contractors.rb create mode 100644 db/seeds/transcription_files.rb create mode 100644 db/seeds/transcription_packages.rb create mode 100644 db/seeds/transcriptions.rb create mode 100644 lib/fakes/va_box_service.rb create mode 100644 lib/tasks/create_transcriptions_trigger.rake create mode 100644 lib/tasks/update_transcriptions.rake create mode 100644 spec/controllers/hearings/transcription_contractors_controller_spec.rb create mode 100644 spec/controllers/hearings/transcription_packages_controller_spec.rb create mode 100644 spec/controllers/hearings/transcription_work_order_controller_spec.rb create mode 100644 spec/factories/transcription_contractor.rb create mode 100644 spec/factories/transcription_package.rb create mode 100644 spec/jobs/hearings/create_bill_of_materials_job_spec.rb create mode 100644 spec/jobs/hearings/monitor_box_job_spec.rb create mode 100644 spec/jobs/hearings/update_transcription_package_status_job_spec.rb create mode 100644 spec/jobs/hearings/va_box_download_job_spec.rb create mode 100644 spec/jobs/hearings/va_box_upload_job_spec.rb create mode 100644 spec/jobs/hearings/work_order_file_job_spec.rb create mode 100644 spec/jobs/hearings/zip_and_upload_transcription_files_job_spec.rb create mode 100644 spec/jobs/hearings/zip_and_upload_transcription_package_job_spec.rb create mode 100644 spec/migrations/hearings/add_task_id_to_transcriptions_spec.rb create mode 100644 spec/migrations/hearings/create_task_id_seq_spec.rb create mode 100644 spec/models/hearings/transcription_contractor_spec.rb create mode 100644 spec/models/hearings/transcription_contractors_model_spec.rb create mode 100644 spec/models/hearings/transcription_package_spec.rb create mode 100644 spec/models/hearings/transcription_spec.rb create mode 100644 spec/models/tasks/review_transcript_task_spec.rb create mode 100644 spec/models/transcription_work_order_spec.rb create mode 100644 spec/services/external_api/va_box_service_spec.rb create mode 100644 spec/workflows/transcription_packages_spec.rb create mode 100644 yarn.lock diff --git a/Gemfile b/Gemfile index ee62f560cca..904cba61e78 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ gem "bgs", git: "https://github.com/department-of-veterans-affairs/ruby-bgs.git" # Bootsnap speeds up app boot (and started to be a default gem in 5.2). gem "bootsnap", require: false gem "browser" +gem "bundler", "~> 2.4.22" gem "business_time", "~> 0.9.3" gem "caseflow", git: "https://github.com/department-of-veterans-affairs/caseflow-commons", ref: "9bd3635fbd8094d25160669f38d8699e2f1d7a98" gem "connect_mpi", git: "https://github.com/department-of-veterans-affairs/connect-mpi.git", ref: "a3a58c64f85b980a8b5ea6347430dd73a99ea74c" @@ -23,11 +24,14 @@ gem "connect_vbms", git: "https://github.com/department-of-veterans-affairs/conn gem "console_tree_renderer", git: "https://github.com/department-of-veterans-affairs/console-tree-renderer.git", tag: "v0.1.1" gem "countries" gem "dry-schema", "~> 1.4" +gem "faraday" +gem "faraday-multipart" gem "fast_jsonapi" gem "fuzzy_match" gem "govdelivery-tms", require: "govdelivery/tms/mail/delivery_method" gem "holidays", "~> 6.4" gem "icalendar" +gem "jwt" gem "kaminari" gem "logstasher" gem "moment_timezone-rails" @@ -91,6 +95,7 @@ gem "sass-rails", "~> 5.0" # Error reporting to Sentry gem "sentry-raven" gem "shoryuken", "3.1.11" +gem "spreadsheet", "~> 1.3" gem "statsd-instrument" gem "stringex", require: false # catch problematic migrations at development/test time @@ -132,8 +137,8 @@ group :test, :development, :demo, :make_docs do gem "rails-erd" gem "rb-readline" gem "rspec" - gem "rspec-rails" # For CircleCI test metadata analysis + gem "rspec-rails" gem "rspec_junit_formatter" gem "rswag-specs" gem "rubocop", "= 0.83", require: false diff --git a/Gemfile.lock b/Gemfile.lock index e6db09febdc..3d93fe6c7be 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1450,6 +1450,7 @@ GEM base64 (0.2.0) benchmark (0.3.0) benchmark-ips (2.14.0) + bigdecimal (3.1.8) bootsnap (1.7.5) msgpack (~> 1.0) brakeman (4.7.1) @@ -1663,6 +1664,8 @@ GEM hana (~> 1.3) regexp_parser (~> 1.5) uri_template (~> 0.7) + jwt (2.8.2) + base64 kaminari (1.2.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.1) @@ -1996,6 +1999,7 @@ GEM rake (>= 0.8.1) ruby-graphviz (1.2.4) ruby-oci8 (2.2.14) + ruby-ole (1.2.13.1) ruby-plsql (0.8.0) ruby-prof (1.4.1) ruby-progressbar (1.13.0) @@ -2069,6 +2073,9 @@ GEM thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) + spreadsheet (1.3.1) + bigdecimal + ruby-ole sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -2150,6 +2157,7 @@ DEPENDENCIES browser bullet (~> 6.1.0) bummr + bundler (~> 2.4.22) bundler-audit business_time (~> 0.9.3) capybara @@ -2167,6 +2175,8 @@ DEPENDENCIES dry-schema (~> 1.4) factory_bot_rails (~> 5.2) faker + faraday + faraday-multipart fast_jsonapi fasterer foreman @@ -2177,6 +2187,7 @@ DEPENDENCIES icalendar immigrant json_schemer (~> 0.2.16) + jwt kaminari knapsack_pro (~> 3.8) logstasher @@ -2250,6 +2261,7 @@ DEPENDENCIES single_cov sniffybara! solargraph + spreadsheet (~> 1.3) sql_tracker statsd-instrument stringex diff --git a/app/controllers/concerns/hearings_concerns.rb b/app/controllers/concerns/hearings_concerns.rb index e55a86ea2e2..b807febce78 100644 --- a/app/controllers/concerns/hearings_concerns.rb +++ b/app/controllers/concerns/hearings_concerns.rb @@ -48,5 +48,11 @@ def check_vso_representation end end end + + def verify_transcription_user + if !TranscriptionTeam.singleton.user_has_access?(current_user) + redirect_to "/unauthorized" + end + end end end diff --git a/app/controllers/hearings/transcription_contractors_controller.rb b/app/controllers/hearings/transcription_contractors_controller.rb new file mode 100644 index 00000000000..086f068ceed --- /dev/null +++ b/app/controllers/hearings/transcription_contractors_controller.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +class Hearings::TranscriptionContractorsController < ApplicationController + include HearingsConcerns::VerifyAccess + + rescue_from ActiveRecord::RecordNotFound, with: :render_record_not_found + rescue_from ActionController::ParameterMissing, with: :render_record_not_found + rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid + before_action :verify_transcription_user + before_action :verify_access_to_hearings, except: [:available_contractors, :filterable_contractors] + + def index + respond_to do |format| + format.html do + render "hearings/index" + end + format.json do + @transcription_contractors = TranscriptionContractor.all + counts = Transcription.counts_for_this_week + transcription_contractor_json = @transcription_contractors.as_json + .each { |contractor| contractor["transcription_count_this_week"] = counts[contractor["id"]] || 0 } + render json: { transcription_contractors: transcription_contractor_json } + end + end + end + + def filterable_contractors + contractors = TranscriptionContractor.select(:id, :name) + render json: { transcription_contractors: contractors } + end + + def available_contractors + contractors = TranscriptionContractor.where(is_available_for_work: true).select(:id, :name) + today = Time.zone.today + standard_date = today + 15.days + Holidays.between(today, today + 15.days, :federal_reserve, :observed).length.days + expedited_date = today + 5.days + Holidays.between(today, today + 5.days, :federal_reserve, :observed).length.days + render json: { + transcription_contractors: contractors, + return_dates: [standard_date.to_formatted_s(:short_date), expedited_date.to_formatted_s(:short_date)] + } + end + + def show + render json: { transcription_contractor: transcription_contractor } + end + + def create + transcription_contractor = TranscriptionContractor.create!(transcription_contractor_params) + render json: { transcription_contractor: transcription_contractor }, status: :created + end + + def update + transcription_contractor.update!(transcription_contractor_params) + render json: { transcription_contractor: transcription_contractor }, status: :accepted + end + + def destroy + transcription_contractor.destroy! + render json: {} + end + + private + + def transcription_contractor + @transcription_contractor ||= TranscriptionContractor.find(params[:id]) + end + + def render_record_not_found + render json: { + "errors": [ + "title": "Contractor Not Found", + "detail": "Contractor with that ID is not found" + ] + }, status: :not_found + end + + def render_record_invalid(error) + render json: { + "errors": [ + "title": error.class.to_s, + "detail": error.message + ] + }, status: :bad_request + end + + def transcription_contractor_params + params + .require(:transcription_contractor) + .permit(:current_goal, + :directory, + :email, + :inactive, + :is_available_for_work, + :name, + :phone, + :poc) + end +end diff --git a/app/controllers/hearings/transcription_files_controller.rb b/app/controllers/hearings/transcription_files_controller.rb index 9701580f50e..df3684ff15e 100644 --- a/app/controllers/hearings/transcription_files_controller.rb +++ b/app/controllers/hearings/transcription_files_controller.rb @@ -5,6 +5,7 @@ class Hearings::TranscriptionFilesController < ApplicationController rescue_from ActiveRecord::RecordNotFound, with: :render_page_not_found before_action :verify_access_to_hearings, only: [:download_transcription_file] + before_action :verify_transcription_user, only: [:transcription_file_tasks] # Downloads file and sends to user's local computer def download_transcription_file @@ -17,9 +18,210 @@ def render_page_not_found redirect_to "/404" end + def transcription_file_tasks + @transcription_files = Hearings::TranscriptionFile.filterable_values + select_based_on_tab + apply_search + apply_filters + setup_pagination + apply_sorting + render json: { + task_page_count: @total_pages, + tasks: { data: build_transcription_json(@transcription_files) }, + tasks_per_page: @page_size, + total_task_count: @total_count + } + end + + def locked + locked_files = Hearings::TranscriptionFile.locked.preload(:locked_by) + files = [] + locked_files.each do |file| + status = file.locked_by_id == current_user.id ? "selected" : "locked" + username = file.try(:locked_by).try(:username) + message = status == "locked" && username ? "Locked by " + username : "" + files << { id: file.id, status: status, message: message } + end + render json: files + end + + def lock + files = Hearings::TranscriptionFile.where(id: params[:file_ids]) + status = params[:status] && params[:status].to_s == "true" ? true : false + lockable_file_ids = [] + files.each do |file| + if file.lockable?(current_user.id) + lockable_file_ids << file.id + end + end + + if status + locked_by_id = current_user.id + locked_at = Time.now.utc + else + locked_by_id = nil + locked_at = nil + end + + Hearings::TranscriptionFile.where(id: lockable_file_ids).update_all( + locked_by_id: locked_by_id, locked_at: locked_at + ) + + locked + end + + def format_docket_number(file) + if file.hearing_type == "Hearing" + return "H" + file.docket_number + end + + "L" + file.docket_number + end + + def selected_files_info + files = [] + ids = params[:file_ids].split(",") + Hearings::TranscriptionFile.where(id: ids).filterable_values.each do |transcription_file| + hearing = transcription_file.hearing + files << { + id: transcription_file.id, + docketNumber: format_docket_number(transcription_file), + firstName: hearing.appeal.appellant_first_name, + lastName: hearing.appeal.appellant_last_name, + isAdvancedOnDocket: transcription_file.advanced_on_docket?, + caseType: transcription_file.case_type, + hearingDate: transcription_file.hearing_date, + appealType: transcription_file.hearing_type == "Hearing" ? "AMA" : "Legacy", + judge: hearing.judge&.full_name&.split(" ")&.last, + regionalOffice: hearing.regional_office&.city, + hearingId: hearing.id + } + end + render json: files + end + + def fetch_file + docket_number = params[:docket_number] + file_path = Hearings::TranscriptionFile.fetch_file_by_docket_and_type(docket_number) + + if file_path + send_file file_path, filename: File.basename(file_path), type: "application/vnd.ms-excel" + else + render json: { error: "File not found" }, status: :not_found + end + end + private def file - @file ||= TranscriptionFile.find(params[:file_id]) + @file ||= Hearings::TranscriptionFile.find(params[:file_id]) + end + + def select_based_on_tab + case params[:tab] + when "Unassigned" + @transcription_files = @transcription_files.unassigned + when "Completed" + @transcription_files = @transcription_files.completed + end + end + + def apply_filters + if params[:filter].present? + params[:filter].each do |filter| + filter_hash = Rack::Utils.parse_query(filter) + if filter_hash["col"] == "hearingTypeColumn" + @transcription_files = @transcription_files.filter_by_hearing_type(filter_hash["val"].split("|")) + end + if filter_hash["col"] == "typesColumn" + @transcription_files = @transcription_files.filter_by_types(filter_hash["val"].split("|")) + end + if filter_hash["col"] == "hearingDateColumn" + @transcription_files = @transcription_files.filter_by_hearing_dates(filter_hash["val"].split(",")) + end + if filter_hash["col"] == "statusColumn" + @transcription_files = @transcription_files.filter_by_status(filter_hash["val"].split("|")) + end + end + end + end + + def apply_search + return if params[:search].blank? + + @transcription_files = @transcription_files.search(params[:search]) + end + + def apply_sorting + sort_by = params[:sort_by] || "id" + order = params[:order] == "asc" ? "ASC" : "DESC" + @transcription_files = + case sort_by + when "hearingDateColumn" + @transcription_files.order_by_hearing_date(order) + when "hearingTypeColumn" + @transcription_files.order_by_hearing_type(order) + when "typesColumn" + @transcription_files.order_by_case_type(order) + else + @transcription_files.order_by_id(order) + end + end + + def setup_pagination + current_page = (params[:page] || 1).to_i + @page_size = (params[:page_size] || 15).to_i + @page_start = (current_page - 1) * @page_size + filtered_count = @transcription_files.reselect("COUNT(transcription_files.id) AS count_all") + @total_count = filtered_count[0].count_all + @total_pages = (@total_count / @page_size.to_f).ceil + @transcription_files = @transcription_files + .limit(@page_size) + .offset(@page_start) + .preload(hearing: [:hearing_day, :appeal]) + end + + def build_transcription_json(transcription_files) + tasks = [] + transcription_files.each do |transcription_file| + task = { + id: transcription_file.id, + externalAppealId: transcription_file.external_appeal_id, + docketNumber: transcription_file.docket_number, + caseDetails: transcription_file.case_details, + isAdvancedOnDocket: transcription_file.advanced_on_docket?, + caseType: transcription_file.case_type, + hearingDate: transcription_file.hearing_date, + hearingType: transcription_file.hearing_type, + fileStatus: transcription_file.file_status + } + task = add_completed_tab_fields(task, transcription_file) if params[:tab] == "Completed" + task = add_all_tab_fields(task, transcription_file) if params[:tab] == "All" + tasks << task + end + tasks + end + + def add_completed_tab_fields(task, transcription_file) + task.merge( + { + workOrder: transcription_file.transcription&.task_number, + expectedReturnDate: transcription_file&.transcription&.transcription_package + &.expected_return_date&.to_formatted_s(:short_date), + returnDate: transcription_file.date_returned_box&.to_formatted_s(:short_date), + contractor: transcription_file&.transcription&.transcription_package&.contractor&.name + } + ) + end + + def add_all_tab_fields(task, transcription_file) + task.merge( + { + workOrder: transcription_file.transcription&.task_number, + uploadDate: transcription_file&.date_returned_box&.to_formatted_s(:short_date), + returnDate: transcription_file.date_returned_box&.to_formatted_s(:short_date), + contractor: transcription_file&.transcription&.transcription_package&.contractor&.name + } + ) end end diff --git a/app/controllers/hearings/transcription_packages_controller.rb b/app/controllers/hearings/transcription_packages_controller.rb new file mode 100644 index 00000000000..413746fcf0f --- /dev/null +++ b/app/controllers/hearings/transcription_packages_controller.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +class Hearings::TranscriptionPackagesController < ApplicationController + include HearingsConcerns::VerifyAccess + + before_action :verify_transcription_user + def transcription_package_tasks + # Initial filter to get only the transcription packages with statuses 'Sent-Overdue' and 'Sent' + @transcription_packages = TranscriptionPackage.with_status_overdue_or_sent + apply_search + apply_filters + setup_pagination + apply_sorting + + render json: { + task_page_count: @total_pages, + tasks: { data: build_package_json(@transcription_packages) }, + tasks_per_page: @page_size, + total_task_count: @total_count + } + end + + def apply_search + return if params[:search].blank? + + @transcription_packages = @transcription_packages.search(params[:search]) + end + + # rubocop:disable Metrics/MethodLength + def apply_filters + if params[:filter].present? + params[:filter].each do |filter| + filter_hash = Rack::Utils.parse_query(filter) + if filter_hash["col"] == "dateSentColumn" + @transcription_packages = + @transcription_packages.filter_by_date(filter_hash["val"].split(","), "transcription_packages.created_at") + end + if filter_hash["col"] == "expectedReturnDateColumn" + @transcription_packages = + @transcription_packages.filter_by_date(filter_hash["val"].split(","), "expected_return_date") + end + if filter_hash["col"] == "contractorColumn" + @transcription_packages = + @transcription_packages.filter_by_contractor(filter_hash["val"].split("|")) + end + if filter_hash["col"] == "statusColumn" + @transcription_packages = + @transcription_packages.filter_by_status(filter_hash["val"].split("|")) + end + end + end + end + # rubocop:enable Metrics/MethodLength + + def apply_sorting + sort_by = params[:sort_by] || "id" + order = params[:order] == "asc" ? "ASC" : "DESC" + @transcription_packages = + case sort_by + when "dateSentColumn" + @transcription_packages.order_by_field(order, "transcription_packages.created_at") + when "expectedReturnDateColumn" + @transcription_packages.order_by_field(order, "expected_return_date") + when "contractorColumn" + @transcription_packages.order_by_field(order, "transcription_contractors.name") + else + @transcription_packages.order_by_field(order, "transcription_packages.id") + end + end + + def setup_pagination + current_page = (params[:page] || 1).to_i + @page_size = (params[:page_size] || 15).to_i + @page_start = (current_page - 1) * @page_size + filtered_count = @transcription_packages.reselect("COUNT(transcription_packages.id) AS count_all") + @total_count = filtered_count[0].count_all + @total_pages = (@total_count / @page_size.to_f).ceil + @transcription_packages = @transcription_packages + .limit(@page_size) + .offset(@page_start) + .preload(:contractor, :transcriptions) + end + + def build_package_json(transcription_packages) + tasks = [] + transcription_packages.each do |transcription_package| + tasks << { + id: transcription_package.id, + workOrder: transcription_package.task_number, + items: transcription_package.contents_count, + dateSent: transcription_package.created_at.to_formatted_s(:short_date), + expectedReturnDate: transcription_package.expected_return_date.to_formatted_s(:short_date), + contractor: transcription_package.contractor.name, + status: transcription_package.status + } + end + tasks + end +end diff --git a/app/controllers/hearings/transcription_work_order_controller.rb b/app/controllers/hearings/transcription_work_order_controller.rb new file mode 100644 index 00000000000..c8965d20ebe --- /dev/null +++ b/app/controllers/hearings/transcription_work_order_controller.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +class Hearings::TranscriptionWorkOrderController < ApplicationController + include HearingsConcerns::VerifyAccess + before_action :set_task_number + + def display_wo_summary + wo_summary = ::TranscriptionWorkOrder.display_wo_summary(@task_number) + respond_to do |format| + format.html { render "hearings/index" } + format.json { render json: { data: wo_summary } } + end + end + + def display_wo_contents + wo_content = ::TranscriptionWorkOrder.display_wo_contents(@task_number) + if wo_content.present? + render json: { data: wo_content } + else + render_error("Transcription content not found.", :not_found) + end + end + + def unassign_wo + begin + wo_content = ::TranscriptionWorkOrder.unassign_wo(@task_number) + render json: { data: wo_content } + rescue StandardError => error + Rails.logger.error("Failed to unassign work order: #{error.message}") + render_error("Something went wrong.", :internal_server_error) + end + end + + def unassigning_work_order + begin + Transcription.unassign_by_task_number(@task_number) + TranscriptionPackage.cancel_by_task_number(@task_number) + TranscriptionFile.reset_files(@task_number) + rescue StandardError => error + Rails.logger.error( + "Error in unassigning work order for task number #{@task_number}: #{error.message}" + ) + end + end + + private + + def set_task_number + @task_number = params[:task_number] + end + + def render_error(message, status) + render json: { error: message }, status: status + end +end diff --git a/app/controllers/hearings/transcriptions_controller.rb b/app/controllers/hearings/transcriptions_controller.rb new file mode 100644 index 00000000000..352217cd4aa --- /dev/null +++ b/app/controllers/hearings/transcriptions_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class Hearings::TranscriptionsController < ApplicationController + include HearingsConcerns::VerifyAccess + + rescue_from ActiveRecord::RecordNotFound, with: :render_record_not_found + before_action :verify_transcription_user + + def next_transcription + transcription = Transcription.first_empty_transcription_file + render json: { id: transcription&.id, task_id: transcription&.task_id } + end +end diff --git a/app/controllers/hearings_application_controller.rb b/app/controllers/hearings_application_controller.rb index 09094f9def4..1866f37d1cc 100644 --- a/app/controllers/hearings_application_controller.rb +++ b/app/controllers/hearings_application_controller.rb @@ -11,6 +11,7 @@ class HearingsApplicationController < ApplicationController before_action :verify_access_to_reader_or_hearings, only: [:show_hearing_worksheet_index] before_action :verify_view_hearing_schedule_access, only: [:index] before_action :check_vso_representation, only: [:show_hearing_details_index] + before_action :verify_transcription_user, only: [:transcription_file_dispatch] def set_application RequestStore.store[:application] = "hearings" @@ -32,6 +33,10 @@ def index render "hearings/index" end + def transcription_file_dispatch + render "hearings/index" + end + def check_hearings_out_of_service render "out_of_service", layout: "application" if Rails.cache.read("hearings_out_of_service") end diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index 25dc678b3c0..c26f9185b9c 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -186,6 +186,24 @@ def request_hearing_disposition_change } end + def upload_transcription_to_vbms + return unless task.type == "ReviewTranscriptTask" && task.status == "in_progress" + + new_instructions = task.instructions.push(params[:task][:instructions]) + + Transcription.where(task_id: task.id).update_all( + updated_by_id: current_user.id, + uploaded_to_vbms_date: Time.zone.now, + updated_at: Time.zone.now + ) + + Task.where(id: task.id).update_all( + instructions: new_instructions, + status: Constants.TASK_STATUSES.completed, + closed_at: Time.zone.now + ) + end + private def send_initial_notification_letter diff --git a/app/controllers/test/users_controller.rb b/app/controllers/test/users_controller.rb index 4f28830ee7f..827a0647f3a 100644 --- a/app/controllers/test/users_controller.rb +++ b/app/controllers/test/users_controller.rb @@ -54,7 +54,8 @@ class Test::UsersController < ApplicationController { name: "Hearings", links: { - current_schedule: "/hearings/schedule" + current_schedule: "/hearings/schedule", + transcription_file_dispatch_queue: "/hearings/transcription_files" } }, { diff --git a/app/jobs/hearings/create_bill_of_materials_job.rb b/app/jobs/hearings/create_bill_of_materials_job.rb new file mode 100644 index 00000000000..f38e39c1d87 --- /dev/null +++ b/app/jobs/hearings/create_bill_of_materials_job.rb @@ -0,0 +1,244 @@ +# frozen_string_literal: true + +module Hearings + class CreateBillOfMaterialsJob < CaseflowJob + include Hearings::EnsureCurrentUserIsSet + + class BomFileUploadError < StandardError; end + + class BomFile + # Purpose: We cannot track a BoM file using the transcription_files table since it does not belong to a hearing. + # ....... This P.O.R.O. will allow us to use TranscriptionFileUpload to upload the BoM file to s3 by passing in + # ....... an object shaped similarly to a TranscriptionFile record. + attr_reader :file_name, :file_type, :tmp_location + + def initialize(file_path) + @file_name = File.basename(file_path) + @file_type = "json" + @tmp_location = file_path + end + + def update_status!(*args); end + end + + retry_on(TranscriptionFileUpload::FileUploadError) do |job, _exception| + job.clean_up_tmp_file + fail BomFileUploadError + end + + def perform(work_order) + @work_order = work_order + ensure_current_user_is_set + bom_hash = create_bom_hash + save_json_file(bom_hash) + upload_to_s3!(@bom_file_path) + true + end + + def clean_up_tmp_file + File.delete(@bom_file_path) if @bom_file_path + end + + private + + def create_bom_hash + { + bomFormat: "CycloneDX", + specVersion: "1.3", + serialNumber: "urn:uuid:#{SecureRandom.uuid}", + version: 1, + metadata: { + timestamp: Time.zone.now, + tools: metadata_tools, + component: metadata_component, + supplier: metadata_supplier + }, + components: components(transcription_files(@work_order[:hearings])) + } + end + + def metadata_tools + { + vendor: "CASEFLOW", + name: self.class.to_s, + version: "1.0", + hashes: hashes_for_metadata_tools, + authors: [ + { + name: "CASEFLOW", + email: "OITAppealsHelpDesk@va.gov" + } + ] + } + end + + def hashes_for_metadata_tools + job_file_path = Rails.root.join("app", "jobs", "hearings", "create_bill_of_materials_job.rb") + [ + { + alg: "MD5", + content: create_md5_hash(job_file_path) + } + ] + end + + def metadata_component + { + type: "file", + name: "bva_workorder", + version: "1.0.0.x" + } + end + + def metadata_supplier + { + name: "CASEFLOW", + url: "appeals.cf.uat.ds.va.gov/hearings", + contact: { + name: "OITAppealsHelpDesk", + email: "OITAppealsHelpDesk@va.gov" + } + } + end + + def file_description(file_type) + case file_type + when "mp3" + "Audio file for appeal" + when "rtf" + "Word compatible file created from supplied vtt file" + end + end + + def components(files) + components = files.map do |file| + { + :type => "file", + "bom-ref" => bom_ref(file), + :author => file.file_type == "mp3" ? "Webex" : "CASEFLOW", + :name => file.file_name, + :version => "1.0.0.x", + :description => file_description(file.file_type), + :hashes => component_hashes(file.file_name), + :licenses => licenses(file.file_name), + :purl => purl(file) + } + end + components << work_order_file_component + end + + def work_order_file_component + work_order_file_name = @work_order[:work_order_name] + ".xls" + { + :type => "file", + "bom-ref" => bom_ref(work_order_file_name), + :author => "CASEFLOW", + :name => work_order_file_name, + :version => "1.0.0.x", + :description => "Work order file", + :hashes => component_hashes(work_order_file_name), + :licenses => licenses(work_order_file_name), + :purl => purl(work_order_file_name) + } + end + + def bom_ref(file) + bucket = "vaec-appeals-caseflow-#{Rails.deploy_env}" + base = "https://#{bucket}.s3.#{ENV['AWS_REGION']}.amazonaws.com" + case file + when TranscriptionFile + "#{base}/#{file.aws_link}" + when String + # for work order xls + "#{base}/#{bucket}/transcription_text/#{file}" + end + end + + def component_hashes(file_name) + base_path = Rails.root.join("tmp", "transcription_files") + tmp_dirs = { mp3: base_path.join("mp3"), rtf: base_path.join("rtf"), xls: base_path.join("xls") } + extension = file_name.split(".").last.to_sym + [ + { + alg: "MD5", + content: create_md5_hash(tmp_dirs[extension].join(file_name)) + } + ] + end + + def transcription_files(hearing_lookups) + standard_lookups = hearing_lookups.find_all { |hash| hash[:hearing_type] == Hearing.name } + legacy_lookups = hearing_lookups.find_all { |hash| hash[:hearing_type] == LegacyHearing.name } + ActiveRecord::Base.transaction do + files = transcription_file_query_builder(Hearing.name, standard_lookups.pluck(:hearing_id)) + files_for_legacy = transcription_file_query_builder(LegacyHearing.name, legacy_lookups.pluck(:hearing_id)) + files + files_for_legacy + end + end + + def transcription_file_query_builder(hearing_type, hearing_ids) + TranscriptionFile.where( + hearing_type: hearing_type, + hearing_id: hearing_ids, + file_type: %w(mp3 rtf), + file_status: "Successful upload (AWS)" + ) + end + + def save_json_file(hash) + tmp_path = Rails.root.join("tmp", "transcription_files", "json") + FileUtils.mkdir_p(tmp_path) unless File.directory?(tmp_path) + file_name = "#{@work_order[:work_order_name].sub('BVA', 'BOM')}.json" + file_path = tmp_path.join(file_name) + File.open(file_path, "w") { |f| f.write(JSON.pretty_generate(hash)) } + @bom_file_path = file_path.to_s + end + + def create_md5_hash(file_path) + Digest::MD5.file(file_path) + end + + def licenses(file_name, id = "MIT") + extension = file_name.split(".").last + content_type = case extension + when "mp3" then "audio/mp3" + when "rtf" then "application/rtf" + when "xls" + id = "GPL-3.0" + "application/vnd.ms-excel" + end + generate_license_hash(id, content_type, extension) + end + + def generate_license_hash(id, content_type, extension) + [ + { + license: { + id: id, + text: { + contentType: content_type, + encoding: extension + } + } + } + ] + end + + def purl(file) + case file + when TranscriptionFile + name_without_extension = file.file_name.split(".")[0] + "pkg:#{file.file_type}/#{name_without_extension}@1.0.0" + when String + # for work order xls since there is no associated db record + name_without_extension = file.split(".")[0] + "pkg:xls/#{name_without_extension}@1.0.0" + end + end + + def upload_to_s3!(file_path) + ruby_object = BomFile.new(file_path) + TranscriptionFileUpload.new(ruby_object).call + end + end +end diff --git a/app/jobs/hearings/download_transcription_file_job.rb b/app/jobs/hearings/download_transcription_file_job.rb index 8d04372928d..ed38599bece 100644 --- a/app/jobs/hearings/download_transcription_file_job.rb +++ b/app/jobs/hearings/download_transcription_file_job.rb @@ -187,7 +187,7 @@ def docket_number # # Returns: TranscriptionFile object def find_or_create_transcription_file(file_name_arg = file_name) - TranscriptionFile.find_or_create_by( + Hearings::TranscriptionFile.find_or_create_by( file_name: file_name_arg, hearing_id: hearing.id, hearing_type: hearing.class.name, diff --git a/app/jobs/hearings/monitor_box_job.rb b/app/jobs/hearings/monitor_box_job.rb new file mode 100644 index 00000000000..b555852c30c --- /dev/null +++ b/app/jobs/hearings/monitor_box_job.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +# This job is responsible for retrieving newly added transcription files from VA Box.com folders. +# +# It connects to the Box.com API via {ExternalApi::VaBoxService}, queries transcription contractor folders, and +# returns a list of files that have been added since the last check. +# This class is intended to be used for hourly checks triggered by appeals-lambda +# to handle new files from Box.com after being uploaded by transcription contractors. +# The parent folder id is stored as an environment variable. +# +# Example usage: +# +# BoxFileRetrievalJob.perform_later +# +# The `perform_later` method will enqueue the job to be performed asynchronously. +class Hearings::MonitorBoxJob < ApplicationJob + queue_as :low_priority + + attr_reader :box_service + + def initialize + @box_service = ExternalApi::VaBoxService.new + end + + def perform + files = poll_box_dot_com_for_new_files + files.count > 0 ? download_box_files(files) : true + rescue StandardError => error + log_error(error) + raise error + end + + def poll_box_dot_com_for_new_files + files = net_new_files + + files_with_permitted_keys(files) + end + + def download_box_files(files) + Hearings::VaBoxDownloadJob.perform_later(files) + end + + private + + def box_parent_folder_id + ENV["BOX_PARENT_FOLDER_ID"] + end + + def files_with_permitted_keys(files) + permitted_keys = [:name, :id, :created_at, :modified_at] + files.map { |hash| hash.slice!(*permitted_keys) } + files + end + + def net_new_files + cursor = most_recent_returned_file_time || 1.hour.ago.utc + + files = all_files_from_box_subfolders&.find_all do |file| + Time.parse(file[:created_at]).utc > cursor + end + + find_webex_formatted_files(files) + end + + def most_recent_returned_file_time + Hearings::TranscriptionFile.maximum(:date_returned_box) + end + + def find_webex_formatted_files(files) + files.find_all do |file| + file[:name].match?(webex_file_naming_convention) + end + end + + def webex_file_naming_convention + # Returns a regular expression to find files using our naming convention transcription files from Webex hearings. + # This is important since the Box.com folders will also contain transcription files from Pexip hearings + # that we are not interested in for this workflow. + # + # - For transcription files, "__." + # - ex. "123456-7_XXXXX_Hearing.XXX" + # - ex. "1234567_XXXXX_LegacyHearing.XXX" + # - For work orders, "--.xls" + # - ex. "BVA-2024-0001.xls" + /(^[^_a-z]+_[^_]+_((Hearing)|(LegacyHearing))+.\w+\z)|(BVA-\d+-\d+.xls)/ + end + + def all_files_from_box_subfolders + folder_ids.map do |id| + box_service.get_folder_items( + folder_id: id, + item_type: "file", + query_string: "sort=date&direction=desc&fields=name,created_at,modified_at" + ) + end.flatten + end + + def folder_ids + folders = box_service&.get_folder_items(folder_id: ENV["BOX_PARENT_FOLDER_ID"]) + folders.map { |folder| folder[:id] if folder[:name].downcase.include?("return") }.compact + end +end diff --git a/app/jobs/hearings/update_transcription_package_status_job.rb b/app/jobs/hearings/update_transcription_package_status_job.rb new file mode 100644 index 00000000000..ba1653cac21 --- /dev/null +++ b/app/jobs/hearings/update_transcription_package_status_job.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Hearings::UpdateTranscriptionPackageStatusJob < ApplicationJob + queue_with_priority :low_priority + + def perform + update_overdue_for_assigned_packages + update_overdue_for_completed_packages + update_failed_retrieval_packages + end + + private + + def update_overdue_for_assigned_packages + TranscriptionPackage.where(status: COPY::TRANSCRIPTION_STATUS_SENT_FILTER_OPTION) + .where("expected_return_date > ?", Time.zone.now) + .update_all(status: COPY::TRANSCRIPTION_STATUS_OVERDUE_FILTER_OPTION) + end + + def update_overdue_for_completed_packages + TranscriptionPackage.where(status: COPY::TRANSCRIPTION_DISPATCH_COMPLETED_TAB) + .where("returned_at > ?", Time.zone.now) + .update_all(status: COPY::TRANSCRIPTION_STATUS_OVERDUE_FILTER_OPTION) + end + + def update_failed_retrieval_packages + TranscriptionPackage.where(status: COPY::TRANSCRIPTION_STATUS_RETRIEVAL_FAILURE_FILTER_VALUE) + .update_all(status: COPY::TRANSCRIPTION_STATUS_RETRIEVAL_FAILURE_FILTER_OPTION) + end +end diff --git a/app/jobs/hearings/va_box_download_job.rb b/app/jobs/hearings/va_box_download_job.rb new file mode 100644 index 00000000000..972358ba89f --- /dev/null +++ b/app/jobs/hearings/va_box_download_job.rb @@ -0,0 +1,207 @@ +# frozen_string_literal: true + +class Hearings::VaBoxDownloadJob < CaseflowJob + queue_as :low_priority + S3_BUCKET = "vaec-appeals-caseflow" + + class BoxDownloadError < StandardError; end + class BoxDownloadJobFileUploadError < StandardError; end + + def perform(files_info) + @all_paths = [] + box_service = ExternalApi::VaBoxService.new + + files_info.collect do |current_file| + file_name = current_file[:name] + tmp_folder = select_folder(file_name) + file_extension = extract_file_extension(file_name) + begin + box_service.download_file(current_file[:id], tmp_folder) + @all_paths << tmp_folder + if file_extension == "zip" + unzip_file(tmp_folder, current_file) + else + upload_s3_modified_transcription_file(tmp_folder, current_file) + end + end + end + cleanup_tmp_files + end + + private + + def extract_file_extension(file_name) + File.extname(file_name).delete(".").to_s + end + + def update_database(current_information, file_status) + transcription_records = Hearings::TranscriptionFile.where( + hearing_id: current_information["id"].to_i, + hearing_type: current_information["hearing_type"], + file_type: current_information["file_type"] + ) + + current_extension = extract_file_extension(current_information["file_name"]) + transcript_text_path = (current_extension == "pdf") ? "transcript_pdf" : "transcript_text" + aws_link = create_link_aws(current_information, file_status, transcript_text_path) + + if transcription_records.count > 0 + update_transcription_file_record(transcription_records, current_information, file_status, aws_link) + else + create_transcription_file_record(current_information, file_status, aws_link) + end + + modified_task_tree(current_information, file_status) + rescue ActiveRecord::RecordInvalid => error + Rails.logger.error "Failed to create transcription file: #{error.message}" + end + + def modified_task_tree(current_information, file_status) + if current_information["hearing_type"] == "Hearing" + current_hearing = Hearing.find(current_information["id"]) + if current_hearing.disposition == "held" + create_review_transcript_task(current_hearing.appeal_id, file_status) + end + else + current_legacy_hearing = LegacyHearing.find(current_information["id"].to_i) + current_vacols_id = current_legacy_hearing.current_vacols_id + current_appeal_id = current_legacy_hearing.appeal_id + hearing_record = VACOLS::CaseHearing.for_appeals(current_vacols_id) + if hearing_record.hearing_disp == "H" + create_review_transcript_task(current_appeal_id, file_status) + end + end + end + + def create_review_transcript_task(appeal_id, file_status) + if file_status == "Successful upload (AWS)" + appeal = Appeal.find(appeal_id) + root_task = RootTask.find_or_create_by!(appeal: appeal) + ReviewTranscriptTask.create!( + appeal: appeal, + parent: root_task + ) + end + end + + def create_link_aws(current_information, file_status, transcript_text_path) + if file_status == "Failed upload (AWS)" + nil + else + "vaec-appeals-caseflow-test/#{transcript_text_path}/#{current_information['file_name']}" + end + end + + def create_transcription_file_record(current_information, file_status, aws_link) + if current_information["hearing_type"] == "Hearing" + transcription_id = Hearing.find(current_information["id"]).transcription&.id + else + current_legacy_hearing = LegacyHearing.find(current_information["id"].to_i) + current_vacols_id = current_legacy_hearing.current_vacols_id + hearings = VACOLS::CaseHearing.for_appeals(current_vacols_id) + transcription_id = hearings.taskno + end + + unless transcription_id + fail StandardError, "Transcription for #{current_information['hearing_type']} \ + - ID: #{current_information['id']} does not exist." + end + + add_transcription_file_record(current_information, file_status, aws_link, transcription_id) + end + + def add_transcription_file_record(current_information, file_status, aws_link, transcription_id) + Hearings::TranscriptionFile.create!( + hearing_id: current_information["id"], + hearing_type: current_information["hearing_type"], + docket_number: current_information["docket_number"], + file_name: current_information["file_name"], + file_type: current_information["file_type"], + file_status: file_status, + transcription_id: transcription_id, + date_upload_aws: Time.zone.now, + aws_link: aws_link + ) + end + + def update_transcription_file_record(transcription_records, current_information, file_status, aws_link) + transcription_records.each do |tr| + tr.update!( + date_upload_aws: Time.zone.now, + updated_at: Time.zone.now, + date_returned_box: current_information["date_returned_box"], + file_status: file_status, + aws_link: aws_link + ) + end + end + + def select_folder(file_name) + file_extension = extract_file_extension(file_name) + current_path = Rails.root.join("tmp", "file_from_box", file_extension.to_s, file_name.to_s) + FileUtils.mkdir_p(File.dirname(current_path)) unless Dir.exist?(file_extension) + current_path + end + + def unzip_file(tmp_folder, current_file) + file_extension = extract_file_extension(current_file[:name]) + Zip::File.open(tmp_folder) do |zip_file| + list_files = [] + zip_file.each do |f| + f_path = Rails.root.join("tmp", "file_from_box", file_extension.to_s, f.name.to_s) + FileUtils.mkdir_p(File.dirname(f_path)) + zip_file.extract(f, f_path) unless File.exist?(f_path) + list_files << f_path unless File.directory?(f_path.to_s) + end + list_files.each do |my_file| + @all_paths << my_file.to_s + upload_s3_modified_transcription_file(my_file.to_s, current_file) + end + end + end + + def upload_to_s3(tmp_folder, file_name) + begin + S3Service.store_file(s3_location(file_name), tmp_folder, :filepath) + rescue StandardError => error + Rails.logger.error "Error to upload #{file_name} to S3: #{error.message}" + raise BoxDownloadJobFileUploadError + end + end + + def s3_location(file_name) + transcript_text_path = (extract_file_extension(file_name) == "pdf") ? "transcript_pdf" : "transcript_text" + folder_name = (Rails.deploy_env == :prod) ? S3_BUCKET : "#{S3_BUCKET}-#{Rails.deploy_env}" + "#{folder_name}/#{transcript_text_path}/#{file_name}" + end + + def get_information(file_name, current_file) + info = {} + info["id"] = file_name.split("_")[1] + info["hearing_type"] = file_name.split("_")[2].split(".")[0] + info["docket_number"] = file_name.split("_")[0] + info["file_type"] = File.extname(file_name).delete(".").to_s + info["date_returned_box"] = current_file[:created_at] + info["file_name"] = file_name + info + end + + def upload_s3_modified_transcription_file(file_path, current_file) + begin + if File.exist?(file_path) + file_name = File.basename(file_path) + s3_upload_result = upload_to_s3(file_path, file_name) + file_status = (s3_upload_result == file_path) ? "Failed upload (AWS)" : "Successful upload (AWS)" + info = get_information(file_name, current_file) + update_database(info, file_status) + else + fail "The file does not exist" + end + end + end + + def cleanup_tmp_files + @all_paths&.each { |path| File.delete(path) if File.exist?(path) } + Rails.logger.info("Cleaned up the following files from tmp: #{@all_paths}") + end +end diff --git a/app/jobs/hearings/va_box_upload_job.rb b/app/jobs/hearings/va_box_upload_job.rb new file mode 100644 index 00000000000..f159a5a3450 --- /dev/null +++ b/app/jobs/hearings/va_box_upload_job.rb @@ -0,0 +1,179 @@ +# frozen_string_literal: true + +class Hearings::VaBoxUploadJob < CaseflowJob + include Shoryuken::Worker + queue_as :low_priority + include Hearings::SendTranscriptionIssuesEmail + + S3_BUCKET = "vaec-appeals-caseflow" + + shoryuken_options retry_intervals: [3.seconds, 30.seconds, 5.minutes, 30.minutes, 2.hours, 5.hours] + + class BoxUploadError < StandardError; end + + retry_on StandardError, wait: :exponentially_longer do |job, exception| + job.cleanup_tmp_files + error_details = { error: { type: "upload", message: exception.message }, provider: "Box" } + job.send_transcription_issues_email(error_details) unless job.email_sent?(:upload) + job.mark_email_sent(:upload) + fail BoxUploadError + end + + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity + def perform(file_info, box_folder_id) + @all_paths = [] + @email_sent_flags = { transcription_package: false, child_folder_id: false, upload: false } + + box_service = ExternalApi::VaBoxService.new + + file_info[:hearings].each_with_index do |hearing, index| + begin + transcription_package = find_transcription_package(hearing) + unless transcription_package + error_details = { + error: { + type: "transcription_package", + message: "Transcription package not found for hearing ID: #{hearing[:hearing_id]}" + }, + provider: "Box" + } + send_transcription_issues_email(error_details) unless email_sent?(:transcription_package) + mark_email_sent(:transcription_package) + next + end + s3_file_path = transcription_package.aws_link_zip + contractor_name = file_info[:contractor_name] + child_folder_id = box_service.get_child_folder_id(box_folder_id, contractor_name) + unless child_folder_id + error_details = { + error: { + type: "child_folder_id", + message: "Child folder ID not found for contractor name: #{contractor_name}" + }, + provider: "Box" + } + send_transcription_issues_email(error_details) unless email_sent?(:child_folder_id) + mark_email_sent(:child_folder_id) + break + end + + # Download file from S3 + local_file_path = download_file_from_s3(s3_file_path) + + if index == 0 + upsert_to_box(box_service, local_file_path, child_folder_id, transcription_package, file_info, hearing) + else + create_to_box(box_service, local_file_path, child_folder_id, transcription_package, file_info, hearing) + end + + # Update transcription files after successful upload + update_transcription_files(hearing, file_info, transcription_package) + rescue StandardError => error + log_error(error, extra: { transcription_package_id: transcription_package&.id }) + error_details = { error: { type: "upload", message: error.message }, provider: "Box" } + send_transcription_issues_email(error_details) unless email_sent?(:upload) + mark_email_sent(:upload) + next + end + end + end + # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + + private + + def find_transcription_package(hearing) + if hearing[:hearing_type] == "LegacyHearing" + TranscriptionPackageLegacyHearing.find_by(legacy_hearing_id: hearing[:hearing_id])&.transcription_package + else + TranscriptionPackageHearing.find_by(hearing_id: hearing[:hearing_id])&.transcription_package + end + end + + def download_file_from_s3(s3_path) + local_path = Rails.root.join("tmp", "transcription_files", File.basename(s3_path)) + Caseflow::S3Service.fetch_file(s3_path, local_path) + @all_paths << local_path + Rails.logger.info("File successfully downloaded from S3: #{local_path}") + local_path + end + + # rubocop:disable Metrics/ParameterLists + def upsert_to_box(box_service, local_file_path, child_folder_id, transcription_package, file_info, hearing) + ActiveRecord::Base.transaction do + box_service.upload_file(local_file_path, child_folder_id) + Rails.logger.info("File successfully uploaded to Box folder ID: #{child_folder_id}") + transcription_package.update!( + date_upload_box: Time.current, + status: "Successful Upload (BOX)", + task_number: file_info[:work_order_name], + expected_return_date: file_info[:return_date], + updated_by_id: RequestStore[:current_user].id + ) + transcription = ::Transcription.find_or_initialize_by(task_number: file_info[:work_order_name]) + transcription.update!( + expected_return_date: file_info[:return_date], + hearing_id: hearing[:hearing_id], + sent_to_transcriber_date: Time.current, + transcriber: file_info[:contractor_name], + transcription_contractor_id: transcription_package.contractor_id, + updated_by_id: RequestStore[:current_user].id + ) + end + end + # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength + + # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength + def create_to_box(box_service, local_file_path, child_folder_id, transcription_package, file_info, hearing) + ActiveRecord::Base.transaction do + box_service.upload_file(local_file_path, child_folder_id) + Rails.logger.info("File successfully uploaded to Box folder ID: #{child_folder_id}") + transcription_package.update!( + date_upload_box: Time.current, + status: "Successful Upload (BOX)", + task_number: file_info[:work_order_name], + expected_return_date: file_info[:return_date], + updated_by_id: RequestStore[:current_user].id + ) + transcription = ::Transcription.new( + task_number: file_info[:work_order_name], + expected_return_date: file_info[:return_date], + hearing_id: hearing[:hearing_id], + hearing_type: hearing[:hearing_type], + sent_to_transcriber_date: Time.current, + transcriber: file_info[:contractor_name], + transcription_contractor_id: transcription_package.contractor_id, + updated_by_id: RequestStore[:current_user].id + ) + transcription.save! + end + end + # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength + + def update_transcription_files(hearing, file_info, transcription_package) + Hearings::TranscriptionFile.where( + hearing_id: hearing[:hearing_id], hearing_type: hearing[:hearing_type] + ).update_all( + date_upload_box: Time.current, + updated_by_id: RequestStore[:current_user].id, + expected_return_date: file_info[:return_date], + sent_to_transcriber_date: Time.current, + task_number: file_info[:work_order_name], + transcriber: file_info[:contractor_name], + transcription_contractor_id: transcription_package.contractor_id, + transcription_status: "sent" + ) + end + + def cleanup_tmp_files + @all_paths&.each { |path| File.delete(path) if File.exist?(path) } + Rails.logger.info("Cleaned up the following files from tmp: #{@all_paths}") + end + + def email_sent?(type) + @email_sent_flags[type] + end + + def mark_email_sent(type) + @email_sent_flags[type] = true + end +end diff --git a/app/jobs/hearings/work_order_file_job.rb b/app/jobs/hearings/work_order_file_job.rb new file mode 100644 index 00000000000..7cbef96aa0c --- /dev/null +++ b/app/jobs/hearings/work_order_file_job.rb @@ -0,0 +1,138 @@ +# frozen_string_literal: true + +class Hearings::WorkOrderFileJob < CaseflowJob + queue_with_priority :low_priority + + S3_BUCKET = "vaec-appeals-caseflow" + TMP_FOLDER = Rails.root.join("tmp", "transcription_files", "xls") + + attr_reader :file_name, :file_path + + class WorkOrderFileUploadError < StandardError; end + + retry_on WorkOrderFileUploadError, wait: :exponentially_longer do |job, _exception| + job.send_failure_notification + false + end + + def initialize(*args) + super(*args) + @file_name = nil + @file_path = nil + end + + def perform(work_order) + work_book = create_spreadsheet(work_order) + write_to_workbook(work_book, work_order[:work_order_name]) + upload_to_s3(work_order[:work_order_name]) + true + end + + def send_failure_notification + WorkOrderFileIssuesMailer.send_notification + end + + private + + def create_spreadsheet(work_order) + workbook = Spreadsheet::Workbook.new + worksheet = workbook.create_worksheet + + worksheet.row(0).concat ["Work Order", work_order[:work_order_name]] + worksheet.row(2).concat ["Return Date", work_order[:return_date]] + worksheet.row(4).concat ["Contractor Name", work_order[:contractor]] + + create_table(work_order[:hearings], worksheet) + workbook + end + + def write_to_workbook(workbook, work_order_name) + @file_name = "#{work_order_name}.xls" + @file_path = TMP_FOLDER.join(@file_name) + workbook.write(@file_path) + end + + def create_table(hearings_data, worksheet) + setup_worksheet_header(worksheet) + hearings = fetch_hearings(hearings_data) + populate_table_data(hearings, worksheet) + end + + def setup_worksheet_header(worksheet) + header_format = Spreadsheet::Format.new weight: :bold, border: :thin + columns = ["DOCKET NUMBER", "FIRST NAME", "LAST NAME", "TYPES", "HEARING DATE", "RO", "VLJ", "APPEAL TYPE"] + set_border_format(worksheet.row(6), header_format) + worksheet.row(6).concat(columns) + end + + def fetch_hearings(hearings_data) + Hearing.includes(:appeal).where(id: hearings_data.pluck(:hearing_id)) + end + + def populate_table_data(hearings, worksheet) + table_data = hearings.map { |hearing| format_hearing_data(hearing) } + append_table_data_to_worksheet(table_data, worksheet) + end + + def format_hearing_data(hearing) + begin + appeal = hearing.appeal + rescue StandardError + Rails.logger.error "Work Order File Job failed to fetch appeal from hearing #{hearing.id}" + return default_hearing_data + end + + hearing_date = format_hearing_date(appeal) + [ + appeal.docket_number, + hearing.appellant_first_name, + hearing.appellant_last_name, + appeal.type, + hearing_date, + hearing.regional_office.name, + hearing.judge.full_name, + appeal_type(appeal) + ] + end + + def default_hearing_data + ["N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"] + end + + def format_hearing_date(appeal) + appeal.hearing_day_if_schedueled&.strftime("%m/%d/%Y") || "" + end + + def appeal_type(appeal) + appeal.is_a?(LegacyAppeal) ? "Legacy" : "AMA" + end + + def append_table_data_to_worksheet(table_data, worksheet) + table_data.each_with_index do |row, index| + worksheet.row(7 + index).replace(row) + end + end + + def set_border_format(row, row_format) + (0..7).each { |col_index| row.set_format(col_index, row_format) } + end + + def upload_to_s3(work_order_name) + begin + S3Service.store_file(s3_location, @file_path, :filepath) + rescue StandardError => error + Rails.logger.error "Work Order File Job failed to upload Work Order #{work_order_name} to S3: #{error.message}" + cleanup_tmp_file + raise WorkOrderFileUploadError + end + end + + def s3_location + folder_name = (Rails.deploy_env == :prod) ? S3_BUCKET : "#{S3_BUCKET}-#{Rails.deploy_env}" + "#{folder_name}/transcript_text/#{@file_name}" + end + + def cleanup_tmp_file + File.delete(@file_path) if File.exist?(@file_path) + end +end diff --git a/app/jobs/hearings/zip_and_upload_transcription_files_job.rb b/app/jobs/hearings/zip_and_upload_transcription_files_job.rb new file mode 100644 index 00000000000..6017b82fe80 --- /dev/null +++ b/app/jobs/hearings/zip_and_upload_transcription_files_job.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +module Hearings + class ZipAndUploadTranscriptionFilesJob < CaseflowJob + include EnsureCurrentUserIsSet + + queue_as :low_priority + attr_reader :tmp_files_to_cleanup + + class ZipFileUploadError < StandardError; end + + retry_on TranscriptionFileUpload::FileUploadError, wait: :exponentially_longer do |job, _exception| + job.cleanup_tmp_files + fail ZipFileUploadError + end + + def perform(hearing_lookup_hashes) + @tmp_files_to_cleanup = [] + ensure_current_user_is_set + hearing_lookup_hashes.each do |hearing_lookup_hash| + process_hearing(hearing_lookup_hash) + end + true + end + + def cleanup_tmp_files + @tmp_files_to_cleanup&.each { |path| File.delete(path) if File.exist?(path) } + Rails.logger.info("Cleaned up the following files from tmp: #{@tmp_files_to_cleanup}") + end + + private + + ALLOWED_HEARING_CLASSES = { + "Hearing" => Hearing, + "LegacyHearing" => LegacyHearing + }.freeze + + TRANSCRIPTION_FILE_TYPES = %w[mp3 rtf].freeze + + def process_hearing(hearing_lookup_hash) + hearing = fetch_hearing(hearing_lookup_hash) + tmp_file_paths = fetch_transcription_files(hearing) + zip_file_path = create_zip_file(tmp_file_paths, hearing) + formatted_zip_path = rename_before_upload(zip_file_path) + @tmp_files_to_cleanup += tmp_file_paths + [formatted_zip_path] + create_and_upload_transcription_file(hearing, formatted_zip_path) + end + + def fetch_hearing(hearing_lookup_hash) + hearing_class = ALLOWED_HEARING_CLASSES[hearing_lookup_hash[:hearing_type]] + hearing_class.find(hearing_lookup_hash[:hearing_id]) + end + + def fetch_transcription_files(hearing) + hearing.transcription_files.where(file_type: TRANSCRIPTION_FILE_TYPES).map(&:fetch_file_from_s3!) + end + + def create_zip_file(file_paths, hearing) + zip_file_name = generate_zip_file_name(hearing) + Zip::File.open(zip_file_name, create: true) do |zip_file| + file_paths.each { |path| zip_file.add(File.basename(path), path) } + end + zip_file_name + end + + def generate_zip_file_name(hearing) + File.join( + Rails.root, "tmp", "transcription_files", "zip", "#{hearing.docket_number}_#{hearing.id}_#{hearing.class}.zip" + ) + end + + def rename_before_upload(zip_file_path) + checksum = xor_checksum(zip_file_path) + creation_date = format_creation_date(zip_file_path) + new_path = zip_file_path.sub(".", "-#{checksum}-#{creation_date}.") + File.rename(zip_file_path, new_path) + new_path + end + + def xor_checksum(file_path) + checksum = File.open(file_path, "rb").each_byte.reduce(0, :^) + checksum.to_s(16) + end + + def format_creation_date(file_path) + File.ctime(file_path).strftime("%Y%m%d") + end + + def create_and_upload_transcription_file(hearing, file_path) + Hearings::TranscriptionFile.create!( + file_name: File.basename(file_path), + hearing_id: hearing.id, + hearing_type: hearing.class.name, + docket_number: hearing.docket_number, + file_type: "zip", + created_by_id: RequestStore[:current_user].id + ).upload_to_s3! + rescue ActiveRecord::RecordInvalid => error + Rails.logger.error "Failed to create transcription file: #{error.message}" + end + end +end diff --git a/app/jobs/hearings/zip_and_upload_transcription_package_job.rb b/app/jobs/hearings/zip_and_upload_transcription_package_job.rb new file mode 100644 index 00000000000..3d15c88924e --- /dev/null +++ b/app/jobs/hearings/zip_and_upload_transcription_package_job.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +class Hearings::ZipAndUploadTranscriptionPackageJob < CaseflowJob + include Hearings::SendTranscriptionIssuesEmail + queue_as :low_priority + S3_BUCKET = "vaec-appeals-caseflow" + class ZipTranscriptionPackageUploadError < StandardError; end + + retry_on TranscriptionFileUpload::FileUploadError, wait: :exponentially_longer do |job, _exception| + job.cleanup_tmp_files + details_hash = { error: { type: "upload" }, provider: "S3" } + error_details = job.build_error_details(exception, details_hash) + job.send_transcription_issues_email(error_details) + fail ZipTranscriptionPackageUploadError + end + + def perform(work_order) + @work_order = work_order + @all_paths = [] + + ActiveRecord::Base.transaction do + fetch_files + transcription_package_tmp = create_master_file_zip + upload_transcription_package_to_s3(transcription_package_tmp) + save_transcription_package_in_database(transcription_package_tmp) + end + end + + private + + def fetch_files + @work_order_tmp_path = fetch_file("xls", @work_order[:work_order_name]) + @bom_file_tmp_path = fetch_file("json", @work_order[:work_order_name].sub("BVA", "BOM")) + @transcription_files_zip_tmp_paths = fetch_all_files("zip") + end + + def fetch_file(extension, filename) + path = Rails.root.join("tmp", "transcription_files", extension, "#{filename}.#{extension}") + @all_paths << path + path + end + + def fetch_all_files(extension) + Dir.glob(Rails.root.join("tmp", "transcription_files", extension, "*.#{extension}")).tap do |files| + @all_paths.concat(files) + end + end + + def create_master_file_zip + zip_master_file_path = generate_zip_master_file_name + Zip::File.open(zip_master_file_path, create: true) do |zip_file| + zip_file.add(File.basename(@work_order_tmp_path), @work_order_tmp_path) + zip_file.add(File.basename(@bom_file_tmp_path), @bom_file_tmp_path) + @transcription_files_zip_tmp_paths.each { |path| zip_file.add(File.basename(path), path) } + end + rename_before_upload(zip_master_file_path) + end + + def generate_zip_master_file_name + File.join( + Rails.root, "tmp", "transcription_files", "zip", "master.zip" + ) + end + + def rename_before_upload(zip_master_file_path) + checksum = xor_checksum(zip_master_file_path) + new_name = File.basename(@work_order_tmp_path).sub("BVA", "BVA#{checksum}").split(".")[0] + new_path = zip_master_file_path.sub("master", new_name) + File.rename(zip_master_file_path, new_path) + @all_paths << new_path + new_path + end + + def xor_checksum(zip_master_file) + checksum = File.open(zip_master_file, "rb").each_byte.reduce(0, :^) + checksum.to_s(16) + end + + def upload_transcription_package_to_s3(transcription_package_tmp) + transcription_package_name = File.basename(transcription_package_tmp) + begin + S3Service.store_file(s3_location(transcription_package_tmp), transcription_package_tmp, :filepath) + Rails.logger.info("File successfully uploaded to S3 location") + rescue StandardError => error + Rails.logger.error "Transcription Package Job failed to upload Transcription Package + #{transcription_package_name} to S3: #{error.message}" + raise TranscriptionFileUpload + end + end + + def s3_location(file_path) + folder_name = Rails.env.production? ? S3_BUCKET : "#{S3_BUCKET}-#{Rails.env}" + "#{folder_name}/transcript_text/#{File.basename(file_path)}" + end + + def save_transcription_package_in_database(transcription_package_tmp) + ::TranscriptionPackage.create!( + aws_link_zip: s3_location(transcription_package_tmp), + aws_link_work_order: s3_location(@work_order_tmp_path), + created_by_id: RequestStore[:current_user].id, + status: "Successful upload (AWS)", + task_number: @work_order[:work_order_name], + contractor_id: ::TranscriptionContractor.find_by(name: @work_order[:contractor_name])&.id + ) + rescue ActiveRecord::RecordInvalid => error + Rails.logger.error "Failed to create transcription file: #{error.message}" + end + + def cleanup_tmp_files + @all_paths&.each { |path| File.delete(path) if File.exist?(path) } + Rails.logger.info("Cleaned up the following files from tmp: #{@all_paths}") + end +end diff --git a/app/mailers/work_order_file_issues_mailer.rb b/app/mailers/work_order_file_issues_mailer.rb new file mode 100644 index 00000000000..458faa0ce07 --- /dev/null +++ b/app/mailers/work_order_file_issues_mailer.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# rubocop:disable Rails/ApplicationMailer +class WorkOrderFileIssuesMailer < ActionMailer::Base + default from: "Board of Veterans' Appeals " + layout "work_order_file_issues_mailer" + + MAIL_ADDRESSES = { + development: "Caseflow@test.com", + test: "Caseflow@test.com", + uat: "BID_Appeals_UAT@bah.com", + prodtest: "VHACHABID_Appeals_ProdTest@va.gov", + prod: "BVAHearingTeam@VA.gov" + }.freeze + + def send_notification + mail(subject: subject, to: to_mail) + end + + private + + def subject + "Caseflow unable to upload to AWS S3 bucket" + end + + def to_mail + MAIL_ADDRESSES[Rails.deploy_env] + end +end +# rubocop:enable Rails/ApplicationMailer diff --git a/app/models/hearing.rb b/app/models/hearing.rb index ae4cf65c02b..baacc6149ec 100644 --- a/app/models/hearing.rb +++ b/app/models/hearing.rb @@ -45,12 +45,13 @@ class Hearing < CaseflowRecord belongs_to :judge, class_name: "User" belongs_to :created_by, class_name: "User" has_one :transcription, -> { order(created_at: :desc) } + has_many :transcriptions, as: :hearing has_many :hearing_views, as: :hearing has_one :hearing_location, as: :hearing has_many :hearing_issue_notes has_many :email_events, class_name: "SentHearingEmailEvent" has_many :email_recipients, class_name: "HearingEmailRecipient" - has_many :transcription_files, as: :hearing + has_many :transcription_files, class_name: "Hearings::TranscriptionFile", as: :hearing class HearingDayFull < StandardError; end @@ -304,6 +305,41 @@ def serialized_email_events end end + def closest_regional_office_city + return nil unless hearing_day + + query = <<-SQL + SELECT closest_regional_office_city + FROM cached_appeal_attributes + WHERE closest_regional_office_key = '#{hearing_day.id}' + LIMIT 1 + SQL + result = ActiveRecord::Base.connection.execute(query) + result.first&.dig("closest_regional_office_city") + end + + def original_appeal_type + return nil unless appeal.aod_based_on_age + + appeal.type + end + + def mo_appeal_type + query = <<-SQL + SELECT appeal_type, granted + FROM advance_on_docket_motions + WHERE appeal_id = '#{appeal.id}' + LIMIT 1 + SQL + result = ActiveRecord::Base.connection.execute(query) + docket_motion = result.first + appeal_type = docket_motion&.dig("appeal_type") + granted = docket_motion&.dig("granted") + return nil unless granted + + appeal_type + end + private def update_appeal_states_on_hearing_create diff --git a/app/models/hearings/forms/hearing_update_form.rb b/app/models/hearings/forms/hearing_update_form.rb index b1a686b2692..9ea889b809c 100644 --- a/app/models/hearings/forms/hearing_update_form.rb +++ b/app/models/hearings/forms/hearing_update_form.rb @@ -13,8 +13,10 @@ class HearingUpdateForm < BaseHearingUpdateForm protected def update_hearing - Transcription.find_or_create_by(hearing: hearing) unless transcription_attributes.blank? - # a new HearingLocation is created here if hearing_location_attributes is present + if transcription_attributes.present? + transcription = Transcription.find_or_create_by(hearing_type: hearing.class.name, hearing_id: hearing.id) + transcription.update!(transcription_attributes) + end hearing.update!(hearing_updates) update_advance_on_docket_motion unless advance_on_docket_motion_attributes.blank? end @@ -67,7 +69,6 @@ def hearing_updates summary: summary, transcript_requested: transcript_requested, transcript_sent_date: transcript_sent_date, - transcription_attributes: transcription_attributes, witness: witness, email_recipients_attributes: email_recipients_attributes }.compact diff --git a/app/models/hearings/transcription.rb b/app/models/hearings/transcription.rb index c187eaebceb..c023a9585c7 100644 --- a/app/models/hearings/transcription.rb +++ b/app/models/hearings/transcription.rb @@ -1,5 +1,40 @@ # frozen_string_literal: true class Transcription < CaseflowRecord - belongs_to :hearing + belongs_to :hearing, polymorphic: true + belongs_to :legacy_hearing, polymorphic: true + belongs_to :transcription_contractor + has_many :transcription_files, class_name: "Hearings::TranscriptionFile" + belongs_to :transcription_package, foreign_key: :task_number, primary_key: :task_number + before_create :sequence_task_id + + validates :hearing_type, inclusion: { in: %w[Hearing LegacyHearing] } + validates :hearing, presence: true + + validate :hearing_must_exist + + scope :counts_for_this_week, lambda { + where(sent_to_transcriber_date: Time.zone.today.beginning_of_week.yesterday..Time.zone.today) + .group(:transcription_contractor_id) + .count + } + + scope :first_empty_transcription_file, lambda { + where(transcription_status: "unassigned").order(:task_id).first + } + + def self.unassign_by_task_number(task_number) + where(task_number: task_number).update_all(transcription_status: "unassigned") + end + + private + + def sequence_task_id + self.task_id = Hearings::TranscriptionSequenceId.new(User.system_user.id, task_id) + .before_insert_on_transcriptions(self) + end + + def hearing_must_exist + errors.add(:hearing, "must exist") if hearing.blank? + end end diff --git a/app/models/hearings/transcription_contractor.rb b/app/models/hearings/transcription_contractor.rb new file mode 100644 index 00000000000..a6cce74a0e7 --- /dev/null +++ b/app/models/hearings/transcription_contractor.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class TranscriptionContractor < ApplicationRecord + acts_as_paranoid + + has_many :transcriptions + + validates :current_goal, presence: true + validates :directory, presence: true + validates :email, presence: true + validates :inactive, inclusion: { in: [true, false] } + validates :is_available_for_work, inclusion: { in: [true, false] } + validates :name, presence: true + validates :phone, presence: true + validates :poc, presence: true + + before_update :assign_previous_goal + + def self.all_contractors + all.order(:name) + end + + private + + # auto assign the value of current goal to previous goal when current goal changes + def assign_previous_goal + self.previous_goal = current_goal_was + end +end diff --git a/app/models/hearings/transcription_file.rb b/app/models/hearings/transcription_file.rb index 6342e0f8fc6..96fb979fdfb 100644 --- a/app/models/hearings/transcription_file.rb +++ b/app/models/hearings/transcription_file.rb @@ -1,15 +1,121 @@ # frozen_string_literal: true -class TranscriptionFile < CaseflowRecord +class Hearings::TranscriptionFile < CaseflowRecord belongs_to :hearing, polymorphic: true belongs_to :transcription belongs_to :docket - VALID_FILE_TYPES = %w[mp3 mp4 vtt rtf xls csv].freeze + belongs_to :locked_by, class_name: "User" + + VALID_FILE_TYPES = %w[mp3 mp4 vtt rtf xls csv zip doc pdf].freeze validates :file_type, inclusion: { in: VALID_FILE_TYPES, message: "'%s' is not valid" } + scope :filterable_values, lambda { + select(" + transcription_files.*, + (CASE WHEN aod_based_on_age IS NOT NULL THEN aod_based_on_age ELSE false END) AS aod_based_on_age, + (CASE WHEN aodm.granted IS NOT NULL THEN aodm.granted ELSE false END) AS aod_motion_granted, + scheduled_for, + CONCAT_WS(' ', + CASE WHEN aodm.granted OR aod_based_on_age THEN 'AOD' END, + CASE WHEN appeals.stream_type IS NOT NULL THEN appeals.stream_type ELSE 'original' END + ) AS sortable_case_type + ") + .joins("LEFT OUTER JOIN hearings ON hearings.id = transcription_files.hearing_id AND + transcription_files.hearing_type = 'Hearing'") + .joins("LEFT OUTER JOIN legacy_hearings ON legacy_hearings.id = transcription_files.hearing_id AND + transcription_files.hearing_type = 'LegacyHearing'") + .joins("LEFT OUTER JOIN appeals ON hearings.appeal_id = appeals.id AND + transcription_files.hearing_type = 'Hearing'") + .joins("LEFT OUTER JOIN legacy_appeals ON hearings.appeal_id = legacy_appeals.id AND + transcription_files.hearing_type = 'LegacyHearing'") + .joins("LEFT OUTER JOIN advance_on_docket_motions AS aodm ON + ((aodm.appeal_id = appeals.id AND aodm.appeal_type = 'Appeal') OR + (aodm.appeal_id = legacy_appeals.id AND aodm.appeal_type = 'LegacyAppeal')) + AND aodm.granted = true") + .joins("LEFT OUTER JOIN hearing_days ON hearing_days.id = hearings.hearing_day_id OR + hearing_days.id = legacy_hearings.hearing_day_id") + .joins("LEFT OUTER JOIN claimants ON claimants.decision_review_id = appeals.id AND + claimants.decision_review_type = 'Appeal'") + .joins("LEFT OUTER JOIN people ON people.participant_id = claimants.participant_id") + .joins("LEFT OUTER JOIN veterans ON veterans.file_number = appeals.veteran_file_number") + .joins("LEFT OUTER JOIN transcriptions ON transcriptions.id = transcription_files.transcription_id") + } + + scope :unassigned, -> { where(file_status: Constants.TRANSCRIPTION_FILE_STATUSES.upload.success) } + + scope :completed, lambda { + where(file_status: ["Successful upload (AWS)", "Failed Retrieval (BOX)", "Overdue"]) + } + + scope :filter_by_hearing_type, ->(values) { where("transcription_files.hearing_type IN (?)", values) } + + scope :filter_by_status, ->(values) { where("file_status IN (?)", values) } + + scope :filter_by_types, lambda { |values| + filter_parts = [] + stream_types = [] + values.each do |value| + if value == "AOD" + filter_parts << + "(aod_based_on_age = true OR aodm.granted = true)" + else + stream_types << value + filter_parts << + "((transcription_files.hearing_type = 'Hearing' AND stream_type IN (?)) OR + transcription_files.hearing_type = 'LegacyHearing')" + end + end + where(filter_parts.join(" OR "), stream_types) + } + + scope :filter_by_hearing_dates, lambda { |values| + mode = values[0] + if mode == "between" + start_date = values[1] + " 00:00:00" + end_date = values[2] + " 23:59:59" + where(Arel.sql("scheduled_for >= '" + start_date + "' AND scheduled_for <= '" + end_date + "'")) + elsif mode == "before" + date = values[1] + " 00:00:00" + where(Arel.sql("scheduled_for < '" + date + "'")) + elsif mode == "after" + date = values[1] + " 23:59:59" + where(Arel.sql("scheduled_for > '" + date + "'")) + elsif mode == "on" + start_date = values[1] + " 00:00:00" + end_date = values[1] + " 23:59:59" + where(Arel.sql("scheduled_for >= '" + start_date + "' AND scheduled_for <= '" + end_date + "'")) + end + } + + scope :search, lambda { |search| + where("(docket_number LIKE :query) OR + (LOWER(CONCAT_WS(' ', people.first_name, people.last_name)) LIKE :query) OR + (LOWER(CONCAT_WS(' ', veterans.first_name, veterans.last_name)) LIKE :query) OR + (veterans.file_number LIKE :query) OR + (LOWER(transcriptions.task_number) LIKE :query)", query: "%#{search.downcase.strip}%") + } + + scope :order_by_id, ->(direction) { order(Arel.sql("id " + direction)) } + scope :order_by_hearing_date, ->(direction) { order(Arel.sql("scheduled_for " + direction)) } + scope :order_by_hearing_type, ->(direction) { order(Arel.sql("hearing_type " + direction)) } + scope :order_by_case_type, ->(direction) { order(Arel.sql("sortable_case_type " + direction)) } + + scope :locked, -> { where(locked_at: (Time.now.utc - 2.hours)..Time.now.utc) } + + # Purpose:Fetches the file by docket number and type + # Return:The temporary save location of the file + def self.fetch_file_by_docket_and_type(docket_number) + file = where(docket_number: docket_number, file_type: "xls") + .where.not(date_returned_box: nil) + .first + return nil unless file + + file.fetch_file_from_s3! + end + # Purpose: Fetches file from S3 # Return: The temporary save location of the file def fetch_file_from_s3! @@ -43,7 +149,7 @@ def convert_to_rtf! # Purpose: Maps file handling process with associated field to update DATE_FIELDS = { - retrieval: :date_receipt_webex, + retrieval: :date_receipt_recording, upload: :date_upload_aws, conversion: :date_converted }.freeze @@ -79,4 +185,57 @@ def tmp_location def clean_up_tmp_location File.delete(tmp_location) if File.exist?(tmp_location) end + + # Purpose: Get hearing date from associated hearing_day + # + # Returns: string, a date formated like mm/dd/yyyy + def hearing_date + scheduled_for.to_formatted_s(:short_date) + end + + # Purpose: Returns advance on docket status from associated advance_on_docket_motion + # + # Returns: boolean, true of either age based is true or motion granted + def advanced_on_docket? + aod_based_on_age || aod_motion_granted + end + + # Purpose: Returns a formatted stream_type from an AMA appeal + # + # Returns: string, defaults to Original if not AMA + def case_type + (hearing.appeal.try(:stream_type) || "Original").capitalize + end + + # Purpose: Returns the external appeal id from an AMA appeal or Legacy appeal + # + # Returns: string, defaults to blank of not AMA + def external_appeal_id + hearing.appeal.external_id + end + + # Purpose: Returns a formatted value containing the veteral name and file number + # + # Returns: string + def case_details + appellant_name = hearing.appeal.appellant_or_veteran_name + file_number = hearing.appeal.veteran_file_number + "#{appellant_name} (#{file_number})" + end + + # Purpose: Returns true if record is not locked, was locked by user_id, or locked more than two hours ago + def lockable?(user_id) + !locked_by_id || locked_by_id == user_id || locked_at < Time.now.utc - 2.hours + end + + def self.reset_files(task_number) + transcription = Transcription.find_by(task_number: task_number) + return unless transcription + + transcription_files = Hearings::TranscriptionFile.where(transcription_id: transcription.id) + + transcription_files.each do |file| + file.update(file_status: "Successful upload (AWS)", date_upload_box: nil) + end + end end diff --git a/app/models/hearings/transcription_package.rb b/app/models/hearings/transcription_package.rb new file mode 100644 index 00000000000..a2035dcd93d --- /dev/null +++ b/app/models/hearings/transcription_package.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +class TranscriptionPackage < CaseflowRecord + belongs_to :contractor, class_name: "TranscriptionContractor" + has_many :transcription_package_hearings + has_many :hearings, through: :transcription_package_hearings + has_many :transcription_package_legacy_hearings + has_many :legacy_hearings, through: :transcription_package_legacy_hearings + has_many :transcriptions, foreign_key: :task_number, primary_key: :task_number + + scope :filter_by_date, lambda { |values, field_name| + mode = values[0] + if mode == "between" + start_date = values[1] + " 00:00:00" + end_date = values[2] + " 23:59:59" + where(Arel.sql(field_name + " >= '" + start_date + "' AND " + field_name + " <= '" + end_date + "'")) + elsif mode == "before" + date = values[1] + " 00:00:00" + where(Arel.sql(field_name + " < '" + date + "'")) + elsif mode == "after" + date = values[1] + " 23:59:59" + where(Arel.sql(field_name + " > '" + date + "'")) + elsif mode == "on" + start_date = values[1] + " 00:00:00" + end_date = values[1] + " 23:59:59" + where(Arel.sql(field_name + " >= '" + start_date + "' AND " + field_name + " <= '" + end_date + "'")) + end + } + + scope :filter_by_contractor, ->(values) { where("transcription_contractors.name IN (?)", values) } + + scope :filter_by_status, ->(values) { where(status: values) } + + scope :search, ->(search) { where("LOWER(task_number) LIKE :query", query: "%#{search.downcase.strip}%") } + + scope :order_by_field, ->(direction, field_name) { order(Arel.sql(field_name + " " + direction)) } + + scope :with_status_overdue_or_sent, -> { joins(:contractor).where(status: ["Overdue", "Successful Upload (BOX)"]) } + + def contractor_name + contractor&.name + end + + def all_hearings + (hearings + legacy_hearings).map { |hearing| serialize_hearing(hearing) } + end + + def formatted_date_upload_box + format_date_for_table(date_upload_box) + end + + def formatted_returned_at + format_date_for_table(returned_at) + end + + def contents_count + (hearings + legacy_hearings).length + end + + def self.cancel_by_task_number(task_number) + find_by(task_number: task_number)&.update(status: "cancelled") + end + + private + + def format_date_for_table(date) + date.utc.strftime("%-m/%-d/%Y") + end + + def format_case_details(hearing) + file_number = format_file_number(hearing.veteran_file_number) + full_name = format_full_name(hearing.veteran_first_name, hearing.veteran_last_name) + [full_name, file_number].join(" ") + end + + def format_file_number(file_number) + "(#{file_number})" + end + + def format_full_name(first_name, last_name) + "#{first_name} #{last_name}" + end + + def serialize_hearing(hearing) + { + docketNumber: hearing.docket_number, + caseDetails: format_case_details(hearing), + hearingType: hearing.class.name, + appealId: hearing.appeal.external_id + } + end +end diff --git a/app/models/hearings/transcription_package_hearing.rb b/app/models/hearings/transcription_package_hearing.rb new file mode 100644 index 00000000000..3ee5d1100c7 --- /dev/null +++ b/app/models/hearings/transcription_package_hearing.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class TranscriptionPackageHearing < ApplicationRecord + belongs_to :hearing + belongs_to :transcription_package +end diff --git a/app/models/hearings/transcription_package_legacy_hearing.rb b/app/models/hearings/transcription_package_legacy_hearing.rb new file mode 100644 index 00000000000..d857e229f83 --- /dev/null +++ b/app/models/hearings/transcription_package_legacy_hearing.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class TranscriptionPackageLegacyHearing < ApplicationRecord + belongs_to :legacy_hearing + belongs_to :transcription_package +end diff --git a/app/models/legacy_hearing.rb b/app/models/legacy_hearing.rb index ab86f4c0914..3265833ed95 100644 --- a/app/models/legacy_hearing.rb +++ b/app/models/legacy_hearing.rb @@ -74,11 +74,14 @@ class LegacyHearing < CaseflowRecord has_many :hearing_views, as: :hearing has_many :appeal_stream_snapshots, foreign_key: :hearing_id has_one :hearing_location, as: :hearing + has_one :transcription, -> { order(created_at: :desc) } has_many :email_events, class_name: "SentHearingEmailEvent", foreign_key: :hearing_id has_many :email_recipients, class_name: "HearingEmailRecipient", foreign_key: :hearing_id - has_many :transcription_files, as: :hearing + has_many :transcription_files, class_name: "Hearings::TranscriptionFile", as: :hearing + has_many :transcriptions, as: :hearing alias_attribute :location, :hearing_location + accepts_nested_attributes_for :transcription, reject_if: proc { |attributes| attributes.blank? } accepts_nested_attributes_for :hearing_location, reject_if: proc { |attributes| attributes.blank? } accepts_nested_attributes_for :email_recipients, reject_if: proc { |attributes| attributes.blank? } diff --git a/app/models/tasks/review_transcript_task.rb b/app/models/tasks/review_transcript_task.rb new file mode 100644 index 00000000000..0f9da7cf7da --- /dev/null +++ b/app/models/tasks/review_transcript_task.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class ReviewTranscriptTask < Task + before_validation :set_assignee + + USER_ACTIONS = [ + Constants.TASK_ACTIONS.TRANSCRIPT_NO_ERRORS_FOUND.to_h, + Constants.TASK_ACTIONS.TRANSCRIPT_ERRORS_FOUND_AND_CORRECTED.to_h, + Constants.TASK_ACTIONS.CANCEL_REVIEW_TRANSCRIPT_TASK.to_h + ].freeze + + def label + COPY::REVIEW_TRANSCRIPT_TASK_LABEL + end + + def available_actions(user) + return USER_ACTIONS if assigned_to == user + + [] + end + + def default_instructions + [COPY::REVIEW_TRANSCRIPT_TASK_DEFAULT_INSTRUCTIONS] + end + + def set_assignee + self.assigned_to ||= HearingAdmin.singleton + end +end diff --git a/app/models/transcription_work_order.rb b/app/models/transcription_work_order.rb new file mode 100644 index 00000000000..57f882855bb --- /dev/null +++ b/app/models/transcription_work_order.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true + +class TranscriptionWorkOrder + include ActiveModel::Model + + def self.display_wo_summary(task_number) + wo_info = fetch_wo_info(task_number) + wo_file_info = fetch_wo_file_info(task_number) + wo_info.merge(wo_file_info) + end + + def self.display_wo_contents(task_number) + transcription_package = + TranscriptionPackage + .preload(hearings: [:appeal], legacy_hearings: [:appeal]) + .find_by(task_number: task_number) + + return {} unless transcription_package + + transcription_package.all_hearings + end + + def self.unassign_wo(task_number) + update_transcription_package(task_number) + update_transcription_info(task_number) + get_banner_messages(task_number) + end + + def self.get_banner_messages(task_number) + transcription_package = ::TranscriptionPackage.find_by(task_number: task_number) + return {} unless transcription_package + + contractor_name = transcription_package.contractor.name + { + hearing_message: COPY::HEARING_BANNER_MESSAGE, + work_order_message: format(COPY::WORK_ORDER_BANNER_MESSAGE, contractor_name, contractor_name) + } + end + + def self.update_transcription_package(task_number) + transcription_package = TranscriptionPackage.find_by(task_number: task_number) + return false unless transcription_package + + if transcription_package + transcription_package.update( + status: "#{task_number} to cancelled", + updated_by_id: current_user.id, + updated_at: Time.zone.now + ) + else + Rails.logger.warn("TranscriptionPackage with task_number #{task_number} not found") + end + end + + def self.update_transcription_info(task_number) + update_transcriptions(task_number) + update_transcription_files(task_number) + end + + def self.update_transcriptions(task_number) + transcription_ids = fetch_transcription_ids(task_number) + return if transcription_ids.empty? + + Transcription.where(id: transcription_ids).update_all( + updated_by_id: current_user.id, + deleted_at: Time.zone.now, + updated_at: Time.zone.now + ) + end + + def self.update_transcription_files(task_number) + transcription_file_ids = fetch_transcription_file_ids(task_number) + return if transcription_file_ids.empty? + + Hearings::TranscriptionFile.where(id: transcription_file_ids).update_all( + date_upload_box: nil, + file_status: "Successful upload (AWS)", + updated_by_id: current_user.id, + updated_at: Time.zone.now + ) + end + + def self.fetch_transcription_ids(task_number) + Transcription.where(task_number: task_number).pluck(:id) + end + + def self.fetch_transcription_file_ids(task_number) + Transcription + .where(task_number: task_number) + .joins(:transcription_files) + .pluck("transcription_files.id") + end + + def self.fetch_wo_info(task_number) + wo_info = TranscriptionPackage.joins(:contractor).select('transcription_packages.id, + transcription_packages.expected_return_date, + transcription_packages.task_number, + transcription_packages.aws_link_zip, + transcription_contractors.name') + .find_by(task_number: task_number) + + return {} unless wo_info + + if wo_info + { + returnDate: wo_info.expected_return_date.strftime("%m/%d/%Y"), + workOrder: wo_info.task_number, + contractorName: wo_info.name, + workOrderLink: wo_info.aws_link_zip + } + end + end + + def self.fetch_wo_file_info(task_number) + transcription = find_transcription_with_files(task_number) + return {} unless transcription + + { woFileInfo: transcription.transcription_files.map { |file| build_file_info(file) }, + workOrderStatus: fetch_wo_file_status(task_number) } + end + + def self.fetch_wo_file_status(task_number) + transcription = find_transcription_with_files(task_number) + return {} unless transcription + + { currentStatus: check_status_file(transcription.transcription_files) } + end + + def self.find_transcription_with_files(task_number) + transcription = Transcription.includes( + transcription_files: { hearing: [:hearing_day, :appeal, :judge] } + ).find_by(task_number: task_number) + transcription + end + + def self.check_status_file(all_file) + status_complete = true + all_file.each do |current_file| + if current_file.file_status != "Successful upload (AWS)" + status_complete = false + end + end + status_complete + end + + def self.build_file_info(file) + { + docket_number: file.docket_number, + case_type: file.hearing_type, + hearing_date: file.hearing&.hearing_day&.scheduled_for&.strftime("%m/%d/%Y"), + first_name: file.hearing&.appellant_first_name, + last_name: file.hearing&.appellant_last_name, + judge_name: file.hearing&.judge&.full_name, + regional_office: file.hearing&.closest_regional_office_city, + types: build_types(file.hearing) + } + end + + def self.build_types(hearing) + [ + hearing&.original_appeal_type, + hearing&.mo_appeal_type + ].compact.join(", ") + end +end diff --git a/app/models/user.rb b/app/models/user.rb index d218be4b0fc..b405b330b55 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -118,6 +118,10 @@ def in_hearing_or_transcription_organization? HearingsManagement.singleton.users.include?(self) || TranscriptionTeam.singleton.users.include?(self) end + def in_hearing_and_transcription_organization? + hearings_user? && TranscriptionTeam.singleton.users.include?(self) + end + def can_withdraw_issues? CaseReview.singleton.users.include?(self) || %w[NWQ VACO].exclude?(regional_office) end @@ -378,6 +382,9 @@ def user_info_for_idt # rubocop:disable Metrics/MethodLength def selectable_organizations orgs = organizations.select(&:selectable_in_queue?) + if in_hearing_and_transcription_organization? + orgs << { name: "Transcription Dispatch", url: "/hearings/transcription_files" } + end judge_team_judges = judge? ? [self] : [] judge_team_judges |= administered_judge_teams.map(&:judge) if FeatureToggle.enabled?(:judge_admin_scm) diff --git a/app/repositories/hearing_repository.rb b/app/repositories/hearing_repository.rb index 01a94b30293..797eebd6dfd 100644 --- a/app/repositories/hearing_repository.rb +++ b/app/repositories/hearing_repository.rb @@ -36,7 +36,7 @@ def update_vacols_hearing!(vacols_record, hearing_hash) vacols_record.update_hearing!(hearing_hash.merge(staff_id: vacols_record.slogid)) if hearing_hash.present? end - # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def slot_new_hearing(attrs, override_full_hearing_day_validation: false) hearing_day = HearingDay.find(attrs[:hearing_day_id]) fail HearingDayFull if !override_full_hearing_day_validation && hearing_day.hearing_day_full? @@ -48,7 +48,7 @@ def slot_new_hearing(attrs, override_full_hearing_day_validation: false) scheduled_timezone = HearingDatetimeService.timezone_from_time_string(attrs[:scheduled_time_string]) if attrs[:appeal].is_a?(LegacyAppeal) - vacols_hearing = create_vacols_hearing( + hearing = vacols_hearing = create_vacols_hearing( hearing_day: hearing_day, appeal: attrs[:appeal], scheduled_for: scheduled_for, @@ -59,7 +59,7 @@ def slot_new_hearing(attrs, override_full_hearing_day_validation: false) AppealRepository.update_location!(attrs[:appeal], LegacyAppeal::LOCATION_CODES[:caseflow]) vacols_hearing else - Hearing.create!( + hearing = Hearing.create!( appeal: attrs[:appeal], hearing_day_id: hearing_day.id, hearing_location_attributes: attrs[:hearing_location_attrs] || {}, @@ -70,8 +70,22 @@ def slot_new_hearing(attrs, override_full_hearing_day_validation: false) notes: attrs[:notes] ) end + # Remove this code after testing + if hearing.id > 100 && ApplicationController.dependencies_faked? + Transcription.create!( + hearing_id: hearing.id, + hearing_type: hearing.class.name, + task_number: "5001", + transcriber: "Real Contractor", + sent_to_transcriber_date: Time.zone.today, + expected_return_date: Time.zone.tomorrow, + uploaded_to_vbms_date: Time.zone.today, + return_date: Time.zone.tomorrow + ) + end + hearing end - # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def load_vacols_data(hearing) vacols_record = MetricsService.record("VACOLS: HearingRepository.load_vacols_data: #{hearing.vacols_id}", @@ -114,9 +128,10 @@ def hearings_for(case_hearings) case_hearings.map do |vacols_record| begin - hearing = LegacyHearing - .assign_or_create_from_vacols_record(vacols_record, - legacy_hearing: fetched_hearings_hash[vacols_record.hearing_pkseq]) + hearing = LegacyHearing.assign_or_create_from_vacols_record( + vacols_record, + legacy_hearing: fetched_hearings_hash[vacols_record.hearing_pkseq] + ) set_vacols_values(hearing, vacols_record) rescue RegionalOffice::NotFoundError => error Raven.capture_exception(error, extra: { legacy_hearing_vacols_id: vacols_record.hearing_pkseq }) diff --git a/app/serializers/hearings/hearing_serializer.rb b/app/serializers/hearings/hearing_serializer.rb index 5bb7dcd5652..1c5f742fd3a 100644 --- a/app/serializers/hearings/hearing_serializer.rb +++ b/app/serializers/hearings/hearing_serializer.rb @@ -20,6 +20,9 @@ class HearingSerializer } end end + attribute :transcription_contractors do + TranscriptionContractor.all_contractors.index_by(&:id).transform_values(&:name) + end attribute :appeal_external_id attribute :appeal_id attribute :appellant_address_line_1 @@ -134,4 +137,15 @@ class HearingSerializer params[:user]&.timezone end attribute :scheduled_in_timezone + attribute :determine_service_name + attribute :scheduled_time do |hearing| + hearing.scheduled_time&.strftime("%m/%d/%Y") + end + attribute :date_receipt_recording do |hearing| + hearing.transcription_files + .where(file_type: "mp3") + .limit(1) + .pluck(:date_receipt_recording) + .first&.strftime("%m/%d/%Y") + end end diff --git a/app/serializers/hearings/transcription_file_serializer.rb b/app/serializers/hearings/transcription_file_serializer.rb index 2f13cd94492..c4333389319 100644 --- a/app/serializers/hearings/transcription_file_serializer.rb +++ b/app/serializers/hearings/transcription_file_serializer.rb @@ -7,6 +7,7 @@ class TranscriptionFileSerializer attribute :docket_number attribute :hearing_type attribute :date_upload_aws + attribute :date_returned_box attribute :file_name attribute :file_status attribute :file_type diff --git a/app/serializers/hearings/transcription_package_serializer.rb b/app/serializers/hearings/transcription_package_serializer.rb new file mode 100644 index 00000000000..145adc1c24b --- /dev/null +++ b/app/serializers/hearings/transcription_package_serializer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class Hearings::TranscriptionPackageSerializer + include FastJsonapi::ObjectSerializer + + set_key_transform :camel_lower + + attribute :task_number + attribute :date_sent, &:formatted_date_upload_box + attribute :return_date, &:formatted_returned_at + attribute :status + attribute :hearings, &:all_hearings + attribute :contractor_name + attribute :order_contents_count, &:contents_count +end diff --git a/app/services/external_api/va_box_service.rb b/app/services/external_api/va_box_service.rb new file mode 100644 index 00000000000..cd4c198d8d0 --- /dev/null +++ b/app/services/external_api/va_box_service.rb @@ -0,0 +1,231 @@ +# frozen_string_literal: true + +class ExternalApi::VaBoxService + BASE_URL = "https://api.box.com" + UPLOAD_URL = "https://upload.box.com/api/2.0" + FILES_URI = "2.0/files" + CHUNK_SIZE = 50 * 1024 * 1024 # 50 MB + + attr_reader :client_secret, :client_id, :enterprise_id, :private_key, :passphrase + + def initialize + @client_secret = ENV["BOX_CLIENT_SECRET"] + @client_id = ENV["BOX_CLIENT_ID"] + @enterprise_id = ENV["BOX_ENTERPRISE_ID"] + @private_key = ENV["BOX_PRIVATE_KEY"].gsub("\\n", "\n") + @passphrase = ENV["BOX_PASSPHRASE"] + end + + def download_file(file_id, destination_path) + uri = "#{FILES_URI}/#{file_id}/content" + response = box_conn.get(uri) + + if response.success? + File.open(destination_path, "wb") do |file| + file.write(response.body) + end + Rails.logger.info("File downloaded successfully to #{destination_path}") + elsif response.status == 302 + + redirect_url = response.headers["location"] + follow_redirect_and_download(redirect_url, destination_path) + else + Rails.logger.info("Failed to download the file. Response code: #{response.status}") + Rails.logger.info("Response body: #{response.body}") + fail "Error: #{response.body}" + end + rescue StandardError => error + log_error(error) + end + + def upload_file(file_path, folder_id) + file_size = File.size(file_path) + + if file_size <= CHUNK_SIZE + Rails.logger.info("Uploading single file: #{file_path}") + upload_single_file(file_path, folder_id) + else + Rails.logger.info("Chunkifying and uploading file: #{file_path}") + chunkify_and_upload(file_path, folder_id) + end + end + + def get_folder_items(folder_id:, item_type: "folder", query_string: nil) + uri = "2.0/folders/#{folder_id}/items" + uri += "?" + query_string unless query_string.nil? + + response = box_conn.get(uri) + body = parse_json(response.body) + + response.success? ? filter_items_by_type(body[:entries], item_type) : handle_error(response) + end + + def get_child_folder_id(parent_folder_id, child_folder_name) + folders = get_folder_items(parent_folder_id) + matching_folder = folders.find { |folder| folder[:name] == child_folder_name } + if matching_folder + matching_folder[:id] + else + fail "Folder '#{child_folder_name}' not found in parent folder '#{parent_folder_id}'" + end + end + + def upload_single_file(file_path, folder_id) + uri = "files/content" + file = Faraday::UploadIO.new(File.new(file_path), "application/zip") + + response = upload_conn.post(uri) do |request| + request.body = { + attributes: { + name: File.basename(file_path), + parent: { id: folder_id } + }.to_json, + file: file + } + end + + response.success? ? parse_json(response.body) : handle_error(response) + end + + def ensure_access_token + @access_token = Rails.cache.read(:box_access_token) || fetch_access_token + end + + private + + def follow_redirect_and_download(url, destination_path) + redirect_response = Faraday.get(url) + + if redirect_response.status == 200 + File.open(destination_path, "wb") do |file| + file.write(redirect_response.body) + end + Rails.logger.info("File downloaded successfully to #{destination_path} via redirect") + else + Rails.logger.info( + "Failed to download file from redirect. Status: #{redirect_response.status}, Body: #{redirect_response.body}" + ) + end + end + + def fetch_access_token + response = fetch_jwt_access_token + @access_token = response[:access_token] + Rails.cache.write(:box_access_token, @access_token, expires_in: (response[:expires_in] - 60)) + @access_token + end + + def box_conn + ensure_access_token + + Faraday.new(BASE_URL) do |f| + f.headers["Authorization"] = "Bearer #{@access_token}" + f.headers["Content-Type"] = "application/json" + f.response :logger, ::Logger.new($stdout) + f.use Faraday::Adapter::NetHttp + end + end + + def upload_conn + ensure_access_token + + Faraday.new(UPLOAD_URL) do |f| + f.headers["Authorization"] = "Bearer #{@access_token}" + f.request :multipart + f.request :url_encoded + f.response :logger, ::Logger.new($stdout) + f.use Faraday::Adapter::NetHttp + end + end + + # rubocop:disable Metrics/MethodLength + def fetch_jwt_access_token + url = "#{BASE_URL}/oauth2/token" + payload = { + iss: @client_id, + sub: @enterprise_id, + box_sub_type: "enterprise", + aud: url, + jti: SecureRandom.uuid, + exp: (Time.now.utc + 60).to_i + } + + rsa_private = OpenSSL::PKey::RSA.new(@private_key, @passphrase) + token = JWT.encode(payload, rsa_private, "RS256") + + body = { + grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", + assertion: token, + client_id: @client_id, + client_secret: @client_secret + } + + uri = URI.parse(url) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + request = Net::HTTP::Post.new(uri.request_uri, { "Content-Type" => "application/json" }) + request.body = body.to_json + + response = http.request(request) + + if response.code == "200" + body = JSON.parse(response.body, symbolize_names: true) + body + else + fail "Error: #{response.body}" + end + rescue StandardError => error + log_error(error) + end + # rubocop:enable Metrics/MethodLength + + def chunkify_and_upload(file_path, folder_id) + chunk_paths = split_file(file_path) + + chunk_paths.each_with_index do |chunk_path, _index| + upload_single_file(chunk_path, folder_id) + File.delete(chunk_path) # Clean up the chunk file after upload + end + end + + def split_file(file_path) + chunk_paths = [] + file_size = File.size(file_path) + num_chunks = (file_size.to_f / CHUNK_SIZE).ceil + + File.open(file_path, "rb") do |file| + num_chunks.times do |i| + chunk_path = "#{file_path}.part#{i}" + chunk_paths << chunk_path + + File.open(chunk_path, "wb") do |chunk_file| + chunk_file.write(file.read(CHUNK_SIZE)) + end + end + end + + chunk_paths + end + + def log_error(error) + Rails.logger.error(error.backtrace.join("\n")) + Rails.logger.error(error.message) + end + + def filter_items_by_type(items, item_type) + return items unless item_type.in? %w(file folder) + + items.find_all { |item| item[:type] == item_type } + end + + def parse_json(json) + JSON.parse(json, symbolize_names: true) + end + + def handle_error(response) + Rails.logger.info("Response body: #{response.body}") + fail ::StandardError, "Error: #{response.status} #{response.reason_phrase}" + rescue StandardError => error + log_error(error) + end +end diff --git a/app/services/hearings/transcription_sequence_id.rb b/app/services/hearings/transcription_sequence_id.rb new file mode 100644 index 00000000000..8b2d37d13a9 --- /dev/null +++ b/app/services/hearings/transcription_sequence_id.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# This file defines a Ruby function that mimics the functionality of the PL/SQL trigger. +# It's not a direct translation, but provides a similar behavior for managing task IDs. + +# Define a constant to represent the system generated tag. +SYSTEM_GEN_TAG = User.system_user.id + +# Define a class to represent the transcriptions table. +class Hearings::TranscriptionSequenceId + attr_accessor :created_by_id, :task_id + + # Initialize a new transcription object. + def initialize(created_by_id, task_id = nil) + @created_by_id = created_by_id + @task_id = task_id + end + + # Simulate the trigger execution. + def before_insert_on_transcriptions(transcription) + # Call the trigger function to update the task ID. + trg_myseq(transcription) + end + + # Define a function to simulate the trigger behavior. + def trg_myseq(transcription) + # Check if the transcription was created by the system. + if transcription.created_by_id == SYSTEM_GEN_TAG + # Generate a new task ID if the transcription was created by the system. + transcription.task_id = next_task_id + end + # Return the updated transcription object. + transcription.task_id + end + + # Define a function to simulate the sequence. + def next_task_id + # Replace this with your actual sequence implementation. + # For example, you could use a database or a counter variable. + # This implementation simply returns an incremented value. + @task_id ||= 0 + # Switch this value back to 1 after testing + @task_id += 5000 + + @task_id + end +end diff --git a/app/views/hearings/index.html.erb b/app/views/hearings/index.html.erb index 30f37fe1828..f263c9b17c3 100644 --- a/app/views/hearings/index.html.erb +++ b/app/views/hearings/index.html.erb @@ -17,6 +17,7 @@ mstIdentification: FeatureToggle.enabled?(:mst_identification, user: current_user), pactIdentification: FeatureToggle.enabled?(:pact_identification, user: current_user), legacyMstPactIdentification: FeatureToggle.enabled?(:legacy_mst_pact_identification, user: current_user), + organizations: current_user.selectable_organizations.map {|o| o.slice(:name, :url)}, userCanAddVirtualHearingDays: FeatureToggle.enabled?(:national_vh_queue, user: current_user), userCanAssignHearingSchedule: current_user.can_assign_hearing_schedule?, userCanBuildHearingSchedule: current_user.can?('Build HearSched'), diff --git a/app/views/layouts/work_order_file_issues_mailer.html.erb b/app/views/layouts/work_order_file_issues_mailer.html.erb new file mode 100644 index 00000000000..c83fd898a0b --- /dev/null +++ b/app/views/layouts/work_order_file_issues_mailer.html.erb @@ -0,0 +1,16 @@ + + + + + + + + + <%= yield %> + <%= yield :intro %> + <%= yield :content %> + <%= yield :signature %> + + diff --git a/app/workflows/transcription_file_upload.rb b/app/workflows/transcription_file_upload.rb index 19db1c44742..d24c503c578 100644 --- a/app/workflows/transcription_file_upload.rb +++ b/app/workflows/transcription_file_upload.rb @@ -11,7 +11,9 @@ class TranscriptionFileUpload vtt: "transcript_raw", rtf: "transcript_text", xls: "transcript_text", - csv: "transcript_text" + csv: "transcript_text", + zip: "transcript_text", + json: "transcript_text" }.freeze class FileUploadError < StandardError; end diff --git a/app/workflows/transcription_packages.rb b/app/workflows/transcription_packages.rb new file mode 100644 index 00000000000..2fea57867b3 --- /dev/null +++ b/app/workflows/transcription_packages.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class TranscriptionPackages + include ActiveModel::Model + include ActiveModel::Validations + + include MailRequestValidator::Distribution + include MailRequestValidator::DistributionDestination + + attr_reader :work_order_params + + def initialize(work_order_params) + @work_order_params = work_order_params + end + + def call + Hearings::WorkOrderFileJob.perform_now(work_order_params) ? create_zip_file : return + end + + def create_zip_file + Hearings::ZipAndUploadTranscriptionFilesJob.perform_now(work_order_params.hearings) ? create_bom_file : return + end + + def create_bom_file + Hearings::CreateBomFileJob.perform_now(work_order_params) ? create_transcription_package : return + end + + def create_transcription_package + Hearings::CreateTranscriptionPackageJob.perform_now(work_order_params) ? upload_transcription_package : return + end + + def upload_transcription_package + Hearings::VaBoxUploadJob.perform_now(work_order_params) + end +end diff --git a/client/COPY.json b/client/COPY.json index f390031085c..8f6d7c17239 100644 --- a/client/COPY.json +++ b/client/COPY.json @@ -742,6 +742,50 @@ "TEAM_MANAGEMENT_IHP_WRITING_VSO_OPTION": "IHP-writing VSO", "TEAM_MANAGEMENT_FIELD_VSO_OPTION": "Field VSO", "TEAM_MANAGEMENT_DROPDOWN_LABEL": "Please enter a valid name or CSS ID to search for a user", + "TRANSCRIPTION_SETTINGS_HEADER": "Transcription settings", + "TRANSCRIPTION_SETTINGS_SUBHEADER": "Edit current contractors", + "TRANSCRIPTION_SETTINGS_BOX_LINK": "Contractor folder name in box.com: ", + "TRANSCRIPTION_SETTINGS_CONTRACTOR_NAME": "Contractor Name", + "TRANSCRIPTION_SETTINGS_REMOVE_CONTRACTOR_MODAL_TITLE": "Remove contractor", + "TRANSCRIPTION_SETTINGS_CONTRACTOR_REMOVAL_SUCCESS": "Contractor removed successfully", + "TRANSCRIPTION_SETTINGS_CONTRACTOR_REMOVAL_FAIL": "Failed to remove contractor", + "TRANSCRIPTION_SETTINGS_EDIT_TOTAL_HEARINGS_MODAL_TITLE": "Edit total hearings per week", + "TRANSCTIPTION_SETTINGS_EDIT_TOTAL_HEARINGS_MODAL_CONTRACTOR": "%s", + "TRANSCRIPTION_SETTINGS_EDIT_TOTAL_HEARINGS_MODAL_CURRENT_GOAL": "Current goal of hearings per week: %s", + "TRANSCRIPTION_SETTINGS_EDIT_TOTAL_HEARINGS_MODAL_INPUT_TEXT": "Hearings per week", + "TRANSCRIPTION_SETTINGS_EDIT_TOTAL_HEARINGS_SUCCESS": "Total hearings per week successfully edited", + "TRANSCRIPTION_SETTINGS_EDIT_TOTAL_HEARINGS_FAIL": "Failed to edit total hearings per week", + "TRANSCRIPTION_SETTINGS_EDIT_TOTAL_HEARINGS_VALIDATION": "Value must be between 1 and 1000.", + "TRANSCRIPTION_SETTINGS_REMOVE_CONTRACTOR_MODAL_TEXT": "This will permanently remove this contractor from the list of assignable contractors.", + "TRANSCRIPTION_SETTINGS_POC_ADDRESS": "POC: ", + "TRANSCRIPTION_SETTINGS_HEARINGS_SENT": "Hearings sent to %s this week: ", + "TRANSCRIPTION_SETTINGS_WORK_TOGGLE": "Temporarily stop work assignment", + "TRANSCRIPTION_SETTINGS_REMOVE": "Remove contractor", + "TRANSCRIPTION_SETTINGS_ADD": "Add contractor", + "TRANSCRIPTION_QUEUE_LINK": "Back to Transcription queue", + "TRANSCRIPTION_SETTINGS_EDIT": "Edit %s information", + "TRANSCRIPTION_SETTINGS_CANCEL": "Cancel", + "TRANSCRIPTION_SETTINGS_CONFIRM": "Confirm", + "TRANSCRIPTION_SETTINGS_ADD_FORM_DESCRIPTION": "This will add a contractor to the list of assignable contractors.", + "TRANSCRIPTION_SETTINGS_EDIT_FORM_DESCRIPTION": "This will update the contractor's information.", + "TRANSCRIPTION_SETTINGS_LABEL_NAME": "Contractor name", + "TRANSCRIPTION_SETTINGS_LABEL_DIRECTORY": "Contractor folder name in box.com", + "TRANSCRIPTION_SETTINGS_LABEL_POC": "POC name", + "TRANSCRIPTION_SETTINGS_LABEL_PHONE": "POC phone number", + "TRANSCRIPTION_SETTINGS_LABEL_EMAIL": "POC email", + "TRANSCRIPTION_SETTINGS_CREATE_SUCCESS": "Success", + "TRANSCRIPTION_SETTINGS_CREATE_MESSAGE": "You have successfully added %s to current contractors", + "TRANSCRIPTION_SETTINGS_UPDATE_MESSAGE": "You have successfully updated %s", + "TRANSCRIPTION_SETTINGS_UPDATE_HEARINGS_GOAL_MESSAGE": "You have successfully updated %s total hearings goal", + "TRANSCRIPTION_SETTINGS_ERROR_TITLE": "Unknown error", + "TRANSCRIPTION_SETTINGS_ERROR_MESSAGE": "Please try again later", + "TRANSCRIPTION_TABLE_PACKAGE_FILE": "Package file", + "TRANSCRIPTION_TABLE_MODIFY_WORK_ORDER": "Modify work order", + "TRANSCRIPTION_TABLE_DISPATCH_WORK_ORDER": "Dispatch work order", + "TRANSCRIPTION_TABLE_FIRST_NAME": "First Name", + "TRANSCRIPTION_TABLE_LAST_NAME": "Last Name", + "TRANSCRIPTION_TABLE_RO": "RO", + "TRANSCRIPTION_TABLE_APPEAL_TYPE": "Appeal Type", "USER_MANAGEMENT_PAGE_DROPDOWN_LINK": "Caseflow user management", "USER_MANAGEMENT_STATUS_PAGE_TITLE": "Caseflow user status management", "USER_MANAGEMENT_PAGE_DESCRIPTION": "Marking a user inactive will remove them from all organizations they are a member of. Once marked inactive, a user cannot be assigned new tasks.", @@ -1519,5 +1563,63 @@ }, "VHA_BANNER_DISPOSITIONS_CANNOT_BE_UPDATED_NON_ADMIN": "Requests for issue modifications have been submitted for this case. Dispositions cannot be made until a VHA admin completes review of the requested changes.", "VHA_BANNER_DISPOSITIONS_CANNOT_BE_UPDATED_ADMIN": "Requests for issue modifications have been submitted for this case. Dispositions cannot be made until a VHA admin completes review of the requested changes. Click the \"Edit issues\" button above to review the issue modification requests.", - "REMANDS_NOT_EDITABLE": "Remands can not be edited." + "REMANDS_NOT_EDITABLE": "Remands can not be edited.", + "REVIEW_TRANSCRIPT_TASK_LABEL": "Review Transcript task", + "REVIEW_TRANSCRIPT_TASK_DEFAULT_INSTRUCTIONS": "Review the hearing transcript and upload the final to VBMS once it has been reviewed for errors or corrected.", + "TRANSCRIPTION_DISPATCH_ASSIGNED_TAB": "Assigned", + "TRANSCRIPTION_DISPATCH_COMPLETED_TAB": "Completed", + "TRANSCRIPTION_FILE_DISPATCH_ALL_TAB": "All transcription", + "TRANSCRIPTION_STATUS_OVERDUE_FILTER_OPTION": "Overdue", + "TRANSCRIPTION_STATUS_SENT_FILTER_OPTION": "Sent", + "TRANSCRIPTION_STATUS_BOX_UPLOAD_SUCCESS_FILTER_OPTION_VALUE": "Successful Upload (BOX)", + "TRANSCRIPTION_STATUS_AWS_UPLOAD_SUCCESS_FILTER_OPTION_VALUE": "Successful upload (AWS)", + "TRANSCRIPTION_STATUS_RETRIEVAL_FAILURE_FILTER_VALUE": "Failed Retrieval (BOX)", + "TRANSCRIPTION_STATUS_COMPLETED_FILTER_OPTION": "Completed", + "TRANSCRIPTION_STATUS_RETRIEVAL_FAILURE_FILTER_OPTION": "Retrieval Failure", + "TRANSCRIPTION_FILE_DISPATCH_SELECT_COLUMN_NAME": "Select All", + "TRANSCRIPTION_FILE_DISPATCH_DOCKET_NUMBER_COLUMN_NAME": "Docket Number", + "TRANSCRIPTION_FILE_DISPATCH_CASE_DETAILS_COLUMN_NAME": "Case Details", + "TRANSCRIPTION_FILE_DISPATCH_TYPES_COLUMN_NAME": "Types", + "TRANSCRIPTION_FILE_DISPATCH_HEARING_DATE_COLUMN_NAME": "Hearing date", + "TRANSCRIPTION_FILE_DISPATCH_HEARING_TYPE_COLUMN_NAME": "Hearing Type", + "TRANSCRIPTION_FILE_DISPATCH_STATUS_COLUMN_NAME": "Status", + "TRANSCRIPTION_FILE_DISPATCH_WORK_ORDER_COLUMN_NAME": "Work order", + "TRANSCRIPTION_FILE_DISPATCH_ITEMS_COLUMN_NAME": "Order Contents", + "TRANSCRIPTION_FILE_UNASSIGN_WORK_ORDER_MODAL_TEXT": "Unassigning this order will return all appeals back to the Unassigned Transcription queue.", + "TRANSCRIPTION_FILE_UNASSIGN_WORK_ORDER_MODAL_BOLD_TEXT": "Please ensure that all hearing files are removed from the contractors's box.com folder.", + "TRANSCRIPTION_FILE_DISPATCH_DATE_SENT_COLUMN_NAME": "Date Sent", + "TRANSCRIPTION_FILE_DISPATCH_RETURN_DATE_COLUMN_NAME": "Return date", + "TRANSCRIPTION_FILE_DISPATCH_EXPECTED_RETURN_DATE_COLUMN_NAME": "Expected Return Date", + "TRANSCRIPTION_FILE_DISPATCH_UPLOAD_DATE_COLUMN_NAME": "Upload date", + "TRANSCRIPTION_FILE_DISPATCH_CONTRACTOR_COLUMN_NAME": "Contractor", + "TRANSCRIPTION_FILE_DISPATCH_UNASSIGNED_TAB_DESCRIPTION": "Hearing audio files owned by the Transcription team that are unassigned to a contractor:", + "TRANSCRIPTION_FILE_DISPATCH_UNASSIGNED_TAB_SEARCH": "Search by Docket Number, Claimant Name or File Number", + "TRANSCRIPTION_FILE_DISPATCH_UNASSIGNED_TAB_PROMPT": "Please select the files you would like to dispatch for transcription:", + "TRANSCRIPTION_FILE_DISPATCH_ASSIGNED_TAB_SEARCH": "Search by Work Order", + "TRANSCRIPTION_FILE_DISPATCH_ASSIGNED_TAB_DESCRIPTION": "Work orders owned by the Transcription team that have been sent to a contractor:", + "TRANSCRIPTION_FILE_DISPATCH_COMPLETED_TAB_SEARCH": "Search by Work Order or Docket Number", + "TRANSCRIPTION_FILE_DISPATCH_COMPLETED_TAB_DESCRIPTION": "Work orders owned by the Transcription team that have been returned from a contractor:", + "TRANSCRIPTION_FILE_DISPATCH_ALL_TAB_SEARCH": "Search by Work Order or Docket Number", + "TRANSCRIPTION_FILE_DISPATCH_ALL_TAB_DESCRIPTION": "All transcription owned by the Transcription team:", + "TRANSCRIPTION_FILE_DISPATCH_TYPE": "Type to search...", + "TRANSCRIPTION_FILE_DISPATCH_LINK": "Transcription settings", + "TRANSCRIPTION_FILE_DISPATCH_FILE_SELECTED": "%s file%s selected", + "DATE_PICKER_APPLY": "Apply Filter", + "DATE_PICKER_TO": "To", + "DATE_PICKER_FROM": "From", + "DATE_PICKER_DATE": "Date", + "DATE_PICKER_DROPDOWN_BETWEEN": "Between these dates", + "DATE_PICKER_DROPDOWN_BEFORE": "Before this date", + "DATE_PICKER_DROPDOWN_AFTER": "After this date", + "DATE_PICKER_DROPDOWN_ON": "On this date", + "DATE_PICKER_CLEAR": "Clear filter", + "DATE_PICKER_DROPDOWN_LABEL": "Date filter parameters", + "DATE_PICKER_QUICK_BUTTON_30": "Last 30 days", + "WORK_ORDER_BANNER_MESSAGE": "Work Order Transcription Package will need to be manually removed from the box.com folder for %s, please inform %s as they may have picked up the Work Order", + "HEARING_BANNER_MESSAGE": "All Hearing files have been set back to the UnAssigned state for Dispatching", + "UPLOAD_TRANSCRIPTION_VBMS_TEXT": "By uploading to VBMS, you are confirming that you have reviewed the transcript in Caseflow and have found no errors.", + "UPLOAD_TRANSCRIPTION_VBMS_TEXT_AREA": "Please provide context and instructions for this action", + "UPLOAD_TRANSCRIPTION_VBMS_TITLE": "Upload transcript to VBMS", + "UPLOAD_TRANSCRIPTION_VBMS_BUTTON": "Upload to VBMS" + } diff --git a/client/app/components/Checkbox.jsx b/client/app/components/Checkbox.jsx index 79199ea778a..e71350469f6 100644 --- a/client/app/components/Checkbox.jsx +++ b/client/app/components/Checkbox.jsx @@ -64,7 +64,7 @@ Checkbox.propTypes = { /** * The initial value of the `input` element; use for uncontrolled components where not using `value` prop */ - defaultValue: PropTypes.bool, + defaultValue: PropTypes.string, /** * Text (or other node) to display in associated `label` element @@ -117,7 +117,7 @@ Checkbox.propTypes = { /** * The value of the named `input` element(s); required for a controlled component */ - value: PropTypes.bool, + value: PropTypes.string, styling: PropTypes.object, ariaLabel: PropTypes.string }; diff --git a/client/app/components/DatePicker.jsx b/client/app/components/DatePicker.jsx new file mode 100644 index 00000000000..971bcbcceb5 --- /dev/null +++ b/client/app/components/DatePicker.jsx @@ -0,0 +1,318 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { css } from 'glamor'; +import FilterIcon from './icons/FilterIcon'; +import SearchableDropdown from '../components/SearchableDropdown'; +import Button from '../components/Button'; +import COPY from '../../COPY'; +import moment from 'moment-timezone'; + +const datePickerStyle = css({ + paddingLeft: '0.95rem', + paddingTop: '0.3rem', + verticalAlign: 'middle', + position: 'relative', + fontWeight: 'normal', + display: 'table-cell', + '& svg': { + cursor: 'pointer' + }, + '& .date-picker.right': { + right: 0, + }, + '& .date-picker.left': { + left: '10px', + } +}); + +const menuStyle = css({ + position: 'absolute', + background: 'white', + width: '250px', + border: '1px solid #CCC', + boxShadow: '5px 5px 4px -3px #d6d7d9', + zIndex: '1', + '& .input-wrapper': { + padding: '0 1rem 2.5rem', + '& input': { + margin: '0' + }, + '& label': { + padding: '0 0 8px 0', + margin: '0' + } + }, + '& .clear-wrapper': { + borderBottom: '1px solid #d6d7d9', + textAlign: 'center', + padding: '1rem', + marginBottom: '2rem', + '& a': { + cursor: 'pointer' + } + }, + '& .button-wrapper': { + borderTop: '1px solid #d6d7d9', + textAlign: 'center', + padding: '0.75rem', + '& button': { + margin: '0' + } + }, + '& .quick-buttons': { + padding: '0 0 2rem 0', + textAlign: 'center', + borderBottom: '1px solid #d6d7d9', + marginBottom: '2rem', + } +}); + +/* Custom filter method to pass in a QueueTable column object */ +/* This is called for every row of data in the table */ +/* rowValue is a date string such as '5/15/2024' */ +/* filterValues is the array of filter options such as ['between,2024-05-01,2024-05-31'] +/* It returns true or false if the row belongs in the data still */ +export const datePickerFilterValue = (rowValue, filterValues) => { + let pick = false; + const rowDate = moment(rowValue).valueOf(); + + if (filterValues.length && rowDate) { + const filterOptions = filterValues[0].split(','); + + if (filterOptions) { + const mode = filterOptions[0]; + + if (mode === 'between') { + const startDate = moment(`${filterOptions[1]} 00:00:00`).valueOf(); + const endDate = moment(`${filterOptions[2]} 23:59:59`).valueOf(); + + pick = rowDate >= startDate && rowDate <= endDate; + } else if (mode === 'before') { + const date = moment(`${filterOptions[1]} 00:00:00`).valueOf(); + + pick = rowDate < date; + } else if (mode === 'after') { + const date = moment(`${filterOptions[1]} 23:59:59`).valueOf(); + + pick = rowDate > date; + } else if (mode === 'on') { + const startDate = moment(`${filterOptions[1]} 00:00:00`).valueOf(); + const endDate = moment(`${filterOptions[1]} 23:59:59`).valueOf(); + + pick = rowDate >= startDate && rowDate <= endDate; + } + } + } + + return pick; +}; + +class DatePicker extends React.PureComponent { + constructor(props) { + super(props); + + const position = (props.settings && props.settings.position) || 'left'; + const buttons = (props.settings && props.settings.buttons) || false; + const selected = (props.selected && props.selected) || false; + + this.state = { + open: false, + mode: '', + startDate: '', + endDate: '', + position, + buttons, + selected + }; + } + + apply() { + const { onChange } = this.props; + + if (onChange) { + onChange(`${this.state.mode },${ this.state.startDate },${ this.state.endDate}`); + } + + this.hideDropdown(); + } + + toggleDropdown = () => { + const open = !this.state.open; + + this.setState({ open }); + if (open) { + const { values } = this.props; + + if (values && values.length) { + const splitValues = values[0].split(','); + + if (splitValues) { + this.setState({ mode: splitValues[0], startDate: splitValues[1], endDate: splitValues[2] }); + } + } + } + } + + hideDropdown = () => this.setState({ open: false }); + + componentDidMount() { + document.addEventListener('click', this.onGlobalClick, true); + } + + componentWillUnmount() { + document.removeEventListener('click', this.onGlobalClick); + } + + onGlobalClick = (event) => { + if (!this.rootElem) { + return; + } + + const clickIsInsideThisComponent = this.rootElem.contains(event.target); + + if (!clickIsInsideThisComponent) { + this.hideDropdown(); + } + } + + isFilterOpen = () => { + return this.state.open || this.state.selected; + } + + buttonDisabled = () => { + let disabled = true; + + if (this.state.mode === 'between') { + disabled = this.state.startDate === '' || this.state.endDate === ''; + } else if (this.state.mode !== '') { + disabled = this.state.startDate === ''; + } + + return disabled; + } + + clearFilter = () => { + const { onChange } = this.props; + + this.setState({ mode: '', startDate: '', endDate: '' }); + + if (onChange) { + onChange('', true); + } + + this.hideDropdown(); + } + + updateMode = (mode) => { + this.setState({ mode }); + if (mode !== 'between') { + this.setState({ endDate: '' }); + } + } + + quickButtons = (option) => { + let mode = ''; + let startDate = ''; + let endDate = ''; + const format = 'YYYY-MM-DD'; + const { onChange } = this.props; + + if (option === 30) { + mode = 'between'; + startDate = moment().subtract(30, 'days'). + format(format); + endDate = moment().format(format); + } + + if (onChange) { + onChange(`${ mode },${ startDate },${ endDate}`, false); + } + + this.hideDropdown(); + } + + render() { + return { + this.rootElem = rootElem; + }}> + + + {this.state.open && +
+
+
+ + {this.state.buttons && +
+
+ } + +
+ this.updateMode(option.value)} + filterOption={() => true} + value={this.state.mode} /> +
+ + {this.state.mode !== '' && +
+ + this.setState({ startDate: event.target.value })} + /> +
+ } + + {this.state.mode === 'between' && +
+ + this.setState({ endDate: event.target.value })} + /> +
+ } +
+ +
+
+ } +
; + } +} + +DatePicker.propTypes = { + onChange: PropTypes.func, + values: PropTypes.array, + getRef: PropTypes.func, + label: PropTypes.string, + settings: PropTypes.object, + selected: PropTypes.bool, +}; + +export default DatePicker; diff --git a/client/app/components/DateSelector.jsx b/client/app/components/DateSelector.jsx index f692a209cbf..deb64566ba0 100644 --- a/client/app/components/DateSelector.jsx +++ b/client/app/components/DateSelector.jsx @@ -25,6 +25,7 @@ export const DateSelector = (props) => { noFutureDates = false, inputStyling, validateDate, + ariaLabelText, ...passthroughProps } = props; @@ -76,6 +77,7 @@ export const DateSelector = (props) => { max={max} dateErrorMessage={dateErrorMessage} inputStyling={inputStyling} + ariaLabelText={ariaLabelText} /> ); }; @@ -155,7 +157,12 @@ DateSelector.propTypes = { /** * Disables form submission if date is empty or invalid */ - validateDate: PropTypes.func + validateDate: PropTypes.func, + + /** + * Label text for screen readers + */ + ariaLabelText: PropTypes.string }; export default DateSelector; diff --git a/client/app/components/DropdownFilter.jsx b/client/app/components/DropdownFilter.jsx index 455488e66e9..64b51eec36c 100644 --- a/client/app/components/DropdownFilter.jsx +++ b/client/app/components/DropdownFilter.jsx @@ -82,6 +82,7 @@ class DropdownFilter extends React.PureComponent { DropdownFilter.propTypes = { children: PropTypes.node, + name: PropTypes.string, isClearEnabled: PropTypes.bool, clearFilters: PropTypes.func, handleClose: PropTypes.func, diff --git a/client/app/components/FilterOption.jsx b/client/app/components/FilterOption.jsx index 311e4873d7e..971dffe41f8 100644 --- a/client/app/components/FilterOption.jsx +++ b/client/app/components/FilterOption.jsx @@ -3,11 +3,11 @@ import PropTypes from 'prop-types'; import { css } from 'glamor'; const listStyling = css({ - paddingBottom: 0, + paddingBottom: '1px', margin: 0, maxHeight: '345px', wordBreak: 'break-word', - width: '218px', + width: '250px', overflowY: 'auto', listStyleType: 'none', paddingLeft: 0 diff --git a/client/app/components/SearchBar.jsx b/client/app/components/SearchBar.jsx index 6544b35e549..a229126bf8b 100644 --- a/client/app/components/SearchBar.jsx +++ b/client/app/components/SearchBar.jsx @@ -97,6 +97,7 @@ export default class SearchBar extends React.Component { id = uuid.v4(), inputProps, value, + defaultValue, loading, onClearSearch, isSearchAhead, @@ -153,6 +154,7 @@ export default class SearchBar extends React.Component { } placeholder={placeholder} value={value} + defaultValue={defaultValue} {...inputProps} /> {hasInternalText && ( @@ -256,6 +258,11 @@ SearchBar.propTypes = { */ value: PropTypes.string, + /** + * The defaultValue of the `input` element + */ + defaultValue: PropTypes.string, + /** * Callback fired when search is initiated, either but pressing search or enter if `submitUsingEnterKey` is true * diff --git a/client/app/components/TableFilter.jsx b/client/app/components/TableFilter.jsx index c7851170fd6..2aea1bc7d52 100644 --- a/client/app/components/TableFilter.jsx +++ b/client/app/components/TableFilter.jsx @@ -8,6 +8,8 @@ import COPY from '../../COPY'; import FilterIcon from './icons/FilterIcon'; import QueueDropdownFilter from '../queue/QueueDropdownFilter'; import FilterOption from './FilterOption'; +import DateSelector from './DateSelector'; +import DatePicker from './DatePicker'; const iconStyle = css( { @@ -137,13 +139,17 @@ class TableFilter extends React.PureComponent { // // Adds the text (string) for a filtered value to an internal list. The list holds all the // values to filter by. - updateSelectedFilter = (value, columnName) => { + updateSelectedFilter = (value, columnName, resetValue) => { const { filteredByList } = this.props; const filtersForColumn = _.get(filteredByList, String(columnName)); let newFilters = []; - if (filtersForColumn) { - if (filtersForColumn.includes(value)) { + if (filtersForColumn || resetValue) { + if (resetValue) { + if (value !== '') { + newFilters = [value]; + } + } else if (filtersForColumn.includes(value)) { newFilters = _.pull(filtersForColumn, value); } else { newFilters = filtersForColumn.concat([value]); @@ -189,7 +195,9 @@ class TableFilter extends React.PureComponent { columnName, anyFiltersAreSet, valueName, - getFilterValues + getFilterValues, + dateFilter, + filterType, } = this.props; const filterOptions = tableData && columnName ? @@ -201,29 +209,64 @@ class TableFilter extends React.PureComponent { // not display correctly when they are checked. getFilterValues; - return ( - - - - {this.state.open && - - this.updateSelectedFilter(value, columnName)} /> - - } - - ); + const renderFilterIcon = () => { + return ( + + + + {this.state.open && + + this.updateSelectedFilter(value, columnName, false)} /> + + } + + ); + }; + + const formatDate = (date) => new Date(date).toLocaleDateString('en-US', { timeZone: 'UTC' }); + + let filter = ''; + + if (dateFilter) { + filter = ( + + this.updateSelectedFilter(formatDate(value), columnName, reset)} /> + + ); + } else if (filterType === 'date-picker') { + + const dates = _.get(this.props.filteredByList, String(columnName)); + const filterSettings = this.props.filterSettings || {}; + + filter = ( this.updateSelectedFilter(value, columnName, true)} + settings={filterSettings} + selected={this.isFilterOpen()} /> + ); + } else { + filter = renderFilterIcon(); + } + + return filter; } } @@ -247,6 +290,9 @@ TableFilter.propTypes = { updateFilters: PropTypes.func, filterOptionsFromApi: PropTypes.array, multiValueDelimiter: PropTypes.string, + dateFilter: PropTypes.bool, + filterType: PropTypes.string, + filterSettings: PropTypes.object, }; export default TableFilter; diff --git a/client/app/components/TextField.jsx b/client/app/components/TextField.jsx index 8b1c1e44c97..094e88b1a90 100644 --- a/client/app/components/TextField.jsx +++ b/client/app/components/TextField.jsx @@ -115,6 +115,7 @@ export const TextField = (props) => { max={max} autoComplete={autoComplete} disabled={disabled} + aria-label={ariaLabelText} {...inputProps} {...ariaLabelObj} {...inputStyling} diff --git a/client/app/components/icons/ExternalLinkIcon.jsx b/client/app/components/icons/ExternalLinkIcon.jsx index d4718daef54..6dd7a1d3246 100644 --- a/client/app/components/icons/ExternalLinkIcon.jsx +++ b/client/app/components/icons/ExternalLinkIcon.jsx @@ -32,7 +32,7 @@ export const ExternalLinkIcon = (props) => { 12.562375,12.0997312 12.562375,11.3774715 L12.562375,8.46400456 C12.562375,8.37888838 12.535625,8.30904784 12.4819375,8.25448292 C12.4284063,8.19969476 12.3599688,8.17242825 12.2767813,8.17242825 Z" - id="external-link-outline-box"> + > + 15.8009687,0.172974943 Z"> diff --git a/client/app/hearings/HearingsApp.jsx b/client/app/hearings/HearingsApp.jsx index 299029f8c48..8c20aa638e9 100644 --- a/client/app/hearings/HearingsApp.jsx +++ b/client/app/hearings/HearingsApp.jsx @@ -1,4 +1,4 @@ -import { BrowserRouter, Switch } from 'react-router-dom'; +import { BrowserRouter, Switch, useLocation } from 'react-router-dom'; import { detect } from 'detect-browser'; import Footer from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/Footer'; import PropTypes from 'prop-types'; @@ -14,6 +14,7 @@ import AssignHearingsContainer from './containers/AssignHearingsContainer'; import BuildScheduleContainer from './containers/BuildScheduleContainer'; import BuildScheduleUploadContainer from './containers/BuildScheduleUploadContainer'; import DailyDocketContainer from './containers/DailyDocketContainer'; +import { TranscriptionSettingsContainer } from './containers/TranscriptionSettingsContainer'; import { HearingDetailsContainer } from './containers/DetailsContainer'; import HearingWorksheetContainer from './containers/HearingWorksheetContainer'; import HearingWorksheetPrintAllContainer from './containers/HearingWorksheetPrintAllContainer'; @@ -23,6 +24,9 @@ import PageRoute from '../components/PageRoute'; import ReviewAssignmentsContainer from './containers/ReviewAssignmentsContainer'; import ScrollToTop from '../components/ScrollToTop'; import UnsupportedBrowserBanner from '../components/UnsupportedBrowserBanner'; +import { TranscriptionFileDispatchView } from './components/TranscriptionFileDispatchView'; +import ConfirmWorkOrderModal from './components/transcriptionProcessing/ConfirmWorkOrderModal'; +import { WorkOrderDetails } from './components/WorkOrderDetails'; export default class HearingsApp extends React.PureComponent { userPermissionProps = () => { @@ -119,6 +123,28 @@ export default class HearingsApp extends React.PureComponent { ; }; + routeForTranscriptionFileDispatch = () => + + + routeForWorkOrderSummary =() => { + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const taskNumber = queryParams.get('taskNumber'); + + return ; + } + routeForTranscriptionSettings = ({ match: history }) => ( + + + + ); + + routeForConfirmWorkOrder = ({ history }) => ( + + history.goBack()} /> + + ); + render = () => + + + +