93 строки
3.7 KiB
Ruby
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
|