Skip to content

Commit

Permalink
feat(fworks): frameworks can be categorised
Browse files Browse the repository at this point in the history
  • Loading branch information
ryantk committed Sep 19, 2023
1 parent 609e340 commit 2af1836
Show file tree
Hide file tree
Showing 28 changed files with 191 additions and 24 deletions.
19 changes: 19 additions & 0 deletions app/controllers/frameworks/categorisations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Frameworks::CategorisationsController < Frameworks::ApplicationController
def edit
@framework = Frameworks::Framework.find(params[:framework_id])
end

def update
@framework = Frameworks::Framework.find(params[:framework_id])

@framework.update!(category_params)

redirect_to @framework
end

private

def category_params
params.require(:frameworks_framework).permit(support_category_ids: [])
end
end
7 changes: 7 additions & 0 deletions app/models/frameworks/activity_event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Frameworks::ActivityEvent < ApplicationRecord
include Frameworks::Activity

def loaded_data
OpenStruct.new(**data, **activity_log_item.subject.try(:activity_event_data_for, self).presence || {})
end
end
4 changes: 3 additions & 1 deletion app/models/frameworks/activity_log_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ class Frameworks::ActivityLogItem < ApplicationRecord

belongs_to :actor, polymorphic: true, optional: true
belongs_to :subject, polymorphic: true, optional: true
delegated_type :activity, types: %w[Frameworks::ActivityLoggableVersion]
delegated_type :activity, types: %w[Frameworks::ActivityLoggableVersion Frameworks::ActivityEvent]

default_scope { order("created_at DESC") }

before_create do
self.guid ||= Current.request_id
Expand Down
6 changes: 6 additions & 0 deletions app/models/frameworks/activity_loggable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ module Frameworks::ActivityLoggable
private

def log_latest_version_in_activity_log
return unless previous_changes.any?

Frameworks::ActivityLogItem.create!(subject: self, activity: versions.last, activity_type: "Frameworks::ActivityLoggableVersion")
end

def log_activity_event(event, data = {})
Frameworks::ActivityLogItem.create!(subject: self, activity: Frameworks::ActivityEvent.new(event:, data:))
end
end
7 changes: 6 additions & 1 deletion app/models/frameworks/framework.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ class Frameworks::Framework < ApplicationRecord
include Sourceable
include Presentable
include ActivityLogPresentable
include ActivityEventLoggable
include Filterable
include Sortable

belongs_to :provider
belongs_to :provider_contact, optional: true
belongs_to :proc_ops_lead, class_name: "Support::Agent", optional: true
belongs_to :e_and_o_lead, class_name: "Support::Agent", optional: true
belongs_to :support_category, class_name: "Support::Category", optional: true

has_many :framework_categories
has_many :support_categories, through: :framework_categories,
after_add: :log_framework_category_added,
after_remove: :log_framework_category_removed

validates :provider_id, presence: { message: "Please select a provider" }, on: :creation_form
validates :provider_contact_id, presence: { message: "Please select a contact" }, on: :creation_form
Expand Down
20 changes: 20 additions & 0 deletions app/models/frameworks/framework/activity_event_loggable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Frameworks::Framework::ActivityEventLoggable
extend ActiveSupport::Concern

def activity_event_data_for(activity_event)
case activity_event.event
when "framework_category_added", "framework_category_removed"
{ support_category: Support::Category.find(activity_event.data["support_category_id"]) }
end
end

protected

def log_framework_category_added(category)
log_activity_event("framework_category_added", support_category_id: category.id)
end

def log_framework_category_removed(category)
log_activity_event("framework_category_removed", support_category_id: category.id)
end
end
2 changes: 1 addition & 1 deletion app/models/frameworks/framework/filterable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Frameworks::Framework::Filterable
included do
scope :by_status, ->(statuses) { where(status: Array(statuses)) }
scope :by_provider, ->(provider_ids) { where(provider_id: Array(provider_ids)) }
scope :by_category, ->(category_ids) { where(support_category_id: Array(category_ids)) }
scope :by_category, ->(category_ids) { joins(:framework_categories).where(framework_categories: { support_category_id: Array(category_ids) }) }
scope :by_e_and_o_lead, ->(e_and_o_lead_ids) { where(e_and_o_lead_id: Array(e_and_o_lead_ids)) }
scope :by_proc_ops_lead, ->(proc_ops_lead_ids) { where(proc_ops_lead_id: Array(proc_ops_lead_ids)) }
scope :by_provider_contact, ->(provider_contact_ids) { where(provider_contact_id: Array(provider_contact_ids)) }
Expand Down
3 changes: 1 addition & 2 deletions app/models/frameworks/framework/filtering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ def results
end

def available_category_options
Support::Category.order("title ASC").where(id: Frameworks::Framework.pluck(:support_category_id))
.map { |category| [category.title, category.id] }
Support::Category.order("title ASC").where(id: Frameworks::FrameworkCategory.pluck(:support_category_id)).pluck(:title, :id)
end

def available_provider_filter_options
Expand Down
4 changes: 2 additions & 2 deletions app/models/frameworks/framework/presentable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def provider_framework_owner_email
provider_contact&.email
end

def category_name
support_category&.title
def category_names
support_categories.pluck(:title).join(", ")
end

def proc_ops_lead_name
Expand Down
2 changes: 1 addition & 1 deletion app/models/frameworks/framework/sortable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Frameworks::Framework::Sortable
extend ActiveSupport::Concern

included do
scope :sort_by_updated, ->(direction = "descending") { order("updated_at #{safe_direction(direction)}") }
scope :sort_by_updated, ->(direction = "descending") { order("frameworks_frameworks.updated_at #{safe_direction(direction)}") }
scope :sort_by_dfe_start_date, ->(direction = "descending") { order("dfe_start_date #{safe_direction(direction)}") }
scope :sort_by_dfe_end_date, ->(direction = "descending") { order("dfe_end_date #{safe_direction(direction)}") }
scope :sort_by_provider_start_date, ->(direction = "descending") { order("provider_start_date #{safe_direction(direction)}") }
Expand Down
4 changes: 4 additions & 0 deletions app/models/frameworks/framework_category.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Frameworks::FrameworkCategory < ApplicationRecord
belongs_to :support_category, class_name: "Support::Category"
belongs_to :framework
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= render "frameworks/activity_log_items/activity/activity_event/#{activity_log_item.activity.event}",
activity_log_item:,
activity: activity_log_item.activity,
subject: activity_log_item.subject %>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added category "<%= activity.loaded_data.support_category.title %>" to framework
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removed category "<%= activity.loaded_data.support_category.title %>" from framework
22 changes: 22 additions & 0 deletions app/views/frameworks/categorisations/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<%= content_for :title, "GHBS | Frameworks | Edit Framework Categories" %>

<span class="govuk-caption-l">Edit Framework Categories</span>
<h1 class="govuk-heading-l"><%= @framework.name %></h2>

<%= form_for @framework, url: frameworks_framework_categorisations_path(@framework), method: :put do |f| %>

<% procurement_category_grouped_options.each do |group, options| %>
<% next if group == "Or" %>

<%= f.govuk_check_boxes_fieldset :support_category_ids, legend: { text: group, size: "s" } do %>
<% options.each do |category| %>
<%= f.govuk_check_box :support_category_ids, category[1], label: { text: category[0] } %>
<% end %>
<% end %>
<% end %>

<div class="govuk-button-group flex-align-center">
<%= f.govuk_submit "Save changes" %>
<%= link_to "Cancel", @framework, class: "govuk-link govuk-link--no-visited-state" %>
</div>
<% end %>
4 changes: 2 additions & 2 deletions app/views/frameworks/frameworks/_framework.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
</div>

<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">Category</dt>
<dd class="govuk-summary-list__value"><%= framework.category_name %></dd>
<dt class="govuk-summary-list__key">Categories</dt>
<dd class="govuk-summary-list__value"><%= framework.category_names %></dd>
</div>
</dl>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
</div>

<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">Category</dt>
<dd class="govuk-summary-list__value"><%= @framework.category_name %></dd>
<dt class="govuk-summary-list__key">Categories</dt>
<dd class="govuk-summary-list__value"><%= @framework.category_names %></dd>
<dd class="govuk-summary-list__actions"><%= link_to "Change", edit_frameworks_framework_categorisations_path(@framework, back_to: current_url_b64(:framework_details)), class: "govuk-link", "data-turbo" => false %></dd>
</div>

<div class="govuk-summary-list__row">
Expand Down
4 changes: 3 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,9 @@
# Frameworks portal
namespace :frameworks do
root to: "dashboards#index"
resources :frameworks
resources :frameworks do
resource :categorisations, only: %w[edit update]
end
resources :provider_contacts
resources :providers

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateFrameworksFrameworkCategories < ActiveRecord::Migration[7.0]
def change
create_table :frameworks_framework_categories, id: :uuid do |t|
t.uuid :support_category_id, null: false, foreign_key: true
t.uuid :framework_id, null: false, foreign_key: true

t.timestamps
end

remove_column :frameworks_frameworks, :support_category_id, :uuid
end
end
10 changes: 10 additions & 0 deletions db/migrate/20230915110928_create_frameworks_activity_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateFrameworksActivityEvents < ActiveRecord::Migration[7.0]
def change
create_table :frameworks_activity_events, id: :uuid do |t|
t.string :event
t.jsonb :data

t.timestamps
end
end
end
17 changes: 15 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2023_09_13_084703) do
ActiveRecord::Schema[7.0].define(version: 2023_09_15_110928) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
enable_extension "pg_trgm"
Expand Down Expand Up @@ -207,6 +207,13 @@
t.index ["user_id"], name: "index_framework_requests_on_user_id"
end

create_table "frameworks_activity_events", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "event"
t.jsonb "data"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

create_table "frameworks_activity_log_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "actor_id"
t.string "actor_type"
Expand All @@ -219,10 +226,16 @@
t.datetime "updated_at", null: false
end

create_table "frameworks_framework_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "support_category_id", null: false
t.uuid "framework_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

create_table "frameworks_frameworks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.integer "source", default: 0
t.integer "status", default: 0
t.uuid "support_category_id"
t.string "name"
t.string "short_name"
t.string "url"
Expand Down
6 changes: 6 additions & 0 deletions spec/factories/frameworks/activity_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FactoryBot.define do
factory :frameworks_activity_event, class: "Frameworks::ActivityEvent" do
event { 1 }
data { "" }
end
end
6 changes: 6 additions & 0 deletions spec/factories/frameworks/framework_categories.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FactoryBot.define do
factory :frameworks_framework_category, class: "Frameworks::FrameworkCategory" do
support_category { nil }
framework { nil }
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require "rails_helper"

describe "Agent can categorise framework", js: true do
include_context "with a framework evaluation agent"

let(:framework) { create(:frameworks_framework) }

before { define_basic_categories }

it "saves the categories" do
visit frameworks_framework_path(framework)
within ".govuk-summary-list__row", text: "Categories" do
click_on "Change"
end
check "Laptops"
check "Electricity"
click_on "Save changes"

expect(page).to have_summary("Categories", "Laptops, Electricity")
click_on "History"
expect(page).to have_content('Added category "Laptops" to framework')
expect(page).to have_content('Added category "Electricity" to framework')
end
end
5 changes: 5 additions & 0 deletions spec/models/frameworks/activity_event_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require "rails_helper"

RSpec.describe Frameworks::ActivityEvent, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
6 changes: 2 additions & 4 deletions spec/models/frameworks/activity_loggable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

expect { provider.save! }.to change(Frameworks::ActivityLogItem, :count).from(0).to(1)

activity_log_item = Frameworks::ActivityLogItem.first
expect(activity_log_item.subject).to eq(provider)
activity_log_item = Frameworks::ActivityLogItem.where(subject: provider).reorder("created_at ASC").first
expect(provider.versions.count).to eq(1)
expect(activity_log_item.activity).to eq(provider.versions.last)
end
Expand All @@ -20,8 +19,7 @@

expect { provider.update(short_name: "TestProvider") }.to change(Frameworks::ActivityLogItem, :count).from(1).to(2)

activity_log_item = Frameworks::ActivityLogItem.last
expect(activity_log_item.subject).to eq(provider)
activity_log_item = Frameworks::ActivityLogItem.where(subject: provider).reorder("created_at ASC").last
expect(provider.versions.count).to eq(2)
expect(activity_log_item.activity).to eq(provider.versions.last)
end
Expand Down
10 changes: 5 additions & 5 deletions spec/models/frameworks/framework/filterable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
let(:proc_ops_lead) { create(:support_agent) }

before do
create(:frameworks_framework, name: "DfE Approved - Provider 1", status: :dfe_approved, provider: provider_1, support_category: category_1)
create(:frameworks_framework, name: "Not Approved - Provider 2", status: :not_approved, provider: provider_2, support_category: category_2)
create(:frameworks_framework, name: "Cab Approved - Provider 1", status: :cab_approved, provider: provider_1, support_category: category_1)
create(:frameworks_framework, name: "Evaluating - Provider 1", status: :evaluating, provider: provider_1, support_category: category_2, proc_ops_lead:)
create(:frameworks_framework, name: "Evaluating - Provider 2", status: :evaluating, provider: provider_2, support_category: category_1, e_and_o_lead:)
create(:frameworks_framework, name: "DfE Approved - Provider 1", status: :dfe_approved, provider: provider_1, support_category_ids: [category_1.id])
create(:frameworks_framework, name: "Not Approved - Provider 2", status: :not_approved, provider: provider_2, support_category_ids: [category_2.id])
create(:frameworks_framework, name: "Cab Approved - Provider 1", status: :cab_approved, provider: provider_1, support_category_ids: [category_1.id])
create(:frameworks_framework, name: "Evaluating - Provider 1", status: :evaluating, provider: provider_1, support_category_ids: [category_2.id], proc_ops_lead:)
create(:frameworks_framework, name: "Evaluating - Provider 2", status: :evaluating, provider: provider_2, support_category_ids: [category_1.id], e_and_o_lead:)
end

describe "filtering by status" do
Expand Down

0 comments on commit 2af1836

Please sign in to comment.