tunas 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tunas-0.1.0/.gitignore ADDED
@@ -0,0 +1,30 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Test / coverage
13
+ .pytest_cache/
14
+ .coverage
15
+ .coverage.*
16
+ htmlcov/
17
+
18
+ # Type checker / linter caches
19
+ .mypy_cache/
20
+ .ruff_cache/
21
+
22
+ # Editor / OS
23
+ .DS_Store
24
+ .idea/
25
+ .vscode/
26
+
27
+ # Agents
28
+ .antigravitycli
29
+ CLAUDE.md
30
+ GEMINI.md
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to `tunas` will be documented in this file. The format is
4
+ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
5
+ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] — Unreleased
8
+
9
+ Initial release of the `tunas` library, providing a parser and domain model for USA Swimming `.cl2` (Hy-Tek SDIF v3) files.
10
+
11
+ ### Added
12
+ - **Unified Entry Point:** `read_cl2(source, *, strict=False, encoding="cp1252", errors="replace") -> tuple[list[Meet], ParseReport]` accepts paths, directories, lists, or file-like objects.
13
+ - **Spec Coverage:** Parses every meet-results record type (`A0`–`G0`, `Z0`); qualifying-time records (`J0`–`J2`) surface as warnings.
14
+ - **Domain Model:** Dataclass graph covering `Meet`, `Club`, `Swimmer`, and the swim/result hierarchy (`Swim` → `IndividualSwim`, `RelaySwim`; `MeetResult` → `IndividualSwim`, `Relay`), as well as `Split`, `SwimmerContact`, `SwimmerRegistration`, `MeetHost`, `SourceFile`, `Time`, and `Event`. Swimmers expose a unified `swims` list.
15
+ - **Pure and Faithful Parsing:** Meets are self-contained. Swimmers are unified within a meet by member ID (`id_short`, falling back to `id_long`).
16
+ - **Zero Data Loss:** All entered swims are kept, including non-time outcomes (scratches, DQs, no-shows tracked via `ResultStatus`). Missing optional fields are set to `None`. Raw line contents are preserved on validation failures.
17
+ - **Structured Error Model:** Structural (M1) violations raise `ParseError`. Data quality (M2) violations emit warnings or raise in `strict` mode. `ParseReport` provides query helpers (`by_severity`, `warnings_for`) and aggregates counts.
18
+ - **Time Standards:** Offline lookup helpers (`qualifies_for`, `standard_time`, `all_qualified`) using bundled 2025–2028 motivational standards.
19
+ - **Type-hinted:** Fully type-hinted and marked `py.typed`.
20
+
tunas-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Andy Joe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tunas-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: tunas
3
+ Version: 0.1.0
4
+ Summary: USA Swimming meet result (.cl2 / SDIF v3) parser and analysis library
5
+ Project-URL: Homepage, https://github.com/ajoe2/tunas
6
+ Project-URL: Documentation, https://github.com/ajoe2/tunas/tree/main/docs
7
+ Project-URL: Repository, https://github.com/ajoe2/tunas
8
+ Project-URL: Issues, https://github.com/ajoe2/tunas/issues
9
+ Project-URL: Changelog, https://github.com/ajoe2/tunas/blob/main/CHANGELOG.md
10
+ Author-email: Andy Joe <ajoe2468@gmail.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: cl2,hy-tek,sdif,swim-meet,swimming,usa-swimming
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.12
23
+ Description-Content-Type: text/markdown
24
+
25
+ # tunas
26
+
27
+ [![PyPI](https://img.shields.io/pypi/v/tunas.svg)](https://pypi.org/project/tunas/)
28
+ [![Python](https://img.shields.io/pypi/pyversions/tunas.svg)](https://pypi.org/project/tunas/)
29
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
30
+
31
+ A Python library for parsing and analyzing USA Swimming meet result files (`.cl2` / Hy-Tek SDIF v3).
32
+
33
+ `tunas` parses `.cl2` files into clean, idiomatic Python objects (`Meet`, `Club`, `Swimmer`, `IndividualSwim`, `Relay`) and bundles USA Swimming time standards for offline qualifying-time lookups.
34
+
35
+ ## Install
36
+
37
+ ```
38
+ pip install tunas
39
+ ```
40
+
41
+ Requires Python 3.12+ and has zero third-party runtime dependencies.
42
+
43
+ ## Quick example
44
+
45
+ ```python
46
+ from tunas import read_cl2
47
+
48
+ meets, report = read_cl2("results.cl2")
49
+
50
+ for meet in meets:
51
+ print(f"{meet.name} ({meet.start_date})")
52
+ for swim in meet.individual_swims:
53
+ outcome = swim.time if swim.time is not None else swim.status.value # DQ / NS / ...
54
+ print(f" {swim.swimmer.full_name:<24} {swim.event.name:<16} {outcome}")
55
+
56
+ if report.warnings:
57
+ print(f"{len(report.warnings)} records flagged — see report.warnings")
58
+ ```
59
+
60
+ `read_cl2` parses files into self-contained `Meet` objects and a `ParseReport`. Swimmers are scoped to each meet; reconcile cross-meet swimmers by grouping on `id_short` or `id_long` in your application.
61
+
62
+ ## Features
63
+
64
+ - **SDIF v3 Parser:** Parses `.cl2` meet result files including relays (E0/F0) and splits (G0).
65
+ - **Self-Contained Meets:** No global state or cross-file merging; parsing is a pure function of input bytes.
66
+ - **Clean Object Model:** Slotted dataclasses with direct object references (swimmers, clubs, swims, splits, and contact/registration data).
67
+ - **Offline Time Standards:** Local lookup of USA Swimming B through AAAA motivational cuts.
68
+ - **Lenient Parsing:** Recovers from data-quality issues by default with a detailed `ParseReport`. Opt-in `strict=True` raises on the first violation.
69
+ - **Type-Safe:** Fully typed and carries the `py.typed` marker.
70
+
71
+ ## Documentation
72
+
73
+ Full documentation is available in [`docs/`](docs/):
74
+
75
+ - [Getting started](docs/getting_started.md)
76
+ - [Architecture](docs/architecture.md)
77
+ - [Parsing](docs/parsing.md) · [Models](docs/models.md) · [Time](docs/time.md) · [Event](docs/event.md) · [Enums](docs/enums.md) · [Exceptions](docs/exceptions.md) · [Standards](docs/standards.md)
78
+ - [CL2 / SDIF v3 format](docs/cl2_format.md) · [Cookbook](docs/cookbook.md)
79
+
80
+ ## Development
81
+
82
+ Managed with [`uv`](https://docs.astral.sh/uv/) (Python 3.12+):
83
+
84
+ ```bash
85
+ uv sync # set up the environment
86
+ uv run pytest # run the (offline, self-contained) test suite
87
+ uv run pytest --cov=tunas # with coverage (gated at 95%)
88
+ uv run ruff check && uv run mypy src/tunas # lint + type-check
89
+ ```
90
+
91
+ See [docs/architecture.md](docs/architecture.md#development) for the full
92
+ workflow and the release process.
93
+
94
+ ## Status
95
+
96
+ `tunas` is in **alpha**. The API documented in `docs/` is stable, but breaking changes may occur before 1.0 based on real-world feedback.
97
+
98
+ ## License
99
+
100
+ [MIT](LICENSE)
101
+
102
+
tunas-0.1.0/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # tunas
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/tunas.svg)](https://pypi.org/project/tunas/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/tunas.svg)](https://pypi.org/project/tunas/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+
7
+ A Python library for parsing and analyzing USA Swimming meet result files (`.cl2` / Hy-Tek SDIF v3).
8
+
9
+ `tunas` parses `.cl2` files into clean, idiomatic Python objects (`Meet`, `Club`, `Swimmer`, `IndividualSwim`, `Relay`) and bundles USA Swimming time standards for offline qualifying-time lookups.
10
+
11
+ ## Install
12
+
13
+ ```
14
+ pip install tunas
15
+ ```
16
+
17
+ Requires Python 3.12+ and has zero third-party runtime dependencies.
18
+
19
+ ## Quick example
20
+
21
+ ```python
22
+ from tunas import read_cl2
23
+
24
+ meets, report = read_cl2("results.cl2")
25
+
26
+ for meet in meets:
27
+ print(f"{meet.name} ({meet.start_date})")
28
+ for swim in meet.individual_swims:
29
+ outcome = swim.time if swim.time is not None else swim.status.value # DQ / NS / ...
30
+ print(f" {swim.swimmer.full_name:<24} {swim.event.name:<16} {outcome}")
31
+
32
+ if report.warnings:
33
+ print(f"{len(report.warnings)} records flagged — see report.warnings")
34
+ ```
35
+
36
+ `read_cl2` parses files into self-contained `Meet` objects and a `ParseReport`. Swimmers are scoped to each meet; reconcile cross-meet swimmers by grouping on `id_short` or `id_long` in your application.
37
+
38
+ ## Features
39
+
40
+ - **SDIF v3 Parser:** Parses `.cl2` meet result files including relays (E0/F0) and splits (G0).
41
+ - **Self-Contained Meets:** No global state or cross-file merging; parsing is a pure function of input bytes.
42
+ - **Clean Object Model:** Slotted dataclasses with direct object references (swimmers, clubs, swims, splits, and contact/registration data).
43
+ - **Offline Time Standards:** Local lookup of USA Swimming B through AAAA motivational cuts.
44
+ - **Lenient Parsing:** Recovers from data-quality issues by default with a detailed `ParseReport`. Opt-in `strict=True` raises on the first violation.
45
+ - **Type-Safe:** Fully typed and carries the `py.typed` marker.
46
+
47
+ ## Documentation
48
+
49
+ Full documentation is available in [`docs/`](docs/):
50
+
51
+ - [Getting started](docs/getting_started.md)
52
+ - [Architecture](docs/architecture.md)
53
+ - [Parsing](docs/parsing.md) · [Models](docs/models.md) · [Time](docs/time.md) · [Event](docs/event.md) · [Enums](docs/enums.md) · [Exceptions](docs/exceptions.md) · [Standards](docs/standards.md)
54
+ - [CL2 / SDIF v3 format](docs/cl2_format.md) · [Cookbook](docs/cookbook.md)
55
+
56
+ ## Development
57
+
58
+ Managed with [`uv`](https://docs.astral.sh/uv/) (Python 3.12+):
59
+
60
+ ```bash
61
+ uv sync # set up the environment
62
+ uv run pytest # run the (offline, self-contained) test suite
63
+ uv run pytest --cov=tunas # with coverage (gated at 95%)
64
+ uv run ruff check && uv run mypy src/tunas # lint + type-check
65
+ ```
66
+
67
+ See [docs/architecture.md](docs/architecture.md#development) for the full
68
+ workflow and the release process.
69
+
70
+ ## Status
71
+
72
+ `tunas` is in **alpha**. The API documented in `docs/` is stable, but breaking changes may occur before 1.0 based on real-world feedback.
73
+
74
+ ## License
75
+
76
+ [MIT](LICENSE)
77
+
78
+
@@ -0,0 +1,90 @@
1
+ [project]
2
+ name = "tunas"
3
+ dynamic = ["version"]
4
+ description = "USA Swimming meet result (.cl2 / SDIF v3) parser and analysis library"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Andy Joe", email = "ajoe2468@gmail.com" }]
10
+ keywords = ["swimming", "sdif", "cl2", "hy-tek", "usa-swimming", "swim-meet"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Topic :: Scientific/Engineering :: Information Analysis",
19
+ "Typing :: Typed",
20
+ ]
21
+ dependencies = []
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/ajoe2/tunas"
25
+ Documentation = "https://github.com/ajoe2/tunas/tree/main/docs"
26
+ Repository = "https://github.com/ajoe2/tunas"
27
+ Issues = "https://github.com/ajoe2/tunas/issues"
28
+ Changelog = "https://github.com/ajoe2/tunas/blob/main/CHANGELOG.md"
29
+
30
+ [build-system]
31
+ requires = ["hatchling"]
32
+ build-backend = "hatchling.build"
33
+
34
+ [tool.hatch.version]
35
+ path = "src/tunas/_version.py"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/tunas"]
39
+
40
+ [tool.hatch.build.targets.sdist]
41
+ include = [
42
+ "src/tunas",
43
+ "README.md",
44
+ "LICENSE",
45
+ "CHANGELOG.md",
46
+ "pyproject.toml",
47
+ ]
48
+
49
+ [dependency-groups]
50
+ dev = [
51
+ "pytest>=8",
52
+ "pytest-cov>=5",
53
+ "ruff>=0.6",
54
+ "mypy>=1.11",
55
+ "openpyxl>=3.1",
56
+ ]
57
+
58
+ [tool.pytest.ini_options]
59
+ testpaths = ["tests"]
60
+ addopts = "-ra --strict-markers"
61
+
62
+ [tool.coverage.run]
63
+ source = ["tunas"]
64
+
65
+ [tool.coverage.report]
66
+ # CI runs `pytest --cov=tunas`; this gates the result.
67
+ fail_under = 95
68
+ show_missing = true
69
+
70
+ [tool.ruff]
71
+ line-length = 100
72
+ target-version = "py312"
73
+ src = ["src"]
74
+
75
+ [tool.ruff.lint]
76
+ select = ["E", "F", "I", "B", "UP", "SIM"]
77
+
78
+ [tool.mypy]
79
+ python_version = "3.12"
80
+ strict = true
81
+ files = ["src/tunas"]
82
+
83
+ [tool.pyright]
84
+ # Lets Pylance resolve the project venv (pytest, etc.) and the tests/ helpers
85
+ # (conftest) that pytest puts on sys.path at runtime.
86
+ include = ["src", "tests", "scripts"]
87
+ extraPaths = ["tests"]
88
+ venvPath = "."
89
+ venv = ".venv"
90
+ pythonVersion = "3.12"
@@ -0,0 +1,110 @@
1
+ """tunas — USA Swimming meet result (.cl2 / SDIF v3) parser library.
2
+
3
+ Parses Hy-Tek SDIF v3 ``.cl2`` files into a clean, well-typed domain model and
4
+ bundles USA Swimming motivational time standards for offline lookups.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from tunas._version import __version__
10
+ from tunas.enums import (
11
+ Affiliation,
12
+ AttachStatus,
13
+ Citizenship,
14
+ Course,
15
+ Ethnicity,
16
+ EventTimeClass,
17
+ FileType,
18
+ MeetType,
19
+ MemberStatus,
20
+ Organization,
21
+ Region,
22
+ RelayLegOrder,
23
+ ResultStatus,
24
+ Season,
25
+ Session,
26
+ Sex,
27
+ SplitType,
28
+ Stroke,
29
+ )
30
+ from tunas.event import Event
31
+ from tunas.exceptions import ParseError, StandardsError, TunasError
32
+ from tunas.geography import LSC, Country, State
33
+ from tunas.models import (
34
+ Club,
35
+ ClubEntryCounts,
36
+ IndividualSwim,
37
+ Meet,
38
+ MeetHost,
39
+ MeetResult,
40
+ Relay,
41
+ RelaySwim,
42
+ SourceFile,
43
+ Split,
44
+ Swim,
45
+ Swimmer,
46
+ SwimmerContact,
47
+ SwimmerRegistration,
48
+ )
49
+ from tunas.parser import IssueKind, ParseReport, ParseWarning, Severity, read_cl2
50
+ from tunas.standards import TimeStandard, all_qualified, qualifies_for, standard_time
51
+ from tunas.time import Time
52
+
53
+ __all__ = [
54
+ "__version__",
55
+ # parsing
56
+ "read_cl2",
57
+ "ParseReport",
58
+ "ParseWarning",
59
+ "Severity",
60
+ "IssueKind",
61
+ # exceptions
62
+ "TunasError",
63
+ "ParseError",
64
+ "StandardsError",
65
+ # models
66
+ "Meet",
67
+ "Club",
68
+ "Swimmer",
69
+ "Swim",
70
+ "MeetResult",
71
+ "IndividualSwim",
72
+ "Relay",
73
+ "RelaySwim",
74
+ "Split",
75
+ "SwimmerContact",
76
+ "SwimmerRegistration",
77
+ "MeetHost",
78
+ "SourceFile",
79
+ "ClubEntryCounts",
80
+ # value types
81
+ "Time",
82
+ "Event",
83
+ # enums
84
+ "Sex",
85
+ "Stroke",
86
+ "Course",
87
+ "Session",
88
+ "AttachStatus",
89
+ "MeetType",
90
+ "Region",
91
+ "EventTimeClass",
92
+ "Organization",
93
+ "FileType",
94
+ "SplitType",
95
+ "ResultStatus",
96
+ "RelayLegOrder",
97
+ "MemberStatus",
98
+ "Season",
99
+ "Ethnicity",
100
+ "Affiliation",
101
+ "Citizenship",
102
+ "LSC",
103
+ "State",
104
+ "Country",
105
+ # standards
106
+ "TimeStandard",
107
+ "qualifies_for",
108
+ "standard_time",
109
+ "all_qualified",
110
+ ]
File without changes