class GekanatorQuestionsController < ApplicationController def index questions = GekanatorQuestion .accepted .includes(:gekanator_question_examples) .order(priority_weight: :desc, id: :asc) .to_a deprecated_tag_keys = deprecated_tag_keys_for(questions) render json: { questions: questions.filter_map { |question| json = question_json(question) next if hidden_question?(json[:condition], deprecated_tag_keys) json } } end private def question_json question condition = condition_json(question.condition).deep_symbolize_keys json = { record_id: question.id, id: question_id_for(question, condition), text: question_text_for(question, condition), kind: question.kind, condition: condition, source: question.source, priority_weight: question.priority_weight } if question.kind == 'post_similarity' || question.kind == 'tag' json[:example_answers] = example_answers_json(question) end json end def question_id_for question, condition case condition[:type] when 'tag' "tag:#{ condition[:key] }" when 'source' "source:#{ condition[:host] }" when 'original-year' "original-year:#{ condition[:year] }" when 'original-month' "original-month:#{ condition[:month] }" when 'original-month-day' "original-month-day:#{ condition[:monthDay] || condition[:month_day] }" when 'title-length-at-least' "title:length-at-least:#{ condition[:length] }" when 'title-length-greater-than' "title:length-at-least:#{ condition[:length].to_i + 1 }" when 'title-has-ascii' 'title:ascii' when 'title-contains' "title:contains:#{ condition[:text] }" when 'post-similarity' "post-similarity:#{ question.id }" else "catalog:#{ question.id }" end end def condition_json condition json = condition.deep_dup.as_json if json['type'] == 'original-month-day' && json['monthDay'].blank? json['monthDay'] = json.delete('month_day') end if json['type'] == 'title-length-greater-than' json['type'] = 'title-length-at-least' json['length'] = json['length'].to_i + 1 end json end def question_text_for question, condition return question.text unless question.kind == 'title' case condition[:type] when 'title-length-at-least' "タイトルは #{ condition[:length] } 文字以上?" when 'title-contains' "題名に「#{ condition[:text] }」が含まれる?" else question.text end end def example_answers_json question question .gekanator_question_examples .group_by(&:post_id) .transform_values { |examples| aggregate_answer(examples) } end def aggregate_answer examples examples .group_by(&:answer) .map { |answer, grouped| [answer, grouped.sum(&:weight), grouped.max_by(&:updated_at)&.updated_at] } .sort_by { |(_answer, weight, updated_at)| [-weight, -(updated_at&.to_f || 0)] } .first &.first end def deprecated_tag_keys_for questions tag_keys = questions.filter_map { |question| condition = condition_json(question.condition) next unless condition['type'] == 'tag' condition['key'].to_s.presence }.uniq return {} if tag_keys.empty? categories = [] names = [] tag_keys.each do |key| category, name = parse_tag_key(key) categories << category names << name end Tag .joins(:tag_name) .where(category: categories.uniq) .where(tag_names: { name: names.uniq }) .where.not(deprecated_at: nil) .pluck('tags.category', 'tag_names.name') .each_with_object({ }) do |(category, name), h| h["#{ category }:#{ name }"] = true end end def hidden_question? condition, deprecated_tag_keys condition[:type] == 'tag' && deprecated_tag_keys[condition[:key].to_s] end def parse_tag_key key parts = key.to_s.split(':') [parts.first.to_s, parts.drop(1).join(':')] end end