Skip to content

Fix display names and add avatar color randomization for anonymous accounts#591

Open
klnyzzz33 wants to merge 4 commits intomasterfrom
fix/anon_user_name
Open

Fix display names and add avatar color randomization for anonymous accounts#591
klnyzzz33 wants to merge 4 commits intomasterfrom
fix/anon_user_name

Conversation

@klnyzzz33
Copy link
Copy Markdown
Contributor

@klnyzzz33 klnyzzz33 commented Apr 30, 2026

The PR contains two fixes:

  1. Anonymous account display names are now derived from the generated usernames. See related issue explained in Anonymous accounts don't have a display name web#1095. This PR contains the proposed fix.
  2. During normal registration, accounts get randomized avatar colors. Anonymous account creation was missing the color randomization, now it's consistent.

@github-actions
Copy link
Copy Markdown

ArchLens - No architecturally relevant changes to the existing views

@klnyzzz33
Copy link
Copy Markdown
Contributor Author

Also, #590 is now part of this PR as I didn't really see the point in splitting them up.

@mircealungu
Copy link
Copy Markdown
Member

mircealungu commented Apr 30, 2026

Small note on scope: the change in add_anon_user from UserAvatar(new_user.id, animal, None, None) to using UserAvatar.random_colors() is technically unrelated to the display-name fix described in #1095 — it changes anonymous-avatar rendering as a side effect.

It's defensible (anonymous avatars now match the regular-user shape, which is arguably part of "give anonymous accounts a real identity"), and I'm not asking to split it out. But could you add a line to the PR description noting the avatar-color change so a future blame-spelunker doesn't wonder why colors started appearing in a display-name PR?

Also opened a follow-up for the backfill of accounts created during the #589#591 window: #593

@klnyzzz33 klnyzzz33 changed the title Anonymous account display names are now derived from the generated usernames Fix anonymous account display names and add anonymous account avatar color randomization Apr 30, 2026
@klnyzzz33 klnyzzz33 changed the title Fix anonymous account display names and add anonymous account avatar color randomization Fix display names and add avatar color randomization for anonymous accounts Apr 30, 2026
@klnyzzz33
Copy link
Copy Markdown
Contributor Author

I added the extra changes to the PR description and title as requested.

Working on the backfill script now

@klnyzzz33
Copy link
Copy Markdown
Contributor Author

klnyzzz33 commented Apr 30, 2026

#!/usr/bin/env python
import re

from sqlalchemy import or_

from zeeguu.core.model.user import User


def derive_display_name(username: str) -> str | None:
    """
    Derive display name from a generated username with the given format:
    quirky_owl42 -> Quirky Owl
    - split on first underscore
    - strip trailing digits from the second segment
    - capitalize each part
    """
    if not username or "_" not in username:
        return None

    first_part, second_part = username.split("_", 1)
    second_part = re.sub(r"\d+$", "", second_part)

    if not first_part or not second_part:
        return None

    return f"{first_part.capitalize()} {second_part.capitalize()}"


def backfill_display_names(dry_run=True):
    """
    Backfill display name for users that don't have one, and which were created
    between BACKFILL_START and BACKFILL_END.

    Args:
        dry_run: If True, don't commit changes, just show what would be done
    """
    users_to_update: list[User] = (
        User.query
        .filter(or_(User.name.is_(None), User.name == User.email))
        .all()
    )

    total = len(users_to_update)
    print(f"Found {total} users without display name")

    updated_count = 0
    skipped_count = 0
    for user in users_to_update:
        derived_display_name = derive_display_name(user.username)

        if not derived_display_name:
            skipped_count += 1
            print(f"Skipping user_id={user.id} username={user.username}")
            continue

        updated_count += 1
        print(f"user_id={user.id} username={user.username} name={user.name} -> name={derived_display_name}")

        if not dry_run:
            user.name = derived_display_name
            db.session.add(user)

    if not dry_run:
        db.session.commit()

    print(f"\n{'DRY RUN - ' if dry_run else ''}Results:")
    print(f"  Users updated: {updated_count}")


if __name__ == "__main__":
    from zeeguu.api.app import create_app
    from zeeguu.core.model import db

    app = create_app()
    app.app_context().push()

    import argparse

    parser = argparse.ArgumentParser(description="Backfill display name on users")
    parser.add_argument("--apply", action="store_true", help="Actually apply changes (default is dry run)")
    args = parser.parse_args()

    dry_run = not args.apply

    if dry_run:
        print("Running in DRY RUN mode. Use --apply to actually update the database.\n")
    else:
        print("Running in APPLY mode. Changes will be committed to the database.\n")

    backfill_display_names(dry_run=dry_run)

@klnyzzz33
Copy link
Copy Markdown
Contributor Author

klnyzzz33 commented Apr 30, 2026

I added the backfill script here @mircealungu.

One important note: in #593 you mentioned that the script probably needs to filter for anonymous users only, as they are the only affected users. However, users who upgraded their anonymous account to a full account since yesterday also still have name=NONE currently on prod. So we have to do the backfill for both anonymous and normal users.

Also, some older accounts that were upgraded from anonymous accounts might still have their emails set as their display names (before it was fixed in #579). This is potentially an issue, since on the Friends tab you can look up users by their exact display name, which would be their emails here. So I added this case to the query as well.

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.

2 participants