Skip to content

Commit

Permalink
Export a CSV of all continuing activities
Browse files Browse the repository at this point in the history
The first step in updating activities with the new transparency
identifier: producing a list of activities that fulfill the conditions
to continue, from the old GB-GOV-13 identifier corresponding to BEIS, to
the new GB-GOV-26 identifier corresponding to DSIT.

The list will need to be signed off by DSIT.

Usually we would have implemented this as a rake task to be run in the
server console, but we no longer have this level of access.

This ad-hoc export is temporary, and will be removed after all the
relevant activities are switched to the new identifier.
  • Loading branch information
CristinaRO committed Jan 10, 2024
1 parent e5c6356 commit 5ed9d0a
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 0 deletions.
14 changes: 14 additions & 0 deletions app/controllers/exports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,18 @@ def spending_breakdown_download
response.stream.write(spending_breakdown_csv)
response.stream.close
end

def continuing_activities
authorize :export, :show_continuing_activities?

respond_to do |format|
format.csv do
export = Export::ContinuingActivities.new

stream_csv_download(filename: export.filename, headers: export.headers) do |csv|
export.rows.each { |row| csv << row }
end
end
end
end
end
4 changes: 4 additions & 0 deletions app/policies/export_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ def show_budgets?
def show_spending_breakdown?
user.service_owner?
end

def show_continuing_activities?
user.service_owner?
end
end
68 changes: 68 additions & 0 deletions app/services/export/continuing_activities.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
class Export::ContinuingActivities
def filename
"continuing_activities.csv"
end

def headers
[
"Partner Organisation name",
"Activity name",
"RODA ID",
"Transparency identifier",
"Partner Organisation ID",
"Status",
"Level"
]
end

def rows
activities.map do |activity|
partner_organisation_name = activity.organisation.name
activity_title = activity.title
roda_identifier = activity.roda_identifier
transparency_identifier = activity.transparency_identifier
partner_organisation_identifier = activity.partner_organisation_identifier
status = I18n.t("activity.programme_status.#{activity.programme_status}")
level = I18n.t("table.body.activity.level.#{activity.level}")

[partner_organisation_name, activity_title, roda_identifier, transparency_identifier, partner_organisation_identifier, status, level]
end
end

def activities
# active statuses, regardless of any associated actual spend
definitely_active = Activity
.joins(:organisation)
.includes(:organisation)
.where.not(level: "fund")
.where(is_oda: [nil, true])
.where.not(programme_status: ["completed", "stopped", "cancelled", "finalisation", "paused"])
.order("organisations.name, programme_status")

cut_off_quarter = FinancialQuarter.new(2022, 4)

# activities that MAY need to continue, IF they have actual spend more recent than FQ4 2022-2023
potentially_active_due_to_actuals = Activity
.joins(:organisation, :actuals)
.includes(:organisation)
.where.not(level: "fund")
.where(is_oda: [nil, true])
.where(programme_status: ["completed", "stopped", "cancelled", "finalisation", "paused"])
.where("transactions.date > ?", cut_off_quarter.end_date)
.order("organisations.name, programme_status")

# activities that MAY need to continue, IF they have forecasts for quarters after FQ4 2022-2023
potentially_active_due_to_forecasts = Activity
.joins(:organisation)
.includes(:organisation)
.where.not(level: "fund")
.where(is_oda: [nil, true])
.where(programme_status: "paused")
.order("organisations.name, programme_status")
potentially_active_due_to_forecasts = potentially_active_due_to_forecasts.select do |activity|
Forecast.unscoped.where(parent_activity_id: activity.id).where("period_start_date > ?", cut_off_quarter.end_date).any?
end

(definitely_active + potentially_active_due_to_actuals + potentially_active_due_to_forecasts).uniq
end
end
22 changes: 22 additions & 0 deletions app/views/exports/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,28 @@
= a11y_action_link("Request new", spending_breakdown_exports_path(fund_id: fund.id), t("table.export.spending_breakdown.name", fund: fund.name), ["govuk-link--no-visited-state"])
- else
= a11y_action_link("Request", spending_breakdown_exports_path(fund_id: fund.id), t("table.export.spending_breakdown.name", fund: fund.name), ["govuk-link--no-visited-state"])

%h1.govuk-heading-m
Ad-hoc exports

%table.govuk-table
%thead.govuk-table__head
%tr.govuk-table__row
%th.govuk-table__header{scope: "col"}
Purpose
%th.govuk-table__header{scope: "col"}
= t("table.export.header.format")
%th.govuk-table__header{scope: "col"}
= t("table.header.default.actions")
%tbody.govuk-table__body
%tr.govuk-table__row
%td.govuk-table__cell
Activities continuing under GB-GOV-26
%td.govuk-table__cell
CSV
%td.govuk-table__cell
= a11y_action_link("Download", continuing_activities_exports_path(format: "csv"))

%h1.govuk-heading-m
= t("page_content.export.organisations.title")

Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
member do
get "spending_breakdown_download"
end
get "continuing_activities", on: :collection
end

namespace :exports do
Expand Down
119 changes: 119 additions & 0 deletions spec/services/export/continuing_activities_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
RSpec.describe Export::ContinuingActivities do
let(:export) { Export::ContinuingActivities.new }

describe "#activities" do
context "a completed activity" do
let!(:project) { create(:project_activity, programme_status: "completed") }

context "without actual spend" do
it "is not included" do
expect(export.activities).to_not include(project)
end
end

context "with actual spend before or including in FQ4 2022-2023" do
before { create(:actual, parent_activity: project, date: FinancialQuarter.new(2022, 4).end_date) }

it "is not included" do
expect(export.activities).to_not include(project)
end
end

context "with actual spend after FQ4 2022-2023" do
before { create(:actual, parent_activity: project, date: FinancialQuarter.new(2023, 1).start_date) }

it "is included" do
expect(export.activities).to include(project)
end
end
end

context "a spend_in_progress activity" do
let!(:project) { create(:project_activity, programme_status: "spend_in_progress") }

context "without actual spend" do
it "is included" do
expect(export.activities).to include(project)
end
end

context "with actual spend before or including in FQ4 2022-2023" do
before { create(:actual, parent_activity: project, date: FinancialQuarter.new(2022, 4).end_date) }

it "is included" do
expect(export.activities).to include(project)
end
end

context "with actual spend after FQ4 2022-2023" do
before { create(:actual, parent_activity: project, date: FinancialQuarter.new(2023, 1).start_date) }

it "is included" do
expect(export.activities).to include(project)
end
end

context "for ISPF non-ODA" do
before { project.update(is_oda: false) }

it "is not included" do
expect(export.activities).to_not include(project)
end
end
end

context "a paused activity" do
let!(:project) { create(:project_activity, programme_status: "paused") }

context "with neither actual spend nor forecasts" do
it "is not included" do
expect(export.activities).to_not include(project)
end
end

context "with actual spend before or including in FQ4 2022-2023 and no forecasts" do
before { create(:actual, parent_activity: project, date: FinancialQuarter.new(2022, 4).end_date) }

it "is not included" do
expect(export.activities).to_not include(project)
end
end

context "with actual spend after FQ4 2022-2023 and no forecasts" do
before { create(:actual, parent_activity: project, date: FinancialQuarter.new(2023, 1).start_date) }

it "is included" do
expect(export.activities).to include(project)
end
end

context "with forecasts before or including in FQ4 2022-2023 and no actual spend" do
before do
ReportingCycle.new(project, 2, 2022).tick
# initialising a reporting cycle like that and "tick"ing over the report cycle
# gives us a report for FQ3 2022-2023, in which we can report a forecast for FQ4 2022-2023
forecast_history = ForecastHistory.new(project, financial_quarter: 4, financial_year: 2022)
forecast_history.set_value(1000)
end

it "is not included" do
expect(export.activities).to_not include(project)
end
end

context "with forecasts after FQ4 2022-2023 and no actual spend" do
before do
ReportingCycle.new(project, 3, 2022).tick
# initialising a reporting cycle like that and "tick"ing over the report cycle
# gives us a report for FQ4 2022-2023, in which we can report a forecast for FQ1 2023-2024
forecast_history = ForecastHistory.new(project, financial_quarter: 1, financial_year: 2023)
forecast_history.set_value(1000)
end

it "is included" do
expect(export.activities).to include(project)
end
end
end
end
end

0 comments on commit 5ed9d0a

Please sign in to comment.