effective_cpd 0.0.1 → 0.1
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 +4 -4
- data/README.md +89 -10
- data/app/controllers/admin/cpd_audit_level_questions_controller.rb +13 -0
- data/app/controllers/admin/cpd_audit_levels_controller.rb +13 -0
- data/app/controllers/admin/cpd_audit_reviews_controller.rb +13 -0
- data/app/controllers/admin/cpd_audits_controller.rb +41 -0
- data/app/controllers/effective/cpd_audit_reviews_controller.rb +60 -0
- data/app/controllers/effective/cpd_audits_controller.rb +55 -0
- data/app/controllers/effective/cpd_cycles_controller.rb +1 -0
- data/app/controllers/effective/cpd_statements_controller.rb +1 -3
- data/app/datatables/admin/effective_cpd_audit_level_questions_datatable.rb +30 -0
- data/app/datatables/admin/effective_cpd_audit_levels_datatable.rb +34 -0
- data/app/datatables/admin/effective_cpd_audit_reviews_datatable.rb +29 -0
- data/app/datatables/admin/effective_cpd_audits_datatable.rb +67 -0
- data/app/datatables/admin/effective_cpd_statements_datatable.rb +3 -3
- data/app/datatables/effective_cpd_available_audit_reviews_datatable.rb +37 -0
- data/app/datatables/effective_cpd_available_audits_datatable.rb +30 -0
- data/app/datatables/{effective_cpd_datatable.rb → effective_cpd_available_cycles_datatable.rb} +3 -4
- data/app/datatables/effective_cpd_completed_audit_reviews_datatable.rb +32 -0
- data/app/datatables/effective_cpd_completed_audits_datatable.rb +24 -0
- data/app/datatables/effective_cpd_completed_statements_datatable.rb +28 -0
- data/app/helpers/effective_cpd_audits_helper.rb +48 -0
- data/app/mailers/effective/cpd_mailer.rb +155 -3
- data/app/models/effective/cpd_activity.rb +16 -3
- data/app/models/effective/cpd_audit.rb +380 -0
- data/app/models/effective/cpd_audit_level.rb +87 -0
- data/app/models/effective/cpd_audit_level_question.rb +91 -0
- data/app/models/effective/cpd_audit_level_question_option.rb +34 -0
- data/app/models/effective/cpd_audit_level_section.rb +50 -0
- data/app/models/effective/cpd_audit_response.rb +86 -0
- data/app/models/effective/cpd_audit_response_option.rb +6 -0
- data/app/models/effective/cpd_audit_review.rb +222 -0
- data/app/models/effective/cpd_audit_review_item.rb +34 -0
- data/app/models/effective/cpd_category.rb +12 -3
- data/app/models/effective/cpd_cycle.rb +7 -0
- data/app/models/effective/cpd_rule.rb +3 -1
- data/app/models/effective/cpd_statement.rb +15 -8
- data/app/views/admin/cpd_activities/_form.html.haml +1 -1
- data/app/views/admin/cpd_audit_level_questions/_form.html.haml +100 -0
- data/app/views/admin/cpd_audit_levels/_form.html.haml +24 -0
- data/app/views/admin/cpd_audit_levels/_form_content_audit.html.haml +15 -0
- data/app/views/admin/cpd_audit_levels/_form_content_audit_review.html.haml +15 -0
- data/app/views/admin/cpd_audit_levels/_form_cpd_audit_level.html.haml +52 -0
- data/app/views/admin/cpd_audit_levels/_form_cpd_audit_level_section.html.haml +10 -0
- data/app/views/admin/cpd_audit_reviews/_cpd_audit_review.html.haml +1 -0
- data/app/views/admin/cpd_audit_reviews/_form.html.haml +13 -0
- data/app/views/admin/cpd_audits/_audit_reviewer_fields.html.haml +2 -0
- data/app/views/admin/cpd_audits/_auditee_fields.html.haml +2 -0
- data/app/views/admin/cpd_audits/_form.html.haml +46 -0
- data/app/views/admin/cpd_audits/_form_conflict.html.haml +24 -0
- data/app/views/admin/cpd_audits/_form_determination.html.haml +10 -0
- data/app/views/admin/cpd_audits/_form_exemption.html.haml +24 -0
- data/app/views/admin/cpd_audits/_form_extension.html.haml +24 -0
- data/app/views/admin/cpd_audits/_form_new.html.haml +28 -0
- data/app/views/admin/cpd_audits/_status.html.haml +121 -0
- data/app/views/admin/cpd_categories/_form.html.haml +1 -1
- data/app/views/admin/cpd_cycles/_form.html.haml +4 -4
- data/app/views/admin/cpd_cycles/_form_cpd_cycle.html.haml +3 -0
- data/app/views/admin/cpd_statements/_cpd_statement.html.haml +7 -0
- data/app/views/effective/cpd_audit_level_questions/_cpd_audit_level_question.html.haml +5 -0
- data/app/views/effective/cpd_audit_responses/_cpd_audit_response.html.haml +4 -0
- data/app/views/effective/cpd_audit_responses/_fields.html.haml +13 -0
- data/app/views/effective/cpd_audit_responses/fields/_choose_one.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_date.html.haml +2 -0
- data/app/views/effective/cpd_audit_responses/fields/_email.html.haml +2 -0
- data/app/views/effective/cpd_audit_responses/fields/_long_answer.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_number.html.haml +2 -0
- data/app/views/effective/cpd_audit_responses/fields/_select_all_that_apply.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_select_up_to_1.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_select_up_to_2.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_select_up_to_3.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_select_up_to_4.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_select_up_to_5.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_short_answer.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/fields/_upload_file.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_choose_one.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_date.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_email.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_long_answer.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_number.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_select_all_that_apply.html.haml +5 -0
- data/app/views/effective/cpd_audit_responses/responses/_select_up_to_1.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_select_up_to_2.html.haml +5 -0
- data/app/views/effective/cpd_audit_responses/responses/_select_up_to_3.html.haml +5 -0
- data/app/views/effective/cpd_audit_responses/responses/_select_up_to_4.html.haml +5 -0
- data/app/views/effective/cpd_audit_responses/responses/_select_up_to_5.html.haml +5 -0
- data/app/views/effective/cpd_audit_responses/responses/_short_answer.html.haml +1 -0
- data/app/views/effective/cpd_audit_responses/responses/_upload_file.html.haml +4 -0
- data/app/views/effective/cpd_audit_review_items/_cpd_audit_review_item.html.haml +6 -0
- data/app/views/effective/cpd_audit_review_items/_fields.html.haml +11 -0
- data/app/views/effective/cpd_audit_reviews/_conflict.html.haml +15 -0
- data/app/views/effective/cpd_audit_reviews/_cpd_audit_level_section.html.haml +23 -0
- data/app/views/effective/cpd_audit_reviews/_cpd_audit_review.html.haml +17 -0
- data/app/views/effective/cpd_audit_reviews/_cpd_statement.html.haml +15 -0
- data/app/views/effective/cpd_audit_reviews/_layout.html.haml +24 -0
- data/app/views/effective/cpd_audit_reviews/_recommendation.html.haml +9 -0
- data/app/views/effective/cpd_audit_reviews/_summary.html.haml +47 -0
- data/app/views/effective/cpd_audit_reviews/complete.html.haml +20 -0
- data/app/views/effective/cpd_audit_reviews/conflict.html.haml +21 -0
- data/app/views/effective/cpd_audit_reviews/cpd_audit_level_section.html.haml +43 -0
- data/app/views/effective/cpd_audit_reviews/cpd_statement.html.haml +35 -0
- data/app/views/effective/cpd_audit_reviews/information.html.haml +5 -0
- data/app/views/effective/cpd_audit_reviews/instructions.html.haml +10 -0
- data/app/views/effective/cpd_audit_reviews/questionnaire.html.haml +8 -0
- data/app/views/effective/cpd_audit_reviews/recommendation.html.haml +21 -0
- data/app/views/effective/cpd_audit_reviews/start.html.haml +10 -0
- data/app/views/effective/cpd_audit_reviews/statements.html.haml +29 -0
- data/app/views/effective/cpd_audit_reviews/submit.html.haml +14 -0
- data/app/views/effective/cpd_audit_reviews/waiting.html.haml +9 -0
- data/app/views/effective/cpd_audits/_conflict.html.haml +15 -0
- data/app/views/effective/cpd_audits/_cpd_audit.html.haml +12 -0
- data/app/views/effective/cpd_audits/_cpd_audit_level_section.html.haml +14 -0
- data/app/views/effective/cpd_audits/_exemption.html.haml +26 -0
- data/app/views/effective/cpd_audits/_extension.html.haml +30 -0
- data/app/views/effective/cpd_audits/_files.html.haml +12 -0
- data/app/views/effective/cpd_audits/_layout.html.haml +33 -0
- data/app/views/effective/cpd_audits/_summary.html.haml +54 -0
- data/app/views/effective/cpd_audits/_waiting.html.haml +6 -0
- data/app/views/effective/cpd_audits/complete.html.haml +24 -0
- data/app/views/effective/cpd_audits/conflict.html.haml +22 -0
- data/app/views/effective/cpd_audits/cpd_audit_level_section.html.haml +20 -0
- data/app/views/effective/cpd_audits/exemption.html.haml +19 -0
- data/app/views/effective/cpd_audits/extension.html.haml +21 -0
- data/app/views/effective/cpd_audits/files.html.haml +7 -0
- data/app/views/effective/cpd_audits/information.html.haml +5 -0
- data/app/views/effective/cpd_audits/instructions.html.haml +5 -0
- data/app/views/effective/cpd_audits/questionnaire.html.haml +5 -0
- data/app/views/effective/cpd_audits/start.html.haml +11 -0
- data/app/views/effective/cpd_audits/submit.html.haml +15 -0
- data/app/views/effective/cpd_audits/waiting.html.haml +24 -0
- data/app/views/effective/cpd_mailer/README.md +1 -0
- data/app/views/effective/cpd_mailer/cpd_audit_closed.liquid +15 -0
- data/app/views/effective/cpd_mailer/cpd_audit_conflict_resolved.liquid +15 -0
- data/app/views/effective/cpd_mailer/cpd_audit_conflicted.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_exemption_denied.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_exemption_granted.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_exemption_request.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_extension_denied.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_extension_granted.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_extension_request.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_opened.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_review_opened.liquid +15 -0
- data/app/views/effective/cpd_mailer/cpd_audit_review_ready.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_review_submitted.liquid +11 -0
- data/app/views/effective/cpd_mailer/cpd_audit_reviewed.liquid +13 -0
- data/app/views/effective/cpd_mailer/cpd_audit_submitted.liquid +13 -0
- data/app/views/effective/cpd_statement_activities/_cpd_statement_activity.html.haml +65 -0
- data/app/views/effective/cpd_statement_activities/_form.html.haml +9 -10
- data/app/views/effective/cpd_statements/_activities.html.haml +3 -64
- data/app/views/effective/cpd_statements/_activities_edit.html.haml +2 -0
- data/app/views/effective/cpd_statements/_activities_table.html.haml +64 -0
- data/app/views/effective/cpd_statements/_agreements.html.haml +19 -5
- data/app/views/effective/cpd_statements/_cpd_statement.html.haml +6 -3
- data/app/views/effective/cpd_statements/_summary.html.haml +26 -31
- data/app/views/effective/cpd_statements/activities.html.haml +1 -1
- data/app/views/effective/cpd_statements/agreements.html.haml +7 -1
- data/app/views/effective/cpd_statements/submit.html.haml +1 -1
- data/config/effective_cpd.rb +47 -10
- data/config/routes.rb +18 -1
- data/db/migrate/01_create_effective_cpd.rb.erb +157 -1
- data/db/seeds.rb +2 -1
- data/lib/effective_cpd.rb +42 -3
- data/lib/effective_cpd/version.rb +1 -1
- data/lib/generators/effective_cpd/install_generator.rb +16 -3
- metadata +168 -9
- data/app/datatables/effective_cpd_statements_datatable.rb +0 -23
- data/app/views/admin/cpd_statements/_form.html.haml +0 -6
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class CpdAuditLevelQuestion < ActiveRecord::Base
|
|
3
|
+
belongs_to :cpd_audit_level
|
|
4
|
+
belongs_to :cpd_audit_level_section
|
|
5
|
+
|
|
6
|
+
has_many :cpd_audit_level_question_options, -> { CpdAuditLevelQuestionOption.sorted }, inverse_of: :cpd_audit_level_question, dependent: :delete_all
|
|
7
|
+
accepts_nested_attributes_for :cpd_audit_level_question_options, reject_if: :all_blank, allow_destroy: true
|
|
8
|
+
|
|
9
|
+
has_many :cpd_audit_responses
|
|
10
|
+
|
|
11
|
+
has_one_attached :upload_file
|
|
12
|
+
has_rich_text :body
|
|
13
|
+
|
|
14
|
+
if respond_to?(:log_changes)
|
|
15
|
+
log_changes(to: :cpd_audit_level, except: [:cpd_audit_responses])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
CATEGORIES = [
|
|
19
|
+
'Choose one', # Radios
|
|
20
|
+
'Select all that apply', # Checks
|
|
21
|
+
'Select up to 1',
|
|
22
|
+
'Select up to 2',
|
|
23
|
+
'Select up to 3',
|
|
24
|
+
'Select up to 4',
|
|
25
|
+
'Select up to 5',
|
|
26
|
+
'Date', # Date Field
|
|
27
|
+
'Email', # Email Field
|
|
28
|
+
'Number', # Numeric Field
|
|
29
|
+
'Long Answer', # Text Area
|
|
30
|
+
'Short Answer', # Text Field
|
|
31
|
+
'Upload File' # File field
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
WITH_OPTIONS_CATEGORIES = [
|
|
35
|
+
'Choose one', # Radios
|
|
36
|
+
'Select all that apply', # Checks
|
|
37
|
+
'Select up to 1',
|
|
38
|
+
'Select up to 2',
|
|
39
|
+
'Select up to 3',
|
|
40
|
+
'Select up to 4',
|
|
41
|
+
'Select up to 5'
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
effective_resource do
|
|
45
|
+
title :text
|
|
46
|
+
category :string
|
|
47
|
+
required :boolean
|
|
48
|
+
|
|
49
|
+
position :integer
|
|
50
|
+
|
|
51
|
+
timestamps
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
scope :deep, -> { with_rich_text_body.includes(:cpd_audit_level_section, :cpd_audit_level_question_options) }
|
|
55
|
+
scope :sorted, -> { order(:position) }
|
|
56
|
+
|
|
57
|
+
before_validation(if: -> { cpd_audit_level_section.present? }) do
|
|
58
|
+
self.cpd_audit_level = cpd_audit_level_section.cpd_audit_level
|
|
59
|
+
self.position ||= (cpd_audit_level_section.cpd_audit_level_questions.map(&:position).compact.max || -1) + 1
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
validates :title, presence: true
|
|
63
|
+
validates :category, presence: true, inclusion: { in: CATEGORIES }
|
|
64
|
+
validates :position, presence: true
|
|
65
|
+
validates :cpd_audit_level_question_options, presence: true, if: -> { question_option? }
|
|
66
|
+
|
|
67
|
+
before_destroy do
|
|
68
|
+
if (count = cpd_audit_responses.length) > 0
|
|
69
|
+
raise("#{count} audit responses belong to this question")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Create choose_one? and select_all_that_apply? methods for each category
|
|
74
|
+
CATEGORIES.each do |category|
|
|
75
|
+
define_method(category.parameterize.underscore + '?') { self.category == category }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def question_option?
|
|
79
|
+
WITH_OPTIONS_CATEGORIES.include?(category)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def category_partial
|
|
83
|
+
category.to_s.parameterize.underscore
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_s
|
|
87
|
+
title.presence || 'audit question'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class CpdAuditLevelQuestionOption < ActiveRecord::Base
|
|
3
|
+
belongs_to :cpd_audit_level_question
|
|
4
|
+
|
|
5
|
+
has_many :cpd_audit_response_options
|
|
6
|
+
|
|
7
|
+
effective_resource do
|
|
8
|
+
title :text
|
|
9
|
+
position :integer
|
|
10
|
+
|
|
11
|
+
timestamps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
before_validation(if: -> { cpd_audit_level_question.present? }) do
|
|
15
|
+
self.position ||= (cpd_audit_level_question.cpd_audit_level_question_options.map { |obj| obj.position }.compact.max || -1) + 1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
scope :sorted, -> { order(:position) }
|
|
19
|
+
|
|
20
|
+
validates :title, presence: true
|
|
21
|
+
validates :position, presence: true
|
|
22
|
+
|
|
23
|
+
before_destroy do
|
|
24
|
+
if (count = cpd_audit_response_options.length) > 0
|
|
25
|
+
raise("#{count} audit response options belong to this question option")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
title.presence || 'New Audit Question Option'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class CpdAuditLevelSection < ActiveRecord::Base
|
|
3
|
+
belongs_to :cpd_audit_level
|
|
4
|
+
|
|
5
|
+
has_rich_text :top_content
|
|
6
|
+
has_rich_text :bottom_content
|
|
7
|
+
|
|
8
|
+
has_many :cpd_audit_level_questions, -> { CpdAuditLevelQuestion.sorted }, inverse_of: :cpd_audit_level_section, dependent: :destroy
|
|
9
|
+
accepts_nested_attributes_for :cpd_audit_level_questions, allow_destroy: true
|
|
10
|
+
|
|
11
|
+
has_many :cpd_audit_responses
|
|
12
|
+
|
|
13
|
+
if respond_to?(:log_changes)
|
|
14
|
+
log_changes(to: :cpd_audit_level, except: [:cpd_audit_responses])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
effective_resource do
|
|
18
|
+
title :string
|
|
19
|
+
position :integer
|
|
20
|
+
|
|
21
|
+
timestamps
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
scope :deep, -> {
|
|
25
|
+
with_rich_text_top_content
|
|
26
|
+
.with_rich_text_bottom_content
|
|
27
|
+
.includes(cpd_audit_level_questions: [:rich_text_body])
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
scope :sorted, -> { order(:position) }
|
|
31
|
+
|
|
32
|
+
before_validation(if: -> { cpd_audit_level.present? }) do
|
|
33
|
+
self.position ||= (cpd_audit_level.cpd_audit_level_sections.map(&:position).compact.max || -1) + 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
validates :title, presence: true
|
|
37
|
+
validates :position, presence: true
|
|
38
|
+
|
|
39
|
+
before_destroy do
|
|
40
|
+
if (count = cpd_audit_responses.length) > 0
|
|
41
|
+
raise("#{count} audit responses belong to this section")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_s
|
|
46
|
+
title.presence || 'audit section'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class CpdAuditResponse < ActiveRecord::Base
|
|
3
|
+
belongs_to :cpd_audit
|
|
4
|
+
belongs_to :cpd_audit_level_section
|
|
5
|
+
belongs_to :cpd_audit_level_question
|
|
6
|
+
|
|
7
|
+
has_many :cpd_audit_response_options, dependent: :delete_all
|
|
8
|
+
has_many :cpd_audit_level_question_options, through: :cpd_audit_response_options
|
|
9
|
+
|
|
10
|
+
has_one_attached :upload_file
|
|
11
|
+
|
|
12
|
+
if respond_to?(:log_changes)
|
|
13
|
+
log_changes(to: :cpd_audit)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
effective_resource do
|
|
17
|
+
# The response
|
|
18
|
+
date :date
|
|
19
|
+
email :string
|
|
20
|
+
number :integer
|
|
21
|
+
long_answer :text
|
|
22
|
+
short_answer :text
|
|
23
|
+
|
|
24
|
+
timestamps
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
scope :submitted, -> { where(cpd_audit: Effective::CpdAudit.where.not(submitted_at: nil)) }
|
|
28
|
+
scope :deep, -> { includes(:cpd_audit, :cpd_audit_level_question) }
|
|
29
|
+
|
|
30
|
+
before_validation(if: -> { cpd_audit_level_question.present? }) do
|
|
31
|
+
self.cpd_audit_level_section = cpd_audit_level_question.cpd_audit_level_section
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
validates :date, presence: true, if: -> { cpd_audit_level_question&.required? && cpd_audit_level_question.date? }
|
|
35
|
+
validates :email, presence: true, email: true, if: -> { cpd_audit_level_question&.required? && cpd_audit_level_question.email? }
|
|
36
|
+
validates :number, presence: true, if: -> { cpd_audit_level_question&.required? && cpd_audit_level_question.number? }
|
|
37
|
+
validates :long_answer, presence: true, if: -> { cpd_audit_level_question&.required? && cpd_audit_level_question.long_answer? }
|
|
38
|
+
validates :short_answer, presence: true, if: -> { cpd_audit_level_question&.required? && cpd_audit_level_question.short_answer? }
|
|
39
|
+
validates :upload_file, presence: true, if: -> { cpd_audit_level_question&.required? && cpd_audit_level_question.upload_file? }
|
|
40
|
+
validates :cpd_audit_level_question_option_ids, presence: true, if: -> { cpd_audit_level_question&.required? && cpd_audit_level_question.question_option? }
|
|
41
|
+
|
|
42
|
+
validates :cpd_audit_level_question_option_ids, if: -> { cpd_audit_level_question&.choose_one? },
|
|
43
|
+
length: { maximum: 1, message: 'please choose 1 option only' }
|
|
44
|
+
|
|
45
|
+
validates :cpd_audit_level_question_option_ids, if: -> { cpd_audit_level_question&.select_up_to_1? },
|
|
46
|
+
length: { maximum: 1, message: 'please select 1 option or fewer' }
|
|
47
|
+
|
|
48
|
+
validates :cpd_audit_level_question_option_ids, if: -> { cpd_audit_level_question&.select_up_to_2? },
|
|
49
|
+
length: { maximum: 2, message: 'please select 2 options or fewer' }
|
|
50
|
+
|
|
51
|
+
validates :cpd_audit_level_question_option_ids, if: -> { cpd_audit_level_question&.select_up_to_3? },
|
|
52
|
+
length: { maximum: 3, message: 'please select 3 options or fewer' }
|
|
53
|
+
|
|
54
|
+
validates :cpd_audit_level_question_option_ids, if: -> { cpd_audit_level_question&.select_up_to_4? },
|
|
55
|
+
length: { maximum: 4, message: 'please select 4 options or fewer' }
|
|
56
|
+
|
|
57
|
+
validates :cpd_audit_level_question_option_ids, if: -> { cpd_audit_level_question&.select_up_to_5? },
|
|
58
|
+
length: { maximum: 5, message: 'please select 5 options or fewer' }
|
|
59
|
+
|
|
60
|
+
def to_s
|
|
61
|
+
'audit response'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def response
|
|
65
|
+
return nil unless cpd_audit_level_question.present?
|
|
66
|
+
|
|
67
|
+
return date if cpd_audit_level_question.date?
|
|
68
|
+
return email if cpd_audit_level_question.email?
|
|
69
|
+
return number if cpd_audit_level_question.number?
|
|
70
|
+
return long_answer if cpd_audit_level_question.long_answer?
|
|
71
|
+
return short_answer if cpd_audit_level_question.short_answer?
|
|
72
|
+
return upload_file if cpd_audit_level_question.upload_file?
|
|
73
|
+
|
|
74
|
+
return cpd_audit_level_question_options.first if cpd_audit_level_question.choose_one?
|
|
75
|
+
return cpd_audit_level_question_options.first if cpd_audit_level_question.select_up_to_1?
|
|
76
|
+
return cpd_audit_level_question_options if cpd_audit_level_question.question_option?
|
|
77
|
+
|
|
78
|
+
raise('unknown response for unexpected cpd audit question category')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def category_partial
|
|
82
|
+
cpd_audit_level_question&.category_partial
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class CpdAuditReview < ActiveRecord::Base
|
|
3
|
+
attr_accessor :current_user
|
|
4
|
+
attr_accessor :current_step
|
|
5
|
+
|
|
6
|
+
belongs_to :cpd_audit
|
|
7
|
+
belongs_to :cpd_audit_level
|
|
8
|
+
belongs_to :user, polymorphic: true # Auditor
|
|
9
|
+
|
|
10
|
+
has_many :cpd_audit_review_items, -> { CpdAuditReviewItem.sorted }, inverse_of: :cpd_audit_review
|
|
11
|
+
accepts_nested_attributes_for :cpd_audit_review_items, reject_if: :all_blank, allow_destroy: true
|
|
12
|
+
|
|
13
|
+
if respond_to?(:log_changes)
|
|
14
|
+
log_changes(to: :cpd_audit, except: :wizard_steps)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
acts_as_email_form
|
|
18
|
+
acts_as_tokened
|
|
19
|
+
|
|
20
|
+
acts_as_wizard(
|
|
21
|
+
start: 'Start',
|
|
22
|
+
information: 'Information',
|
|
23
|
+
instructions: 'Instructions',
|
|
24
|
+
|
|
25
|
+
# Optional based on cpd_audit_level options
|
|
26
|
+
conflict: 'Conflict of Interest',
|
|
27
|
+
|
|
28
|
+
waiting: 'Waiting on Auditee Submission',
|
|
29
|
+
|
|
30
|
+
statements: 'Review CPD Statements',
|
|
31
|
+
# ... There will be one step per cpd_statement here. "statement1"
|
|
32
|
+
|
|
33
|
+
questionnaire: 'Review Questionnaire Responses',
|
|
34
|
+
# ... There will be one step per cpd_audit_level_sections here
|
|
35
|
+
|
|
36
|
+
recommendation: 'Recommendation',
|
|
37
|
+
submit: 'Confirm & Submit',
|
|
38
|
+
complete: 'Complete'
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
effective_resource do
|
|
42
|
+
token :string
|
|
43
|
+
due_date :date
|
|
44
|
+
|
|
45
|
+
# Auditor response
|
|
46
|
+
conflict_of_interest :boolean
|
|
47
|
+
conflict_of_interest_reason :text
|
|
48
|
+
|
|
49
|
+
# Rolling comments
|
|
50
|
+
comments :text
|
|
51
|
+
|
|
52
|
+
# Recommendation Step
|
|
53
|
+
recommendation :string
|
|
54
|
+
|
|
55
|
+
# Status Dates
|
|
56
|
+
submitted_at :datetime
|
|
57
|
+
|
|
58
|
+
# acts_as_statused
|
|
59
|
+
# I'm not using acts_as_statused yet, but I probably will later
|
|
60
|
+
status :string
|
|
61
|
+
status_steps :text
|
|
62
|
+
|
|
63
|
+
# Wizard Progress
|
|
64
|
+
wizard_steps :text
|
|
65
|
+
|
|
66
|
+
timestamps
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
scope :deep, -> { includes(:cpd_audit, :cpd_audit_level, :user) }
|
|
70
|
+
scope :sorted, -> { order(:id) }
|
|
71
|
+
|
|
72
|
+
scope :available, -> { where(submitted_at: nil) }
|
|
73
|
+
scope :completed, -> { where.not(submitted_at: nil) }
|
|
74
|
+
|
|
75
|
+
before_validation(if: -> { new_record? }) do
|
|
76
|
+
self.cpd_audit_level ||= cpd_audit&.cpd_audit_level
|
|
77
|
+
self.due_date ||= deadline_to_review()
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
validate(if: -> { recommendation.present? }) do
|
|
81
|
+
unless cpd_audit_level.recommendations.include?(recommendation)
|
|
82
|
+
self.errors.add(:recommendation, 'must exist in this audit level')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
after_commit(on: :create) { send_email(:cpd_audit_review_opened) }
|
|
87
|
+
after_commit(on: :destroy) { cpd_audit.review! }
|
|
88
|
+
|
|
89
|
+
def to_s
|
|
90
|
+
'audit review'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def to_s
|
|
94
|
+
persisted? ? "#{cpd_audit_level} Audit Review by #{user}" : 'audit review'
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Find or build
|
|
98
|
+
def cpd_audit_review_item(item)
|
|
99
|
+
unless item.kind_of?(CpdAuditResponse) || item.kind_of?(CpdStatementActivity)
|
|
100
|
+
raise("expected a cpd_audit_response or cpd_statement_activity")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
cpd_audit_review_item = cpd_audit_review_items.find { |cari| cari.item == item }
|
|
104
|
+
cpd_audit_review_item ||= cpd_audit_review_items.build(item: item)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# The dynamic CPD Statement review steps
|
|
108
|
+
def auditee_cpd_statements
|
|
109
|
+
cpd_audit.user.cpd_statements.select do |cpd_statement|
|
|
110
|
+
cpd_statement.completed? && (submitted_at.blank? || cpd_statement.submitted_at < submitted_at)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def cpd_statement(wizard_step)
|
|
115
|
+
cpd_cycle_id = (wizard_step.to_s.split('statement').last.to_i rescue false)
|
|
116
|
+
auditee_cpd_statements.find { |cpd_statement| cpd_statement.cpd_cycle_id == cpd_cycle_id }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def dynamic_wizard_statement_steps
|
|
120
|
+
@statement_steps ||= auditee_cpd_statements.each_with_object({}) do |cpd_statement, h|
|
|
121
|
+
h["statement#{cpd_statement.cpd_cycle_id}".to_sym] = "#{cpd_statement.cpd_cycle.to_s} Activities"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The dynamic CPD Audit Level Sections steps
|
|
126
|
+
def cpd_audit_level_section(wizard_step)
|
|
127
|
+
position = (wizard_step.to_s.split('section').last.to_i rescue false)
|
|
128
|
+
cpd_audit_level.cpd_audit_level_sections.find { |section| (section.position + 1) == position }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def dynamic_wizard_questionnaire_steps
|
|
132
|
+
@questionnaire_steps ||= cpd_audit_level.cpd_audit_level_sections.each_with_object({}) do |section, h|
|
|
133
|
+
h["section#{section.position+1}".to_sym] = section.title
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def dynamic_wizard_steps
|
|
138
|
+
dynamic_wizard_statement_steps.merge(dynamic_wizard_questionnaire_steps)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def can_visit_step?(step)
|
|
142
|
+
return (step == :complete) if completed? # Can only view complete step once submitted
|
|
143
|
+
can_revisit_completed_steps(step)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def required_steps
|
|
147
|
+
steps = [:start, :information, :instructions]
|
|
148
|
+
|
|
149
|
+
steps << :conflict if cpd_audit_level.conflict_of_interest?
|
|
150
|
+
|
|
151
|
+
if conflict_of_interest?
|
|
152
|
+
return steps + [:submit, :complete]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
steps += [:waiting] unless ready?
|
|
156
|
+
|
|
157
|
+
steps += [:statements] + dynamic_wizard_statement_steps.keys
|
|
158
|
+
steps += [:questionnaire] + dynamic_wizard_questionnaire_steps.keys
|
|
159
|
+
steps += [:recommendation, :submit, :complete]
|
|
160
|
+
|
|
161
|
+
steps
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def wizard_step_title(step)
|
|
165
|
+
WIZARD_STEPS[step] || dynamic_wizard_steps.fetch(step)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Called by CpdAudit.extension_granted
|
|
169
|
+
def extension_granted!
|
|
170
|
+
self.due_date = deadline_to_review()
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Called by CpdAudit.submit!
|
|
174
|
+
def ready!
|
|
175
|
+
send_email(:cpd_audit_review_ready)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Called by review wizard submit step
|
|
179
|
+
def submit!
|
|
180
|
+
update!(submitted_at: Time.zone.now)
|
|
181
|
+
cpd_audit.review! # maybe go from submitted->removed
|
|
182
|
+
|
|
183
|
+
send_email(:cpd_audit_review_submitted)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# When ready, the applicant review wizard hides the waiting step
|
|
187
|
+
def ready?
|
|
188
|
+
cpd_audit&.was_submitted?
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def in_progress?
|
|
192
|
+
submitted_at.blank?
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def completed?
|
|
196
|
+
submitted_at.present?
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def email_form_defaults(action)
|
|
200
|
+
{ from: EffectiveCpd.mailer_sender }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def send_email(email)
|
|
204
|
+
EffectiveCpd.send_email(email, self, email_form_params) unless email_form_skip?
|
|
205
|
+
true
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def deadline_to_conflict_of_interest
|
|
209
|
+
cpd_audit.deadline_to_conflict_of_interest
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def deadline_to_review
|
|
213
|
+
return nil unless cpd_audit_level&.days_to_review.present?
|
|
214
|
+
|
|
215
|
+
date = cpd_audit.deadline_to_submit
|
|
216
|
+
return nil unless date.present?
|
|
217
|
+
|
|
218
|
+
EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_review)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
end
|
|
222
|
+
end
|