فهرست منبع

GUI: frontend decouple get function list (#820) (#858)

* get function list

* per Fred

---------

Co-authored-by: Fred Lefévère-Laoide <90181748+FredLL-Avaiga@users.noreply.github.com>
Dinh Long Nguyen 1 سال پیش
والد
کامیت
2ed3aa52de

+ 16 - 9
frontend/taipy-gui/base/src/app.ts

@@ -3,7 +3,7 @@ import { sendWsMessage, TAIPY_CLIENT_ID } from "../../src/context/wsUtils";
 import { uploadFile } from "../../src/workers/fileupload";
 import { uploadFile } from "../../src/workers/fileupload";
 
 
 import { Socket, io } from "socket.io-client";
 import { Socket, io } from "socket.io-client";
-import { VariableManager } from "./variableManager";
+import { DataManager } from "./dataManager";
 import { initSocket } from "./utils";
 import { initSocket } from "./utils";
 
 
 export type OnInitHandler = (appManager: TaipyApp) => void;
 export type OnInitHandler = (appManager: TaipyApp) => void;
@@ -13,7 +13,8 @@ export class TaipyApp {
     socket: Socket;
     socket: Socket;
     _onInit: OnInitHandler | undefined;
     _onInit: OnInitHandler | undefined;
     _onChange: OnChangeHandler | undefined;
     _onChange: OnChangeHandler | undefined;
-    variableManager: VariableManager | undefined;
+    variableData: DataManager | undefined;
+    functionData: DataManager | undefined;
     appId: string;
     appId: string;
     clientId: string;
     clientId: string;
     context: string;
     context: string;
@@ -28,7 +29,8 @@ export class TaipyApp {
         socket = socket || io("/", { autoConnect: false });
         socket = socket || io("/", { autoConnect: false });
         this.onInit = onInit;
         this.onInit = onInit;
         this.onChange = onChange;
         this.onChange = onChange;
-        this.variableManager = undefined;
+        this.variableData = undefined;
+        this.functionData = undefined;
         this.clientId = "";
         this.clientId = "";
         this.context = "";
         this.context = "";
         this.appId = "";
         this.appId = "";
@@ -74,27 +76,32 @@ export class TaipyApp {
 
 
     // Public methods
     // Public methods
     getEncodedName(varName: string, module: string) {
     getEncodedName(varName: string, module: string) {
-        return this.variableManager?.getEncodedName(varName, module);
+        return this.variableData?.getEncodedName(varName, module);
     }
     }
 
 
     getName(encodedName: string) {
     getName(encodedName: string) {
-        return this.variableManager?.getName(encodedName);
+        return this.variableData?.getName(encodedName);
     }
     }
 
 
     get(encodedName: string) {
     get(encodedName: string) {
-        return this.variableManager?.get(encodedName);
+        return this.variableData?.get(encodedName);
     }
     }
 
 
     getInfo(encodedName: string) {
     getInfo(encodedName: string) {
-        return this.variableManager?.getInfo(encodedName);
+        return this.variableData?.getInfo(encodedName);
     }
     }
 
 
     getDataTree() {
     getDataTree() {
-        return this.variableManager?.getDataTree();
+        return this.variableData?.getDataTree();
     }
     }
 
 
     getAllData() {
     getAllData() {
-        return this.variableManager?.getAllData();
+        return this.variableData?.getAllData();
+    }
+
+    getFunctionList() {
+        const functionData = this.functionData?.getDataTree()[this.context];
+        return Object.keys(functionData || {});
     }
     }
 
 
     // This update will only send the request to Taipy Gui backend
     // This update will only send the request to Taipy Gui backend

+ 29 - 33
frontend/taipy-gui/base/src/variableManager.ts → frontend/taipy-gui/base/src/dataManager.ts

@@ -1,73 +1,69 @@
-export interface VariableModuleData {
-    [key: string]: VariableName;
-}
+export type ModuleData = Record<string, VarName>;
 
 
-interface VariableName {
-    [key: string]: VariableData;
-}
+export type VarName = Record<string, VarData>;
 
 
-interface VariableData {
+interface VarData {
     type: string;
     type: string;
     value: unknown;
     value: unknown;
     encoded_name: string;
     encoded_name: string;
 }
 }
 
 
 // This class hold the information of variables and real-time value of variables
 // This class hold the information of variables and real-time value of variables
-export class VariableManager {
+export class DataManager {
     // key: encoded name, value: real-time value
     // key: encoded name, value: real-time value
     _data: Record<string, unknown>;
     _data: Record<string, unknown>;
     // Initial data fetched from taipy-gui backend
     // Initial data fetched from taipy-gui backend
-    _variables: VariableModuleData;
+    _init_data: ModuleData;
 
 
-    constructor(variableModuleData: VariableModuleData) {
+    constructor(variableModuleData: ModuleData) {
         this._data = {};
         this._data = {};
-        this._variables = {};
+        this._init_data = {};
         this.init(variableModuleData);
         this.init(variableModuleData);
     }
     }
 
 
-    init(variableModuleData: VariableModuleData) {
+    init(variableModuleData: ModuleData) {
         // Identify changes between the new and old data
         // Identify changes between the new and old data
-        const changes: VariableModuleData = {};
-        for (const context in this._variables) {
+        const changes: ModuleData = {};
+        for (const context in this._init_data) {
             if (!(context in variableModuleData)) {
             if (!(context in variableModuleData)) {
-                changes[context] = this._variables[context];
+                changes[context] = this._init_data[context];
                 continue;
                 continue;
             }
             }
-            for (const variable in this._variables[context]) {
+            for (const variable in this._init_data[context]) {
                 if (!(variable in variableModuleData[context])) {
                 if (!(variable in variableModuleData[context])) {
                     if (!(context in changes)) {
                     if (!(context in changes)) {
                         changes[context] = {};
                         changes[context] = {};
                     }
                     }
-                    changes[context][variable] = this._variables[context][variable];
+                    changes[context][variable] = this._init_data[context][variable];
                 }
                 }
             }
             }
         }
         }
-        if (Object.keys(changes).length === 0) {
+        if (Object.keys(changes).length !== 0) {
             console.error("Unmatched data tree! Removed changes: ", changes);
             console.error("Unmatched data tree! Removed changes: ", changes);
         }
         }
         // Reset the initial data
         // Reset the initial data
-        this._variables = variableModuleData;
+        this._init_data = variableModuleData;
         this._data = {};
         this._data = {};
-        for (const context in this._variables) {
-            for (const variable in this._variables[context]) {
-                const vData = this._variables[context][variable];
+        for (const context in this._init_data) {
+            for (const variable in this._init_data[context]) {
+                const vData = this._init_data[context][variable];
                 this._data[vData["encoded_name"]] = vData.value;
                 this._data[vData["encoded_name"]] = vData.value;
             }
             }
         }
         }
     }
     }
 
 
     getEncodedName(varName: string, module: string): string | undefined {
     getEncodedName(varName: string, module: string): string | undefined {
-        if (module in this._variables && varName in this._variables[module]) {
-            return this._variables[module][varName].encoded_name;
+        if (module in this._init_data && varName in this._init_data[module]) {
+            return this._init_data[module][varName].encoded_name;
         }
         }
         return undefined;
         return undefined;
     }
     }
 
 
     // return [name, moduleName]
     // return [name, moduleName]
     getName(encodedName: string): [string, string] | undefined {
     getName(encodedName: string): [string, string] | undefined {
-        for (const context in this._variables) {
-            for (const variable in this._variables[context]) {
-                const vData = this._variables[context][variable];
+        for (const context in this._init_data) {
+            for (const variable in this._init_data[context]) {
+                const vData = this._init_data[context][variable];
                 if (vData.encoded_name === encodedName) {
                 if (vData.encoded_name === encodedName) {
                     return [variable, context];
                     return [variable, context];
                 }
                 }
@@ -83,10 +79,10 @@ export class VariableManager {
         return this._data[encodedName];
         return this._data[encodedName];
     }
     }
 
 
-    getInfo(encodedName: string): VariableData | undefined {
-        for (const context in this._variables) {
-            for (const variable in this._variables[context]) {
-                const vData = this._variables[context][variable];
+    getInfo(encodedName: string): VarData | undefined {
+        for (const context in this._init_data) {
+            for (const variable in this._init_data[context]) {
+                const vData = this._init_data[context][variable];
                 if (vData.encoded_name === encodedName) {
                 if (vData.encoded_name === encodedName) {
                     return { ...vData, value: this._data[encodedName] };
                     return { ...vData, value: this._data[encodedName] };
                 }
                 }
@@ -95,8 +91,8 @@ export class VariableManager {
         return undefined;
         return undefined;
     }
     }
 
 
-    getDataTree(): VariableModuleData {
-        return this._variables;
+    getDataTree(): ModuleData {
+        return this._init_data;
     }
     }
 
 
     getAllData(): Record<string, unknown> {
     getAllData(): Record<string, unknown> {

+ 2 - 2
frontend/taipy-gui/base/src/index.ts

@@ -1,9 +1,9 @@
 import { TaipyApp, createApp, OnChangeHandler, OnInitHandler } from "./app";
 import { TaipyApp, createApp, OnChangeHandler, OnInitHandler } from "./app";
-import { VariableModuleData } from "./variableManager";
+import { ModuleData } from "./dataManager";
 
 
 export default TaipyApp;
 export default TaipyApp;
 export { TaipyApp, createApp };
 export { TaipyApp, createApp };
-export type { OnChangeHandler, OnInitHandler, VariableModuleData };
+export type { OnChangeHandler, OnInitHandler, ModuleData };
 
 
 window.addEventListener("beforeunload", () => {
 window.addEventListener("beforeunload", () => {
     document.cookie = "tprh=;path=/;Max-Age=-99999999;";
     document.cookie = "tprh=;path=/;Max-Age=-99999999;";

+ 12 - 8
frontend/taipy-gui/base/src/utils.ts

@@ -2,7 +2,7 @@ import { Socket } from "socket.io-client";
 import { IdMessage, storeClientId } from "../../src/context/utils";
 import { IdMessage, storeClientId } from "../../src/context/utils";
 import { WsMessage, sendWsMessage } from "../../src/context/wsUtils";
 import { WsMessage, sendWsMessage } from "../../src/context/wsUtils";
 import { TaipyApp } from "./app";
 import { TaipyApp } from "./app";
-import { VariableManager, VariableModuleData } from "./variableManager";
+import { DataManager, ModuleData } from "./dataManager";
 
 
 interface MultipleUpdatePayload {
 interface MultipleUpdatePayload {
     name: string;
     name: string;
@@ -49,7 +49,7 @@ const processWsMessage = (message: WsMessage, appManager: TaipyApp) => {
             for (const muPayload of message.payload as [MultipleUpdatePayload]) {
             for (const muPayload of message.payload as [MultipleUpdatePayload]) {
                 const encodedName = muPayload.name;
                 const encodedName = muPayload.name;
                 const { value } = muPayload.payload;
                 const { value } = muPayload.payload;
-                appManager.variableManager?.update(encodedName, value);
+                appManager.variableData?.update(encodedName, value);
                 appManager.onChange && appManager.onChange(appManager, encodedName, value);
                 appManager.onChange && appManager.onChange(appManager, encodedName, value);
             }
             }
         } else if (message.type === "ID") {
         } else if (message.type === "ID") {
@@ -61,12 +61,16 @@ const processWsMessage = (message: WsMessage, appManager: TaipyApp) => {
             const mc = (message.payload as Record<string, unknown>).data as string;
             const mc = (message.payload as Record<string, unknown>).data as string;
             window.localStorage.setItem("ModuleContext", mc);
             window.localStorage.setItem("ModuleContext", mc);
             appManager.context = mc;
             appManager.context = mc;
-        } else if (message.type === "GVS") {
-            const variableData = (message.payload as Record<string, unknown>).data as VariableModuleData;
-            if (appManager.variableManager) {
-                appManager.variableManager.init(variableData);
+        } else if (message.type === "GDT") {
+            const payload = message.payload as Record<string, ModuleData>;
+            const variableData = payload.variable;
+            const functionData = payload.function;
+            if (appManager.variableData && appManager.functionData) {
+                appManager.variableData.init(variableData);
+                appManager.functionData.init(functionData);
             } else {
             } else {
-                appManager.variableManager = new VariableManager(variableData);
+                appManager.variableData = new DataManager(variableData);
+                appManager.functionData = new DataManager(functionData);
                 appManager.onInit && appManager.onInit(appManager);
                 appManager.onInit && appManager.onInit(appManager);
             }
             }
         } else if (message.type === "AID") {
         } else if (message.type === "AID") {
@@ -88,6 +92,6 @@ const postWsMessageProcessing = (message: WsMessage, appManager: TaipyApp) => {
         appManager.appId !== "" &&
         appManager.appId !== "" &&
         appManager.context !== ""
         appManager.context !== ""
     ) {
     ) {
-        sendWsMessage(appManager.socket, "GVS", "get_variables", {}, appManager.clientId, appManager.context);
+        sendWsMessage(appManager.socket, "GDT", "get_data_tree", {}, appManager.clientId, appManager.context);
     }
     }
 };
 };

+ 1 - 1
frontend/taipy-gui/src/context/wsUtils.ts

@@ -18,7 +18,7 @@ export type WsMessageType =
     | "PR"
     | "PR"
     | "ACK"
     | "ACK"
     | "GMC"
     | "GMC"
-    | "GVS"
+    | "GDT"
     | "AID";
     | "AID";
 
 
 export interface WsMessage {
 export interface WsMessage {

+ 5 - 0
taipy/gui/_renderers/json.py

@@ -13,6 +13,7 @@ from __future__ import annotations
 from datetime import date, datetime, time
 from datetime import date, datetime, time
 from json import JSONEncoder
 from json import JSONEncoder
 from pathlib import Path
 from pathlib import Path
+from types import FunctionType, LambdaType
 
 
 from flask.json.provider import DefaultJSONProvider
 from flask.json.provider import DefaultJSONProvider
 
 
@@ -32,6 +33,10 @@ def _default(o):
         return _date_to_string(o)
         return _date_to_string(o)
     if isinstance(o, Path):
     if isinstance(o, Path):
         return str(o)
         return str(o)
+    if isinstance(o, FunctionType):
+        return o.__name__
+    if isinstance(o, LambdaType):
+        return str(o)
     try:
     try:
         raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
         raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
     except Exception as e:
     except Exception as e:

+ 26 - 16
taipy/gui/gui.py

@@ -617,8 +617,8 @@ class Gui:
                         self.__request_var_update(message.get("payload"))
                         self.__request_var_update(message.get("payload"))
                     elif msg_type == _WsType.GET_MODULE_CONTEXT.value:
                     elif msg_type == _WsType.GET_MODULE_CONTEXT.value:
                         self.__handle_ws_get_module_context(payload)
                         self.__handle_ws_get_module_context(payload)
-                    elif msg_type == _WsType.GET_VARIABLES.value:
-                        self.__handle_ws_get_variables()
+                    elif msg_type == _WsType.GET_DATA_TREE.value:
+                        self.__handle_ws_get_data_tree()
                     elif msg_type == _WsType.APP_ID.value:
                     elif msg_type == _WsType.APP_ID.value:
                         self.__handle_ws_app_id(message)
                         self.__handle_ws_app_id(message)
                 self.__send_ack(message.get("ack_id"))
                 self.__send_ack(message.get("ack_id"))
@@ -1064,20 +1064,9 @@ class Gui:
                     }
                     }
                 )
                 )
 
 
-    def __handle_ws_get_variables(self):
-        # Get Variables
-        self.__pre_render_pages()
+    def __get_variable_tree(self, data: t.Dict[str, t.Any]):
         # Module Context -> Variable -> Variable data (name, type, initial_value)
         # Module Context -> Variable -> Variable data (name, type, initial_value)
         variable_tree: t.Dict[str, t.Dict[str, t.Dict[str, t.Any]]] = {}
         variable_tree: t.Dict[str, t.Dict[str, t.Dict[str, t.Any]]] = {}
-        data = vars(self._bindings()._get_data_scope())
-        data = {
-            k: v
-            for k, v in data.items()
-            if not k.startswith("_")
-            and not callable(v)
-            and "TpExPr" not in k
-            and not isinstance(v, (ModuleType, FunctionType, LambdaType, type, Page))
-        }
         for k, v in data.items():
         for k, v in data.items():
             if isinstance(v, _TaipyBase):
             if isinstance(v, _TaipyBase):
                 data[k] = v.get()
                 data[k] = v.get()
@@ -1091,10 +1080,31 @@ class Gui:
                 "value": data[k],
                 "value": data[k],
                 "encoded_name": k,
                 "encoded_name": k,
             }
             }
+        return variable_tree
+
+    def __handle_ws_get_data_tree(self):
+        # Get Variables
+        self.__pre_render_pages()
+        data = {
+            k: v
+            for k, v in vars(self._get_data_scope()).items()
+            if not k.startswith("_")
+            and not callable(v)
+            and "TpExPr" not in k
+            and not isinstance(v, (ModuleType, FunctionType, LambdaType, type, Page))
+        }
+        function_data = {
+            k: v
+            for k, v in vars(self._get_data_scope()).items()
+            if not k.startswith("_") and "TpExPr" not in k and isinstance(v, (FunctionType, LambdaType))
+        }
         self.__send_ws(
         self.__send_ws(
             {
             {
-                "type": _WsType.GET_VARIABLES.value,
-                "payload": {"data": variable_tree},
+                "type": _WsType.GET_DATA_TREE.value,
+                "payload": {
+                    "variable": self.__get_variable_tree(data),
+                    "function": self.__get_variable_tree(function_data),
+                },
             }
             }
         )
         )
 
 

+ 1 - 1
taipy/gui/gui_types.py

@@ -48,7 +48,7 @@ class _WsType(Enum):
     PARTIAL = "PR"
     PARTIAL = "PR"
     ACKNOWLEDGEMENT = "ACK"
     ACKNOWLEDGEMENT = "ACK"
     GET_MODULE_CONTEXT = "GMC"
     GET_MODULE_CONTEXT = "GMC"
-    GET_VARIABLES = "GVS"
+    GET_DATA_TREE = "GDT"
 
 
 
 
 NumberTypes = {"int", "int64", "float", "float64"}
 NumberTypes = {"int", "int64", "float", "float64"}