Skip to main content

USA Swimming meet result (.cl2 / SDIF v3 and Hy-Tek .hy3) parser and analysis library

Project description

tunas

PyPI Python License: MIT

A Python library for parsing USA Swimming meet result files (.cl2 / SDIF v3 and Hy-Tek .hy3) into structured Python objects and performing offline qualifying time standard lookups.

📖 Documentation: https://ajoe2.github.io/tunas/

Installation

pip install tunas

Requires Python 3.12+. Currently, the runtime depends only on the Python standard library.

Quick Start

read_cl2 is the primary entry point. It parses file paths, directories, lists of paths, or text streams, returning a list of Meet objects and a ParseReport:

from tunas import read_cl2

meets, report = read_cl2("results.cl2")

for meet in meets:
    print(f"{meet.name} ({meet.start_date})")
    for swim in meet.individual_swims:
        outcome = swim.time if swim.time is not None else swim.status.value
        print(f"  {swim.swimmer.full_name:<24} {swim.event.name:<16} {outcome}")

if report.warnings:
    print(f"{len(report.warnings)} records flagged — inspect report.warnings")

Hy-Tek .hy3 results parse the same way via read_hy3, producing the identical Meet object graph:

from tunas import read_hy3

meets, report = read_hy3("Meet Results-Winter Champs-001.hy3")

Core Concepts

All parsed data is contained in independent Meet objects:

  • Meet: Owns swimmers, clubs, and results (accessible via meet.individual_swims and meet.relays, or filtered via meet.individual_swims_for(event) and meet.relays_for(event)). Carries metadata including name, dates, location, course, meet_type, host (MeetHost), and source_file (SourceFile for file-level provenance).
  • Swimmer: Scoped to one meet. Exposes full_name, swims, individual_swims, relay_swims, and swims_in(event). Includes identity (id_short/id_long), birthday, sex, citizenship, and optional contact/registration PII.
  • Club: Scoped to one meet and keyed by (team_code, lsc). Carries coach, entry_counts, address, and associated results and swimmers.
  • Swim: The uniform interface for individual swims (IndividualSwim) and relay legs (RelaySwim), exposing swimmer, time (Time or None), status (ResultStatus), session, event, date, meet, course, and splits. Scratches and disqualifications are preserved.
  • Event: A 90+ member enum of (distance, stroke, course), comparable in declaration order, with helpers (is_relay, leg_event, leg_strokes, Event.find). Filter with swimmer.swims_in(event) or meet.individual_swims_for(event).
  • Relays: Relay squads contain RelaySwim legs (legs) and alternates. Each leg reports its individual event, so it sorts alongside flat-start swims.
  • Time: Immutable centisecond value type — Time.parse("1:04.87"), ordering, addition/subtraction, and minute/second/hundredth/total_seconds accessors.
  • Split: Per-leg splits (distance, time, split_type) attach to the swim that produced them.
  • Scoping: Meets are independent and never merged; swimmers and clubs are scoped to their respective meet. Group by id_short (or id_long) to track athletes across meets.
from tunas import read_cl2, Event

meets, _ = read_cl2("season/")
for meet in meets:
    for swim in meet.swimmers[0].swims_in(Event.FREE_100_SCY):
        print(meet.name, swim.session.value, swim.time)

Offline Time Standards

USA Swimming motivational standards (B through AAAA, the bundled 2025–2028 cuts) are available locally with no setup or network access. Lookups are keyed by single-year age group (10 & under, 11-12, 13-14, 15-16, 17-18) and sex:

from tunas import qualifies_for, all_qualified, standard_time, Sex, Event, Time

# Fastest standard achieved (or None):
qualifies_for(Time.parse("1:05.23"), Event.FREE_100_SCY, age=12, sex=Sex.FEMALE)
# → TimeStandard.BB

# Every standard met, slowest first:
all_qualified(Time.parse("1:05.23"), Event.FREE_100_SCY, age=12, sex=Sex.FEMALE)
# → [TimeStandard.B, TimeStandard.BB]

# The cutoff time for a given standard (or None if undefined):
standard_time(TimeStandard.AAAA, Event.FREE_100_SCY, age=12, sex=Sex.FEMALE)
# → Time(...)

Standards are defined for MALE/FEMALE only; passing Sex.MIXED raises ValueError.

Error Handling

Parsing is lenient by default to recover from common exporter bugs; warnings are collected in a ParseReport.

meets, report = read_cl2("messy/")
for w in report.warnings:
    print(f"{w.source}:{w.line_no} [{w.severity.value}] {w.record_type}: {w.reason}")

Use strict=True to fail fast and raise ParseError on the first warning. Structural violations always raise.

Features

  • Complete SDIF v3 coverage: Parses every meet-results record type (A0G0, Z0), including relays, relay alternates, and per-leg splits. Registration (D1/D2) and demographic (D3) records populate optional PII fields; qualifying-time records (J0J2) surface as warnings.
  • Hy-Tek .hy3 support: read_hy3 parses the reverse-engineered .hy3 results format (confirmed fields only) into the same Meet object graph, capturing data SDIF omits — disqualification codes/reasons, converted seed times, and meet sanction numbers.
  • Clean object model: Slotted dataclasses with pre-wired object references (including back-references) and zero global state. Value types (Time, Split, MeetHost, …) are frozen and hashable.
  • Zero data loss: All entered swims are kept — including non-time outcomes (scratches, DQs, no-shows via ResultStatus). Missing optional fields become None; raw line contents are preserved on validation failures.
  • Lenient by default, strict on demand: Recovers from common exporter bugs and reports each issue as a structured ParseWarning (with severity, kind, column, and raw line); strict=True fails fast on the first problem.
  • Offline standards: Local O(1) lookup of USA Swimming B through AAAA motivational cuts, bundled as JSON — no setup or network.
  • Robust decoding: Defaults to CP-1252 (to preserve column alignment and accented names), tolerates BOMs, short/long lines, and mixed line endings.
  • Parallel execution: Deterministic concurrent parsing of multiple files on a thread pool (max_workers), with output identical to the sequential default.
  • Type-safe: Fully type-hinted and marked py.typed; passes mypy --strict.

Documentation

Full docs site: https://ajoe2.github.io/tunas/

Development

Managed with uv:

uv sync                                      # Setup environment (incl. dev deps)
uv run pytest                                # Run offline test suite
uv run pytest --cov=tunas                    # Run with coverage check (95% gate)
uv run ruff check && uv run ruff format      # Lint and format
uv run mypy src/tunas                        # Type-check (strict)
uv run mkdocs serve                          # Preview the docs site locally

The test suite is fully self-contained and offline — real-world coverage comes from committed "golden" .cl2 files plus hand-verified expected-state JSON under tests/data/, so no data download is needed.

Status

tunas is in alpha. The public API is stable, but subject to revision before 1.0.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tunas-0.2.0.tar.gz (81.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tunas-0.2.0-py3-none-any.whl (88.1 kB view details)

Uploaded Python 3

File details

Details for the file tunas-0.2.0.tar.gz.

File metadata

  • Download URL: tunas-0.2.0.tar.gz
  • Upload date:
  • Size: 81.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tunas-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f37cd160ba158eda39609075af2e03c4d16a89094a94309dcd75c653db894530
MD5 c192db2a407d619d531caa74ad9d7b93
BLAKE2b-256 4ccad1b62d89afca9a77f5a916d38014b0661a9bc52a1e33e4e2ffe75e471401

See more details on using hashes here.

Provenance

The following attestation bundles were made for tunas-0.2.0.tar.gz:

Publisher: publish.yml on ajoe2/tunas

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tunas-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: tunas-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 88.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tunas-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6ed7da867f191cc7c9d904190c97eac9b3c3fe26d24e8f779d37b371931cd7f0
MD5 c7e3cd2d5d3c2d0f913dee70384d205e
BLAKE2b-256 363a25cd4beaf7e77da6f5e64d23a9297d4eb980c8bea521baba8c552c5c095d

See more details on using hashes here.

Provenance

The following attestation bundles were made for tunas-0.2.0-py3-none-any.whl:

Publisher: publish.yml on ajoe2/tunas

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page