fitzroy 1.2.0 → 1.3.0
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 +344 -131
- package/dist/index.d.ts +253 -249
- package/dist/index.js +196 -80
- 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,14 @@ 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
|
+
]).nullable().optional();
|
|
1532
1589
|
var PlayerGameStatsSchema = z.object({
|
|
1533
1590
|
goals: statNum,
|
|
1534
1591
|
behinds: statNum,
|
|
@@ -1606,8 +1663,8 @@ var PlayerStatsItemSchema = z.object({
|
|
|
1606
1663
|
teamId: z.string(),
|
|
1607
1664
|
playerStats: z.object({
|
|
1608
1665
|
stats: PlayerGameStatsSchema,
|
|
1609
|
-
timeOnGroundPercentage:
|
|
1610
|
-
}).passthrough()
|
|
1666
|
+
timeOnGroundPercentage: statNum
|
|
1667
|
+
}).passthrough().nullable().optional()
|
|
1611
1668
|
}).passthrough();
|
|
1612
1669
|
var PlayerStatsListSchema = z.object({
|
|
1613
1670
|
homeTeamPlayerStats: z.array(PlayerStatsItemSchema),
|
|
@@ -2284,12 +2341,14 @@ function toFixture(item, season, fallbackRoundNumber, competition) {
|
|
|
2284
2341
|
async function fetchFixture(query) {
|
|
2285
2342
|
const competition = query.competition ?? "AFLM";
|
|
2286
2343
|
if (query.source === "squiggle") {
|
|
2344
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("squiggle"));
|
|
2287
2345
|
const client2 = new SquiggleClient();
|
|
2288
2346
|
const result = await client2.fetchGames(query.season, query.round ?? void 0);
|
|
2289
2347
|
if (!result.success) return result;
|
|
2290
2348
|
return ok(transformSquiggleGamesToFixture(result.data.games, query.season));
|
|
2291
2349
|
}
|
|
2292
2350
|
if (query.source === "footywire") {
|
|
2351
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
2293
2352
|
const fwClient = new FootyWireClient();
|
|
2294
2353
|
const result = await fwClient.fetchSeasonFixture(query.season);
|
|
2295
2354
|
if (!result.success) return result;
|
|
@@ -2633,6 +2692,7 @@ function parseSeasonPage(html, year) {
|
|
|
2633
2692
|
const results = [];
|
|
2634
2693
|
let currentRound = 0;
|
|
2635
2694
|
let currentRoundType = "HomeAndAway";
|
|
2695
|
+
let lastHARound = 0;
|
|
2636
2696
|
let matchCounter = 0;
|
|
2637
2697
|
$("table").each((_i, table) => {
|
|
2638
2698
|
const $table = $(table);
|
|
@@ -2642,10 +2702,14 @@ function parseSeasonPage(html, year) {
|
|
|
2642
2702
|
if (roundMatch?.[1] && border !== "1") {
|
|
2643
2703
|
currentRound = Number.parseInt(roundMatch[1], 10);
|
|
2644
2704
|
currentRoundType = inferRoundType(text);
|
|
2705
|
+
if (currentRoundType === "HomeAndAway") {
|
|
2706
|
+
lastHARound = currentRound;
|
|
2707
|
+
}
|
|
2645
2708
|
return;
|
|
2646
2709
|
}
|
|
2647
2710
|
if (border !== "1" && inferRoundType(text) === "Finals") {
|
|
2648
2711
|
currentRoundType = "Finals";
|
|
2712
|
+
currentRound = finalsRoundNumber(text, lastHARound);
|
|
2649
2713
|
return;
|
|
2650
2714
|
}
|
|
2651
2715
|
if (border !== "1") return;
|
|
@@ -2960,6 +3024,7 @@ function transformLadderEntries(entries) {
|
|
|
2960
3024
|
async function fetchLadder(query) {
|
|
2961
3025
|
const competition = query.competition ?? "AFLM";
|
|
2962
3026
|
if (query.source === "squiggle") {
|
|
3027
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("squiggle"));
|
|
2963
3028
|
const client2 = new SquiggleClient();
|
|
2964
3029
|
const result = await client2.fetchStandings(query.season, query.round ?? void 0);
|
|
2965
3030
|
if (!result.success) return result;
|
|
@@ -2971,6 +3036,7 @@ async function fetchLadder(query) {
|
|
|
2971
3036
|
});
|
|
2972
3037
|
}
|
|
2973
3038
|
if (query.source === "afl-tables") {
|
|
3039
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
2974
3040
|
const atClient = new AflTablesClient();
|
|
2975
3041
|
const resultsResult = await atClient.fetchSeasonResults(query.season);
|
|
2976
3042
|
if (!resultsResult.success) return resultsResult;
|
|
@@ -3105,6 +3171,7 @@ async function fetchMatchResults(query) {
|
|
|
3105
3171
|
return ok(transformMatchItems(itemsResult.data, query.season, competition));
|
|
3106
3172
|
}
|
|
3107
3173
|
case "footywire": {
|
|
3174
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
3108
3175
|
const client = new FootyWireClient();
|
|
3109
3176
|
const result = await client.fetchSeasonResults(query.season);
|
|
3110
3177
|
if (!result.success) return result;
|
|
@@ -3114,6 +3181,7 @@ async function fetchMatchResults(query) {
|
|
|
3114
3181
|
return result;
|
|
3115
3182
|
}
|
|
3116
3183
|
case "afl-tables": {
|
|
3184
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
3117
3185
|
const client = new AflTablesClient();
|
|
3118
3186
|
const result = await client.fetchSeasonResults(query.season);
|
|
3119
3187
|
if (!result.success) return result;
|
|
@@ -3123,6 +3191,7 @@ async function fetchMatchResults(query) {
|
|
|
3123
3191
|
return result;
|
|
3124
3192
|
}
|
|
3125
3193
|
case "squiggle": {
|
|
3194
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("squiggle"));
|
|
3126
3195
|
const client = new SquiggleClient();
|
|
3127
3196
|
const result = await client.fetchGames(query.season, query.round ?? void 0, 100);
|
|
3128
3197
|
if (!result.success) return result;
|
|
@@ -3148,7 +3217,7 @@ async function resolveTeamId(client, teamName, competition) {
|
|
|
3148
3217
|
async function fetchFromAflApi(query) {
|
|
3149
3218
|
const client = new AflApiClient();
|
|
3150
3219
|
const competition = query.competition ?? "AFLM";
|
|
3151
|
-
const season = query.season ?? (
|
|
3220
|
+
const season = query.season ?? resolveDefaultSeason(competition);
|
|
3152
3221
|
const [teamIdResult, seasonResult] = await Promise.all([
|
|
3153
3222
|
resolveTeamId(client, query.team, competition),
|
|
3154
3223
|
client.resolveCompSeason(competition, season)
|
|
@@ -3171,8 +3240,8 @@ async function fetchFromAflApi(query) {
|
|
|
3171
3240
|
jumperNumber: p.jumperNumber ?? null,
|
|
3172
3241
|
position: p.position ?? null,
|
|
3173
3242
|
dateOfBirth: p.player.dateOfBirth ?? null,
|
|
3174
|
-
heightCm: p.player.heightInCm
|
|
3175
|
-
weightKg: p.player.weightInKg
|
|
3243
|
+
heightCm: p.player.heightInCm || null,
|
|
3244
|
+
weightKg: p.player.weightInKg || null,
|
|
3176
3245
|
gamesPlayed: null,
|
|
3177
3246
|
goals: null,
|
|
3178
3247
|
draftYear: p.player.draftYear ? Number.parseInt(p.player.draftYear, 10) || null : null,
|
|
@@ -3186,8 +3255,9 @@ async function fetchFromAflApi(query) {
|
|
|
3186
3255
|
return ok(players);
|
|
3187
3256
|
}
|
|
3188
3257
|
async function fetchFromFootyWire(query) {
|
|
3189
|
-
const client = new FootyWireClient();
|
|
3190
3258
|
const competition = query.competition ?? "AFLM";
|
|
3259
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
3260
|
+
const client = new FootyWireClient();
|
|
3191
3261
|
const teamName = normaliseTeamName(query.team);
|
|
3192
3262
|
const result = await client.fetchPlayerList(teamName);
|
|
3193
3263
|
if (!result.success) return result;
|
|
@@ -3199,8 +3269,9 @@ async function fetchFromFootyWire(query) {
|
|
|
3199
3269
|
return ok(players);
|
|
3200
3270
|
}
|
|
3201
3271
|
async function fetchFromAflTables(query) {
|
|
3202
|
-
const client = new AflTablesClient();
|
|
3203
3272
|
const competition = query.competition ?? "AFLM";
|
|
3273
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
3274
|
+
const client = new AflTablesClient();
|
|
3204
3275
|
const teamName = normaliseTeamName(query.team);
|
|
3205
3276
|
const result = await client.fetchPlayerList(teamName);
|
|
3206
3277
|
if (!result.success) return result;
|
|
@@ -3235,80 +3306,82 @@ function toNullable(value) {
|
|
|
3235
3306
|
}
|
|
3236
3307
|
function transformOne(item, matchId, season, roundNumber, competition, source, teamIdMap) {
|
|
3237
3308
|
const inner = item.player.player.player;
|
|
3238
|
-
const stats = item.playerStats
|
|
3239
|
-
const clearances = stats
|
|
3309
|
+
const stats = item.playerStats?.stats;
|
|
3310
|
+
const clearances = stats?.clearances;
|
|
3240
3311
|
return {
|
|
3241
3312
|
matchId,
|
|
3242
3313
|
season,
|
|
3243
3314
|
roundNumber,
|
|
3244
|
-
team: normaliseTeamName(
|
|
3315
|
+
team: normaliseTeamName(
|
|
3316
|
+
teamIdMap?.get(item.teamId) ?? AFL_API_TEAM_IDS.get(item.teamId) ?? item.teamId
|
|
3317
|
+
),
|
|
3245
3318
|
competition,
|
|
3246
3319
|
playerId: inner.playerId,
|
|
3247
3320
|
givenName: inner.playerName.givenName,
|
|
3248
3321
|
surname: inner.playerName.surname,
|
|
3249
3322
|
displayName: `${inner.playerName.givenName} ${inner.playerName.surname}`,
|
|
3250
3323
|
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
|
|
3324
|
+
kicks: toNullable(stats?.kicks),
|
|
3325
|
+
handballs: toNullable(stats?.handballs),
|
|
3326
|
+
disposals: toNullable(stats?.disposals),
|
|
3327
|
+
marks: toNullable(stats?.marks),
|
|
3328
|
+
goals: toNullable(stats?.goals),
|
|
3329
|
+
behinds: toNullable(stats?.behinds),
|
|
3330
|
+
tackles: toNullable(stats?.tackles),
|
|
3331
|
+
hitouts: toNullable(stats?.hitouts),
|
|
3332
|
+
freesFor: toNullable(stats?.freesFor),
|
|
3333
|
+
freesAgainst: toNullable(stats?.freesAgainst),
|
|
3334
|
+
contestedPossessions: toNullable(stats?.contestedPossessions),
|
|
3335
|
+
uncontestedPossessions: toNullable(stats?.uncontestedPossessions),
|
|
3336
|
+
contestedMarks: toNullable(stats?.contestedMarks),
|
|
3337
|
+
intercepts: toNullable(stats?.intercepts),
|
|
3265
3338
|
centreClearances: toNullable(clearances?.centreClearances),
|
|
3266
3339
|
stoppageClearances: toNullable(clearances?.stoppageClearances),
|
|
3267
3340
|
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
|
|
3341
|
+
inside50s: toNullable(stats?.inside50s),
|
|
3342
|
+
rebound50s: toNullable(stats?.rebound50s),
|
|
3343
|
+
clangers: toNullable(stats?.clangers),
|
|
3344
|
+
turnovers: toNullable(stats?.turnovers),
|
|
3345
|
+
onePercenters: toNullable(stats?.onePercenters),
|
|
3346
|
+
bounces: toNullable(stats?.bounces),
|
|
3347
|
+
goalAssists: toNullable(stats?.goalAssists),
|
|
3348
|
+
disposalEfficiency: toNullable(stats?.disposalEfficiency),
|
|
3349
|
+
metresGained: toNullable(stats?.metresGained),
|
|
3350
|
+
goalAccuracy: toNullable(stats?.goalAccuracy),
|
|
3351
|
+
marksInside50: toNullable(stats?.marksInside50),
|
|
3352
|
+
tacklesInside50: toNullable(stats?.tacklesInside50),
|
|
3353
|
+
shotsAtGoal: toNullable(stats?.shotsAtGoal),
|
|
3354
|
+
scoreInvolvements: toNullable(stats?.scoreInvolvements),
|
|
3355
|
+
totalPossessions: toNullable(stats?.totalPossessions),
|
|
3356
|
+
timeOnGroundPercentage: toNullable(item.playerStats?.timeOnGroundPercentage),
|
|
3357
|
+
ratingPoints: toNullable(stats?.ratingPoints),
|
|
3358
|
+
dreamTeamPoints: toNullable(stats?.dreamTeamPoints),
|
|
3359
|
+
effectiveDisposals: toNullable(stats?.extendedStats?.effectiveDisposals),
|
|
3360
|
+
effectiveKicks: toNullable(stats?.extendedStats?.effectiveKicks),
|
|
3361
|
+
kickEfficiency: toNullable(stats?.extendedStats?.kickEfficiency),
|
|
3362
|
+
kickToHandballRatio: toNullable(stats?.extendedStats?.kickToHandballRatio),
|
|
3363
|
+
pressureActs: toNullable(stats?.extendedStats?.pressureActs),
|
|
3364
|
+
defHalfPressureActs: toNullable(stats?.extendedStats?.defHalfPressureActs),
|
|
3365
|
+
spoils: toNullable(stats?.extendedStats?.spoils),
|
|
3366
|
+
hitoutsToAdvantage: toNullable(stats?.extendedStats?.hitoutsToAdvantage),
|
|
3367
|
+
hitoutWinPercentage: toNullable(stats?.extendedStats?.hitoutWinPercentage),
|
|
3368
|
+
hitoutToAdvantageRate: toNullable(stats?.extendedStats?.hitoutToAdvantageRate),
|
|
3369
|
+
groundBallGets: toNullable(stats?.extendedStats?.groundBallGets),
|
|
3370
|
+
f50GroundBallGets: toNullable(stats?.extendedStats?.f50GroundBallGets),
|
|
3371
|
+
interceptMarks: toNullable(stats?.extendedStats?.interceptMarks),
|
|
3372
|
+
marksOnLead: toNullable(stats?.extendedStats?.marksOnLead),
|
|
3373
|
+
contestedPossessionRate: toNullable(stats?.extendedStats?.contestedPossessionRate),
|
|
3374
|
+
contestOffOneOnOnes: toNullable(stats?.extendedStats?.contestOffOneOnOnes),
|
|
3375
|
+
contestOffWins: toNullable(stats?.extendedStats?.contestOffWins),
|
|
3376
|
+
contestOffWinsPercentage: toNullable(stats?.extendedStats?.contestOffWinsPercentage),
|
|
3377
|
+
contestDefOneOnOnes: toNullable(stats?.extendedStats?.contestDefOneOnOnes),
|
|
3378
|
+
contestDefLosses: toNullable(stats?.extendedStats?.contestDefLosses),
|
|
3379
|
+
contestDefLossPercentage: toNullable(stats?.extendedStats?.contestDefLossPercentage),
|
|
3380
|
+
centreBounceAttendances: toNullable(stats?.extendedStats?.centreBounceAttendances),
|
|
3381
|
+
kickins: toNullable(stats?.extendedStats?.kickins),
|
|
3382
|
+
kickinsPlayon: toNullable(stats?.extendedStats?.kickinsPlayon),
|
|
3383
|
+
ruckContests: toNullable(stats?.extendedStats?.ruckContests),
|
|
3384
|
+
scoreLaunches: toNullable(stats?.extendedStats?.scoreLaunches),
|
|
3312
3385
|
source
|
|
3313
3386
|
};
|
|
3314
3387
|
}
|
|
@@ -3391,6 +3464,7 @@ async function fetchPlayerStats(query) {
|
|
|
3391
3464
|
return ok(allStats);
|
|
3392
3465
|
}
|
|
3393
3466
|
case "footywire": {
|
|
3467
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
|
|
3394
3468
|
const fwClient = new FootyWireClient();
|
|
3395
3469
|
const idsResult = await fwClient.fetchSeasonMatchIds(query.season);
|
|
3396
3470
|
if (!idsResult.success) return idsResult;
|
|
@@ -3420,6 +3494,7 @@ async function fetchPlayerStats(query) {
|
|
|
3420
3494
|
return ok(allStats);
|
|
3421
3495
|
}
|
|
3422
3496
|
case "afl-tables": {
|
|
3497
|
+
if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
|
|
3423
3498
|
const atClient = new AflTablesClient();
|
|
3424
3499
|
const atResult = await atClient.fetchSeasonPlayerStats(query.season);
|
|
3425
3500
|
if (!atResult.success) return atResult;
|
|
@@ -3443,7 +3518,37 @@ async function fetchTeamStats(query) {
|
|
|
3443
3518
|
}
|
|
3444
3519
|
case "afl-tables": {
|
|
3445
3520
|
const client = new AflTablesClient();
|
|
3446
|
-
|
|
3521
|
+
const statsResult = await client.fetchTeamStats(query.season);
|
|
3522
|
+
if (!statsResult.success) return statsResult;
|
|
3523
|
+
const needsGp = statsResult.data.some((e) => e.gamesPlayed === 0);
|
|
3524
|
+
const gpMap = /* @__PURE__ */ new Map();
|
|
3525
|
+
if (needsGp) {
|
|
3526
|
+
const resultsResult = await client.fetchSeasonResults(query.season);
|
|
3527
|
+
if (resultsResult.success) {
|
|
3528
|
+
for (const m of resultsResult.data) {
|
|
3529
|
+
gpMap.set(m.homeTeam, (gpMap.get(m.homeTeam) ?? 0) + 1);
|
|
3530
|
+
gpMap.set(m.awayTeam, (gpMap.get(m.awayTeam) ?? 0) + 1);
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
const enriched = statsResult.data.map((entry) => ({
|
|
3535
|
+
...entry,
|
|
3536
|
+
gamesPlayed: gpMap.get(entry.team) ?? entry.gamesPlayed
|
|
3537
|
+
}));
|
|
3538
|
+
if (summaryType === "averages") {
|
|
3539
|
+
return ok(
|
|
3540
|
+
enriched.map((entry) => ({
|
|
3541
|
+
...entry,
|
|
3542
|
+
stats: Object.fromEntries(
|
|
3543
|
+
Object.entries(entry.stats).map(([k, v]) => [
|
|
3544
|
+
k,
|
|
3545
|
+
entry.gamesPlayed > 0 ? +(v / entry.gamesPlayed).toFixed(1) : 0
|
|
3546
|
+
])
|
|
3547
|
+
)
|
|
3548
|
+
}))
|
|
3549
|
+
);
|
|
3550
|
+
}
|
|
3551
|
+
return ok(enriched);
|
|
3447
3552
|
}
|
|
3448
3553
|
case "afl-api":
|
|
3449
3554
|
case "squiggle":
|
|
@@ -3462,19 +3567,30 @@ async function fetchTeamStats(query) {
|
|
|
3462
3567
|
function teamTypeForComp(comp) {
|
|
3463
3568
|
return comp === "AFLW" ? "WOMEN" : "MEN";
|
|
3464
3569
|
}
|
|
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) => ({
|
|
3570
|
+
function toTeams(data, competition) {
|
|
3571
|
+
return data.map((t) => ({
|
|
3472
3572
|
teamId: String(t.id),
|
|
3473
3573
|
name: normaliseTeamName(t.name),
|
|
3474
3574
|
abbreviation: t.abbreviation ?? "",
|
|
3475
3575
|
competition
|
|
3476
3576
|
})).filter((t) => AFL_SENIOR_TEAMS.has(t.name));
|
|
3477
|
-
|
|
3577
|
+
}
|
|
3578
|
+
async function fetchTeams(query) {
|
|
3579
|
+
const client = new AflApiClient();
|
|
3580
|
+
if (!query?.competition && !query?.teamType) {
|
|
3581
|
+
const [menResult, womenResult] = await Promise.all([
|
|
3582
|
+
client.fetchTeams("MEN"),
|
|
3583
|
+
client.fetchTeams("WOMEN")
|
|
3584
|
+
]);
|
|
3585
|
+
if (!menResult.success) return menResult;
|
|
3586
|
+
if (!womenResult.success) return womenResult;
|
|
3587
|
+
return ok([...toTeams(menResult.data, "AFLM"), ...toTeams(womenResult.data, "AFLW")]);
|
|
3588
|
+
}
|
|
3589
|
+
const competition = query?.competition ?? "AFLM";
|
|
3590
|
+
const teamType = query?.teamType ?? teamTypeForComp(competition);
|
|
3591
|
+
const result = await client.fetchTeams(teamType);
|
|
3592
|
+
if (!result.success) return result;
|
|
3593
|
+
return ok(toTeams(result.data, competition));
|
|
3478
3594
|
}
|
|
3479
3595
|
async function fetchSquad(query) {
|
|
3480
3596
|
const client = new AflApiClient();
|
|
@@ -3495,8 +3611,8 @@ async function fetchSquad(query) {
|
|
|
3495
3611
|
jumperNumber: p.jumperNumber ?? null,
|
|
3496
3612
|
position: p.position ?? null,
|
|
3497
3613
|
dateOfBirth: p.player.dateOfBirth ? new Date(p.player.dateOfBirth) : null,
|
|
3498
|
-
heightCm: p.player.heightInCm
|
|
3499
|
-
weightKg: p.player.weightInKg
|
|
3614
|
+
heightCm: p.player.heightInCm || null,
|
|
3615
|
+
weightKg: p.player.weightInKg || null,
|
|
3500
3616
|
draftYear: p.player.draftYear ? Number.parseInt(p.player.draftYear, 10) || null : null,
|
|
3501
3617
|
draftPosition: p.player.draftPosition ? Number.parseInt(p.player.draftPosition, 10) || null : null,
|
|
3502
3618
|
draftType: p.player.draftType ?? null,
|