ilc-models
ilc-models contains all data models for the ILC project.
It's a set of pure Pydantic v2 models with no I/O — a League of Match
objects, each with its Lineups, Goal/Card/Substitution events, and
the TableRow results computed from them, plus User and UserPublic
models for accounts. Its only dependencies are pydantic (with the email
extra) and argon2-cffi, which User uses to hash passwords. This package
is the shared data contract used across the ilc-api, ilc-app and
ilc-admin repositories, so a field change here is a breaking change for
all three.
Installation
Requires Python 3.12 or later.
pip install ilc-models
Or with uv:
uv add ilc-models
Quick start
from ilc_models import League, Match, Teams, Score
league = League(
league_id=1,
name="Premiership",
year=2025,
start="2025-08-01",
end="2026-05-01",
current=True,
coverage={"lineups": True},
teams=["Team A", "Team B"],
rounds={
"1": [
Match(
match_id=1,
kickoff="2025-08-09T15:00:00+01:00",
round="1",
teams=Teams(home="Team A", away="Team B"),
status="FT",
score=Score(home=2, away=1),
)
]
},
)
for row in league.table():
print(row)
Model map
League
rounds: dict[str, list[Match]]
Match
teams: Teams # home / away team names
score: Score # home / away goals
goals: list[Goal]
cards: list[Card]
substitutions: list[Substitution]
lineups: Lineups
home: Lineup
away: Lineup
starting: list[tuple[int, BasePlayer]] # shirt_no, player
subs: list[tuple[int, BasePlayer]]
Goal, Card, Substitution and LineupStatus all subclass the abstract
BaseEvent and are aliased together as Event. League.players holds
every Player who has featured in the league, keyed by player_id as a
string.
Users are modelled separately: User (the stored shape, including
password_hash) subclasses UserPublic (just email).
Users
from ilc_models import User
user = User.create("alice@example.com", "correct horse battery staple")
user.verify_password("correct horse battery staple") # True
user.verify_password("wrong") # False
# After a successful login, upgrade the hash if its parameters are outdated
attempt = "correct horse battery staple"
if user.verify_password(attempt) and user.needs_rehash():
user.set_password(attempt)
# ...then persist the user
User.create hashes the password with argon2 and never stores it. To load a
user from the database, pass the stored hash instead:
User(email=..., password_hash=...). Supplying both password and
password_hash raises an error.
Warning:
User.model_dump()includespassword_hash. Anything that leaves the API should useUserPublic(e.g. FastAPI'sresponse_model=UserPublic).
Conventions worth knowing
A few behaviours are deliberate and easy to trip over — see the full usage guide for detail:
Goal.teamis the team credited with the goal, not the scorer's team. For an own goal the scorer belongs to the opposing team — callMatch.event_team(event)to resolve the team a player in an event actually plays for.- Player equality compares
player_idalone (falling back tonameonly when both IDs are0), because the upstream API spells player names differently in event payloads versus player downloads. League.table()models the Scottish-style split — once a team has played more thanLeague.splitmatches, the table freezes the top/bottom halves by position at the split point and sorts each half independently.League.splitis the number of matches each team plays before the split; the default of0means no split.- The
team,teams,h2handplayerfilters onLeague.matches()are alternatives, not combinable. If you pass more than one, only the first applies, in that order.League.match_players()works the same way (withoutplayer), andMatch.players()prefersteamsoverteam. - Dates are validated ISO strings, not
dateobjects.Match.kickoffis a full offset-aware timestamp;League.start/endandDeduction.dateareyyyy-mm-dd. UseMatch.dateto get adatetime.date. EventTime.plus(stoppage time) is only valid at the end of a half — i.e. whenminutesis 45, 90, 105 or 120.
Documentation
Full API reference and usage guide: ilc-models.readthedocs.io
See CHANGELOG.md for release history.
Development
Task runner is just; Python tooling is
uv.
just check # ruff check --fix, ruff format, then ty check (type checking)
just test # run tests with pytest
just cov # pytest with coverage, then regenerate the coverage badge
just docs # build the Sphinx docs locally
just tox # run the tests across Python 3.12-3.14 and lowest/highest dependencies
License
MIT — see LICENSE.txt.
Metadata
Release files for ilc-models 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ilc_models-0.3.0.tar.gz | 102.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ilc_models-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 118.8 kB
Release files / ilc_models-0.3.0.tar.gz
| Download URL | ilc_models-0.3.0.tar.gz |
|---|---|
| Size | 102.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f0ec21aa382df752cc4dc6c760ea0e944bead001ba07031d3920064ed7b0913f
|
|
BLAKE2b-256 checksum How to use checksums |
6076b1dcfdd96b86c3ac405f2a33f9ebf3756bc4d8cb4ea99ceac1ff57a3d335
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / ilc_models-0.3.0-py3-none-any.whl
| Download URL | ilc_models-0.3.0-py3-none-any.whl |
|---|---|
| Size | 16.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
21a5f3ecdcdfebb5c0895190751fb7093a93e8e035816e6e07c9c9b33c903d77
|
|
BLAKE2b-256 checksum How to use checksums |
cadbc474caac06cf804b81088958166d474fde6a6e2a529c5f593ed860a8b2f8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for ilc-models 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.