Add badges to key tags (#3074)
* Add badges to key tags * Adjust vertical-align * Fix YouTube tag mistake and refactor map to pluck
This commit is contained in:
parent
18d6ff0d74
commit
b731df44ca
13 changed files with 114 additions and 5 deletions
|
|
@ -104,6 +104,12 @@
|
|||
overflow-y: auto;
|
||||
h1 {
|
||||
position:relative;
|
||||
img {
|
||||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
vertical-align: -0.22em;
|
||||
margin-right: 0.15em;
|
||||
}
|
||||
}
|
||||
a{
|
||||
color:rgb(97, 97, 97);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ module Admin
|
|||
def tag_params
|
||||
accessible = %i[
|
||||
name
|
||||
category
|
||||
badge_id
|
||||
supported
|
||||
alias_for
|
||||
wiki_body_markdown
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class Internal::TagsController < Internal::ApplicationController
|
|||
def tag_params
|
||||
allowed_params = %i[
|
||||
supported rules_markdown short_summary pretty_name bg_color_hex
|
||||
text_color_hex tag_moderator_id remove_moderator_id alias_for
|
||||
text_color_hex tag_moderator_id remove_moderator_id alias_for badge_id category
|
||||
]
|
||||
params.require(:tag).permit(allowed_params)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,6 +41,20 @@ module BadgeRewarder
|
|||
award_badges(usernames, "dev-contributor", message_markdown)
|
||||
end
|
||||
|
||||
def self.award_tag_badges
|
||||
Tag.where.not(badge_id: nil).find_each do |tag|
|
||||
past_winner_user_ids = BadgeAchievement.where(badge_id: tag.badge_id).pluck(:user_id)
|
||||
winning_article = Article.where("score > 100").where.not(user_id: past_winner_user_ids).order("score DESC").cached_tagged_with(tag).first
|
||||
if winning_article
|
||||
award_badges(
|
||||
[winning_article.user.username],
|
||||
tag.badge.slug,
|
||||
"Congratulations on posting the most beloved [##{tag.name}](#{ApplicationConfig['APP_PROTOCOL'] + ApplicationConfig['APP_DOMAIN']}/t/#{tag.name}) post from the past week! 🙌",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.award_contributor_badges_from_github(since = 1.day.ago, message_markdown = "Thank you so much for your contributions!")
|
||||
client = Octokit::Client.new
|
||||
badge = Badge.find_by(slug: "dev-contributor")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ class Badge < ApplicationRecord
|
|||
mount_uploader :badge_image, BadgeUploader
|
||||
|
||||
has_many :badge_achievements
|
||||
has_many :tags
|
||||
has_many :users, through: :badge_achievements
|
||||
|
||||
validates :title, presence: true, uniqueness: true
|
||||
|
|
|
|||
|
|
@ -8,13 +8,17 @@ class Tag < ActsAsTaggableOn::Tag
|
|||
NAMES = %w[
|
||||
beginners career computerscience git go java javascript react vue webassembly
|
||||
linux productivity python security webdev css php laravel opensource npm a11y
|
||||
ruby cpp dotnet swift testing devops vim kotlin rust elixir graphql blockchain
|
||||
ruby cpp dotnet swift testing devops vim kotlin rust elixir graphql blockchain sre
|
||||
scala vscode docker kubernetes aws android ios angular csharp typescript django rails
|
||||
clojure ubuntu elm gamedev flutter dart bash machinelearning sql
|
||||
].freeze
|
||||
|
||||
ALLOWED_CATEGORIES = %w[uncategorized language library tool site_mechanic location subcommunity].freeze
|
||||
|
||||
attr_accessor :tag_moderator_id, :remove_moderator_id
|
||||
|
||||
belongs_to :badge, optional: true
|
||||
|
||||
mount_uploader :profile_image, ProfileImageUploader
|
||||
mount_uploader :social_image, ProfileImageUploader
|
||||
|
||||
|
|
@ -22,6 +26,7 @@ class Tag < ActsAsTaggableOn::Tag
|
|||
format: /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/, allow_nil: true
|
||||
validates :bg_color_hex,
|
||||
format: /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/, allow_nil: true
|
||||
validates :category, inclusion: { in: ALLOWED_CATEGORIES }
|
||||
|
||||
validate :validate_alias
|
||||
before_validation :evaluate_markdown
|
||||
|
|
@ -51,6 +56,10 @@ class Tag < ActsAsTaggableOn::Tag
|
|||
end
|
||||
end
|
||||
|
||||
def self.valid_categories
|
||||
ALLOWED_CATEGORIES
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def evaluate_markdown
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
<%= render "articles/tags/meta" %>
|
||||
<% end %>
|
||||
<% params.delete(:i) %>
|
||||
<% expiry_minutes = (params[:timeframe].blank? || params[:timeframe] == "latest") ? 4 : 20 %>
|
||||
<% cache("tag-stories-index-#{params.to_s}-#{true}-#{@tag_model.updated_at}", expires_in: expiry_minutes.minutes) do %>
|
||||
<% expiry_minutes = params[:timeframe].blank? || params[:timeframe] == "latest" ? 4 : 20 %>
|
||||
<% cache("tag-stories-index-#{params}-#{true}-#{@tag_model.updated_at}", expires_in: expiry_minutes.minutes) do %>
|
||||
<style>
|
||||
.tag-header {
|
||||
border: 2px solid<%= HexComparer.new([@tag_model.bg_color_hex || "#0000000", @tag_model.text_color_hex || "#ffffff"]).brightness(0.88) %>;
|
||||
|
|
@ -13,6 +13,9 @@
|
|||
<div class="user-profile-header tag-header">
|
||||
<div class="tag-or-query-header-container">
|
||||
<h1>
|
||||
<% if @tag_model.badge_id && @tag_model.badge %>
|
||||
<img src="<%= cloudinary @tag_model.badge.badge_image_url, 180 %>" style="transform: rotate(<%= -25 + (@tag_model.id.digits.first * 5) %>deg" />
|
||||
<% end %>
|
||||
<% if @tag_model && @tag_model.pretty_name.present? %>
|
||||
<%= @tag_model.pretty_name %>
|
||||
<% else %>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
<div>ID</div>
|
||||
<div>Alias For</div>
|
||||
<div>Taggings Count</div>
|
||||
<div>Category</div>
|
||||
<div>Is Moderated?</div>
|
||||
</div>
|
||||
<% @tags.each do |tag| %>
|
||||
|
|
@ -38,6 +39,7 @@
|
|||
<div><%= tag.id %></div>
|
||||
<div><%= tag.alias_for %></div>
|
||||
<div><%= tag.taggings_count %></div>
|
||||
<div><%= tag.category %></div>
|
||||
<div><%= tag.tag_moderator_ids.any? %></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@
|
|||
<%= f.label :supported %>
|
||||
<%= f.check_box :supported %>
|
||||
</div>
|
||||
<div>
|
||||
<%= f.label :badge_id %>
|
||||
<% badges_array = Badge.pluck(:title, :id) %>
|
||||
<%= f.select(:badge_id, options_for_select([["None", nil]] + badges_array, @tag.badge_id)) %>
|
||||
</div>
|
||||
<div>
|
||||
<%= f.label :category %>
|
||||
<%= f.select(:category, options_for_select(Tag.valid_categories, @tag.category)) %>
|
||||
</div>
|
||||
<div>
|
||||
<%= f.label :alias_for %>
|
||||
<%= f.text_field :alias_for %>
|
||||
|
|
|
|||
6
db/migrate/20190606202826_add_badge_id_to_tags.rb
Normal file
6
db/migrate/20190606202826_add_badge_id_to_tags.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class AddBadgeIdToTags < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :tags, :badge_id, :integer
|
||||
add_column :tags, :category, :string, default: "uncategorized", null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2019_05_21_190118) do
|
||||
ActiveRecord::Schema.define(version: 2019_06_06_202826) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
|
|
@ -762,8 +762,10 @@ ActiveRecord::Schema.define(version: 2019_05_21_190118) do
|
|||
|
||||
create_table "tags", id: :serial, force: :cascade do |t|
|
||||
t.string "alias_for"
|
||||
t.integer "badge_id"
|
||||
t.string "bg_color_hex"
|
||||
t.string "buffer_profile_id_code"
|
||||
t.string "category", default: "uncategorized", null: false
|
||||
t.datetime "created_at"
|
||||
t.integer "hotness_score", default: 0
|
||||
t.string "keywords_for_search"
|
||||
|
|
|
|||
|
|
@ -118,4 +118,55 @@ RSpec.describe BadgeRewarder do
|
|||
expect(user.badge_achievements.size).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "::award_tag_badges" do
|
||||
let(:user) { create(:user) }
|
||||
let(:second_user) { create(:user) }
|
||||
let(:third_user) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:second_article) { create(:article, user_id: second_user.id) }
|
||||
let(:third_article) { create(:article, user_id: third_user.id) }
|
||||
let(:badge) { create(:badge) }
|
||||
let(:tag) { create(:tag, badge_id: badge.id) }
|
||||
|
||||
it "awards badge if qualifying article by score and tagged appropriately" do
|
||||
article.update_columns(cached_tag_list: tag.name, score: 101)
|
||||
described_class.award_tag_badges
|
||||
expect(user.badge_achievements.size).to eq(1)
|
||||
expect(user.badge_achievements.last.badge_id).to eq(badge.id)
|
||||
end
|
||||
it "renders html for message" do
|
||||
article.update_columns(cached_tag_list: tag.name, score: 101)
|
||||
described_class.award_tag_badges
|
||||
expect(user.badge_achievements.size).to eq(1)
|
||||
expect(user.badge_achievements.last.rewarding_context_message).to include("<a ")
|
||||
expect(user.badge_achievements.last.rewarding_context_message).to include(ApplicationConfig["APP_DOMAIN"])
|
||||
end
|
||||
it "does not award badge if qualifying article by score but not tagged appropriately" do
|
||||
article.update_columns(cached_tag_list: "differenttag", score: 101)
|
||||
described_class.award_tag_badges
|
||||
expect(user.badge_achievements.size).to eq(0)
|
||||
end
|
||||
it "does not award badge if tagged appropriately but not high enoughs core" do
|
||||
article.update_columns(cached_tag_list: tag.name, score: 80)
|
||||
described_class.award_tag_badges
|
||||
expect(user.badge_achievements.size).to eq(0)
|
||||
end
|
||||
it "does not award badge to user who has previously won" do
|
||||
article.update_columns(cached_tag_list: tag.name, score: 201)
|
||||
second_article.update_columns(cached_tag_list: tag.name, score: 150)
|
||||
third_article.update_columns(cached_tag_list: tag.name, score: 120)
|
||||
described_class.award_tag_badges
|
||||
expect(user.reload.badge_achievements.size).to eq(1)
|
||||
expect(second_user.reload.badge_achievements.size).to eq(0)
|
||||
described_class.award_tag_badges
|
||||
expect(user.reload.badge_achievements.size).to eq(1)
|
||||
expect(second_user.reload.badge_achievements.size).to eq(1)
|
||||
expect(third_user.reload.badge_achievements.size).to eq(0)
|
||||
described_class.award_tag_badges
|
||||
expect(user.reload.badge_achievements.size).to eq(1)
|
||||
expect(second_user.reload.badge_achievements.size).to eq(1)
|
||||
expect(third_user.reload.badge_achievements.size).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -38,4 +38,8 @@ RSpec.describe Tag, type: :model do
|
|||
tag.save
|
||||
expect(tag.reload.updated_at).to be > 1.minute.ago
|
||||
end
|
||||
|
||||
it "knows class valid categories" do
|
||||
expect(Tag.valid_categories).to include("tool")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue