Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend user model with total contributions field. #1209

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

ahmedxgouda
Copy link
Collaborator

@ahmedxgouda ahmedxgouda commented Mar 30, 2025

Resolves #1192

  • Backend implementation
  • Frontend implementation
  • Backend tests
  • Frontend tests

Copy link
Contributor

coderabbitai bot commented Mar 30, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a contributions count statistic for users. This new data is now visible on the user profile and community pages with a dedicated icon.
    • Enhanced data retrieval so that the contributions information consistently appears across the application.
  • Tests

    • Updated tests to ensure that the contributions count displays correctly across user interfaces.

Walkthrough

This pull request introduces a new contributions_count field across both backend and frontend components. On the backend, the User model now includes this field with a default value and updates in the data sync and indexing mixin to use the pre-calculated value rather than aggregating on the fly. The GraphQL layer exposes the field via the UserNode and its resolver. On the frontend, TypeScript types, queries, and UI components are updated to display the contributions count along with corresponding tests and mock data adjustments.

Changes

File(s) Change Summary
backend/.../graphql/nodes/user.py, backend/.../graphql/nodes/user_test.py Added contributions_count field and resolver to the GraphQL UserNode; updated test expectations to include the new field.
backend/.../models/user.py, backend/.../models/mixins/user.py, backend/.../migrations/0021_user_contributions_count.py, backend/.../models/user_test.py Introduced contributions_count field in the User model (default 0) with sync logic to update its value, modified the indexing mixin to return this field directly, and added a migration and test adjustments.
frontend/src/types/user.ts, frontend/.../queries/userQueries.ts Extended TypeScript types to include contributions_count/contributionsCount and updated the GraphQL query to fetch this field.
frontend/__tests__/e2e/pages/UserDetails.spec.ts, frontend/__tests__/unit/data/mockUserDetails.ts, frontend/__tests__/unit/pages/UserDetails.test.tsx, frontend/.../page.tsx Updated the User Details UI by adding a new icon and assertion for displaying contributions count; mock data was enhanced to supply a contributions count.

Assessment against linked issues

Objective Addressed Explanation
Extend User Model with contributions_count field and populate it during data sync [#1192]
Update indexing mixin to utilize the pre-calculated contributions_count field [#1192]
Modify user details UI and GraphQL query to incorporate contributions_count [#1192]

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0bb56d3 and 9441988.

📒 Files selected for processing (1)
  • backend/apps/github/migrations/0021_user_contributions_count.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/github/migrations/0021_user_contributions_count.py
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run frontend e2e tests

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 0

🧹 Nitpick comments (2)
backend/apps/github/models/user.py (2)

85-96: Consider moving contribution calculation to a separate method.

The implementation correctly calculates the total contributions by summing the contributions_count from all repository contributions for the user. However, this logic could be better encapsulated in a separate method to improve code organization and maintainability.

Consider refactoring the code like this:

- if does_exist:
-     from apps.github.models.repository_contributor import RepositoryContributor
- 
-     contributions_count = (
-         RepositoryContributor.objects.by_humans()
-         .filter(user=user)
-         .aggregate(total_contributions=Sum("contributions_count"))[
-             "total_contributions"
-         ]
-         or 0
-     )
-     user.contributions_count = contributions_count
+ if does_exist:
+     user.update_contributions_count()

And add this method to the User class:

def update_contributions_count(self):
    """Update the user's total contributions count."""
    from apps.github.models.repository_contributor import RepositoryContributor
    
    self.contributions_count = (
        RepositoryContributor.objects.by_humans()
        .filter(user=self)
        .aggregate(total_contributions=Sum("contributions_count"))[
            "total_contributions"
        ]
        or 0
    )

86-87: Consider moving import to the module level.

Importing RepositoryContributor inside the method prevents potential circular imports but could be moved to the top of the file for better code organization, as it's a standard practice in Python.

from django.db import models
from django.db.models import Sum

from apps.common.models import TimestampedModel
from apps.github.constants import GITHUB_GHOST_USER_LOGIN, OWASP_FOUNDATION_LOGIN
from apps.github.models.common import GenericUserModel, NodeModel
from apps.github.models.mixins.user import UserIndexMixin
from apps.github.models.organization import Organization
+from apps.github.models.repository_contributor import RepositoryContributor
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90e8cd5 and 68effca.

📒 Files selected for processing (7)
  • backend/apps/github/graphql/nodes/user.py (3 hunks)
  • backend/apps/github/migrations/0020_user_contributions_count.py (1 hunks)
  • backend/apps/github/models/mixins/user.py (1 hunks)
  • backend/apps/github/models/user.py (3 hunks)
  • frontend/src/api/queries/userQueries.ts (1 hunks)
  • frontend/src/pages/UserDetails.tsx (2 hunks)
  • frontend/src/types/user.ts (3 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
frontend/src/pages/UserDetails.tsx (1)
frontend/src/types/user.ts (1)
  • user (3-18)
backend/apps/github/models/user.py (2)
backend/apps/github/models/repository_contributor.py (2)
  • from_github (43-53)
  • RepositoryContributor (12-78)
backend/apps/github/models/managers/repository_contributor.py (2)
  • by_humans (12-16)
  • by_humans (36-38)
backend/apps/github/graphql/nodes/user.py (1)
backend/apps/github/models/mixins/user.py (1)
  • idx_contributions_count (107-109)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run frontend e2e tests
🔇 Additional comments (15)
frontend/src/api/queries/userQueries.ts (1)

48-48: Good addition to the user query!

Adding the contributionsCount field to the GraphQL query allows the frontend to display this important statistic for users, enhancing the user profile data shown in the UI.

backend/apps/github/migrations/0020_user_contributions_count.py (1)

11-16: Migration looks good for adding the contributions count field.

The migration correctly adds an IntegerField with a default value of 0 and appropriate verbose name. This matches the PR's objective to extend the user model with a total contributions field.

frontend/src/pages/UserDetails.tsx (2)

9-9: Good import of the code merge icon for contributions.

The faCodeMerge icon is semantically appropriate for representing user contributions.


218-222: Well-implemented contributions count display.

The implementation follows the existing pattern for user stats, correctly using millify for formatting large numbers and pluralize for grammatical correctness. This enhances the user profile with valuable contribution information.

backend/apps/github/models/mixins/user.py (1)

107-109:

✅ Verification successful

Performance improvement by directly returning stored value.

This change simplifies the code and improves performance by directly returning the stored contributions_count field instead of calculating it on-the-fly with database queries. However, ensure that there's a mechanism elsewhere in the codebase to properly maintain and update this field when a user's contributions change.

To verify that the contributions_count field is properly updated elsewhere in the codebase, run:


🏁 Script executed:

#!/bin/bash
# Search for code that updates the contributions_count field
rg -A 2 -B 2 "contributions_count\s*=" --type py

Length of output: 3398


Update Confirmed: Contributions Count Field Maintenance Verified

The code now directly returns the stored contributions_count value, providing a clear performance benefit. Our investigation confirms that the field is properly maintained elsewhere in the codebase. Specifically:

  • In backend/apps/github/models/user.py, the contributions_count field is updated (e.g., via the assignment user.contributions_count = contributions_count followed by user.save()).
  • Tests in backend/tests/apps/github/models/repository_contributor_test.py verify that the contributions count is correctly maintained.

No further changes are required.

backend/apps/github/models/user.py (4)

4-4: Good addition of Sum import for aggregation.

The import of Sum from django.db.models is appropriate for calculating the total contributions across repositories.


26-26: LGTM: New contributions_count field added properly.

The contributions_count field is correctly defined as an IntegerField with a default value of 0, which is appropriate for tracking user contributions.


73-76: Good explanation through comments.

The comments clearly explain why you're creating the does_exist flag, which helps with understanding the implementation approach.


79-79: Properly setting the existence flag.

Setting does_exist to True when a user is found in the database is correct and aligns with the implementation design.

frontend/src/types/user.ts (3)

17-17: LGTM: Properly added contributions_count to user type.

The contributions_count property is correctly added to the user type with the appropriate type of number.


61-61: LGTM: Properly added contributions_count to User type.

The contributions_count property is correctly added to the User type with the appropriate type of number.


82-82: LGTM: Properly added contributionsCount to UserDetailsProps interface.

The contributionsCount property is correctly added to the UserDetailsProps interface with the appropriate type of number. The naming follows the camelCase convention used for other properties in this interface.

backend/apps/github/graphql/nodes/user.py (3)

47-47: LGTM: Properly added contributions_count field to UserNode.

The contributions_count field is correctly defined as a graphene.Int() type, which is appropriate for exposing the contributions count in the GraphQL schema.


63-63: LGTM: Properly added contributions_count to fields tuple.

The contributions_count field is correctly added to the fields tuple in the Meta class, which makes it available for GraphQL queries.


94-96: LGTM: Proper implementation of resolve_contributions_count.

The resolver method correctly returns self.idx_contributions_count, which (based on the relevant code snippet) directly returns the contributions_count value from the user model.

Copy link
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: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68effca and 798dbf5.

📒 Files selected for processing (5)
  • backend/tests/apps/github/graphql/nodes/user_test.py (1 hunks)
  • backend/tests/apps/github/models/user_test.py (1 hunks)
  • frontend/__tests__/e2e/pages/UserDetails.spec.ts (1 hunks)
  • frontend/__tests__/unit/data/mockUserDetails.ts (1 hunks)
  • frontend/__tests__/unit/pages/UserDetails.test.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run backend tests
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (5)
frontend/__tests__/unit/data/mockUserDetails.ts (1)

15-15: LGTM: Properly added the new contributions count field to the mock data.

The addition of contributionsCount: 100 to the mock user data correctly aligns with the new field being added to the user model. This change ensures that tests using this mock data will properly verify the display of contribution counts in the UI.

backend/tests/apps/github/graphql/nodes/user_test.py (1)

37-37: LGTM: Test properly updated to include the new field.

Adding "contributions_count" to the expected fields ensures that the test verifies this new field is properly exposed through the GraphQL API. This is an appropriate and necessary test update.

frontend/__tests__/e2e/pages/UserDetails.spec.ts (1)

30-30: LGTM: E2E test correctly validates the new contributions count UI element.

The added assertion ensures that the text '100 Contributions' is visible on the page, validating that the UI properly displays the user's contribution count. This is a good addition to the test coverage.

frontend/__tests__/unit/pages/UserDetails.test.tsx (1)

176-178: LGTM: Unit test properly verifies the contributions count display.

The added assertions correctly validate that the contributions count is displayed in the statistics section of the user details page. This complements the E2E test and ensures proper test coverage for the new feature.

backend/tests/apps/github/models/user_test.py (1)

35-35: Good addition of RepositoryContributor.objects mock.

This correctly adds a mock for the RepositoryContributor objects manager, which aligns with the PR's objective of adding contribution tracking functionality to the User model.

@patch("apps.github.models.user.User.objects.get")
@patch("apps.github.models.user.User.save")
def test_update_data(self, mock_save, mock_get):
def test_update_data(self, mock_save, mock_get, mock_repo_contributor):
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Test should utilize the mock_repo_contributor parameter.

While you've correctly added the parameter corresponding to the new mock, the test doesn't actually use this mock anywhere in its implementation. For proper test coverage of the new contribution counting functionality, you should:

  1. Set up appropriate return values for the mock
  2. Add assertions that verify the contributions_count is updated correctly
def test_update_data(self, mock_save, mock_get, mock_repo_contributor):
    gh_user_mock = Mock()
    gh_user_mock.raw_data = {"node_id": "12345"}
    gh_user_mock.bio = "Bio"
    gh_user_mock.hireable = True
    gh_user_mock.twitter_username = "twitter"

    mock_user = Mock(spec=User)
    mock_user.node_id = "12345"
    mock_get.return_value = mock_user

+   # Set up mock for repository contributor
+   mock_repo_contributor.filter.return_value.count.return_value = 5

    updated_user = User.update_data(gh_user_mock, save=True)

    mock_get.assert_called_once_with(node_id="12345")
    assert updated_user.node_id == "12345"
+   # Verify the contributions count is updated
+   mock_repo_contributor.filter.assert_called_once_with(user=mock_user)
+   assert updated_user.contributions_count == 5
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_update_data(self, mock_save, mock_get, mock_repo_contributor):
def test_update_data(self, mock_save, mock_get, mock_repo_contributor):
gh_user_mock = Mock()
gh_user_mock.raw_data = {"node_id": "12345"}
gh_user_mock.bio = "Bio"
gh_user_mock.hireable = True
gh_user_mock.twitter_username = "twitter"
mock_user = Mock(spec=User)
mock_user.node_id = "12345"
mock_get.return_value = mock_user
# Set up mock for repository contributor
mock_repo_contributor.filter.return_value.count.return_value = 5
updated_user = User.update_data(gh_user_mock, save=True)
mock_get.assert_called_once_with(node_id="12345")
assert updated_user.node_id == "12345"
# Verify the contributions count is updated
mock_repo_contributor.filter.assert_called_once_with(user=mock_user)
assert updated_user.contributions_count == 5

@ahmedxgouda ahmedxgouda marked this pull request as ready for review March 30, 2025 07:50
@ahmedxgouda
Copy link
Collaborator Author

Hello @arkid15r . It's been a week :). Could you please review it?

@ahmedxgouda
Copy link
Collaborator Author

Hello @arkid15r . It's been a week :). Could you please review it?

I couldn't merge the migrations because of the permission error as I told you.

Copy link
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: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69b042d and ac783a3.

📒 Files selected for processing (5)
  • frontend/__tests__/unit/data/mockUserDetails.ts (1 hunks)
  • frontend/__tests__/unit/pages/UserDetails.test.tsx (1 hunks)
  • frontend/src/app/community/users/[userKey]/page.tsx (2 hunks)
  • frontend/src/server/queries/userQueries.ts (1 hunks)
  • frontend/src/types/user.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/types/user.ts
  • frontend/tests/unit/pages/UserDetails.test.tsx
  • frontend/tests/unit/data/mockUserDetails.ts
🧰 Additional context used
🧬 Code Definitions (1)
frontend/src/app/community/users/[userKey]/page.tsx (1)
frontend/src/types/user.ts (1)
  • user (3-18)
🪛 GitHub Actions: Run CI/CD
frontend/src/app/community/users/[userKey]/page.tsx

[error] 203-203: Unstaged changes detected. Run make check and use git add to address it.

🔇 Additional comments (3)
frontend/src/server/queries/userQueries.ts (1)

49-49: Addition of contributionsCount field looks good!

The field is properly placed within the user query section and will enable the frontend to display the user's total contribution count. This aligns with the PR objective of extending the user model with a total contributions field.

frontend/src/app/community/users/[userKey]/page.tsx (2)

9-9: Import of faCodeMerge icon is appropriate.

The icon choice is well-suited for representing user contributions in the UI.


195-207: Implementation of the contributions count in userStats is well done.

The implementation correctly follows the established pattern of other user statistics, displaying the count with an appropriate icon and label. The optional chaining with fallback to 0 ensures the UI handles cases where the data might be undefined.

🧰 Tools
🪛 GitHub Actions: Run CI/CD

[error] 203-203: Unstaged changes detected. Run make check and use git add to address it.

Copy link
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: 1

🧹 Nitpick comments (2)
backend/apps/github/migrations/0021_user_contributions_count.py (2)

9-9: Consider using double quotes for consistency.

The static analysis tool suggests using double quotes instead of single quotes to maintain consistency with the codebase style guide.

-        ('github', '0020_repositorycontributor_user_contrib_idx'),
+        ("github", "0020_repositorycontributor_user_contrib_idx"),
🧰 Tools
🪛 Ruff (0.8.2)

9-9: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


9-9: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


14-16: Consider using double quotes for string literals.

For consistency with the codebase style guide, consider using double quotes for these string literals.

-            model_name='user',
-            name='contributions_count',
-            field=models.IntegerField(default=0, verbose_name='Contributions count'),
+            model_name="user",
+            name="contributions_count",
+            field=models.IntegerField(default=0, verbose_name="Contributions count"),
🧰 Tools
🪛 Ruff (0.8.2)

14-14: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


15-15: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


16-16: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac783a3 and 0bb56d3.

📒 Files selected for processing (2)
  • backend/apps/github/migrations/0021_user_contributions_count.py (1 hunks)
  • frontend/src/app/community/users/[userKey]/page.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/app/community/users/[userKey]/page.tsx
🧰 Additional context used
🪛 Ruff (0.8.2)
backend/apps/github/migrations/0021_user_contributions_count.py

9-9: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


9-9: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


14-14: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


15-15: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


16-16: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (1)
backend/apps/github/migrations/0021_user_contributions_count.py (1)

1-18: LGTM! Migration adds contributions_count field to User model.

The migration adds an IntegerField with default value 0 to track user contributions, which aligns with the PR objective of extending the user model with a total contributions field.

🧰 Tools
🪛 Ruff (0.8.2)

9-9: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


9-9: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


14-14: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


15-15: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


16-16: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)

@ahmedxgouda
Copy link
Collaborator Author

Hello @arkid15r . It's been a week :). Could you please review it?

I couldn't merge the migrations because of the permission error as I told you.

Problem addressed!

Copy link

sonarqubecloud bot commented Apr 9, 2025

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

Successfully merging this pull request may close these issues.

Extend User Model with contributions_count Field
1 participant