Parcourir la source

Support for pandas TimeStamp (#1412)

* Support for pandas TimeStamp

* do not recalculate nodes position in DAG on status change

* fix lint

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide il y a 11 mois
Parent
commit
67207e364d

+ 26 - 8
frontend/taipy/src/ScenarioDag.tsx

@@ -1,4 +1,5 @@
 import React, { useCallback, useEffect, useMemo, useState } from "react";
 import React, { useCallback, useEffect, useMemo, useState } from "react";
+import { Point } from '@projectstorm/geometry';
 import { CanvasWidget } from "@projectstorm/react-canvas-core";
 import { CanvasWidget } from "@projectstorm/react-canvas-core";
 import Box from "@mui/material/Box";
 import Box from "@mui/material/Box";
 import AppBar from "@mui/material/AppBar";
 import AppBar from "@mui/material/AppBar";
@@ -11,7 +12,13 @@ import createEngine from "@projectstorm/react-diagrams";
 import deepEqual from "fast-deep-equal/es6";
 import deepEqual from "fast-deep-equal/es6";
 
 
 import { DisplayModel, TaskStatuses } from "./utils/types";
 import { DisplayModel, TaskStatuses } from "./utils/types";
-import { addStatusToDisplayModel, createDagreEngine, initDiagram, populateModel, relayoutDiagram } from "./utils/diagram";
+import {
+    addStatusToDisplayModel,
+    createDagreEngine,
+    initDiagram,
+    populateModel,
+    relayoutDiagram,
+} from "./utils/diagram";
 import {
 import {
     createRequestUpdateAction,
     createRequestUpdateAction,
     createSendActionNameAction,
     createSendActionNameAction,
@@ -116,7 +123,6 @@ const ScenarioDag = (props: ScenarioDagProps) => {
                 // Do nothing
                 // Do nothing
             }
             }
         }
         }
-        dm = addStatusToDisplayModel(dm, taskStatuses);
         setDisplayModel((oldDm) => (deepEqual(oldDm, dm) ? oldDm : dm));
         setDisplayModel((oldDm) => (deepEqual(oldDm, dm) ? oldDm : dm));
     }, [props.scenario, props.defaultScenario, taskStatuses]);
     }, [props.scenario, props.defaultScenario, taskStatuses]);
 
 
@@ -124,7 +130,10 @@ const ScenarioDag = (props: ScenarioDagProps) => {
 
 
     const zoomToFit = useCallback(() => engine.zoomToFit(), [engine]);
     const zoomToFit = useCallback(() => engine.zoomToFit(), [engine]);
 
 
-    const onClick = useCallback((id: string) => onAction && dispatch(createSendActionNameAction(props.id, module, onSelect, id, onAction)), [props.id, onAction, onSelect, module, dispatch]);
+    const onClick = useCallback(
+        (id: string) => onAction && dispatch(createSendActionNameAction(props.id, module, onSelect, id, onAction)),
+        [props.id, onAction, onSelect, module, dispatch]
+    );
 
 
     useEffect(() => {
     useEffect(() => {
         const model = new TaipyDiagramModel(onClick);
         const model = new TaipyDiagramModel(onClick);
@@ -133,14 +142,23 @@ const ScenarioDag = (props: ScenarioDagProps) => {
         if (displayModel) {
         if (displayModel) {
             setScenarioId(displayModel[0]);
             setScenarioId(displayModel[0]);
             // populate model
             // populate model
-            doLayout = populateModel(displayModel, model);
+            doLayout = populateModel(addStatusToDisplayModel(displayModel, taskStatuses), model);
+        }
+        const rects = engine.getModel() && engine
+            .getModel()
+            .getNodes()
+            .reduce((pv, nm) => {
+                pv[nm.getID()] = nm.getPosition();
+                return pv;
+            }, {} as Record<string, Point>);
+        const hasPos = rects && Object.keys(rects).length;
+        if (hasPos) {
+            model.getNodes().forEach(nm => rects[nm.getID()] && nm.setPosition(rects[nm.getID()]));
         }
         }
         engine.setModel(model);
         engine.setModel(model);
-        // Block deletion
-        //engine.getActionEventBus().registerAction(new DeleteItemsAction({ keyCodes: [1] }));
         model.setLocked(true);
         model.setLocked(true);
-        doLayout && setTimeout(relayout, 500);
-    }, [displayModel, engine, relayout, onClick]);
+        doLayout && !hasPos && setTimeout(relayout, 500);
+    }, [displayModel, taskStatuses, engine, relayout, onClick]);
 
 
     useEffect(() => {
     useEffect(() => {
         const showVar = getUpdateVar(props.updateVars, "show");
         const showVar = getUpdateVar(props.updateVars, "show");

+ 1 - 1
frontend/taipy/src/ScenarioViewer.tsx

@@ -366,7 +366,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
             }
             }
         }
         }
         setValid(!!sc);
         setValid(!!sc);
-        setSubmissionStatus(0);
+        // setSubmissionStatus(0);
         setScenario((oldSc) => (oldSc === sc ? oldSc : sc ? (deepEqual(oldSc, sc) ? oldSc : sc) : invalidScenario));
         setScenario((oldSc) => (oldSc === sc ? oldSc : sc ? (deepEqual(oldSc, sc) ? oldSc : sc) : invalidScenario));
     }, [props.scenario, props.defaultScenario]);
     }, [props.scenario, props.defaultScenario]);
 
 

+ 2 - 2
frontend/taipy/src/utils/diagram.ts

@@ -130,8 +130,8 @@ export const relayoutDiagram = (engine: DiagramEngine, dagreEngine: DagreEngine)
     engine.repaintCanvas();
     engine.repaintCanvas();
 };
 };
 
 
-export const addStatusToDisplayModel = (dm?: DisplayModel, taskStatuses?: TaskStatuses) => {
-    if (dm && taskStatuses) {
+export const addStatusToDisplayModel = (dm: DisplayModel, taskStatuses?: TaskStatuses) => {
+    if (taskStatuses) {
         Object.values(dm[1]).forEach((node) =>
         Object.values(dm[1]).forEach((node) =>
             Object.entries(node).forEach(([id, detail]) => {
             Object.entries(node).forEach(([id, detail]) => {
                 detail.status = taskStatuses[id];
                 detail.status = taskStatuses[id];

+ 2 - 0
taipy/gui/utils/date.py

@@ -23,6 +23,8 @@ def _date_to_string(date_val: t.Union[datetime, date, time]) -> str:
     if isinstance(date_val, datetime):
     if isinstance(date_val, datetime):
         # return date.isoformat() + 'Z', if possible
         # return date.isoformat() + 'Z', if possible
         try:
         try:
+            if date_val.tzinfo is None:
+                return date_val.isoformat()
             return date_val.astimezone(utc).isoformat()
             return date_val.astimezone(utc).isoformat()
         except Exception as e:
         except Exception as e:
             # astimezone() fails on Windows for pre-epoch times
             # astimezone() fails on Windows for pre-epoch times

+ 1 - 4
taipy/gui_core/_adapters.py

@@ -19,7 +19,6 @@ from collections.abc import Iterable
 from dataclasses import dataclass
 from dataclasses import dataclass
 from datetime import date, datetime
 from datetime import date, datetime
 from enum import Enum
 from enum import Enum
-from numbers import Number
 from operator import attrgetter, contains, eq, ge, gt, le, lt, ne
 from operator import attrgetter, contains, eq, ge, gt, le, lt, ne
 
 
 import pandas as pd
 import pandas as pd
@@ -182,10 +181,8 @@ class _GuiCoreDatanodeAdapter(_TaipyBase):
                     return (None, None, True, None)
                     return (None, None, True, None)
                 val_type = (
                 val_type = (
                     "date"
                     "date"
-                    if "date" in type(value).__name__
+                    if "date" in type(value).__name__.lower() or "timestamp" in type(value).__name__.lower()
                     else type(value).__name__
                     else type(value).__name__
-                    if isinstance(value, Number)
-                    else None
                 )
                 )
                 if isinstance(value, float) and math.isnan(value):
                 if isinstance(value, float) and math.isnan(value):
                     value = None
                     value = None