-- Construct episodes across the complete, globally ordered input history. -- A gap exactly equal to the configured threshold remains in the same episode. CREATE OR REPLACE TABLE sequenced_events AS WITH with_previous AS ( SELECT *, lag(event_ts) OVER ( PARTITION BY vehicle_token ORDER BY event_ts, internal_event_id ) AS previous_event_ts FROM clean_events ), with_markers AS ( SELECT *, CASE WHEN previous_event_ts IS NULL THEN 1 WHEN event_ts - previous_event_ts > (SELECT episode_gap_days FROM build_config) * INTERVAL '1 day' THEN 1 ELSE 0 END AS starts_episode FROM with_previous ), with_episode_number AS ( SELECT *, sum(starts_episode) OVER ( PARTITION BY vehicle_token ORDER BY event_ts, internal_event_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS episode_number FROM with_markers ) SELECT *, row_number() OVER ( PARTITION BY vehicle_token, episode_number ORDER BY event_ts, internal_event_id ) AS attempt_number, row_number() OVER ( PARTITION BY vehicle_token, episode_number ORDER BY event_ts DESC, internal_event_id DESC ) AS reverse_attempt_number FROM with_episode_number; CREATE OR REPLACE TABLE episode_daily_activity AS SELECT vehicle_token, episode_number, cast(event_ts AS DATE) AS event_date, count(*) AS events_in_day FROM sequenced_events GROUP BY vehicle_token, episode_number, cast(event_ts AS DATE); CREATE OR REPLACE TABLE episode_aggregates AS SELECT e.vehicle_token, e.vehicle_bucket, e.episode_number, min(e.event_ts) AS episode_start, max(e.event_ts) AS episode_end, count(*) AS attempt_count, count(e.canonical_outcome) AS labeled_attempt_count, coalesce(bool_or(e.canonical_outcome = 'pass'), false) AS eventually_passed, max(d.events_in_day) AS max_events_in_day, (list(e.observed_make ORDER BY e.event_ts DESC, e.internal_event_id DESC) FILTER (WHERE e.observed_make IS NOT NULL))[1] AS episode_last_observed_make, (list(e.observed_model ORDER BY e.event_ts DESC, e.internal_event_id DESC) FILTER (WHERE e.observed_model IS NOT NULL))[1] AS episode_last_observed_model, (list(e.observed_model_year ORDER BY e.event_ts DESC, e.internal_event_id DESC) FILTER (WHERE e.observed_model_year IS NOT NULL))[1] AS episode_last_observed_model_year FROM sequenced_events AS e JOIN episode_daily_activity AS d ON d.vehicle_token = e.vehicle_token AND d.episode_number = e.episode_number AND d.event_date = cast(e.event_ts AS DATE) GROUP BY e.vehicle_token, e.vehicle_bucket, e.episode_number; CREATE OR REPLACE TABLE inspection_episodes AS SELECT a.vehicle_token, a.vehicle_bucket, a.episode_number, a.episode_start, a.episode_end, f.internal_event_id AS first_internal_event_id, f.source_era, f.public_county, f.canonical_outcome AS first_outcome, f.outcome_label_source AS first_outcome_label_source, f.program_type AS first_program_type, f.test_type AS first_test_type, l.canonical_outcome AS final_outcome, a.attempt_count, a.labeled_attempt_count, a.eventually_passed, a.max_events_in_day, a.episode_last_observed_make, a.episode_last_observed_model, a.episode_last_observed_model_year FROM episode_aggregates AS a JOIN sequenced_events AS f ON f.vehicle_token = a.vehicle_token AND f.episode_number = a.episode_number AND f.attempt_number = 1 JOIN sequenced_events AS l ON l.vehicle_token = a.vehicle_token AND l.episode_number = a.episode_number AND l.reverse_attempt_number = 1;