Getting data out¶
Every reader returns a Table, and every Table offers the same four ways out. Pick the one that
suits where the data is going.
import fmsave
with fmsave.open("career.fm") as career_save:
squad = career_save.players().where(club_uid=career_save.managed_clubs()[0].club_uid)
rows = squad.to_dicts() # nested dicts of Python values
columns = squad.to_columns() # flat columns of Python values
frame = squad.to_pandas() # needs fmsave[pandas]
squad.write_csv("squad.csv") # flat columns
squad.write_json("squad.json") # nested, one array
squad.write_jsonl("squad.jsonl") # nested, one record per line
to_polars() is there too, and needs fmsave[polars]. Tables keep working after the save is
closed, so exporting outside the with block is fine.
Nested or flat¶
A record is nested. A player carries an ability group, an attributes group, a contract, and
so on. Two of the four forms keep that shape and two flatten it.
Nested:
to_dicts(),write_json(),write_jsonl().Flat:
to_columns(),to_pandas(),to_polars(),write_csv().
The flattening rules are short:
A nested group becomes
<field>_<subfield>:ability_current,contract_wage,attributes_finishing. These are the namespandas.json_normalize(sep="_")would give.A coded value becomes two columns: a label column and a
<field>_codecolumn holding the raw number, socontract_squad_statusandcontract_squad_status_code.A tuple stays one value: a JSON array in JSON, a
;-joined string in CSV, or compact JSON text in CSV when its items are groups.
A player flattens to 217 columns. To see the names for any record type without opening a save:
import fmsave.export
fmsave.export.column_names(fmsave.Player) # ("uid", "name", "first_name", ...)
Nested JSON carries the label and the code, so JSON is the lossless format. Reach for it when you are handing the data to another program. CSV is for reading and for spreadsheets.
to_dicts(json_ready=True) gives the same nesting write_json uses: dates as ISO strings, tuples
as lists, everything JSON-serialisable.
From the command line¶
fmsave export does the same work without Python, and needs a table and a scope.
fmsave export career.fm players --managed-club -o squad.csv
fmsave export career.fm players --managed-club --columns name,age,ability_current,contract_wage
fmsave export career.fm players --all --format json -o players.json
fmsave export career.fm fixtures --managed-club --format jsonl -o fixtures.jsonl
The scope is required, and exactly one of:
--managed-club: the club you run.--club VALUE: one club by uid, name or short name, in any case. A name that matches more than one club is an error that lists the matches, so you can pick the uid.--competition VALUE: one competition by id, or by name once--competition-namessupplies one.--nation VALUE: one nation, by nation id.--all: every row.
The rest:
--format {csv,json,jsonl}, defaultcsv.--columns NAMES: flat column names, comma-separated, written in the order you give them.-o PATH: a file instead of standard output.--competition-names PATH: a UTF-8 CSV ofdatabase_id,name. See What a save holds.--strict: stop rather than write when a reader’s checks fail. See Trusting a number.
Table names on the command line use hyphens where the Python method uses underscores:
managed-clubs, league-tables, player-match-stats, set-pieces.
Four tables (training, mentoring, tactics and set-pieces) only exist for the club you
manage. Any other scope returns no rows.