Browse Source

Allow passing a year to generating lettings validations docs task

pull/3011/head
Kat 1 month ago
parent
commit
64c4dae4f7
  1. 21
      app/services/documentation_generator.rb
  2. 47
      lib/tasks/generate_lettings_documentation.rake
  3. 202
      spec/lib/tasks/generate_lettings_documentation_spec.rb
  4. 28
      spec/services/documentation_generator_spec.rb

21
app/services/documentation_generator.rb

@ -9,9 +9,7 @@ class DocumentationGenerator
include Validations::SoftValidations
include Validations::Sales::SoftValidations
def describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type)
form = FormHandler.instance.forms["current_#{log_type}"]
def describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
all_validation_methods.each do |meth|
if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: false, log_type:, collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}").exists?
Rails.logger.info("Validation #{meth} already exists for #{form.start_date.year}")
@ -69,7 +67,7 @@ class DocumentationGenerator
end
end
def describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type)
def describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type)
validation_descriptions = {}
all_validation_methods[0..5].each do |meth|
validation_source = method(meth).source
@ -87,16 +85,11 @@ class DocumentationGenerator
validation_descriptions[meth.to_s] = result
end
current_form = FormHandler.instance.forms["current_#{log_type}"]
previous_form = FormHandler.instance.forms["previous_#{log_type}"]
[current_form, previous_form].each do |form|
interruption_screen_pages = form.pages.select { |page| page.questions.first.type == "interruption_screen" }
interruption_screen_pages_grouped_by_question = interruption_screen_pages.group_by { |page| page.questions.first.id }
interruption_screen_pages_grouped_by_question.each do |_question_id, pages|
pages.map do |page|
save_soft_validation(form, page, validation_descriptions, log_type)
end
interruption_screen_pages = form.pages.select { |page| page.questions.first.type == "interruption_screen" }
interruption_screen_pages_grouped_by_question = interruption_screen_pages.group_by { |page| page.questions.first.id }
interruption_screen_pages_grouped_by_question.each do |_question_id, pages|
pages.map do |page|
save_soft_validation(form, page, validation_descriptions, log_type)
end
end
end

47
lib/tasks/generate_lettings_documentation.rake

@ -1,6 +1,12 @@
namespace :generate_lettings_documentation do
desc "Generate documentation for hard lettings validations"
task describe_lettings_validations: :environment do
task :describe_lettings_validations, %i[year] => :environment do |_task, args|
form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:describe_lettings_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
include Validations::SetupValidations
include Validations::HouseholdValidations
@ -19,39 +25,52 @@ namespace :generate_lettings_documentation do
Validations::LocalAuthorityValidations].map { |x| x.instance_methods + x.private_instance_methods }.flatten
all_helper_methods = all_methods - all_validation_methods
DocumentationGenerator.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, "lettings")
DocumentationGenerator.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, "lettings")
end
desc "Generate documentation for soft lettings validations"
task describe_soft_lettings_validations: :environment do
task :describe_soft_lettings_validations, %i[year] => :environment do |_task, args|
include Validations::SoftValidations
form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:describe_soft_lettings_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
all_helper_methods = Validations::SoftValidations.private_instance_methods
all_validation_methods = Validations::SoftValidations.instance_methods
DocumentationGenerator.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, "lettings")
DocumentationGenerator.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, "lettings")
end
desc "Generate documentation for hard bu lettings validations"
task describe_bu_lettings_validations: :environment do
task :describe_bu_lettings_validations, %i[year] => :environment do |_task, args|
form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:describe_bu_lettings_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
row_parser_class = "BulkUpload::Lettings::Year#{form_year}::RowParser".constantize
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
[[FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2023, "lettings")], BulkUpload::Lettings::Year2023::RowParser],
[FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2024, "lettings")], BulkUpload::Lettings::Year2024::RowParser]].each do |form, row_parser_class|
all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") }
all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") }
all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods
all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods
field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors")
DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "lettings")
end
field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors")
DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "lettings")
end
desc "Generate documentation for lettings numeric validations"
task add_numeric_lettings_validations: :environment do
form = FormHandler.instance.forms["current_lettings"]
task :add_numeric_lettings_validations, %i[year] => :environment do |_task, args|
form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:add_numeric_lettings_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
form.numeric_questions.each do |question|
next unless question.min || question.max

202
spec/lib/tasks/generate_lettings_documentation_spec.rb

@ -6,14 +6,24 @@ RSpec.describe "generate_lettings_documentation" do
subject(:task) { Rake::Task["generate_lettings_documentation:add_numeric_lettings_validations"] }
before do
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => FormHandler.instance.forms["current_lettings"], "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "when the rake task is run" do
it "creates new validation documentation records" do
expect { task.invoke }.to change(LogValidation, :count)
allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return(FormHandler.instance.forms["current_lettings"])
expect { task.invoke(2024) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "minimum").count).to be_positive
expect(LogValidation.where(validation_name: "range").count).to be_positive
any_min_validation = LogValidation.where(validation_name: "minimum").first
@ -30,8 +40,194 @@ RSpec.describe "generate_lettings_documentation" do
end
it "skips if the validation already exists in the database" do
task.invoke
expect { task.invoke }.not_to change(LogValidation, :count)
task.invoke(2024)
expect { task.invoke(2024) }.not_to change(LogValidation, :count)
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:add_numeric_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
end
describe ":describe_lettings_validations", type: :task do
subject(:task) { Rake::Task["generate_lettings_documentation:describe_lettings_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => "2024_form", "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_lettings").and_return("2025_form")
expect(documentation_generator).to receive(:describe_hard_validations).with(client, "2025_form", anything, anything, "lettings")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return("2024_form")
expect(documentation_generator).to receive(:describe_hard_validations).with(client, "2024_form", anything, anything, "lettings")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:describe_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
describe ":describe_bu_lettings_validations", type: :task do
subject(:task) { Rake::Task["generate_lettings_documentation:describe_bu_lettings_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => "2024_form", "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_lettings").and_return("2025_form")
expect(documentation_generator).to receive(:describe_bu_validations).with(client, "2025_form", anything, anything, anything, anything, "lettings")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return("2024_form")
expect(documentation_generator).to receive(:describe_bu_validations).with(client, "2024_form", anything, anything, anything, anything, "lettings")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:describe_bu_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
describe ":describe_soft_lettings_validations", type: :task do
subject(:task) { Rake::Task["generate_lettings_documentation:describe_soft_lettings_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => "2024_form", "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_lettings").and_return("2025_form")
expect(documentation_generator).to receive(:describe_soft_validations).with(client, "2025_form", anything, anything, "lettings")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return("2024_form")
expect(documentation_generator).to receive(:describe_soft_validations).with(client, "2024_form", anything, anything, "lettings")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:describe_soft_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end

28
spec/services/documentation_generator_spec.rb

@ -17,10 +17,11 @@ describe DocumentationGenerator do
describe ":describe_hard_validations" do
context "when the service is run with lettings type" do
let(:log_type) { "lettings" }
let(:form) { FormHandler.instance.forms["current_lettings"] }
it "creates new validation documentation records" do
expect(Rails.logger).to receive(:info).with(/described/).at_least(:once)
expect { described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect { described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "validate_numeric_min_max").count).to eq(1)
any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.")
@ -37,12 +38,12 @@ describe DocumentationGenerator do
it "calls the client" do
expect(client).to receive(:chat)
described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type)
described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end
it "skips if the validation already exists in the database" do
described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type)
expect { described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count)
described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
expect { described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count)
end
context "when the response is not a JSON" do
@ -51,7 +52,7 @@ describe DocumentationGenerator do
it "raises an error" do
expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once)
expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once)
described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type)
described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end
end
@ -61,17 +62,18 @@ describe DocumentationGenerator do
it "raises an error" do
expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once)
expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once)
described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type)
described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end
end
end
context "when the service is run with sales type" do
let(:log_type) { "sales" }
let(:form) { FormHandler.instance.forms["current_sales"] }
it "creates new validation documentation records" do
expect(Rails.logger).to receive(:info).with(/described/).at_least(:once)
expect { described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect { described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "validate_numeric_min_max").count).to eq(1)
any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.")
@ -97,9 +99,10 @@ describe DocumentationGenerator do
context "when the service is run for lettings" do
let(:log_type) { "lettings" }
let(:form) { FormHandler.instance.forms["current_lettings"] }
it "creates new validation documentation records" do
expect { described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect { described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "rent_soft_validation_triggered?").count).to be_positive
any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.")
@ -115,21 +118,22 @@ describe DocumentationGenerator do
it "calls the client" do
expect(client).to receive(:chat)
described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type)
described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end
it "skips if the validation already exists in the database" do
described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type)
expect { described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count)
described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type)
expect { described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count)
end
end
context "when the service is run for sales" do
let(:log_type) { "sales" }
let(:form) { FormHandler.instance.forms["current_sales"] }
let(:all_validation_methods) { ["income2_outside_soft_range_for_ecstat?"] }
it "creates new validation documentation records" do
expect { described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect { described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "income2_outside_soft_range_for_ecstat?").count).to be_positive
any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.")

Loading…
Cancel
Save