Skip to content

Commit 2dcea29

Browse files
authored
Merge pull request #206 from mapswipe/feat/admin-user-team-ui
2 parents c3eac25 + 1ae590b commit 2dcea29

File tree

5 files changed

+64
-10
lines changed

5 files changed

+64
-10
lines changed

apps/contributor/admin.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import typing
22

3+
from admin_auto_filters.filters import AutocompleteFilterFactory
34
from django.contrib import admin
5+
from django.db import models
6+
from django.db.models.functions import Coalesce
7+
from django.http import HttpRequest
48
from django.urls import reverse
59
from django.utils.html import format_html
610
from djangoql.admin import DjangoQLSearchMixin # type: ignore[reportMissingTypeStubs]
@@ -23,9 +27,12 @@ class ContributorUserAdmin(DjangoQLSearchMixin, admin.ModelAdmin): # type: igno
2327
"modified_at",
2428
)
2529
list_display = ("firebase_id", "username", "team", "created_at")
30+
list_filter = (
31+
AutocompleteFilterFactory("Team", "team"),
32+
"created_at",
33+
)
2634
ordering = ("username", "team", "created_at")
2735
search_fields = ("username",)
28-
# list_filter = ("team",)
2936
list_select_related = True
3037
autocomplete_fields = ("team",)
3138

@@ -65,19 +72,49 @@ class ContributorTeamAdmin(
6572
UserResourceAdmin,
6673
admin.ModelAdmin, # type: ignore[reportMissingTypeArgument]
6774
):
68-
list_display = ("name", "view_team_members")
75+
list_display = (
76+
"name",
77+
"members_count",
78+
)
6979
ordering = ("name",)
7080
search_fields = ("name",)
7181
list_select_related = True
7282

83+
@typing.override
84+
def get_queryset(self, request: HttpRequest) -> models.QuerySet[ContributorTeam]:
85+
return (
86+
super()
87+
.get_queryset(request)
88+
.annotate(
89+
members_count=Coalesce(
90+
models.Subquery(
91+
ContributorUser.objects.filter(
92+
team=models.OuterRef("id"),
93+
)
94+
.order_by()
95+
.values("team")
96+
.annotate(c=models.Count("id"))
97+
.values("c")[:1],
98+
output_field=models.IntegerField(),
99+
),
100+
0,
101+
),
102+
)
103+
)
104+
73105
@typing.override
74106
def save_model(self, request, obj, form, change): # type: ignore[reportMissingParameterType]
75107
super().save_model(request, obj, form, change)
76108
FirebaseContributorTeam(obj).trigger()
77109

78-
def view_team_members(self, obj): # type: ignore[reportMissingParameterType]
79-
url = reverse("admin:contributor_contributoruser_changelist") + f"?team__id__exact={obj.id}"
80-
return format_html('<a href="{}">View Team Members</a>', url)
110+
@admin.display(ordering="members_count", description="Members count")
111+
def members_count(self, obj: ContributorTeam):
112+
url = reverse("admin:contributor_contributoruser_changelist") + f"?team={obj.id}"
113+
return format_html(
114+
'<a href="{}">{}</a>',
115+
url,
116+
obj.members_count, # type: ignore[reportAttributeAccessIssue]
117+
)
81118

82119

83120
@admin.register(ContributorUserGroupMembership)

main/cache.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Key:
3131
@staticmethod
3232
@contextmanager
3333
def redis_lock(lock_id: str, *, lock_expire: int | None = None):
34-
lock_expire_ = lock_expire or settings.REDIS_LOCK_EXPIRE
34+
lock_expire_ = lock_expire or settings.DEFAULT_REDIS_LOCK_EXPIRE
3535
timeout_at: float = time.monotonic() + lock_expire_ - 3
3636
# cache.add fails if the key already exists
3737
status = cache.add(lock_id, 1, lock_expire_)

main/settings.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def urlparse(value) -> ParseResult:
201201
"djangoql",
202202
"rest_framework",
203203
"drf_spectacular",
204+
"admin_auto_filters",
204205
# - Health-check
205206
"health_check", # required
206207
"health_check.db",
@@ -406,7 +407,7 @@ def urlparse(value) -> ParseResult:
406407
)
407408

408409
# Redis lock
409-
REDIS_LOCK_EXPIRE = 60 * 10 # Lock expires in 10min (in seconds)
410+
DEFAULT_REDIS_LOCK_EXPIRE = 60 * 10 # Lock expires in 10min (in seconds)
410411

411412
# Cache
412413
CACHE_REDIS_URL = env("CACHE_REDIS_URL")
@@ -419,9 +420,10 @@ def urlparse(value) -> ParseResult:
419420
"OPTIONS": {
420421
"CLIENT_CLASS": "django_redis.client.DefaultClient",
421422
},
422-
"local-memory": {
423-
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
424-
},
423+
"KEY_PREFIX": "djc-",
424+
},
425+
"local-memory": {
426+
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
425427
},
426428
}
427429

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies = [
2222
"django-storages[s3]",
2323
"djangoql", # Admin panel query search
2424
"djangorestframework",
25+
"django-admin-autocomplete-filter",
2526
"drf-spectacular",
2627
"factory-boy~=3.2.1",
2728
"gunicorn~=20.1.0",

uv.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)