Running the platform
Data sources
Competitions, teams, players, venues, fixtures and results can come from elsewhere: a federation, a booking system, another league tool, a spreadsheet. Each place is a data source, and a new one plugs in without changing the core.
How records stay linked
Every competition, team, player, venue and fixture can carry a source reference: the source's name and the record's id there. The pair is unique, so a run upserts: what's there already is updated, the rest is created, and running the same import again changes nothing.
- The source owns what it sends: names, a fixture's kick-off, venue, sides, round and status, and results.
- The platform owns everything else: availability, squads, team sheets, fees, messages. No run touches them.
- A played fixture stays played even when its source still lists it as scheduled.
- Detaching: a record detached from its source is the platform's to edit, and runs leave it as edited.
Runs
In the admin, System, Data sources lists each source with what it brought (and how much of it is detached),
its settings page if it has one, its schedule (every so many hours, or only when asked) and "Run now", and the
log of runs: what each created, updated, left or skipped, its problems, and why a run failed. Runs are Messenger
jobs; app:matchday:run starts the ones whose schedule says so.
"Record by record" opens a run: every record it created or updated, with each field before and after, and every
detached record it left where the source differs. Each line has the source's own id for the record and the
record exactly as the source sent it, so any change goes back to what caused it. A source whose settings are an
admin page says so by implementing HasSettings.
From the command line:
php bin/console app:source:import # lists the sources and whether they're switched on
php bin/console app:source:import <name> # runs one at once, and says what it did
Spreadsheets
The first source moves a league in from its spreadsheets. In System, Data sources, "Import a spreadsheet":
- Upload a CSV of teams, players or fixtures (comma, semicolon or tab separated, in UTF-8 or the Windows encoding Excel saves in), with the sport, the time zone its times are in, and the competition for fixtures without a competition column. A template for each kind is linked there.
- Map each field to the column holding it: guessed from the headers, in English or Romanian (Home, Away, Date, Time; Gazde, Oaspeți, Data, Ora; and so on).
- Preview the rows as they'll import, and each row left out with why (a date that isn't one, a team playing itself, a missing side).
- Import: the valid rows go in as a run of the spreadsheet source, logged with the rows left out.
Teams, venues and competitions are known by their names, and a fixture by its competition, sides and day (or its own id column), so importing a corrected file again updates rather than duplicates. A fixtures sheet brings its competition, teams and venues; its scores count as confirmed results. A players sheet with a team column puts each new player in that team's squad; squads are the platform's after that.
Calendar feeds
To subscribe to. Players live in their phone's calendar:
- A player's private feed holds every team they play for, their own matches and their pairs'.
- A team's private feed holds its matches.
- A competition's public feed is at
/calendar/competition/<slug>.ics.
The app gets a player's links from GET /api/v1/me/calendar; POST /api/v1/me/calendar/reset makes a new
private link and ends the old one. Each fixture keeps one event however it changes, so a moved match moves in
the calendar, and a called-off one shows as cancelled. Calendars fetch feeds every hour or so.
To import. For a league that publishes only an iCal feed, add it under System, Calendar feeds to import: its address (https), the competition its events belong to, their sport, and the time zone of times given without one. The "Calendar feeds (iCal)" source then brings its events in as fixtures, on its schedule or when run. Each event's title names its sides ("Home v Away", "Home vs Away", "Home - Away") and its location the venue; events that don't name two sides, and feeds that can't be read, are problems in the run's log.
Detaching
A competition, team, player, venue or fixture page in the admin says which source a record came from. "Detach from its source" makes it the platform's to edit: runs leave it as it is. "Follow its source again" hands it back, and the next run brings the source's version over what was edited. Data sources lists the detached records with where their source said otherwise at the last run that left them: here and at the source, field by field.
Writing a source
A source is one class implementing App\Source\DataSource, found by itself: no core change. The guide,
Writing a data source, walks through writing one for a federation or booking system and testing
it, from src/Source/Example/ExampleFederationSource.php, a complete connector for a sample federation API
(registered in development and tests only). Every source passes the contract test in
tests/Support/DataSourceContractTestCase.php.
A source that takes results back implements SendsResults: each result confirmed on the platform for one of
its fixtures is sent to it in the background, never one it sent itself.
Found a mistake? Tell us.