Przeglądaj źródła

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 11 miesięcy temu
rodzic
commit
67207e364d

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

@@ -1,4 +1,5 @@
 import React, { useCallback, useEffect, useMemo, useState } from "react";
+import { Point } from '@projectstorm/geometry';
 import { CanvasWidget } from "@projectstorm/react-canvas-core";
 import Box from "@mui/material/Box";
 import AppBar from "@mui/material/AppBar";
@@ -11,7 +12,13 @@ import createEngine from "@projectstorm/react-diagrams";
 import deepEqual from "fast-deep-equal/es6";
 
 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 {
     createRequestUpdateAction,
     createSendActionNameAction,
@@ -116,7 +123,6 @@ const ScenarioDag = (props: ScenarioDagProps) => {
                 // Do nothing
             }
         }
-        dm = addStatusToDisplayModel(dm, taskStatuses);
         setDisplayModel((oldDm) => (deepEqual(oldDm, dm) ? oldDm : dm));
     }, [props.scenario, props.defaultScenario, taskStatuses]);
 
@@ -124,7 +130,10 @@ const ScenarioDag = (props: ScenarioDagProps) => {
 
     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(() => {
         const model = new TaipyDiagramModel(onClick);
@@ -133,14 +142,23 @@ const ScenarioDag = (props: ScenarioDagProps) => {
         if (displayModel) {
             setScenarioId(displayModel[0]);
             // 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);
-        // Block deletion
-        //engine.getActionEventBus().registerAction(new DeleteItemsAction({ keyCodes: [1] }));
         model.setLocked(true);
-        doLayout && setTimeout(relayout, 500);
-    }, [displayModel, engine, relayout, onClick]);
+        doLayout && !hasPos && setTimeout(relayout, 500);
+    }, [displayModel, taskStatuses, engine, relayout, onClick]);
 
     useEffect(() => {
         const showVar = getUpdateVar(props.updateVars, "show");

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

@@ -366,7 +366,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
             }
         }
         setValid(!!sc);
-        setSubmissionStatus(0);
+        // setSubmissionStatus(0);
         setScenario((oldSc) => (oldSc === sc ? oldSc : sc ? (deepEqual(oldSc, sc) ? oldSc : sc) : invalidScenario));
     }, [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();
 };
 
-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.entries(node).forEach(([id, detail]) => {
                 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):
         # return date.isoformat() + 'Z', if possible
         try:
+            if date_val.tzinfo is None:
+                return date_val.isoformat()
             return date_val.astimezone(utc).isoformat()
         except Exception as e:
             # 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 datetime import date, datetime
 from enum import Enum
-from numbers import Number
 from operator import attrgetter, contains, eq, ge, gt, le, lt, ne
 
 import pandas as pd
@@ -182,10 +181,8 @@ class _GuiCoreDatanodeAdapter(_TaipyBase):
                     return (None, None, True, None)
                 val_type = (
                     "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__
-                    if isinstance(value, Number)
-                    else None
                 )
                 if isinstance(value, float) and math.isnan(value):
                     value = None