effective_cpd 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09c97e5c49d9195e2b402e395d3039510de53b4dda1503ad052af6e7dc28221b'
4
- data.tar.gz: 24f617a5dd535b960ef3d9056956395f647a4a5581613014f8786b7d850b521e
3
+ metadata.gz: 3f74220d92f6cb570ae692fae9610f7aa3c16435f025381cda0f1d76d37eb462
4
+ data.tar.gz: 5370d91a541e895acccdfe6249e7fab92083bd2e7a39138a5f1c37fe0f6141f1
5
5
  SHA512:
6
- metadata.gz: e0d0aa17d7b171d1226a40852f79f8440a271cb7bd451bce1260887be7a2c722409d9d8fe4e96a1337e1911935985ef3b737e0cf75807e911714f945637afb3d
7
- data.tar.gz: 46744c3a3cbd29b133958be01a375238c0e8d413ba9e64eb0e020fdae9505af907e03c3257d93cecd13df534c9dd7bedd309d58a9052596aad5552161b1aa9ff
6
+ metadata.gz: 62863f2ebce7908df8170c7e981c026e2e16eff493376a2c21c00bfdeaeee1a2dd394713bfa5be6d1986452cd519ac6d94000f15c38ec8d5c3e561c4dd8eb297
7
+ data.tar.gz: f039df8345571dc4812372211026330d2fb69bc20eb398110cb8e0a5831273c4940132770dfdcb85212867e8b1beb5c7761b81ea52ae6b803f4579d65c48e4f3
@@ -44,6 +44,12 @@ $(document).on('click', '[data-cpd-edit-activity]', function(event) {
44
44
  $activity.children('.statement-activity-form').show()
45
45
  });
46
46
 
47
+ // When we click any x button
48
+ $(document).on('click', '[data-cpd-collapse]', function(event) {
49
+ event.preventDefault()
50
+ collapse_effective_cpd_activities()
51
+ });
52
+
47
53
  // Initializers
48
54
  $(document).ready(function() { initialize_effective_cpd_activities() });
49
55
  $(document).on('turbolinks:load', function() { initialize_effective_cpd_activities() });
@@ -0,0 +1,13 @@
1
+ module Admin
2
+ class CpdSpecialRulesController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_cpd) }
5
+
6
+ include Effective::CrudController
7
+
8
+ def permitted_params
9
+ params.require(:effective_cpd_special_rule).permit!
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Admin
2
+ class EffectiveCpdSpecialRulesDatatable < Effective::Datatable
3
+ datatable do
4
+ col :id, visible: false
5
+ col :created_at, visible: false
6
+ col :updated_at, visible: false
7
+
8
+ col :cpd_cycle, label: cpd_cycle_label.titleize
9
+
10
+ col :cpd_rules
11
+
12
+ col :category
13
+ col :max_credits_per_cycle, label: 'Max ' + cpd_credits_label.titleize
14
+
15
+ actions_col
16
+ end
17
+
18
+ collection do
19
+ cpd_cycle = Effective::CpdCycle.find(attributes[:cpd_cycle_id])
20
+ Effective::CpdSpecialRule.all.deep.where(cpd_cycle: cpd_cycle)
21
+ end
22
+ end
23
+ end
@@ -11,6 +11,9 @@ module Effective
11
11
  has_many :cpd_rules, dependent: :delete_all
12
12
  accepts_nested_attributes_for :cpd_rules, allow_destroy: true
13
13
 
14
+ has_many :cpd_special_rules, dependent: :delete_all
15
+ accepts_nested_attributes_for :cpd_special_rules, allow_destroy: true
16
+
14
17
  has_many :cpd_statements
15
18
 
16
19
  if respond_to?(:log_changes)
@@ -6,6 +6,9 @@ module Effective
6
6
  # For a Category: A maximum of 35 PDHs/year may be claimed in the Contributions to Knowledge category
7
7
  has_rich_text :category_credit_description
8
8
 
9
+ has_many :cpd_special_rule_mates, dependent: :destroy, inverse_of: :cpd_rule
10
+ has_many :cpd_special_rules, -> { CpdSpecialRule.sorted }, through: :cpd_special_rule_mates
11
+
9
12
  if respond_to?(:log_changes)
10
13
  log_changes(to: :cpd_cycle)
11
14
  end
@@ -34,6 +37,7 @@ module Effective
34
37
  timestamps
35
38
  end
36
39
 
40
+ scope :sorted, -> { order(:id) }
37
41
  scope :deep, -> { with_rich_text_category_credit_description.includes(:cpd_cycle, :ruleable) }
38
42
  scope :categories, -> { where(ruleable_type: 'Effective::CpdCategory') }
39
43
  scope :activities, -> { where(ruleable_type: 'Effective::CpdActivity') }
@@ -84,7 +88,13 @@ module Effective
84
88
  end
85
89
 
86
90
  def to_s
87
- formula.presence || 'category'
91
+ if activity?
92
+ formula.presence || ruleable.to_s.presence || 'activity rule'
93
+ elsif category?
94
+ ruleable.to_s.presence || 'category rule'
95
+ else
96
+ 'new rule'
97
+ end
88
98
  end
89
99
 
90
100
  def activity?
@@ -78,7 +78,7 @@ module Effective
78
78
  end
79
79
  end
80
80
 
81
- # This enforced CycleCategory.max_credits_per_cycle
81
+ # This enforces CycleCategory.max_credits_per_cycle
82
82
  statement.cpd_statement_activities.group_by(&:cpd_category).each do |cpd_category, activities|
83
83
  rule = cycle.rule_for(cpd_category)
84
84
  max_credits_per_cycle = rule.max_credits_per_cycle
@@ -98,6 +98,28 @@ module Effective
98
98
  end
99
99
  end
100
100
 
101
+ # This enforces cumulative max credits CpdSpecialRule.max_credits_per_cycle special rules
102
+ cycle.cpd_special_rules.select(&:cumulative_max_credits?).each do |special_rule|
103
+ cpd_categories = special_rule.ruleables.select { |obj| obj.kind_of?(Effective::CpdCategory) }
104
+
105
+ max_credits_per_cycle = special_rule.max_credits_per_cycle
106
+ raise('expected max credits per cycle to be present') unless max_credits_per_cycle.to_i > 0
107
+
108
+ activities = statement.cpd_statement_activities.select { |sa| cpd_categories.include?(sa.cpd_category) }
109
+
110
+ activities.each do |activity|
111
+ next if activity.marked_for_destruction?
112
+
113
+ max_credits_per_cycle -= activity.score # We're already scored. Counting down...
114
+
115
+ if max_credits_per_cycle < 0
116
+ activity.score = [activity.score + max_credits_per_cycle, 0].max
117
+ activity.carry_forward = activity.max_score - activity.score
118
+ activity.reduced_messages["category_#{activity.cpd_category_id}"] = "You have reached the cumulative maximum of #{special_rule.max_credits_per_cycle}/#{cpd_cycle_label} for activities in the #{cpd_categories.map(&:to_s).to_sentence} categories"
119
+ end
120
+ end
121
+ end
122
+
101
123
  # This enforces the max_cycles_can_carry_forward logic
102
124
  # If an Activity cannot be carried forward another cycle, its carry_forward should be 0
103
125
  next_cycle = @cycles[@cycles.index(cycle) + 1]
@@ -0,0 +1,54 @@
1
+ module Effective
2
+ class CpdSpecialRule < ActiveRecord::Base
3
+ belongs_to :cpd_cycle
4
+
5
+ has_many :cpd_special_rule_mates, dependent: :destroy, inverse_of: :cpd_special_rule
6
+ has_many :cpd_rules, -> { CpdRule.sorted }, through: :cpd_special_rule_mates
7
+
8
+ if respond_to?(:log_changes)
9
+ log_changes
10
+ end
11
+
12
+ CATEGORIES = ['cumulative max credits']
13
+
14
+ effective_resource do
15
+ category :string # Special rule tyoes
16
+
17
+ # For cumulative max credits
18
+ max_credits_per_cycle :integer
19
+
20
+ timestamps
21
+ end
22
+
23
+ scope :deep, -> { includes(:cpd_special_rule_mates, cpd_rules: [:ruleable]) }
24
+ scope :sorted, -> { order(:id) }
25
+
26
+ before_validation do
27
+ self.category ||= CATEGORIES.first
28
+ end
29
+
30
+ validates :category, presence: true, inclusion: { in: CATEGORIES }
31
+
32
+ with_options(if: -> { cumulative_max_credits? }) do
33
+ validates :max_credits_per_cycle, presence: true, numericality: { greater_than: 0 }
34
+ end
35
+
36
+ def to_s
37
+ if cumulative_max_credits?
38
+ "Cumulative max #{max_credits_per_cycle} credits"
39
+ else
40
+ 'cpd special rule'
41
+ end
42
+ end
43
+
44
+ def cumulative_max_credits?
45
+ category == 'cumulative max credits'
46
+ end
47
+
48
+ # Right now this is going to be just Effective::CpdCategory objects
49
+ def ruleables
50
+ cpd_rules.map(&:ruleable)
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ module Effective
2
+ class CpdSpecialRuleMate < ActiveRecord::Base
3
+ belongs_to :cpd_rule
4
+ belongs_to :cpd_special_rule
5
+ end
6
+ end
@@ -3,6 +3,9 @@
3
3
  = render 'admin/cpd_cycles/form_cpd_cycle', cpd_cycle: cpd_cycle
4
4
 
5
5
  - if cpd_cycle.persisted?
6
+ = tab 'Special Rules' do
7
+ = render 'admin/cpd_cycles/form_cpd_special_rules', cpd_cycle: cpd_cycle
8
+
6
9
  = tab 'Category and Activity Rules' do
7
10
  = tabs do
8
11
  - Effective::CpdCategory.deep.sorted.each do |cpd_category|
@@ -6,9 +6,14 @@
6
6
  %p= link_to 'Edit Category', edit_path, target: '_blank'
7
7
 
8
8
  = f.fields_for :cpd_rules, cpd_cycle.rule_for(cpd_category) do |fc|
9
- = fc.number_field :max_credits_per_cycle,
10
- label: "Max #{cpd_credits_label} per #{cpd_cycle_label}",
11
- hint: "The maximum number of #{cpd_credits_label} that may be earned in this category for this #{cpd_cycle_label}. Leave blank for no limit."
9
+ .row
10
+ .col
11
+ = fc.number_field :max_credits_per_cycle,
12
+ label: "Max #{cpd_credits_label} per #{cpd_cycle_label}",
13
+ hint: "The maximum number of #{cpd_credits_label} that may be earned in this category for this #{cpd_cycle_label}. Leave blank for no limit."
14
+ .col
15
+ = f.select :cpd_special_rule_ids, cpd_cycle.cpd_special_rules,
16
+ label: 'Special Rules', hint: "Special rules operate on more than one category at a time"
12
17
 
13
18
  = fc.rich_text_area :category_credit_description,
14
19
  hint: "A description of the maximum #{cpd_credits_label} and carry forward policy of activities for this category"
@@ -0,0 +1,4 @@
1
+ %p Special rules operate on more than one category at a time.
2
+
3
+ - datatable = Admin::EffectiveCpdSpecialRulesDatatable.new(cpd_cycle_id: cpd_cycle.id)
4
+ = render_datatable(datatable, inline: true)
@@ -0,0 +1,18 @@
1
+ = effective_form_with(model: [:admin, cpd_special_rule], engine: true) do |f|
2
+ - if inline_datatable?
3
+ = f.hidden_field :cpd_cycle_id
4
+ - else
5
+ = f.select :cpd_cycle_id, Effective::CpdCycle.sorted.all, label: cpd_cycle_label.titleize
6
+
7
+ = f.select :category, Effective::CpdSpecialRule::CATEGORIES
8
+
9
+ = f.show_if :category, 'cumulative max credits' do
10
+ = f.number_field :max_credits_per_cycle, label: "Cumulative max credits per #{cpd_cycle_label}",
11
+ hint: "The cumulative max #{cpd_credits_label} per #{cpd_cycle_label} that can be earned between the following categories."
12
+
13
+ - cpd_category_rules = cpd_special_rule.cpd_cycle.cpd_rules.select(&:category?)
14
+
15
+ = f.select :cpd_rule_ids, cpd_category_rules, label: 'Cumulative max included categories',
16
+ hint: "These categories will shared a maximum cumulative #{cpd_credits_label} per #{cpd_cycle_label}."
17
+
18
+ = effective_submit(f)
@@ -6,6 +6,7 @@
6
6
  - activity = cpd_statement_activity.cpd_activity
7
7
  - rule = statement.cpd_cycle.rule_for(activity)
8
8
 
9
+ .float-right= link_to icon('x', class: 'small-1'), '#', 'data-cpd-collapse': true
9
10
  %h3= activity
10
11
 
11
12
  - if activity.body.present?
@@ -7,6 +7,8 @@
7
7
  - next if rule.unavailable?
8
8
 
9
9
  = tab(category) do
10
+ .float-right= link_to icon('x', class: 'small-1'), '#', 'data-cpd-collapse': true
11
+
10
12
  %h3
11
13
  #{category}
12
14
  %small
@@ -50,7 +50,7 @@
50
50
 
51
51
  .statement-activity-form.row{style: 'display: none;'}
52
52
  .col
53
- .border.p-3
53
+ .border.px-3
54
54
  = yield(statement_activity)
55
55
 
56
56
  %hr
@@ -4,6 +4,8 @@ EffectiveCpd.setup do |config|
4
4
 
5
5
  config.cpd_cycles_table_name = :cpd_cycles
6
6
  config.cpd_rules_table_name = :cpd_rules
7
+ config.cpd_special_rules_table_name = :cpd_special_rules
8
+ config.cpd_special_rule_mates_table_name = :cpd_special_rule_mates
7
9
 
8
10
  config.cpd_statements_table_name = :cpd_statements
9
11
  config.cpd_statement_activities_table_name = :cpd_statement_activities
data/config/routes.rb CHANGED
@@ -32,6 +32,7 @@ EffectiveCpd::Engine.routes.draw do
32
32
  resources :cpd_activities, except: [:show]
33
33
  resources :cpd_cycles, except: [:show]
34
34
  resources :cpd_rules, only: [:index]
35
+ resources :cpd_special_rules, except: [:show]
35
36
 
36
37
  resources :cpd_statements, only: [:index, :show]
37
38
 
@@ -51,6 +51,24 @@ class CreateEffectiveCpd < ActiveRecord::Migration[6.0]
51
51
  t.datetime :created_at
52
52
  end
53
53
 
54
+ create_table <%= @cpd_special_rules_table_name %> do |t|
55
+ t.references :cpd_cycle
56
+
57
+ t.integer :max_credits_per_cycle
58
+ t.string :category
59
+
60
+ t.datetime :updated_at
61
+ t.datetime :created_at
62
+ end
63
+
64
+ create_table <%= @cpd_special_rule_mates_table_name %> do |t|
65
+ t.references :cpd_rule
66
+ t.references :cpd_special_rule
67
+
68
+ t.datetime :updated_at
69
+ t.datetime :created_at
70
+ end
71
+
54
72
  create_table <%= @cpd_statement_activities_table_name %> do |t|
55
73
  t.references :cpd_statement
56
74
  t.references :cpd_activity
data/db/seeds.rb CHANGED
@@ -7,6 +7,8 @@ if Rails.env.test?
7
7
  Effective::CpdCategory.delete_all
8
8
  Effective::CpdActivity.delete_all
9
9
  Effective::CpdRule.delete_all
10
+ Effective::CpdSpecialRule.delete_all
11
+ Effective::CpdSpecialRuleMate.delete_all
10
12
 
11
13
  ActionText::RichText.where(record_type: ['Effective::CpdCycle', 'Effective::CpdCycle', 'Effective::CpdActivity', 'Effective::CpdAudit', 'Effective::CpdAuditLevelSection', 'Effective::CpdAuditLevelQuestion']).delete_all
12
14
  end
data/lib/effective_cpd.rb CHANGED
@@ -8,7 +8,7 @@ module EffectiveCpd
8
8
  def self.config_keys
9
9
  [
10
10
  :cpd_categories_table_name, :cpd_activities_table_name,
11
- :cpd_cycles_table_name, :cpd_rules_table_name,
11
+ :cpd_cycles_table_name, :cpd_rules_table_name, :cpd_special_rules_table_name, :cpd_special_rule_mates_table_name,
12
12
  :cpd_statements_table_name, :cpd_statement_activities_table_name,
13
13
  :cpd_audit_levels_table_name, :cpd_audit_level_sections_table_name,
14
14
  :cpd_audit_level_questions_table_name, :cpd_audit_level_question_options_table_name,
@@ -1,3 +1,3 @@
1
1
  module EffectiveCpd
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'
3
3
  end
@@ -25,6 +25,8 @@ module EffectiveCpd
25
25
 
26
26
  @cpd_cycles_table_name = ':' + EffectiveCpd.cpd_cycles_table_name.to_s
27
27
  @cpd_rules_table_name = ':' + EffectiveCpd.cpd_rules_table_name.to_s
28
+ @cpd_special_rules_table_name = ':' + EffectiveCpd.cpd_special_rules_table_name.to_s
29
+ @cpd_special_rule_mates_table_name = ':' + EffectiveCpd.cpd_special_rule_mates_table_name.to_s
28
30
 
29
31
  @cpd_statements_table_name = ':' + EffectiveCpd.cpd_statements_table_name.to_s
30
32
  @cpd_statement_activities_table_name = ':' + EffectiveCpd.cpd_statement_activities_table_name.to_s
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_cpd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-18 00:00:00.000000000 Z
11
+ date: 2021-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -188,6 +188,7 @@ files:
188
188
  - app/controllers/admin/cpd_categories_controller.rb
189
189
  - app/controllers/admin/cpd_cycles_controller.rb
190
190
  - app/controllers/admin/cpd_rules_controller.rb
191
+ - app/controllers/admin/cpd_special_rules_controller.rb
191
192
  - app/controllers/admin/cpd_statements_controller.rb
192
193
  - app/controllers/effective/cpd_audit_reviews_controller.rb
193
194
  - app/controllers/effective/cpd_audits_controller.rb
@@ -202,6 +203,7 @@ files:
202
203
  - app/datatables/admin/effective_cpd_categories_datatable.rb
203
204
  - app/datatables/admin/effective_cpd_cycles_datatable.rb
204
205
  - app/datatables/admin/effective_cpd_rules_datatable.rb
206
+ - app/datatables/admin/effective_cpd_special_rules_datatable.rb
205
207
  - app/datatables/admin/effective_cpd_statements_datatable.rb
206
208
  - app/datatables/effective_cpd_available_audit_reviews_datatable.rb
207
209
  - app/datatables/effective_cpd_available_audits_datatable.rb
@@ -227,6 +229,8 @@ files:
227
229
  - app/models/effective/cpd_cycle.rb
228
230
  - app/models/effective/cpd_rule.rb
229
231
  - app/models/effective/cpd_scorer.rb
232
+ - app/models/effective/cpd_special_rule.rb
233
+ - app/models/effective/cpd_special_rule_mate.rb
230
234
  - app/models/effective/cpd_statement.rb
231
235
  - app/models/effective/cpd_statement_activity.rb
232
236
  - app/views/admin/cpd_activities/_form.html.haml
@@ -253,6 +257,8 @@ files:
253
257
  - app/views/admin/cpd_cycles/_form_content.html.haml
254
258
  - app/views/admin/cpd_cycles/_form_cpd_cycle.html.haml
255
259
  - app/views/admin/cpd_cycles/_form_cpd_rules.html.haml
260
+ - app/views/admin/cpd_cycles/_form_cpd_special_rules.html.haml
261
+ - app/views/admin/cpd_special_rules/_form.html.haml
256
262
  - app/views/admin/cpd_statements/_cpd_statement.html.haml
257
263
  - app/views/effective/cpd_audit_level_questions/_cpd_audit_level_question.html.haml
258
264
  - app/views/effective/cpd_audit_responses/_cpd_audit_response.html.haml