"""Audit: every COURSE/VIDEO resource should carry >=2 checkpoints. Read-only. Exits nonzero (listing offenders) when any in-scope resource has fewer, so it can gate CI / a release check. DOCS and ARTICLE are exempt — a reference site's table of contents isn't a learning sequence, so those keep the single manual-completion checkbox. """ import sys from django.core.management.base import BaseCommand from django.db.models import Count from apps.resources.models import Resource SCOPED_TYPES = ("COURSE", "VIDEO") class Command(BaseCommand): help = ( "Verify every COURSE/VIDEO resource has at least --min checkpoints " "(default 2). Exits 1 and lists offenders if not. DOCS/ARTICLE exempt." ) def add_arguments(self, parser): parser.add_argument( "--min", type=int, default=2, help="Minimum checkpoints required per COURSE/VIDEO resource.", ) def handle(self, *args, **options): minimum = options["min"] scoped = Resource.objects.filter(type__in=SCOPED_TYPES) total = scoped.count() offenders = list( scoped.annotate(n=Count("checkpoints")) .filter(n__lt=minimum) .order_by("type", "title") ) if not offenders: self.stdout.write(self.style.SUCCESS( f"OK: all {total} COURSE/VIDEO resources have >={minimum} checkpoints." )) return self.stdout.write(self.style.ERROR( f"{len(offenders)} of {total} COURSE/VIDEO resources have " f"<{minimum} checkpoints:" )) for r in offenders: self.stdout.write( f" [{r.type}] {r.title} ({r.provider}) - {r.n} checkpoint(s)" ) sys.exit(1)