Skip to content

Conversation

@kart-u
Copy link
Contributor

@kart-u kart-u commented Nov 18, 2025

Proposed change

Resolves #2623
This is 1st PR

Migrated the fields has_public_member_page, is_owasp_staff, and contributions_count from backend/apps/github/models/user.py to backend/apps/owasp/models/member_profile.py.

Added these fields, created the migration backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py for the initial data sync, and updated the code paths to write to MemberProfile along with User for subsequent updates in backend/apps/github/management/commands/github_update_users.py.

After running make github-update-users, the values are being updated successfully.
confirm2

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 18, 2025

Summary by CodeRabbit

  • New Features

    • Added member profile fields to track staff status, public member page availability, and contribution count
    • Enabled automatic synchronization of GitHub user data to corresponding member profiles
  • Tests

    • Enhanced test coverage for member profile synchronization

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds 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

Cohort / File(s) Summary
Model & Helper
backend/apps/owasp/models/member_profile.py
Adds has_public_member_page, is_owasp_staff, contributions_count fields and a bulk_save static method delegating to BulkSaveModel.
Schema Migration
backend/apps/owasp/migrations/0066_memberprofile_contributions_count_and_more.py
Migration that adds the three new fields to MemberProfile.
Data Migration
backend/apps/owasp/migrations/0067_memberprofile_backward_compatibility.py
Data migration that iterates github.User rows, get_or_create's matching MemberProfile, copies the three fields, and performs batched bulk_update.
Sync Command
backend/apps/github/management/commands/github_update_users.py
Extends user sync to get_or_create MemberProfile per active User, mirror contributions/flags onto the profile, accumulate profiles, and perform batched MemberProfile.bulk_save alongside User bulk saves.
Admin UI
backend/apps/owasp/admin/member_profile.py
Updates MemberProfileAdmin.list_display and the "Contribution Information" fieldset to include is_owasp_staff, has_public_member_page, and contributions_count.
Tests
backend/tests/apps/github/management/commands/github_update_users_test.py, backend/tests/apps/owasp/admin/member_profile_test.py
Adds mock_member_profile patches, adapts test signatures, adds a test for MemberProfile creation path, and updates admin tests for new list_display ordering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Pay attention to data migration 0067: batching, get_or_create behavior, and correct field copying.
  • Verify github_update_users bulk-save ordering and that both User and MemberProfile batches persist intended state.
  • Check tests correctly mock MemberProfile and cover created vs existing-profile flows.

Possibly related PRs

Suggested reviewers

  • arkid15r
  • kasya

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately summarizes the main objective: migrating user fields to MemberProfile with schema creation and synchronization logic as the primary changes.
Description check ✅ Passed Description is directly related to the changeset, clearly explaining the field migration from User to MemberProfile and the implementation steps undertaken.
Linked Issues check ✅ Passed PR successfully implements the Phase 1 objectives from issue #2623: adds three fields to MemberProfile, creates backward compatibility migration, updates synchronization logic, and includes expanded test coverage.
Out of Scope Changes check ✅ Passed All changes are scoped to Phase 1 requirements: schema additions, data migration, synchronization updates, admin interface changes, and comprehensive test updates without removing original User fields.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@kart-u
Copy link
Contributor Author

kart-u commented Nov 18, 2025

@arkid15r Should I raise a second PR with the tests and field removal now, or wait for this one to be reviewed first?

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: 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 MemberProfile but doesn't verify that:

  1. MemberProfile.objects.get_or_create was called with correct arguments
  2. Profile fields (contributions_count, is_owasp_staff, has_public_member_page) were set correctly
  3. MemberProfile.bulk_save was called with the correct field list

This 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 adding is_owasp_staff to list_filter.

Since is_owasp_staff is a boolean flag that distinguishes a specific user category, adding it to list_filter would 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: Add verbose_name and help_text to has_public_member_page.

For consistency with the other fields in this model (is_owasp_staff, contributions_count, and existing fields like is_owasp_board_member), has_public_member_page should include verbose_name and help_text attributes 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1fd0879 and 7d33b9a.

📒 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_user on line 59-60 is correct because get_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_save implementation in backend/apps/common/models.py (line 34) does call objects.clear() after saving. Both User.bulk_save and MemberProfile.bulk_save delegate 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_save with the correct model class and parameters, consistent with the pattern used in other models like User and Commit.

Copy link
Collaborator

@ahmedxgouda ahmedxgouda left a comment

Choose a reason for hiding this comment

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

Please don't request a review unless the checks and tests are passing.
Image
Also, you need to address the bot's suggestions.

@ahmedxgouda ahmedxgouda self-assigned this Nov 19, 2025
@kart-u
Copy link
Contributor Author

kart-u commented Nov 19, 2025

Please don't request a review unless the checks and tests are passing. Image Also, you need to address the bot's suggestions.

Sorry, this was a mistake on my part. I only ran make test and forgot to run make check, which caused the lint/format issues. I’ve corrected them now, and I also addressed the bot’s recommendation

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: 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:

  1. Mock users lack is_owasp_staff and has_public_member_page attributes that the command accesses (see backend/apps/github/management/commands/github_update_users.py lines 54-56).
  2. The test doesn't verify that MemberProfile.bulk_save is 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_staff and has_public_member_page attributes, and the test should verify MemberProfile.bulk_save is 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_staff and has_public_member_page attributes, and verify MemberProfile.bulk_save is called.


171-194: Add mock attributes and verify MemberProfile operations.

Same issues: mock user needs is_owasp_staff and has_public_member_page attributes, and verify MemberProfile.bulk_save is called.


233-261: Add mock attributes and verify MemberProfile operations.

Same issues: mock users need is_owasp_staff and has_public_member_page attributes, and verify MemberProfile.bulk_save is 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_text would 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=True branch. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7d33b9a and e78b731.

📒 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.

@kart-u kart-u requested a review from ahmedxgouda November 19, 2025 19:30
@sonarqubecloud
Copy link

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/owasp/models/member_profile.py (1)

74-86: Consider adding help_text for contributions_count for admin clarity

The boolean fields here all provide verbose_name and help_text, which makes the admin clearer. contributions_count only has a verbose_name; adding a brief help_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 the created=True MemberProfile branch

test_handle_member_profile_created verifies the previously untested path where a profile is newly created, including linking github_user and syncing contributions_count, is_owasp_staff, and has_public_member_page. As a small optional improvement, you could also patch builtins.print here (for consistency with the other tests) and/or assert the args passed to mock_member_profile.bulk_save if you want even stricter coverage of the batch-save call.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e78b731 and e6fb0e2.

📒 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 correct

Importing BulkSaveModel and delegating via MemberProfile.bulk_save() is consistent with the User.bulk_save / RepositoryContributor.bulk_save helpers 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 path

Patching MemberProfile at the command module path and standardizing objects.get_or_create.return_value across 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 input

The new assertions mirror the existing User.bulk_save checks and ensure the command always calls MemberProfile.bulk_save, even when there are no users. This tightens coverage of the new persistence path without changing behavior.

Copy link
Collaborator

@ahmedxgouda ahmedxgouda left a 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 👇 👇

Copy link
Collaborator

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.

Comment on lines 55 to +63
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
Copy link
Collaborator

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?

Comment on lines +68 to +81
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"),
)
Copy link
Collaborator

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

@ahmedxgouda
Copy link
Collaborator

@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 :)

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.

Migrate non-GitHub fields from github.User to owasp.MemberProfile

2 participants