Skip to content

Comment feed improvements#4566

Open
hlbmtc wants to merge 5 commits intomainfrom
feat/4510-comment-feed-improvements
Open

Comment feed improvements#4566
hlbmtc wants to merge 5 commits intomainfrom
feat/4510-comment-feed-improvements

Conversation

@hlbmtc
Copy link
Copy Markdown
Contributor

@hlbmtc hlbmtc commented Apr 1, 2026

Summary by CodeRabbit

  • New Features
    • Comments now include a persisted key-factor votes impact score surfaced in comment data.
    • Feed UI: new sort option to order comments by key-factor impact.
    • Feed API/UI: optional filter to exclude comments from bots-only projects.

hlbmtc added 2 commits April 1, 2026 18:49
- Denormalized key_factor_votes_score on the comments table
- Added `exclude_bots_only_project`
-
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 408ca2ab-20b4-4fdc-827b-7b3aa4939a8f

📥 Commits

Reviewing files that changed from the base of the PR and between 269f809 and 59892b6.

📒 Files selected for processing (2)
  • comments/migrations/0026_comment_key_factor_votes_score.py
  • comments/services/common.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • comments/migrations/0026_comment_key_factor_votes_score.py

📝 Walkthrough

Walkthrough

Adds a denormalized, indexed key_factor_votes_score FloatField to Comment with backfill and an instance update method; triggers comment score updates on key-factor vote changes; exposes and propagates the score through serializers, API params, types, frontend UI/sorting; and indexes Project.bot_leaderboard_status.

Changes

Cohort / File(s) Summary
Comment model & migration
comments/migrations/0026_comment_key_factor_votes_score.py, comments/models.py
Adds Comment.key_factor_votes_score: FloatField(db_index=True, default=0, editable=False), backfill migration that computes per-comment sum of per-key-factor mean(abs(vote.score)) with batched bulk_update, and update_key_factor_votes_score() method (uses numpy.mean).
Vote -> comment update flow
comments/services/key_factors/common.py
After recalculating KeyFactor.votes_score, code now calls key_factor.comment.update_key_factor_votes_score() to persist updated per-comment aggregate.
Feed & aggregation logic
comments/services/feed.py, comments/services/common.py
get_comments_feed gains exclude_bots_only_project filter excluding projects with BotLeaderboardStatus.BOTS_ONLY; weekly/top-comments annotation renamed to annotated_key_factor_votes_score and usages updated.
API, serializers & types
comments/serializers/common.py, front_end/src/services/api/comments/comments.shared.ts, front_end/src/types/comment.ts
Serializers expose key_factor_votes_score; API params add optional exclude_bots_only_project?: boolean; front-end comment type adds optional key_factor_votes_score?: number.
Frontend UI & sorting
front_end/src/components/comment_feed/comment_feed_content.tsx, front_end/src/components/comment_feed/comment_feed_card.tsx
Adds -key_factor_votes_score sort option and label, includes exclude_bots_only_project: true when search empty, and passes comment.key_factor_votes_score to CommentCard.
Project field indexing
projects/migrations/0023_alter_project_bot_leaderboard_status.py, projects/models.py
Adds db_index=True and explicit choices/default/helptext to Project.bot_leaderboard_status via migration and model change.

Sequence Diagrams

sequenceDiagram
    participant User
    participant Frontend
    participant API
    participant KeyFactorVoteService as KeyFactorVoteService
    participant KeyFactor
    participant Comment
    participant Database

    User->>Frontend: Click vote on key factor
    Frontend->>API: POST /key-factor-vote
    API->>KeyFactorVoteService: key_factor_vote(...)
    KeyFactorVoteService->>Database: create/update Vote
    KeyFactorVoteService->>KeyFactor: recalc votes_score
    KeyFactor->>Database: save(update_fields=["votes_score"])
    KeyFactorVoteService->>Comment: comment.update_key_factor_votes_score()
    Comment->>Database: prefetch key_factors and votes
    Comment->>Comment: compute sum(mean(abs(v.score)) per key_factor)
    Comment->>Database: save(update_fields=["key_factor_votes_score"])
    KeyFactorVoteService-->>API: result
    API-->>Frontend: 200 OK
    Frontend-->>User: UI updates
Loading
sequenceDiagram
    participant User
    participant Frontend
    participant CommentsAPI
    participant FeedService as get_comments_feed
    participant Database

    User->>Frontend: Open comments feed (empty search)
    Frontend->>CommentsAPI: GET /comments?exclude_bots=...&exclude_bots_only_project=true&sort=-key_factor_votes_score
    CommentsAPI->>FeedService: get_comments_feed(...)
    FeedService->>Database: filter out projects with BotLeaderboardStatus.BOTS_ONLY
    FeedService->>Database: annotate/return comments with annotated_key_factor_votes_score
    Database-->>FeedService: Comment list
    FeedService-->>CommentsAPI: serialized comments (include key_factor_votes_score)
    CommentsAPI-->>Frontend: Response
    Frontend->>Frontend: Render sorted feed by key_factor_votes_score
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • elisescu
  • lsabor
  • ncarazon

Poem

🐰 I hopped through votes and sums today,
Counting means in a careful way.
Key factors whispered, numbers soared,
I stored the score and then I roared —
Sorted, indexed, ready to play! 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Comment feed improvements' is vague and generic, using non-descriptive language that doesn't convey specific information about the substantive changes in the changeset. Consider a more specific title like 'Add key factor votes score denormalization and filtering' or 'Improve comment feed with votes scoring and bot project filtering' to better reflect the main changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/4510-comment-feed-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hlbmtc hlbmtc marked this pull request as ready for review April 1, 2026 16:50
@hlbmtc
Copy link
Copy Markdown
Contributor Author

hlbmtc commented Apr 1, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
comments/serializers/common.py (1)

85-86: Minor: Missing trailing comma for consistency.

The tuple entries in fields typically have trailing commas for cleaner diffs. Consider adding one after "key_factor_votes_score".

✏️ Suggested fix
             "is_pinned",
-            "key_factor_votes_score"
+            "key_factor_votes_score",
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comments/serializers/common.py` around lines 85 - 86, The tuple of serializer
fields ending with the string "key_factor_votes_score" in the fields definition
should include a trailing comma for consistency and cleaner diffs; update the
fields tuple (the variable/attribute named fields in the serializer/class) to
add a comma after "key_factor_votes_score".
comments/models.py (1)

3-3: Consider using Python's built-in for mean calculation instead of NumPy.

Using numpy solely for computing a simple mean adds a heavyweight dependency for minimal benefit. Python's statistics.mean or a manual calculation would be more appropriate here, especially since the list is small (votes per key factor).

♻️ Proposed refactor to remove numpy dependency

Remove the numpy import at line 3:

-import numpy as np

Update the method:

 def update_key_factor_votes_score(self):
     score = 0.0

     for kf in self.key_factors.prefetch_related("votes").all():
         votes = [abs(v.score) for v in kf.votes.all()]
         if votes:
-            score += np.mean(votes)
+            score += sum(votes) / len(votes)

     self.key_factor_votes_score = score
     self.save(update_fields=["key_factor_votes_score"])

     return score

Also applies to: 185-196

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comments/models.py` at line 3, The module currently imports NumPy solely for
computing averages (import numpy as np) and uses np.mean in the votes-averaging
logic; remove the heavy dependency by deleting the "import numpy as np" and
replace all uses of np.mean (in the votes-per-key-factor routine) with Python's
built-in calculation (either statistics.mean after adding "import statistics" or
a simple sum(votes)/len(votes) with a guard for empty lists). Update the
function(s) that compute the mean accordingly and ensure any related imports are
adjusted and edge cases (empty lists) are handled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@comments/migrations/0026_comment_key_factor_votes_score.py`:
- Around line 9-25: The migration keeps all matching Comment objects and related
votes in memory via the prefetch and the to_update list; instead process in
smaller chunks: iterate the Comment queryset in batches (e.g., using .iterator()
or by slicing with a limit/offset or by primary-key range), for each batch
compute score for each comment using comment.key_factors.all() / kf.votes.all()
as in the diff, collect updates into to_update for that batch, call
Comment.objects.bulk_update(to_update, ["key_factor_votes_score"],
batch_size=500) immediately for that batch, then clear to_update before
processing the next batch; reference the Comment model, key_factors, votes,
key_factor_votes_score, to_update and bulk_update when applying this change.

In `@front_end/src/components/comment_feed/comment_feed_content.tsx`:
- Around line 160-161: The current logic forces exclude_bots_only_project when
debouncedSearch is empty, which hides bots-only-project comments even if the
user set the visible filter to "Include bots"; update the filter construction so
exclude_bots_only_project is only applied when excludeBots is true (e.g. change
the spread from ...(!debouncedSearch && { exclude_bots_only_project: true }) to
guard with excludeBots) or alternatively expose exclude_bots_only_project as its
own UI state and use that flag instead; key symbols to update: debouncedSearch,
exclude_bots_only_project, and excludeBots within the object that builds the
query/filters.

---

Nitpick comments:
In `@comments/models.py`:
- Line 3: The module currently imports NumPy solely for computing averages
(import numpy as np) and uses np.mean in the votes-averaging logic; remove the
heavy dependency by deleting the "import numpy as np" and replace all uses of
np.mean (in the votes-per-key-factor routine) with Python's built-in calculation
(either statistics.mean after adding "import statistics" or a simple
sum(votes)/len(votes) with a guard for empty lists). Update the function(s) that
compute the mean accordingly and ensure any related imports are adjusted and
edge cases (empty lists) are handled.

In `@comments/serializers/common.py`:
- Around line 85-86: The tuple of serializer fields ending with the string
"key_factor_votes_score" in the fields definition should include a trailing
comma for consistency and cleaner diffs; update the fields tuple (the
variable/attribute named fields in the serializer/class) to add a comma after
"key_factor_votes_score".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e1f0e716-a48b-478b-a23f-2304b03a4697

📥 Commits

Reviewing files that changed from the base of the PR and between bed5f2d and 93a19bf.

📒 Files selected for processing (12)
  • comments/migrations/0026_comment_key_factor_votes_score.py
  • comments/models.py
  • comments/serializers/common.py
  • comments/services/common.py
  • comments/services/feed.py
  • comments/services/key_factors/common.py
  • front_end/src/components/comment_feed/comment_feed_card.tsx
  • front_end/src/components/comment_feed/comment_feed_content.tsx
  • front_end/src/services/api/comments/comments.shared.ts
  • front_end/src/types/comment.ts
  • projects/migrations/0023_alter_project_bot_leaderboard_status.py
  • projects/models.py

Comment on lines +9 to +25
comments = (
Comment.objects.filter(key_factors__votes__isnull=False)
.distinct()
.prefetch_related("key_factors__votes")
)
to_update = []
for comment in comments:
score = 0.0
for kf in comment.key_factors.all():
votes = [abs(v.score) for v in kf.votes.all()]
if votes:
score += np.mean(votes)
if score:
comment.key_factor_votes_score = score
to_update.append(comment)

Comment.objects.bulk_update(to_update, ["key_factor_votes_score"], batch_size=500)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This still holds the full backfill set in memory.

batch_size=500 only chunks the SQL update; to_update is still populated once, after prefetching every matching comment and related vote. On production-sized tables that is a risky migration shape. Please process comments in smaller chunks and flush bulk_update per chunk, or move the aggregation into SQL.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comments/migrations/0026_comment_key_factor_votes_score.py` around lines 9 -
25, The migration keeps all matching Comment objects and related votes in memory
via the prefetch and the to_update list; instead process in smaller chunks:
iterate the Comment queryset in batches (e.g., using .iterator() or by slicing
with a limit/offset or by primary-key range), for each batch compute score for
each comment using comment.key_factors.all() / kf.votes.all() as in the diff,
collect updates into to_update for that batch, call
Comment.objects.bulk_update(to_update, ["key_factor_votes_score"],
batch_size=500) immediately for that batch, then clear to_update before
processing the next batch; reference the Comment model, key_factors, votes,
key_factor_votes_score, to_update and bulk_update when applying this change.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-4566-feat-4510-comment-feed-improve-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:feat-4510-comment-feed-improvements-59892b6
🗄️ PostgreSQL NeonDB branch preview/pr-4566-feat-4510-comment-feed-improve
Redis Fly Redis mtc-redis-pr-4566-feat-4510-comment-feed-improve

Details

  • Commit: 05be830c9d077bcd3cd36d09a64f713cb500c420
  • Branch: feat/4510-comment-feed-improvements
  • Fly App: metaculus-pr-4566-feat-4510-comment-feed-improve

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant