nrcd 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.
Files changed (69) hide show
  1. nrcd-0.1.0/.github/workflows/ci.yml +42 -0
  2. nrcd-0.1.0/.github/workflows/publish.yml +25 -0
  3. nrcd-0.1.0/.gitignore +25 -0
  4. nrcd-0.1.0/CHANGELOG.md +13 -0
  5. nrcd-0.1.0/CITATION.cff +36 -0
  6. nrcd-0.1.0/LICENSE +21 -0
  7. nrcd-0.1.0/PKG-INFO +342 -0
  8. nrcd-0.1.0/README.md +308 -0
  9. nrcd-0.1.0/data/README.md +7 -0
  10. nrcd-0.1.0/docs/API_KEYS.md +153 -0
  11. nrcd-0.1.0/examples/README.md +41 -0
  12. nrcd-0.1.0/examples/compare_improvement.py +37 -0
  13. nrcd-0.1.0/examples/load_dataset_example.py +69 -0
  14. nrcd-0.1.0/examples/race_context_example.py +68 -0
  15. nrcd-0.1.0/examples/standardize_one_result.py +28 -0
  16. nrcd-0.1.0/examples/track_compare_meets.py +76 -0
  17. nrcd-0.1.0/examples/track_indoor_examples.py +87 -0
  18. nrcd-0.1.0/examples/track_outdoor_examples.py +151 -0
  19. nrcd-0.1.0/examples/xc_examples.py +123 -0
  20. nrcd-0.1.0/local_api_keys.env.example +27 -0
  21. nrcd-0.1.0/pyproject.toml +73 -0
  22. nrcd-0.1.0/src/nrcd/__init__.py +28 -0
  23. nrcd-0.1.0/src/nrcd/data/__init__.py +16 -0
  24. nrcd-0.1.0/src/nrcd/data/schema.py +94 -0
  25. nrcd-0.1.0/src/nrcd/enrich/__init__.py +53 -0
  26. nrcd-0.1.0/src/nrcd/enrich/altitude.py +138 -0
  27. nrcd-0.1.0/src/nrcd/enrich/api_usage.py +67 -0
  28. nrcd-0.1.0/src/nrcd/enrich/batch.py +86 -0
  29. nrcd-0.1.0/src/nrcd/enrich/cache.py +97 -0
  30. nrcd-0.1.0/src/nrcd/enrich/config.py +37 -0
  31. nrcd-0.1.0/src/nrcd/enrich/context.py +163 -0
  32. nrcd-0.1.0/src/nrcd/enrich/geocode.py +63 -0
  33. nrcd-0.1.0/src/nrcd/enrich/guide.py +81 -0
  34. nrcd-0.1.0/src/nrcd/enrich/http.py +28 -0
  35. nrcd-0.1.0/src/nrcd/enrich/throttle.py +32 -0
  36. nrcd-0.1.0/src/nrcd/enrich/timezone_lookup.py +59 -0
  37. nrcd-0.1.0/src/nrcd/enrich/weather.py +296 -0
  38. nrcd-0.1.0/src/nrcd/py.typed +0 -0
  39. nrcd-0.1.0/src/nrcd/standardize/__init__.py +156 -0
  40. nrcd-0.1.0/src/nrcd/standardize/altitude.py +283 -0
  41. nrcd-0.1.0/src/nrcd/standardize/config.py +58 -0
  42. nrcd-0.1.0/src/nrcd/standardize/context.py +187 -0
  43. nrcd-0.1.0/src/nrcd/standardize/events.py +92 -0
  44. nrcd-0.1.0/src/nrcd/standardize/factors.py +94 -0
  45. nrcd-0.1.0/src/nrcd/standardize/grade.py +80 -0
  46. nrcd-0.1.0/src/nrcd/standardize/pipeline.py +502 -0
  47. nrcd-0.1.0/src/nrcd/standardize/reference.py +340 -0
  48. nrcd-0.1.0/src/nrcd/standardize/sport.py +50 -0
  49. nrcd-0.1.0/src/nrcd/standardize/time.py +65 -0
  50. nrcd-0.1.0/src/nrcd/standardize/track.py +179 -0
  51. nrcd-0.1.0/src/nrcd/standardize/units.py +164 -0
  52. nrcd-0.1.0/src/nrcd/standardize/validation.py +53 -0
  53. nrcd-0.1.0/src/nrcd/standardize/wind.py +132 -0
  54. nrcd-0.1.0/tests/conftest.py +7 -0
  55. nrcd-0.1.0/tests/live_api_config.py +109 -0
  56. nrcd-0.1.0/tests/test_altitude_barometric.py +52 -0
  57. nrcd-0.1.0/tests/test_enrich.py +139 -0
  58. nrcd-0.1.0/tests/test_enrich_api_usage.py +30 -0
  59. nrcd-0.1.0/tests/test_enrich_batch.py +23 -0
  60. nrcd-0.1.0/tests/test_enrich_cache_throttle.py +65 -0
  61. nrcd-0.1.0/tests/test_enrich_live_openweather.py +148 -0
  62. nrcd-0.1.0/tests/test_events.py +68 -0
  63. nrcd-0.1.0/tests/test_grade_warning.py +49 -0
  64. nrcd-0.1.0/tests/test_live_api_config.py +24 -0
  65. nrcd-0.1.0/tests/test_nrcd_schema.py +34 -0
  66. nrcd-0.1.0/tests/test_packaging.py +24 -0
  67. nrcd-0.1.0/tests/test_sport_and_docs.py +104 -0
  68. nrcd-0.1.0/tests/test_standardize.py +244 -0
  69. nrcd-0.1.0/tests/test_standardize_result.py +41 -0
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - name: Install package and test deps
21
+ run: |
22
+ python -m pip install --upgrade pip
23
+ pip install -e ".[dev]"
24
+ - name: Ruff
25
+ run: ruff check src tests examples
26
+ - name: Run tests
27
+ run: pytest -q
28
+ - name: Smoke-run examples
29
+ run: |
30
+ for script in \
31
+ examples/xc_examples.py \
32
+ examples/track_outdoor_examples.py \
33
+ examples/track_indoor_examples.py \
34
+ examples/track_compare_meets.py \
35
+ examples/compare_improvement.py \
36
+ examples/race_context_example.py \
37
+ examples/standardize_one_result.py; do
38
+ python "$script"
39
+ done
40
+ # load_dataset_example.py requires optional Zenodo CSVs (pip install "nrcd[data]")
41
+ - name: Build wheel
42
+ run: pip install build && python -m build
@@ -0,0 +1,25 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.12"
19
+ - name: Build package
20
+ run: |
21
+ python -m pip install --upgrade pip
22
+ pip install build twine
23
+ python -m build
24
+ twine check dist/*
25
+ - uses: pypa/gh-action-pypi-publish@release/v1
nrcd-0.1.0/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ # Dataset (download from Zenodo; see data/README.md)
2
+ data/*.csv
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+ .Python
9
+ .env
10
+ local_api_keys.env
11
+ .venv
12
+ venv/
13
+ ENV/
14
+ dist/
15
+ build/
16
+ *.egg-info/
17
+
18
+ # Test / tooling
19
+ .pytest_cache/
20
+ .mypy_cache/
21
+
22
+ # OS / editor
23
+ .DS_Store
24
+ .idea/
25
+ .vscode/
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-06-06
4
+
5
+ Initial release.
6
+
7
+ - Sport-specific entry points — `standardize_xc`, `standardize_road`, `standardize_outdoor_track`, `standardize_indoor_track` (plus low-level `standardize_result`)
8
+ - Clock-string times and flexible distance/units (m, km, mi; °F/°C; ft/m)
9
+ - `nrcd.enrich` — optional weather and meet-altitude API backfill
10
+ - `nrcd.data` — Zenodo CSV helpers (`pip install "nrcd[data]"`)
11
+ - Examples, docs, and tests
12
+
13
+ [0.1.0]: https://github.com/National-Running-Club-Database/nrcd/releases/tag/v0.1.0
@@ -0,0 +1,36 @@
1
+ cff-version: 1.2.0
2
+ title: "nrcd: NRCD performance standardization library"
3
+ message: "If you use this software or the NRCD standardization formulas, please cite the paper."
4
+ type: software
5
+ version: 0.1.0
6
+ date-released: 2026-06-06
7
+ license: MIT
8
+ repository-code: "https://github.com/National-Running-Club-Database/nrcd"
9
+ url: "https://github.com/National-Running-Club-Database/nrcd"
10
+ authors:
11
+ - family-names: Karr
12
+ given-names: Jonathan A.
13
+ name-particle: Jr
14
+ - family-names: Fryer
15
+ given-names: Ryan M.
16
+ - family-names: Darden
17
+ given-names: Ben
18
+ - family-names: Pell
19
+ given-names: Nicholas
20
+ - family-names: Ambrose
21
+ given-names: Kayla
22
+ - family-names: Hall
23
+ given-names: Evan
24
+ - family-names: Bualuan
25
+ given-names: Ramzi K.
26
+ - family-names: Chawla
27
+ given-names: Nitesh V.
28
+ preferred-citation:
29
+ type: article
30
+ title: "NRCD: An Open Database of Collegiate Running with Unified Performance Standardization"
31
+ year: 2026
32
+ notes: "arXiv preprint forthcoming"
33
+ identifiers:
34
+ - type: url
35
+ value: "https://zenodo.org/records/17917357"
36
+ description: "NRCD dataset export (optional)"
nrcd-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 National Running Club Database
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.
nrcd-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,342 @@
1
+ Metadata-Version: 2.4
2
+ Name: nrcd
3
+ Version: 0.1.0
4
+ Summary: NRCD performance standardization formulas (cross country, track, road)
5
+ Project-URL: Homepage, https://github.com/National-Running-Club-Database/nrcd
6
+ Project-URL: Repository, https://github.com/National-Running-Club-Database/nrcd
7
+ Project-URL: Documentation, https://github.com/National-Running-Club-Database/nrcd#readme
8
+ Project-URL: Bug Tracker, https://github.com/National-Running-Club-Database/nrcd/issues
9
+ Project-URL: Dataset, https://zenodo.org/records/17917357
10
+ Author: Ryan M. Fryer, Ben Darden, Nicholas Pell, Kayla Ambrose, Evan Hall, Ramzi K. Bualuan, Nitesh V. Chawla
11
+ Author-email: "Jonathan A. Karr Jr" <jkarr@nd.edu>
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: cross-country,running,sports-analytics,standardization
15
+ Classifier: Development Status :: 3 - Alpha
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering
23
+ Requires-Python: >=3.10
24
+ Provides-Extra: apis
25
+ Requires-Dist: requests>=2.28.0; extra == 'apis'
26
+ Provides-Extra: data
27
+ Requires-Dist: pandas>=2.0.0; extra == 'data'
28
+ Provides-Extra: dev
29
+ Requires-Dist: pandas>=2.0.0; extra == 'dev'
30
+ Requires-Dist: pytest>=7.0; extra == 'dev'
31
+ Requires-Dist: requests>=2.28.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # nrcd
36
+
37
+ Python library for **National Running Club Database (NRCD)** performance standardization (cross country, track, road). Implements the formulas documented in the NRCD resource paper.
38
+
39
+ [![GitHub](https://img.shields.io/badge/GitHub-National--Running--Club--Database%2Fnrcd-181717?logo=github)](https://github.com/National-Running-Club-Database/nrcd)
40
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue?logo=python&logoColor=white)](https://www.python.org/downloads/)
41
+ [![PyPI](https://img.shields.io/badge/PyPI-nrcd-3775A9?logo=pypi&logoColor=white)](https://pypi.org/project/nrcd/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
43
+ [![CI](https://github.com/National-Running-Club-Database/nrcd/actions/workflows/ci.yml/badge.svg)](https://github.com/National-Running-Club-Database/nrcd/actions/workflows/ci.yml)
44
+
45
+ Use this to **standardize your own race results** — no NRCD dataset download required.
46
+
47
+ ## Quick start
48
+
49
+ Times can be **seconds** or clock strings (`"22:15"`, `"4:12.00"`, `"10.52"`). XC distances accept **m/km/mi** or labels like `"5k"`. Invalid time, distance, or unit strings raise **ValueError** with a short hint (they do not return NaN silently).
50
+
51
+ **Cross country — 5K in 22:15**
52
+
53
+ ```python
54
+ from nrcd import standardize_xc # or: from nrcd.standardize import standardize_xc
55
+
56
+ std = standardize_xc("22:15", gender="M", reported_distance="5k")
57
+ ```
58
+
59
+ **Cross country — 8 km with weather, grade, altitude**
60
+
61
+ ```python
62
+ std = standardize_xc(
63
+ "27:30",
64
+ gender="M",
65
+ reported_distance=8,
66
+ distance_unit="km",
67
+ actual_distance=8.01,
68
+ temperature=72, # °F (default)
69
+ dew_point=65,
70
+ elevation_gain=2.5, # % grade (default)
71
+ elevation_loss=2.5,
72
+ meet_elevation=5200, # feet (default)
73
+ )
74
+ ```
75
+
76
+ **Outdoor track — 100m with wind**
77
+
78
+ ```python
79
+ from nrcd import standardize_outdoor_track
80
+
81
+ std = standardize_outdoor_track(
82
+ "13.52",
83
+ gender="F",
84
+ event_name="100m",
85
+ wind_mps=2.0,
86
+ )
87
+ ```
88
+
89
+ **Indoor track — 200m on a banked 200m oval**
90
+
91
+ ```python
92
+ from nrcd import standardize_indoor_track
93
+
94
+ std = standardize_indoor_track(
95
+ "21.80",
96
+ gender="M",
97
+ event_name="200m",
98
+ lap_length_m=200,
99
+ banked=True,
100
+ )
101
+ ```
102
+
103
+ **Road — half marathon with weather and grade**
104
+
105
+ ```python
106
+ from nrcd import standardize_road
107
+
108
+ std = standardize_road(
109
+ "1:25:30",
110
+ gender="F",
111
+ event_name="Half Marathon",
112
+ temperature=55,
113
+ dew_point=48,
114
+ elevation_gain=1.2,
115
+ elevation_loss=1.2,
116
+ )
117
+ ```
118
+
119
+ ### Pipelines
120
+
121
+
122
+ | Sport | Function | What differs |
123
+ | ------------- | --------------------------- | --------------------------------------------------------------------------------------- |
124
+ | Cross country | `standardize_xc` | Weather, grade, altitude, then **Riegel to NIRCA 8k/6k** |
125
+ | Road | `standardize_road` | Same weather / grade / altitude as XC; distance from `event_name`; **no Riegel target** |
126
+ | Outdoor track | `standardize_outdoor_track` | Sprint **wind**, weather, grade, altitude |
127
+ | Indoor track | `standardize_indoor_track` | **Lap/bank venue** factors; no wind |
128
+
129
+
130
+ `standardize_result` remains available when you need a custom `sport_name` string.
131
+
132
+ **Unit switches** (optional kwargs on all pipelines):
133
+
134
+
135
+ | Field | Default | Alternative |
136
+ | ---------------------------------- | ----------- | ----------------------------- |
137
+ | `temperature`, `dew_point` | **°F** | `temp_unit="C"` |
138
+ | `meet_elevation` | **feet** | `venue_elevation_unit="m"` |
139
+ | `elevation_gain`, `elevation_loss` | **% grade** | `grade_input="feet"` or `"m"` |
140
+
141
+
142
+ ### More examples
143
+
144
+ Runnable scripts in [examples/](examples/):
145
+
146
+
147
+ | | Script |
148
+ | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
149
+ | XC | [xc_examples.py](examples/xc_examples.py), [compare_improvement.py](examples/compare_improvement.py), [standardize_one_result.py](examples/standardize_one_result.py) |
150
+ | Track | [track_outdoor_examples.py](examples/track_outdoor_examples.py), [track_indoor_examples.py](examples/track_indoor_examples.py), [track_compare_meets.py](examples/track_compare_meets.py) |
151
+ | Both | [race_context_example.py](examples/race_context_example.py), [load_dataset_example.py](examples/load_dataset_example.py) |
152
+
153
+
154
+ `load_dataset_example.py` exits with code 1 if Zenodo CSVs are not in `data/` — that is expected without the optional dataset.
155
+
156
+ ```bash
157
+ pip install nrcd
158
+ python examples/xc_examples.py
159
+ python examples/track_outdoor_examples.py
160
+ ```
161
+
162
+ ## Install
163
+
164
+ Requires Python 3.10+.
165
+
166
+ ```bash
167
+ pip install nrcd # after first PyPI release
168
+ pip install "nrcd[apis]" # optional: weather / elevation APIs
169
+ pip install "nrcd[data]" # optional: Zenodo CSV helpers (pandas)
170
+ ```
171
+
172
+ Until the package is on PyPI, install from source:
173
+
174
+ ```bash
175
+ git clone https://github.com/National-Running-Club-Database/nrcd
176
+ cd nrcd
177
+ pip install -e .
178
+ ```
179
+
180
+ From source (development):
181
+
182
+ ```bash
183
+ git clone https://github.com/National-Running-Club-Database/nrcd
184
+ cd nrcd
185
+ pip install -e ".[dev]"
186
+ pip install -e ".[dev,apis]"
187
+ ```
188
+
189
+ ### API keys (optional — `nrcd.enrich` only)
190
+
191
+ Standardization does **not** need API keys. Use enrichment only when backfilling **weather** or **meet altitude** from city/state.
192
+
193
+ 1. `pip install "nrcd[apis]"`
194
+ 2. [![OpenWeather](https://img.shields.io/badge/OpenWeather-sign%20up-EE7023?logo=openweathermap)](https://home.openweathermap.org/users/sign_up) → [![API keys](https://img.shields.io/badge/OpenWeather-API%20keys-EE7023?logo=openweathermap)](https://home.openweathermap.org/api_keys) → set `NRCD_OPENWEATHER_API_KEY`
195
+ 3. For **weather** at a race date/time, also [![TimeZoneDB](https://img.shields.io/badge/TimeZoneDB-register-0066CC)](https://timezonedb.com/register) → `NRCD_TIMEZONE_API_KEY`
196
+ 4. **Meet altitude** uses free USGS EPQS (no key) after OpenWeather geocodes the city
197
+
198
+ ```bash
199
+ export NRCD_OPENWEATHER_API_KEY="your_key"
200
+ export NRCD_TIMEZONE_API_KEY="your_key" # weather only
201
+ ```
202
+
203
+ **Live tests (optional):** copy `local_api_keys.env.example` → `local_api_keys.env` (gitignored), add your OpenWeather key, then `pytest -m live_api -v`. AQI history starts **2020-11-27**; default test date is **2024-10-12**.
204
+
205
+ **Full walkthrough:** [![API keys guide](https://img.shields.io/badge/docs-API__KEYS-0366d6?logo=readthedocs)](docs/API_KEYS.md). In Python: `from nrcd.enrich import API_GUIDE; print(API_GUIDE)`.
206
+
207
+ Historical OpenWeather timemachine weather may require a **paid** OpenWeather plan; geocoding + USGS altitude often work on the free tier.
208
+
209
+ ## Do you need the NRCD dataset?
210
+
211
+
212
+ | Use case | Zenodo CSVs needed? |
213
+ | --------------------------------------- | ------------------- |
214
+ | Standardize your own results | **No** |
215
+ | `examples/` scripts | **No** |
216
+ | `nrcd.enrich` API backfill | **No** |
217
+ | `examples/load_dataset_example.py` only | **Yes** (optional) |
218
+
219
+
220
+ Optional public export: [![Zenodo dataset](https://img.shields.io/badge/Zenodo-NRCD%20dataset-1682D4?logo=zenodo)](https://zenodo.org/records/17917357) (see [![data/README.md](https://img.shields.io/badge/data-README-lightgrey)](data/README.md)).
221
+
222
+ ## API
223
+
224
+ Full parameter tables: `from nrcd import PARAMETERS_DOC` or `help(nrcd.standardize)`.
225
+
226
+ ### Entry points
227
+
228
+
229
+ | Function | Use for |
230
+ | --------------------------- | ----------------------------------------------------------------------- |
231
+ | `standardize_xc` | Cross country — distance in m/km/mi; Riegel to NIRCA targets |
232
+ | `standardize_road` | Road / marathon — `event_name`; weather, grade, altitude |
233
+ | `standardize_outdoor_track` | Outdoor track — `event_name`; wind on sprints |
234
+ | `standardize_indoor_track` | Indoor track — `event_name`; lap length / banking |
235
+ | `standardize_result` | Low-level — any `sport_name` (advanced) |
236
+ | `standardize_seconds` | Dispatch from a `RaceContext` / `XCRaceContext` row |
237
+ | `enrich_race_context` | Fill missing weather/altitude on a context (`pip install "nrcd[apis]"`) |
238
+
239
+
240
+ ### `nrcd.standardize` — pipelines & context
241
+
242
+
243
+ | Name | Description |
244
+ | ---------------------------------------------------------------- | ---------------------------------------------------------------- |
245
+ | `RaceContext` | Dataclass for one result (XC or track); `time_str` or `time_sec` |
246
+ | `XCRaceContext` | XC-focused `RaceContext` subclass |
247
+ | `StandardizeConfig` | Paper coefficients (Riegel exponents, heat k, grade bases, …) |
248
+ | `PARAMETERS_DOC` | Full required/optional parameter reference (text) |
249
+ | `PARAMETER_SPECS` | Same metadata as structured `ParameterSpec` tuples |
250
+ | `parameter_specs()` | Return `PARAMETER_SPECS` |
251
+ | `required_for("xc" | "road" | "outdoor_track" | "indoor_track")` | Minimal field names per pipeline |
252
+
253
+
254
+ ### `nrcd.standardize` — time, distance, units
255
+
256
+
257
+ | Name | Description |
258
+ | ---------------------------------- | ----------------------------------------------------------------- |
259
+ | `parse_time` | `"22:15"`, `"1:10:13"`, or seconds → float seconds |
260
+ | `format_time` | Seconds → clock string |
261
+ | `parse_distance` | `"5k"`, `8`, `"8000m"` + unit → meters |
262
+ | `distance_to_meters` | Numeric distance with `distance_unit` (`m` / `km` / `mi`) |
263
+ | `c_to_f`, `f_to_c` | Temperature conversion |
264
+ | `feet_to_meters`, `meters_to_feet` | Length conversion |
265
+ | `temperature_to_fahrenheit` | Value + `temp_unit` (`F` / `C`) → °F |
266
+ | `venue_elevation_to_feet` | Meet altitude + `venue_elevation_unit` (`ft` / `m`) → ft |
267
+ | `grade_percent_from_feet` | Vertical ft over course → % grade |
268
+ | `grade_percent_from_meters` | Vertical m over course → % grade |
269
+ | `resolve_grade_percent` | Normalize gain/loss with `grade_input` (`percent` / `feet` / `m`) |
270
+
271
+
272
+ ### `nrcd.standardize` — factors & sport helpers
273
+
274
+
275
+ | Name | Description |
276
+ | --------------------------------------------------------------------- | ---------------------------------------------------------- |
277
+ | `weather_factor` | Heat slowdown multiplier from temp + dew point (°F) |
278
+ | `heat_index`, `heat_slowdown_percent` | Hadley-style heat index components |
279
+ | `elevation_factor` | Maurer grade multiplier from % gain/loss |
280
+ | `apply_course_grade_factor` | Grade step on a race time |
281
+ | `warn_one_sided_course_grade` | Warn when only gain or only loss is set |
282
+ | `apply_meet_altitude` | Peronnet meet-altitude correction |
283
+ | `peronnet_f_alt`, `sea_level_time_seconds` | Altitude factor and sea-level equivalent |
284
+ | `resolve_meet_altitude_inputs` | Parse elevation + barometric pressure from a record |
285
+ | `barometric_pressure_hpa_from_record` | hPa from row fields |
286
+ | `barometric_pressure_torr_from_hpa`, `parse_barometric_pressure_hpa` | Pressure unit helpers |
287
+ | `riegel_convert`, `riegel_exponent` | Distance conversion |
288
+ | `xc_target_distance_m` | NIRCA reference XC distance (8000 M / 6000 F) |
289
+ | `apply_factors` | Multiply time by weather/grade/altitude/track/wind factors |
290
+ | `is_cross_country`, `is_track`, `is_outdoor_track`, `is_indoor_track` | Sport name checks |
291
+ | `normalize_sport_name`, `pipeline_kind` | Sport string normalization / `xc` vs `track` |
292
+
293
+
294
+ ### `nrcd.data` — Zenodo CSV helpers
295
+
296
+
297
+ | Name | Description |
298
+ | ------------------------------ | ----------------------------------------------------- |
299
+ | `meet_altitude_column` | Altitude column name in a meet DataFrame |
300
+ | `meet_altitude_ft_from_record` | Meet altitude (ft) from a merged row + course details |
301
+ | `derive_course_details_fields` | Derived weather/grade fields from course details |
302
+
303
+
304
+ ### `nrcd.enrich` — optional APIs (`pip install "nrcd[apis]"`)
305
+
306
+
307
+ | Name | Description |
308
+ | --------------------------------------------------------------------- | ------------------------------------------------------------------------- |
309
+ | `API_GUIDE` | Full signup guide (text); see also [![API keys guide](https://img.shields.io/badge/docs-API__KEYS-0366d6?logo=readthedocs)](docs/API_KEYS.md) |
310
+ | `EnrichConfig` | Throttle intervals, cache TTL, API keys |
311
+ | `api_keys_from_env` | Load keys from environment |
312
+ | `fetch_weather` | Temperature, dew point, humidity, AQI for city/state + date |
313
+ | `lookup_altitude_ft`, `lookup_altitude_detail`, `lookup_elevation_ft` | Meet altitude (USGS EPQS, ft) |
314
+ | `enrich_race_context`, `enrich_race_context_result` | Backfill a `RaceContext` in place |
315
+ | `run_enrich_jobs`, `EnrichJob`, `JobResult` | Batch enrichment with thread pool |
316
+ | `EnrichResult`, `ApiUsage`, `WeatherData`, `AltitudeResult` | Result / usage dataclasses |
317
+ | `cache_stats`, `clear_enrich_cache`, `reset_throttle_state` | Cache and rate-limit controls |
318
+ | `AQI_HISTORY_AVAILABLE_FROM`, `AQI_HISTORY_AVAILABLE_UNIX` | AQI history window constants |
319
+
320
+
321
+ ## Citation
322
+
323
+ > **NRCD: An Open Database of Collegiate Running with Unified Performance Standardization**
324
+ > Jonathan A. Karr Jr, Ryan M. Fryer, Ben Darden, Nicholas Pell, Kayla Ambrose, Evan Hall, Ramzi K. Bualuan, and Nitesh V. Chawla.
325
+ > arXiv preprint (forthcoming).
326
+
327
+ Dataset (if using Zenodo export): [![Zenodo dataset](https://img.shields.io/badge/Zenodo-NRCD%20dataset-1682D4?logo=zenodo)](https://zenodo.org/records/17917357)
328
+
329
+ ## Author
330
+
331
+ [![Jonathan Karr ORCID](https://img.shields.io/badge/Jonathan%20Karr-ORCID-a6ce39?logo=orcid)](https://orcid.org/0009-0000-1600-6122)
332
+ [![Email](https://img.shields.io/badge/jkarr%40nd.edu-Email-D14836?logo=gmail)](mailto:jkarr@nd.edu)
333
+
334
+ ## Development
335
+
336
+ ```bash
337
+ pytest
338
+ ```
339
+
340
+ See [![Changelog](https://img.shields.io/badge/Changelog-CHANGELOG.md-blue)](CHANGELOG.md). Package version: [![src/nrcd/__init__.py](https://img.shields.io/badge/version-src%2Fnrcd%2F__init__.py-lightgrey)](src/nrcd/__init__.py) (`__version__`).
341
+
342
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)