Files
PYTV/core/management/commands/state.py
2026-03-08 16:48:58 -04:00

110 lines
5.2 KiB
Python

from django.core.management.base import BaseCommand
from core.models import AppUser, Library, Channel, MediaItem, Airing, ScheduleTemplate
from core.services.scheduler import ScheduleGenerator
from django.utils import timezone
from datetime import timedelta, date
import textwrap
class Command(BaseCommand):
help = "Displays a beautifully formatted terminal dashboard of the current backend state."
def add_arguments(self, parser):
parser.add_argument('--channel', type=int, help='Inspect specific channel schedule')
parser.add_argument('--test-generate', action='store_true', help='Trigger generation for today if inspecting a channel')
def get_color(self, text, code):
"""Helper to wrap string in bash color codes"""
return f"\033[{code}m{text}\033[0m"
def handle(self, *args, **options):
channel_id = options.get('channel')
test_generate = options.get('test_generate')
if channel_id:
self.inspect_channel(channel_id, test_generate)
return
# 1. Gather Aggregate Metrics
total_users = AppUser.objects.count()
total_libraries = Library.objects.count()
total_channels = Channel.objects.count()
total_media = MediaItem.objects.count()
total_templates = ScheduleTemplate.objects.count()
# Gather near-term Airing metrics
now = timezone.now()
tomorrow = now + timedelta(days=1)
airings_today = Airing.objects.filter(starts_at__gte=now, starts_at__lt=tomorrow).count()
airings_total = Airing.objects.count()
self.stdout.write(self.get_color("\n=== PYTV Backend State Dashboard ===", "1;34"))
self.stdout.write(f"\n{self.get_color('Core Metrics:', '1;36')}")
self.stdout.write(f" Users: {self.get_color(str(total_users), '1;32')}")
self.stdout.write(f" Libraries: {self.get_color(str(total_libraries), '1;32')}")
self.stdout.write(f" Media Items: {self.get_color(str(total_media), '1;32')}")
self.stdout.write(f" Channels: {self.get_color(str(total_channels), '1;32')}")
self.stdout.write(f"\n{self.get_color('Scheduling Engine:', '1;36')}")
self.stdout.write(f" Active Templates: {self.get_color(str(total_templates), '1;33')}")
self.stdout.write(f" Airings (Next 24h): {self.get_color(str(airings_today), '1;33')}")
self.stdout.write(f" Airings (Total): {self.get_color(str(airings_total), '1;33')}")
# 2. Build Tree View of Channels
self.stdout.write(f"\n{self.get_color('Channel Tree:', '1;36')}")
channels = Channel.objects.prefetch_related('scheduletemplate_set').all()
if not channels:
self.stdout.write(" (No channels configured)")
for c in channels:
status_color = "1;32" if c.is_active else "1;31"
status_text = "ACTIVE" if c.is_active else "INACTIVE"
self.stdout.write(f"\n 📺 [{c.id}] {c.name} (Ch {c.channel_number or '-'}) ({self.get_color(status_text, status_color)})")
# Show templates
templates = c.scheduletemplate_set.filter(is_active=True).order_by('-priority')
if not templates:
self.stdout.write(" ⚠️ No active schedule templates.")
else:
for t in templates:
blocks_count = t.scheduleblock_set.count()
self.stdout.write(f" 📄 Template: {t.name} (Priority {t.priority}) -> {blocks_count} Blocks")
self.stdout.write(f"\nUse {self.get_color('--channel <id>', '1;37')} to inspect detailed schedule.\n")
def inspect_channel(self, channel_id, test_generate):
try:
channel = Channel.objects.get(id=channel_id)
except Channel.DoesNotExist:
self.stdout.write(self.get_color(f"Error: Channel {channel_id} not found.", "1;31"))
return
if test_generate:
self.stdout.write(self.get_color(f"\nTriggering schedule generation for {channel.name}...", "1;33"))
generator = ScheduleGenerator(channel)
count = generator.generate_for_date(date.today())
self.stdout.write(f"Done. Created {self.get_color(str(count), '1;32')} new airings.")
now = timezone.now()
end_window = now + timedelta(hours=12)
airings = Airing.objects.filter(
channel=channel,
ends_at__gt=now,
starts_at__lt=end_window
).select_related('media_item').order_by('starts_at')
self.stdout.write(self.get_color(f"\n=== Schedule for {channel.name} (Next 12h) ===", "1;34"))
if not airings:
self.stdout.write(self.get_color(" (No airings scheduled in this window)", "1;33"))
else:
for a in airings:
time_str = f"{a.starts_at.strftime('%H:%M')} - {a.ends_at.strftime('%H:%M')}"
if a.starts_at <= now <= a.ends_at:
self.stdout.write(f" {self.get_color('▶ ON AIR', '1;32')} {self.get_color(time_str, '1;37')} | {a.media_item.title}")
else:
self.stdout.write(f" {time_str} | {a.media_item.title}")
self.stdout.write("\n")