From 4e64d28e9e9940af3288f2c51e3dbe52c380f154 Mon Sep 17 00:00:00 2001 From: Michael Mattig Date: Thu, 28 Nov 2024 17:05:38 +0100 Subject: [PATCH] add example for data usage --- examples/data_usage.ipynb | 375 ++++++++++++++++++++++++++++++++++++++ geoengine/__init__.py | 4 +- geoengine/workflow.py | 54 ++++++ setup.cfg | 2 +- 4 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 examples/data_usage.ipynb diff --git a/examples/data_usage.ipynb b/examples/data_usage.ipynb new file mode 100644 index 00000000..8d2b8595 --- /dev/null +++ b/examples/data_usage.ipynb @@ -0,0 +1,375 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Access Usage Log as Data Producer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import geoengine as ge\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connect to the Virtual Data Trustee" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "ge.initialize(\"http://localhost:3030/api\", (\"admin@localhost\", \"adminadmin\")) # TODO: load from .env for demo TODO: set remote url" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Access Usage Log" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
computationIdcountdatatimestampuserId
0ca82e727-b1a5-4f0a-963d-e4379da4bcf36GdalSource2024-11-28 15:48:23.003000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
1ca82e727-b1a5-4f0a-963d-e4379da4bcf31OgrSource2024-11-28 15:48:13.003000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
20017ed59-a369-4869-8945-0eecf5f09bf51OgrSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
321799f3d-2a70-474b-8c09-0792921138672GdalSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
435208ed7-f6f2-4f26-a85f-c3b1f8b6d9d92GdalSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
53d2e3da4-f678-4b6f-bbe3-329ba7ab38704GdalSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
647661177-49e7-4fc7-95af-6112b3707b3e4GdalSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
75e58efe5-6d56-4369-b396-a5d36b6327172GdalSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
86a0d85c8-ab99-4fef-b9ef-cefcaa5e0f9c2GdalSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
96a5239be-f9a8-44fc-a9da-02d7ab1c02bc2GdalSource2024-11-28 15:39:12.932000+00:00d5d3b34c-360c-4ed0-aedc-69c289222e7f
\n", + "
" + ], + "text/plain": [ + " computationId count data \\\n", + "0 ca82e727-b1a5-4f0a-963d-e4379da4bcf3 6 GdalSource \n", + "1 ca82e727-b1a5-4f0a-963d-e4379da4bcf3 1 OgrSource \n", + "2 0017ed59-a369-4869-8945-0eecf5f09bf5 1 OgrSource \n", + "3 21799f3d-2a70-474b-8c09-079292113867 2 GdalSource \n", + "4 35208ed7-f6f2-4f26-a85f-c3b1f8b6d9d9 2 GdalSource \n", + "5 3d2e3da4-f678-4b6f-bbe3-329ba7ab3870 4 GdalSource \n", + "6 47661177-49e7-4fc7-95af-6112b3707b3e 4 GdalSource \n", + "7 5e58efe5-6d56-4369-b396-a5d36b632717 2 GdalSource \n", + "8 6a0d85c8-ab99-4fef-b9ef-cefcaa5e0f9c 2 GdalSource \n", + "9 6a5239be-f9a8-44fc-a9da-02d7ab1c02bc 2 GdalSource \n", + "\n", + " timestamp userId \n", + "0 2024-11-28 15:48:23.003000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "1 2024-11-28 15:48:13.003000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "2 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "3 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "4 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "5 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "6 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "7 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "8 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f \n", + "9 2024-11-28 15:39:12.932000+00:00 d5d3b34c-360c-4ed0-aedc-69c289222e7f " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "usage = ge.data_usage(0, 10)\n", + "usage" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Access Usage Summary" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
countdatasettimestamp
06GdalSource2024-11-28 15:48:00+00:00
11OgrSource2024-11-28 15:48:00+00:00
230GdalSource2024-11-28 15:39:00+00:00
31OgrSource2024-11-28 15:39:00+00:00
\n", + "
" + ], + "text/plain": [ + " count dataset timestamp\n", + "0 6 GdalSource 2024-11-28 15:48:00+00:00\n", + "1 1 OgrSource 2024-11-28 15:48:00+00:00\n", + "2 30 GdalSource 2024-11-28 15:39:00+00:00\n", + "3 1 OgrSource 2024-11-28 15:39:00+00:00" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "usage_summary = ge.data_usage_summary(ge.UsageSummaryGranularity.MINUTES)\n", + "usage_summary\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot usage summary" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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YP/+/Zo/f74kyeFwuHia3CklJcX5n7TD4VB6erqLJ8r9yAg3Ki4uztUj5HoxMTGaOXOmJP69vpbExESdP39eEhldL/69hh14H+WMXP85V3nd5s2b1aJFC3Xo0EHlypVTeHi4pIu/7eM/pYv+/vtvDRs2TGfOnJG3t7dmz54tNzd+b3CpXbt2adq0aUpKSlJoaKj69etHRpeJjY3V3LlzdeLECZUtW1ZPPPGEq0fKVXbs2KEaNWpo5MiR6tevn6vHyZU2b96sZs2a6bHHHtPdd9+tyMhISfx7fant27erf//+On78uNzc3PTNN9+oRIkSZHSJPXv2aM6cObpw4YJKly6tJ554gn+vL7N//36tXr1ax48fV40aNXTPPfe4eqRch/dR1rLzPcTnXOViMTExqlu3rtq3b693333X1ePkStu2bVP9+vX1yCOPKCQkRN99951q166tL774QhI/1EgXf5ipU6eOGjRooJMnT+rw4cPy9PTU1KlTdffdd8vd3d3VI7rcli1bFBUVperVq2vnzp3y9/dXVFSURo8e7erRco1PPvlEzz//vPLly6ehQ4fq9ddf5+/XJfbt26e6devqiSee0DvvvOPqcXKl7du3q169enrsscdUuXJlzZgxQwUKFNDixYsl8e+1JG3dulX333+/ateurejoaElS4cKF9cMPPygkJMTF0+UOW7ZsUfPmzRUZGakNGzaoUqVK6tChg1544QVXj5Zr8D7KWra/hwxyrffff9+0bNnSGGNMWlqaGTNmjOndu7fp0aOHOXjwoGuHywXOnj1r6tWrZ3r16mWMMSY9Pd2MGTPG9OnTx8WT5R7JycmmdevW5plnnjHGGJOYmGgOHjxoHnzwQRMSEmJ+++03F0/oerGxsSYyMtIMHDjQpKenm5MnT5rhw4ebBg0amDNnzrh6vFxj9uzZpmXLluaLL74w7u7uZtSoUc774uLiXDhZ7vDVV1+Z5s2bG2OMSU1NNa+99prp1KmTad26tVm9erU5d+6ciyd0rQsXLphmzZqZF1980bnsf//7n+nUqZNJTEx04WS5x4ULF0yjRo1M165djTEX/16tWLHCVK9e3ZQrV878888/xpiL/9flVdHR0SY8PNwMGjTInD9/3hw8eNB06NDBtGrVytWj5Rq8j7KWE+8h9g/mYgcPHlTBggUlSXXq1NHcuXMVHR2txYsX6+6779by5csl5d0TghMSEnTmzBm1bNlS0sVj9w8cOKD58+erTp06uu+++7Ry5UpJeTcjDw8PnT171nl4koeHh4oWLarffvtNtWrVUpcuXRQbGyspb2aUnp6uH374QeXLl1f//v0lSUFBQWrTpo3Wrl2rPXv2uHjC3KN69eo6ffq0mjRpojFjxmjw4MGaOHGi+vfvrylTpiglJcXVI7rUwYMHlZKSovT0dN1///1auXKlPD09dfjwYbVp00bfffed0tPT8+TfM+niebEnTpxQ7dq1nctWr17t/Lfozjvv1IwZM3ThwgUXTulaDodDCQkJqlOnjiTJ399fdevW1a+//qqgoCA9+uijSklJkcPhyJPvo5SUFH3++eeqVauWBg0aJC8vLxUtWlTdunXT77//7vy/LK/jfXRtOfUeolzlYu7u7tq+fbt+++03FS5cWD/99JNmzZqlbdu2qVq1auratatSU1Pz7GEUBQsWVGJiot555x39888/eu211/TJJ5/o2Wef1SuvvKLAwEC1a9dOJ0+ezLMZSRcLVcZhN25ubkpOTpYk/fjjjypSpIi6du0qKe+eWF6sWDG1aNFCBQsWdF4MJTQ0VP7+/s6scPHfo8OHD+vUqVPq16+fpk6dqt69e2vcuHFq06aNPDw8XD2iS/n6+urgwYP6888/FRISotmzZ+ujjz7SypUr1bRpUw0aNEhnzpzJs3/P/Pz85O7urg8//FDz58/Xq6++qsmTJ+v111/Xhx9+qDp16mjgwIF5+gdkLy8vSdJvv/3mXJbx79HMmTOVlpamLl26SMq7/14HBgaqadOm8vPzc54/FBoamun/trzOy8tLxhjeR9eQI+8h2/aBwXYxMTHmzjvvNHfeead55JFHTFpamklOTjbGGHPo0CETEhJi5s6d6+IpXSc9Pd38+uuvpmTJkqZ58+YmMDDQTJ8+3Xl/SkqKCQgIMJMnT3bhlK6TlpZmjDHm999/N5GRkWbo0KHO+5KSkowxFw/1KlWqlPMwgbwk45CIs2fPOpdlZGaMMRUrVjTLly933p49e3bODZdLtWrVyqxfv94YY0y7du1MYGCgcXNzM//5z39cPJnrXbhwwZQuXdoUL17c1KlTxyQkJJjU1FTn/cHBwWbGjBkunNB1Mv5ebdu2zdSqVcs89thjpmjRouaDDz7ItF5ISIgZMWKEK0bMNaZOnWqqVKliPvnkE+eyjPzGjRtnatasaU6cOOGq8Vzu2LFjzj9n5BIfH2/Kli1roqOjnfctWrQox2fLTaZOnWoqV67M++gqcuI9xJ6rXOLAgQP65ZdfNGvWLO3bt0/SxSbdunVrHTlyREeOHJGbm5vzt8NJSUkqXLiwgoKCXDl2jro0o71798rhcKhZs2b6559/9OGHH6pkyZLOQ05SUlJ07NgxRUREqHjx4i6ePOckJiZKuvhbqozfyFSvXl2PPfaYfv75Z40ZM0aS5OnpKUkKDg7Oc3s/MzIyxsgYI19fX+d9GZklJiYqISFBaWlpkqQhQ4aoTZs22rt3b84PnMMufQ9dLn/+/FqxYoWef/55LV26VPPmzdPkyZPVv39/jRs3LqdHdZnLMzLGyMvLS6+//rp8fHx08uRJJScnOy8Wc/LkSZUsWVJFixZ12cw57dKMMv59qVixotauXatp06apePHiqlGjhnPduLg4lSpVShERES6bOacdO3ZMq1ev1tKlS3X8+HFJUqNGjVShQgV9+eWX+vLLLyX9379LkZGROnnypJKSklw2c07LyGjJkiU6duyYgoODnf92Z+Ry4cIFxcXFOXMZPHiwOnTooMOHD7ty9Bxz+c9GkvTQQw+pUqVK+vzzz/P8++jSfGJjYxUcHOy8L9veQzddy2CbTZs2maJFi5ry5cub4sWLmwIFCpiJEyeas2fPmlOnTpmXX37ZOBwO07JlS3Py5Emzd+9e8+abb5oKFSqYQ4cOuXr8HHG1jCZNmuS8sEdycrKpXr26eeutt5y3R44caSIjI82+fftcOXqO2bp1qyldurTzIhVpaWnO38ocOHDA9OzZ01SrVs288MILJikpyRw+fNi88cYbplKlSub48eOuHD3HXJ7R1U7oTUtLM6dOnTLBwcFm3bp15u233zbe3t5m3bp1OT1ujrtWPhnvo/Hjx5v8+fObiIgIs2HDBmPMxYukfPLJJ2b79u2uGTqHXS2jjJxOnTpl3n33XRMYGGjuvfdes379erN582YzYsQIExERkaf/Lbo0J2OMqV27tnn++eeNMRd/azxq1ChTokQJs2fPHpfMnNM2b95sKleubCpUqGCKFy9uqlev7vyt+d9//20efvhhU69ePeeFY+Lj482AAQPM3XffnWcutHO1jGJiYq5YLzY21vj6+pro6GgzatQo4+XllSf+vTbm6j8bvf/++yYpKcns3bvXPPzww+b+++/Ps++jy/Px9fU1H3zwgTl8+HCm9ex+D1GuXOzkyZOmatWqZuDAgebkyZPmwIEDZsSIESZ//vymb9++5tSpUyYuLs5MmjTJlCxZ0gQGBpqKFSuasLAw5w83t7usMnr11VfNnj17THp6uunfv7+pVKmSqVChgnn44YdN4cKFzV9//eXq8XPE3r17TYUKFUxQUJAJDg7O9ENNxqFJhw8fNhMmTHC+jypVqmSKFCniPMzrdnetjK5WsFJTU81dd91l7rvvPuPt7W3Wrl2b0+PmuOvJZ8eOHaZ9+/Z55u/V5bL6e5ZRQM+ePWvmzp1rqlWrZoKDg02ZMmVMmTJl8sy/11bvo4ycPvzwQxMeHm6KFCli6tWrZ4oXL55nMvrnn39MSEiIGTBggNm1a5dZsGCBadasmenRo4fzkO3o6GjTp08fExYWZgoVKmTuuusuU6hQoTyfUc+ePU1qamqmf5dOnTplatSoYVq3bp1nfhFmzLV/NvLx8TF9+/Y1CQkJZv/+/aZPnz6mePHiee59ZPWzY2xsrHNdu99DlCsXO3z4sClbtqyZP39+puUff/yxueOOO8xrr71mjLl4/tC5c+fMrFmzzPLly83+/ftdMa5LWGX0+uuvG2Munof27bffmm7dupmxY8fmmfOIkpOTzdChQ02bNm3M3LlzzbPPPmsKFix41YKVmppqEhISzDfffGMWLVpk9u7d68rRc4xVRpf/Vj0+Pt6EhIQYb29vs2nTJleNnWOu5z2UkpJijPm/8/XymuvJ6NJz9owxZtmyZWbz5s1X/Jb0dnUjf8/i4+PNmjVrzKBBg8wnn3yS6VyH29m5c+dMp06dTKdOnTItHzhwoKlVq1amZWfPnjV79+41EydONN9//32e2at3IxkZc/H//nz58hlfX9889YufrH42CgoKMoMGDTLGXLwUe158H1n97PjGG284Pwbi4MGDxsPDw7b3EOXKhdLT083OnTtNcHCw+fnnn40xF0+KzvDBBx8Yh8NhFi5c6KoRXe56M1qwYIGrRswVFi1aZD7//HNjjDF79uy56g9+lxeIvMYqo0uzSU5ONu+8806eKejGXP97KC+7nvdQenr6FSUrL7me91FezufcuXPmjTfeMB9++KEx5v/25K1YscJUrFjRxMfHOy9clVddT0YZv+wxxpjTp0+bXr16mZ07d7pkXlfg58esXW8+GRetOH36tHn55Zdtew9RrnKBZ555xpQsWdJ55ZZLfzPcvn1707hxY5OUlJSnf7CxyqhRo0YmMTEx09W58rJdu3aZZ5991gQGBjp/qLlw4QIfZnqJa2W0atUqY4zJ0z8AGpP1e+j8+fMuni534O+ZtawySkhIcPF0rrF7927nnzP+X1+xYoWJjIw0CQkJmc7hy6uuN6OTJ08aY0yeLaT8/Ji168kno3RdWtj/La4W6ELm/394W79+/VSsWDG1bdtWx48fl6enp1JTUyVJERERSktLk6enZ566oluG680oPT1dXl5ezqtz5VUZVy+LjIzUwIED1bp1az3xxBOaO3euXnvtNbVq1cp5Fa+8yiqjRx99VKdOnXJeRSivuZ73UF7+oFeJv2fX43oyyitXK7tc6dKlJV38/y3jg1zPnz+v5ORk5//1/fr1U7Vq1ZScnJznPuhVuv6MqlevnunKnHkFPz9m7Uby8fb2liTly5fP1gHgYunp6ea7774zdevWNXfffbc5cOCA876XXnrJtGjRwly4cCHP/ubBGDK6Wbt27TJdunQxDofDBAQEmDVr1rh6pFyHjLJGPtbIyBoZZW316tWmVKlSxhhjXn/9dePn52dWr17t4qlyFzK6Ej8bZc1V+dhY03C90tLSnL9lMf//tzKPPvqo8ufPr//+978qX7687r//fhljtHz5cq1YscLZrPMKMrJ2aUbXUrp0aZ09e1aBgYFavny5KlasmEPT5Q5klDXysUZG1sjImlVGHh4eCgwMVPfu3fXpp59q5cqVqlmzZg5O6HpkZO3Sz7DkZ6Mr5ZZ8HMbkwf3NOezIkSOKjY3VqVOn9NBDD0m6enmQLn6Q2ZQpUxQTEyMfHx89/fTTKl++vMtmzylkZM0qo8ulp6fro48+0oABA7RkyRLnB3bezsgoa+RjjYyskZG1G81owYIFioqKkq+vr5YuXarq1avn5LguQUbWEhISnIexBQYGSsr881Be/9ko1+Zj634wXGHz5s2mQoUKplKlSsbb29u0bNnS1SPlOmRk7WYz+uOPP8yuXbuyd7hcgoyyRj7WyMgaGVm7mYz2799v2rVrZ7Zu3Zr9A+YCZGRt8+bNpmHDhqZKlSqmfv365vnnn8+zF+64mtycD+UqG+3evduEhISY4cOHm+3bt5u5c+ea0NBQs2XLlut6fF44RpaMrP3bjPICMsoa+VgjI2tkZO3fZJRXrp5IRtZ27txpChUqZPr3729++OEH89FHH5mQkBDz4IMPmm3btlk+/nb/2Si358M5V9noxx9/VM2aNfXaa68pX758KlasmCpUqKCDBw9q69ateuihh+Tv73/Nx+eFq7uQkbV/m1FeQEZZIx9rZGSNjKz9m4wKFCiQw9O6BhlZ++qrr/TQQw9p7Nixki4eWrthwwZ9/PHH6tatm+bMmaNChQplOsfoUrf7z0a5PZ+8ea3hHBIbG6v9+/c7L+/40UcfacWKFRo8eLD69u2ru+++W3///bek/7tsbV5DRtbIyBoZZY18rJGRNTKyRkbWyMja9u3bdfz4cedtNzc3Va9eXb169dKBAwfUrVs35/K8KNfnk637xfKojA8fXb58ufH39ze1atUy7dq1M15eXmbevHnODzOrWrWqadWqlStHdRkyskZG1sgoa+RjjYyskZE1MrJGRtYyMnrvvfdMvXr1zPz5840xFz/KwM/Pz3z66afmxx9/NJGRkWbHjh2uHNUlbpV88mblzSYZv2HJ2N1YpUoVzZs3T23atFHhwoXVrVs355VuJKlFixY6ffq080oneQEZWSMja2SUNfKxRkbWyMgaGVkjI2uX76G77777lD9/fvXo0UN16tRRlSpV9PTTT6tLly6qXr26Dh48qAMHDrho2px3q+XDOVc22bVrlz799FMdPnxYhQsX1uDBgxUQEKA6deqoTp06eu655xQfHy9J8vLykiTt379fxYsXzzOfvk5G1sjIGhlljXyskZE1MrJGRtbIyNqlGQUHB2vw4MGqUaOGxo0bpw0bNmj//v3q27evHn/8cUnS2bNnValSJRUtWtTFk+eMWzEf9lzZYMuWLapbt64OHTqk06dPa/ny5WrdurUSExOd6zzwwAPavn27xo8fr7Vr1+rVV1/VL7/8otdee00eHh4unD5nkJE1MrJGRlkjH2tkZI2MrJGRNTKydnlGK1asUOvWrXX+/HmVL19eTz75pAYMGOAsDpI0bdo0nT9/XsHBwS6cPGfcsvm47IDE28SBAwdM5cqVzYABA4wxF48H/fXXX03lypXNunXrnOvt2rXL9OzZ0wQFBZmyZcua6tWrm40bN7pq7BxFRtbIyBoZZY18rJGRNTKyRkbWyMhaVhmtXbvW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jMz0VaBAAUkXC869996r4cOH66+//pKnp6dmzZolSfL09FRaWlqmba5cuVIlS5bU66+/rlq1aqlMmTLau3evbTOvXr36itsVKlSwbfsAANeiXAEAbkndu3fXqVOn1L59e61du1bR0dGaP3++OnfurLS0NK1Zs0ZvvfWW1q1bp3379umHH37Q8ePHnWUmPDxcmzdv1s6dO3XixAmlpKSoTJky2rdvn77++mtFR0drwoQJzjJmh2+//VZTp07VP//8o6FDh+rPP/9Ujx49bNs+AMC1KFcAgFtS0aJFtWLFCqWlpalJkyaqXLmyevfurcDAQLm5ucnf319//PGHHnroIZUtW1ZvvPGG3nnnHTVr1kyS1K1bN5UrV061atVScHCwVqxYoUceeUR9+vRRjx49VK1aNa1cuVKDBw+2bebhw4fr66+/VpUqVTRjxgx99dVXqlixom3bBwC4FlcLBAAgBzgcDs2aNSvT53sBAG4v7LkCAAAAABtQrgAAAADABnzOFQAAOYCj8AHg9seeKwAAAACwAeUKAAAAAGxAuQIAAAAAG1CuAAAAAMAGlCsAAAAAsAHlCgAAAABsQLkCAAAAABtQrgAAAADABv8PYCb7qQ3jbxEAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ge.plot_data_usage_summary(ge.UsageSummaryGranularity.MINUTES)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'DataFrame' object has no attribute 'to_datetime'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[10], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m pivot_df \u001b[38;5;241m=\u001b[39m usage_summary\u001b[38;5;241m.\u001b[39mpivot(index\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtimestamp\u001b[39m\u001b[38;5;124m'\u001b[39m, columns\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdataset\u001b[39m\u001b[38;5;124m'\u001b[39m, values\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcount\u001b[39m\u001b[38;5;124m'\u001b[39m)\u001b[38;5;241m.\u001b[39mfillna(\u001b[38;5;241m0\u001b[39m)\n\u001b[0;32m----> 2\u001b[0m pivot_df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtimestamp\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[43mpivot_df\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mto_datetime\u001b[49m(pivot_df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtimestamp\u001b[39m\u001b[38;5;124m'\u001b[39m])\u001b[38;5;241m.\u001b[39mdt\u001b[38;5;241m.\u001b[39mtz_localize(\u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[1;32m 4\u001b[0m pivot_df\n\u001b[1;32m 6\u001b[0m pivot_df\u001b[38;5;241m.\u001b[39mplot(kind\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbar\u001b[39m\u001b[38;5;124m'\u001b[39m, figsize\u001b[38;5;241m=\u001b[39m(\u001b[38;5;241m10\u001b[39m, \u001b[38;5;241m6\u001b[39m))\n", + "File \u001b[0;32m~/git/geoengine-python/env/lib/python3.10/site-packages/pandas/core/generic.py:5902\u001b[0m, in \u001b[0;36mNDFrame.__getattr__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 5895\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m 5896\u001b[0m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_internal_names_set\n\u001b[1;32m 5897\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_metadata\n\u001b[1;32m 5898\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_accessors\n\u001b[1;32m 5899\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_info_axis\u001b[38;5;241m.\u001b[39m_can_hold_identifiers_and_holds_name(name)\n\u001b[1;32m 5900\u001b[0m ):\n\u001b[1;32m 5901\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m[name]\n\u001b[0;32m-> 5902\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mobject\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__getattribute__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'DataFrame' object has no attribute 'to_datetime'" + ] + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "\n", + "pivot_df = usage_summary.pivot(index='timestamp', columns='dataset', values='count').fillna(0)\n", + "pivot_df['timestamp'] = pd.to_datetime(pivot_df['timestamp']).dt.tz_localize(None)\n", + "\n", + "pivot_df\n", + "\n", + "pivot_df.plot(kind='bar', figsize=(10, 6))\n", + "\n", + "plt.title('Data Usage by Dataset over time')\n", + "plt.xlabel('Timestamp')\n", + "plt.ylabel('Count')\n", + "plt.xticks(rotation=45)\n", + "plt.legend(title='Dataset')\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/geoengine/__init__.py b/geoengine/__init__.py index fca41ddb..7ec8d6ca 100644 --- a/geoengine/__init__.py +++ b/geoengine/__init__.py @@ -6,6 +6,7 @@ from geoengine_openapi_client.exceptions import BadRequestException, OpenApiException, ApiTypeError, ApiValueError, \ ApiKeyError, ApiAttributeError, ApiException, NotFoundException +from geoengine_openapi_client import UsageSummaryGranularity from .auth import Session, get_session, initialize, reset from .colorizer import Colorizer, ColorBreakpoint, LinearGradientColorizer, PaletteColorizer, \ LogarithmicGradientColorizer @@ -30,7 +31,8 @@ MultiBandRasterColorizer from .util import clamp_datetime_ms_ns -from .workflow import WorkflowId, Workflow, workflow_by_id, register_workflow, get_quota, update_quota +from .workflow import WorkflowId, Workflow, workflow_by_id, register_workflow, get_quota, update_quota, data_usage, \ + data_usage_summary, plot_data_usage_summary from .raster import RasterTile2D from .raster_workflow_rio_writer import RasterWorkflowRioWriter diff --git a/geoengine/workflow.py b/geoengine/workflow.py index e19a2273..c0200d2f 100644 --- a/geoengine/workflow.py +++ b/geoengine/workflow.py @@ -967,3 +967,57 @@ def update_quota(user_id: UUID, new_available_quota: int, timeout: int = 60) -> ), _request_timeout=timeout ) + + +def data_usage(offset: int = 0, limit: int = 10) -> List[geoengine_openapi_client.DataUsage]: + ''' + Get data usage + ''' + + session = get_session() + + with geoengine_openapi_client.ApiClient(session.configuration) as api_client: + user_api = geoengine_openapi_client.UserApi(api_client) + response = user_api.data_usage_handler(offset=offset, limit=limit) + + # create dataframe from response + usage_dicts = [data_usage.dict(by_alias=True) for data_usage in response] + df = pd.DataFrame(usage_dicts) + + return df + + +def data_usage_summary(granularity: geoengine_openapi_client.UsageSummaryGranularity, dataset: Optional[str] = None, offset: int = 0, limit: int = 10) -> List[geoengine_openapi_client.DataUsage]: + ''' + Get data usage summary + ''' + + session = get_session() + + with geoengine_openapi_client.ApiClient(session.configuration) as api_client: + user_api = geoengine_openapi_client.UserApi(api_client) + response = user_api.data_usage_summary_handler(dataset=dataset, granularity=granularity, offset=offset, limit=limit) + + # create dataframe from response + usage_dicts = [data_usage.dict(by_alias=True) for data_usage in response] + df = pd.DataFrame(usage_dicts) + + return df + + +def plot_data_usage_summary(granularity: geoengine_openapi_client.UsageSummaryGranularity, dataset: Optional[str] = None, offset: int = 0, limit: int = 10): + import matplotlib.pyplot as plt + import pandas as pd + + df = data_usage_summary(granularity, dataset, offset, limit) + df['timestamp'] = pd.to_datetime(df['timestamp']).dt.tz_localize(None) + + pivot_df = df.pivot(index='timestamp', columns='dataset', values='count').fillna(0) + pivot_df.plot(kind='bar', figsize=(10, 6)) + + plt.title('Data Usage by Dataset over time') + plt.xlabel('Timestamp') + plt.ylabel('Count') + plt.xticks(rotation=45) + plt.legend(title='Dataset') + plt.show() diff --git a/setup.cfg b/setup.cfg index 675ed7c4..ea9753f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,7 +18,7 @@ package_dir = packages = find: python_requires = >=3.9 install_requires = - geoengine-openapi-client == 0.0.17 + geoengine-openapi-client @ git+https://github.com/geo-engine/openapi-client@esg-quota#subdirectory=python # TODO update when merged geopandas >=0.9,<0.15 matplotlib >=3.5,<3.8 numpy >=1.21,<2