-
-
Notifications
You must be signed in to change notification settings - Fork 289
Migrate User fields to MemberProfile (PR 1: Schema & Sync) #2686
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
base: main
Are you sure you want to change the base?
Migrate User fields to MemberProfile (PR 1: Schema & Sync) #2686
Conversation
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughAdds three fields to owasp.MemberProfile, migrates values from github.User via a data migration, updates the github_update_users sync to create/update MemberProfile records in batches, adjusts the admin display, and extends tests to mock and assert MemberProfile behavior. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
@arkid15r Should I raise a second PR with the tests and field removal now, or wait for this one to be reviewed first? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/tests/apps/github/management/commands/github_update_users_test.py (1)
33-82: Add assertions to verify MemberProfile synchronization behavior.The test mocks
MemberProfilebut doesn't verify that:
MemberProfile.objects.get_or_createwas called with correct arguments- Profile fields (
contributions_count,is_owasp_staff,has_public_member_page) were set correctlyMemberProfile.bulk_savewas called with the correct field listThis means the MemberProfile synchronization logic introduced in the command isn't actually being tested.
Add assertions after line 81 to verify MemberProfile behavior:
# Verify MemberProfile synchronization assert mock_member_profile.objects.get_or_create.call_count == 3 mock_member_profile.objects.get_or_create.assert_any_call(github_user_id=1) mock_member_profile.objects.get_or_create.assert_any_call(github_user_id=2) mock_member_profile.objects.get_or_create.assert_any_call(github_user_id=3) # Verify MemberProfile.bulk_save was called assert mock_member_profile.bulk_save.call_count == 2 # Verify bulk_save was called with correct fields call_args = mock_member_profile.bulk_save.call_args_list[-1] assert call_args[1]['fields'] == ('contributions_count', 'is_owasp_staff', 'has_public_member_page')Apply similar assertions to the other test methods (lines 87-124, 130-160, 166-194, 200-222, 228-261).
🧹 Nitpick comments (3)
backend/apps/owasp/admin/member_profile.py (1)
24-29: Consider addingis_owasp_stafftolist_filter.Since
is_owasp_staffis a boolean flag that distinguishes a specific user category, adding it tolist_filterwould allow admins to quickly filter and view only staff members, similar to the existing filters for board members, former staff, and GSoC mentors.list_filter = ( "is_owasp_board_member", + "is_owasp_staff", "is_former_owasp_staff", "is_gsoc_mentor", "nest_created_at", )backend/apps/owasp/models/member_profile.py (2)
74-74: Addverbose_nameandhelp_texttohas_public_member_page.For consistency with the other fields in this model (
is_owasp_staff,contributions_count, and existing fields likeis_owasp_board_member),has_public_member_pageshould includeverbose_nameandhelp_textattributes to improve clarity in the admin interface and API documentation.- has_public_member_page = models.BooleanField(default=True) + has_public_member_page = models.BooleanField( + default=True, + verbose_name="Has Public Member Page", + help_text="Indicates if the member has a public profile page on the OWASP website.", + )
83-83: Remove extra blank line for style consistency.There's an extra blank line at line 83 that's inconsistent with the code style used elsewhere in the model.
contributions_count = models.PositiveIntegerField( verbose_name="Contributions count", default=0 ) - - + def __str__(self) -> str:
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
backend/apps/github/management/commands/github_update_users.py(2 hunks)backend/apps/owasp/admin/member_profile.py(1 hunks)backend/apps/owasp/migrations/0066_memberprofile_contributions_count_and_more.py(1 hunks)backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py(1 hunks)backend/apps/owasp/models/member_profile.py(2 hunks)backend/tests/apps/github/management/commands/github_update_users_test.py(6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2223
File: backend/apps/owasp/models/entity_member.py:50-56
Timestamp: 2025-09-06T19:28:14.297Z
Learning: In the OWASP/Nest project, when migrating scraper logic to GitHub .md file parsing, the sync_leaders method uses member_name as the primary identifier for finding and updating existing EntityMember records, not member_email. This approach is chosen because names are more stable identifiers in markdown files, while emails might be added/updated over time.
🧬 Code graph analysis (5)
backend/apps/owasp/models/member_profile.py (4)
backend/apps/common/models.py (2)
BulkSaveModel(10-34)TimestampedModel(37-46)backend/apps/nest/api/internal/nodes/user.py (1)
is_owasp_staff(14-16)backend/apps/github/models/user.py (1)
bulk_save(167-169)backend/apps/github/models/commit.py (1)
bulk_save(67-75)
backend/apps/github/management/commands/github_update_users.py (2)
backend/apps/owasp/models/member_profile.py (2)
MemberProfile(12-92)bulk_save(90-92)backend/apps/github/models/user.py (2)
User(28-216)bulk_save(167-169)
backend/tests/apps/github/management/commands/github_update_users_test.py (1)
backend/tests/apps/github/management/commands/github_sync_user_test.py (1)
mock_member_profile(84-86)
backend/apps/owasp/migrations/0066_memberprofile_contributions_count_and_more.py (1)
backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py (1)
Migration(15-24)
backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py (2)
backend/apps/owasp/models/member_profile.py (1)
MemberProfile(12-92)backend/apps/owasp/migrations/0066_memberprofile_contributions_count_and_more.py (1)
Migration(6-28)
🪛 Ruff (0.14.5)
backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py
5-5: Missing docstring in public function
(D103)
5-5: Unused function argument: schema_editor
(ARG001)
🔇 Additional comments (4)
backend/apps/github/management/commands/github_update_users.py (2)
58-64: LGTM! MemberProfile synchronization logic is correct.The implementation properly creates or retrieves a MemberProfile for each user and synchronizes the migrated fields. The conditional assignment of
github_useron line 59-60 is correct becauseget_or_create(github_user_id=user.id)only sets the foreign key ID when creating, not the actual relationship object.
68-75: Verification confirms code is working as intended—no issues found.The
BulkSaveModel.bulk_saveimplementation inbackend/apps/common/models.py(line 34) does callobjects.clear()after saving. BothUser.bulk_saveandMemberProfile.bulk_savedelegate to this method, ensuring lists are cleared properly after each batch. The original review's assessment is correct.backend/apps/owasp/migrations/0066_memberprofile_contributions_count_and_more.py (1)
13-27: LGTM! Schema migration is correct.The field definitions are consistent with the model and the migration dependencies are properly set.
backend/apps/owasp/models/member_profile.py (1)
89-92: LGTM! Bulk save method correctly delegates to BulkSaveModel.The static method properly delegates to
BulkSaveModel.bulk_savewith the correct model class and parameters, consistent with the pattern used in other models likeUserandCommit.
backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py
Outdated
Show resolved
Hide resolved
backend/tests/apps/github/management/commands/github_update_users_test.py
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/tests/apps/github/management/commands/github_update_users_test.py (1)
42-81: Add mock attributes and verify MemberProfile operations.The test has two gaps:
- Mock users lack
is_owasp_staffandhas_public_member_pageattributes that the command accesses (see backend/apps/github/management/commands/github_update_users.py lines 54-56).- The test doesn't verify that
MemberProfile.bulk_saveis called with the correct arguments.Apply this diff:
- mock_user1 = MagicMock(id=1, title="User 1", contributions_count=0) - mock_user2 = MagicMock(id=2, title="User 2", contributions_count=0) - mock_user3 = MagicMock(id=3, title="User 3", contributions_count=0) + mock_user1 = MagicMock(id=1, title="User 1", contributions_count=0, + is_owasp_staff=False, has_public_member_page=True) + mock_user2 = MagicMock(id=2, title="User 2", contributions_count=0, + is_owasp_staff=True, has_public_member_page=False) + mock_user3 = MagicMock(id=3, title="User 3", contributions_count=0, + is_owasp_staff=False, has_public_member_page=True)And add this assertion after line 81:
assert mock_member_profile.bulk_save.call_count == 2 mock_member_profile.bulk_save.assert_called_with( mock_member_profile.objects.get_or_create.return_value[0], fields=("contributions_count", "is_owasp_staff", "has_public_member_page"), )
♻️ Duplicate comments (4)
backend/tests/apps/github/management/commands/github_update_users_test.py (4)
92-124: Add mock attributes and verify MemberProfile operations.Same issues as the previous test: mock users need
is_owasp_staffandhas_public_member_pageattributes, and the test should verifyMemberProfile.bulk_saveis called.Apply similar changes as suggested for test_handle_with_default_offset.
135-160: Add mock attributes and verify MemberProfile operations.Same issues: mock users need
is_owasp_staffandhas_public_member_pageattributes, and verifyMemberProfile.bulk_saveis called.
171-194: Add mock attributes and verify MemberProfile operations.Same issues: mock user needs
is_owasp_staffandhas_public_member_pageattributes, and verifyMemberProfile.bulk_saveis called.
233-261: Add mock attributes and verify MemberProfile operations.Same issues: mock users need
is_owasp_staffandhas_public_member_pageattributes, and verifyMemberProfile.bulk_saveis called.
🧹 Nitpick comments (2)
backend/apps/owasp/models/member_profile.py (1)
80-82: Consider adding help_text for clarity.While the field name is self-explanatory, adding
help_textwould improve consistency with other fields in the model and clarify what counts as a "contribution."contributions_count = models.PositiveIntegerField( - verbose_name="Contributions count", default=0 + verbose_name="Contributions count", + default=0, + help_text="Total number of contributions to OWASP repositories", )backend/tests/apps/github/management/commands/github_update_users_test.py (1)
263-299: Good test, but enhance bulk_save verification.This test properly addresses the previous review comment about testing the
created=Truebranch. However, the bulk_save assertion should verify the fields parameter to ensure the correct fields are being updated.Enhance the assertion at line 299:
- mock_member_profile.bulk_save.assert_called_once() + mock_member_profile.bulk_save.assert_called_once() + call_args = mock_member_profile.bulk_save.call_args + assert call_args[1]["fields"] == ( + "contributions_count", + "is_owasp_staff", + "has_public_member_page", + )
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
backend/apps/owasp/admin/member_profile.py(2 hunks)backend/apps/owasp/migrations/0066_memberprofile_contributions_count_and_more.py(1 hunks)backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py(1 hunks)backend/apps/owasp/models/member_profile.py(2 hunks)backend/tests/apps/github/management/commands/github_update_users_test.py(7 hunks)backend/tests/apps/owasp/admin/member_profile_test.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py
- backend/apps/owasp/admin/member_profile.py
- backend/apps/owasp/migrations/0066_memberprofile_contributions_count_and_more.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2223
File: backend/apps/owasp/models/entity_member.py:50-56
Timestamp: 2025-09-06T19:28:14.297Z
Learning: In the OWASP/Nest project, when migrating scraper logic to GitHub .md file parsing, the sync_leaders method uses member_name as the primary identifier for finding and updating existing EntityMember records, not member_email. This approach is chosen because names are more stable identifiers in markdown files, while emails might be added/updated over time.
🧬 Code graph analysis (2)
backend/apps/owasp/models/member_profile.py (3)
backend/apps/common/models.py (1)
BulkSaveModel(10-34)backend/apps/github/models/user.py (1)
bulk_save(167-169)backend/apps/github/models/commit.py (1)
bulk_save(67-75)
backend/tests/apps/github/management/commands/github_update_users_test.py (2)
backend/apps/github/management/commands/github_update_users.py (2)
Command(16-81)handle(28-81)backend/apps/owasp/models/member_profile.py (1)
bulk_save(89-91)
🔇 Additional comments (3)
backend/tests/apps/owasp/admin/member_profile_test.py (1)
15-17: LGTM!The test correctly reflects the addition of the three migrated fields to the admin list display.
backend/apps/owasp/models/member_profile.py (2)
8-8: LGTM!The import supports the bulk_save delegation pattern, consistent with how User and Commit models handle bulk operations.
88-91: LGTM!The bulk_save method correctly follows the delegation pattern used by User and Commit models, enabling efficient batch operations.
backend/tests/apps/github/management/commands/github_update_users_test.py
Show resolved
Hide resolved
|
There was a problem hiding this 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/owasp/models/member_profile.py (1)
74-86: Consider adding help_text forcontributions_countfor admin clarityThe boolean fields here all provide
verbose_nameandhelp_text, which makes the admin clearer.contributions_countonly has averbose_name; adding a briefhelp_text(e.g., what counts as a “contribution”) would keep this model’s metadata consistent and more self-documenting.backend/tests/apps/github/management/commands/github_update_users_test.py (1)
265-301: New test nicely covers thecreated=TrueMemberProfile branch
test_handle_member_profile_createdverifies the previously untested path where a profile is newly created, including linkinggithub_userand syncingcontributions_count,is_owasp_staff, andhas_public_member_page. As a small optional improvement, you could also patchbuiltins.printhere (for consistency with the other tests) and/or assert the args passed tomock_member_profile.bulk_saveif you want even stricter coverage of the batch-save call.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
backend/apps/owasp/models/member_profile.py(2 hunks)backend/tests/apps/github/management/commands/github_update_users_test.py(7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2223
File: backend/apps/owasp/models/entity_member.py:50-56
Timestamp: 2025-09-06T19:28:14.297Z
Learning: In the OWASP/Nest project, when migrating scraper logic to GitHub .md file parsing, the sync_leaders method uses member_name as the primary identifier for finding and updating existing EntityMember records, not member_email. This approach is chosen because names are more stable identifiers in markdown files, while emails might be added/updated over time.
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: backend/apps/mentorship/graphql/mutations/module.py:28-29
Timestamp: 2025-07-16T13:49:58.648Z
Learning: In the OWASP Nest mentorship system, mentors can be created with only github_user initially (without nest_user) when assigned to modules. This allows mentors to be assigned before they've signed into the system. When these users eventually sign in, the nest_user is automatically linked to the existing github_user, creating a complete mentor profile. This design provides flexibility in mentor assignment workflows.
🧬 Code graph analysis (2)
backend/apps/owasp/models/member_profile.py (2)
backend/apps/common/models.py (1)
BulkSaveModel(10-34)backend/apps/github/models/user.py (1)
bulk_save(167-169)
backend/tests/apps/github/management/commands/github_update_users_test.py (3)
backend/apps/owasp/models/member_profile.py (1)
bulk_save(93-95)backend/apps/github/models/repository_contributor.py (1)
bulk_save(77-79)backend/apps/github/management/commands/github_update_users.py (2)
Command(16-81)handle(28-81)
🔇 Additional comments (3)
backend/apps/owasp/models/member_profile.py (1)
8-8: Bulk-save wrapper matches existing pattern and looks correctImporting
BulkSaveModeland delegating viaMemberProfile.bulk_save()is consistent with theUser.bulk_save/RepositoryContributor.bulk_savehelpers and should integrate cleanly with existing batch-save infrastructure. No issues from a correctness or API-usage standpoint.Also applies to: 92-95
backend/tests/apps/github/management/commands/github_update_users_test.py (2)
33-41: MemberProfile mocking correctly wires tests to the new sync pathPatching
MemberProfileat the command module path and standardizingobjects.get_or_create.return_valueacross these tests keeps the existing scenarios working while exercising the new profile-sync behavior. This looks consistent and correct; no functional issues spotted.Also applies to: 83-92, 126-135, 162-171, 196-205, 226-235
221-224: Good addition: assert MemberProfile.bulk_save is called on empty inputThe new assertions mirror the existing
User.bulk_savechecks and ensure the command always callsMemberProfile.bulk_save, even when there are no users. This tightens coverage of the new persistence path without changing behavior.
ahmedxgouda
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job. Left a few comments 👇 👇
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This command was originally made for updating the GitHub.User contributions count. So, I suggest to move this command to Owasp app and only update the contributions count of MemberProfile.
| user.contributions_count = user_contributions.get(user.id, 0) | ||
| users.append(user) | ||
|
|
||
| profile, created = MemberProfile.objects.get_or_create(github_user_id=user.id) | ||
| if created: | ||
| profile.github_user = user | ||
| profile.contributions_count = user.contributions_count | ||
| profile.is_owasp_staff = user.is_owasp_staff | ||
| profile.has_public_member_page = user.has_public_member_page |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only need to update the contribution_count of the MemberProfile. No other fields. Also, you should update the field directly from the calculated value not by accessing the contribution count field of the GitHub.User. You will remove this field from GitHub.User in another PR, right?
| MemberProfile.bulk_save( | ||
| profiles, | ||
| fields=( | ||
| "contributions_count", | ||
| "is_owasp_staff", | ||
| "has_public_member_page", | ||
| ), | ||
| ) | ||
|
|
||
| User.bulk_save(users, fields=("contributions_count",)) | ||
| MemberProfile.bulk_save( | ||
| profiles, | ||
| fields=("contributions_count", "is_owasp_staff", "has_public_member_page"), | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, just update the contributions_count
|
@kart-u Also, I suggest to consider your branch naming approach. The branch name is too long. Although, this comment is not related to your changes and is not required for approval. Just a suggestion :) |





Proposed change
Resolves #2623
This is 1st PR
Migrated the fields
has_public_member_page,is_owasp_staff, andcontributions_countfrombackend/apps/github/models/user.pytobackend/apps/owasp/models/member_profile.py.Added these fields, created the migration
backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.pyfor the initial data sync, and updated the code paths to write toMemberProfilealong withUserfor subsequent updates inbackend/apps/github/management/commands/github_update_users.py.After running

make github-update-users, the values are being updated successfully.Checklist
make check-testlocally; all checks and tests passed.