Skip to content

Commit 60799c2

Browse files
authored
Fix message counts in stats (#73)
Fixes message counts to include threaded messages in the following views: - https://smallbets.com/rooms/1/stats - https://smallbets.com/stats - https://smallbets.com/stats/rooms If I've overlooked a statistic or if you notice any issues with these changes, please let me know and I'll look into it. Fixes #69
1 parent cccc8e3 commit 60799c2

File tree

3 files changed

+70
-38
lines changed

3 files changed

+70
-38
lines changed

app/controllers/rooms/stats_controller.rb

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def show
99
access_count: @room.memberships.joins(:user).where(users: { suspended_at: nil, active: true }).count,
1010
visibility_count: @room.visible_memberships.joins(:user).where(users: { suspended_at: nil, active: true }).count,
1111
starred_count: @room.memberships.where(involvement: "everything").joins(:user).where(users: { suspended_at: nil, active: true }).count,
12-
messages_count: @room.messages.count,
12+
messages_count: all_messages_count_for_room(@room),
1313
last_message_at: @room.messages.where(active: true).order(created_at: :desc).first&.created_at
1414
}
1515

@@ -37,11 +37,21 @@ def set_room
3737
@room = Current.user.rooms.find(params[:room_id])
3838
end
3939

40+
# Count messages in a room including messages in threads
41+
def all_messages_count_for_room(room)
42+
Message.joins("LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'")
43+
.joins("LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id")
44+
.where("messages.room_id = :room_id OR parent_messages.room_id = :room_id", room_id: room.id)
45+
.active.distinct.count
46+
end
47+
4048
# Get top talkers for a specific room
4149
def top_talkers_for_room(room, limit = 10)
42-
User.select("users.id, users.name, COUNT(messages.id) AS message_count, COALESCE(users.membership_started_at, users.created_at) as joined_at")
50+
User.select("users.id, users.name, COUNT(DISTINCT messages.id) AS message_count, COALESCE(users.membership_started_at, users.created_at) as joined_at")
4351
.joins("INNER JOIN messages ON messages.creator_id = users.id AND messages.active = true")
44-
.where("messages.room_id = ?", room.id)
52+
.joins("LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'")
53+
.joins("LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id")
54+
.where("messages.room_id = :room_id OR parent_messages.room_id = :room_id", room_id: room.id)
4555
.where("users.active = true AND users.suspended_at IS NULL")
4656
.group("users.id, users.name, users.membership_started_at, users.created_at")
4757
.order("message_count DESC, joined_at ASC, users.id ASC")
@@ -50,9 +60,11 @@ def top_talkers_for_room(room, limit = 10)
5060

5161
# Get user stats for a specific room
5262
def user_stats_for_room(user_id, room)
53-
User.select("users.id, users.name, COUNT(messages.id) AS message_count")
63+
User.select("users.id, users.name, COUNT(DISTINCT messages.id) AS message_count")
5464
.joins("INNER JOIN messages ON messages.creator_id = users.id AND messages.active = true")
55-
.where("messages.room_id = ?", room.id)
65+
.joins("LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'")
66+
.joins("LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id")
67+
.where("messages.room_id = :room_id OR parent_messages.room_id = :room_id", room_id: room.id)
5668
.where("users.id = ?", user_id)
5769
.group("users.id")
5870
.first
@@ -68,28 +80,32 @@ def calculate_user_rank_in_room(user_id, room)
6880

6981
# Count users with more messages
7082
users_with_more_messages = User.joins("INNER JOIN messages ON messages.creator_id = users.id AND messages.active = true")
71-
.where("messages.room_id = ?", room.id)
72-
.where("users.active = true AND users.suspended_at IS NULL")
73-
.group("users.id")
74-
.having("COUNT(messages.id) > ?", stats.message_count.to_i)
75-
.count.size
83+
.joins("LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'")
84+
.joins("LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id")
85+
.where("messages.room_id = :room_id OR parent_messages.room_id = :room_id", room_id: room.id)
86+
.where("users.active = true AND users.suspended_at IS NULL")
87+
.group("users.id")
88+
.having("COUNT(DISTINCT messages.id) > ?", stats.message_count.to_i)
89+
.count.size
7690

77-
# Count users with same number of messages but earlier join date
91+
# Count users with the same number of messages but earlier join date
7892
if stats.message_count.to_i > 0
7993
users_with_same_messages_earlier_join = User.joins("INNER JOIN messages ON messages.creator_id = users.id AND messages.active = true")
80-
.where("messages.room_id = ?", room.id)
81-
.where("users.active = true AND users.suspended_at IS NULL")
82-
.group("users.id")
83-
.having("COUNT(messages.id) = ?", stats.message_count.to_i)
84-
.where("COALESCE(users.membership_started_at, users.created_at) < ?",
85-
user.membership_started_at || user.created_at)
86-
.count.size
94+
.joins("LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'")
95+
.joins("LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id")
96+
.where("messages.room_id = :room_id OR parent_messages.room_id = :room_id", room_id: room.id)
97+
.where("users.active = true AND users.suspended_at IS NULL")
98+
.group("users.id")
99+
.having("COUNT(DISTINCT messages.id) = ?", stats.message_count.to_i)
100+
.where("COALESCE(users.membership_started_at, users.created_at) < ?",
101+
user.membership_started_at || user.created_at)
102+
.count.size
87103
else
88104
# For users with 0 messages, count users with earlier join date
89105
users_with_same_messages_earlier_join = User.where("COALESCE(membership_started_at, created_at) < ?",
90-
user.membership_started_at || user.created_at)
91-
.where("active = true AND users.suspended_at IS NULL")
92-
.count
106+
user.membership_started_at || user.created_at)
107+
.where("active = true AND users.suspended_at IS NULL")
108+
.count
93109
end
94110

95111
users_with_more_messages + users_with_same_messages_earlier_join + 1

app/controllers/stats_controller.rb

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -324,25 +324,34 @@ def all
324324
def rooms
325325
@page_title = "Room Stats"
326326

327+
rooms_message_count_subquery = <<~SQL
328+
(
329+
SELECT COUNT(DISTINCT messages.id) FROM messages
330+
LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'
331+
LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id
332+
WHERE messages.active = true AND (messages.room_id = rooms.id OR parent_messages.room_id = rooms.id)
333+
) AS message_count
334+
SQL
335+
327336
# Get all open rooms ordered by message count
328-
@rooms = Room.select("rooms.*, COUNT(messages.id) AS message_count")
329-
.joins(:messages)
330-
.where("messages.active = true")
331-
.where(type: "Rooms::Open")
332-
.group("rooms.id")
333-
.order("message_count DESC, rooms.created_at ASC")
334-
.includes(:creator) # Include creator to avoid N+1 queries
337+
@rooms = Room.select("rooms.*", rooms_message_count_subquery)
338+
.where(type: "Rooms::Open")
339+
.group("rooms.id")
340+
.order("message_count DESC, rooms.created_at ASC")
341+
.includes(:creator) # Include creator to avoid N+1 queries
335342

336343
# For each room, find the top talker
337344
@top_talkers = {}
338345
@rooms.each do |room|
339-
top_talker = User.select("users.id, users.name, COUNT(messages.id) AS message_count")
340-
.joins("INNER JOIN messages ON messages.creator_id = users.id AND messages.active = true")
341-
.where("messages.room_id = ?", room.id)
342-
.where("users.active = true AND users.suspended_at IS NULL")
343-
.group("users.id, users.name")
344-
.order("message_count DESC")
345-
.first
346+
top_talker = User.select("users.id, users.name, COUNT(DISTINCT messages.id) AS message_count")
347+
.joins("INNER JOIN messages ON messages.creator_id = users.id AND messages.active = true")
348+
.joins("LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'")
349+
.joins("LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id")
350+
.where("messages.room_id = :room_id OR parent_messages.room_id = :room_id", room_id: room.id)
351+
.where("users.active = true AND users.suspended_at IS NULL")
352+
.group("users.id, users.name")
353+
.order("message_count DESC")
354+
.first
346355

347356
@top_talkers[room.id] = top_talker if top_talker
348357
end

app/services/stats_service.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,16 @@ def self.total_counts
342342

343343
# Get top rooms by message count
344344
def self.top_rooms_by_message_count(limit = 10)
345-
Room.select("rooms.*, COUNT(messages.id) AS message_count")
346-
.joins(:messages)
347-
.where("messages.active = true")
345+
rooms_message_count_subquery = <<~SQL
346+
(
347+
SELECT COUNT(DISTINCT messages.id) FROM messages
348+
LEFT JOIN rooms threads ON messages.room_id = threads.id AND threads.type = 'Rooms::Thread'
349+
LEFT JOIN messages parent_messages ON threads.parent_message_id = parent_messages.id
350+
WHERE messages.active = true AND (messages.room_id = rooms.id OR parent_messages.room_id = rooms.id)
351+
) AS message_count
352+
SQL
353+
354+
Room.select("rooms.*", rooms_message_count_subquery)
348355
.where(type: "Rooms::Open") # Only include open rooms
349356
.group("rooms.id")
350357
.order("message_count DESC, rooms.created_at ASC")

0 commit comments

Comments
 (0)