Browse Source

Allow passing a year to generating lettings validations docs task

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

11
app/services/documentation_generator.rb

@ -9,9 +9,7 @@ class DocumentationGenerator
include Validations::SoftValidations include Validations::SoftValidations
include Validations::Sales::SoftValidations include Validations::Sales::SoftValidations
def describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) def describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
form = FormHandler.instance.forms["current_#{log_type}"]
all_validation_methods.each do |meth| 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? 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}") Rails.logger.info("Validation #{meth} already exists for #{form.start_date.year}")
@ -69,7 +67,7 @@ class DocumentationGenerator
end end
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 = {} validation_descriptions = {}
all_validation_methods[0..5].each do |meth| all_validation_methods[0..5].each do |meth|
validation_source = method(meth).source validation_source = method(meth).source
@ -87,10 +85,6 @@ class DocumentationGenerator
validation_descriptions[meth.to_s] = result validation_descriptions[meth.to_s] = result
end 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 = 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 = interruption_screen_pages.group_by { |page| page.questions.first.id }
interruption_screen_pages_grouped_by_question.each do |_question_id, pages| interruption_screen_pages_grouped_by_question.each do |_question_id, pages|
@ -99,7 +93,6 @@ class DocumentationGenerator
end end
end end
end end
end
private private

39
lib/tasks/generate_lettings_documentation.rake

@ -1,6 +1,12 @@
namespace :generate_lettings_documentation do namespace :generate_lettings_documentation do
desc "Generate documentation for hard lettings validations" 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"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
include Validations::SetupValidations include Validations::SetupValidations
include Validations::HouseholdValidations include Validations::HouseholdValidations
@ -19,27 +25,37 @@ namespace :generate_lettings_documentation do
Validations::LocalAuthorityValidations].map { |x| x.instance_methods + x.private_instance_methods }.flatten Validations::LocalAuthorityValidations].map { |x| x.instance_methods + x.private_instance_methods }.flatten
all_helper_methods = all_methods - all_validation_methods 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 end
desc "Generate documentation for soft lettings validations" 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 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"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
all_helper_methods = Validations::SoftValidations.private_instance_methods all_helper_methods = Validations::SoftValidations.private_instance_methods
all_validation_methods = Validations::SoftValidations.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 end
desc "Generate documentation for hard bu lettings validations" 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"]) 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
@ -47,11 +63,14 @@ namespace :generate_lettings_documentation do
field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors") 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") DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "lettings")
end end
end
desc "Generate documentation for lettings numeric validations" desc "Generate documentation for lettings numeric validations"
task add_numeric_lettings_validations: :environment do task :add_numeric_lettings_validations, %i[year] => :environment do |_task, args|
form = FormHandler.instance.forms["current_lettings"] 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| form.numeric_questions.each do |question|
next unless question.min || question.max 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"] } subject(:task) { Rake::Task["generate_lettings_documentation:add_numeric_lettings_validations"] }
before do 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.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment) Rake::Task.define_task(:environment)
task.reenable task.reenable
end end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "when the rake task is run" do context "when the rake task is run" do
it "creates new validation documentation records" 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: "minimum").count).to be_positive
expect(LogValidation.where(validation_name: "range").count).to be_positive expect(LogValidation.where(validation_name: "range").count).to be_positive
any_min_validation = LogValidation.where(validation_name: "minimum").first any_min_validation = LogValidation.where(validation_name: "minimum").first
@ -30,8 +40,194 @@ RSpec.describe "generate_lettings_documentation" do
end end
it "skips if the validation already exists in the database" do it "skips if the validation already exists in the database" do
task.invoke task.invoke(2024)
expect { task.invoke }.not_to change(LogValidation, :count) 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 end
end end

28
spec/services/documentation_generator_spec.rb

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

Loading…
Cancel
Save