biovision-poll 0.1.180917.0 → 0.2.200324.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/admin/poll_answers_controller.rb +6 -5
  3. data/app/controllers/admin/poll_questions_controller.rb +6 -5
  4. data/app/controllers/admin/polls_controller.rb +7 -6
  5. data/app/controllers/poll_answers_controller.rb +15 -9
  6. data/app/controllers/poll_questions_controller.rb +15 -9
  7. data/app/controllers/polls_controller.rb +25 -16
  8. data/app/helpers/polls_helper.rb +19 -29
  9. data/app/models/poll.rb +50 -18
  10. data/app/models/poll_answer.rb +29 -36
  11. data/app/models/poll_question.rb +33 -39
  12. data/app/models/poll_user.rb +10 -1
  13. data/app/models/poll_vote.rb +20 -5
  14. data/app/services/biovision/components/polls_component.rb +21 -0
  15. data/app/views/admin/components/links/_polls.html.erb +1 -0
  16. data/app/views/admin/poll_answers/entity/_data.html.erb +0 -0
  17. data/app/views/admin/poll_answers/entity/_in_list.html.erb +0 -3
  18. data/app/views/admin/poll_answers/show.html.erb +25 -35
  19. data/app/views/admin/poll_questions/entity/_data.html.erb +0 -0
  20. data/app/views/admin/poll_questions/entity/_in_list.html.erb +6 -5
  21. data/app/views/admin/poll_questions/show.html.erb +29 -37
  22. data/app/views/admin/poll_votes/entity/_data.html.erb +0 -0
  23. data/app/views/admin/poll_votes/entity/_in_list.html.erb +6 -5
  24. data/app/views/admin/polls/entity/_data.html.erb +0 -0
  25. data/app/views/admin/polls/entity/_in_list.html.erb +1 -3
  26. data/app/views/admin/polls/index.html.erb +8 -2
  27. data/app/views/admin/polls/show.html.erb +32 -30
  28. data/app/views/admin/polls/users.html.erb +2 -1
  29. data/app/views/poll_answers/_form.html.erb +36 -30
  30. data/app/views/poll_answers/_poll_answer.html.erb +22 -2
  31. data/app/views/poll_answers/edit.html.erb +6 -5
  32. data/app/views/poll_answers/new.html.erb +8 -7
  33. data/app/views/poll_questions/_form.html.erb +53 -43
  34. data/app/views/poll_questions/edit.html.erb +6 -5
  35. data/app/views/poll_questions/new.html.erb +7 -6
  36. data/app/views/polls/_form.html.erb +64 -41
  37. data/app/views/polls/_poll.html.erb +11 -6
  38. data/app/views/polls/edit.html.erb +5 -4
  39. data/app/views/polls/new.html.erb +3 -2
  40. data/app/views/polls/results.html.erb +22 -0
  41. data/config/locales/polls-ru.yml +28 -26
  42. data/config/routes.rb +19 -15
  43. data/db/migrate/20200323000001_create_biovision_polls.rb +121 -0
  44. data/lib/biovision/poll/version.rb +3 -1
  45. metadata +10 -12
  46. data/app/uploaders/poll_image_uploader.rb +0 -46
  47. data/app/views/admin/index/dashboard/_biovision_poll.html.erb +0 -10
  48. data/db/migrate/20170906000001_create_polls.rb +0 -70
  49. data/db/migrate/20170906000002_create_poll_questions.rb +0 -22
  50. data/db/migrate/20170906000003_create_poll_answers.rb +0 -20
  51. data/db/migrate/20170906000004_create_poll_votes.rb +0 -20
  52. data/db/migrate/20180412000010_create_poll_users.rb +0 -21
  53. data/db/migrate/20180903111111_rename_vote_footprint.rb +0 -11
@@ -1,30 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Answer to poll question
4
+ #
5
+ # Attributes:
6
+ # created_at [DateTime]
7
+ # data [jsonb]
8
+ # poll_question_id [PollQuestion]
9
+ # poll_votes_count [integer]
10
+ # priority [integer]
11
+ # simple_image_id [SimpleImage], optional
12
+ # text [string]
13
+ # updated_at [DateTime]
14
+ # uuid [uuid]
1
15
  class PollAnswer < ApplicationRecord
2
- TEXT_LIMIT = 140
3
- PRIORITY_RANGE = (1..50)
16
+ include Checkable
17
+ include HasSimpleImage
18
+ include HasUuid
19
+ include NestedPriority
4
20
 
5
- mount_uploader :image, PollImageUploader
21
+ TEXT_LIMIT = 140
6
22
 
7
23
  belongs_to :poll_question, counter_cache: true
8
24
  has_many :poll_votes, dependent: :delete_all
9
25
 
10
- after_initialize :set_next_priority
11
-
12
26
  before_validation { self.text = text.to_s.strip }
13
- before_validation :normalize_priority
14
27
 
15
28
  validates_presence_of :text
16
29
  validates_length_of :text, maximum: TEXT_LIMIT
17
- validates_uniqueness_of :text, scope: [:poll_question_id]
30
+ validates_uniqueness_of :text, scope: :poll_question_id
18
31
 
19
- scope :ordered_by_priority, -> { order('priority asc, text asc') }
20
- scope :siblings, ->(item) { where(poll_question_id: item.poll_question_id) }
32
+ scope :list_for_visitors, -> { ordered_by_priority }
33
+ scope :list_for_administration, -> { ordered_by_priority }
34
+
35
+ # @param [PollAnswer] entity
36
+ def self.siblings(entity)
37
+ where(poll_question_id: entity&.poll_question_id)
38
+ end
21
39
 
22
40
  def self.entity_parameters
23
- %i(image text)
41
+ %i[text simple_image_id]
24
42
  end
25
43
 
26
44
  def self.creation_parameters
27
- entity_parameters + %i(poll_question_id)
45
+ entity_parameters + %i[poll_question_id]
28
46
  end
29
47
 
30
48
  # @param [User] user
@@ -39,29 +57,4 @@ class PollAnswer < ApplicationRecord
39
57
  def vote_percent
40
58
  poll_votes_count.to_f / poll_question.vote_count * 100
41
59
  end
42
-
43
- # @param [Integer] delta
44
- def change_priority(delta)
45
- new_priority = priority + delta
46
- adjacent = self.class.siblings(self).find_by(priority: new_priority)
47
- if adjacent.is_a?(self.class) && (adjacent.id != id)
48
- adjacent.update!(priority: priority)
49
- end
50
- update(priority: new_priority)
51
-
52
- self.class.siblings(self).map { |e| [e.id, e.priority] }.to_h
53
- end
54
-
55
- private
56
-
57
- def set_next_priority
58
- if id.nil? && priority == 1
59
- self.priority = self.class.siblings(self).maximum(:priority).to_i + 1
60
- end
61
- end
62
-
63
- def normalize_priority
64
- self.priority = PRIORITY_RANGE.first if priority < PRIORITY_RANGE.first
65
- self.priority = PRIORITY_RANGE.last if priority > PRIORITY_RANGE.last
66
- end
67
60
  end
@@ -1,36 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Poll question
4
+ #
5
+ # Attributes:
6
+ # comment [string], optional
7
+ # created_at [DateTime]
8
+ # data [jsonb]
9
+ # multiple_choice [boolean]
10
+ # poll_answers_count [integer]
11
+ # poll_id [Poll]
12
+ # priority [integer]
13
+ # simple_image_id [SimpleImage], optional
14
+ # text [string]
15
+ # updated_at [DateTime]
16
+ # uuid [uuid]
1
17
  class PollQuestion < ApplicationRecord
18
+ include Checkable
19
+ include HasSimpleImage
20
+ include HasUuid
21
+ include NestedPriority
2
22
  include Toggleable
3
23
 
4
- TEXT_LIMIT = 140
5
- COMMENT_LIMIT = 140
6
- PRIORITY_RANGE = (1..100)
24
+ COMMENT_LIMIT = 255
25
+ TEXT_LIMIT = 255
7
26
 
8
27
  toggleable :multiple_choice
9
28
 
10
- mount_uploader :image, PollImageUploader
11
-
12
29
  belongs_to :poll, counter_cache: true
13
30
  has_many :poll_answers, dependent: :delete_all
14
31
 
15
- after_initialize :set_next_priority
16
-
17
32
  before_validation { self.text = text.to_s.strip }
18
- before_validation :normalize_priority
19
33
 
20
34
  validates_presence_of :text
21
35
  validates_length_of :text, maximum: TEXT_LIMIT
22
36
  validates_length_of :comment, maximum: COMMENT_LIMIT
23
- validates_uniqueness_of :text, scope: [:poll_id]
37
+ validates_uniqueness_of :text, scope: :poll_id
38
+
39
+ scope :list_for_visitors, -> { ordered_by_priority }
40
+ scope :list_for_administration, -> { ordered_by_priority }
24
41
 
25
- scope :ordered_by_priority, -> { order('priority asc, text asc') }
26
- scope :siblings, ->(item) { where(poll_id: item.poll_id) }
42
+ # @param [PollQuestion] entity
43
+ def self.siblings(entity)
44
+ where(poll_id: entity&.poll_id)
45
+ end
27
46
 
28
47
  def self.entity_parameters
29
- PollQuestion.toggleable_attributes + %i(image text comment)
48
+ PollQuestion.toggleable_attributes + %i[comment simple_image_id text]
30
49
  end
31
50
 
32
51
  def self.creation_parameters
33
- entity_parameters + %i(poll_id)
52
+ entity_parameters + %i[poll_id]
34
53
  end
35
54
 
36
55
  # @param [User] user
@@ -39,31 +58,6 @@ class PollQuestion < ApplicationRecord
39
58
  end
40
59
 
41
60
  def vote_count
42
- poll_answers.pluck(:poll_votes_count).reduce(&:+)
43
- end
44
-
45
- # @param [Integer] delta
46
- def change_priority(delta)
47
- new_priority = priority + delta
48
- adjacent = self.class.siblings(self).find_by(priority: new_priority)
49
- if adjacent.is_a?(self.class) && (adjacent.id != id)
50
- adjacent.update!(priority: priority)
51
- end
52
- update(priority: new_priority)
53
-
54
- self.class.siblings(self).map { |e| [e.id, e.priority] }.to_h
55
- end
56
-
57
- private
58
-
59
- def set_next_priority
60
- if id.nil? && priority == 1
61
- self.priority = self.class.siblings(self).maximum(:priority).to_i + 1
62
- end
63
- end
64
-
65
- def normalize_priority
66
- self.priority = PRIORITY_RANGE.first if priority < PRIORITY_RANGE.first
67
- self.priority = PRIORITY_RANGE.last if priority > PRIORITY_RANGE.last
61
+ poll_answers.pluck(:poll_votes_count).sum
68
62
  end
69
63
  end
@@ -1,10 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # User allowed to vote in poll
4
+ #
5
+ # Attributes:
6
+ # created_at [DateTime]
7
+ # poll_id [Poll]
8
+ # user_id [User]
9
+ # updated_at [DateTime]
1
10
  class PollUser < ApplicationRecord
2
11
  include HasOwner
3
12
 
4
13
  belongs_to :poll
5
14
  belongs_to :user
6
15
 
7
- validates_uniqueness_of :user_id, scope: [:poll_id]
16
+ validates_uniqueness_of :user_id, scope: :poll_id
8
17
 
9
18
  scope :list_for_administration, -> { order('id desc') }
10
19
  end
@@ -1,21 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Vote for poll answer
4
+ #
5
+ # Attributes:
6
+ # agent_id [Agent], optional
7
+ # created_at [DateTime]
8
+ # data [jsonb]
9
+ # ip [inet], optional
10
+ # poll_answer_id [PollAnswer]
11
+ # slug [string]
12
+ # updated_at [DateTime]
13
+ # user_id [User], optional
14
+ # uuid [uuid]
1
15
  class PollVote < ApplicationRecord
2
- PER_PAGE = 20
16
+ include HasOwner
17
+ include HasUuid
3
18
 
4
19
  belongs_to :poll_answer, counter_cache: true
5
20
  belongs_to :user
6
21
  belongs_to :agent, optional: true
7
22
 
8
- before_save { self.footprint = "#{user_id}:#{ip}:#{agent_id}" }
9
-
10
23
  scope :recent, -> { order('id desc') }
24
+ scope :list_for_visitors, -> { recent }
25
+ scope :list_for_administration, -> { recent }
11
26
 
12
27
  # @param [Integer] page
13
28
  def self.page_for_administration(page = 1)
14
- recent.page(page).per(PER_PAGE)
29
+ list_for_administration.page(page)
15
30
  end
16
31
 
17
32
  # @param [Integer] page
18
33
  def self.page_for_visitors(page = 1)
19
- recent.page(page).per(PER_PAGE)
34
+ list_for_visitors.page(page)
20
35
  end
21
36
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Biovision
4
+ module Components
5
+ # Managing polls
6
+ class PollsComponent < BaseComponent
7
+
8
+ protected
9
+
10
+ # @param [Hash] data
11
+ # @return [Hash]
12
+ def normalize_settings(data)
13
+ result = {}
14
+ numbers = %w[answer_limit question_limit]
15
+ numbers.each { |f| result[f] = data[f].to_i }
16
+
17
+ result
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1 @@
1
+ <li><%= render 'admin/polls/nav_item' %></li>
@@ -1,6 +1,3 @@
1
- <div class="image">
2
- <%= poll_image_preview(entity) %>
3
- </div>
4
1
  <div class="data">
5
2
  <div><%= admin_poll_answer_link(entity) %></div>
6
3
  <div class="secondary info">
@@ -1,9 +1,10 @@
1
1
  <% content_for :meta_title, t('.title', poll: @entity.poll.name, answer: @entity.poll_question.text, question: @entity.text) %>
2
2
  <% content_for :breadcrumbs do %>
3
- <%= link_to(t('admin.polls.nav_item.text'), admin_polls_path) %>
4
- <%= admin_poll_link(@entity.poll) %>
5
- <%= admin_poll_question_link(@entity.poll_question) %>
6
- <span><%= @entity.priority %>: <%= @entity.text %></span>
3
+ <%= admin_biovision_component_link(component_handler.component) %>
4
+ <%= link_to(t('admin.polls.index.nav_text'), admin_polls_path) %>
5
+ <%= admin_poll_link(@entity.poll) %>
6
+ <%= admin_poll_question_link(@entity.poll_question) %>
7
+ <span><%= @entity.priority %>: <%= @entity.text %></span>
7
8
  <% end %>
8
9
 
9
10
  <article>
@@ -14,40 +15,29 @@
14
15
  <li><%= edit_icon(edit_poll_answer_path(id: @entity.id)) %></li>
15
16
  </ul>
16
17
 
17
- <% unless @entity.image.blank? %>
18
- <figure>
19
- <%= poll_image_medium(@entity) %>
20
- <figcaption><%= t('activerecord.attributes.poll_answer.image') %></figcaption>
21
- </figure>
22
- <% end %>
23
-
24
18
  <dl>
25
- <dt><%= t(:created_at) %></dt>
26
- <dd>
27
- <%= time_tag(@entity.created_at) %>
28
- </dd>
29
-
30
- <dt><%= t(:updated_at) %></dt>
31
- <dd>
32
- <%= time_tag(@entity.updated_at) %>
33
- </dd>
34
-
35
- <dt><%= t('activerecord.attributes.poll_answer.priority') %></dt>
36
- <dd>
37
- <%= @entity.priority %>
38
- </dd>
39
-
40
- <dt><%= t('activerecord.attributes.poll_answer.poll_question_id') %></dt>
41
- <dd>
42
- <%= admin_poll_question_link(@entity.poll_question) %>
43
- </dd>
44
-
45
- <dt><%= t('activerecord.attributes.poll_answer.text') %></dt>
46
- <dd>
47
- <%= @entity.text %>
48
- </dd>
19
+ <div>
20
+ <dt><%= t('activerecord.attributes.poll_answer.poll_question_id') %></dt>
21
+ <dd>
22
+ <%= admin_poll_question_link(@entity.poll_question) %>
23
+ </dd>
24
+ </div>
25
+
26
+ <%= render partial: 'shared/entity/priority', locals: { entity: @entity } %>
27
+
28
+ <div>
29
+ <dt><%= t('activerecord.attributes.poll_answer.text') %></dt>
30
+ <dd>
31
+ <div class="text"><%= @entity.text %></div>
32
+ </dd>
33
+ </div>
34
+
35
+ <%= render partial: 'shared/entity/uuid', locals: { entity: @entity } %>
36
+ <%= render partial: 'shared/entity/timestamps', locals: { entity: @entity } %>
49
37
  </dl>
50
38
 
39
+ <%= render partial: 'admin/poll_answers/entity/data', locals: { entity: @entity } %>
40
+
51
41
  <section>
52
42
  <h2><%= t(:poll_vote_count, count: @entity.poll_votes_count) %></h2>
53
43
 
@@ -1,6 +1,3 @@
1
- <div class="image">
2
- <%= poll_image_preview(entity) %>
3
- </div>
4
1
  <div class="data">
5
2
  <div><%= admin_poll_question_link(entity) %></div>
6
3
  <% unless entity.comment.blank? %>
@@ -9,11 +6,15 @@
9
6
  </div>
10
7
  <% end %>
11
8
  <div class="secondary info">
12
- <%= t(:poll_answer_count, count: entity.poll_answers_count) %>
9
+ <%= t(:poll_answer_count, count: entity.poll_answers_count) %>,
10
+ <%= t(:poll_vote_count, count: entity.vote_count) %>
13
11
  <% if entity.poll_answers.any? %>
14
12
  <ol>
15
13
  <% entity.poll_answers.ordered_by_priority.each do |answer| %>
16
- <li><%= admin_poll_answer_link(answer) %></li>
14
+ <li>
15
+ <%= admin_poll_answer_link(answer) %>:
16
+ <%= answer.poll_votes_count %> (<%= answer.vote_percent %>%)
17
+ </li>
17
18
  <% end %>
18
19
  </ol>
19
20
  <% end %>
@@ -1,8 +1,9 @@
1
1
  <% content_for :meta_title, t('.title', poll: @entity.poll.name, question: @entity.text) %>
2
2
  <% content_for :breadcrumbs do %>
3
- <%= link_to(t('admin.polls.nav_item.text'), admin_polls_path) %>
4
- <%= admin_poll_link(@entity.poll) %>
5
- <span><%= @entity.priority %>: <%= @entity.text %></span>
3
+ <%= admin_biovision_component_link(component_handler.component) %>
4
+ <%= link_to(t('admin.polls.index.nav_text'), admin_polls_path) %>
5
+ <%= admin_poll_link(@entity.poll) %>
6
+ <span><%= @entity.priority %>: <%= @entity.text %></span>
6
7
  <% end %>
7
8
 
8
9
  <article>
@@ -13,47 +14,38 @@
13
14
  <li><%= edit_icon(edit_poll_question_path(id: @entity.id)) %></li>
14
15
  </ul>
15
16
 
16
- <% unless @entity.image.blank? %>
17
- <figure>
18
- <%= poll_image_medium(@entity) %>
19
- <figcaption><%= t('activerecord.attributes.poll_question.image') %></figcaption>
20
- </figure>
21
- <% end %>
22
-
23
17
  <dl>
24
- <dt><%= t(:created_at) %></dt>
25
- <dd>
26
- <%= time_tag(@entity.created_at) %>
27
- </dd>
28
-
29
- <dt><%= t(:updated_at) %></dt>
30
- <dd>
31
- <%= time_tag(@entity.updated_at) %>
32
- </dd>
18
+ <div>
19
+ <dt><%= t('attributes.poll') %></dt>
20
+ <dd>
21
+ <%= admin_poll_link(@entity.poll) %>
22
+ </dd>
23
+ </div>
33
24
 
34
- <dt><%= t('activerecord.attributes.poll_question.poll_id') %></dt>
35
- <dd>
36
- <%= admin_poll_link(@entity.poll) %>
37
- </dd>
25
+ <%= render partial: 'shared/entity/priority', locals: { entity: @entity } %>
38
26
 
39
- <dt><%= t('activerecord.attributes.poll_question.priority') %></dt>
40
- <dd>
41
- <%= @entity.priority %>
42
- </dd>
43
-
44
- <dt><%= t('activerecord.attributes.poll_question.text') %></dt>
45
- <dd>
46
- <%= @entity.text %>
47
- </dd>
27
+ <div>
28
+ <dt><%= t('activerecord.attributes.poll_question.text') %></dt>
29
+ <dd>
30
+ <div class="text"><%= @entity.text %></div>
31
+ </dd>
32
+ </div>
48
33
 
49
34
  <% unless @entity.comment.blank? %>
35
+ <div>
50
36
  <dt><%= t('activerecord.attributes.poll_question.comment') %></dt>
51
37
  <dd>
52
- <%= @entity.comment %>
38
+ <div class="text"><%= @entity.comment %></div>
53
39
  </dd>
40
+ </div>
54
41
  <% end %>
42
+
43
+ <%= render partial: 'shared/entity/uuid', locals: { entity: @entity } %>
44
+ <%= render partial: 'shared/entity/timestamps', locals: { entity: @entity } %>
55
45
  </dl>
56
46
 
47
+ <%= render partial: 'admin/poll_questions/entity/data', locals: { entity: @entity } %>
48
+
57
49
  <%=
58
50
  render(
59
51
  partial: 'shared/admin/toggleable',
@@ -76,10 +68,10 @@
76
68
  </section>
77
69
 
78
70
  <% if @entity.poll_answers.count < 10 %>
79
- <section>
80
- <h2><%= t('poll_answers.new.heading') %></h2>
71
+ <section>
72
+ <h2><%= t('poll_answers.new.heading') %></h2>
81
73
 
82
- <%= render partial: 'poll_answers/form', locals: { entity: @entity.poll_answers.new } %>
83
- </section>
74
+ <%= render partial: 'poll_answers/form', locals: { entity: @entity.poll_answers.new } %>
75
+ </section>
84
76
  <% end %>
85
77
  </article>