Fix dashboards

This commit is contained in:
phil 2024-03-24 11:21:11 +05:30
parent d539a72e6a
commit 5434c7d6ef
6 changed files with 330 additions and 74 deletions

240
pdm.lock generated
View file

@ -5,7 +5,7 @@
groups = ["default", "dev", "mqtt"] groups = ["default", "dev", "mqtt"]
strategy = ["cross_platform"] strategy = ["cross_platform"]
lock_version = "4.4.1" lock_version = "4.4.1"
content_hash = "sha256:c76681e48baaf1b76e410a3d5cf54e6712a74ced0429536b2246b79dad62446f" content_hash = "sha256:07dee91945ef2f5557c7130b7ab9fee6bbba2763ce65a3cc18f0d3c89429c30f"
[[package]] [[package]]
name = "aiomqtt" name = "aiomqtt"
@ -263,6 +263,41 @@ files = [
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
] ]
[[package]]
name = "contourpy"
version = "1.2.0"
requires_python = ">=3.9"
summary = "Python library for calculating contours of 2D quadrilateral grids"
dependencies = [
"numpy<2.0,>=1.20",
]
files = [
{file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"},
{file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"},
{file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"},
{file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"},
{file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"},
{file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"},
{file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"},
{file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"},
{file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"},
{file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"},
{file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"},
{file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"},
{file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"},
{file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"},
{file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"},
{file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"},
]
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "41.0.5" version = "41.0.5"
@ -297,6 +332,16 @@ files = [
{file = "cryptography-41.0.5.tar.gz", hash = "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7"}, {file = "cryptography-41.0.5.tar.gz", hash = "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7"},
] ]
[[package]]
name = "cycler"
version = "0.12.1"
requires_python = ">=3.8"
summary = "Composable style cycles"
files = [
{file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
{file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
]
[[package]] [[package]]
name = "decorator" name = "decorator"
version = "5.1.1" version = "5.1.1"
@ -371,6 +416,32 @@ files = [
{file = "fiona-1.9.5.tar.gz", hash = "sha256:99e2604332caa7692855c2ae6ed91e1fffdf9b59449aa8032dd18e070e59a2f7"}, {file = "fiona-1.9.5.tar.gz", hash = "sha256:99e2604332caa7692855c2ae6ed91e1fffdf9b59449aa8032dd18e070e59a2f7"},
] ]
[[package]]
name = "fonttools"
version = "4.50.0"
requires_python = ">=3.8"
summary = "Tools to manipulate font files"
files = [
{file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3dfb102e7f63b78c832e4539969167ffcc0375b013080e6472350965a5fe8048"},
{file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e58fe34cb379ba3d01d5d319d67dd3ce7ca9a47ad044ea2b22635cd2d1247fc"},
{file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c673ab40d15a442a4e6eb09bf007c1dda47c84ac1e2eecbdf359adacb799c24"},
{file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b3ac35cdcd1a4c90c23a5200212c1bb74fa05833cc7c14291d7043a52ca2aaa"},
{file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8844e7a2c5f7ecf977e82eb6b3014f025c8b454e046d941ece05b768be5847ae"},
{file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f849bd3c5c2249b49c98eca5aaebb920d2bfd92b3c69e84ca9bddf133e9f83f0"},
{file = "fonttools-4.50.0-cp311-cp311-win32.whl", hash = "sha256:39293ff231b36b035575e81c14626dfc14407a20de5262f9596c2cbb199c3625"},
{file = "fonttools-4.50.0-cp311-cp311-win_amd64.whl", hash = "sha256:c33d5023523b44d3481624f840c8646656a1def7630ca562f222eb3ead16c438"},
{file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b4a886a6dbe60100ba1cd24de962f8cd18139bd32808da80de1fa9f9f27bf1dc"},
{file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b2ca1837bfbe5eafa11313dbc7edada79052709a1fffa10cea691210af4aa1fa"},
{file = "fonttools-4.50.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0493dd97ac8977e48ffc1476b932b37c847cbb87fd68673dee5182004906828"},
{file = "fonttools-4.50.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77844e2f1b0889120b6c222fc49b2b75c3d88b930615e98893b899b9352a27ea"},
{file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3566bfb8c55ed9100afe1ba6f0f12265cd63a1387b9661eb6031a1578a28bad1"},
{file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:35e10ddbc129cf61775d58a14f2d44121178d89874d32cae1eac722e687d9019"},
{file = "fonttools-4.50.0-cp312-cp312-win32.whl", hash = "sha256:cc8140baf9fa8f9b903f2b393a6c413a220fa990264b215bf48484f3d0bf8710"},
{file = "fonttools-4.50.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ccc85fd96373ab73c59833b824d7a73846670a0cb1f3afbaee2b2c426a8f931"},
{file = "fonttools-4.50.0-py3-none-any.whl", hash = "sha256:48fa36da06247aa8282766cfd63efff1bb24e55f020f29a335939ed3844d20d3"},
{file = "fonttools-4.50.0.tar.gz", hash = "sha256:fa5cf61058c7dbb104c2ac4e782bf1b2016a8cf2f69de6e4dd6a865d2c969bb5"},
]
[[package]] [[package]]
name = "geoalchemy2" name = "geoalchemy2"
version = "0.14.3" version = "0.14.3"
@ -508,6 +579,95 @@ files = [
{file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
] ]
[[package]]
name = "kiwisolver"
version = "1.4.5"
requires_python = ">=3.7"
summary = "A fast implementation of the Cassowary constraint solver"
files = [
{file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"},
{file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"},
{file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"},
{file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"},
{file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"},
{file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"},
{file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"},
{file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"},
{file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"},
{file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"},
{file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"},
{file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"},
{file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"},
{file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"},
{file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"},
{file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"},
{file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"},
{file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"},
{file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"},
{file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"},
{file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"},
{file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"},
{file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"},
{file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"},
{file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"},
{file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"},
{file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"},
{file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"},
{file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"},
{file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"},
{file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"},
{file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"},
{file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"},
{file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"},
{file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"},
{file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"},
{file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"},
{file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"},
{file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"},
{file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"},
{file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"},
{file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"},
{file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"},
{file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"},
{file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"},
{file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
]
[[package]]
name = "matplotlib"
version = "3.8.3"
requires_python = ">=3.9"
summary = "Python plotting package"
dependencies = [
"contourpy>=1.0.1",
"cycler>=0.10",
"fonttools>=4.22.0",
"kiwisolver>=1.3.1",
"numpy<2,>=1.21",
"packaging>=20.0",
"pillow>=8",
"pyparsing>=2.3.1",
"python-dateutil>=2.7",
]
files = [
{file = "matplotlib-3.8.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5184e07c7e1d6d1481862ee361905b7059f7fe065fc837f7c3dc11eeb3f2f900"},
{file = "matplotlib-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7e7e0993d0758933b1a241a432b42c2db22dfa37d4108342ab4afb9557cbe3e"},
{file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04b36ad07eac9740fc76c2aa16edf94e50b297d6eb4c081e3add863de4bb19a7"},
{file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c42dae72a62f14982f1474f7e5c9959fc4bc70c9de11cc5244c6e766200ba65"},
{file = "matplotlib-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf5932eee0d428192c40b7eac1399d608f5d995f975cdb9d1e6b48539a5ad8d0"},
{file = "matplotlib-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:40321634e3a05ed02abf7c7b47a50be50b53ef3eaa3a573847431a545585b407"},
{file = "matplotlib-3.8.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:09074f8057917d17ab52c242fdf4916f30e99959c1908958b1fc6032e2d0f6d4"},
{file = "matplotlib-3.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5745f6d0fb5acfabbb2790318db03809a253096e98c91b9a31969df28ee604aa"},
{file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97653d869a71721b639714b42d87cda4cfee0ee74b47c569e4874c7590c55c5"},
{file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242489efdb75b690c9c2e70bb5c6550727058c8a614e4c7716f363c27e10bba1"},
{file = "matplotlib-3.8.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:83c0653c64b73926730bd9ea14aa0f50f202ba187c307a881673bad4985967b7"},
{file = "matplotlib-3.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef6c1025a570354297d6c15f7d0f296d95f88bd3850066b7f1e7b4f2f4c13a39"},
{file = "matplotlib-3.8.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fa93695d5c08544f4a0dfd0965f378e7afc410d8672816aff1e81be1f45dbf2e"},
{file = "matplotlib-3.8.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9764df0e8778f06414b9d281a75235c1e85071f64bb5d71564b97c1306a2afc"},
{file = "matplotlib-3.8.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5e431a09e6fab4012b01fc155db0ce6dccacdbabe8198197f523a4ef4805eb26"},
{file = "matplotlib-3.8.3.tar.gz", hash = "sha256:7b416239e9ae38be54b028abbf9048aff5054a9aba5416bef0bd17f9162ce161"},
]
[[package]] [[package]]
name = "matplotlib-inline" name = "matplotlib-inline"
version = "0.1.6" version = "0.1.6"
@ -680,6 +840,64 @@ files = [
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
] ]
[[package]]
name = "pillow"
version = "10.2.0"
requires_python = ">=3.8"
summary = "Python Imaging Library (Fork)"
files = [
{file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"},
{file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"},
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"},
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"},
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"},
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"},
{file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"},
{file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"},
{file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"},
{file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"},
{file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"},
{file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"},
{file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"},
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"},
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"},
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"},
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"},
{file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"},
{file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"},
{file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"},
{file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"},
{file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"},
{file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"},
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"},
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"},
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"},
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"},
{file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"},
{file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"},
{file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"},
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"},
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"},
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"},
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"},
{file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"},
{file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"},
]
[[package]]
name = "plotly"
version = "5.20.0"
requires_python = ">=3.8"
summary = "An open-source, interactive data visualization library for Python"
dependencies = [
"packaging",
"tenacity>=6.2.0",
]
files = [
{file = "plotly-5.20.0-py3-none-any.whl", hash = "sha256:837a9c8aa90f2c0a2f0d747b82544d014dc2a2bdde967b5bb1da25b53932d1a9"},
{file = "plotly-5.20.0.tar.gz", hash = "sha256:bf901c805d22032cfa534b2ff7c5aa6b0659e037f19ec1e0cca7f585918b5c89"},
]
[[package]] [[package]]
name = "pretty-errors" name = "pretty-errors"
version = "1.2.25" version = "1.2.25"
@ -865,6 +1083,16 @@ files = [
{file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
] ]
[[package]]
name = "pyparsing"
version = "3.1.2"
requires_python = ">=3.6.8"
summary = "pyparsing module - Classes and methods to define and execute parsing grammars"
files = [
{file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"},
{file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"},
]
[[package]] [[package]]
name = "pyproj" name = "pyproj"
version = "3.6.1" version = "3.6.1"
@ -1178,6 +1406,16 @@ files = [
{file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"},
] ]
[[package]]
name = "tenacity"
version = "8.2.3"
requires_python = ">=3.7"
summary = "Retry code until it succeeds"
files = [
{file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
{file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
]
[[package]] [[package]]
name = "traitlets" name = "traitlets"
version = "5.14.0" version = "5.14.0"

View file

@ -27,6 +27,8 @@ dependencies = [
"websockets>=12.0", "websockets>=12.0",
"aiosqlite>=0.19.0", "aiosqlite>=0.19.0",
"psycopg>=3.1.18", "psycopg>=3.1.18",
"plotly>=5.20.0",
"matplotlib>=3.8.3",
] ]
requires-python = ">=3.11,<4" requires-python = ">=3.11,<4"
readme = "README.md" readme = "README.md"

View file

@ -1 +1 @@
__version__: str = '2023.4.dev51+g15fe7fa.d20240318' __version__: str = '2023.4.dev53+gd539a72.d20240320'

View file

@ -1,6 +1,7 @@
import logging import logging
from pathlib import Path from pathlib import Path
from json import dumps from json import dumps
from typing import Annotated
from fastapi import Depends, APIRouter, HTTPException, status, responses from fastapi import Depends, APIRouter, HTTPException, status, responses
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
@ -14,8 +15,8 @@ from gisaf.database import fastapi_db_session as db_session
from gisaf.models.authentication import User from gisaf.models.authentication import User
from gisaf.models.dashboard import ( from gisaf.models.dashboard import (
DashboardPage, DashboardPageSection, DashboardPage, DashboardPageSection,
DashboadPageSectionType, DashboardPage_, DashboardPageMetaData,
DashboardGroup, DashboardHome, DashboardGroup, DashboardHome, Dashboard, DashboardSection
) )
from gisaf.models.misc import NotADataframeError from gisaf.models.misc import NotADataframeError
from gisaf.security import get_current_active_user from gisaf.security import get_current_active_user
@ -46,10 +47,12 @@ async def get_groups(
) -> list[DashboardGroup]: ) -> list[DashboardGroup]:
query = select(DashboardPage) query = select(DashboardPage)
data = await db_session.exec(query) data = await db_session.exec(query)
groups: dict[str, DashboardPage_] = {} groups: dict[str, DashboardPageMetaData] = {}
for page in data.all(): for page in data.all():
page_field = DashboardPage_(name=page.name, group=page.group, page_field = DashboardPageMetaData(name=page.name, group=page.group,
description=page.description) description=page.description,
viewable_role=page.viewable_role
)
group = groups.get(page.group) group = groups.get(page.group)
if group is None: if group is None:
group = DashboardGroup(name=page.group, pages=[page_field]) group = DashboardGroup(name=page.group, pages=[page_field])
@ -80,9 +83,10 @@ async def get_home() -> DashboardHome:
async def get_dashboard_page(group: str, name: str, async def get_dashboard_page(group: str, name: str,
db_session: db_session, db_session: db_session,
user: User = Depends(get_current_active_user), user: User = Depends(get_current_active_user),
) -> DashboardPage_: ) -> Dashboard:
query1 = select(DashboardPage).where((DashboardPage.name==name) query1 = select(DashboardPage).\
& (DashboardPage.group==group)) options(selectinload(DashboardPage.sections)).\
where((DashboardPage.name==name) & (DashboardPage.group==group))
data1 = await db_session.exec(query1) data1 = await db_session.exec(query1)
page = data1.one_or_none() page = data1.one_or_none()
if not page: if not page:
@ -98,7 +102,7 @@ async def get_dashboard_page(group: str, name: str,
username = user.username if user is not None else "Anonymous" username = user.username if user is not None else "Anonymous"
logger.info(f'{username} tried to access dashboard page {name}') logger.info(f'{username} tried to access dashboard page {name}')
raise HTTPException(status.HTTP_401_UNAUTHORIZED) raise HTTPException(status.HTTP_401_UNAUTHORIZED)
dp = DashboardPage_( dp = Dashboard(
name=page.name, name=page.name,
group=page.group, group=page.group,
description=page.description, description=page.description,
@ -111,7 +115,7 @@ async def get_dashboard_page(group: str, name: str,
for p in page.expanded_panes.split(',') for p in page.expanded_panes.split(',')
] if page.expanded_panes else [], ] if page.expanded_panes else [],
sections=[ sections=[
DashboadPageSectionType( DashboardSection(
name=dps.name, name=dps.name,
plot=dps.get_plot_url() plot=dps.get_plot_url()
) )
@ -126,7 +130,7 @@ async def get_dashboard_page(group: str, name: str,
if isinstance(df, gpd.GeoDataFrame): if isinstance(df, gpd.GeoDataFrame):
gdf = pd.DataFrame(df.drop(columns=['geometry'])) gdf = pd.DataFrame(df.drop(columns=['geometry']))
df = gdf df = gdf
dp.dfData = df.to_json(orient='table', double_precision=2) dp.dfData = df.reset_index().to_dict(orient='records')
except NotADataframeError: except NotADataframeError:
logger.warning(f'Dashboard: cannot read dataframe for page {page.name}') logger.warning(f'Dashboard: cannot read dataframe for page {page.name}')
except Exception as err: except Exception as err:

View file

@ -21,7 +21,7 @@ from gisaf.models.info import (LegendItem, ModelAction, ModelInfo,
TagActions) TagActions)
from gisaf.models.measures import MeasuresItem from gisaf.models.measures import MeasuresItem
from gisaf.models.survey import Equipment, SurveyMeta, Surveyor from gisaf.models.survey import Equipment, SurveyMeta, Surveyor
from gisaf.config import Survey, conf from gisaf.config import conf
from gisaf.models.bootstrap import BootstrapData from gisaf.models.bootstrap import BootstrapData
from gisaf.models.store import Store, StoreNameOnly from gisaf.models.store import Store, StoreNameOnly
from gisaf.models.project import Project from gisaf.models.project import Project
@ -29,14 +29,12 @@ from gisaf.models.authentication import UserRoleLink #, ACL
from gisaf.database import pandas_query, fastapi_db_session as db_session from gisaf.database import pandas_query, fastapi_db_session as db_session
from gisaf.security import ( from gisaf.security import (
Token, Token,
authenticate_user, get_current_user, create_access_token, authenticate_user, get_current_active_user, create_access_token,
) )
from gisaf.registry import registry, NotInRegistry from gisaf.registry import registry, NotInRegistry
from gisaf.custom_store_base import BaseStore from gisaf.custom_store_base import BaseStore
from gisaf.redis_tools import store as redis_store from gisaf.redis_tools import store as redis_store
from gisaf.models.info import ( from gisaf.models.info import FeatureInfo
FeatureInfo, InfoItem, Attachment, InfoCategory
)
from gisaf.live_utils import get_live_feature_info from gisaf.live_utils import get_live_feature_info
from gisaf.plugins import manager as plugin_manager, NoSuchAction from gisaf.plugins import manager as plugin_manager, NoSuchAction
from gisaf.utils import gisTypeSymbolMap from gisaf.utils import gisTypeSymbolMap
@ -53,7 +51,7 @@ api = APIRouter(
@api.get('/bootstrap') @api.get('/bootstrap')
async def bootstrap( async def bootstrap(
user: Annotated[UserRead, Depends(get_current_user)]) -> BootstrapData: user: Annotated[UserRead, Depends(get_current_active_user)]) -> BootstrapData:
return BootstrapData(user=user) return BootstrapData(user=user)
@ -78,7 +76,7 @@ async def login_for_access_token(
@api.get('/logout') @api.get('/logout')
async def logout( async def logout(
user: Annotated[UserRead, Depends(get_current_user)]): user: Annotated[UserRead, Depends(get_current_active_user)]):
logger.info(f'{user.username} ({user.id}) logged out') logger.info(f'{user.username} ({user.id}) logged out')
@ -100,7 +98,7 @@ async def get_roles(
@api.get('/acls') @api.get('/acls')
async def get_acls(db_session: db_session, async def get_acls(db_session: db_session,
user: Annotated[User, Depends(get_current_user)]) -> list[UserRoleLink]: user: Annotated[User, Depends(get_current_active_user)]) -> list[UserRoleLink]:
"""New: ACLs returned as UserRoleLink""" """New: ACLs returned as UserRoleLink"""
if not user or not user.has_role('manager'): if not user or not user.has_role('manager'):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)

View file

@ -3,8 +3,10 @@ from pickle import loads
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
import logging import logging
# from typing import Any
from sqlmodel import Field, Relationship, String from matplotlib.figure import Figure
from sqlmodel import Field, Relationship, String, JSON
from pydantic import BaseModel from pydantic import BaseModel
import pandas as pd import pandas as pd
@ -13,12 +15,10 @@ from gisaf.models.metadata import gisaf
from gisaf.models.models_base import Model from gisaf.models.models_base import Model
from gisaf.models.misc import NotADataframeError from gisaf.models.misc import NotADataframeError
import matplotlib.pyplot as plt
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
try:
import matplotlib.pyplot as plt
except ImportError:
plt = None # type: ignore
class DashboardPageSource(Model, table=True): class DashboardPageSource(Model, table=True):
@ -34,17 +34,26 @@ class DashboardPageCommon:
Base class for DashboardPage and DashboardPageSection, where some methods Base class for DashboardPage and DashboardPageSection, where some methods
are common, eg. attachments are common, eg. attachments
""" """
name: str
df: bytes
plot: bytes
#plot: dict[str, Any] | None = Field(sa_type=JSON(none_as_null=True)) # type: ignore
attachment: str | None
html: str | None = None
def get_attachment_url(self): def ensure_dir_exists(self):
## Serve through web front-end (nginx static file) raise NotImplementedError()
if not self.attachment:
return
base_storage_url = conf.dashboard.base_storage_url
if not base_storage_url:
base_storage_url = '/dashboard-attachment/'
return f'{base_storage_url}{self.group}/{self.attachment}'
def save_plot(self, plot): def get_plot_file_path(self) -> Path:
raise NotImplementedError()
def get_attachment_file_name(self) -> str:
raise NotImplementedError()
def get_plot_file_name(self):
raise NotImplementedError()
def save_plot(self, plot: plt.Axes | plt.Figure): # type: ignore
""" """
Render the matplotlib plot (or figure) and save it in the filesystem Render the matplotlib plot (or figure) and save it in the filesystem
:param plot: matplotlib plot or figure... :param plot: matplotlib plot or figure...
@ -53,11 +62,10 @@ class DashboardPageCommon:
self.ensure_dir_exists() self.ensure_dir_exists()
## Different types of figures supported ## Different types of figures supported
fig = None fig: Figure | None = None
if plt: if isinstance(plot, plt.Axes): # type: ignore
if isinstance(plot, plt.Axes): fig = plot.figure # type: ignore
fig = plot.figure elif isinstance(plot, plt.Figure): # type: ignore
elif isinstance(plot, plt.Figure):
fig = plot fig = plot
if fig: if fig:
fig.savefig(self.get_plot_file_path(), bbox_inches='tight') fig.savefig(self.get_plot_file_path(), bbox_inches='tight')
@ -71,7 +79,7 @@ class DashboardPageCommon:
#f'in {self.get_attachment_file_name()}') #f'in {self.get_attachment_file_name()}')
return self.get_plot_file_name() return self.get_plot_file_name()
def save_attachment(self, attached, name=None): def save_attachment(self, attached, name=None) -> str | None:
""" """
Save the attachment in the filesystem Save the attachment in the filesystem
:param attached: matplotlib plot or figure... :param attached: matplotlib plot or figure...
@ -84,11 +92,11 @@ class DashboardPageCommon:
self.ensure_dir_exists() self.ensure_dir_exists()
## Different types of figures supported ## Different types of figures supported
fig = None fig: Figure | None = None
if plt: if plt:
if isinstance(attached, plt.Axes): if isinstance(attached, plt.Axes): # type: ignore
fig = attached.figure fig = attached.figure # type: ignore
elif isinstance(attached, plt.Figure): elif isinstance(attached, plt.Figure): # type: ignore
fig = attached fig = attached
if fig: if fig:
fig.savefig(self.get_attachment_file_name(), bbox_inches='tight') fig.savefig(self.get_attachment_file_name(), bbox_inches='tight')
@ -96,7 +104,7 @@ class DashboardPageCommon:
if attached and not fig: if attached and not fig:
logger.warning('Cannot save dashboard attachment (unknown attachment type)') logger.warning('Cannot save dashboard attachment (unknown attachment type)')
return return None
#logger.info(f'Saved attachment of dashboard page {self.group}/{self.name} ' #logger.info(f'Saved attachment of dashboard page {self.group}/{self.name} '
#f'in {self.get_attachment_file_name()}') #f'in {self.get_attachment_file_name()}')
@ -114,10 +122,18 @@ class DashboardPageCommon:
raise NotADataframeError() raise NotADataframeError()
def get_plot(self): def get_plot(self):
if self.plot is not None:
return loads(self.plot) return loads(self.plot)
class DashboardPage(Model, DashboardPageCommon, table=True): class DashboardPageMetaData(BaseModel):
name: str
group: str
description: str
viewable_role: str | None = None
class DashboardPage(Model, DashboardPageCommon, DashboardPageMetaData, table=True):
__tablename__ = 'dashboard_page' # type: ignore __tablename__ = 'dashboard_page' # type: ignore
__table_args__ = gisaf.table_args __table_args__ = gisaf.table_args
@ -125,19 +141,12 @@ class DashboardPage(Model, DashboardPageCommon, table=True):
menu = 'Dashboard' menu = 'Dashboard'
id: int = Field(primary_key=True) id: int = Field(primary_key=True)
name: str time: datetime | None = Field(default_factory=datetime.now)
notebook: str notebook: str | None = None
group: str source_id: int | None = Field(foreign_key=gisaf.table('dashboard_page_source.id'))
description: str expanded_panes: str | None
attachment: str
html: str
viewable_role: str
df: bytes
plot: bytes
source_id: int = Field(foreign_key=gisaf.table('dashboard_page_source.id'))
time: datetime
expanded_panes: str
source: DashboardPageSource = Relationship() source: DashboardPageSource = Relationship()
sections: list['DashboardPageSection'] = Relationship()
def __str__(self): def __str__(self):
return f'{self.group:s}/{self.name:s}' return f'{self.group:s}/{self.name:s}'
@ -169,6 +178,15 @@ class DashboardPage(Model, DashboardPageCommon, table=True):
else: else:
raise UserWarning('Cannot save attachment: no notebook/base_storage_dir in gisaf config') raise UserWarning('Cannot save attachment: no notebook/base_storage_dir in gisaf config')
def get_attachment_url(self):
## Serve through web front-end (nginx static file)
if not self.attachment:
return
base_storage_url = conf.dashboard.base_storage_url
if not base_storage_url:
base_storage_url = '/dashboard-attachment/'
return f'{base_storage_url}{self.group}/{self.attachment}'
def get_notebook_url(self): def get_notebook_url(self):
if self.notebook: if self.notebook:
base_url = conf.dashboard.base_source_url base_url = conf.dashboard.base_source_url
@ -188,13 +206,9 @@ class DashboardPageSection(Model, DashboardPageCommon, table=True):
id: str = Field(primary_key=True) id: str = Field(primary_key=True)
name: str name: str
dashboard_page_id: int = Field(foreign_key=gisaf.table('dashboard_page.id')) dashboard_page_id: int = Field(foreign_key=gisaf.table('dashboard_page.id'))
dashboard_page: DashboardPage = Relationship() dashboard_page: DashboardPage = Relationship(back_populates='sections')
description: str description: str
attachment: str
html: str
df: bytes
plot: str
def __str__(self): def __str__(self):
return f'{self.name} for dashboard page #{self.dashboard_page_id}' return f'{self.name} for dashboard page #{self.dashboard_page_id}'
@ -266,28 +280,28 @@ class Widget(Model, table=True):
menu = 'Dashboard' menu = 'Dashboard'
class DashboadPageSectionType(BaseModel): class DashboardSection(BaseModel):
name: str name: str
plot: str plot: str
class DashboardPage_(BaseModel): class Dashboard(BaseModel):
name: str name: str
group: str group: str
description: str description: str
time: datetime | None = Field(default_factory=datetime.now) time: datetime | None = Field(default_factory=datetime.now)
html: str | None = None html: str | None = None
attachment: str | None = None attachment: str | None = None
dfData: str | None = None dfData: list = []
plotData: str | None = None plotData: str | None = None
notebook: str | None = None notebook: str | None = None
expandedPanes: list[str] | None = None expandedPanes: list[str] | None = None
sections: list[DashboadPageSectionType] | None = None sections: list[DashboardSection] | None = None
class DashboardGroup(BaseModel): class DashboardGroup(BaseModel):
name: str name: str
pages: list[DashboardPage_] pages: list[DashboardPageMetaData]
class DashboardHome(BaseModel): class DashboardHome(BaseModel):