Skip to content

Commit 78282dd

Browse files
committed
Work in progress: detect gaps in post stream
1 parent 30a9d36 commit 78282dd

6 files changed

Lines changed: 155 additions & 15 deletions

File tree

app/assets/javascripts/discourse/models/post_stream.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,9 @@ Discourse.PostStream = Em.Object.extend({
522522
@method updateFromJson
523523
**/
524524
updateFromJson: function(postStreamData) {
525-
var postStream = this;
525+
var postStream = this,
526+
posts = this.get('posts');
526527

527-
var posts = this.get('posts');
528528
posts.clear();
529529
if (postStreamData) {
530530
// Load posts if present

app/serializers/post_stream_serializer_mixin.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ def self.included(klass)
55
end
66

77
def post_stream
8-
{ posts: posts,
9-
stream: object.filtered_post_ids }
8+
result = { posts: posts, stream: object.filtered_post_ids }
9+
result[:gaps] = object.gaps if object.gaps.present?
10+
result
1011
end
1112

1213
def posts
1314
return @posts if @posts.present?
1415
@posts = []
15-
@highest_number_in_posts = 0
16+
highest_number_in_posts = 0
1617
if object.posts
1718
object.posts.each_with_index do |p, idx|
18-
@highest_number_in_posts = p.post_number if p.post_number > @highest_number_in_posts
19+
highest_number_in_posts = p.post_number if p.post_number > highest_number_in_posts
1920
ps = PostSerializer.new(p, scope: scope, root: false)
2021
ps.topic_slug = object.topic.slug
2122
ps.topic_view = object

lib/gap_finder.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#
2+
# This is used for finding the gaps between a subset of elements in an array
3+
# and the original layout. We use this is Discourse to find gaps between posts.
4+
#
5+
module GapFinder
6+
7+
def self.find(subset, original)
8+
return if subset.nil? or original.nil?
9+
10+
i = j = 0
11+
gaps = {}
12+
current_gap = []
13+
14+
while
15+
e1 = subset[i]
16+
e2 = original[j]
17+
18+
if (e1 == e2)
19+
if current_gap.size > 0
20+
gaps[e1] = current_gap.dup
21+
current_gap = []
22+
end
23+
24+
i = i + 1
25+
else
26+
current_gap << e2
27+
end
28+
j = j + 1
29+
30+
break if (i >= subset.size) || (j >= original.size)
31+
end
32+
# if current_gap.size > 0
33+
# gaps[e1] = current_gap.dup
34+
# end
35+
36+
if j < original.size
37+
current_gap = gaps[original.last] = gaps[original.last] || []
38+
while j < original.size
39+
current_gap << original[j]
40+
j = j + 1
41+
end
42+
end
43+
44+
# If we have no gaps, return nil
45+
gaps.keys.size > 0 ? gaps : nil
46+
end
47+
48+
end

lib/topic_view.rb

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require_dependency 'topic_query'
33
require_dependency 'filter_best_posts'
44
require_dependency 'summarize'
5+
require_dependency 'gap_finder'
56

67
class TopicView
78

@@ -44,6 +45,15 @@ def canonical_path
4445
path
4546
end
4647

48+
def contains_gaps?
49+
@contains_gaps
50+
end
51+
52+
def gaps
53+
return unless @contains_gaps
54+
GapFinder.find(filtered_post_ids, unfiltered_posts.order(:sort_order).pluck(:id))
55+
end
56+
4757
def last_post
4858
return nil if @posts.blank?
4959
@last_post ||= @posts.last
@@ -113,9 +123,7 @@ def sort_order_for_post_number(post_number)
113123

114124
# Filter to all posts near a particular post number
115125
def filter_posts_near(post_number)
116-
117126
min_idx, max_idx = get_minmax_ids(post_number)
118-
119127
filter_posts_in_range(min_idx, max_idx)
120128
end
121129

@@ -255,14 +263,36 @@ def find_topic(topic_id)
255263
finder.first
256264
end
257265

266+
def unfiltered_posts
267+
result = @topic.posts.where(hidden: false)
268+
result = result.with_deleted if @user.try(:staff?)
269+
result
270+
end
271+
258272
def setup_filtered_posts
259-
@filtered_posts = @topic.posts.where(hidden: false)
273+
274+
# Certain filters might leave gaps between posts. If that's true, we can return a gap structure
275+
@contains_gaps = false
276+
@filtered_posts = unfiltered_posts
260277
@filtered_posts = @filtered_posts.with_deleted if @user.try(:staff?)
261-
@filtered_posts = @filtered_posts.summary if @filter == 'summary'
262-
@filtered_posts = @filtered_posts.where('posts.post_type <> ?', Post.types[:moderator_action]) if @best.present?
263-
return unless @username_filters.present?
264-
usernames = @username_filters.map{|u| u.downcase}
265-
@filtered_posts = @filtered_posts.where('post_number = 1 or user_id in (select u.id from users u where username_lower in (?))', usernames)
278+
279+
# Filters
280+
if @filter == 'summary'
281+
@filtered_posts = @filtered_posts.summary
282+
@contains_gaps = true
283+
end
284+
285+
if @best.present?
286+
@filtered_posts = @filtered_posts.where('posts.post_type <> ?', Post.types[:moderator_action])
287+
@contains_gaps = true
288+
end
289+
290+
if @username_filters.present?
291+
usernames = @username_filters.map{|u| u.downcase}
292+
@filtered_posts = @filtered_posts.where('post_number = 1 or user_id in (select u.id from users u where username_lower in (?))', usernames)
293+
@contains_gaps = true
294+
end
295+
266296
end
267297

268298
def check_and_raise_exceptions

spec/components/gap_finder_spec.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
require 'spec_helper'
2+
require 'cache'
3+
4+
describe GapFinder do
5+
6+
it 'returns no gaps for empty data' do
7+
GapFinder.find(nil, nil).should be_nil
8+
end
9+
10+
it 'returns no gaps with one element' do
11+
GapFinder.find([1], [1]).should be_nil
12+
end
13+
14+
it 'returns no gaps when all elements are present' do
15+
GapFinder.find([1,2,3], [1,2,3]).should be_nil
16+
end
17+
18+
it 'returns a single element gap' do
19+
GapFinder.find([1,3], [1,2,3]).should == {3 => [2]}
20+
end
21+
22+
it 'returns a gap when one is present' do
23+
GapFinder.find([1,2,3,6,7], [1,2,3,4,5,6,7]).should == {6 => [4, 5]}
24+
end
25+
26+
it 'returns multiple gaps' do
27+
GapFinder.find([1,5,6,7,10], [1,2,3,4,5,6,7,8,9,10]).should == {5 => [2,3,4], 10 => [8, 9]}
28+
end
29+
30+
it 'returns a gap in the beginning' do
31+
GapFinder.find([2,3,4], [1,2,3,4]).should == {2 => [1]}
32+
end
33+
34+
it 'returns a gap in the ending' do
35+
GapFinder.find([1,2,3], [1,2,3,4]).should == {4 => [4]}
36+
end
37+
38+
it 'returns a large gap in the ending' do
39+
GapFinder.find([1,2,3], [1,2,3,4,5,6]).should == {6 => [4,5,6]}
40+
end
41+
42+
end

spec/components/topic_view_spec.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@
8989

9090
end
9191

92-
9392
it "raises NotLoggedIn if the user isn't logged in and is trying to view a private message" do
9493
Topic.any_instance.expects(:private_message?).returns(true)
9594
lambda { TopicView.new(topic.id, nil) }.should raise_error(Discourse::NotLoggedIn)
@@ -233,6 +232,26 @@
233232
p6.save!
234233
end
235234

235+
describe "contains_gaps?" do
236+
it "does not contain contains_gaps with default filtering" do
237+
topic_view.contains_gaps?.should be_false
238+
end
239+
240+
it "contains contains_gaps when filtered by username" do
241+
TopicView.new(topic.id, coding_horror, username_filters: ['eviltrout']).contains_gaps?.should be_true
242+
end
243+
244+
it "contains contains_gaps when filtered by summary" do
245+
TopicView.new(topic.id, coding_horror, filter: 'summary').contains_gaps?.should be_true
246+
end
247+
248+
it "contains contains_gaps when filtered by best" do
249+
TopicView.new(topic.id, coding_horror, best: 5).contains_gaps?.should be_true
250+
end
251+
252+
end
253+
254+
236255
describe '#filter_posts_paged' do
237256
before { SiteSetting.stubs(:posts_per_page).returns(2) }
238257

0 commit comments

Comments
 (0)