このコミットが含まれているのは:
@@ -166,7 +166,7 @@ class TagsController < ApplicationController
|
||||
|
||||
rows =
|
||||
(internal_rows + external_rows)
|
||||
.sort_by { |row| [-row[:post_count], row[:name]] }
|
||||
.sort_by { |row| [-row['post_count'], row['name']] }
|
||||
.first(20)
|
||||
|
||||
render json: rows
|
||||
|
||||
@@ -6,8 +6,13 @@ class ExternalTag < ApplicationRecord
|
||||
has_many :posts, through: :post_external_tags
|
||||
|
||||
has_many :nico_tag_versions, foreign_key: :tag_id, inverse_of: :external_tag
|
||||
has_many :nico_tag_relations, foreign_key: :tag_id, dependent: :destroy
|
||||
has_many :linked_tags, through: :nico_tag_relations, source: :nico_tag
|
||||
|
||||
has_many :nico_tag_relations,
|
||||
foreign_key: :nico_tag_id,
|
||||
inverse_of: :nico_tag,
|
||||
dependent: :destroy
|
||||
|
||||
has_many :linked_tags, through: :nico_tag_relations, source: :tag
|
||||
|
||||
def snapshot_linked_tag_names
|
||||
linked_tags.joins(:tag_name).order('tag_names.name').pluck('tag_names.name')
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
class NicoTagRelation < ApplicationRecord
|
||||
belongs_to :nico_tag, class_name: 'Tag'
|
||||
belongs_to :tag, class_name: 'Tag'
|
||||
belongs_to :nico_tag,
|
||||
class_name: 'ExternalTag',
|
||||
foreign_key: :nico_tag_id,
|
||||
inverse_of: :nico_tag_relations
|
||||
belongs_to :tag, class_name: 'Tag', foreign_key: :tag_id
|
||||
|
||||
validates :nico_tag_id, presence: true
|
||||
validates :tag_id, presence: true
|
||||
|
||||
@@ -28,8 +28,11 @@ class Tag < ApplicationRecord
|
||||
has_many :post_tags, inverse_of: :tag
|
||||
has_many :posts, through: :post_tags
|
||||
|
||||
has_many :nico_tag_relations, foreign_key: :nico_tag_id, dependent: :destroy
|
||||
has_many :linked_tags, through: :nico_tag_relations, source: :tag
|
||||
has_many :reversed_nico_tag_relations,
|
||||
class_name: 'NicoTagRelation',
|
||||
foreign_key: :nico_tag_id,
|
||||
dependent: :destroy
|
||||
has_many :linked_nico_tags, through: :reversed_nico_tag_relations, source: :nico_tag
|
||||
|
||||
has_many :tag_implications, foreign_key: :parent_tag_id, dependent: :destroy
|
||||
has_many :children, through: :tag_implications, source: :tag
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
class NicoTagVersionRecorder < VersionRecorder
|
||||
def self.record! tag:, event_type:, created_by_user:
|
||||
new(tag:, event_type:, created_by_user:).record!
|
||||
def self.record! external_tag:, event_type:, created_by_user:
|
||||
new(external_tag:, event_type:, created_by_user:).record!
|
||||
end
|
||||
|
||||
def initialize tag:, event_type:, created_by_user:
|
||||
super(record: tag, event_type:, created_by_user:)
|
||||
def initialize external_tag:, event_type:, created_by_user:
|
||||
super(record: external_tag, event_type:, created_by_user:)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def version_class = NicoTagVersion
|
||||
def version_association = :nico_tag_versions
|
||||
def record_key = :tag
|
||||
def record_key = :external_tag
|
||||
|
||||
def snapshot_attributes
|
||||
{ name: "#{ @record.platform }:#{ @record.name }",
|
||||
linked_tags: @record.snapshot_linked_tag_names.join(' ') }
|
||||
end
|
||||
|
||||
def tracks_version_no_on_record? = false
|
||||
end
|
||||
|
||||
@@ -47,10 +47,14 @@ class VersionRecorder
|
||||
end
|
||||
|
||||
def update_record_version_no! version_no
|
||||
return unless tracks_version_no_on_record?
|
||||
|
||||
@record.update_columns(version_no:)
|
||||
@record.version_no = version_no
|
||||
end
|
||||
|
||||
def tracks_version_no_on_record? = true
|
||||
|
||||
def validate_version_sequence! latest
|
||||
if !(latest) && @event_type != 'create'
|
||||
raise "#{ version_class.name } first event must be create"
|
||||
@@ -60,7 +64,7 @@ class VersionRecorder
|
||||
raise "#{ version_class.name } create event already exists"
|
||||
end
|
||||
|
||||
return unless latest
|
||||
return if !(latest) || !(tracks_version_no_on_record?)
|
||||
|
||||
if @record.version_no != latest.version_no
|
||||
raise ("#{ record_class.name }##{ @record.id } version_no is #{ @record.version_no }, " +
|
||||
|
||||
@@ -30,9 +30,28 @@ namespace :nico do
|
||||
end
|
||||
end
|
||||
|
||||
PostTag.where(post_id: post.id, tag_id: to_remove.to_a).find_each do |pt|
|
||||
pt.destroy!
|
||||
PostTag.where(post_id: post.id, tag_id: to_remove.to_a).find_each(&:destroy!)
|
||||
end
|
||||
|
||||
def sync_post_external_tags! post, desired_external_tag_ids, current_external_tag_ids: nil
|
||||
current_external_tag_ids ||=
|
||||
PostExternalTag.where(post_id: post.id).pluck(:external_tag_id).to_set
|
||||
desired_external_tag_ids = desired_external_tag_ids.compact.to_set
|
||||
|
||||
to_add = desired_external_tag_ids - current_external_tag_ids
|
||||
to_remove = current_external_tag_ids - desired_external_tag_ids
|
||||
|
||||
ExternalTag.where(id: to_add.to_a).find_each do |external_tag|
|
||||
begin
|
||||
PostExternalTag.create!(post:, external_tag:)
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
;
|
||||
end
|
||||
end
|
||||
|
||||
PostExternalTag
|
||||
.where(post_id: post.id, external_tag_id: to_remove.to_a)
|
||||
.find_each(&:destroy!)
|
||||
end
|
||||
|
||||
mysql_user = ENV['MYSQL_USER']
|
||||
@@ -111,58 +130,63 @@ namespace :nico do
|
||||
sync_post_tags!(post, [Tag.tagme.id, Tag.bot.id, Tag.niconico.id, Tag.video.id])
|
||||
end
|
||||
|
||||
tags = post.tags
|
||||
# 既存のタグ Id. 集合
|
||||
kept_tag_ids = tags.pluck(:id).to_set
|
||||
# うち内部タグ Id. 集合
|
||||
kept_non_nico_tag_ids = tags.pluck(:id).to_set
|
||||
kept_tag_ids = post.tags.pluck(:id).to_set
|
||||
|
||||
# 既存の外部タグ Id. 集合
|
||||
kept_external_tag_ids = post.external_tags.pluck(:id).to_set
|
||||
|
||||
# 記載すべき外部タグ Id. のリスト
|
||||
desired_external_tag_ids = []
|
||||
|
||||
# 記載すべき外部タグ Id. および連携される内部タグ Id. のリスト
|
||||
desired_nico_tag_based_ids = []
|
||||
# 記載すべき内部タグ Id. のリスト
|
||||
desired_non_nico_tag_ids = []
|
||||
desired_tag_ids = kept_tag_ids.to_a
|
||||
|
||||
datum['tags'].each do |raw|
|
||||
tag = ExternalTag.find_or_create_by!(platform: :nico, name: raw)
|
||||
|
||||
unless tag.nico_tag_versions.exists?
|
||||
NicoTagVersionRecorder.record!(tag:, event_type: :create, created_by_user: nil)
|
||||
NicoTagVersionRecorder.record!(external_tag: tag,
|
||||
event_type: :create,
|
||||
created_by_user: nil)
|
||||
end
|
||||
|
||||
desired_nico_tag_based_ids << tag.id
|
||||
desired_external_tag_ids << tag.id
|
||||
|
||||
# 新たに記載される外部タグと連携される内部タグを記載
|
||||
unless tag.id.in?(kept_tag_ids)
|
||||
linked_ids = tag.linked_tags.pluck(:id)
|
||||
desired_non_nico_tag_ids.concat(linked_ids)
|
||||
desired_nico_tag_based_ids.concat(linked_ids)
|
||||
# 連携タグは記載すれども消除せず.
|
||||
unless tag.id.in?(kept_external_tag_ids)
|
||||
desired_tag_ids.concat(tag.linked_tags.pluck(:id))
|
||||
end
|
||||
end
|
||||
|
||||
deerjikist = Deerjikist.find_by(platform: :nico, code: datum['user'])
|
||||
if deerjikist
|
||||
desired_non_nico_tag_ids << deerjikist.tag_id
|
||||
desired_nico_tag_based_ids << deerjikist.tag_id
|
||||
elsif !(Tag.where(id: kept_non_nico_tag_ids).where(category: :deerjikist).exists?)
|
||||
desired_non_nico_tag_ids << Tag.no_deerjikist.id
|
||||
desired_nico_tag_based_ids << Tag.no_deerjikist.id
|
||||
desired_tag_ids << deerjikist.tag_id
|
||||
elsif !(Tag.where(id: kept_tag_ids).where(category: :deerjikist).exists?)
|
||||
desired_tag_ids << Tag.no_deerjikist.id
|
||||
end
|
||||
|
||||
desired_nico_tag_based_ids.uniq!
|
||||
desired_external_tag_ids.uniq!
|
||||
desired_tag_ids.uniq!
|
||||
|
||||
desired_all_tag_ids = kept_non_nico_tag_ids.to_a + desired_nico_tag_based_ids
|
||||
desired_non_nico_tag_ids.concat(kept_non_nico_tag_ids.to_a)
|
||||
desired_non_nico_tag_ids.uniq!
|
||||
if kept_non_nico_tag_ids != desired_non_nico_tag_ids.to_set
|
||||
desired_all_tag_ids << Tag.bot.id
|
||||
# 外部タグの記載に際しては “bot 操作” タグを記載しなぃ.
|
||||
if kept_tag_ids != desired_tag_ids.to_set
|
||||
desired_tag_ids << Tag.bot.id
|
||||
desired_tag_ids.uniq!
|
||||
end
|
||||
desired_all_tag_ids.uniq!
|
||||
|
||||
sync_post_tags!(post, desired_all_tag_ids, current_tag_ids: kept_tag_ids)
|
||||
tags_changed =
|
||||
kept_tag_ids != desired_tag_ids.to_set ||
|
||||
kept_external_tag_ids != desired_external_tag_ids.to_set
|
||||
|
||||
sync_post_tags!(post, desired_tag_ids, current_tag_ids: kept_tag_ids)
|
||||
sync_post_external_tags!(post, desired_external_tag_ids,
|
||||
current_external_tag_ids: kept_external_tag_ids)
|
||||
|
||||
if post_created
|
||||
PostVersionRecorder.record!(post:, event_type: :create, created_by_user: nil)
|
||||
elsif post_changed || kept_tag_ids != desired_all_tag_ids.to_set
|
||||
elsif post_changed || tags_changed
|
||||
PostVersionRecorder.ensure_snapshot!(post, created_by_user: nil)
|
||||
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: nil)
|
||||
end
|
||||
|
||||
@@ -378,4 +378,119 @@ RSpec.describe 'nico:sync' do
|
||||
expect(versions.second.title).to eq('changed title')
|
||||
expect(versions.second.tags).to eq(snapshot_tags(post.reload))
|
||||
end
|
||||
|
||||
def create_external_tag!(name)
|
||||
ExternalTag.create!(platform: :nico, name:)
|
||||
end
|
||||
|
||||
def create_nico_sync_post!
|
||||
post = Post.create!(
|
||||
title: 't',
|
||||
url: 'https://www.nicovideo.jp/watch/sm9',
|
||||
uploaded_user: nil
|
||||
)
|
||||
|
||||
PostTag.create!(post:, tag: Tag.no_deerjikist)
|
||||
|
||||
post
|
||||
end
|
||||
|
||||
def run_nico_sync_with_tags! tags
|
||||
stub_python([{
|
||||
'code' => 'sm9',
|
||||
'title' => 't',
|
||||
'tags' => tags,
|
||||
'user' => nil
|
||||
}])
|
||||
|
||||
allow(URI).to receive(:open).and_return(StringIO.new('<html></html>'))
|
||||
|
||||
run_rake_task('nico:sync')
|
||||
end
|
||||
|
||||
it '外部タグが新規記載されたとき,その時点の連携タグを記載する' do
|
||||
post = create_nico_sync_post!
|
||||
|
||||
external_tag = create_external_tag!('AAA')
|
||||
linked_tag = create_tag!('spec_linked', category: :general)
|
||||
link_nico_to_tag!(external_tag, linked_tag)
|
||||
|
||||
run_nico_sync_with_tags!(['AAA'])
|
||||
|
||||
expect(post.reload.external_tags).to include(external_tag)
|
||||
expect(post.tags).to include(linked_tag)
|
||||
end
|
||||
|
||||
it '外部タグに差分がない場合,内外マッピングが変はっても連携タグを再評価しない' do
|
||||
post = create_nico_sync_post!
|
||||
|
||||
external_tag = create_external_tag!('AAA')
|
||||
old_linked_tag = create_tag!('spec_old_linked', category: :general)
|
||||
new_linked_tag = create_tag!('spec_new_linked', category: :general)
|
||||
|
||||
relation = link_nico_to_tag!(external_tag, old_linked_tag)
|
||||
|
||||
run_nico_sync_with_tags!(['AAA'])
|
||||
|
||||
expect(post.reload.tags).to include(old_linked_tag)
|
||||
|
||||
# 人手で連携タグを消除する.
|
||||
PostTag.find_by!(post:, tag: old_linked_tag).destroy!
|
||||
|
||||
# 内外マッピングを変更する.
|
||||
relation.destroy!
|
||||
link_nico_to_tag!(external_tag, new_linked_tag)
|
||||
|
||||
# 外部タグ自体には差分が無い.
|
||||
run_nico_sync_with_tags!(['AAA'])
|
||||
|
||||
post.reload
|
||||
|
||||
expect(post.external_tags).to include(external_tag)
|
||||
expect(post.tags).not_to include(old_linked_tag)
|
||||
expect(post.tags).not_to include(new_linked_tag)
|
||||
end
|
||||
|
||||
it '外部タグの消除では連携タグを消除せず,再記載時にその時点の内外マッピングを適用する' do
|
||||
post = create_nico_sync_post!
|
||||
|
||||
external_tag = create_external_tag!('AAA')
|
||||
old_linked_tag = create_tag!('spec_old_linked', category: :general)
|
||||
new_linked_tag = create_tag!('spec_new_linked', category: :general)
|
||||
|
||||
relation = link_nico_to_tag!(external_tag, old_linked_tag)
|
||||
|
||||
# 外部タグを新規記載する.
|
||||
run_nico_sync_with_tags!(['AAA'])
|
||||
|
||||
post.reload
|
||||
expect(post.external_tags).to include(external_tag)
|
||||
expect(post.tags).to include(old_linked_tag)
|
||||
|
||||
# 外部タグを消除する.
|
||||
run_nico_sync_with_tags!([])
|
||||
|
||||
post.reload
|
||||
expect(post.external_tags).not_to include(external_tag)
|
||||
|
||||
# 外部タグの消除によって連携タグまでは消除されない.
|
||||
expect(post.tags).to include(old_linked_tag)
|
||||
|
||||
# 外部タグが記載されてゐない間に内外マッピングを変更する.
|
||||
relation.destroy!
|
||||
link_nico_to_tag!(external_tag, new_linked_tag)
|
||||
|
||||
# 同じ外部タグを再記載する.
|
||||
run_nico_sync_with_tags!(['AAA'])
|
||||
|
||||
post.reload
|
||||
|
||||
expect(post.external_tags).to include(external_tag)
|
||||
|
||||
# 旧連携タグは自動的には消除されない.
|
||||
expect(post.tags).to include(old_linked_tag)
|
||||
|
||||
# 再記載時点の内外マッピングが新たに適用される.
|
||||
expect(post.tags).to include(new_linked_tag)
|
||||
end
|
||||
end
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする