Skip to content

Commit

Permalink
Alter Django Admin media views to surface content reports (#4254)
Browse files Browse the repository at this point in the history
* Use a more ambiguous related name for reports

* Change the media views to order records by total report count

* Use plural name for media_reports reference

* Let media ID be used for autocomplete, and return unaltered queryset

* Add more fields to sort on

* Add links to the pending reports in a column

* Rename to "total report count"

* Expose the decisions tables (for now)

* Add filtering based on pending reports

* More tweaks to table

* Make pending the default filter rather than all

* Revert back to media-specific tables

* Wording and whitespace corrections

Co-authored-by: sarayourfriend <git@sarayourfriend.pictures>

---------

Co-authored-by: sarayourfriend <git@sarayourfriend.pictures>
  • Loading branch information
2 people authored and obulat committed May 8, 2024
1 parent e0617e3 commit 4d89ad8
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 8 deletions.
18 changes: 10 additions & 8 deletions api/api/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

from api.admin.forms import UserPreferencesAdminForm
from api.admin.media_report import (
AudioListViewAdmin,
AudioReportAdmin,
ImageListViewAdmin,
ImageReportAdmin,
MediaReportAdmin,
MediaSubreportAdmin,
Expand All @@ -22,6 +24,8 @@
ImageReport,
UserPreferences,
)
from api.models.audio import AudioDecision
from api.models.image import ImageDecision
from api.models.media import AbstractDeletedMedia, AbstractSensitiveMedia
from api.models.oauth import ThrottledApplication

Expand All @@ -35,14 +39,8 @@
admin.site.register(Group, GroupAdmin)


@admin.register(Image)
class ImageAdmin(admin.ModelAdmin):
search_fields = ("identifier",)


@admin.register(Audio)
class AudioAdmin(admin.ModelAdmin):
search_fields = ("identifier",)
admin.site.register(Image, ImageListViewAdmin)
admin.site.register(Audio, AudioListViewAdmin)


# Register the MediaReportAdmin classes and its subclasses
Expand All @@ -56,6 +54,10 @@ class AudioAdmin(admin.ModelAdmin):
]:
admin.site.register(klass, MediaSubreportAdmin)

# Temporary addition of model admin for decisions while this view gets built
admin.site.register(ImageDecision, admin.ModelAdmin)
admin.site.register(AudioDecision, admin.ModelAdmin)


@admin.register(ContentProvider)
class ProviderAdmin(admin.ModelAdmin):
Expand Down
115 changes: 115 additions & 0 deletions api/api/admin/media_report.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from django.conf import settings
from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from django.db.models import Count, F, Min
from django.urls import reverse
from django.utils.html import format_html
from django.utils.safestring import mark_safe

import structlog
from elasticsearch import NotFoundError
Expand All @@ -12,6 +17,108 @@
logger = structlog.get_logger(__name__)


class PredeterminedOrderChangelist(ChangeList):
"""
ChangeList class which does not apply any default ordering to the items.
This is necessary for lists where the ordering is done on an annotated field, since
the changelist attempts to apply the ordering to a QuerySet which is not aware that
it has the annotated field available (and thus raises a FieldError).
The caveat to this is that the ordering *must* be applied in
ModelAdmin::get_queryset
"""

def _get_default_ordering(self):
return []


class PendingRecordCountFilter(admin.SimpleListFilter):
title = "pending record count"
parameter_name = "pending_record_count"

def choices(self, changelist):
"""Set default to "pending" rather than "all"."""
choices = list(super().choices(changelist))
choices[0]["display"] = "Pending"
return choices

def lookups(self, request, model_admin):
return (("all", "All"),)

def queryset(self, request, queryset):
value = self.value()
if value != "all":
return queryset.filter(pending_report_count__gt=0)

return queryset


class MediaListAdmin(admin.ModelAdmin):
list_display = (
"identifier",
"total_report_count",
"pending_report_count",
"oldest_report_date",
"pending_reports_links",
)
list_filter = (PendingRecordCountFilter,)
# Disable link display for images
list_display_links = None
# Allow autocomplete to work from other referenced fields
search_fields = ("identifier",)
media_type = None
# Ordering is not set here, see get_queryset

def total_report_count(self, obj):
return obj.total_report_count

def pending_report_count(self, obj):
return obj.pending_report_count

def oldest_report_date(self, obj):
return obj.oldest_report_date

def pending_reports_links(self, obj):
reports = getattr(obj, f"{self.media_type}_report")
pending_reports = reports.filter(decision__isnull=True)
data = []
for report in pending_reports.all():
url = reverse(
f"admin:api_{self.media_type}report_change", args=(report.id,)
)
data.append(format_html('<a href="{}">Report {}</a>', url, report.id))

return mark_safe(", ".join(data))

def get_queryset(self, request):
qs = super().get_queryset(request)
# Return all available image if this is for an autocomplete request
if "autocomplete" in request.path:
return qs

# Filter down to only instances with reports
qs = qs.filter(**{f"{self.media_type}_report__isnull": False})
# Annotate and order by report count
qs = qs.annotate(total_report_count=Count(f"{self.media_type}_report"))
# Show total pending reports by subtracting the number of reports
# from the number of reports that have decisions
qs = qs.annotate(
pending_report_count=F("total_report_count")
- Count(f"{self.media_type}_report__decision__pk")
)
qs = qs.annotate(
oldest_report_date=Min(f"{self.media_type}_report__created_at")
)
qs = qs.order_by(
"-total_report_count", "-pending_report_count", "oldest_report_date"
)
return qs

def get_changelist(self, request, **kwargs):
return PredeterminedOrderChangelist


class MediaReportAdmin(admin.ModelAdmin):
change_form_template = "admin/api/media_report/change_form.html"
list_display = ("id", "reason", "is_pending", "description", "created_at", "url")
Expand Down Expand Up @@ -156,6 +263,14 @@ class AudioReportAdmin(MediaReportAdmin):
media_type = "audio"


class ImageListViewAdmin(MediaListAdmin):
media_type = "image"


class AudioListViewAdmin(MediaListAdmin):
media_type = "audio"


class MediaSubreportAdmin(admin.ModelAdmin):
exclude = ("media_obj",)
search_fields = ("media_obj__identifier",)
Expand Down

0 comments on commit 4d89ad8

Please sign in to comment.