From 10550dfb972937f93818df7c8c25d9ad24891a9b Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Thu, 4 Apr 2024 09:22:53 +0100 Subject: [PATCH 1/6] Add FakeData::StaffGenerator This encapsulates the logic related to generating staff users. --- app/lib/fake_data/staff_generator.rb | 39 ++++++++++++++++++++++ lib/tasks/example_data.rake | 34 +------------------ spec/factories/staff.rb | 2 +- spec/lib/fake_data/staff_generator_spec.rb | 13 ++++++++ 4 files changed, 54 insertions(+), 34 deletions(-) create mode 100644 app/lib/fake_data/staff_generator.rb create mode 100644 spec/lib/fake_data/staff_generator_spec.rb diff --git a/app/lib/fake_data/staff_generator.rb b/app/lib/fake_data/staff_generator.rb new file mode 100644 index 0000000000..ae9814da07 --- /dev/null +++ b/app/lib/fake_data/staff_generator.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class FakeData::StaffGenerator + include ServicePattern + + def call + RECORDS.each do |record| + Staff.create!(confirmed_at: Time.zone.now, password: "password", **record) + end + end + + RECORDS = [ + { + name: "Dave (assessor)", + email: "assessor.dave@education.gov.uk", + assess_permission: true, + }, + { + name: "Beryl (assessor)", + email: "assessor.beryl@education.gov.uk", + assess_permission: true, + }, + { + name: "Jeff (admin)", + email: "admin.jeff@education.gov.uk", + verify_permission: true, + }, + { + name: "Sally (manager)", + email: "manager.sally@education.gov.uk", + change_name_permission: true, + change_work_history_permission: true, + reverse_decision_permission: true, + support_console_permission: true, + withdraw_permission: true, + }, + { name: "Antonio (helpdesk)", email: "helpdesk.antonio@education.gov.uk" }, + ].freeze +end diff --git a/lib/tasks/example_data.rake b/lib/tasks/example_data.rake index 6ab525f458..642ff90b03 100644 --- a/lib/tasks/example_data.rake +++ b/lib/tasks/example_data.rake @@ -14,9 +14,7 @@ namespace :example_data do Faker::Config.locale = "en-GB" Faker::UniqueGenerator.clear - staff_members.each do |staff| - FactoryBot.create(:staff, :with_change_email_permission, **staff) - end + FakeData::StaffGenerator.call create_application_forms end @@ -51,36 +49,6 @@ namespace :example_data do task regenerate: %i[reset generate] end -def staff_members - [ - { - name: "Dave (assessor)", - email: "assessor.dave@education.gov.uk", - assess_permission: true, - }, - { - name: "Beryl (assessor)", - email: "assessor.beryl@education.gov.uk", - assess_permission: true, - }, - { - name: "Jeff (admin)", - email: "admin.jeff@education.gov.uk", - verify_permission: true, - }, - { - name: "Sally (manager)", - email: "manager.sally@education.gov.uk", - change_name_permission: true, - change_work_history_permission: true, - reverse_decision_permission: true, - support_console_permission: true, - withdraw_permission: true, - }, - { name: "Antonio (helpdesk)", email: "helpdesk.antonio@education.gov.uk" }, - ] -end - def evidential_traits_for(region) checks = [region.status_check, region.sanction_check] diff --git a/spec/factories/staff.rb b/spec/factories/staff.rb index 141e09a9d3..d2339bfcfb 100644 --- a/spec/factories/staff.rb +++ b/spec/factories/staff.rb @@ -53,7 +53,7 @@ # FactoryBot.define do factory :staff do - sequence(:email) { |n| "test#{n}@example.org" } + email { Faker::Internet.email } password { "example123" } name { Faker::Name.name } confirmed_at { Time.zone.now } diff --git a/spec/lib/fake_data/staff_generator_spec.rb b/spec/lib/fake_data/staff_generator_spec.rb new file mode 100644 index 0000000000..bc61b913e7 --- /dev/null +++ b/spec/lib/fake_data/staff_generator_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe FakeData::StaffGenerator do + describe "#call" do + subject(:call) { described_class.call } + + it "doesn't raise an error" do + expect { call }.to_not raise_error + end + end +end From e95ae839ed6ff9f4abd58ed64a312517c7dac43f Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Fri, 5 Apr 2024 07:52:17 +0100 Subject: [PATCH 2/6] Add FakeData::DateGenerator This adds a class which encapsulates the logic related to generating dates for fake applications. --- app/lib/fake_data/date_generator.rb | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 app/lib/fake_data/date_generator.rb diff --git a/app/lib/fake_data/date_generator.rb b/app/lib/fake_data/date_generator.rb new file mode 100644 index 0000000000..0a4c963e3d --- /dev/null +++ b/app/lib/fake_data/date_generator.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "active_support/testing/time_helpers" + +class FakeData::DateGenerator + include ActiveSupport::Testing::TimeHelpers + + def initialize + @date = Faker::Time.between(from: 6.months.ago, to: 3.months.ago) + end + + attr_reader :date + + def next_short + @date = Faker::Time.between(from: date, to: date + 1.hour) + end + + def next_long + @date = Faker::Time.between(from: date, to: date + 2.weeks) + end + + def travel_to_date(&block) + travel_to(date, &block) + end + + def travel_to_next_short(&block) + travel_to(next_short, &block) + end + + def travel_to_next_long(&block) + travel_to(next_long, &block) + end +end From 91f32117618b864264b6b5af0a4919ea004db6c3 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Fri, 5 Apr 2024 12:46:00 +0100 Subject: [PATCH 3/6] Add FakeData::ApplicationFormParameters This encapsulates the logic related to which kinds of fake applications we might want to generate. --- .../fake_data/application_form_parameters.rb | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 app/lib/fake_data/application_form_parameters.rb diff --git a/app/lib/fake_data/application_form_parameters.rb b/app/lib/fake_data/application_form_parameters.rb new file mode 100644 index 0000000000..828c8b1e87 --- /dev/null +++ b/app/lib/fake_data/application_form_parameters.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +class FakeData::ApplicationFormParameters + def initialize( + submitted: false, + pre_assessment: false, + assessment: false, + further_information: false, + verification: false, + review: false, + requested: false, + received: false, + expired: false, + awarded: false, + declined: false + ) + @submitted = submitted + @pre_assessment = pre_assessment + @assessment = assessment + @further_information = further_information + @verification = verification + @review = review + + @requested = requested + @received = received + @expired = expired + + @awarded = awarded + @declined = declined + end + + def submit? + @submitted || @pre_assessment || @assessment || @further_information || + @verification || @review || @awarded || @declined + end + + def pre_assess? + @pre_assessment || @assessment || @further_information || @verification || + @review || @awarded || @declined + end + + def decline_after_pre_assessment? + @pre_assessment && @declined + end + + def assess? + @assessment || @further_information || @verification || @review || + @awarded || @declined + end + + def decline_after_assessment? + @assessment && @declined + end + + def request_further_information? + @further_information + end + + def receive_further_information? + @further_information && (verify? || @received) + end + + def decline_after_further_information? + @further_information && @declined + end + + def verify? + @verification || @review || @awarded || @declined + end + + def review? + @review || (@verification && @declined) + end + + def decline_after_review? + (@review && @declined) || (@verification && @declined) + end + + def receive_professional_standing? + assess? || (@pre_assessment && @received) + end + + def request_verification? + review? || receive_verification? || expire_verification? || + (@verification && @requested) + end + + def receive_verification? + review? || award? || (@verification && @received) + end + + def expire_verification? + @verification && @expired + end + + def award? + @awarded + end +end From 5160179e69747df1cbd00f48344f47eb6f41d033 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Fri, 5 Apr 2024 13:04:20 +0100 Subject: [PATCH 4/6] Add FakeData::ApplicationFormGenerator This generates fake applications based on the given parameters and the region. --- app/components/status_tag/component.rb | 1 + .../fake_data/application_form_generator.rb | 579 ++++++++++++++++++ app/models/concerns/requestable.rb | 1 + spec/factories/application_forms.rb | 11 - .../application_form_generator_spec.rb | 16 + .../services/destroy_application_form_spec.rb | 1 + 6 files changed, 598 insertions(+), 11 deletions(-) create mode 100644 app/lib/fake_data/application_form_generator.rb create mode 100644 spec/lib/fake_data/application_form_generator_spec.rb diff --git a/app/components/status_tag/component.rb b/app/components/status_tag/component.rb index 327025bd04..38b8fa83c1 100644 --- a/app/components/status_tag/component.rb +++ b/app/components/status_tag/component.rb @@ -46,6 +46,7 @@ def tags pre_assessment: "pink", preliminary_check: "pink", received: "purple", + received_and_overdue: "pink", received_consent: "purple", received_ecctis: "purple", received_further_information: "purple", diff --git a/app/lib/fake_data/application_form_generator.rb b/app/lib/fake_data/application_form_generator.rb new file mode 100644 index 0000000000..43e728e485 --- /dev/null +++ b/app/lib/fake_data/application_form_generator.rb @@ -0,0 +1,579 @@ +# frozen_string_literal: true + +class FakeData::ApplicationFormGenerator + include ServicePattern + + def initialize(region:, params:) + @region = region + @params = FakeData::ApplicationFormParameters.new(**params) + end + + def call + create_application_form + + return application_form unless params.submit? + + submit_application_form + + return application_form unless params.pre_assess? + + if application_form.requires_preliminary_check + pre_assess_application_form(decline: params.decline_after_pre_assessment?) + end + + if application_form.teaching_authority_provides_written_statement && + params.receive_professional_standing? + receive_professional_standing + end + + if params.decline_after_pre_assessment? + decline_application_form + return application_form + end + + return application_form unless params.assess? + + assess_application_form( + decline: params.decline_after_assessment?, + further_information: params.request_further_information?, + ) + + if params.decline_after_assessment? + decline_application_form + return application_form + end + + request_further_information if params.request_further_information? + + receive_further_information if params.receive_further_information? + + if params.decline_after_further_information? + review_further_information(passed: false) + decline_application_form + return application_form + end + + return application_form unless params.verify? + + if params.receive_further_information? + review_further_information(passed: true) + end + + verify_application_form + + return application_form unless params.request_verification? + + request_verification + + overdue_verification if params.expire_verification? + + return application_form unless params.receive_verification? + + receive_verification + + unless params.review? || params.decline_after_review? || params.award? + return application_form + end + + verify_verification(passed: !params.review?) + + if params.decline_after_review? + review_verification(passed: false) + decline_application_form + elsif params.award? + review_verification(passed: true) if params.review? + award_application_form + end + + application_form + end + + private + + attr_reader :region, :params, :application_form + + delegate :assessment, to: :application_form + + def create_application_form + traits = [] + + if params.submit? + traits += %i[ + with_personal_information + with_degree_qualification + with_identification_document + with_age_range + with_subjects + ] + + traits << %i[ + with_english_language_medium_of_instruction + with_english_language_provider + with_english_language_exemption_by_citizenship + with_english_language_exemption_by_qualification + ].sample + + unless region.application_form_skip_work_history + traits << :with_work_history + end + + if region.status_check_written? || region.sanction_check_written? + traits << :with_written_statement + end + + if region.status_check_online? || region.sanction_check_online? + traits << :with_registration_number + end + end + + @application_form = + FactoryBot.create( + :application_form, + *traits, + created_at: date_generator.date, + region:, + ) + end + + def submit_application_form + date_generator.travel_to_next_long do + SubmitApplicationForm.call( + application_form:, + user: application_form.teacher, + ) + end + end + + def pre_assess_application_form(decline:) + date_generator.next_long + + user = admin_user + + assessment.sections.preliminary.each do |assessment_section| + params = + if decline + { + passed: false, + selected_failure_reasons: { + assessment_section.failure_reasons.sample => { + notes: "", + }, + }, + } + else + { passed: true, selected_failure_reasons: {} } + end + + date_generator.travel_to_next_short do + UpdateAssessmentSection.call(assessment_section:, user:, params:) + end + end + + AssignApplicationFormAssessor.call(application_form:, user:, assessor: nil) + end + + def receive_professional_standing + requestable = assessment.professional_standing_request + + date_generator.travel_to_next_long do + if application_form.teaching_authority_provides_written_statement + requestable.update!(location_note: Faker::Lorem.sentence) + end + + ReceiveRequestable.call(requestable:, user: admin_user) + end + end + + def assess_application_form(decline: false, further_information: false) + user = assessor_user + + assessment.sections.not_preliminary.each do |assessment_section| + params = + if decline || further_information + failure_reasons = + assessment_section.failure_reasons.filter do |failure_reason| + ( + further_information && + FailureReasons.further_information?(failure_reason) + ) || (decline && FailureReasons.decline?(failure_reason)) + end + + if failure_reasons.empty? + { passed: true, selected_failure_reasons: {} } + else + { + passed: false, + selected_failure_reasons: + failure_reasons + .sample(rand(1..3)) + .index_with do |failure_reason| + { + notes: Faker::Lorem.sentence, + work_histories: + ( + if FailureReasons.chooses_work_history?( + failure_reason, + ) + application_form.work_histories + else + [] + end + ), + } + end, + } + end + else + { passed: true, selected_failure_reasons: {} } + end + + date_generator.travel_to_next_long do + if assessment_section.age_range_subjects? + VerifyAgeRangeSubjects.call( + assessment:, + user:, + age_range_min: assessment.age_range_min, + age_range_max: assessment.age_range_max, + age_range_note: Faker::Lorem.sentence, + subjects: Subject.all.sample(rand(1..3)).map(&:value), + subjects_note: Faker::Lorem.sentence, + ) + end + + UpdateAssessmentSection.call(assessment_section:, user:, params:) + end + end + end + + def request_further_information + date_generator.travel_to_next_short do + RequestFurtherInformation.call( + assessment:, + user: application_form.assessor, + ) + end + end + + def receive_further_information + assessment + .further_information_requests + .each do |further_information_request| + further_information_request.items.each do |item| + if item.text? + item.update!(response: Faker::Lorem.sentence) + elsif item.document? + FactoryBot.create(:upload, document: item.document) + elsif item.work_history_contact? + item.update!( + contact_name: Faker::Name.name, + contact_job: Faker::Job.title, + contact_email: Faker::Internet.email, + ) + end + end + + date_generator.travel_to_next_long do + ReceiveRequestable.call( + requestable: further_information_request, + user: application_form.teacher, + ) + end + end + end + + def review_further_information(passed:) + assessment + .further_information_requests + .each do |further_information_request| + date_generator.travel_to_next_long do + ReviewRequestable.call( + requestable: further_information_request, + user: assessor_user, + passed:, + note: Faker::Lorem.sentence, + ) + end + end + end + + def verify_application_form + professional_standing = + if application_form.teaching_authority_provides_written_statement || + application_form.reduced_evidence_accepted || + !application_form.needs_work_history + false + else + [true, false].sample + end + + qualifications = application_form.qualifications.sample(rand(0..2)) + qualifications_assessor_note = Faker::Lorem.sentence + + work_histories = + if application_form.reduced_evidence_accepted || + !application_form.needs_work_history + [] + else + application_form.work_histories + end + + date_generator.travel_to_next_long do + VerifyAssessment.call( + assessment:, + user: assessor_user, + professional_standing:, + qualifications:, + qualifications_assessor_note:, + work_histories:, + ) + end + end + + def request_verification + user = admin_user + + date_generator.next_long + + if assessment.professional_standing_request.present? && + !application_form.teaching_authority_provides_written_statement + date_generator.travel_to_date do + RequestRequestable.call( + requestable: assessment.professional_standing_request, + user:, + ) + end + end + + assessment.qualification_requests.each do |requestable| + requestable.update!( + consent_method: %w[ + signed_ecctis + signed_institution + unsigned + none + ].sample, + ) + + if requestable.consent_method_signed? + consent_request = + assessment.consent_requests.create!( + qualification: requestable.qualification, + ) + FactoryBot.create( + :upload, + document: consent_request.unsigned_consent_document, + ) + elsif requestable.consent_method_unsigned? + assessment.update!(unsigned_consent_document_generated: true) + end + + next if requestable.consent_method_signed? + date_generator.travel_to_next_short do + RequestRequestable.call(requestable:, user:) + end + end + + date_generator.travel_to_next_short do + RequestConsent.call(assessment:, user:) + end + end + + def receive_verification + user = admin_user + + if assessment.professional_standing_request.present? && + !application_form.teaching_authority_provides_written_statement + receive_professional_standing + end + + assessment.reference_requests.each do |requestable| + contact_response = [true, false].sample + dates_response = [true, false].sample + hours_response = [true, false].sample + children_response = [true, false].sample + lessons_response = [true, false].sample + reports_response = [true, false].sample + misconduct_response = [true, false].sample + satisfied_response = [true, false].sample + + requestable.update!( + contact_response:, + contact_name: contact_response ? "" : Faker::Name.name, + contact_job: contact_response ? "" : Faker::Job.title, + contact_comment: contact_response ? "" : Faker::Lorem.sentence, + dates_response:, + dates_comment: dates_response ? "" : Faker::Lorem.sentence, + hours_response:, + hours_comment: hours_response ? "" : Faker::Lorem.sentence, + children_response:, + children_comment: children_response ? "" : Faker::Lorem.sentence, + lessons_response:, + lessons_comment: lessons_response ? "" : Faker::Lorem.sentence, + reports_response:, + reports_comment: reports_response ? "" : Faker::Lorem.sentence, + misconduct_response:, + misconduct_comment: misconduct_response ? Faker::Lorem.sentence : "", + satisfied_response:, + satisfied_comment: satisfied_response ? "" : Faker::Lorem.sentence, + additional_information_response: Faker::Lorem.sentence, + ) + + date_generator.travel_to_next_long do + ReceiveRequestable.call( + requestable:, + user: requestable.work_history.contact_email, + ) + end + end + + assessment.qualification_requests.each do |requestable| + consent_request = + assessment.consent_requests.find_by( + qualification: requestable.qualification, + ) + + if consent_request + FactoryBot.create( + :upload, + document: consent_request.signed_consent_document, + ) + + date_generator.travel_to_next_long do + ReceiveRequestable.call( + requestable: consent_request, + user: application_form.teacher, + ) + end + + date_generator.travel_to_next_long do + VerifyRequestable.call( + requestable: consent_request, + user:, + passed: true, + note: Faker::Lorem.sentence, + ) + end + + date_generator.travel_to_next_short do + RequestRequestable.call(requestable:, user:) + end + end + + date_generator.travel_to_next_long do + ReceiveRequestable.call(requestable:, user:) + end + end + end + + def overdue_verification + user = "Expirer" + + requestables + .filter(&:requested?) + .each do |requestable| + date_generator.travel_to_next_long do + ExpireRequestable.call(requestable:, user:) + end + end + end + + def verify_verification(passed:) + user = admin_user + + requestables + .reject(&:verified?) + .each do |requestable| + date_generator.travel_to_next_short do + VerifyRequestable.call( + requestable:, + user:, + passed:, + note: Faker::Lorem.sentence, + ) + end + end + + unless passed + date_generator.travel_to_next_short do + ReviewAssessment.call(assessment:, user:) + end + end + end + + def review_verification(passed:) + user = assessor_user + + requestables + .reject(&:reviewed?) + .each do |requestable| + date_generator.travel_to_next_short do + ReviewRequestable.call( + requestable:, + user:, + passed:, + note: Faker::Lorem.sentence, + ) + end + end + end + + def award_application_form + assessment.award! + + trn = Faker::Number.leading_zero_number(digits: 10) + access_your_teaching_qualifications_url = Faker::Internet.url + user = admin_user + + date_generator.travel_to_next_long do + DQTTRNRequest.create!(request_id: SecureRandom.uuid, application_form:) + ApplicationFormStatusUpdater.call(application_form:, user:) + end + + date_generator.travel_to_next_short do + AwardQTS.call( + application_form:, + user:, + trn:, + access_your_teaching_qualifications_url:, + ) + end + end + + def decline_application_form + assessment.decline! + + date_generator.travel_to_next_long do + DeclineQTS.call(application_form:, user: assessor_user) + end + end + + def requestables + assessment.reference_requests.to_a + + assessment.qualification_requests.to_a + + assessment.consent_requests.to_a + + ( + if assessment.professional_standing_request.present? && + !application_form.teaching_authority_provides_written_statement + [assessment.professional_standing_request] + else + [] + end + ) + end + + def date_generator + @date_generator ||= FakeData::DateGenerator.new + end + + def assessor_user + Staff.where(assess_permission: true).sample + end + + def admin_user + Staff.where(verify_permission: true).sample + end + + def manager_user + Staff.where(withdraw_permission: true).sample + end +end diff --git a/app/models/concerns/requestable.rb b/app/models/concerns/requestable.rb index bf4b3297cd..110fc2e1b3 100644 --- a/app/models/concerns/requestable.rb +++ b/app/models/concerns/requestable.rb @@ -15,6 +15,7 @@ module Requestable scope :respondable, -> { requested.not_received.merge(ApplicationForm.assessable) } scope :verified, -> { where.not(verified_at: nil) } + scope :not_verified, -> { where(verified_at: nil) } has_one :application_form, through: :assessment end diff --git a/spec/factories/application_forms.rb b/spec/factories/application_forms.rb index c47fa623f4..3824faccb2 100644 --- a/spec/factories/application_forms.rb +++ b/spec/factories/application_forms.rb @@ -163,17 +163,6 @@ statuses { %w[assessment_not_started] } submitted_at { Time.zone.now } working_days_since_submission { 0 } - - after(:create) do |application_form, _evaluator| - create( - :timeline_event, - :stage_changed, - application_form:, - creator: application_form.teacher, - old_value: "draft", - new_value: "not_started", - ) - end end trait :preliminary_check do diff --git a/spec/lib/fake_data/application_form_generator_spec.rb b/spec/lib/fake_data/application_form_generator_spec.rb new file mode 100644 index 0000000000..8f4e128ac3 --- /dev/null +++ b/spec/lib/fake_data/application_form_generator_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe FakeData::ApplicationFormGenerator do + describe "#call" do + subject(:call) { described_class.call(region:, params:) } + + let(:region) { create(:region) } + let(:params) { {} } + + it "doesn't raise an error" do + expect { call }.to_not raise_error + end + end +end diff --git a/spec/services/destroy_application_form_spec.rb b/spec/services/destroy_application_form_spec.rb index 3a05e3b5fe..89dab2b531 100644 --- a/spec/services/destroy_application_form_spec.rb +++ b/spec/services/destroy_application_form_spec.rb @@ -14,6 +14,7 @@ :with_work_history, ) + create(:timeline_event, :stage_changed, application_form:) create(:note, application_form:) create(:dqt_trn_request, application_form:) From 72b6f5dbd0f0796c1f10bca938b2a16edc6c9a6a Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Fri, 5 Apr 2024 13:08:32 +0100 Subject: [PATCH 5/6] Add fake data Rake tasks This replaces the existing example data tasks and uses the new fake data generation classes added in the previous commits. --- .github/actions/deploy-environment/action.yml | 2 +- .../{example-data.yaml => fake-data.yaml} | 6 +- README.md | 4 +- lib/tasks/example_data.rake | 230 ------------------ lib/tasks/fake_data.rake | 106 ++++++++ 5 files changed, 112 insertions(+), 236 deletions(-) rename .github/workflows/{example-data.yaml => fake-data.yaml} (89%) delete mode 100644 lib/tasks/example_data.rake create mode 100644 lib/tasks/fake_data.rake diff --git a/.github/actions/deploy-environment/action.yml b/.github/actions/deploy-environment/action.yml index 57ef7aa72a..550845da44 100644 --- a/.github/actions/deploy-environment/action.yml +++ b/.github/actions/deploy-environment/action.yml @@ -57,7 +57,7 @@ runs: shell: bash run: | make ci review get-cluster-credentials - kubectl exec -n tra-development deployment/apply-for-qts-review-${PULL_REQUEST_NUMBER}-web -- sh -c "cd /app && /usr/local/bin/bundle exec rails db:seed review_app:configure example_data:generate" + kubectl exec -n tra-development deployment/apply-for-qts-review-${PULL_REQUEST_NUMBER}-web -- sh -c "cd /app && /usr/local/bin/bundle exec rails db:seed review_app:configure fake_data:generate" env: PULL_REQUEST_NUMBER: ${{ inputs.pull-request-number }} diff --git a/.github/workflows/example-data.yaml b/.github/workflows/fake-data.yaml similarity index 89% rename from .github/workflows/example-data.yaml rename to .github/workflows/fake-data.yaml index 37e774efae..e165bffd6b 100644 --- a/.github/workflows/example-data.yaml +++ b/.github/workflows/fake-data.yaml @@ -1,4 +1,4 @@ -name: Example data +name: Fake data on: workflow_dispatch: @@ -16,7 +16,7 @@ jobs: name: Regenerate runs-on: ubuntu-latest - environment: ${{ github.event.inputs.environment }}_aks + environment: ${{ github.event.inputs.environment }} steps: - uses: actions/checkout@v4 @@ -35,4 +35,4 @@ jobs: kubectl exec \ -n tra-${{ github.event.inputs.environment }} \ deployment/apply-for-qts-${{ github.event.inputs.environment }}-web \ - -- sh -c "cd /app && /usr/local/bin/bundle exec rails db:seed example_data:regenerate" + -- sh -c "cd /app && /usr/local/bin/bundle exec rails db:seed fake_data:regenerate" diff --git a/README.md b/README.md index 06edb05e8f..88574799e0 100644 --- a/README.md +++ b/README.md @@ -121,10 +121,10 @@ Setup the project (re-run after `Gemfile` or `package.json` updates, automatical bin/setup ``` -Example data and personas. For development and non-production environments we have an example data generation script that includes persona based logins. +Generate fake application forms and staff. For development and non-production environments we have a script that generates useful fake data. ```bash -bundle exec rake example_data:generate +bundle exec rake fake_data:generate ``` To enable the 'personas' feature, from the rails console diff --git a/lib/tasks/example_data.rake b/lib/tasks/example_data.rake deleted file mode 100644 index 642ff90b03..0000000000 --- a/lib/tasks/example_data.rake +++ /dev/null @@ -1,230 +0,0 @@ -namespace :example_data do - desc "Create example data for testing." - task generate: :environment do - if HostingEnvironment.production? - raise "THIS TASK CANNOT BE RUN IN PRODUCTION" - end - - if Teacher.any? - puts "Noop as DB already contains data" - exit - end - - Teacher.reset_column_information - Faker::Config.locale = "en-GB" - Faker::UniqueGenerator.clear - - FakeData::StaffGenerator.call - - create_application_forms - end - - desc "Reset database suitable for generating example data." - task reset: :environment do - if HostingEnvironment.production? - raise "THIS TASK CANNOT BE RUN IN PRODUCTION" - end - - TimelineEvent.delete_all - DQTTRNRequest.delete_all - ReminderEmail.delete_all - ConsentRequest.delete_all - FurtherInformationRequestItem.delete_all - FurtherInformationRequest.delete_all - ProfessionalStandingRequest.delete_all - QualificationRequest.delete_all - ReferenceRequest.delete_all - SelectedFailureReason.delete_all - AssessmentSection.delete_all - Assessment.delete_all - Qualification.delete_all - WorkHistory.delete_all - Note.delete_all - ApplicationForm.delete_all - Teacher.delete_all - Staff.delete_all - end - - desc "Reset and regenerate example data." - task regenerate: %i[reset generate] -end - -def evidential_traits_for(region) - checks = [region.status_check, region.sanction_check] - - [].tap do |traits| - unless region.application_form_skip_work_history - traits << :with_work_history - end - traits << :with_written_statement if checks.include?("written") - traits << :with_registration_number if checks.include?("online") - end -end - -def english_language_trait - %i[ - with_english_language_medium_of_instruction - with_english_language_provider - with_english_language_exemption_by_citizenship - with_english_language_exemption_by_qualification - ].sample -end - -def application_form_traits_for(region) - evidential_traits = evidential_traits_for(region) - - traits = - %i[ - with_personal_information - with_degree_qualification - with_identification_document - with_age_range - with_subjects - ] + evidential_traits + [english_language_trait] - - [ - [], - traits, - traits + %i[submitted], - (traits + %i[preliminary_check] if region.requires_preliminary_check), - traits + %i[submitted action_required_by_external], - traits + %i[submitted action_required_by_admin], - traits + %i[awarded], - traits + %i[declined], - ].compact -end - -def create_requestables( - application_form, - assessment, - received: false, - expired: false -) - unless application_form.teaching_authority_provides_written_statement - assessment.sections.update_all(passed: true) - end - - factory_prefix = received ? "received" : "requested" - traits = expired ? %i[expired] : [] - - if application_form.teaching_authority_provides_written_statement || - (application_form.needs_written_statement && rand(4).zero?) - FactoryBot.create( - :"#{factory_prefix}_professional_standing_request", - *traits, - assessment:, - ) - - unless application_form.teaching_authority_provides_written_statement - assessment.verify! - end - elsif (work_histories = application_form.work_histories).present? && - rand(3).zero? - work_histories.each do |work_history| - FactoryBot.create( - :"#{factory_prefix}_reference_request", - *traits, - assessment:, - work_history:, - ) - end - - assessment.verify! - elsif (qualifications = application_form.qualifications).present? && - rand(2).zero? - qualifications.each do |qualification| - qualification_request = - FactoryBot.create( - :"#{factory_prefix}_qualification_request", - assessment:, - qualification:, - ) - - next unless qualification_request.consent_method_signed? - FactoryBot.create( - :"#{factory_prefix}_consent_request", - *traits, - assessment:, - qualification:, - ) - end - - assessment.verify! - elsif !expired - FactoryBot.create( - :"#{factory_prefix}_further_information_request", - *traits, - :with_items, - assessment:, - ) - assessment.request_further_information! - end - - ApplicationFormStatusUpdater.call( - application_form:, - user: "Example data generator", - ) -end - -def create_application_forms - Region.all.find_each do |region| - application_form_traits_for(region).each do |traits| - application_form = FactoryBot.create(:application_form, *traits, region:) - - next unless application_form.submitted? - - assessment = AssessmentFactory.call(application_form:) - - if application_form.reduced_evidence_accepted || - !application_form.needs_work_history - assessment.update!(induction_required: false) - end - - if CountryCode.scotland?(application_form.country.code) - assessment.update!(scotland_full_registration: true) - end - - if application_form.declined_at.present? - FactoryBot.create( - :selected_failure_reason, - :further_informationable, - assessment_section: assessment.sections.first, - key: assessment.sections.first.failure_reasons.sample, - ) - FactoryBot.create( - :selected_failure_reason, - :declinable, - assessment_section: assessment.sections.second, - key: assessment.sections.second.failure_reasons.sample, - ) - - declined_at = Faker::Time.between(from: 6.months.ago, to: Time.zone.now) - - application_form.update!( - declined_at:, - submitted_at: declined_at - 2.months, - created_at: declined_at - 2.months, - ) - elsif application_form.awarded_at.present? - awarded_at = Faker::Time.between(from: 6.months.ago, to: Time.zone.now) - - application_form.update!( - awarded_at:, - submitted_at: awarded_at - 2.months, - created_at: awarded_at - 2.months, - ) - elsif application_form.action_required_by_external? - create_requestables(application_form, assessment) - elsif application_form.action_required_by_admin? - state = %i[received expired received_and_expired].sample - - create_requestables( - application_form, - assessment, - received: %i[received received_and_expired].include?(state), - expired: %i[expired received_and_expired].include?(state), - ) - end - end - end -end diff --git a/lib/tasks/fake_data.rake b/lib/tasks/fake_data.rake new file mode 100644 index 0000000000..2ab40e9682 --- /dev/null +++ b/lib/tasks/fake_data.rake @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +namespace :fake_data do + desc "Generate fake staff and teachers." + task generate: :environment do + if HostingEnvironment.production? + raise "This task cannot be run in production." + end + + if Staff.any? || Teacher.any? + puts "Database already contains staff and teachers." + exit + end + + Faker::Config.locale = "en-GB" + Faker::UniqueGenerator.clear + + FakeData::StaffGenerator.call + + countries = Country.includes(:regions) + count = countries.count + + countries.each_with_index do |country, index| + region = country.regions.sample + + puts "#{index + 1}/#{count} - Generating applications for #{CountryName.from_country(country)}" + + application_form_params_for_region(region).each do |params| + FakeData::ApplicationFormGenerator.call(region:, params:) + end + end + + UpdateWorkingDaysJob.new.perform + end + + desc "Reset database in preparation for generating fake data." + task reset: :environment do + if HostingEnvironment.production? + raise "This task cannot be run in production." + end + + TimelineEvent.delete_all + DQTTRNRequest.delete_all + ReminderEmail.delete_all + ConsentRequest.delete_all + FurtherInformationRequestItem.delete_all + FurtherInformationRequest.delete_all + ProfessionalStandingRequest.delete_all + QualificationRequest.delete_all + ReferenceRequest.delete_all + SelectedFailureReason.delete_all + AssessmentSection.delete_all + Assessment.delete_all + Qualification.delete_all + WorkHistory.delete_all + Note.delete_all + ApplicationForm.delete_all + Teacher.delete_all + Staff.delete_all + end + + desc "Reset and regenerate fake data." + task regenerate: %i[reset generate] +end + +def application_form_params_for_region(region) + params = [{}, { submitted: true }] + + if region.requires_preliminary_check || + region.teaching_authority_provides_written_statement + params += [ + { pre_assessment: true }, + { pre_assessment: true, declined: true }, + { pre_assessment: true, received: true }, + ] + end + + if rand(5).zero? + params += [ + { further_information: true }, + { further_information: true, received: true }, + { further_information: true, declined: true }, + { further_information: true, received: true, declined: true }, + ] + end + + params += [ + { assessment: true }, + { assessment: true, declined: true }, + { verification: true }, + { verification: true, requested: true }, + { verification: true, received: true }, + ] + + params << { verification: true, expired: true } if rand(10).zero? + + if rand(20).zero? + params << { verification: true, received: true, expired: true } + end + + params << { verification: true, declined: true } + + params << { review: true } # if rand(10).zero? + + params + [{ review: rand(20).zero?, awarded: true }] +end From c4d8320a7024b9aeda71ef8088476b46ea0bb321 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Fri, 5 Apr 2024 13:35:43 +0100 Subject: [PATCH 6/6] Don't send emails when generating fake data This prevents any emails from being sent out while we're generating fake data. --- app/services/deliver_email.rb | 18 +++++++++++++++++- lib/tasks/fake_data.rake | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/services/deliver_email.rb b/app/services/deliver_email.rb index f7f1dcddb1..edd9f3b2b0 100644 --- a/app/services/deliver_email.rb +++ b/app/services/deliver_email.rb @@ -11,12 +11,28 @@ def initialize(application_form:, mailer:, action:, **params) end def call - create_job + create_job unless self.class.paused? create_timeline_event end + def self.pause + Rails.logger.info "Pausing email deliveries!" + @paused = true + end + + def self.continue + Rails.logger.info "Continuing email deliveries!" + @paused = false + end + + def self.paused? + @paused + end + private + @paused = false + attr_reader :application_form, :mailer, :action, :params def mail diff --git a/lib/tasks/fake_data.rake b/lib/tasks/fake_data.rake index 2ab40e9682..81beafdd14 100644 --- a/lib/tasks/fake_data.rake +++ b/lib/tasks/fake_data.rake @@ -17,6 +17,8 @@ namespace :fake_data do FakeData::StaffGenerator.call + DeliverEmail.pause + countries = Country.includes(:regions) count = countries.count @@ -31,6 +33,7 @@ namespace :fake_data do end UpdateWorkingDaysJob.new.perform + DeliverEmail.continue end desc "Reset database in preparation for generating fake data."