このコミットが含まれているのは:
@@ -1,3 +1,6 @@
|
||||
require 'set'
|
||||
|
||||
|
||||
class MaterialsController < ApplicationController
|
||||
rescue_from MaterialZipExporter::EmptyExportError, with: :render_zip_empty
|
||||
rescue_from MaterialZipExporter::DuplicatePathError, with: :render_zip_duplicate_path
|
||||
@@ -13,12 +16,14 @@ class MaterialsController < ApplicationController
|
||||
offset = (page - 1) * limit
|
||||
|
||||
filters = material_index_filters
|
||||
tag_graph = material_index_tag_graph(filters)
|
||||
q = Material.includes(:material_export_items,
|
||||
thumbnail_attachment: :blob,
|
||||
file_attachment: :blob,
|
||||
tag: :tag_name)
|
||||
q = q.where(tag_id: nil) if filters[:tag_state] == 'untagged'
|
||||
q = q.where.not(tag_id: nil) if filters[:tag_state] == 'tagged'
|
||||
q = apply_material_tag_filter(q, filters, tag_graph)
|
||||
q = q.where('materials.created_at >= ?', filters[:created_from]) if filters[:created_from]
|
||||
q = q.where('materials.created_at <= ?', filters[:created_to]) if filters[:created_to]
|
||||
q = q.where('materials.updated_at >= ?', filters[:updated_from]) if filters[:updated_from]
|
||||
@@ -37,8 +42,16 @@ class MaterialsController < ApplicationController
|
||||
.offset(offset)
|
||||
.to_a
|
||||
|
||||
render json: { materials: MaterialRepr.list_many(materials, host: request.base_url),
|
||||
count: }
|
||||
response = { materials: MaterialRepr.list_many(materials, host: request.base_url),
|
||||
count: }
|
||||
if filters[:tag_id]
|
||||
response[:tag_scope] = material_index_tag_scope(filters, tag_graph)
|
||||
end
|
||||
if filters[:group_by] == 'parent_tag'
|
||||
response[:groups] = material_index_groups(materials, tag_graph)
|
||||
end
|
||||
|
||||
render json: response
|
||||
end
|
||||
|
||||
def show
|
||||
@@ -198,9 +211,18 @@ class MaterialsController < ApplicationController
|
||||
direction = params[:direction].to_s.downcase
|
||||
direction = 'desc' unless ['asc', 'desc'].include?(direction)
|
||||
|
||||
group_by = params[:group_by].to_s.presence
|
||||
group_by = 'none' unless ['none', 'parent_tag'].include?(group_by)
|
||||
|
||||
tag_id = params[:tag_id].to_i
|
||||
tag_id = nil if tag_id <= 0
|
||||
|
||||
{ q: params[:q].to_s.strip.presence,
|
||||
tag_state:,
|
||||
media_kind:,
|
||||
tag_id:,
|
||||
include_descendants: bool?(:include_descendants),
|
||||
group_by:,
|
||||
created_from: parse_time_param(:created_from),
|
||||
created_to: parse_time_param(:created_to),
|
||||
updated_from: parse_time_param(:updated_from),
|
||||
@@ -270,6 +292,147 @@ class MaterialsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def apply_material_tag_filter q, filters, tag_graph
|
||||
return q unless filters[:tag_id]
|
||||
|
||||
tag_ids =
|
||||
if tag_graph
|
||||
tag_graph[:scope_tag_ids]
|
||||
else
|
||||
[filters[:tag_id]]
|
||||
end
|
||||
|
||||
q.where(tag_id: tag_ids)
|
||||
end
|
||||
|
||||
def material_index_tag_graph filters
|
||||
return nil unless filters[:tag_id]
|
||||
|
||||
children_by_parent_id = Hash.new { |h, k| h[k] = [] }
|
||||
TagImplication.pluck(:parent_tag_id, :tag_id).each do |parent_id, child_id|
|
||||
children_by_parent_id[parent_id] << child_id
|
||||
end
|
||||
|
||||
scope_tag_ids =
|
||||
if filters[:include_descendants]
|
||||
collect_material_descendant_ids(filters[:tag_id], children_by_parent_id)
|
||||
else
|
||||
Set.new([filters[:tag_id]])
|
||||
end
|
||||
tags_by_id =
|
||||
Tag
|
||||
.joins(:tag_name)
|
||||
.where(id: scope_tag_ids)
|
||||
.pluck('tags.id', 'tag_names.name', 'tags.category', 'tags.deprecated_at')
|
||||
.each_with_object({ }) do |(id, name, category, deprecated_at), hash|
|
||||
hash[id] = { id:, name:, category:, deprecated: deprecated_at.present? }
|
||||
end
|
||||
|
||||
selected_tag = tags_by_id[filters[:tag_id]]
|
||||
return nil unless selected_tag
|
||||
|
||||
group_tag_id_by_tag_id = { }
|
||||
children_by_parent_id[filters[:tag_id]].each do |child_tag_id|
|
||||
assign_material_group_branch(child_tag_id,
|
||||
children_by_parent_id:,
|
||||
tags_by_id:,
|
||||
group_tag_id_by_tag_id:,
|
||||
current_group_tag_id: nil,
|
||||
seen: Set.new([filters[:tag_id]]))
|
||||
end
|
||||
|
||||
{ selected_tag_id: filters[:tag_id],
|
||||
selected_tag:,
|
||||
scope_tag_ids: scope_tag_ids.to_a,
|
||||
tags_by_id:,
|
||||
group_tag_id_by_tag_id: }
|
||||
end
|
||||
|
||||
def collect_material_descendant_ids selected_tag_id, children_by_parent_id
|
||||
ids = Set.new([selected_tag_id])
|
||||
stack = [selected_tag_id]
|
||||
|
||||
until stack.empty?
|
||||
tag_id = stack.pop
|
||||
children_by_parent_id[tag_id].each do |child_tag_id|
|
||||
next if ids.include?(child_tag_id)
|
||||
|
||||
ids << child_tag_id
|
||||
stack << child_tag_id
|
||||
end
|
||||
end
|
||||
|
||||
ids
|
||||
end
|
||||
|
||||
def assign_material_group_branch tag_id, children_by_parent_id:, tags_by_id:,
|
||||
group_tag_id_by_tag_id:, current_group_tag_id:, seen:
|
||||
return if seen.include?(tag_id)
|
||||
|
||||
tag = tags_by_id[tag_id]
|
||||
return unless tag
|
||||
|
||||
seen = seen.dup << tag_id
|
||||
next_group_tag_id = current_group_tag_id
|
||||
next_group_tag_id = tag_id if next_group_tag_id.nil? && !tag[:deprecated]
|
||||
group_tag_id_by_tag_id[tag_id] = next_group_tag_id if next_group_tag_id
|
||||
|
||||
children_by_parent_id[tag_id].each do |child_tag_id|
|
||||
assign_material_group_branch(child_tag_id,
|
||||
children_by_parent_id:,
|
||||
tags_by_id:,
|
||||
group_tag_id_by_tag_id:,
|
||||
current_group_tag_id: next_group_tag_id,
|
||||
seen:)
|
||||
end
|
||||
end
|
||||
|
||||
def material_index_groups materials, tag_graph
|
||||
return [] unless tag_graph
|
||||
|
||||
groups_by_key = { }
|
||||
|
||||
materials.each do |material|
|
||||
next unless material.tag_id
|
||||
|
||||
group_tag_id =
|
||||
if material.tag_id == tag_graph[:selected_tag_id]
|
||||
tag_graph[:selected_tag_id]
|
||||
else
|
||||
tag_graph[:group_tag_id_by_tag_id][material.tag_id] || tag_graph[:selected_tag_id]
|
||||
end
|
||||
|
||||
group_tag = tag_graph[:tags_by_id][group_tag_id]
|
||||
next unless group_tag
|
||||
|
||||
key = "tag:#{ group_tag_id }"
|
||||
group =
|
||||
groups_by_key[key] ||= { key:,
|
||||
tag: group_tag_repr(group_tag),
|
||||
material_ids: [],
|
||||
count: 0 }
|
||||
group[:material_ids] << material.id
|
||||
group[:count] += 1
|
||||
end
|
||||
|
||||
groups_by_key.values.sort_by { |group| group[:tag][:name] }
|
||||
end
|
||||
|
||||
def material_index_tag_scope filters, tag_graph
|
||||
return nil unless tag_graph
|
||||
|
||||
{
|
||||
tag: group_tag_repr(tag_graph[:selected_tag]),
|
||||
include_descendants: filters[:include_descendants]
|
||||
}
|
||||
end
|
||||
|
||||
def group_tag_repr group_tag
|
||||
{ id: group_tag[:id],
|
||||
name: group_tag[:name],
|
||||
category: group_tag[:category] }
|
||||
end
|
||||
|
||||
def material_index_order_sql filters
|
||||
direction = filters[:direction] == 'asc' ? 'ASC' : 'DESC'
|
||||
sort_sql =
|
||||
|
||||
新しい課題から参照
ユーザをブロックする