Coverage for baibu/users/admin.py: 98%

84 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-10-06 13:07 +0000

1from allauth.account.decorators import secure_admin_login 

2from django import forms 

3from django.conf import settings 

4from django.contrib import admin 

5from django.contrib.auth import admin as auth_admin 

6from django.utils import timezone 

7from django.utils.translation import gettext_lazy as _ 

8 

9from .forms import UserAdminChangeForm 

10from .forms import UserAdminCreationForm 

11from .models import AccountDeletionRequest 

12from .models import ConsentRecord 

13from .models import User 

14 

15if settings.DJANGO_ADMIN_FORCE_ALLAUTH: 

16 admin.autodiscover() 

17 admin.site.login = secure_admin_login(admin.site.login) # type: ignore[method-assign] 

18 

19 

20class ConsentRecordInline(admin.TabularInline): 

21 model = ConsentRecord 

22 extra = 0 

23 can_delete = False 

24 fields = ("scope", "tier", "source", "granted_at", "revoked_at") 

25 readonly_fields = fields 

26 

27 def has_add_permission(self, request, obj=None): 

28 return False 

29 

30 

31@admin.register(User) 

32class UserAdmin(auth_admin.UserAdmin): 

33 form = UserAdminChangeForm 

34 add_form = UserAdminCreationForm 

35 inlines = (ConsentRecordInline,) 

36 fieldsets = ( 

37 (None, {"fields": ("email", "phone", "phone_verified", "password")}), 

38 (_("Profile"), {"fields": ("name", "city", "country")}), 

39 ( 

40 _("Permissions"), 

41 {"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")}, 

42 ), 

43 (_("Important dates"), {"fields": ("last_login", "date_joined")}), 

44 ) 

45 add_fieldsets = ((None, {"classes": ("wide",), "fields": ("email", "password1", "password2")}),) 

46 list_display = ("email", "name", "is_active", "is_staff", "date_joined") 

47 list_filter = ("is_active", "is_staff", "is_superuser") 

48 search_fields = ("email", "name", "phone") 

49 ordering = ("-date_joined",) 

50 

51 def get_inlines(self, request, obj): 

52 # Consent history only exists once the user does. 

53 return self.inlines if obj else () 

54 

55 

56@admin.register(ConsentRecord) 

57class ConsentRecordAdmin(admin.ModelAdmin): 

58 """Read-only: consent history is append-only.""" 

59 

60 list_display = ("user", "scope", "tier", "source", "granted_at", "revoked_at") 

61 list_filter = ("scope", "tier", "source") 

62 search_fields = ("user__email", "user__name") 

63 readonly_fields = ("id", "user", "tier", "source", "scope", "granted_at", "revoked_at", "metadata", "created_at") 

64 

65 def has_add_permission(self, request): 

66 return False 

67 

68 def has_change_permission(self, request, obj=None): 

69 return False 

70 

71 def has_delete_permission(self, request, obj=None): 

72 return False 

73 

74 

75class AccountDeletionRequestAdminForm(forms.ModelForm): 

76 class Meta: 

77 model = AccountDeletionRequest 

78 fields = ("status", "resolution_notes") 

79 

80 def clean_status(self): 

81 new_status = self.cleaned_data["status"] 

82 current = AccountDeletionRequest.objects.get(pk=self.instance.pk) 

83 if not current.can_transition_to(new_status): 

84 raise forms.ValidationError( 

85 _("A request cannot move from %(old)s to %(new)s."), 

86 params={"old": current.get_status_display(), "new": new_status}, 

87 ) 

88 return new_status 

89 

90 

91@admin.register(AccountDeletionRequest) 

92class AccountDeletionRequestAdmin(admin.ModelAdmin): 

93 form = AccountDeletionRequestAdminForm 

94 list_display = ("id", "status", "source", "requester_email", "requested_at", "completed_at") 

95 list_filter = ("status", "source") 

96 search_fields = ("id", "requester_email", "requester_name", "user__email") 

97 readonly_fields = ( 

98 "id", 

99 "source", 

100 "user", 

101 "requester_name", 

102 "requester_email", 

103 "request_details", 

104 "metadata", 

105 "requested_at", 

106 "updated_at", 

107 "completed_at", 

108 ) 

109 fields = (*readonly_fields, "status", "resolution_notes") 

110 actions = ("mark_completed",) 

111 

112 def has_add_permission(self, request): 

113 return False 

114 

115 def save_model(self, request, obj, form, change): 

116 if obj.status == AccountDeletionRequest.Status.COMPLETED and obj.completed_at is None: 

117 obj.completed_at = timezone.now() 

118 obj.clear_personal_details() 

119 super().save_model(request, obj, form, change) 

120 

121 @admin.action(description=_("Mark selected in-progress requests as completed")) 

122 def mark_completed(self, request, queryset): 

123 updated = 0 

124 for deletion_request in queryset.filter(status=AccountDeletionRequest.Status.IN_PROGRESS): 

125 deletion_request.status = AccountDeletionRequest.Status.COMPLETED 

126 deletion_request.completed_at = timezone.now() 

127 deletion_request.clear_personal_details() 

128 deletion_request.save() 

129 updated += 1 

130 self.message_user(request, _("%(count)d request(s) marked completed.") % {"count": updated})