Files
sports-division/test-data.sql

356 lines
18 KiB
PL/PgSQL

-- =============================================================================
-- test-data.sql
-- Bulk sample data for the Dominica Sports Division app.
--
-- Adds, for BOTH seeded tournaments (1 = Inter-Zone Championship 2026,
-- 2 = National Championship 2026):
-- * 60 students (30 male / 30 female) spread across the secondary schools
-- * Under-16 Boys & Under-16 Girls event levels for EVERY individual
-- (non-relay) event in the catalogue
-- * ~16 registrations per event level
-- * Results recorded in the form each discipline actually uses:
-- - Field events: a Score per athlete (mark -> WA points, placement, points)
-- - High jump: a per-bar attempt grid (clears / fails), then a Score
-- - Track: a preliminary Heat round (heats of <= 8) feeding an
-- 8-athlete Final; placement points are awarded ONLY from
-- the final.
--
-- Marks are VARIED per event level (a deterministic per-registration jitter and
-- a per-level base offset) so no two competitions look identical.
--
-- The students / event levels / registrations are inserted idempotently. The
-- scoring artifacts (Scores, heats, heights, attempts) are rebuilt on every run
-- (deterministically), so re-running reproduces the same data -- but any manual
-- scoring done on these U16 test event levels via the UI will be overwritten.
--
-- Field / jump marks are in METRES. Relays are skipped (they need RelayTeams).
--
-- Run with:
-- PGPASSWORD=... psql -h localhost -U postgres -d sportsdivision -f test-data.sql
-- =============================================================================
BEGIN;
-- ---------------------------------------------------------------------------
-- Single source of truth: every individual event to populate, with the base
-- mark for the winner and the step between consecutive places.
-- is_track = true -> seconds, lower is better
-- is_track = false -> metres, higher is better
-- ---------------------------------------------------------------------------
CREATE TEMP TABLE _ev (ename text, base numeric, step numeric, is_track boolean) ON COMMIT DROP;
INSERT INTO _ev (ename, base, step, is_track) VALUES
-- Track (seconds)
('80m', 10.00, 0.15, true),
('100m', 11.50, 0.18, true),
('150m', 17.50, 0.25, true),
('200m', 23.50, 0.30, true),
('300m', 38.00, 0.45, true),
('400m', 52.00, 0.70, true),
('800m', 125.00, 1.50, true),
('1200m', 200.00, 2.50, true),
('1500m', 255.00, 3.00, true),
('3000m', 560.00, 6.00, true),
('5000m', 980.00, 10.0, true),
('80mH', 13.00, 0.25, true),
('QC 60m Sprint', 7.50, 0.12, true),
-- Field (metres)
('Long Jump', 6.40, 0.15, false),
('Triple Jump', 13.00, 0.30, false),
('Shot Put 3kg', 12.50, 0.40, false),
('Shot Put 4kg', 11.50, 0.38, false),
('Shot Put 5kg', 10.50, 0.35, false),
('Shot Put 6kg', 9.50, 0.33, false),
('Discus 1kg', 38.00, 1.20, false),
('Discus 1.25kg', 34.00, 1.10, false),
('Discus 1.5kg', 30.00, 1.00, false),
('Discus 1.75kg', 27.00, 0.90, false),
('Javelin 400g', 42.00, 1.50, false),
('Javelin 500g', 48.00, 1.60, false),
('Javelin 600g', 45.00, 1.50, false),
('Javelin 700g', 40.00, 1.40, false),
('Javelin 800g', 38.00, 1.30, false),
('Throwing the Cricket Ball', 65.00, 2.00, false),
('QC Javelin Throw', 40.00, 1.40, false),
-- High jump (metres)
('High Jump', 1.85, 0.04, false),
('QC High Jump Open', 1.80, 0.04, false);
-- ---------------------------------------------------------------------------
-- 1. STUDENTS (60: TD-0001 .. TD-0060; first 30 Male, next 30 Female)
-- ---------------------------------------------------------------------------
WITH secids AS (
SELECT array_agg("SchoolId" ORDER BY "SchoolId") AS ids, count(*)::int AS c
FROM "Schools"
WHERE "SchoolLevel" = 'Secondary' AND "IsActive"
)
INSERT INTO "Students"
("ExistingStudentId", "FirstName", "LastName", "DateOfBirth", "Sex", "SchoolId", "IsActive")
SELECT
'TD-' || lpad(g::text, 4, '0'),
(ARRAY['Liam','Noah','Ethan','Mason','Logan','Lucas','Jack','Aiden','Caleb','Isaac',
'Ava','Mia','Zoe','Lily','Emma','Sofia','Maya','Chloe','Ella','Grace'])[1 + (g % 20)],
(ARRAY['Joseph','Charles','Baptiste','Pierre','Williams','John','Thomas','James','Henry','George',
'Edwards','Francis','Lewis','Daniel','Peters','Roberts','Andrew','Paul','Mark','Joseph'])[1 + ((g * 7) % 20)],
make_date(2010 + (g % 2), 1 + (g % 12), 1 + (g % 27)),
CASE WHEN g <= 30 THEN 'Male' ELSE 'Female' END,
secids.ids[1 + (g % secids.c)],
true
FROM generate_series(1, 60) AS g
CROSS JOIN secids
WHERE NOT EXISTS (
SELECT 1 FROM "Students" s WHERE s."ExistingStudentId" = 'TD-' || lpad(g::text, 4, '0')
);
-- ---------------------------------------------------------------------------
-- 2. TOURNAMENT EVENT LEVELS (every _ev event x U16 Boys/Girls x 2 tournaments)
-- Age restriction waived so the U16 sample athletes are always eligible.
-- ---------------------------------------------------------------------------
WITH combos AS (
SELECT t.tid, e."EventId" AS eid, el."EventLevelId" AS lid
FROM (VALUES (1), (2)) AS t(tid)
CROSS JOIN _ev
CROSS JOIN (VALUES ('Under 16 Boys'), ('Under 16 Girls')) AS lv(name)
JOIN "Events" e ON e."Name" = _ev.ename
JOIN "EventLevels" el ON el."Name" = lv.name
)
INSERT INTO "TournamentEventLevels"
("TournamentId", "EventId", "EventLevelId", "AgeRestrictionWaived")
SELECT c.tid, c.eid, c.lid, true
FROM combos c
WHERE NOT EXISTS (
SELECT 1 FROM "TournamentEventLevels" x
WHERE x."TournamentId" = c.tid AND x."EventId" = c.eid AND x."EventLevelId" = c.lid
);
-- ---------------------------------------------------------------------------
-- 3. REGISTRATIONS (~16 sample students per event level, sex-matched)
-- ---------------------------------------------------------------------------
WITH testtels AS (
SELECT tel."TournamentEventLevelId" AS tid, el."Sex" AS sex
FROM "TournamentEventLevels" tel
JOIN "EventLevels" el ON el."EventLevelId" = tel."EventLevelId"
JOIN "Events" e ON e."EventId" = tel."EventId"
WHERE tel."TournamentId" IN (1, 2)
AND el."Name" IN ('Under 16 Boys', 'Under 16 Girls')
AND e."Name" IN (SELECT ename FROM _ev)
),
studs AS (
SELECT "StudentId", "Sex",
row_number() OVER (PARTITION BY "Sex" ORDER BY "StudentId") AS rn
FROM "Students"
WHERE "ExistingStudentId" LIKE 'TD-%'
)
INSERT INTO "EventRegistrations"
("TournamentEventLevelId", "StudentId", "RegisteredBy", "RegisteredAt")
SELECT t.tid, s."StudentId", 'seed@sportsdivision.dm', TIMESTAMP WITH TIME ZONE '2026-06-01 09:00:00+00'
FROM testtels t
JOIN studs s ON s."Sex" = t.sex
WHERE ((s.rn + t.tid) % 30) < 16 -- 16 of the 30 per level, varied per TEL
AND NOT EXISTS (
SELECT 1 FROM "EventRegistrations" r
WHERE r."TournamentEventLevelId" = t.tid AND r."StudentId" = s."StudentId"
);
-- ===========================================================================
-- SCORING (rebuilt from scratch each run for the U16 test event levels)
-- ===========================================================================
-- The test event levels we own (U16 Boys/Girls, both tournaments, _ev events).
CREATE TEMP TABLE _tel ON COMMIT DROP AS
SELECT tel."TournamentEventLevelId" AS tid, e."EventId" AS eid, e."Name" AS ename, e."Category" AS cat
FROM "TournamentEventLevels" tel
JOIN "EventLevels" el ON el."EventLevelId" = tel."EventLevelId"
JOIN "Events" e ON e."EventId" = tel."EventId"
WHERE tel."TournamentId" IN (1, 2)
AND el."Name" IN ('Under 16 Boys', 'Under 16 Girls')
AND e."Name" IN (SELECT ename FROM _ev);
-- Clear any existing scoring artifacts for those event levels (FK-safe order).
DELETE FROM "HeatLanes" WHERE "HeatId" IN (
SELECT h."HeatId" FROM "Heats" h JOIN "Rounds" r ON r."RoundId" = h."RoundId"
WHERE r."TournamentEventLevelId" IN (SELECT tid FROM _tel));
DELETE FROM "Heats" WHERE "RoundId" IN (
SELECT "RoundId" FROM "Rounds" WHERE "TournamentEventLevelId" IN (SELECT tid FROM _tel));
DELETE FROM "Rounds" WHERE "TournamentEventLevelId" IN (SELECT tid FROM _tel);
DELETE FROM "HighJumpAttempts" WHERE "HighJumpHeightId" IN (
SELECT "HighJumpHeightId" FROM "HighJumpHeights" WHERE "TournamentEventLevelId" IN (SELECT tid FROM _tel));
DELETE FROM "HighJumpHeights" WHERE "TournamentEventLevelId" IN (SELECT tid FROM _tel);
DELETE FROM "Scores" WHERE "EventRegistrationId" IN (
SELECT er."EventRegistrationId" FROM "EventRegistrations" er WHERE er."TournamentEventLevelId" IN (SELECT tid FROM _tel));
-- Per-athlete performance, VARIED: a per-level base offset (+/-6% by tid) and a
-- deterministic per-registration jitter shuffle finishing order and marks, so
-- different athletes win different events and no two competitions are identical.
CREATE TEMP TABLE _perf ON COMMIT DROP AS
WITH b AS (
SELECT er."EventRegistrationId" AS erid, t.tid, t.eid, t.ename, t.cat,
p.base, p.step, p.is_track,
(p.base * (1 + (((t.tid % 7) - 3) * 0.02))) AS base_v,
((er."EventRegistrationId"::bigint * 48271) % 100000) AS seed,
((((er."EventRegistrationId"::bigint * 1103515245 + 12345) % 1000)::numeric / 1000.0) - 0.5) AS jit
FROM _tel t
JOIN "EventRegistrations" er ON er."TournamentEventLevelId" = t.tid AND er."StudentId" IS NOT NULL
JOIN _ev p ON p.ename = t.ename
),
o AS (SELECT *, row_number() OVER (PARTITION BY tid ORDER BY seed) AS pos0 FROM b),
m AS (
SELECT *,
CASE WHEN is_track THEN base_v + step * (pos0 - 1) + jit * step
ELSE base_v - step * (pos0 - 1) + jit * step END AS raw0
FROM o
)
SELECT erid, tid, eid, ename, cat, is_track, jit,
round(raw0::numeric, 2) AS raw,
row_number() OVER (PARTITION BY tid ORDER BY CASE WHEN is_track THEN raw0 ELSE -raw0 END) AS pos,
count(*) OVER (PARTITION BY tid) AS n
FROM m;
-- ---------------------------------------------------------------------------
-- 4a. FIELD events (single competition; everyone placed, top 8 score points)
-- ---------------------------------------------------------------------------
INSERT INTO "Scores"
("EventRegistrationId", "RawPerformance", "CalculatedPoints", "Placement", "PlacementPoints", "RecordedBy", "RecordedAt")
SELECT pf.erid, pf.raw,
COALESCE(CASE WHEN sc."A" IS NOT NULL AND (pf.raw - sc."B") > 0
THEN floor(sc."A" * power(pf.raw - sc."B", sc."C"))::int ELSE 0 END, 0),
pf.pos, COALESCE(ppc."Points", 0),
'seed@sportsdivision.dm', TIMESTAMP WITH TIME ZONE '2026-06-02 14:00:00+00'
FROM _perf pf
LEFT JOIN "ScoringConstants" sc ON sc."EventId" = pf.eid
LEFT JOIN "PlacementPointConfigs" ppc ON ppc."Placement" = pf.pos
WHERE pf.cat = 'Field' AND pf.raw > 0;
-- ---------------------------------------------------------------------------
-- 4b. HIGH JUMP (per-level bar ladder, jittered clearances with ties)
-- ---------------------------------------------------------------------------
CREATE TEMP TABLE _hj ON COMMIT DROP AS
SELECT pf.erid, pf.tid, pf.eid, pf.pos,
(1.30 + (pf.tid % 6) * 0.05)::numeric AS start_h,
GREATEST(0, LEAST(7,
(round((pf.n - pf.pos)::numeric / pf.n * 7) + round(pf.jit * 2))::int)) AS cidx
FROM _perf pf
WHERE pf.cat = 'HighJump';
-- bar ladder: 8 bars at 0.05 spacing from the per-level start height
INSERT INTO "HighJumpHeights" ("TournamentEventLevelId", "Height", "SortOrder")
SELECT s.tid, round((s.start_h + 0.05 * k)::numeric, 2), k + 1
FROM (SELECT DISTINCT tid, start_h FROM _hj) s
CROSS JOIN generate_series(0, 7) AS k;
-- attempts: clear every bar up to the athlete's height, fail (XXX) the next one
INSERT INTO "HighJumpAttempts" ("HighJumpHeightId", "EventRegistrationId", "Attempt1", "Attempt2", "Attempt3")
SELECT hh."HighJumpHeightId", a.erid,
CASE WHEN hh."Height" <= a.cleared THEN 'Clear' ELSE 'Fail' END,
CASE WHEN hh."Height" > a.cleared THEN 'Fail' END,
CASE WHEN hh."Height" > a.cleared THEN 'Fail' END
FROM (SELECT erid, tid, round((start_h + 0.05 * cidx)::numeric, 2) AS cleared FROM _hj) a
JOIN "HighJumpHeights" hh
ON hh."TournamentEventLevelId" = a.tid
AND hh."Height" <= a.cleared + 0.05;
-- high jump scores: rank by cleared height (ties broken by finishing order)
INSERT INTO "Scores"
("EventRegistrationId", "RawPerformance", "CalculatedPoints", "Placement", "PlacementPoints", "RecordedBy", "RecordedAt")
SELECT z.erid, z.cleared,
COALESCE(CASE WHEN sc."A" IS NOT NULL AND (z.cleared - sc."B") > 0
THEN floor(sc."A" * power(z.cleared - sc."B", sc."C"))::int ELSE 0 END, 0),
z.place, COALESCE(ppc."Points", 0),
'seed@sportsdivision.dm', TIMESTAMP WITH TIME ZONE '2026-06-02 14:00:00+00'
FROM (
SELECT h.erid, h.eid, round((h.start_h + 0.05 * h.cidx)::numeric, 2) AS cleared,
row_number() OVER (PARTITION BY h.tid ORDER BY (h.start_h + 0.05 * h.cidx) DESC, h.pos ASC) AS place
FROM _hj h
) z
LEFT JOIN "ScoringConstants" sc ON sc."EventId" = z.eid
LEFT JOIN "PlacementPointConfigs" ppc ON ppc."Placement" = z.place;
-- ---------------------------------------------------------------------------
-- 4c. TRACK (prelim Heat round of <=8-lane heats -> 8-athlete Final)
-- Placement points are awarded only from the Final round.
-- ---------------------------------------------------------------------------
CREATE TEMP TABLE _trk ON COMMIT DROP AS
SELECT pf.erid, pf.tid, pf.eid, pf.pos, pf.n,
pf.raw AS prelim_time,
round((pf.raw * 0.985)::numeric, 2) AS final_time,
ceil(pf.n / 8.0)::int AS heats
FROM _perf pf
WHERE pf.cat = 'Track';
-- prelim round (Top 3 per heat + 2 fastest losers advance to the final)
INSERT INTO "Rounds" ("TournamentEventLevelId", "RoundType", "RoundOrder", "AdvanceTopN", "AdvanceFastestLosers", "Status")
SELECT DISTINCT tid, 'Heat', 1, 3, 2, 'Completed' FROM _trk;
-- final round
INSERT INTO "Rounds" ("TournamentEventLevelId", "RoundType", "RoundOrder", "AdvanceTopN", "AdvanceFastestLosers", "Status")
SELECT DISTINCT tid, 'Final', 2, NULL::int, NULL::int, 'Completed' FROM _trk;
-- prelim heats: ceil(n/8) heats so no heat exceeds 8 lanes
INSERT INTO "Heats" ("RoundId", "HeatNumber", "Status")
SELECT r."RoundId", hn.n, 'Completed'
FROM "Rounds" r
JOIN (SELECT DISTINCT tid, heats FROM _trk) d ON d.tid = r."TournamentEventLevelId"
CROSS JOIN LATERAL generate_series(1, d.heats) AS hn(n)
WHERE r."RoundOrder" = 1;
-- final heat (single heat of <= 8)
INSERT INTO "Heats" ("RoundId", "HeatNumber", "Status")
SELECT r."RoundId", 1, 'Completed'
FROM "Rounds" r
JOIN (SELECT DISTINCT tid FROM _trk) d ON d.tid = r."TournamentEventLevelId"
WHERE r."RoundOrder" = 2;
-- prelim lanes: round-robin athletes into the heats; fastest 8 overall advance
INSERT INTO "HeatLanes"
("HeatId", "EventRegistrationId", "LaneNumber", "Time", "IsAdvanced", "AdvanceReason",
"IsDNS", "IsDNF", "IsDQ", "RecordedBy", "RecordedAt")
SELECT h."HeatId", t.erid,
row_number() OVER (PARTITION BY h."HeatId" ORDER BY t.prelim_time),
t.prelim_time,
(t.pos <= 8),
CASE WHEN t.pos <= 6 THEN 'TopN' WHEN t.pos <= 8 THEN 'FastestLoser' ELSE NULL END,
false, false, false, 'seed@sportsdivision.dm', TIMESTAMP WITH TIME ZONE '2026-06-02 14:00:00+00'
FROM _trk t
JOIN "Rounds" r ON r."TournamentEventLevelId" = t.tid AND r."RoundOrder" = 1
JOIN "Heats" h ON h."RoundId" = r."RoundId" AND h."HeatNumber" = ((t.pos - 1) % t.heats) + 1;
-- final lanes: the 8 finalists, lane = final placement
INSERT INTO "HeatLanes"
("HeatId", "EventRegistrationId", "LaneNumber", "Time", "IsAdvanced", "AdvanceReason",
"IsDNS", "IsDNF", "IsDQ", "RecordedBy", "RecordedAt")
SELECT h."HeatId", t.erid, t.pos, t.final_time, false, NULL,
false, false, false, 'seed@sportsdivision.dm', TIMESTAMP WITH TIME ZONE '2026-06-02 14:00:00+00'
FROM _trk t
JOIN "Rounds" r ON r."TournamentEventLevelId" = t.tid AND r."RoundOrder" = 2
JOIN "Heats" h ON h."RoundId" = r."RoundId"
WHERE t.pos <= 8;
-- track scores: ONLY the 8 finalists, placement & points from the final
INSERT INTO "Scores"
("EventRegistrationId", "RawPerformance", "CalculatedPoints", "Placement", "PlacementPoints", "RecordedBy", "RecordedAt")
SELECT t.erid, t.final_time,
COALESCE(CASE WHEN sc."A" IS NOT NULL AND (sc."B" - t.final_time) > 0
THEN floor(sc."A" * power(sc."B" - t.final_time, sc."C"))::int ELSE 0 END, 0),
t.pos, COALESCE(ppc."Points", 0),
'seed@sportsdivision.dm', TIMESTAMP WITH TIME ZONE '2026-06-02 14:00:00+00'
FROM _trk t
LEFT JOIN "ScoringConstants" sc ON sc."EventId" = t.eid
LEFT JOIN "PlacementPointConfigs" ppc ON ppc."Placement" = t.pos
WHERE t.pos <= 8;
COMMIT;
-- ---------------------------------------------------------------------------
-- Summary (informational; safe to run repeatedly)
-- ---------------------------------------------------------------------------
SELECT 'students (TD-)' AS metric, count(*) AS value FROM "Students" WHERE "ExistingStudentId" LIKE 'TD-%'
UNION ALL SELECT 'event levels (t1)', count(*) FROM "TournamentEventLevels" WHERE "TournamentId" = 1
UNION ALL SELECT 'event levels (t2)', count(*) FROM "TournamentEventLevels" WHERE "TournamentId" = 2
UNION ALL SELECT 'registrations (total)', count(*) FROM "EventRegistrations"
UNION ALL SELECT 'scores (total)', count(*) FROM "Scores"
UNION ALL SELECT 'track heat lanes', count(*) FROM "HeatLanes"
UNION ALL SELECT 'high jump heights', count(*) FROM "HighJumpHeights"
UNION ALL SELECT 'high jump attempts', count(*) FROM "HighJumpAttempts"
UNION ALL SELECT 'individual events scored',
count(DISTINCT tel."EventId")
FROM "Scores" s
JOIN "EventRegistrations" er ON er."EventRegistrationId" = s."EventRegistrationId"
JOIN "TournamentEventLevels" tel ON tel."TournamentEventLevelId" = er."TournamentEventLevelId";