svthalia/concrexit

View on GitHub
website/members/admin.py

Summary

Maintainability
A
30 mins
Test Coverage
"""Register admin pages for the models."""
import csv
import datetime

from django.contrib import admin, messages
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.db.models import Count, Q
from django.http import HttpResponse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from members import services
from members.models import EmailChange, Member

from . import forms, models


class MembershipInline(admin.StackedInline):
    model = models.Membership
    classes = ["collapse"]
    extra = 0


class ProfileInline(admin.StackedInline):
    fields = [
        "starting_year",
        "programme",
        "address_street",
        "address_street2",
        "address_postal_code",
        "address_city",
        "address_country",
        "student_number",
        "phone_number",
        "receive_optin",
        "receive_registration_confirmation",
        "receive_newsletter",
        "receive_oldmembers",
        "birthday",
        "show_birthday",
        "initials",
        "nickname",
        "display_name_preference",
        "profile_description",
        "website",
        "photo",
        "emergency_contact",
        "emergency_contact_phone_number",
        "event_permissions",
    ]
    classes = ["collapse"]
    model = models.Profile
    can_delete = False

    def get_fields(self, request, obj=None):
        fields = super().get_fields(request, obj)
        if obj and obj.is_staff:
            fields = fields + ["email_gsuite_only"]
        return fields


class MembershipTypeListFilter(admin.SimpleListFilter):
    title = _("current membership type")
    parameter_name = "membership"

    def lookups(self, request, model_admin):
        return models.Membership.MEMBERSHIP_TYPES + (("none", _("None")),)

    def queryset(self, request, queryset):
        if not self.value():
            return queryset
        if self.value() == "none":
            return queryset.exclude(
                Q(membership__until__isnull=True)
                | Q(membership__until__gt=timezone.now().date()),
                membership__isnull=False,
            )

        return queryset.exclude(membership=None).filter(
            Q(membership__until__isnull=True)
            | Q(membership__until__gt=timezone.now().date()),
            membership__type=self.value(),
        )


class AgeListFilter(admin.SimpleListFilter):
    title = _("Age")
    parameter_name = "birthday"

    def lookups(self, request, model_admin):
        return (
            ("18+", _("≥ 18")),
            ("18-", _("< 18")),
            ("unknown", _("Unknown")),
        )

    def queryset(self, request, queryset):
        if not self.value():
            return queryset

        today = datetime.date.today()
        eightteen_years_ago = today.replace(year=today.year - 18)

        if self.value() == "unknown":
            return queryset.filter(profile__birthday__isnull=True)
        if self.value() == "18+":
            return queryset.filter(profile__birthday__lte=eightteen_years_ago)
        if self.value() == "18-":
            return queryset.filter(profile__birthday__gt=eightteen_years_ago)

        return queryset


class HasPermissionsFilter(admin.SimpleListFilter):
    title = _("Has individual permissions")
    parameter_name = "permissions"

    def lookups(self, request, model_admin):
        return (
            ("yes", _("Yes")),
            ("no", _("No")),
        )

    def queryset(self, request, queryset):
        if not self.value():
            return queryset

        queryset = queryset.annotate(permission_count=Count("user_permissions"))

        if self.value() == "yes":
            return queryset.filter(permission_count__gt=0)

        return queryset.filter(permission_count=0)


class UserAdmin(BaseUserAdmin):
    form = forms.UserChangeForm
    add_form = forms.UserCreationForm

    actions = [
        "address_csv_export",
        "student_number_csv_export",
        "email_csv_export",
        "minimise_data",
    ]

    inlines = (
        ProfileInline,
        MembershipInline,
    )
    list_filter = (
        MembershipTypeListFilter,
        "is_superuser",
        HasPermissionsFilter,
        "groups",
        AgeListFilter,
        "profile__event_permissions",
        "profile__receive_optin",
        "profile__receive_newsletter",
        "profile__receive_oldmembers",
        "profile__starting_year",
    )

    fieldsets = (
        (
            _("Personal"),
            {"fields": ("first_name", "last_name", "email", "username", "password")},
        ),
        (
            _("Permissions"),
            {
                "fields": (
                    "is_active",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                    "date_joined",
                    "last_login",
                ),
                "classes": ("collapse",),
            },
        ),
    )

    # Facet counts would crash for this admin. See #3584.
    show_facets = admin.ShowFacets.NEVER

    def email_csv_export(self, request, queryset):
        response = HttpResponse(content_type="text/csv")
        response["Content-Disposition"] = 'attachment;filename="email.csv"'
        writer = csv.writer(response)
        writer.writerow([_("First name"), _("Last name"), _("Email")])
        for user in queryset:
            writer.writerow(
                [
                    user.first_name,
                    user.last_name,
                    user.email,
                ]
            )
        return response

    email_csv_export.short_description = _(
        "Download email addresses for selected users"
    )

    def address_csv_export(self, request, queryset):
        response = HttpResponse(content_type="text/csv")
        response[
            "Content-Disposition"
        ] = 'attachment;\
                                           filename="addresses.csv"'
        writer = csv.writer(response)
        writer.writerow(
            [
                _("First name"),
                _("Last name"),
                _("Address"),
                _("Address line 2"),
                _("Postal code"),
                _("City"),
                _("Country"),
            ]
        )
        for user in queryset.exclude(profile=None):
            writer.writerow(
                [
                    user.first_name,
                    user.last_name,
                    user.profile.address_street,
                    user.profile.address_street2,
                    user.profile.address_postal_code,
                    user.profile.address_city,
                    user.profile.get_address_country_display(),
                ]
            )
        return response

    address_csv_export.short_description = _("Download addresses for selected users")

    def student_number_csv_export(self, request, queryset):
        response = HttpResponse(content_type="text/csv")
        response["Content-Disposition"] = 'attachment;filename="student_numbers.csv"'
        writer = csv.writer(response)
        writer.writerow([_("First name"), _("Last name"), _("Student number")])
        for user in queryset.exclude(profile=None):
            writer.writerow(
                [user.first_name, user.last_name, user.profile.student_number]
            )
        return response

    student_number_csv_export.short_description = _(
        "Download student number export for selected users"
    )

    def minimise_data(self, request, queryset):
        processed = len(
            services.execute_data_minimisation(
                members=Member.objects.filter(pk__in=queryset)
            )
        )
        if processed == 0:
            self.message_user(
                request,
                _(
                    "Data minimisation could not be executed "
                    "for the selected user(s)."
                ),
                messages.ERROR,
            )
        else:
            self.message_user(
                request,
                _("Data minimisation was executed for {} user(s).").format(processed),
                messages.SUCCESS,
            )

    minimise_data.short_description = _("Minimise data for the selected users")


@admin.register(models.Member)
class MemberAdmin(UserAdmin):
    def has_module_permission(self, request):
        return False


admin.site.register(EmailChange)

# re-register User admin
admin.site.unregister(get_user_model())
admin.site.register(get_user_model(), UserAdmin)