Merge pull request #519 from AyuntamientoMadrid/fix-tagcloud-488

Fix tagcloud
This commit is contained in:
Juanjo Bazán
2015-09-17 17:24:39 +02:00
20 changed files with 219 additions and 23 deletions

View File

@@ -12,7 +12,7 @@ class DebatesController < ApplicationController
@debates = @search_terms.present? ? Debate.search(@search_terms) : Debate.all
@debates = @debates.tagged_with(@tag_filter) if @tag_filter
@debates = @debates.page(params[:page]).for_render.send("sort_by_#{@current_order}")
@tag_cloud = Debate.tag_counts.order(taggings_count: :desc, name: :asc).limit(20)
@tag_cloud = Debate.tag_counts.order(debates_count: :desc, name: :asc).limit(20)
set_debate_votes(@debates)
end

View File

@@ -11,7 +11,7 @@ class ProposalsController < ApplicationController
@proposals = @search_terms.present? ? Proposal.search(@search_terms) : Proposal.all
@proposals = @proposals.tagged_with(@tag_filter) if @tag_filter
@proposals = @proposals.page(params[:page]).for_render.send("sort_by_#{@current_order}")
@tag_cloud = Proposal.tag_counts.order(taggings_count: :desc, name: :asc).limit(20)
@tag_cloud = Proposal.tag_counts.order(proposals_count: :desc, name: :asc).limit(20)
set_proposal_votes(@proposals)
end

View File

@@ -1,7 +1,7 @@
module TagsHelper
def taggable_path(taggable, tag_name)
case taggable
def taggable_path(taggable_type, tag_name)
case taggable_type
when 'debate'
debates_path(tag: tag_name)
when 'proposal'
@@ -11,4 +11,18 @@ module TagsHelper
end
end
def taggable_counter_field(taggable_type)
"#{taggable_type.underscore.pluralize}_count"
end
def tag_cloud(tags, classes, counter_field = :taggings_count)
return [] if tags.empty?
max_count = tags.sort_by(&counter_field).last.send(counter_field).to_f
tags.each do |tag|
index = ((tag.send(counter_field) / max_count) * (classes.size - 1))
yield tag, classes[index.nan? ? 0 : index.round]
end
end
end

View File

@@ -127,6 +127,14 @@ class Debate < ActiveRecord::Base
cached_votes_up/flags_count.to_f < 5
end
def after_hide
self.tags.each{ |t| t.decrement_custom_counter_for('Debate') }
end
def after_restore
self.tags.each{ |t| t.increment_custom_counter_for('Debate') }
end
def self.title_max_length
@@title_max_length ||= self.columns.find { |c| c.name == 'title' }.limit || 80
end

View File

@@ -105,6 +105,14 @@ class Proposal < ActiveRecord::Base
cached_votes_up)
end
def after_hide
self.tags.each{ |t| t.decrement_custom_counter_for('Proposal') }
end
def after_restore
self.tags.each{ |t| t.increment_custom_counter_for('Proposal') }
end
def self.title_max_length
@@title_max_length ||= self.columns.find { |c| c.name == 'title' }.limit || 80
end

View File

@@ -42,7 +42,7 @@
<%= link_to debate.description, debate %>
<div class="truncate"></div>
</div>
<%= render "shared/tags", debate: debate, limit: 5 %>
<%= render "shared/tags", taggable: debate, limit: 5 %>
</div>
</div>

View File

@@ -59,7 +59,7 @@
<%= safe_html_with_links @debate.description %>
<%= render 'shared/tags', debate: @debate %>
<%= render 'shared/tags', taggable: @debate %>
<div class="js-moderator-debate-actions moderator-actions">
<%= render 'actions', debate: @debate %>

View File

@@ -42,7 +42,7 @@
<%= link_to proposal.summary, proposal %>
<div class="truncate"></div>
</div>
<%= render "shared/tags", proposal: proposal, limit: 5 %>
<%= render "shared/tags", taggable: proposal, limit: 5 %>
</div>
</div>

View File

@@ -78,7 +78,7 @@
<h4><%= @proposal.question %></h4>
<%= render 'shared/tags', proposal: @proposal %>
<%= render 'shared/tags', taggable: @proposal %>
<div class="js-moderator-proposal-actions moderator-actions">
<%= render 'actions', proposal: @proposal %>

View File

@@ -3,6 +3,9 @@
<h3><%= t("shared.tags_cloud.tags") %></h3>
<br>
<% tag_cloud @tag_cloud, %w[s m l] do |tag, css_class| %>
<%= link_to sanitize("#{tag.name} <span class='label round info'>#{tag.taggings_count}</span>"), taggable_path(taggable, tag.name), class: css_class %>
<%= link_to taggable_path(taggable, tag.name), class: css_class do %>
<%= tag.name %>
<span class='label round info'><%= tag.send(taggable_counter_field(taggable)) %></span>
<% end %>
<% end %>
</div>

View File

@@ -1,5 +1,4 @@
<%- limit ||= nil %>
<%- taggable = defined?(debate) ? debate : proposal %>
<% if taggable.tags.any? %>
<span class='tags'>

View File

@@ -1,8 +1,43 @@
ActsAsTaggableOn::Tagging.class_eval do
after_destroy :touch_taggable
module ActsAsTaggableOn
def touch_taggable
taggable.touch if taggable.present?
Tagging.class_eval do
after_create :increment_tag_custom_counter
after_destroy :touch_taggable, :decrement_tag_custom_counter
def touch_taggable
taggable.touch if taggable.present?
end
def increment_tag_custom_counter
tag.increment_custom_counter_for(taggable_type)
end
def decrement_tag_custom_counter
tag.decrement_custom_counter_for(taggable_type)
end
end
Tag.class_eval do
def increment_custom_counter_for(taggable_type)
Tag.increment_counter(custom_counter_field_name_for(taggable_type), id)
end
def decrement_custom_counter_for(taggable_type)
Tag.decrement_counter(custom_counter_field_name_for(taggable_type), id)
end
def recalculate_custom_counter_for(taggable_type)
visible_taggables = taggable_type.constantize.includes(:taggings).where('taggings.taggable_type' => taggable_type, 'taggings.tag_id' => id)
update(custom_counter_field_name_for(taggable_type) => visible_taggables.count)
end
private
def custom_counter_field_name_for(taggable_type)
"#{taggable_type.underscore.pluralize}_count"
end
end
end

View File

@@ -68,7 +68,7 @@ tags = Faker::Lorem.words(25)
author = User.reorder("RANDOM()").first
description = "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>"
debate = Debate.create!(author: author,
title: Faker::Lorem.sentence(3),
title: Faker::Lorem.sentence(3).truncate(60),
created_at: rand((Time.now - 1.week) .. Time.now),
description: description,
tag_list: tags.sample(3).join(','),
@@ -84,7 +84,7 @@ tags = Faker::Lorem.words(25)
author = User.reorder("RANDOM()").first
description = "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>"
proposal = Proposal.create!(author: author,
title: Faker::Lorem.sentence(3),
title: Faker::Lorem.sentence(3).truncate(60),
question: Faker::Lorem.sentence(3),
summary: Faker::Lorem.sentence(3),
responsible_name: Faker::Name.name,

View File

@@ -0,0 +1,8 @@
class AddExtraCountersToTags < ActiveRecord::Migration
def change
add_column :tags, :debates_count, :integer, default: 0
add_column :tags, :proposals_count, :integer, default: 0
add_index :tags, :debates_count
add_index :tags, :proposals_count
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150914191003) do
ActiveRecord::Schema.define(version: 20150917102718) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -253,12 +253,16 @@ ActiveRecord::Schema.define(version: 20150914191003) do
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "tags", force: :cascade do |t|
t.string "name", limit: 40
t.integer "taggings_count", default: 0
t.boolean "featured", default: false
t.string "name", limit: 40
t.integer "taggings_count", default: 0
t.boolean "featured", default: false
t.integer "debates_count", default: 0
t.integer "proposals_count", default: 0
end
add_index "tags", ["debates_count"], name: "index_tags_on_debates_count", using: :btree
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
add_index "tags", ["proposals_count"], name: "index_tags_on_proposals_count", using: :btree
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false

View File

@@ -4,6 +4,7 @@ module ActsAsParanoidAliases
base.extend(ClassMethods)
def hide
return false if hidden?
update_attribute(:hidden_at, Time.now)
after_hide
end
@@ -24,8 +25,13 @@ module ActsAsParanoidAliases
end
def restore(opts={})
return false unless hidden?
super(opts)
update_attribute(:confirmed_hide_at, nil)
after_restore
end
def after_restore
end
end
@@ -48,12 +54,12 @@ module ActsAsParanoidAliases
def hide_all(ids)
return if ids.blank?
where(id: ids).update_all(hidden_at: Time.now)
where(id: ids).each(&:hide)
end
def restore_all(ids)
return if ids.blank?
only_hidden.where(id: ids).update_all(hidden_at: nil)
only_hidden.where(id: ids).each(&:restore)
end
end
end

11
lib/tasks/tags.rake Normal file
View File

@@ -0,0 +1,11 @@
namespace :tags do
desc "Recalculates the debate and proposals counters"
task custom_count: :environment do
ActsAsTaggableOn::Tag.find_in_batches do |tasks|
tasks.each do |task|
task.recalculate_custom_counter_for('Debate')
task.recalculate_custom_counter_for('Proposal')
end
end
end
end

View File

@@ -0,0 +1,72 @@
require 'rails_helper'
describe 'ActsAsTaggableOn' do
describe 'Tagging' do
describe "when tagging debates or proposals" do
let(:proposal) { create(:proposal) }
let(:debate) { create(:debate) }
it "increases and decreases the tag's custom counters" do
tag = ActsAsTaggableOn::Tag.create(name: "foo")
expect(tag.debates_count).to eq(0)
expect(tag.proposals_count).to eq(0)
proposal.tag_list.add("foo")
proposal.save
tag.reload
expect(tag.debates_count).to eq(0)
expect(tag.proposals_count).to eq(1)
debate.tag_list.add("foo")
debate.save
tag.reload
expect(tag.debates_count).to eq(1)
expect(tag.proposals_count).to eq(1)
proposal.tag_list.remove("foo")
proposal.save
tag.reload
expect(tag.debates_count).to eq(1)
expect(tag.proposals_count).to eq(0)
debate.tag_list.remove("foo")
debate.save
tag.reload
expect(tag.debates_count).to eq(0)
expect(tag.proposals_count).to eq(0)
end
end
end
describe 'Tag' do
describe "#recalculate_custom_counter_for" do
it "updates the counters of proposals and debates, taking into account hidden ones" do
tag = ActsAsTaggableOn::Tag.create(name: "foo")
create(:proposal, tag_list: "foo")
create(:proposal, :hidden, tag_list: "foo")
create(:debate, tag_list: "foo")
create(:debate, :hidden, tag_list: "foo")
tag.update(debates_count: 0, proposals_count: 0)
tag.recalculate_custom_counter_for('Debate')
expect(tag.debates_count).to eq(1)
tag.recalculate_custom_counter_for('Proposal')
expect(tag.proposals_count).to eq(1)
end
end
end
end

View File

@@ -350,6 +350,20 @@ describe Debate do
end
end
describe "custom tag counters when hiding/restoring" do
it "decreases the tag counter when hiden, and increases it when restored" do
debate = create(:debate, tag_list: "foo")
tag = ActsAsTaggableOn::Tag.where(name: 'foo').first
expect(tag.debates_count).to eq(1)
debate.hide
expect(tag.reload.debates_count).to eq(0)
debate.restore
expect(tag.reload.debates_count).to eq(1)
end
end
describe "conflictive debates" do
it "should return true when it has more than 1 flag for 5 positive votes" do

View File

@@ -193,6 +193,20 @@ describe Proposal do
end
end
describe "custom tag counters when hiding/restoring" do
it "decreases the tag counter when hiden, and increases it when restored" do
proposal = create(:proposal, tag_list: "foo")
tag = ActsAsTaggableOn::Tag.where(name: 'foo').first
expect(tag.proposals_count).to eq(1)
proposal.hide
expect(tag.reload.proposals_count).to eq(0)
proposal.restore
expect(tag.reload.proposals_count).to eq(1)
end
end
describe "#confidence_score" do
it "takes into account votes" do