Commit fc09b34b6f2d9eccbb59b4081486f1f4bbe530ae

Authored by Victor Costa
1 parent d484e7ea

Store ranking data in db

db/migrate/20150825160320_create_ranking_item_table.rb 0 → 100644
... ... @@ -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 &lt; Grape::API
6 6 get ':id/ranking' do
7 7 article = find_article(environment.articles, params[:id])
8 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 11 {:proposals => article.ranking, :updated_at => DateTime.now}
10 12 end
11 13 ranking[:proposals] = paginate ranking[:proposals]
... ...
lib/proposals_discussion_plugin/proposal.rb
... ... @@ -7,6 +7,8 @@ class ProposalsDiscussionPlugin::Proposal &lt; TinyMceArticle
7 7  
8 8 has_many :locations, :class_name => 'Region', :through => :article_categorizations, :source => :category
9 9  
  10 + has_one :ranking_item
  11 +
10 12 def self.short_description
11 13 _("Proposal")
12 14 end
... ...
lib/proposals_discussion_plugin/proposals_holder.rb
... ... @@ -41,6 +41,10 @@ class ProposalsDiscussionPlugin::ProposalsHolder &lt; Folder
41 41 end
42 42  
43 43 def ranking
  44 + ProposalsDiscussionPlugin::RankingItem.joins(:proposal => :parent).where('parents_articles.id' => self.id)
  45 + end
  46 +
  47 + def compute_ranking
44 48 max_hits = proposals.maximum(:hits)
45 49 min_hits = proposals.minimum(:hits)
46 50  
... ... @@ -48,9 +52,17 @@ class ProposalsDiscussionPlugin::ProposalsHolder &lt; Folder
48 52 w = [(proposal.hits - max_hits).abs, (proposal.hits - min_hits).abs, 1].max.to_f
49 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 65 end
53   - ranking.sort_by { |p| p[:effective_support] }.reverse
54 66 end
55 67  
56 68 def cache_key_with_person(params = {}, user = nil, language = 'en')
... ...
lib/proposals_discussion_plugin/ranking_item.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +class ProposalsDiscussionPlugin::RankingItem < Noosfero::Plugin::ActiveRecord
  2 +
  3 + belongs_to :proposal
  4 +
  5 + attr_accessible :proposal, :abstract, :votes_for, :votes_against, :hits, :effective_support
  6 +
  7 +end
... ...
test/unit/api_test.rb
... ... @@ -33,7 +33,7 @@ class APITest &lt; ActiveSupport::TestCase
33 33  
34 34 get "/api/v1/proposals_discussion_plugin/#{topic.id}/ranking?#{params.to_query}"
35 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 37 end
38 38  
39 39 should 'suggest article children' do
... ...
test/unit/ranking_item_test.rb 0 → 100644
... ... @@ -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 &lt; ActiveSupport::TestCase
62 62 assert_equal 10, topic.max_score
63 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 85 end
... ...