From 47babf2ed73837808c2cb871af708a4960935c10 Mon Sep 17 00:00:00 2001 From: ErikKalkoken Date: Sat, 8 Feb 2020 00:51:13 +0100 Subject: [PATCH] Refactor common functions for creating admin user list, peformance tweaks --- allianceauth/authentication/admin.py | 394 ++++++++---------- .../static/authentication/css/admin.css | 2 +- allianceauth/groupmanagement/admin.py | 9 +- allianceauth/services/admin.py | 112 +---- .../services/static/services/admin.css | 2 +- 5 files changed, 179 insertions(+), 340 deletions(-) diff --git a/allianceauth/authentication/admin.py b/allianceauth/authentication/admin.py index 12ec14fa..a3df70b2 100644 --- a/allianceauth/authentication/admin.py +++ b/allianceauth/authentication/admin.py @@ -100,63 +100,153 @@ class UserProfileInline(admin.StackedInline): return False -class UserAdmin(BaseUserAdmin): +def user_profile_pic(obj): + """profile pic column data for user objects + + works for both User objects and objects with `user` as FK to User + To be used for all user based admin lists (requires CSS) """ - Extending Django's UserAdmin model + user_obj = obj.user if hasattr(obj, 'user') else obj + if user_obj.profile.main_character: + return format_html( + '', + user_obj.profile.main_character.portrait_url(size=32) + ) + else: + return '' +user_profile_pic.short_description = '' + + +def user_username(obj): + """user column data for user objects + + works for both User objects and objects with `user` as FK to User + To be used for all user based admin lists + """ + link = reverse( + 'admin:{}_{}_change'.format( + obj._meta.app_label, + type(obj).__name__.lower() + ), + args=(obj.pk,) + ) + user_obj = obj.user if hasattr(obj, 'user') else obj + return format_html( + '{}
{}', + link, + user_obj.username, + user_obj.profile.main_character.character_name \ + if user_obj.profile.main_character else '' + ) + +user_username.short_description = 'user / main' +user_username.admin_order_field = 'username' + + +def user_main_organization(obj): + """main organization column data for user objects + + works for both User objects and objects with `user` as FK to User + To be used for all user based admin lists + """ + user_obj = obj.user if hasattr(obj, 'user') else obj + if user_obj.profile.main_character: + corporation = user_obj.profile.main_character.corporation_name + else: + corporation = '' + if (user_obj.profile.main_character + and user_obj.profile.main_character.alliance_id + ): + alliance = user_obj.profile.main_character.alliance_name + else: + alliance = '' + return format_html('{}
{}', + corporation, + alliance + ) + +user_main_organization.short_description = 'Corporation / Alliance (Main)' +user_main_organization.admin_order_field = \ + 'profile__main_character__corporation_name' + + +class MainCorporationsFilter(admin.SimpleListFilter): + """Custom filter to filter on corporations from mains only + + works for both User objects and objects with `user` as FK to User + To be used for all user based admin lists + """ + title = 'corporation' + parameter_name = 'main_corporations' + + def lookups(self, request, model_admin): + qs = EveCharacter.objects\ + .exclude(userprofile=None)\ + .values('corporation_id', 'corporation_name')\ + .distinct()\ + .order_by(Lower('corporation_name')) + return tuple( + [(x['corporation_id'], x['corporation_name']) for x in qs] + ) + + def queryset(self, request, qs): + if self.value() is None: + return qs.all() + else: + if qs.model == User: + return qs\ + .filter(profile__main_character__corporation_id=\ + self.value()) + else: + return qs\ + .filter(user__profile__main_character__corporation_id=\ + self.value()) + + +class MainAllianceFilter(admin.SimpleListFilter): + """Custom filter to filter on alliances from mains only + + works for both User objects and objects with `user` as FK to User + To be used for all user based admin lists + """ + title = 'alliance' + parameter_name = 'main_alliances' + + def lookups(self, request, model_admin): + qs = EveCharacter.objects\ + .exclude(alliance_id=None)\ + .exclude(userprofile=None)\ + .values('alliance_id', 'alliance_name')\ + .distinct()\ + .order_by(Lower('alliance_name')) + return tuple( + [(x['alliance_id'], x['alliance_name']) for x in qs] + ) + + def queryset(self, request, qs): + if self.value() is None: + return qs.all() + else: + if qs.model == User: + return qs\ + .filter(profile__main_character__alliance_id=self.value()) + else: + return qs\ + .filter(user__profile__main_character__alliance_id=\ + self.value()) + + +class UserAdmin(BaseUserAdmin): + """Extending Django's UserAdmin model + + Behavior of groups and characters columns can be configured via settings + """ class Media: css = { "all": ("authentication/css/admin.css",) } - - - class MainCorporationsFilter(admin.SimpleListFilter): - """Custom filter to filter on corporations from mains only""" - title = 'corporation' - parameter_name = 'main_corporations' - - def lookups(self, request, model_admin): - qs = EveCharacter.objects\ - .exclude(userprofile=None)\ - .values('corporation_id', 'corporation_name')\ - .distinct()\ - .order_by(Lower('corporation_name')) - return tuple( - [(x['corporation_id'], x['corporation_name']) for x in qs] - ) - - def queryset(self, request, queryset): - if self.value() is None: - return queryset.all() - else: - return queryset\ - .filter(profile__main_character__corporation_id=self.value()) - - - class MainAllianceFilter(admin.SimpleListFilter): - """Custom filter to filter on alliances from mains only""" - title = 'alliance' - parameter_name = 'main_alliances' - - def lookups(self, request, model_admin): - qs = EveCharacter.objects\ - .exclude(alliance_id=None)\ - .exclude(userprofile=None)\ - .values('alliance_id', 'alliance_name')\ - .distinct()\ - .order_by(Lower('alliance_name')) - return tuple( - [(x['alliance_id'], x['alliance_name']) for x in qs] - ) - - def queryset(self, request, queryset): - if self.value() is None: - return queryset.all() - else: - return queryset\ - .filter(profile__main_character__alliance_id=self.value()) - class RealGroupsFilter(admin.SimpleListFilter): """Custom filter to get groups w/o Autogroups""" @@ -167,8 +257,8 @@ class UserAdmin(BaseUserAdmin): qs = Group.objects.all().order_by(Lower('name')) if _has_auto_groups: qs = qs\ - .filter(managedalliancegroup__exact=None)\ - .filter(managedcorpgroup__exact=None) + .filter(managedalliancegroup__isnull=True)\ + .filter(managedcorpgroup__isnull=True) return tuple([(x.pk, x.name) for x in qs]) def queryset(self, request, queryset): @@ -208,20 +298,24 @@ class UserAdmin(BaseUserAdmin): # Check update_groups is redefined/overloaded if svc.update_groups.__module__ != ServicesHook.update_groups.__module__: action = make_service_hooks_update_groups_action(svc) - actions[action.__name__] = (action, - action.__name__, - action.short_description) + actions[action.__name__] = ( + action, + action.__name__, + action.short_description + ) + # Create sync nickname action if service implements it if svc.sync_nickname.__module__ != ServicesHook.sync_nickname.__module__: action = make_service_hooks_sync_nickname_action(svc) - actions[action.__name__] = (action, - action.__name__, - action.short_description) - + actions[action.__name__] = ( + action, action.__name__, + action.short_description + ) return actions + def _list_2_html_w_tooltips(self, my_items: list, max_items: int) -> str: - """converts list of strings into HTML with cutoff and tooltip when > max""" + """converts list of strings into HTML with cutoff and tooltip""" items_truncated_str = ', '.join(my_items[:max_items]) if len(my_items) <= max_items: return items_truncated_str @@ -242,11 +336,11 @@ class UserAdmin(BaseUserAdmin): show_full_result_count = True list_display = ( - '_profile_pic', - '_user', + user_profile_pic, + user_username, '_state', '_groups', - '_main_organization', + user_main_organization, '_characters', 'is_active', 'date_joined', @@ -269,67 +363,13 @@ class UserAdmin(BaseUserAdmin): 'character_ownerships__character__character_name' ) - - def _profile_pic(self, obj): - """profile pic column data for user objects""" - if obj.profile.main_character: - return format_html( - '', - obj.profile.main_character.portrait_url(size=32) - ) - else: - return '' - _profile_pic.short_description = '' - - - def _user(self, obj): - """user column data for user objects""" - link = reverse( - 'admin:{}_{}_change'.format( - obj._meta.app_label, - type(obj).__name__.lower() - ), - args=(obj.pk,) - ) - return format_html( - '{}
{}', - link, - obj.username, - obj.profile.main_character.character_name \ - if obj.profile.main_character else '' - ) - - _user.short_description = 'user / main' - _user.admin_order_field = 'username' - - - def _main_organization(self, obj): - """main organization column data for user objects""" - if obj.profile.main_character: - corporation = obj.profile.main_character.corporation_name - else: - corporation = '' - if (obj.profile.main_character - and obj.profile.main_character.alliance_id - ): - alliance = obj.profile.main_character.alliance_name - else: - alliance = '' - return format_html('{}
{}', - corporation, - alliance - ) - - _main_organization.short_description = 'Corporation / Alliance (Main)' - _main_organization.admin_order_field = \ - 'profile__main_character__corporation_name' - def _characters(self, obj): my_characters = [ x.character.character_name for x in CharacterOwnership.objects\ .filter(user=obj)\ - .order_by('character__character_name') + .order_by('character__character_name')\ + .select_related() ] return self._list_2_html_w_tooltips( my_characters, @@ -352,8 +392,8 @@ class UserAdmin(BaseUserAdmin): else: my_groups = [ x.name for x in obj.groups\ - .filter(managedalliancegroup=None)\ - .filter(managedcorpgroup=None)\ + .filter(managedalliancegroup__isnull=True)\ + .filter(managedcorpgroup__isnull=True)\ .order_by('name') ] @@ -425,61 +465,12 @@ class BaseOwnershipAdmin(admin.ModelAdmin): css = { "all": ("authentication/css/admin.css",) } - - class MainCorporationsFilter(admin.SimpleListFilter): - """Custom filter to filter on corporations from mains only""" - title = 'corporation' - parameter_name = 'main_corporations' - - def lookups(self, request, model_admin): - qs = EveCharacter.objects\ - .exclude(userprofile=None)\ - .values('corporation_id', 'corporation_name')\ - .distinct()\ - .order_by(Lower('corporation_name')) - return tuple( - [(x['corporation_id'], x['corporation_name']) for x in qs] - ) - - def queryset(self, request, queryset): - if self.value() is None: - return queryset.all() - else: - return queryset.filter( - user__profile__main_character__corporation_id=self.value() - ) - - - class MainAllianceFilter(admin.SimpleListFilter): - """Custom filter to filter on alliances from mains only""" - title = 'alliance' - parameter_name = 'main_alliances' - - def lookups(self, request, model_admin): - qs = EveCharacter.objects\ - .exclude(alliance_id=None)\ - .exclude(userprofile=None)\ - .values('alliance_id', 'alliance_name')\ - .distinct()\ - .order_by(Lower('alliance_name')) - return tuple( - [(x['alliance_id'], x['alliance_name']) for x in qs] - ) - - def queryset(self, request, queryset): - if self.value() is None: - return queryset.all() - else: - return queryset.filter( - user__profile__main_character__alliance_id=self.value() - ) - - + list_select_related = True list_display = ( - '_profile_pic', - '_user', - '_main_organization', + user_profile_pic, + user_username, + user_main_organization, 'character', ) search_fields = ( @@ -498,61 +489,6 @@ class BaseOwnershipAdmin(admin.ModelAdmin): return 'owner_hash', 'character' return tuple() - - def _profile_pic(self, obj): - """profile pic column data for user objects""" - if obj.user.profile.main_character: - return format_html( - '', - obj.user.profile.main_character.portrait_url(size=32) - ) - else: - return '' - _profile_pic.short_description = '' - - - def _user(self, obj): - """user column data for user objects""" - link = reverse( - 'admin:{}_{}_change'.format( - obj._meta.app_label, - type(obj).__name__.lower() - ), - args=(obj.pk,) - ) - return format_html( - '{}
{}', - link, - obj.user.username, - obj.user.profile.main_character.character_name \ - if obj.user.profile.main_character else '' - ) - - _user.short_description = 'user / main' - _user.admin_order_field = 'user__username' - - - def _main_organization(self, obj): - """main organization column data for user objects""" - if obj.user.profile.main_character: - corporation = obj.user.profile.main_character.corporation_name - else: - corporation = '' - if (obj.user.profile.main_character - and obj.user.profile.main_character.alliance_id - ): - alliance = obj.user.profile.main_character.alliance_name - else: - alliance = '' - return format_html('{}
{}', - corporation, - alliance - ) - - _main_organization.short_description = 'Corporation / Alliance (Main)' - _main_organization.admin_order_field = \ - 'user__profile__main_character__corporation_name' - @admin.register(OwnershipRecord) class OwnershipRecordAdmin(BaseOwnershipAdmin): diff --git a/allianceauth/authentication/static/authentication/css/admin.css b/allianceauth/authentication/static/authentication/css/admin.css index b3b2a8f0..5fe8a4d8 100644 --- a/allianceauth/authentication/static/authentication/css/admin.css +++ b/allianceauth/authentication/static/authentication/css/admin.css @@ -4,7 +4,7 @@ CSS for allianceauth admin site /* styling for profile pic */ .img-circle { border-radius: 50%; } -.column-_profile_pic { width: 50px; } +.column-user_profile_pic { width: 50px; } /* tooltip */ .tooltip { diff --git a/allianceauth/groupmanagement/admin.py b/allianceauth/groupmanagement/admin.py index 5a8cf5e6..6d1a7ef0 100644 --- a/allianceauth/groupmanagement/admin.py +++ b/allianceauth/groupmanagement/admin.py @@ -50,11 +50,14 @@ if _has_auto_groups: value = self.value() if value == 'Yes': return queryset.exclude( - managedalliancegroup__exact=None, - managedcorpgroup__exact=None + managedalliancegroup__isnull=True, + managedcorpgroup__isnull=True ) elif value == 'No': - return queryset.filter(managedalliancegroup__exact=None).filter(managedcorpgroup__exact=None) + return queryset.filter( + managedalliancegroup__isnull=True, + managedcorpgroup__isnull=True + ) else: return queryset diff --git a/allianceauth/services/admin.py b/allianceauth/services/admin.py index 97e6f1d6..061b97e1 100644 --- a/allianceauth/services/admin.py +++ b/allianceauth/services/admin.py @@ -6,61 +6,13 @@ from django.utils.html import format_html from allianceauth import hooks from allianceauth.eveonline.models import EveCharacter +from allianceauth.authentication.admin import user_profile_pic, \ + user_username, user_main_organization, MainCorporationsFilter,\ + MainAllianceFilter from .models import NameFormatConfig -class MainCorporationsFilter(admin.SimpleListFilter): - """Custom filter to show corporations from service users only - To be used together with ServicesUserAdmin class - """ - title = 'corporation' - parameter_name = 'main_corporations' - - def lookups(self, request, model_admin): - qs = EveCharacter.objects\ - .exclude(userprofile=None)\ - .values('corporation_id', 'corporation_name')\ - .distinct()\ - .order_by(Lower('corporation_name')) - return tuple( - [(x['corporation_id'], x['corporation_name']) for x in qs] - ) - - def queryset(self, request, queryset): - if self.value() is None: - return queryset.all() - else: - return queryset\ - .filter(user__profile__main_character__corporation_id=self.value()) - - -class MainAllianceFilter(admin.SimpleListFilter): - """Custom filter to show alliances from service users only - To be used together with ServicesUserAdmin class - """ - title = 'alliance' - parameter_name = 'main_alliances' - - def lookups(self, request, model_admin): - qs = EveCharacter.objects\ - .exclude(alliance_id=None)\ - .exclude(userprofile=None)\ - .values('alliance_id', 'alliance_name')\ - .distinct()\ - .order_by(Lower('alliance_name')) - return tuple( - [(x['alliance_id'], x['alliance_name']) for x in qs] - ) - - def queryset(self, request, queryset): - if self.value() is None: - return queryset.all() - else: - return queryset\ - .filter(user__profile__main_character__alliance_id=self.value()) - - class ServicesUserAdmin(admin.ModelAdmin): """Parent class for UserAdmin classes for all services""" class Media: @@ -75,9 +27,9 @@ class ServicesUserAdmin(admin.ModelAdmin): ordering = ('user__username', ) list_select_related = True list_display = ( - '_profile_pic', - '_user', - '_main_organization', + user_profile_pic, + user_username, + user_main_organization, '_date_joined' ) list_filter = ( @@ -86,58 +38,6 @@ class ServicesUserAdmin(admin.ModelAdmin): 'user__date_joined' ) - def _profile_pic(self, obj): - if obj.user.profile.main_character: - return format_html( - '', - obj.user.profile.main_character.portrait_url(size=32) - ) - else: - return '' - _profile_pic.short_description = '' - - - def _user(self, obj): - link = reverse( - 'admin:{}_{}_change'.format( - obj._meta.app_label, - type(obj).__name__.lower() - ), - args=(obj.pk,) - ) - return format_html( - '{}
{}', - link, - obj.user.username, - obj.user.profile.main_character.character_name \ - if obj.user.profile.main_character else '' - ) - - _user.short_description = 'user / main' - _user.admin_order_field = 'user__username' - - - def _main_organization(self, obj): - if obj.user.profile.main_character: - corporation = obj.user.profile.main_character.corporation_name - else: - corporation = '' - if (obj.user.profile.main_character - and obj.user.profile.main_character.alliance_id - ): - alliance = obj.user.profile.main_character.alliance_name - else: - alliance = '' - return format_html('{}
{}', - corporation, - alliance - ) - - _main_organization.short_description = 'Corporation / Alliance (Main)' - _main_organization.admin_order_field = \ - 'profile__main_character__corporation_name' - - def _date_joined(self, obj): return obj.user.date_joined diff --git a/allianceauth/services/static/services/admin.css b/allianceauth/services/static/services/admin.css index 72fedd99..46796c5f 100644 --- a/allianceauth/services/static/services/admin.css +++ b/allianceauth/services/static/services/admin.css @@ -3,4 +3,4 @@ CSS for allianceauth admin site */ .img-circle { border-radius: 50%; } -.column-_profile_pic { width: 50px; } \ No newline at end of file +.column-user_profile_pic { width: 50px; } \ No newline at end of file