File indexing completed on 2026-10-20 10:15:19
0001 """Prepare one existing buffered incident for explicit TeamComms publication."""
0002
0003 import json
0004 import os
0005 from pathlib import Path
0006
0007 from django.core.management.base import BaseCommand, CommandError
0008 from django.utils import timezone
0009 from teamcomms.comms.schemas import Audience
0010
0011 from monitor_app.models import AppLog, CapcomNotice
0012
0013
0014 class Command(BaseCommand):
0015 help = "Prepare one observed SWF notice; does not publish or trigger work."
0016
0017 def add_arguments(self, parser):
0018 parser.add_argument("--event-id", type=int, required=True, help="Existing AppLog ID")
0019 parser.add_argument("--subscriber", default="capcom")
0020 parser.add_argument("--source", default="swf-monitor:epic-devcloud.org/prod",
0021 help="Stable installation namespace; retain it for retries")
0022 parser.add_argument("--audience", required=True, help="Explicit TC audience JSON object")
0023 parser.add_argument("--topic", default="", help="Optional TC message topic")
0024 parser.add_argument("--output", required=True, help="New private JSON payload file")
0025
0026 def handle(self, *args, **options):
0027 event_id, subscriber = options["event_id"], options["subscriber"]
0028 if event_id <= 0 or not subscriber or len(subscriber) > 100:
0029 raise CommandError("Select a positive event ID and a valid subscriber")
0030 source = options["source"]
0031 if not source or len(source) > 160:
0032 raise CommandError("Source namespace must contain 1–160 characters")
0033 if len(options["topic"]) > 160:
0034 raise CommandError("Topic must contain at most 160 characters")
0035 try:
0036 audience = Audience.model_validate(json.loads(options["audience"]))
0037 except ValueError as error:
0038 raise CommandError("Audience must be a valid, explicit TC audience JSON object") from error
0039 dedup_key = f"event:{event_id}:{subscriber}"
0040
0041 rows = list(CapcomNotice.objects.filter(
0042 subscriber=subscriber, dedup_key=dedup_key).order_by("id")[:2])
0043 if len(rows) != 1:
0044 raise CommandError("Expected exactly one buffered notice for this event/subscriber")
0045 notice = rows[0]
0046 event = AppLog.objects.filter(pk=event_id).only("timestamp").first()
0047 if event is None or timezone.is_naive(event.timestamp):
0048 raise CommandError("Source event and its timezone-aware timestamp are required")
0049 payload = {
0050 "source": source,
0051 "event_id": f"applog:{event_id}",
0052 "audience": audience.model_dump(mode="json", exclude_defaults=True),
0053 "observed_at": event.timestamp.isoformat(),
0054 "topic": options["topic"],
0055 "content": (
0056 f"Observed SWF completion/event: {notice.title}\n"
0057 "This reports an existing incident; it does not trigger a new job or action.\n"
0058 f"Severity: {notice.severity}\n"
0059 f"{notice.detail}\n"
0060 f"Source event: AppLog {event_id} ({dedup_key})\n"
0061 f"Observed at: {event.timestamp.isoformat()}\n"
0062 f"Buffered at: {notice.created_at.isoformat()}\n"
0063 f"{notice.url}"
0064 ),
0065 }
0066 encoded = json.dumps(payload, ensure_ascii=False, indent=2) + "\n"
0067 output = Path(options["output"]).expanduser()
0068 try:
0069
0070 fd = os.open(output, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
0071 with os.fdopen(fd, "w", encoding="utf-8") as handle:
0072 handle.write(encoded)
0073 except OSError as error:
0074 raise CommandError(f"Could not create prepared payload: {error}") from error
0075 self.stdout.write(f"Prepared AppLog {event_id} in {output}; no publication performed")