Commit fc09b34b6f2d9eccbb59b4081486f1f4bbe530ae
1 parent
d484e7ea
Exists in
master
and in
7 other branches
Store ranking data in db
Showing
8 changed files
with
90 additions
and
3 deletions
Show diff stats
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +class CreateRankingItemTable < ActiveRecord::Migration | ||
2 | + | ||
3 | + def self.up | ||
4 | + create_table :proposals_discussion_plugin_ranking_items do |t| | ||
5 | + t.integer :position | ||
6 | + t.string :abstract | ||
7 | + t.integer :votes_for | ||
8 | + t.integer :votes_against | ||
9 | + t.integer :hits | ||
10 | + t.decimal :effective_support | ||
11 | + t.integer :proposal_id | ||
12 | + t.timestamps | ||
13 | + end | ||
14 | + add_index( | ||
15 | + :proposals_discussion_plugin_ranking_items, | ||
16 | + [:proposal_id], | ||
17 | + name: 'index_proposals_discussion_plugin_ranking_proposal_id' | ||
18 | + ) | ||
19 | + end | ||
20 | + | ||
21 | + def self.down | ||
22 | + drop_table :proposals_discussion_plugin_ranking_items | ||
23 | + end | ||
24 | + | ||
25 | +end |
lib/proposals_discussion_plugin/api.rb
@@ -6,6 +6,8 @@ class ProposalsDiscussionPlugin::API < Grape::API | @@ -6,6 +6,8 @@ class ProposalsDiscussionPlugin::API < Grape::API | ||
6 | get ':id/ranking' do | 6 | get ':id/ranking' do |
7 | article = find_article(environment.articles, params[:id]) | 7 | article = find_article(environment.articles, params[:id]) |
8 | ranking = Rails.cache.fetch("#{article.cache_key}/proposals_ranking", expires_in: 30.minutes) do | 8 | ranking = Rails.cache.fetch("#{article.cache_key}/proposals_ranking", expires_in: 30.minutes) do |
9 | + #FIXME call update_ranking in an async job | ||
10 | + article.update_ranking | ||
9 | {:proposals => article.ranking, :updated_at => DateTime.now} | 11 | {:proposals => article.ranking, :updated_at => DateTime.now} |
10 | end | 12 | end |
11 | ranking[:proposals] = paginate ranking[:proposals] | 13 | ranking[:proposals] = paginate ranking[:proposals] |
lib/proposals_discussion_plugin/proposal.rb
@@ -7,6 +7,8 @@ class ProposalsDiscussionPlugin::Proposal < TinyMceArticle | @@ -7,6 +7,8 @@ class ProposalsDiscussionPlugin::Proposal < TinyMceArticle | ||
7 | 7 | ||
8 | has_many :locations, :class_name => 'Region', :through => :article_categorizations, :source => :category | 8 | has_many :locations, :class_name => 'Region', :through => :article_categorizations, :source => :category |
9 | 9 | ||
10 | + has_one :ranking_item | ||
11 | + | ||
10 | def self.short_description | 12 | def self.short_description |
11 | _("Proposal") | 13 | _("Proposal") |
12 | end | 14 | end |
lib/proposals_discussion_plugin/proposals_holder.rb
@@ -41,6 +41,10 @@ class ProposalsDiscussionPlugin::ProposalsHolder < Folder | @@ -41,6 +41,10 @@ class ProposalsDiscussionPlugin::ProposalsHolder < Folder | ||
41 | end | 41 | end |
42 | 42 | ||
43 | def ranking | 43 | def ranking |
44 | + ProposalsDiscussionPlugin::RankingItem.joins(:proposal => :parent).where('parents_articles.id' => self.id) | ||
45 | + end | ||
46 | + | ||
47 | + def compute_ranking | ||
44 | max_hits = proposals.maximum(:hits) | 48 | max_hits = proposals.maximum(:hits) |
45 | min_hits = proposals.minimum(:hits) | 49 | min_hits = proposals.minimum(:hits) |
46 | 50 | ||
@@ -48,9 +52,17 @@ class ProposalsDiscussionPlugin::ProposalsHolder < Folder | @@ -48,9 +52,17 @@ class ProposalsDiscussionPlugin::ProposalsHolder < Folder | ||
48 | w = [(proposal.hits - max_hits).abs, (proposal.hits - min_hits).abs, 1].max.to_f | 52 | w = [(proposal.hits - max_hits).abs, (proposal.hits - min_hits).abs, 1].max.to_f |
49 | effective_support = (proposal.votes_for - proposal.votes_against)/w | 53 | effective_support = (proposal.votes_for - proposal.votes_against)/w |
50 | 54 | ||
51 | - {:id => proposal.id, :abstract => proposal.abstract, :votes_for => proposal.votes_for, :votes_against => proposal.votes_against, :hits => proposal.hits, :effective_support => effective_support} | 55 | + ProposalsDiscussionPlugin::RankingItem.new(:proposal => proposal, :abstract => proposal.abstract, :votes_for => proposal.votes_for, :votes_against => proposal.votes_against, :hits => proposal.hits, :effective_support => effective_support) |
56 | + end | ||
57 | + ranking.sort_by { |p| p.effective_support }.reverse | ||
58 | + end | ||
59 | + | ||
60 | + def update_ranking | ||
61 | + new_ranking = compute_ranking | ||
62 | + transaction do | ||
63 | + self.ranking.destroy_all | ||
64 | + new_ranking.each {|item| item.save!} | ||
52 | end | 65 | end |
53 | - ranking.sort_by { |p| p[:effective_support] }.reverse | ||
54 | end | 66 | end |
55 | 67 | ||
56 | def cache_key_with_person(params = {}, user = nil, language = 'en') | 68 | def cache_key_with_person(params = {}, user = nil, language = 'en') |
test/unit/api_test.rb
@@ -33,7 +33,7 @@ class APITest < ActiveSupport::TestCase | @@ -33,7 +33,7 @@ class APITest < ActiveSupport::TestCase | ||
33 | 33 | ||
34 | get "/api/v1/proposals_discussion_plugin/#{topic.id}/ranking?#{params.to_query}" | 34 | get "/api/v1/proposals_discussion_plugin/#{topic.id}/ranking?#{params.to_query}" |
35 | json = JSON.parse(last_response.body) | 35 | json = JSON.parse(last_response.body) |
36 | - assert_equal [proposal2.id, proposal3.id, proposal1.id], json['proposals'].map {|p| p['id']} | 36 | + assert_equal [proposal2.abstract, proposal3.abstract, proposal1.abstract], json['proposals'].map {|p| p['abstract']} |
37 | end | 37 | end |
38 | 38 | ||
39 | should 'suggest article children' do | 39 | should 'suggest article children' do |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +require_relative '../test_helper' | ||
2 | + | ||
3 | +class RankingItemTest < ActiveSupport::TestCase | ||
4 | + | ||
5 | + def setup | ||
6 | + @profile = fast_create(Community) | ||
7 | + @person = fast_create(Person) | ||
8 | + @discussion = ProposalsDiscussionPlugin::Discussion.create!(:name => 'discussion', :profile => person, :allow_topics => false) | ||
9 | + end | ||
10 | + | ||
11 | + attr_reader :profile, :person, :discussion | ||
12 | + | ||
13 | + should 'return associated ranking item in proposal' do | ||
14 | + proposal = ProposalsDiscussionPlugin::Proposal.create!(:name => 'test', :abstract => 'abstract', :profile => profile, :parent => discussion) | ||
15 | + discussion.update_ranking | ||
16 | + assert proposal.ranking_item | ||
17 | + end | ||
18 | + | ||
19 | +end |
test/unit/topic_test.rb
@@ -62,4 +62,24 @@ class TopicTest < ActiveSupport::TestCase | @@ -62,4 +62,24 @@ class TopicTest < ActiveSupport::TestCase | ||
62 | assert_equal 10, topic.max_score | 62 | assert_equal 10, topic.max_score |
63 | end | 63 | end |
64 | 64 | ||
65 | + should 'generate ranking for topics' do | ||
66 | + topic2 = ProposalsDiscussionPlugin::Topic.new(:name => 'test2', :profile => @profile, :parent => @discussion) | ||
67 | + proposal1 = ProposalsDiscussionPlugin::Proposal.create!(:parent => topic, :profile => profile, :name => "proposal1", :abstract => 'abstract') | ||
68 | + proposal2 = ProposalsDiscussionPlugin::Proposal.create!(:parent => topic, :profile => profile, :name => "proposal2", :abstract => 'abstract') | ||
69 | + proposal3 = ProposalsDiscussionPlugin::Proposal.create!(:parent => topic2, :profile => profile, :name => "proposal3", :abstract => 'abstract') | ||
70 | + | ||
71 | + topic.update_ranking | ||
72 | + topic2.update_ranking | ||
73 | + assert_equal [proposal1.abstract, proposal2.abstract], topic.ranking.map(&:abstract) | ||
74 | + assert_equal [proposal3.abstract], topic2.ranking.map(&:abstract) | ||
75 | + end | ||
76 | + | ||
77 | + should 'update ranking for a topic' do | ||
78 | + proposal1 = ProposalsDiscussionPlugin::Proposal.create!(:parent => topic, :profile => profile, :name => "proposal1", :abstract => 'abstract') | ||
79 | + proposal2 = ProposalsDiscussionPlugin::Proposal.create!(:parent => topic, :profile => profile, :name => "proposal2", :abstract => 'abstract') | ||
80 | + topic.update_ranking | ||
81 | + topic.update_ranking | ||
82 | + assert_equal [proposal1.abstract, proposal2.abstract], topic.ranking.map(&:abstract) | ||
83 | + end | ||
84 | + | ||
65 | end | 85 | end |