From c8ca68e1a43bfdc8244513bcd5e7b81a8ea07224 Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 25 Mar 2024 10:10:58 +0530 Subject: [PATCH] Dashboard: fix plots --- src/gisaf/api/dashboard.py | 20 +++++--------------- src/gisaf/models/dashboard.py | 17 ++++++++++++----- src/gisaf/utils.py | 11 +++++++++++ 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/gisaf/api/dashboard.py b/src/gisaf/api/dashboard.py index 2050287..bb6b2b7 100644 --- a/src/gisaf/api/dashboard.py +++ b/src/gisaf/api/dashboard.py @@ -1,7 +1,5 @@ import logging from pathlib import Path -from json import dumps -from typing import Annotated from fastapi import Depends, APIRouter, HTTPException, status, responses from sqlalchemy.orm import selectinload @@ -10,7 +8,7 @@ import pandas as pd import geopandas as gpd from gisaf.config import conf -from gisaf.utils import NumpyEncoder +from gisaf.utils import dict_array_to_list from gisaf.database import fastapi_db_session as db_session from gisaf.models.authentication import User from gisaf.models.dashboard import ( @@ -138,16 +136,8 @@ async def get_dashboard_page(group: str, name: str, 'see debug message') logger.exception(err) if page.plot: - try: - plot = page.get_plot() - plotData = { - 'data': [d.to_plotly_json() for d in plot.data], - 'layout': plot.layout.to_plotly_json(), - } - except Exception as err: - logger.warning(f'Dashboard: cannot add plot for page {page.name}, ' - 'see debug message') - logger.exception(err) - else: - dp.plotData = dumps(plotData, cls=NumpyEncoder) + plot = page.get_plot() + ## Convert manually numpy arrays to lists + dp.plotData = [dict_array_to_list(d.to_plotly_json()) for d in plot.data] + dp.plotLayout = plot.layout.to_plotly_json() return dp \ No newline at end of file diff --git a/src/gisaf/models/dashboard.py b/src/gisaf/models/dashboard.py index 76c2068..5a3f350 100644 --- a/src/gisaf/models/dashboard.py +++ b/src/gisaf/models/dashboard.py @@ -3,9 +3,11 @@ from pickle import loads from pathlib import Path from datetime import datetime import logging +from typing import Any # from typing import Any from matplotlib.figure import Figure +from plotly.graph_objs._figure import Figure as PlotlyFigure from sqlmodel import Field, Relationship, String, JSON from pydantic import BaseModel import pandas as pd @@ -110,18 +112,22 @@ class DashboardPageCommon: #f'in {self.get_attachment_file_name()}') return self.attachment - def get_page_df(self): + def get_page_df(self) -> pd.DataFrame | None: """ Get the dataframe of the page """ if not self.df: - return + return None try: - return pd.read_pickle(BytesIO(self.df), compression=None) + df = pd.read_pickle(BytesIO(self.df), compression=None) except KeyError: raise NotADataframeError() + if isinstance(df, dict): + return None + else: + return df - def get_plot(self): + def get_plot(self) -> PlotlyFigure | None: if self.plot is not None: return loads(self.plot) @@ -293,7 +299,8 @@ class Dashboard(BaseModel): html: str | None = None attachment: str | None = None dfData: list = [] - plotData: str | None = None + plotData: list[dict] | None = None + plotLayout: dict[str, Any] | None = None notebook: str | None = None expandedPanes: list[str] | None = None sections: list[DashboardSection] | None = None diff --git a/src/gisaf/utils.py b/src/gisaf/utils.py index 0894374..3273e83 100644 --- a/src/gisaf/utils.py +++ b/src/gisaf/utils.py @@ -83,6 +83,17 @@ gisTypeSymbolMap = { # ).transform +def dict_array_to_list(d: dict) -> dict: + '''Convert any ndarray a dict to plain python list. + Useful for transforming a Dataframe to a serializable object''' + for k, v in d.items(): + if isinstance(v, dict): + dict_array_to_list(v) + else: + if isinstance(v, ndarray): + d[k] = v.tolist() + return d + class NumpyEncoder(JSONEncoder): """ Encoder that can serialize numpy arrays and datetime objects