effective_cpd 0.1 → 0.1.5

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -0
  3. data/app/assets/javascripts/effective_cpd/activities.js +6 -0
  4. data/app/controllers/admin/cpd_special_rules_controller.rb +13 -0
  5. data/app/datatables/admin/effective_cpd_special_rules_datatable.rb +23 -0
  6. data/app/helpers/effective_cpd_helper.rb +10 -0
  7. data/app/models/concerns/effective_cpd_user.rb +29 -0
  8. data/app/models/effective/cpd_cycle.rb +20 -2
  9. data/app/models/effective/cpd_rule.rb +20 -5
  10. data/app/models/effective/cpd_scorer.rb +23 -1
  11. data/app/models/effective/cpd_special_rule.rb +54 -0
  12. data/app/models/effective/cpd_special_rule_mate.rb +6 -0
  13. data/app/models/effective/cpd_statement.rb +10 -3
  14. data/app/views/admin/cpd_cycles/_form.html.haml +3 -0
  15. data/app/views/admin/cpd_cycles/_form_content.html.haml +8 -0
  16. data/app/views/admin/cpd_cycles/_form_cpd_rules.html.haml +12 -7
  17. data/app/views/admin/cpd_cycles/_form_cpd_special_rules.html.haml +4 -0
  18. data/app/views/admin/cpd_special_rules/_form.html.haml +18 -0
  19. data/app/views/effective/cpd_audits/cpd_audit_level_section.html.haml +1 -1
  20. data/app/views/effective/cpd_statement_activities/_form.html.haml +1 -0
  21. data/app/views/effective/cpd_statements/_activities_new.html.haml +3 -0
  22. data/app/views/effective/cpd_statements/_activities_table.html.haml +1 -1
  23. data/app/views/effective/cpd_statements/_layout.html.haml +3 -4
  24. data/app/views/effective/cpd_statements/submit.html.haml +1 -2
  25. data/config/effective_cpd.rb +2 -0
  26. data/config/routes.rb +1 -0
  27. data/db/migrate/01_create_effective_cpd.rb.erb +18 -0
  28. data/db/seeds.rb +15 -13
  29. data/lib/effective_cpd.rb +1 -1
  30. data/lib/effective_cpd/engine.rb +7 -0
  31. data/lib/effective_cpd/version.rb +1 -1
  32. data/lib/generators/effective_cpd/install_generator.rb +2 -0
  33. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03ea8530cc9464864529c2138bc50c557e6673bfe603c506a61536938f088fb2
4
- data.tar.gz: 6cc9d5170e3aa9f4ad18b86422f4634a0a1127d1b133a598288f816e544cbcba
3
+ metadata.gz: c655d5b1c2e948affbfee9558e51b7e66ca5cfd0e43fbf2a58fecc7a3b2d885f
4
+ data.tar.gz: 06fe0a9add6534d48e5b51055343018c75723fa288f9051666fd29767fab0f86
5
5
  SHA512:
6
- metadata.gz: d82e2b524bf841a537aecdee7c17a6f29d1ff5d046d7af4a42d7cb05e19c39ed7c23d9886f5603dbfca389eecd144846cbb49ccc83ce182143c0961b804b2ddb
7
- data.tar.gz: 60f571e5289e2376eeefe5b465ad1f11685cfb6718269c121ba45b69408a8f2f94b73f216191e16ab8f669f33c3369f256ce12611e08d4943ad101d2000b2387
6
+ metadata.gz: bd95fa02fad56e41914ac568da4cdaf0b2413297a35fbccf4d8c32659d1e2e30c0e474871d33f124a03f7c962372a9136149cd587a520c6c41447e269692901a
7
+ data.tar.gz: 212aaa715dcee4635d708a579fad03e18bdfd8b253515d2a3b3b2dcaf1419eb7237766c68260d83992c6520d802fc128ebd355537b820578ea823b9362101661
data/README.md CHANGED
@@ -134,6 +134,39 @@ As an admin, visit the CPD Categories, then CPD Cycles, and CPD Audit levels.
134
134
 
135
135
  Once all these 3 areas have been configured, users can submit statements and audits can be performed.
136
136
 
137
+ ## Required Score
138
+
139
+ You can specify the required score in the CPD Cycle.
140
+
141
+ You can also programatically do it. Add the following to your user class.
142
+
143
+ ```
144
+ # This is an ActiveRecord concern to add the has_many :cpd_statements
145
+ effective_cpd_user
146
+
147
+ # We require 100 points in the last 3 years.
148
+ def cpd_statement_required_score(cpd_statement)
149
+ # We always consider the 3 year window, of the passed cpd_statement and the last two statements
150
+ last_two_statements = cpd_statements.select do |statement|
151
+ statement.completed? && statement.cpd_cycle_id < cpd_statement.cpd_cycle_id
152
+ end.last(2)
153
+
154
+ # They can submit 0 0 100
155
+ return 0 if last_two_statements.length < 2
156
+
157
+ # 100 points in the last 3 years.
158
+ required_score = 100
159
+
160
+ # Score so far
161
+ existing_score = last_two_statements.sum { |statement| statement.score }
162
+ raise('expected existing_score to be >= 0') if existing_score < 0
163
+
164
+ # Required score minus previous
165
+ return 0 if existing_score >= required_score
166
+ (required_score - existing_score)
167
+ end
168
+ ```
169
+
137
170
  ## Authorization
138
171
 
139
172
  All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
@@ -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
@@ -30,6 +30,16 @@ module EffectiveCpdHelper
30
30
  end
31
31
  end
32
32
 
33
+ def cpd_statement_submit_label(cpd_statement)
34
+ label = "Yes, I understand I will not be able to submit more #{cpd_credits_label} or modify any of my activities for this #{cpd_cycle_label}, once I submit."
35
+
36
+ if (end_at = cpd_statement.cpd_cycle.end_at).present?
37
+ label += " The last date to submit this form is #{end_at.strftime("%B %d, %Y")}."
38
+ end
39
+
40
+ label
41
+ end
42
+
33
43
  def effective_cpd_categories
34
44
  @effective_cpd_categories ||= Effective::CpdCategory.deep.sorted
35
45
  end
@@ -0,0 +1,29 @@
1
+ # EffectiveCpdUser
2
+ #
3
+ # Mark your user model with effective_cpd_user to get a few helpers
4
+ # And user specific point required scores
5
+
6
+ module EffectiveCpdUser
7
+ extend ActiveSupport::Concern
8
+
9
+ module Base
10
+ def effective_cpd_user
11
+ include ::EffectiveCpdUser
12
+ end
13
+ end
14
+
15
+ included do
16
+ has_many :cpd_statements, -> { Effective::CpdStatement.sorted }, class_name: 'Effective::CpdStatement'
17
+ has_many :cpd_audits, -> { Effective::CpdAudit.sorted }, inverse_of: :user, class_name: 'Effective::CpdAudit'
18
+ has_many :cpd_audit_reviews, -> { Effective::CpdAuditReview.sorted }, inverse_of: :user, class_name: 'Effective::CpdAuditReview'
19
+ end
20
+
21
+ def cpd_statement_required_score(cpd_statement)
22
+ nil
23
+ end
24
+
25
+ module ClassMethods
26
+ def effective_cpd_user?; true; end
27
+ end
28
+
29
+ end
@@ -1,6 +1,7 @@
1
1
  module Effective
2
2
  class CpdCycle < ActiveRecord::Base
3
3
  has_rich_text :all_steps_content # Update build_from_cycle() below if these change
4
+ has_rich_text :sidebar_content
4
5
  has_rich_text :start_content
5
6
  has_rich_text :activities_content
6
7
  has_rich_text :agreements_content
@@ -10,6 +11,9 @@ module Effective
10
11
  has_many :cpd_rules, dependent: :delete_all
11
12
  accepts_nested_attributes_for :cpd_rules, allow_destroy: true
12
13
 
14
+ has_many :cpd_special_rules, dependent: :delete_all
15
+ accepts_nested_attributes_for :cpd_special_rules, allow_destroy: true
16
+
13
17
  has_many :cpd_statements
14
18
 
15
19
  if respond_to?(:log_changes)
@@ -29,6 +33,7 @@ module Effective
29
33
 
30
34
  scope :deep, -> {
31
35
  with_rich_text_all_steps_content
36
+ .with_rich_text_sidebar_content
32
37
  .with_rich_text_start_content
33
38
  .with_rich_text_activities_content
34
39
  .with_rich_text_submit_content
@@ -76,13 +81,26 @@ module Effective
76
81
  attributes = cycle.dup.attributes.except('title', 'token', 'start_at', 'end_at')
77
82
  assign_attributes(attributes)
78
83
 
79
- [:all_steps_content, :start_content, :activities_content, :submit_content, :complete_content].each do |rich_text|
84
+ [:all_steps_content, :sidebar_content, :start_content, :activities_content, :submit_content, :complete_content].each do |rich_text|
80
85
  self.send("#{rich_text}=", cycle.send(rich_text))
81
86
  end
82
87
 
83
88
  cycle.cpd_rules.each do |rule|
84
89
  attributes = rule.dup.attributes.except('cpd_cycle_id')
85
- self.cpd_rules.build(attributes)
90
+ cpd_rule = self.cpd_rules.build(attributes)
91
+
92
+ if rule.category?
93
+ cpd_rule.category_credit_description = rule.category_credit_description
94
+ end
95
+ end
96
+
97
+ cycle.cpd_special_rules.each do |special_rule|
98
+ attributes = special_rule.dup.attributes.except('cpd_cycle_id')
99
+ cpd_special_rule = self.cpd_special_rules.build(attributes)
100
+
101
+ special_rule.ruleables.each do |ruleable|
102
+ cpd_special_rule.cpd_special_rule_mates.build(cpd_rule: self.rule_for(ruleable))
103
+ end
86
104
  end
87
105
 
88
106
  self
@@ -3,6 +3,12 @@ module Effective
3
3
  belongs_to :cpd_cycle
4
4
  belongs_to :ruleable, polymorphic: true # Activity or Category
5
5
 
6
+ # For a Category: A maximum of 35 PDHs/year may be claimed in the Contributions to Knowledge category
7
+ has_rich_text :category_credit_description
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
+
6
12
  if respond_to?(:log_changes)
7
13
  log_changes(to: :cpd_cycle)
8
14
  end
@@ -12,7 +18,6 @@ module Effective
12
18
 
13
19
  effective_resource do
14
20
  # A plaintext description of the formula
15
- # For a Category: A maximum of 35 PDHs/year may be claimed in the Contributions to Knowledge category
16
21
  # For a Activity: 15 hours of work equals 1 credit
17
22
  credit_description :text
18
23
 
@@ -23,6 +28,7 @@ module Effective
23
28
  formula :string
24
29
 
25
30
  # Maximum number of cycles can carry forward
31
+ # Only considered for activities
26
32
  max_cycles_can_carry_forward :integer
27
33
 
28
34
  # Cannot be entered in this cycle
@@ -31,19 +37,22 @@ module Effective
31
37
  timestamps
32
38
  end
33
39
 
34
- scope :deep, -> { includes(:cpd_cycle, :ruleable) }
40
+ scope :sorted, -> { order(:id) }
41
+ scope :deep, -> { with_rich_text_category_credit_description.includes(:cpd_cycle, :ruleable) }
35
42
  scope :categories, -> { where(ruleable_type: 'Effective::CpdCategory') }
36
43
  scope :activities, -> { where(ruleable_type: 'Effective::CpdActivity') }
37
44
  scope :unavailable, -> { where(unavailable: true) }
38
45
 
39
46
  #validates :cpd_cycle_id, uniqueness: { scope: [:ruleable_id, :ruleable_type] }
40
- validates :credit_description, presence: true
41
47
  validates :max_credits_per_cycle, numericality: { greater_than: 0, allow_nil: true }
42
- validates :max_cycles_can_carry_forward, numericality: { greater_than: 0, allow_nil: true }
48
+ validates :max_cycles_can_carry_forward, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
43
49
 
44
50
  validates :formula, presence: true, if: -> { activity? }
45
51
  validates :formula, absence: true, if: -> { category? }
46
52
 
53
+ validates :credit_description, presence: true, if: -> { activity? }
54
+ validates :category_credit_description, presence: true, if: -> { category? }
55
+
47
56
  validate(if: -> { formula.present? }) do
48
57
  if formula.gsub('amount2', '').gsub('amount', '').gsub(' ', '').match(INVALID_FORMULA_CHARS).present?
49
58
  self.errors.add(:formula, "may only contain amount, amount2 and 0-9 + - / * ( ) characters")
@@ -79,7 +88,13 @@ module Effective
79
88
  end
80
89
 
81
90
  def to_s
82
- 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
83
98
  end
84
99
 
85
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
@@ -54,9 +54,9 @@ module Effective
54
54
  self.score ||= 0
55
55
  end
56
56
 
57
- validate(if: -> { completed? && cpd_cycle.required_score.present? }) do
58
- min = cpd_cycle.required_score
59
- self.errors.add(:score, "must be #{min} or greater to submit statement") if score < min
57
+ validate(if: -> { completed? }) do
58
+ min = required_score()
59
+ self.errors.add(:score, "must be #{min} or greater to submit a statement") if score < min
60
60
  end
61
61
 
62
62
  with_options(if: -> { current_step == :agreements }) do
@@ -87,6 +87,13 @@ module Effective
87
87
  submitted_at.present?
88
88
  end
89
89
 
90
+ def required_score
91
+ required_by_cycle = cpd_cycle&.required_score
92
+ required_by_user = user.cpd_statement_required_score(self) if user.respond_to?(:cpd_statement_required_score)
93
+
94
+ [required_by_cycle.to_i, required_by_user.to_i].max
95
+ end
96
+
90
97
  def carry_forward
91
98
  cpd_statement_activities.sum { |activity| activity.carry_forward.to_i }
92
99
  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|
@@ -1,9 +1,17 @@
1
1
  = effective_form_with(model: [:admin, cpd_cycle], engine: true) do |f|
2
+ %h2 All Steps Content
3
+
2
4
  .card.mb-4
3
5
  .card-body
4
6
  %h5.card-title All Steps Content
5
7
  = f.rich_text_area :all_steps_content, label: false, hint: 'displayed on all statement steps'
6
8
 
9
+ .card.mb-4
10
+ .card-body
11
+ %h5.card-title Sidebar Content
12
+ = f.rich_text_area :sidebar_content, label: false, hint: 'displayed on the sidebar on all statement steps'
13
+
14
+ %h2 Individual Steps Content
7
15
  .card.mb-4
8
16
  .card-body
9
17
  %h5.card-title Start Step
@@ -6,12 +6,17 @@
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."
12
-
13
- = fc.text_field :credit_description,
14
- hint: "A simple description of the maximum #{cpd_credits_label} and carry forward policy of activities for this category"
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
+ = fc.select :cpd_special_rule_ids, cpd_cycle.cpd_special_rules, multiple: true,
16
+ label: 'Special Rules', hint: "Special rules operate on more than one category at a time"
17
+
18
+ = fc.rich_text_area :category_credit_description,
19
+ hint: "A description of the maximum #{cpd_credits_label} and carry forward policy of activities for this category"
15
20
 
16
21
  = fc.check_box :unavailable, label: "Unavailable in this #{cpd_cycle_label}"
17
22
 
@@ -49,7 +54,7 @@
49
54
 
50
55
  = fa.number_field :max_cycles_can_carry_forward,
51
56
  label: "Max #{cpd_cycles_label} can carry forward",
52
- hint: "leave blank for no limit"
57
+ hint: "leave blank for no limit. enter zero for no carry forward."
53
58
 
54
59
  = fa.check_box :unavailable, label: "Unavailable in this #{cpd_cycle_label}"
55
60
 
@@ -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)
@@ -12,7 +12,7 @@
12
12
  - cpd_audit_response = resource.cpd_audit_response(cpd_audit_level_question)
13
13
 
14
14
  = f.fields_for :cpd_audit_responses, cpd_audit_response do |fcar|
15
- = render('/effective/cpd_audit_responses/fields', f: fcar, cpd_audit_level_question: cpd_audit_level_question)
15
+ .mt-2= render('/effective/cpd_audit_responses/fields', f: fcar, cpd_audit_level_question: cpd_audit_level_question)
16
16
 
17
17
  - if cpd_audit_level_section.bottom_content.present?
18
18
  .mb-2= cpd_audit_level_section.bottom_content
@@ -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
@@ -22,6 +24,7 @@
22
24
  .mb-3
23
25
  %strong #{cpd_credits_label.titleize} Calculation
24
26
  %br
27
+ = rule.category_credit_description
25
28
  = rule.credit_description
26
29
 
27
30
  %div
@@ -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
@@ -22,10 +22,7 @@
22
22
  %h4= resource.score.to_i
23
23
 
24
24
  %td
25
- - if cpd_cycle.required_score.to_i > 0
26
- #{cpd_credits_label} out of #{cpd_cycle.required_score} required
27
- - else
28
- #{cpd_credits_label} in current #{cpd_cycle_label}
25
+ #{cpd_credits_label} out of #{resource.required_score} required
29
26
 
30
27
  %tr
31
28
  %td
@@ -33,5 +30,7 @@
33
30
 
34
31
  %td #{cpd_credits_label} in last 3 #{cpd_cycles_label} (including this #{cpd_cycle_label})
35
32
 
33
+ = cpd_cycle.sidebar_content
34
+
36
35
  .col-9
37
36
  = yield
@@ -14,7 +14,6 @@
14
14
  = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
15
15
  = f.hidden_field :current_step
16
16
 
17
- = f.check_box :confirm_readonly,
18
- label: 'Yes, I understand that I will not be able to modify this statement after submission'
17
+ = f.check_box :confirm_readonly, label: cpd_statement_submit_label(f.object)
19
18
 
20
19
  = f.submit 'Submit Statement', center: true
@@ -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,9 +7,10 @@ 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
-
13
14
  end
14
15
 
15
16
  # Build the first CpdCycle
@@ -18,11 +19,12 @@ cycle = Effective::CpdCycle.create!(
18
19
  start_at: now.beginning_of_year,
19
20
  end_at: now.end_of_year,
20
21
  required_score: 100,
21
- all_steps_content: "<div>All Steps Content</div>",
22
- start_content: "<div>Start Content</div>",
23
- activities_content: "<div>Activities Content</div>",
24
- submit_content: "<div>Submit Content</div>",
25
- complete_content: "<div>Complete Content</div>"
22
+ all_steps_content: "All Steps Content",
23
+ start_content: "Start Content",
24
+ activities_content: "Activities Content",
25
+ submit_content: "Submit Content",
26
+ complete_content: "Complete Content",
27
+ sidebar_content: "Sidebar Content"
26
28
  )
27
29
 
28
30
  # Professional Practice
@@ -33,7 +35,7 @@ category = Effective::CpdCategory.create!(
33
35
  Effective::CpdRule.create!(
34
36
  cpd_cycle: cycle,
35
37
  ruleable: category,
36
- credit_description: 'Upto a maximum of 20 claimable points per CPD year. Points cannot be carried forward to future years.',
38
+ category_credit_description: 'Upto a maximum of 20 claimable points per CPD year. Points cannot be carried forward to future years.',
37
39
  max_credits_per_cycle: 20
38
40
  )
39
41
 
@@ -74,7 +76,7 @@ category = Effective::CpdCategory.create!(
74
76
  Effective::CpdRule.create!(
75
77
  cpd_cycle: cycle,
76
78
  ruleable: category,
77
- credit_description: 'Upto a maximum of 20 claimable points per CPD year. Points cannot be carried forward to future years.',
79
+ category_credit_description: 'Upto a maximum of 20 claimable points per CPD year. Points cannot be carried forward to future years.',
78
80
  max_credits_per_cycle: 20
79
81
  )
80
82
 
@@ -144,7 +146,7 @@ category = Effective::CpdCategory.create!(
144
146
  Effective::CpdRule.create!(
145
147
  cpd_cycle: cycle,
146
148
  ruleable: category,
147
- credit_description: 'Upto a maximum of 35 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
149
+ category_credit_description: 'Upto a maximum of 35 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
148
150
  max_credits_per_cycle: 35
149
151
  )
150
152
 
@@ -183,7 +185,7 @@ category = Effective::CpdCategory.create!(
183
185
  Effective::CpdRule.create!(
184
186
  cpd_cycle: cycle,
185
187
  ruleable: category,
186
- credit_description: 'Upto a maximum of 15 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
188
+ category_credit_description: 'Upto a maximum of 15 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
187
189
  max_credits_per_cycle: 15
188
190
  )
189
191
 
@@ -265,7 +267,7 @@ category = Effective::CpdCategory.create!(
265
267
  Effective::CpdRule.create!(
266
268
  cpd_cycle: cycle,
267
269
  ruleable: category,
268
- credit_description: 'Upto a maximum of 15 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
270
+ category_credit_description: 'Upto a maximum of 15 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
269
271
  max_credits_per_cycle: 15
270
272
  )
271
273
 
@@ -319,7 +321,7 @@ category = Effective::CpdCategory.create!(
319
321
  Effective::CpdRule.create!(
320
322
  cpd_cycle: cycle,
321
323
  ruleable: category,
322
- credit_description: 'Upto a maximum of 30 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
324
+ category_credit_description: 'Upto a maximum of 30 claimable points per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
323
325
  max_credits_per_cycle: 30
324
326
  )
325
327
 
@@ -421,7 +423,7 @@ category = Effective::CpdCategory.create!(
421
423
  Effective::CpdRule.create!(
422
424
  cpd_cycle: cycle,
423
425
  ruleable: category,
424
- credit_description: 'Upto a maximum of 10 points per mentee or position per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
426
+ category_credit_description: 'Upto a maximum of 10 points per mentee or position per CPD year. Points may be carried over upto a maximum of 2 years after the year in which they were earned.',
425
427
  max_credits_per_cycle: nil
426
428
  )
427
429
 
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,
@@ -7,5 +7,12 @@ module EffectiveCpd
7
7
  eval File.read("#{config.root}/config/effective_cpd.rb")
8
8
  end
9
9
 
10
+ # Include acts_as_addressable concern and allow any ActiveRecord object to call it
11
+ initializer 'effective_cpd.active_record' do |app|
12
+ ActiveSupport.on_load :active_record do
13
+ ActiveRecord::Base.extend(EffectiveCpdUser::Base)
14
+ end
15
+ end
16
+
10
17
  end
11
18
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveCpd
2
- VERSION = '0.1'
2
+ VERSION = '0.1.5'
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'
4
+ version: 0.1.5
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-04-30 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
@@ -212,6 +214,7 @@ files:
212
214
  - app/helpers/effective_cpd_audits_helper.rb
213
215
  - app/helpers/effective_cpd_helper.rb
214
216
  - app/mailers/effective/cpd_mailer.rb
217
+ - app/models/concerns/effective_cpd_user.rb
215
218
  - app/models/effective/cpd_activity.rb
216
219
  - app/models/effective/cpd_audit.rb
217
220
  - app/models/effective/cpd_audit_level.rb
@@ -226,6 +229,8 @@ files:
226
229
  - app/models/effective/cpd_cycle.rb
227
230
  - app/models/effective/cpd_rule.rb
228
231
  - app/models/effective/cpd_scorer.rb
232
+ - app/models/effective/cpd_special_rule.rb
233
+ - app/models/effective/cpd_special_rule_mate.rb
229
234
  - app/models/effective/cpd_statement.rb
230
235
  - app/models/effective/cpd_statement_activity.rb
231
236
  - app/views/admin/cpd_activities/_form.html.haml
@@ -252,6 +257,8 @@ files:
252
257
  - app/views/admin/cpd_cycles/_form_content.html.haml
253
258
  - app/views/admin/cpd_cycles/_form_cpd_cycle.html.haml
254
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
255
262
  - app/views/admin/cpd_statements/_cpd_statement.html.haml
256
263
  - app/views/effective/cpd_audit_level_questions/_cpd_audit_level_question.html.haml
257
264
  - app/views/effective/cpd_audit_responses/_cpd_audit_response.html.haml