diff --git a/.gitignore b/.gitignore index 43ec3040..f6810af9 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,8 @@ logs/ .env .env.* *.local.json +CLAUDE.local.md +.claude/settings.json ############################################ diff --git a/plugins/FantasyPremierLeague/v2/configValidation.json b/plugins/FantasyPremierLeague/v2/configValidation.json new file mode 100644 index 00000000..6d6960b9 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "FPL manager ID", + "dataStream": { "name": "managerInformation" }, + "success": "Successfully connected to FPL", + "error": "Can't find this FPL Manager ID — check the URL on the FPL website", + "required": true + } + ] +} diff --git a/plugins/FantasyPremierLeague/v2/cspell.json b/plugins/FantasyPremierLeague/v2/cspell.json new file mode 100644 index 00000000..c2cec065 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/cspell.json @@ -0,0 +1,9 @@ +{ + "words": [ + "FPL", + "Gameweek", + "bboost", + "freehit", + "favourite" + ] +} diff --git a/plugins/FantasyPremierLeague/v2/custom_types.json b/plugins/FantasyPremierLeague/v2/custom_types.json new file mode 100644 index 00000000..d482f539 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/custom_types.json @@ -0,0 +1,23 @@ +[ + { + "name": "FPL Gameweek", + "sourceType": "Gameweek", + "icon": "calendar", + "singular": "Gameweek", + "plural": "Gameweeks" + }, + { + "name": "FPL Classic League", + "sourceType": "Classic League", + "icon": "trophy", + "singular": "Classic League", + "plural": "Classic Leagues" + }, + { + "name": "FPL H2H League", + "sourceType": "H2H League", + "icon": "swords", + "singular": "H2H League", + "plural": "H2H Leagues" + } +] diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/classicLeagues.json b/plugins/FantasyPremierLeague/v2/dataStreams/classicLeagues.json new file mode 100644 index 00000000..16c0a703 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/classicLeagues.json @@ -0,0 +1,61 @@ +{ + "name": "classicLeagues", + "displayName": "Classic Leagues", + "description": "Classic leagues the configured manager is a member of", + "visibility": { "type": "hidden" }, + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "expandInnerObjects": false, + "endpointPath": "entry/{{dataSource.userID}}", + "pathToData": "leagues.classic", + "getArgs": [], + "headers": [] + }, + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": "string", + "name": "id", + "displayName": "League ID", + "role": "value" + }, + { + "name": "name", + "displayName": "League Name", + "role": "label" + }, + { + "name": "short_name", + "displayName": "Short Name" + }, + { + "name": "league_type", + "displayName": "League Type" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "entry_rank", + "displayName": "Current Rank" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "entry_last_rank", + "displayName": "Last Rank" + } + ], + "timeframes": false, + "matches": "none", + "tags": ["Leagues"] +} diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/gameweekData.json b/plugins/FantasyPremierLeague/v2/dataStreams/gameweekData.json new file mode 100644 index 00000000..3c08f432 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/gameweekData.json @@ -0,0 +1,137 @@ +{ + "timeframes": false, + "name": "gameweekData", + "description": "Details and statistics for each gameweek in the season", + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "bootstrap-static", + "postRequestScript": "gameweekData.js", + "getArgs": [], + "headers": [] + }, + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "id" + }, + { + "displayName": "Gameweek", + "name": "name" + }, + { + "shape": [ + "date", + { + "timeZone": "Etc/UTC" + } + ], + "name": "deadline_time" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "highest_score" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "average_entry_score" + }, + { + "displayName": "BB Chip Played", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "chip_plays.bboost" + }, + { + "displayName": "TC Chip Played", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "chip_plays.3xc" + }, + { + "displayName": "Wildcard Chip Played", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "chip_plays.wildcard" + }, + { + "displayName": "Free Hit Chip Played", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "chip_plays.freehit" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "most_transferred_in" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "most_captained" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "most_vice_captained" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "transfers_made" + } + ], + "displayName": "Gameweek Data", + "baseDataSourceName": "httpRequestUnscoped", + "matches": "none", + "tags": ["Game Info"] +} \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/h2hLeagueSummary.json b/plugins/FantasyPremierLeague/v2/dataStreams/h2hLeagueSummary.json new file mode 100644 index 00000000..80a36d69 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/h2hLeagueSummary.json @@ -0,0 +1,135 @@ +{ + "timeframes": false, + "name": "h2hLeagueSummary", + "description": "Standings for a selected head-to-head league", + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "leagues-h2h/{{leagueID}}/standings/", + "pathToData": "standings.results", + "getArgs": [], + "headers": [] + }, + "baseDataSourceName": "httpRequestUnscoped", + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "points_for", + "displayName": "Points For" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "rank", + "expand": [ + { + "compareTo": "last_rank", + "shape": "number", + "comparisonName": "compare1", + "comparisonType": "absolute", + "displayName": "Rank Change" + } + ] + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "last_rank" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "matches_won", + "displayName": "Won" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "matches_drawn", + "displayName": "Drawn" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "matches_lost", + "displayName": "Lost" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "total", + "displayName": "Total Points" + }, + { + "displayName": "Team Name", + "name": "entry_name" + }, + { + "displayName": "Player Name", + "name": "player_name" + } + ], + "defaultShaping": { + "sort": { + "by": [ + [ + "rank", + "asc" + ] + ] + } + }, + "displayName": "H2H League Summary", + "matches": "none", + "tags": ["Leagues"], + "ui": [ + { + "name": "leagueID", + "label": "H2H League", + "placeholder": "Select an H2H league", + "type": "autocomplete", + "allowCustomValues": false, + "isMulti": false, + "validation": { + "required": true, + "message": "You must select an H2H league" + }, + "data": { + "source": "dataStream", + "dataStreamName": "h2hLeagues", + "dataSourceConfig": {} + } + } + ] +} diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/h2hLeagues.json b/plugins/FantasyPremierLeague/v2/dataStreams/h2hLeagues.json new file mode 100644 index 00000000..54803894 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/h2hLeagues.json @@ -0,0 +1,57 @@ +{ + "name": "h2hLeagues", + "displayName": "H2H Leagues", + "description": "Head-to-head leagues the configured manager is a member of", + "visibility": { "type": "hidden" }, + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "expandInnerObjects": false, + "endpointPath": "entry/{{dataSource.userID}}", + "pathToData": "leagues.h2h", + "getArgs": [], + "headers": [] + }, + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": "string", + "name": "id", + "displayName": "League ID", + "role": "value" + }, + { + "name": "name", + "displayName": "League Name", + "role": "label" + }, + { + "name": "short_name", + "displayName": "Short Name" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "entry_rank", + "displayName": "Current Rank" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "entry_last_rank", + "displayName": "Last Rank" + } + ], + "timeframes": false, + "matches": "none", + "tags": ["Leagues"] +} diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/leagueSummary.json b/plugins/FantasyPremierLeague/v2/dataStreams/leagueSummary.json new file mode 100644 index 00000000..60ba3fda --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/leagueSummary.json @@ -0,0 +1,92 @@ +{ + "timeframes": false, + "name": "leagueSummary", + "displayName": "League Summary", + "description": "Standings for a selected classic league", + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "leagues-classic/{{objects[0].rawId}}/standings/", + "pathToData": "standings.results", + "getArgs": [], + "headers": [] + }, + "baseDataSourceName": "httpRequestScopedSingle", + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "event_total", + "displayName": "Weekly Points" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "rank", + "expand": [ + { + "compareTo": "last_rank", + "shape": "number", + "comparisonName": "compare1", + "comparisonType": "absolute", + "displayName": "Rank Change" + } + ] + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "last_rank" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "total", + "displayName": "Total Points" + }, + { + "displayName": "Team Name", + "name": "entry_name" + }, + { + "displayName": "Player Name", + "name": "player_name" + }, + { + "name": "sourceId", + "displayName": "Object ID", + "shape": "string", + "visible": false + } + ], + "defaultShaping": { + "sort": { + "by": [ + [ + "rank", + "asc" + ] + ] + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Classic League"] } }, + "tags": ["Leagues"] +} \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/managerHistory.json b/plugins/FantasyPremierLeague/v2/dataStreams/managerHistory.json new file mode 100644 index 00000000..4d0b115d --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/managerHistory.json @@ -0,0 +1,52 @@ +{ + "timeframes": false, + "name": "managerHistory", + "description": "Points and rank for the configured manager's previous seasons", + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "entry/{{dataSource.userID}}/history", + "pathToData": "past", + "getArgs": [], + "headers": [] + }, + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "total_points" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "rank" + }, + { + "pattern": ".*" + } + ], + "defaultShaping": { + "sort": { + "by": [ + [ + "season_name", + "asc" + ] + ] + } + }, + "displayName": "Manager History", + "baseDataSourceName": "httpRequestUnscoped", + "matches": "none", + "tags": ["Manager"] +} \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/managerInformation.json b/plugins/FantasyPremierLeague/v2/dataStreams/managerInformation.json new file mode 100644 index 00000000..7be13075 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/managerInformation.json @@ -0,0 +1,155 @@ +{ + "name": "managerInformation", + "description": "Profile and season summary for the configured manager", + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "entry/{{dataSource.userID}}", + "pathToData": "", + "postRequestScript": "managerInformation.js", + "getArgs": [], + "headers": [] + }, + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": "string", + "name": "id", + "displayName": "Manager ID" + }, + { + "name": "name", + "displayName": "Team Name" + }, + { + "name": "player_first_name", + "displayName": "First Name" + }, + { + "name": "player_last_name", + "displayName": "Last Name" + }, + { + "name": "player_region_name", + "displayName": "Country" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "favourite_team", + "displayName": "Favourite Team" + }, + { + "shape": [ + "date", + { + "timeZone": "Etc/UTC" + } + ], + "name": "joined_time", + "displayName": "Joined Date" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "years_active", + "displayName": "Years Playing" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "current_event", + "displayName": "Current Gameweek" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "summary_overall_points", + "displayName": "Overall Points" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "summary_overall_rank", + "displayName": "Overall Rank" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "summary_event_points", + "displayName": "GW Points" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "summary_event_rank", + "displayName": "GW Rank" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ], + "formatExpression": "{{ $['bank']/10 }}", + "name": "bank", + "displayName": "Transfer Budget" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ], + "formatExpression": "{{ $['value']/10 }}", + "name": "value", + "displayName": "Squad Value" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "total_transfers", + "displayName": "Total Transfers" + } + ], + "timeframes": false, + "displayName": "Manager Information", + "baseDataSourceName": "httpRequestUnscoped", + "matches": "none", + "tags": ["Manager"] +} diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/managerSummary.json b/plugins/FantasyPremierLeague/v2/dataStreams/managerSummary.json new file mode 100644 index 00000000..ad670bac --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/managerSummary.json @@ -0,0 +1,112 @@ +{ + "timeframes": false, + "name": "managerSummary", + "description": "Weekly points, rank and transfers for the configured manager this season", + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "entry/{{dataSource.userID}}/history/", + "pathToData": "current", + "getArgs": [], + "headers": [] + }, + "manualConfigApply": true, + "rowPath": [], + "metadata": [ + { + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ], + "formatExpression": "{{ $['bank']/10 }}", + "name": "bank" + }, + { + "displayName": "Transfers", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "event_transfers" + }, + { + "displayName": "Transfer Cost", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "event_transfers_cost" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "overall_rank" + }, + { + "shape": "percent", + "name": "percentile_rank" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "points_on_bench" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "total_points" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ], + "formatExpression": "{{ $['value']/10 }}", + "name": "value" + }, + { + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "points", + "displayName": "Weekly Points" + }, + { + "displayName": "Week", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ], + "name": "event" + } + ], + "displayName": "Manager Summary", + "baseDataSourceName": "httpRequestUnscoped", + "matches": "none", + "tags": ["Manager"] +} diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/scripts/gameweekData.js b/plugins/FantasyPremierLeague/v2/dataStreams/scripts/gameweekData.js new file mode 100644 index 00000000..92cbe25b --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/scripts/gameweekData.js @@ -0,0 +1,10 @@ +result = data.events.map(function (event) { + var chipMap = {}; + // Key by chip name, not array index — the API's chip_plays order/length + // varies per gameweek, but gameweekData.json's columns (chip_plays.bboost, + // chip_plays.freehit, etc.) need a stable path. + (event.chip_plays || []).forEach(function (chip) { + chipMap[chip.chip_name] = chip.num_played; + }); + return Object.assign({}, event, { chip_plays: chipMap }); +}); diff --git a/plugins/FantasyPremierLeague/v2/dataStreams/scripts/managerInformation.js b/plugins/FantasyPremierLeague/v2/dataStreams/scripts/managerInformation.js new file mode 100644 index 00000000..d6bc18c0 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/dataStreams/scripts/managerInformation.js @@ -0,0 +1,8 @@ +var manager = Object.assign({}, data); +// Drop variable-length branches (league count, gameweeks entered and cup +// progress differ per manager) so expandInnerObjects can't flatten them +// into an unbounded, manager-dependent set of columns. +delete manager.leagues; +delete manager.entered_events; +delete manager.kit; +result = [manager]; diff --git a/plugins/FantasyPremierLeague/v2/defaultContent/fplSummary.dash.json b/plugins/FantasyPremierLeague/v2/defaultContent/fplSummary.dash.json new file mode 100644 index 00000000..a6161877 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/defaultContent/fplSummary.dash.json @@ -0,0 +1,331 @@ +{ + "name": "Summary", + "schemaVersion": "1.4", + "variables": ["{{variables.[Classic League]}}"], + "dashboard": { + "_type": "layout/grid", + "contents": [ + { + "static": false, + "w": 3, + "moved": false, + "h": 2, + "x": 0, + "y": 0, + "i": "0269ec38-dc97-41b8-b060-4c2884721154", + "z": 0, + "config": { + "dataStream": { + "name": "managerInformation", + "id": "{{dataStreams.managerInformation}}", + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Manager Information", + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "hiddenColumns": [ + "started_event", + "favourite_team", + "player_region_id", + "player_region_iso_code_short", + "player_region_iso_code_long", + "name", + "name_change_blocked", + "last_deadline_bank", + "last_deadline_value", + "last_deadline_total_transfers", + "club_badge_src" + ], + "transpose": true, + "columnOrder": [ + "id", + "player_first_name", + "player_last_name", + "player_region_name", + "joined_time", + "years_active", + "summary_overall_points", + "summary_overall_rank", + "current_event", + "summary_event_points", + "summary_event_rank" + ], + "columnDisplayNames": {} + } + } + } + } + }, + { + "static": false, + "w": 3, + "moved": false, + "h": 2, + "x": 3, + "y": 0, + "i": "f82014ea-6c39-49d8-85bb-09c759d0b98c", + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "managerHistory", + "id": "{{dataStreams.managerHistory}}", + "sort": { + "by": [ + [ + "season_name", + "desc" + ] + ] + }, + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "All previous seasons", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Manager History", + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "columnOrder": [ + "season_name", + "total_points", + "rank" + ], + "hiddenColumns": [] + } + } + } + } + }, + { + "static": false, + "w": 2, + "moved": false, + "h": 2, + "x": 6, + "y": 0, + "i": "a7dfcdca-d6cf-46e4-afcf-21b6c3afd447", + "z": 0, + "config": { + "_type": "tile/image", + "description": "", + "title": "", + "visualisation": { + "config": { + "title": "", + "src": "https://resources.premierleague.com/photos/2020/08/11/0d811107-6d08-4e1c-b9ee-17dab9d065e7/FPL-Statement-Lead.png?width=930&height=620" + } + } + } + }, + { + "static": false, + "w": 5, + "moved": false, + "h": 2, + "x": 3, + "y": 2, + "i": "e8c567cf-74a6-480a-a028-1e2b654e5f89", + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "gameweekData", + "id": "{{dataStreams.gameweekData}}", + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "Static data about the current season", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Gameweek Data", + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "columnOrder": [ + "id", + "name", + "deadline_time", + "average_entry_score", + "highest_score", + "chip_plays.bboost", + "chip_plays.3xc", + "chip_plays.wildcard", + "chip_plays.freehit", + "most_transferred_in", + "transfers_made", + "most_captained", + "most_vice_captained" + ], + "hiddenColumns": [] + } + } + } + } + }, + { + "static": false, + "w": 3, + "moved": false, + "h": 2, + "x": 0, + "y": 2, + "i": "ec44d240-6ac6-49f3-90c0-fd88ba75c2e7", + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "leagueSummary", + "id": "{{dataStreams.leagueSummary}}", + "sort": { + "by": [ + [ + "rank", + "asc" + ] + ] + }, + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "Standings for the selected classic league", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "League Summary", + "scope": { + "scope": "{{scopes.[Classic Leagues]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Classic League]}}" + }, + "variables": [ + "{{variables.[Classic League]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "columnOrder": [ + "rank", + "entry_name", + "event_total", + "total", + "rank[Expanded].comparedTo[last_rank].compare1" + ], + "hiddenColumns": [ + "rank_sort", + "last_rank", + "entry" + ] + } + } + } + } + }, + { + "static": false, + "w": 4, + "moved": false, + "h": 3, + "x": 0, + "y": 4, + "i": "121a65b8-b410-40df-9208-770ae5fa1262", + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "managerSummary", + "id": "{{dataStreams.managerSummary}}", + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "This season", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Weekly Points", + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisGroup": "none", + "showLegend": false, + "range": { + "type": "auto" + }, + "showGrid": true, + "grouping": false, + "xAxisData": "event", + "displayMode": "actual", + "showTotals": false, + "yAxisLabel": "", + "horizontalLayout": "vertical", + "showValue": false, + "yAxisData": [ + "points" + ], + "showYAxisLabel": true, + "xAxisLabel": "", + "legendPosition": "bottom", + "showXAxisLabel": true + } + } + } + } + }, + { + "static": false, + "w": 4, + "moved": false, + "h": 3, + "x": 4, + "y": 4, + "i": "1808f062-623d-458a-a755-84cb9a00d23f", + "z": 0, + "config": { + "timeframe": "none", + "dataStream": { + "name": "managerSummary", + "id": "{{dataStreams.managerSummary}}", + "pluginConfigId": "{{configId}}" + }, + "_type": "tile/data-stream", + "description": "This season", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Overall Rank", + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "event", + "yAxisColumn": [ + "overall_rank" + ], + "seriesColumn": "none" + } + } + } + } + } + ], + "version": 98, + "columns": 8 + }, + "path": "fpl-summary", + "folderPath": [] +} \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/defaultContent/manifest.json b/plugins/FantasyPremierLeague/v2/defaultContent/manifest.json new file mode 100644 index 00000000..df08b637 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/defaultContent/manifest.json @@ -0,0 +1,8 @@ +{ + "items": [ + { + "name": "fplSummary", + "type": "dashboard" + } + ] +} \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/defaultContent/scopes.json b/plugins/FantasyPremierLeague/v2/defaultContent/scopes.json new file mode 100644 index 00000000..ce3e5d1e --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/defaultContent/scopes.json @@ -0,0 +1,12 @@ +[ + { + "name": "Classic Leagues", + "matches": { "sourceType": { "type": "oneOf", "values": ["Classic League"] } }, + "variable": { + "name": "Classic League", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/docs/README.md b/plugins/FantasyPremierLeague/v2/docs/README.md new file mode 100644 index 00000000..d4512f21 --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/docs/README.md @@ -0,0 +1,52 @@ +Fantasy Premier League (FPL) is the official free-to-play game from the Premier League. This plugin connects to the public [FPL API](https://fantasy.premierleague.com/api/bootstrap-static/) to bring a manager's profile, league standings and season stats into SquaredUp — no authentication is required. + +## Setup + +You'll need your FPL Manager ID. + +1. Sign in to the [Fantasy Premier League website](https://fantasy.premierleague.com/) and open **Points** or **My Team** to see your team page. +2. Copy the Manager ID from the URL — for example `https://fantasy.premierleague.com/entry/**1234567**/event/1` has a Manager ID of `1234567`. +3. In SquaredUp, add the Fantasy Premier League data source and paste the ID into the **FPL manager ID** field. + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| ----- | ---------- | ---------------- | -------- | +| **FPL manager ID** | Identifies which manager's team, leagues and history the plugin queries. | The URL of your team page on the [Fantasy Premier League website](https://fantasy.premierleague.com/). | Yes | + +On save, the plugin validates the ID by fetching the manager's profile; an ID that doesn't match an existing manager fails setup with an error. + +## What this plugin monitors + +- **Manager profile and history** — team name, country, and points/rank for every previous season played. +- **This season's performance** — weekly points, overall rank, transfer activity and squad value. +- **Leagues** — standings for the manager's classic and head-to-head leagues. +- **Gameweeks** — deadlines, average scores and chip usage across the season. + +The out-of-the-box dashboard is a single **Summary** covering the manager's profile, history and current season. + +## Data streams + +- **Manager Information** — the configured manager's profile and current season summary, one row. +- **Manager History** — points and rank for each of the manager's previous seasons. +- **Manager Summary** — weekly points, rank and transfers for the configured manager this season. +- **Gameweek Data** — deadline, average score and chip usage for each gameweek in the season (account-wide). +- **League Summary** — standings for a selected classic league. +- **H2H League Summary** — standings for a selected head-to-head league. +- **Classic Leagues** — the classic leagues the configured manager is a member of (used for import and league selection). +- **H2H Leagues** — the head-to-head leagues the configured manager is a member of (used for import and league selection). + +## What gets indexed + +| Object type | API source | Represents | +| ----------- | ---------- | ---------- | +| **Gameweek** | `GET /bootstrap-static` | One gameweek in the current season. | +| **Classic League** | `GET /entry/{managerId}` | A classic league the configured manager is a member of. | +| **H2H League** | `GET /entry/{managerId}` | A head-to-head league the configured manager is a member of. | + +## Known limitations + +- **Manager ID changes each season** — the FPL API assigns entries per season, so an ID used for testing or dashboards may need updating when a new season starts. +- **League standings depend on the season being live** — before the season's first gameweek deadline, league and current-season data streams return no rows. +- **Read-only** — the plugin never creates, modifies, or deletes anything in Fantasy Premier League. +- **Summary dashboard image tile** — the OOB Summary dashboard embeds an image hosted at `resources.premierleague.com`. It only displays if that domain is in your organization's list of allowed embed URLs; otherwise the tile shows blank. diff --git a/plugins/FantasyPremierLeague/v2/icon.svg b/plugins/FantasyPremierLeague/v2/icon.svg new file mode 100644 index 00000000..71ea572f --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/indexDefinitions/default.json b/plugins/FantasyPremierLeague/v2/indexDefinitions/default.json new file mode 100644 index 00000000..9308e61d --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/indexDefinitions/default.json @@ -0,0 +1,57 @@ +{ + "steps": [ + { + "name": "Gameweeks", + "dataStream": { + "name": "gameweekData" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Gameweek" }, + "properties": [ + { "deadlineTime": "deadline_time" }, + { "highestScore": "highest_score" }, + { "averageEntryScore": "average_entry_score" }, + { "transfersMade": "transfers_made" } + ] + } + }, + { + "name": "Classic Leagues", + "dataStream": { + "name": "classicLeagues" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Classic League" }, + "properties": [ + { "shortName": "short_name" }, + { "leagueType": "league_type" }, + { "currentRank": "entry_rank" }, + { "lastRank": "entry_last_rank" } + ] + } + }, + { + "name": "H2H Leagues", + "dataStream": { + "name": "h2hLeagues" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "H2H League" }, + "properties": [ + { "shortName": "short_name" }, + { "currentRank": "entry_rank" }, + { "lastRank": "entry_last_rank" } + ] + } + } + ] +} diff --git a/plugins/FantasyPremierLeague/v2/metadata.json b/plugins/FantasyPremierLeague/v2/metadata.json new file mode 100644 index 00000000..12588f9e --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/metadata.json @@ -0,0 +1,38 @@ +{ + "name": "fantasy-premier-league", + "displayName": "Fantasy Premier League", + "version": "2.0.0", + "author": { + "name": "@TimWheeler-SQUP", + "type": "community" + }, + "description": "FPL data source to display manager information, season's points, leagues and previous seasons results.", + "category": "Fun", + "type": "cloud", + "restrictedToPlatforms": [], + "importNotSupported": false, + "schemaVersion": "2.0", + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "authMode": "none", + "baseUrl": "https://fantasy.premierleague.com/api" + } + }, + "keywords": ["FPL", "Fantasy", "Premier League", "Football"], + "objectTypes": ["Gameweek", "Classic League", "H2H League"], + "screenshots": ["https://github.com/squaredup/plugins/blob/main/plugins/FantasyPremierLeague/v2/screenshots/summary-dashboard.png"], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/FantasyPremierLeague/v2/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/FantasyPremierLeague/v2", + "label": "Repository" + } + ] +} \ No newline at end of file diff --git a/plugins/FantasyPremierLeague/v2/screenshots/summary-dashboard.png b/plugins/FantasyPremierLeague/v2/screenshots/summary-dashboard.png new file mode 100644 index 00000000..23d64fdc Binary files /dev/null and b/plugins/FantasyPremierLeague/v2/screenshots/summary-dashboard.png differ diff --git a/plugins/FantasyPremierLeague/v2/ui.json b/plugins/FantasyPremierLeague/v2/ui.json new file mode 100644 index 00000000..ac2baecd --- /dev/null +++ b/plugins/FantasyPremierLeague/v2/ui.json @@ -0,0 +1,12 @@ +[ + { + "type": "text", + "name": "userID", + "label": "FPL manager ID", + "help": "Your FPL Manager ID — found in the URL when viewing your team on the FPL website (e.g. https://fantasy.premierleague.com/entry/**123456**/event/1)", + "validation": { + "required": true + }, + "placeholder": "Enter manager ID e.g. 1234567" + } +]