From 245851512ecb834a8fd5216e46a6538c15a1f092 Mon Sep 17 00:00:00 2001 From: Dave Clarke Date: Tue, 13 Oct 2026 17:36:26 +0100 Subject: [PATCH] =?UTF-8?q?Add=20Pok=C3=A9API=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Imports every Pokémon species, battle type and generation from the free PokéAPI, with streams for profiles, base stats, moves, Pokédex entries, type matchups and generation summaries, plus an Overview dashboard and a perspective per object type. Co-Authored-By: Claude Opus 5.5 --- .github/CODEOWNERS | 1 + plugins/PokeAPI/v1/configValidation.json | 11 + plugins/PokeAPI/v1/cspell.json | 24 + plugins/PokeAPI/v1/custom_types.json | 23 + .../v1/dataStreams/generationSummary.json | 31 + .../PokeAPI/v1/dataStreams/generations.json | 36 ++ plugins/PokeAPI/v1/dataStreams/pokemon.json | 40 ++ .../v1/dataStreams/pokemonByGeneration.json | 23 + .../PokeAPI/v1/dataStreams/pokemonByType.json | 24 + .../PokeAPI/v1/dataStreams/pokemonMoves.json | 24 + .../v1/dataStreams/pokemonProfile.json | 32 + .../v1/dataStreams/pokemonSpecies.json | 169 ++++++ .../PokeAPI/v1/dataStreams/pokemonStats.json | 24 + .../dataStreams/scripts/generationSummary.js | 26 + .../scripts/pokemonByGeneration.js | 15 + .../v1/dataStreams/scripts/pokemonByType.js | 15 + .../v1/dataStreams/scripts/pokemonMoves.js | 47 ++ .../v1/dataStreams/scripts/pokemonProfile.js | 33 ++ .../v1/dataStreams/scripts/pokemonSpecies.js | 48 ++ .../v1/dataStreams/scripts/pokemonStats.js | 20 + .../v1/dataStreams/scripts/typeMatchups.js | 32 + .../PokeAPI/v1/dataStreams/scripts/types.js | 4 + .../PokeAPI/v1/dataStreams/typeMatchups.json | 26 + plugins/PokeAPI/v1/dataStreams/types.json | 36 ++ .../v1/defaultContent/generation.dash.json | 293 +++++++++ .../PokeAPI/v1/defaultContent/manifest.json | 8 + .../v1/defaultContent/overview.dash.json | 430 ++++++++++++++ .../v1/defaultContent/pokemon.dash.json | 561 ++++++++++++++++++ .../v1/defaultContent/pokemonType.dash.json | 407 +++++++++++++ plugins/PokeAPI/v1/defaultContent/scopes.json | 38 ++ plugins/PokeAPI/v1/docs/README.md | 61 ++ plugins/PokeAPI/v1/icon.svg | 1 + .../PokeAPI/v1/indexDefinitions/default.json | 34 ++ plugins/PokeAPI/v1/metadata.json | 37 ++ plugins/PokeAPI/v1/ui.json | 1 + 35 files changed, 2635 insertions(+) create mode 100644 plugins/PokeAPI/v1/configValidation.json create mode 100644 plugins/PokeAPI/v1/cspell.json create mode 100644 plugins/PokeAPI/v1/custom_types.json create mode 100644 plugins/PokeAPI/v1/dataStreams/generationSummary.json create mode 100644 plugins/PokeAPI/v1/dataStreams/generations.json create mode 100644 plugins/PokeAPI/v1/dataStreams/pokemon.json create mode 100644 plugins/PokeAPI/v1/dataStreams/pokemonByGeneration.json create mode 100644 plugins/PokeAPI/v1/dataStreams/pokemonByType.json create mode 100644 plugins/PokeAPI/v1/dataStreams/pokemonMoves.json create mode 100644 plugins/PokeAPI/v1/dataStreams/pokemonProfile.json create mode 100644 plugins/PokeAPI/v1/dataStreams/pokemonSpecies.json create mode 100644 plugins/PokeAPI/v1/dataStreams/pokemonStats.json create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/generationSummary.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/pokemonByGeneration.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/pokemonByType.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/pokemonMoves.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/pokemonProfile.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/pokemonSpecies.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/pokemonStats.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/typeMatchups.js create mode 100644 plugins/PokeAPI/v1/dataStreams/scripts/types.js create mode 100644 plugins/PokeAPI/v1/dataStreams/typeMatchups.json create mode 100644 plugins/PokeAPI/v1/dataStreams/types.json create mode 100644 plugins/PokeAPI/v1/defaultContent/generation.dash.json create mode 100644 plugins/PokeAPI/v1/defaultContent/manifest.json create mode 100644 plugins/PokeAPI/v1/defaultContent/overview.dash.json create mode 100644 plugins/PokeAPI/v1/defaultContent/pokemon.dash.json create mode 100644 plugins/PokeAPI/v1/defaultContent/pokemonType.dash.json create mode 100644 plugins/PokeAPI/v1/defaultContent/scopes.json create mode 100644 plugins/PokeAPI/v1/docs/README.md create mode 100644 plugins/PokeAPI/v1/icon.svg create mode 100644 plugins/PokeAPI/v1/indexDefinitions/default.json create mode 100644 plugins/PokeAPI/v1/metadata.json create mode 100644 plugins/PokeAPI/v1/ui.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4c0b7722..e37b4129 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -34,5 +34,6 @@ plugins/WorldCup2026/* @TimWheeler-SQUP plugins/Notion/* @clarkd plugins/Algolia/* @andrewmumblebee plugins/Vercel/* @andrewmumblebee +plugins/PokeAPI/* @clarkd diff --git a/plugins/PokeAPI/v1/configValidation.json b/plugins/PokeAPI/v1/configValidation.json new file mode 100644 index 00000000..24df4938 --- /dev/null +++ b/plugins/PokeAPI/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "Connect to PokéAPI", + "dataStream": { "name": "types" }, + "required": true, + "error": "Could not reach PokéAPI (https://pokeapi.co). Check your network connection or agent egress.", + "success": "Connected to PokéAPI successfully." + } + ] +} diff --git a/plugins/PokeAPI/v1/cspell.json b/plugins/PokeAPI/v1/cspell.json new file mode 100644 index 00000000..878cb8f6 --- /dev/null +++ b/plugins/PokeAPI/v1/cspell.json @@ -0,0 +1,24 @@ +{ + "words": [ + "Arceus", + "Charizard", + "clarkd", + "colour", + "Eevee", + "Farfetchd", + "Gigantamax", + "Heartgold", + "learnset", + "learnsets", + "localised", + "matchups", + "pokeapi", + "pokedex", + "Poké", + "Pokédex", + "Ponyta", + "Soulsilver", + "Tera", + "visualisation" + ] +} diff --git a/plugins/PokeAPI/v1/custom_types.json b/plugins/PokeAPI/v1/custom_types.json new file mode 100644 index 00000000..05c541ad --- /dev/null +++ b/plugins/PokeAPI/v1/custom_types.json @@ -0,0 +1,23 @@ +[ + { + "name": "Pokémon", + "sourceType": "Pokemon", + "icon": "paw", + "singular": "Pokémon", + "plural": "Pokémon" + }, + { + "name": "Pokémon Type", + "sourceType": "Pokemon Type", + "icon": "fire", + "singular": "Pokémon Type", + "plural": "Pokémon Types" + }, + { + "name": "Pokémon Generation", + "sourceType": "Pokemon Generation", + "icon": "gamepad", + "singular": "Generation", + "plural": "Generations" + } +] diff --git a/plugins/PokeAPI/v1/dataStreams/generationSummary.json b/plugins/PokeAPI/v1/dataStreams/generationSummary.json new file mode 100644 index 00000000..68ec1b9a --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/generationSummary.json @@ -0,0 +1,31 @@ +{ + "name": "generationSummary", + "displayName": "Generation Summary", + "description": "Main region, games and the number of species, moves, abilities and types a generation introduced", + "tags": ["Generation"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/generation/{{object.rawId}}", + "postRequestScript": "generationSummary.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Pokemon Generation"] } }, + "timeframes": false, + "metadata": [ + { "name": "generationId", "displayName": "Generation ID", "shape": "string", "visible": false }, + { + "name": "generationName", + "displayName": "Generation", + "shape": "string", + "role": "label" + }, + { "name": "region", "displayName": "Region", "shape": "string" }, + { "name": "games", "displayName": "Games", "shape": "string" }, + { "name": "newSpecies", "displayName": "New Species", "shape": "number", "role": "value" }, + { "name": "newMoves", "displayName": "New Moves", "shape": "number" }, + { "name": "newAbilities", "displayName": "New Abilities", "shape": "number" }, + { "name": "newTypes", "displayName": "New Types", "shape": "number" }, + { "name": "typesIntroduced", "displayName": "Types Introduced", "shape": "string" }, + { "sourceId": "generationId", "sourceType": "Pokemon Generation", "name": "generationName" } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/generations.json b/plugins/PokeAPI/v1/dataStreams/generations.json new file mode 100644 index 00000000..5aed3a72 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/generations.json @@ -0,0 +1,36 @@ +{ + "name": "generations", + "displayName": "Generations", + "description": "Pokémon game generations, from Generation I onwards", + "tags": ["Pokémon", "Generation"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/generation", + "getArgs": [{ "key": "limit", "value": "100" }], + "pathToData": "results" + }, + "matches": "none", + "timeframes": false, + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "url", "displayName": "URL", "shape": "url", "visible": false }, + { "name": "name", "displayName": "Slug", "shape": "string", "visible": false }, + { + "name": "id", + "displayName": "ID", + "shape": "string", + "role": "id", + "computed": true, + "valueExpression": "{{ $['url'].split('/').filter(Boolean).pop() }}" + }, + { + "name": "generationName", + "displayName": "Generation", + "shape": "string", + "role": "label", + "computed": true, + "valueExpression": "{{ 'Generation ' + $['name'].split('-').pop().toUpperCase() }}" + } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/pokemon.json b/plugins/PokeAPI/v1/dataStreams/pokemon.json new file mode 100644 index 00000000..0a9a90c3 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/pokemon.json @@ -0,0 +1,40 @@ +{ + "name": "pokemon", + "displayName": "Pokémon", + "description": "Every Pokémon species in the National Pokédex", + "tags": ["Pokémon"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/pokemon-species", + "pathToData": "results", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "200" }, + "in": { "realm": "payload", "path": "next" } + } + }, + "matches": "none", + "timeframes": false, + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "url", "displayName": "URL", "shape": "url", "visible": false }, + { "name": "name", "displayName": "Slug", "shape": "string", "visible": false }, + { + "name": "id", + "displayName": "Pokédex No.", + "shape": "string", + "role": "id", + "computed": true, + "valueExpression": "{{ $['url'].split('/').filter(Boolean).pop() }}" + }, + { + "name": "pokemonName", + "displayName": "Pokémon", + "shape": "string", + "role": "label", + "computed": true, + "valueExpression": "{{ $['name'].split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ') }}" + } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/pokemonByGeneration.json b/plugins/PokeAPI/v1/dataStreams/pokemonByGeneration.json new file mode 100644 index 00000000..bcf87619 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/pokemonByGeneration.json @@ -0,0 +1,23 @@ +{ + "name": "pokemonByGeneration", + "displayName": "Pokémon by Generation", + "description": "Pokémon species introduced in a generation", + "tags": ["Generation", "Pokémon"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/generation/{{object.rawId}}", + "postRequestScript": "pokemonByGeneration.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Pokemon Generation"] } }, + "timeframes": false, + "defaultShaping": { "sort": { "by": [["pokedexNumber", "asc"]] } }, + "metadata": [ + { "name": "generationId", "displayName": "Generation ID", "shape": "string", "visible": false }, + { "name": "generationName", "displayName": "Generation", "shape": "string" }, + { "name": "pokemonId", "displayName": "Pokémon ID", "shape": "string", "visible": false }, + { "name": "pokemonName", "displayName": "Pokémon", "shape": "string", "role": "label" }, + { "name": "pokedexNumber", "displayName": "Pokédex No.", "shape": ["number", { "thousandsSeparator": false }] }, + { "sourceId": "pokemonId", "sourceType": "Pokemon", "name": "pokemonName" } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/pokemonByType.json b/plugins/PokeAPI/v1/dataStreams/pokemonByType.json new file mode 100644 index 00000000..94299134 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/pokemonByType.json @@ -0,0 +1,24 @@ +{ + "name": "pokemonByType", + "displayName": "Pokémon by Type", + "description": "Pokémon that have a type, with whether it is their primary or secondary type", + "tags": ["Type", "Pokémon"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/type/{{object.rawId}}", + "postRequestScript": "pokemonByType.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Pokemon Type"] } }, + "timeframes": false, + "defaultShaping": { "sort": { "by": [["pokedexNumber", "asc"]] } }, + "metadata": [ + { "name": "typeId", "displayName": "Type ID", "shape": "string", "visible": false }, + { "name": "typeName", "displayName": "Type", "shape": "string" }, + { "name": "pokemonId", "displayName": "Pokémon ID", "shape": "string", "sourceType": "Pokemon", "visible": false }, + { "name": "pokemonName", "displayName": "Pokémon", "shape": "string", "role": "label", "sourceId": "pokemonId", "sourceType": "Pokemon", "objectPropertyPath": "name" }, + { "name": "pokedexNumber", "displayName": "Pokédex No.", "shape": ["number", { "thousandsSeparator": false }] }, + { "name": "slot", "displayName": "Slot", "shape": "string" }, + { "sourceId": "pokemonId", "sourceType": "Pokemon", "name": "pokemonName" } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/pokemonMoves.json b/plugins/PokeAPI/v1/dataStreams/pokemonMoves.json new file mode 100644 index 00000000..fa7a7b03 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/pokemonMoves.json @@ -0,0 +1,24 @@ +{ + "name": "pokemonMoves", + "displayName": "Pokémon Moves", + "description": "Moves a Pokémon can learn in the most recent game it appears in, with learn method and level", + "tags": ["Pokémon", "Moves"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/pokemon/{{object.rawId}}", + "postRequestScript": "pokemonMoves.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Pokemon"] } }, + "timeframes": false, + "defaultShaping": { "sort": { "by": [["learnMethod", "asc"], ["level", "asc"]] } }, + "metadata": [ + { "name": "pokemonId", "displayName": "Pokédex No.", "shape": "string", "visible": false }, + { "sourceId": "pokemonId", "sourceType": "Pokemon", "name": "pokemonName" }, + { "name": "pokemonName", "displayName": "Pokémon", "shape": "string" }, + { "name": "move", "displayName": "Move", "shape": "string", "role": "label" }, + { "name": "learnMethod", "displayName": "Learn Method", "shape": "string" }, + { "name": "level", "displayName": "Level", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "game", "displayName": "Game", "shape": "string" } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/pokemonProfile.json b/plugins/PokeAPI/v1/dataStreams/pokemonProfile.json new file mode 100644 index 00000000..90588def --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/pokemonProfile.json @@ -0,0 +1,32 @@ +{ + "name": "pokemonProfile", + "displayName": "Pokémon Profile", + "description": "Types, size, abilities, base stat total and artwork for a Pokémon", + "tags": ["Pokémon"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/pokemon/{{object.rawId}}", + "postRequestScript": "pokemonProfile.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Pokemon"] } }, + "timeframes": false, + "metadata": [ + { "name": "pokemonId", "displayName": "ID", "shape": "string", "visible": false }, + { "sourceId": "pokemonId", "sourceType": "Pokemon", "name": "pokemonName" }, + { "name": "pokemonName", "displayName": "Pokémon", "shape": "string", "role": "label" }, + { "name": "pokedexNumber", "displayName": "Pokédex No.", "shape": ["number", { "thousandsSeparator": false }] }, + { "name": "primaryType", "displayName": "Primary Type", "shape": "string" }, + { "name": "secondaryType", "displayName": "Secondary Type", "shape": "string" }, + { "name": "types", "displayName": "Types", "shape": "string" }, + { "name": "heightM", "displayName": "Height (m)", "shape": ["number", { "decimalPlaces": 1 }] }, + { "name": "weightKg", "displayName": "Weight (kg)", "shape": ["number", { "decimalPlaces": 1 }] }, + { "name": "baseExperience", "displayName": "Base Experience", "shape": "number" }, + { "name": "abilities", "displayName": "Abilities", "shape": "string" }, + { "name": "hiddenAbility", "displayName": "Hidden Ability", "shape": "string" }, + { "name": "statTotal", "displayName": "Base Stat Total", "shape": "number", "role": "value" }, + { "name": "artworkUrl", "displayName": "Artwork", "shape": ["url", { "label": "Official artwork" }] }, + { "name": "spriteUrl", "displayName": "Sprite", "shape": ["url", { "label": "Sprite" }] }, + { "name": "cryUrl", "displayName": "Cry", "shape": ["url", { "label": "Cry" }] } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/pokemonSpecies.json b/plugins/PokeAPI/v1/dataStreams/pokemonSpecies.json new file mode 100644 index 00000000..62df72ec --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/pokemonSpecies.json @@ -0,0 +1,169 @@ +{ + "name": "pokemonSpecies", + "displayName": "Pokémon Species", + "description": "Pokédex entry, category, generation, breeding and capture details for a Pokémon species", + "tags": [ + "Pokémon", + "Species" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/pokemon-species/{{object.rawId}}", + "postRequestScript": "pokemonSpecies.js" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Pokemon" + ] + } + }, + "timeframes": false, + "metadata": [ + { + "name": "pokemonId", + "displayName": "Pokédex No.", + "shape": "string", + "visible": false, + "role": "id" + }, + { + "sourceId": "pokemonId", + "sourceType": "Pokemon", + "name": "pokemonName" + }, + { + "name": "pokemonName", + "displayName": "Pokémon", + "shape": "string", + "role": "label" + }, + { + "name": "genus", + "displayName": "Category", + "shape": "string" + }, + { + "name": "generation", + "displayName": "Generation", + "shape": "string" + }, + { + "name": "rarity", + "displayName": "Rarity", + "shape": "string" + }, + { + "name": "isLegendary", + "displayName": "Legendary", + "shape": "boolean" + }, + { + "name": "isMythical", + "displayName": "Mythical", + "shape": "boolean" + }, + { + "name": "isBaby", + "displayName": "Baby", + "shape": "boolean" + }, + { + "name": "captureRate", + "displayName": "Capture Rate", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ] + }, + { + "name": "baseHappiness", + "displayName": "Base Happiness", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ] + }, + { + "name": "femalePercent", + "displayName": "Female", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "name": "genderless", + "displayName": "Genderless", + "shape": "boolean" + }, + { + "name": "eggCycles", + "displayName": "Egg Cycles", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ] + }, + { + "name": "growthRate", + "displayName": "Growth Rate", + "shape": "string" + }, + { + "name": "habitat", + "displayName": "Habitat", + "shape": "string" + }, + { + "name": "colour", + "displayName": "Colour", + "shape": "string" + }, + { + "name": "bodyShape", + "displayName": "Body Shape", + "shape": "string" + }, + { + "name": "eggGroups", + "displayName": "Egg Groups", + "shape": "string" + }, + { + "name": "evolvesFrom", + "displayName": "Evolves From", + "shape": "string" + }, + { + "name": "pokedexEntry", + "displayName": "Pokédex Entry", + "shape": "string" + }, + { + "name": "pokedexEntryGame", + "displayName": "Entry Game", + "shape": "string" + }, + { + "name": "formCount", + "displayName": "Forms", + "shape": [ + "number", + { + "decimalPlaces": 0 + } + ] + } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/pokemonStats.json b/plugins/PokeAPI/v1/dataStreams/pokemonStats.json new file mode 100644 index 00000000..07578022 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/pokemonStats.json @@ -0,0 +1,24 @@ +{ + "name": "pokemonStats", + "displayName": "Pokémon Stats", + "description": "Base stats and effort value yield for a Pokémon, one row per stat", + "tags": ["Pokémon", "Stats"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/pokemon/{{object.rawId}}", + "postRequestScript": "pokemonStats.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Pokemon"] } }, + "timeframes": false, + "defaultShaping": { "sort": { "by": [["statOrder", "asc"]] } }, + "metadata": [ + { "name": "pokemonId", "displayName": "Pokédex No.", "shape": "string", "visible": false }, + { "name": "pokemonName", "displayName": "Pokémon", "shape": "string" }, + { "name": "stat", "displayName": "Stat", "shape": "string", "role": "label" }, + { "name": "statOrder", "displayName": "Stat Order", "shape": "number", "visible": false }, + { "name": "baseStat", "displayName": "Base Stat", "shape": ["number", { "decimalPlaces": 0 }], "role": "value" }, + { "name": "effortYield", "displayName": "EV Yield", "shape": ["number", { "decimalPlaces": 0 }] }, + { "sourceId": "pokemonId", "sourceType": "Pokemon", "name": "pokemonName" } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/generationSummary.js b/plugins/PokeAPI/v1/dataStreams/scripts/generationSummary.js new file mode 100644 index 00000000..dc84b5ee --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/generationSummary.js @@ -0,0 +1,26 @@ +const titleCase = (slug) => + String(slug || '') + .split('-') + .filter(Boolean) + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(' '); + +const english = (data.names || []).find((n) => n.language && n.language.name === 'en'); + +// Match the indexed types: Stellar, Unknown and Shadow have no Pokémon or matchups +const excludedTypes = ['stellar', 'unknown', 'shadow']; +const battleTypes = (data.types || []).filter((t) => !excludedTypes.includes(t.name)); + +result = [ + { + generationId: String(data.id), + generationName: english ? english.name : titleCase(data.name), + region: titleCase(data.main_region && data.main_region.name), + games: (data.version_groups || []).map((g) => titleCase(g.name)).join(', '), + newSpecies: (data.pokemon_species || []).length, + newMoves: (data.moves || []).length, + newAbilities: (data.abilities || []).length, + newTypes: battleTypes.length, + typesIntroduced: battleTypes.map((t) => titleCase(t.name)).join(', ') + } +]; diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/pokemonByGeneration.js b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonByGeneration.js new file mode 100644 index 00000000..89e10d98 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonByGeneration.js @@ -0,0 +1,15 @@ +// pathToData cannot reach the parent generation fields, so carry them onto each species row +const english = (data.names || []).find((n) => n.language && n.language.name === "en"); +const generationName = english ? english.name : data.name; +const titleCase = (slug) => slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "); + +result = (data.pokemon_species || []).map((s) => { + const pokemonId = s.url.split("/").filter(Boolean).pop(); + return { + generationId: String(data.id), + generationName, + pokemonId, + pokemonName: titleCase(s.name), + pokedexNumber: Number(pokemonId) + }; +}); diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/pokemonByType.js b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonByType.js new file mode 100644 index 00000000..bb53a2ef --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonByType.js @@ -0,0 +1,15 @@ +// The type endpoint returns one body per type; rows are derived from the body, not context.objects +const english = (data.names || []).find((n) => n.language && n.language.name === "en"); +const typeName = english ? english.name : data.name; + +// Alternate forms (Mega, Gigantamax, regional) have ids >= 10000 and are not indexed as Pokemon, +// so keeping them would inflate per-type counts. Ids below 10000 equal the Pokemon object's rawId. +result = (data.pokemon || []) + .map((entry) => ({ + typeId: String(data.id), + typeName, + pokemonId: entry.pokemon.url.split("/").filter(Boolean).pop(), + slot: entry.slot === 1 ? "Primary" : "Secondary" + })) + .filter((row) => Number(row.pokemonId) < 10000) + .map((row) => ({ ...row, pokedexNumber: Number(row.pokemonId) })); diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/pokemonMoves.js b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonMoves.js new file mode 100644 index 00000000..5fe8a5d4 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonMoves.js @@ -0,0 +1,47 @@ +// Version-group ids are chronological except these two retro Japanese releases, +// which were added to the API out of sequence and would otherwise look "newest" +const excluded = ["red-green-japan", "blue-japan"]; + +const titleCase = (slug) => + String(slug || "") + .split("-") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" "); +const groupId = (vg) => Number(String(vg.url).split("/").filter(Boolean).pop()); + +// Flatten to one entry per (move, version group, method) +const entries = (data.moves || []).flatMap((m) => + (m.version_group_details || []) + .filter((d) => !excluded.includes(d.version_group.name)) + .map((d) => ({ move: m.move.name, detail: d, id: groupId(d.version_group) })), +); + +// Prefer the newest game with a real learnset: some recent groups (e.g. champions) +// only carry the "train" method, so require at least one level-up entry. Fall back +// to the newest group of any kind if none has level-up data. +const pickNewest = (list) => + list.reduce((best, e) => (best === null || e.id > best ? e.id : best), null); +const levelUpGroup = pickNewest(entries.filter((e) => e.detail.move_learn_method.name === "level-up")); +const chosen = levelUpGroup !== null ? levelUpGroup : pickNewest(entries); + +if (chosen === null) { + result = []; +} else { + const pokemonName = titleCase(data.species && data.species.name); + result = entries + .filter((e) => e.id === chosen) + .map((e) => { + const isLevelUp = e.detail.move_learn_method.name === "level-up"; + // Since Generation VII a level-up move at level 0 is learned on evolving + const onEvolution = isLevelUp && e.detail.level_learned_at === 0; + return { + pokemonId: String(data.id), + pokemonName, + move: titleCase(e.move), + learnMethod: onEvolution ? "Evolution" : titleCase(e.detail.move_learn_method.name), + // The API reports 0 for non-level-up methods, which is not a real level + level: isLevelUp && !onEvolution ? e.detail.level_learned_at : null, + game: titleCase(e.detail.version_group.name), + }; + }); +} diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/pokemonProfile.js b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonProfile.js new file mode 100644 index 00000000..ffd2a7df --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonProfile.js @@ -0,0 +1,33 @@ +const title = (slug) => + (slug || "") + .split("-") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" "); + +const typeNames = (data.types || []) + .slice() + .sort((a, b) => a.slot - b.slot) + .map((t) => title(t.type.name)); +const abilities = data.abilities || []; +const visible = abilities.filter((a) => !a.is_hidden).sort((a, b) => a.slot - b.slot); +const hidden = abilities.find((a) => a.is_hidden); + +result = [ + { + pokemonId: String(data.id), + pokemonName: title(data.species && data.species.name), + pokedexNumber: data.id, + primaryType: typeNames[0] || null, + secondaryType: typeNames[1] || null, + types: typeNames.join(" / "), + heightM: data.height / 10, + weightKg: data.weight / 10, + baseExperience: data.base_experience, + abilities: visible.map((a) => title(a.ability.name)).join(", "), + hiddenAbility: hidden ? title(hidden.ability.name) : null, + statTotal: (data.stats || []).reduce((s, x) => s + x.base_stat, 0), + artworkUrl: _.get(data, ["sprites", "other", "official-artwork", "front_default"], null), + spriteUrl: _.get(data, ["sprites", "front_default"], null), + cryUrl: _.get(data, ["cries", "latest"], null), + }, +]; diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/pokemonSpecies.js b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonSpecies.js new file mode 100644 index 00000000..00e58c54 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonSpecies.js @@ -0,0 +1,48 @@ +// One row per species. A script is needed because names, genera and flavour text are +// arrays of per-language entries that must be filtered to English and reduced to one value. +const title = (slug) => + slug + ? slug + .split("-") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" ") + : null; + +const english = (entries) => (entries || []).filter((e) => e.language && e.language.name === "en"); + +const englishName = english(data.names)[0]; +const englishGenus = english(data.genera)[0]; + +// Flavour text is ordered oldest to newest, so the last English entry is the most recent game +const entries = english(data.flavor_text_entries); +const latest = entries[entries.length - 1]; + +// gender_rate is the chance of being female in eighths; -1 means genderless +const genderless = data.gender_rate === -1; + +result = [ + { + pokemonId: String(data.id), + pokemonName: englishName ? englishName.name : title(data.name), + genus: englishGenus ? englishGenus.genus : null, + generation: data.generation ? "Generation " + data.generation.name.split("-").pop().toUpperCase() : null, + isLegendary: !!data.is_legendary, + isMythical: !!data.is_mythical, + isBaby: !!data.is_baby, + rarity: data.is_legendary ? "Legendary" : data.is_mythical ? "Mythical" : data.is_baby ? "Baby" : "Standard", + captureRate: data.capture_rate, + baseHappiness: data.base_happiness, + femalePercent: genderless ? null : (data.gender_rate / 8) * 100, + genderless, + eggCycles: data.hatch_counter, + growthRate: title(data.growth_rate && data.growth_rate.name), + habitat: title(data.habitat && data.habitat.name), + colour: title(data.color && data.color.name), + bodyShape: title(data.shape && data.shape.name), + eggGroups: (data.egg_groups || []).map((g) => title(g.name)).join(", "), + evolvesFrom: title(data.evolves_from_species && data.evolves_from_species.name), + pokedexEntry: latest ? latest.flavor_text.replace(/\s+/g, " ").trim() : null, + pokedexEntryGame: latest ? title(latest.version.name) : null, + formCount: (data.varieties || []).length, + }, +]; diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/pokemonStats.js b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonStats.js new file mode 100644 index 00000000..848a1f7c --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/pokemonStats.js @@ -0,0 +1,20 @@ +const titleCase = (slug) => + slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "); + +const labels = { + hp: "HP", + attack: "Attack", + defense: "Defense", + "special-attack": "Sp. Atk", + "special-defense": "Sp. Def", + speed: "Speed", +}; + +result = (data.stats || []).map((s, i) => ({ + pokemonId: String(data.id), + pokemonName: titleCase(data.species.name), + stat: labels[s.stat.name] || titleCase(s.stat.name), + statOrder: i + 1, + baseStat: s.base_stat, + effortYield: s.effort, +})); diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/typeMatchups.js b/plugins/PokeAPI/v1/dataStreams/scripts/typeMatchups.js new file mode 100644 index 00000000..b36f2451 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/typeMatchups.js @@ -0,0 +1,32 @@ +// damage_relations is an object of six arrays, not a list of rows, so each (relation, other type) +// pair has to be unrolled into its own row. Multiplier and wording are fixed per relation key. +const relations = { + double_damage_to: { direction: "Attacking", multiplier: 2, effectiveness: "Super effective", relation: "Double damage to" }, + half_damage_to: { direction: "Attacking", multiplier: 0.5, effectiveness: "Not very effective", relation: "Half damage to" }, + no_damage_to: { direction: "Attacking", multiplier: 0, effectiveness: "No effect", relation: "No damage to" }, + double_damage_from: { direction: "Defending", multiplier: 2, effectiveness: "Super effective", relation: "Double damage from" }, + half_damage_from: { direction: "Defending", multiplier: 0.5, effectiveness: "Not very effective", relation: "Half damage from" }, + no_damage_from: { direction: "Defending", multiplier: 0, effectiveness: "No effect", relation: "No damage from" }, +}; + +// The slug ("fire") is title-cased; the localised name is only used for the scoped type itself +const titleCase = (slug) => slug.charAt(0).toUpperCase() + slug.slice(1); +const idFromUrl = (url) => url.split("/").filter(Boolean).pop(); + +// Identity comes from the response body, so it stays correct when a tile is scoped to several types +const english = (data.names || []).find((n) => n.language && n.language.name === "en"); +const typeName = english ? english.name : titleCase(data.name); +const typeId = String(data.id); + +result = Object.entries(relations).flatMap(([key, rel]) => + ((data.damage_relations || {})[key] || []).map((other) => ({ + typeId, + typeName, + direction: rel.direction, + multiplier: rel.multiplier, + effectiveness: rel.effectiveness, + relation: rel.relation, + otherTypeId: idFromUrl(other.url), + otherTypeName: titleCase(other.name), + })), +); diff --git a/plugins/PokeAPI/v1/dataStreams/scripts/types.js b/plugins/PokeAPI/v1/dataStreams/scripts/types.js new file mode 100644 index 00000000..cf76df0f --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/scripts/types.js @@ -0,0 +1,4 @@ +// Stellar (Tera-only), Unknown and Shadow are listed by the API but have no Pokémon or matchups +const excluded = ["stellar", "unknown", "shadow"]; + +result = (data.results || []).filter((t) => !excluded.includes(t.name)); diff --git a/plugins/PokeAPI/v1/dataStreams/typeMatchups.json b/plugins/PokeAPI/v1/dataStreams/typeMatchups.json new file mode 100644 index 00000000..8919dc19 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/typeMatchups.json @@ -0,0 +1,26 @@ +{ + "name": "typeMatchups", + "displayName": "Type Matchups", + "description": "Damage multipliers a type deals to and takes from every other type", + "tags": ["Type"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/type/{{object.rawId}}", + "postRequestScript": "typeMatchups.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Pokemon Type"] } }, + "timeframes": false, + "defaultShaping": { "sort": { "by": [["direction", "asc"], ["multiplier", "desc"]] } }, + "metadata": [ + { "name": "typeId", "displayName": "Type ID", "shape": "string", "visible": false }, + { "name": "typeName", "displayName": "Type", "shape": "string", "role": "label" }, + { "name": "direction", "displayName": "Direction", "shape": "string" }, + { "name": "multiplier", "displayName": "Multiplier", "shape": ["number", { "decimalPlaces": 1 }], "role": "value" }, + { "name": "effectiveness", "displayName": "Effectiveness", "shape": "string" }, + { "name": "relation", "displayName": "Relation", "shape": "string" }, + { "name": "otherTypeId", "displayName": "Other Type ID", "shape": "string", "visible": false }, + { "name": "otherTypeName", "displayName": "Other Type", "shape": "string" }, + { "sourceId": "otherTypeId", "sourceType": "Pokemon Type", "name": "otherTypeName" } + ] +} diff --git a/plugins/PokeAPI/v1/dataStreams/types.json b/plugins/PokeAPI/v1/dataStreams/types.json new file mode 100644 index 00000000..23d07c79 --- /dev/null +++ b/plugins/PokeAPI/v1/dataStreams/types.json @@ -0,0 +1,36 @@ +{ + "name": "types", + "displayName": "Types", + "description": "Pokémon battle types (excludes Stellar, Unknown and Shadow, which have no Pokémon)", + "tags": ["Pokémon", "Type"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/type", + "getArgs": [{ "key": "limit", "value": "100" }], + "postRequestScript": "types.js" + }, + "matches": "none", + "timeframes": false, + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "url", "displayName": "URL", "shape": "url", "visible": false }, + { "name": "name", "displayName": "Slug", "shape": "string", "visible": false }, + { + "name": "id", + "displayName": "ID", + "shape": "string", + "role": "id", + "computed": true, + "valueExpression": "{{ $['url'].split('/').filter(Boolean).pop() }}" + }, + { + "name": "typeName", + "displayName": "Type", + "shape": "string", + "role": "label", + "computed": true, + "valueExpression": "{{ $['name'].charAt(0).toUpperCase() + $['name'].slice(1) }}" + } + ] +} diff --git a/plugins/PokeAPI/v1/defaultContent/generation.dash.json b/plugins/PokeAPI/v1/defaultContent/generation.dash.json new file mode 100644 index 00000000..935f6127 --- /dev/null +++ b/plugins/PokeAPI/v1/defaultContent/generation.dash.json @@ -0,0 +1,293 @@ +{ + "name": "Generation", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [ + "{{variables.[Generation]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "d4e5226c-939f-4367-9012-2004b5e44308", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Region", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Generation]}}" + }, + "variables": [ + "{{variables.[Generation]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "region", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "51be13e5-693c-489b-9703-a21f249dcd0c", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "New Species", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Generation]}}" + }, + "variables": [ + "{{variables.[Generation]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "newSpecies", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "e25cf818-b860-44d2-a1d4-796731977fca", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "New Moves", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Generation]}}" + }, + "variables": [ + "{{variables.[Generation]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "newMoves", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "529edfd7-d18f-43ee-9597-4141f32a61c8", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "New Abilities", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Generation]}}" + }, + "variables": [ + "{{variables.[Generation]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "newAbilities", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "61ea6346-6eea-460b-b12d-a211371b8caa", + "x": 0, + "y": 2, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Generation Details", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Generation]}}" + }, + "variables": [ + "{{variables.[Generation]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "games", + "newTypes", + "typesIntroduced" + ], + "hiddenColumns": [ + "generationId", + "generationName", + "region", + "newSpecies", + "newMoves", + "newAbilities" + ] + } + } + } + } + }, + { + "i": "f8fe6713-811b-403c-9f61-eafe24064669", + "x": 0, + "y": 5, + "w": 4, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Species Introduced", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByGeneration]}}", + "name": "pokemonByGeneration", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "pokedexNumber", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Generation]}}" + }, + "variables": [ + "{{variables.[Generation]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "pokedexNumber", + "pokemonName" + ], + "hiddenColumns": [ + "generationId", + "generationName", + "pokemonId" + ], + "rowLinkColumnName": { + "value": "pokemonName", + "label": "Pokémon" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/PokeAPI/v1/defaultContent/manifest.json b/plugins/PokeAPI/v1/defaultContent/manifest.json new file mode 100644 index 00000000..30a2086f --- /dev/null +++ b/plugins/PokeAPI/v1/defaultContent/manifest.json @@ -0,0 +1,8 @@ +{ + "items": [ + { "name": "overview", "type": "dashboard" }, + { "name": "pokemon", "type": "dashboard" }, + { "name": "pokemonType", "type": "dashboard" }, + { "name": "generation", "type": "dashboard" } + ] +} diff --git a/plugins/PokeAPI/v1/defaultContent/overview.dash.json b/plugins/PokeAPI/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..a5b54085 --- /dev/null +++ b/plugins/PokeAPI/v1/defaultContent/overview.dash.json @@ -0,0 +1,430 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "e5d6e761-2c9e-4b2c-b5f1-e08f1cb9ec05", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Pokémon", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByGeneration]}}", + "name": "pokemonByGeneration", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "species" + } + } + } + } + }, + { + "i": "c94e4559-ef32-4018-8077-44b89f5c70ea", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Types", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByType]}}", + "name": "pokemonByType", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "typeName" + ] + } + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "typeName_distinctCount", + "comparisonColumn": "none", + "label": "battle types" + } + } + } + } + }, + { + "i": "ce2b0c47-0e63-467a-888a-1370eebf1deb", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Generations", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "generations" + } + } + } + } + }, + { + "i": "24a53298-a086-4eb6-a812-cc06e4d6494d", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Moves", + "description": "Moves introduced across every generation", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "sum", + "names": [ + "newMoves" + ] + } + ] + } + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "newMoves_sum", + "comparisonColumn": "none", + "label": "moves" + } + } + } + } + }, + { + "i": "c81cb9df-0e10-41be-b789-06b5db700415", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Species per Generation", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByGeneration]}}", + "name": "pokemonByGeneration", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "generationName", + "uniqueValues" + ], + [ + "generationId", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "generationId_uniqueValues", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "generationName_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Species", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": true, + "grouping": false, + "range": { + "type": "auto" + } + } + } + } + } + }, + { + "i": "fe6a113f-a472-4e93-813e-b417d9c024a1", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Pokémon per Primary Type", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByType]}}", + "name": "pokemonByType", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "slot", + "operation": "equals", + "value": "Primary" + } + ] + }, + "group": { + "by": [ + [ + "typeName", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "typeName_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Pokémon", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": true, + "grouping": false, + "range": { + "type": "auto" + } + } + } + } + } + }, + { + "i": "b22f0684-249d-4e32-81f5-a08baa640e2a", + "x": 0, + "y": 6, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Generation Summary", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[generationSummary]}}", + "name": "generationSummary", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "generationId", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Generations]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "generationName", + "region", + "newSpecies", + "newMoves", + "newAbilities", + "games" + ], + "hiddenColumns": [ + "generationId", + "newTypes", + "typesIntroduced" + ], + "rowLinkColumnName": { + "value": "generationName", + "label": "Generation" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/PokeAPI/v1/defaultContent/pokemon.dash.json b/plugins/PokeAPI/v1/defaultContent/pokemon.dash.json new file mode 100644 index 00000000..a2c96cad --- /dev/null +++ b/plugins/PokeAPI/v1/defaultContent/pokemon.dash.json @@ -0,0 +1,561 @@ +{ + "name": "Pokémon", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [ + "{{variables.[Pokémon]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "46f2a2a4-dd00-4edb-8427-67e77fef0ff5", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Types", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonProfile]}}", + "name": "pokemonProfile", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "types", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "52457286-4349-4beb-9f5f-c36475081063", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Category", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonSpecies]}}", + "name": "pokemonSpecies", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "genus", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "c9a2d2ea-6373-4560-8b85-9ee121c255e9", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Generation", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonSpecies]}}", + "name": "pokemonSpecies", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "generation", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "d7bfa116-47a6-450e-b71d-703385e4258e", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Base Stat Total", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonProfile]}}", + "name": "pokemonProfile", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "statTotal", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "ef049825-6169-40ec-b129-3b852b357a1f", + "x": 0, + "y": 2, + "w": 4, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Pokédex Entry", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonSpecies]}}", + "name": "pokemonSpecies", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "pokedexEntry", + "pokedexEntryGame" + ], + "hiddenColumns": [ + "pokemonId", + "pokemonName", + "genus", + "generation", + "rarity", + "isLegendary", + "isMythical", + "isBaby", + "captureRate", + "baseHappiness", + "femalePercent", + "genderless", + "eggCycles", + "growthRate", + "habitat", + "colour", + "bodyShape", + "eggGroups", + "evolvesFrom", + "formCount" + ] + } + } + } + } + }, + { + "i": "61e211f8-88c3-409d-9ccc-f79a9ec970ba", + "x": 0, + "y": 4, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Base Stats", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonStats]}}", + "name": "pokemonStats", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "statOrder", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "stat", + "yAxisData": [ + "baseStat" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Base stat", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": true, + "grouping": false, + "range": { + "type": "auto" + } + } + } + } + } + }, + { + "i": "6ef68602-db72-40f9-ba0f-e6f24fa58c0d", + "x": 2, + "y": 4, + "w": 1, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Profile", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonProfile]}}", + "name": "pokemonProfile", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "pokedexNumber", + "heightM", + "weightKg", + "abilities", + "hiddenAbility", + "baseExperience", + "artworkUrl", + "spriteUrl", + "cryUrl" + ], + "hiddenColumns": [ + "pokemonId", + "pokemonName", + "primaryType", + "secondaryType", + "types", + "statTotal" + ] + } + } + } + } + }, + { + "i": "c219a921-9f1d-4e09-99d6-9e93bdd2c4b8", + "x": 3, + "y": 4, + "w": 1, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Species Details", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonSpecies]}}", + "name": "pokemonSpecies", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "rarity", + "evolvesFrom", + "habitat", + "colour", + "bodyShape", + "captureRate", + "baseHappiness", + "femalePercent", + "genderless", + "eggGroups", + "eggCycles", + "growthRate", + "formCount" + ], + "hiddenColumns": [ + "pokemonId", + "pokemonName", + "genus", + "generation", + "isLegendary", + "isMythical", + "isBaby", + "pokedexEntry", + "pokedexEntryGame" + ] + } + } + } + } + }, + { + "i": "4754bedc-3227-4846-8517-ea6887794c54", + "x": 0, + "y": 9, + "w": 3, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Moves", + "description": "Moves learnable in the most recent game the Pokémon appears in", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonMoves]}}", + "name": "pokemonMoves", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "learnMethod", + "asc" + ], + [ + "level", + "asc" + ], + [ + "move", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "move", + "learnMethod", + "level", + "game" + ], + "hiddenColumns": [ + "pokemonId", + "pokemonName" + ] + } + } + } + } + }, + { + "i": "d12b4fc5-14a8-4317-8b07-531d6448211e", + "x": 3, + "y": 9, + "w": 1, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Moves by Learn Method", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonMoves]}}", + "name": "pokemonMoves", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "learnMethod", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "scope": "{{scopes.[All Pokémon]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon]}}" + }, + "variables": [ + "{{variables.[Pokémon]}}" + ], + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "learnMethod_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + } + ] + } +} diff --git a/plugins/PokeAPI/v1/defaultContent/pokemonType.dash.json b/plugins/PokeAPI/v1/defaultContent/pokemonType.dash.json new file mode 100644 index 00000000..999a32a0 --- /dev/null +++ b/plugins/PokeAPI/v1/defaultContent/pokemonType.dash.json @@ -0,0 +1,407 @@ +{ + "name": "Pokémon Type", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [ + "{{variables.[Pokémon Type]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "c3268220-7d94-454a-bfee-f191783a0732", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Pokémon", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByType]}}", + "name": "pokemonByType", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon Type]}}" + }, + "variables": [ + "{{variables.[Pokémon Type]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "with this type" + } + } + } + } + }, + { + "i": "e865c706-7685-477a-88d7-058ba1769cdc", + "x": 0, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Weak to", + "description": "Attacking types that deal double damage to this type", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[typeMatchups]}}", + "name": "typeMatchups", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "relation", + "operation": "equals", + "value": "Double damage from" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon Type]}}" + }, + "variables": [ + "{{variables.[Pokémon Type]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "types" + } + } + } + } + }, + { + "i": "358110a7-5502-40a5-9da2-60de10164959", + "x": 1, + "y": 0, + "w": 1, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Primary vs Secondary", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByType]}}", + "name": "pokemonByType", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "slot", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon Type]}}" + }, + "variables": [ + "{{variables.[Pokémon Type]}}" + ], + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "slot_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "8db3b8a8-6875-452b-9acc-16695f65a058", + "x": 2, + "y": 0, + "w": 1, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Attacking", + "description": "Damage this type deals to other types", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[typeMatchups]}}", + "name": "typeMatchups", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "direction", + "operation": "equals", + "value": "Attacking" + } + ] + }, + "sort": { + "by": [ + [ + "multiplier", + "desc" + ], + [ + "otherTypeName", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon Type]}}" + }, + "variables": [ + "{{variables.[Pokémon Type]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "otherTypeName", + "multiplier", + "effectiveness" + ], + "hiddenColumns": [ + "typeId", + "typeName", + "direction", + "relation", + "otherTypeId" + ], + "columnDisplayNames": { + "otherTypeName": "Target" + }, + "rowLinkColumnName": { + "value": "otherTypeName", + "label": "Other Type" + } + } + } + } + } + }, + { + "i": "4a3b9ada-0ddf-4b89-84d1-0e7910e00a3a", + "x": 3, + "y": 0, + "w": 1, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Defending", + "description": "Damage this type takes from other types", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[typeMatchups]}}", + "name": "typeMatchups", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "direction", + "operation": "equals", + "value": "Defending" + } + ] + }, + "sort": { + "by": [ + [ + "multiplier", + "desc" + ], + [ + "otherTypeName", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon Type]}}" + }, + "variables": [ + "{{variables.[Pokémon Type]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "otherTypeName", + "multiplier", + "effectiveness" + ], + "hiddenColumns": [ + "typeId", + "typeName", + "direction", + "relation", + "otherTypeId" + ], + "columnDisplayNames": { + "otherTypeName": "Attacker" + }, + "rowLinkColumnName": { + "value": "otherTypeName", + "label": "Other Type" + } + } + } + } + } + }, + { + "i": "21b1a322-2352-4d0b-ad73-65082a40d408", + "x": 0, + "y": 4, + "w": 4, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Pokémon", + "description": "", + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pokemonByType]}}", + "name": "pokemonByType", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "pokedexNumber", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Pokémon Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Pokémon Type]}}" + }, + "variables": [ + "{{variables.[Pokémon Type]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "pokedexNumber", + "pokemonName", + "slot" + ], + "hiddenColumns": [ + "typeId", + "typeName", + "pokemonId" + ], + "rowLinkColumnName": { + "value": "pokemonName", + "label": "Pokémon" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/PokeAPI/v1/defaultContent/scopes.json b/plugins/PokeAPI/v1/defaultContent/scopes.json new file mode 100644 index 00000000..4042f7d9 --- /dev/null +++ b/plugins/PokeAPI/v1/defaultContent/scopes.json @@ -0,0 +1,38 @@ +[ + { + "name": "All Pokémon", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Pokemon"] } + }, + "variable": { + "name": "Pokémon", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Pokémon Types", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Pokemon Type"] } + }, + "variable": { + "name": "Pokémon Type", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Generations", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Pokemon Generation"] } + }, + "variable": { + "name": "Generation", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/PokeAPI/v1/docs/README.md b/plugins/PokeAPI/v1/docs/README.md new file mode 100644 index 00000000..59d20f0a --- /dev/null +++ b/plugins/PokeAPI/v1/docs/README.md @@ -0,0 +1,61 @@ +Explore every Pokémon, battle type and generation in SquaredUp, including base stats, Pokédex entries, type matchups and learnable moves. The data comes from [PokéAPI](https://pokeapi.co), a free and open RESTful Pokémon API ([API documentation](https://pokeapi.co/docs/v2)). + +## Setup + +PokéAPI is free and needs no account, API key or sign-in. + +1. Add the **PokéAPI** data source. +2. Give it a name and save. There is nothing else to configure. + +SquaredUp then imports every Pokémon, type and generation, and installs the out-of-the-box dashboards. + +## Configuration fields + +This plugin has no configuration fields. + +On save, the plugin checks that `https://pokeapi.co` is reachable by fetching the list of types. A failure means SquaredUp (or the agent running the plugin) cannot reach the internet address `pokeapi.co`. + +## What this plugin monitors + +- **Pokémon:** types, height, weight, abilities, base stats and effort value yield, official artwork and cry, plus the moves each Pokémon can learn. +- **Pokédex data:** the latest English Pokédex entry, category, generation, rarity (legendary, mythical or baby), capture rate, gender ratio, egg groups, habitat and what each Pokémon evolves from. +- **Types:** every attacking and defending damage multiplier (the type chart), and which Pokémon have each type as their primary or secondary type. +- **Generations:** each generation's main region and games, the species it introduced, and how many new moves, abilities and types it added. + +The out-of-the-box dashboards include an **Overview** plus a perspective for each **Pokémon**, **Pokémon Type** and **Generation**. + +## Data streams + +- **Pokémon Profile:** types, height (m), weight (kg), base experience, abilities, base stat total, and links to artwork, sprite and cry. One row per Pokémon. +- **Pokémon Stats:** HP, Attack, Defense, Sp. Atk, Sp. Def and Speed, with effort value yield. One row per stat for each Pokémon. +- **Pokémon Species:** Pokédex entry, category, generation, rarity, capture rate, base happiness, gender ratio, egg cycles, growth rate, habitat, colour, shape, egg groups, evolves-from and number of forms. One row per Pokémon. +- **Pokémon Moves:** moves learnable in the most recent game the Pokémon appears in, with learn method (Level Up, Evolution, Machine, Egg or Tutor) and level. One row per move for each Pokémon. +- **Type Matchups:** damage multipliers (2×, ½× or 0×) a type deals to and takes from every other type. One row per matchup for each type. +- **Pokémon by Type:** the Pokémon that have a type, and whether it is their primary or secondary type. Rows link to the Pokémon. +- **Generation Summary:** main region, games, and the number of new species, moves, abilities and types for each generation. +- **Pokémon by Generation:** the species a generation introduced. Rows link to the Pokémon. + +Select several objects on any of these streams to compare them side by side, for example the base stats of two Pokémon or the Pokémon counts of every type. + +The **Pokémon**, **Types** and **Generations** streams are hidden. They build the object list, and **Types** also checks the connection on save. + +## What gets indexed + +| Object type | API source | Represents | +| ----------- | ---------- | ---------- | +| **Pokémon** | `GET /api/v2/pokemon-species` | A species in the National Pokédex. The object ID is its National Pokédex number. | +| **Pokémon Type** | `GET /api/v2/type` | A battle type, such as Fire or Water. Stellar, Unknown and Shadow are skipped because they have no Pokémon. | +| **Generation** | `GET /api/v2/generation` | A game generation, such as Generation I. | + +The data is re-imported every 12 hours, so new species and generations appear automatically once PokéAPI adds them. + +## Known limitations + +- **Species only, no alternate forms.** Mega Evolutions, Gigantamax and regional forms are not indexed and are left out of **Pokémon by Type**. Profiles, stats and moves show each species' default form. +- **Names come from API identifiers.** Object, move, game and ability names are built from PokéAPI's identifiers, so punctuation is missing: "Mr Mime", "Farfetchd", "Lets Go Eevee", "Heartgold Soulsilver". **Pokémon Species** shows the official English Pokémon name, such as "Mr. Mime". +- **Moves cover one game.** **Pokémon Moves** shows the newest game in which the Pokémon has a level-up learnset, which differs between Pokémon (for example Scarlet Violet for Charizard, Legends Arceus for Ponyta). Learnsets from other games are not shown. +- **No evolution chains.** Only the species a Pokémon evolves from is shown. +- **Images are links.** Artwork, sprites and cries are links rather than images inside tiles. +- **English only.** Names, categories and Pokédex entries are in English. +- **No time range.** PokéAPI is static reference data, so no data stream has a timeframe. +- **Read-only.** The plugin only reads public data from PokéAPI, which has no rate limit but asks clients to use it fairly. diff --git a/plugins/PokeAPI/v1/icon.svg b/plugins/PokeAPI/v1/icon.svg new file mode 100644 index 00000000..c1c0242d --- /dev/null +++ b/plugins/PokeAPI/v1/icon.svg @@ -0,0 +1 @@ +pokeapi diff --git a/plugins/PokeAPI/v1/indexDefinitions/default.json b/plugins/PokeAPI/v1/indexDefinitions/default.json new file mode 100644 index 00000000..5cc70be0 --- /dev/null +++ b/plugins/PokeAPI/v1/indexDefinitions/default.json @@ -0,0 +1,34 @@ +{ + "steps": [ + { + "name": "pokemon", + "dataStream": { "name": "pokemon" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "pokemonName", + "type": { "value": "Pokemon" } + } + }, + { + "name": "types", + "dataStream": { "name": "types" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "typeName", + "type": { "value": "Pokemon Type" } + } + }, + { + "name": "generations", + "dataStream": { "name": "generations" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "generationName", + "type": { "value": "Pokemon Generation" } + } + } + ] +} diff --git a/plugins/PokeAPI/v1/metadata.json b/plugins/PokeAPI/v1/metadata.json new file mode 100644 index 00000000..768b8d27 --- /dev/null +++ b/plugins/PokeAPI/v1/metadata.json @@ -0,0 +1,37 @@ +{ + "name": "pokeapi", + "displayName": "PokéAPI", + "version": "1.0.0", + "author": { + "name": "@clarkd", + "type": "community" + }, + "description": "Explore every Pokémon, type and generation from the free PokéAPI, with base stats, Pokédex entries, type matchups and learnable moves.", + "category": "Fun", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": ["pokemon", "pokémon", "pokedex", "pokeapi", "games"], + "objectTypes": ["Pokemon", "Pokemon Type", "Pokemon Generation"], + "links": [ + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/PokeAPI/v1", + "label": "Repository" + }, + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/PokeAPI/v1/docs/README.md", + "label": "Help adding this plugin" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "authMode": "none", + "baseUrl": "https://pokeapi.co/api/v2" + } + } +} diff --git a/plugins/PokeAPI/v1/ui.json b/plugins/PokeAPI/v1/ui.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/plugins/PokeAPI/v1/ui.json @@ -0,0 +1 @@ +[]