浏览代码

Merge branch 'develop' into 1396-discussion-adding-color_range_step-property-to-metric-component

Nam Nguyen 11 月之前
父节点
当前提交
f976188fe2

+ 29 - 22
README.md

@@ -11,52 +11,59 @@
 </div>
 </div>
 
 
 <h1 align="center">
 <h1 align="center">
-Data and AI algorithms into production-ready web apps
+Build Python Data & AI web applications
 </h1>
 </h1>
 
 
 <div align="center">
 <div align="center">
-Taipy is an open-source Python library for easy, end-to-end application development,<br />featuring what-if analyses, smart pipeline execution, built-in scheduling, and deployment tools.
+From simple pilots to production-ready web applications in no time. No more compromise on performance, customization, and scalability.
 </div>
 </div>
 
 
-  <p align="center">
+<br />
+
+<div align="center">
+
+**Go beyond existing libraries**
+</div>
+
+
+
+  <p align="left">
+    <br />
+    <a href="https://docs.taipy.io/en/latest/"><strong>📚 Explore the docs </strong></a>
     <br />
     <br />
-    <a href="https://docs.taipy.io/en/latest/"><strong>Explore the docs »</strong></a>
-    <br/><br/>
-    <a href="https://discord.com/invite/SJyz2VJGxV">Discord support</a>
-    ·
-    <a href="https://docs.taipy.io/en/latest/gallery/">Demos & Examples</a>
+    <a href="https://discord.com/invite/SJyz2VJGxV">  🫱🏼‍🫲🏼 Discord support</a>
+    <br />
+    <a href="https://docs.taipy.io/en/latest/gallery/"> 👀 Demos & Examples</a>
   </p>
   </p>
 
 
 &nbsp;
 &nbsp;
 
 
 ## ⭐️ What's Taipy?
 ## ⭐️ What's Taipy?
-
-Taipy is designed for data scientists and machine learning engineers to build full-stack apps.
+Taipy is designed for data scientists and machine learning engineers to build data & AI web applications.
 &nbsp;
 &nbsp;
 
 
-⭐️ Enables building production-ready web applications.<br />
-⭐️ No need to learn new languages or full-stack frameworks.<br />
+⭐️ Enables building production-ready web applications. <br />
+⭐️ No need to learn new languages. Only Python is needed.<br />
 ⭐️ Concentrate on Data and AI algorithms without development and deployment complexities.
 ⭐️ Concentrate on Data and AI algorithms without development and deployment complexities.
 
 
 &nbsp;
 &nbsp;
 
 
+<h4 align="left">
+Taipy is a Two-in-One Tool for UI Generation and Scenario/Data Management
+</h4>
+
+ <br />
+
 | User Interface Generation  | Scenario and Data Management |
 | User Interface Generation  | Scenario and Data Management |
 | --------  | -------- |
 | --------  | -------- |
-|<img src="readme_img/gui_creation.webp" alt="Interface Animation"  width="850px" height="250px" /> | <img src="readme_img/scenario_and_data_mgt.gif" alt="Back-End Animation"  width="100%"/>
+|<img src="readme_img/taipy_github_GUI_video.gif" alt="Interface Animation"  width="100%" /> | <img src="readme_img/taipy_github_scenarios_video.gif" alt="Back-End Animation"  width="100%"/>
 
 
 &nbsp;
 &nbsp;
 
 
 ## ✨ Features
 ## ✨ Features
-- **Python-Based UI Framework:** Taipy is designed for Python users, particularly those working in AI and data science. It allows them to create full stack applications without needing to learn additional skills like HTML, CSS, or JavaScript.
-
-
-- **Pre-Built Components for Data Pipelines:** Taipy includes pre-built components that allow users to interact with data pipelines, including visualization and management tools.
-
-
-- **Scenario and Data Management Features:** Taipy offers features for managing different business scenarios and data, which can be useful for applications like demand forecasting or production planning.
-
+<img src="readme_img/taipy_github_scenario.png" alt="Scenario Banner"  width="49%" />  <img src="readme_img/taipy-github-optimized.png" alt="Back-End Animation"  width="49.7%"/>
+<img src="readme_img/taipy_github_data_support.png" alt="Back-End Animation"  width="49.7%" />
 
 
-- **Version Management and Pipeline Orchestration:** It includes tools for managing application versions, pipeline versions, and data versions, which are beneficial for multi-user environments.
 
 
 &nbsp;
 &nbsp;
 
 

+ 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];

二进制
readme_img/taipy-github-optimized.png


二进制
readme_img/taipy_banner.png


二进制
readme_img/taipy_github_GUI_video.gif


二进制
readme_img/taipy_github_data_support.png


二进制
readme_img/taipy_github_scenario.png


二进制
readme_img/taipy_github_scenarios_video.gif


+ 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