fitzroy 1.2.0 → 1.3.1
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.
- package/dist/cli.js +378 -146
- package/dist/index.d.ts +253 -249
- package/dist/index.js +203 -84
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,6 +20,12 @@ var UnsupportedSourceError = class extends Error {
|
|
|
20
20
|
}
|
|
21
21
|
name = "UnsupportedSourceError";
|
|
22
22
|
};
|
|
23
|
+
function aflwUnsupportedError(source) {
|
|
24
|
+
return new UnsupportedSourceError(
|
|
25
|
+
`AFLW data is not available from ${source}. Use --source afl-api for AFLW data.`,
|
|
26
|
+
source
|
|
27
|
+
);
|
|
28
|
+
}
|
|
23
29
|
var ValidationError = class extends Error {
|
|
24
30
|
constructor(message, issues) {
|
|
25
31
|
super(message);
|
|
@@ -119,6 +125,10 @@ function toAestString(date) {
|
|
|
119
125
|
});
|
|
120
126
|
return formatter.format(date);
|
|
121
127
|
}
|
|
128
|
+
function resolveDefaultSeason(competition = "AFLM") {
|
|
129
|
+
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
130
|
+
return competition === "AFLW" ? year - 1 : year;
|
|
131
|
+
}
|
|
122
132
|
var MONTH_ABBREV_TO_INDEX = /* @__PURE__ */ new Map([
|
|
123
133
|
["jan", 0],
|
|
124
134
|
["feb", 1],
|
|
@@ -233,6 +243,26 @@ function normaliseTeamName(raw) {
|
|
|
233
243
|
const trimmed = raw.trim();
|
|
234
244
|
return ALIAS_MAP.get(trimmed.toLowerCase()) ?? trimmed;
|
|
235
245
|
}
|
|
246
|
+
var AFL_API_TEAM_IDS = /* @__PURE__ */ new Map([
|
|
247
|
+
["CD_T10", "Adelaide Crows"],
|
|
248
|
+
["CD_T20", "Brisbane Lions"],
|
|
249
|
+
["CD_T30", "Carlton"],
|
|
250
|
+
["CD_T40", "Collingwood"],
|
|
251
|
+
["CD_T50", "Essendon"],
|
|
252
|
+
["CD_T60", "Fremantle"],
|
|
253
|
+
["CD_T70", "Geelong Cats"],
|
|
254
|
+
["CD_T1000", "Gold Coast Suns"],
|
|
255
|
+
["CD_T1010", "GWS Giants"],
|
|
256
|
+
["CD_T80", "Hawthorn"],
|
|
257
|
+
["CD_T90", "Melbourne"],
|
|
258
|
+
["CD_T100", "North Melbourne"],
|
|
259
|
+
["CD_T110", "Port Adelaide"],
|
|
260
|
+
["CD_T120", "Richmond"],
|
|
261
|
+
["CD_T130", "St Kilda"],
|
|
262
|
+
["CD_T160", "Sydney Swans"],
|
|
263
|
+
["CD_T150", "West Coast Eagles"],
|
|
264
|
+
["CD_T140", "Western Bulldogs"]
|
|
265
|
+
]);
|
|
236
266
|
|
|
237
267
|
// src/transforms/footywire-player-stats.ts
|
|
238
268
|
import * as cheerio from "cheerio";
|
|
@@ -487,6 +517,14 @@ var FINALS_PATTERN = /final|elimination|qualifying|preliminary|semi|grand/i;
|
|
|
487
517
|
function inferRoundType(roundName) {
|
|
488
518
|
return FINALS_PATTERN.test(roundName) ? "Finals" : "HomeAndAway";
|
|
489
519
|
}
|
|
520
|
+
function finalsRoundNumber(headerText, lastHARound) {
|
|
521
|
+
const lower = headerText.toLowerCase();
|
|
522
|
+
if (lower.includes("qualifying") || lower.includes("elimination")) return lastHARound + 1;
|
|
523
|
+
if (lower.includes("semi")) return lastHARound + 2;
|
|
524
|
+
if (lower.includes("preliminary")) return lastHARound + 3;
|
|
525
|
+
if (lower.includes("grand")) return lastHARound + 4;
|
|
526
|
+
return lastHARound + 1;
|
|
527
|
+
}
|
|
490
528
|
function toMatchStatus(raw) {
|
|
491
529
|
switch (raw) {
|
|
492
530
|
case "CONCLUDED":
|
|
@@ -783,6 +821,7 @@ function parseMatchList(html, year) {
|
|
|
783
821
|
const $ = cheerio2.load(html);
|
|
784
822
|
const results = [];
|
|
785
823
|
let currentRound = 0;
|
|
824
|
+
let lastHARound = 0;
|
|
786
825
|
let currentRoundType = "HomeAndAway";
|
|
787
826
|
$("tr").each((_i, row) => {
|
|
788
827
|
const roundHeader = $(row).find("td[colspan='7']");
|
|
@@ -792,6 +831,11 @@ function parseMatchList(html, year) {
|
|
|
792
831
|
const roundMatch = /Round\s+(\d+)/i.exec(text);
|
|
793
832
|
if (roundMatch?.[1]) {
|
|
794
833
|
currentRound = Number.parseInt(roundMatch[1], 10);
|
|
834
|
+
if (currentRoundType === "HomeAndAway") {
|
|
835
|
+
lastHARound = currentRound;
|
|
836
|
+
}
|
|
837
|
+
} else if (currentRoundType === "Finals") {
|
|
838
|
+
currentRound = finalsRoundNumber(text, lastHARound);
|
|
795
839
|
}
|
|
796
840
|
return;
|
|
797
841
|
}
|
|
@@ -862,6 +906,7 @@ function parseFixtureList(html, year) {
|
|
|
862
906
|
const $ = cheerio2.load(html);
|
|
863
907
|
const fixtures = [];
|
|
864
908
|
let currentRound = 0;
|
|
909
|
+
let lastHARound = 0;
|
|
865
910
|
let currentRoundType = "HomeAndAway";
|
|
866
911
|
let gameNumber = 0;
|
|
867
912
|
$("tr").each((_i, row) => {
|
|
@@ -872,6 +917,11 @@ function parseFixtureList(html, year) {
|
|
|
872
917
|
const roundMatch = /Round\s+(\d+)/i.exec(text);
|
|
873
918
|
if (roundMatch?.[1]) {
|
|
874
919
|
currentRound = Number.parseInt(roundMatch[1], 10);
|
|
920
|
+
if (currentRoundType === "HomeAndAway") {
|
|
921
|
+
lastHARound = currentRound;
|
|
922
|
+
}
|
|
923
|
+
} else if (currentRoundType === "Finals") {
|
|
924
|
+
currentRound = finalsRoundNumber(text, lastHARound);
|
|
875
925
|
}
|
|
876
926
|
return;
|
|
877
927
|
}
|
|
@@ -1528,7 +1578,15 @@ var CfsPlayerInnerSchema = z.object({
|
|
|
1528
1578
|
captain: z.boolean().optional(),
|
|
1529
1579
|
playerJumperNumber: z.number().optional()
|
|
1530
1580
|
}).passthrough();
|
|
1531
|
-
var statNum = z.
|
|
1581
|
+
var statNum = z.union([
|
|
1582
|
+
z.number(),
|
|
1583
|
+
z.string().transform((s) => {
|
|
1584
|
+
if (s === "" || s === "-") return null;
|
|
1585
|
+
const n = Number(s);
|
|
1586
|
+
return Number.isNaN(n) ? null : n;
|
|
1587
|
+
}),
|
|
1588
|
+
z.boolean().transform((b) => b ? 1 : 0)
|
|
1589
|
+
]).nullable().optional();
|
|
1532
1590
|
var PlayerGameStatsSchema = z.object({
|
|
1533
1591
|
goals: statNum,
|
|
1534
1592
|
behinds: statNum,
|
|
@@ -1606,8 +1664,8 @@ var PlayerStatsItemSchema = z.object({
|
|
|
1606
1664
|
teamId: z.string(),
|
|
1607
1665
|
playerStats: z.object({
|
|
1608
1666
|
stats: PlayerGameStatsSchema,
|
|
1609
|
-
timeOnGroundPercentage:
|
|
1610
|
-
}).passthrough()
|
|
1667
|
+
timeOnGroundPercentage: statNum
|
|
1668
|
+
}).passthrough().nullable().optional()
|
|
1611
1669
|
}).passthrough();
|
|
1612
1670
|
var PlayerStatsListSchema = z.object({
|
|
1613
1671
|
homeTeamPlayerStats: z.array(PlayerStatsItemSchema),
|
|
@@ -2284,12 +2342,14 @@ function toFixture(item, season, fallbackRoundNumber, competition) {
|
|
|
2284
2342
|
async function fetchFixture(query) {
|
|
2285
2343
|
const competition = query.competition ?? "AFLM";
|
|
2286
2344
|
if (query.source === "squiggle") {
|
|
2345
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("squiggle"));
|
|
2287
2346
|
const client2 = new SquiggleClient();
|
|
2288
2347
|
const result = await client2.fetchGames(query.season, query.round ?? void 0);
|
|
2289
2348
|
if (!result.success) return result;
|
|
2290
2349
|
return ok(transformSquiggleGamesToFixture(result.data.games, query.season));
|
|
2291
2350
|
}
|
|
2292
2351
|
if (query.source === "footywire") {
|
|
2352
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
2293
2353
|
const fwClient = new FootyWireClient();
|
|
2294
2354
|
const result = await fwClient.fetchSeasonFixture(query.season);
|
|
2295
2355
|
if (!result.success) return result;
|
|
@@ -2633,6 +2693,7 @@ function parseSeasonPage(html, year) {
|
|
|
2633
2693
|
const results = [];
|
|
2634
2694
|
let currentRound = 0;
|
|
2635
2695
|
let currentRoundType = "HomeAndAway";
|
|
2696
|
+
let lastHARound = 0;
|
|
2636
2697
|
let matchCounter = 0;
|
|
2637
2698
|
$("table").each((_i, table) => {
|
|
2638
2699
|
const $table = $(table);
|
|
@@ -2642,10 +2703,14 @@ function parseSeasonPage(html, year) {
|
|
|
2642
2703
|
if (roundMatch?.[1] && border !== "1") {
|
|
2643
2704
|
currentRound = Number.parseInt(roundMatch[1], 10);
|
|
2644
2705
|
currentRoundType = inferRoundType(text);
|
|
2706
|
+
if (currentRoundType === "HomeAndAway") {
|
|
2707
|
+
lastHARound = currentRound;
|
|
2708
|
+
}
|
|
2645
2709
|
return;
|
|
2646
2710
|
}
|
|
2647
2711
|
if (border !== "1" && inferRoundType(text) === "Finals") {
|
|
2648
2712
|
currentRoundType = "Finals";
|
|
2713
|
+
currentRound = finalsRoundNumber(text, lastHARound);
|
|
2649
2714
|
return;
|
|
2650
2715
|
}
|
|
2651
2716
|
if (border !== "1") return;
|
|
@@ -2960,6 +3025,7 @@ function transformLadderEntries(entries) {
|
|
|
2960
3025
|
async function fetchLadder(query) {
|
|
2961
3026
|
const competition = query.competition ?? "AFLM";
|
|
2962
3027
|
if (query.source === "squiggle") {
|
|
3028
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("squiggle"));
|
|
2963
3029
|
const client2 = new SquiggleClient();
|
|
2964
3030
|
const result = await client2.fetchStandings(query.season, query.round ?? void 0);
|
|
2965
3031
|
if (!result.success) return result;
|
|
@@ -2971,6 +3037,7 @@ async function fetchLadder(query) {
|
|
|
2971
3037
|
});
|
|
2972
3038
|
}
|
|
2973
3039
|
if (query.source === "afl-tables") {
|
|
3040
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
2974
3041
|
const atClient = new AflTablesClient();
|
|
2975
3042
|
const resultsResult = await atClient.fetchSeasonResults(query.season);
|
|
2976
3043
|
if (!resultsResult.success) return resultsResult;
|
|
@@ -3105,6 +3172,7 @@ async function fetchMatchResults(query) {
|
|
|
3105
3172
|
return ok(transformMatchItems(itemsResult.data, query.season, competition));
|
|
3106
3173
|
}
|
|
3107
3174
|
case "footywire": {
|
|
3175
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
3108
3176
|
const client = new FootyWireClient();
|
|
3109
3177
|
const result = await client.fetchSeasonResults(query.season);
|
|
3110
3178
|
if (!result.success) return result;
|
|
@@ -3114,6 +3182,7 @@ async function fetchMatchResults(query) {
|
|
|
3114
3182
|
return result;
|
|
3115
3183
|
}
|
|
3116
3184
|
case "afl-tables": {
|
|
3185
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
3117
3186
|
const client = new AflTablesClient();
|
|
3118
3187
|
const result = await client.fetchSeasonResults(query.season);
|
|
3119
3188
|
if (!result.success) return result;
|
|
@@ -3123,6 +3192,7 @@ async function fetchMatchResults(query) {
|
|
|
3123
3192
|
return result;
|
|
3124
3193
|
}
|
|
3125
3194
|
case "squiggle": {
|
|
3195
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("squiggle"));
|
|
3126
3196
|
const client = new SquiggleClient();
|
|
3127
3197
|
const result = await client.fetchGames(query.season, query.round ?? void 0, 100);
|
|
3128
3198
|
if (!result.success) return result;
|
|
@@ -3148,7 +3218,7 @@ async function resolveTeamId(client, teamName, competition) {
|
|
|
3148
3218
|
async function fetchFromAflApi(query) {
|
|
3149
3219
|
const client = new AflApiClient();
|
|
3150
3220
|
const competition = query.competition ?? "AFLM";
|
|
3151
|
-
const season = query.season ?? (
|
|
3221
|
+
const season = query.season ?? resolveDefaultSeason(competition);
|
|
3152
3222
|
const [teamIdResult, seasonResult] = await Promise.all([
|
|
3153
3223
|
resolveTeamId(client, query.team, competition),
|
|
3154
3224
|
client.resolveCompSeason(competition, season)
|
|
@@ -3171,8 +3241,8 @@ async function fetchFromAflApi(query) {
|
|
|
3171
3241
|
jumperNumber: p.jumperNumber ?? null,
|
|
3172
3242
|
position: p.position ?? null,
|
|
3173
3243
|
dateOfBirth: p.player.dateOfBirth ?? null,
|
|
3174
|
-
heightCm: p.player.heightInCm
|
|
3175
|
-
weightKg: p.player.weightInKg
|
|
3244
|
+
heightCm: p.player.heightInCm || null,
|
|
3245
|
+
weightKg: p.player.weightInKg || null,
|
|
3176
3246
|
gamesPlayed: null,
|
|
3177
3247
|
goals: null,
|
|
3178
3248
|
draftYear: p.player.draftYear ? Number.parseInt(p.player.draftYear, 10) || null : null,
|
|
@@ -3186,8 +3256,9 @@ async function fetchFromAflApi(query) {
|
|
|
3186
3256
|
return ok(players);
|
|
3187
3257
|
}
|
|
3188
3258
|
async function fetchFromFootyWire(query) {
|
|
3189
|
-
const client = new FootyWireClient();
|
|
3190
3259
|
const competition = query.competition ?? "AFLM";
|
|
3260
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
3261
|
+
const client = new FootyWireClient();
|
|
3191
3262
|
const teamName = normaliseTeamName(query.team);
|
|
3192
3263
|
const result = await client.fetchPlayerList(teamName);
|
|
3193
3264
|
if (!result.success) return result;
|
|
@@ -3199,8 +3270,9 @@ async function fetchFromFootyWire(query) {
|
|
|
3199
3270
|
return ok(players);
|
|
3200
3271
|
}
|
|
3201
3272
|
async function fetchFromAflTables(query) {
|
|
3202
|
-
const client = new AflTablesClient();
|
|
3203
3273
|
const competition = query.competition ?? "AFLM";
|
|
3274
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
3275
|
+
const client = new AflTablesClient();
|
|
3204
3276
|
const teamName = normaliseTeamName(query.team);
|
|
3205
3277
|
const result = await client.fetchPlayerList(teamName);
|
|
3206
3278
|
if (!result.success) return result;
|
|
@@ -3235,80 +3307,82 @@ function toNullable(value) {
|
|
|
3235
3307
|
}
|
|
3236
3308
|
function transformOne(item, matchId, season, roundNumber, competition, source, teamIdMap) {
|
|
3237
3309
|
const inner = item.player.player.player;
|
|
3238
|
-
const stats = item.playerStats
|
|
3239
|
-
const clearances = stats
|
|
3310
|
+
const stats = item.playerStats?.stats;
|
|
3311
|
+
const clearances = stats?.clearances;
|
|
3240
3312
|
return {
|
|
3241
3313
|
matchId,
|
|
3242
3314
|
season,
|
|
3243
3315
|
roundNumber,
|
|
3244
|
-
team: normaliseTeamName(
|
|
3316
|
+
team: normaliseTeamName(
|
|
3317
|
+
teamIdMap?.get(item.teamId) ?? AFL_API_TEAM_IDS.get(item.teamId) ?? item.teamId
|
|
3318
|
+
),
|
|
3245
3319
|
competition,
|
|
3246
3320
|
playerId: inner.playerId,
|
|
3247
3321
|
givenName: inner.playerName.givenName,
|
|
3248
3322
|
surname: inner.playerName.surname,
|
|
3249
3323
|
displayName: `${inner.playerName.givenName} ${inner.playerName.surname}`,
|
|
3250
3324
|
jumperNumber: item.player.jumperNumber ?? null,
|
|
3251
|
-
kicks: toNullable(stats
|
|
3252
|
-
handballs: toNullable(stats
|
|
3253
|
-
disposals: toNullable(stats
|
|
3254
|
-
marks: toNullable(stats
|
|
3255
|
-
goals: toNullable(stats
|
|
3256
|
-
behinds: toNullable(stats
|
|
3257
|
-
tackles: toNullable(stats
|
|
3258
|
-
hitouts: toNullable(stats
|
|
3259
|
-
freesFor: toNullable(stats
|
|
3260
|
-
freesAgainst: toNullable(stats
|
|
3261
|
-
contestedPossessions: toNullable(stats
|
|
3262
|
-
uncontestedPossessions: toNullable(stats
|
|
3263
|
-
contestedMarks: toNullable(stats
|
|
3264
|
-
intercepts: toNullable(stats
|
|
3325
|
+
kicks: toNullable(stats?.kicks),
|
|
3326
|
+
handballs: toNullable(stats?.handballs),
|
|
3327
|
+
disposals: toNullable(stats?.disposals),
|
|
3328
|
+
marks: toNullable(stats?.marks),
|
|
3329
|
+
goals: toNullable(stats?.goals),
|
|
3330
|
+
behinds: toNullable(stats?.behinds),
|
|
3331
|
+
tackles: toNullable(stats?.tackles),
|
|
3332
|
+
hitouts: toNullable(stats?.hitouts),
|
|
3333
|
+
freesFor: toNullable(stats?.freesFor),
|
|
3334
|
+
freesAgainst: toNullable(stats?.freesAgainst),
|
|
3335
|
+
contestedPossessions: toNullable(stats?.contestedPossessions),
|
|
3336
|
+
uncontestedPossessions: toNullable(stats?.uncontestedPossessions),
|
|
3337
|
+
contestedMarks: toNullable(stats?.contestedMarks),
|
|
3338
|
+
intercepts: toNullable(stats?.intercepts),
|
|
3265
3339
|
centreClearances: toNullable(clearances?.centreClearances),
|
|
3266
3340
|
stoppageClearances: toNullable(clearances?.stoppageClearances),
|
|
3267
3341
|
totalClearances: toNullable(clearances?.totalClearances),
|
|
3268
|
-
inside50s: toNullable(stats
|
|
3269
|
-
rebound50s: toNullable(stats
|
|
3270
|
-
clangers: toNullable(stats
|
|
3271
|
-
turnovers: toNullable(stats
|
|
3272
|
-
onePercenters: toNullable(stats
|
|
3273
|
-
bounces: toNullable(stats
|
|
3274
|
-
goalAssists: toNullable(stats
|
|
3275
|
-
disposalEfficiency: toNullable(stats
|
|
3276
|
-
metresGained: toNullable(stats
|
|
3277
|
-
goalAccuracy: toNullable(stats
|
|
3278
|
-
marksInside50: toNullable(stats
|
|
3279
|
-
tacklesInside50: toNullable(stats
|
|
3280
|
-
shotsAtGoal: toNullable(stats
|
|
3281
|
-
scoreInvolvements: toNullable(stats
|
|
3282
|
-
totalPossessions: toNullable(stats
|
|
3283
|
-
timeOnGroundPercentage: toNullable(item.playerStats
|
|
3284
|
-
ratingPoints: toNullable(stats
|
|
3285
|
-
dreamTeamPoints: toNullable(stats
|
|
3286
|
-
effectiveDisposals: toNullable(stats
|
|
3287
|
-
effectiveKicks: toNullable(stats
|
|
3288
|
-
kickEfficiency: toNullable(stats
|
|
3289
|
-
kickToHandballRatio: toNullable(stats
|
|
3290
|
-
pressureActs: toNullable(stats
|
|
3291
|
-
defHalfPressureActs: toNullable(stats
|
|
3292
|
-
spoils: toNullable(stats
|
|
3293
|
-
hitoutsToAdvantage: toNullable(stats
|
|
3294
|
-
hitoutWinPercentage: toNullable(stats
|
|
3295
|
-
hitoutToAdvantageRate: toNullable(stats
|
|
3296
|
-
groundBallGets: toNullable(stats
|
|
3297
|
-
f50GroundBallGets: toNullable(stats
|
|
3298
|
-
interceptMarks: toNullable(stats
|
|
3299
|
-
marksOnLead: toNullable(stats
|
|
3300
|
-
contestedPossessionRate: toNullable(stats
|
|
3301
|
-
contestOffOneOnOnes: toNullable(stats
|
|
3302
|
-
contestOffWins: toNullable(stats
|
|
3303
|
-
contestOffWinsPercentage: toNullable(stats
|
|
3304
|
-
contestDefOneOnOnes: toNullable(stats
|
|
3305
|
-
contestDefLosses: toNullable(stats
|
|
3306
|
-
contestDefLossPercentage: toNullable(stats
|
|
3307
|
-
centreBounceAttendances: toNullable(stats
|
|
3308
|
-
kickins: toNullable(stats
|
|
3309
|
-
kickinsPlayon: toNullable(stats
|
|
3310
|
-
ruckContests: toNullable(stats
|
|
3311
|
-
scoreLaunches: toNullable(stats
|
|
3342
|
+
inside50s: toNullable(stats?.inside50s),
|
|
3343
|
+
rebound50s: toNullable(stats?.rebound50s),
|
|
3344
|
+
clangers: toNullable(stats?.clangers),
|
|
3345
|
+
turnovers: toNullable(stats?.turnovers),
|
|
3346
|
+
onePercenters: toNullable(stats?.onePercenters),
|
|
3347
|
+
bounces: toNullable(stats?.bounces),
|
|
3348
|
+
goalAssists: toNullable(stats?.goalAssists),
|
|
3349
|
+
disposalEfficiency: toNullable(stats?.disposalEfficiency),
|
|
3350
|
+
metresGained: toNullable(stats?.metresGained),
|
|
3351
|
+
goalAccuracy: toNullable(stats?.goalAccuracy),
|
|
3352
|
+
marksInside50: toNullable(stats?.marksInside50),
|
|
3353
|
+
tacklesInside50: toNullable(stats?.tacklesInside50),
|
|
3354
|
+
shotsAtGoal: toNullable(stats?.shotsAtGoal),
|
|
3355
|
+
scoreInvolvements: toNullable(stats?.scoreInvolvements),
|
|
3356
|
+
totalPossessions: toNullable(stats?.totalPossessions),
|
|
3357
|
+
timeOnGroundPercentage: toNullable(item.playerStats?.timeOnGroundPercentage),
|
|
3358
|
+
ratingPoints: toNullable(stats?.ratingPoints),
|
|
3359
|
+
dreamTeamPoints: toNullable(stats?.dreamTeamPoints),
|
|
3360
|
+
effectiveDisposals: toNullable(stats?.extendedStats?.effectiveDisposals),
|
|
3361
|
+
effectiveKicks: toNullable(stats?.extendedStats?.effectiveKicks),
|
|
3362
|
+
kickEfficiency: toNullable(stats?.extendedStats?.kickEfficiency),
|
|
3363
|
+
kickToHandballRatio: toNullable(stats?.extendedStats?.kickToHandballRatio),
|
|
3364
|
+
pressureActs: toNullable(stats?.extendedStats?.pressureActs),
|
|
3365
|
+
defHalfPressureActs: toNullable(stats?.extendedStats?.defHalfPressureActs),
|
|
3366
|
+
spoils: toNullable(stats?.extendedStats?.spoils),
|
|
3367
|
+
hitoutsToAdvantage: toNullable(stats?.extendedStats?.hitoutsToAdvantage),
|
|
3368
|
+
hitoutWinPercentage: toNullable(stats?.extendedStats?.hitoutWinPercentage),
|
|
3369
|
+
hitoutToAdvantageRate: toNullable(stats?.extendedStats?.hitoutToAdvantageRate),
|
|
3370
|
+
groundBallGets: toNullable(stats?.extendedStats?.groundBallGets),
|
|
3371
|
+
f50GroundBallGets: toNullable(stats?.extendedStats?.f50GroundBallGets),
|
|
3372
|
+
interceptMarks: toNullable(stats?.extendedStats?.interceptMarks),
|
|
3373
|
+
marksOnLead: toNullable(stats?.extendedStats?.marksOnLead),
|
|
3374
|
+
contestedPossessionRate: toNullable(stats?.extendedStats?.contestedPossessionRate),
|
|
3375
|
+
contestOffOneOnOnes: toNullable(stats?.extendedStats?.contestOffOneOnOnes),
|
|
3376
|
+
contestOffWins: toNullable(stats?.extendedStats?.contestOffWins),
|
|
3377
|
+
contestOffWinsPercentage: toNullable(stats?.extendedStats?.contestOffWinsPercentage),
|
|
3378
|
+
contestDefOneOnOnes: toNullable(stats?.extendedStats?.contestDefOneOnOnes),
|
|
3379
|
+
contestDefLosses: toNullable(stats?.extendedStats?.contestDefLosses),
|
|
3380
|
+
contestDefLossPercentage: toNullable(stats?.extendedStats?.contestDefLossPercentage),
|
|
3381
|
+
centreBounceAttendances: toNullable(stats?.extendedStats?.centreBounceAttendances),
|
|
3382
|
+
kickins: toNullable(stats?.extendedStats?.kickins),
|
|
3383
|
+
kickinsPlayon: toNullable(stats?.extendedStats?.kickinsPlayon),
|
|
3384
|
+
ruckContests: toNullable(stats?.extendedStats?.ruckContests),
|
|
3385
|
+
scoreLaunches: toNullable(stats?.extendedStats?.scoreLaunches),
|
|
3312
3386
|
source
|
|
3313
3387
|
};
|
|
3314
3388
|
}
|
|
@@ -3334,11 +3408,11 @@ async function fetchPlayerStats(query) {
|
|
|
3334
3408
|
client.fetchPlayerStats(query.matchId)
|
|
3335
3409
|
]);
|
|
3336
3410
|
if (!statsResult.success) return statsResult;
|
|
3337
|
-
const teamIdMap2 =
|
|
3411
|
+
const teamIdMap2 = new Map(AFL_API_TEAM_IDS);
|
|
3338
3412
|
if (rosterResult.success) {
|
|
3339
3413
|
const match = rosterResult.data.match;
|
|
3340
|
-
teamIdMap2.set(match.homeTeamId, match.homeTeam.name);
|
|
3341
|
-
teamIdMap2.set(match.awayTeamId, match.awayTeam.name);
|
|
3414
|
+
teamIdMap2.set(match.homeTeamId, normaliseTeamName(match.homeTeam.name));
|
|
3415
|
+
teamIdMap2.set(match.awayTeamId, normaliseTeamName(match.awayTeam.name));
|
|
3342
3416
|
}
|
|
3343
3417
|
return ok(
|
|
3344
3418
|
transformPlayerStats(
|
|
@@ -3348,7 +3422,7 @@ async function fetchPlayerStats(query) {
|
|
|
3348
3422
|
query.round ?? 0,
|
|
3349
3423
|
competition,
|
|
3350
3424
|
"afl-api",
|
|
3351
|
-
teamIdMap2
|
|
3425
|
+
teamIdMap2
|
|
3352
3426
|
)
|
|
3353
3427
|
);
|
|
3354
3428
|
}
|
|
@@ -3391,6 +3465,7 @@ async function fetchPlayerStats(query) {
|
|
|
3391
3465
|
return ok(allStats);
|
|
3392
3466
|
}
|
|
3393
3467
|
case "footywire": {
|
|
3468
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
3394
3469
|
const fwClient = new FootyWireClient();
|
|
3395
3470
|
const idsResult = await fwClient.fetchSeasonMatchIds(query.season);
|
|
3396
3471
|
if (!idsResult.success) return idsResult;
|
|
@@ -3420,6 +3495,7 @@ async function fetchPlayerStats(query) {
|
|
|
3420
3495
|
return ok(allStats);
|
|
3421
3496
|
}
|
|
3422
3497
|
case "afl-tables": {
|
|
3498
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
3423
3499
|
const atClient = new AflTablesClient();
|
|
3424
3500
|
const atResult = await atClient.fetchSeasonPlayerStats(query.season);
|
|
3425
3501
|
if (!atResult.success) return atResult;
|
|
@@ -3443,7 +3519,39 @@ async function fetchTeamStats(query) {
|
|
|
3443
3519
|
}
|
|
3444
3520
|
case "afl-tables": {
|
|
3445
3521
|
const client = new AflTablesClient();
|
|
3446
|
-
|
|
3522
|
+
const statsResult = await client.fetchTeamStats(query.season);
|
|
3523
|
+
if (!statsResult.success) return statsResult;
|
|
3524
|
+
const needsGp = statsResult.data.some((e) => e.gamesPlayed === 0);
|
|
3525
|
+
const gpMap = /* @__PURE__ */ new Map();
|
|
3526
|
+
if (needsGp) {
|
|
3527
|
+
const resultsResult = await client.fetchSeasonResults(query.season);
|
|
3528
|
+
if (resultsResult.success) {
|
|
3529
|
+
for (const m of resultsResult.data) {
|
|
3530
|
+
const home = normaliseTeamName(m.homeTeam);
|
|
3531
|
+
const away = normaliseTeamName(m.awayTeam);
|
|
3532
|
+
gpMap.set(home, (gpMap.get(home) ?? 0) + 1);
|
|
3533
|
+
gpMap.set(away, (gpMap.get(away) ?? 0) + 1);
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
const enriched = statsResult.data.map((entry) => ({
|
|
3538
|
+
...entry,
|
|
3539
|
+
gamesPlayed: gpMap.get(normaliseTeamName(entry.team)) ?? entry.gamesPlayed
|
|
3540
|
+
}));
|
|
3541
|
+
if (summaryType === "averages") {
|
|
3542
|
+
return ok(
|
|
3543
|
+
enriched.map((entry) => ({
|
|
3544
|
+
...entry,
|
|
3545
|
+
stats: Object.fromEntries(
|
|
3546
|
+
Object.entries(entry.stats).map(([k, v]) => [
|
|
3547
|
+
k,
|
|
3548
|
+
entry.gamesPlayed > 0 ? +(v / entry.gamesPlayed).toFixed(1) : 0
|
|
3549
|
+
])
|
|
3550
|
+
)
|
|
3551
|
+
}))
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
3554
|
+
return ok(enriched);
|
|
3447
3555
|
}
|
|
3448
3556
|
case "afl-api":
|
|
3449
3557
|
case "squiggle":
|
|
@@ -3462,19 +3570,30 @@ async function fetchTeamStats(query) {
|
|
|
3462
3570
|
function teamTypeForComp(comp) {
|
|
3463
3571
|
return comp === "AFLW" ? "WOMEN" : "MEN";
|
|
3464
3572
|
}
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
const teamType = query?.teamType ?? teamTypeForComp(query?.competition ?? "AFLM");
|
|
3468
|
-
const result = await client.fetchTeams(teamType);
|
|
3469
|
-
if (!result.success) return result;
|
|
3470
|
-
const competition = query?.competition ?? "AFLM";
|
|
3471
|
-
const teams = result.data.map((t) => ({
|
|
3573
|
+
function toTeams(data, competition) {
|
|
3574
|
+
return data.map((t) => ({
|
|
3472
3575
|
teamId: String(t.id),
|
|
3473
3576
|
name: normaliseTeamName(t.name),
|
|
3474
3577
|
abbreviation: t.abbreviation ?? "",
|
|
3475
3578
|
competition
|
|
3476
3579
|
})).filter((t) => AFL_SENIOR_TEAMS.has(t.name));
|
|
3477
|
-
|
|
3580
|
+
}
|
|
3581
|
+
async function fetchTeams(query) {
|
|
3582
|
+
const client = new AflApiClient();
|
|
3583
|
+
if (!query?.competition && !query?.teamType) {
|
|
3584
|
+
const [menResult, womenResult] = await Promise.all([
|
|
3585
|
+
client.fetchTeams("MEN"),
|
|
3586
|
+
client.fetchTeams("WOMEN")
|
|
3587
|
+
]);
|
|
3588
|
+
if (!menResult.success) return menResult;
|
|
3589
|
+
if (!womenResult.success) return womenResult;
|
|
3590
|
+
return ok([...toTeams(menResult.data, "AFLM"), ...toTeams(womenResult.data, "AFLW")]);
|
|
3591
|
+
}
|
|
3592
|
+
const competition = query?.competition ?? "AFLM";
|
|
3593
|
+
const teamType = query?.teamType ?? teamTypeForComp(competition);
|
|
3594
|
+
const result = await client.fetchTeams(teamType);
|
|
3595
|
+
if (!result.success) return result;
|
|
3596
|
+
return ok(toTeams(result.data, competition));
|
|
3478
3597
|
}
|
|
3479
3598
|
async function fetchSquad(query) {
|
|
3480
3599
|
const client = new AflApiClient();
|
|
@@ -3495,8 +3614,8 @@ async function fetchSquad(query) {
|
|
|
3495
3614
|
jumperNumber: p.jumperNumber ?? null,
|
|
3496
3615
|
position: p.position ?? null,
|
|
3497
3616
|
dateOfBirth: p.player.dateOfBirth ? new Date(p.player.dateOfBirth) : null,
|
|
3498
|
-
heightCm: p.player.heightInCm
|
|
3499
|
-
weightKg: p.player.weightInKg
|
|
3617
|
+
heightCm: p.player.heightInCm || null,
|
|
3618
|
+
weightKg: p.player.weightInKg || null,
|
|
3500
3619
|
draftYear: p.player.draftYear ? Number.parseInt(p.player.draftYear, 10) || null : null,
|
|
3501
3620
|
draftPosition: p.player.draftPosition ? Number.parseInt(p.player.draftPosition, 10) || null : null,
|
|
3502
3621
|
draftType: p.player.draftType ?? null,
|