Skip to content

Instantly share code, notes, and snippets.

@mathjazz
Created November 25, 2024 20:28
Show Gist options
  • Save mathjazz/bc5a9fa6359179fa1677c43b4d8f2080 to your computer and use it in GitHub Desktop.
Save mathjazz/bc5a9fa6359179fa1677c43b4d8f2080 to your computer and use it in GitHub Desktop.
diff --git a/pontoon/base/forms.py b/pontoon/base/forms.py
index 73fe2a93b..181cb2686 100644
--- a/pontoon/base/forms.py
+++ b/pontoon/base/forms.py
@@ -10,12 +10,10 @@ from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.urls import reverse
-from django.utils import timezone
from pontoon.base import utils
from pontoon.base.models import (
Locale,
- PermissionChangelog,
ProjectLocale,
User,
UserProfile,
@@ -134,29 +132,11 @@ class LocalePermsForm(UserPermissionLogFormMixin, forms.ModelForm):
before_count = self.user.badges_promotion_count
- now = timezone.now()
self.assign_users_to_groups("translators", translators)
- removal = PermissionChangelog.objects.filter(
- performed_by=self.user,
- action_type=PermissionChangelog.ActionType.REMOVED,
- created_at__gte=now,
- )
self.assign_users_to_groups("managers", managers)
after_count = self.user.badges_promotion_count
- # Check if user was demoted from Manager to Translator
- # In this case, it doesn't count as a promotion
- #
- # TODO:
- # This code is the only consumer of the PermissionChangelog
- # model, so we should refactor in the future to simplify
- # how promotions are retrieved. (see #2195)
-
- for item in removal:
- if "managers" in item.group.name:
- after_count -= 1
-
# Award Community Builder badge
if (
after_count > before_count
diff --git a/pontoon/base/models/user.py b/pontoon/base/models/user.py
index 366f394d7..2080d7b92 100644
--- a/pontoon/base/models/user.py
+++ b/pontoon/base/models/user.py
@@ -1,3 +1,4 @@
+from datetime import timedelta
from hashlib import md5
from urllib.parse import quote, urlencode
@@ -7,7 +8,7 @@ from guardian.shortcuts import get_objects_for_user
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
-from django.db.models import Count, Q
+from django.db.models import Count, Exists, OuterRef, Q
from django.urls import reverse
from django.utils import timezone
@@ -243,26 +244,28 @@ def badges_review_count(self):
@property
def badges_promotion_count(self):
"""Role promotions performed by user that count towards their badges"""
- added_logs = self.changed_permissions_log.filter(
+ added_entries = self.changed_permissions_log.filter(
action_type="added",
created_at__gte=settings.BADGES_START_DATE,
- ).order_by("created_at")
-
- unmatched_added = []
-
- # Don't count any 'added' logs that have a corresponding
- # 'removed' log within the same group
- for added in added_logs:
- removed_exists = self.changed_permissions_log.filter(
- action_type="removed",
- created_at__gte=settings.BADGES_START_DATE,
- group=added.group,
- ).exists()
-
- if not removed_exists:
- unmatched_added.append(added)
+ )
- return len(unmatched_added)
+ # Check if user was demoted from Manager to Translator.
+ # In this case, it doesn't count as a promotion.
+ #
+ # TODO:
+ # This code is the only consumer of the PermissionChangelog model, so we should
+ # refactor to simplify how promotions are retrieved. (see #2195)
+ return added_entries.exclude(
+ Exists(
+ self.changed_permissions_log.filter(
+ performed_by=OuterRef("performed_by"),
+ performed_on=OuterRef("performed_on"),
+ action_type="removed",
+ created_at__gt=OuterRef("created_at"),
+ created_at__lte=OuterRef("created_at") + timedelta(milliseconds=10),
+ )
+ )
+ ).count()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment