cs_comments_service/presenters/thread.rb

93 строки
3.7 KiB
Ruby

require_relative 'thread_utils'
require 'new_relic/agent/method_tracer'
class ThreadPresenter
def self.factory(thread, user)
# use when working with one thread at a time. fetches extended /
# derived attributes from the db and explicitly initializes an instance.
course_id = thread.course_id
thread_key = thread._id.to_s
is_read, unread_count = ThreadUtils.get_read_states([thread], user, course_id).fetch(thread_key, [false, thread.comment_count])
is_endorsed = ThreadUtils.get_endorsed([thread]).fetch(thread_key, false)
self.new thread, user, is_read, unread_count, is_endorsed
end
def initialize(thread, user, is_read, unread_count, is_endorsed)
# generally not intended for direct use. instantiated by self.factory or
# by thread list presenters.
@thread = thread
@user = user
@is_read = is_read
@unread_count = unread_count
@is_endorsed = is_endorsed
end
def to_hash with_responses=false, resp_skip=0, resp_limit=nil
raise ArgumentError unless resp_skip >= 0
raise ArgumentError unless resp_limit.nil? or resp_limit >= 1
h = @thread.to_hash
h["read"] = @is_read
h["unread_comments_count"] = @unread_count
h["endorsed"] = @is_endorsed || false
if with_responses
unless resp_skip == 0 && resp_limit.nil?
# need to find responses first, set the window accordingly, then fetch the comments
# bypass mongoid/odm, to get just the response ids we need as directly as possible
responses = Content.collection.find({"comment_thread_id" => @thread._id, "parent_id" => {"$exists" => false}})
responses = responses.sort({"sk" => 1})
all_response_ids = responses.select({"_id" => 1}).to_a.map{|doc| doc["_id"] }
response_ids = (resp_limit.nil? ? all_response_ids[resp_skip..-1] : (all_response_ids[resp_skip,resp_limit])) || []
# now use the odm to fetch the desired responses and their comments
content = Content.where({"parent_id" => {"$in" => response_ids}}).to_a + Content.where({"_id" => {"$in" => response_ids}}).to_a
content.sort!{|a,b| a.sk <=> b.sk }
response_total = all_response_ids.length
else
content = Content.where({"comment_thread_id" => @thread._id}).order_by({"sk"=> 1})
response_total = content.to_a.select{|d| d.depth == 0 }.length
end
h = merge_comments_recursive(h, content)
h["resp_skip"] = resp_skip
h["resp_limit"] = resp_limit
h["resp_total"] = response_total
end
h
end
def merge_comments_recursive thread_hash, comments
thread_id = thread_hash["id"]
root = thread_hash = thread_hash.merge("children" => [])
ancestry = [thread_hash]
# weave the fetched comments into a single hierarchical doc
comments.each do | comment |
thread_hash = comment.to_hash.merge("children" => [])
parent_id = comment.parent_id || thread_id
found_parent = false
while ancestry.length > 0 do
if parent_id == ancestry.last["id"] then
# found the children collection to which this comment belongs
ancestry.last["children"] << thread_hash
ancestry << thread_hash
found_parent = true
break
else
# try again with one level back in the ancestry til we find the parent
ancestry.pop
next
end
end
if not found_parent
# if we arrive here, it means a parent_id somewhere in the result set
# is pointing to an invalid place. reset the ancestry search path.
ancestry = [root]
end
end
ancestry.first
end
include ::NewRelic::Agent::MethodTracer
add_method_tracer :to_hash
add_method_tracer :merge_comments_recursive
end