diff --git a/app/controllers/reports/annual_reports_controller.rb b/app/controllers/reports/annual_reports_controller.rb index 76bc4aff23..774b21e301 100644 --- a/app/controllers/reports/annual_reports_controller.rb +++ b/app/controllers/reports/annual_reports_controller.rb @@ -4,11 +4,11 @@ class Reports::AnnualReportsController < ApplicationController def index # 2813_update_annual_report -- changed to earliest_reporting_year # so that we can do system tests and staging - foundation_year = current_organization.earliest_reporting_year + @foundation_year = current_organization.earliest_reporting_year @actual_year = Time.current.year - @years = (foundation_year...@actual_year).to_a + @years = (@foundation_year...@actual_year).to_a @month_remaining_to_report = 12 - Time.current.month end @@ -32,12 +32,35 @@ def recalculate redirect_to reports_annual_report_path(year), notice: "Recalculated annual report!" end + def range + year_start = range_params[:year_start].to_i + year_end = range_params[:year_end].to_i + + if year_end < year_start + flash[:error] = "End year must be greater than or equal to start year." + redirect_to reports_annual_reports_path and return + end + + reports = Reports::AnnualSurveyReportService.new(organization: current_organization, year_start: year_start, year_end: year_end).call + + respond_to do |format| + format.csv do + send_data Exports::ExportReportCSVService.new(reports:).generate_csv(range: true), + filename: "NdbnAnnuals-#{year_start}-#{year_end}.csv" + end + end + end + private def year_param params.require(:year) end + def range_params + params.permit(:year_start, :year_end) + end + def validate_show_params not_found! unless year_param.to_i.positive? end diff --git a/app/services/exports/export_report_csv_service.rb b/app/services/exports/export_report_csv_service.rb index ad6c51da99..1707277d2d 100644 --- a/app/services/exports/export_report_csv_service.rb +++ b/app/services/exports/export_report_csv_service.rb @@ -7,8 +7,8 @@ def initialize(reports:) @reports = reports end - def generate_csv - csv_data = generate_csv_data + def generate_csv(range: false) + csv_data = range ? generate_range_csv_data : generate_csv_data ::CSV.generate(headers: true) do |csv| csv_data.each { |row| csv << row } @@ -31,5 +31,36 @@ def generate_csv_data csv_data end + + def generate_range_csv_data + return [] if @reports.empty? + + # Ordered unique headers (in first-seen order) + header_index = {} + + # Cache each year's flattened entries so we don't re-walk twice + yearly_entries = @reports.map do |report| + entries = {} + + report.all_reports.each do |section| + section.fetch("entries", {}).each do |key, value| + header_index[key] ||= true + entries[key] = value + end + end + + {year: report["year"], entries: entries} + end + + headers = header_index.keys + csv_data = [] + csv_data << ["Year"] + headers + + yearly_entries.each do |row| + csv_data << [row[:year]] + headers.map { |h| row[:entries][h] } + end + + csv_data + end end end diff --git a/app/services/reports/annual_survey_report_service.rb b/app/services/reports/annual_survey_report_service.rb new file mode 100644 index 0000000000..e58e9b98af --- /dev/null +++ b/app/services/reports/annual_survey_report_service.rb @@ -0,0 +1,18 @@ +module Reports + class AnnualSurveyReportService + def initialize(organization:, year_start:, year_end:) + @organization = organization + @year_start = year_start + @year_end = year_end + end + + def call + (@year_start..@year_end).map do |year| + Reports.retrieve_report(organization: @organization, year: year, recalculate: true) + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error("Failed to retrieve annual report for year #{year}: #{e.message}") + nil + end.compact + end + end +end diff --git a/app/views/reports/annual_reports/index.html.erb b/app/views/reports/annual_reports/index.html.erb index fbfffdfdb3..882788a721 100644 --- a/app/views/reports/annual_reports/index.html.erb +++ b/app/views/reports/annual_reports/index.html.erb @@ -27,6 +27,15 @@