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:
Ben Halpern 2019-06-07 19:09:43 -04:00 committed by GitHub
parent 18d6ff0d74
commit b731df44ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 114 additions and 5 deletions

View file

@ -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);

View file

@ -21,6 +21,8 @@ module Admin
def tag_params
accessible = %i[
name
category
badge_id
supported
alias_for
wiki_body_markdown

View file

@ -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

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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 %>

View file

@ -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 %>

View file

@ -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 %>

View 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

View file

@ -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"

View file

@ -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

View file

@ -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