Admin: per-user activity file (logins, logouts, orders) with CSV export

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 12:17:11 +03:30
parent dbf0ff52d2
commit bda415e579
4 changed files with 143 additions and 1 deletions

View File

@@ -51,9 +51,52 @@ class SendSMSForm(forms.Form):
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ('id', 'public_id', 'name', 'phone', 'grade_id', 'created_at')
list_display = ('id', 'public_id', 'name', 'phone', 'grade_id', 'created_at', 'activity_link')
search_fields = ('name', 'phone')
@admin.display(description='پرونده')
def activity_link(self, obj):
from django.urls import reverse
from django.utils.html import format_html
return format_html('<a href="{}">فعالیت</a>', reverse('admin:accounts_user_activity', args=[obj.pk]))
def get_urls(self):
from django.urls import path
return [
path('<int:user_id>/activity/', self.admin_site.admin_view(self.activity_view),
name='accounts_user_activity'),
] + super().get_urls()
def activity_view(self, request, user_id):
"""ورود/خروج و خریدهای یک کاربر؛ ?format=csv برای اکسل. فقط با اجازه‌ی دیدنِ کاربر و ورود و سفارش."""
import csv
from django.http import Http404, HttpResponse
from django.shortcuts import render
from payment.models import Order
from .activity import COLUMNS, user_activity
from .public_id import encode
perms = ('accounts.view_user', 'accounts.view_loginevent', 'payment.view_order')
if not all(request.user.has_perm(p) for p in perms):
raise PermissionDenied
user = User.objects.filter(pk=user_id).first()
if user is None:
raise Http404
rows = user_activity(user)
if request.GET.get('format') == 'csv':
response = HttpResponse(content_type='text/csv; charset=utf-8')
response['Content-Disposition'] = f'attachment; filename="user-{user.pk}-activity.csv"'
response.write('\ufeff') # BOM: اکسل بی‌آن فارسی را خراب می‌خواند
writer = csv.writer(response)
writer.writerow(COLUMNS)
for r in rows:
writer.writerow([r['at_local'], r['kind'], r['detail'], r['amount'], r['ip']])
return response
return render(request, 'admin/accounts_user_activity.html', {
**self.admin_site.each_context(request), 'target': user, 'public_id': encode(user.pk),
'rows': rows, 'columns': COLUMNS, 'title': 'پرونده‌ی کاربر',
})
@admin.display(description='شناسه‌ی نمایشی')
def public_id(self, obj):
from .public_id import encode