Skip to content

Conversation

@karunarapolu
Copy link

@karunarapolu karunarapolu commented Dec 28, 2025

Fixes issue #2575
Design Achievement Badges for Team Overview - Award and Display Team & User Milestones

Overview

This PR implements a comprehensive achievement badge system for teams on the Team Overview Dashboard. Teams and individual team members can now earn badges for milestones such as "Top Contributor," "Early Bird," and other achievement categories. Badges are displayed prominently on the Team Overview Dashboard and user profiles.

Features Implemented

1. Team Badge Model & Data Structure

  • New Model: TeamBadge - Associates badges with teams and optionally with individual users within teams
  • Scope Support: Badges can be scoped to:
    • team - Team-level achievements
    • topuser_team - Individual user achievements within a team context
  • Award Types:
    • Automatic: Awarded based on predefined criteria (contributions, closed issues, rankings)
    • Manual: Awarded by team admins/leaders
  • Metadata: Stores reason for award, timestamp, and who awarded it

2. Badge Award System

Implemented in feed_signals.py

3. Admin Interface & Management

  • Team Badge Admin: Full CRUD operations for badge management

4. UI/UX Components

Team Overview Dashboard (team_overview.html)

  • Team-level achievements with icon and title
  • Team member badges under their profile pictures

User Profile

  • Badge showcase section displaying:
    • Team-specific achievements highlighted

5. Automatic Badge Evaluation

Implemented via evaluate_team_badges() and evaluate_user_team_badges():

  • Runs periodically via Django signals (post_save on Contribution model)
  • Evaluates all automatic badges against team metrics

6. Testing

Test Coverage

Comprehensive test suite in website/tests/test_team_badges.py:

7. Json file of the badges in website>fixtures>team_badges.json

8. Icons for badges in website>static>img>badges>team_badges

Badges should be manually added in production database inside Badges model for the team badges to work!!

Screenshot 2025-12-28 144124 Screenshot 2025-12-28 150352 Screenshot 2025-12-28 150502

Summary by CodeRabbit

  • New Features

    • Team badges system for awarding and tracking team achievements
    • Team member badge assignment for team admins
    • Reminder settings form for user preferences
  • UI Updates

    • Redesigned badge listings and cards with clearer icons/placeholders
    • Expanded team overview with member management, modals, and badge sections
    • Navigation updated to surface User and Team Badges
  • Tests

    • Added tests covering team badge model, assignment, and views
  • Migrations

    • Database schema extended to support team-scoped badges and team badge records

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

@github-actions
Copy link
Contributor

👋 Hi @karunarapolu!

This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:

  • The PR author
  • DonnieBLT
  • coderabbitai
  • copilot

Once a valid peer review is submitted, this check will pass automatically. Thank you!

@github-actions github-actions bot added migrations PR contains database migration files needs-peer-review PR needs peer review labels Dec 28, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 28, 2025

Walkthrough

Adds team-scoped badges: new TeamBadge model and Badge.scope, migrations, admin and fixtures; automatic evaluation via signals and helper functions; views, URLs and templates for team badges and assignment; tests, forms, and UI updates; several non-functional formatting/comment tweaks across codebase.

Changes

Cohort / File(s) Summary
Schema & Models
website/models.py, website/migrations/0261_badge_scope_teambadge.py, website/migrations/0262_alter_badge_scope.py, website/migrations/0263_alter_badge_scope.py, website/migrations/0264_teambadge_user_alter_userbadge_awarded_by.py
Adds TeamBadge model, adds/updates Badge.scope choices (user, team, topuser_team), and adjusts related_name on UserBadge.awarded_by; introduces sequential schema migrations.
Signals & Badge Evaluation
website/feed_signals.py
New helpers for team metrics and contributor queries; award/revoke functions; evaluators evaluate_team_badges and evaluate_user_team_badges; post_save signal handlers for Contribution and Issue to trigger evaluations.
Admin
website/admin.py
Registers TeamBadge with TeamBadgeAdmin (list_display, list_filter, search_fields, date_hierarchy).
Views & Routing
blt/urls.py, website/views/core.py, website/views/teams.py, website/views/user.py
Exposes assign_team_member_badge view and registers team_badges route; badge_list now accepts scope and filters accordingly; team overview and user profile contexts include team_badges; view-level badge-assignment workflow added.
Templates & Navigation
website/templates/badges.html, website/templates/badge_user_list.html, website/templates/includes/sidenav.html, website/templates/404.html, website/templates/features.html, website/templates/home.html, website/templates/profile.html, website/templates/team_overview.html
Updates badge links from badgesuser_badges, adds team_badges navigation; refactors badge card layout; large Team Overview UI overhaul (team header, team badges section, member slider, modals, JS flows); profile shows team badges alongside user badges.
Fixtures & Tests
website/fixtures/team_badges.json, website/tests/test_team_badges.py, website/tests/test_github_commands.py, website/tests/test_slack_commands.py
Adds team badge fixture (automatic/manual badges) and tests for TeamBadge model, assignment, and view; minor test imports/formatting tweaks.
Forms
website/forms.py
Adds ReminderSettingsForm and updates imports to include Badge and TeamBadge.
Management Commands (formatting)
website/management/commands/fetch_discord_servers.py, website/management/commands/update_github_issues.py, website/management/commands/update_repos_dynamic.py
Reformatting of conditional assignments (logo_url, closed_at, merged_at) to multi-line parenthesized expressions; no logic change.
Miscellaneous config & minor edits
.env.example, .gitignore, blt/settings.py, test_duplicate_checker.py, website/views/Simulation.py, website/views/company.py, website/views/organization.py, website/views/slackbot.py
Small whitespace/comment edits (.env spacing, .gitignore adds *.deb, comment reformat/typo, docstring blank-line, minor conditional formatting); no behavioral changes.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant TeamsView as Teams View (assign_team_member_badge)
  participant DB as Database (TeamBadge)
  participant Redirect as Response/Redirect

  Client->>TeamsView: POST assign badge (admin)
  TeamsView->>DB: check team membership & existing assignment
  alt valid & not exists
    TeamsView->>DB: create TeamBadge record
    TeamsView->>Redirect: redirect with success message
  else invalid / not authorized
    TeamsView->>Redirect: redirect with error message
  end
Loading
sequenceDiagram
  participant System
  participant Signals as Signal Handler
  participant Evaluator as evaluate_team_badges
  participant DB as Database (TeamBadge/Badge)
  participant Notification as Optional downstream actions

  System->>Signals: Contribution or Issue post_save
  Signals->>Evaluator: evaluate team badges for team
  Evaluator->>DB: query metrics, contributors, existing TeamBadge
  alt criteria met & not awarded
    Evaluator->>DB: create TeamBadge (award)
    Evaluator->>Notification: emit side-effects (optional)
  else criteria not met & badge exists
    Evaluator->>DB: remove TeamBadge (revoke)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.76% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/team badges' clearly and directly summarizes the main objective of the pull request—implementing a team badges feature—as evidenced by the comprehensive changes adding TeamBadge model, award/evaluation system, UI components, and admin interfaces.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between ee437ec and b2fbbba.

📒 Files selected for processing (1)
  • website/views/company.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • website/views/company.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test

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.

@github-actions
Copy link
Contributor

📊 Monthly Leaderboard

Hi @karunarapolu! Here's how you rank for December 2025:

Rank User PRs Reviews Comments Total
#24 @haroon0x 0 3 3 18
#25 @karunarapolu 1 0 3 16
#26 @snigdhachoudhury 1 0 3 16

Leaderboard based on contributions in December 2025. Keep up the great work! 🚀

@github-actions
Copy link
Contributor

❌ Pre-commit checks failed

The pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them:

# Install pre-commit if you haven't already
pip install pre-commit

# Run pre-commit on all files
pre-commit run --all-files

# Or run pre-commit on staged files only
pre-commit run

After running these commands, the pre-commit hooks will automatically fix most issues.
Please review the changes, commit them, and push to your branch.

💡 Tip: You can set up pre-commit to run automatically on every commit by running:

pre-commit install
Pre-commit output
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[WARNING] repo `https://github.com/pre-commit/pre-commit-hooks` uses deprecated stage names (commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pre-commit/pre-commit-hooks` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/pycqa/isort.
[WARNING] repo `https://github.com/pycqa/isort` uses deprecated stage names (commit, merge-commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pycqa/isort` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Initializing environment for https://github.com/djlint/djLint.
[INFO] Initializing environment for local.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pycqa/isort.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/djlint/djLint.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for local.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
check python ast.........................................................Passed
check builtin type constructor use.......................................Passed
check yaml...............................................................Passed
fix python encoding pragma...............................................Passed
mixed line ending........................................................Passed
isort....................................................................Failed
- hook id: isort
- files were modified by this hook

Fixing /home/runner/work/BLT/BLT/blt/urls.py
Fixing /home/runner/work/BLT/BLT/website/feed_signals.py
Fixing /home/runner/work/BLT/BLT/website/forms.py
Fixing /home/runner/work/BLT/BLT/website/views/teams.py
Fixing /home/runner/work/BLT/BLT/website/views/user.py


For more information, see the pre-commit documentation.

@github-actions github-actions bot added the pre-commit: failed Pre-commit checks failed label Dec 28, 2025
@github-actions github-actions bot added the tests: failed Django tests failed label Dec 28, 2025
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: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
website/feed_signals.py (1)

8-26: Import error: Count should be from django.db.models, not .models.

The Count aggregate function is imported from .models (the local models module), but it should be imported from django.db.models. This will cause an ImportError at runtime.

🔎 Proposed fix
+from django.db.models import Count
+
 from .models import (
     Activity,
     BaconEarning,
     Badge,
     Bid,
-    Count,
     Contribution,
     ForumPost,
🧹 Nitpick comments (14)
.env.example (1)

36-36: Remove the extra blank line to maintain consistent section spacing.

The insertion of a blank line at line 36 creates two consecutive blank lines between the Slack and BlueSky sections, which is inconsistent with the single-line spacing used elsewhere in the file (e.g., between SUPERUSER, DATABASE, SENTRY, and SLACK sections).

🔎 Proposed fix
 SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
-
 #BlueSky User Details
 BLUESKY_USERNAME=example.bsky.social
website/forms.py (1)

10-22: Remove unused imports.

Badge and TeamBadge are imported but not used anywhere in this file. These unused imports should be removed to keep the codebase clean.

🔎 Proposed fix
 from website.models import (
     Bid,
-    Badge,
     Hackathon,
     HackathonPrize,
     HackathonSponsor,
     IpReport,
     Job,
     Monitor,
     Organization,
     ReminderSettings,
     Repo,
     Room,
     UserProfile,
-    TeamBadge,
 )
website/management/commands/update_github_issues.py (1)

165-176: Semantics preserved for closed_at / merged_at; optional micro‑refactor

The new ternary expressions keep the original behavior (nullable closed_at, merged_at only when merged). If you want to reduce duplication, you could reuse the previously parsed merged_at instead of reparsing from the JSON.

Optional small cleanup using existing merged_at
-                        github_issue, created = GitHubIssue.objects.update_or_create(
+                        github_issue, created = GitHubIssue.objects.update_or_create(
@@
-                                "closed_at": (
-                                    timezone.make_aware(datetime.strptime(pr["closed_at"], "%Y-%m-%dT%H:%M:%SZ"))
-                                    if pr.get("closed_at")
-                                    else None
-                                ),
-                                "merged_at": (
-                                    timezone.make_aware(
-                                        datetime.strptime(pr["pull_request"]["merged_at"], "%Y-%m-%dT%H:%M:%SZ")
-                                    )
-                                    if merged
-                                    else None
-                                ),
+                                "closed_at": (
+                                    timezone.make_aware(datetime.strptime(pr["closed_at"], "%Y-%m-%dT%H:%M:%SZ"))
+                                    if pr.get("closed_at")
+                                    else None
+                                ),
+                                # Reuse the earlier parsed merged_at value
+                                "merged_at": merged_at if merged else None,
website/templates/includes/sidenav.html (1)

319-325: User & team badge links in sidenav wired correctly; label tweak optional

The new user_badges entry under Users and team_badges entry under Teams both use the same active-state pattern as neighboring links and look functionally sound.

If you want to make the distinction even clearer in the UI, you could consider renaming the team submenu label to something like “Team Badges” while keeping the URL the same, but that’s purely cosmetic.

Also applies to: 370-376

website/templates/profile.html (2)

519-522: Team label rendering is fine; optional UX polish

Showing user_badge.team in red above the awarded date works, but you might consider prefixing it with a short label (e.g., “Team:”) or using a slightly smaller font so it visually reads as metadata rather than a second title. Not required, just a UX refinement.


953-1552: Large legacy commented-out profile layout

You updated the URL here to user_badges, which keeps even the legacy block consistent. Given this entire section is wrapped in {% comment %}, consider deleting it in a follow-up to reduce noise and avoid future inconsistencies between live and commented code.

website/views/core.py (1)

1457-1471: Scope-aware badge_list behavior is correct; consider minor refinements

The branching on scope == "team" with scope__in=["team", "topuser_team"] vs user-scoped badges annotated by user_count matches the new Badge.scope semantics and URL wiring.

Two small, non-blocking suggestions:

  • Pass scope into the context so badges.html can easily adjust headings/layout based on whether it’s showing user or team badges.
  • Add a deterministic ordering to the team branch (e.g., .order_by("title") or "-created_at") to avoid implicit DB ordering differences across backends.

Otherwise this looks good.

website/tests/test_team_badges.py (1)

40-45: Unique-constraint test is too generic and may not reflect the model

test_team_badge_unique_constraint asserts that any Exception is raised on a duplicate (team, badge) TeamBadge, but:

  • The current TeamBadge model snippet does not show a uniqueness constraint; without one, this test will fail because the second create will succeed.
  • Even if you add a constraint, catching a broad Exception can allow unrelated bugs (e.g., DB connectivity issues) to make the test pass.

Consider either:

  • Adding an explicit UniqueConstraint/unique_together on the intended fields and asserting a specific exception (typically IntegrityError), or
  • Removing this test if you intentionally allow multiple TeamBadge rows for the same (team, badge) combination.
website/views/teams.py (1)

4-6: Unused import: defaultdict.

The defaultdict import is not used anywhere in this file.

🔎 Proposed fix
-from collections import defaultdict
-
-
website/migrations/0263_alter_badge_scope.py (1)

1-22: LGTM!

This migration correctly updates the scope field choices to use a human-readable label "Topuser Team" instead of the raw value "topuser_team". The dependency chain is correct.

Consider squashing migrations 0261, 0262, and 0263 before merging to production to reduce migration overhead, but this is optional.

website/models.py (1)

1697-1712: Consider adding a unique constraint to prevent duplicate badge awards.

The TeamBadge model correctly associates badges with teams and optionally with users. However, without a database-level constraint, duplicate badges could be awarded if the application-level check in award_team_badge is bypassed or encounters a race condition.

Also, adding related_name to ForeignKey fields would improve reverse query clarity.

🔎 Proposed improvements
 class TeamBadge(models.Model):
-    team = models.ForeignKey(Organization, on_delete=models.CASCADE)
-    user=models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, default=None)# assign a indivitual user the badge for achivement in a group
-    badge = models.ForeignKey(Badge, on_delete=models.CASCADE)
+    team = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name="team_badges")
+    user = models.ForeignKey(
+        User, on_delete=models.CASCADE, null=True, blank=True, default=None,
+        related_name="team_badge_awards",
+        help_text="Assign badge to individual user for achievement in a team"
+    )
+    badge = models.ForeignKey(Badge, on_delete=models.CASCADE, related_name="team_badge_awards")
     awarded_by = models.ForeignKey(
         User,
         null=True,
         blank=True,
         related_name="awarded_badges_to_team",
         on_delete=models.SET_NULL,
     )
     awarded_at = models.DateTimeField(auto_now_add=True)
     reason = models.TextField(blank=True, null=True)

+    class Meta:
+        constraints = [
+            models.UniqueConstraint(
+                fields=["team", "badge", "user"],
+                name="unique_team_badge_user"
+            )
+        ]
+
     def __str__(self):
         return f"{self.team} - {self.badge.title}"
website/feed_signals.py (3)

273-273: Duplicate logger declaration.

logger is already defined at line 29. This redeclaration is redundant and could cause confusion.

🔎 Proposed fix
-logger = logging.getLogger(__name__)

402-409: Performance issue: sorting all organizations in memory for rank comparison.

evaluate_team_badges fetches all organizations and computes an activity score for each one in Python. With many organizations, this causes O(n) queries and O(n log n) sorting per evaluation. Consider using database-level aggregation or caching scores.

🔎 Proposed optimization
elif metric == "team_top_activity_rank":
    # Check if this team has the highest score without loading all teams
    team_score = get_team_activity_score(team)
    # Only award if we can confirm this is top (or use cached leaderboard)
    higher_exists = Organization.objects.annotate(
        # This would require a custom annotation or raw SQL
        # For now, consider caching the top team periodically
    ).filter(activity_score__gt=team_score).exists()
    if not higher_exists:
        award_team_badge(team, badge, reason="Top activity team")
    else:
        revoke_team_badge(team, badge)

Alternatively, run this ranking check in a periodic task rather than on every contribution.


458-468: Duplicate badge evaluation when issue is closed.

When an issue is closed, issue_updated evaluates badges for instance.user's team (lines 460-462) and then again for instance.closed_by's team (lines 467-468). If the user and closer are on the same team, the evaluation runs twice unnecessarily.

🔎 Proposed fix to avoid duplicate evaluation
 @receiver(post_save, sender=Issue)
 def issue_updated(sender, instance, **kwargs):
-    team = get_user_team(instance.user)
-    evaluate_team_badges(team)
-    evaluate_user_team_badges(team)
-
-    if instance.status != "closed" or not instance.closed_by:
-        return
-
-    team = get_user_team(instance.closed_by)
-    evaluate_team_badges(team)
+    teams_to_evaluate = set()
+    
+    if instance.user:
+        user_team = get_user_team(instance.user)
+        if user_team:
+            teams_to_evaluate.add(user_team)
+    
+    if instance.status == "closed" and instance.closed_by:
+        closer_team = get_user_team(instance.closed_by)
+        if closer_team:
+            teams_to_evaluate.add(closer_team)
+    
+    for team in teams_to_evaluate:
+        evaluate_team_badges(team)
+        evaluate_user_team_badges(team)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 679ce84 and ee437ec.

⛔ Files ignored due to path filters (17)
  • website/static/img/badges/team_badges/challenge_conquerors.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/collaborative_force.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/community_hero.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/consistent_performers.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/dependable_contributor.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/discussion_driven_team.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/discussion_driver.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/early_bird.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/issue_crusher.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/issue_dominators.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/issue_initiator.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/most_active_team.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/pull_request_pro.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/team_mvp.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/team_powerhouse.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/team_support_star.png is excluded by !**/*.png
  • website/static/img/badges/team_badges/top_contributor.png is excluded by !**/*.png
📒 Files selected for processing (36)
  • .env.example
  • .gitignore
  • blt/settings.py
  • blt/urls.py
  • test_duplicate_checker.py
  • website/admin.py
  • website/feed_signals.py
  • website/fixtures/team_badges.json
  • website/forms.py
  • website/management/commands/fetch_discord_servers.py
  • website/management/commands/update_github_issues.py
  • website/management/commands/update_repos_dynamic.py
  • website/migrations/0261_badge_scope_teambadge.py
  • website/migrations/0262_alter_badge_scope.py
  • website/migrations/0263_alter_badge_scope.py
  • website/migrations/0264_teambadge_user_alter_userbadge_awarded_by.py
  • website/models.py
  • website/templates/404.html
  • website/templates/badge_user_list.html
  • website/templates/badges.html
  • website/templates/features.html
  • website/templates/home.html
  • website/templates/includes/header.html
  • website/templates/includes/sidenav.html
  • website/templates/profile.html
  • website/templates/team_overview.html
  • website/tests/test_github_commands.py
  • website/tests/test_slack_commands.py
  • website/tests/test_team_badges.py
  • website/views/Simulation.py
  • website/views/company.py
  • website/views/core.py
  • website/views/organization.py
  • website/views/slackbot.py
  • website/views/teams.py
  • website/views/user.py
🧰 Additional context used
🧬 Code graph analysis (10)
website/views/core.py (2)
website/models.py (1)
  • Badge (1658-1678)
website/api/views.py (2)
  • filter (375-414)
  • filter (844-911)
website/views/teams.py (1)
website/models.py (3)
  • Badge (1658-1678)
  • TeamBadge (1697-1712)
  • Organization (181-289)
website/tests/test_team_badges.py (1)
website/models.py (2)
  • Badge (1658-1678)
  • TeamBadge (1697-1712)
website/views/user.py (1)
website/models.py (1)
  • TeamBadge (1697-1712)
website/migrations/0264_teambadge_user_alter_userbadge_awarded_by.py (3)
website/migrations/0261_badge_scope_teambadge.py (1)
  • Migration (8-41)
website/migrations/0262_alter_badge_scope.py (1)
  • Migration (6-22)
website/migrations/0263_alter_badge_scope.py (1)
  • Migration (6-22)
blt/urls.py (2)
website/views/teams.py (1)
  • assign_team_member_badge (361-402)
website/views/core.py (1)
  • badge_list (1457-1471)
website/admin.py (1)
website/models.py (1)
  • TeamBadge (1697-1712)
website/migrations/0262_alter_badge_scope.py (3)
website/migrations/0261_badge_scope_teambadge.py (1)
  • Migration (8-41)
website/migrations/0263_alter_badge_scope.py (1)
  • Migration (6-22)
website/migrations/0264_teambadge_user_alter_userbadge_awarded_by.py (1)
  • Migration (8-38)
website/feed_signals.py (1)
website/models.py (8)
  • Contribution (1438-1465)
  • Hunt (540-573)
  • Issue (590-739)
  • Organization (181-289)
  • TeamBadge (1697-1712)
  • UserBadge (1681-1695)
  • UserProfile (896-1109)
  • Badge (1658-1678)
website/forms.py (1)
website/models.py (2)
  • Badge (1658-1678)
  • TeamBadge (1697-1712)
🪛 dotenv-linter (4.0.0)
.env.example

[warning] 36-36: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (30)
website/views/organization.py (1)

2575-2575: Approved: Formatting consolidation of exclude clause.

The single-argument .exclude(path="/organizations/") consolidation is cleaner and maintains identical functionality. No behavioral changes.

website/views/slackbot.py (1)

430-430: Formatting change approved.

The newline adjustment at the end of the file is fine; the method not allowed response is correct.

website/tests/test_slack_commands.py (1)

4-4: Formatting change approved.

The blank line after the module docstring follows PEP 257 conventions; test suite is well-structured with comprehensive coverage of success and error scenarios.

website/tests/test_github_commands.py (1)

5-6: LGTM!

The timedelta import is correctly placed and is used throughout the test file for mocking timestamps and date calculations.

.gitignore (1)

28-28: LGTM!

Adding *.deb to ignore Debian package files is a sensible addition to prevent build artifacts from being tracked.

test_duplicate_checker.py (1)

9-9: No concerns.

Trivial whitespace change with no functional impact.

website/management/commands/fetch_discord_servers.py (1)

108-112: LGTM!

The multi-line formatting improves readability without changing the logic.

website/management/commands/update_repos_dynamic.py (1)

381-416: LGTM!

The multi-line formatting for closed_at and merged_at conditional expressions improves readability while preserving the original logic.

website/views/company.py (1)

2042-2046: LGTM!

The multi-line formatting improves readability of the conditional avatar access.

website/views/Simulation.py (2)

174-176: LGTM!

Formatting change improves readability of the conditional message.


213-217: LGTM!

Formatting change improves readability of the conditional message.

website/forms.py (1)

446-476: LGTM!

The ReminderSettingsForm is well-implemented:

  • Proper TimeField with multiple input formats for flexibility
  • Timezone choices populated from pytz.common_timezones
  • Helpful user-facing messages about the 15-minute notification delay
  • Consistent widget styling with the rest of the codebase
website/migrations/0264_teambadge_user_alter_userbadge_awarded_by.py (1)

1-38: Migration wiring for badge→user relations looks consistent

Dependencies and FK definitions for teambadge.user and userbadge.awarded_by match the described model changes (AUTH_USER_MODEL, nullable, proper related_name). No issues from a schema/migration standpoint.

website/templates/home.html (1)

1142-1147: Home “Badges” card correctly targets user_badges

Updating this tile to {% url 'user_badges' %} keeps home navigation consistent with the new user/team badge routes.

website/templates/badge_user_list.html (1)

71-75: “Back to Badges” now aligned with user_badges route

The back link correctly returns users to the user badges page, matching the new routing scheme.

website/templates/features.html (1)

191-193: Badges feature card correctly targets user_badges

Routing this CTA to {% url 'user_badges' %} is consistent with the new user/team badge separation.

website/templates/profile.html (1)

497-507: Badge header link to user_badges looks correct

Using the user_badges URL name here matches the updated routing while keeping the underlying /badges/ path stable. No issues from a routing or template standpoint.

website/migrations/0262_alter_badge_scope.py (1)

1-22: Badge.scope migration is consistent with the model

Adding "topuser_team" to choices and bumping max_length to 20 aligns with Badge.BADGE_SCOPES in website.models.Badge. This fits cleanly in the 0261→0262→0263 migration chain.

blt/urls.py (2)

329-342: New import for assign_team_member_badge is appropriate

Importing assign_team_member_badge alongside the other team views cleanly exposes it for the new route additions below; nothing to change here.


686-689: URL name change to user_badges — verified no remaining badges references

The rename is safe; no remaining reverse("badges") or {% url 'badges' %} references found in the codebase.

website/views/teams.py (1)

360-402: The badge assignment view logic is sound.

The view correctly:

  • Validates team membership and admin rights before allowing badge assignment.
  • Uses get_object_or_404 for safe object retrieval.
  • Checks for existing badge assignment before creating a duplicate.
  • Provides user feedback via messages.

One minor note: the available_badges on line 45 filters only type="manual" badges, but this view doesn't validate that the selected badge is of type "manual". Consider adding validation to prevent assignment of automatic badges.

website/views/user.py (1)

430-433: Chaining different model types may cause template issues.

user_badges is a QuerySet of UserBadge objects and team_badges is a QuerySet of TeamBadge objects. While chain() works to combine them, these models have different attributes (e.g., UserBadge has no team field, TeamBadge has no direct user relationship pattern as UserBadge).

Ensure the template accessing user_badges handles both types gracefully, or consider normalizing the data structure.

Also, minor style: add a space after = on line 430.

-        team_badges =TeamBadge.objects.filter(user=user)
-        user_badges = chain(user_badges, team_badges)#combining them
+        team_badges = TeamBadge.objects.filter(user=user)
+        user_badges = chain(user_badges, team_badges)  # combining them
website/admin.py (1)

1154-1159: LGTM!

The TeamBadgeAdmin configuration follows the existing UserBadgeAdmin pattern and provides appropriate list display, filtering, and search capabilities. The registration is correct.

Also applies to: 1182-1182

website/templates/404.html (1)

122-133: LGTM!

The badges section is correctly split into "User Badges" and "Team Badges" with appropriate URL references. The styling is consistent with other items in the section.

Minor note: With 7 items now in this grid (vs 6 columns at xl:grid-cols-6), the last item will wrap to a new row. This may be intentional, but verify the visual layout is acceptable.

website/migrations/0261_badge_scope_teambadge.py (1)

1-41: LGTM!

This migration correctly:

  1. Adds the scope field to the Badge model with appropriate choices and default.
  2. Creates the TeamBadge model with proper foreign key relationships and cascading deletes.
  3. Uses SET_NULL for awarded_by to preserve badge records if the awarder is deleted.

The max_length=10 for scope is corrected to 20 in the subsequent migration (0262) to accommodate the "topuser_team" value.

website/models.py (1)

1663-1673: LGTM on the badge scoping implementation.

The addition of BADGE_SCOPES and the scope field provides a clean way to differentiate between user, team, and top-user badges. The default of "user" ensures backward compatibility with existing badges.

Minor formatting nit: the comment on line 1667 has inconsistent spacing ("topuser_team","Topuser Team"), but this is cosmetic.

website/templates/badges.html (1)

46-72: Clean badge card implementation with proper fallback.

The conditional rendering for badge icons with a gradient placeholder fallback is well-implemented. The use of the first letter of the title as a visual identifier is a nice touch.

website/templates/team_overview.html (1)

45-79: Team badges section is well-structured.

The team badges display with icon, title, date, and optional reason is clean and provides good user feedback with the empty state message.

website/feed_signals.py (2)

276-294: Team member lookup logic is clear and functional.

The helper functions get_user_team and get_team_members correctly handle the admin and managers relationships, with proper handling of None cases and use of distinct() to avoid duplicates.


356-373: Badge award/revoke functions are well-implemented.

The award_team_badge function correctly checks for existing badges before creating new ones, preventing duplicates at the application level. The logging provides good visibility into badge operations.

Note: Consider adding a database-level unique constraint on TeamBadge (as mentioned in the models.py review) to handle race conditions.

@github-project-automation github-project-automation bot moved this from Backlog to Ready in 📌 OWASP BLT Project Board Dec 28, 2025
@karunarapolu
Copy link
Author

@coderabbitai generate docstrings

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 28, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 28, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #5369

coderabbitai bot added a commit that referenced this pull request Dec 28, 2025
Docstrings generation was requested by @karunarapolu.

* #5368 (comment)

The following files were modified:

* `website/feed_signals.py`
* `website/forms.py`
* `website/management/commands/fetch_discord_servers.py`
* `website/management/commands/update_github_issues.py`
* `website/management/commands/update_repos_dynamic.py`
* `website/models.py`
* `website/tests/test_team_badges.py`
* `website/views/Simulation.py`
* `website/views/company.py`
* `website/views/core.py`
* `website/views/organization.py`
* `website/views/slackbot.py`
* `website/views/teams.py`
* `website/views/user.py`
@github-actions github-actions bot added the last-active: 0d PR last updated 0 days ago label Dec 29, 2025
@github-actions
Copy link
Contributor

💬 Reminder: Unresolved Conversations

Hi @karunarapolu!

This pull request has 14 unresolved conversations that need to be addressed.

Please review and resolve the pending discussions so we can move forward with merging this PR.

Thank you! 🙏

@github-actions github-actions bot added last-active: 1d PR last updated 1 day ago and removed last-active: 0d PR last updated 0 days ago last-active: 1d PR last updated 1 day ago labels Jan 1, 2026
@github-actions github-actions bot added the last-active: 0d PR last updated 0 days ago label Jan 2, 2026
@github-actions github-actions bot removed the last-active: 0d PR last updated 0 days ago label Jan 3, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 3, 2026

❌ Pre-commit checks failed

The pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them:

# Install pre-commit if you haven't already
pip install pre-commit

# Run pre-commit on all files
pre-commit run --all-files

# Or run pre-commit on staged files only
pre-commit run

After running these commands, the pre-commit hooks will automatically fix most issues.
Please review the changes, commit them, and push to your branch.

💡 Tip: You can set up pre-commit to run automatically on every commit by running:

pre-commit install
Pre-commit output
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[WARNING] repo `https://github.com/pre-commit/pre-commit-hooks` uses deprecated stage names (commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pre-commit/pre-commit-hooks` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/pycqa/isort.
[WARNING] repo `https://github.com/pycqa/isort` uses deprecated stage names (commit, merge-commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pycqa/isort` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Initializing environment for https://github.com/djlint/djLint.
[INFO] Initializing environment for local.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pycqa/isort.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/djlint/djLint.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for local.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
check python ast.........................................................Passed
check builtin type constructor use.......................................Passed
check yaml...............................................................Passed
fix python encoding pragma...............................................Passed
mixed line ending........................................................Passed
isort....................................................................Failed
- hook id: isort
- files were modified by this hook

Fixing /home/runner/work/BLT/BLT/blt/urls.py
Fixing /home/runner/work/BLT/BLT/website/feed_signals.py
Fixing /home/runner/work/BLT/BLT/website/forms.py
Fixing /home/runner/work/BLT/BLT/website/views/teams.py
Fixing /home/runner/work/BLT/BLT/website/views/user.py


For more information, see the pre-commit documentation.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 3, 2026

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output
WARNING:root:No DATABASE_URL environment variable set, and so no databases setup
Reading .env file from /home/runner/work/BLT/BLT/.env
DATABASE_URL: not set
no database url detected in settings, using sqlite
DEBUG This service is instrumented using OpenTelemetry. OpenTelemetry or one of its components could not be imported; please add compatible versions of opentelemetry-api and opentelemetry-instrumentation packages in order to get Storage Tracing data.
   [ERROR] Error: no such table: auth_user
Traceback (most recent call last):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/backends/utils.py", line 105, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/backends/sqlite3/base.py", line 360, in execute
    return super().execute(query, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.OperationalError: no such table: auth_user

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/test_bacon_system.py", line 39, in <module>
    user, created = User.objects.get_or_create(username="bacon_test_user", defaults={"email": "test@bacon.com"})
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/query.py", line 948, in get_or_create
    return self.get(**kwargs), False
           ^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/query.py", line 631, in get
    num = len(clone)
          ^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/query.py", line 368, in __len__
    self._fetch_all()
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/query.py", line 1954, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/query.py", line 93, in __iter__
    results = compiler.execute_sql(
              ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1623, in execute_sql
    cursor.execute(sql, params)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/backends/utils.py", line 79, in execute
    return self._execute_with_wrappers(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/backends/utils.py", line 92, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/backends/utils.py", line 100, in _execute
    with self.db.wrap_database_errors:
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/backends/utils.py", line 105, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/backends/sqlite3/base.py", line 360, in execute
    return super().execute(query, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.OperationalError: no such table: auth_user
   [ERROR] Error: no such table: socialaccount_socialaccount
   [ERROR] Error: no such table: website_activity
============================================================
BACON TOKEN SYSTEM TEST
============================================================

1. Checking signal module...
   [OK] Signal module loaded
   [OK] Function exists: True

2. Testing giveBacon function...

3. Checking users with GitHub connected...

4. Checking connection activities...

5. Checking signal receivers...
   [INFO] Found 1 receivers
   - ((140547881456064, 140548216490112), <weakref at 0x7fd3da8b4c70; to 'function' at 0x7fd3da8c51c0 (reward_social_account_connection)>, False)

============================================================
TEST COMPLETE
============================================================

If giveBacon works but users aren't getting tokens,
the signal might not be firing during OAuth signup.

Watch logs during signup: docker-compose logs -f app
Look for: SIGNAL FIRED messages
============================================================
/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/dj_rest_auth/registration/serializers.py:228: UserWarning: app_settings.USERNAME_REQUIRED is deprecated, use: app_settings.SIGNUP_FIELDS['username']['required']
  required=allauth_account_settings.USERNAME_REQUIRED,
/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/dj_rest_auth/registration/serializers.py:230: UserWarning: app_settings.EMAIL_REQUIRED is deprecated, use: app_settings.SIGNUP_FIELDS['email']['required']
  email = serializers.EmailField(required=allauth_account_settings.EMAIL_REQUIRED)
/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/dj_rest_auth/registration/serializers.py:288: UserWarning: app_settings.EMAIL_REQUIRED is deprecated, use: app_settings.SIGNUP_FIELDS['email']['required']
  email = serializers.EmailField(required=allauth_account_settings.EMAIL_REQUIRED)
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
CommandError: Conflicting migrations detected; multiple leaf nodes in the migration graph: (0263_githubissue_githubissue_pr_merged_idx_and_more, 0264_teambadge_user_alter_userbadge_awarded_by in website).
To fix them run 'python manage.py makemigrations --merge'
Found 312 test(s).

For more information, see the Django testing documentation.

@github-actions github-actions bot added last-active: 0d PR last updated 0 days ago last-active: 1d PR last updated 1 day ago and removed last-active: 0d PR last updated 0 days ago last-active: 1d PR last updated 1 day ago labels Jan 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

files-changed: 53 PR changes 53 files last-active: 0d PR last updated 0 days ago migrations PR contains database migration files needs-peer-review PR needs peer review pre-commit: failed Pre-commit checks failed quality: high tests: failed Django tests failed

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

1 participant