Skip to content

Clone Past Match (admin)

Clone a CLOSED match, validate that srsim can load its recorded feed, create an is_test row, and enqueue a live Celery listener pointed at srsim.

Item Value
URL /admin/test-match-listeners
Nav Play → Clone Past Match
Availability Non-main only (develop, staging, test)
REST GET /api/matches?league=&day=&status=closed&is_test=false&listener_events_archived=true, POST /api/matches/{id}/clone
Match picker Shared admin_match_picker + AdminMatches.bindPicker (archived live matches only)

Operator flow

  1. Choose league and local calendar day (shared pick-one controls; Day defaults to yesterday). You can also open this page from match details via Clone match, which deep-links with query params league, day (operator local calendar day of Match.scheduled), and match_id (internal match id) so the picker loads pre-selected.
  2. Pick a CLOSED live (non-test) match that has listener events archived to S3. A View match link opens that row’s match details page.
  3. Optionally click a form preset (does not change the selected match or clone start):
  4. Normal-time — reset stream + listener fields to page defaults; Wait for listener off (continuous session, Sportradar-like).
  5. Speed throughplayback_speed=0, max_ms_between_events=100, rewrite_ts_strategy=0, Wait for listener on (one-shot), listener_timing_source=ts, listener_post_issuance_pause_seconds=35, listener_sr_request_timeout=60 (fast feed, human answer window; timeout above the pause so the HTTP read does not die).
  6. Optionally set clone start datetime (default: now UTC), or use +1 / +5 / +10 / +30 / +60 to add minutes to the current start (or to now if empty). A future kickoff delays start_match_listener until that time (Speed through one-shot and Normal-time continuous). With Wait for listener off, this is also the continuous srsim session kickoff (Match.scheduled): the feed clock starts here even if no listener is connected. It is not the same as srsim start_at (which slices the archive). See SRSim parameters — continuous sessions.
  7. Optionally enable Push notifications for the clone (default off). When off, push fan-out skips the match (skip_reason=match_push_disabled) so the auto-followed creator is not notified during srsim. Toggle later on match details (including mid-match). See Push notifications.
  8. Optionally edit In-game question overrides (max questions, min interval, points, period distribution). Points accept an absolute value or *N multiplier of the league default. Fields prefill from the selected match; blank uses league defaults. Same semantics as Matches.
  9. Optionally edit Sponsorships (match sponsor + question sponsorship slots). Prefills from the source match via match detail; clear sponsor with None, remove or add slots as needed. Written on clone create (before the listener starts). Slot count cannot exceed the clone’s effective question_max_count.
  10. Optionally set srsim stream properties (playback_speed, start_at, Wait for listener, etc.).
  11. Optionally set listener env overrides (including listener_post_issuance_pause_seconds for a real-time pause after each issued question during fast playback, and listener_force_exit_code for ops exit-code testing). listener_write_logs_to_file defaults to on.
  12. Clone — fails with 404 if no archived recording exists in AWS_S3_MATCH_ARCHIVE_BUCKET for the match shortcode ({sr_id[:8]}-{match_id[:8]}.json). No clone row is created on failure. On success, the page redirects to the new clone’s match details (/admin/matches/{clone_id}). The creator is auto-followed on the server (force); unrestricted Play Following also gets the clone id in localStorage so the filter bar picks it up without a manual follow.

POST /api/matches/{id}/clone accepts the same override_question_* fields as match PATCH. Omitted fields copy the source match; explicit null clears (league defaults); a value sets the clone override. The authenticated admin is stored as created_by_user_id on the new is_test row (shown as Created by on match details).

The same omit / null / value pattern applies to sponsor_brand_id and question_sponsorships (omit copies source slots; [] clears; otherwise the provided list is written, each item with brand_id + strategy/target/ best_effort).

The clone is created as scheduled.

Wait for listener off (default): clone injects session={internal match id}. The continuous srsim session owns status — inprogress when the producer starts; with game_delay_at_event, delayed then back to inprogress; closed when the stream completes. The MatchListener does not write status. Operator Stop ends the Celery listener only; srsim keeps advancing — use POST /sessions/{internal_match_id}/reset or /stop to rewind/halt (see what-if scenarios).

Wait for listener on: no session= (one-shot replay). The listener sets inprogress when it starts and closed when it exits. Reconnect restarts the archive from the beginning (or start_at).

Auto-dispatch includes is_test rows (open MatchListenerRecord still prevents double-start). On develop, Beat only dispatches test clones (is_test=True); staging/main dispatch includes live and test.

The listener stream uses the clone’s Sportradar match id (Match.sr_id) against srsim (which loads the source archive from S3). New listener runs still write live event files under {FILESYSTEM_CONTAINER_DATA_DIR}/listener/{league}/{Match.shortcode}.json.

To browse existing test clones, use Matches and filter Test only.