55from django .contrib import admin
66from django .contrib .auth .admin import UserAdmin as DefaultUserAdmin
77from django .contrib .sites .models import Site
8+ from django .db .models import Sum
89from django .urls import reverse
910from django .utils .html import format_html
1011from django .utils .translation import gettext_lazy as _
@@ -79,6 +80,27 @@ def _link_field(obj):
7980 return _link_field
8081
8182
83+ def display_human_size (size ):
84+ """Format a size in bytes into a human readable string."""
85+ if size is None :
86+ return "-"
87+
88+ suffixes = ["B" , "KB" , "MB" , "GB" , "TB" , "PB" ]
89+ while size >= 1024 :
90+ size /= 1024
91+ suffixes .pop (0 )
92+
93+ return f"{ size :.2f} { suffixes [0 ]} "
94+
95+
96+ def display_human_duration (duration ):
97+ """Format a duration in seconds into a human readable string."""
98+ if duration is None :
99+ return "-"
100+
101+ return str (datetime .timedelta (seconds = duration ))
102+
103+
82104class BaseFileAdmin (admin .ModelAdmin ):
83105 """Base admin class for file model."""
84106
@@ -227,8 +249,34 @@ class ConsumerSiteAdmin(admin.ModelAdmin):
227249 "video_show_download_default" ,
228250 "inactive_resources" ,
229251 "inactive_features" ,
252+ "duration_usage" ,
253+ "size_usage" ,
230254 )
231- readonly_fields = ["id" , "created_on" , "updated_on" ]
255+ readonly_fields = [
256+ "id" ,
257+ "created_on" ,
258+ "updated_on" ,
259+ "duration_usage" ,
260+ "size_usage" ,
261+ ]
262+
263+ @admin .display (description = _ ("Size" ))
264+ def size_usage (self , instance ):
265+ """Return the size of the videos in the consumer site."""
266+ size_usage = Video .objects .filter (playlist__consumer_site = instance ).aggregate (
267+ Sum ("size" )
268+ )["size__sum" ]
269+
270+ return display_human_size (size_usage )
271+
272+ @admin .display (description = _ ("Duration" ))
273+ def duration_usage (self , instance ):
274+ """Return the duration of the videos in the consumer site."""
275+ duration_usage = Video .objects .filter (
276+ playlist__consumer_site = instance
277+ ).aggregate (Sum ("duration" ))["duration__sum" ]
278+
279+ return display_human_duration (duration_usage )
232280
233281
234282class OrganizationUsersInline (admin .TabularInline ):
@@ -254,6 +302,29 @@ class OrganizationAdmin(admin.ModelAdmin):
254302 list_display = ("name" ,)
255303 inlines = [OrganizationUsersInline , OrganizationConsumerSitesInline ]
256304
305+ readonly_fields = [
306+ "duration_usage" ,
307+ "size_usage" ,
308+ ]
309+
310+ @admin .display (description = _ ("Size" ))
311+ def size_usage (self , instance ):
312+ """Return the size of the videos in the organization."""
313+ size_usage = Video .objects .filter (playlist__organization = instance ).aggregate (
314+ Sum ("size" )
315+ )["size__sum" ]
316+
317+ return display_human_size (size_usage )
318+
319+ @admin .display (description = _ ("Duration" ))
320+ def duration_usage (self , instance ):
321+ """Return the duration of the videos in the organization."""
322+ duration_usage = Video .objects .filter (
323+ playlist__organization = instance
324+ ).aggregate (Sum ("duration" ))["duration__sum" ]
325+
326+ return display_human_duration (duration_usage )
327+
257328
258329class AudioTrackInline (admin .TabularInline ):
259330 """Inline for audio tracks of a video."""
@@ -317,24 +388,12 @@ class VideoAdmin(BaseFileAdmin):
317388 @admin .display (description = _ ("Size" ))
318389 def display_size (self , obj ):
319390 """Return the size of the video."""
320- if obj .size is None :
321- return "-"
322-
323- suffixes = ["B" , "KB" , "MB" , "GB" , "TB" , "PB" ]
324- size = obj .size
325- while size >= 1024 :
326- size /= 1024
327- suffixes .pop (0 )
328-
329- return f"{ size :.2f} { suffixes [0 ]} "
391+ return display_human_size (obj .size )
330392
331393 @admin .display (description = _ ("Duration" ))
332394 def display_duration (self , obj ):
333395 """Return the duration of the video."""
334- if obj .duration is None :
335- return "-"
336-
337- return str (datetime .timedelta (seconds = obj .duration ))
396+ return display_human_duration (obj .duration )
338397
339398
340399@admin .register (TimedTextTrack )
@@ -440,13 +499,17 @@ class PlaylistAdmin(admin.ModelAdmin):
440499 "retention_duration" ,
441500 "updated_on" ,
442501 "created_on" ,
502+ "duration_usage" ,
503+ "size_usage" ,
443504 )
444505 readonly_fields = [
445506 "id" ,
446507 "created_by" ,
447508 "created_on" ,
448509 "duplicated_from" ,
449510 "updated_on" ,
511+ "duration_usage" ,
512+ "size_usage" ,
450513 ]
451514 list_filter = (
452515 "consumer_site__domain" ,
@@ -467,6 +530,24 @@ class PlaylistAdmin(admin.ModelAdmin):
467530 )
468531 verbose_name = _ ("Playlist" )
469532
533+ @admin .display (description = _ ("Size" ))
534+ def size_usage (self , instance ):
535+ """Return the size of the videos in the playlist."""
536+ size_usage = Video .objects .filter (playlist = instance ).aggregate (Sum ("size" ))[
537+ "size__sum"
538+ ]
539+
540+ return display_human_size (size_usage )
541+
542+ @admin .display (description = _ ("Duration" ))
543+ def duration_usage (self , instance ):
544+ """Return the duration of the videos in the playlist."""
545+ duration_usage = Video .objects .filter (playlist = instance ).aggregate (
546+ Sum ("duration" )
547+ )["duration__sum" ]
548+
549+ return display_human_duration (duration_usage )
550+
470551
471552@admin .register (LTIPassport )
472553class LTIPassportAdmin (admin .ModelAdmin ):
0 commit comments