mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(db): backfill non-canonical WHOOP workout sport_type values
Existing WHOOP Workout rows still carry whatever the pre-fix write path stored — a whoop_sport_<id> placeholder or WHOOP's raw sport_name — so the sport-map fix only corrects new syncs. This one-shot UPDATE re-derives every existing WHOOP row's sport_type from the same table sport-map.ts uses, generated directly from that table so the historical data and the live mapper can't drift apart. Unmatched values default to "other", matching the mapper's fallback. Idempotent: the WHERE clause skips rows already on a canonical value, so a re-run touches zero rows.
This commit is contained in:
1 parent
43884569d3
commit
86e92f38ca
1 file changed
+289
@@ -0,0 +1,289 @@
|
||||
-- v1.29.x — backfill non-canonical WHOOP `Workout.sport_type` values.
|
||||
--
|
||||
-- Root cause (confirmed from prod data): WHOOP was the only workout
|
||||
-- integration that never normalised its sport to HealthLog's canonical
|
||||
-- `WorkoutSportType` enum (see `workoutSportTypeEnum` in
|
||||
-- src/lib/validations/workout.ts). `upsertWhoopWorkout()` in
|
||||
-- src/lib/whoop/sync-workout.ts wrote WHOOP's raw `sport_name` verbatim, or
|
||||
-- a literal `whoop_sport_<sport_id>` placeholder when WHOOP sent only the
|
||||
-- (now-retired) numeric id — so existing WHOOP rows carry values the
|
||||
-- `/insights/workouts` list can't map to an icon/label (a WHOOP-tracked bike
|
||||
-- ride never rendered as "cycling"). `mapWhoopSportType()`
|
||||
-- (src/lib/whoop/sport-map.ts) fixes the write path going forward; this
|
||||
-- migration re-derives every EXISTING WHOOP `Workout` row from the same
|
||||
-- table so historical data and the live mapper never drift apart.
|
||||
--
|
||||
-- The WHEN arms below were generated FROM `WHOOP_SPORT_TABLE` in
|
||||
-- src/lib/whoop/sport-map.ts (one JS-side script run against that exact
|
||||
-- table, not hand-transcribed) and cover both shapes a pre-fix row could
|
||||
-- carry:
|
||||
-- - the literal `whoop_sport_<id>` placeholder (id present, no name), and
|
||||
-- - the raw `sport_name` WHOOP sent (name present), normalised the same
|
||||
-- way the SQL expression below normalises the stored column: lowercase,
|
||||
-- any run of characters outside `[a-z0-9_]` collapsed to a single `_`.
|
||||
-- (`normaliseSportKey()` in sport-map.ts mirrors this exact rule so a
|
||||
-- future re-generation of this list stays consistent with the SQL.)
|
||||
--
|
||||
-- Anything neither arm recognises falls to `ELSE 'other'` — same fallback
|
||||
-- `mapWhoopSportType()` uses, never leaves a row on a `whoop_sport_<n>`
|
||||
-- placeholder.
|
||||
--
|
||||
-- Idempotent: the WHERE clause excludes rows whose `sport_type` is ALREADY
|
||||
-- one of the 20 canonical values, so a re-run touches zero rows once the
|
||||
-- first run has completed. Safe to re-run after a partial failure.
|
||||
UPDATE "workouts"
|
||||
SET "sport_type" = CASE
|
||||
lower(regexp_replace(trim("sport_type"), '[^a-zA-Z0-9_]+', '_', 'g'))
|
||||
WHEN 'whoop_sport__1' THEN 'other'
|
||||
WHEN 'whoop_sport_0' THEN 'running'
|
||||
WHEN 'whoop_sport_1' THEN 'cycling'
|
||||
WHEN 'whoop_sport_16' THEN 'other'
|
||||
WHEN 'whoop_sport_17' THEN 'basketball'
|
||||
WHEN 'whoop_sport_18' THEN 'rowing'
|
||||
WHEN 'whoop_sport_19' THEN 'other'
|
||||
WHEN 'whoop_sport_20' THEN 'other'
|
||||
WHEN 'whoop_sport_21' THEN 'other'
|
||||
WHEN 'whoop_sport_22' THEN 'golf'
|
||||
WHEN 'whoop_sport_24' THEN 'other'
|
||||
WHEN 'whoop_sport_25' THEN 'other'
|
||||
WHEN 'whoop_sport_27' THEN 'other'
|
||||
WHEN 'whoop_sport_28' THEN 'other'
|
||||
WHEN 'whoop_sport_29' THEN 'other'
|
||||
WHEN 'whoop_sport_30' THEN 'soccer'
|
||||
WHEN 'whoop_sport_31' THEN 'other'
|
||||
WHEN 'whoop_sport_32' THEN 'tennis'
|
||||
WHEN 'whoop_sport_33' THEN 'swimming'
|
||||
WHEN 'whoop_sport_34' THEN 'tennis'
|
||||
WHEN 'whoop_sport_35' THEN 'running'
|
||||
WHEN 'whoop_sport_36' THEN 'other'
|
||||
WHEN 'whoop_sport_37' THEN 'swimming'
|
||||
WHEN 'whoop_sport_38' THEN 'strength'
|
||||
WHEN 'whoop_sport_39' THEN 'hiit'
|
||||
WHEN 'whoop_sport_42' THEN 'dance'
|
||||
WHEN 'whoop_sport_43' THEN 'mindAndBody'
|
||||
WHEN 'whoop_sport_44' THEN 'yoga'
|
||||
WHEN 'whoop_sport_45' THEN 'strength'
|
||||
WHEN 'whoop_sport_47' THEN 'other'
|
||||
WHEN 'whoop_sport_48' THEN 'crossTraining'
|
||||
WHEN 'whoop_sport_49' THEN 'mixedCardio'
|
||||
WHEN 'whoop_sport_51' THEN 'crossTraining'
|
||||
WHEN 'whoop_sport_52' THEN 'hiking'
|
||||
WHEN 'whoop_sport_53' THEN 'other'
|
||||
WHEN 'whoop_sport_55' THEN 'rowing'
|
||||
WHEN 'whoop_sport_56' THEN 'hiit'
|
||||
WHEN 'whoop_sport_57' THEN 'cycling'
|
||||
WHEN 'whoop_sport_59' THEN 'strength'
|
||||
WHEN 'whoop_sport_60' THEN 'crossTraining'
|
||||
WHEN 'whoop_sport_61' THEN 'rowing'
|
||||
WHEN 'whoop_sport_62' THEN 'mixedCardio'
|
||||
WHEN 'whoop_sport_63' THEN 'walking'
|
||||
WHEN 'whoop_sport_64' THEN 'other'
|
||||
WHEN 'whoop_sport_65' THEN 'elliptical'
|
||||
WHEN 'whoop_sport_66' THEN 'stairClimber'
|
||||
WHEN 'whoop_sport_70' THEN 'mindAndBody'
|
||||
WHEN 'whoop_sport_71' THEN 'other'
|
||||
WHEN 'whoop_sport_73' THEN 'other'
|
||||
WHEN 'whoop_sport_74' THEN 'other'
|
||||
WHEN 'whoop_sport_75' THEN 'other'
|
||||
WHEN 'whoop_sport_76' THEN 'other'
|
||||
WHEN 'whoop_sport_77' THEN 'other'
|
||||
WHEN 'whoop_sport_82' THEN 'other'
|
||||
WHEN 'whoop_sport_83' THEN 'stairClimber'
|
||||
WHEN 'whoop_sport_84' THEN 'hiit'
|
||||
WHEN 'whoop_sport_85' THEN 'other'
|
||||
WHEN 'whoop_sport_86' THEN 'other'
|
||||
WHEN 'whoop_sport_87' THEN 'other'
|
||||
WHEN 'whoop_sport_88' THEN 'other'
|
||||
WHEN 'whoop_sport_89' THEN 'other'
|
||||
WHEN 'whoop_sport_90' THEN 'other'
|
||||
WHEN 'whoop_sport_91' THEN 'other'
|
||||
WHEN 'whoop_sport_92' THEN 'other'
|
||||
WHEN 'whoop_sport_93' THEN 'other'
|
||||
WHEN 'whoop_sport_94' THEN 'crossTraining'
|
||||
WHEN 'whoop_sport_95' THEN 'other'
|
||||
WHEN 'whoop_sport_96' THEN 'hiit'
|
||||
WHEN 'whoop_sport_97' THEN 'cycling'
|
||||
WHEN 'whoop_sport_98' THEN 'hiit'
|
||||
WHEN 'whoop_sport_99' THEN 'other'
|
||||
WHEN 'whoop_sport_100' THEN 'other'
|
||||
WHEN 'whoop_sport_101' THEN 'tennis'
|
||||
WHEN 'whoop_sport_102' THEN 'other'
|
||||
WHEN 'whoop_sport_103' THEN 'hiit'
|
||||
WHEN 'whoop_sport_104' THEN 'other'
|
||||
WHEN 'whoop_sport_105' THEN 'other'
|
||||
WHEN 'whoop_sport_106' THEN 'tennis'
|
||||
WHEN 'whoop_sport_107' THEN 'mindAndBody'
|
||||
WHEN 'whoop_sport_108' THEN 'other'
|
||||
WHEN 'whoop_sport_109' THEN 'other'
|
||||
WHEN 'whoop_sport_110' THEN 'crossTraining'
|
||||
WHEN 'whoop_sport_111' THEN 'other'
|
||||
WHEN 'whoop_sport_112' THEN 'other'
|
||||
WHEN 'whoop_sport_113' THEN 'other'
|
||||
WHEN 'whoop_sport_121' THEN 'other'
|
||||
WHEN 'whoop_sport_123' THEN 'strength'
|
||||
WHEN 'whoop_sport_125' THEN 'other'
|
||||
WHEN 'whoop_sport_126' THEN 'cycling'
|
||||
WHEN 'whoop_sport_127' THEN 'hiit'
|
||||
WHEN 'whoop_sport_128' THEN 'mindAndBody'
|
||||
WHEN 'whoop_sport_230' THEN 'tennis'
|
||||
WHEN 'whoop_sport_231' THEN 'tennis'
|
||||
WHEN 'whoop_sport_232' THEN 'basketball'
|
||||
WHEN 'whoop_sport_233' THEN 'other'
|
||||
WHEN 'whoop_sport_234' THEN 'golf'
|
||||
WHEN 'whoop_sport_235' THEN 'other'
|
||||
WHEN 'whoop_sport_236' THEN 'other'
|
||||
WHEN 'whoop_sport_237' THEN 'other'
|
||||
WHEN 'whoop_sport_238' THEN 'other'
|
||||
WHEN 'whoop_sport_239' THEN 'other'
|
||||
WHEN 'whoop_sport_240' THEN 'other'
|
||||
WHEN 'whoop_sport_248' THEN 'crossTraining'
|
||||
WHEN 'whoop_sport_249' THEN 'tennis'
|
||||
WHEN 'whoop_sport_250' THEN 'crossTraining'
|
||||
WHEN 'whoop_sport_251' THEN 'other'
|
||||
WHEN 'whoop_sport_252' THEN 'walking'
|
||||
WHEN 'whoop_sport_253' THEN 'running'
|
||||
WHEN 'whoop_sport_254' THEN 'other'
|
||||
WHEN 'whoop_sport_255' THEN 'other'
|
||||
WHEN 'whoop_sport_258' THEN 'mindAndBody'
|
||||
WHEN 'whoop_sport_259' THEN 'yoga'
|
||||
WHEN 'whoop_sport_261' THEN 'stairClimber'
|
||||
WHEN 'whoop_sport_262' THEN 'other'
|
||||
WHEN 'whoop_sport_263' THEN 'other'
|
||||
WHEN 'whoop_sport_264' THEN 'other'
|
||||
WHEN 'whoop_sport_266' THEN 'walking'
|
||||
WHEN 'whoop_sport_267' THEN 'other'
|
||||
WHEN 'whoop_sport_268' THEN 'other'
|
||||
WHEN 'whoop_sport_269' THEN 'other'
|
||||
WHEN 'whoop_sport_270' THEN 'other'
|
||||
WHEN 'whoop_sport_272' THEN 'other'
|
||||
WHEN 'activity' THEN 'other'
|
||||
WHEN 'running' THEN 'running'
|
||||
WHEN 'cycling' THEN 'cycling'
|
||||
WHEN 'baseball' THEN 'other'
|
||||
WHEN 'basketball' THEN 'basketball'
|
||||
WHEN 'rowing' THEN 'rowing'
|
||||
WHEN 'fencing' THEN 'other'
|
||||
WHEN 'field_hockey' THEN 'other'
|
||||
WHEN 'football' THEN 'other'
|
||||
WHEN 'golf' THEN 'golf'
|
||||
WHEN 'ice_hockey' THEN 'other'
|
||||
WHEN 'lacrosse' THEN 'other'
|
||||
WHEN 'rugby' THEN 'other'
|
||||
WHEN 'sailing' THEN 'other'
|
||||
WHEN 'skiing' THEN 'other'
|
||||
WHEN 'soccer' THEN 'soccer'
|
||||
WHEN 'softball' THEN 'other'
|
||||
WHEN 'squash' THEN 'tennis'
|
||||
WHEN 'swimming' THEN 'swimming'
|
||||
WHEN 'tennis' THEN 'tennis'
|
||||
WHEN 'track_field' THEN 'running'
|
||||
WHEN 'volleyball' THEN 'other'
|
||||
WHEN 'water_polo' THEN 'swimming'
|
||||
WHEN 'wrestling' THEN 'strength'
|
||||
WHEN 'boxing' THEN 'hiit'
|
||||
WHEN 'dance' THEN 'dance'
|
||||
WHEN 'pilates' THEN 'mindAndBody'
|
||||
WHEN 'yoga' THEN 'yoga'
|
||||
WHEN 'weightlifting' THEN 'strength'
|
||||
WHEN 'cross_country_skiing' THEN 'other'
|
||||
WHEN 'functional_fitness' THEN 'crossTraining'
|
||||
WHEN 'duathlon' THEN 'mixedCardio'
|
||||
WHEN 'gymnastics' THEN 'crossTraining'
|
||||
WHEN 'hiking_rucking' THEN 'hiking'
|
||||
WHEN 'horseback_riding' THEN 'other'
|
||||
WHEN 'kayaking' THEN 'rowing'
|
||||
WHEN 'martial_arts' THEN 'hiit'
|
||||
WHEN 'mountain_biking' THEN 'cycling'
|
||||
WHEN 'powerlifting' THEN 'strength'
|
||||
WHEN 'rock_climbing' THEN 'crossTraining'
|
||||
WHEN 'paddleboarding' THEN 'rowing'
|
||||
WHEN 'triathlon' THEN 'mixedCardio'
|
||||
WHEN 'walking' THEN 'walking'
|
||||
WHEN 'surfing' THEN 'other'
|
||||
WHEN 'elliptical' THEN 'elliptical'
|
||||
WHEN 'stairmaster' THEN 'stairClimber'
|
||||
WHEN 'meditation' THEN 'mindAndBody'
|
||||
WHEN 'other' THEN 'other'
|
||||
WHEN 'diving' THEN 'other'
|
||||
WHEN 'operations_tactical' THEN 'other'
|
||||
WHEN 'operations_medical' THEN 'other'
|
||||
WHEN 'operations_flying' THEN 'other'
|
||||
WHEN 'operations_water' THEN 'other'
|
||||
WHEN 'ultimate' THEN 'other'
|
||||
WHEN 'climber' THEN 'stairClimber'
|
||||
WHEN 'jumping_rope' THEN 'hiit'
|
||||
WHEN 'australian_football' THEN 'other'
|
||||
WHEN 'skateboarding' THEN 'other'
|
||||
WHEN 'coaching' THEN 'other'
|
||||
WHEN 'ice_bath' THEN 'other'
|
||||
WHEN 'commuting' THEN 'other'
|
||||
WHEN 'gaming' THEN 'other'
|
||||
WHEN 'snowboarding' THEN 'other'
|
||||
WHEN 'motocross' THEN 'other'
|
||||
WHEN 'caddying' THEN 'other'
|
||||
WHEN 'obstacle_course_racing' THEN 'crossTraining'
|
||||
WHEN 'motor_racing' THEN 'other'
|
||||
WHEN 'hiit' THEN 'hiit'
|
||||
WHEN 'spin' THEN 'cycling'
|
||||
WHEN 'jiu_jitsu' THEN 'hiit'
|
||||
WHEN 'manual_labor' THEN 'other'
|
||||
WHEN 'cricket' THEN 'other'
|
||||
WHEN 'pickleball' THEN 'tennis'
|
||||
WHEN 'inline_skating' THEN 'other'
|
||||
WHEN 'box_fitness' THEN 'hiit'
|
||||
WHEN 'spikeball' THEN 'other'
|
||||
WHEN 'wheelchair_pushing' THEN 'other'
|
||||
WHEN 'paddle_tennis' THEN 'tennis'
|
||||
WHEN 'barre' THEN 'mindAndBody'
|
||||
WHEN 'stage_performance' THEN 'other'
|
||||
WHEN 'high_stress_work' THEN 'other'
|
||||
WHEN 'parkour' THEN 'crossTraining'
|
||||
WHEN 'gaelic_football' THEN 'other'
|
||||
WHEN 'hurling_camogie' THEN 'other'
|
||||
WHEN 'circus_arts' THEN 'other'
|
||||
WHEN 'massage_therapy' THEN 'other'
|
||||
WHEN 'strength_trainer' THEN 'strength'
|
||||
WHEN 'watching_sports' THEN 'other'
|
||||
WHEN 'assault_bike' THEN 'cycling'
|
||||
WHEN 'kickboxing' THEN 'hiit'
|
||||
WHEN 'stretching' THEN 'mindAndBody'
|
||||
WHEN 'table_tennis' THEN 'tennis'
|
||||
WHEN 'badminton' THEN 'tennis'
|
||||
WHEN 'netball' THEN 'basketball'
|
||||
WHEN 'sauna' THEN 'other'
|
||||
WHEN 'disc_golf' THEN 'golf'
|
||||
WHEN 'yard_work' THEN 'other'
|
||||
WHEN 'air_compression' THEN 'other'
|
||||
WHEN 'percussive_massage' THEN 'other'
|
||||
WHEN 'paintball' THEN 'other'
|
||||
WHEN 'ice_skating' THEN 'other'
|
||||
WHEN 'handball' THEN 'other'
|
||||
WHEN 'f45_training' THEN 'crossTraining'
|
||||
WHEN 'padel' THEN 'tennis'
|
||||
WHEN 'barry_s' THEN 'crossTraining'
|
||||
WHEN 'dedicated_parenting' THEN 'other'
|
||||
WHEN 'stroller_walking' THEN 'walking'
|
||||
WHEN 'stroller_jogging' THEN 'running'
|
||||
WHEN 'toddlerwearing' THEN 'other'
|
||||
WHEN 'babywearing' THEN 'other'
|
||||
WHEN 'barre3' THEN 'mindAndBody'
|
||||
WHEN 'hot_yoga' THEN 'yoga'
|
||||
WHEN 'stadium_steps' THEN 'stairClimber'
|
||||
WHEN 'polo' THEN 'other'
|
||||
WHEN 'musical_performance' THEN 'other'
|
||||
WHEN 'kite_boarding' THEN 'other'
|
||||
WHEN 'dog_walking' THEN 'walking'
|
||||
WHEN 'water_skiing' THEN 'other'
|
||||
WHEN 'wakeboarding' THEN 'other'
|
||||
WHEN 'cooking' THEN 'other'
|
||||
WHEN 'cleaning' THEN 'other'
|
||||
WHEN 'public_speaking' THEN 'other'
|
||||
ELSE 'other'
|
||||
END
|
||||
WHERE "source" = 'WHOOP'
|
||||
AND "sport_type" NOT IN (
|
||||
'walking', 'running', 'cycling', 'hiking', 'swimming', 'rowing',
|
||||
'elliptical', 'stairClimber', 'yoga', 'mindAndBody', 'strength', 'hiit',
|
||||
'dance', 'golf', 'tennis', 'basketball', 'soccer', 'crossTraining',
|
||||
'mixedCardio', 'other'
|
||||
);
|
||||
Reference in new issue
Block a user