Skip to content

Instantly share code, notes, and snippets.

@DustinAlandzes
Last active March 21, 2021 23:07
Show Gist options
  • Save DustinAlandzes/b765d54ba13b8ee28d7871dc207e7712 to your computer and use it in GitHub Desktop.
Save DustinAlandzes/b765d54ba13b8ee28d7871dc207e7712 to your computer and use it in GitHub Desktop.
Copy of slippi-graph.ipynb
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "Copy of slippi-graph.ipynb",
"private_outputs": true,
"provenance": [],
"collapsed_sections": [],
"toc_visible": true,
"machine_shape": "hm",
"authorship_tag": "ABX9TyMZdEVpNFuBttC+a1Ittz8j",
"include_colab_link": true
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/DustinAlandzes/b765d54ba13b8ee28d7871dc207e7712/copy-of-slippi-graph.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pTeKDnpr4fwS"
},
"source": [
"https://py-slippi.readthedocs.io/en/latest/source/slippi.html\n",
"\n",
"https://github.com/BrodyVoth/slippi-cumulative-stats/blob/master/slippi-stats.js"
]
},
{
"cell_type": "code",
"metadata": {
"id": "23xpBNf7yEA4"
},
"source": [
"!pip install py-slippi\n",
"!pip install requests\n",
"\n",
"!pip install requests-cache\n",
"import requests_cache\n",
"requests_cache.install_cache('requests_cache')\n",
"# https://github.com/reclosedev/requests-cache#usage-example\n",
"# requests_cache helps prevent unneeded waiting for the data to re-download\n",
"\n",
"# upgrade ipython and ipykernel so I can use async/await\n",
"!pip install ipython ipykernel --upgrade"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "zgJ1BjbcyOGC"
},
"source": [
"from typing import Text\n",
"import requests\n",
"import os\n",
"from pathlib import Path\n",
"\n",
"SLP_DIRECTORY_NAME = '/content/slp'\n",
"Path(SLP_DIRECTORY_NAME).mkdir(parents=True, exist_ok=True)\n",
" \n",
"# download a slp file for testing\n",
"# TODO: https://docs.python.org/3/library/pathlib.html\n",
"\n",
"# with open(f'{SLP_DIRECTORY_NAME}/test.slp', 'wb') as f:\n",
"# response = requests.get(\"https://spaceanimalz.com/f/8c72f820cf154b4581de/?raw=1\")\n",
"# f.write(response.content)\n",
"\n",
"# test_game = Game(f'./{SLP_DIRECTORY_NAME}/test.slp')\n",
"# duration_in_minutes = test_game.metadata.duration // 60 // 30\n",
"# print(f\"duration: {duration_in_minutes} minutes\") # in minutes\n",
"# print(test_game.start.stage) # stage is in start attribute"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "AeM6CqJGfQED"
},
"source": [
"# cursed stuff I'm doing to download slippi files from seafile\n",
"\n",
"generate a share link (I use an expiration date) and put it in the share_link variable below\n",
"\n",
"![image.png](data:image/png;base64,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)"
]
},
{
"cell_type": "code",
"metadata": {
"id": "hxlrGJ6sGekL"
},
"source": [
"from typing import Any, Dict, Optional, Text, Set\n",
"from pathlib import Path\n",
"import os\n",
"from multiprocessing import Pool\n",
"\n",
"class SeafileException(Exception):\n",
" \"\"\" to make it clear this is expected if you are not using a shared folder \"\"\"\n",
"\n",
"\n",
"def get_list_of_file_links_from_share_link(share_link: Text, path: Optional[Text] = None) -> Set[Text]:\n",
" \"\"\" takes in a seafile share link, \n",
" outputs a list of local file paths (pathlib objects?) \n",
"\n",
" make sure the share link has the ending forward slash (/)\n",
" \n",
" use the api to list the files and directories in the shared folder\n",
"\n",
" based on https://github.com/mperreir/share_link_dl\n",
" \"\"\"\n",
"\n",
" # create the api link from the share link\n",
" api_link: Text = f\"{share_link.replace('/d/', '/api/v2.1/share-links/')}dirents/\"\n",
" if path:\n",
" api_link = f\"{api_link}?path={requests.utils.quote(path)}\"\n",
" print(api_link)\n",
"\n",
" # do a GET request to the api link\n",
" response: requests.Response = requests.get(api_link)\n",
" try:\n",
" response.raise_for_status()\n",
" except requests.HTTPError as e:\n",
" status_code: int = e.response.status_code\n",
" if status_code == 400:\n",
" raise SeafileException(response.json()['error_msg'])\n",
" else:\n",
" raise\n",
"\n",
" file_urls: List[Text] = []\n",
" for file_or_directory in response.json()['dirent_list']:\n",
" is_directory = file_or_directory['is_dir']\n",
" if is_directory:\n",
" # call this function recursively with the path parameter set to the 'folder_path' key\n",
" folder_path = file_or_directory['folder_path']\n",
" file_urls.extend(download_seafile_directory_from_share_link(share_link, folder_path))\n",
" else:\n",
" # add the file url to the set\n",
" file_path: Text = file_or_directory['file_path']\n",
" file_url: Text = \"{}files/?p={}&dl=1\".format(share_link, requests.utils.quote(file_path))\n",
" file_urls.append(file_url)\n",
"\n",
" return file_urls\n",
"\n",
"share_link: Text = \"https://spaceanimalz.com/d/96a2f7687715427ead65/\"\n",
"slp_urls = get_list_of_file_links_from_share_link(share_link)\n",
"print(slp_urls)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "PIFPxR_xvKwn"
},
"source": [
"async def download_and_save(url: Text) -> Path:\n",
" destination_path = Path('/content/slp', f'./{url.replace(\"&dl=1\", \"\").split(\"?p=\")[1]}')\n",
"\n",
" # create parent directory if it doesn't exist\n",
" destination_path.parent.mkdir(parents=True, exist_ok=True)\n",
"\n",
" # only download it if it isn't already downloaded\n",
" if destination_path.is_file():\n",
" print(f\"Already downloaded {desination_path}\")\n",
" return destination_path\n",
"\n",
" print(f\"Downloading {url} to {destination_path}\")\n",
" response = requests.get(url)\n",
" with open(destination_path, \"wb\") as f:\n",
" f.write(response.content)\n",
" return destination_path\n",
"\n",
"for url in slp_urls:\n",
" await download_and_save(url)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "mKjkXJr1HXYp"
},
"source": [
"import glob\n",
"from typing import Any, Dict, List, Text\n",
"from slippi import Game\n",
"\n",
"import pandas as pd\n",
"\n",
"STAGE_MAPPING = {\n",
" 2: 'Fountain of Dreams',\n",
" 3: 'Pokemon Stadium',\n",
" 8: 'Yoshi\\'s Story',\n",
" 28: 'Dream Land N64',\n",
" 31: 'Battlefield',\n",
" 32: 'Final Destination'\n",
"}\n",
"\n",
"# todo: all stages, from friendlies. create dict from int enum\n",
"\n",
"async def load_slp_file(path: Text) -> Dict[Text, Any]:\n",
" \"\"\" given a path, loads game into memory and returns a dictionary with the information \"\"\"\n",
" with open(path, 'rb') as f:\n",
" game = Game(f)\n",
" return {'duration': game.metadata.duration / 60 / 60, 'date': game.metadata.date, 'Stage': STAGE_MAPPING[game.start.stage],\n",
" 'Version': str(game.start.slippi.version)}\n",
"\n",
"\n",
"games: List[Dict[Text, List[Any]]] = []\n",
"for path in glob.glob(directory + \"**/*.slp\", recursive=True):\n",
" try:\n",
" game = await load_file(path)\n",
"\n",
" # todo: if any of them fail, don't append any\n",
"\n",
" games['duration'].append(game['duration'])\n",
" games['date'].append(game['date'])\n",
" # games['character_1'].append(game.metadata.players[0])\n",
" # games['character_2'].append(game.metadata.players[1])\n",
" games['Stage'].append(game['Stage'])\n",
" games['Version'].append(str(game['Version'])\n",
" # games['character_1_win'].append(None)\n",
" # games['character_2_win'].append(None)\n",
" # print(game.metadata.players[0].netplay.code)\n",
" # print(game.metadata.players[1].netplay.code)\n",
" # print(game.metadata.players[1].netplay.name)\n",
" # print(game.metadata.players[1].characters)\n",
" except Exception as e:\n",
" # don't stop parsing because one file isn't working\n",
" print(f\"There was a problem parsing {path}\")\n",
" # delete it? could be that it just downloaded badly and I need to handle large files better\n",
" print(e)\n",
"\n",
"print(f\"Number of games: {len(games)}\")"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "BOqyMBjWsSwB"
},
"source": [
"for key in list_of_games.keys():\n",
" print(key, len(list_of_games[key]))"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "ENeUMsTIroyT"
},
"source": [
"df = pd.DataFrame(list_of_games)\n",
"df.to_parquet('df.parquet.gzip', compression='gzip')\n",
"pd.read_parquet('df.parquet.gzip')"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "YJQ8RZzkHZlK"
},
"source": [
"# group by day: game count, duration sum, win loss ratio, \n",
"# per character, duration per stage, per matchup\n",
"\n",
"import altair as alt\n",
"\n",
"alt.data_transformers.disable_max_rows() # MaxRowsError\n",
"minutes_per_day_and_stages_chart = alt.Chart(df).properties(\n",
" width=750,\n",
" height=500\n",
").transform_aggregate(\n",
" daily_minutes = 'sum(duration)',\n",
" groupby=['date', 'Stage']\n",
" ).mark_bar().encode(\n",
" x = alt.X('yearmonthdate(date):O'),\n",
" y = alt.Y('daily_minutes:Q'),\n",
" color = 'Stage:N'\n",
")\n",
"\n",
"\n",
"minutes_per_day_and_stages_chart"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "rlbXZ2EbCt0k"
},
"source": [
""
],
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment