From 47fdc134d223825257fe8f757fbb7040fc732688 Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 20 Jul 2026 00:07:44 -0700 Subject: [PATCH 01/12] feat: add Stroke.DIVING for the hy3 'F' stroke code --- hytek_parser/hy3/enums.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hytek_parser/hy3/enums.py b/hytek_parser/hy3/enums.py index a4f9113..db255bd 100644 --- a/hytek_parser/hy3/enums.py +++ b/hytek_parser/hy3/enums.py @@ -23,6 +23,12 @@ class Stroke(Enum): BREASTSTROKE = "C", "3", 3 BUTTERFLY = "D", "4", 4 MEDLEY = "E", "5", 5 + # Diving. Hy-Tek writes 'F' in the stroke column and the DIVE COUNT + # (11 championship / 6 dual) in the distance column. Without this member + # 'F' falls through select_from_enum() to UNKNOWN, which is a catch-all + # for any unrecognized byte -- making diving indistinguishable from file + # corruption for every consumer. + DIVING = "F", "6", 6 UNKNOWN = "U", "0", 0 From 87c4f76c144dae1d8fa403cfd34102b8fe8dd208 Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 20 Jul 2026 08:20:30 -0700 Subject: [PATCH 02/12] fix(enums): split Stroke.DIVING into board-specific members Hy-Tek uses three diving stroke chars, not one: F = 1-metre springboard, G = 3-metre springboard, H = platform. The prior DIVING = "F", "6", 6 treated F as the only diving code and extrapolated numeric aliases "6"/6 from the FREESTYLE..MEDLEY pattern (1..5) with no corpus evidence Hy-Tek ever writes those in the stroke column for diving -- a wrong alias could silently mis-map some other numeric code onto diving. Measured on a real corpus: 2012 PAC-12 (F=26 G=26 H=26), 2007 SEC (F=60 G=60 H=54), 2005 PAC-10 (F=15 G=15 H=12) all show three parallel diving events. Across 13 multi-diving-event files, G never appears without F and H never appears without G (strict nesting). Dive counts match within a meet across chars, and median scores ascend F < G < H, tracking increasing degree of difficulty. HS meets emit only F, consistent with NFHS being 1-metre only. G and H previously fell through select_from_enum() to UNKNOWN, silently dropping all 3-metre and platform diving results. Drop the numeric aliases; use the character form only, since select_from_enum(Stroke, ...) is only ever called with a single extracted stroke-column char (hy3) or event_stroke field (hyv), never a numeric value, in the whole codebase. --- hytek_parser/hy3/enums.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hytek_parser/hy3/enums.py b/hytek_parser/hy3/enums.py index db255bd..eefaa1c 100644 --- a/hytek_parser/hy3/enums.py +++ b/hytek_parser/hy3/enums.py @@ -23,12 +23,18 @@ class Stroke(Enum): BREASTSTROKE = "C", "3", 3 BUTTERFLY = "D", "4", 4 MEDLEY = "E", "5", 5 - # Diving. Hy-Tek writes 'F' in the stroke column and the DIVE COUNT - # (11 championship / 6 dual) in the distance column. Without this member - # 'F' falls through select_from_enum() to UNKNOWN, which is a catch-all - # for any unrecognized byte -- making diving indistinguishable from file - # corruption for every consumer. - DIVING = "F", "6", 6 + # Diving. Hy-Tek encodes the BOARD in the stroke column: + # F = 1-metre springboard, G = 3-metre springboard, H = platform. + # Verified on a multi-conference corpus: the chars nest strictly + # (F, then F+G, then F+G+H -- never G without F, never H without G), + # carry the same dive count within a meet, and their median scores + # ascend with degree of difficulty. + # Without these members all three fall through select_from_enum() to + # UNKNOWN, which is the catch-all for any unrecognized byte -- making + # diving indistinguishable from file corruption. + DIVING_1M = "F" + DIVING_3M = "G" + DIVING_PLATFORM = "H" UNKNOWN = "U", "0", 0 From b259ef5815175346bd48c1fca5e9c66f02b344c7 Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Fri, 24 Jul 2026 15:20:55 -0700 Subject: [PATCH 03/12] fix(hy3): tolerate malformed relay F3 legs and orphaned H1 DQ lines Two hy3 line parsers raised on real-world Meet Manager exports instead of degrading gracefully: - f3_parser: an F3 relay leg referencing a swimmer meet-id with no D1 roster record raised KeyError. Skip the unrostered leg (the relay keeps its other legs), matching the existing tolerance for the empty-swimmers and absent leg-1 cases. - h1_parser: an H1 DQ-detail line whose last_entry carries no DQ slot raised an AssertionError. Make it a no-op, mirroring h2_parser -- an H1 can resolve to a non-DQ entry (e.g. a relay DQ, or a non-DQ entry emitted between the DQ result and its H1). The DQ result itself is unaffected; only the reason string is skipped. Adds regression unit tests for both. --- .../hy3/line_parsers/f_relay_parsers.py | 9 ++++- hytek_parser/hy3/line_parsers/h_dq_parsers.py | 10 ++++-- .../hy3/line_parsers/test_f_relay_parsers.py | 28 +++++++++++++++ tests/hy3/line_parsers/test_h_dq_parsers.py | 35 +++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/hytek_parser/hy3/line_parsers/f_relay_parsers.py b/hytek_parser/hy3/line_parsers/f_relay_parsers.py index 092b01f..58157ca 100644 --- a/hytek_parser/hy3/line_parsers/f_relay_parsers.py +++ b/hytek_parser/hy3/line_parsers/f_relay_parsers.py @@ -172,7 +172,14 @@ def f3_parser( # Out of swimmers break - swimmer = file.meet.swimmers[swimmer_meet_id] + swimmer = file.meet.swimmers.get(swimmer_meet_id) + if swimmer is None: + # The F3 leg references a swimmer meet-id with no D1 roster record in + # this file — seen in some incomplete meet exports (e.g. relay legs for + # athletes the roster section omits). Skip the leg rather than raising a + # KeyError; the relay keeps its other legs. Mirrors the tolerance the + # empty-swimmers and absent-leg-1 cases already get below. + continue swimmer_leg = safe_cast(int, extract(line, 15 + offset, 1)) # Hy-Tek encodes legs 1..8; preserve the leg number as-is. diff --git a/hytek_parser/hy3/line_parsers/h_dq_parsers.py b/hytek_parser/hy3/line_parsers/h_dq_parsers.py index d0ce3e4..66d5711 100644 --- a/hytek_parser/hy3/line_parsers/h_dq_parsers.py +++ b/hytek_parser/hy3/line_parsers/h_dq_parsers.py @@ -17,9 +17,13 @@ def h1_parser( dq_code = select_from_enum(DisqualificationCode, extract(line, 3, 2)) dq_info = extract(line, 5, 124) # Whitespace is stripped - assert ( - entry.prelim_dq_info or entry.swimoff_dq_info or entry.finals_dq_info - ), "There must be a DQ for there to be an H1 line" + # No-op if the entry carries no DQ slot to attach to — mirrors h2_parser. + # An H1 can appear whose last_entry is not the DQ'd swim it describes (e.g. a + # relay DQ, or a non-DQ entry emitted between the DQ result and its H1); skip + # the detail rather than raising. The DQ result itself is unaffected, only the + # human-readable reason string is dropped. + if not (entry.prelim_dq_info or entry.swimoff_dq_info or entry.finals_dq_info): + return file if entry.finals_dq_info: # DQ happened in prelims diff --git a/tests/hy3/line_parsers/test_f_relay_parsers.py b/tests/hy3/line_parsers/test_f_relay_parsers.py index 8fccd81..ed5cfea 100644 --- a/tests/hy3/line_parsers/test_f_relay_parsers.py +++ b/tests/hy3/line_parsers/test_f_relay_parsers.py @@ -134,6 +134,34 @@ def test_f3_with_8_swimmers_keeps_all_8_keyed(self): entry = result.meet.last_event[1].last_entry self.assertEqual(set(entry.swimmers.keys()), {1, 2, 3, 4, 5, 6, 7, 8}) + @staticmethod + def _f3_line(pairs): + # Build an F3 line from (meet_id, leg) pairs. Each 13-char block is a + # 5-char right-justified meet_id (cols 4-8), 6 pad, 1-char leg (col 15), pad. + line = "F3 " + for mid, leg in pairs: + line += f"{mid:>5d}" + " " * 6 + f"{leg:d}" + " " + return line + + def test_f3_unrostered_swimmer_id_is_skipped_not_raised(self): + # Regression: an F3 relay leg referencing a swimmer meet-id with no D1 + # roster record in the file must be skipped, not raise KeyError. Roster + # has ids 1..8; id 42 is absent, so leg 1 is dropped and 2..4 are kept. + file, opts = self._file_with_relay_entry_and_4_swimmers() + f3_line = self._f3_line([(42, 1), (2, 2), (3, 3), (4, 4)]) + result = f3_parser(f3_line, file, opts) # must not raise + entry = result.meet.last_event[1].last_entry + self.assertEqual(set(entry.swimmers.keys()), {2, 3, 4}) + + def test_f3_all_unrostered_yields_empty_swimmers_no_raise(self): + # Every leg references an absent id → empty swimmers dict, still no raise + # (the empty-dict age guard already handles this downstream). + file, opts = self._file_with_relay_entry_and_4_swimmers() + f3_line = self._f3_line([(40, 1), (41, 2), (42, 3), (43, 4)]) + result = f3_parser(f3_line, file, opts) # must not raise + entry = result.meet.last_event[1].last_entry + self.assertEqual(entry.swimmers, {}) + class TestF2BackupTimingFields(unittest.TestCase): """F2 timing fields. Same offsets as E2 for the five timing diff --git a/tests/hy3/line_parsers/test_h_dq_parsers.py b/tests/hy3/line_parsers/test_h_dq_parsers.py index e5bc77a..3ee1523 100644 --- a/tests/hy3/line_parsers/test_h_dq_parsers.py +++ b/tests/hy3/line_parsers/test_h_dq_parsers.py @@ -69,5 +69,40 @@ def test_h2_empty_detail_is_none(self) -> None: self.assertIsNone(entry.finals_dq_info.info_str_detail) +class TestH1DqParser(unittest.TestCase): + + def _file_with_entry(self): + opts = {"default_country": "USA"} + file = ParsedHytekFile() + file.meet = Meet() + file.meet.last_team = ("FOO", Team("Foo Bar", "FOO", "foo", "", "", "", "", "", "", "", "", "", "", "", {})) + d_line = "D1M 27Hansen Mads 10272010 13 27" + e_line = "E1M 27HanseXX 50D 11109 0U 0.00 22X 37.41S 37.41S 0.00 0.00 0NN N 70" + file = d1_parser(d_line, file, opts) + file = e1_parser(e_line, file, opts) + return file, opts + + def test_h1_sets_reason_on_finals_dq(self) -> None: + from hytek_parser.hy3.line_parsers.h_dq_parsers import h1_parser + file, opts = self._file_with_entry() + entry = file.meet.last_event[1].last_entry + entry.finals_dq_info = DisqualificationInfo(DisqualificationCode.FLY_KICK_ALTERNATING, "") + # "H1" + "1A" (FLY_KICK_ALTERNATING, matches the slot's code) + reason text + result = h1_parser("H11AAlternating Kick", file, opts) + self.assertEqual("Alternating Kick", result.meet.last_event[1].last_entry.finals_dq_info.info_str) + + def test_h1_no_dq_is_noop(self) -> None: + # Regression: an H1 line whose entry carries no DQ slot must be a no-op, + # not raise — mirroring h2_parser. (A relay DQ, or a non-DQ entry emitted + # between the DQ result and its H1, resolves last_entry to a non-DQ entry.) + from hytek_parser.hy3.line_parsers.h_dq_parsers import h1_parser + file, opts = self._file_with_entry() + result = h1_parser("H11AAlternating Kick", file, opts) # must not raise + entry = result.meet.last_event[1].last_entry + self.assertIsNone(entry.finals_dq_info) + self.assertIsNone(entry.swimoff_dq_info) + self.assertIsNone(entry.prelim_dq_info) + + if __name__ == "__main__": unittest.main() From 3856af2e1f01685f55b5d54eac989fb87af6b478 Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 27 Jul 2026 21:50:02 -0700 Subject: [PATCH 04/12] feat(hy3): add parse_reaction_time for the E2/F2 reaction columns Reaction and relay-takeoff columns need their own coercion. parse_time_or_none requires a positive value, but a takeover slot records an early exchange as a negative number -- 7,868 such values across a 33,008-file corpus -- so reusing it would drop every one of them. Sentinels are plural and slot-dependent: blank, 0.00 in any sign spelling, and the literal NRT ("No Reaction Time") that Meet Manager writes into unmeasured takeover slots in 513 files. All map to None. Values above the plausible reaction range are passed through unchanged. A minority of files put something else in these columns; filtering here would make that population permanently invisible. --- hytek_parser/hy3/_utils.py | 29 ++++++++++++++++++++++++ tests/hy3/test_utils.py | 45 +++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/hytek_parser/hy3/_utils.py b/hytek_parser/hy3/_utils.py index e2c32c7..2ba6462 100644 --- a/hytek_parser/hy3/_utils.py +++ b/hytek_parser/hy3/_utils.py @@ -30,3 +30,32 @@ def parse_time_or_none(raw_time: str) -> Optional[float]: """ val = parse_time(raw_time) return val if isinstance(val, float) and val > 0.0 else None + + +def parse_reaction_time(raw: str) -> Optional[float]: + """Parse a reaction/takeoff-time column (E2 col 83-87, F2 col 83-102). + + NEGATIVE VALUES ARE MEANINGFUL and load-bearing here: a relay takeover slot + records an early exchange as a negative number (7,868 values corpus-wide). + ``parse_time_or_none`` requires > 0.0 and would silently destroy every one + of them -- do not substitute it. + + Sentinels, all meaning "not recorded": blank, 0.00 in any sign spelling, + and the literal NRT ("No Reaction Time") that Meet Manager writes into + takeover slots when the exchange was not measured. + + Values above the plausible reaction range are returned unchanged. A + minority of files put something else in these columns; its meaning is + unresolved, and filtering it here would make it permanently invisible. + """ + val = raw.strip() + if not val or val.upper() == "NRT": + return None + try: + num = float(val) + except ValueError: + # Observed malformed forms: a bare "+" sign, stray high bytes. + return None + # float() maps "0.00", "+0.00" and "-0.00" all to zero; all three are the + # "not recorded" sentinel. + return None if num == 0.0 else num diff --git a/tests/hy3/test_utils.py b/tests/hy3/test_utils.py index 5081ec7..ec66bfa 100644 --- a/tests/hy3/test_utils.py +++ b/tests/hy3/test_utils.py @@ -2,6 +2,7 @@ import datetime from hytek_parser._utils import safe_cast, int_or_none, select_from_enum, date_or_none from hytek_parser.hy3.schemas import Stroke +from hytek_parser.hy3._utils import parse_reaction_time class TestUtils(unittest.TestCase): @@ -29,6 +30,48 @@ def test_date_or_none(self) -> None: self.assertIsNone(date_or_none("")) self.assertEqual(datetime.date(1970, 1, 2), date_or_none("01021970")) +class TestParseReactionTime(unittest.TestCase): + """Contract for the E2/F2 reaction-time columns. + + Every case below is a token form observed in a full-corpus scan of 33,008 + HY3 files; none are invented. + """ + + def test_plain_value(self) -> None: + self.assertAlmostEqual(0.56, parse_reaction_time(" 0.56"), places=2) + + def test_negative_value_is_preserved(self) -> None: + # Relay takeovers record an early exchange as a negative number. + # 7,868 such values exist corpus-wide; dropping them is the single + # worst failure mode for this field. + self.assertAlmostEqual(-0.12, parse_reaction_time("-0.12"), places=2) + + def test_unsigned_zero_is_sentinel(self) -> None: + self.assertIsNone(parse_reaction_time(" 0.00")) + + def test_plus_zero_is_sentinel(self) -> None: + self.assertIsNone(parse_reaction_time("+0.00")) + + def test_minus_zero_is_sentinel(self) -> None: + self.assertIsNone(parse_reaction_time("-0.00")) + + def test_nrt_is_sentinel(self) -> None: + # Meet Manager writes "No Reaction Time" into unmeasured takeover slots. + self.assertIsNone(parse_reaction_time(" NRT")) + self.assertIsNone(parse_reaction_time("NRT")) + + def test_blank_is_none(self) -> None: + self.assertIsNone(parse_reaction_time(" ")) + self.assertIsNone(parse_reaction_time("")) + + def test_malformed_bare_sign_is_none(self) -> None: + self.assertIsNone(parse_reaction_time(" +")) + + def test_implausible_value_is_passed_through(self) -> None: + # Values above 2.0 cannot be reaction times, but their meaning is + # unresolved. The parser reports what the file says; consumers decide. + self.assertAlmostEqual(9.30, parse_reaction_time(" 9.30"), places=2) + + if __name__=='__main__': unittest.main() - \ No newline at end of file From 9f09e0d40972aaf7510a3ff1d4f73348a9a9861d Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 27 Jul 2026 21:53:55 -0700 Subject: [PATCH 05/12] feat(hy3): parse the E2 reaction time at col 83-87 The parser read backup_4 through col 82 and then jumped to the date at col 88, dropping the five columns between. They hold the swimmer's start reaction time: 2.56% of 53.7M E2 rows corpus-wide carry a value in the plausible range. Includes an offset regression test. A one-column error still produces plausible-looking floats, so the neighbouring date is the tripwire that catches a misread. --- .../hy3/line_parsers/e_event_parsers.py | 5 +- hytek_parser/hy3/schemas.py | 4 ++ .../hy3/line_parsers/test_e_event_parsers.py | 61 +++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/hytek_parser/hy3/line_parsers/e_event_parsers.py b/hytek_parser/hy3/line_parsers/e_event_parsers.py index caf8462..b3b37ec 100644 --- a/hytek_parser/hy3/line_parsers/e_event_parsers.py +++ b/hytek_parser/hy3/line_parsers/e_event_parsers.py @@ -2,7 +2,7 @@ from typing import Any from hytek_parser._utils import extract, get_age_group, safe_cast, select_from_enum -from hytek_parser.hy3._utils import parse_time, parse_time_or_none +from hytek_parser.hy3._utils import parse_reaction_time, parse_time, parse_time_or_none from hytek_parser.hy3.enums import ( Course, DisqualificationCode, @@ -117,6 +117,8 @@ def e2_parser( button_3_time = parse_time_or_none(extract(line, 55, 8)) backup_4_time = parse_time_or_none(extract(line, 75, 8)) alt_time_code = extract(line, 96, 1) or None # observed: 'A' / 'K' / blank + # col 83-87. Signed; sentinels are blank / 0.00 in any sign spelling. + reaction_time = parse_reaction_time(extract(line, 83, 5)) raw_date = extract(line, 88, 8).strip() date_ = datetime.strptime(raw_date, "%m%d%Y").date() if raw_date else None @@ -150,6 +152,7 @@ def e2_parser( setattr(entry, f"{prefix}_button_2_time", button_2_time) setattr(entry, f"{prefix}_button_3_time", button_3_time) setattr(entry, f"{prefix}_backup_4_time", backup_4_time) + setattr(entry, f"{prefix}_reaction_time", reaction_time) setattr(entry, f"{prefix}_alt_time_code", alt_time_code) event.last_entry = entry diff --git a/hytek_parser/hy3/schemas.py b/hytek_parser/hy3/schemas.py index c47bab1..17df30d 100644 --- a/hytek_parser/hy3/schemas.py +++ b/hytek_parser/hy3/schemas.py @@ -119,6 +119,7 @@ class EventEntry: prelim_button_2_time: Optional[float] = None prelim_button_3_time: Optional[float] = None prelim_backup_4_time: Optional[float] = None + prelim_reaction_time: Optional[float] = None # col 96; semantics unverified — observed 'A'/'K'/blank prelim_alt_time_code: Optional[str] = None @@ -139,6 +140,7 @@ class EventEntry: swimoff_button_2_time: Optional[float] = None swimoff_button_3_time: Optional[float] = None swimoff_backup_4_time: Optional[float] = None + swimoff_reaction_time: Optional[float] = None # col 96; semantics unverified — observed 'A'/'K'/blank swimoff_alt_time_code: Optional[str] = None @@ -159,6 +161,7 @@ class EventEntry: finals_button_2_time: Optional[float] = None finals_button_3_time: Optional[float] = None finals_backup_4_time: Optional[float] = None + finals_reaction_time: Optional[float] = None # col 96; semantics unverified — observed 'A'/'K'/blank finals_alt_time_code: Optional[str] = None @@ -216,6 +219,7 @@ def __init__( setattr(self, f"{course}_button_2_time", None) setattr(self, f"{course}_button_3_time", None) setattr(self, f"{course}_backup_4_time", None) + setattr(self, f"{course}_reaction_time", None) setattr(self, f"{course}_alt_time_code", None) self.prelim_dq_info = None diff --git a/tests/hy3/line_parsers/test_e_event_parsers.py b/tests/hy3/line_parsers/test_e_event_parsers.py index c7b64d0..4472624 100644 --- a/tests/hy3/line_parsers/test_e_event_parsers.py +++ b/tests/hy3/line_parsers/test_e_event_parsers.py @@ -1,3 +1,4 @@ +import datetime import unittest from hytek_parser.hy3.schemas import ParsedHytekFile from hytek_parser.hy3.line_parsers.d_swimmer_parsers import d1_parser @@ -217,5 +218,65 @@ def test_e2_genuinely_blank_button_field_is_none(self): self.assertIsNone(entry.finals_button_1_time) +class TestE2ReactionTime(unittest.TestCase): + """E2 col 83-87 carries the swimmer's start reaction time.""" + + def _build_file(self): + opts = {"default_country": "USA"} + file = ParsedHytekFile() + file.meet = Meet() + file.meet.last_team = ( + "FOO", + Team("Foo Bar", "FOO", "FOO", "", "", "", "", "", "", "", "", "", "", "", {}), + ) + d_line = "D1M 27Hansen Mads 10272010 13 27" + e1_line = "E1M 27HanseXX 50D 11109 0U 0.00 22X 37.41S 37.41S 0.00 0.00 0NN N 70" + file = d1_parser(d_line, file, opts) + file = e1_parser(e1_line, file, opts) + return file, opts + + def test_e2_reaction_time_populated(self): + """Real row: 2026 CA SCS Summer A-G Champs, reaction 0.56.""" + file, opts = self._build_file() + e2 = "E2P 38.78L 0 1 3 6 34 0 38.87 38.63 0.00 38.78 0.00 0.5607242026 0 27" + self.assertEqual(130, len(e2)) + file = e2_parser(e2, file, opts) + entry = file.meet.events["22X"].last_entry + self.assertAlmostEqual(0.56, entry.prelim_reaction_time, places=2) + + def test_e2_reaction_time_does_not_shift_the_date(self): + """Offset regression: reading col 83-87 must leave the date at col 88. + + A one-column error still yields plausible-looking floats, so the date + is the only cheap tripwire that catches it. + """ + file, opts = self._build_file() + e2 = "E2P 38.78L 0 1 3 6 34 0 38.87 38.63 0.00 38.78 0.00 0.5607242026 0 27" + file = e2_parser(e2, file, opts) + entry = file.meet.events["22X"].last_entry + self.assertEqual(datetime.date(2026, 7, 24), entry.prelim_date) + # The pad time must also be untouched by the new read. + self.assertAlmostEqual(38.78, entry.prelim_pad_time, places=2) + + def test_e2_zero_reaction_time_is_none(self): + """Real row: 2014 STAR Tarheel States, reaction column reads 0.00.""" + file, opts = self._build_file() + e2 = "E2F 56.83Y 0 1 3 5 15 0 56.75 0.00 0.00 56.83 0.00 0.0003222014 0 76" + self.assertEqual(130, len(e2)) + file = e2_parser(e2, file, opts) + entry = file.meet.events["22X"].last_entry + self.assertIsNone(entry.finals_reaction_time) + + def test_e2_blank_reaction_time_is_none(self): + """Real row from the same meet with an empty reaction column.""" + file, opts = self._build_file() + e2 = "E2P 36.26L 0 1 3 3 74 0 36.43 36.46 0.00 36.26 0.00 07262026 0 56" + self.assertEqual(130, len(e2)) + file = e2_parser(e2, file, opts) + entry = file.meet.events["22X"].last_entry + self.assertIsNone(entry.prelim_reaction_time) + self.assertEqual(datetime.date(2026, 7, 26), entry.prelim_date) + + if __name__=='__main__': unittest.main() From 9f88696f71825bd75332d55db476851f1fdfd60b Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 27 Jul 2026 21:57:53 -0700 Subject: [PATCH 06/12] feat(hy3): parse the four F2 relay takeoff times at cols 83-102 The code already noted a '15-column gap' before F2's date field without identifying it. That gap is relay legs 2, 3 and 4 (3 x 5 chars); leg 1 sits in the 5-char slot F2 shares with E2. The columns are reaction times, and the file format says so itself: 513 files write the literal NRT ('No Reaction Time') into unmeasured takeover slots, and leg 1 in those same rows carries a signed +0.00. The physics corroborates it across the corpus. Leg 1 is a block start: 23 negatives out of 87,069, median 0.61. Legs 2-4 are exchanges: 2,702 / 2,282 / 2,861 negatives, median ~0.24. Nothing but a takeoff time behaves that way. Exposed as a positional 4-list rather than four named attributes, matching the file's uniform column layout. --- .../hy3/line_parsers/f_relay_parsers.py | 15 ++++-- hytek_parser/hy3/schemas.py | 6 ++- .../hy3/line_parsers/test_f_relay_parsers.py | 54 +++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/hytek_parser/hy3/line_parsers/f_relay_parsers.py b/hytek_parser/hy3/line_parsers/f_relay_parsers.py index 58157ca..e20a295 100644 --- a/hytek_parser/hy3/line_parsers/f_relay_parsers.py +++ b/hytek_parser/hy3/line_parsers/f_relay_parsers.py @@ -2,7 +2,7 @@ from typing import Any from hytek_parser._utils import extract, get_age_group, safe_cast, select_from_enum -from hytek_parser.hy3._utils import parse_time, parse_time_or_none +from hytek_parser.hy3._utils import parse_reaction_time, parse_time, parse_time_or_none from hytek_parser.hy3.enums import ( Course, DisqualificationCode, @@ -108,9 +108,11 @@ def f2_parser( overall_place = safe_cast(int, extract(line, 30, 4)) # previously-dropped F2 timing fields. The five timing-column - # offsets are IDENTICAL to e2_parser; only alt_time_code differs because F2 - # has a 15-column gap before its date field (date at col 103, not 88). - # F2 alt_time_code lives at col 111, not col 96. + # offsets are IDENTICAL to e2_parser. F2 then carries FOUR reaction-time + # slots at cols 83-102 (5 chars each) where E2 carries one at 83-87, which + # is why F2's date sits at col 103 and its alt_time_code at col 111. + # Slot 1 is the leadoff block start; slots 2-4 are exchange takeovers and + # are legitimately negative when a swimmer leaves early. pad_time = parse_time_or_none(extract(line, 63, 12)) button_1_time = parse_time_or_none(extract(line, 39, 8)) button_2_time = parse_time_or_none(extract(line, 47, 8)) @@ -118,6 +120,10 @@ def f2_parser( backup_4_time = parse_time_or_none(extract(line, 75, 8)) alt_time_code = extract(line, 111, 1) or None # F2 offset; observed: 'A'/'K'/blank + reaction_times = [ + parse_reaction_time(extract(line, 83 + 5 * i, 5)) for i in range(4) + ] + raw_date = extract(line, 103, 8).strip() date_ = datetime.strptime(raw_date, "%m%d%Y").date() if raw_date else None @@ -150,6 +156,7 @@ def f2_parser( setattr(entry, f"{prefix}_button_2_time", button_2_time) setattr(entry, f"{prefix}_button_3_time", button_3_time) setattr(entry, f"{prefix}_backup_4_time", backup_4_time) + setattr(entry, f"{prefix}_reaction_times", reaction_times) setattr(entry, f"{prefix}_alt_time_code", alt_time_code) event.last_entry = entry diff --git a/hytek_parser/hy3/schemas.py b/hytek_parser/hy3/schemas.py index 17df30d..71a4617 100644 --- a/hytek_parser/hy3/schemas.py +++ b/hytek_parser/hy3/schemas.py @@ -1,5 +1,5 @@ from datetime import date, datetime -from typing import Optional, Union +from typing import List, Optional, Union from attrs import Factory, define, field @@ -120,6 +120,7 @@ class EventEntry: prelim_button_3_time: Optional[float] = None prelim_backup_4_time: Optional[float] = None prelim_reaction_time: Optional[float] = None + prelim_reaction_times: Optional[List[Optional[float]]] = None # col 96; semantics unverified — observed 'A'/'K'/blank prelim_alt_time_code: Optional[str] = None @@ -141,6 +142,7 @@ class EventEntry: swimoff_button_3_time: Optional[float] = None swimoff_backup_4_time: Optional[float] = None swimoff_reaction_time: Optional[float] = None + swimoff_reaction_times: Optional[List[Optional[float]]] = None # col 96; semantics unverified — observed 'A'/'K'/blank swimoff_alt_time_code: Optional[str] = None @@ -162,6 +164,7 @@ class EventEntry: finals_button_3_time: Optional[float] = None finals_backup_4_time: Optional[float] = None finals_reaction_time: Optional[float] = None + finals_reaction_times: Optional[List[Optional[float]]] = None # col 96; semantics unverified — observed 'A'/'K'/blank finals_alt_time_code: Optional[str] = None @@ -220,6 +223,7 @@ def __init__( setattr(self, f"{course}_button_3_time", None) setattr(self, f"{course}_backup_4_time", None) setattr(self, f"{course}_reaction_time", None) + setattr(self, f"{course}_reaction_times", None) setattr(self, f"{course}_alt_time_code", None) self.prelim_dq_info = None diff --git a/tests/hy3/line_parsers/test_f_relay_parsers.py b/tests/hy3/line_parsers/test_f_relay_parsers.py index ed5cfea..19e9dc5 100644 --- a/tests/hy3/line_parsers/test_f_relay_parsers.py +++ b/tests/hy3/line_parsers/test_f_relay_parsers.py @@ -1,3 +1,4 @@ +import datetime import unittest from hytek_parser.hy3.schemas import ( @@ -213,5 +214,58 @@ def test_f2_blank_alt_code(self): self.assertIsNone(entry.finals_alt_time_code) +class TestF2ReactionTimes(unittest.TestCase): + """F2 cols 83-102 carry four reaction times: leg 1 is a block start, + legs 2-4 are exchange takeovers.""" + + def _build_file_with_relay_entry(self): + opts = {"default_country": "USA"} + file = ParsedHytekFile() + file.meet = Meet() + file.meet.last_team = ( + "FOO", + Team("Foo Bar", "FOO", "FOO", "", "", "", "", "", "", "", "", "", "", "", {}), + ) + f1_line = "F1FOO A 0FFG 200E 0109 0S 30.00 2 112.37Y 112.37Y 52.00 0.00 NN 4 NA 29" + file = f1_parser(f1_line, file, opts) + return file, opts + + def test_f2_four_reaction_times_with_negative_takeover(self): + """Real row: 2026 CA SCS Summer A-G Champs. Leg 4 exchanged early.""" + file, opts = self._build_file_with_relay_entry() + f2 = "F2F 121.77L 0 1 6 3 11 0 121.70 121.98 0.00 121.77 0.00 0.64 0.47 0.40-0.2907242026 0 0 19" + self.assertEqual(130, len(f2)) + file = f2_parser(f2, file, opts) + entry = file.meet.last_event[1].last_entry + rts = entry.finals_reaction_times + self.assertEqual(4, len(rts)) + self.assertAlmostEqual(0.64, rts[0], places=2) + self.assertAlmostEqual(0.47, rts[1], places=2) + self.assertAlmostEqual(0.40, rts[2], places=2) + self.assertAlmostEqual(-0.29, rts[3], places=2) + + def test_f2_nrt_sentinel_and_signed_zero(self): + """Real row: 2019 MT Western Zone Age Group Champs. + + Leg 1 writes '+0.00'; the three takeover slots write the literal NRT + ('No Reaction Time'). All four mean 'not recorded'. + """ + file, opts = self._build_file_with_relay_entry() + f2 = "F2F 272.84L 0 2 1 5 19 0 273.04 272.96 0.00 272.84 0.00+0.00 NRT NRT NRT08072019 0 80" + self.assertEqual(130, len(f2)) + file = f2_parser(f2, file, opts) + entry = file.meet.last_event[1].last_entry + self.assertEqual([None, None, None, None], entry.finals_reaction_times) + + def test_f2_reaction_times_do_not_shift_the_date(self): + """Offset regression: F2's date sits at col 103, after all four slots.""" + file, opts = self._build_file_with_relay_entry() + f2 = "F2F 272.84L 0 2 1 5 19 0 273.04 272.96 0.00 272.84 0.00+0.00 NRT NRT NRT08072019 0 80" + file = f2_parser(f2, file, opts) + entry = file.meet.last_event[1].last_entry + self.assertEqual(datetime.date(2019, 8, 7), entry.finals_date) + self.assertAlmostEqual(272.84, entry.finals_pad_time, places=2) + + if __name__ == "__main__": unittest.main() From f353ad43403d7ff963e2c6f5781a221846dfca0d Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 27 Jul 2026 22:09:47 -0700 Subject: [PATCH 07/12] test(hy3): add reaction-time fixtures for dense and NRT-sentinel files Two PII-redacted slices of real meets, covering the two opposite shapes the reaction columns take. The dense fixture is a modern fully-automatic-timing meet: 35 of its 37 E2 rows carry a start reaction time (0.53-0.90), all four F2 takeoff slots are populated on every relay, and one relay has three negative takeovers. That last one is the regression guard -- a >0.0 filter on these columns would drop every early exchange and the tests would still look green without it. The NRT fixture is the opposite: an older generation that wrote the literal NRT into every takeover slot and a signed +0.00 into every leadoff, with the E2 reaction column blank throughout. Everything must parse to None. A raw-file tripwire asserts the sentinels are still physically present, so a regenerated fixture that lost them cannot pass by vacuous truth. Redaction extends the documented rules to two fields they had missed: the 5-char last-name prefix that E1 and each F3 relay-leg slot carry. No parser reads them, but they are real surname fragments. Verified by diffing every emitted line against its source line and asserting the differing character positions fall only inside the redacted column ranges. --- tests/hy3/fixtures/README.md | 20 ++- .../hy3/fixtures/mm_reaction_times_dense.hy3 | 131 +++++++++++++++ tests/hy3/fixtures/mm_relay_nrt_sentinel.hy3 | 158 ++++++++++++++++++ tests/hy3/test_integration.py | 131 +++++++++++++++ 4 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 tests/hy3/fixtures/mm_reaction_times_dense.hy3 create mode 100644 tests/hy3/fixtures/mm_relay_nrt_sentinel.hy3 diff --git a/tests/hy3/fixtures/README.md b/tests/hy3/fixtures/README.md index ec47302..c5a1d00 100644 --- a/tests/hy3/fixtures/README.md +++ b/tests/hy3/fixtures/README.md @@ -16,6 +16,8 @@ from publicly distributed meet results: | `mm4_ymca_col92_division_citizenship.hy3` | MM4 4.0Ec | 2013 YMCA Nationals Short Course (Virginia Swimming) | E1 col-92 `meet_division` (`SW`); D1 `citizenship` (`USA`); C1 `region` (NE/MD/NI); E2 pad+button timing; F2 relay pad+button timing | | `mm_col77_division.hy3` | MM5 6.0Cc | 2015 State/Non-State Open 25 yd (Wisconsin Swimming) | E1 col-77 `meet_division` (`JV`); E2 `alt_time_code` (`A`, `K`); pad-vs-button divergence (pad=107.39 vs btn1=102.49); C1 `region` (`WI`) | | `mm_pad_button_divergence.hy3` | MM5 8.0Fd | 2025 MT HOT Tropical Meet (Montana Swimming) | clear pad-vs-button divergence (pad=36.26 vs result=75.29, half-pool touchpad); E2 `alt_time_code` (`A`); two LSC regions (MT, WY) | +| `mm_reaction_times_dense.hy3` | MM5 8.0Gh | 2026 CA SCS Summer A/G Champs @ BREA (Southern California Swimming) | E2 `reaction_time` densely populated (35 of 37 rows, 0.53-0.90); F2 four-slot `reaction_times` fully populated, including a relay with three negative takeovers | +| `mm_relay_nrt_sentinel.hy3` | MM5 7.0Dd | 2019 Western Zone Age Group Championships (Montana Swimming) | F2 `NRT` sentinel on every takeover slot and a signed `+0.00` on every leadoff; E2 reaction column blank throughout | ## Redaction @@ -28,12 +30,23 @@ same column width so the files remain parseable: - `D1` date_of_birth → `01011970` - `C1` contact_name_1 (cols 56-85) → `Test Contact` padded to 30 chars, or blank if original was blank - `C1` contact_name_2 (cols 86-115) → `Test Contact` padded to 30 chars, or blank if original was blank -- `C2` address_1, city, zip_code → blank (state and country retained) +- `C2` address_1, address_2, city, zip_code → blank (state and country retained) - `C3` daytime_phone, evening_phone, fax, email → blank +- `E1` cols 9-13 (the first five characters of the swimmer's last name) → `Swimm` +- `F3` cols 9-13 of each 13-char relay-leg slot (same name prefix) → `Swimm` + +The `E1`/`F3` name prefixes are not read by any parser, but they do carry a +real surname fragment, so they are redacted to match the `D1` placeholder. +Fixtures added before this rule was written still carry them. Team codes, team names, meet name, facility, and event metadata are intact — these are public information from the original meet results. +Every replacement is the same width as the field it replaces, so all lines +stay 130 characters and every column offset is preserved. Line checksums +(the trailing two characters) are left as-is; the parser does not validate +them. + ## Coverage gaps (known) `backup_4_time` (E2 col 75-82) is always zero in the three source meets; @@ -41,6 +54,11 @@ no real file with a non-zero value was found among the designated sources. The f is exercised by the line-parser-level tests in `test_e_event_parsers.py` but is not covered by any integration fixture. +A minority of files put values above 2.0 in the E2 reaction column (80,706 +across a 33,008-file corpus, concentrated in 117 files that are 100% +implausible). Their meaning is unresolved; the parser passes them through +unchanged and no fixture asserts on them. + ## Bug references The bugs these fixtures exercise are described in the PR that introduced diff --git a/tests/hy3/fixtures/mm_reaction_times_dense.hy3 b/tests/hy3/fixtures/mm_reaction_times_dense.hy3 new file mode 100644 index 0000000..05d475d --- /dev/null +++ b/tests/hy3/fixtures/mm_reaction_times_dense.hy3 @@ -0,0 +1,131 @@ +A107Results From MM to TM Hy-Tek, Ltd MM5 8.0Gh 07262026 7:31 PMMt. San Antonio College Aq Ctr - Site License 83 +B12026 CA SCS Summer A/G Champs @ BREA Mt Sac College 072320260726202607232026 0 88 +B2 010101L1 33.00 S26-210 72 +C1LMST Lynwood Marlins Swim Team CATest Contact Test Contact 0 3 04 +C2 CA USA 86 +D1F11303Swimmer11303 Test 16301011970 14 0 N 23 +E1F11303SwimmFG 100C 13 14 0S 8.00 37A 88.30L 77.99Y 0.00 0.00 NN N 50 +E2P 90.43L 0 2 5 5 40 0 90.64 90.59 0.00 90.43 0.00 0.6507242026 0 07 +E1F11303SwimmFG 50C 13 14 0S 8.00 61A 39.97L 35.30Y 0.00 0.00 NN N 40 +E2P 40.18L 0 3 8 4 32 0 40.21 40.28 0.00 40.18 0.00 0.5907252026 0 96 +E1F11303SwimmFG 50A 13 14 0S 8.00 99A 33.34L 33.34L 0.00 0.00 NN Y 30 +E2P 33.16L 0 1 7 3 43 0 33.13 33.26 0.00 33.16 0.00 0.5907262026 0 96 +D1M11300Swimmer11300 Test 16001011970 17 0 N 82 +D1M11301Swimmer11301 Test 16101011970 18 0 N 32 +D1M11302Swimmer11302 Test 16201011970 17 0 N 22 +E1M11302SwimmMB 200B 15109 0S 8.00 4B 156.16L 156.16L 0.00 0.00 NN Y 90 +E2P 157.33L 0 3 9 8 22 0 157.58 157.44 0.00 157.33 0.00 0.6807232026 0 67 +G1P 2 0.00P 4 75.53P 6 0.00P 8 157.33 54 +E1M11302SwimmMB 100A 15109 0S 8.00 12B 57.91L 57.91L 0.00 0.00 NN N 80 +E2P 58.89L 0 9 7 7 57 0 59.17 59.06 0.00 58.89 0.00 0.6407232026 0 37 +E1M11302SwimmMB 200A 15109 0S 8.00 22B 136.47L 136.47L 0.00 0.00 NN Y 11 +E2P 137.09L 0 2 5 6 49 0 137.21 137.24 0.00 137.09 0.00 0.6607242026 0 57 +G1P 2 63.40P 4 137.09 52 +D1M11304Swimmer11304 Test 16401011970 15 0 N 13 +E1M11304SwimmMB 200B 15109 0S 8.00 4B 147.11L 147.11L 0.00 0.00 NN N 50 +E2F 149.26L 0 3 9 8 17 18 149.42 149.47 0.00 149.26 0.00 0.8207232026 0 0 87 +G1F 2 34.65F 4 71.83F 6 110.56F 8 149.26 74 +E1M11304SwimmMB 200B 15109 0S 8.00 4B 147.11L 147.11L 0.00 0.00 NN N 50 +E2P 150.14L 0 5 9 9 19 0 150.26 150.20 0.00 150.14 0.00 0.7207232026 0 37 +G1P 2 0.00P 4 71.85P 6 0.00P 8 150.14 44 +E1M11304SwimmMB 200D 15109 0S 8.00 8B 146.49L 146.49L 0.00 0.00 NN N 60 +E2P 148.44L 0 5 1 9 22 0 148.69 148.65 0.00 148.44 0.00 0.6807232026 0 67 +G1P 2 68.55P 4 148.44 52 +E1M11304SwimmMB 100A 15109 0S 8.00 12B 57.82L 57.82L 0.00 0.00 NN N 50 +E2P 60.19L 0 9 6 9 65 0 60.14 59.96 0.00 60.19 0.00 0.7807232026 0 17 +E1M11304SwimmMB 100D 15109 0S 8.00 68B 63.38L 55.98Y 0.00 0.00 NN N 80 +E2P 65.07L 0 5 5 8 43 0 65.40 65.12 0.00 65.07 0.00 0.7707252026 0 07 +E1M11304SwimmMB 100B 15109 0S 8.00 94B 67.59L 67.59L 0.00 0.00 NN N 60 +E2P 68.59L 0 5 6 5 27 0 68.72 68.65 0.00 68.59 0.00 0.7507262026 0 37 +E1M11304SwimmMB 800A 15109 0S 8.00102B 600.02L 600.02L 0.00 0.00 NN N 60 +E2F 601.34L 0 3 8 7 18 0 602.14 0.00 0.00 601.34 0.00 07262026K 0 0 07 +G1F 2 66.88F 4 139.18F 6 213.02F 8 288.20F10 365.07F12 444.20F14 523.34F16 601.34 10 +F1LMST A 0MMB 200A 15109 0S 20.00 46 109.33L 109.33L 0.00 0.00 NN 4 NA 99 +F2F 110.12L 0 3 8 4 17 0 110.28 110.29 0.00 110.12 0.00 0.69 0.09 0.71 0.3807242026 0 88 +G1F 2 27.03F 4 54.91F 6 84.37F 8 110.12 44 +F3M11302SwimmF1M11301SwimmF2M11300SwimmF3M11304SwimmF4 45 +C1MSST Maywood Sparks Swim Team CA 0 3 92 +C2 CA 12 +D1M11605Swimmer11605 Test 42401011970 12 0 USA N 71 +E1M11605SwimmMB 200E 11 12 0S 8.00 72 167.41L 167.41L 0.00 0.00 NN N 29 +E2F 167.66L 0 1 9 9 18 0 167.67 167.80 0.00 167.66 0.00 0.5907252026 0 0 87 +G1F 2 37.40F 4 79.11F 6 130.85F 8 167.66 84 +E1M11605SwimmMB 200E 11 12 0S 8.00 72 167.41L 167.41L 0.00 0.00 NN N 29 +E2P 165.13L 0 1 3 5 17 0 165.10 165.26 0.00 165.13 0.00 0.6707252026 0 47 +G1P 2 79.52P 4 165.13 52 +E1M11605SwimmMB 100A 11 12 0S 8.00 10 66.13L 66.13L 4.00 0.00 NN N 88 +E2F 65.03L 0 1 7 4 13 0 65.07 65.12 0.00 65.03 0.00 0.6107232026 0 0 96 +G1F 2 31.18F 4 65.03 22 +E1M11605SwimmMB 100A 11 12 0S 8.00 10 66.13L 66.13L 4.00 0.00 NN N 88 +E2P 65.09L 0 2 3 4 13 0 65.44 65.57 0.00 65.09 0.00 0.7107232026K 0 47 +G1P 2 65.09 01 +E1M11605SwimmMB 200A 11 12 0S 8.00 26 143.78L 143.78L 0.00 0.00 NN N 29 +E2P 146.19L 0 2 7 6 21 0 146.35 146.25 0.00 146.19 0.00 0.7107242026 0 57 +G1P 2 69.57P 4 146.19 62 +E1M11605SwimmMB 50B 11 12 0S 8.00 92 35.27L 35.27L 13.00 0.00 NN N 19 +E2F 34.07L 0 2 8 6 6 0 34.14 34.15 0.00 34.07 0.00 0.6207262026 0 0 86 +E1M11605SwimmMB 50B 11 12 0S 8.00 92 35.27L 35.27L 13.00 0.00 NN N 19 +E2P 34.22L 0 4 4 3 6 0 34.37 34.85 0.00 34.22 0.00 0.5307262026K 0 27 +E1M11605SwimmMB 50A 11 12 0S 8.00 98 29.92L 29.92L 5.00 0.00 NN N 09 +E2F 29.57L 0 1 2 3 12 0 29.81 29.60 0.00 29.57 0.00 0.6807262026 0 0 07 +E1M11605SwimmMB 50A 11 12 0S 8.00 98 29.92L 29.92L 5.00 0.00 NN N 09 +E2P 30.41L 0 3 7 4 16 0 30.56 30.49 0.00 30.41 0.00 0.6507262026 0 96 +E1M11605SwimmMB 100B 11 12 0S 8.00 2 76.14L 76.14L 14.00 0.00 NN N 98 +E2F 74.59L 0 2 9 5 5 0 74.72 74.88 0.00 74.59 0.00 0.6507232026 0 0 17 +G1F 2 35.39F 4 74.59 32 +E1M11605SwimmMB 100B 11 12 0S 8.00 2 76.14L 76.14L 14.00 0.00 NN N 98 +E2P 74.72L 0 3 4 4 8 0 74.62 74.91 0.00 74.72 0.00 0.6807232026 0 96 +G1P 2 0.00P 4 74.72 22 +D1M11610Swimmer11610 Test 42901011970 11 0 USA N 52 +E1M11610SwimmMB 200E 11 12 0S 8.00 72 177.26L 177.26L 0.00 0.00 NN N 39 +E2F 167.02L 0 1 1 8 17 0 167.20 167.18 0.00 167.02 0.00 0.5807252026 0 0 57 +G1F 2 36.95F 4 81.38F 6 128.57F 8 167.02 94 +E1M11610SwimmMB 200E 11 12 0S 8.00 72 177.26L 177.26L 0.00 0.00 NN N 39 +E2P 169.17L 0 2 1 6 18 0 169.35 169.24 0.00 169.17 0.00 0.7407252026 0 67 +G1P 2 81.05P 4 169.17 52 +E1M11610SwimmMB 50C 11 12 0S 8.00 36 39.83L 39.83L 2.00 0.00 NN N 09 +E2F 39.45L 0 1 7 6 15 0 39.44 39.33 0.00 39.45 0.00 0.7107242026 0 0 17 +E1M11610SwimmMB 50C 11 12 0S 8.00 36 39.83L 39.83L 2.00 0.00 NN N 09 +E2P 39.53L 0 5 3 5 13 0 39.64 39.56 0.00 39.53 0.00 0.6907242026 0 17 +E1M11610SwimmMB 100C 11 12 0S 8.00 84 89.16L 89.16L 6.00 0.00 NN N 19 +E2F 85.20L 0 1 3 2 11 0 85.22 85.38 0.00 85.20 0.00 0.6907262026 0 0 07 +G1F 2 40.93F 4 85.20 22 +E1M11610SwimmMB 100C 11 12 0S 8.00 84 89.16L 89.16L 6.00 0.00 NN N 19 +E2P 86.88L 0 4 8 4 14 0 86.90 86.97 0.00 86.88 0.00 0.7007262026 0 37 +G1P 2 86.88 11 +D1M11615Swimmer11615 Test 43401011970 10 0 USA N 94 +E1M11615SwimmMB 100B 5 10 0S 8.00 28 90.96L 90.96L 9.00 0.00 NN N 00 +E2F 86.96L 0 2 1 8 8 9 87.06 87.11 0.00 86.96 0.00 0.9007242026 0 0 27 +G1F 2 43.22F 4 86.96 32 +E1M11615SwimmMB 100B 5 10 0S 8.00 28 90.96L 90.96L 9.00 0.00 NN N 00 +E2P 89.01L 0 3 7 2 9 0 89.01 89.10 0.00 89.01 0.00 0.8307242026 0 96 +G1P 2 89.01 01 +E1M11615SwimmMB 50D 5 10 0S 8.00 64 37.69L 37.69L 5.00 0.00 NN N 00 +E2F 37.80L 0 1 4 4 13 12 37.91 37.90 0.00 37.80 0.00 0.6207252026 0 0 17 +E1M11615SwimmMB 50D 5 10 0S 8.00 64 37.69L 37.69L 5.00 0.00 NN N 00 +E2P 37.77L 0 4 4 4 12 0 37.72 37.74 0.00 37.77 0.00 0.6507252026 0 17 +E1M11615SwimmMB 50B 5 10 0S 8.00 90 43.41L 43.41L 6.00 0.00 NN N 89 +E2F 41.01L 0 1 6 3 12 11 41.16 40.97 0.00 41.01 0.00 0.6707262026 0 0 96 +E1M11615SwimmMB 50B 5 10 0S 8.00 90 43.41L 43.41L 6.00 0.00 NN N 89 +E2P 41.48L 0 2 2 3 11 0 41.47 41.43 0.00 41.48 0.00 0.7407262026 0 96 +E1M11615SwimmMB 200E 5 10 0S 8.00 70 191.56L 191.56L 5.00 0.00 NN N 20 +E2F 188.69L 0 1 4 3 12 0 188.91 188.88 0.00 188.69 0.00 07252026 0 0 37 +G1F 2 40.33F 4 88.65F 6 147.69F 8 188.69 94 +D1M11614Swimmer11614 Test 43301011970 9 0 USA N 13 +D1M11617Swimmer11617 Test 43601011970 8 0 USA N 05 +D1M11612Swimmer11612 Test 43101011970 12 0 USA N 83 +D1M11613Swimmer11613 Test 43201011970 8 0 USA N 35 +D1M11609Swimmer11609 Test 42801011970 12 0 USA N 31 +F1MSST A 0MMB 200E 5 10 0S 20.00 48 173.66L 173.66L 0.00 0.00 NN 4 NA 79 +F2F 171.48LQ67 0 2 6 0 0 0 171.56 171.61 0.00 171.48 0.00 0.63-0.16-0.36-0.5807242026 0 0 79 +G1F 2 44.96F 4 93.02F 6 132.13F 8 171.48 74 +F3M11613SwimmF1M11614SwimmF2M11615SwimmF3M11617SwimmF4 83 +H167Early take-off swimmer #3 80 +F1MSST A 0MMB 200E 11 12 0S 20.00 50 143.38L 143.38L 24.00 0.00 NN 4 NA 00 +F2F 142.14L 0 3 5 1 7 0 142.25 142.28 0.00 142.14 0.00 0.65 0.26 0.77 0.3907242026 0 0 88 +G1F 2 34.66F 4 74.66F 6 110.68F 8 142.14 74 +F3M11605SwimmF1M11610SwimmF2M11612SwimmF3M11609SwimmF4 92 +F1MSST A 0MMB 200A 11 12 0S 20.00 42 132.65L 132.65L 24.00 0.00 NN 4 NA 99 +F2F 125.86L 0 3 5 1 7 0 125.98 126.02 0.00 125.86 0.00 0.62 0.61 0.12 0.3607242026 0 0 88 +G1F 2 31.02F 4 64.03F 6 96.69F 8 125.86 54 +F3M11609SwimmF1M11612SwimmF2M11610SwimmF3M11605SwimmF4 03 diff --git a/tests/hy3/fixtures/mm_relay_nrt_sentinel.hy3 b/tests/hy3/fixtures/mm_relay_nrt_sentinel.hy3 new file mode 100644 index 0000000..2cc43ab --- /dev/null +++ b/tests/hy3/fixtures/mm_relay_nrt_sentinel.hy3 @@ -0,0 +1,158 @@ +A107Results From MM to TM Hy-Tek, Ltd MM5 7.0Dd 08112019 11:47 AMInland Empire SC Championship Meet 11 +B12019 Western Zone Age Group Championships Mt Hood CC Aquatic Center 080720190810201908072019 0 50 +B2 010201L1 0.00 19-090 42 +C1IES Inland Empire All Stars Inland Empire IE 0 0 57 +C2 ID USA 22 +D1M 5802Swimmer5802 Test 77801011970 12 0 N 80 +E1M 5802SwimmMB 100A 11 12 0S 9.00 12 64.30L 64.30L 0.00 0.00 NN Y 00 +E2P 63.92L 0 4 8 2 22 0 64.04 64.03 0.00 63.92 0.00 08072019 46 +E1M 5802SwimmMB 100B 11 12 0S 9.00 18 70.77L 70.77L 6.00 0.00 NN N 10 +E2F 70.08L 0 1 5 3 11 0 70.18 70.12 0.00 70.08 0.00+0.0008072019 0 0 86 +G1F 2 34.10F 4 70.08 22 +E1M 5802SwimmMB 100B 11 12 0S 9.00 18 70.77L 70.77L 6.00 0.00 NN N 10 +E2P 71.54L 0 2 2 3 10 0 71.69 71.68 0.00 71.54 0.00 08072019 36 +G1P 4 71.54 01 +E1M 5802SwimmMB 50D 11 12 0S 9.00 36 32.88L 32.88L 0.00 0.00 NN Y 10 +E2P 32.75L 0 5 6 1 28 0 32.86 32.98 0.00 32.75 0.00+0.0008082019 96 +E1M 5802SwimmMB 200B 11 12 0S 9.00 46 154.36L 154.36L 11.00 0.00 NN N 50 +E2F 151.89L 0 2 5 2 8 0 152.24 152.03 0.00 151.89 0.00 08082019 0 76 +G1F 2 74.76F 4 151.89 42 +E1M 5802SwimmMB 50B 11 12 0S 9.00 64 33.83L 33.83L 3.00 0.00 NN N 00 +E2F 33.14L 0 1 1 6 14 0 33.16 33.42 0.00 33.14 0.00+0.0008092019 0 66 +E1M 5802SwimmMB 50B 11 12 0S 9.00 64 33.83L 33.83L 3.00 0.00 NN N 00 +E2P 33.85L 0 4 2 1 15 0 34.04 34.00 0.00 33.85 0.00+0.0008092019 76 +E1M 5802SwimmMB 50A 11 12 0S 9.00 80 29.01L 29.01L 0.00 0.00 NN N 99 +E2P 29.86L 0 4 2 5 35 0 29.98 29.97 0.00 29.86 0.00+0.0008102019 07 +D1F 5798Swimmer5798 Test 77401011970 11 0 N 32 +E1F 5798SwimmFG 100A 11 12 0S 9.00 11 67.64L 67.64L 0.00 0.00 NN Y 00 +E2P 66.80L 0 8 2 1 46 0 66.78 66.93 0.00 66.80 0.00 08072019 56 +E1F 5798SwimmFG 100B 11 12 0S 9.00 17 79.74L 79.74L 0.00 0.00 NN Y 10 +E2P 78.41L 0 4 8 8 50 0 78.36 78.43 0.00 78.41 0.00 08072019 56 +E1F 5798SwimmFG 50D 11 12 0S 9.00 35 33.98L 33.98L 0.00 0.00 NN Y 10 +E2P 34.06L 0 8 4 1 53 0 33.94 33.89 0.00 34.06 0.00+0.0008082019 86 +E1F 5798SwimmFG 200E 11 12 0S 9.00 41 162.39L 162.39L 0.00 0.00 NN Y 30 +E2P 169.16L 0 6 1 7 53 0 169.18 169.27 0.00 169.16 0.00 08082019 07 +G1P 4 78.44P 8 169.16 62 +E1F 5798SwimmFG 50B 11 12 0S 9.00 63 33.97L 30.00Y 0.00 0.00 NN N 00 +E2P 34.19L 0 5 1 1 22 0 34.19 34.28 0.00 34.19 0.00+0.0008092019 86 +E1F 5798SwimmFG 50A 11 12 0S 9.00 79 30.30L 30.30L 0.00 0.00 NN Y 99 +E2P 31.27L 0 6 1 6 52 0 31.28 31.32 0.00 31.27 0.00+0.0008102019 66 +D1F 5795Swimmer5795 Test 77101011970 13 0 N 95 +E1F 5795SwimmFG 100A 13 14 0S 9.00 13 60.22L 53.19Y 0.00 0.00 NN N 99 +E2P 62.45L 0 5 7 1 24 0 62.55 62.64 0.00 62.45 0.00+0.0008072019 86 +E1F 5795SwimmFG 200B 13 14 0S 9.00 19 143.14L 126.43Y 0.00 0.00 NN N 20 +E2P 156.09L 0 4 8 6 45 0 156.15 156.12 0.00 156.09 0.00 08072019 96 +G1P 4 76.53P 8 156.09 62 +E1F 5795SwimmFG 100D 13 14 0S 9.00 37 67.48L 67.48L 0.00 0.00 NN N 00 +E2P 68.49L 0 4 3 3 23 0 68.63 68.63 0.00 68.49 0.00 08082019 56 +E1F 5795SwimmFG 100B 13 14 0S 9.00 65 66.43L 58.67Y 0.00 0.00 NN N 10 +E2P 70.36L 0 5 5 3 22 0 70.52 70.46 0.00 70.36 0.00+0.0008092019 86 +E1F 5795SwimmFG 400A 13 14 0S 9.00 69 287.03L 287.03L 0.00 0.00 NN Y 20 +E2P 289.28L 0 6 5 5 41 0 0.00 0.00 0.00 289.28 0.00 08092019K 76 +G1P 4 69.07P 8 143.68P12 217.79P16 289.28 55 +E1F 5795SwimmFG 200D 13 14 0S 9.00 87 151.23L 133.57Y 0.00 0.00 NN N 30 +E2P 159.61L 0 5 7 4 37 0 159.73 159.57 0.00 159.61 0.00+0.0008102019 57 +G1P 4 74.48P 8 159.61 52 +D1F 5801Swimmer5801 Test 77701011970 14 0 N 66 +D1F 5796Swimmer5796 Test 77201011970 13 0 N 03 +D1F 5800Swimmer5800 Test 77601011970 12 0 N 34 +D1M 5797Swimmer5797 Test 77301011970 12 0 N 43 +D1M 5803Swimmer5803 Test 77901011970 12 0 N 40 +D1F 5808Swimmer5808 Test 78401011970 12 0 N 02 +D1F 5794Swimmer5794 Test 77001011970 14 0 N 03 +D1M 5804Swimmer5804 Test 78001011970 10 0 N 60 +D1F 5809Swimmer5809 Test 78501011970 12 0 N 55 +F1IES A 0FFG 400A 0 12 0S 16.50 23 273.00L 273.00L 0.00 0.00 NN 4 NA 88 +F2F 271.71L 0 2 2 4 18 0 271.85 271.87 0.00 271.71 0.00+0.00 NRT NRT NRT08072019 0 80 +G1F 2 32.74F 4 70.24F 6 103.02F 8 140.46F10 171.21F12 207.43F14 238.25F16 271.71 99 +F3F 5809SwimmF1F 5808SwimmF2F 5798SwimmF3F 5800SwimmF4 05 +F1IES A 0MMB 400A 0 12 0S 16.50 24 275.00L 275.00L 0.00 0.00 NN 4 NA 98 +F2F 280.94L 0 1 6 6 21 0 281.18 281.10 0.00 280.94 0.00+0.00 NRT NRT NRT08072019 0 80 +G1F 2 31.36F 4 66.06F 6 104.51F 8 150.83F10 181.86F12 217.12F14 247.45F16 280.94 00 +F3M 5802SwimmF1M 5804SwimmF2M 5803SwimmF3M 5797SwimmF4 14 +F1IES A 0FFG 400A 0 14 0S 16.50 25 260.00L 260.00L 0.00 0.00 NN 4 NA 88 +F2F 260.55L 0 2 2 4 20 0 260.73 260.67 0.00 260.55 0.00+0.00 NRT NRT NRT08072019 0 70 +G1F 2 30.80F 4 64.21F 6 93.97F 8 128.94F10 160.17F12 195.26F14 226.51F16 260.55 99 +F3F 5795SwimmF1F 5801SwimmF2F 5796SwimmF3F 5794SwimmF4 64 +C1MT Montana Swimming Montana MT 0 0 92 +C2 MT USA 47 +C3 60 +D1M 4857Swimmer4857 Test 8501011970 12 0 N 62 +E1M 4857SwimmMB 50C 11 12 0S 9.00 6 34.79L 34.79L 1.00 0.00 NN N 10 +E2F 37.79L 0 1 7 8 16 0 37.98 37.84 0.00 37.79 0.00+0.0008072019 0 17 +E1M 4857SwimmMB 50C 11 12 0S 9.00 6 34.79L 34.79L 1.00 0.00 NN N 10 +E2P 37.61L 0 2 5 4 14 0 37.60 37.58 0.00 37.61 0.00+0.0008072019 86 +E1M 4857SwimmMB 50D 11 12 0S 9.00 36 31.57L 31.57L 0.00 0.00 NN N 10 +E2P 32.92L 0 4 4 6 33 0 32.76 32.88 0.00 32.92 0.00+0.0008082019 96 +E1M 4857SwimmMB 200E 11 12 0S 9.00 42 158.18L 158.18L 0.00 0.00 NN Y 60 +E2P 160.09L 0 5 4 2 25 0 160.30 160.18 0.00 160.09 0.00+0.0008082019 27 +G1P 4 75.87P 8 160.09 62 +E1M 4857SwimmMB 100C 11 12 0S 9.00 58 76.58L 76.58L 5.00 0.00 NN N 40 +E2F 80.18L 0 1 2 4 12 0 80.24 80.37 0.00 80.18 0.00+0.0008092019 0 86 +G1F 2 37.50F 4 80.18 32 +E1M 4857SwimmMB 100C 11 12 0S 9.00 58 76.58L 76.58L 5.00 0.00 NN N 40 +E2P 82.34L 0 3 3 5 13 0 82.52 82.47 0.00 82.34 0.00+0.0008092019 86 +G1P 4 82.34 01 +E1M 4857SwimmMB 50A 11 12 0S 9.00 80 29.45L 29.45L 0.00 0.00 NN Y 20 +E2P 29.63L 0 5 8 5 33 0 30.00 29.60 0.00 29.63 0.00+0.0008102019 86 +E1M 4857SwimmMB 200C 11 12 0S 9.00 90 168.56L 168.56L 4.00 0.00 NN N 60 +E2F 175.16L 0 1 7 8 13 0 0.00 175.28 0.00 175.16 0.00+0.0008102019 0 07 +G1F 2 40.20F 4 83.64F 6 128.82F 8 175.16 74 +D1F 4860Swimmer4860 Test 8801011970 12 0 N 22 +E1F 4860SwimmFG 100B 11 12 0S 9.00 17 73.57L 64.98Y 0.00 0.00 NN N 10 +E2P 75.07L 0 6 6 3 27 0 75.18 75.11 0.00 75.07 0.00 08072019 46 +E1F 4860SwimmFG 50D 11 12 0S 9.00 35 33.61L 33.61L 0.00 0.00 NN Y 89 +E2P 35.18L 0 7 1 8 59 0 35.41 35.36 0.00 35.18 0.00+0.0008082019 86 +E1F 4860SwimmFG 200E 11 12 0S 9.00 41 179.42L 179.42L 0.00 0.00 NN Y 20 +E2P 184.27L 0 8 2 5 61 0 0.00 184.36 0.00 184.27 0.00 08082019 56 +G1P 4 82.32P 8 184.27 52 +E1F 4860SwimmFG 200B 11 12 0S 9.00 45 159.75L 159.75L 0.00 0.00 NN N 20 +E2F 164.78L 0 3 5 6 30 0 164.97 164.79 0.00 164.78 0.00+0.0008082019 0 67 +G1F 2 80.36F 4 164.78 42 +E1F 4860SwimmFG 50B 11 12 0S 9.00 63 33.70L 33.70L 0.00 0.00 NN N 89 +E2P 35.07L 0 1 1 8 33 0 35.13 35.16 0.00 35.07 0.00+0.0008092019 76 +E1F 4860SwimmFG 50A 11 12 0S 9.00 79 34.54L 34.54L 0.00 0.00 NN Y 99 +E2P 31.88L 0 10 8 1 65 0 32.02 32.01 0.00 31.88 0.00+0.0008102019 96 +D1F 4847Swimmer4847 Test 7501011970 14 0 N 60 +E1F 4847SwimmFG 100C 13 14 0S 9.00 7 85.73L 85.73L 0.00 0.00 NN Y 99 +E2P 87.08L 0 8 4 5 60 0 87.19 87.19 0.00 87.08 0.00 08072019 56 +E1F 4847SwimmFG 100A 13 14 0S 9.00 13 71.16L 71.16L 0.00 0.00 NN Y 99 +E2P 66.83L 0 13 4 3 88 0 67.19 67.16 0.00 66.83 0.00+0.0008072019K 67 +E1F 4847SwimmFG 200A 13 14 0S 9.00 31 139.05L 139.05L 0.00 0.00 NN Y 20 +E2P 138.57L 0 8 4 2 46 0 138.70 138.74 0.00 138.57 0.00 08082019 96 +G1P 4 67.72P 8 138.57 62 +E1F 4847SwimmFG 200C 13 14 0S 9.00 59 185.57L 185.57L 0.00 0.00 NN Y 40 +E2P 184.86L 0 6 7 4 43 0 184.98 184.83 0.00 184.86 0.00 08092019 17 +G1P 4 89.67P 8 184.86 72 +E1F 4847SwimmFG 200E 13 14 0S 9.00 75 161.83L 161.83L 0.00 0.00 NN Y 30 +E2P 158.71L 0 10 3 1 56 0 158.80 158.86 0.00 158.71 0.00 08102019 17 +G1P 4 77.36P 8 158.71 52 +E1F 4847SwimmFG 1500A 13 14 0S 9.00 91 1128.49L 1109.69Y 0.00 0.00 NN N 90 +E2F 1135.14L 0 4 1 3 21 0 1135.36 1135.20 0.00 1135.14 0.00+0.0008102019 0 67 +G1F 2 33.64F 4 71.36F 6 109.21F 8 147.26F10 184.40F12 222.25F14 260.32F16 298.26F18 336.92F20 375.47F22 413.88 14 +G1F24 452.87F26 491.21F28 530.69F30 569.41F32 608.28F34 646.17F36 684.92F38 723.71F40 761.55F42 799.35F44 837.41 35 +G1F46 875.10F48 912.76F50 949.75F52 987.17F54 1024.17F56 1061.89F58 1098.76F60 1135.14 81 +D1F 4855Swimmer4855 Test 8301011970 12 0 N 42 +D1F 4863Swimmer4863 Test 9101011970 11 0 N 43 +D1F 4850Swimmer4850 Test 7801011970 13 0 N 83 +D1F 4851Swimmer4851 Test 7901011970 14 0 N 32 +D1F 4867Swimmer4867 Test 9501011970 10 0 N 32 +D1F 4862Swimmer4862 Test 9001011970 14 0 N 83 +D1M 4861Swimmer4861 Test 8901011970 12 0 N 22 +D1M 4852Swimmer4852 Test 8001011970 12 0 N 71 +D1F 4849Swimmer4849 Test 7701011970 14 0 N 71 +D1M 4864Swimmer4864 Test 9201011970 12 0 N 73 +D1F 4854Swimmer4854 Test 8201011970 11 0 N 51 +F1MT A 0FFG 400A 0 12 0S 16.50 23 278.79L 278.79L 0.00 0.00 NN 4 NA 09 +F2F 283.53L 0 2 7 7 24 0 283.52 283.60 0.00 283.53 0.00+0.00 NRT NRT NRT08072019 0 80 +G1F 2 32.03F 4 67.17F 6 102.03F 8 142.62F10 178.41F12 217.71F14 249.11F16 283.53 99 +F3F 4863SwimmF1F 4854SwimmF2F 4867SwimmF3F 4855SwimmF4 05 +F1MT A 0MMB 400A 0 12 0S 16.50 24 286.81L 286.81L 0.00 0.00 NN 4 NA 09 +F2F 273.54L 0 1 7 4 18 0 273.63 273.68 0.00 273.54 0.00+0.00 NRT NRT NRT08072019 0 90 +G1F 2 31.36F 4 66.76F 6 100.42F 8 138.93F10 170.88F12 205.49F14 238.62F16 273.54 10 +F3M 4857SwimmF1M 4861SwimmF2M 4864SwimmF3M 4852SwimmF4 14 +F1MT A 0FFG 400A 0 14 0S 16.50 25 259.62L 259.62L 0.00 0.00 NN 4 NA 98 +F2F 256.54LQ6G 0 2 6 0 0 0 256.76 256.64 0.00 256.54 0.00+0.00 NRT NRT NRT08072019 47 0 61 +G1F 2 30.39F 4 64.15F 6 95.75F 8 131.27F10 161.13F12 195.44F14 224.77F16 256.54 99 +F3F 4862SwimmF1F 4849SwimmF2F 4851SwimmF3F 4850SwimmF4 34 +H16GEarly take-off swimmer #3 90 diff --git a/tests/hy3/test_integration.py b/tests/hy3/test_integration.py index e04e16c..e28bee6 100644 --- a/tests/hy3/test_integration.py +++ b/tests/hy3/test_integration.py @@ -366,5 +366,136 @@ def test_divergence_entry_pad_roughly_half_of_button_1(self) -> None: self.assertAlmostEqual(btn1, 75.29, places=2) +class TestReactionTimesDense(unittest.TestCase): + """MM5 8.0Gh (2026 CA SCS Summer A/G Champs) — reaction times populated. + + A modern fully-automatic-timing meet: nearly every individual E2 row + carries a start reaction time, and every relay F2 row carries all four + takeoff slots including negative (early) exchanges. + """ + + @classmethod + def setUpClass(cls) -> None: + cls.parsed = parse_hy3(str(FIXTURE_DIR / "mm_reaction_times_dense.hy3")) + cls.meet = cls.parsed.meet + + def _individual_reaction_times(self) -> list: + return [ + v + for _ev, e in _all_entries(self.meet, individual_only=True) + for slot in ("prelim", "swimoff", "finals") + if (v := _slot_field(e, slot, "reaction_time")) is not None + ] + + def _relay_reaction_times(self) -> list: + return [ + v + for _ev, e in _all_entries(self.meet, relay_only=True) + for slot in ("prelim", "swimoff", "finals") + if (v := _slot_field(e, slot, "reaction_times")) is not None + ] + + def test_individual_reaction_times_populated_and_plausible(self) -> None: + values = self._individual_reaction_times() + self.assertGreater(len(values), 20, "expected a densely populated fixture") + # A one-column misread of E2 col 83-87 still yields plausible-looking + # floats, so bound the range rather than merely asserting non-None. + self.assertTrue( + all(0.0 < v <= 2.0 for v in values), + f"implausible reaction times: {sorted(values)[:5]}", + ) + + def test_relay_entries_expose_four_reaction_slots(self) -> None: + quads = self._relay_reaction_times() + self.assertGreater(len(quads), 0, "no relay reaction-time lists parsed") + for quad in quads: + self.assertEqual(4, len(quad), f"expected 4 takeoff slots, got {quad}") + fully_populated = [q for q in quads if all(v is not None for v in q)] + self.assertGreater( + len(fully_populated), 0, "expected at least one all-slots relay row" + ) + + def test_relay_has_a_negative_takeover(self) -> None: + """Slots 2-4 are exchanges and go negative when a swimmer leaves early. + + This is the regression guard for anyone tempted to route these columns + through a >0.0 filter, which would silently drop every early exchange. + """ + negatives = [ + v for quad in self._relay_reaction_times() for v in quad[1:] if v and v < 0 + ] + self.assertGreater(len(negatives), 0, "no negative takeover value parsed") + self.assertTrue( + all(-2.0 <= v < 0.0 for v in negatives), + f"implausible negative takeovers: {negatives}", + ) + + def test_relay_leadoff_slot_is_never_negative(self) -> None: + """Slot 1 is a block start, not an exchange; it cannot be early.""" + leadoffs = [q[0] for q in self._relay_reaction_times() if q[0] is not None] + self.assertGreater(len(leadoffs), 0, "no leadoff reaction times parsed") + self.assertTrue(all(v > 0 for v in leadoffs), f"negative leadoff: {leadoffs}") + + +class TestRelayNrtSentinel(unittest.TestCase): + """MM5 7.0Dd (2019 Western Zone Age Group Champs) — the NRT sentinel. + + The opposite of the dense fixture: Meet Manager wrote the literal ``NRT`` + ("No Reaction Time") into every takeover slot and a signed ``+0.00`` into + every leadoff. Both spellings mean "not recorded" and must parse to None + rather than to 0.0 or a crash. + """ + + FIXTURE = "mm_relay_nrt_sentinel.hy3" + + @classmethod + def setUpClass(cls) -> None: + cls.parsed = parse_hy3(str(FIXTURE_DIR / cls.FIXTURE)) + cls.meet = cls.parsed.meet + + def test_fixture_still_contains_the_raw_sentinels(self) -> None: + """Tripwire: a regenerated fixture that lost NRT would pass silently.""" + raw = (FIXTURE_DIR / self.FIXTURE).read_text(encoding="latin-1") + f2_lines = [l for l in raw.splitlines() if l.startswith("F2")] + self.assertGreater(len(f2_lines), 0, "fixture has no F2 lines") + self.assertTrue( + any("NRT" in l for l in f2_lines), "fixture no longer carries NRT" + ) + self.assertTrue( + any("+0.00" in l[82:102] for l in f2_lines), + "fixture no longer carries a signed +0.00 leadoff", + ) + + def test_every_relay_reaction_slot_is_none(self) -> None: + quads = [ + v + for _ev, e in _all_entries(self.meet, relay_only=True) + for slot in ("prelim", "swimoff", "finals") + if (v := _slot_field(e, slot, "reaction_times")) is not None + ] + self.assertGreater(len(quads), 0, "no relay reaction-time lists parsed") + for quad in quads: + self.assertEqual([None, None, None, None], quad) + + def test_individual_reaction_times_all_none(self) -> None: + """This generation left E2 col 83-87 blank throughout.""" + values = [ + _slot_field(e, slot, "reaction_time") + for _ev, e in _all_entries(self.meet, individual_only=True) + for slot in ("prelim", "swimoff", "finals") + ] + self.assertGreater(len(values), 0, "no individual entries parsed") + self.assertTrue(all(v is None for v in values)) + + def test_results_still_parse_around_the_sentinels(self) -> None: + """The sentinel must not cost the file its swimmers, events or times.""" + self.assertGreater(len(self.meet.swimmers), 0) + self.assertGreater(len(self.meet.events), 0) + finals = [ + e.finals_time for _ev, e in _all_entries(self.meet) if e.finals_time + ] + self.assertGreater(len(finals), 0, "no finals times parsed") + + if __name__ == "__main__": unittest.main() From c0a0c5c642ea41d1ad1ca3b73912f38992892d1d Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 27 Jul 2026 22:34:37 -0700 Subject: [PATCH 08/12] test(hy3): redact the E1/F3 surname prefixes in the older fixtures E1 cols 9-13, and the same columns in each 13-char F3 relay-leg slot, hold the first five characters of the swimmer's last name. The documented redaction rule set did not cover them, so the five fixtures added before the rule existed still carried 109 distinct real surname fragments between them. No parser reads either field -- e1_parser takes the swimmer id at cols 4-8 and the event gender at col 14, and f3_parser takes the id and the leg number at col 15 -- so blanking them to Swimm, matching the D1 last_name placeholder Swimmer, is parse-safe. All 106 tests pass unchanged. Only those columns were touched: every file is byte-for-byte the same length, and a character-level diff against the previous revision confirms every changed position falls inside an E1 or F3 name-prefix range. Line checksums and all other record types are untouched. A full re-audit of all seven fixtures against the complete documented rule set now reports no unredacted personal field of any kind. --- tests/hy3/fixtures/README.md | 2 +- .../fixtures/mm2_2_0fg_relay_multi_team.hy3 | 154 +++++++++--------- .../mm3_3_0ea_individuals_blank_date.hy3 | 120 +++++++------- .../mm4_ymca_col92_division_citizenship.hy3 | 14 +- tests/hy3/fixtures/mm_col77_division.hy3 | 8 +- .../hy3/fixtures/mm_pad_button_divergence.hy3 | 6 +- 6 files changed, 152 insertions(+), 152 deletions(-) diff --git a/tests/hy3/fixtures/README.md b/tests/hy3/fixtures/README.md index c5a1d00..859c649 100644 --- a/tests/hy3/fixtures/README.md +++ b/tests/hy3/fixtures/README.md @@ -37,7 +37,7 @@ same column width so the files remain parseable: The `E1`/`F3` name prefixes are not read by any parser, but they do carry a real surname fragment, so they are redacted to match the `D1` placeholder. -Fixtures added before this rule was written still carry them. +The rule applies to every fixture in this directory. Team codes, team names, meet name, facility, and event metadata are intact — these are public information from the original meet results. diff --git a/tests/hy3/fixtures/mm2_2_0fg_relay_multi_team.hy3 b/tests/hy3/fixtures/mm2_2_0fg_relay_multi_team.hy3 index 7394f0c..65d49bb 100644 --- a/tests/hy3/fixtures/mm2_2_0fg_relay_multi_team.hy3 +++ b/tests/hy3/fixtures/mm2_2_0fg_relay_multi_team.hy3 @@ -4,273 +4,273 @@ B2Hosted by - Westchester Family YMCA @ Loyola Marymount University C1BRSC Bruin Swim Club CA 0 0 58 C2 CA USA 41 D1F 514Swimmer514 Test 47601011970 10 0 52 -E1F 514CampbFG 100E 9 10 0S 2.75 1 117.31Y 117.31Y 4.00 0.00 2NN N 99 +E1F 514SwimmFG 100E 9 10 0S 2.75 1 117.31Y 117.31Y 4.00 0.00 2NN N 99 E2F 103.87Y 0 3 8 4 13 0 103.87 104.07 103.85 103.87 0.00 0 26 D1M 524Swimmer524 Test 48601011970 10 0 93 -E1M 524HardiMB 100E 9 10 0S 2.75 2 110.00Y 110.00Y 13.00 0.00 1NN N 99 +E1M 524SwimmMB 100E 9 10 0S 2.75 2 110.00Y 110.00Y 13.00 0.00 1NN N 99 E2F 93.24Y 0 2 3 2 6 0 93.21 93.27 93.24 93.24 0.00 0 55 D1F 517Swimmer517 Test 47901011970 9 0 54 -E1F 517MoskoFG 100E 9 10 0S 2.75 1 108.00Y 108.00Y 13.00 0.00 2NN N 20 +E1F 517SwimmFG 100E 9 10 0S 2.75 1 108.00Y 108.00Y 13.00 0.00 2NN N 20 E2F 99.23Y 0 4 8 2 6 0 99.23 99.25 99.10 99.23 0.00 0 75 D1F 535Swimmer535 Test 49701011970 9 0 62 D1F 525Swimmer525 Test 48701011970 9 0 42 -E1F 525SchecFG 100E 9 10 0S 2.75 1 108.03Y 108.03Y 14.00 0.00 2NN N 00 +E1F 525SwimmFG 100E 9 10 0S 2.75 1 108.03Y 108.03Y 14.00 0.00 2NN N 00 E2F 99.09Y 0 3 4 2 5 0 99.05 99.11 99.09 99.09 0.00 0 85 D1M 522Swimmer522 Test 48401011970 9 0 51 -E1M 522VitouMB 100E 9 10 0S 2.75 2 100.00Y 100.00Y 0.00 0.00 0NN N 00 +E1M 522SwimmMB 100E 9 10 0S 2.75 2 100.00Y 100.00Y 0.00 0.00 0NN N 00 E2F 0.00YR 0 3 2 0 0 0 0.00 0.00 0.00 0.00 0.00 0 54 F1BRSC A 0FFG 200E 9 10 0S 5.00 21 0.00 0.00 0.00 0.00 0NN N 57 F2F 195.89YQ66 0 1 5 0 0 0 195.89 195.84 195.92 195.89 0.00 0 07 -F3F 517MoskoF1F 514CampbF2F 525SchecF3F 535NeapoF4 14 +F3F 517SwimmF1F 514SwimmF2F 525SwimmF3F 535SwimmF4 14 C1COLA City Of Los Angeles CATest Contact Test Contact 0 0 87 C2 CA USA 39 C3 83 D1F 465Swimmer465 Test 42701011970 9 0 USA 77 -E1F 465CarbaFG 100E 9 10 0S 2.75 1 106.90Y 106.90Y 0.00 0.00 2NN N 89 +E1F 465SwimmFG 100E 9 10 0S 2.75 1 106.90Y 106.90Y 0.00 0.00 2NN N 89 E2F 105.13Y 0 4 1 7 17 0 105.08 105.13 105.15 105.13 0.00 0 06 D1M 486Swimmer486 Test 44801011970 9 0 82 -E1M 486FloreMB 100E 9 10 0S 2.75 2 87.49Y 87.49Y 0.00 0.00 1NN N 10 +E1M 486SwimmMB 100E 9 10 0S 2.75 2 87.49Y 87.49Y 0.00 0.00 1NN N 10 E2F 89.02YQ2Q 0 4 4 0 0 0 89.15 89.01 89.02 89.02 0.00 0 26 D1F 459Swimmer459 Test 42101011970 10 0 47 -E1F 459GilleFG 100E 9 10 0S 2.75 1 99.01Y 99.01Y 4.00 0.00 1NN N 89 +E1F 459SwimmFG 100E 9 10 0S 2.75 1 99.01Y 99.01Y 4.00 0.00 1NN N 89 E2F 94.18Y 0 5 7 4 13 0 94.15 94.43 94.18 94.18 0.00 0 85 D1M 467Swimmer467 Test 42901011970 9 0 USA 86 -E1M 467VazquMB 100E 9 10 0S 2.75 2 101.94Y 101.94Y 13.00 0.00 2NN N 40 +E1M 467SwimmMB 100E 9 10 0S 2.75 2 101.94Y 101.94Y 13.00 0.00 2NN N 40 E2F 98.46Y 0 3 1 5 6 0 98.42 98.56 98.46 98.46 0.00 0 85 C1LAC Los Angeles County CATest Contact 0 0 93 D1F 330Swimmer330 Test 29201011970 9 0 61 -E1F 330BaronFG 100E 9 10 0S 2.75 1 88.54Y 88.54Y 17.00 0.00 1NN N 00 +E1F 330SwimmFG 100E 9 10 0S 2.75 1 88.54Y 88.54Y 17.00 0.00 1NN N 00 E2F 83.67Y 0 6 6 2 2 0 83.87 83.67 83.64 83.67 0.00 0 85 D1M 322Swimmer322 Test 28401011970 10 0 83 -E1M 322BarreMB 100E 9 10 0S 2.75 2 95.00Y 95.00Y 16.00 0.00 2NN N 99 +E1M 322SwimmMB 100E 9 10 0S 2.75 2 95.00Y 95.00Y 16.00 0.00 2NN N 99 E2F 96.36Y 0 4 7 6 3 0 96.64 96.36 96.25 96.36 0.00 0 85 D1F 329Swimmer329 Test 29101011970 10 0 83 -E1F 329ContrFG 100E 9 10 0S 2.75 1 99.70Y 99.70Y 20.00 0.00 2NN N 20 +E1F 329SwimmFG 100E 9 10 0S 2.75 1 99.70Y 99.70Y 20.00 0.00 2NN N 20 E2F 94.72Y 0 5 8 5 1 0 94.68 94.72 94.75 94.72 0.00 0 85 D1F 320Swimmer320 Test 28201011970 9 0 90 -E1F 320LemusFG 100E 9 10 0S 2.75 1 105.90Y 105.90Y 11.00 0.00 2NN N 20 +E1F 320SwimmFG 100E 9 10 0S 2.75 1 105.90Y 105.90Y 11.00 0.00 2NN N 20 E2F 100.16Y 0 4 6 4 8 0 100.16 100.22 100.05 100.16 0.00 0 75 D1F 308Swimmer308 Test 27001011970 9 0 42 -E1F 308SaineFG 100E 9 10 0S 2.75 1 87.77Y 87.77Y 12.00 0.00 1NN N 10 +E1F 308SwimmFG 100E 9 10 0S 2.75 1 87.77Y 87.77Y 12.00 0.00 1NN N 10 E2F 89.41Y 0 6 5 6 7 0 89.41 89.34 89.41 89.41 0.00 0 75 F1LAC A 0FFG 200E 9 10 0S 5.00 21 172.33Y 172.33Y 40.00 0.00 0NN N 68 F2F 166.81Y 0 1 4 1 1 0 166.81 166.88 166.78 166.81 0.00 0 36 -F3F 308SaineF1F 320LemusF2F 330BaronF3F 329ContrF4 34 +F3F 308SwimmF1F 320SwimmF2F 330SwimmF3F 329SwimmF4 34 C1LASC Los Angeles Swim Club CATest Contact Test Contact 0 0 21 C2 CA USA 32 D1M 281Swimmer281 Test 24301011970 9 0 USA 95 -E1M 281BadgeMB 100E 9 10 0S 2.75 2 108.27Y 108.27Y 7.00 0.00 2NN N 99 +E1M 281SwimmMB 100E 9 10 0S 2.75 2 108.27Y 108.27Y 7.00 0.00 2NN N 99 E2F 108.47Y 0 2 5 6 10 0 108.49 108.47 108.36 108.47 0.00 0 36 D1F 288Swimmer288 Test 25001011970 9 0 31 -E1F 288EliotFG 100E 9 10 0S 2.75 1 100.27Y 100.27Y 1.00 0.00 2NN N 00 +E1F 288SwimmFG 100E 9 10 0S 2.75 1 100.27Y 100.27Y 1.00 0.00 2NN N 00 E2F 104.62Y 0 4 5 6 16 0 104.62 104.63 104.60 104.62 0.00 0 16 D1F 297Swimmer297 Test 25901011970 9 0 43 -E1F 297WilsoFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 3.00 0.00 2NN N 59 +E1F 297SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 3.00 0.00 2NN N 59 E2F 103.89Y 0 2 3 3 14 0 103.85 103.93 103.89 103.89 0.00 0 26 C1ORSC Olimpiakan Reservner CA 0 0 61 C2 CA USA 71 C3 51 D1F 549Swimmer549 Test 51101011970 9 0 13 -E1F 549NazarFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 39 +E1F 549SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 39 E2F 113.10Y 0 2 6 4 21 0 113.15 113.10 112.96 113.10 0.00 0 95 D1M 547Swimmer547 Test 50901011970 10 0 72 -E1M 547SerobMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 59 +E1M 547SwimmMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 59 E2F 117.73YQ1B 0 1 5 0 0 0 117.68 117.73 117.73 117.73 0.00 0 66 D1M 559Swimmer559 Test 52101011970 9 0 04 -E1M 559ShahbMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 49 +E1M 559SwimmMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 49 E2F 145.72YQ3E 0 1 2 0 0 0 145.72 145.70 145.73 145.72 0.00 0 66 D1F 556Swimmer556 Test 51801011970 10 0 92 -E1F 556ShereFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 49 +E1F 556SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 49 E2F 114.04Y 0 1 5 1 22 0 114.04 113.91 114.07 114.04 0.00 0 06 C1PALY Palisades Malibu YMCA CATest Contact Test Contact 0 0 03 C2 CA USA 41 D1M 86Swimmer86 Test 8601011970 10 0 53 -E1M 86KirshMB 100E 9 10 0S 2.75 2 114.16Y 114.16Y 9.00 0.00 2NN N 00 +E1M 86SwimmMB 100E 9 10 0S 2.75 2 114.16Y 114.16Y 9.00 0.00 2NN N 00 E2F 104.03Y 0 2 7 5 9 0 104.25 104.03 103.72 104.03 0.00 0 85 D1F 498Swimmer498 Test 46001011970 10 0 79 -E1F 498NadalFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 9.00 0.00 2NN N 29 +E1F 498SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 9.00 0.00 2NN N 29 E2F 100.49Y 0 2 2 1 9 0 100.49 100.47 100.60 100.49 0.00 0 85 D1M 88Swimmer88 Test 8801011970 10 0 43 -E1M 88Saab MB 100E 9 10 0S 2.75 2 97.74Y 97.74Y 17.00 0.00 2NN N 59 +E1M 88SwimmMB 100E 9 10 0S 2.75 2 97.74Y 97.74Y 17.00 0.00 2NN N 59 E2F 95.66Y 0 3 4 2 2 0 95.69 95.66 95.66 95.66 0.00 0 85 C1PVAC Palos Verdes Aquatic Club CATest Contact Test Contact 0 0 67 C2 CA USA 00 C3 92 D1F 346Swimmer346 Test 30801011970 10 0 51 -E1F 346InscoFG 100E 9 10 0S 2.75 1 87.08Y 87.08Y 20.00 0.00 1NN N 10 +E1F 346SwimmFG 100E 9 10 0S 2.75 1 87.08Y 87.08Y 20.00 0.00 1NN N 10 E2F 82.56Y 0 6 4 1 1 0 82.53 82.56 82.75 82.56 0.00 0 65 C1ROSE Rose Bowl Aquatics CA 0 0 10 C2 CA USA 41 D1M 257Swimmer257 Test 22801011970 10 0 12 -E1M 257AbramMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 6.00 0.00 2NN N 39 +E1M 257SwimmMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 6.00 0.00 2NN N 39 E2F 112.75Y 0 1 3 1 11 0 112.80 112.75 112.39 112.75 0.00 0 16 D1F 258Swimmer258 Test 22901011970 9 0 14 -E1F 258ArrevFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 49 +E1F 258SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 49 E2F 104.89YQ1L 0 1 3 0 0 0 104.89 104.91 104.86 104.89 0.00 0 66 D1M 232Swimmer232 Test 20301011970 10 0 30 -E1M 232Chol MB 100E 9 10 0S 2.75 2 90.43Y 90.43Y 16.00 0.00 1NN N 69 +E1M 232SwimmMB 100E 9 10 0S 2.75 2 90.43Y 90.43Y 16.00 0.00 1NN N 69 E2F 90.47Y 0 4 6 3 3 0 90.47 90.56 90.43 90.47 0.00 0 65 D1F 240Swimmer240 Test 21101011970 9 0 52 -E1F 240HalleFG 100E 9 10 0S 2.75 1 106.84Y 106.84Y 0.00 0.00 2NN N 99 +E1F 240SwimmFG 100E 9 10 0S 2.75 1 106.84Y 106.84Y 0.00 0.00 2NN N 99 E2F 105.87Y 0 4 7 8 18 0 105.87 107.12 105.74 105.87 0.00 0 36 D1F 238Swimmer238 Test 20901011970 9 0 71 -E1F 238HoffmFG 100E 9 10 0S 2.75 1 125.48Y 125.48Y 5.00 0.00 2NN N 10 +E1F 238SwimmFG 100E 9 10 0S 2.75 1 125.48Y 125.48Y 5.00 0.00 2NN N 10 E2F 102.80Y 0 2 4 2 12 0 102.92 102.78 102.80 102.80 0.00 0 06 D1F 233Swimmer233 Test 20401011970 10 0 24 -E1F 233KhineFG 100E 9 10 0S 2.75 1 91.67Y 91.67Y 5.00 0.00 1NN N 89 +E1F 233SwimmFG 100E 9 10 0S 2.75 1 91.67Y 91.67Y 5.00 0.00 1NN N 89 E2F 92.88Y 0 6 8 7 12 0 92.88 93.00 92.83 92.88 0.00 0 95 D1F 223Swimmer223 Test 19401011970 10 0 01 -E1F 223King FG 100E 9 10 0S 2.75 1 91.11Y 91.11Y 13.00 0.00 1NN N 59 +E1F 223SwimmFG 100E 9 10 0S 2.75 1 91.11Y 91.11Y 13.00 0.00 1NN N 59 E2F 87.83Y 0 6 7 5 6 0 87.93 87.83 87.57 87.83 0.00 0 95 D1F 224Swimmer224 Test 19501011970 9 0 73 D1M 218Swimmer218 Test 18901011970 9 0 63 -E1M 218RichaMB 100E 9 10 0S 2.75 2 99.32Y 99.32Y 12.00 0.00 1NN N 00 +E1M 218SwimmMB 100E 9 10 0S 2.75 2 99.32Y 99.32Y 12.00 0.00 1NN N 00 E2F 93.43Y 0 3 3 1 7 0 93.43 93.38 93.47 93.43 0.00 0 75 D1F 228Swimmer228 Test 19901011970 10 0 41 -E1F 228Shi FG 100E 9 10 0S 2.75 1 96.40Y 96.40Y 9.00 0.00 1NN N 88 +E1F 228SwimmFG 100E 9 10 0S 2.75 1 96.40Y 96.40Y 9.00 0.00 1NN N 88 E2F 91.37Y 0 5 3 3 9 0 91.32 91.51 91.37 91.37 0.00 0 65 D1F 237Swimmer237 Test 20801011970 9 0 30 -E1F 237Woo FG 100E 9 10 0S 2.75 1 111.02Y 111.02Y 2.00 0.00 2NN N 98 +E1F 237SwimmFG 100E 9 10 0S 2.75 1 111.02Y 111.02Y 2.00 0.00 2NN N 98 E2F 103.91Y 0 3 5 5 15 0 103.91 104.06 103.86 103.91 0.00 0 16 D1M 253Swimmer253 Test 22401011970 10 0 99 -E1M 253Yu MB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 18 +E1M 253SwimmMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 18 E2F 106.12YQ1E 0 1 4 0 0 0 106.15 106.12 106.06 106.12 0.00 0 46 F1ROSE A 0FFG 200E 9 10 0S 5.00 21 0.00 0.00 32.00 0.00 0NN N 77 F2F 168.94Y 0 1 3 3 3 0 169.10 168.94 168.89 168.94 0.00 0 46 -F3F 228Shi F1F 233KhineF2F 224MonteF3F 223King F4 52 +F3F 228SwimmF1F 233SwimmF2F 224SwimmF3F 223SwimmF4 52 F1ROSE B 0FFG 200E 9 10 0S 5.00 21 0.00 0.00 0.00 0.00 0NN N 57 F2F 196.17YQ66 0 1 2 0 0 0 196.33 196.17 196.15 196.17 0.00 0 76 -F3F 237Woo F1F 240HalleF2F 238HoffmF3F 258ArrevF4 33 +F3F 237SwimmF1F 240SwimmF2F 238SwimmF3F 258SwimmF4 33 C1RYL Royal Swim Team CATest Contact 0 0 32 C2 CA USA 07 C3 46 D1M 4Swimmer4 Test 401011970 10 0 80 -E1M 4RezniMB 100E 9 10 0S 2.75 2 97.33Y 97.33Y 20.00 0.00 2NN N 99 +E1M 4SwimmMB 100E 9 10 0S 2.75 2 97.33Y 97.33Y 20.00 0.00 2NN N 99 E2F 95.32Y 0 4 8 5 1 0 95.32 95.37 95.28 95.32 0.00 0 75 D1M 17Swimmer17 Test 1701011970 10 0 42 -E1M 17StarkMB 100E 9 10 0S 2.75 2 104.77Y 104.77Y 14.00 0.00 1NN N 30 +E1M 17SwimmMB 100E 9 10 0S 2.75 2 104.77Y 104.77Y 14.00 0.00 1NN N 30 E2F 91.96Y 0 2 4 1 5 0 91.96 91.92 92.17 91.96 0.00 0 75 C1TORR Swim Torrance CATest Contact Test Contact 0 0 91 C2 CA USA 25 C3 56 D1F 564Swimmer564 Test 52501011970 10 0 91 -E1F 564MorgaFG 100E 9 10 0S 2.75 1 105.98Y 105.98Y 6.00 0.00 1NN N 10 +E1F 564SwimmFG 100E 9 10 0S 2.75 1 105.98Y 105.98Y 6.00 0.00 1NN N 10 E2F 92.65Y 0 4 2 1 11 0 92.65 92.48 92.65 92.65 0.00 0 85 C1TSM Team Santa Monica CATest Contact Test Contact 0 0 21 C2 CA USA 44 C3 09 D1F 501Swimmer501 Test 46301011970 9 0 00 -E1F 501Dodd FG 100E 9 10 0S 2.75 1 91.38Y 91.38Y 16.00 0.00 1NN N 59 +E1F 501SwimmFG 100E 9 10 0S 2.75 1 91.38Y 91.38Y 16.00 0.00 1NN N 59 E2F 86.66Y 0 6 1 3 3 0 86.61 86.72 86.66 86.66 0.00 0 75 D1F 502Swimmer502 Test 46401011970 10 0 90 -E1F 502MarshFG 100E 9 10 0S 2.75 1 90.95Y 90.95Y 0.00 0.00 1NN N 99 +E1F 502SwimmFG 100E 9 10 0S 2.75 1 90.95Y 90.95Y 0.00 0.00 1NN N 99 E2F 86.78YQ 0 6 2 0 0 0 86.85 86.78 86.67 86.78 0.00 0 16 C1UN Unattached CA 0 0 46 C2 CA USA 41 D1F 299Swimmer299 Test 26101011970 10 0 83 -E1F 299SrivaFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 59 +E1F 299SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 59 E2F 105.27YQ2Q 0 2 7 0 0 0 105.58 0.00 104.97 105.27 0.00 0 36 C1WEST Westchester YMCA Orcas CATest Contact Test Contact 0 0 21 C2 CA USA 16 C3 41 D1M 157Swimmer157 Test 12801011970 9 0 31 -E1M 157AlberMB 100E 9 10 0S 2.75 2 118.39Y 118.39Y 5.00 0.00 2NN N 10 +E1M 157SwimmMB 100E 9 10 0S 2.75 2 118.39Y 118.39Y 5.00 0.00 2NN N 10 E2F 114.66Y 0 2 1 7 12 0 114.64 114.71 114.66 114.66 0.00 0 16 D1F 165Swimmer165 Test 13601011970 10 0 24 D1F 154Swimmer154 Test 12501011970 9 0 48 -E1F 154GarciFG 100E 9 10 0S 2.75 1 96.89Y 96.89Y 15.00 0.00 1NN N 00 +E1F 154SwimmFG 100E 9 10 0S 2.75 1 96.89Y 96.89Y 15.00 0.00 1NN N 00 E2F 87.58Y 0 5 6 1 4 0 87.62 87.58 87.54 87.58 0.00 0 85 D1M 186Swimmer186 Test 15701011970 10 0 61 -E1M 186JindeMB 100E 9 10 0S 2.75 2 113.48Y 113.48Y 11.00 0.00 2NN N 20 +E1M 186SwimmMB 100E 9 10 0S 2.75 2 113.48Y 113.48Y 11.00 0.00 2NN N 20 E2F 103.54Y 0 2 2 4 8 0 103.54 103.47 103.60 103.54 0.00 0 95 D1F 193Swimmer193 Test 16401011970 9 0 65 -E1F 193KaczoFG 100E 9 10 0S 2.75 1 139.90Y 139.90Y 0.00 0.00 2NN N 10 +E1F 193SwimmFG 100E 9 10 0S 2.75 1 139.90Y 139.90Y 0.00 0.00 2NN N 10 E2F 120.91YQ1A 0 2 5 0 0 0 120.81 120.94 120.91 120.91 0.00 0 46 D1F 180Swimmer180 Test 15101011970 9 0 61 -E1F 180Lima FG 100E 9 10 0S 2.75 1 104.87Y 104.87Y 7.00 0.00 2NN N 69 +E1F 180SwimmFG 100E 9 10 0S 2.75 1 104.87Y 104.87Y 7.00 0.00 2NN N 69 E2F 101.19Y 0 4 3 5 10 0 101.17 101.19 101.44 101.19 0.00 0 06 D1M 160Swimmer160 Test 13101011970 9 0 31 -E1M 160LorelMB 100E 9 10 0S 2.75 2 104.76Y 104.76Y 0.00 0.00 2NN N 10 +E1M 160SwimmMB 100E 9 10 0S 2.75 2 104.76Y 104.76Y 0.00 0.00 2NN N 10 E2F 110.55YQ2Q 0 3 8 0 0 0 110.55 110.39 110.61 110.55 0.00 0 56 D1F 198Swimmer198 Test 16901011970 9 0 93 -E1F 198MavroFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 49 +E1F 198SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 49 E2F 127.28YQ3Q 0 1 4 0 0 0 127.37 127.23 127.28 127.28 0.00 0 76 D1F 178Swimmer178 Test 14901011970 10 0 31 -E1F 178Pei FG 100E 9 10 0S 2.75 1 94.49Y 94.49Y 11.00 0.00 1NN N 09 +E1F 178SwimmFG 100E 9 10 0S 2.75 1 94.49Y 94.49Y 11.00 0.00 1NN N 09 E2F 89.49Y 0 5 4 2 8 0 89.42 89.56 89.49 89.49 0.00 0 95 D1F 159Swimmer159 Test 13001011970 9 0 22 D1M 164Swimmer164 Test 13501011970 10 0 57 -E1M 164SioriMB 100E 9 10 0S 2.75 2 94.78Y 94.78Y 0.00 0.00 2NN N 10 +E1M 164SwimmMB 100E 9 10 0S 2.75 2 94.78Y 94.78Y 0.00 0.00 2NN N 10 E2F 101.17YQ2Q 0 4 2 0 0 0 101.16 0.00 101.19 101.17 0.00 0 16 D1F 179Swimmer179 Test 15001011970 9 0 81 -E1F 179SioriFG 100E 9 10 0S 2.75 1 111.51Y 111.51Y 0.00 0.00 2NN N 00 +E1F 179SwimmFG 100E 9 10 0S 2.75 1 111.51Y 111.51Y 0.00 0.00 2NN N 00 E2F 119.25YQ1A 0 3 3 0 0 0 119.13 119.41 119.25 119.25 0.00 0 56 F1WEST A 0FFG 200E 9 10 0S 5.00 21 0.00 0.00 34.00 0.00 0NN N 87 F2F 168.79Y 0 1 6 2 2 0 168.76 168.79 168.81 168.79 0.00 0 46 -F3F 178Pei F1F 159PrentF2F 154GarciF3F 180Lima F4 42 +F3F 178SwimmF1F 159SwimmF2F 154SwimmF3F 180SwimmF4 42 F1WEST B 0FFG 200E 9 10 0S 5.00 21 0.00 0.00 0.00 0.00 0NN N 67 F2F 214.80YQ66 0 1 7 0 0 0 214.98 214.80 214.76 214.80 0.00 0 66 -F3F 193KaczoF1F 165FundeF2F 179SioriF3F 198MavroF4 64 +F3F 193SwimmF1F 165SwimmF2F 179SwimmF3F 198SwimmF4 64 C1WSTA Westside Aquatics CATest Contact Test Contact 0 0 79 C2 CA USA 17 C3 61 D1F 29Swimmer29 Test 2901011970 10 0 63 -E1F 29KelkaFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 19 +E1F 29SwimmFG 100E 9 10 0S 2.75 1 0.00Y 0.00Y 0.00 0.00 2NN N 19 E2F 103.00YQ3D 0 2 1 0 0 0 102.93 103.00 103.02 103.00 0.00 0 26 D1M 506Swimmer506 Test 46801011970 10 0 92 -E1M 506KoffmMB 100E 9 10 0S 2.75 2 89.96Y 89.96Y 15.00 0.00 1NN N 30 +E1M 506SwimmMB 100E 9 10 0S 2.75 2 89.96Y 89.96Y 15.00 0.00 1NN N 30 E2F 91.09Y 0 4 3 4 4 0 91.16 91.09 91.09 91.09 0.00 0 65 C1ZAP Zenith Aquatic Program CATest Contact Test Contact 0 0 65 C2 CA USA 27 C3 60 D1F 399Swimmer399 Test 36101011970 10 0 94 -E1F 399AbeleFG 100E 9 10 0S 2.75 1 99.98Y 99.98Y 12.00 0.00 2NN N 10 +E1F 399SwimmFG 100E 9 10 0S 2.75 1 99.98Y 99.98Y 12.00 0.00 2NN N 10 E2F 100.09Y 0 4 4 3 7 0 100.06 100.09 100.09 100.09 0.00 0 85 D1M 404Swimmer404 Test 36601011970 9 0 23 -E1M 404AbeleMB 100E 9 10 0S 2.75 2 111.83Y 111.83Y 12.00 0.00 2NN N 00 +E1M 404SwimmMB 100E 9 10 0S 2.75 2 111.83Y 111.83Y 12.00 0.00 2NN N 00 E2F 102.06Y 0 2 6 3 7 0 102.06 102.08 102.06 102.06 0.00 0 85 D1M 441Swimmer441 Test 40301011970 9 0 52 -E1M 441BurchMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 49 +E1M 441SwimmMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 49 E2F 136.84YQ3K 0 1 6 0 0 0 136.87 136.84 136.78 136.84 0.00 0 86 D1F 416Swimmer416 Test 37801011970 9 0 55 -E1F 416ChambFG 100E 9 10 0S 2.75 1 113.87Y 113.87Y 0.00 0.00 2NN N 99 +E1F 416SwimmFG 100E 9 10 0S 2.75 1 113.87Y 113.87Y 0.00 0.00 2NN N 99 E2F 107.09Y 0 3 1 7 20 0 107.09 107.09 107.15 107.09 0.00 0 16 D1M 380Swimmer380 Test 34201011970 10 0 57 -E1M 380ChungMB 100E 9 10 0S 2.75 2 96.74Y 96.74Y 17.00 0.00 1NN N 20 +E1M 380SwimmMB 100E 9 10 0S 2.75 2 96.74Y 96.74Y 17.00 0.00 1NN N 20 E2F 88.90Y 0 4 1 2 2 0 88.87 88.95 88.90 88.90 0.00 0 85 D1M 424Swimmer424 Test 38601011970 10 0 92 -E1M 424Fink MB 100E 9 10 0S 2.75 2 88.96Y 88.96Y 20.00 0.00 1NN N 89 +E1M 424SwimmMB 100E 9 10 0S 2.75 2 88.96Y 88.96Y 20.00 0.00 1NN N 89 E2F 83.41Y 0 4 5 1 1 0 83.49 83.30 83.41 83.41 0.00 0 55 D1M 411Swimmer411 Test 37301011970 9 0 42 -E1M 411GrissMB 100E 9 10 0S 2.75 2 98.07Y 98.07Y 0.00 0.00 2NN N 10 +E1M 411SwimmMB 100E 9 10 0S 2.75 2 98.07Y 98.07Y 0.00 0.00 2NN N 10 E2F 98.67YQ1K 0 3 5 0 0 0 98.64 98.67 98.75 98.67 0.00 0 56 D1F 384Swimmer384 Test 34601011970 9 0 32 -E1F 384HarmaFG 100E 9 10 0S 2.75 1 98.71Y 98.71Y 17.00 0.00 2NN N 00 +E1F 384SwimmFG 100E 9 10 0S 2.75 1 98.71Y 98.71Y 17.00 0.00 2NN N 00 E2F 95.96Y 0 5 2 6 2 0 95.93 96.00 95.96 95.96 0.00 0 85 D1F 433Swimmer433 Test 39501011970 10 0 74 -E1F 433Hung FG 100E 9 10 0S 2.75 1 112.04Y 112.04Y 7.00 0.00 1NN N 69 +E1F 433SwimmFG 100E 9 10 0S 2.75 1 112.04Y 112.04Y 7.00 0.00 1NN N 69 E2F 92.29Y 0 3 2 1 10 0 92.39 92.22 92.29 92.29 0.00 0 85 D1F 377Swimmer377 Test 33901011970 10 0 05 -E1F 377IrawaFG 100E 9 10 0S 2.75 1 88.02Y 88.02Y 14.00 0.00 1NN N 10 +E1F 377SwimmFG 100E 9 10 0S 2.75 1 88.02Y 88.02Y 14.00 0.00 1NN N 10 E2F 87.76Y 0 6 3 4 5 0 87.76 87.80 87.66 87.76 0.00 0 85 D1M 408Swimmer408 Test 37001011970 9 0 05 -E1M 408MelloMB 100E 9 10 0S 2.75 2 100.20Y 100.20Y 14.00 0.00 2NN N 10 +E1M 408SwimmMB 100E 9 10 0S 2.75 2 100.20Y 100.20Y 14.00 0.00 2NN N 10 E2F 98.45Y 0 3 7 4 5 0 98.62 98.45 98.28 98.45 0.00 0 95 D1F 382Swimmer382 Test 34401011970 10 0 89 -E1F 382Nye FG 100E 9 10 0S 2.75 1 99.13Y 99.13Y 15.00 0.00 2NN N 19 +E1F 382SwimmFG 100E 9 10 0S 2.75 1 99.13Y 99.13Y 15.00 0.00 2NN N 19 E2F 98.22Y 0 5 1 8 4 0 98.14 98.22 98.25 98.22 0.00 0 75 D1F 393Swimmer393 Test 35501011970 10 0 04 -E1F 393RozziFG 100E 9 10 0S 2.75 1 112.92Y 112.92Y 0.00 0.00 2NN N 30 +E1F 393SwimmFG 100E 9 10 0S 2.75 1 112.92Y 112.92Y 0.00 0.00 2NN N 30 E2F 106.99Y 0 3 7 6 19 0 107.08 106.99 106.94 106.99 0.00 0 46 D1F 421Swimmer421 Test 38301011970 9 0 37 -E1F 421ShundFG 100E 9 10 0S 2.75 1 111.87Y 111.87Y 6.00 0.00 2NN N 10 +E1F 421SwimmFG 100E 9 10 0S 2.75 1 111.87Y 111.87Y 6.00 0.00 2NN N 10 E2F 102.01Y 0 3 6 3 11 0 102.11 101.66 102.01 102.01 0.00 0 85 D1F 412Swimmer412 Test 37401011970 10 0 61 -E1F 412SimmoFG 100E 9 10 0S 2.75 1 95.46Y 95.46Y 16.00 0.00 2NN N 10 +E1F 412SwimmFG 100E 9 10 0S 2.75 1 95.46Y 95.46Y 16.00 0.00 2NN N 10 E2F 96.54Y 0 5 5 7 3 0 96.60 96.49 96.54 96.54 0.00 0 85 D1M 409Swimmer409 Test 37101011970 10 0 16 -E1M 409SusmaMB 100E 9 10 0S 2.75 2 99.90Y 99.90Y 15.00 0.00 2NN N 40 +E1M 409SwimmMB 100E 9 10 0S 2.75 2 99.90Y 99.90Y 15.00 0.00 2NN N 40 E2F 98.04Y 0 3 6 3 4 0 98.08 98.00 98.04 98.04 0.00 0 75 D1M 443Swimmer443 Test 40501011970 9 0 44 -E1M 443WerksMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 59 +E1M 443SwimmMB 100E 9 10 0S 2.75 2 0.00Y 0.00Y 0.00 0.00 2NN N 59 E2F 134.20YQ3B 0 2 8 0 0 0 134.20 134.13 134.22 134.20 0.00 0 46 diff --git a/tests/hy3/fixtures/mm3_3_0ea_individuals_blank_date.hy3 b/tests/hy3/fixtures/mm3_3_0ea_individuals_blank_date.hy3 index 31dd419..a174d5b 100644 --- a/tests/hy3/fixtures/mm3_3_0ea_individuals_blank_date.hy3 +++ b/tests/hy3/fixtures/mm3_3_0ea_individuals_blank_date.hy3 @@ -5,259 +5,259 @@ C1CSSC Conejo Simi Swim Club CA C2 CA 00 C3 78 D1M 3Swimmer3 Test 301011970 14 0 72 -E1M 3AbernXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1M 3SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1059.09Y 0 5 6 3 3 0 1059.12 1059.20 1059.29 1059.09 0.00 0 86 G1F 2 28.52F 4 59.65F 6 91.86F 8 123.74F10 156.07F12 187.87F14 220.60F16 252.18F18 284.96F20 316.89F22 349.84 14 G1F24 382.53F26 413.54F28 446.49F30 478.69F32 511.71F34 544.77F36 577.25F38 609.89F40 642.70F42 675.23F44 707.22 35 G1F46 739.63F48 772.69F50 805.56F52 838.32F54 870.98F56 903.09F58 934.16F60 965.87F62 998.03F64 1028.80F66 1059.09 95 -E1M 3AbernXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 78 +E1M 3SwimmXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 78 E2F 316.89Y 0 1 1 1 1 0 0.00 0.00 0.00 0.00 0.00 0 74 -E1M 3AbernXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 +E1M 3SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 E2F 642.70Y 0 1 1 2 2 0 0.00 0.00 0.00 0.00 0.00 0 64 D1F 6Swimmer6 Test 601011970 14 0 82 -E1F 6AnderXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1F 6SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1245.05Y 0 2 5 3 23 0 1245.05 0.00 0.00 1333.01 0.00 0 85 G1F 2 32.74F 4 68.04F 6 105.37F 8 143.13F10 180.39F12 218.25F14 255.45F16 293.51F18 331.91F20 369.42F22 407.61 04 G1F24 445.49F26 483.74F28 521.26F30 560.02F32 598.13F34 636.05F36 674.20F38 712.33F40 750.49F42 788.77F44 826.84 25 G1F46 865.03F48 902.95F50 941.33F52 1017.71F54 1056.27F56 1093.98F58 1132.57F60 1170.47F62 1208.24F64 1245.05F66 1245.05 16 -E1F 6AnderXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 +E1F 6SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 E2F 750.49Y 0 3 1 2 18 0 0.00 0.00 0.00 0.00 0.00 0 84 D1F 8Swimmer8 Test 801011970 17 0 16 -E1F 8BarthXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 +E1F 8SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 E2F 1108.67Y 0 4 2 2 9 0 0.00 1108.85 1108.71 1108.67 0.00 0 26 G1F 2 31.02F 4 64.00F 6 97.60F 8 131.31F10 165.08F12 198.75F14 231.99F16 265.80F18 299.58F20 333.47F22 367.39 93 G1F24 401.24F26 435.38F28 468.94F30 502.58F32 536.32F34 569.71F36 603.26F38 636.54F40 670.15F42 703.72F44 737.03 05 G1F46 770.62F48 804.29F50 837.83F52 871.71F54 905.31F56 939.24F58 973.54F60 1007.66F62 1041.67F64 1075.60F66 1108.67 95 D1F 9Swimmer9 Test 901011970 14 0 91 -E1F 9BelteXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 +E1F 9SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 E2F 1373.28Y 0 2 7 6 29 0 0.00 1373.24 1373.33 1373.73 0.00 0 56 G1F 2 36.25F 4 74.81F 6 115.85F 8 156.42F10 198.06F12 239.77F14 282.43F16 324.00F18 366.02F20 408.06F22 450.20 93 G1F24 492.38F26 535.44F28 577.95F30 620.07F32 661.95F34 704.14F36 747.28F38 790.11F40 832.91F42 876.84F44 919.42 45 G1F46 962.43F48 1005.39F50 1046.62F52 1090.06F54 1131.85F56 1173.59F58 1216.44F60 1258.07F62 1299.40F64 1338.68F66 1373.28 56 D1M 10Swimmer10 Test 1001011970 11 0 80 -E1M 10Berg XX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 38 +E1M 10SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 38 E2F 1318.72Y 0 1 1 3 27 0 0.00 1320.08 1320.12 1318.72 0.00 0 46 G1F 2 32.94F 4 70.75F 6 109.66F 8 149.32F10 188.92F12 228.73F14 269.14F16 309.49F18 350.93F20 390.51F22 430.54 24 G1F24 472.72F26 512.75F28 553.27F30 594.20F32 633.24F34 673.51F36 713.93F38 754.41F40 794.48F42 836.07F44 877.26 25 G1F46 918.00F48 957.75F50 998.68F52 1038.70F54 1079.41F56 1120.45F58 1159.68F60 1198.65F62 1239.66F64 1279.66F66 1318.72 66 -E1M 10Berg XX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 58 +E1M 10SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 58 E2F 794.48Y 0 3 3 4 20 0 0.00 0.00 0.00 0.00 0.00 0 94 D1M 12Swimmer12 Test 1201011970 16 0 79 -E1M 12Berg XX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 38 +E1M 12SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 38 E2F 1036.96Y 0 5 2 2 2 0 0.00 1037.12 1036.94 1036.96 0.00 0 26 G1F 2 28.46F 4 59.39F 6 90.58F 8 122.26F10 153.54F12 184.86F14 215.99F16 247.34F18 278.57F20 310.06F22 341.62 04 G1F24 372.85F26 404.20F28 435.42F30 467.39F32 499.16F34 531.05F36 562.68F38 594.97F40 626.84F42 658.54F44 690.06 35 G1F46 721.52F48 753.23F50 784.82F52 816.47F54 848.39F56 880.09F58 911.77F60 943.54F62 975.25F64 1006.67F66 1036.96 85 D1F 15Swimmer15 Test 1501011970 14 0 50 -E1F 15BrownXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 09 +E1F 15SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 09 E2F 1434.25Y 0 1 5 5 31 0 1434.52 1434.26 1434.35 1457.04 0.00 0 96 G1F 2 36.08F 4 75.49F 6 117.29F 8 159.81F10 202.06F12 245.15F14 287.30F16 330.69F18 375.57F20 418.48F22 462.22 14 G1F24 506.74F26 549.43F28 592.63F30 636.68F32 680.13F34 725.14F36 769.61F38 814.69F40 859.23F42 903.39F44 949.63 45 G1F46 993.06F48 1038.09F50 1083.34F52 1128.81F54 1174.46F56 1220.01F58 1263.71F60 1309.12F62 1350.87F64 1394.06F66 1434.25 36 D1F 22Swimmer22 Test 2201011970 14 0 13 -E1F 22ChoppXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 +E1F 22SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 E2F 1218.18Y 0 2 4 1 21 0 1218.24 0.00 0.00 1218.18 0.00 0 85 G1F 2 31.45F 4 65.51F 6 100.64F 8 0.00F10 172.02F12 208.27F14 244.22F16 281.13F18 317.58F20 354.52F22 392.34 53 G1F24 429.81F26 467.76F28 505.36F30 542.98F32 581.43F34 619.52F36 657.55F38 695.50F40 732.62F42 770.66F44 809.21 45 G1F46 846.73F48 883.82F50 922.18F52 960.79F54 999.75F56 1036.82F58 1073.92F60 0.00F62 1110.63F64 1185.28F66 1218.18 75 -E1F 22ChoppXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 88 +E1F 22SwimmXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 88 E2F 354.52Y 0 1 5 5 5 0 0.00 0.00 0.00 0.00 0.00 0 74 -E1F 22ChoppXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 +E1F 22SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 E2F 732.62Y 0 2 8 6 14 0 0.00 0.00 0.00 0.00 0.00 0 94 D1F 26Swimmer26 Test 2601011970 13 0 73 -E1F 26ContrXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 98 +E1F 26SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 98 E2F 1327.67Y 0 2 6 5 28 0 1327.66 1327.75 1327.71 1327.67 0.00 0 17 G1F 2 34.96F 4 73.15F 6 112.35F 8 152.55F10 192.35F12 233.14F14 273.68F16 313.84F18 354.39F20 395.04F22 434.32 04 G1F24 474.61F26 515.22F28 556.11F30 596.77F32 636.92F34 677.10F36 718.83F38 760.27F40 801.54F42 841.92F44 882.94 35 G1F46 924.06F48 964.76F50 1005.23F52 1045.46F54 1086.73F56 1128.29F58 1169.85F60 1211.02F62 1250.74F64 1290.68F66 1327.67 46 D1F 27Swimmer27 Test 2701011970 13 0 40 -E1F 27CookeXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 +E1F 27SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 E2F 1105.99Y 0 4 1 1 8 0 1106.08 0.00 1106.16 1105.99 0.00 0 26 G1F 2 29.95F 4 62.51F 6 95.45F 8 128.43F10 161.34F12 194.26F14 227.69F16 261.20F18 294.82F20 328.51F22 362.38 93 G1F24 396.42F26 430.33F28 463.98F30 497.82F32 531.88F34 565.63F36 599.34F38 633.12F40 667.26F42 701.41F44 735.93 25 G1F46 769.54F48 803.20F50 837.26F52 871.55F54 905.76F56 939.56F58 973.14F60 1007.14F62 1040.31F64 1073.47F66 1105.99 95 -E1F 27CookeXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 09 +E1F 27SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 09 E2F 667.26Y 0 1 4 4 4 0 0.00 0.00 0.00 0.00 0.00 0 74 D1M 35Swimmer35 Test 3501011970 17 0 16 -E1M 35DeVetXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1M 35SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1065.26Y 0 5 4 4 4 0 1065.23 1065.24 1065.23 1065.26 0.00 0 76 G1F 2 28.92F 4 60.26F 6 92.75F 8 124.86F10 157.16F12 189.74F14 221.75F16 253.89F18 286.44F20 318.77F22 351.19 14 G1F24 383.58F26 416.03F28 448.38F30 480.72F32 513.10F34 545.36F36 577.77F38 610.48F40 643.29F42 675.70F44 707.93 25 G1F46 740.58F48 773.12F50 805.73F52 838.69F54 871.33F56 904.08F58 936.84F60 969.35F62 1001.84F64 1034.57F66 1065.26 85 D1M 36Swimmer36 Test 3601011970 12 0 85 -E1M 36DeVetXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1M 36SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1309.84Y 0 1 2 2 26 0 0.00 1310.04 1310.13 1309.84 0.00 0 46 G1F 2 33.68F 4 71.32F 6 110.32F 8 149.08F10 188.21F12 227.74F14 268.53F16 308.71F18 348.89F20 389.26F22 429.26 24 G1F24 470.18F26 511.53F28 551.41F30 592.01F32 633.07F34 673.57F36 713.53F38 752.63F40 793.93F42 834.00F44 874.44 05 G1F46 915.12F48 955.79F50 996.34F52 1036.27F54 1076.88F56 1116.94F58 1157.57F60 1197.54F62 1237.64F64 1276.91F66 1309.84 66 D1M 40Swimmer40 Test 4001011970 11 0 94 -E1M 40FeldmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1M 40SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1267.87Y 0 1 3 1 25 0 1267.97 1267.94 1268.03 1267.87 0.00 0 27 G1F 2 32.61F 4 68.63F 6 105.70F 8 143.49F10 181.79F12 220.12F14 257.91F16 296.44F18 335.47F20 374.69F22 413.41 24 G1F24 453.34F26 492.36F28 531.93F30 571.39F32 610.93F34 649.00F36 687.75F38 727.11F40 766.51F42 805.74F44 845.87 35 G1F46 885.24F48 924.52F50 964.27F52 1003.12F54 1042.56F56 1081.53F58 1120.90F60 1159.38F62 1196.56F64 1234.27F66 1267.87 26 -E1M 40FeldmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 +E1M 40SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 E2F 766.51Y 0 3 2 3 19 0 0.00 0.00 0.00 0.00 0.00 0 94 D1F 48Swimmer48 Test 4801011970 14 0 65 -E1F 48FogleXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 +E1F 48SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 E2F 1067.37Y 0 5 5 6 6 0 1067.60 1067.55 1067.54 1067.37 0.00 0 96 G1F 2 29.77F 4 61.49F 6 93.81F 8 125.87F10 158.40F12 190.57F14 222.45F16 254.87F18 287.03F20 319.81F22 351.69 14 G1F24 384.43F26 416.69F28 449.10F30 481.40F32 513.65F34 546.17F36 578.56F38 611.37F40 644.08F42 676.56F44 708.86 35 G1F46 741.98F48 774.85F50 807.28F52 840.24F54 873.04F56 905.77F58 938.88F60 971.48F62 1003.78F64 1036.19F66 1067.37 95 D1F 50Swimmer50 Test 5001011970 13 0 40 -E1F 50GlassXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 +E1F 50SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 E2F 1515.31Y 0 1 7 6 32 0 0.00 1515.25 0.00 1515.31 0.00 0 95 G1F 2 37.05F 4 78.62F 6 122.21F 8 166.92F10 212.56F12 258.92F14 305.19F16 352.10F18 399.08F20 444.48F22 490.38 04 G1F24 536.02F26 583.02F28 630.26F30 676.14F32 722.91F34 769.36F36 815.98F38 863.43F40 909.87F42 958.06F44 1005.48 45 G1F46 1053.29F48 1101.26F50 1148.43F52 1195.89F54 1244.10F56 1288.12F58 1333.91F60 1380.30F62 1427.05F64 1472.10F66 1515.31 36 D1F 51Swimmer51 Test 5101011970 11 0 82 -E1F 51GonzaXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 98 +E1F 51SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 98 E2F 0.00YR 0 2 1 0 0 0 0.00 0.00 0.00 0.00 0.00 0 44 G1F 2 0.00F 4 0.00F 6 0.00F 8 0.00F10 0.00F12 0.00F14 0.00F16 0.00F18 0.00F20 0.00F22 0.00 20 G1F24 0.00F26 0.00F28 0.00F30 0.00F32 0.00F34 0.00F36 0.00F38 0.00F40 0.00F42 0.00F44 0.00 90 G1F46 0.00F48 0.00F50 0.00F52 0.00F54 0.00F56 0.00F58 0.00F60 0.00F62 0.00F64 0.00F66 0.00 11 D1M 68Swimmer68 Test 6801011970 14 0 53 -E1M 68HillnXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 98 +E1M 68SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 98 E2F 1172.34Y 0 3 3 6 16 0 1172.37 1172.36 1172.50 1172.34 0.00 0 96 G1F 2 30.15F 4 63.69F 6 97.72F 8 132.73F10 167.60F12 202.66F14 238.21F16 273.40F18 308.94F20 344.29F22 379.88 14 G1F24 415.23F26 451.07F28 487.63F30 523.78F32 559.39F34 595.26F36 630.93F38 667.74F40 704.68F42 740.81F44 777.35 45 G1F46 814.66F48 851.27F50 886.99F52 922.77F54 959.22F56 994.98F58 1030.57F60 1066.44F62 1102.17F64 1137.66F66 1172.34 16 -E1M 68HillnXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 09 +E1M 68SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 09 E2F 704.68Y 0 2 3 3 11 0 0.00 0.00 0.00 0.00 0.00 0 84 D1M 77Swimmer77 Test 7701011970 17 0 61 -E1M 77KavanXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 +E1M 77SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 E2F 1111.11Y 0 3 7 2 10 0 0.00 1111.16 1111.17 1111.11 0.00 0 16 G1F 2 31.39F 4 64.92F 6 98.62F 8 132.50F10 166.61F12 200.88F14 234.93F16 268.88F18 302.86F20 336.48F22 369.91 14 G1F24 403.73F26 437.36F28 471.54F30 505.38F32 539.04F34 573.21F36 607.15F38 640.97F40 674.52F42 707.90F44 741.10 05 G1F46 774.42F48 808.13F50 841.55F52 875.24F54 909.19F56 943.25F58 977.08F60 1010.82F62 1044.17F64 1078.27F66 1111.11 75 -E1M 77KavanXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 88 +E1M 77SwimmXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 88 E2F 336.48Y 0 1 2 3 3 0 0.00 0.00 0.00 0.00 0.00 0 64 -E1M 77KavanXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 +E1M 77SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 E2F 674.52Y 0 1 5 5 5 0 0.00 0.00 0.00 0.00 0.00 0 74 D1M 83Swimmer83 Test 8301011970 13 0 21 -E1M 83KriegXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 +E1M 83SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 E2F 0.00YR 0 5 1 0 0 0 0.00 0.00 0.00 0.00 0.00 0 54 G1F 2 0.00F 4 0.00F 6 0.00F 8 0.00F10 0.00F12 0.00F14 0.00F16 0.00F18 0.00F20 0.00F22 0.00 20 G1F24 0.00F26 0.00F28 0.00F30 0.00F32 0.00F34 0.00F36 0.00F38 0.00F40 0.00F42 0.00F44 0.00 90 G1F46 0.00F48 0.00F50 0.00F52 0.00F54 0.00F56 0.00F58 0.00F60 0.00F62 0.00F64 0.00F66 0.00 11 D1F 87Swimmer87 Test 8701011970 14 0 45 -E1F 87LibanXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 +E1F 87SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 E2F 1127.92Y 0 4 3 4 12 0 1128.01 1128.05 1128.02 1127.92 0.00 0 86 G1F 2 30.45F 4 63.56F 6 97.40F 8 130.99F10 164.90F12 198.80F14 231.98F16 266.16F18 299.98F20 333.94F22 367.77 24 G1F24 401.81F26 436.07F28 470.06F30 504.37F32 538.84F34 573.17F36 607.79F38 641.95F40 676.22F42 710.75F44 745.75 25 G1F46 780.57F48 815.69F50 850.76F52 885.39F54 920.20F56 955.55F58 990.82F60 1025.98F62 1060.50F64 1094.84F66 1127.92 06 -E1F 87LibanXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 +E1F 87SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 E2F 676.22Y 0 1 7 6 6 0 0.00 0.00 0.00 0.00 0.00 0 74 D1F 92Swimmer92 Test 9201011970 16 0 10 -E1F 92Luk XX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 87 +E1F 92SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 87 E2F 1179.57Y 0 4 4 5 17 0 1179.73 1179.60 1179.65 0.00 0.00 0 76 G1F 2 31.75F 4 66.31F 6 101.52F 8 137.03F10 172.79F12 208.82F14 244.77F16 280.53F18 316.52F20 352.39F22 388.52 04 G1F24 424.48F26 460.31F28 496.11F30 532.23F32 568.28F34 604.49F36 640.60F38 676.99F40 712.60F42 749.02F44 785.35 25 G1F46 821.95F48 858.27F50 894.30F52 930.82F54 966.21F56 1002.69F58 1039.15F60 1075.55F62 1111.31F64 1145.90F66 1179.57 06 -E1F 92Luk XX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 08 +E1F 92SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 08 E2F 712.60Y 0 2 4 4 12 0 0.00 0.00 0.00 0.00 0.00 0 84 D1M 95Swimmer95 Test 9501011970 15 0 80 -E1M 95ManchXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 +E1M 95SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 78 E2F 1075.25Y 0 3 6 1 7 0 1075.29 1075.44 1075.28 1075.25 0.00 0 86 G1F 2 30.01F 4 62.79F 6 95.69F 8 128.47F10 161.21F12 193.50F14 226.60F16 259.60F18 292.32F20 324.75F22 357.16 83 G1F24 389.97F26 422.57F28 454.79F30 487.40F32 520.15F34 552.65F36 585.60F38 618.39F40 651.08F42 684.13F44 716.95 35 G1F46 749.84F48 782.84F50 815.63F52 848.50F54 880.96F56 913.36F58 946.17F60 978.52F62 1011.39F64 1043.78F66 1075.25 85 -E1M 95ManchXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 +E1M 95SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 98 E2F 651.08Y 0 1 3 3 3 0 0.00 0.00 0.00 0.00 0.00 0 64 D1F 100Swimmer100 Test 10001011970 12 0 02 -E1F 100MontoXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 +E1F 100SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 E2F 1427.09Y 0 1 6 4 30 0 1427.23 0.00 1427.23 1427.09 0.00 0 46 G1F 2 36.05F 4 75.66F 6 115.92F 8 156.09F10 196.74F12 238.00F14 278.72F16 320.63F18 361.25F20 402.75F22 441.94 14 G1F24 483.76F26 524.74F28 565.67F30 607.19F32 648.61F34 689.40F36 731.12F38 772.52F40 812.99F42 855.50F44 898.51 45 G1F46 942.10F48 986.40F50 1029.88F52 1073.68F54 1117.73F56 1161.98F58 1207.11F60 1252.06F62 1296.94F64 1342.79F66 1427.09 56 -E1F 100MontoXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 +E1F 100SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 E2F 812.99Y 0 3 5 5 21 0 0.00 0.00 0.00 0.00 0.00 0 94 D1F 101Swimmer101 Test 10101011970 14 0 02 -E1F 101MontoXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 +E1F 101SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 E2F 1208.84Y 0 4 6 7 20 0 1208.89 1208.84 1208.89 1208.84 0.00 0 17 G1F 2 33.12F 4 68.79F 6 105.06F 8 141.58F10 178.17F12 214.56F14 251.06F16 287.92F18 324.58F20 361.76F22 398.64 14 G1F24 435.59F26 472.91F28 509.68F30 547.34F32 584.70F34 622.31F36 660.10F38 697.26F40 734.41F42 771.27F44 807.92 35 G1F46 844.57F48 881.17F50 918.11F52 955.27F54 992.62F56 1029.15F58 1065.75F60 1102.74F62 1138.80F64 1174.27F66 1208.84 26 -E1F 101MontoXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 +E1F 101SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 E2F 734.41Y 0 2 7 7 15 0 0.00 0.00 0.00 0.00 0.00 0 94 D1M 103Swimmer103 Test 10301011970 16 0 41 -E1M 103NguyeXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 +E1M 103SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 E2F 1146.61Y 0 3 4 4 14 0 1146.66 1146.55 1146.55 1146.61 0.00 0 07 G1F 2 30.14F 4 63.21F 6 97.42F 8 132.21F10 166.91F12 201.92F14 236.35F16 271.61F18 306.92F20 342.64F22 377.93 83 G1F24 412.94F26 448.36F28 483.80F30 518.40F32 553.85F34 588.73F36 624.30F38 659.24F40 694.49F42 729.72F44 765.38 35 G1F46 800.03F48 835.55F50 871.01F52 906.10F54 940.42F56 974.72F58 1010.22F60 1044.49F62 1079.43F64 1113.57F66 1146.61 65 -E1M 103NguyeXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 +E1M 103SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 E2F 694.39Y 0 2 1 2 10 0 0.00 0.00 0.00 0.00 0.00 0 84 D1M 104Swimmer104 Test 10401011970 15 0 52 -E1M 104NguyeXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 +E1M 104SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 E2F 1137.95Y 0 3 5 3 13 0 1137.98 1138.01 1138.02 1151.58 0.00 0 96 G1F 2 29.93F 4 61.79F 6 95.08F 8 129.15F10 162.21F12 195.82F14 231.13F16 265.83F18 300.58F20 334.71F22 369.61 93 G1F24 403.24F26 438.14F28 474.17F30 508.54F32 543.63F34 578.44F36 613.71F38 648.30F40 683.51F42 718.30F44 753.35 05 G1F46 789.63F48 825.82F50 862.07F52 897.75F54 933.20F56 967.79F58 1003.57F60 1037.98F62 1072.75F64 1106.65F66 1137.95 16 -E1M 104NguyeXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 39 +E1M 104SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 39 E2F 683.51Y 0 1 8 8 8 0 0.00 0.00 0.00 0.00 0.00 0 84 D1F 106Swimmer106 Test 10601011970 12 0 50 -E1F 106Park XX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1F 106SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1224.12Y 0 2 3 2 22 0 1224.09 1224.26 1224.10 1224.12 0.00 0 76 G1F 2 33.20F 4 69.47F 6 106.53F 8 144.32F10 181.18F12 219.07F14 257.03F16 293.99F18 331.83F20 368.97F22 406.11 14 G1F24 443.44F26 480.87F28 518.37F30 556.35F32 594.27F34 632.03F36 669.36F38 707.28F40 744.66F42 782.29F44 819.04 45 G1F46 856.73F48 894.40F50 931.69F52 968.91F54 1006.91F56 1044.44F58 1081.90F60 1119.49F62 1154.29F64 1190.59F66 1224.12 26 -E1F 106Park XX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 78 +E1F 106SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 78 E2F 744.66Y 0 3 4 1 17 0 0.00 0.00 0.00 0.00 0.00 0 94 D1F 107Swimmer107 Test 10701011970 15 0 33 -E1F 107Park XX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1F 107SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1206.23Y 0 4 7 6 19 0 0.00 1206.33 0.00 1206.23 0.00 0 85 G1F 2 32.68F 4 68.95F 6 105.51F 8 142.06F10 179.43F12 216.40F14 253.33F16 290.96F18 328.97F20 366.59F22 403.54 24 G1F24 440.57F26 477.55F28 515.33F30 553.25F32 590.96F34 628.19F36 664.37F38 700.64F40 737.39F42 771.94F44 807.88 35 G1F46 844.31F48 880.63F50 918.10F52 954.86F54 991.89F56 1028.80F58 1065.54F60 1102.41F62 1138.85F64 1173.07F66 1206.23 06 -E1F 107Park XX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 78 +E1F 107SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 78 E2F 737.39Y 0 2 6 8 16 0 0.00 0.00 0.00 0.00 0.00 0 94 D1F 112Swimmer112 Test 11201011970 15 0 50 -E1F 112QuinnXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 +E1F 112SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 E2F 1066.27Y 0 5 7 5 5 0 0.00 1066.34 1066.46 1066.27 0.00 0 36 G1F 2 29.12F 4 60.57F 6 92.87F 8 125.10F10 157.62F12 189.96F14 222.46F16 254.84F18 286.97F20 319.13F22 351.30 93 G1F24 383.58F26 415.74F28 448.16F30 480.15F32 512.49F34 544.74F36 577.10F38 609.60F40 641.91F42 674.38F44 706.74 25 G1F46 739.41F48 772.36F50 805.66F52 838.31F54 870.97F56 903.98F58 937.35F60 970.21F62 1003.04F64 1035.20F66 1066.27 65 -E1F 112QuinnXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 +E1F 112SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 29 E2F 641.91Y 0 1 2 1 1 0 0.00 0.00 0.00 0.00 0.00 0 64 D1M 114Swimmer114 Test 11401011970 14 0 44 -E1M 114Shen XX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 +E1M 114SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 68 E2F 1147.58Y 0 3 2 5 15 0 0.00 1147.73 1147.85 1147.58 0.00 0 56 G1F 2 30.35F 4 63.52F 6 97.73F 8 132.14F10 167.14F12 201.94F14 237.09F16 272.17F18 307.43F20 342.60F22 377.77 83 G1F24 413.06F26 448.36F28 483.74F30 518.57F32 553.54F34 588.41F36 623.11F38 658.19F40 693.20F42 728.67F44 764.03 25 G1F46 799.28F48 833.98F50 869.42F52 904.71F54 939.79F56 974.42F58 1008.90F60 1044.22F62 1079.51F64 1114.63F66 1147.58 16 -E1M 114Shen XX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 +E1M 114SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 88 E2F 693.20Y 0 2 2 1 9 0 0.00 0.00 0.00 0.00 0.00 0 64 D1M 116Swimmer116 Test 11601011970 17 0 12 -E1M 116SommeXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 +E1M 116SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 19 E2F 1019.96Y 0 5 3 1 1 0 1020.28 1020.14 1020.17 1019.96 0.00 0 76 G1F 2 28.37F 4 59.30F 6 90.53F 8 122.34F10 153.50F12 184.85F14 216.11F16 247.32F18 278.66F20 310.23F22 341.84 83 G1F24 372.93F26 404.00F28 435.16F30 466.35F32 497.71F34 528.76F36 560.28F38 591.54F40 622.48F42 653.21F44 684.34 15 G1F46 715.30F48 746.08F50 776.58F52 807.47F54 837.90F56 868.63F58 899.04F60 929.39F62 959.82F64 990.33F66 1019.96 95 D1F 120Swimmer120 Test 12001011970 12 0 06 -E1F 120TapawXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 +E1F 120SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 88 E2F 1257.27Y 0 2 2 4 24 0 0.00 1257.27 1257.12 1257.27 0.00 0 46 G1F 2 32.54F 4 68.29F 6 105.25F 8 142.99F10 180.46F12 218.02F14 256.09F16 294.03F18 331.66F20 369.39F22 407.41 14 G1F24 446.39F26 484.53F28 522.97F30 561.91F32 601.20F34 639.45F36 678.16F38 717.26F40 756.03F42 796.40F44 835.27 25 G1F46 873.94F48 913.67F50 951.82F52 992.11F54 1030.90F56 1069.74F58 1108.84F60 1145.98F62 1183.50F64 1221.29F66 1257.27 26 D1M 125Swimmer125 Test 12501011970 13 0 93 -E1M 125VillaXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 09 +E1M 125SwimmXX 1650A 11109 0A 0.00 1 0.00 0.00 0.00 0.00 NN N 09 E2F 1197.63Y 0 3 1 7 18 0 0.00 1197.76 1197.70 1197.63 0.00 0 66 G1F 2 30.37F 4 64.30F 6 99.09F 8 134.66F10 170.51F12 205.70F14 242.61F16 278.97F18 314.43F20 350.82F22 388.24 83 G1F24 424.49F26 460.93F28 497.30F30 534.33F32 571.15F34 609.31F36 646.63F38 683.34F40 721.43F42 758.61F44 795.34 15 G1F46 833.11F48 869.27F50 907.08F52 944.31F54 981.61F56 1019.34F58 1056.70F60 1094.19F62 1129.81F64 1166.52F66 1197.63 16 -E1M 125VillaXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 09 +E1M 125SwimmXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 09 E2F 350.82Y 0 1 4 4 4 0 0.00 0.00 0.00 0.00 0.00 0 64 -E1M 125VillaXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 19 +E1M 125SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 19 E2F 721.43Y 0 2 5 5 13 0 0.00 0.00 0.00 0.00 0.00 0 84 C1UNAT Unattached Unattached 0 0 51 D1F 131Swimmer131 Test 13101011970 15 0 73 -E1F 131KargoXX 1650A 11109 0A 0.00 1 0.00Y 0.00Y 0.00 0.00 NN N 69 +E1F 131SwimmXX 1650A 11109 0A 0.00 1 0.00Y 0.00Y 0.00 0.00 NN N 69 E2F 1126.69Y 0 4 5 3 11 0 1126.71 1126.70 1126.71 1126.69 0.00 0 96 G1F 2 30.58F 4 63.67F 6 97.25F 8 130.58F10 163.81F12 196.81F14 230.90F16 264.46F18 298.52F20 332.57F22 366.70 04 G1F24 400.59F26 434.90F28 468.81F30 503.22F32 537.85F34 573.05F36 608.17F38 642.23F40 677.08F42 712.48F44 747.76 25 G1F46 783.28F48 818.52F50 853.47F52 887.93F54 923.47F56 959.59F58 995.07F60 1030.57F62 1062.70F64 1094.73F66 1126.69 06 -E1F 131KargoXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 98 +E1F 131SwimmXX 500A 11109 0A 0.00101 0.00 0.00 0.00 0.00 NN N 98 E2F 332.57Y 0 1 3 2 2 0 0.00 0.00 0.00 0.00 0.00 0 64 -E1F 131KargoXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 09 +E1F 131SwimmXX 1000A 11109 0A 0.00102 0.00 0.00 0.00 0.00 NN N 09 E2F 677.08Y 0 1 6 7 7 0 0.00 0.00 0.00 0.00 0.00 0 74 diff --git a/tests/hy3/fixtures/mm4_ymca_col92_division_citizenship.hy3 b/tests/hy3/fixtures/mm4_ymca_col92_division_citizenship.hy3 index 3840a86..e52f8e0 100644 --- a/tests/hy3/fixtures/mm4_ymca_col92_division_citizenship.hy3 +++ b/tests/hy3/fixtures/mm4_ymca_col92_division_citizenship.hy3 @@ -4,29 +4,29 @@ B22013 National YMCA Short Course Greensboro NC C1ANA Andover/North Andover YMCA Andover-MA NE 0 0 27 C2 MA 62 D1F 1906Swimmer1906 Test 39301011970 18 0 USA 22 -E1F 1906CammaFW 50A 0109 13S 10.00101 24.56Y 24.56Y 0.00 0.00 NN SW N 11 +E1F 1906SwimmFW 50A 0109 13S 10.00101 24.56Y 24.56Y 0.00 0.00 NN SW N 11 E2P 24.63Y 0 18 6 5 108 0 24.68 24.67 0.00 24.63 0.00 04032013 76 D1F 1904Swimmer1904 Test 39101011970 16 0 USA 72 -E1F 1904LennoFW 200A 0109 13S 10.00201 114.59Y 114.59Y 0.00 0.00 NN SW N 61 +E1F 1904SwimmFW 200A 0109 13S 10.00201 114.59Y 114.59Y 0.00 0.00 NN SW N 61 E2P 115.49Y 0 7 8 1 78 0 115.43 115.45 0.00 115.49 0.00 0.0004042013 47 D1F 1903Swimmer1903 Test 39001011970 16 0 USA 97 -E1F 1903RussoFW 50A 0109 13S 10.00101 24.95Y 24.95Y 0.00 0.00 NN SW N 51 +E1F 1903SwimmFW 50A 0109 13S 10.00101 24.95Y 24.95Y 0.00 0.00 NN SW N 51 E2P 25.06Y 0 5 8 4 173 0 25.00 24.98 0.00 25.06 0.00 04032013 46 D1F 1905Swimmer1905 Test 39201011970 13 0 USA 61 -E1F 1905Ty FW 100B 0109 13S 10.00105 58.95Y 58.95Y 0.00 0.00 NN SW N 20 +E1F 1905SwimmFW 100B 0109 13S 10.00105 58.95Y 58.95Y 0.00 0.00 NN SW N 20 E2P 60.67Y 0 11 2 6 150 0 60.60 60.54 0.00 60.67 0.00 0.0004032013 07 F1ANA A 0FFW 200E 0109 13S 40.00109 112.00Y 112.00Y 0.00 0.00 NN 4 SW NA 10 F2P 111.38Y 0 2 6 3 56 0 111.33 111.36 0.00 111.38 0.00 0.00 +0.0 +0.0 +0.004032013 48 G1P 2 27.54P 4 58.97P 6 86.27P 8 111.38 94 -F3F 1906CammaP1F 1905Ty P2F 1903RussoP3F 1904LennoP4 63 +F3F 1906SwimmP1F 1905SwimmP2F 1903SwimmP3F 1904SwimmP4 63 C1SPY Anne Arundel County YMCA Anne Arundel-MD MD 0 0 57 C2 MD USA 69 D1F 1893Swimmer1893 Test 38001011970 16 0 USA 94 -E1F 1893AtkinFW 100D 0109 13S 10.00205 59.31Y 59.31Y 0.00 0.00 NN SW N 41 +E1F 1893SwimmFW 100D 0109 13S 10.00205 59.31Y 59.31Y 0.00 0.00 NN SW N 41 E2P 59.56Y 0 4 2 5 130 0 59.51 59.42 0.00 59.56 0.00 0.0004042013 07 G1P 2 27.65P 4 59.56 52 C1AYST Auburn YMCA-WEIU Auburn-NY NI 0 0 61 C2 NY USA 62 D1F 2298Swimmer2298 Test 78501011970 15 0 41 -E1F 2298D'ArrFW 50A 0109 13A 10.00141 26.05Y 26.05Y 0.00 0.00 NN SW TN 80 +E1F 2298SwimmFW 50A 0109 13A 10.00141 26.05Y 26.05Y 0.00 0.00 NN SW TN 80 E2F 26.17Y 0 7 8 7 130 0 26.24 0.00 0.00 26.17 0.00 0.0004032013 0 76 diff --git a/tests/hy3/fixtures/mm_col77_division.hy3 b/tests/hy3/fixtures/mm_col77_division.hy3 index 960fd19..c753f97 100644 --- a/tests/hy3/fixtures/mm_col77_division.hy3 +++ b/tests/hy3/fixtures/mm_col77_division.hy3 @@ -5,14 +5,14 @@ C1BB BERLIN BARRACUDA SWIM TEAM WITest Contact C2 WI 32 C3 06 D1M 5570Swimmer5570 Test 41901011970 11 0 USA N 81 -E1M 5570FritzMB 100B 0109 0S 4.00 52 104.05Y 104.05Y 4.00 0.00 NN N 50 +E1M 5570SwimmMB 100B 0109 0S 4.00 52 104.05Y 104.05Y 4.00 0.00 NN N 50 E2F 102.54Y 0 2 8 5 13 0 102.49 0.00 0.00 107.39 0.00 11082015A 0 96 D1M 5564Swimmer5564 Test 41301011970 14 0 USA N 64 -E1M 5564NeubaMB 200E 13 14 0S 4.00 32 171.94Y 171.94Y 14.00 0.00JV NN N 31 +E1M 5564SwimmMB 200E 13 14 0S 4.00 32 171.94Y 171.94Y 14.00 0.00JV NN N 31 E2F 161.36Y 0 1 6 1 5 0 161.34 0.00 161.40 161.36 0.00 11082015 0 66 D1F 5563Swimmer5563 Test 41201011970 13 0 USA N 96 -E1F 5563SonneFG 200E 13 14 0S 4.00 31 165.74Y 165.74Y 14.00 0.00JV NN N 41 +E1F 5563SwimmFG 200E 13 14 0S 4.00 31 165.74Y 165.74Y 14.00 0.00JV NN N 41 E2F 156.28Y 0 4 7 1 5 0 0.00 156.17 0.00 156.28 0.00 11082015 0 56 -E1F 5563SonneFG 100C 13 14 0S 4.00 41 89.72Y 89.72Y 3.00 0.00JV NN N 01 +E1F 5563SwimmFG 100C 13 14 0S 4.00 41 89.72Y 89.72Y 3.00 0.00JV NN N 01 E2F 89.75Y 0 3 2 5 14 0 0.00 69.80 0.00 89.75 0.00 11082015K 0 76 G1F 2 42.43F 4 89.75 32 diff --git a/tests/hy3/fixtures/mm_pad_button_divergence.hy3 b/tests/hy3/fixtures/mm_pad_button_divergence.hy3 index d3034eb..ff684f6 100644 --- a/tests/hy3/fixtures/mm_pad_button_divergence.hy3 +++ b/tests/hy3/fixtures/mm_pad_button_divergence.hy3 @@ -4,12 +4,12 @@ B22025 MT HOT Tropical Meet Hardin Community Activity Center C1BAC Billings Aquatic Club Stingray MT 0 0 84 C2 MT 58 D1F 175Swimmer175 Test 14401011970 11 0 USA N 53 -E1F 175AnderFW 100A 11 12 0S 3.00 1E 74.66Y 74.66Y 7.00 0.00 NN N 20 +E1F 175SwimmFW 100A 11 12 0S 3.00 1E 74.66Y 74.66Y 7.00 0.00 NN N 20 E2F 75.29Y 0 10 7 4 10 0 75.29 0.00 75.32 36.26 0.00 01122025A 0 96 -E1F 175AnderFW 50D 11 12 0S 3.00 5E 37.65Y 37.65Y 15.00 0.00 NN N 30 +E1F 175SwimmFW 50D 11 12 0S 3.00 5E 37.65Y 37.65Y 15.00 0.00 NN N 30 E2F 36.50Y 0 8 2 5 4 0 36.54 0.00 0.00 36.50 0.00 01122025 0 06 C1SST Sheridan Swim Team WY 0 0 99 C2 WY 38 D1M 94Swimmer94 Test 6301011970 11 0 USA N 85 -E1M 94AguirMM 100D 11 12 0S 3.00 13F 81.05Y 81.05Y 20.00 0.00 NN N 40 +E1M 94SwimmMM 100D 11 12 0S 3.00 13F 81.05Y 81.05Y 20.00 0.00 NN N 40 E2F 78.76Y 0 3 5 1 1 0 78.71 0.00 0.00 78.76 0.00 01122025 0 16 From e4bb55aa768009624a0c41140422143433861174 Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Mon, 27 Jul 2026 22:36:57 -0700 Subject: [PATCH 09/12] test(hy3): redact the F3 surname prefixes in the relay-alternates fixture Completes the previous commit, which covered five of the six fixtures that predate the E1/F3 name-prefix rule and missed this one. 221 occurrences, 142 distinct real surname fragments, all in F3 leg slots -- this fixture exists to exercise relays with alternates, so it is almost entirely F3 content. Two of its F3 lines carry a fifth leg, so anything checking only the first four slots will under-report it; all eight slots are covered here, matching f3_parser's own range. No parser reads cols 9-13 of a leg slot -- f3_parser takes the swimmer id at cols 4-8 and the leg number at col 15 -- so blanking them to Swimm is parse-safe. The file is byte-for-byte the same length, a character-level diff against the previous revision puts every changed position inside an F3 name-prefix range, and test_bug2b_relay_entries_with_alternates_preserve_leg_numbers, which reads this fixture, still passes along with the rest of the 106. Every fixture in the directory now satisfies the documented rule set. --- .../fixtures/mm5_8_0fd_relay_alternates.hy3 | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/tests/hy3/fixtures/mm5_8_0fd_relay_alternates.hy3 b/tests/hy3/fixtures/mm5_8_0fd_relay_alternates.hy3 index fc9ed45..8174b6c 100644 --- a/tests/hy3/fixtures/mm5_8_0fd_relay_alternates.hy3 +++ b/tests/hy3/fixtures/mm5_8_0fd_relay_alternates.hy3 @@ -15,15 +15,15 @@ D1M 481Swimmer481 Test F1BI A 0MMB 200A 0109 0S 30.00 17 94.21Y 94.21Y 28.00 0.00 NN 4 NA 09 F2F 96.28Y 0 2 6 5 13 0 96.20 96.33 0.00 96.28 0.00 02012025 0 56 G1F 2 23.73F 4 47.72F 6 71.47F 8 96.28 54 -F3M 465McLalF1M 467PatteF2M 461CorleF3M 460BobroF4 24 +F3M 465SwimmF1M 467SwimmF2M 461SwimmF3M 460SwimmF4 24 F1BI A 0MMB 200A 0109 0S 30.00 17 94.21Y 94.21Y 28.00 0.00 NN 4 NA 09 F2P 96.19Y 0 5 7 5 12 0 96.18 96.32 0.00 96.19 0.00 01312025 66 G1P 2 23.84P 4 48.57P 6 72.65P 8 96.19 94 -F3M 465McLalP1M 479CorleP2M 461CorleP3M 481SpendP4 54 +F3M 465SwimmP1M 479SwimmP2M 461SwimmP3M 481SwimmP4 54 F1BI B 0MMB 200A 0109 0S 30.00 17 99.76Y 99.76Y 0.00 0.00 NN X4 NB 69 F2P 99.95Y X 0 3 1 0 0 0 99.96 99.95 0.00 99.95 0.00 01312025 17 G1P 2 25.19P 4 50.49P 6 75.52P 8 99.95 94 -F3M 496MorseP3M 472JohnsP4 77 +F3M 496SwimmP3M 472SwimmP4 77 C1MAC Bishop McNamara High School Bishop McNamara US 0 0 89 C2 MD USA 44 D1M 367Swimmer367 Test 36701011970 0JR 0 USA N 38 @@ -33,11 +33,11 @@ D1M 365Swimmer365 Test F1MAC A 0MMB 200A 0109 0S 30.00 17 108.05Y 108.05Y 0.00 0.00 NN 4 NA 29 F2F 0.00YR 0 1 3 0 0 0 0.00 0.00 0.00 0.00 0.00 02012025 0 55 G1F 2 0.00F 4 0.00F 6 0.00F 8 0.00 53 -F3M 368HarriF1M 367DickeF2M 365MosleF3M 364EvansF4 44 +F3M 368SwimmF1M 367SwimmF2M 365SwimmF3M 364SwimmF4 44 F1MAC A 0MMB 200A 0109 0S 30.00 17 108.05Y 108.05Y 0.00 0.00 NN 4 NA 29 F2P 103.47Y 0 2 1 2 19 0 103.41 103.51 0.00 103.47 0.00 01312025 66 G1P 2 27.21P 4 52.88P 6 79.07P 8 103.47 94 -F3M 368HarriP1M 367DickeP2M 365MosleP3M 364EvansP4 74 +F3M 368SwimmP1M 367SwimmP2M 365SwimmP3M 364SwimmP4 74 C1DJO Bishop O'Connell High School Bishop O'ConnellPV 0 0 00 C2 VA USA 43 D1M 113Swimmer113 Test 11301011970 14FR 0 USA N 34 @@ -51,15 +51,15 @@ D1M 121Swimmer121 Test F1DJO A 0MMB 200A 0109 0S 30.00 17 93.81Y 93.81Y 40.00 0.00 NN 4 NA 39 F2F 92.22Y 0 2 4 1 9 0 92.26 92.23 0.00 92.22 0.00 02012025 0 26 G1F 2 23.28F 4 46.39F 6 69.35F 8 92.22 54 -F3M 92BuckhF1M 98FahleF2M 119Cru MF3M 89FlemiF4 23 +F3M 92SwimmF1M 98SwimmF2M 119SwimmF3M 89SwimmF4 23 F1DJO A 0MMB 200A 0109 0S 30.00 17 93.81Y 93.81Y 40.00 0.00 NN 4 NA 39 F2P 93.17Y 0 5 2 4 9 0 93.10 93.10 0.00 93.17 0.00 01312025 26 G1P 2 23.54P 4 46.99P 6 70.50P 8 93.17 84 -F3M 92BuckhP1M 98FahleP2M 119Cru MP3M 89FlemiP4 53 +F3M 92SwimmP1M 98SwimmP2M 119SwimmP3M 89SwimmP4 53 F1DJO B 0MMB 200A 0109 0S 30.00 17 97.72Y 97.72Y 0.00 0.00 NN X4 NB 89 F2P 98.82Y X 0 5 1 0 0 0 98.82 98.88 0.00 98.82 0.00 01312025 07 G1P 2 24.30P 4 48.98P 6 74.78P 8 98.82 94 -F3M 113BraveP1M 87MilleP2M 121SayboP3M 118MilliP4 64 +F3M 113SwimmP1M 87SwimmP2M 121SwimmP3M 118SwimmP4 64 C1BU Bullis School Bullis PVTest Contact Test Contact 0 0 09 C2 MD USA 16 C3 73 @@ -74,15 +74,15 @@ D1M 560Swimmer560 Test F1BU A 0MMB 200A 0109 0S 30.00 17 101.23Y 101.23Y 18.00 0.00 NN 4 NA 29 F2F 97.33Y 0 1 4 1 17 0 97.07 97.33 0.00 97.33 0.00 02012025 0 66 G1F 2 23.99F 4 48.81F 6 73.41F 8 97.33 54 -F3M 557Guo F1M 574NarciF2M 565SimonF3M 575AroraF4 63 +F3M 557SwimmF1M 574SwimmF2M 565SwimmF3M 575SwimmF4 63 F1BU A 0MMB 200A 0109 0S 30.00 17 101.23Y 101.23Y 18.00 0.00 NN 4 NA 29 F2P 100.52Y 0 3 8 4 17 0 100.52 0.00 100.45 100.52 0.00 01312025 76 G1P 2 24.35P 4 50.47P 6 76.44P 8 100.52 84 -F3M 565SimonP1M 574NarciP2M 560ZachaP3M 575AroraP4 74 +F3M 565SwimmP1M 574SwimmP2M 560SwimmP3M 575SwimmP4 74 F1BU B 0MMB 200A 0109 0S 30.00 17 107.83Y 107.83Y 0.00 0.00 NN X4 NB 89 F2P 108.24Y X 0 2 7 0 0 0 108.23 108.33 0.00 108.24 0.00 01312025 17 G1P 2 24.41P 4 50.54P 6 86.22P 8 108.24 84 -F3M 563BhandP2M 573HussaP3M 578Li P4 69 +F3M 563SwimmP2M 573SwimmP3M 578SwimmP4 69 C1DEM DeMatha Catholic High School DeMatha Test Contact Test Contact 0 0 01 C2 MD USA 23 C3 52 @@ -98,15 +98,15 @@ D1M 453Swimmer453 Test F1DEM A 0MMB 200A 0109 0S 30.00 17 85.16Y 85.16Y 64.00 0.00 NN 4 NA 39 F2F 84.25Y 0 3 3 1 1 0 84.29 84.28 0.00 84.25 0.00 02012025 0 36 G1F 2 21.35F 4 43.02F 6 64.16F 8 84.25 44 -F3M 446RooneF1M 424LopezF2M 448GinsbF3M 423Ross F4 34 +F3M 446SwimmF1M 424SwimmF2M 448SwimmF3M 423SwimmF4 34 F1DEM A 0MMB 200A 0109 0S 30.00 17 85.16Y 85.16Y 64.00 0.00 NN 4 NA 39 F2P 89.09Y 0 4 4 1 3 0 88.91 89.03 0.00 89.09 0.00 01312025 46 G1P 2 22.17P 4 45.06P 6 67.07P 8 89.09 84 -F3M 446RooneP1M 425RamirP2M 448GinsbP3M 423Ross P4 54 +F3M 446SwimmP1M 425SwimmP2M 448SwimmP3M 423SwimmP4 54 F1DEM B 0MMB 200A 0109 0S 30.00 17 92.78Y 92.78Y 0.00 0.00 NN X4 NB 79 F2P 92.54Y X 0 4 3 0 0 0 92.55 92.53 0.00 92.54 0.00 01312025 86 G1P 2 23.30P 4 46.35P 6 70.07P 8 92.54 74 -F3M 449Nedd P1M 444BrownP2M 432JenkiP3M 453ShotwP4 64 +F3M 449SwimmP1M 444SwimmP2M 432SwimmP3M 453SwimmP4 64 C1FH Flint Hill School Flint HIll PV 0 0 33 C2 VA USA 30 D1M 747Swimmer747 Test 74701011970 1726 0 N 02 @@ -120,15 +120,15 @@ D1M 757Swimmer757 Test F1FH A 0MMB 200A 0109 0S 30.00 17 93.47Y 93.47Y 50.00 0.00 NN 4 NA 19 F2F 90.03Y 0 3 1 5 5 0 90.09 90.06 0.00 90.03 0.00 02012025 0 26 G1F 2 22.09F 4 44.90F 6 67.91F 8 90.03 54 -F3M 751LandwF1M 752LindrF2M 757Tan F3M 747BermuF4 53 +F3M 751SwimmF1M 752SwimmF2M 757SwimmF3M 747SwimmF4 53 F1FH A 0MMB 200A 0109 0S 30.00 17 93.47Y 93.47Y 50.00 0.00 NN 4 NA 19 F2P 91.92Y 0 4 6 3 7 0 91.95 92.01 0.00 91.92 0.00 01312025 36 G1P 2 22.68P 4 45.71P 6 69.72P 8 91.92 84 -F3M 751LandwP1M 752LindrP2M 745CarstP3M 747BermuP4 94 +F3M 751SwimmP1M 752SwimmP2M 745SwimmP3M 747SwimmP4 94 F1FH B 0MMB 200A 0109 0S 30.00 17 101.02Y 101.02Y 0.00 0.00 NN X4 NB 49 F2P 101.18Y X 0 4 8 0 0 0 101.20 0.00 101.11 101.18 0.00 01312025 96 G1P 2 26.56P 4 51.49P 6 78.43P 8 101.18 94 -F3M 749Choi P1M 750JurutP2M 746ChughP3 90 +F3M 749SwimmP1M 750SwimmP2M 746SwimmP3 90 C1GDS Georgetown Day School 0 0 60 D1M 551Swimmer551 Test 55101011970 14FR 0 N 40 D1M 553Swimmer553 Test 55301011970 0FR 0 N 77 @@ -138,11 +138,11 @@ D1M 550Swimmer550 Test F1GDS A 0MMB 200A 0109 0S 30.00 17 109.43Y 109.43Y 12.00 0.00 NN 4 NA 59 F2F 104.73Y 0 1 6 3 19 0 104.69 104.82 0.00 104.73 0.00 02012025 0 96 G1F 2 27.04F 4 53.12F 6 79.04F 8 104.73 54 -F3M 550MckenF1M 551HirshF2M 547KeeleF3M 553HolleF4 34 +F3M 550SwimmF1M 551SwimmF2M 547SwimmF3M 553SwimmF4 34 F1GDS A 0MMB 200A 0109 0S 30.00 17 109.43Y 109.43Y 12.00 0.00 NN 4 NA 59 F2P 108.00Y 0 2 8 3 20 0 108.03 0.00 107.98 108.00 0.00 01312025 86 G1P 2 27.23P 4 54.22P 6 81.16P 8 108.00 74 -F3M 554Li P1M 551HirshP2M 547KeeleP3M 553HolleP4 33 +F3M 554SwimmP1M 551SwimmP2M 547SwimmP3M 553SwimmP4 33 C1PREP GEORGETOWN PREPARATORY SCHOOL GEORGETOWN PREP Test Contact Test Contact 0 0 17 C2 MD USA 39 C3 74 @@ -157,15 +157,15 @@ D1M 614Swimmer614 Test F1PREP A 0MMB 200A 0109 0S 30.00 17 85.84Y 85.84Y 56.00 0.00 NN 4 NA 00 F2F 84.56Y 0 3 4 2 2 0 84.67 84.70 0.00 84.56 0.00 02012025 0 46 G1F 2 20.91F 4 42.79F 6 64.54F 8 84.56 64 -F3M 626OehleF1M 629KratzF2M 627HugheF3M 614VanasF4 74 +F3M 626SwimmF1M 629SwimmF2M 627SwimmF3M 614SwimmF4 74 F1PREP A 0MMB 200A 0109 0S 30.00 17 85.84Y 85.84Y 56.00 0.00 NN 4 NA 00 F2P 86.34Y 0 3 4 1 1 0 86.30 86.38 0.00 86.34 0.00 01312025 36 G1P 2 21.12P 4 43.69P 6 65.80P 8 86.34 84 -F3M 626OehleP1M 629KratzP2M 627HugheP3M 614VanasP4 05 +F3M 626SwimmP1M 629SwimmP2M 627SwimmP3M 614SwimmP4 05 F1PREP B 0MMB 200A 0109 0S 30.00 17 99.93Y 99.93Y 0.00 0.00 NN X4 NB 40 F2P 96.09Y X 0 5 8 0 0 0 96.08 0.00 96.05 96.09 0.00 01312025 96 G1P 2 24.49P 4 48.20P 6 72.08P 8 96.09 84 -F3M 612GeoghP1M 621AndreP2M 625BedesP3M 615EsqueP4 34 +F3M 612SwimmP1M 621SwimmP2M 625SwimmP3M 615SwimmP4 34 C1GONZ Gonzaga College High School Gonzaga MDTest Contact Test Contact 0 0 68 C2 DC USA 90 C3 19 @@ -182,15 +182,15 @@ D1M 158Swimmer158 Test F1GONZ A 0MMB 200A 0109 0S 30.00 17 84.88Y 84.88Y 54.00 0.00 NN 4 NA 10 F2F 85.25Y 0 3 5 3 3 0 85.21 85.26 0.00 85.25 0.00 02012025 0 36 G1F 2 21.45F 4 43.21F 6 64.67F 8 85.25 44 -F3M 149BrookF1M 158WilkiF2M 191FisheF3M 147Bice F4 73 +F3M 149SwimmF1M 158SwimmF2M 191SwimmF3M 147SwimmF4 73 F1GONZ A 0MMB 200A 0109 0S 30.00 17 84.88Y 84.88Y 54.00 0.00 NN 4 NA 10 F2P 87.83Y 0 5 4 1 2 0 87.76 87.88 0.00 87.83 0.00 01312025 56 G1P 2 22.47P 4 44.86P 6 66.59P 8 87.83 94 -F3M 177ThiedP1M 148BishoP2M 191FisheP3M 147Bice P4 73 +F3M 177SwimmP1M 148SwimmP2M 191SwimmP3M 147SwimmP4 73 F1GONZ B 0MMB 200A 0109 0S 30.00 17 115.74Y 115.74Y 0.00 0.00 NN X4 NB 50 F2P 119.78Y X 0 1 7 0 0 0 119.62 119.72 0.00 119.78 0.00 01312025 37 G1P 2 28.41P 4 58.89P 6 88.69P 8 119.78 15 -F3M 159Beck P1M 198GuerrP2M 168ManciP3M 162FrownP4 44 +F3M 159SwimmP1M 198SwimmP2M 168SwimmP3M 162SwimmP4 44 C1LS Landon School Varsity SwimmingLandon School MD 0 0 61 C2 MD USA 44 C3 82 @@ -207,15 +207,15 @@ D1M 718Swimmer718 Test F1LS A 0MMB 200A 0109 0S 30.00 17 92.47Y 92.47Y 26.00 0.00 NN 4 NA 39 F2F 96.47Y 0 2 7 6 14 0 96.43 96.35 0.00 96.47 0.00 02012025 0 66 G1F 2 24.06F 4 48.16F 6 73.31F 8 96.47 54 -F3M 716Guo F1M 721IacovF2M 733MarchF3M 714AnvarF4 43 +F3M 716SwimmF1M 721SwimmF2M 733SwimmF3M 714SwimmF4 43 F1LS A 0MMB 200A 0109 0S 30.00 17 92.47Y 92.47Y 26.00 0.00 NN 4 NA 39 F2P 97.25Y 0 5 3 6 14 0 97.18 97.25 0.00 97.25 0.00 01312025 66 G1P 2 24.18P 4 48.16P 6 72.75P 8 97.25 84 -F3M 716Guo P1M 741SchwaP2M 721IacovP3M 727PederP4M 733MarchP5 27 +F3M 716SwimmP1M 741SwimmP2M 721SwimmP3M 727SwimmP4M 733SwimmP5 27 F1LS B 0MMB 200A 0109 0S 30.00 17 113.25Y 113.25Y 0.00 0.00 NN X4 NB 79 F2P 110.79Y X 0 1 6 0 0 0 110.82 110.81 0.00 110.79 0.00 01312025 17 G1P 2 25.71P 4 50.62P 6 81.60P 8 110.79 84 -F3M 718Shao P1M 743LaPlaP2M 737HanikP3M 740ReisfP4 04 +F3M 718SwimmP1M 743SwimmP2M 737SwimmP3M 740SwimmP4 04 C1MAR MARET SCHOOL FROGS PVTest Contact Test Contact 0 0 02 C2 DC USA 67 D1M 210Swimmer210 Test 21001011970 0 0 N 11 @@ -225,7 +225,7 @@ D1M 208Swimmer208 Test F1MAR A 0MMB 200A 0109 0S 30.00 17 106.81Y 106.81Y 0.00 0.00 NN 4 NA 39 F2P 0.00YR 0 2 2 0 0 0 0.00 0.00 0.00 0.00 0.00 01312025 45 G1P 2 0.00P 4 0.00P 6 0.00P 8 0.00 83 -F3M 210SchesP1M 208VandiP2M 201TekleP3M 200SisleP4 64 +F3M 210SwimmP1M 208SwimmP2M 201SwimmP3M 200SwimmP4 64 C1GC Our Lady of Good Counsel Good Counsel Test Contact Test Contact 0 0 34 C2 MD USA 51 D1M 272Swimmer272 Test 27201011970 18SR 0 N 29 @@ -240,15 +240,15 @@ D1M 277Swimmer277 Test F1GC A 0MMB 200A 0109 0S 30.00 17 93.07Y 93.07Y 14.00 0.00 NN 4 NA 09 F2F 98.84Y 0 1 5 2 18 0 98.69 98.85 0.00 98.84 0.00 02012025 0 86 G1F 2 25.45F 4 49.64F 6 74.06F 8 98.84 54 -F3M 272BoscoF1M 304SeageF2M 341CulkiF3M 277WatkiF4 54 +F3M 272SwimmF1M 304SwimmF2M 341SwimmF3M 277SwimmF4 54 F1GC A 0MMB 200A 0109 0S 30.00 17 93.07Y 93.07Y 14.00 0.00 NN 4 NA 09 F2P 101.26Y 0 3 3 5 18 0 101.20 101.21 0.00 101.26 0.00 01312025 56 G1P 2 25.72P 4 51.16P 6 76.39P 8 101.26 84 -F3M 272BoscoP1M 281MarkoP2M 341CulkiP3M 277WatkiP4M 304SeageP5 48 +F3M 272SwimmP1M 281SwimmP2M 341SwimmP3M 277SwimmP4M 304SwimmP5 48 F1GC B 0MMB 200A 0109 0S 30.00 17 110.80Y 110.80Y 0.00 0.00 NN X4 NB 59 F2P 110.58Y X 0 1 5 0 0 0 110.66 110.63 0.00 110.58 0.00 01312025 17 G1P 2 29.86P 4 57.63P 6 85.71P 8 110.58 05 -F3M 322LaceyP1M 274GarveP2M 279EssigP3M 276StoneP4 84 +F3M 322SwimmP1M 274SwimmP2M 279SwimmP3M 276SwimmP4 84 C1POTO Potomac School Swim Team Potomac PV 0 0 76 C2 VA USA 33 C3 51 @@ -263,15 +263,15 @@ D1M 40Swimmer40 Test F1POTO A 0MMB 200A 0109 0S 30.00 17 90.06Y 90.06Y 30.00 0.00 NN 4 NA 89 F2F 95.21Y 0 2 5 4 12 0 95.08 95.24 0.00 95.21 0.00 02012025 0 56 G1F 2 23.16F 4 47.01F 6 70.98F 8 95.21 44 -F3M 51TiernF1M 52BulfoF2M 46MajeeF3M 47OzcanF4 83 +F3M 51SwimmF1M 52SwimmF2M 46SwimmF3M 47SwimmF4 83 F1POTO A 0MMB 200A 0109 0S 30.00 17 90.06Y 90.06Y 30.00 0.00 NN 4 NA 89 F2P 94.93Y 0 3 5 2 10 0 95.01 94.97 0.00 94.93 0.00 01312025 66 G1P 2 23.42P 4 47.79P 6 71.90P 8 94.93 84 -F3M 49RossmP1M 52BulfoP2M 46MajeeP3M 51TiernP4 44 +F3M 49SwimmP1M 52SwimmP2M 46SwimmP3M 51SwimmP4 44 F1POTO B 0MMB 200A 0109 0S 30.00 17 103.16Y 103.16Y 0.00 0.00 NN X4 NB 40 F2P 99.72Y X 0 2 5 0 0 0 99.74 99.75 0.00 99.72 0.00 01312025 07 G1P 2 25.17P 4 50.23P 6 74.95P 8 99.72 84 -F3M 40TraveP2M 39SardaP3M 50SteveP4 80 +F3M 40SwimmP2M 39SwimmP3M 50SwimmP4 80 C1SMR Ryken High School Ryken HS 0 0 02 C2 MD USA 25 D1M 15Swimmer15 Test 1501011970 17 0 N 60 @@ -281,11 +281,11 @@ D1M 24Swimmer24 Test F1SMR A 0MMB 200A 0109 0S 30.00 17 98.21Y 98.21Y 24.00 0.00 NN 4 NA 49 F2F 96.58Y 0 2 8 7 15 0 96.57 0.00 96.57 96.58 0.00 02012025 0 76 G1F 2 23.70F 4 48.38F 6 72.88F 8 96.58 64 -F3M 24ThompF1M 23ThompF2M 15MagnoF3M 21smithF4 44 +F3M 24SwimmF1M 23SwimmF2M 15SwimmF3M 21SwimmF4 44 F1SMR A 0MMB 200A 0109 0S 30.00 17 98.21Y 98.21Y 24.00 0.00 NN 4 NA 49 F2P 99.07Y 0 4 1 6 16 0 99.04 99.11 0.00 99.07 0.00 01312025 56 G1P 2 24.31P 4 49.38P 6 74.04P 8 99.07 84 -F3M 24ThompP1M 23ThompP2M 15MagnoP3M 21smithP4 74 +F3M 24SwimmP1M 23SwimmP2M 15SwimmP3M 21SwimmP4 74 C1SID Sidwell Friends School Test Contact Test Contact 0 0 53 C2 DC 89 C3 71 @@ -296,11 +296,11 @@ D1M 347Swimmer347 Test F1SID A 0MMB 200A 0109 0S 30.00 17 94.34Y 94.34Y 32.00 0.00 NN 4 NA 39 F2F 95.08Y 0 2 2 3 11 0 94.99 95.09 0.00 95.08 0.00 02012025 0 66 G1F 2 23.35F 4 47.91F 6 72.23F 8 95.08 54 -F3M 357Iimi F1M 347SiMa F2M 356Guo F3M 346JiminF4 22 +F3M 357SwimmF1M 347SwimmF2M 356SwimmF3M 346SwimmF4 22 F1SID A 0MMB 200A 0109 0S 30.00 17 94.34Y 94.34Y 32.00 0.00 NN 4 NA 39 F2P 96.31Y 0 4 7 5 13 0 96.23 96.33 0.00 96.31 0.00 01312025 56 G1P 2 23.41P 4 48.63P 6 73.08P 8 96.31 74 -F3M 357Iimi P1M 347SiMa P2M 356Guo P3M 346JiminP4 52 +F3M 357SwimmP1M 347SwimmP2M 356SwimmP3M 346SwimmP4 52 F1SID B 0MMB 200A 0109 0S 30.00 17 105.11Y 105.11Y 0.00 0.00 NN X4 NB 79 F2P 0.00YR X 0 2 6 0 0 0 0.00 0.00 0.00 0.00 0.00 01312025 06 G1P 2 0.00P 4 0.00P 6 0.00P 8 0.00 83 @@ -318,15 +318,15 @@ D1M 520Swimmer520 Test F1STA A 0MMB 200A 0109 0S 30.00 17 87.87Y 87.87Y 46.00 0.00 NN 4 NA 69 F2F 91.18Y 0 3 7 7 7 0 91.18 91.14 0.00 91.18 0.00 02012025 0 36 G1F 2 22.56F 4 45.34F 6 68.49F 8 91.18 64 -F3M 503Suh F1M 520WaltoF2M 514Lee F3M 504ChalkF4 32 +F3M 503SwimmF1M 520SwimmF2M 514SwimmF3M 504SwimmF4 32 F1STA A 0MMB 200A 0109 0S 30.00 17 87.87Y 87.87Y 46.00 0.00 NN 4 NA 69 F2P 91.77Y 0 5 5 3 6 0 91.76 91.84 0.00 91.77 0.00 01312025 46 G1P 2 22.64P 4 45.53P 6 69.34P 8 91.77 84 -F3M 503Suh P1M 520WaltoP2M 514Lee P3M 504ChalkP4 62 +F3M 503SwimmP1M 520SwimmP2M 514SwimmP3M 504SwimmP4 62 F1STA B 0MMB 200A 0109 0S 30.00 17 94.17Y 94.17Y 0.00 0.00 NN X4 NB 89 F2P 100.83Y X 0 3 2 0 0 0 100.86 100.99 0.00 100.83 0.00 01312025 17 G1P 2 25.70P 4 49.26P 6 75.12P 8 100.83 84 -F3M 527LustbP1M 519MosotP2M 523ButniP3M 506MetzrP4 55 +F3M 527SwimmP1M 519SwimmP2M 523SwimmP3M 506SwimmP4 55 C1SAES St. Andrew's Episcopal School St. Andrew's Epi 0 0 98 C2 MD USA 31 D1M 53Swimmer53 Test 5301011970 15SO 0 N 61 @@ -336,12 +336,12 @@ D1M 66Swimmer66 Test F1SAES A 0MMB 200A 0109 0S 30.00 17 102.19Y 102.19Y 0.00 0.00 NN 4 NA 79 F2F 97.46YQ6F 0 2 1 0 0 0 97.51 97.39 0.00 97.46 0.00 02012025 0 07 G1F 2 25.74F 4 49.82F 6 74.43F 8 97.46 64 -F3M 66WalthF1M 53AroraF2M 65SweenF3M 62PortnF4 24 +F3M 66SwimmF1M 53SwimmF2M 65SwimmF3M 62SwimmF4 24 H16FEarly take-off swimmer #2 90 F1SAES A 0MMB 200A 0109 0S 30.00 17 102.19Y 102.19Y 0.00 0.00 NN 4 NA 79 F2P 97.87Y 0 2 4 1 15 0 97.82 97.97 0.00 97.87 0.00 01312025 76 G1P 2 25.27P 4 49.72P 6 74.71P 8 97.87 94 -F3M 66WalthP1M 53AroraP2M 65SweenP3M 62PortnP4 54 +F3M 66SwimmP1M 53SwimmP2M 65SwimmP3M 62SwimmP4 54 C1SJC St. John's College High SchoolSJC PVTest Contact Test Contact 0 0 53 C2 DC USA 40 C3 92 @@ -356,15 +356,15 @@ D1M 417Swimmer417 Test F1SJC A 0MMB 200A 0109 0S 30.00 17 94.15Y 94.15Y 44.00 0.00 NN 4 NA 39 F2F 92.96Y 0 3 8 8 8 0 93.13 0.00 92.95 92.96 0.00 02012025 0 56 G1F 2 22.73F 4 46.51F 6 70.28F 8 92.96 54 -F3M 389DawsoF1M 390FosteF2M 375KetteF3M 373Egan F4 83 +F3M 389SwimmF1M 390SwimmF2M 375SwimmF3M 373SwimmF4 83 F1SJC A 0MMB 200A 0109 0S 30.00 17 94.15Y 94.15Y 44.00 0.00 NN 4 NA 39 F2P 93.04Y 0 4 2 4 8 0 93.02 93.06 0.00 93.04 0.00 01312025 26 G1P 2 22.72P 4 46.68P 6 70.26P 8 93.04 84 -F3M 389DawsoP1M 390FosteP2M 375KetteP3M 373Egan P4 14 +F3M 389SwimmP1M 390SwimmP2M 375SwimmP3M 373SwimmP4 14 F1SJC B 0MMB 200A 0109 0S 30.00 17 115.15Y 115.15Y 0.00 0.00 NN X4 NB 89 F2P 113.12Y X 0 1 2 0 0 0 113.13 113.16 0.00 113.12 0.00 01312025 96 G1P 2 31.36P 4 58.56P 6 86.91P 8 113.12 84 -F3M 394DierlP1M 412BococP2M 417McIntP3M 393BarbeP4 34 +F3M 394SwimmP1M 412SwimmP2M 417SwimmP3M 393SwimmP4 34 C1PVI St. Paul VI High School Paul VI VA 0 0 92 C2 VA USA 96 C3 60 @@ -379,15 +379,15 @@ D1M 219Swimmer219 Test F1PVI A 0MMB 200A 0109 0S 30.00 17 93.71Y 93.71Y 34.00 0.00 NN 4 NA 59 F2F 93.74Y 0 2 3 2 10 0 93.60 93.66 0.00 93.74 0.00 02012025 0 56 G1F 2 23.97F 4 47.68F 6 70.94F 8 93.74 64 -F3M 226Choi F1M 228CubarF2M 214CaputF3M 245Cox F4 92 +F3M 226SwimmF1M 228SwimmF2M 214SwimmF3M 245SwimmF4 92 F1PVI A 0MMB 200A 0109 0S 30.00 17 93.71Y 93.71Y 34.00 0.00 NN 4 NA 59 F2P 95.85Y 0 3 6 3 11 0 95.81 95.83 0.00 95.85 0.00 01312025 66 G1P 2 24.21P 4 48.58P 6 72.54P 8 95.85 84 -F3M 226Choi P1M 228CubarP2M 214CaputP3M 245Cox P4 23 +F3M 226SwimmP1M 228SwimmP2M 214SwimmP3M 245SwimmP4 23 F1PVI B 0MMB 200A 0109 0S 30.00 17 110.53Y 110.53Y 0.00 0.00 NN X4 NB 99 F2P 106.85Y X 0 1 4 0 0 0 106.80 107.09 0.00 106.85 0.00 01312025 27 G1P 2 24.92P 4 63.94P 6 82.63P 8 106.85 94 -F3M 232HeislP1M 218PutchP2M 216HarviP3M 219RommeP4 94 +F3M 232SwimmP1M 218SwimmP2M 216SwimmP3M 219SwimmP4 94 C1SSSA St. Stephen's & St. Agnes VATest Contact Test Contact 0 0 31 C2 VA USA 51 C3 99 @@ -402,15 +402,15 @@ D1M 262Swimmer262 Test F1SSSA A 0MMB 200A 0109 0S 30.00 17 93.32Y 93.32Y 48.00 0.00 NN 4 NA 89 F2F 91.00Y 0 3 2 6 6 0 90.90 91.00 0.00 91.00 0.00 02012025 0 16 G1F 2 23.46F 4 45.52F 6 69.76F 8 91.00 54 -F3M 250TineoF1M 262UngurF2M 259BethkF3M 249IngraF4 54 +F3M 250SwimmF1M 262SwimmF2M 259SwimmF3M 249SwimmF4 54 F1SSSA A 0MMB 200A 0109 0S 30.00 17 93.32Y 93.32Y 48.00 0.00 NN 4 NA 89 F2P 91.32Y 0 5 6 2 5 0 91.34 91.30 0.00 91.32 0.00 01312025 26 G1P 2 23.40P 4 45.64P 6 70.29P 8 91.32 74 -F3M 250TineoP1M 262UngurP2M 259BethkP3M 249IngraP4 84 +F3M 250SwimmP1M 262SwimmP2M 259SwimmP3M 249SwimmP4 84 F1SSSA B 0MMB 200A 0109 0S 30.00 17 115.92Y 115.92Y 0.00 0.00 NN X4 NB 40 F2P 115.41Y X 0 1 1 0 0 0 115.53 115.47 0.00 115.41 0.00 01312025 17 G1P 2 28.31P 4 55.74P 6 85.46P 8 115.41 84 -F3M 263RiedyP1M 260ClarkP2M 261LiebeP3M 267SokolP4 64 +F3M 263SwimmP1M 260SwimmP2M 261SwimmP3M 267SwimmP4 64 C1HTSHSThe Heights School The Heights MDTest Contact Test Contact 0 0 67 C2 MD USA 94 C3 63 @@ -425,12 +425,12 @@ D1M 139Swimmer139 Test F1HTSHSA 0MMB 200A 0109 0S 30.00 17 88.43Y 88.43Y 52.00 0.00 NN 4 NA 10 F2F 88.38Y 0 3 6 4 4 0 88.29 88.55 0.00 88.38 0.00 02012025 0 56 G1F 2 22.33F 4 44.28F 6 66.55F 8 88.38 64 -F3M 139Rose F1M 136LepreF2M 132FrancF3M 130DeLelF4 73 +F3M 139SwimmF1M 136SwimmF2M 132SwimmF3M 130SwimmF4 73 F1HTSHSA 0MMB 200A 0109 0S 30.00 17 88.43Y 88.43Y 52.00 0.00 NN 4 NA 10 F2P 89.23Y 0 4 5 2 4 0 89.16 89.27 0.00 89.23 0.00 01312025 46 G1P 2 22.37P 4 44.50P 6 66.88P 8 89.23 84 -F3M 139Rose P1M 136LepreP2M 132FrancP3M 130DeLelP4 04 +F3M 139SwimmP1M 136SwimmP2M 132SwimmP3M 130SwimmP4 04 F1HTSHSB 0MMB 200A 0109 0S 30.00 17 104.66Y 104.66Y 0.00 0.00 NN X4 NB 60 F2P 100.08Y X 0 2 3 0 0 0 100.07 100.04 0.00 100.08 0.00 01312025 96 G1P 2 24.34P 4 49.16P 6 75.47P 8 100.08 84 -F3M 133GarveP1M 143MulhoP2M 138RogerP3M 145CardoP4 84 +F3M 133SwimmP1M 143SwimmP2M 138SwimmP3M 145SwimmP4 84 From 59a6a1b82bc00e78b2584789fef2bc8738e5dd4e Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Tue, 28 Jul 2026 10:39:18 -0700 Subject: [PATCH 10/12] fix(hy3): reject non-finite reaction-time tokens float() happily parses "nan"/"inf"/"-inf" and all fit the 5-character E2/F2 reaction-time column, so parse_reaction_time let them through. A bare int() downstream then raises (ValueError on nan, OverflowError on inf) instead of yielding None like every other malformed token, dropping the whole file from a run. Reject non-finite values and add unit test coverage for nan and inf in both signs. --- hytek_parser/hy3/_utils.py | 8 ++++++++ tests/hy3/test_utils.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/hytek_parser/hy3/_utils.py b/hytek_parser/hy3/_utils.py index 2ba6462..f77efcc 100644 --- a/hytek_parser/hy3/_utils.py +++ b/hytek_parser/hy3/_utils.py @@ -1,3 +1,4 @@ +import math from typing import Optional, Union from hytek_parser._utils import safe_cast, select_from_enum @@ -56,6 +57,13 @@ def parse_reaction_time(raw: str) -> Optional[float]: except ValueError: # Observed malformed forms: a bare "+" sign, stray high bytes. return None + if not math.isfinite(num): + # float() accepts "nan"/"inf"/"-inf", all five characters or fewer, + # so they fit this column like any other token. Neither is a + # reaction time -- and a bare int() downstream would raise on them + # (ValueError on nan, OverflowError on inf) instead of yielding None + # like every other malformed token, dropping the whole file. + return None # float() maps "0.00", "+0.00" and "-0.00" all to zero; all three are the # "not recorded" sentinel. return None if num == 0.0 else num diff --git a/tests/hy3/test_utils.py b/tests/hy3/test_utils.py index ec66bfa..7756e0a 100644 --- a/tests/hy3/test_utils.py +++ b/tests/hy3/test_utils.py @@ -72,6 +72,22 @@ def test_implausible_value_is_passed_through(self) -> None: # unresolved. The parser reports what the file says; consumers decide. self.assertAlmostEqual(9.30, parse_reaction_time(" 9.30"), places=2) + def test_nan_is_none(self) -> None: + # float() accepts "nan" (5 characters, fits the column); a bare int() + # downstream would raise ValueError on it instead of yielding None. + self.assertIsNone(parse_reaction_time(" nan")) + + def test_negative_nan_is_none(self) -> None: + self.assertIsNone(parse_reaction_time(" -nan")) + + def test_inf_is_none(self) -> None: + # float() accepts "inf"; a bare int() downstream would raise + # OverflowError on it instead of yielding None. + self.assertIsNone(parse_reaction_time(" inf")) + + def test_negative_inf_is_none(self) -> None: + self.assertIsNone(parse_reaction_time(" -inf")) + if __name__=='__main__': unittest.main() From 7bcd57ff56a211c175c1c23557564545a1074694 Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Tue, 28 Jul 2026 10:39:25 -0700 Subject: [PATCH 11/12] docs(hy3): correct fixture provenance and E2 sentinel claims Two inaccuracies in the mm_relay_nrt_sentinel.hy3 documentation: - The fixtures README attributed the file to "Montana Swimming", but its A1 licensee and first C1 team both read Inland Empire; corrected to the LSC the file's own C1 region actually names. - Both the README and a test docstring claimed the E2 reaction column is "blank throughout". Measured: 25 of the 40 E2 rows carry a signed +0.00 sentinel and only 15 are blank. The test itself was already correct (both spellings parse to None); only the prose was wrong, and it matters here because this fixture is the only integration coverage of the +0.00 sentinel -- a maintainer trusting the blank claim could swap in a genuinely blank fixture and silently lose that coverage with a green suite. --- tests/hy3/fixtures/README.md | 2 +- tests/hy3/test_integration.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/hy3/fixtures/README.md b/tests/hy3/fixtures/README.md index 859c649..5ab4175 100644 --- a/tests/hy3/fixtures/README.md +++ b/tests/hy3/fixtures/README.md @@ -17,7 +17,7 @@ from publicly distributed meet results: | `mm_col77_division.hy3` | MM5 6.0Cc | 2015 State/Non-State Open 25 yd (Wisconsin Swimming) | E1 col-77 `meet_division` (`JV`); E2 `alt_time_code` (`A`, `K`); pad-vs-button divergence (pad=107.39 vs btn1=102.49); C1 `region` (`WI`) | | `mm_pad_button_divergence.hy3` | MM5 8.0Fd | 2025 MT HOT Tropical Meet (Montana Swimming) | clear pad-vs-button divergence (pad=36.26 vs result=75.29, half-pool touchpad); E2 `alt_time_code` (`A`); two LSC regions (MT, WY) | | `mm_reaction_times_dense.hy3` | MM5 8.0Gh | 2026 CA SCS Summer A/G Champs @ BREA (Southern California Swimming) | E2 `reaction_time` densely populated (35 of 37 rows, 0.53-0.90); F2 four-slot `reaction_times` fully populated, including a relay with three negative takeovers | -| `mm_relay_nrt_sentinel.hy3` | MM5 7.0Dd | 2019 Western Zone Age Group Championships (Montana Swimming) | F2 `NRT` sentinel on every takeover slot and a signed `+0.00` on every leadoff; E2 reaction column blank throughout | +| `mm_relay_nrt_sentinel.hy3` | MM5 7.0Dd | 2019 Western Zone Age Group Championships (Inland Empire Swimming) | F2 `NRT` sentinel on every takeover slot and a signed `+0.00` on every leadoff; E2 reaction column carries a signed `+0.00` on 25 of 40 rows, blank on the other 15 | ## Redaction diff --git a/tests/hy3/test_integration.py b/tests/hy3/test_integration.py index e28bee6..4086836 100644 --- a/tests/hy3/test_integration.py +++ b/tests/hy3/test_integration.py @@ -478,7 +478,9 @@ def test_every_relay_reaction_slot_is_none(self) -> None: self.assertEqual([None, None, None, None], quad) def test_individual_reaction_times_all_none(self) -> None: - """This generation left E2 col 83-87 blank throughout.""" + """This generation's E2 col 83-87 is a signed +0.00 sentinel on most + rows (25 of 40) and blank on the rest (15 of 40) -- never blank + throughout. Both spellings are "not recorded" and must parse to None.""" values = [ _slot_field(e, slot, "reaction_time") for _ev, e in _all_entries(self.meet, individual_only=True) From b7b2b21881ce9abdd0fd55da4ac784ddf46931ff Mon Sep 17 00:00:00 2001 From: Felipe Salum Date: Tue, 28 Jul 2026 10:39:27 -0700 Subject: [PATCH 12/12] chore: ignore uv.lock A library should not ship a lockfile; it was untracked but not gitignored, so a future git add -A could have pushed it into history. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 1d43ed4..6716a47 100644 --- a/.gitignore +++ b/.gitignore @@ -139,3 +139,6 @@ cython_debug/ # Hytek test files hytek-test-files/ + +# A library ships no lockfile +uv.lock