104 行
4.0 KiB
Ruby
104 行
4.0 KiB
Ruby
# Counts uses recorded values only; history/diff endpoints retain their own contract.
|
|
class CountsVersionRepr
|
|
def self.many(rows, resource:, language_code:, at: nil, inclusive: true)
|
|
new(rows.to_a, resource:, language_code:, at:, inclusive:).serialise
|
|
end
|
|
|
|
def initialize(rows, resource:, language_code:, at:, inclusive:)
|
|
@rows = rows
|
|
@at = at
|
|
@inclusive = inclusive
|
|
@resource = resource
|
|
@language = language_code
|
|
@users = User.where(id: rows.map(&:created_by_user_id).compact).pluck(:id, :name).to_h
|
|
@languages = master_names(Language)
|
|
@scripts = master_names(Script)
|
|
@tag_versions = {}
|
|
load_tag_versions if resource == 'posts'
|
|
end
|
|
|
|
def serialise
|
|
@rows.map do |row|
|
|
resource_id = row.public_send(@resource == 'posts' ? :post_id : :tag_id)
|
|
common = { id: row.id, resource_id:,
|
|
version_no: row.version_no, is_latest: row.attributes['counts_latest'].to_i == 1,
|
|
created_at: row.created_at.iso8601,
|
|
created_by_user: row.created_by_user_id &&
|
|
{ id: row.created_by_user_id, name: @users[row.created_by_user_id] } }
|
|
common.merge(@resource == 'posts' ? post(row) : tag(row))
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def master_names(model)
|
|
model.select('code AS counts_code', :name).to_h do |record|
|
|
[record.attributes['counts_code'], record.name]
|
|
end
|
|
end
|
|
|
|
def json(value)
|
|
value.is_a?(String) ? JSON.parse(value) : value
|
|
end
|
|
|
|
def load_tag_versions
|
|
pairs = @rows.flat_map { Array(json(_1.tags_json)) }
|
|
.filter_map { |entry| [entry['tag_id'], entry['version_no']] if entry['tag_id'] }
|
|
.uniq
|
|
pairs.each_slice(500) do |batch|
|
|
placeholders = batch.map { '(?, ?)' }.join(', ')
|
|
scope = TagVersion.all
|
|
if @at
|
|
scope = scope.where("created_at #{ @inclusive ? '<=' : '<' } ?", @at)
|
|
end
|
|
scope.where(["(tag_id, version_no) IN (#{ placeholders })", *batch.flatten])
|
|
.select(:tag_id, :version_no, :name, :category, :names).each do |version|
|
|
@tag_versions[[version.tag_id, version.version_no]] = version
|
|
end
|
|
end
|
|
end
|
|
|
|
def names(value)
|
|
(json(value) || {}).sort.map do |language, entry|
|
|
aliases = entry.fetch('aliases', []).map do |alias_entry|
|
|
alias_entry.merge('script_name' => @scripts[alias_entry['script_code']])
|
|
end
|
|
{ language_code: language, language_name: @languages[language],
|
|
script_code: entry['script_code'], script_name: @scripts[entry['script_code']],
|
|
name: entry['name'], auto_generated: entry['auto_generated'], aliases: }
|
|
end
|
|
end
|
|
|
|
def post(row)
|
|
snapshots = Array(json(row.tags_json)).map do |entry|
|
|
version = @tag_versions[[entry['tag_id'], entry['version_no']]]
|
|
{ id: entry['tag_id'] || entry['external_tag_id'],
|
|
kind: entry['tag_id'] ? 'internal' : 'external',
|
|
tag_id: entry['tag_id'], external_tag_id: entry['external_tag_id'],
|
|
version_no: entry['version_no'], name: entry['name'] || version&.name,
|
|
category: entry['category'] || version&.category,
|
|
sections: entry.fetch('sections', []), names: version ? names(version.names) : [] }
|
|
end
|
|
{ title: row.title, url: row.url, thumbnail_base: row.thumbnail_base,
|
|
tags: snapshots, parents: row.parent_post_ids.to_s.split.map { { id: _1.to_i } },
|
|
video_ms: row.video_ms,
|
|
original_created_from: row.original_created_from&.iso8601,
|
|
original_created_before: row.original_created_before&.iso8601 }
|
|
end
|
|
|
|
def tag(row)
|
|
recorded_names = names(row.names)
|
|
selected = recorded_names.find { _1[:language_code] == @language }
|
|
aliases = recorded_names.flat_map do |entry|
|
|
entry[:aliases].map {
|
|
_1.merge('language_code' => entry[:language_code],
|
|
'language_name' => entry[:language_name])
|
|
}
|
|
end
|
|
{ name: selected&.fetch(:name) || row.name, names: recorded_names,
|
|
category: row.category, aliases:,
|
|
parents: row.parent_tag_ids.to_s.split.map { { id: _1.to_i } },
|
|
deprecated: row.deprecated_at&.iso8601 }
|
|
end
|
|
end
|