endurancepy 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.
- endurancepy-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- endurancepy-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- endurancepy-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +32 -0
- endurancepy-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +29 -0
- endurancepy-0.1.0/.github/workflows/ci.yml +47 -0
- endurancepy-0.1.0/.github/workflows/integration.yml +23 -0
- endurancepy-0.1.0/.github/workflows/release.yml +47 -0
- endurancepy-0.1.0/.gitignore +75 -0
- endurancepy-0.1.0/CHANGELOG.md +143 -0
- endurancepy-0.1.0/CODE_OF_CONDUCT.md +52 -0
- endurancepy-0.1.0/CONTRIBUTING.md +129 -0
- endurancepy-0.1.0/LICENSE +21 -0
- endurancepy-0.1.0/PKG-INFO +240 -0
- endurancepy-0.1.0/README.md +177 -0
- endurancepy-0.1.0/RELEASING.md +40 -0
- endurancepy-0.1.0/docs/analyse_fastf1.md +757 -0
- endurancepy-0.1.0/docs/plan_implementation.md +398 -0
- endurancepy-0.1.0/docs/usage.md +113 -0
- endurancepy-0.1.0/examples/README.md +27 -0
- endurancepy-0.1.0/examples/lap_analysis.py +52 -0
- endurancepy-0.1.0/examples/plot_pace_by_class.py +59 -0
- endurancepy-0.1.0/examples/quickstart.ipynb +122 -0
- endurancepy-0.1.0/examples/quickstart.py +51 -0
- endurancepy-0.1.0/pyproject.toml +102 -0
- endurancepy-0.1.0/src/endurancepy/__init__.py +59 -0
- endurancepy-0.1.0/src/endurancepy/_types.py +110 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/__init__.py +12 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/analysis.py +209 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/classification.py +143 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/client.py +69 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/discovery.py +251 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/headers.py +72 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/timecards.py +14 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/timeparse.py +47 -0
- endurancepy-0.1.0/src/endurancepy/alkamel/weather.py +55 -0
- endurancepy-0.1.0/src/endurancepy/cache.py +195 -0
- endurancepy-0.1.0/src/endurancepy/core.py +425 -0
- endurancepy-0.1.0/src/endurancepy/events.py +250 -0
- endurancepy-0.1.0/src/endurancepy/exceptions.py +23 -0
- endurancepy-0.1.0/src/endurancepy/logger.py +20 -0
- endurancepy-0.1.0/src/endurancepy/plotting.py +101 -0
- endurancepy-0.1.0/src/endurancepy/py.typed +0 -0
- endurancepy-0.1.0/src/endurancepy/results.py +90 -0
- endurancepy-0.1.0/src/endurancepy/standings.py +127 -0
- endurancepy-0.1.0/src/endurancepy/track_status.py +63 -0
- endurancepy-0.1.0/tests/conftest.py +33 -0
- endurancepy-0.1.0/tests/fixtures/analysis_sample.csv +12 -0
- endurancepy-0.1.0/tests/fixtures/classification_race_sample.csv +5 -0
- endurancepy-0.1.0/tests/fixtures/weather_sample.csv +4 -0
- endurancepy-0.1.0/tests/test_analysis.py +120 -0
- endurancepy-0.1.0/tests/test_cache.py +58 -0
- endurancepy-0.1.0/tests/test_classification.py +75 -0
- endurancepy-0.1.0/tests/test_client.py +75 -0
- endurancepy-0.1.0/tests/test_discovery.py +57 -0
- endurancepy-0.1.0/tests/test_discovery_resolve.py +49 -0
- endurancepy-0.1.0/tests/test_events.py +67 -0
- endurancepy-0.1.0/tests/test_examples.py +35 -0
- endurancepy-0.1.0/tests/test_integration_network.py +52 -0
- endurancepy-0.1.0/tests/test_load.py +64 -0
- endurancepy-0.1.0/tests/test_plotting.py +43 -0
- endurancepy-0.1.0/tests/test_results.py +83 -0
- endurancepy-0.1.0/tests/test_smoke.py +49 -0
- endurancepy-0.1.0/tests/test_standings.py +60 -0
- endurancepy-0.1.0/tests/test_track_status.py +47 -0
- endurancepy-0.1.0/tests/test_weather.py +42 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Report something that isn't working as expected
|
|
4
|
+
title: "[Bug] "
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
|
|
11
|
+
A clear and concise description of the bug.
|
|
12
|
+
|
|
13
|
+
## To reproduce
|
|
14
|
+
|
|
15
|
+
Steps (or a minimal code snippet) to reproduce the behaviour:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import endurancepy as ep
|
|
19
|
+
# ...
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Expected behaviour
|
|
23
|
+
|
|
24
|
+
What you expected to happen.
|
|
25
|
+
|
|
26
|
+
## Actual behaviour
|
|
27
|
+
|
|
28
|
+
What actually happened (include the full error traceback if any).
|
|
29
|
+
|
|
30
|
+
## Context
|
|
31
|
+
|
|
32
|
+
- Series / season / event / session involved (e.g. WEC 2024 Le Mans, Race):
|
|
33
|
+
- EndurancePy version / commit:
|
|
34
|
+
- Python version:
|
|
35
|
+
- OS:
|
|
36
|
+
|
|
37
|
+
## Additional notes
|
|
38
|
+
|
|
39
|
+
Anything else that might help — but **do not attach raw Al Kamel timing files**
|
|
40
|
+
(they are not redistributable). A small synthetic example is fine.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest a feature, an API, or a series/season to support
|
|
4
|
+
title: "[Feature] "
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem / motivation
|
|
10
|
+
|
|
11
|
+
What are you trying to do, and what's missing today?
|
|
12
|
+
|
|
13
|
+
## Proposed solution
|
|
14
|
+
|
|
15
|
+
Describe the feature or API you'd like. If it mirrors something in FastF1, please
|
|
16
|
+
link or name the equivalent.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
# Sketch of the desired API, if relevant
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Series / scope
|
|
23
|
+
|
|
24
|
+
Which series or seasons does this concern (WEC, ELMS, AsLMS, Le Mans Cup, IMSA)?
|
|
25
|
+
|
|
26
|
+
## Alternatives considered
|
|
27
|
+
|
|
28
|
+
Any alternative approaches you've thought about.
|
|
29
|
+
|
|
30
|
+
## Additional context
|
|
31
|
+
|
|
32
|
+
Anything else (links to public result pages, references, etc.).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
Briefly describe what this PR does and why.
|
|
4
|
+
|
|
5
|
+
## Related issue
|
|
6
|
+
|
|
7
|
+
Closes #<!-- issue number, if any -->
|
|
8
|
+
|
|
9
|
+
## Type of change
|
|
10
|
+
|
|
11
|
+
- [ ] Bug fix
|
|
12
|
+
- [ ] New feature
|
|
13
|
+
- [ ] Documentation
|
|
14
|
+
- [ ] Refactor / chore
|
|
15
|
+
- [ ] Tests
|
|
16
|
+
|
|
17
|
+
## Checklist
|
|
18
|
+
|
|
19
|
+
- [ ] I have read the [CONTRIBUTING](../CONTRIBUTING.md) guide.
|
|
20
|
+
- [ ] My change follows the design in `docs/` (analysis & implementation plan).
|
|
21
|
+
- [ ] `ruff` (format + check) and `mypy` pass.
|
|
22
|
+
- [ ] Tests pass (`pytest`) and I added tests for new logic.
|
|
23
|
+
- [ ] I did **not** commit any raw Al Kamel timing data; fixtures are synthetic
|
|
24
|
+
or otherwise redistributable.
|
|
25
|
+
- [ ] Documentation / docstrings updated where relevant.
|
|
26
|
+
|
|
27
|
+
## Notes for reviewers
|
|
28
|
+
|
|
29
|
+
Anything specific you'd like reviewers to focus on.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint:
|
|
12
|
+
name: Lint & type-check
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
- name: Install dev dependencies
|
|
20
|
+
run: |
|
|
21
|
+
python -m pip install --upgrade pip
|
|
22
|
+
pip install -e ".[dev]"
|
|
23
|
+
- name: Ruff (format check)
|
|
24
|
+
run: ruff format --check .
|
|
25
|
+
- name: Ruff (lint)
|
|
26
|
+
run: ruff check .
|
|
27
|
+
- name: Mypy
|
|
28
|
+
run: mypy src
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
strategy:
|
|
34
|
+
fail-fast: false
|
|
35
|
+
matrix:
|
|
36
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: actions/setup-python@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: ${{ matrix.python-version }}
|
|
42
|
+
- name: Install package with dev extras
|
|
43
|
+
run: |
|
|
44
|
+
python -m pip install --upgrade pip
|
|
45
|
+
pip install -e ".[dev]"
|
|
46
|
+
- name: Run tests
|
|
47
|
+
run: pytest -m "not network" --cov=endurancepy --cov-report=term-missing
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: integration
|
|
2
|
+
|
|
3
|
+
# On-demand end-to-end test against the live Al Kamel portal. Runs only when
|
|
4
|
+
# manually dispatched (GitHub Actions runners have open internet access, unlike
|
|
5
|
+
# the default development environment).
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
integration:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- name: Install package with dev extras
|
|
19
|
+
run: |
|
|
20
|
+
python -m pip install --upgrade pip
|
|
21
|
+
pip install -e ".[dev]"
|
|
22
|
+
- name: Run network integration tests
|
|
23
|
+
run: pytest -m network -q
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Build the package and publish it to PyPI when a version tag is pushed.
|
|
4
|
+
# Publishing uses PyPI Trusted Publishing (OIDC) — no API token/secret is stored
|
|
5
|
+
# in the repo. See RELEASING.md for the one-time PyPI setup.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build distributions
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Build sdist and wheel
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip build
|
|
24
|
+
python -m build
|
|
25
|
+
- name: Check metadata
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade "twine>=6" "packaging>=24.2"
|
|
28
|
+
python -m twine check dist/*
|
|
29
|
+
- uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish:
|
|
35
|
+
name: Publish to PyPI
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
39
|
+
environment: pypi
|
|
40
|
+
permissions:
|
|
41
|
+
id-token: write # required for Trusted Publishing
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/download-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: dist
|
|
46
|
+
path: dist/
|
|
47
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
share/python-wheels/
|
|
21
|
+
*.egg-info/
|
|
22
|
+
.installed.cfg
|
|
23
|
+
*.egg
|
|
24
|
+
MANIFEST
|
|
25
|
+
|
|
26
|
+
# Unit test / coverage reports
|
|
27
|
+
htmlcov/
|
|
28
|
+
.tox/
|
|
29
|
+
.nox/
|
|
30
|
+
.coverage
|
|
31
|
+
.coverage.*
|
|
32
|
+
.cache
|
|
33
|
+
nosetests.xml
|
|
34
|
+
coverage.xml
|
|
35
|
+
*.cover
|
|
36
|
+
*.py,cover
|
|
37
|
+
.hypothesis/
|
|
38
|
+
.pytest_cache/
|
|
39
|
+
|
|
40
|
+
# Jupyter Notebook
|
|
41
|
+
.ipynb_checkpoints
|
|
42
|
+
|
|
43
|
+
# Environments
|
|
44
|
+
.env
|
|
45
|
+
.venv
|
|
46
|
+
env/
|
|
47
|
+
venv/
|
|
48
|
+
ENV/
|
|
49
|
+
env.bak/
|
|
50
|
+
venv.bak/
|
|
51
|
+
|
|
52
|
+
# IDEs
|
|
53
|
+
.idea/
|
|
54
|
+
.vscode/
|
|
55
|
+
*.swp
|
|
56
|
+
|
|
57
|
+
# OS
|
|
58
|
+
.DS_Store
|
|
59
|
+
Thumbs.db
|
|
60
|
+
|
|
61
|
+
# EndurancePy data cache (downloaded Al Kamel / timing archives)
|
|
62
|
+
cache/
|
|
63
|
+
*.cache
|
|
64
|
+
.endurancepy_cache/
|
|
65
|
+
|
|
66
|
+
# Documentation builds
|
|
67
|
+
docs/_build/
|
|
68
|
+
site/
|
|
69
|
+
|
|
70
|
+
# mypy / type checkers
|
|
71
|
+
.mypy_cache/
|
|
72
|
+
.dmypy.json
|
|
73
|
+
dmypy.json
|
|
74
|
+
.pyre/
|
|
75
|
+
.ruff_cache/
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-06-16
|
|
11
|
+
|
|
12
|
+
First release: load endurance racing timing & results data (WEC, ELMS, AsLMS,
|
|
13
|
+
Le Mans Cup, IMSA) from the Al Kamel archives, with a FastF1-style API.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Discovery: decode (`unquote`) the portal's percent-encoded result paths so
|
|
18
|
+
built URLs are encoded exactly once (no more `%2520`), and make event/session
|
|
19
|
+
matching prefer substring containment then `partial_ratio` (short queries like
|
|
20
|
+
`"Spa"` no longer mis-resolve). Verified end-to-end against the live portal.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Championship standings (milestone 3.1): `compute_standings(results, ...)`
|
|
25
|
+
aggregates points across rounds into a `Standings` table — configurable points
|
|
26
|
+
system (named / sequence / mapping), `by` car/crew/team/manufacturer, overall
|
|
27
|
+
or `per_class`. A generic calculator, not a replica of any series' exact rules.
|
|
28
|
+
Exposed as `ep.compute_standings` / `ep.Standings`.
|
|
29
|
+
- Event schedule (milestone 3.0): `get_event_schedule(year, series, season=...)`
|
|
30
|
+
builds an `EventSchedule` (one row per event: round, name, date, sessions)
|
|
31
|
+
from a season's discovered files; `get_event(...)`, `EventSchedule`
|
|
32
|
+
`get_event_by_round`/`get_event_by_name`, and `Event.get_session`/`get_race`
|
|
33
|
+
(the returned session carries its season so `load()` needs no `season=`).
|
|
34
|
+
`discovery.build_events` / `session_datetime` underpin it.
|
|
35
|
+
- Examples (`examples/`): `quickstart.py` and `quickstart.ipynb` (load a real
|
|
36
|
+
session via `season=`), `lap_analysis.py` (offline analysis of a local
|
|
37
|
+
Analysis CSV) and `plot_pace_by_class.py` (green-flag pace box plot). The
|
|
38
|
+
offline examples are executed in the test suite so they don't rot.
|
|
39
|
+
- Top-level convenience re-exports: `read_analysis`, `read_classification`,
|
|
40
|
+
`read_weather`.
|
|
41
|
+
- Project groundwork: MIT `LICENSE`, `README`, `.gitignore`.
|
|
42
|
+
- Design documentation:
|
|
43
|
+
- `docs/analyse_fastf1.md` — exhaustive inventory of FastF1's content and its
|
|
44
|
+
mapping onto endurance / Al Kamel data.
|
|
45
|
+
- `docs/plan_implementation.md` — phase-2 implementation plan.
|
|
46
|
+
- Community health files: `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, GitHub issue
|
|
47
|
+
templates and a pull request template.
|
|
48
|
+
- Continuous integration workflow (ruff, mypy, pytest).
|
|
49
|
+
- Package skeleton under `src/endurancepy/` (milestone 2.0): the public API
|
|
50
|
+
surface (`get_session`, `get_event`, `get_event_schedule`, `Cache`, `Series`,
|
|
51
|
+
`set_log_level`), data-object class stubs (`Session`, `Laps`/`Lap`,
|
|
52
|
+
`SessionResults`/`CarResult`, `Event`/`EventSchedule`), the `Series` registry,
|
|
53
|
+
reference column schemas, exceptions, logging, and the Al Kamel parser module
|
|
54
|
+
layout (data loading not implemented yet). Project tooling (`pyproject.toml`
|
|
55
|
+
with Hatchling, ruff, mypy, pytest) and a smoke-test suite.
|
|
56
|
+
- Cache and Al Kamel client (milestone 2.1):
|
|
57
|
+
- `Cache`: two-stage on-disk cache — stage 1 raw HTTP via `requests-cache`
|
|
58
|
+
(SQLite), stage 2 parsed `DataFrame`s as Parquet plus JSON metadata,
|
|
59
|
+
namespaced by `PARSER_VERSION`. Includes `enable_cache`, `clear_cache`,
|
|
60
|
+
`get_cache_info`, `set_disabled`/`set_enabled`, `disabled()` context
|
|
61
|
+
manager, `offline_mode`, and dataframe/metadata save/load helpers.
|
|
62
|
+
- `alkamel.client`: `build_results_url` (verified `Results/...` tree, URL
|
|
63
|
+
encoding, optional `Hour N`) and a cache-aware `download`.
|
|
64
|
+
- Offline tests covering URL building, Parquet round-trips and the
|
|
65
|
+
download path.
|
|
66
|
+
- Analysis CSV parser (milestone 2.2 — the core):
|
|
67
|
+
- `alkamel.timeparse`: tolerant duration parsing (`SS.mmm` / `M:SS.mmm` /
|
|
68
|
+
`H:MM:SS.mmm`, including the >24h rollover).
|
|
69
|
+
- `alkamel.headers.read_alkamel_csv`: tolerant CSV reading (BOM, leading-space
|
|
70
|
+
headers, trailing separator, all values as stripped strings).
|
|
71
|
+
- `alkamel.analysis.to_laps`/`read_analysis`: map the Analysis CSV to `Laps`
|
|
72
|
+
and derive `Stint`, `PitInTime`/`PitOutTime`, `LapStartTime`, the cumulative
|
|
73
|
+
`SectorNSessionTime`, overall and in-class `Position`, `IsPersonalBest`,
|
|
74
|
+
`IsAccurate` and `DriverChange`. (Gaps, `Hour` and `LapStartDate` need
|
|
75
|
+
session context and are filled later.)
|
|
76
|
+
- `Laps.pick_*` filters implemented: `pick_cars`, `pick_classes`,
|
|
77
|
+
`pick_manufacturers`, `pick_stints`, `pick_drivers`, `pick_teams`,
|
|
78
|
+
`pick_laps`, `pick_fastest`, `pick_quicklaps`, `pick_track_status`,
|
|
79
|
+
`pick_wo_box`, `pick_box_laps`, `pick_accurate`; plus
|
|
80
|
+
`SessionResults.pick_classes`.
|
|
81
|
+
- Synthetic Analysis-CSV fixture and tests (multi-class, multi-driver, a pit
|
|
82
|
+
stop and an FCY lap).
|
|
83
|
+
- Session results from laps (milestone 2.3):
|
|
84
|
+
- `results.from_laps`: reconstructs `SessionResults` (one row per car/crew)
|
|
85
|
+
from the laps — final order by laps then total time, overall and per class,
|
|
86
|
+
with each car's `Crew`, `BestLapTime`, lap count and total time. (The
|
|
87
|
+
Classification-CSV parser is deferred until its format is verified.)
|
|
88
|
+
- `Session.results` now derives from `laps` when available; `Session.cars`
|
|
89
|
+
(finishing order) and `Session.get_car` implemented.
|
|
90
|
+
- Added `Crew` and `BestLapTime` to the results schema; tests for the
|
|
91
|
+
derivation and the session car helpers.
|
|
92
|
+
- Track-status timeline (milestone 2.4):
|
|
93
|
+
- `track_status.from_laps`: reconstructs the session flag timeline (one row
|
|
94
|
+
per change) from the laps' finish-line flags, with a flag registry mapping
|
|
95
|
+
raw codes (GF/FCY/SC/SF/FF/Code60/…) to readable statuses.
|
|
96
|
+
- `Session.track_status` now derives from `laps` when available.
|
|
97
|
+
- (The Weather-CSV parser is deferred until its format is verified.)
|
|
98
|
+
- Session loading (milestone 2.5):
|
|
99
|
+
- `Session.load(source=...)` reads an Analysis CSV from a path, bytes or
|
|
100
|
+
`http(s)` URL into `Session.laps` (results and track status derive from it);
|
|
101
|
+
parsed laps are cached as Parquet and reused on a subsequent `load()`.
|
|
102
|
+
- Raises `SessionNotAvailableError` when no source is given and the laps are
|
|
103
|
+
not cached. (Automatic discovery of remote files and the `Event`/
|
|
104
|
+
`EventSchedule` layer remain to be implemented.)
|
|
105
|
+
- Automatic discovery in `Session.load` (milestone 2.9):
|
|
106
|
+
- `Session.load(season="<NN_YYYY>")` downloads the portal `?season=` index,
|
|
107
|
+
fuzzy-matches the event and session names, and downloads the Analysis,
|
|
108
|
+
Classification and Weather CSVs automatically (latest hour for races).
|
|
109
|
+
- `discovery.resolve_session_files` / `fetch_index`; `Series.keyword` to
|
|
110
|
+
select the right series folder.
|
|
111
|
+
- Offline tests for the resolution logic; a `network`-marked end-to-end
|
|
112
|
+
integration test (self-skips when the portal is unreachable) plus a manual
|
|
113
|
+
`integration` GitHub Actions workflow to run it on a networked runner.
|
|
114
|
+
- Race Classification parser (milestone 2.8):
|
|
115
|
+
- `alkamel.classification.read_classification`/`to_results`: parse the race
|
|
116
|
+
Classification CSV (`POSITION;NUMBER;TEAM;DRIVER_1..5;VEHICLE;CLASS;STATUS;
|
|
117
|
+
LAPS;TOTAL_TIME;FL_TIME;...`) into `SessionResults`, handling the
|
|
118
|
+
apostrophe time format (`5:44'41.101`) and deriving per-class positions and
|
|
119
|
+
each car's crew/manufacturer. Tolerant of the wider practice/qualifying
|
|
120
|
+
layouts (missing columns left empty).
|
|
121
|
+
- `Session.load(results_source=...)` populates `results` from a Classification
|
|
122
|
+
CSV (otherwise results are derived from the laps).
|
|
123
|
+
- Weather parser & file discovery (milestone 2.7), based on the **verified**
|
|
124
|
+
real Al Kamel formats (captured via a one-off GitHub Actions probe, since the
|
|
125
|
+
development sandbox cannot reach the portal):
|
|
126
|
+
- `alkamel.weather.read_weather`/`to_weather`: parse the Weather CSV
|
|
127
|
+
(`TIME_UTC_SECONDS;TIME_UTC_STR;AIR_TEMP;TRACK_TEMP;HUMIDITY;PRESSURE;
|
|
128
|
+
WIND_SPEED;WIND_DIRECTION`) into FastF1-style `weather_data` (`Time`
|
|
129
|
+
relative to the first sample; `Rainfall` not provided).
|
|
130
|
+
- `alkamel.discovery`: parse a portal `?season=` page into structured
|
|
131
|
+
`ResultFile` records (season/event/series/session/hour/kind + URL builder)
|
|
132
|
+
by reading the embedded `Results/...CSV` paths.
|
|
133
|
+
- `Session.load(weather_source=...)` populates `weather_data`.
|
|
134
|
+
- Confirmed the Analysis CSV format (old & modern) and the Classification CSV
|
|
135
|
+
headers (race `05_`, practice `03_`, qualifying `90_`) against real files.
|
|
136
|
+
- Plotting colour helpers (milestone 2.6):
|
|
137
|
+
- `plotting.get_class_color` / `get_manufacturer_color` (pure functions
|
|
138
|
+
returning hex colours, case-insensitive, with a default fallback) plus
|
|
139
|
+
`list_classes`/`list_manufacturers` and `setup_mpl` (needs the `plot`
|
|
140
|
+
extra). Colours are organised by class and manufacturer rather than driver.
|
|
141
|
+
|
|
142
|
+
[Unreleased]: https://github.com/RomainFl50/EndurancePy/compare/v0.1.0...HEAD
|
|
143
|
+
[0.1.0]: https://github.com/RomainFl50/EndurancePy/releases/tag/v0.1.0
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our commitment
|
|
4
|
+
|
|
5
|
+
EndurancePy is an open, welcoming community. We are committed to providing a
|
|
6
|
+
friendly, safe and inclusive environment for everyone who participates —
|
|
7
|
+
contributors, maintainers and users alike — regardless of background or
|
|
8
|
+
experience level.
|
|
9
|
+
|
|
10
|
+
## Our standards
|
|
11
|
+
|
|
12
|
+
We expect everyone in the project to:
|
|
13
|
+
|
|
14
|
+
- Be respectful, considerate and patient.
|
|
15
|
+
- Welcome newcomers and help them get started.
|
|
16
|
+
- Give and accept constructive feedback gracefully.
|
|
17
|
+
- Focus on what is best for the community and the project.
|
|
18
|
+
- Show empathy towards other community members.
|
|
19
|
+
|
|
20
|
+
Behaviour that makes others feel unwelcome or unsafe is not acceptable and may
|
|
21
|
+
result in removal from project spaces at the maintainers' discretion.
|
|
22
|
+
|
|
23
|
+
## Adopted standard
|
|
24
|
+
|
|
25
|
+
This project adopts the **Contributor Covenant, version 2.1**, as its full Code
|
|
26
|
+
of Conduct. Please read the complete text here:
|
|
27
|
+
|
|
28
|
+
- <https://www.contributor-covenant.org/version/2/1/code_of_conduct/>
|
|
29
|
+
|
|
30
|
+
It defines the expected standards of behaviour, the responsibilities of
|
|
31
|
+
maintainers, the scope of this policy, and the enforcement guidelines in full.
|
|
32
|
+
|
|
33
|
+
## Scope
|
|
34
|
+
|
|
35
|
+
This Code of Conduct applies within all project spaces (the repository, issues,
|
|
36
|
+
pull requests, discussions) and when an individual is representing the project
|
|
37
|
+
in public spaces.
|
|
38
|
+
|
|
39
|
+
## Reporting
|
|
40
|
+
|
|
41
|
+
If you experience or witness a problem, please report it privately to the project
|
|
42
|
+
maintainer:
|
|
43
|
+
|
|
44
|
+
- **Romain Flambard** — romain.flambard@hotmail.fr
|
|
45
|
+
|
|
46
|
+
All reports will be reviewed and handled confidentially. Maintainers are
|
|
47
|
+
obligated to respect the privacy and security of the reporter of any incident.
|
|
48
|
+
|
|
49
|
+
## Attribution
|
|
50
|
+
|
|
51
|
+
This Code of Conduct is adapted from the
|
|
52
|
+
[Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Contributing to EndurancePy
|
|
2
|
+
|
|
3
|
+
First off — thank you for taking the time to contribute! EndurancePy aims to do
|
|
4
|
+
for endurance racing (WEC, ELMS, Asian Le Mans Series, Le Mans Cup, IMSA) what
|
|
5
|
+
[FastF1](https://github.com/theOehrly/Fast-F1) did for Formula 1, and it is very
|
|
6
|
+
much a community effort.
|
|
7
|
+
|
|
8
|
+
This project is in an **early / design phase** — there is no runnable package
|
|
9
|
+
yet, only the design groundwork. That means there is a lot of high-impact work
|
|
10
|
+
available, and good first contributions are easy to find.
|
|
11
|
+
|
|
12
|
+
By participating, you agree to abide by our
|
|
13
|
+
[Code of Conduct](CODE_OF_CONDUCT.md).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Ways to contribute
|
|
18
|
+
|
|
19
|
+
- **Code** — implement one of the milestones (parsers, cache, core objects…).
|
|
20
|
+
- **Documentation** — improve docs, examples, docstrings.
|
|
21
|
+
- **Test data** — help build small, shareable fixtures (see the legal note
|
|
22
|
+
below — do **not** commit raw Al Kamel archives).
|
|
23
|
+
- **Issues** — report bugs, request a series/season, or propose an API design.
|
|
24
|
+
- **Review** — review open pull requests.
|
|
25
|
+
|
|
26
|
+
If you are unsure where to start, **open an issue** describing what you'd like to
|
|
27
|
+
work on, so we can avoid duplicated effort and agree on the approach before you
|
|
28
|
+
write code.
|
|
29
|
+
|
|
30
|
+
## Understand the design first
|
|
31
|
+
|
|
32
|
+
Before writing code, please read the design documents — they are the functional
|
|
33
|
+
and technical spec for the package:
|
|
34
|
+
|
|
35
|
+
- [`docs/analyse_fastf1.md`](docs/analyse_fastf1.md) — exhaustive inventory of
|
|
36
|
+
FastF1's content and how each piece maps onto endurance / Al Kamel data.
|
|
37
|
+
- [`docs/plan_implementation.md`](docs/plan_implementation.md) — the
|
|
38
|
+
implementation plan: package layout, target column schemas, parsing pipeline,
|
|
39
|
+
derivation algorithms, cache design, milestones and test strategy.
|
|
40
|
+
|
|
41
|
+
The guiding principle: **mirror FastF1's API and content** wherever the
|
|
42
|
+
underlying public data exists.
|
|
43
|
+
|
|
44
|
+
## Development setup
|
|
45
|
+
|
|
46
|
+
> The package skeleton is being bootstrapped; once `pyproject.toml` lands, the
|
|
47
|
+
> setup below applies.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 1. Fork and clone
|
|
51
|
+
git clone https://github.com/<your-username>/EndurancePy.git
|
|
52
|
+
cd EndurancePy
|
|
53
|
+
|
|
54
|
+
# 2. Create a virtual environment (Python 3.10+)
|
|
55
|
+
python -m venv .venv
|
|
56
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
57
|
+
|
|
58
|
+
# 3. Install in editable mode with dev extras
|
|
59
|
+
pip install -e ".[dev]"
|
|
60
|
+
|
|
61
|
+
# 4. (optional) install pre-commit hooks
|
|
62
|
+
pre-commit install
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Coding guidelines
|
|
66
|
+
|
|
67
|
+
- **Python 3.10+**, with type hints on public functions.
|
|
68
|
+
- **Formatting & linting**: [`ruff`](https://docs.astral.sh/ruff/)
|
|
69
|
+
(`ruff format` + `ruff check`).
|
|
70
|
+
- **Type checking**: `mypy`.
|
|
71
|
+
- **Docstrings** on all public classes/functions (NumPy or Google style).
|
|
72
|
+
- **Mirror FastF1 naming** where it makes sense (`get_session`, `Session.load`,
|
|
73
|
+
`Session.laps`, `pick_*`, `Cache`) so FastF1 users feel at home. Document any
|
|
74
|
+
deliberate divergence (see the plan).
|
|
75
|
+
- Keep parsers **tolerant**: Al Kamel CSV headers drift across seasons/series —
|
|
76
|
+
match columns by name, handle the BOM and leading-space header variants.
|
|
77
|
+
|
|
78
|
+
## Tests
|
|
79
|
+
|
|
80
|
+
- Use `pytest`. Run the suite with:
|
|
81
|
+
```bash
|
|
82
|
+
pytest
|
|
83
|
+
```
|
|
84
|
+
- New parsing logic must come with tests.
|
|
85
|
+
- Prefer **small, synthetic fixtures** that reproduce the format (a couple of
|
|
86
|
+
cars, two classes, a few laps, a pit stop, a caution period). **Do not commit
|
|
87
|
+
real Al Kamel timing files** (see the legal note).
|
|
88
|
+
- Network-dependent tests must be marked (e.g. `@pytest.mark.network`) and stay
|
|
89
|
+
off by default.
|
|
90
|
+
|
|
91
|
+
## Commit messages
|
|
92
|
+
|
|
93
|
+
Use clear, descriptive messages. We loosely follow
|
|
94
|
+
[Conventional Commits](https://www.conventionalcommits.org/), e.g.:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
feat(alkamel): parse Analysis CSV into Laps
|
|
98
|
+
fix(timeparse): handle the 24: elapsed-time rollover
|
|
99
|
+
docs: clarify per-class position derivation
|
|
100
|
+
test(headers): cover the IMSA WithSections variant
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Pull request workflow
|
|
104
|
+
|
|
105
|
+
1. Create a topic branch from `main` (`feat/...`, `fix/...`, `docs/...`).
|
|
106
|
+
2. Make focused commits; keep PRs reasonably small and single-purpose.
|
|
107
|
+
3. Ensure `ruff`, `mypy` and `pytest` pass locally.
|
|
108
|
+
4. Fill in the pull request template and link any related issue.
|
|
109
|
+
5. Be responsive to review feedback. Maintainers may request changes before merge.
|
|
110
|
+
|
|
111
|
+
## ⚖️ Legal note (please read)
|
|
112
|
+
|
|
113
|
+
EndurancePy parses **publicly published** timing archives. **Al Kamel Systems
|
|
114
|
+
explicitly asserts ownership of its timing data and warns against
|
|
115
|
+
redistribution.** Therefore:
|
|
116
|
+
|
|
117
|
+
- **Never commit raw Al Kamel data** (CSV/PDF archives) to this repository, and
|
|
118
|
+
do not bundle or republish it in any artifact.
|
|
119
|
+
- Test fixtures must be **synthetic** or otherwise clearly redistributable.
|
|
120
|
+
- Always respect each site's Terms of Service and `robots.txt`, identify your
|
|
121
|
+
client honestly, and keep request rates reasonable (the cache exists partly
|
|
122
|
+
for this reason).
|
|
123
|
+
|
|
124
|
+
Contributions that redistribute proprietary data will not be accepted.
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
By contributing, you agree that your contributions will be licensed under the
|
|
129
|
+
project's [MIT License](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Romain Flambard and the EndurancePy contributors
|
|
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.
|