+ <%= form.label :service_provider_id, style: "display: block" %>
+ <%= form.number_field :service_provider_id %>
+
+
+
+ <%= form.label :year, style: "display: block" %>
+ <%= form.number_field :year %>
+
+
+
+ <%= form.label :delivered_current_year, style: "display: block" %>
+ <%= form.text_area :delivered_current_year %>
+
+
+
+ <%= form.label :to_deliver_next_year, style: "display: block" %>
+ <%= form.text_area :to_deliver_next_year %>
+
+
+
+ <%= link_to "Show this cx action plan", @cx_action_plan %> |
+ <%= link_to "Back to cx action plans", cx_action_plans_path %>
+
diff --git a/app/views/cx_action_plans/index.html.erb b/app/views/cx_action_plans/index.html.erb
new file mode 100644
index 000000000..8a6a73cfb
--- /dev/null
+++ b/app/views/cx_action_plans/index.html.erb
@@ -0,0 +1,14 @@
+
+ <%= link_to "Edit this cx action plan", edit_cx_action_plan_path(@cx_action_plan) %> |
+ <%= link_to "Back to cx action plans", cx_action_plans_path %>
+
+ <%= button_to "Destroy this cx action plan", @cx_action_plan, method: :delete %>
+
diff --git a/config/routes.rb b/config/routes.rb
index 4dce97061..c09975771 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,7 @@
require 'sidekiq/web'
Rails.application.routes.draw do
+ resources :cx_action_plans
resources :ivn_components
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
diff --git a/db/migrate/20230927214053_create_cx_action_plans.rb b/db/migrate/20230927214053_create_cx_action_plans.rb
new file mode 100644
index 000000000..657968609
--- /dev/null
+++ b/db/migrate/20230927214053_create_cx_action_plans.rb
@@ -0,0 +1,12 @@
+class CreateCxActionPlans < ActiveRecord::Migration[7.0]
+ def change
+ create_table :cx_action_plans do |t|
+ t.integer :service_provider_id
+ t.integer :year
+ t.text :delivered_current_year
+ t.text :to_deliver_next_year
+
+ t.timestamps
+ end
+ end
+end
diff --git a/spec/models/cx_action_plan_spec.rb b/spec/models/cx_action_plan_spec.rb
new file mode 100644
index 000000000..6ebec0ef9
--- /dev/null
+++ b/spec/models/cx_action_plan_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe CxActionPlan, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/requests/cx_action_plans_spec.rb b/spec/requests/cx_action_plans_spec.rb
new file mode 100644
index 000000000..587952eb8
--- /dev/null
+++ b/spec/requests/cx_action_plans_spec.rb
@@ -0,0 +1,135 @@
+require 'rails_helper'
+
+# This spec was generated by rspec-rails when you ran the scaffold generator.
+# It demonstrates how one might use RSpec to test the controller code that
+# was generated by Rails when you ran the scaffold generator.
+#
+# It assumes that the implementation code is generated by the rails scaffold
+# generator. If you are using any extension libraries to generate different
+# controller code, this generated spec may or may not pass.
+#
+# It only uses APIs available in rails and/or rspec-rails. There are a number
+# of tools you can use to make these specs even more expressive, but we're
+# sticking to rails and rspec-rails APIs to keep things simple and stable.
+
+RSpec.describe "/cx_action_plans", type: :request do
+
+ # This should return the minimal set of attributes required to create a valid
+ # CxActionPlan. As you add validations to CxActionPlan, be sure to
+ # adjust the attributes here as well.
+ let(:valid_attributes) {
+ skip("Add a hash of attributes valid for your model")
+ }
+
+ let(:invalid_attributes) {
+ skip("Add a hash of attributes invalid for your model")
+ }
+
+ describe "GET /index" do
+ it "renders a successful response" do
+ CxActionPlan.create! valid_attributes
+ get cx_action_plans_url
+ expect(response).to be_successful
+ end
+ end
+
+ describe "GET /show" do
+ it "renders a successful response" do
+ cx_action_plan = CxActionPlan.create! valid_attributes
+ get cx_action_plan_url(cx_action_plan)
+ expect(response).to be_successful
+ end
+ end
+
+ describe "GET /new" do
+ it "renders a successful response" do
+ get new_cx_action_plan_url
+ expect(response).to be_successful
+ end
+ end
+
+ describe "GET /edit" do
+ it "renders a successful response" do
+ cx_action_plan = CxActionPlan.create! valid_attributes
+ get edit_cx_action_plan_url(cx_action_plan)
+ expect(response).to be_successful
+ end
+ end
+
+ describe "POST /create" do
+ context "with valid parameters" do
+ it "creates a new CxActionPlan" do
+ expect {
+ post cx_action_plans_url, params: { cx_action_plan: valid_attributes }
+ }.to change(CxActionPlan, :count).by(1)
+ end
+
+ it "redirects to the created cx_action_plan" do
+ post cx_action_plans_url, params: { cx_action_plan: valid_attributes }
+ expect(response).to redirect_to(cx_action_plan_url(CxActionPlan.last))
+ end
+ end
+
+ context "with invalid parameters" do
+ it "does not create a new CxActionPlan" do
+ expect {
+ post cx_action_plans_url, params: { cx_action_plan: invalid_attributes }
+ }.to change(CxActionPlan, :count).by(0)
+ end
+
+
+ it "renders a response with 422 status (i.e. to display the 'new' template)" do
+ post cx_action_plans_url, params: { cx_action_plan: invalid_attributes }
+ expect(response).to have_http_status(:unprocessable_entity)
+ end
+
+ end
+ end
+
+ describe "PATCH /update" do
+ context "with valid parameters" do
+ let(:new_attributes) {
+ skip("Add a hash of attributes valid for your model")
+ }
+
+ it "updates the requested cx_action_plan" do
+ cx_action_plan = CxActionPlan.create! valid_attributes
+ patch cx_action_plan_url(cx_action_plan), params: { cx_action_plan: new_attributes }
+ cx_action_plan.reload
+ skip("Add assertions for updated state")
+ end
+
+ it "redirects to the cx_action_plan" do
+ cx_action_plan = CxActionPlan.create! valid_attributes
+ patch cx_action_plan_url(cx_action_plan), params: { cx_action_plan: new_attributes }
+ cx_action_plan.reload
+ expect(response).to redirect_to(cx_action_plan_url(cx_action_plan))
+ end
+ end
+
+ context "with invalid parameters" do
+
+ it "renders a response with 422 status (i.e. to display the 'edit' template)" do
+ cx_action_plan = CxActionPlan.create! valid_attributes
+ patch cx_action_plan_url(cx_action_plan), params: { cx_action_plan: invalid_attributes }
+ expect(response).to have_http_status(:unprocessable_entity)
+ end
+
+ end
+ end
+
+ describe "DELETE /destroy" do
+ it "destroys the requested cx_action_plan" do
+ cx_action_plan = CxActionPlan.create! valid_attributes
+ expect {
+ delete cx_action_plan_url(cx_action_plan)
+ }.to change(CxActionPlan, :count).by(-1)
+ end
+
+ it "redirects to the cx_action_plans list" do
+ cx_action_plan = CxActionPlan.create! valid_attributes
+ delete cx_action_plan_url(cx_action_plan)
+ expect(response).to redirect_to(cx_action_plans_url)
+ end
+ end
+end
From 3f7f00de42c8f13813d92c5f3118244e483e9bab Mon Sep 17 00:00:00 2001
From: Ryan Wold
Date: Wed, 27 Sep 2023 15:54:57 -0700
Subject: [PATCH 4/9] /admin/cx_action_plans
---
.../{ => admin}/cx_action_plans_controller.rb | 8 ++--
app/models/cx_action_plan.rb | 1 +
.../cx_action_plans/_cx_action_plan.html.erb | 0
.../admin/cx_action_plans/_form.html.erb | 37 +++++++++++++++
app/views/admin/cx_action_plans/edit.html.erb | 13 ++++++
.../admin/cx_action_plans/index.html.erb | 44 ++++++++++++++++++
app/views/admin/cx_action_plans/new.html.erb | 11 +++++
app/views/admin/cx_action_plans/show.html.erb | 46 +++++++++++++++++++
app/views/cx_action_plans/_form.html.erb | 37 ---------------
app/views/cx_action_plans/edit.html.erb | 10 ----
app/views/cx_action_plans/index.html.erb | 14 ------
app/views/cx_action_plans/new.html.erb | 9 ----
app/views/cx_action_plans/show.html.erb | 10 ----
config/routes.rb | 3 +-
db/schema.rb | 11 ++++-
15 files changed, 169 insertions(+), 85 deletions(-)
rename app/controllers/{ => admin}/cx_action_plans_controller.rb (77%)
rename app/views/{ => admin}/cx_action_plans/_cx_action_plan.html.erb (100%)
create mode 100644 app/views/admin/cx_action_plans/_form.html.erb
create mode 100644 app/views/admin/cx_action_plans/edit.html.erb
create mode 100644 app/views/admin/cx_action_plans/index.html.erb
create mode 100644 app/views/admin/cx_action_plans/new.html.erb
create mode 100644 app/views/admin/cx_action_plans/show.html.erb
delete mode 100644 app/views/cx_action_plans/_form.html.erb
delete mode 100644 app/views/cx_action_plans/edit.html.erb
delete mode 100644 app/views/cx_action_plans/index.html.erb
delete mode 100644 app/views/cx_action_plans/new.html.erb
delete mode 100644 app/views/cx_action_plans/show.html.erb
diff --git a/app/controllers/cx_action_plans_controller.rb b/app/controllers/admin/cx_action_plans_controller.rb
similarity index 77%
rename from app/controllers/cx_action_plans_controller.rb
rename to app/controllers/admin/cx_action_plans_controller.rb
index 04dfadad3..8f1ee7f71 100644
--- a/app/controllers/cx_action_plans_controller.rb
+++ b/app/controllers/admin/cx_action_plans_controller.rb
@@ -1,4 +1,4 @@
-class CxActionPlansController < ApplicationController
+class Admin::CxActionPlansController < AdminController
before_action :set_cx_action_plan, only: %i[ show edit update destroy ]
# GET /cx_action_plans or /cx_action_plans.json
@@ -12,11 +12,13 @@ def show
# GET /cx_action_plans/new
def new
+ @service_providers = ServiceProvider.all.includes(:organization).order('organizations.name', 'service_providers.name')
@cx_action_plan = CxActionPlan.new
end
# GET /cx_action_plans/1/edit
def edit
+ @service_providers = ServiceProvider.all.includes(:organization).order('organizations.name', 'service_providers.name')
end
# POST /cx_action_plans or /cx_action_plans.json
@@ -25,7 +27,7 @@ def create
respond_to do |format|
if @cx_action_plan.save
- format.html { redirect_to cx_action_plan_url(@cx_action_plan), notice: "Cx action plan was successfully created." }
+ format.html { redirect_to admin_cx_action_plan_url(@cx_action_plan), notice: "Cx action plan was successfully created." }
format.json { render :show, status: :created, location: @cx_action_plan }
else
format.html { render :new, status: :unprocessable_entity }
@@ -38,7 +40,7 @@ def create
def update
respond_to do |format|
if @cx_action_plan.update(cx_action_plan_params)
- format.html { redirect_to cx_action_plan_url(@cx_action_plan), notice: "Cx action plan was successfully updated." }
+ format.html { redirect_to admin_cx_action_plan_url(@cx_action_plan), notice: "Cx action plan was successfully updated." }
format.json { render :show, status: :ok, location: @cx_action_plan }
else
format.html { render :edit, status: :unprocessable_entity }
diff --git a/app/models/cx_action_plan.rb b/app/models/cx_action_plan.rb
index 13cafe6ab..d90f9e63b 100644
--- a/app/models/cx_action_plan.rb
+++ b/app/models/cx_action_plan.rb
@@ -1,2 +1,3 @@
class CxActionPlan < ApplicationRecord
+ belongs_to :service_provider
end
diff --git a/app/views/cx_action_plans/_cx_action_plan.html.erb b/app/views/admin/cx_action_plans/_cx_action_plan.html.erb
similarity index 100%
rename from app/views/cx_action_plans/_cx_action_plan.html.erb
rename to app/views/admin/cx_action_plans/_cx_action_plan.html.erb
diff --git a/app/views/admin/cx_action_plans/_form.html.erb b/app/views/admin/cx_action_plans/_form.html.erb
new file mode 100644
index 000000000..1cd5a5a02
--- /dev/null
+++ b/app/views/admin/cx_action_plans/_form.html.erb
@@ -0,0 +1,37 @@
+<%= form_with(model: cx_action_plan, url: cx_action_plan.persisted? ? admin_cx_action_plan_path : admin_cx_action_plans_path, local: true, data: { turbo: false }) do |form| %>
+ <% if cx_action_plan.errors.any? %>
+
+
<%= pluralize(cx_action_plan.errors.count, "error") %> prohibited this cx_action_plan from being saved:
+
+
+ <% cx_action_plan.errors.each do |error| %>
+ - <%= error.full_message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :service_provider_id, class: "usa-label" %>
+ <%= form.select :service_provider_id, options_for_select(@service_providers.map { |p| ["#{p.organization.abbreviation} - #{p.name}", p.id] }, cx_action_plan.service_provider_id), { prompt: "Which Service Provider?" }, { class: "usa-select" } %>
+
+
+
+ <%= form.label :year, class: "usa-label" %>
+ <%= form.number_field :year, class: "usa-input" %>
+
+
+
+ <%= form.label :delivered_current_year, class: "usa-label" %>
+ <%= form.text_area :delivered_current_year, class: "usa-textarea" %>
+
+
+
+ <%= form.label :to_deliver_next_year, class: "usa-label" %>
+ <%= form.text_area :to_deliver_next_year, class: "usa-textarea" %>
+
+
+
+ <%= form.submit class: "usa-button" %>
+
+<% end %>
diff --git a/app/views/admin/cx_action_plans/edit.html.erb b/app/views/admin/cx_action_plans/edit.html.erb
new file mode 100644
index 000000000..f826fa8ef
--- /dev/null
+++ b/app/views/admin/cx_action_plans/edit.html.erb
@@ -0,0 +1,13 @@
+<% content_for :navigation_title do %>
+ Editing CX Action Plan
+<% end %>
+
+
+ <%= link_to admin_cx_action_plan_path(@cx_action_plan) do %>
+
+ Back to CX Action Plan
+ <% end %>
+
+
+
+<%= render "form", cx_action_plan: @cx_action_plan %>
diff --git a/app/views/admin/cx_action_plans/index.html.erb b/app/views/admin/cx_action_plans/index.html.erb
new file mode 100644
index 000000000..640d2a596
--- /dev/null
+++ b/app/views/admin/cx_action_plans/index.html.erb
@@ -0,0 +1,44 @@
+<% content_for :navigation_title do %>
+ CX Action Plans
+
+ <%= link_to new_admin_cx_action_plan_path, class: "usa-button usa-button-inverted float-right" do %>
+
+ New CX Action Plan
+ <% end %>
+<% end %>
+
+
+
+
+
+
+ Organization name
+ |
+
+ Service Provider name
+ |
+
+ Year
+ |
+
+ |
+
+
+ <% @cx_action_plans.each do |cx_action_plan| %>
+
+
+ <%= cx_action_plan.service_provider.organization.name %>
+ |
+
+ <%= cx_action_plan.service_provider.name %>
+ |
+
+ <%= cx_action_plan.year %>
+ |
+
+ <%= link_to 'View', admin_cx_action_plan_path(cx_action_plan) %>
+ |
+
+ <% end %>
+
+
diff --git a/app/views/admin/cx_action_plans/new.html.erb b/app/views/admin/cx_action_plans/new.html.erb
new file mode 100644
index 000000000..b6ae15f2f
--- /dev/null
+++ b/app/views/admin/cx_action_plans/new.html.erb
@@ -0,0 +1,11 @@
+<% content_for :navigation_title do %>
+ New CX Action Plan
+<% end %>
+
+ <%= link_to admin_cx_action_plans_path do %>
+
+ Back to CX Action Plans
+ <% end %>
+
+
+<%= render "form", cx_action_plan: @cx_action_plan %>
diff --git a/app/views/admin/cx_action_plans/show.html.erb b/app/views/admin/cx_action_plans/show.html.erb
new file mode 100644
index 000000000..edd16a422
--- /dev/null
+++ b/app/views/admin/cx_action_plans/show.html.erb
@@ -0,0 +1,46 @@
+<% content_for :navigation_title do %>
+ CX Action Plan
+ <%= link_to edit_admin_cx_action_plan_path(@cx_action_plan), class: "usa-button usa-button-inverted float-right" do %>
+
+ Edit
+ <% end %>
+<% end %>
+
+ <%= link_to admin_cx_action_plans_path do %>
+
+ Back to CX Action Plans
+ <% end %>
+
+
+>
+
+ Organization name:
+ <%= @cx_action_plan.service_provider.organization.name %>
+
+
+
+ Service provider:
+ <%= link_to @cx_action_plan.service_provider.name, admin_service_provider_path(@cx_action_plan.service_provider) %>
+
+
+
+ Year:
+ <%= @cx_action_plan.year %>
+
+
+
+ Delivered current year:
+ <%= to_markdown(@cx_action_plan.delivered_current_year) %>
+
+
+
+ To deliver next year:
+ <%= to_markdown(@cx_action_plan.to_deliver_next_year) %>
+
+
+
+
+
+ <%= button_to "Destroy", admin_cx_action_plan_path(@cx_action_plan), class: "usa-button usa-button--secondary float-right", method: :delete %>
+
+
diff --git a/app/views/cx_action_plans/_form.html.erb b/app/views/cx_action_plans/_form.html.erb
deleted file mode 100644
index 3eab89d0b..000000000
--- a/app/views/cx_action_plans/_form.html.erb
+++ /dev/null
@@ -1,37 +0,0 @@
-<%= form_with(model: cx_action_plan) do |form| %>
- <% if cx_action_plan.errors.any? %>
-
-
<%= pluralize(cx_action_plan.errors.count, "error") %> prohibited this cx_action_plan from being saved:
-
-
- <% cx_action_plan.errors.each do |error| %>
- - <%= error.full_message %>
- <% end %>
-
-
- <% end %>
-
-
- <%= form.label :service_provider_id, style: "display: block" %>
- <%= form.number_field :service_provider_id %>
-
-
-
- <%= form.label :year, style: "display: block" %>
- <%= form.number_field :year %>
-
-
-
- <%= form.label :delivered_current_year, style: "display: block" %>
- <%= form.text_area :delivered_current_year %>
-
-
-
- <%= form.label :to_deliver_next_year, style: "display: block" %>
- <%= form.text_area :to_deliver_next_year %>
-
-
-
- <%= form.submit %>
-
-<% end %>
diff --git a/app/views/cx_action_plans/edit.html.erb b/app/views/cx_action_plans/edit.html.erb
deleted file mode 100644
index 987cd4020..000000000
--- a/app/views/cx_action_plans/edit.html.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-Editing cx action plan
-
-<%= render "form", cx_action_plan: @cx_action_plan %>
-
-
-
-
- <%= link_to "Show this cx action plan", @cx_action_plan %> |
- <%= link_to "Back to cx action plans", cx_action_plans_path %>
-
diff --git a/app/views/cx_action_plans/index.html.erb b/app/views/cx_action_plans/index.html.erb
deleted file mode 100644
index 8a6a73cfb..000000000
--- a/app/views/cx_action_plans/index.html.erb
+++ /dev/null
@@ -1,14 +0,0 @@
-<%= notice %>
-
-Cx action plans
-
-
- <% @cx_action_plans.each do |cx_action_plan| %>
- <%= render cx_action_plan %>
-
- <%= link_to "Show this cx action plan", cx_action_plan %>
-
- <% end %>
-
-
-<%= link_to "New cx action plan", new_cx_action_plan_path %>
diff --git a/app/views/cx_action_plans/new.html.erb b/app/views/cx_action_plans/new.html.erb
deleted file mode 100644
index 7b38a81af..000000000
--- a/app/views/cx_action_plans/new.html.erb
+++ /dev/null
@@ -1,9 +0,0 @@
-New cx action plan
-
-<%= render "form", cx_action_plan: @cx_action_plan %>
-
-
-
-
- <%= link_to "Back to cx action plans", cx_action_plans_path %>
-
diff --git a/app/views/cx_action_plans/show.html.erb b/app/views/cx_action_plans/show.html.erb
deleted file mode 100644
index cbb81462b..000000000
--- a/app/views/cx_action_plans/show.html.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-<%= notice %>
-
-<%= render @cx_action_plan %>
-
-
- <%= link_to "Edit this cx action plan", edit_cx_action_plan_path(@cx_action_plan) %> |
- <%= link_to "Back to cx action plans", cx_action_plans_path %>
-
- <%= button_to "Destroy this cx action plan", @cx_action_plan, method: :delete %>
-
diff --git a/config/routes.rb b/config/routes.rb
index c09975771..1ea804b35 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,7 +3,6 @@
require 'sidekiq/web'
Rails.application.routes.draw do
- resources :cx_action_plans
resources :ivn_components
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
@@ -104,6 +103,7 @@
post 'remove_service_provider_manager', to: 'service_providers#remove_service_provider_manager', as: :remove_service_provider_manager
end
end
+ resources :cx_action_plans
resources :services do
collection do
get 'catalog', to: 'services#catalog', as: :catalog
@@ -145,6 +145,7 @@
get 'events', to: 'collections#events', as: :events
end
end
+
resources :omb_cx_reporting_collections
resources :cscrm_data_collections do
member do
diff --git a/db/schema.rb b/db/schema.rb
index 07a472680..3a37111bc 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -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_26_183233) do
+ActiveRecord::Schema[7.0].define(version: 2023_09_27_214053) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -178,6 +178,15 @@
t.index ["user_id"], name: "index_cscrm_data_collections2_on_user_id"
end
+ create_table "cx_action_plans", force: :cascade do |t|
+ t.integer "service_provider_id"
+ t.integer "year"
+ t.text "delivered_current_year"
+ t.text "to_deliver_next_year"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
create_table "digital_product_versions", force: :cascade do |t|
t.bigint "digital_product_id"
t.string "store_url"
From f9581e884a9f40677192ad856c4dfab7d61f46be Mon Sep 17 00:00:00 2001
From: Ryan Wold
Date: Wed, 27 Sep 2023 16:05:24 -0700
Subject: [PATCH 5/9] make cx_action_plan data available via API
---
.../api/v1/cx_action_plans_controller.rb | 23 +++++++++++++++++++
app/models/cx_action_plan.rb | 17 ++++++++++++++
app/serializers/cx_action_plan_serializer.rb | 15 +++++++++++-
config/routes.rb | 1 +
4 files changed, 55 insertions(+), 1 deletion(-)
create mode 100644 app/controllers/api/v1/cx_action_plans_controller.rb
diff --git a/app/controllers/api/v1/cx_action_plans_controller.rb b/app/controllers/api/v1/cx_action_plans_controller.rb
new file mode 100644
index 000000000..90f0be1bc
--- /dev/null
+++ b/app/controllers/api/v1/cx_action_plans_controller.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Api
+ module V1
+ class CxActionPlansController < ::ApiController
+ def index
+ respond_to do |format|
+ format.json do
+ render json: CxActionPlan.order(:id), each_serializer: CxActionPlanSerializer
+ end
+ end
+ end
+
+ def show
+ respond_to do |format|
+ format.json do
+ render json: CxActionPlan.find(params[:id]), serializer: CxActionPlanSerializer
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/cx_action_plan.rb b/app/models/cx_action_plan.rb
index d90f9e63b..c92e21b38 100644
--- a/app/models/cx_action_plan.rb
+++ b/app/models/cx_action_plan.rb
@@ -1,3 +1,20 @@
class CxActionPlan < ApplicationRecord
belongs_to :service_provider
+
+
+ def organization_id
+ self.service_provider.organization_id
+ end
+
+ def organization_name
+ self.service_provider.organization.name
+ end
+
+ def service_provider_name
+ self.service_provider.name
+ end
+
+ def services
+ self.service_provider.services
+ end
end
diff --git a/app/serializers/cx_action_plan_serializer.rb b/app/serializers/cx_action_plan_serializer.rb
index bfff3ee29..d3898ddc6 100644
--- a/app/serializers/cx_action_plan_serializer.rb
+++ b/app/serializers/cx_action_plan_serializer.rb
@@ -1,3 +1,16 @@
class CxActionPlanSerializer < ActiveModel::Serializer
- attributes :id, :service_provider_id, :year, :delivered_current_year, :to_deliver_next_year
+ attributes :id,
+ :organization_id,
+ :organization_name,
+ :service_provider_id,
+ :service_provider_name,
+ :year,
+ :delivered_current_year,
+ :to_deliver_next_year,
+ :services
+
+
+ def services
+ ActiveModel::Serializer::CollectionSerializer.new(object.services, serializer: ServiceSerializer)
+ end
end
diff --git a/config/routes.rb b/config/routes.rb
index 1ea804b35..a4de109b2 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -45,6 +45,7 @@
resources :forms, only: %i[index show]
resources :websites, only: [:index]
resources :service_providers, only: [:index]
+ resources :cx_action_plans, only: %i[index show]
resources :services, only: %i[index show]
resources :personas, only: [:index]
resources :goals, only: [:index]
From 651587ab71b3853de65cc044d5e537f9e29a6f7b Mon Sep 17 00:00:00 2001
From: Ryan Wold
Date: Wed, 27 Sep 2023 20:54:59 -0700
Subject: [PATCH 6/9] translate cscrm2 export values
---
app/models/cscrm_data_collection2.rb | 169 +++++++++++++++++-
.../cscrm_data_collections2/edit.html.erb | 2 +-
2 files changed, 165 insertions(+), 6 deletions(-)
diff --git a/app/models/cscrm_data_collection2.rb b/app/models/cscrm_data_collection2.rb
index 006a5980c..415266f09 100644
--- a/app/models/cscrm_data_collection2.rb
+++ b/app/models/cscrm_data_collection2.rb
@@ -409,6 +409,147 @@ def self.established_process_information_sharing_options
}
end
+
+ #
+ #
+ # Custom logic applied to fields for data export
+ # example: 8 checkbox values being consolidated into a value between 1-3
+ #
+ #
+
+ def self.export_conversion_question_10(field)
+ return nil if field.length == 1
+
+ # 0 = Not Identified
+ # 1 = 1 or 2 identified
+ # 2 = All but suppliers identified
+ # 3 = All identified
+ # Note: Critical item selections not scored, for info only; however,
+ # If critical items were selected, assumption was that items had been identified
+
+ question_option_selections = YAML.load(field) # parse the string encoded as an array, to an array
+ question_option_selections_without_not_identified = question_option_selections - ["Not identified"] # remove Not Identified option
+ question_option_selections_without_suppliers = question_option_selections_without_not_identified - ["Critical Suppliers Identified"] # remove Not Identified and Suppliers option
+
+ if question_option_selections_without_not_identified.size == 8 # if all are selected
+ 3
+ elsif question_option_selections_without_suppliers.size == 7
+ 2
+ elsif (1..2).include?(question_option_selections_without_not_identified.size)
+ 1
+ elsif question_option_selections.include?("Not identified")
+ 0
+ else
+ "not scored"
+ end
+ end
+
+ def self.export_conversion_question_12(field)
+ return nil if field.length == 1
+
+ # 0 = Not Considered
+ # 1 = up to 2 selections
+ # 2 = 3 to 6 selections
+ # 3 = All
+
+ question_option_selections = YAML.load(field)
+ question_option_selections_without_not_considered = question_option_selections - ["Not considered"]
+ question_option_selections_without_other = question_option_selections_without_not_considered - ["Other"]
+
+ if question_option_selections_without_other.size == 7 # if all are selected
+ 3
+ elsif (3..6).include?(question_option_selections_without_other.size)
+ 2
+ elsif (1..2).include?(question_option_selections_without_other.size)
+ 1
+ elsif question_option_selections.include?("Not considered")
+ 0
+ else
+ "not scored"
+ end
+ end
+
+ def self.export_conversion_question_14(field)
+ return nil if field.length == 1
+
+ # 0 = Not Considered
+ # 1 = Some Products and/or Services
+ # 2 = Some Products/All Services or All
+ # Products/Some Services
+ # 3 = All Product and Services
+ # Note: If 1 “all” option selected score = 2
+
+ question_option_selections = YAML.load(field)
+ question_option_selections_without_not_conducted = question_option_selections - ["Not conducted"]
+
+ if question_option_selections_without_not_conducted.include?("Conducted for all prioritized products") &&
+ question_option_selections_without_not_conducted.include?("Conducted for all prioritized services")
+ 3
+ elsif question_option_selections_without_not_conducted.include?("Conducted for all prioritized products") ||
+ question_option_selections_without_not_conducted.include?("Conducted for all prioritized services")
+ 2
+ elsif question_option_selections_without_not_conducted.include?("Conducted for some prioritized products") ||
+ question_option_selections_without_not_conducted.include?("Conducted for some prioritized services")
+ 1
+ elsif question_option_selections.include?("Not conducted")
+ 0
+ else
+ "not scored"
+ end
+ end
+
+ def self.export_conversion_question_16(field)
+ # 0 = Not established
+ # 1 = Partial/in-Process Internal process
+ # 2 = Internal Process established and/or FASC process planned/in-process
+ # 3 = Internal and FASC process established
+
+ if field == "5"
+ 3
+ elsif field == "2" ||
+ field == "3" ||
+ field == "4"
+ 2
+ elsif field == "1"
+ 1
+ elsif field == "0"
+ 0
+ else
+ "not scored"
+ end
+ end
+
+ def self.export_conversion_question_17(field)
+ return nil if field.length == 1
+
+ # 0 = Not Considered;
+ # 1 = Response option(s), other than SCRAs;
+ # 2 = Response options includes “SCRAs” but not “mitigations”
+ # 3 = ”SCRAs” and “Mitigations” options selected
+
+ question_option_selections = YAML.load(field)
+
+ if question_option_selections.include?("SCRAs are conducted for critical suppliers") &&
+ question_option_selections.include?("Mitigations to improve resilience/address assessed risks associated with critical suppliers are identified and implemented")
+ 3
+ elsif question_option_selections.include?("SCRAs are conducted for critical suppliers") &&
+ !question_option_selections.include?("Mitigations to improve resilience/address assessed risks associated with critical suppliers are identified and implemented")
+ 2
+ elsif question_option_selections.include?("Critical Suppliers are identified in COOP and Recovery plans") ||
+ question_option_selections.include?("Business Impact Analysis considers supplier and product dependency risks and resiliency requirements")
+ 1
+ elsif question_option_selections.include?("Not considered")
+ 0
+ else
+ "not scored"
+ end
+ end
+
+ #
+ # end custom export logic
+ #
+
+
def self.to_csv
collections = CscrmDataCollection2.order('year, quarter')
@@ -453,29 +594,33 @@ def self.to_csv
"clearly_defined_roles_value",
"clearly_defined_roles",
"clearly_defined_roles_comments",
- "identified_assets_and_essential_functions_value",
"identified_assets_and_essential_functions",
+ "identified_assets_and_essential_functions_value",
+ "identified_assets_and_essential_functions_translated_value",
"identified_assets_and_essential_functions_comments",
"prioritization_process_value",
"prioritization_process",
"prioritization_process_comments",
- "considerations_in_procurement_processes_value",
"considerations_in_procurement_processes",
+ "considerations_in_procurement_processes_value",
+ "considerations_in_procurement_processes_translated_value",
"considerations_in_procurement_processes_comments",
"documented_methodology_value",
"documented_methodology",
"documented_methodology_comments",
- "conducts_scra_for_prioritized_products_and_services_value",
"conducts_scra_for_prioritized_products_and_services",
+ "conducts_scra_for_prioritized_products_and_services_translated_value",
"conducts_scra_for_prioritized_products_and_services_comments",
"personnel_required_to_complete_training_value",
"personnel_required_to_complete_training",
"personnel_required_to_complete_training_comments",
- "established_process_information_sharing_with_fasc_value",
"established_process_information_sharing_with_fasc",
+ "established_process_information_sharing_with_fasc_value",
+ "established_process_information_sharing_with_fasc_translated_value",
"established_process_information_sharing_with_fasc_comments",
- "cybersecurity_supply_chain_risk_considerations_value",
"cybersecurity_supply_chain_risk_considerations",
+ "cybersecurity_supply_chain_risk_considerations_value",
+ "cybersecurity_supply_chain_risk_considerations_translated_value",
"cybersecurity_supply_chain_risk_considerations_comments",
"process_for_product_authenticity_value",
"process_for_product_authenticity",
@@ -534,30 +679,44 @@ def self.to_csv
CscrmDataCollection2.question_9[:options].key(collection.clearly_defined_roles.to_i),
collection.clearly_defined_roles,
collection.clearly_defined_roles_comments,
+
CscrmDataCollection2.question_10[:options].key(collection.identified_assets_and_essential_functions.to_i),
collection.identified_assets_and_essential_functions,
+ export_conversion_question_10(collection.identified_assets_and_essential_functions),
collection.identified_assets_and_essential_functions_comments,
+
CscrmDataCollection2.question_11[:options].key(collection.prioritization_process.to_i),
collection.prioritization_process,
collection.prioritization_process_comments,
+
CscrmDataCollection2.question_12[:options].key(collection.considerations_in_procurement_processes.to_i),
collection.considerations_in_procurement_processes,
+ export_conversion_question_12(collection.considerations_in_procurement_processes),
collection.considerations_in_procurement_processes_comments,
+
CscrmDataCollection2.question_13[:options].key(collection.documented_methodology.to_i),
collection.documented_methodology,
collection.documented_methodology_comments,
+
CscrmDataCollection2.question_14[:options].key(collection.conducts_scra_for_prioritized_products_and_services.to_i),
collection.conducts_scra_for_prioritized_products_and_services,
+ export_conversion_question_14(collection.conducts_scra_for_prioritized_products_and_services),
collection.conducts_scra_for_prioritized_products_and_services_comments,
+
CscrmDataCollection2.question_15[:options].key(collection.personnel_required_to_complete_training.to_i),
collection.personnel_required_to_complete_training,
collection.personnel_required_to_complete_training_comments,
+
CscrmDataCollection2.question_16[:options].key(collection.established_process_information_sharing_with_fasc.to_i),
collection.established_process_information_sharing_with_fasc,
+ export_conversion_question_16(collection.established_process_information_sharing_with_fasc),
collection.established_process_information_sharing_with_fasc_comments,
+
CscrmDataCollection2.question_17[:options].key(collection.cybersecurity_supply_chain_risk_considerations.to_i),
collection.cybersecurity_supply_chain_risk_considerations,
+ export_conversion_question_17(collection.cybersecurity_supply_chain_risk_considerations),
collection.cybersecurity_supply_chain_risk_considerations_comments,
+
CscrmDataCollection2.question_18[:options].key(collection.process_for_product_authenticity.to_i),
collection.process_for_product_authenticity,
collection.process_for_product_authenticity_comments,
diff --git a/app/views/admin/cscrm_data_collections2/edit.html.erb b/app/views/admin/cscrm_data_collections2/edit.html.erb
index a7484c466..da45687dc 100644
--- a/app/views/admin/cscrm_data_collections2/edit.html.erb
+++ b/app/views/admin/cscrm_data_collections2/edit.html.erb
@@ -3,7 +3,7 @@
<% end %>
- <%= link_to admin_cscrm_data_collections2_index_path(@cscrm_data_collection) do %>
+ <%= link_to admin_cscrm_data_collections2_path(@cscrm_data_collection) do %>
Back to CSCRM Data Collection
<% end %>
From a70117ae9298ea8dd9d59a7477b977fe636277d4 Mon Sep 17 00:00:00 2001
From: Ryan Wold
Date: Wed, 27 Sep 2023 20:56:54 -0700
Subject: [PATCH 7/9] rm comments
---
app/controllers/admin/cx_action_plans_controller.rb | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/app/controllers/admin/cx_action_plans_controller.rb b/app/controllers/admin/cx_action_plans_controller.rb
index 8f1ee7f71..d2dff6c4e 100644
--- a/app/controllers/admin/cx_action_plans_controller.rb
+++ b/app/controllers/admin/cx_action_plans_controller.rb
@@ -1,27 +1,22 @@
class Admin::CxActionPlansController < AdminController
before_action :set_cx_action_plan, only: %i[ show edit update destroy ]
- # GET /cx_action_plans or /cx_action_plans.json
def index
@cx_action_plans = CxActionPlan.all
end
- # GET /cx_action_plans/1 or /cx_action_plans/1.json
def show
end
- # GET /cx_action_plans/new
def new
@service_providers = ServiceProvider.all.includes(:organization).order('organizations.name', 'service_providers.name')
@cx_action_plan = CxActionPlan.new
end
- # GET /cx_action_plans/1/edit
def edit
@service_providers = ServiceProvider.all.includes(:organization).order('organizations.name', 'service_providers.name')
end
- # POST /cx_action_plans or /cx_action_plans.json
def create
@cx_action_plan = CxActionPlan.new(cx_action_plan_params)
@@ -36,7 +31,6 @@ def create
end
end
- # PATCH/PUT /cx_action_plans/1 or /cx_action_plans/1.json
def update
respond_to do |format|
if @cx_action_plan.update(cx_action_plan_params)
@@ -49,7 +43,6 @@ def update
end
end
- # DELETE /cx_action_plans/1 or /cx_action_plans/1.json
def destroy
@cx_action_plan.destroy
@@ -60,12 +53,10 @@ def destroy
end
private
- # Use callbacks to share common setup or constraints between actions.
def set_cx_action_plan
@cx_action_plan = CxActionPlan.find(params[:id])
end
- # Only allow a list of trusted parameters through.
def cx_action_plan_params
params.require(:cx_action_plan).permit(:service_provider_id, :year, :delivered_current_year, :to_deliver_next_year)
end
From ef002a141a28a5e7f9107b3cd034e57666052b20 Mon Sep 17 00:00:00 2001
From: Ryan Wold
Date: Wed, 27 Sep 2023 20:59:41 -0700
Subject: [PATCH 8/9] title row
---
app/models/cscrm_data_collection2.rb | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/models/cscrm_data_collection2.rb b/app/models/cscrm_data_collection2.rb
index 415266f09..a669fd341 100644
--- a/app/models/cscrm_data_collection2.rb
+++ b/app/models/cscrm_data_collection2.rb
@@ -609,6 +609,7 @@ def self.to_csv
"documented_methodology",
"documented_methodology_comments",
"conducts_scra_for_prioritized_products_and_services",
+ "conducts_scra_for_prioritized_products_and_services_value",
"conducts_scra_for_prioritized_products_and_services_translated_value",
"conducts_scra_for_prioritized_products_and_services_comments",
"personnel_required_to_complete_training_value",
From 2e96aa1aacd4ae7b310c0a86119ce1896f7f789c Mon Sep 17 00:00:00 2001
From: Ryan Wold
Date: Wed, 27 Sep 2023 21:09:20 -0700
Subject: [PATCH 9/9] default spec
---
.../{ => admin}/cx_action_plans_spec.rb | 38 +++++++++++--------
1 file changed, 22 insertions(+), 16 deletions(-)
rename spec/requests/{ => admin}/cx_action_plans_spec.rb (81%)
diff --git a/spec/requests/cx_action_plans_spec.rb b/spec/requests/admin/cx_action_plans_spec.rb
similarity index 81%
rename from spec/requests/cx_action_plans_spec.rb
rename to spec/requests/admin/cx_action_plans_spec.rb
index 587952eb8..a4ee7f117 100644
--- a/spec/requests/cx_action_plans_spec.rb
+++ b/spec/requests/admin/cx_action_plans_spec.rb
@@ -12,8 +12,8 @@
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
-RSpec.describe "/cx_action_plans", type: :request do
-
+RSpec.describe "/admin/cx_action_plans", type: :request do
+
# This should return the minimal set of attributes required to create a valid
# CxActionPlan. As you add validations to CxActionPlan, be sure to
# adjust the attributes here as well.
@@ -28,7 +28,7 @@
describe "GET /index" do
it "renders a successful response" do
CxActionPlan.create! valid_attributes
- get cx_action_plans_url
+ get admin_cx_action_plans_url
expect(response).to be_successful
end
end
@@ -36,14 +36,20 @@
describe "GET /show" do
it "renders a successful response" do
cx_action_plan = CxActionPlan.create! valid_attributes
- get cx_action_plan_url(cx_action_plan)
+ get admin_cx_action_plan_url(cx_action_plan)
expect(response).to be_successful
end
end
describe "GET /new" do
+ let(:user) { FactoryBot.create(:user, :admin) }
+
+ before do
+ sign_in(user)
+ end
+
it "renders a successful response" do
- get new_cx_action_plan_url
+ get new_admin_cx_action_plan_url
expect(response).to be_successful
end
end
@@ -51,7 +57,7 @@
describe "GET /edit" do
it "renders a successful response" do
cx_action_plan = CxActionPlan.create! valid_attributes
- get edit_cx_action_plan_url(cx_action_plan)
+ get edit_admin_cx_action_plan_url(cx_action_plan)
expect(response).to be_successful
end
end
@@ -60,12 +66,12 @@
context "with valid parameters" do
it "creates a new CxActionPlan" do
expect {
- post cx_action_plans_url, params: { cx_action_plan: valid_attributes }
+ post admin_cx_action_plans_url, params: { cx_action_plan: valid_attributes }
}.to change(CxActionPlan, :count).by(1)
end
it "redirects to the created cx_action_plan" do
- post cx_action_plans_url, params: { cx_action_plan: valid_attributes }
+ post admin_cx_action_plans_url, params: { cx_action_plan: valid_attributes }
expect(response).to redirect_to(cx_action_plan_url(CxActionPlan.last))
end
end
@@ -73,16 +79,16 @@
context "with invalid parameters" do
it "does not create a new CxActionPlan" do
expect {
- post cx_action_plans_url, params: { cx_action_plan: invalid_attributes }
+ post admin_cx_action_plans_url, params: { cx_action_plan: invalid_attributes }
}.to change(CxActionPlan, :count).by(0)
end
-
+
it "renders a response with 422 status (i.e. to display the 'new' template)" do
- post cx_action_plans_url, params: { cx_action_plan: invalid_attributes }
+ post admin_cx_action_plans_url, params: { cx_action_plan: invalid_attributes }
expect(response).to have_http_status(:unprocessable_entity)
end
-
+
end
end
@@ -94,27 +100,27 @@
it "updates the requested cx_action_plan" do
cx_action_plan = CxActionPlan.create! valid_attributes
- patch cx_action_plan_url(cx_action_plan), params: { cx_action_plan: new_attributes }
+ patch admin_cx_action_plan_url(cx_action_plan), params: { cx_action_plan: new_attributes }
cx_action_plan.reload
skip("Add assertions for updated state")
end
it "redirects to the cx_action_plan" do
cx_action_plan = CxActionPlan.create! valid_attributes
- patch cx_action_plan_url(cx_action_plan), params: { cx_action_plan: new_attributes }
+ patch admin_cx_action_plan_url(cx_action_plan), params: { cx_action_plan: new_attributes }
cx_action_plan.reload
expect(response).to redirect_to(cx_action_plan_url(cx_action_plan))
end
end
context "with invalid parameters" do
-
+
it "renders a response with 422 status (i.e. to display the 'edit' template)" do
cx_action_plan = CxActionPlan.create! valid_attributes
patch cx_action_plan_url(cx_action_plan), params: { cx_action_plan: invalid_attributes }
expect(response).to have_http_status(:unprocessable_entity)
end
-
+
end
end