Skip to content

Commit

Permalink
feat(fworks): basic framework evaulations crud
Browse files Browse the repository at this point in the history
  • Loading branch information
ryantk committed Sep 28, 2023
1 parent 52e604d commit bf9d342
Show file tree
Hide file tree
Showing 70 changed files with 870 additions and 50 deletions.
1 change: 1 addition & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Rails/BulkChangeTable:
- 'db/migrate/20221014140003_add_more_trust_fields_to_support_organisations.rb'
- 'db/migrate/20230210133230_refactor_notifications_into_one_table.rb'
- 'db/migrate/20230919092926_update_lot_frameworks_framework.rb'
- 'db/migrate/20230919144709_add_reference_sequence_to_frameworks_framework.rb'

# Offense count: 2
# Configuration parameters: Include.
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ ruby "3.2.1"

gem "aasm"
gem "application_insights"
gem "ar-sequence"
gem "aws-sdk-s3", require: false
#
# awaiting PR merge here: https://github.com/Azure/azure-storage-ruby/pull/228 due to need for faraday 2
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ GEM
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
application_insights (0.5.6)
ar-sequence (0.2.1)
activerecord
ast (2.4.2)
attr_required (1.0.1)
aws-eventstream (1.2.0)
Expand Down Expand Up @@ -668,6 +670,7 @@ PLATFORMS
DEPENDENCIES
aasm
application_insights
ar-sequence
aws-sdk-s3
azure-storage-blob!
better_errors
Expand Down
5 changes: 5 additions & 0 deletions app/assets/stylesheets/base/_misc.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
.flex-align-center {
display: flex;
align-items: center;
gap: 10px;

.govuk-body {
margin-bottom: 0;
}
}

.pull-up-10 {
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/frameworks/evaluation_contacts_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class Frameworks::EvaluationContactsController < Frameworks::ApplicationController
end
50 changes: 50 additions & 0 deletions app/controllers/frameworks/evaluations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
class Frameworks::EvaluationsController < Frameworks::ApplicationController
before_action :redirect_to_register_tab, unless: :turbo_frame_request?, only: :index
before_action :set_form_options, only: %i[new create]

def index
@filtering = Frameworks::Evaluation.filtering(filter_form_params)
@evaluations = @filtering.results.paginate(page: params[:evaluations_page])
end

def show
@evaluation = Frameworks::Evaluation.find(params[:id])
@activity_log_items = @evaluation.activity_log_items.paginate(page: params[:history_page])
end

def new
@evaluation = Frameworks::Evaluation.new(framework_id: params[:framework_id])
end

def create
@evaluation = Frameworks::Evaluation.new(evaluation_params)

if @evaluation.save
redirect_to @evaluation
else
render :new
end
end

private

def set_form_options
@frameworks = Frameworks::Framework.for_evaluation
@agents = Support::Agent.framework_evaluators
end

def filter_form_params
params.fetch(:evaluations_filter, {}).permit(
:sort_by, :sort_order,
status: []
)
end

def evaluation_params
params.require(:frameworks_evaluation).permit(:framework_id, :assignee_id)
end

def redirect_to_register_tab
redirect_to frameworks_root_path(anchor: "evaluations", **request.params.except(:controller, :action))
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class Frameworks::CategorisationsController < Frameworks::ApplicationController
class Frameworks::FrameworkCategorisationsController < Frameworks::ApplicationController
def edit
@framework = Frameworks::Framework.find(params[:framework_id])
end
Expand All @@ -8,7 +8,7 @@ def update

@framework.update!(category_params)

redirect_to @framework
redirect_to frameworks_framework_path(@framework, back_to: params[:back_to])
end

private
Expand Down
4 changes: 4 additions & 0 deletions app/models/concerns/filter_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def initial_scope(proc)
define_method :scoped_records do
proc.call
end

define_method :available_sort_options do
proc.call.available_sort_options
end
end

def sort_options(proc)
Expand Down
9 changes: 9 additions & 0 deletions app/models/frameworks/activity_log_item/presentable.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
module Frameworks::ActivityLogItem::Presentable
extend ActiveSupport::Concern

def activity_type_identifier
activity_type.demodulize.underscore
end

def subject_type_identifier
subject_type.demodulize.underscore
end

def display_subject
"#{subject.try(:activity_log_display_type)} #{subject.try(:activity_log_display_short_name)}"
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ module Frameworks::ActivityLoggable::ActivityLogPresentable
delegate :version_at, to: :paper_trail
end

def specific_change_template_for(_activity_loggable_version)
nil
end

def activity_log_display_name
try(:full_name) || try(:short_name) || try(:name) || try(:reference)
end
Expand Down
26 changes: 26 additions & 0 deletions app/models/frameworks/activity_loggable_version.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
class Frameworks::ActivityLoggableVersion < PaperTrail::Version
include Presentable

after_create_commit :save_default_attributes_on_item_created

def field_changes
object_changes.except("id", "created_at", "updated_at")
end

def changed_fields_only?(*fields)
field_changes.keys == Array(fields)
end

def changed_fields_including?(*fields)
field_changes.keys.intersection(Array(fields)).any?
end

def item_association_at_version_or_current(association:, id:, version_at: created_at)
current_version_of_association = item.class.reflections[association].klass.find_by(id:)
current_version_of_association.try(:version_at, version_at).presence || current_version_of_association
end

private

def save_default_attributes_on_item_created
return unless item.id_previously_changed?

object_changes_including_database_default_values = item.reload.attributes.each_with_object(object_changes) do |(attr, value), all_changes|
all_changes[attr] ||= [nil, value]
end

update!(object_changes: object_changes_including_database_default_values)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ def presentable_changes
Frameworks::ActivityLoggableVersion::PresentableChanges.new(self)
end

def specific_change_template
item.specific_change_template_for(self)
end

def display_field_version(field:, value:)
display_value =
if field.ends_with?("_id")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ def initialize(version)
def each
return to_enum(:each) unless block_given?

@version.object_changes.except("id", "created_at", "updated_at").each do |field, changes|
@version.field_changes.each do |field, changes|
from = @version.display_field_version(field:, value: changes.first)
to = @version.display_field_version(field:, value: changes.last)

# Ignore empty -> empty changes
next if [String(to), String(from)].all?(&:empty?)

yield PresentableChange.new(field:, from:, to:)
end
end
Expand Down
13 changes: 13 additions & 0 deletions app/models/frameworks/evaluation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Frameworks::Evaluation < ApplicationRecord
include Frameworks::ActivityLoggable
include Filterable
include Sortable
include StatusChangeable
include Presentable
include ActivityLogPresentable

belongs_to :framework
has_one :provider, through: :framework
belongs_to :assignee, class_name: "Support::Agent"
belongs_to :contact, class_name: "Frameworks::ProviderContact", optional: true
end
8 changes: 8 additions & 0 deletions app/models/frameworks/evaluation/activity_log_presentable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Frameworks::Evaluation::ActivityLogPresentable
extend ActiveSupport::Concern

def specific_change_template_for(activity_loggable_version)
return "evaluations/status" if activity_loggable_version.changed_fields_only?("status")
return "evaluations/contact" if activity_loggable_version.changed_fields_only?("contact_id")
end
end
13 changes: 13 additions & 0 deletions app/models/frameworks/evaluation/filterable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Frameworks::Evaluation::Filterable
extend ActiveSupport::Concern

included do
scope :by_status, ->(statuses) { where(status: Array(statuses)) }
end

class_methods do
def filtering(params = {})
Frameworks::Evaluation::Filtering.new(params)
end
end
end
7 changes: 7 additions & 0 deletions app/models/frameworks/evaluation/filtering.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Frameworks::Evaluation::Filtering
include FilterForm

initial_scope -> { Frameworks::Evaluation }

filter_by :status, options: -> { Frameworks::Evaluation.statuses.map { |label, _id| [label.humanize, label] } }
end
43 changes: 43 additions & 0 deletions app/models/frameworks/evaluation/presentable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Frameworks::Evaluation::Presentable
extend ActiveSupport::Concern

def framework_name
framework.try(:name)
end

def framework_reference_and_name
framework.try(:reference_and_name)
end

def framework_provider_name
framework.try(:provider_name)
end

def framework_category_names
framework.try(:category_names)
end

def display_status
ApplicationController.render(partial: "frameworks/evaluations/status", locals: { status: })
end

def display_assignee
assignee.try(:full_name)
end

def display_last_updated
"- TODO -"
end

def contact_name
contact.try(:name)
end

def contact_email
contact.try(:email)
end

def contact_phone
contact.try(:phone)
end
end
27 changes: 27 additions & 0 deletions app/models/frameworks/evaluation/sortable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Frameworks::Evaluation::Sortable
extend ActiveSupport::Concern

included do
scope :sort_by_updated, ->(direction = "descending") { order("frameworks_evaluations.updated_at #{safe_direction(direction)}") }
scope :sort_by_reference, ->(direction = "descending") { order("frameworks_evaluations.reference #{safe_direction(direction)}") }
end

class_methods do
def sorted_by(sort_by:, sort_order:)
return sort_by_reference("descending") unless sort_by.present? && sort_order.present?

public_send("sort_by_#{sort_by}", sort_order)
end

def available_sort_options
[
%w[Reference reference],
%w[Updated updated],
]
end

def safe_direction(direction)
direction == "descending" ? "DESC" : "ASC"
end
end
end
53 changes: 53 additions & 0 deletions app/models/frameworks/evaluation/status_changeable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module Frameworks::Evaluation::StatusChangeable
extend ActiveSupport::Concern

included do
include AASM

enum status: {
draft: 0,
in_progress: 1,
approved: 2,
not_approved: 3,
cancelled: 4,
}, _scopes: false

aasm column: :status do
Frameworks::Evaluation.statuses.each { |status, _| state status.to_sym }

event :start do
transitions from: :draft, to: :in_progress, after: :after_starting_evaluation
end

event :approve do
transitions from: :in_progress, to: :approved, after: :after_approving
end

event :disapprove do
transitions from: :in_progress, to: :not_approved, after: :after_disapproving
end

event :cancel do
transitions from: :in_progress, to: :cancelled, after: :after_cancelling
end
end
end

private

def after_starting_evaluation
framework.start_evaluation!(self)
end

def after_approving
framework.approve_evaluation!(self)
end

def after_disapproving
framework.disapprove_evaluation!(self)
end

def after_cancelling
framework.cancel_evaluation!(self)
end
end
Loading

0 comments on commit bf9d342

Please sign in to comment.