feat: generate initials avatar as default profile image#38638
feat: generate initials avatar as default profile image#38638SantiagoSuHe wants to merge 1 commit into
Conversation
|
Thanks for the pull request, @SantiagoSuHe! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. 🔘 Update the status of your PRYour PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate. Where can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
When a user has not uploaded a profile photo, generate a personalized JPEG avatar with their initials on a colored circle background instead of returning a generic static placeholder image. Images are generated on first request and cached in storage using a content-addressable key based on username + name. A name change automatically produces a new cache key and a fresh image on the next request. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8657283 to
0a196f5
Compare
|
|
||
|
|
||
| _AVATAR_COLORS = [ | ||
| '#1565C0', '#2E7D32', '#6A1B9A', '#C62828', '#E65100', |
There was a problem hiding this comment.
update to reference paragon / brand color options - may want to use the paragon label colors here (PR is still a draft so TBD)
Summary
Open edX shows a generic static placeholder when a user has not uploaded a profile photo. This PR replaces that placeholder with a personalized avatar: a colored circle with the user's initials, similar to what Google and other platforms do.
A proposal for this feature was posted in the Open edX community forum for visibility: https://discuss.openedx.org/t/profile-avatar-modernization/19046
How it works
Images are generated lazily: nothing happens at account creation time. The first time someone requests the profile image URLs for a user (for example, when the user logs in and the
edx-user-infocookie is built), the avatar is generated using Pillow and saved to storage. Every subsequent request finds the file already in storage and returns the URL directly, with no regeneration cost.The avatar is personalized in two ways: the initials come from the user's full name (first letter of first and last name), and the background color is deterministically derived from the username, so the same user always gets the same color. If the user has no name set, the first letter of the username is used as a fallback.
When a user updates their name, the cache key changes automatically (it is based on
md5(username + name)), so a new avatar is generated on the next request reflecting the updated name.Why the backend?
The initial approach considered implementing this in the Profile MFE as a React component. However, profile images are displayed in many parts of the platform, not just the profile page. Implementing the initials avatar only in the frontend would mean replicating the logic in every MFE and leaving most of the platform unaffected.
The correct solution is to generate a real JPEG image in the backend and serve it as the default profile image URL. This way, every consumer of
get_profile_image_urls_for_user()gets the initials avatar automatically without any additional changes.Where profile images are displayed in Open edX
frontend-app-header) -- readsuser_image_urlsfrom theedx-user-infocookie set at loginfrontend-app-discussions) -- same cookiefrontend-app-learner-dashboard) -- same cookiefrontend-app-profile) -- not affected by this PR (see below)frontend-app-account) -- not affected by this PR (see below)Known limitations -- separate PRs required
Two MFEs ignore the URL returned by the backend when
has_imageisfalseand render their own SVG fallback instead:frontend-app-profile): theProfileAvatarcomponent checksisDefaultand renders a hardcoded SVG regardless of thesrcURL from the APIfrontend-app-account): same patternThese require separate PRs in their respective repositories to use the backend URL unconditionally.
Note: the Profile MFE fix has already been implemented in
openedx/frontend-app-profileas a companion PR.Changes
openedx/core/djangoapps/profile_images/images.pygenerate_initials_image(username, name)which generates a JPEG avatar for each configured size using Pillowmd5(username + name), generated only once per username/name combinationopenedx/core/djangoapps/user_api/accounts/image_helpers.py_get_default_profile_image_urls()to callgenerate_initials_image()instead of returning static placeholder URLsUserProfile.has_profile_imagesemantics are unchanged -- it remainsFalseuntil the user explicitly uploads a photoTesting
Added tests for
_get_initials(),_get_avatar_color(), andgenerate_initials_image()covering edge cases (empty name, None, whitespace, name change cache invalidation, storage caching behavior).Updated existing tests in
test_image_helpers.pyto mockgenerate_initials_imagefor the default image path.Dependencies
No new dependencies. Pillow is already a required dependency of
openedx-platform.