biovision-poll 0.1.180917.0 → 0.2.200324.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/admin/poll_answers_controller.rb +6 -5
- data/app/controllers/admin/poll_questions_controller.rb +6 -5
- data/app/controllers/admin/polls_controller.rb +7 -6
- data/app/controllers/poll_answers_controller.rb +15 -9
- data/app/controllers/poll_questions_controller.rb +15 -9
- data/app/controllers/polls_controller.rb +25 -16
- data/app/helpers/polls_helper.rb +19 -29
- data/app/models/poll.rb +50 -18
- data/app/models/poll_answer.rb +29 -36
- data/app/models/poll_question.rb +33 -39
- data/app/models/poll_user.rb +10 -1
- data/app/models/poll_vote.rb +20 -5
- data/app/services/biovision/components/polls_component.rb +21 -0
- data/app/views/admin/components/links/_polls.html.erb +1 -0
- data/app/views/admin/poll_answers/entity/_data.html.erb +0 -0
- data/app/views/admin/poll_answers/entity/_in_list.html.erb +0 -3
- data/app/views/admin/poll_answers/show.html.erb +25 -35
- data/app/views/admin/poll_questions/entity/_data.html.erb +0 -0
- data/app/views/admin/poll_questions/entity/_in_list.html.erb +6 -5
- data/app/views/admin/poll_questions/show.html.erb +29 -37
- data/app/views/admin/poll_votes/entity/_data.html.erb +0 -0
- data/app/views/admin/poll_votes/entity/_in_list.html.erb +6 -5
- data/app/views/admin/polls/entity/_data.html.erb +0 -0
- data/app/views/admin/polls/entity/_in_list.html.erb +1 -3
- data/app/views/admin/polls/index.html.erb +8 -2
- data/app/views/admin/polls/show.html.erb +32 -30
- data/app/views/admin/polls/users.html.erb +2 -1
- data/app/views/poll_answers/_form.html.erb +36 -30
- data/app/views/poll_answers/_poll_answer.html.erb +22 -2
- data/app/views/poll_answers/edit.html.erb +6 -5
- data/app/views/poll_answers/new.html.erb +8 -7
- data/app/views/poll_questions/_form.html.erb +53 -43
- data/app/views/poll_questions/edit.html.erb +6 -5
- data/app/views/poll_questions/new.html.erb +7 -6
- data/app/views/polls/_form.html.erb +64 -41
- data/app/views/polls/_poll.html.erb +11 -6
- data/app/views/polls/edit.html.erb +5 -4
- data/app/views/polls/new.html.erb +3 -2
- data/app/views/polls/results.html.erb +22 -0
- data/config/locales/polls-ru.yml +28 -26
- data/config/routes.rb +19 -15
- data/db/migrate/20200323000001_create_biovision_polls.rb +121 -0
- data/lib/biovision/poll/version.rb +3 -1
- metadata +10 -12
- data/app/uploaders/poll_image_uploader.rb +0 -46
- data/app/views/admin/index/dashboard/_biovision_poll.html.erb +0 -10
- data/db/migrate/20170906000001_create_polls.rb +0 -70
- data/db/migrate/20170906000002_create_poll_questions.rb +0 -22
- data/db/migrate/20170906000003_create_poll_answers.rb +0 -20
- data/db/migrate/20170906000004_create_poll_votes.rb +0 -20
- data/db/migrate/20180412000010_create_poll_users.rb +0 -21
- data/db/migrate/20180903111111_rename_vote_footprint.rb +0 -11
data/app/models/poll_answer.rb
CHANGED
@@ -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
|
-
|
3
|
-
|
16
|
+
include Checkable
|
17
|
+
include HasSimpleImage
|
18
|
+
include HasUuid
|
19
|
+
include NestedPriority
|
4
20
|
|
5
|
-
|
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:
|
30
|
+
validates_uniqueness_of :text, scope: :poll_question_id
|
18
31
|
|
19
|
-
scope :
|
20
|
-
scope :
|
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
|
41
|
+
%i[text simple_image_id]
|
24
42
|
end
|
25
43
|
|
26
44
|
def self.creation_parameters
|
27
|
-
entity_parameters + %i
|
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
|
data/app/models/poll_question.rb
CHANGED
@@ -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
|
-
|
5
|
-
|
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:
|
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
|
-
|
26
|
-
|
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
|
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
|
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).
|
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
|
data/app/models/poll_user.rb
CHANGED
@@ -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:
|
16
|
+
validates_uniqueness_of :user_id, scope: :poll_id
|
8
17
|
|
9
18
|
scope :list_for_administration, -> { order('id desc') }
|
10
19
|
end
|
data/app/models/poll_vote.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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>
|
File without changes
|
@@ -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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
<
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
|
File without changes
|
@@ -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
|
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
|
-
|
4
|
-
|
5
|
-
|
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
|
-
<
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
35
|
-
<dd>
|
36
|
-
<%= admin_poll_link(@entity.poll) %>
|
37
|
-
</dd>
|
25
|
+
<%= render partial: 'shared/entity/priority', locals: { entity: @entity } %>
|
38
26
|
|
39
|
-
<
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
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
|
-
|
80
|
-
|
71
|
+
<section>
|
72
|
+
<h2><%= t('poll_answers.new.heading') %></h2>
|
81
73
|
|
82
|
-
|
83
|
-
|
74
|
+
<%= render partial: 'poll_answers/form', locals: { entity: @entity.poll_answers.new } %>
|
75
|
+
</section>
|
84
76
|
<% end %>
|
85
77
|
</article>
|